diff --git a/source/grideditlinks.pas b/source/grideditlinks.pas index f1a96354..35a50c78 100644 --- a/source/grideditlinks.pas +++ b/source/grideditlinks.pas @@ -193,6 +193,44 @@ type procedure SetBounds(R: TRect); virtual; stdcall; end; + TDataTypeEditorLink = class(TInterfacedObject, IVTEditLink) + private + FTree: TVirtualStringTree; + FTreeSelect: TVirtualStringTree; + FMemoHelp: TMemo; + FNode: PVirtualNode; + FColumn: TColumnIndex; + FTextBounds: TRect; + FStopping: Boolean; + FFinalKeyDown: Integer; + FOldWndProc: TWndMethod; + procedure DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure EditWndProc(var Message: TMessage); + procedure DoTreeSelectGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; + Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString); + procedure DoTreeSelectInitNode(Sender: TBaseVirtualTree; + ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); + procedure DoTreeSelectInitChildren(Sender: TBaseVirtualTree; + Node: PVirtualNode; var ChildCount: Cardinal); + procedure DoTreeSelectHotChange(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode); + procedure DoTreeSelectPaintText(Sender: TBaseVirtualTree; + const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; + TextType: TVSTTextType); + procedure DoTreeSelectFocusChanging(Sender: TBaseVirtualTree; OldNode, NewNode: + PVirtualNode; OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean); + procedure DoTreeSelectFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); + public + Datatype: TDatatypeIndex; + constructor Create(Tree: TVirtualStringTree); overload; + destructor Destroy; override; + function BeginEdit: Boolean; virtual; stdcall; + function CancelEdit: Boolean; virtual; stdcall; + function EndEdit: Boolean; virtual; stdcall; + function GetBounds: TRect; virtual; stdcall; + function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual; stdcall; + procedure ProcessMessage(var Message: TMessage); virtual; stdcall; + procedure SetBounds(R: TRect); virtual; stdcall; + end; function GetColumnDefaultType(var Text: WideString): TColumnDefaultType; function GetColumnDefaultClause(DefaultType: TColumnDefaultType; Text: WideString): WideString; @@ -1413,4 +1451,292 @@ begin end; + +{ Datatype selector } +constructor TDataTypeEditorLink.Create(Tree: TVirtualStringTree); +var + ParentControl: TWinControl; +begin + inherited Create; + FTree := Tree; + // Enable mouse scrolling on FtreeSelect, plus ensure the + // tree is not partly hidden when it pops up in a bottom cell + ParentControl := GetParentForm(FTree); + // Avoid flicker + SendMessage(ParentControl.Handle, WM_SETREDRAW, 0, 0); + + FTreeSelect := TVirtualStringTree.Create(FTree); + FTreeSelect.TreeOptions.PaintOptions := FTreeSelect.TreeOptions.PaintOptions + - [toShowTreeLines, toShowButtons, toShowRoot] + + [toHotTrack, toUseExplorerTheme, toHideTreeLinesIfThemed]; + FTreeSelect.TreeOptions.SelectionOptions := FTreeSelect.TreeOptions.SelectionOptions + + [toFullRowSelect]; + FTreeSelect.Header.Columns.Add; + FTreeSelect.Header.AutoSizeIndex := 0; + FTreeSelect.Header.Options := FTreeSelect.Header.Options + [hoAutoResize]; + FTreeSelect.Parent := ParentControl; + FTreeSelect.TextMargin := 0; + FTreeSelect.RootNodeCount := Length(DatatypeCategories); + FTreeSelect.OnGetText := DoTreeSelectGetText; + FTreeSelect.OnInitNode := DoTreeSelectInitNode; + FTreeSelect.OnInitChildren := DoTreeSelectInitChildren; + FTreeSelect.OnKeyDown := DoKeyDown; + FTreeSelect.OnHotChange := DoTreeSelectHotChange; + FTreeSelect.OnPaintText := DoTreeSelectPaintText; + FTreeSelect.Hide; + FOldWndProc := FTreeSelect.WindowProc; + FTreeSelect.WindowProc := EditWndProc; + + FMemoHelp := TMemo.Create(FTree); + FMemoHelp.Parent := ParentControl; + FMemoHelp.Color := clInfoBk; + FMemoHelp.Font.Color := clInfoText; + FMemoHelp.BorderStyle := bsNone; + FMemoHelp.BevelKind := bkFlat; + FMemoHelp.BevelInner := bvNone; + FMemoHelp.Hide; +end; + + +destructor TDataTypeEditorLink.Destroy; +begin + inherited; + FreeAndNil(FTreeSelect); + FreeAndNil(FMemoHelp); + if FFinalKeyDown = VK_TAB then begin + SendMessage(FTree.Handle, WM_KEYDOWN, FFinalKeyDown, 0); + SendMessage(FTree.Handle, WM_KEYDOWN, VK_F2, 0); + end; +end; + + +function TDataTypeEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; +var + NodeText: WideString; + dt: TDatatype; + CatNode, TypeNode: PVirtualNode; +begin + Result := not FStopping; + if not Result then + Exit; + FNode := Node; + FColumn := Column; + FTree.GetTextInfo(FNode, FColumn, FTreeSelect.Font, FTextBounds, NodeText); + // Highlighted cell has white font, fix that + FTreeSelect.Font.Color := FTree.Font.Color; + + // Find and select current datatype in tree + dt := GetDataTypeByName(NodeText); + CatNode := FTreeSelect.GetFirst; + while Assigned(CatNode) do begin + if CatNode.Index = Cardinal(dt.Category) then begin + TypeNode := FTreeSelect.GetFirstChild(CatNode); + while Assigned(TypeNode) do begin + if FTreeSelect.Text[TypeNode, 0] = NodeText then begin + FTreeSelect.FocusedNode := TypeNode; + FTreeSelect.Selected[TypeNode] := True; + break; + end; + TypeNode := FTreeSelect.GetNextSibling(TypeNode); + end; + end; + CatNode := FTreeSelect.GetNextSibling(CatNode); + end; + if Assigned(FTreeSelect.FocusedNode) then + FTreeSelect.ScrollIntoView(FTreeSelect.FocusedNode, True); + FTreeSelect.OnFocusChanging := DoTreeSelectFocusChanging; + FTreeSelect.OnFocusChanged := DoTreeSelectFocusChanged; +end; + + +function TDataTypeEditorLink.BeginEdit: Boolean; +begin + Result := not FStopping; + if Result then begin + SendMessage(FTreeSelect.Parent.Handle, WM_SETREDRAW, 1, 0); + FTreeSelect.Show; + FTreeSelect.SetFocus; + end; +end; + + +function TDataTypeEditorLink.CancelEdit: Boolean; +begin + Result := not FStopping; + if Result then begin + FStopping := True; + FTree.CancelEditNode; + if FTree.CanFocus then + FTree.SetFocus; + end; +end; + + +function TDataTypeEditorLink.EndEdit: Boolean; +var + newtext: WideString; +begin + Result := not FStopping; + if Not Result then + Exit; + newtext := FTreeSelect.Text[FTreeSelect.FocusedNode, 0]; + if newtext <> FTree.Text[FNode, FColumn] then + FTree.Text[FNode, FColumn] := newtext; + if FTree.CanFocus then + FTree.SetFocus; +end; + + +function TDataTypeEditorLink.GetBounds: TRect; +begin + Result := FTreeSelect.BoundsRect; +end; + + +procedure TDataTypeEditorLink.SetBounds(R: TRect); +begin + // Set position of tree. As the tree's parent is mainform, not listcolumns, add listcolumn's x + y positions + FTreeSelect.SetBounds(R.Left + FTree.ClientOrigin.X, + R.Top + FTree.ClientOrigin.Y - FTreeSelect.Header.Height - GetSystemMetrics(SM_CYMENU), + R.Right-R.Left, + 250); +end; + + +procedure TDataTypeEditorLink.ProcessMessage(var Message: TMessage); +begin + FTreeSelect.WindowProc(Message); +end; + + +procedure TDataTypeEditorLink.DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); +begin + FFinalKeyDown := Key; + case Key of + // Cancel by Escape + VK_ESCAPE: FTree.CancelEditNode; + // Apply changes and end editing by [Ctrl +] Enter or Tab + VK_RETURN, VK_TAB: FTree.EndEditNode; + end; +end; + + +procedure TDataTypeEditorLink.EditWndProc(var Message: TMessage); +begin + case Message.Msg of + WM_CHAR: + if not (TWMChar(Message).CharCode in [VK_ESCAPE, VK_TAB]) then + FOldWndProc(Message); + WM_GETDLGCODE: + Message.Result := Message.Result or DLGC_WANTARROWS or DLGC_WANTALLKEYS or DLGC_WANTTAB; + else + FOldWndProc(Message); + end; +end; + + +procedure TDataTypeEditorLink.DoTreeSelectInitNode(Sender: TBaseVirtualTree; + ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); +begin + // First level nodes always expanded + if Sender.GetNodeLevel(Node) = 0 then + InitialStates := InitialStates + [ivsExpanded, ivsHasChildren]; +end; + + +procedure TDataTypeEditorLink.DoTreeSelectInitChildren(Sender: TBaseVirtualTree; + Node: PVirtualNode; var ChildCount: Cardinal); +var + i: Integer; +begin + // Tell number of datatypes per category + ChildCount := 0; + if Sender.GetNodeLevel(Node) = 0 then for i:=Low(Datatypes) to High(Datatypes) do begin + if Datatypes[i].Category = DatatypeCategories[Node.Index].Index then + Inc(ChildCount); + end; +end; + + +procedure TDataTypeEditorLink.DoTreeSelectGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; + Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString); +var + i: Integer; + Counter: Cardinal; +begin + // Get cell text + case Sender.GetNodeLevel(Node) of + 0: CellText := DatatypeCategories[Node.Index].Name; + 1: begin + Counter := 0; + for i:=Low(Datatypes) to High(Datatypes) do begin + if Datatypes[i].Category = DatatypeCategories[Node.Parent.Index].Index then begin + Inc(Counter); + if Counter = Node.Index+1 then begin + CellText := Datatypes[i].Name; + break; + end; + end; + end; + end; + end; +end; + + +procedure TDataTypeEditorLink.DoTreeSelectHotChange(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode); +var + R: TRect; + NodeText: WideString; + bmp: TBitMap; +begin + // Display help box for hovered datatype + FMemoHelp.Clear; + if Assigned(NewNode) and (Sender.GetNodeLevel(NewNode) = 1) then begin + R := FTreeSelect.GetDisplayRect(NewNode, 0, False); + NodeText := FTreeSelect.Text[NewNode, 0]; + FMemoHelp.Width := Min(250, FTreeSelect.Left); + FMemoHelp.Left := FTreeSelect.Left - FMemoHelp.Width + (Integer(FTreeSelect.Indent) Div 2); + FMemoHelp.Top := FTreeSelect.Top + R.Top + 3; + FMemoHelp.Text := GetDatatypeByName(NodeText).Description; + // Calc height of memo + bmp := TBitMap.Create; + bmp.Canvas.Font.Assign(FMemoHelp.Font); + R := Rect(0, 0, FMemoHelp.Width, 0); + DrawText(bmp.Canvas.Handle, PChar(FMemoHelp.Text), Length(FMemoHelp.Text), R, DT_WORDBREAK or DT_CALCRECT); + FreeAndNil(bmp); + FMemoHelp.Height := R.Bottom + 2; + FMemoHelp.Show; + end; + if FMemoHelp.GetTextLen = 0 then + FMemoHelp.Hide; +end; + + +procedure TDataTypeEditorLink.DoTreeSelectPaintText(Sender: TBaseVirtualTree; + const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; + TextType: TVSTTextType); +begin + // Give datatype column specific color, as set in preferences + case Sender.GetNodeLevel(Node) of + 0: TargetCanvas.Font.Style := TargetCanvas.Font.Style + [fsBold]; + 1: if not (vsSelected in Node.States) then + TargetCanvas.Font.Color := DatatypeCategories[Node.Parent.Index].Color; + end; +end; + + +procedure TDataTypeEditorLink.DoTreeSelectFocusChanging(Sender: TBaseVirtualTree; OldNode, NewNode: + PVirtualNode; OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean); +begin + // Allow only 2nd level datatypes to be focused, not their category + Allowed := Sender.GetNodeLevel(NewNode) = 1; +end; + +procedure TDataTypeEditorLink.DoTreeSelectFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); +begin + // Datatype selected - end editing + FTree.EndEditNode; +end; + + end. diff --git a/source/main.pas b/source/main.pas index 6b47e8f4..236921fd 100644 --- a/source/main.pas +++ b/source/main.pas @@ -769,18 +769,6 @@ type prefEnableEnumEditor, prefEnableSetEditor, prefEnableNullBG : Boolean; - prefFieldColorNumeric, - prefFieldColorText, - prefFieldColorBinary, - prefFieldColorDatetime, - prefFieldColorEnum, - prefFieldColorSet, - prefNullColorNumeric, - prefNullColorText, - prefNullColorBinary, - prefNullColorDatetime, - prefNullColorEnum, - prefNullColorSet, prefNullColorDefault, prefNullBG : TColor; CreateDatabaseForm : TCreateDatabaseForm; @@ -1336,12 +1324,14 @@ begin FixVT(DataGrid); FixVT(QueryGrid); // Load color settings - prefFieldColorNumeric := GetRegValue(REGNAME_FIELDCOLOR_NUMERIC, DEFAULT_FIELDCOLOR_NUMERIC); - prefFieldColorText := GetRegValue(REGNAME_FIELDCOLOR_TEXT, DEFAULT_FIELDCOLOR_TEXT); - prefFieldColorBinary := GetRegValue(REGNAME_FIELDCOLOR_BINARY, DEFAULT_FIELDCOLOR_BINARY); - prefFieldColorDatetime := GetRegValue(REGNAME_FIELDCOLOR_DATETIME, DEFAULT_FIELDCOLOR_DATETIME); - prefFieldColorEnum := GetRegValue(REGNAME_FIELDCOLOR_ENUM, DEFAULT_FIELDCOLOR_ENUM); - prefFieldColorSet := GetRegValue(REGNAME_FIELDCOLOR_SET, DEFAULT_FIELDCOLOR_SET); + DatatypeCategories[Integer(dtcInteger)].Color := GetRegValue(REGNAME_FIELDCOLOR_NUMERIC, DEFAULT_FIELDCOLOR_NUMERIC); + DatatypeCategories[Integer(dtcReal)].Color := GetRegValue(REGNAME_FIELDCOLOR_NUMERIC, DEFAULT_FIELDCOLOR_NUMERIC); + DatatypeCategories[Integer(dtcText)].Color := GetRegValue(REGNAME_FIELDCOLOR_TEXT, DEFAULT_FIELDCOLOR_TEXT); + DatatypeCategories[Integer(dtcBinary)].Color := GetRegValue(REGNAME_FIELDCOLOR_BINARY, DEFAULT_FIELDCOLOR_BINARY); + DatatypeCategories[Integer(dtcTemporal)].Color := GetRegValue(REGNAME_FIELDCOLOR_DATETIME, DEFAULT_FIELDCOLOR_DATETIME); + DatatypeCategories[Integer(dtcIntegerNamed)].Color := GetRegValue(REGNAME_FIELDCOLOR_ENUM, DEFAULT_FIELDCOLOR_ENUM); + DatatypeCategories[Integer(dtcSet)].Color := GetRegValue(REGNAME_FIELDCOLOR_SET, DEFAULT_FIELDCOLOR_SET); + DatatypeCategories[Integer(dtcSetNamed)].Color := GetRegValue(REGNAME_FIELDCOLOR_SET, DEFAULT_FIELDCOLOR_SET); prefNullBG := GetRegValue(REGNAME_BG_NULL, DEFAULT_BG_NULL); CalcNullColors; // Editor enablings @@ -7368,14 +7358,11 @@ end; procedure TMainForm.CalcNullColors; +var + i: Integer; begin - prefNullColorNumeric := ColorAdjustBrightness(prefFieldColorNumeric, COLORSHIFT_NULLFIELDS); - prefNullColorText := ColorAdjustBrightness(prefFieldColorText, COLORSHIFT_NULLFIELDS); - prefNullColorBinary := ColorAdjustBrightness(prefFieldColorBinary, COLORSHIFT_NULLFIELDS); - prefNullColorDatetime := ColorAdjustBrightness(prefFieldColorDatetime, COLORSHIFT_NULLFIELDS); - prefNullColorEnum := ColorAdjustBrightness(prefFieldColorEnum, COLORSHIFT_NULLFIELDS); - prefNullColorSet := ColorAdjustBrightness(prefFieldColorSet, COLORSHIFT_NULLFIELDS); - prefNullColorDefault := ColorAdjustBrightness(clWindow, COLORSHIFT_NULLFIELDS); + for i:=Low(DatatypeCategories) to High(DatatypeCategories) do + DatatypeCategories[i].NullColor := ColorAdjustBrightness(DatatypeCategories[i].Color, COLORSHIFT_NULLFIELDS); end; @@ -7387,7 +7374,6 @@ procedure TMainForm.GridPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); var - isNull: Boolean; cl: TColor; r: PGridResult; begin @@ -7404,34 +7390,15 @@ begin if r.Columns[Column].IsPriPart then TargetCanvas.Font.Style := TargetCanvas.Font.Style + [fsBold]; - // NULL value - isNull := r.Rows[Node.Index].Cells[Column].IsNull; - // Do not apply any color on a selected, highlighted cell to keep readability if (Node = Sender.FocusedNode) and (Column = Sender.FocusedColumn) then cl := clHighlightText else if vsSelected in Node.States then cl := clBlack - // Numeric field - else if r.Columns[Column].DatatypeCat in [dtcInteger, dtcReal] then - if isNull then cl := prefNullColorNumeric else cl := prefFieldColorNumeric - // Date field - else if r.Columns[Column].DatatypeCat = dtcTemporal then - if isNull then cl := prefNullColorDatetime else cl := prefFieldColorDatetime - // Text field - else if r.Columns[Column].DatatypeCat = dtcText then - if isNull then cl := prefNullColorText else cl := prefFieldColorText - // Text field - else if r.Columns[Column].DatatypeCat = dtcBinary then - if isNull then cl := prefNullColorBinary else cl := prefFieldColorBinary - // Enum field - else if r.Columns[Column].DatatypeCat = dtcIntegerNamed then - if isNull then cl := prefNullColorEnum else cl := prefFieldColorEnum - // Set field - else if r.Columns[Column].DatatypeCat = dtcSetNamed then - if isNull then cl := prefNullColorSet else cl := prefFieldColorSet + else if r.Rows[Node.Index].Cells[Column].IsNull then + cl := DatatypeCategories[Integer(r.Columns[Column].DatatypeCat)].NullColor else - if isNull then cl := prefNullColorDefault else cl := clWindowText; + cl := DatatypeCategories[Integer(r.Columns[Column].DatatypeCat)].Color; TargetCanvas.Font.Color := cl; end; diff --git a/source/mysql_structures.pas b/source/mysql_structures.pas index a9047df0..1d522c8b 100644 --- a/source/mysql_structures.pas +++ b/source/mysql_structures.pas @@ -7,7 +7,7 @@ unit mysql_structures; interface uses - Classes, Widestrings; + Classes, Widestrings, Graphics; {$I const.inc} @@ -22,13 +22,14 @@ type dtPoint, dtLinestring, dtPolygon, dtGeometry, dtMultipoint, dtMultilinestring, dtMultipolygon, dtGeometrycollection); // MySQL data type categorization - TDatatypeCategoryIndex = (dtcInteger, dtcReal, dtcTemporal, dtcText, dtcBinary, - dtcIntegerNamed, dtcSet, dtcSetNamed, dtcSpatial); + TDatatypeCategoryIndex = (dtcInteger, dtcIntegerNamed, dtcReal, dtcText, dtcBinary, dtcTemporal, + dtcSpatial, dtcSet, dtcSetNamed); // MySQL data type structure TDatatype = record Index: TDatatypeIndex; Name: String[18]; + Description: String; HasLength: Boolean; // Can have Length- or Set-attribute? RequiresLength: Boolean; // Must have a Length- or Set-attribute? HasUnsigned: Boolean; // Can be unsigned? @@ -43,6 +44,8 @@ type TDatatypeCategory = record Index: TDatatypeCategoryIndex; Name: String[32]; + Color: TColor; + NullColor: TColor; end; // MySQL functions structure @@ -103,6 +106,9 @@ var ( Index: dtTinyint; Name: 'TINYINT'; + Description: 'TINYINT[(M)] [UNSIGNED] [ZEROFILL]' + CRLF + + 'A very small integer. The signed range is -128 to 127. ' + + 'The unsigned range is 0 to 255.'; HasLength: True; RequiresLength: False; HasUnsigned: True; @@ -114,6 +120,9 @@ var ( Index: dtSmallint; Name: 'SMALLINT'; + Description: 'SMALLINT[(M)] [UNSIGNED] [ZEROFILL]' + CRLF + + 'A small integer. The signed range is -32768 to 32767. ' + + 'The unsigned range is 0 to 65535.'; HasLength: True; RequiresLength: False; HasUnsigned: True; @@ -125,6 +134,9 @@ var ( Index: dtMediumint; Name: 'MEDIUMINT'; + Description: 'MEDIUMINT[(M)] [UNSIGNED] [ZEROFILL]' + CRLF + + 'A medium-sized integer. The signed range is -8388608 to 8388607. ' + + 'The unsigned range is 0 to 16777215.'; HasLength: True; RequiresLength: False; HasUnsigned: True; @@ -136,6 +148,9 @@ var ( Index: dtInt; Name: 'INT'; + Description: 'INT[(M)] [UNSIGNED] [ZEROFILL]' + CRLF + + 'A normal-size integer. The signed range is -2147483648 to 2147483647. ' + + 'The unsigned range is 0 to 4294967295.'; HasLength: True; RequiresLength: False; HasUnsigned: True; @@ -147,6 +162,9 @@ var ( Index: dtBigint; Name: 'BIGINT'; + Description: 'BIGINT[(M)] [UNSIGNED] [ZEROFILL]' + CRLF + + 'A large integer. The signed range is -9223372036854775808 to ' + + '9223372036854775807. The unsigned range is 0 to 18446744073709551615.'; HasLength: True; RequiresLength: False; HasUnsigned: True; @@ -158,6 +176,12 @@ var ( Index: dtFloat; Name: 'FLOAT'; + Description: 'FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]' + CRLF + + 'A small (single-precision) floating-point number. Allowable values are '+ + '-3.402823466E+38 to -1.175494351E-38, 0, and 1.175494351E-38 to '+ + '3.402823466E+38. These are the theoretical limits, based on the IEEE '+ + 'standard. The actual range might be slightly smaller depending on your '+ + 'hardware or operating system.'; HasLength: True; RequiresLength: False; HasUnsigned: True; @@ -169,6 +193,12 @@ var ( Index: dtDouble; Name: 'DOUBLE'; + Description: 'DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]' + CRLF + + 'A normal-size (double-precision) floating-point number. Allowable ' + + 'values are -1.7976931348623157E+308 to -2.2250738585072014E-308, 0, and ' + + '2.2250738585072014E-308 to 1.7976931348623157E+308. These are the ' + + 'theoretical limits, based on the IEEE standard. The actual range might ' + + 'be slightly smaller depending on your hardware or operating system.'; HasLength: True; RequiresLength: False; HasUnsigned: True; @@ -180,6 +210,14 @@ var ( Index: dtDecimal; Name: 'DECIMAL'; + Description: 'DECIMAL[(M[,D])] [UNSIGNED] [ZEROFILL]' + CRLF + + 'A packed "exact" fixed-point number. M is the total number of digits ' + + '(the precision) and D is the number of digits after the decimal point ' + + '(the scale). The decimal point and (for negative numbers) the "-" sign ' + + 'are not counted in M. If D is 0, values have no decimal point or ' + + 'fractional part. The maximum number of digits (M) for DECIMAL is 65. ' + + 'The maximum number of supported decimals (D) is 30. If D is omitted, ' + + 'the default is 0. If M is omitted, the default is 10.'; HasLength: True; RequiresLength: True; HasUnsigned: True; @@ -191,6 +229,10 @@ var ( Index: dtDate; Name: 'DATE'; + Description: 'DATE' + CRLF + + 'A date. The supported range is ''1000-01-01'' to ''9999-12-31''. MySQL ' + + 'displays DATE values in ''YYYY-MM-DD'' format, but allows assignment of ' + + 'values to DATE columns using either strings or numbers.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -202,6 +244,10 @@ var ( Index: dtTime; Name: 'TIME'; + Description: 'TIME' + CRLF + + 'A time. The range is ''-838:59:59'' to ''838:59:59''. MySQL displays TIME ' + + 'values in ''HH:MM:SS'' format, but allows assignment of values to TIME ' + + 'columns using either strings or numbers.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -213,6 +259,13 @@ var ( Index: dtYear; Name: 'YEAR'; + Description: 'YEAR[(2|4)]' + CRLF + + 'A year in two-digit or four-digit format. The default is four-digit ' + + 'format. In four-digit format, the allowable values are 1901 to 2155, ' + + 'and 0000. In two-digit format, the allowable values are 70 to 69, ' + + 'representing years from 1970 to 2069. MySQL displays YEAR values in ' + + 'YYYY format, but allows you to assign values to YEAR columns using ' + + 'either strings or numbers.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -224,6 +277,11 @@ var ( Index: dtDatetime; Name: 'DATETIME'; + Description: 'DATETIME' + CRLF + + 'A date and time combination. The supported range is ''1000-01-01 ' + + '00:00:00'' to ''9999-12-31 23:59:59''. MySQL displays DATETIME values in ' + + '''YYYY-MM-DD HH:MM:SS'' format, but allows assignment of values to ' + + 'DATETIME columns using either strings or numbers.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -235,6 +293,13 @@ var ( Index: dtTimestamp; Name: 'TIMESTAMP'; + Description: 'TIMESTAMP' + CRLF + + 'A timestamp. The range is ''1970-01-01 00:00:01'' UTC to ''2038-01-09 ' + + '03:14:07'' UTC. TIMESTAMP values are stored as the number of seconds ' + + 'since the epoch (''1970-01-01 00:00:00'' UTC). A TIMESTAMP cannot ' + + 'represent the value ''1970-01-01 00:00:00'' because that is equivalent to ' + + '0 seconds from the epoch and the value 0 is reserved for representing ' + + '''0000-00-00 00:00:00'', the "zero" TIMESTAMP value.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -246,6 +311,12 @@ var ( Index: dtCHAR; Name: 'CHAR'; + Description: 'CHAR[(M)]' + CRLF + + 'A fixed-length string that is always right-padded with spaces to the ' + + 'specified length when stored. M represents the column length in ' + + 'characters. The range of M is 0 to 255. If M is omitted, the length is 1.' + CRLF + CRLF + + '*Note*: Trailing spaces are removed when CHAR values are retrieved ' + + 'unless the PAD_CHAR_TO_FULL_LENGTH SQL mode is enabled.'; HasLength: True; RequiresLength: True; HasUnsigned: False; @@ -258,6 +329,16 @@ var ( Index: dtVarchar; Name: 'VARCHAR'; + Description: 'VARCHAR(M)' + CRLF + + 'A variable-length string. M represents the maximum column length in ' + + 'characters. The range of M is 0 to 65,535. The effective maximum length ' + + 'of a VARCHAR is subject to the maximum row size (65,535 bytes, which is ' + + 'shared among all columns) and the character set used. For example, utf8 ' + + 'characters can require up to three bytes per character, so a VARCHAR ' + + 'column that uses the utf8 character set can be declared to be a maximum ' + + 'of 21,844 characters. ' + CRLF + CRLF + + '*Note*: MySQL 5.1 follows the standard SQL specification, and does not ' + + 'remove trailing spaces from VARCHAR values.'; HasLength: True; RequiresLength: True; HasUnsigned: False; @@ -270,6 +351,11 @@ var ( Index: dtTinytext; Name: 'TINYTEXT'; + Description: 'TINYTEXT' + CRLF + + 'A TEXT column with a maximum length of 255 (28 - 1) characters. The ' + + 'effective maximum length is less if the value contains multi-byte ' + + 'characters. Each TINYTEXT value is stored using a one-byte length ' + + 'prefix that indicates the number of bytes in the value.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -281,6 +367,14 @@ var ( Index: dtText; Name: 'TEXT'; + Description: 'TEXT[(M)]' + CRLF + + 'A TEXT column with a maximum length of 65,535 (216 - 1) characters. The ' + + 'effective maximum length is less if the value contains multi-byte ' + + 'characters. Each TEXT value is stored using a two-byte length prefix ' + + 'that indicates the number of bytes in the value. ' + CRLF + + 'An optional length M can be given for this type. If this is done, MySQL ' + + 'creates the column as the smallest TEXT type large enough to hold ' + + 'values M characters long.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -292,6 +386,11 @@ var ( Index: dtMediumtext; Name: 'MEDIUMTEXT'; + Description: 'MEDIUMTEXT' + CRLF + + 'A TEXT column with a maximum length of 16,777,215 (224 - 1) characters. ' + + 'The effective maximum length is less if the value contains multi-byte ' + + 'characters. Each MEDIUMTEXT value is stored using a three-byte length ' + + 'prefix that indicates the number of bytes in the value.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -303,6 +402,14 @@ var ( Index: dtLongtext; Name: 'LONGTEXT'; + Description: 'LONGTEXT' + CRLF + + 'A TEXT column with a maximum length of 4,294,967,295 or 4GB (232 - 1) ' + + 'characters. The effective maximum length is less if the value contains ' + + 'multi-byte characters. The effective maximum length of LONGTEXT columns ' + + 'also depends on the configured maximum packet size in the client/server ' + + 'protocol and available memory. Each LONGTEXT value is stored using a ' + + 'four-byte length prefix that indicates the number of bytes in the ' + + 'value.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -314,6 +421,10 @@ var ( Index: dtBinary; Name: 'BINARY'; + Description: 'BINARY(M)' + CRLF + + 'The BINARY type is similar to the CHAR type, but stores binary byte ' + + 'strings rather than non-binary character strings. M represents the ' + + 'column length in bytes.'; HasLength: True; RequiresLength: True; HasUnsigned: False; @@ -326,6 +437,10 @@ var ( Index: dtVarbinary; Name: 'VARBINARY'; + Description: 'VARBINARY(M)' + CRLF + + 'The VARBINARY type is similar to the VARCHAR type, but stores binary ' + + 'byte strings rather than non-binary character strings. M represents the ' + + 'maximum column length in bytes.'; HasLength: True; RequiresLength: True; HasUnsigned: False; @@ -338,6 +453,10 @@ var ( Index: dtTinyblob; Name: 'TINYBLOB'; + Description: 'TINYBLOB' + CRLF + + 'A BLOB column with a maximum length of 255 (28 - 1) bytes. Each ' + + 'TINYBLOB value is stored using a one-byte length prefix that indicates ' + + 'the number of bytes in the value.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -349,6 +468,13 @@ var ( Index: dtBlob; Name: 'BLOB'; + Description: 'BLOB[(M)]' + CRLF + + 'A BLOB column with a maximum length of 65,535 (216 - 1) bytes. Each ' + + 'BLOB value is stored using a two-byte length prefix that indicates the ' + + 'number of bytes in the value. ' + CRLF + + 'An optional length M can be given for this type. If this is done, MySQL ' + + 'creates the column as the smallest BLOB type large enough to hold ' + + 'values M bytes long.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -360,6 +486,10 @@ var ( Index: dtMediumblob; Name: 'MEDIUMBLOB'; + Description: 'MEDIUMBLOB' + CRLF + + 'A BLOB column with a maximum length of 16,777,215 (224 - 1) bytes. Each ' + + 'MEDIUMBLOB value is stored using a three-byte length prefix that ' + + 'indicates the number of bytes in the value.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -371,6 +501,12 @@ var ( Index: dtLongblob; Name: 'LONGBLOB'; + Description: 'LONGBLOB' + CRLF + + 'A BLOB column with a maximum length of 4,294,967,295 or 4GB (232 - 1) ' + + 'bytes. The effective maximum length of LONGBLOB columns depends on the ' + + 'configured maximum packet size in the client/server protocol and ' + + 'available memory. Each LONGBLOB value is stored using a four-byte ' + + 'length prefix that indicates the number of bytes in the value.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -382,6 +518,11 @@ var ( Index: dtEnum; Name: 'ENUM'; + Description: 'ENUM(''value1'',''value2'',...)' + CRLF + + 'An enumeration. A string object that can have only one value, chosen ' + + 'from the list of values ''value1'', ''value2'', ..., NULL or the special '''' ' + + 'error value. An ENUM column can have a maximum of 65,535 distinct ' + + 'values. ENUM values are represented internally as integers.'; HasLength: True; // Obviously this is not meant as "length", but as "set of values" RequiresLength: True; HasUnsigned: False; @@ -394,6 +535,11 @@ var ( Index: dtSet; Name: 'SET'; + Description: 'SET(''value1'',''value2'',...)' + CRLF + + 'A set. A string object that can have zero or more values, each of which ' + + 'must be chosen from the list of values ''value1'', ''value2'', ... A SET ' + + 'column can have a maximum of 64 members. SET values are represented ' + + 'internally as integers.'; HasLength: True; // Same as for ENUM RequiresLength: True; HasUnsigned: False; @@ -406,6 +552,9 @@ var ( Index: dtBit; Name: 'BIT'; + Description: 'BIT[(M)]' + CRLF + + 'A bit-field type. M indicates the number of bits per value, from 1 to ' + + '64. The default is 1 if M is omitted.'; HasLength: True; RequiresLength: False; HasUnsigned: False; @@ -417,6 +566,8 @@ var ( Index: dtPoint; Name: 'POINT'; + Description: 'POINT(x,y)' + CRLF + + 'Constructs a WKB Point using its coordinates.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -428,6 +579,10 @@ var ( Index: dtLinestring; Name: 'LINESTRING'; + Description: 'LINESTRING(pt1,pt2,...)' + CRLF + + 'Constructs a WKB LineString value from a number of WKB Point arguments. ' + + 'If any argument is not a WKB Point, the return value is NULL. If the ' + + 'number of Point arguments is less than two, the return value is NULL.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -439,6 +594,10 @@ var ( Index: dtPolygon; Name: 'POLYGON'; + Description: 'POLYGON(ls1,ls2,...)' + CRLF + + 'Constructs a WKB Polygon value from a number of WKB LineString ' + + 'arguments. If any argument does not represent the WKB of a LinearRing ' + + '(that is, not a closed and simple LineString) the return value is NULL.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -450,6 +609,7 @@ var ( Index: dtGeometry; Name: 'GEOMETRY'; + Description: ''; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -461,6 +621,9 @@ var ( Index: dtMultipoint; Name: 'MULTIPOINT'; + Description: 'MULTIPOINT(pt1,pt2,...)' + CRLF + + 'Constructs a WKB MultiPoint value using WKB Point arguments. If any ' + + 'argument is not a WKB Point, the return value is NULL.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -472,6 +635,9 @@ var ( Index: dtMultilinestring; Name: 'MULTILINESTRING'; + Description: 'MULTILINESTRING(ls1,ls2,...)' + CRLF + + 'Constructs a WKB MultiLineString value using WKB LineString arguments. ' + + 'If any argument is not a WKB LineString, the return value is NULL.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -483,6 +649,10 @@ var ( Index: dtMultipolygon; Name: 'MULTIPOLYGON'; + Description: 'MULTIPOLYGON(poly1,poly2,...)' + CRLF + + 'Constructs a WKB MultiPolygon value from a set of WKB Polygon ' + + 'arguments. If any argument is not a WKB Polygon, the return value is ' + + 'NULL.'; HasLength: False; RequiresLength: False; HasUnsigned: False; @@ -494,6 +664,9 @@ var ( Index: dtGeometrycollection; Name: 'GEOMETRYCOLLECTION'; + Description: 'GEOMETRYCOLLECTION(g1,g2,...)' + CRLF + + 'Constructs a WKB GeometryCollection. If any argument is not a ' + + 'well-formed WKB representation of a geometry, the return value is NULL.'; HasLength: False; RequiresLength: False; HasUnsigned: False; diff --git a/source/options.pas b/source/options.pas index 79a219f5..48d97950 100644 --- a/source/options.pas +++ b/source/options.pas @@ -128,7 +128,7 @@ type implementation -uses main, helpers; +uses main, helpers, mysql_structures; {$R *.DFM} @@ -243,12 +243,14 @@ begin Mainform.prefCSVSeparator := editCSVSeparator.Text; Mainform.prefCSVEncloser := editCSVEncloser.Text; Mainform.prefCSVTerminator := editCSVTerminator.Text; - Mainform.prefFieldColorNumeric := cboxNumeric.Selected; - Mainform.prefFieldColorText := cboxText.Selected; - Mainform.prefFieldColorBinary := cboxBinary.Selected; - Mainform.prefFieldColorDatetime := cboxDatetime.Selected; - Mainform.prefFieldColorEnum := cboxEnum.Selected; - Mainform.prefFieldColorSet := cboxSet.Selected; + DatatypeCategories[Integer(dtcInteger)].Color := cboxNumeric.Selected; + DatatypeCategories[Integer(dtcReal)].Color := cboxNumeric.Selected; + DatatypeCategories[Integer(dtcText)].Color := cboxText.Selected; + DatatypeCategories[Integer(dtcBinary)].Color := cboxBinary.Selected; + DatatypeCategories[Integer(dtcTemporal)].Color := cboxDatetime.Selected; + DatatypeCategories[Integer(dtcIntegerNamed)].Color := cboxEnum.Selected; + DatatypeCategories[Integer(dtcSet)].Color := cboxSet.Selected; + DatatypeCategories[Integer(dtcSetNamed)].Color := cboxSet.Selected; Mainform.prefNullBG := cboxNullBg.Selected; Mainform.CalcNullColors; Mainform.DataGrid.Repaint; diff --git a/source/table_editor.pas b/source/table_editor.pas index 7164b042..e1b98385 100644 --- a/source/table_editor.pas +++ b/source/table_editor.pas @@ -1040,29 +1040,13 @@ begin TextColor := TargetCanvas.Font.Color; case Column of - 2: case dt.Category of - dtcInteger, dtcReal: TextColor := Mainform.prefFieldColorNumeric; - dtcTemporal: TextColor := Mainform.prefFieldColorDateTime; - dtcText: TextColor := Mainform.prefFieldColorText; - dtcBinary: TextColor := Mainform.prefFieldColorBinary; - dtcIntegerNamed: TextColor := Mainform.prefFieldColorEnum; - dtcSet, dtcSetNamed: TextColor := Mainform.prefFieldColorSet; - // TODO: catSpatial - else TextColor := TargetCanvas.Font.Color; - end; + 2: TextColor := DatatypeCategories[Integer(dt.Category)].Color; 6: case GetColumnDefaultType(Default) of cdtNull, cdtNullUpdateTS: - case dt.Category of - dtcInteger, dtcReal: TextColor := Mainform.prefNullColorNumeric; - dtcTemporal: TextColor := Mainform.prefNullColorDateTime; - dtcText: TextColor := Mainform.prefNullColorText; - dtcBinary: TextColor := Mainform.prefNullColorBinary; - dtcIntegerNamed: TextColor := Mainform.prefNullColorEnum; - dtcSet, dtcSetNamed: TextColor := Mainform.prefNullColorSet; - end; + TextColor := DatatypeCategories[Integer(dt.Category)].NullColor; cdtCurTS, cdtCurTSUpdateTS: - TextColor := Mainform.prefFieldColorDateTime; + TextColor := DatatypeCategories[Integer(dtcTemporal)].Color; cdtAutoInc: TextColor := clNavy; end; @@ -1154,17 +1138,15 @@ procedure TfrmTableEditor.listColumnsCreateEditor(Sender: TBaseVirtualTree; var EnumEditor: TEnumEditorLink; DefaultEditor: TColumnDefaultEditorLink; - i: Integer; + DatatypeEditor: TDatatypeEditorLink; Props: TWideStringlist; begin // Start cell editor case Column of 2: begin // Datatype pulldown - EnumEditor := TEnumEditorLink.Create; - EnumEditor.ValueList := TWideStringList.Create; - for i:=Low(Datatypes) to High(Datatypes) do - EnumEditor.ValueList.Add(Datatypes[i].Name); - EditLink := EnumEditor; + DatatypeEditor := TDatatypeEditorLink.Create(Sender as TVirtualStringTree); + DatatypeEditor.Datatype := dtDateTime; + EditLink := DataTypeEditor; end; 8: begin // Collation pulldown EnumEditor := TEnumEditorLink.Create;