aboutsummaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorMax <post@wickenrode.com>2015-11-10 16:13:43 +0100
committerMax <post@wickenrode.com>2015-11-10 16:13:43 +0100
commit2059dfa31eb3d9b629a9734c316984bd8afb3915 (patch)
treebd92065eb9156c572558cf3c6756e179abe048c4 /Source
parentfdef91b45a56b2f94aa477041b8f4185a2e7e7e2 (diff)
downloadsequelpro-2059dfa31eb3d9b629a9734c316984bd8afb3915.tar.gz
sequelpro-2059dfa31eb3d9b629a9734c316984bd8afb3915.tar.bz2
sequelpro-2059dfa31eb3d9b629a9734c316984bd8afb3915.zip
Add a "tooltip" when picking a column type in structure view, explaining basic properties of the type (part of #1090)
This is mostly to help users understand what "Length" actually does for *INT types.
Diffstat (limited to 'Source')
-rw-r--r--Source/SPCategoryAdditions.h1
-rw-r--r--Source/SPComboBoxCell.h73
-rw-r--r--Source/SPComboBoxCell.m137
-rw-r--r--Source/SPScreenAdditions.h44
-rw-r--r--Source/SPScreenAdditions.m54
-rw-r--r--Source/SPTableStructure.h19
-rw-r--r--Source/SPTableStructure.m328
-rw-r--r--Source/SPTableStructureDelegate.m102
-rw-r--r--Source/SPTooltip.m11
9 files changed, 758 insertions, 11 deletions
diff --git a/Source/SPCategoryAdditions.h b/Source/SPCategoryAdditions.h
index 58c40d80..991cb60c 100644
--- a/Source/SPCategoryAdditions.h
+++ b/Source/SPCategoryAdditions.h
@@ -48,6 +48,7 @@
#import "SPColorAdditions.h"
#import "SPFileManagerAdditions.h"
#import "SPDateAdditions.h"
+#import "SPScreenAdditions.h"
#import "NSNotificationCenterThreadingAdditions.h"
#import "NSMutableArray-MultipleSort.h"
diff --git a/Source/SPComboBoxCell.h b/Source/SPComboBoxCell.h
new file mode 100644
index 00000000..39c8f505
--- /dev/null
+++ b/Source/SPComboBoxCell.h
@@ -0,0 +1,73 @@
+//
+// SPComboBoxCell.h
+// sequel-pro
+//
+// Created by Max Lohrmann on 08.11.15.
+// Copyright (c) 2015 Max Lohrmann. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+#import <Cocoa/Cocoa.h>
+
+@class SPComboBoxCell;
+
+@protocol SPComboBoxCellDelegate <NSObject>
+
+@optional
+- (void)comboBoxCell:(SPComboBoxCell *)cell willPopUpWindow:(NSWindow *)win;
+- (void)comboBoxCell:(SPComboBoxCell *)cell willDismissWindow:(NSWindow *)win;
+- (void)comboBoxCellSelectionDidChange:(SPComboBoxCell *)cell;
+
+@end
+
+/**
+ * See this class as a kind of "rapid prototype".
+ * It heavily relies on the inner workings of the NSComboBoxCell to implement
+ * the additional features we want (namely the option to show a tooltip
+ * window beside the popup list).
+ *
+ * App Store-wise it would probably we better to implement this from scratch,
+ * but NSComboBoxCell uses some quite difficult logic inside -filterEvents:
+ * which is the core method of the selection process.
+ *
+ * We could somewhat work around the private interface by relying on notification
+ * NSComboBoxCellWillPopUpNotification
+ * and overriding -[NSWindow addChildWindow:] / -[NSWindow removeChildWindow:]
+ * but NSTableView foils that as it will copy the cell right before the notification
+ * is sent, so we don't know what object to observe.
+ */
+@interface SPComboBoxCell : NSComboBoxCell {
+ id<SPComboBoxCellDelegate> spDelegate;
+}
+
+@property (assign) IBOutlet id<SPComboBoxCellDelegate> spDelegate; // NSComboBoxCell already has a delegate property
+
+/**
+ * The popUp window that contains the item list.
+ * Will return nil if the implementation changes and the underlying ivar
+ * is removed or no longer a NSWindow.
+ */
+- (NSWindow *)spPopUpWindow;
+
+@end
diff --git a/Source/SPComboBoxCell.m b/Source/SPComboBoxCell.m
new file mode 100644
index 00000000..15ddc1db
--- /dev/null
+++ b/Source/SPComboBoxCell.m
@@ -0,0 +1,137 @@
+//
+// SPComboBoxCell.m
+// sequel-pro
+//
+// Created by Max Lohrmann on 08.11.15.
+// Copyright (c) 2015 Max Lohrmann. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+#import "SPComboBoxCell.h"
+#import <objc/runtime.h>
+
+static NSString *_CellSelectionDidChangeNotification = @"NSComboBoxCellSelectionDidChangeNotification";
+static NSString *_CellWillPopUpNotification = @"NSComboBoxCellWillPopUpNotification";
+static NSString *_CellWillDismissNotification = @"NSComboBoxCellWillDismissNotification";
+
+@interface NSComboBoxCell (Apple_Private)
+- (IBAction)popUp:(id)sender;
+@end
+
+@interface SPComboBoxCell ()
+
+- (void)sp_selectionDidChange:(NSNotification *)notification;
+- (void)sp_cellWillPopUp:(NSNotification *)notification;
+- (void)sp_cellWillDismiss:(NSNotification *)notification;
+@end
+
+@implementation SPComboBoxCell
+
+@synthesize spDelegate;
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ SPComboBoxCell *cpy = [super copyWithZone:zone];
+
+ cpy->spDelegate = [self spDelegate];
+
+ return cpy;
+}
+
+- (void)dealloc
+{
+ [self setSpDelegate:nil];
+ [super dealloc];
+}
+
+- (NSWindow *)spPopUpWindow
+{
+ NSWindow *popUp;
+ Ivar popUpVar = object_getInstanceVariable(self,"_popUp",(void **)&popUp);
+ if(popUpVar) {
+ const char *typeEnc = ivar_getTypeEncoding(popUpVar);
+ if(typeEnc[0] == '@' && [popUp isKindOfClass:[NSWindow class]]) { // it is an object and of class NSWindow
+ return popUp;
+ }
+ }
+ return nil;
+}
+
+- (void)popUp:(id)sender
+{
+ // this notification will be sent after the popup window is resized and moved to the correct position
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(sp_cellWillPopUp:)
+ name:_CellWillPopUpNotification
+ object:self];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(sp_cellWillDismiss:)
+ name:_CellWillDismissNotification
+ object:self];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(sp_selectionDidChange:)
+ name:_CellSelectionDidChangeNotification
+ object:self];
+
+ [super popUp:sender]; // this method won't return until the window is closed again
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:_CellSelectionDidChangeNotification
+ object:self];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:_CellWillPopUpNotification
+ object:self];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:_CellWillDismissNotification
+ object:self];
+}
+
+- (void)sp_selectionDidChange:(NSNotification *)notification
+{
+ if([[self spDelegate] respondsToSelector:@selector(comboBoxCellSelectionDidChange:)]) {
+ [[self spDelegate] comboBoxCellSelectionDidChange:self];
+ }
+}
+
+- (void)sp_cellWillPopUp:(NSNotification *)notification
+{
+ NSWindow *popUp = [self spPopUpWindow];
+ if(popUp && [[self spDelegate] respondsToSelector:@selector(comboBoxCell:willPopUpWindow:)]) {
+ [[self spDelegate] comboBoxCell:self willPopUpWindow:popUp];
+ }
+}
+
+- (void)sp_cellWillDismiss:(NSNotification *)notification
+{
+ NSWindow *popUp = [self spPopUpWindow];
+ if(popUp && [[self spDelegate] respondsToSelector:@selector(comboBoxCell:willDismissWindow:)]) {
+ [[self spDelegate] comboBoxCell:self willDismissWindow:popUp];
+ }
+}
+
+@end
diff --git a/Source/SPScreenAdditions.h b/Source/SPScreenAdditions.h
new file mode 100644
index 00000000..7720ce78
--- /dev/null
+++ b/Source/SPScreenAdditions.h
@@ -0,0 +1,44 @@
+//
+// SPScreenAdditions.h
+// sequel-pro
+//
+// Created by Max Lohrmann on 09.11.15.
+// Copyright (c) 2015 Max Lohrmann. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+#import <Cocoa/Cocoa.h>
+
+@interface NSScreen (SPScreenAdditions)
+
++ (NSScreen *)screenAtPoint:(NSPoint)p;
+
+/**
+ * Use this method instead of [[NSScreen screenAtPoint:p] frame] as the
+ * return value of [nil frame] is not neccesarily reliable.
+ * This method will return NSZeroRect if no screen is found.
+ */
++ (NSRect)rectOfScreenAtPoint:(NSPoint)p;
+
+@end
diff --git a/Source/SPScreenAdditions.m b/Source/SPScreenAdditions.m
new file mode 100644
index 00000000..edac0d68
--- /dev/null
+++ b/Source/SPScreenAdditions.m
@@ -0,0 +1,54 @@
+//
+// SPScreenAdditions.m
+// sequel-pro
+//
+// Created by Max Lohrmann on 09.11.15.
+// Copyright (c) 2015 Max Lohrmann. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+#import "SPScreenAdditions.h"
+
+@implementation NSScreen (SPScreenAdditions)
+
++ (NSScreen *)screenAtPoint:(NSPoint)p
+{
+ for(NSScreen* candidate in [NSScreen screens])
+ {
+ NSRect cf = [candidate frame];
+ if(NSPointInRect(p,cf)) {
+ return candidate;
+ }
+ }
+ return nil;
+}
+
++ (NSRect)rectOfScreenAtPoint:(NSPoint)p
+{
+ NSScreen *s = [NSScreen screenAtPoint:p];
+
+ return (s? [s frame] : NSZeroRect);
+}
+
+@end
diff --git a/Source/SPTableStructure.h b/Source/SPTableStructure.h
index 781717f8..c8b0ec0a 100644
--- a/Source/SPTableStructure.h
+++ b/Source/SPTableStructure.h
@@ -41,6 +41,18 @@
@class SPExtendedTableInfo;
@class SPTableInfo;
+@interface SPFieldTypeHelp : NSObject {
+ NSString *typeName;
+ NSString *typeDefinition;
+ NSString *typeRange;
+ NSString *typeDescription;
+}
+@property(readonly) NSString *typeName;
+@property(readonly) NSString *typeDefinition;
+@property(readonly) NSString *typeRange;
+@property(readonly) NSString *typeDescription;
+@end
+
@interface SPTableStructure : NSObject
#ifdef SP_CODA
<NSTableViewDelegate, NSTableViewDataSource, NSComboBoxCellDataSource>
@@ -55,7 +67,10 @@
#endif
IBOutlet SPIndexesController *indexesController;
IBOutlet SPDatabaseData *databaseDataInstance;
-
+
+ IBOutlet NSPanel *structureHelpPanel;
+ IBOutlet NSTextView *structureHelpText;
+
#ifndef SP_CODA /* ivars */
IBOutlet id keySheet;
IBOutlet id resetAutoIncrementSheet;
@@ -158,4 +173,6 @@
// Split view interaction
- (IBAction)unhideIndexesView:(id)sender;
++ (SPFieldTypeHelp *)helpForFieldType:(NSString *)typeName;
+
@end
diff --git a/Source/SPTableStructure.m b/Source/SPTableStructure.m
index d242ae88..81d0c531 100644
--- a/Source/SPTableStructure.m
+++ b/Source/SPTableStructure.m
@@ -53,6 +53,42 @@
static NSString *SPRemoveField = @"SPRemoveField";
static NSString *SPRemoveFieldAndForeignKey = @"SPRemoveFieldAndForeignKey";
+@interface SPFieldTypeHelp ()
+@property(copy,readwrite) NSString *typeName;
+@property(copy,readwrite) NSString *typeDefinition;
+@property(copy,readwrite) NSString *typeRange;
+@property(copy,readwrite) NSString *typeDescription;
+@end
+
+@implementation SPFieldTypeHelp
+
+@synthesize typeName;
+@synthesize typeDefinition;
+@synthesize typeRange;
+@synthesize typeDescription;
+
+- (void)dealloc
+{
+ [self setTypeName:nil];
+ [self setTypeDefinition:nil];
+ [self setTypeRange:nil];
+ [self setTypeDescription:nil];
+ [super dealloc];
+}
+
+@end
+
+static inline SPFieldTypeHelp *MakeFieldTypeHelp(NSString *typeName,NSString *typeDefinition,NSString *typeRange,NSString *typeDescription) {
+ SPFieldTypeHelp *obj = [[SPFieldTypeHelp alloc] init];
+
+ [obj setTypeName: typeName];
+ [obj setTypeDefinition: typeDefinition];
+ [obj setTypeRange: typeRange];
+ [obj setTypeDescription:typeDescription];
+
+ return [obj autorelease];
+}
+
@interface SPTableStructure (PrivateAPI)
- (void)_removeFieldAndForeignKey:(NSNumber *)removeForeignKey;
@@ -1495,4 +1531,296 @@ static NSString *SPRemoveFieldAndForeignKey = @"SPRemoveFieldAndForeignKey";
[super dealloc];
}
++ (SPFieldTypeHelp *)helpForFieldType:(NSString *)typeName
+{
+ static dispatch_once_t token;
+ static NSArray *list;
+ dispatch_once(&token, ^{
+ // NSString *FN(NSNumber *): format a number using the user locale (to make large numbers more legible)
+#define FN(x) [NSNumberFormatter localizedStringFromNumber:x numberStyle:NSNumberFormatterDecimalStyle]
+ NSString *intRangeTpl = NSLocalizedString(@"Signed: %@ to %@\nUnsigned: %@ to %@",@"range of integer types");
+ // NSString *INTR(NSNumber *sMin, NSNumber *sMax, NSNumber *uMin, NSNumber *uMax): return formatted string for integer types (signed min/max, unsigned min/max)
+#define INTR(sMin,sMax,uMin,uMax) [NSString stringWithFormat:intRangeTpl,FN(sMin),FN(sMax),FN(uMin),FN(uMax)]
+ list = [@[
+ MakeFieldTypeHelp(
+ SPMySQLTinyIntType,
+ @"TINYINT[(M)] [UNSIGNED] [ZEROFILL]",
+ INTR(@(-128),@127,@0,@255),
+ NSLocalizedString(@"The smallest integer type, requires 1 byte storage space. M is the optional display width and does not affect the possible value range.",@"description of tinyint")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLSmallIntType,
+ @"SMALLINT[(M)] [UNSIGNED] [ZEROFILL]",
+ INTR(@(-32768), @32767, @0, @65535),
+ NSLocalizedString(@"Requires 2 bytes storage space. M is the optional display width and does not affect the possible value range.",@"description of smallint")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLMediumIntType,
+ @"MEDIUMINT[(M)] [UNSIGNED] [ZEROFILL]",
+ INTR(@(-8388608), @8388607, @0, @16777215),
+ NSLocalizedString(@"Requires 3 bytes storage space. M is the optional display width and does not affect the possible value range.",@"description of mediumint")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLIntType,
+ @"INT[(M)] [UNSIGNED] [ZEROFILL]",
+ INTR(@(-2147483648), @2147483647, @0, @4294967295),
+ NSLocalizedString(@"Requires 4 bytes storage space. M is the optional display width and does not affect the possible value range. INTEGER is an alias to this type.",@"description of int")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLBigIntType,
+ @"BIGINT[(M)] [UNSIGNED] [ZEROFILL]",
+ INTR([NSDecimalNumber decimalNumberWithString:@"-9223372036854775808"], [NSDecimalNumber decimalNumberWithString:@"9223372036854775807"], @0, [NSDecimalNumber decimalNumberWithString:@"18446744073709551615"]),
+ NSLocalizedString(@"Requires 8 bytes storage space. M is the optional display width and does not affect the possible value range. Note: Arithmetic operations might fail for large numbers.",@"description of bigint")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLFloatType,
+ @"FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]",
+ NSLocalizedString(@"Accurate to approx. 7 decimal places", @"range of float"),
+ NSLocalizedString(@"IEEE 754 single-precision floating-point value. M is the maxium number of digits, of which D may be after the decimal point. Note: Many decimal numbers can only be approximated by floating-point values. See DECIMAL if you require exact results.",@"description of float")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLDoubleType,
+ @"DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]",
+ NSLocalizedString(@"Accurate to approx. 15 decimal places", @"range of double"),
+ NSLocalizedString(@"IEEE 754 double-precision floating-point value. M is the maxium number of digits, of which D may be after the decimal point. Note: Many decimal numbers can only be approximated by floating-point values. See DECIMAL if you require exact results.",@"description of double")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLDoublePrecisionType,
+ @"DOUBLE PRECISION[(M,D)] [UNSIGNED] [ZEROFILL]",
+ @"",
+ NSLocalizedString(@"This is an alias for DOUBLE.",@"description of double precision")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLRealType,
+ @"REAL[(M,D)] [UNSIGNED] [ZEROFILL]",
+ @"",
+ NSLocalizedString(@"This is an alias for DOUBLE, unless REAL_AS_FLOAT is configured.",@"description of double real")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLDecimalType,
+ @"DECIMAL[(M[,D])] [UNSIGNED] [ZEROFILL]",
+ NSLocalizedString(@"M (precision): Up to 65 digits\nD (scale): 0 to 30 digits", @"range of decimal"),
+ NSLocalizedString(@"A fixed-point, exact decimal value. M is the maxium number of digits, of which D may be after the decimal point. When rounding, 0-4 is always rounded down, 5-9 up (“round towards nearest”).",@"description of decimal")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLSerialType,
+ @"SERIAL",
+ [NSString stringWithFormat:NSLocalizedString(@"Range: %@ to %@", @"range for serial type"),FN(@0),FN([NSDecimalNumber decimalNumberWithString:@"18446744073709551615"])],
+ NSLocalizedString(@"This is an alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE.",@"description of serial")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLBitType,
+ @"BIT[(M)]",
+ NSLocalizedString(@"M: 1 (default) to 64", @"range for bit type"),
+ NSLocalizedString(@"A bit-field type. M specifies the number of bits. If shorter values are inserted, they will be aligned on the least significant bit. See the SET type if you want to explicitly name each bit.",@"description of bit")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLBoolType,
+ @"BOOL",
+ @"",
+ NSLocalizedString(@"This is an alias for TINYINT(1).",@"description of bool")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLBoolean,
+ @"BOOLEAN",
+ @"",
+ NSLocalizedString(@"This is an alias for TINYINT(1).",@"description of boolean")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLDecType,
+ @"DEC[(M[,D])] [UNSIGNED] [ZEROFILL]",
+ @"",
+ NSLocalizedString(@"This is an alias for DECIMAL.",@"description of dec")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLFixedType,
+ @"FIXED[(M[,D])] [UNSIGNED] [ZEROFILL]",
+ @"",
+ NSLocalizedString(@"This is an alias for DECIMAL.",@"description of fixed")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLNumericType,
+ @"NUMERIC[(M[,D])] [UNSIGNED] [ZEROFILL]",
+ @"",
+ NSLocalizedString(@"This is an alias for DECIMAL.",@"description of numeric")
+ ),
+ // ----------------------------------------------------------------------------------
+ MakeFieldTypeHelp(
+ SPMySQLCharType,
+ @"CHAR(M)",
+ NSLocalizedString(@"M: 0 to 255 characters", @"range for char type"),
+ NSLocalizedString(@"A character string that will require M×w bytes per row, independent of the actual content length. w is the maximum number of bytes a single character can occupy in the given encoding.",@"description of char")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLVarCharType,
+ @"VARCHAR(M)",
+ [NSString stringWithFormat:NSLocalizedString(@"M: %@ to %@ characters", @"range for varchar type"),FN(@0),FN(@(65535))],
+ NSLocalizedString(@"A character string that can store up to M bytes, but requires less space for shorter values. The actual number of characters is further limited by the used encoding and the values of other fields in the row.",@"description of varchar")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLTinyTextType,
+ @"TINYTEXT",
+ NSLocalizedString(@"Up to 255 characters", @"range for tinytext type"),
+ NSLocalizedString(@"A character string that can store up to 255 bytes, but requires less space for shorter values. The actual number of characters is further limited by the used encoding. Unlike VARCHAR this type does not count towards the maximum row length.",@"description of tinytext")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLTextType,
+ @"TEXT[(M)]",
+ [NSString stringWithFormat:NSLocalizedString(@"M: %@ to %@ characters", @"range for text type"),FN(@0),FN(@(65535))],
+ NSLocalizedString(@"A character string that can store up to M bytes, but requires less space for shorter values. The actual number of characters is further limited by the used encoding. Unlike VARCHAR this type does not count towards the maximum row length.",@"description of text")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLMediumTextType,
+ @"MEDIUMTEXT",
+ [NSString stringWithFormat:NSLocalizedString(@"Up to %@ characters (16 MiB)", @"range for mediumtext type"),FN(@16777215)],
+ NSLocalizedString(@"A character string with variable length. The actual number of characters is further limited by the used encoding. Unlike VARCHAR this type does not count towards the maximum row length.",@"description of mediumtext")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLLongTextType,
+ @"LONGTEXT",
+ [NSString stringWithFormat:NSLocalizedString(@"M: %@ to %@ characters (4 GiB)", @"range for longtext type"),FN(@0),FN(@4294967295)],
+ NSLocalizedString(@"A character string with variable length. The actual number of characters is further limited by the used encoding. Unlike VARCHAR this type does not count towards the maximum row length.",@"description of longtext")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLTinyBlobType,
+ @"TINYBLOB",
+ NSLocalizedString(@"Up to 255 bytes", @"range for tinyblob type"),
+ NSLocalizedString(@"A byte array with variable length. Unlike VARBINARY this type does not count towards the maximum row length.",@"description of tinyblob")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLMediumBlobType,
+ @"MEDIUMBLOB",
+ [NSString stringWithFormat:NSLocalizedString(@"Up to %@ bytes (16 MiB)", @"range for mediumblob type"),FN(@16777215)],
+ NSLocalizedString(@"A byte array with variable length. Unlike VARBINARY this type does not count towards the maximum row length.",@"description of mediumblob")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLBlobType,
+ @"BLOB[(M)]",
+ [NSString stringWithFormat:NSLocalizedString(@"M: %@ to %@ bytes", @"range for blob type"),FN(@0),FN(@65535)],
+ NSLocalizedString(@"A byte array with variable length. Unlike VARBINARY this type does not count towards the maximum row length.",@"description of blob")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLLongBlobType,
+ @"LONGBLOB",
+ [NSString stringWithFormat:NSLocalizedString(@"Up to %@ bytes (4 GiB)", @"range for longblob type"),FN(@4294967295)],
+ NSLocalizedString(@"A byte array with variable length. Unlike VARBINARY this type does not count towards the maximum row length.",@"description of longblob")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLBinaryType,
+ @"BINARY(M)",
+ NSLocalizedString(@"M: 0 to 255 bytes", @"range for binary type"),
+ NSLocalizedString(@"A byte array with fixed length. Shorter values will always be padded to the right with 0x00 until they fit M.",@"description of binary")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLVarBinaryType,
+ @"VARBINARY(M)",
+ [NSString stringWithFormat:NSLocalizedString(@"M: %@ to %@ bytes", @"range for varbinary type"),FN(@0),FN(@(65535))],
+ NSLocalizedString(@"A byte array with variable length. The actual number of bytes is further limited by the values of other fields in the row.",@"description of varbinary")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLEnumType,
+ @"ENUM('member',...)",
+ [NSString stringWithFormat:NSLocalizedString(@"Up to %@ distinct members (<%@ in practice)\n1-2 bytes storage", @"range for enum type"),FN(@(65535)),FN(@3000)],
+ NSLocalizedString(@"Defines a list of members, of which every field can use at most one. Values are sorted by their index number (starting at 0 for the first member).",@"description of enum")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLSetType,
+ @"SET('member',...)",
+ NSLocalizedString(@"Range: 1 to 64 members\n1, 2, 3, 4 or 8 bytes storage", @"range for set type"),
+ NSLocalizedString(@"A SET can define up to 64 members (as strings) of which a field can use one or more using a comma-separated list. Upon insertion the order of members is automatically normalized and duplicate members will be eliminated. Assignment of numbers is supported using the same semantics as for BIT types.",@"description of set")
+ ),
+ // --------------------------------------------------------------------------
+ MakeFieldTypeHelp(
+ SPMySQLDateType,
+ @"DATE",
+ NSLocalizedString(@"Range: 1000-01-01 to 9999-12-31", @"range for date type"),
+ NSLocalizedString(@"Stores a date without time information. The representation is YYYY-MM-DD. Invalid values are converted to 0000-00-00.",@"description of date")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLDatetimeType,
+ @"DATETIME[(F)]",
+ NSLocalizedString(@"Range: 1000-01-01 00:00:00.0 to 9999-12-31 23:59:59.999999\nF (precision): 0 (1s) to 6 (1µs)", @"range for datetime type"),
+ NSLocalizedString(@"Stores a date and time of day. The representation is YYYY-MM-DD HH:MM:SS[.I*], I being fractional seconds. Invalid values are converted to 0000-00-00 00:00:00.0. Fractional seconds were added in MySQL 5.6.4 with a precision down to microseconds (6), specified by F.",@"description of datetime")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLTimestampType,
+ @"TIMETSTAMP[(F)]",
+ NSLocalizedString(@"Range: 1970-01-01 00:00:01.0 to 2038-01-19 03:14:07.999999\nF (precision): 0 (1s) to 6 (1µs)", @"range for timestamp type"),
+ NSLocalizedString(@"Stores a date and time of day as seconds since the beginning of the UNIX epoch (1970-01-01 00:00:00). The representation is the same as for DATETIME. Invalid values, as well as \"second zero\", are converted to 0000-00-00 00:00:00.0. Fractional seconds were added in MySQL 5.6.4 with a precision down to microseconds (6), specified by F. Some additional rules may apply.",@"description of timestamp")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLTimeType,
+ @"TIME[(F)]",
+ NSLocalizedString(@"Range: -838:59:59.0 to 838:59:59.0\nF (precision): 0 (1s) to 6 (1µs)", @"range for time type"),
+ NSLocalizedString(@"Stores a time of day, duration or time interval. The representation is HH:MM:SS[.I*], I being fractional seconds.Invalid values are converted to 00:00:00. Fractional seconds were added in MySQL 5.6.4 with a precision down to microseconds (6), specified by F.",@"description of time")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLYearType,
+ @"YEAR(4)",
+ NSLocalizedString(@"Range: 0000, 1901 to 2155", @"range for year type"),
+ NSLocalizedString(@"Represents a 4 digit year value, stored as 1 byte. Invalid values are converted to 0000 and two digit values 0 to 69 will be converted to years 2000 to 2069, resp. 70 to 99 to years 1970 to 1999.\nThe YEAR(2) type was removed in MySQL 5.7.5.",@"description of year")
+ ),
+ // --------------------------------------------------------------------------
+ MakeFieldTypeHelp(
+ SPMySQLGeometryType,
+ @"GEOMETRY",
+ @"",
+ NSLocalizedString(@"Can store a single spatial value of types POINT, LINESTRING or POLYGON. Spatial support in MySQL is based on the OpenGIS Geometry Model.",@"description of geometry")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLPointType,
+ @"POINT",
+ @"",
+ NSLocalizedString(@"Represents a single location in coordinate space using X and Y coordinates. The point is zero-dimensional.",@"description of point")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLLineStringType,
+ @"LINESTRING",
+ @"",
+ NSLocalizedString(@"Represents an ordered set of coordinates where each consecutive pair of two points is connected by a straight line.",@"description of linestring")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLPolygonType,
+ @"POLYGON",
+ @"",
+ NSLocalizedString(@"Creates a surface by combining one LinearRing (ie. a LineString that is closed and simple) as the outside boundary with zero or more inner LinearRings acting as \"holes\".",@"description of polygon")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLMultiPointType,
+ @"MULTIPOINT",
+ @"",
+ NSLocalizedString(@"Represents a set of Points without specifying any kind of relation and/or order between them.",@"description of multipoint")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLMultiLineStringType,
+ @"MULTILINESTRING",
+ @"",
+ NSLocalizedString(@"Represents a collection of LineStrings.",@"description of multilinestring")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLMultiPolygonType,
+ @"MULTIPOLYGON",
+ @"",
+ NSLocalizedString(@"Represents a collection of Polygons. The Polygons making up the MultiPolygon must not intersect.",@"description of multipolygon")
+ ),
+ MakeFieldTypeHelp(
+ SPMySQLGeometryCollectionType,
+ @"GEOMETRYCOLLECTION",
+ @"",
+ NSLocalizedString(@"Represents a collection of objects of any other single- or multi-valued spatial type. The only restriction being, that all objects must share a common coordinate system.",@"description of geometrycollection")
+ ),
+ ] retain];
+#undef FN
+#undef INTR
+ });
+
+ for (SPFieldTypeHelp *item in list) {
+ if ([[item typeName] isEqualToString:typeName]) {
+ return item;
+ }
+ }
+
+ return nil;
+}
+
@end
diff --git a/Source/SPTableStructureDelegate.m b/Source/SPTableStructureDelegate.m
index 6b2249dc..45ec320d 100644
--- a/Source/SPTableStructureDelegate.m
+++ b/Source/SPTableStructureDelegate.m
@@ -40,6 +40,7 @@
#import "SPTablesList.h"
#import "SPPillAttachmentCell.h"
#import "SPIdMenu.h"
+#import "SPComboBoxCell.h"
#import <SPMySQL/SPMySQL.h>
@@ -65,6 +66,8 @@ static void _BuildMenuWithPills(NSMenu *menu,struct _cmpMap *map,size_t mapEntri
- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo;
- (NSString *)_buildPartialColumnDefinitionString:(NSDictionary *)theRow;
+- (void)_displayFieldTypeHelpIfPossible:(SPComboBoxCell *)cell;
+
@end
@implementation SPTableStructure (SPTableStructureDelegate)
@@ -650,6 +653,105 @@ static void _BuildMenuWithPills(NSMenu *menu,struct _cmpMap *map,size_t mapEntri
return @"";
}
+- (void)comboBoxCell:(SPComboBoxCell *)cell willPopUpWindow:(NSWindow *)win
+{
+ // the selected item in the popup list is independent of the displayed text, we have to explicitly set it, too
+ NSInteger pos = [typeSuggestions indexOfObject:[cell stringValue]];
+ if(pos != NSNotFound) {
+ [cell selectItemAtIndex:pos];
+ }
+
+ //set up the help window to the right position
+ NSRect listFrame = [win frame];
+ NSRect helpFrame = [structureHelpPanel frame];
+ helpFrame.origin.y = listFrame.origin.y;
+ helpFrame.size.height = listFrame.size.height;
+ [structureHelpPanel setFrame:helpFrame display:YES];
+
+ [self _displayFieldTypeHelpIfPossible:cell];
+}
+
+- (void)comboBoxCell:(SPComboBoxCell *)cell willDismissWindow:(NSWindow *)win
+{
+ //hide the window if it is still visible
+ [structureHelpPanel orderOut:nil];
+}
+
+- (void)comboBoxCellSelectionDidChange:(SPComboBoxCell *)cell
+{
+ [self _displayFieldTypeHelpIfPossible:cell];
+}
+
+- (void)_displayFieldTypeHelpIfPossible:(SPComboBoxCell *)cell
+{
+ NSString *selected = [typeSuggestions objectOrNilAtIndex:[cell indexOfSelectedItem]];
+
+ const SPFieldTypeHelp *help = [[self class] helpForFieldType:selected];
+
+ if(help) {
+ NSMutableAttributedString *as = [[NSMutableAttributedString alloc] init];
+
+ //title
+ {
+ NSDictionary *titleAttr = @{NSFontAttributeName: [NSFont boldSystemFontOfSize:[NSFont systemFontSize]]};
+ NSAttributedString *title = [[NSAttributedString alloc] initWithString:[help typeDefinition] attributes:titleAttr];
+ [as appendAttributedString:[title autorelease]];
+ [[as mutableString] appendString:@"\n"];
+ }
+
+ //range
+ if([[help typeRange] length]) {
+ NSDictionary *rangeAttr = @{NSFontAttributeName: [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]};
+ NSAttributedString *range = [[NSAttributedString alloc] initWithString:[help typeRange] attributes:rangeAttr];
+ [as appendAttributedString:[range autorelease]];
+ [[as mutableString] appendString:@"\n"];
+ }
+
+ [[as mutableString] appendString:@"\n"];
+
+ //description
+ {
+ NSDictionary *descAttr = @{NSFontAttributeName: [NSFont systemFontOfSize:[NSFont systemFontSize]]};
+ NSAttributedString *desc = [[NSAttributedString alloc] initWithString:[help typeDescription] attributes:descAttr];
+ [as appendAttributedString:[desc autorelease]];
+ }
+
+ [as addAttribute:NSParagraphStyleAttributeName value:[NSParagraphStyle defaultParagraphStyle] range:NSMakeRange(0, [as length])];
+
+ [[structureHelpText textStorage] setAttributedString:[as autorelease]];
+
+ CGRect rect = [as boundingRectWithSize:CGSizeMake([structureHelpText frame].size.width-2, CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading|NSStringDrawingUsesLineFragmentOrigin];
+
+ NSRect winRect = [structureHelpPanel frame];
+
+ CGFloat winAddonSize = (winRect.size.height - [[structureHelpPanel contentView] frame].size.height) + (6*2);
+
+ NSRect popUpFrame = [[cell spPopUpWindow] frame];
+
+ //determine the side on which to add our window based on the space left on screen
+ NSPoint topRightCorner = NSMakePoint(popUpFrame.origin.x, NSMaxY(popUpFrame));
+ NSRect screenRect = [NSScreen rectOfScreenAtPoint:topRightCorner];
+
+ if(NSMaxX(popUpFrame)+10+winRect.size.width > NSMaxX(screenRect)-10) {
+ // exceeds right border, display on the left
+ winRect.origin.x = popUpFrame.origin.x - 10 - winRect.size.width;
+ }
+ else {
+ // display on the right
+ winRect.origin.x = NSMaxX(popUpFrame)+10;
+ }
+
+ winRect.size.height = rect.size.height + winAddonSize;
+ winRect.origin.y = NSMaxY(popUpFrame) - winRect.size.height;
+ [structureHelpPanel setFrame:winRect display:YES];
+
+ [structureHelpPanel orderFront:nil];
+ }
+ else {
+ [structureHelpPanel orderOut:nil];
+ }
+}
+
#pragma mark -
#pragma mark Menu delegate methods (encoding/collation dropdown menu)
diff --git a/Source/SPTooltip.m b/Source/SPTooltip.m
index eefa7520..54a997f6 100644
--- a/Source/SPTooltip.m
+++ b/Source/SPTooltip.m
@@ -317,16 +317,7 @@ static CGFloat slow_in_out (CGFloat t)
NSPoint pos = NSMakePoint([self frame].origin.x, [self frame].origin.y + [self frame].size.height);
// Find the screen which we are displaying on
- NSRect screenFrame = [[NSScreen mainScreen] frame];
- NSScreen* candidate;
- for(candidate in [NSScreen screens])
- {
- NSRect cf = [candidate frame];
- if(NSPointInRect(pos,cf)) {
- screenFrame = cf;
- break;
- }
- }
+ NSRect screenFrame = [NSScreen rectOfScreenAtPoint:pos];
// is contentView a webView calculate actual rendered size via JavaScript
if([[[[self contentView] class] description] isEqualToString:@"WebView"]) {