diff options
Diffstat (limited to 'Source/SPConnectionControllerDelegate.m')
-rw-r--r-- | Source/SPConnectionControllerDelegate.m | 220 |
1 files changed, 136 insertions, 84 deletions
diff --git a/Source/SPConnectionControllerDelegate.m b/Source/SPConnectionControllerDelegate.m index 30b602cd..e18c43ab 100644 --- a/Source/SPConnectionControllerDelegate.m +++ b/Source/SPConnectionControllerDelegate.m @@ -43,19 +43,27 @@ #endif static NSString *SPDatabaseImage = @"database-small"; +static NSString *SPQuickConnectImage = @"quick-connect-icon.pdf"; +static NSString *SPQuickConnectImageWhite = @"quick-connect-icon-white.pdf"; @interface SPConnectionController () +// Privately redeclare as read/write to get the synthesized setter +@property (readwrite, assign) BOOL isEditingConnection; + - (void)_checkHost; - (void)_sortFavorites; - (void)_favoriteTypeDidChange; - (void)_reloadFavoritesViewData; -- (void)_updateFavoritePasswordsFromField:(NSControl *)control; - (NSString *)_stripInvalidCharactersFromString:(NSString *)subject; +- (void)_startEditingConnection; +- (void)_stopEditingConnection; - (void)_setNodeIsExpanded:(BOOL)expanded fromNotification:(NSNotification *)notification; +- (NSString *)_generateNameForConnection; + @end @implementation SPConnectionController (SPConnectionControllerDelegate) @@ -88,25 +96,27 @@ static NSString *SPDatabaseImage = @"database-small"; return ([[(SPTreeNode *)item parentNode] parentNode] == nil); } +- (void)outlineViewSelectionIsChanging:(NSNotification *)notification +{ + if (isEditingConnection) { + [self _stopEditingConnection]; + [[notification object] setNeedsDisplay:YES]; + } +} + - (void)outlineViewSelectionDidChange:(NSNotification *)notification -{ +{ NSInteger selected = [favoritesOutlineView numberOfSelectedRows]; - - if (selected == 1) { - SPTreeNode *node = [self selectedFavoriteNode]; - - [self updateFavoriteSelection:self]; + if (isEditingConnection) { + [self _stopEditingConnection]; + [[notification object] setNeedsDisplay:YES]; + } - if (![node isGroup]) { - [addToFavoritesButton setEnabled:NO]; + if (selected == 1) { + [self updateFavoriteSelection:self]; - favoriteNameFieldWasTouched = YES; - } - else { - [addToFavoritesButton setEnabled:YES]; - } - + favoriteNameFieldWasAutogenerated = NO; [connectionResizeContainer setHidden:NO]; [connectionInstructionsTextField setStringValue:NSLocalizedString(@"Enter connection details below, or choose a favorite", @"enter connection details label")]; } @@ -116,17 +126,58 @@ static NSString *SPDatabaseImage = @"database-small"; } } +- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + if (item == quickConnectItem) { + return (NSCell *)quickConnectCell; + } + + return [tableColumn dataCellForRow:[outlineView rowForItem:item]]; +} + - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item { SPTreeNode *node = (SPTreeNode *)item; - + + // Draw entries with the small system font by default [(SPTableTextFieldCell *)cell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; - [(SPTableTextFieldCell *)cell setImage:(![[node parentNode] parentNode]) ? nil : (![node isGroup]) ? [NSImage imageNamed:SPDatabaseImage] : folderImage]; + + // Set an image as appropriate; the quick connect image for that entry, no image for other + // top-level items, the folder image for group nodes, or the database image for other nodes. + if (![[node parentNode] parentNode]) { + if (node == quickConnectItem) { + if ([outlineView rowForItem:item] == [outlineView selectedRow]) { + [(SPTableTextFieldCell *)cell setImage:[NSImage imageNamed:SPQuickConnectImageWhite]]; + } else { + [(SPTableTextFieldCell *)cell setImage:[NSImage imageNamed:SPQuickConnectImage]]; + } + } else { + [(SPTableTextFieldCell *)cell setImage:nil]; + } + } else { + if ([node isGroup]) { + [(SPTableTextFieldCell *)cell setImage:folderImage]; + } else { + [(SPTableTextFieldCell *)cell setImage:[NSImage imageNamed:SPDatabaseImage]]; + } + } + + // If a favourite item is being edited, draw the text in bold to show state + if (isEditingConnection && ![node isGroup] && [outlineView rowForItem:item] == [outlineView selectedRow]) { + NSMutableAttributedString *editedCellString = [[cell attributedStringValue] mutableCopy]; + [editedCellString addAttribute:NSForegroundColorAttributeName value:[NSColor colorWithDeviceWhite:0.25f alpha:1.f] range:NSMakeRange(0, [editedCellString length])]; + [cell setAttributedStringValue:editedCellString]; + [editedCellString release]; + } } - (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item { - return ([[item parentNode] parentNode]) ? 17 : 22; + if (item == quickConnectItem) { + return 24.f; + } + + return ([[item parentNode] parentNode]) ? 17.f : 22.f; } - (NSString *)outlineView:(NSOutlineView *)outlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tableColumn item:(id)item mouseLocation:(NSPoint)mouseLocation @@ -170,8 +221,18 @@ static NSString *SPDatabaseImage = @"database-small"; } - (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item -{ - return ([[item parentNode] parentNode] != nil); +{ + + // If this is a top level item, only allow the "Quick Connect" item to be selectable + if (![[item parentNode] parentNode]) { + if (item == quickConnectItem) { + return YES; + } + return NO; + } + + // Otherwise allow all items to be selectable + return YES; } - (BOOL)outlineView:(NSOutlineView *)outlineView shouldShowOutlineCellForItem:(id)item @@ -184,6 +245,11 @@ static NSString *SPDatabaseImage = @"database-small"; return ([[item parentNode] parentNode] != nil); } +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + return (item != quickConnectItem); +} + - (void)outlineViewItemDidCollapse:(NSNotification *)notification { [self _setNodeIsExpanded:NO fromNotification:notification]; @@ -210,12 +276,12 @@ static NSString *SPDatabaseImage = @"database-small"; } // If the user is in the process of changing a node's name, trigger a save and prevent dragging. - if (isEditing) { + if (isEditingItemName) { [favoritesController saveFavorites]; [self _reloadFavoritesViewData]; - isEditing = NO; + isEditingItemName = NO; return NO; } @@ -350,77 +416,60 @@ static NSString *SPDatabaseImage = @"database-small"; #ifndef SP_REFACTOR /** - * Trap and control the 'name' field of the selected favorite. If the user pressed - * 'Add Favorite' the 'name' field is set to 'New Favorite'. If the user did not - * change the 'name' field or delete that field it will be set to user@host automatically. + * React to control text changes in the connection interface */ - (void)controlTextDidChange:(NSNotification *)notification { id field = [notification object]; - + + // If a 'name' field was edited, and is now of zero length, trigger a replacement + // with a standard suggestion if (((field == standardNameField) || (field == socketNameField) || (field == sshNameField)) && [self selectedFavoriteNode]) { - - favoriteNameFieldWasTouched = YES; - - NSString *favoriteName = [self _stripInvalidCharactersFromString:[field stringValue]]; - - BOOL nameFieldIsEmpty = [favoriteName length] == 0; - - switch (previousType) - { - case SPTCPIPConnection: - if (nameFieldIsEmpty || (!favoriteNameFieldWasTouched && (field == standardUserField || field == standardSQLHostField))) { - [standardNameField setStringValue:[NSString stringWithFormat:@"%@@%@", [standardUserField stringValue], [standardSQLHostField stringValue]]]; - } - - break; - case SPSocketConnection: - if (nameFieldIsEmpty || (!favoriteNameFieldWasTouched && field == socketUserField)) { - [socketNameField setStringValue:[NSString stringWithFormat:@"%@@localhost", [socketUserField stringValue]]]; - } - - break; - case SPSSHTunnelConnection: - if (nameFieldIsEmpty || (!favoriteNameFieldWasTouched && (field == sshUserField || field == sshSQLHostField))) { - [sshNameField setStringValue:[NSString stringWithFormat:@"%@@%@", [sshUserField stringValue], [sshSQLHostField stringValue]]]; - } - - break; + if (![[self _stripInvalidCharactersFromString:[field stringValue]] length]) { + [self controlTextDidEndEditing:notification]; } - - // Trigger KVO update - [self setName:favoriteName]; - - // If name field is empty enable user@host update - if (nameFieldIsEmpty) favoriteNameFieldWasTouched = NO; + } + + [self _startEditingConnection]; + + if (favoriteNameFieldWasAutogenerated) { + [self setName:[self _generateNameForConnection]]; } } /** - * When a host field finishes editing, ensure that it hasn't been set to "localhost" - * to ensure that socket connections don't inadvertently occur. + * React to the end of control text changes in the connection interface. */ - (void)controlTextDidEndEditing:(NSNotification *)notification { - if ([notification object] == standardSQLHostField || [notification object] == sshSQLHostField) { - [self _checkHost]; + id field = [notification object]; + + // Handle updates to the 'name' field of the selected favourite. The favourite name should + // have leading or trailing spaces removed at the end of editing, and if it's left empty, + // should have a default name set. + if (((field == standardNameField) || (field == socketNameField) || (field == sshNameField)) && [self selectedFavoriteNode]) { + + NSString *favoriteName = [self _stripInvalidCharactersFromString:[field stringValue]]; + + if (![favoriteName length]) { + favoriteName = [self _generateNameForConnection]; + if (favoriteName) { + [self setName:favoriteName]; + } + + // Enable user@host update in reaction to other UI changes + favoriteNameFieldWasAutogenerated = YES; + } else if (![[field stringValue] isEqualToString:[self name]]) { + favoriteNameFieldWasAutogenerated = NO; + [self setName:favoriteName]; + } } -} -/** - * Trap editing end notifications and use them to update the keychain password - * appropriately when name, host, user, password or database changes. - */ -- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor -{ - // Request a password refresh to keep keychain references in sync with favorites, but only if a favorite - // is selected, meaning we're editing an existing one, not a new one. - if (((id)control != (id)favoritesOutlineView) && ([self selectedFavoriteNode])) { - [self _updateFavoritePasswordsFromField:control]; + // When a host field finishes editing, ensure that it hasn't been set to "localhost" to + // ensure that socket connections don't inadvertently occur. + if (field == standardSQLHostField || field == sshSQLHostField) { + [self _checkHost]; } - - // Proceed with editing - return YES; } #endif @@ -442,19 +491,18 @@ static NSString *SPDatabaseImage = @"database-small"; NSInteger selectedTabView = [tabView indexOfTabViewItem:tabViewItem]; if (selectedTabView == previousType) return; - + [self resizeTabViewToConnectionType:selectedTabView animating:YES]; // Update the host as appropriate if ((selectedTabView != SPSocketConnection) && [[self host] isEqualToString:@"localhost"]) { [self setHost:@""]; } - + previousType = selectedTabView; - - // Enable the add to favorites button - [addToFavoritesButton setEnabled:YES]; - + + [self _startEditingConnection]; + [self _favoriteTypeDidChange]; } @@ -510,7 +558,11 @@ static NSString *SPDatabaseImage = @"database-small"; SPTreeNode *node = [self selectedFavoriteNode]; NSInteger selectedRows = [favoritesOutlineView numberOfSelectedRows]; - + + if (node == quickConnectItem) { + return NO; + } + if ((action == @selector(sortFavorites:)) || (action == @selector(reverseSortFavorites:))) { if ([[favoritesRoot allChildLeafs] count] < 2) return NO; |