TicketListController.m [plain text]
/*
* TicketListController.m
*
* $Header$
*
* Copyright 2004 Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*/
#import "TicketListController.h"
#import "CacheCollection.h"
#import "Cache.h"
#import "Utilities.h"
#import "ErrorAlert.h"
#import "Preferences.h"
NSString *ActiveUserMenuToolbarItemIdentifier = @"ActiveUserMenu";
NSString *GetTicketsToolbarItemIdentifier = @"GetTickets";
NSString *RenewTicketsToolbarItemIdentifier = @"RenewTickets";
NSString *DestroyTicketsToolbarItemIdentifier = @"DestroyTickets";
NSString *ChangePasswordToolbarItemIdentifier = @"ChangePassword";
NSString *TicketInfoToolbarItemIdentifier = @"TicketInfo";
NSString *TicketListFrameAutosaveName = @"KATicketListWindowPosition";
@interface ActiveUserToolbarItem : NSToolbarItem
{
}
- (id) initWithItemIdentifier: (NSString *) itemIdentifier;
- (void) validate;
- (void) menuNeedsUpdate: (NSMenu *) menu;
@end
@implementation ActiveUserToolbarItem
// ---------------------------------------------------------------------------
- (id) initWithItemIdentifier: (NSString *) itemIdentifier
{
if ((self = [super initWithItemIdentifier: itemIdentifier])) {
NSRect frameRect = { { 0, 0 }, { 150, 22 } };
NSPopUpButton *button = [[[NSPopUpButton alloc] initWithFrame: frameRect pullsDown: NO] autorelease];
if (button == NULL) {
[self release];
return NULL;
}
[button setAutoenablesItems: NO]; // item state set by menuNeedsUpdate:
[[button menu] setDelegate: self];
[[button cell] setControlSize: NSSmallControlSize];
NSSize minimumSize = [button bounds].size;
NSSize maximumSize = [button bounds].size;
maximumSize.width += 200;
[self setView: button];
[self setMinSize: minimumSize];
[self setMaxSize: maximumSize];
}
return self;
}
// ---------------------------------------------------------------------------
- (void) validate
{
NSMenu *menu = [[self view] menu];
if (menu != NULL) {
[self menuNeedsUpdate: menu];
}
}
// ---------------------------------------------------------------------------
- (void) menuNeedsUpdate: (NSMenu *) menu
{
// Assume all menus are the active user popup menu
// If we get a second menu type, we'll have to figure out something else
int defaultCacheIndex = 0;
[Utilities synchronizeCacheMenu: menu
fontSize: 11 // default size
staticPrefixItemsCount: 0
headerItem: NO
checkDefaultCache: YES
defaultCacheIndex: &defaultCacheIndex
selector: @selector (changeActiveUser:)
sender: [self target]];
NSPopUpButton *popUpButton = (NSPopUpButton *) [self view];
if ((popUpButton != NULL) && [popUpButton isKindOfClass: [NSPopUpButton class]]) {
[popUpButton selectItemAtIndex: defaultCacheIndex];
}
}
@end
#pragma mark -
@implementation TicketListController
// ---------------------------------------------------------------------------
- (id) init
{
if ((self = [super initWithWindowNibName: @"TicketList"])) {
cacheCollection = NULL;
toolbar = NULL;
minuteTimer = NULL;
cacheNameString = NULL;
cascadePoint = NSZeroPoint;
cacheCollection = [[CacheCollection sharedCacheCollection] retain];
if (cacheCollection == NULL) {
[self release];
return NULL;
}
toolbar = [[NSToolbar alloc] initWithIdentifier: @"TicketListToolbar"];
if (toolbar == NULL) {
[self release];
return NULL;
}
[toolbar setAutosavesConfiguration: YES];
}
return self;
}
// ---------------------------------------------------------------------------
- (void) dealloc
{
dprintf ("Ticket List window [[NSNotificationCenter defaultCenter] removeObserver: self];
if (minuteTimer != NULL) {
// invalidate a TargetOwnedTimer before releasing it
[minuteTimer invalidate];
[minuteTimer release];
}
if (cacheCollection != NULL) { [cacheCollection release]; }
if (cacheNameString != NULL) { [cacheNameString release]; }
if (toolbar != NULL) { [toolbar release]; }
[super dealloc];
}
// ---------------------------------------------------------------------------
- (void) windowDidLoad
{
[super windowDidLoad];
dprintf ("TicketListController
minuteTimer = [[TargetOwnedTimer scheduledTimerWithTimeInterval: 30 // controls the minute counters in the window
target: self
selector: @selector (minuteTimer:)
userInfo: NULL
repeats: YES] retain];
// toolbar
[toolbar setDelegate: self];
[[self window] setToolbar: toolbar];
// Automatically resize columns in the tables.
// Do not move columns when disclosure triangles are selected
[cacheCollectionTableView setColumnAutoresizingStyle: NSTableViewSequentialColumnAutoresizingStyle];
[ticketsOutlineView setColumnAutoresizingStyle: NSTableViewSequentialColumnAutoresizingStyle];
[ticketsOutlineView setAutoresizesOutlineColumn: NO];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector (cacheCollectionDidChange:)
name: CacheCollectionDidChangeNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector (preferencesDidChange:)
name: PreferencesDidChangeNotification
object: nil];
// load the first time manually. After that the notifications will do the work
[self cacheCollectionDidChange: NULL];
}
// ---------------------------------------------------------------------------
- (IBAction) showWindow: (id) sender
{
// Check to see if the window was closed before. ([self window] will load the window)
if (![[self window] isVisible]) {
dprintf ("TicketListController
[[self window] center];
[self preferencesDidChange: NULL];
[self synchronizeCascadePoint];
}
[super showWindow: sender];
}
#pragma mark --- Actions --
// ---------------------------------------------------------------------------
- (IBAction) getTickets: (id) sender
{
KLStatus err = [Principal getTickets];
if (err == klNoErr) {
[cacheCollection update];
} else if (err != klUserCanceledErr) {
[ErrorAlert alertForError: err
action: KerberosGetTicketsAction
modalForWindow: [self window]];
}
}
// ---------------------------------------------------------------------------
- (IBAction) changePasswordForSelectedCache: (id) sender
{
Cache *cache = [self selectedCache];
if (cache != NULL) {
KLStatus err = [[cache principal] changePassword];
if (err == klNoErr) {
[cacheCollection update];
} else if (err != klUserCanceledErr) {
[ErrorAlert alertForError: err
action: KerberosChangePasswordAction
modalForWindow: [self window]];
}
}
}
// ---------------------------------------------------------------------------
- (IBAction) destroyTicketsForSelectedCache: (id) sender
{
Cache *cache = [self selectedCache];
if (cache != NULL) {
KLStatus err = [[cache principal] destroyTickets];
if (err == klNoErr) {
[cacheCollection update];
} else if (err != klUserCanceledErr) {
[ErrorAlert alertForError: err
action: KerberosDestroyTicketsAction
modalForWindow: [self window]];
}
}
}
// ---------------------------------------------------------------------------
- (IBAction) renewTicketsForSelectedCache: (id) sender
{
Cache *cache = [self selectedCache];
if (cache != NULL) {
KLStatus err = [[cache principal] renewTickets];
if (err == klNoErr) {
[cacheCollection update];
} else if (err != klUserCanceledErr) {
[ErrorAlert alertForError: err
action: KerberosRenewTicketsAction
modalForWindow: [self window]];
}
}
}
// ---------------------------------------------------------------------------
- (IBAction) validateTicketsForSelectedCache: (id) sender
{
Cache *cache = [self selectedCache];
if (cache != NULL) {
KLStatus err = [[cache principal] validateTickets];
if (err == klNoErr) {
[cacheCollection update];
} else if (err != klUserCanceledErr) {
[ErrorAlert alertForError: err
action: KerberosValidateTicketsAction
modalForWindow: [self window]];
}
}
}
// ---------------------------------------------------------------------------
- (IBAction) changeActiveUser: (id) sender
{
if ([sender isMemberOfClass: [NSMenuItem class]]) {
NSMenuItem *item = sender;
NSMenu *menu = [item menu];
if ([cacheCollection numberOfCaches] > 0) {
Cache *cache = [cacheCollection cacheAtIndex: [menu indexOfItem: item]];
if (![cache isDefault]) {
KLStatus err = [[cache principal] setDefault];
if (err == klNoErr) {
[cacheCollection update];
} else if (err != klUserCanceledErr) {
[ErrorAlert alertForError: err
action: KerberosChangeActiveUserAction
modalForWindow: [self window]];
}
}
}
}
}
// ---------------------------------------------------------------------------
- (IBAction) showTicketInfo: (id) sender
{
Credential *credential = [self selectedCredential];
if (credential != NULL) {
cascadePoint = [credential showInfoWindowCascadingFromPoint: cascadePoint];
}
}
#pragma mark --- Data Source Methods --
// ---------------------------------------------------------------------------
- (int) numberOfRowsInTableView: (NSTableView *) tableView
{
return [cacheCollection numberOfCaches];
}
// ---------------------------------------------------------------------------
- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) tableColumn row: (int) rowIndex
{
if (tableColumn == cacheCollectionTableColumn) {
return [cacheCollection stringValueForTicketColumnAtIndex: rowIndex];
} else if (tableColumn == cacheCollectionTimeRemainingTableColumn) {
return [cacheCollection stringValueForLifetimeColumnAtIndex: rowIndex];
} else {
return @"";
}
}
// ---------------------------------------------------------------------------
- (id) outlineView: (NSOutlineView *) outlineView child: (int) rowIndex ofItem: (id) item
{
Cache *cache = [self selectedCache];
if (cache != NULL) {
if (item == NULL) {
return [cache credentialsVersionAtIndex: rowIndex];
} else {
return [item childAtIndex: rowIndex];
}
}
return NULL;
}
// ---------------------------------------------------------------------------
- (BOOL) outlineView: (NSOutlineView *) outlineView isItemExpandable: (id) item
{
if ([self haveSelectedCache]) {
if (item == NULL) {
return YES;
} else {
return ([item numberOfChildren] > 0);
}
}
return NO;
}
// ---------------------------------------------------------------------------
- (int) outlineView: (NSOutlineView *) outlineView numberOfChildrenOfItem: (id) item
{
Cache *cache = [self selectedCache];
if (cache != NULL) {
if (item == NULL) {
return [cache numberOfCredentialsVersions];
} else {
return [item numberOfChildren];
}
}
return 0;
}
// ---------------------------------------------------------------------------
- (id) outlineView: (NSOutlineView *) outlineView objectValueForTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
{
if ([self haveSelectedCache]) {
if (item != NULL) {
if (tableColumn == ticketsTableColumn) {
return [item stringValueForTicketColumn];
} else if (tableColumn == ticketsTimeRemainingTableColumn) {
if ([outlineView isItemExpanded: item]) {
return @"";
} else {
return [item stringValueForLifetimeColumn];
}
}
}
}
return @"";
}
#pragma mark --- Delegate Methods --
// ---------------------------------------------------------------------------
- (BOOL) outlineView: (NSOutlineView *) outlineView shouldEditTableColumn: (NSTableColumn *) tableColumn item: (id) item
{
return NO;
}
// ---------------------------------------------------------------------------
- (BOOL) splitView: (NSSplitView *) sender canCollapseSubview: (NSView *) subview
{
return !([[sender subviews] objectAtIndex: 0] == subview);
}
// ---------------------------------------------------------------------------
- (float) splitView: (NSSplitView *) sender constrainMinCoordinate: (float) proposedMin ofSubviewAt: (int) offset
{
return (proposedMin + ([[cacheCollectionTableView headerView] frame].size.height +
([cacheCollectionTableView rowHeight] * 2) +
([cacheCollectionTableView intercellSpacing].height * 3)));
}
// ---------------------------------------------------------------------------
- (float) splitView: (NSSplitView *) sender constrainMaxCoordinate: (float) proposedMax ofSubviewAt: (int) offset
{
float m = (proposedMax - ([[ticketsOutlineView headerView] frame].size.height +
([ticketsOutlineView rowHeight] * 2) +
([ticketsOutlineView intercellSpacing].height * 3)));
return m;
}
// ---------------------------------------------------------------------------
- (NSToolbarItem *) toolbar: (NSToolbar *) aToolbar
itemForItemIdentifier: (NSString *) itemIdentifier
willBeInsertedIntoToolbar: (BOOL) flag
{
NSToolbarItem *toolbarItem = NULL;
if ([itemIdentifier isEqual: ActiveUserMenuToolbarItemIdentifier]) {
toolbarItem = [[[ActiveUserToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
[toolbarItem setLabel: NSLocalizedString (@"ActiveUserMenuToolbarItemLabel", NULL)];
[toolbarItem setPaletteLabel: NSLocalizedString (@"ActiveUserMenuToolbarItemLabel", NULL)];
[toolbarItem setToolTip: NSLocalizedString (@"ActiveUserMenuToolbarItemToolTip", NULL)];
[toolbarItem setTarget: self];
} else {
toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
[toolbarItem setTarget: self];
if ([itemIdentifier isEqual: GetTicketsToolbarItemIdentifier]) {
[toolbarItem setLabel: NSLocalizedString (@"GetTicketsToolbarItemLabel", NULL)];
[toolbarItem setPaletteLabel: NSLocalizedString (@"GetTicketsToolbarItemLabel", NULL)];
[toolbarItem setToolTip: NSLocalizedString (@"GetTicketsToolbarItemToolTip", NULL)];
[toolbarItem setImage: [NSImage imageNamed: @"GetTickets"]];
[toolbarItem setAction: @selector (getTickets:)];
} else if ([itemIdentifier isEqual: RenewTicketsToolbarItemIdentifier]) {
[toolbarItem setLabel: NSLocalizedString (@"RenewTicketsToolbarItemLabel", NULL)];
[toolbarItem setPaletteLabel: NSLocalizedString (@"RenewTicketsToolbarItemLabel", NULL)];
[toolbarItem setToolTip: NSLocalizedString (@"RenewTicketsToolbarItemToolTip", NULL)];
[toolbarItem setImage: [NSImage imageNamed: @"RenewTickets"]];
[toolbarItem setAction: @selector (renewTicketsForSelectedCache:)];
} else if ([itemIdentifier isEqual: DestroyTicketsToolbarItemIdentifier]) {
[toolbarItem setLabel: NSLocalizedString (@"DestroyTicketsToolbarItemLabel", NULL)];
[toolbarItem setPaletteLabel: NSLocalizedString (@"DestroyTicketsToolbarItemLabel", NULL)];
[toolbarItem setToolTip: NSLocalizedString (@"DestroyTicketsToolbarItemToolTip", NULL)];
[toolbarItem setImage: [NSImage imageNamed: @"DestroyTickets"]];
[toolbarItem setAction: @selector (destroyTicketsForSelectedCache:)];
} else if ([itemIdentifier isEqual: ChangePasswordToolbarItemIdentifier]) {
[toolbarItem setLabel: NSLocalizedString (@"ChangePasswordToolbarItemLabel", NULL)];
[toolbarItem setPaletteLabel: NSLocalizedString (@"ChangePasswordToolbarItemLabel", NULL)];
[toolbarItem setToolTip: NSLocalizedString (@"ChangePasswordToolbarItemToolTip", NULL)];
[toolbarItem setImage: [NSImage imageNamed: @"ChangePassword"]];
[toolbarItem setAction: @selector (changePasswordForSelectedCache:)];
} else if ([itemIdentifier isEqual: TicketInfoToolbarItemIdentifier]) {
[toolbarItem setLabel: NSLocalizedString (@"TicketInfoToolbarItemLabel", NULL)];
[toolbarItem setPaletteLabel: NSLocalizedString (@"TicketInfoToolbarItemLabel", NULL)];
[toolbarItem setToolTip: NSLocalizedString (@"TicketInfoToolbarItemToolTip", NULL)];
[toolbarItem setImage: [NSImage imageNamed: @"TicketInfo"]];
[toolbarItem setAction: @selector (showTicketInfo:)];
} else {
// Unsupported item type
toolbarItem = NULL;
}
}
return toolbarItem;
}
// ---------------------------------------------------------------------------
- (BOOL) validateToolbarItem: (NSToolbarItem *) theItem
{
NSString *itemIdentifier = [theItem itemIdentifier];
if ([itemIdentifier isEqual: ActiveUserMenuToolbarItemIdentifier]) {
return YES;
} else if ([itemIdentifier isEqual: GetTicketsToolbarItemIdentifier]) {
return YES;
} else if ([itemIdentifier isEqual: RenewTicketsToolbarItemIdentifier]) {
return [self haveSelectedCache];
} else if ([itemIdentifier isEqual: DestroyTicketsToolbarItemIdentifier]) {
return [self haveSelectedCache];
} else if ([itemIdentifier isEqual: ChangePasswordToolbarItemIdentifier]) {
return [self haveSelectedCache];
} else if ([itemIdentifier isEqual: TicketInfoToolbarItemIdentifier]) {
return [self haveSelectedCredential];
}
// Unsupported item type or error
return NO;
}
// ---------------------------------------------------------------------------
- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) aToolbar
{
return [NSArray arrayWithObjects:
ActiveUserMenuToolbarItemIdentifier,
NSToolbarFlexibleSpaceItemIdentifier,
GetTicketsToolbarItemIdentifier,
NSToolbarSeparatorItemIdentifier,
RenewTicketsToolbarItemIdentifier,
DestroyTicketsToolbarItemIdentifier,
ChangePasswordToolbarItemIdentifier,
TicketInfoToolbarItemIdentifier, NULL];
}
// ---------------------------------------------------------------------------
- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) aToolbar
{
return [NSArray arrayWithObjects:
ActiveUserMenuToolbarItemIdentifier,
NSToolbarFlexibleSpaceItemIdentifier,
GetTicketsToolbarItemIdentifier,
NSToolbarSeparatorItemIdentifier,
RenewTicketsToolbarItemIdentifier,
DestroyTicketsToolbarItemIdentifier,
ChangePasswordToolbarItemIdentifier,
NSToolbarSeparatorItemIdentifier,
TicketInfoToolbarItemIdentifier, NULL];
}
#pragma mark -- Callbacks --
// ---------------------------------------------------------------------------
- (void) minuteTimer: (TargetOwnedTimer *) timer
{
// Update things with time values in them:
[cacheCollectionTableView reloadData];
[ticketsOutlineView reloadData];
[self synchronizeWindowTitle];
}
#pragma mark --- Notifications --
// ---------------------------------------------------------------------------
- (void) cacheCollectionDidChange: (NSNotification *) notification
{
dprintf ("Ticket List window [cacheCollectionTableView reloadData];
[ticketsOutlineView reloadData];
// Attempt to select the cache that was previously selected
// Make sure we don't select anything while we are still looking at
// cacheNameString because selecting entries invalidates it
unsigned int newSelectedIndex = NSNotFound;
if (cacheNameString != NULL) {
Cache *cache = [cacheCollection findCacheForName: cacheNameString];
if (cache != NULL) {
newSelectedIndex = [cacheCollection indexOfCache: cache];
}
}
if (newSelectedIndex != NSNotFound) {
[cacheCollectionTableView selectRowIndexes: [NSIndexSet indexSetWithIndex: newSelectedIndex]
byExtendingSelection: NO];
} else if ([cacheCollection numberOfCaches] > 0) {
[cacheCollectionTableView selectRowIndexes: [NSIndexSet indexSetWithIndex: 0]
byExtendingSelection: NO]; // select first one
}
[self tableViewSelectionDidChange: NULL];
[self outlineViewSelectionDidChange: NULL];
// Now that the ticket list is up to date, let other objects know about it
[[NSNotificationCenter defaultCenter] postNotificationName: TicketListDidChangeNotification
object: self];
dprintf ("Ticket List window }
// ---------------------------------------------------------------------------
- (void) preferencesDidChange: (NSNotification *) notification
{
if ([[self window] isVisible]) {
[[self window] saveFrameUsingName: TicketListFrameAutosaveName];
}
if ([[Preferences sharedPreferences] ticketWindowDefaultPosition]) {
[self setWindowFrameAutosaveName: @""]; // Don't save frame position
} else {
[self setWindowFrameAutosaveName: TicketListFrameAutosaveName];
}
}
// ---------------------------------------------------------------------------
- (void) tableViewSelectionDidChange: (NSNotification *) notification
{
[ticketsOutlineView reloadData];
Cache *cache = [self selectedCache];
if (cache != NULL) {
int i;
// Used when cacheCollection changes out from under us
// so the user's selection follows the cache
if (cacheNameString != NULL) { [cacheNameString release]; }
cacheNameString = [[cache ccacheName] retain];
for (i = 0; i < [cache numberOfCredentialsVersions]; i++) {
[ticketsOutlineView expandItem: [cache credentialsVersionAtIndex: i]];
}
} else {
if (cacheNameString != NULL) { [cacheNameString release]; }
cacheNameString = NULL;
}
[self synchronizeToolbar];
[self synchronizeWindowTitle];
[[NSNotificationCenter defaultCenter] postNotificationName: CacheSelectionDidChangeNotification object: self];
}
// ---------------------------------------------------------------------------
- (void) outlineViewSelectionDidChange: (NSNotification *) notification
{
[self synchronizeToolbar];
[self synchronizeWindowTitle];
[[NSNotificationCenter defaultCenter] postNotificationName: TicketSelectionDidChangeNotification object: self];
}
// ---------------------------------------------------------------------------
- (void) windowDidMove: (NSNotification *) notification
{
[self synchronizeCascadePoint];
}
#pragma mark -- Utility Functions --
// ---------------------------------------------------------------------------
- (void) synchronizeWindowTitle
{
Cache *defaultCache = [cacheCollection defaultCache];
if (defaultCache != NULL) {
[[self window] setTitle: [defaultCache stringValueForWindowTitle]];
} else {
[[self window] setTitle: NSLocalizedString (@"KAppStringNoTicketsAvailable", NULL)];
}
}
// ---------------------------------------------------------------------------
- (void) synchronizeCascadePoint
{
NSRect frameRect = [[self window] frame];
NSPoint frameUpperLeft = frameRect.origin;
frameUpperLeft.y += frameRect.size.height;
NSRect contentRect = [[[self window] contentView] frame];
NSPoint contentUpperLeft = [[self window] convertBaseToScreen: contentRect.origin];
contentUpperLeft.y += contentRect.size.height;
float titleBarHeight = frameUpperLeft.y - contentUpperLeft.y;
cascadePoint.x = frameUpperLeft.x + titleBarHeight;
cascadePoint.y = frameUpperLeft.y - titleBarHeight;
//NSLog (@"framePoint is ( //NSLog (@"cascadePoint is (}
// ---------------------------------------------------------------------------
- (void) synchronizeToolbar
{
// Update the toolbar items
[toolbar validateVisibleItems];
}
// ---------------------------------------------------------------------------
- (BOOL) haveSelectedCache
{
return ([cacheCollectionTableView selectedRow] >= 0);
}
// ---------------------------------------------------------------------------
- (Cache *) selectedCache
{
if ([self haveSelectedCache]) {
return [cacheCollection cacheAtIndex: [cacheCollectionTableView selectedRow]];
} else {
return NULL;
}
}
// ---------------------------------------------------------------------------
- (BOOL) selectedCacheNeedsValidation
{
Cache *selectedCache = [self selectedCache];
return ((selectedCache != NULL) && [selectedCache needsValidation]);
}
// ---------------------------------------------------------------------------
- (BOOL) haveSelectedCredential
{
int selectedTicketRow = [ticketsOutlineView selectedRow];
if ((selectedTicketRow >= 0) &&
([[ticketsOutlineView itemAtRow: selectedTicketRow] isMemberOfClass: [Credential class]])) {
return YES;
}
return NO;
}
// ---------------------------------------------------------------------------
- (Credential *) selectedCredential
{
if ([self haveSelectedCredential]) {
return [ticketsOutlineView itemAtRow: [ticketsOutlineView selectedRow]];
} else {
return NULL;
}
}
@end