diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/SPContentFilterManager.m | 324 | ||||
-rw-r--r-- | Source/SPTableContent.m | 13 |
2 files changed, 200 insertions, 137 deletions
diff --git a/Source/SPContentFilterManager.m b/Source/SPContentFilterManager.m index 767065d5..a3f95fa2 100644 --- a/Source/SPContentFilterManager.m +++ b/Source/SPContentFilterManager.m @@ -33,11 +33,10 @@ #define SP_MULTIPLE_SELECTION_PLACEHOLDER_STRING NSLocalizedString(@"[multiple selection]", @"[multiple selection]") #define SP_NO_SELECTION_PLACEHOLDER_STRING NSLocalizedString(@"[no selection]", @"[no selection]") +#define SP_NAME_REQUIRED_PLACEHOLDER_STRING NSLocalizedString(@"[name required]", @"[name required]") @interface SPContentFilterManager (PrivateAPI) -- (void)_initWithNoSelection; - @end @implementation SPContentFilterManager @@ -79,18 +78,8 @@ */ - (void)awakeFromNib { - [contentFilterTextView setAllowsDocumentBackgroundColorChange:YES]; - - NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary]; - - [bindingOptions setObject:NSUnarchiveFromDataTransformerName forKey:@"NSValueTransformerName"]; - - [contentFilterTextView bind:@"backgroundColor" - toObject:[NSUserDefaultsController sharedUserDefaultsController] - withKeyPath:@"values.CustomQueryEditorBackgroundColor" - options:bindingOptions]; - + // Add global group row to contentFilters [contentFilters addObject:[NSDictionary dictionaryWithObjectsAndKeys: @"Global", @"MenuLabel", @"", @"headerOfFileURL", @@ -130,7 +119,12 @@ break; [[self window] makeFirstResponder:contentFilterTableView]; - [self _initWithNoSelection]; + + // Init GUI elements + [contentFilterTableView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; + [[contentFilterNameTextField cell] setPlaceholderString:SP_NO_SELECTION_PLACEHOLDER_STRING]; + [contentFilterNameTextField setStringValue:@""]; + [contentFilterTextView setString:@""]; // Register drag types [contentFilterTableView registerForDraggedTypes:[NSArray arrayWithObject:SPContentFilterPasteboardDragType]]; @@ -153,7 +147,8 @@ /** * Returns the content filters array for fileURL. - * fileURL == nil → global content filters + * + * @param fileURL == The SPDatabaseDocument file URL; if fileURL == nil return the global content filters */ - (NSMutableArray *)contentFilterForFileURL:(NSURL *)fileURL { @@ -293,13 +288,16 @@ } /** - * Insert placeholder - the placeholder string is stored as tooltip + * Insert placeholder - the to be inserted placeholder string is stored in sender's tooltip */ - (IBAction)insertPlaceholder:(id)sender { [contentFilterTextView insertText:[[[sender selectedItem] toolTip] substringToIndex:[[[sender selectedItem] toolTip] rangeOfString:@" – "].location]]; } +/** + * Show save panel sheet for exporting content filters to disk + */ - (IBAction)exportContentFilter:(id)sender { NSSavePanel *panel = [NSSavePanel savePanel]; @@ -314,6 +312,9 @@ [panel beginSheetForDirectory:nil file:nil modalForWindow:[self window] modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:@"exportFilter"]; } +/** + * Show open panel sheet for importing content filters by adding them to current ones + */ - (IBAction)importContentFilterByAdding:(id)sender { NSOpenPanel *panel = [NSOpenPanel openPanel]; @@ -332,6 +333,9 @@ contextInfo:NULL]; } +/** + * Show open panel sheet for importing content filters by replacing the current ones. Not yet implemented + */ - (IBAction)importFavoritesByReplacing:(id)sender { @@ -343,13 +347,6 @@ - (IBAction)closeContentFilterManagerSheet:(id)sender { - // First check for ESC if pressed while inline editing - if(![sender tag] && isTableCellEditing) { - [contentFilterTableView abortEditing]; - isTableCellEditing = NO; - return; - } - [NSApp endSheet:[self window] returnCode:0]; [[self window] orderOut:self]; @@ -381,6 +378,15 @@ } +/** + * It triggers an update of contentFilterTextView and + * resultingClauseContentLabel by inserting @"" into contentFilterTextView + */ +- (IBAction)suppressLeadingFiledPlaceholderWasChanged:(id)sender +{ + [contentFilterTextView insertText:@""]; +} + #pragma mark - #pragma mark SplitView delegate methods @@ -401,7 +407,21 @@ } #pragma mark - -#pragma mark TableView datasource methods +#pragma mark TableView delegate methods + +/** + * Update contentFilterNameTextField if selection of contentFilterTableView was changed. + */ +- (void)tableViewSelectionDidChange:(NSNotification *)aNotification +{ + if([contentFilterTableView selectedRow] > -1) { + NSString *newName = [[contentFilters objectAtIndex:[contentFilterTableView selectedRow]] objectForKey:@"MenuLabel"]; + if(newName) + [contentFilterNameTextField setStringValue:newName]; + else + [contentFilterNameTextField setStringValue:@""]; + } +} /** * Returns the number of all content filters. @@ -494,7 +514,7 @@ } /** - * Sorting by clicking at a column header inside groups + * Sorting by clicking at a column header inside groups. Not yet implemented */ - (void)tableView:(NSTableView*)tableView didClickTableColumn:(NSTableColumn *)tableColumn { @@ -503,111 +523,12 @@ } /** - * contentFilters holds the data if a table row is a group header or not + * If current row's contentFilters object has a key "headerOfFileURL" then row is grouped ie it's an header */ - (BOOL)tableView:(NSTableView *)aTableView isGroupRow:(NSInteger)rowIndex { return ([[contentFilters objectAtIndex:rowIndex] objectForKey:@"headerOfFileURL"]) ? YES : NO; } -/** - * Detect if inline editing was done - then ESC to close the sheet will be activate - */ -- (void)controlTextDidEndEditing:(NSNotification *)aNotification -{ - isTableCellEditing = NO; -} - -/** - * Changes in the name text field will be saved in data source directly - * to update the table view accordingly - */ -- (void)controlTextDidChange:(NSNotification *)notification -{ - - // Do nothing if no filter is selected - if([contentFilterTableView numberOfSelectedRows] < 1) return; - - id object = [notification object]; - - if(object == contentFilterNameTextField) { - [[contentFilters objectAtIndex:[contentFilterTableView selectedRow]] setObject:[contentFilterNameTextField stringValue] forKey:@"MenuLabel"]; - [contentFilterTableView reloadData]; - } - -} - -- (IBAction)suppressLeadingFiledPlaceholderWasChanged:(id)sender -{ - [contentFilterTextView insertText:@""]; -} - -/** - * Parse clause and update labels accordingly - */ -- (void)textViewDidChangeSelection:(NSNotification *)notification -{ - // Do nothing if no filter is selected - if([contentFilterTableView numberOfSelectedRows] < 1) return; - - id object = [notification object]; - - if(object == contentFilterTextView) { - [insertPlaceholderButton setEnabled:([[contentFilterTextView string] length])]; - [resultingClauseLabel setHidden:(![[contentFilterTextView string] length])]; - [resultingClauseContentLabel setHidden:(![[contentFilterTextView string] length])]; - [numberOfArgsLabel setHidden:(![[contentFilterTextView string] length])]; - - NSUInteger numOfArgs = [[[contentFilterTextView string] componentsMatchedByRegex:@"(?<!\\\\)(\\$\\{.*?\\})"] count]; - [numberOfArgsLabel setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Number of arguments: %lu", @"Number of arguments: %lu"), (unsigned long)numOfArgs]]; - - [contentFilterConjunctionTextField setHidden:(numOfArgs < 2)]; - [contentFilterConjunctionLabel setHidden:(numOfArgs < 2)]; - - if(numOfArgs > 2) { - [resultingClauseLabel setStringValue:NSLocalizedString(@"Error", @"error")]; - [resultingClauseContentLabel setStringValue:NSLocalizedString(@"Maximum number of arguments is 2!", @"Maximum number of arguments is 2!")]; - } else { - [resultingClauseLabel setStringValue:@"SELECT * FROM <table> WHERE"]; - NSMutableString *c = [[NSMutableString alloc] init]; - [c setString:[contentFilterTextView string]]; - [c replaceOccurrencesOfRegex:@"(?<!\\\\)\\$BINARY" withString:@"[BINARY]"]; - [c flushCachedRegexData]; - [c replaceOccurrencesOfRegex:@"(?<!\\\\)(\\$\\{.*?\\})" withString:@"[arg]"]; - [c flushCachedRegexData]; - [c replaceOccurrencesOfRegex:@"(?<!\\\\)\\$CURRENT_FIELD" withString:@"<field>"]; - [c flushCachedRegexData]; - [resultingClauseContentLabel setStringValue:[NSString stringWithFormat:@"%@%@", ([suppressLeadingFiledPlaceholderCheckbox state] == NSOnState) ? @"" : @"<field> ", c]]; - [c release]; - } - - } -} -#pragma mark - -#pragma mark Menu validation - -/** - * Menu item validation. - */ -- (BOOL)validateMenuItem:(NSMenuItem *)menuItem -{ - - // Disable all if only GLOBAL is in the table - if([contentFilters count] < 2) return NO; - - SEL action = [menuItem action]; - - if ( (action == @selector(duplicateContentFilter:))) - { - return ([contentFilterTableView numberOfSelectedRows] == 1); - } - else if ( (action == @selector(removeContentFilter:)) || - ( action == @selector(exportFavorites:))) - { - return ([contentFilterTableView numberOfSelectedRows] > 0); - } - - return YES; -} #pragma mark - #pragma mark TableView drag & drop delegate methods @@ -659,7 +580,6 @@ /** * Return whether or not to accept the drop of the supplied rows. */ - - (BOOL)tableView:(NSTableView *)tableView acceptDrop:(id <NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation { @@ -715,10 +635,152 @@ } #pragma mark - +#pragma mark Various Control delegate methods + +/** + * Detect if inline editing was done + */ +- (void)controlTextDidEndEditing:(NSNotification *)aNotification +{ + isTableCellEditing = NO; +} + +/** + * Trap the escape overriding default behaviour and ending editing, + * only within the current row. + */ +- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command +{ + // Trap the escape key + if ( [[control window] methodForSelector:command] == [[control window] methodForSelector:@selector(cancelOperation:)] ) + { + + // Abort editing + [control abortEditing]; + isTableCellEditing = NO; + // Reset name input text field + if([contentFilterTableView selectedRow] > -1) + [contentFilterNameTextField setStringValue: + [[contentFilters objectAtIndex:[contentFilterTableView selectedRow]] objectForKey:@"MenuLabel"]]; + + return TRUE; + } + + return FALSE; +} + +/** + * Changes in the name text field will be saved in data source directly + * to update the table view accordingly. If filter name is changed via inline editing + * in the tableView update name text field accordingly and check for empty names + */ +- (void)controlTextDidChange:(NSNotification *)notification +{ + + // Do nothing if no filter is selected + if([contentFilterTableView numberOfSelectedRows] < 1) return; + + id object = [notification object]; + + if(object == contentFilterNameTextField) { + if([[contentFilterNameTextField stringValue] length]) { + [[contentFilters objectAtIndex:[contentFilterTableView selectedRow]] setObject:[contentFilterNameTextField stringValue] forKey:@"MenuLabel"]; + [contentFilterTableView reloadData]; + } else { + NSBeep(); + [[contentFilters objectAtIndex:[contentFilterTableView selectedRow]] setObject:SP_NAME_REQUIRED_PLACEHOLDER_STRING forKey:@"MenuLabel"]; + [contentFilterNameTextField setStringValue:SP_NAME_REQUIRED_PLACEHOLDER_STRING]; + [contentFilterNameTextField selectText:nil]; + } + } + else if (object == contentFilterTableView) { + NSTextView *editor = [[notification userInfo] objectForKey:@"NSFieldEditor"]; + NSString *newName = [[editor textStorage] string]; + if([newName length]) { + [contentFilterNameTextField setStringValue:newName]; + } else { + NSBeep(); + [editor insertText:SP_NAME_REQUIRED_PLACEHOLDER_STRING]; + [editor setSelectedRange:NSMakeRange(0,[SP_NAME_REQUIRED_PLACEHOLDER_STRING length])]; + [contentFilterNameTextField setStringValue:SP_NAME_REQUIRED_PLACEHOLDER_STRING]; + } + } + +} + +/** + * Parse clause and update labels accordingly + */ +- (void)textViewDidChangeSelection:(NSNotification *)notification +{ + // Do nothing if no filter is selected + if([contentFilterTableView numberOfSelectedRows] < 1) return; + + id object = [notification object]; + + if(object == contentFilterTextView) { + [insertPlaceholderButton setEnabled:([[contentFilterTextView string] length])]; + [resultingClauseLabel setHidden:(![[contentFilterTextView string] length])]; + [resultingClauseContentLabel setHidden:(![[contentFilterTextView string] length])]; + [numberOfArgsLabel setHidden:(![[contentFilterTextView string] length])]; + + NSUInteger numOfArgs = [[[contentFilterTextView string] componentsMatchedByRegex:@"(?<!\\\\)(\\$\\{.*?\\})"] count]; + [numberOfArgsLabel setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Number of arguments: %lu", @"Number of arguments: %lu"), (unsigned long)numOfArgs]]; + + [contentFilterConjunctionTextField setHidden:(numOfArgs < 2)]; + [contentFilterConjunctionLabel setHidden:(numOfArgs < 2)]; + + if(numOfArgs > 2) { + [resultingClauseLabel setStringValue:NSLocalizedString(@"Error", @"error")]; + [resultingClauseContentLabel setStringValue:NSLocalizedString(@"Maximum number of arguments is 2!", @"Maximum number of arguments is 2!")]; + } else { + [resultingClauseLabel setStringValue:@"SELECT * FROM <table> WHERE"]; + NSMutableString *c = [[NSMutableString alloc] init]; + [c setString:[contentFilterTextView string]]; + [c replaceOccurrencesOfRegex:@"(?<!\\\\)\\$BINARY" withString:@"[BINARY]"]; + [c flushCachedRegexData]; + [c replaceOccurrencesOfRegex:@"(?<!\\\\)(\\$\\{.*?\\})" withString:@"[arg]"]; + [c flushCachedRegexData]; + [c replaceOccurrencesOfRegex:@"(?<!\\\\)\\$CURRENT_FIELD" withString:@"<field>"]; + [c flushCachedRegexData]; + [resultingClauseContentLabel setStringValue:[NSString stringWithFormat:@"%@%@", ([suppressLeadingFiledPlaceholderCheckbox state] == NSOnState) ? @"" : @"<field> ", c]]; + [c release]; + } + + } +} +#pragma mark - +#pragma mark Menu validation + +/** + * Menu item validation. + */ +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem +{ + + // Disable all if only GLOBAL is in the table + if([contentFilters count] < 2) return NO; + + SEL action = [menuItem action]; + + if ( (action == @selector(duplicateContentFilter:))) + { + return ([contentFilterTableView numberOfSelectedRows] == 1); + } + else if ( (action == @selector(removeContentFilter:)) || + ( action == @selector(exportFavorites:))) + { + return ([contentFilterTableView numberOfSelectedRows] > 0); + } + + return YES; +} + +#pragma mark - #pragma mark Other /** - * Sheet did end method + * Sheet did end method for removing content filters */ - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo { @@ -873,12 +935,4 @@ } } -- (void)_initWithNoSelection -{ - [contentFilterTableView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; - [[contentFilterNameTextField cell] setPlaceholderString:SP_NO_SELECTION_PLACEHOLDER_STRING]; - [contentFilterNameTextField setStringValue:@""]; - [contentFilterTextView setString:@""]; -} - @end diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m index 122822a3..e79b2c36 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -698,8 +698,17 @@ // Notify listenters that the query has finished [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; - // Trigger a full reload if required - if (fullTableReloadRequired) [self reloadTable:self]; + if ([mySQLConnection queryErrored] && ![mySQLConnection queryCancelled]) { + if(filterString) + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"The table data couldn't be loaded presumably due to used filter clause. \n\nMySQL said: %@", @"message of panel when loading of table failed and presumably due to used filter argument"), [mySQLConnection getLastErrorMessage]]); + else + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"The table data couldn't be loaded.\n\nMySQL said: %@", @"message of panel when loading of table failed"), [mySQLConnection getLastErrorMessage]]); + } else { + // Trigger a full reload if required + if (fullTableReloadRequired) [self reloadTable:self]; + } } /** |