aboutsummaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2011-09-21 00:38:56 +0000
committerrowanbeentje <rowan@beent.je>2011-09-21 00:38:56 +0000
commit549d28e50a35d432531926a6916755dbe26d91c4 (patch)
treecf5ef7d9440fedb7226ea60e7f2b22d5907d428e /Source
parent6278cbbf9d76d69f44d1d7ddf34792b9e0310272 (diff)
downloadsequelpro-549d28e50a35d432531926a6916755dbe26d91c4.tar.gz
sequelpro-549d28e50a35d432531926a6916755dbe26d91c4.tar.bz2
sequelpro-549d28e50a35d432531926a6916755dbe26d91c4.zip
Rework linebreak handling in content and custom query result views, as triggered by Issue #1184:
- Display table cells on a single line for preview purposes - Display gray pilcrow/reverse pilcrow placeholders instead of linebreaks - If a cell contains linebreaks, automatically trigger sheet editing mode - Handle newly displayed linebreaks in column width detection - If using the up/down arrow keys in a field editor, allow them to select the previous/next line within an editor if appropriat (instead of always moving to the previous/next row)
Diffstat (limited to 'Source')
-rw-r--r--Source/SPCopyTable.m35
-rw-r--r--Source/SPCustomQuery.m54
-rw-r--r--Source/SPDataCellFormatter.m47
-rw-r--r--Source/SPTableContent.m40
-rw-r--r--Source/SPTableStructureDelegate.m2
5 files changed, 141 insertions, 37 deletions
diff --git a/Source/SPCopyTable.m b/Source/SPCopyTable.m
index edf0fcf9..c2cadf59 100644
--- a/Source/SPCopyTable.m
+++ b/Source/SPCopyTable.m
@@ -756,6 +756,7 @@ static const NSInteger kBlobAsImageFile = 4;
NSUInteger cellWidth, maxCellWidth, i;
NSRange linebreakRange;
double rowStep;
+ unichar breakChar;
#ifndef SP_REFACTOR /* patch */
NSFont *tableFont = [NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPGlobalResultTableFont]];
#else
@@ -804,10 +805,26 @@ static const NSInteger kBlobAsImageFile = 4;
contentString = [contentString substringToIndex:500];
}
- // If any linebreaks are present, use only the visible part of the string
- linebreakRange = [contentString rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]];
+ // If any linebreaks are present, they are displayed as single characters; replace them with pilcrow/
+ // reverse pilcrow to match display output width.
+ linebreakRange = [contentString rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] options:NSLiteralSearch];
if (linebreakRange.location != NSNotFound) {
- contentString = [contentString substringToIndex:linebreakRange.location];
+ NSMutableString *singleLineString = [[[NSMutableString alloc] initWithString:contentString] autorelease];
+ while (linebreakRange.location != NSNotFound) {
+ breakChar = [singleLineString characterAtIndex:linebreakRange.location];
+ switch (breakChar) {
+ case '\n':
+ [singleLineString replaceCharactersInRange:linebreakRange withString:@"¶"];
+ break;
+ default:
+ [singleLineString replaceCharactersInRange:linebreakRange withString:@"⁋"];
+ if (breakChar == '\r' && NSMaxRange(linebreakRange) < [singleLineString length] && [singleLineString characterAtIndex:linebreakRange.location+1] == '\n') {
+ [singleLineString deleteCharactersInRange:NSMakeRange(linebreakRange.location+1, 1)];
+ }
+ }
+ linebreakRange = [singleLineString rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] options:NSLiteralSearch];
+ }
+ contentString = singleLineString;
}
}
@@ -1042,6 +1059,11 @@ static const NSInteger kBlobAsImageFile = 4;
if([self isCellComplex])
return NO;
+ // Check whether the editor is multiline - if so, allow the arrow down to change selection if it's not
+ // on the final line
+ if (NSMaxRange([[textView string] lineRangeForRange:[textView selectedRange]]) < [[textView string] length])
+ return NO;
+
NSInteger newRow = row+1;
#ifndef SP_REFACTOR
if (newRow>=[[self delegate] numberOfRowsInTableView:self]) return YES; //check if we're already at the end of the list
@@ -1070,7 +1092,12 @@ static const NSInteger kBlobAsImageFile = 4;
{
// If enum field is edited ARROW key navigates through the popup list
- if([self isCellComplex])
+ if ([self isCellComplex])
+ return NO;
+
+ // Check whether the editor is multiline - if so, allow the arrow up to change selection if it's not
+ // on the first line
+ if ([[textView string] lineRangeForRange:[textView selectedRange]].location > 0)
return NO;
if (row==0) return YES; //already at the beginning of the list
diff --git a/Source/SPCustomQuery.m b/Source/SPCustomQuery.m
index c10d62d3..bb32eb41 100644
--- a/Source/SPCustomQuery.m
+++ b/Source/SPCustomQuery.m
@@ -2398,7 +2398,6 @@
if ( aTableView == customQueryView ) {
NSDictionary *columnDefinition;
- BOOL isBlob = NO;
// Retrieve the column defintion
for(id c in cqColumnDefinition) {
@@ -2409,15 +2408,28 @@
}
// Check if current field is a blob
- if([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"textdata"]
- || [[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"blobdata"])
- isBlob = YES;
- else
- isBlob = NO;
+ BOOL isBlob = ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"textdata"]
+ || [[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"blobdata"]);
- if ([multipleLineEditingButton state] == NSOnState || isBlob) {
+ // Determine whether to open the sheet for editing; do so if the multipleLineEditingButton is enabled,
+ // or if the column was a blob or a text, or if it contains linebreaks.
+ BOOL useFieldEditor = NO;
+#ifndef SP_REFACTOR
+ if ([multipleLineEditingButton state] == NSOnState) useFieldEditor = YES;
+#endif
+ if (!useFieldEditor && ![[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"enum"] && isBlob) useFieldEditor = YES;
+ if (!useFieldEditor) {
+ id cellValue = [resultData cellDataAtRow:rowIndex column:[[aTableColumn identifier] integerValue]];
+ if (![cellValue isNSNull]
+ && [[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"string"]
+ && [cellValue rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] options:NSLiteralSearch].location != NSNotFound)
+ {
+ useFieldEditor = YES;
+ }
+ }
- if(fieldEditor) [fieldEditor release], fieldEditor = nil;
+ if (useFieldEditor) {
+ if (fieldEditor) [fieldEditor release], fieldEditor = nil;
fieldEditor = [[SPFieldEditorController alloc] init];
// Remember edited row for reselecting and setting the scroll view after reload
@@ -3829,7 +3841,7 @@
row = [customQueryView editedRow];
column = [customQueryView editedColumn];
- // Retrieve the column defintion
+ // Retrieve the column definition
NSNumber *colIdentifier = [NSArrayObjectAtIndex([customQueryView tableColumns], column) identifier];
for(id c in cqColumnDefinition) {
if([[c objectForKey:@"datacolumnindex"] isEqualToNumber:colIdentifier]) {
@@ -3869,19 +3881,23 @@
shouldBeginEditing = NO;
}
- BOOL isBlob = NO;
+ NSString *fieldType = [columnDefinition objectForKey:@"typegrouping"];
+ isFieldEditable = shouldBeginEditing;
// Check if current field is a blob
- if([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"textdata"]
- || [[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"blobdata"])
- isBlob = YES;
- else
- isBlob = NO;
-
- isFieldEditable = shouldBeginEditing;
+ BOOL isBlob = ([fieldType isEqualToString:@"textdata"] || [fieldType isEqualToString:@"blobdata"]);
- // Check if current edited field is a blob or should be displayed in field editor sheet
- if (isBlob || [multipleLineEditingButton state] == NSOnState)
+ // Use the field editor sheet instead of inline editing if the target field is a text, blob, or binary
+ // type; if it contains linebreaks; or if the force-editing button is enabled.
+ BOOL useFieldEditor = NO;
+#ifndef SP_REFACTOR
+ if ([multipleLineEditingButton state] == NSOnState) useFieldEditor = YES;
+#endif
+ if (!useFieldEditor && fieldType && ![fieldType isEqualToString:@"enum"] && isBlob) useFieldEditor = YES;
+ if (!useFieldEditor && [[aFieldEditor string] rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location != NSNotFound) useFieldEditor = YES;
+
+ // Open the field editor sheet if required
+ if (useFieldEditor)
{
[customQueryView setFieldEditorSelectedRange:[aFieldEditor selectedRange]];
diff --git a/Source/SPDataCellFormatter.m b/Source/SPDataCellFormatter.m
index 29b22cd7..70660223 100644
--- a/Source/SPDataCellFormatter.m
+++ b/Source/SPDataCellFormatter.m
@@ -34,7 +34,7 @@
- (NSString *)stringForObjectValue:(id)anObject
{
// Truncate the string for speed purposes if it's very long - improves table scrolling speed.
- if ([(NSString *)anObject length] > 150) {
+ if ([anObject isKindOfClass:[NSString class]] && [(NSString *)anObject length] > 150) {
return ([NSString stringWithFormat:@"%@...", [anObject substringToIndex:147]]);
}
@@ -53,9 +53,52 @@
return YES;
}
+/**
+ * When producing an attributed string, take the opportunity to convert to a single
+ * line for display, displaying placeholders for CR and LF characters.
+ */
- (NSAttributedString *)attributedStringForObjectValue:(id)anObject withDefaultAttributes:(NSDictionary *)attributes
{
- return [[[NSAttributedString alloc] initWithString:[self stringForObjectValue:anObject] attributes:attributes] autorelease];
+
+ // Start with a base string which has been shortened for fast display
+ NSString *baseString = [self stringForObjectValue:anObject];
+
+ // Look for any linebreaks within the string
+ NSRange linebreakRange = [baseString rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] options:NSLiteralSearch];
+
+ // If there's no linebreaks, return a non-mutable string
+ if (linebreakRange.location == NSNotFound) {
+ return [[[NSAttributedString alloc] initWithString:baseString attributes:attributes] autorelease];
+ }
+
+ NSMutableAttributedString *mutableString;
+ NSUInteger i, j, stringLength = [baseString length];
+ unichar c;
+
+ // Otherwise, prepare a mutable attributed string to alter, and walk along the string.
+ mutableString = [[[NSMutableAttributedString alloc] initWithString:baseString attributes:attributes] autorelease];
+ for (i = linebreakRange.location, j = i; i < stringLength; i++, j++) {
+ c = [baseString characterAtIndex:i];
+ switch (c) {
+ case '\n':
+ [mutableString replaceCharactersInRange:NSMakeRange(j, 1) withString:@"¶"];
+ [mutableString addAttribute:NSForegroundColorAttributeName value:[NSColor lightGrayColor] range:NSMakeRange(j, 1)];
+ break;
+ case '\r':
+ case 0x0085:
+ case 0x000b:
+ case 0x000c:
+ [mutableString replaceCharactersInRange:NSMakeRange(j, 1) withString:@"⁋"];
+ [mutableString addAttribute:NSForegroundColorAttributeName value:[NSColor lightGrayColor] range:NSMakeRange(j, 1)];
+ if (c == '\r' && i + 1 < stringLength && [baseString characterAtIndex:i+1] == '\n') {
+ [mutableString deleteCharactersInRange:NSMakeRange(j+1, 1)];
+ i++;
+ }
+ break;
+ }
+ }
+
+ return mutableString;
}
- (BOOL)isPartialStringValid:(NSString *)partialString newEditingString:(NSString **)newString errorDescription:(NSString **)error
diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m
index 425bc6b1..4f12f021 100644
--- a/Source/SPTableContent.m
+++ b/Source/SPTableContent.m
@@ -4429,12 +4429,25 @@
}
}
- // Open the sheet if the multipleLineEditingButton is enabled or the column was a blob or a text.
- if ((
+ // Determine whether to open the sheet for editing; do so if the multipleLineEditingButton is enabled,
+ // or if the column was a blob or a text, or if it contains linebreaks.
+ BOOL useFieldEditor = NO;
#ifndef SP_REFACTOR
- [multipleLineEditingButton state] == NSOnState ||
+ if ([multipleLineEditingButton state] == NSOnState) useFieldEditor = YES;
#endif
- isBlob) && ![[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"enum"]) {
+ if (!useFieldEditor && ![[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"enum"] && isBlob) useFieldEditor = YES;
+ if (!useFieldEditor) {
+ id cellValue = [tableValues cellDataAtRow:rowIndex column:[[aTableColumn identifier] integerValue]];
+ if (![cellValue isNSNull]
+ && [[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"string"]
+ && [cellValue rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] options:NSLiteralSearch].location != NSNotFound)
+ {
+ useFieldEditor = YES;
+ }
+ }
+
+ // Open the sheet if required
+ if (useFieldEditor) {
// A table is per definitionem editable
isFieldEditable = YES;
@@ -4769,16 +4782,21 @@
}
- NSString *fieldType;
+ // Use the field editor sheet instead of inline editing if the target field is a text, blob, or binary
+ // type; if it contains linebreaks; or if the force-editing button is enabled.
+ BOOL useFieldEditor = NO;
+ NSString *fieldType = [[tableDataInstance columnWithName:[[NSArrayObjectAtIndex([tableContentView tableColumns], column) headerCell] stringValue]] objectForKey:@"typegrouping"];
- // Check if current edited field is a blob
- if ((fieldType = [[tableDataInstance columnWithName:[[NSArrayObjectAtIndex([tableContentView tableColumns], column) headerCell] stringValue]] objectForKey:@"typegrouping"])
- && ![fieldType isEqualToString:@"enum"] && ([fieldType isEqualToString:@"textdata"] || [fieldType isEqualToString:@"blobdata"]
#ifndef SP_REFACTOR
- || [multipleLineEditingButton state] == NSOnState
+ if ([multipleLineEditingButton state] == NSOnState) useFieldEditor = YES;
#endif
- ))
- {
+
+ if (!useFieldEditor && fieldType && ![fieldType isEqualToString:@"enum"] && ([fieldType isEqualToString:@"textdata"] || [fieldType isEqualToString:@"blobdata"])) useFieldEditor = YES;
+
+ if (!useFieldEditor && [[aFieldEditor string] rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location != NSNotFound) useFieldEditor = YES;
+
+ // Open the field editor sheet if required
+ if (useFieldEditor) {
[tableContentView setFieldEditorSelectedRange:[aFieldEditor selectedRange]];
// Cancel editing
diff --git a/Source/SPTableStructureDelegate.m b/Source/SPTableStructureDelegate.m
index 13a18530..1daac497 100644
--- a/Source/SPTableStructureDelegate.m
+++ b/Source/SPTableStructureDelegate.m
@@ -327,7 +327,7 @@
else if (isTimestampType && ([[[originalRow objectForKey:@"default"] uppercaseString] isEqualToString:@"CURRENT_TIMESTAMP"]) ) {
[queryString appendString:@" DEFAULT CURRENT_TIMESTAMP"];
}
- else if ([[originalRow objectForKey:@"default"] length]) {
+ else if ([(NSString *)[originalRow objectForKey:@"default"] length]) {
[queryString appendFormat:@" DEFAULT '%@'", [mySQLConnection prepareString:[originalRow objectForKey:@"default"]]];
}
}