unit fieldeditor; // ------------------------------------- // Field-/Index-Editor // ------------------------------------- interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, ImgList, ToolWin, ExtCtrls, Buttons, VirtualTrees; type TFieldEditorMode = (femFieldAdd,femFieldUpdate,femIndexEditor); TFieldEditForm = class(TForm) pc: TPageControl; tabField: TTabSheet; ButtonCancel: TButton; ButtonOK: TButton; lblName: TLabel; lblType: TLabel; lblLengthSet: TLabel; lblDefault: TLabel; EditDefault: TEdit; EditLength: TEdit; ComboBoxType: TComboBox; EditFieldname: TEdit; GroupBoxAttributes: TGroupBox; CheckBoxBinary: TCheckBox; CheckBoxUnsigned: TCheckBox; CheckBoxZerofill: TCheckBox; CheckBoxNotNull: TCheckBox; CheckBoxAutoIncrement: TCheckBox; tabIndexes: TTabSheet; ComboBoxKeys: TComboBoxEx; lblIndexName: TLabel; CheckBoxUnique: TCheckBox; ButtonAdd: TButton; ButtonDelete: TButton; lblColumnsUsed: TLabel; listColumnsUsed: TListBox; listColumnsAvailable: TListBox; btnAddColumnToIndex: TBitBtn; btnDeleteColumnFromIndex: TBitBtn; lblColumnsAvailable: TLabel; ButtonAddPrimary: TButton; ComboBoxPosition: TComboBox; lblPosition: TLabel; CheckBoxFulltext: TCheckBox; btnAddAllColumnsToIndex: TBitBtn; btnDeleteAllColumnsFromIndex: TBitBtn; btnDatatypeHelp: TButton; lblComment: TLabel; EditComment: TEdit; procedure btnDatatypeHelpClick(Sender: TObject); procedure FormShow(Sender: TObject); procedure ComboBoxTypeChange(Sender: TObject); procedure AddUpdateField(Sender: TObject); procedure ButtonCancelClick(Sender: TObject); procedure pcChange(Sender: TObject); procedure OKClick(Sender: TObject); procedure UpdateKeys(Sender: TObject); procedure ComboBoxKeysChange(Sender: TObject); procedure ButtonAddClick(Sender: TObject); procedure InitFieldEditor(Sender: TObject); procedure InitIndexEditor(Sender: TObject); procedure RemoveField(Sender: TObject); procedure ButtonDeleteClick(Sender: TObject); procedure ButtonAddPrimaryClick(Sender: TObject); procedure ShowKeys(index: Integer=0); procedure CheckBoxUniqueClick(Sender: TObject); procedure AddField(Sender: TObject); procedure CheckBoxFulltextClick(Sender: TObject); procedure btnAddAllColumnsToIndexClick(Sender: TObject); procedure btnDeleteAllColumnsFromIndexClick(Sender: TObject); procedure CheckBoxUnsignedClick(Sender: TObject); procedure CheckBoxZerofillClick(Sender: TObject); procedure listClick(Sender: TObject); private { Private declarations } TempKeys : TStringList; FMode : TFieldEditorMode; FModeWhenCalled : TFieldEditorMode; FFieldName : String; procedure ValidateControls; public { Public declarations } end; function FieldEditorWindow (AOwner : TComponent; AMode : TFieldEditorMode; AFieldName : String = '') : Boolean; {$I const.inc} implementation uses helpers, childwin, Main, mysql_structures, db; var klist : Array of TMysqlIndex; {$R *.DFM} {*** Create form } function FieldEditorWindow (AOwner : TComponent; AMode : TFieldEditorMode; AFieldName : String = '') : Boolean; var f : TFieldEditForm; begin f := TFieldEditForm.Create(AOwner); f.FMode := AMode; // Also remember original mode for restoring when switching pagecontrol tabs f.FModeWhenCalled := AMode; f.FFieldName := AFieldName; // Init both editors f.InitFieldEditor (nil); f.InitIndexEditor (nil); Result := (f.ShowModal = mrOK); FreeAndNil (f); end; {*** Init Field-Editor } procedure TFieldEditForm.InitFieldEditor(Sender: TObject); var strtype : String; i : Integer; ListColumns : TVirtualStringTree; NodeData : PVTreeData; begin // Initiate "Position"-combobox ComboBoxPosition.Items.Clear; ComboBoxPosition.Items.Add('At End of Table'); ComboBoxPosition.Items.Add('At Beginning of Table'); // Reference to childwin's column-ListView ListColumns := Mainform.ChildWin.ListColumns; // get fieldlist // add fieldnames for i:=0 to High(Mainform.Childwin.VTRowDataListColumns) do begin ComboBoxPosition.Items.Add('AFTER ' + mainform.mask(Mainform.Childwin.VTRowDataListColumns[i].Captions[0])); end; // re-fill datatypes-combobox ComboBoxType.Items.Clear; for i := Low(MySqlDataTypeArray) to High(MySqlDataTypeArray) do begin ComboBoxType.Items.Add( MySqlDataTypeArray[i].Name ); end; CheckBoxAutoIncrement.Enabled := true; // Disable Comment editing on old servers. EditComment.Enabled := Mainform.Childwin.mysql_version >= 40100; lblComment.Enabled := EditComment.Enabled; case FMode of // "Field" tab in Add-mode femFieldAdd, femIndexEditor: begin CheckBoxAutoIncrement.Enabled := false; EditFieldName.Text := 'FieldName'; ComboBoxType.ItemIndex := 0; EditLength.Text := ''; EditDefault.Text := ''; EditComment.Text := ''; CheckBoxUnsigned.Checked := true; if Assigned(ListColumns.FocusedNode) then ComboBoxPosition.ItemIndex := ListColumns.FocusedNode.Index+2 else ComboBoxPosition.ItemIndex := 0; end; // "Field" tab in Update-mode femFieldUpdate: begin NodeData := ListColumns.GetNodeData(ListColumns.FocusedNode); EditFieldname.Text := FFieldName; EditLength.Text := getEnumValues( NodeData.Captions[1] ); EditDefault.Text := NodeData.Captions[3]; EditComment.Text := NodeData.Captions[5]; // extract field type strtype := UpperCase( getFirstWord( NodeData.Captions[1] ) ); // field field type structure for i := Low(MySqlDataTypeArray) to High(MySqlDataTypeArray) do begin if (strtype=MySqlDataTypeArray[i].Name) then begin if MySqlDataTypeArray[i].HasLength then begin // enable / disable length field // get default length .. end; ComboBoxType.ItemIndex := MySqlDataTypeArray[i].Index; Break; end; end; // set attributes: strtype := LowerCase( NodeData.Captions[1] ); CheckBoxBinary.Checked := pos('binary', strtype) > 0; CheckBoxZerofill.Checked := pos('zerofill', strtype) > 0; CheckBoxUnsigned.Checked := pos('unsigned', strtype) > 0; CheckBoxNotNull.Checked := lowercase(NodeData.Captions[2]) <> 'yes'; CheckBoxAutoIncrement.Checked := lowercase(NodeData.Captions[4]) = 'auto_increment'; // TODO: Disable 'auto increment' checkbox if field is not part of index or primary key. end; end; end; {*** Init Index-Editor @todo cleanup code, get rid of WITH statement } procedure TFieldEditForm.InitIndexEditor(Sender: TObject); var i : Integer; cwin : TMDIChild; ds: TDataSet; begin listColumnsUsed.Items.Clear; listColumnsAvailable.Items.Clear; setlength(klist, 0); TempKeys := TStringList.Create; cwin := Mainform.ChildWin; ds := cwin.GetResults( 'SHOW KEYS FROM ' + mainform.mask(cwin.SelectedTable) ); for i:=1 to ds.RecordCount do begin if TempKeys.IndexOf(ds.Fields[2].AsString) = -1 then begin TempKeys.Add(ds.Fields[2].AsString); setlength(klist, length(klist)+1); klist[length(klist)-1].Name := ds.Fields[2].AsString; klist[length(klist)-1].Columns := TStringList.Create; klist[length(klist)-1].Columns.Add(ds.Fields[4].AsString); klist[length(klist)-1].Modified := false; klist[length(klist)-1].Unique := (ds.Fields[1].AsString = '0'); if cwin.mysql_version < 40002 then klist[length(klist)-1].Fulltext := (ds.FieldByName('Comment').AsString = 'FULLTEXT') else klist[length(klist)-1].Fulltext := (ds.FieldByName('Index_type').AsString = 'FULLTEXT') end else klist[TempKeys.IndexOf(ds.Fields[2].AsString)].Columns.Add(ds.Fields[4].AsString); ds.Next; end; ds.Close; FreeAndNil(ds); listColumnsAvailable.Items := GetVTCaptions(cwin.ListColumns); showkeys(); end; {*** FormShow } procedure TFieldEditForm.FormShow(Sender: TObject); begin if fMode in [femFieldUpdate, femFieldAdd] then begin pc.ActivePage := tabField; EditFieldName.SetFocus(); end; if fMode in [femIndexEditor] then begin pc.ActivePage := tabIndexes; if Length(klist) > 0 then ComboBoxKeys.SetFocus(); end; ComboBoxTypeChange(self); ValidateControls; end; {*** User selected a new datatype for the selected field. Take care of limitations of different datatypes, toggle clickability of certain checkboxes } procedure TFieldEditForm.ComboBoxTypeChange(Sender: TObject); var FieldType : TMysqlDataTypeRecord; begin // Attributes // Detect column-type FieldType := MySqlDataTypeArray[ComboBoxType.ItemIndex]; // BINARY CheckBoxBinary.Enabled := FieldType.HasBinary; if not CheckBoxBinary.Enabled then CheckBoxBinary.Checked := false; // Ensure checkbox is not ticked // UNSIGNED CheckBoxUnsigned.Enabled := FieldType.HasUnsigned; if not CheckBoxUnsigned.Enabled then CheckBoxUnsigned.Checked := false; // Ensure checkbox is not ticked // ZEROFILL CheckBoxZerofill.Enabled := FieldType.HasZerofill; if not CheckBoxZerofill.Enabled then CheckBoxZerofill.Checked := false; // Ensure checkbox is not ticked // Length/Set EditLength.Enabled := FieldType.HasLength; lblLengthSet.Enabled := EditLength.Enabled; if FieldType.RequiresLength then // Render required field as bold lblLengthSet.Font.Style := lblLengthSet.Font.Style + [fsBold] else lblLengthSet.Font.Style := lblLengthSet.Font.Style - [fsBold]; if not EditLength.Enabled then EditLength.Text := ''; // Fill length/set value with default value if empty if FieldType.RequiresLength then begin if (EditLength.Text = '') and (FieldType.DefLengthSet <> '') then EditLength.Text := FieldType.DefLengthSet; end; // DEFAULT EditDefault.Enabled := FieldType.HasDefault; lblDefault.Enabled := EditDefault.Enabled; if not EditDefault.Enabled then EditDefault.Text := ''; // Ensure text is empty end; {*** Add or update field @todo code cleanup } procedure TFieldEditForm.AddUpdateField(Sender: TObject); var strNull, strAttributes, strAutoIncrement, strLengthSet, strDefault, strComment, strPosition, fielddef, sql_alterfield : String; cwin : TMDIChild; begin // Apply Changes to field-definition cwin := Mainform.ChildWin; // move field if position changed if (ComboBoxPosition.ItemIndex > -1) and (FMode in [femFieldUpdate]) and (cwin.mysql_version < 40001) then begin // Move field position if MessageDLG('You are about to move a field''s position in the table-structure. While there is no handy one-query-method in MySQL to do that, this will be done in 4 steps:'+CRLF+ ' 1. Adding a temporary field at the specified position'+CRLF+ ' 2. Filling the temporary field with the same data as source field'+CRLF+ ' 3. Dropping the source-field'+CRLF+ ' 4. Renaming the temporary field to it''s original name.'+CRLF+CRLF+ 'Be aware that this method can mess up existing indexes in your table or even can result in losing data! If you are not sure you should not use this function on indexed fields.'+CRLF+CRLF+ 'Continue?', mtConfirmation, [mbYes, mbCancel], 0 ) <> mrYes then Exit; end; Screen.Cursor := crSQLWait; try strAttributes := ''; // none of the 3 attributes binary, unsigned, zerofill strNull := ''; strDefault := ''; strAutoIncrement := ''; if CheckBoxBinary.Checked = true then strAttributes := strAttributes + ' BINARY'; if CheckBoxUnsigned.Checked = true then strAttributes := strAttributes + ' UNSIGNED'; if CheckBoxZerofill.Checked = true then strAttributes := strAttributes + ' ZEROFILL'; if (length(EditDefault.Text) > 0) and EditDefault.Enabled then begin strDefault := ' DEFAULT '; // Don't escape certain default values if (UpperCase(EditDefault.Text) = 'CURRENT_TIMESTAMP') or (UpperCase(EditDefault.Text) = 'NULL') then strDefault := strDefault + EditDefault.Text else strDefault := strDefault + esc(EditDefault.Text) end; if CheckBoxNotNull.Enabled then begin if CheckBoxNotNull.Checked then strNull := ' NOT'; strNull := strNull + ' NULL'; end; if CheckBoxAutoIncrement.Checked = True then strAutoIncrement := ' AUTO_INCREMENT'; if (EditLength.text <> '') and EditLength.Enabled then strLengthSet := '(' + EditLength.text + ') ' else strLengthSet := ''; if EditComment.Enabled and (length(EditComment.Text) > 0) then strComment := ' COMMENT ' + esc(EditComment.Text); strPosition := ''; case ComboBoxPosition.ItemIndex of 0 : ; 1 : strPosition := ' FIRST'; else strPosition := ' ' + ComboBoxPosition.Text; end; fielddef := ComboBoxType.Text + // Type strLengthSet + // Length/Set strAttributes + // Attribute strDefault + // Default strNull + // [NOT] Null strAutoIncrement + // Auto_increment strComment; // Comment if (FMode = femFieldAdd) then begin cwin.ExecUpdateQuery( 'ALTER TABLE ' + mainform.mask(cwin.SelectedTable) + ' ' + // table 'ADD ' + mainform.mask(EditFieldname.Text) + ' ' + // new name fielddef + strPosition // Position ); end else if (FMode = femFieldUpdate) then begin sql_alterfield := 'ALTER TABLE ' + mainform.mask(cwin.SelectedTable) + ' ' + 'CHANGE ' + mainform.mask(FFieldName) + ' ' + mainform.mask(EditFieldName.Text) + ' ' + fielddef; if cwin.mysql_version >= 40001 then begin // MySQL 4.0.1+ allows column moving in a ALTER TABLE statement. // @see http://dev.mysql.com/doc/refman/4.1/en/alter-table.html cwin.ExecUpdateQuery( sql_alterfield + strPosition ); end else begin // Use manual mechanism on older servers cwin.ExecUpdateQuery( sql_alterfield ); //ShowMessageFmt ('ComboBox position: %d',[ComboBoxPosition.ItemIndex]); if ComboBoxPosition.ItemIndex > -1 then begin // Move field position cwin.ExecUpdateQuery( 'ALTER TABLE ' + mainform.mask(cwin.SelectedTable) + ' ' + // table 'ADD ' + mainform.mask(TEMPFIELDNAME) + ' ' + // new name fielddef + strPosition // Position ); cwin.ExecUpdateQuery('UPDATE ' + mainform.mask(cwin.SelectedTable) + ' SET '+mainform.mask(TEMPFIELDNAME)+'='+mainform.mask(EditFieldName.Text)); cwin.ExecUpdateQuery('ALTER TABLE ' + mainform.mask(cwin.SelectedTable) + ' DROP '+mainform.mask(EditFieldName.Text)); cwin.ExecUpdateQuery( 'ALTER TABLE ' + mainform.mask(cwin.SelectedTable) + ' ' + 'CHANGE ' + mainform.mask(TEMPFIELDNAME) + ' ' + mainform.mask(EditFieldName.Text) + ' ' + fielddef ); end; end; end; cwin.ShowTableProperties(cwin.SelectedTable); ModalResult := mrOK; except on E: THandledSQLError do begin MessageDlg(E.Message, mtError, [mbOK], 0); ModalResult := mrNone; end; end; Screen.Cursor := crDefault; end; {*** Cancel Form } procedure TFieldEditForm.ButtonCancelClick(Sender: TObject); begin ModalResult := mrCancel; end; {*** User selected another tab in the main-pagecontrol } procedure TFieldEditForm.pcChange(Sender: TObject); begin // Set FMode, according to selected tab if pc.ActivePage = tabField then begin if FModeWhenCalled = femFieldUpdate then begin // "Field" tab selected and original mode was "UpdateField" FMode := femFieldUpdate; end else begin // "Field" tab selected and original mode was "AddField" FMode := femFieldAdd; end; end else if pc.ActivePage = tabIndexes then begin // "Index" tab selected FMode := femIndexEditor; end; ValidateControls; end; {*** OK clicked - call correct save-procedure according to current mode } procedure TFieldEditForm.OKClick(Sender: TObject); begin // add/update what? if fMode in [femFieldUpdate, femFieldAdd] then AddUpdateField(self) else if fMode in [femIndexEditor] then UpdateKeys(self); end; {*** User selected another index in the combobox @todo code cleanup, get rid of WITH statement } procedure TFieldEditForm.ComboBoxKeysChange(Sender: TObject); var i, j : Integer; begin if ComboBoxKeys.ItemIndex > -1 then begin listColumnsAvailable.Items := GetVTCaptions(Mainform.ChildWin.ListColumns); for i:=0 to klist[ComboBoxKeys.ItemIndex].columns.Count-1 do begin j := listColumnsAvailable.Items.IndexOf(klist[ComboBoxKeys.ItemIndex].columns[i]); if j > -1 then listColumnsAvailable.Items.Delete( j ); end; with klist[ComboBoxKeys.ItemIndex] do begin listColumnsUsed.Items := Columns; CheckBoxUnique.OnClick := nil; CheckBoxUnique.Checked := Unique; CheckBoxUnique.OnClick := CheckBoxUniqueClick; CheckBoxFulltext.OnClick := nil; CheckBoxFulltext.Checked := Fulltext; CheckBoxFulltext.OnClick := CheckBoxFulltextClick; end; end; ValidateControls; end; {*** Add new index, init that with default values } procedure TFieldEditForm.ButtonAddClick(Sender: TObject); var kname : String; begin kname := 'NewIndex'; if not InputQuery('New Index...', 'Index-Name:', kname) then exit; if ComboBoxKeys.Items.IndexOf(kname) > -1 then begin MessageDlg('Index-Name '''+kname+''' already used!', mtError, [mbOk], 0); exit; end; setlength(klist, length(klist)+1); klist[length(klist)-1].Name := kname; klist[length(klist)-1].Columns := TStringList.Create; klist[length(klist)-1].Unique := false; klist[length(klist)-1].Fulltext := false; klist[length(klist)-1].Modified := true; showkeys(length(klist)-1); end; {*** Delete existing index } procedure TFieldEditForm.ButtonDeleteClick(Sender: TObject); var i,j : Integer; begin i := ComboBoxKeys.ItemIndex; if i > -1 then if MessageDlg('Delete Index ''' + ComboBoxKeys.Items[ComboBoxKeys.ItemIndex] + ''' ?', mtConfirmation, [mbYes,mbCancel], 0) = mrYes then begin inc(i); // jump to next entry after the one to delete! for j:=i to length(klist)-1 do klist[j-1] := klist[j]; setlength(klist, length(klist)-1); ShowKeys(i-2); end; end; {*** Add primary key } procedure TFieldEditForm.ButtonAddPrimaryClick(Sender: TObject); begin setlength(klist, length(klist)+1); klist[length(klist)-1].Name := 'PRIMARY'; klist[length(klist)-1].Columns := TStringList.Create; klist[length(klist)-1].Unique := false; klist[length(klist)-1].Fulltext := false; klist[length(klist)-1].Modified := true; ShowKeys(length(klist)-1); ButtonAddPrimary.Enabled := false; end; {*** Show indexes in combobox @param integer ItemIndex to select } procedure TFieldEditForm.ShowKeys(index: Integer=0); var i, icon : Integer; begin ComboBoxKeys.ItemsEx.Clear; ButtonAddPrimary.Enabled := true; ButtonDelete.Enabled := false; listColumnsUsed.Enabled := false; listColumnsAvailable.Enabled := false; for i:=0 to length(klist)-1 do begin if (klist[i].Unique) and (klist[i].Name <> 'PRIMARY') then icon := ICONINDEX_UNIQUEKEY else if klist[i].Fulltext then icon := ICONINDEX_FULLTEXTKEY else if klist[i].Name = 'PRIMARY' then icon := ICONINDEX_PRIMARYKEY else icon := ICONINDEX_INDEXKEY; ComboBoxKeys.ItemsEx.AddItem( klist[i].Name, icon, icon, -1, 0, nil); end; if ComboBoxKeys.Items.IndexOf('PRIMARY') > -1 then ButtonAddPrimary.Enabled := false; if (index = -1) and (length(klist) > 0) then index := 0; if index < length(klist) then // careful: only if given index is < length(klist) ComboBoxKeys.ItemIndex := index; with ComboBoxKeys do if Items.Count = 0 then begin Enabled := false; Color := clBtnFace; end else begin Enabled := true; Color := clWindow; end; ComboBoxKeys.OnChange(self); end; {*** Make index unique! } procedure TFieldEditForm.CheckBoxUniqueClick(Sender: TObject); var idx : Integer; begin idx := ComboBoxKeys.ItemIndex; if idx = -1 then Exit; klist[idx].Unique := CheckBoxUnique.Checked; if CheckBoxUnique.Checked then begin klist[idx].Fulltext := false; CheckBoxFulltext.Checked := false; ComboBoxKeys.ItemsEx[idx].ImageIndex := ICONINDEX_UNIQUEKEY; end else begin // Not unique, not fulltext ComboBoxKeys.ItemsEx[idx].ImageIndex := ICONINDEX_INDEXKEY; end; klist[idx].Modified := true; end; {*** Make index fulltext! } procedure TFieldEditForm.CheckBoxFulltextClick(Sender: TObject); var idx : Integer; begin idx := ComboBoxKeys.ItemIndex; if idx = -1 then Exit; klist[idx].Fulltext := CheckBoxFulltext.Checked; if CheckBoxFulltext.Checked then begin klist[idx].Unique := false; CheckBoxUnique.Checked := false; ComboBoxKeys.ItemsEx[idx].ImageIndex := ICONINDEX_FULLTEXTKEY; end else begin // Not unique, not fulltext ComboBoxKeys.ItemsEx[idx].ImageIndex := ICONINDEX_INDEXKEY; end; klist[idx].Modified := true; end; {*** update keys! for each TempKey (see OnFormShow) check if it was changed or even deleted in klist @todo code cleanup, get rid of WITH statement } procedure TFieldEditForm.UpdateKeys(Sender: TObject); var i,j,k, index : Integer; query : String; columns_sql : String; modifiedKeys : Array of Integer; begin query := ''; for i:=0 to TempKeys.Count-1 do begin index := -1; for j:=0 to length(klist)-1 do begin if TempKeys[i] = klist[j].Name then begin index := j; break; end; end; if (index > -1) and (klist[index].Modified) then begin // modify existing key // Remember which keys were modified for the case of an SQL error // so we can switch them back to have .Modified := True SetLength( modifiedKeys, Length(modifiedKeys)+1 ); modifiedKeys[Length(modifiedKeys)-1] := index; // Prepare columns-list columns_sql := ''; for k := 0 to klist[index].Columns.Count - 1 do begin columns_sql := columns_sql + mainform.mask( klist[index].Columns[k] ); if k < klist[index].Columns.Count-1 then columns_sql := columns_sql + ', '; end; // PK: if klist[index].Name = 'PRIMARY' then query := query + 'DROP PRIMARY KEY' + ', ADD PRIMARY KEY (' + columns_sql + '), ' // UNIQUE: else if klist[index].Unique then query := query + 'DROP INDEX ' + mainform.mask(klist[index].Name) + ', ADD UNIQUE ' + mainform.mask(klist[index].Name) + ' (' + columns_sql + '), ' // FULLTEXT: else if klist[index].Fulltext then query := query + 'DROP INDEX ' + mainform.mask(klist[index].Name) + ', ADD FULLTEXT ' + mainform.mask(klist[index].Name) + ' (' + columns_sql + '), ' // INDEX: else query := query + 'DROP INDEX '+ mainform.mask(klist[index].Name) + ', ADD INDEX ' + mainform.mask(klist[index].Name) + ' (' + columns_sql + '), '; klist[index].Modified := false; end else if index = -1 then begin // delete existing key if TempKeys[i] = 'PRIMARY' then query := query + 'DROP PRIMARY KEY, ' else query := query + 'DROP INDEX ' + mainform.mask(TempKeys[i]) + ', '; end; if index > -1 then klist[index].Ready := true; end; for j:=0 to length(klist)-1 do begin if (not klist[j].Ready) then begin // Add a new key // Prepare columns-list columns_sql := ''; for k := 0 to klist[j].Columns.Count - 1 do begin columns_sql := columns_sql + mainform.mask( klist[j].Columns[k] ); if k < klist[j].Columns.Count-1 then columns_sql := columns_sql + ', '; end; // PK: if klist[j].Name = 'PRIMARY' then query := query + 'ADD PRIMARY KEY (' + columns_sql + '), ' // UNIQUE: else if klist[j].Unique then query := query + 'ADD UNIQUE ' + mainform.mask(klist[j].Name) + ' (' + columns_sql + '), ' // UNIQUE: else if klist[j].Fulltext then query := query + 'ADD FULLTEXT ' + mainform.mask(klist[j].Name) + ' (' + columns_sql + '), ' // INDEX: else query := query + 'ADD INDEX '+ mainform.mask(klist[j].Name) + ' (' + columns_sql + '), '; end; end; if query <> '' then begin // Remove trailing comma delete( query, Length(query)-1, 2 ); query := 'ALTER TABLE ' + mainform.mask(Mainform.ChildWin.SelectedTable) + ' ' + query; try // Send query Mainform.ChildWin.ExecUpdateQuery(query); // Refresh listColumns to display correct field-icons Mainform.ChildWin.MenuRefreshClick(Self); ModalResult := mrOK; except On E : Exception do begin MessageDlg( E.Message, mtError, [mbOK], 0 ); // Ensure modified and new indexes will be processed next time the user clicks OK for i := 0 to Length(klist) - 1 do klist[i].Ready := False; for i := 0 to Length(modifiedKeys) - 1 do klist[modifiedKeys[i]].Modified := True; end; end; end; end; {*** Add column to index } procedure TFieldEditForm.AddField(Sender: TObject); var idx : Integer; item: string; begin if listColumnsAvailable.ItemIndex > -1 then begin idx := listColumnsAvailable.ItemIndex; item := listColumnsAvailable.Items[idx]; listColumnsUsed.Items.Add(item); klist[ComboBoxKeys.ItemIndex].Columns.Add(item); listColumnsAvailable.Items.Delete(idx); klist[ComboBoxKeys.ItemIndex].Modified := true; // Highlight previous item to added one. listColumnsAvailable.ItemIndex := Min(Max(idx - 1, 0), listColumnsAvailable.Items.Count - 1); end; ValidateControls; end; {*** Add all columns to index } procedure TFieldEditForm.btnAddAllColumnsToIndexClick(Sender: TObject); begin listColumnsUsed.Items.AddStrings(listColumnsAvailable.Items); klist[ComboBoxKeys.ItemIndex].Columns.AddStrings(listColumnsAvailable.Items); listColumnsAvailable.Items.Clear; klist[ComboBoxKeys.ItemIndex].Modified := true; ValidateControls; end; {*** Delete column from index } procedure TFieldEditForm.RemoveField(Sender: TObject); var idx : Integer; item: string; begin if listColumnsUsed.ItemIndex > -1 then begin idx := listColumnsUsed.ItemIndex; item := listColumnsUsed.Items[idx]; klist[ComboBoxKeys.ItemIndex].Columns.Delete(idx); klist[ComboBoxKeys.ItemIndex].Modified := true; listColumnsAvailable.Items.Add(item); listColumnsUsed.Items.Delete(idx); // Highlight previous item to removed one. listColumnsUsed.ItemIndex := Min(Max(idx - 1, 0), listColumnsUsed.Items.Count - 1); end; ValidateControls; end; {*** Delete all columns from index } procedure TFieldEditForm.btnDeleteAllColumnsFromIndexClick(Sender: TObject); begin klist[ComboBoxKeys.ItemIndex].Columns.Clear; klist[ComboBoxKeys.ItemIndex].Modified := true; listColumnsAvailable.Items.AddStrings(listColumnsUsed.Items); listColumnsUsed.Items.Clear; ValidateControls; end; {*** Call SQL help for selected datatype } procedure TFieldEditForm.btnDatatypeHelpClick(Sender: TObject); begin Mainform.ChildWin.CallSQLHelpWithKeyword(ComboBoxType.Text); end; {*** Ensure correct state of various controls } procedure TFieldEditForm.ValidateControls; var KeySelected : Boolean; begin ButtonOK.Enabled := true; case FMode of femFieldUpdate: begin ButtonOK.Caption := 'Update Field'; end; femFieldAdd: begin ButtonOK.Caption := 'Add Field'; end; femIndexEditor: begin ButtonOK.Caption := 'Update Indexes'; KeySelected := ComboBoxKeys.ItemIndex > -1; // Disable the button if // 1) a key was selected and no columns are listed on the left // or // 2) all existing keys were deleted ButtonOK.Enabled := (KeySelected and (listColumnsUsed.Items.Count > 0)) or (Length(klist) < TempKeys.Count); // Buttons to add or remove columns, only enabled if key was selected and the // relevant list has items left btnAddColumnToIndex.Enabled := KeySelected and (listColumnsAvailable.ItemIndex > -1); btnAddAllColumnsToIndex.Enabled := KeySelected and (listColumnsAvailable.Items.Count > 0); btnDeleteColumnFromIndex.Enabled := KeySelected and (listColumnsUsed.ItemIndex > -1); btnDeleteAllColumnsFromIndex.Enabled := KeySelected and (listColumnsUsed.Items.Count > 0); CheckBoxUnique.Enabled := KeySelected and not (ComboBoxKeys.ItemsEx[ComboBoxKeys.ItemIndex].Caption = 'PRIMARY'); CheckBoxFulltext.Enabled := KeySelected and not (ComboBoxKeys.ItemsEx[ComboBoxKeys.ItemIndex].Caption = 'PRIMARY'); ButtonDelete.Enabled := KeySelected; listColumnsUsed.Enabled := KeySelected; listColumnsAvailable.Enabled := KeySelected; end; end; Caption := Mainform.ChildWin.Description + ' - ' + ButtonOK.Caption; end; {** Call ValidateControls if user selects an item in one of the lists } procedure TFieldEditForm.listClick(Sender: TObject); begin ValidateControls; end; {** "Unsigned" option was (un)checked Ensure "Zerofill" gets unchecked too } procedure TFieldEditForm.CheckBoxUnsignedClick(Sender: TObject); begin if not CheckboxUnsigned.Checked then CheckboxZerofill.Checked := False; end; {** "Zerofill" option was (un)checked. Ensure "Unsigned" is checked too } procedure TFieldEditForm.CheckBoxZerofillClick(Sender: TObject); begin if CheckBoxZerofill.Checked then CheckboxUnsigned.Checked := True; end; end.