From cc84963b250bbf3ae916bf1f4e5d342864dbdfa4 Mon Sep 17 00:00:00 2001 From: Ansgar Becker Date: Thu, 12 Jul 2007 22:33:59 +0000 Subject: [PATCH] Implement a dialog for easier accessing the options for ordered columns. This has one positive side effect: ViewData no longer re-saves an ORDERCLAUSE to registry after it has been read. At the same time the ORDERCLAUSE is read everytime when grid is filled, not only the first time. Those changes were needed to be able to apply an ORDERCLAUSE which was generated and saved by the new dialog. Left for later: Moving order-columns up and down. Tested that with a TUpDown which seems to ignore clicks sometimes and then doesn't fire OnClick. --- source/childwin.dfm | 30 +++- source/childwin.pas | 234 ++++++++++++++++----------- source/data_sorting.dfm | 64 ++++++++ source/data_sorting.pas | 350 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 578 insertions(+), 100 deletions(-) create mode 100644 source/data_sorting.dfm create mode 100644 source/data_sorting.pas diff --git a/source/childwin.dfm b/source/childwin.dfm index 6b045722..678a2d87 100644 --- a/source/childwin.dfm +++ b/source/childwin.dfm @@ -623,9 +623,9 @@ object MDIChild: TMDIChild 496 31) object btnColumnSelection: TSpeedButton - Left = 168 + Left = 187 Top = 4 - Width = 79 + Width = 70 Height = 21 AllowAllUp = True Anchors = [akTop, akRight] @@ -642,12 +642,12 @@ object MDIChild: TMDIChild FF00F7806300E3805C00C18032008080300000004700} Layout = blGlyphRight ParentFont = False - OnClick = btnColumnSelectionClick + OnClick = btnDataClick end object lblDataTop: TLabel Left = 1 Top = 1 - Width = 161 + Width = 104 Height = 29 Align = alLeft Anchors = [akLeft, akTop, akRight, akBottom] @@ -656,6 +656,28 @@ object MDIChild: TMDIChild Layout = tlCenter WordWrap = True end + object btnDataSorting: TSpeedButton + Left = 111 + Top = 4 + Width = 70 + Height = 21 + AllowAllUp = True + Anchors = [akTop, akRight] + GroupIndex = 10 + Caption = 'Sorting' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlack + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + Glyph.Data = { + 52000000424D52000000000000003E0000002800000009000000050000000100 + 010000000000140000000000000000000000020000000200000000000000FFFF + FF00F7806300E3805C00C18032008080300000004700} + Layout = blGlyphRight + ParentFont = False + OnClick = btnDataClick + end object EditDataSearch: TEdit Left = 317 Top = 4 diff --git a/source/childwin.pas b/source/childwin.pas index 20d36f01..edae1a98 100644 --- a/source/childwin.pas +++ b/source/childwin.pas @@ -285,6 +285,7 @@ type MenuReplace: TMenuItem; MenuItem2: TMenuItem; lblDataTop: TLabel; + btnDataSorting: TSpeedButton; procedure menuclearClick(Sender: TObject); procedure popupQueryPopup(Sender: TObject); procedure lboxQueryHelpersClick(Sender: TObject); @@ -293,7 +294,7 @@ type var AllowChange: Boolean); procedure btnTableViewDataClick(Sender: TObject); procedure btnDbViewDataClick(Sender: TObject); - procedure btnColumnSelectionClick(Sender: TObject); + procedure btnDataClick(Sender: TObject); procedure DBtreeExpanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean); procedure ListTablesSelectItem(Sender: TObject; Item: TListItem; @@ -546,7 +547,7 @@ implementation uses Main, createtable, fieldeditor, tbl_properties, tblcomment, optimizetables, copytable, sqlhelp, printlist, - column_selection, mysql; + column_selection, data_sorting, mysql; {$I const.inc} @@ -1356,6 +1357,7 @@ var manualLimitEnd : integer; DisplayedColumnsList : TStringList; tmp : TDataSet; + RewriteOrderClause : Boolean; begin viewingdata := true; try @@ -1411,7 +1413,6 @@ begin if ( not dataselected ) then begin SynMemoFilter.Text := ''; - gridData.SortColumns.Clear(); // Read cached WHERE-clause and set filter reg_value := 'WHERECLAUSE_' + ActualDatabase + '.' + ActualTable; if ( reg.ValueExists( reg_value ) ) then @@ -1422,87 +1423,73 @@ begin tabFilter.tabVisible := true; PageControlBottom.ActivePage := tabFilter; end; - // Read cached ORDER-clause and set Grid.Sortcolumns - reg_value := 'ORDERCLAUSE_' + ActualDatabase + '.' + ActualTable; - if ( reg.ValueExists( reg_value ) ) then - begin - orderclauses := explode( ',', reg.ReadString( reg_value ) ); - for i := 0 to ( orderclauses.Count - 1 ) do - begin - columnname := Trim( Copy( orderclauses[i], 0, Pos( ' ', orderclauses[i] ) ) ); - columnname := trimc( columnname, '`' ); - columnexists := false; - - for j := 0 to ( ListColumns.Items.Count - 1 ) do - begin - if ( ListColumns.Items[j].Caption = columnname ) then - begin - columnexists := true; - Break; - end; - end; - - if ( not columnexists ) then - begin - LogSQL( 'Notice: A stored ORDER-BY clause could not be applied, '+ - 'because the column "' + columnname + '" does not exist!'); - Continue; - end; - - with ( gridData.SortColumns.Add() ) do - begin - Fieldname := columnname; - if ( Copy( orderclauses[i], ( Length( orderclauses[i] ) - 3 ), 4 ) = 'DESC' ) then - begin - SortType := stAscending; - end - else - begin - SortType := stDescending; - end; - end; - end; - end; - end; - - sorting := ''; - for i := 0 to ( gridData.SortColumns.Count - 1 ) do - begin - with ( gridData.SortColumns[i] ) do - begin - if ( SortType <> stNone ) then - begin - if ( sorting <> '' ) then - begin - sorting := sorting + ', '; - end; - sorting := sorting + mask( FieldName ); - end; - - case SortType of - // stNone: ; - stAscending : - begin - sorting := sorting + ' DESC'; - end; - stDescending: - begin - sorting := sorting + ' ASC'; - end; - end; - end; end; + // Read cached ORDER-clause and set Grid.Sortcolumns reg_value := 'ORDERCLAUSE_' + ActualDatabase + '.' + ActualTable; - if ( sorting <> '' ) then - begin - reg.WriteString( reg_value, sorting ); - sorting := 'ORDER BY ' + sorting; - end - else + sorting := ''; + gridData.SortColumns.Clear(); if ( reg.ValueExists( reg_value ) ) then begin - reg.DeleteValue( reg_value ); + orderclauses := explode( ',', reg.ReadString( reg_value ) ); + RewriteOrderClause := False; + for i := 0 to ( orderclauses.Count - 1 ) do + begin + columnname := Trim( Copy( orderclauses[i], 0, Pos( ' ', orderclauses[i] ) ) ); + columnname := trimc( columnname, '`' ); + columnexists := false; + + for j := 0 to ( ListColumns.Items.Count - 1 ) do + begin + if ( ListColumns.Items[j].Caption = columnname ) then + begin + columnexists := true; + Break; + end; + end; + + if ( not columnexists ) then + begin + LogSQL( 'Notice: A stored ORDER-BY clause could not be applied, '+ + 'because the column "' + columnname + '" does not exist!'); + RewriteOrderClause := True; + Continue; + end; + + // Add column to order clause + if sorting <> '' then + sorting := sorting + ', '; + sorting := sorting + orderclauses[i]; + + with ( gridData.SortColumns.Add() ) do + begin + Fieldname := columnname; + if ( Copy( orderclauses[i], ( Length( orderclauses[i] ) - 3 ), 4 ) = 'DESC' ) then + begin + SortType := stAscending; + end + else + begin + SortType := stDescending; + end; + end; + end; + // Old orderclause contained a no longer existing column, so overwrite it + if RewriteOrderClause then + begin + reg.WriteString( reg_value, sorting ); + end; + end; + + if ( sorting <> '' ) then + begin + sorting := 'ORDER BY ' + sorting; + // Signal for the user that we applied an ORDER-clause + btnDataSorting.Font.Color := clRed; + end + else + begin + btnDataSorting.Font.Color := clWindowText; end; MenuLimit.Checked := Mainform.CheckBoxLimit.Checked; @@ -3590,6 +3577,9 @@ var Grid : TSMDBGrid; i : Integer; existed : Boolean; + sortcol : TSMSortColumn; + sorting, reg_value : String; + reg : TRegistry; begin // column-title clicked -> generate "ORDER BY" @@ -3599,28 +3589,72 @@ begin for i:=Grid.SortColumns.Count-1 downto 0 do begin - with Grid.SortColumns[i] do - begin - if FieldName <> column.FieldName then - continue; - existed := true; - case SortType of - stDescending : SortType := stAscending; - stAscending : Grid.SortColumns.Delete(i); - stNone : SortType := stDescending; - end; + sortcol := Grid.SortColumns[i]; + if sortcol.FieldName <> Column.FieldName then + continue; + existed := true; + case sortcol.SortType of + stDescending : sortcol.SortType := stAscending; + stAscending : Grid.SortColumns.Delete(i); + stNone : sortcol.SortType := stDescending; end; end; {add a new sorted column in list - ascending order} - if not existed then with Grid.SortColumns.Add do + if not existed then begin - FieldName := column.FieldName; - SortType := stDescending + sortcol := Grid.SortColumns.Add; + sortcol.FieldName := column.FieldName; + sortcol.SortType := stDescending; end; Grid.DataSource.DataSet.EnableControls; - viewdata(self); + + // Concat orderclause + sorting := ''; + for i := 0 to ( gridData.SortColumns.Count - 1 ) do + begin + sortcol := gridData.SortColumns[i]; + + if ( sortcol.SortType <> stNone ) then + begin + if ( sorting <> '' ) then + begin + sorting := sorting + ', '; + end; + sorting := sorting + mask( sortcol.FieldName ); + end; + + case sortcol.SortType of + // stNone: ; + stAscending : + begin + sorting := sorting + ' DESC'; + end; + stDescending: + begin + sorting := sorting + ' ASC'; + end; + end; + end; + + // Write orderclause to registry + reg := TRegistry.Create(); + reg.OpenKey( REGPATH + '\Servers\' + FConn.Description, true ); + reg_value := 'ORDERCLAUSE_' + ActualDatabase + '.' + ActualTable; + if ( sorting <> '' ) then + begin + reg.WriteString( reg_value, sorting ); + end + else + begin + if ( reg.ValueExists( reg_value ) ) then + reg.DeleteValue( reg_value ); + end; + reg.CloseKey; + + ViewData(self); + end; @@ -5489,16 +5523,22 @@ end; {** Column selection for datagrid } -procedure TMDIChild.btnColumnSelectionClick(Sender: TObject); +procedure TMDIChild.btnDataClick(Sender: TObject); var btn : TSpeedButton; - frm : TColumnSelectionForm; + frm : TForm; begin btn := (Sender as TSpeedButton); if btn.Down then begin - frm := TColumnSelectionForm.Create(self); + // Create the desired form + if btn = btnColumnSelection then + frm := TColumnSelectionForm.Create(self) + else if btn = btnDataSorting then + frm := TDataSortingForm.Create(self) + else + frm := TForm.Create(self); // Dummy fallback, should never get created // Position new form relative to btn's position frm.Top := btn.ClientOrigin.Y + btn.Height; @@ -5506,6 +5546,8 @@ begin // Display form frm.ShowModal; + + btn.Down := False; end; end; diff --git a/source/data_sorting.dfm b/source/data_sorting.dfm new file mode 100644 index 00000000..51a985b0 --- /dev/null +++ b/source/data_sorting.dfm @@ -0,0 +1,64 @@ +object DataSortingForm: TDataSortingForm + Left = 0 + Top = 0 + BorderStyle = bsNone + Caption = 'DataSortingForm' + ClientHeight = 63 + ClientWidth = 204 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + OnShow = FormShow + PixelsPerInch = 96 + TextHeight = 13 + object pnlBevel: TPanel + Left = 0 + Top = 0 + Width = 204 + Height = 63 + Align = alClient + BorderWidth = 3 + TabOrder = 0 + DesignSize = ( + 204 + 63) + object btnOK: TButton + Left = 3 + Top = 34 + Width = 60 + Height = 25 + Anchors = [akLeft, akBottom] + Caption = 'OK' + Default = True + Enabled = False + ModalResult = 1 + TabOrder = 0 + OnClick = btnOKClick + end + object btnCancel: TButton + Left = 68 + Top = 34 + Width = 60 + Height = 25 + Anchors = [akLeft, akBottom] + Cancel = True + Caption = 'Cancel' + ModalResult = 2 + TabOrder = 1 + end + object btnAddCol: TButton + Left = 134 + Top = 34 + Width = 60 + Height = 25 + Anchors = [akLeft, akBottom] + Caption = 'Add Col' + TabOrder = 2 + OnClick = btnAddColClick + end + end +end diff --git a/source/data_sorting.pas b/source/data_sorting.pas new file mode 100644 index 00000000..493c4715 --- /dev/null +++ b/source/data_sorting.pas @@ -0,0 +1,350 @@ +unit data_sorting; + +interface + +uses + Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, + Dialogs, StdCtrls, ExtCtrls, ComCtrls, Buttons, Registry, smdbgrid; + +type + TOrderCol = record + ColumnName: String; + SortDirection: Byte; + end; + + +type + TDataSortingForm = class(TForm) + pnlBevel: TPanel; + btnOK: TButton; + btnCancel: TButton; + btnAddCol: TButton; + procedure btnAddColClick(Sender: TObject); + procedure btnOKClick(Sender: TObject); + procedure FormShow(Sender: TObject); + private + { Private declarations } + ColumnNames : TStringList; + OrderColumns : Array of TOrderCol; + procedure DisplaySortingControls; + procedure dropdownColsChange( Sender: TObject ); + procedure buttonOrderClick( Sender: TObject ); + procedure buttonDeleteClick( Sender: TObject ); + procedure Modified; + public + { Public declarations } + end; + + +const + ORDER_ASC = 0; // Used for tag-value of "Direction"-button + ORDER_DESC = 1; // dito + TXT_ASC = 'ASC'; // Used for caption of "Direction"-button + TXT_DESC = 'DESC'; // dito + LINE_HEIGHT = 20; // Height of automatically created controls + MARGIN = 2; // Space between controls + MARGIN_BIG = 3 * MARGIN; // Space above the very first and last controls, used to separate stuff + +implementation + +uses main; + +{$R *.dfm} + + +{** + Initialization +} +procedure TDataSortingForm.FormShow(Sender: TObject); +var + i : Cardinal; + grid: TSMDBGrid; +begin + // Take column names from listColumns and add here + ColumnNames := TStringList.Create; + for i := 0 to Mainform.Childwin.listColumns.Items.Count-1 do + begin + ColumnNames.Add( Mainform.Childwin.listColumns.Items[i].Caption ); + end; + + // Create one OrderColumns record for each grid.SortColumn + grid := Mainform.Childwin.gridData; + if grid.SortColumns.Count > 0 then // Avoids AV when accessing SortColumn[0] ... ?? + begin + for i := 0 to grid.SortColumns.Count - 1 do + begin + SetLength( OrderColumns, i+1 ); + OrderColumns[i].ColumnName := grid.SortColumns[i].FieldName; + if grid.SortColumns[i].SortType = stDescending then + OrderColumns[i].SortDirection := ORDER_ASC + else + OrderColumns[i].SortDirection := ORDER_DESC; // Also applies for SortType = stNone + end; + end; + + // First creation of controls + DisplaySortingControls; + +end; + + +{** + Create controls for order columns +} +procedure TDataSortingForm.DisplaySortingControls; +var + labelNumber: TLabel; + buttonDelete: TButton; + dropdownCols: TComboBox; + buttonOrder: TSpeedButton; + i, xPosition, topPosition, btnWidth : Integer; +begin + + // Remove previously created components + for i := ComponentCount - 1 downto 0 do + begin + if Components[i].Tag > 0 then + Components[i].Free; + end; + + // Set initial width to avoid resizing form to 0 + xPosition := pnlBevel.Width; + + + // Create line with controls for each order column + for i:=0 to Length(OrderColumns)-1 do + begin + xPosition := pnlBevel.BorderWidth; + topPosition := pnlBevel.BorderWidth + MARGIN_BIG + (i * (LINE_HEIGHT + MARGIN)); + + // 1. Label with number + labelNumber := TLabel.Create(self); + labelNumber.Parent := pnlBevel; + labelNumber.AutoSize := False; // Avoids automatic changes to width + height + labelNumber.Left := xPosition; + labelNumber.Top := topPosition; + labelNumber.Width := 15; + labelNumber.Height := LINE_HEIGHT; + labelNumber.Alignment := taRightJustify; + labelNumber.Layout := tlCenter; + labelNumber.Caption := IntToStr(i+1) + '.'; + labelNumber.Tag := i+1; + Inc( xPosition, labelNumber.Width + MARGIN ); + + // 2. Dropdown with columnnames + dropdownCols := TComboBox.Create(self); + dropdownCols.Parent := pnlBevel; + dropdownCols.Width := 120; + dropdownCols.Height := LINE_HEIGHT; + dropdownCols.Left := xPosition; + dropdownCols.Top := topPosition; + dropdownCols.Items := ColumnNames; + dropdownCols.Style := csDropDownList; // Not editable + dropdownCols.ItemIndex := ColumnNames.IndexOf(OrderColumns[i].ColumnName); + dropdownCols.Tag := i+1; + dropdownCols.OnChange := dropdownColsChange; + Inc( xPosition, dropdownCols.Width + MARGIN ); + + // 3. A button for selecting ASC/DESC + buttonOrder := TSpeedButton.Create(self); + buttonOrder.Parent := pnlBevel; + buttonOrder.Width := 35; + buttonOrder.Height := LINE_HEIGHT; + buttonOrder.Left := xPosition; + buttonOrder.Top := topPosition; + buttonOrder.AllowAllUp := True; // Enables Down = False + buttonOrder.GroupIndex := i+1; // if > 0 enables Down = True + buttonOrder.Caption := TXT_ASC; + if OrderColumns[i].SortDirection = ORDER_DESC then + begin + buttonOrder.Caption := TXT_DESC; + buttonOrder.Down := True; + end; + buttonOrder.Hint := 'Toggle the sort direction for this column.'; + buttonOrder.Tag := i+1; + buttonOrder.OnClick := buttonOrderClick; + Inc( xPosition, buttonOrder.Width + MARGIN ); + + // 4. Button for deleting + buttonDelete := TButton.Create(self); + buttonDelete.Parent := pnlBevel; + buttonDelete.Width := 20; + buttonDelete.Height := LINE_HEIGHT; + buttonDelete.Left := xPosition; + buttonDelete.Top := topPosition; + buttonDelete.Caption := 'X'; + buttonDelete.Hint := 'Drops sorting by this column.'; + buttonDelete.Tag := i+1; + buttonDelete.OnClick := buttonDeleteClick; + Inc( xPosition, buttonDelete.Width + MARGIN ); + + end; + + // Auto-adjust size of form + Height := (pnlBevel.BorderWidth * 2) + // Top + Bottom border + (MARGIN_BIG * 2) + // Separator spaces + (Length(OrderColumns) * (LINE_HEIGHT + MARGIN)) + // Height of created controls + (btnOK.Height + MARGIN); // Height of buttons + Width := xPosition + pnlBevel.BorderWidth; + + // Auto-adjust width and position of main buttons at bottom + btnWidth := (pnlBevel.Width -pnlBevel.BorderWidth*2 - MARGIN) DIV 3 - MARGIN; + btnOK.Width := btnWidth; + btnOK.Top := Height - pnlBevel.BorderWidth - MARGIN - btnOK.Height; + btnOK.Left := pnlBevel.BorderWidth + MARGIN; + btnCancel.Width := btnWidth; + btnCancel.Left := btnOK.Left + btnWidth + MARGIN; + btnAddCol.Width := btnWidth; + btnAddCol.Left := btnCancel.Left + btnWidth + MARGIN; +end; + + +{** + Dropdown for column selection was changed +} +procedure TDataSortingForm.dropdownColsChange( Sender: TObject ); +var + combo : TComboBox; +begin + combo := Sender as TComboBox; + OrderColumns[combo.Tag-1].ColumnName := combo.Text; + + // Enables OK button + Modified; +end; + + +{** + Button for selecting sort-direction was clicked +} +procedure TDataSortingForm.buttonOrderClick( Sender: TObject ); +var + btn : TSpeedButton; +begin + btn := Sender as TSpeedButton; + if btn.Down then + begin + btn.Caption := TXT_DESC; + OrderColumns[btn.Tag-1].SortDirection := ORDER_DESC; + end + else + begin + btn.Caption := TXT_ASC; + OrderColumns[btn.Tag-1].SortDirection := ORDER_ASC; + end; + + // Enables OK button + Modified; +end; + + +{** + Delete order column +} +procedure TDataSortingForm.buttonDeleteClick( Sender: TObject ); +var + btn : TButton; + i : Integer; +begin + btn := Sender as TButton; + + if Length(OrderColumns)>1 then + begin + // Move remaining items one up + for i := btn.Tag-1 to Length(OrderColumns) - 2 do + begin + OrderColumns[i] := OrderColumns[i+1]; + end; + end; + // Delete last item + SetLength(OrderColumns, Length(OrderColumns)-1); + + // Refresh controls + DisplaySortingControls; + + // Enables OK button + Modified; +end; + + +{** + Add a new order column +} +procedure TDataSortingForm.btnAddColClick(Sender: TObject); +var + i, new : Integer; + UnusedColumns : TStringList; +begin + SetLength( OrderColumns, Length(OrderColumns)+1 ); + new := Length(OrderColumns)-1; + + // Take first unused column as default for new sort column + UnusedColumns := TStringList.Create; + UnusedColumns.AddStrings( ColumnNames ); + for i := 0 to Length(OrderColumns) - 1 do + begin + if UnusedColumns.IndexOf(OrderColumns[i].ColumnName) > -1 then + begin + UnusedColumns.Delete( UnusedColumns.IndexOf(OrderColumns[i].ColumnName) ); + end; + end; + if UnusedColumns.Count > 0 then + OrderColumns[new].ColumnName := UnusedColumns[0] + else + OrderColumns[new].ColumnName := ColumnNames[0]; + + // Sort ASC by default + OrderColumns[new].SortDirection := ORDER_ASC; + + // Refresh controls + DisplaySortingControls; + + // Enables OK button + Modified; +end; + + +{** + Gets called when any option has changed. Enables the OK button. +} +procedure TDataSortingForm.Modified; +begin + btnOk.Enabled := True; +end; + + +{** + OK clicked +} +procedure TDataSortingForm.btnOKClick(Sender: TObject); +var + reg : TRegistry; + reg_name, reg_value, sort : String; + i : Integer; +begin + // Concat all sort options to a ORDER clause + reg_value := ''; + for i := 0 to Length(OrderColumns) - 1 do + begin + if reg_value <> '' then + reg_value := reg_value + ', '; + if OrderColumns[i].SortDirection = ORDER_ASC then + sort := TXT_ASC + else + sort := TXT_DESC; + reg_value := reg_value + Mainform.Mask( OrderColumns[i].ColumnName ) + ' ' + sort; + end; + + // Write ORDER clause to registry + reg := TRegistry.Create(); + reg.OpenKey( REGPATH + '\Servers\' + Mainform.Childwin.Description, true ); + reg_name := 'ORDERCLAUSE_' + Mainform.Childwin.ActualDatabase + '.' + Mainform.Childwin.ActualTable; + reg.WriteString( reg_name, reg_value ); + reg.CloseKey; + + // Refresh data + Mainform.Childwin.viewdata(self); +end; + + +end.