Issue #1482: add data sorting dialog

This commit is contained in:
Ansgar Becker
2025-03-04 19:52:01 +01:00
parent a4dc12c517
commit 68aaa451a8
9 changed files with 1289 additions and 81 deletions

View File

@ -126,6 +126,17 @@
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
</Unit>
<Unit>
<Filename Value="source\data_sorting.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="frmDataSorting"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
</Unit>
<Unit>
<Filename Value="source\extra_controls.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
</Units>
</ProjectOptions>
<CompilerOptions>

View File

@ -16,7 +16,7 @@ uses
main, apphelpers, dbconnection, { gnugettext,}
dbstructures, dbstructures.mysql, About, generic_types,
dbstructures.interbase, dbstructures.mssql, dbstructures.postgresql,
dbstructures.sqlite, change_password, loginform {, printlist (EnablePrint not defined) }
dbstructures.sqlite, change_password, loginform, data_sorting, extra_controls {, printlist (EnablePrint not defined) }
;
{$R *.res}

76
source/data_sorting.lfm Normal file
View File

@ -0,0 +1,76 @@
object frmDataSorting: TfrmDataSorting
Left = 0
Height = 121
Top = 0
Width = 255
BorderStyle = bsNone
Caption = 'DataSortingForm'
ClientHeight = 121
ClientWidth = 255
Color = clBtnFace
DesignTimePPI = 120
Font.Color = clWindowText
Font.Height = -15
Font.Name = 'Tahoma'
OnClose = FormClose
OnCreate = FormCreate
OnDeactivate = FormDeactivate
LCLVersion = '3.8.0.0'
object pnlBevel: TPanel
Left = 0
Height = 97
Top = 0
Width = 204
Align = alClient
BorderWidth = 3
ClientHeight = 97
ClientWidth = 204
ParentBackground = False
TabOrder = 0
object btnOK: TButton
Left = 4
Height = 31
Top = 61
Width = 75
Anchors = [akLeft, akBottom]
Caption = 'OK'
Default = True
Enabled = False
ModalResult = 1
TabOrder = 0
OnClick = btnOKClick
end
object btnCancel: TButton
Left = 85
Height = 31
Top = 61
Width = 75
Anchors = [akLeft, akBottom]
Cancel = True
Caption = 'Cancel'
ModalResult = 2
TabOrder = 1
OnClick = btnCancelClick
end
object btnAddCol: TButton
Left = 168
Height = 31
Top = 61
Width = 75
Anchors = [akLeft, akBottom]
Caption = 'Add Col'
TabOrder = 2
OnClick = btnAddColClick
end
object btnReset: TSpeedButton
Left = 36
Height = 31
Top = 25
Width = 156
Action = MainForm.actDataResetSorting
Anchors = [akRight, akBottom]
Images = MainForm.ImageListIcons8
ImageIndex = 139
end
end
end

342
source/data_sorting.pas Normal file
View File

@ -0,0 +1,342 @@
unit data_sorting;
interface
uses
Windows, SysUtils, Classes, Controls, Forms, StdCtrls, ExtCtrls, ComCtrls, Buttons,
Graphics, apphelpers, extra_controls, dbconnection;
type
TfrmDataSorting = class(TExtForm)
pnlBevel: TPanel;
btnOK: TButton;
btnCancel: TButton;
btnAddCol: TButton;
btnReset: TSpeedButton;
procedure btnAddColClick(Sender: TObject);
procedure btnCancelClick(Sender: TObject);
procedure btnOKClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormDeactivate(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure DisplaySortingControls(Sender: TObject);
private
{ Private declarations }
FColumnNames: TStringList;
FSortItems: TSortItems;
FOldOrderClause: String;
FDeleteTimer: TTimer;
FDeleteButtonPressed: TSpeedButton;
procedure DeleteTimerTimer(Sender: TObject);
procedure comboColumnsChange(Sender: TObject);
procedure btnOrderClick(Sender: TObject);
procedure btnDeleteClick(Sender: TObject);
procedure Modified;
public
{ Public declarations }
end;
implementation
uses main;
{$R *.lfm}
procedure TfrmDataSorting.FormCreate(Sender: TObject);
var
i: Integer;
begin
FColumnNames := TStringList.Create;
// Take column names from listColumns and add here
for i:=0 to Mainform.SelectedTableColumns.Count-1 do begin
FColumnNames.Add(Mainform.SelectedTableColumns[i].Name);
end;
FSortItems := TSortItems.Create(True);
FSortItems.Assign(MainForm.DataGridSortItems);
FOldOrderClause := FSortItems.ComposeOrderClause(MainForm.ActiveConnection);
FDeleteTimer := TTimer.Create(Self);
FDeleteTimer.Interval := 100;
FDeleteTimer.Enabled := False;
FDeleteTimer.OnTimer := DeleteTimerTimer;
// First creation of controls
DisplaySortingControls(Sender);
end;
{**
Create controls for order columns
}
procedure TfrmDataSorting.DisplaySortingControls(Sender: TObject);
var
SortItem: TSortItem;
lblNumber: TLabel;
btnDelete: TSpeedButton;
comboColumns: TComboBox;
btnOrder: TSpeedButton;
i, TopPos,
Width1, Width2, Width3, Width4, // Width of controls per row
Margin, // Space between controls
MarginBig: Integer; // Space above the very first and last controls, used to separate stuff
begin
// Remove previously created components, which all have a tag > 0
for i := ComponentCount - 1 downto 0 do begin
if Components[i].Tag > 0 then
Components[i].Free;
end;
Margin := ScaleSize(3);
MarginBig := ScaleSize(Margin * 2);
Width1 := ScaleSize(15);
Width2 := ScaleSize(160);
Width3 := ScaleSize(23);
Width4 := ScaleSize(23);
// Set initial width to avoid resizing form to 0
TopPos := pnlBevel.BorderWidth + MarginBig;
// Create line with controls for each order column
// TODO: disable repaint on every created control. Sending WM_SETREDRAW=0 message creates artefacts.
LockWindowUpdate(pnlBevel.Handle);
for i:=0 to FSortItems.Count-1 do begin
SortItem := FSortItems[i];
// 1. Label with number
lblNumber := TLabel.Create(self);
lblNumber.Parent := pnlBevel;
lblNumber.AutoSize := False; // Avoids automatic changes to width + height
lblNumber.Left := pnlBevel.BorderWidth + MarginBig;
lblNumber.Top := TopPos;
lblNumber.Width := Width1;
lblNumber.Alignment := taRightJustify;
lblNumber.Layout := tlCenter;
lblNumber.Caption := IntToStr(i+1) + '.';
lblNumber.Tag := i+1;
// 2. Dropdown with column names
comboColumns := TComboBox.Create(self);
comboColumns.Parent := pnlBevel;
comboColumns.Width := Width2;
comboColumns.Left := lblNumber.Left + lblNumber.Width + Margin;
comboColumns.Top := TopPos;
comboColumns.Items.Text := FColumnNames.Text;
comboColumns.Style := csDropDownList; // Not editable
comboColumns.ItemIndex := FColumnNames.IndexOf(SortItem.Column);
comboColumns.Tag := i+1;
comboColumns.OnChange := comboColumnsChange;
lblNumber.Height := comboColumns.Height;
// 3. A button for selecting ASC/DESC
btnOrder := TSpeedButton.Create(self);
btnOrder.Parent := pnlBevel;
btnOrder.Width := Width3;
btnOrder.Height := comboColumns.Height;
btnOrder.Left := comboColumns.Left + comboColumns.Width + Margin;
btnOrder.Top := TopPos;
btnOrder.AllowAllUp := True; // Enables Down = False
btnOrder.GroupIndex := i+1; // if > 0 enables Down = True
btnOrder.Glyph.Transparent := True;
//btnOrder.Glyph.AlphaFormat := afDefined;
if SortItem.Order = sioDescending then begin
MainForm.VirtualImageListMain.GetBitmap(110, btnOrder.Glyph);
btnOrder.Down := True;
end else begin
MainForm.VirtualImageListMain.GetBitmap(109, btnOrder.Glyph);
end;
btnOrder.Hint := _('Toggle the sort direction for this column.');
btnOrder.Tag := i+1;
btnOrder.OnClick := btnOrderClick;
// 4. Button for deleting
btnDelete := TSpeedButton.Create(self);
btnDelete.Parent := pnlBevel;
btnDelete.Width := Width4;
btnDelete.Height := comboColumns.Height;
btnDelete.Left := btnOrder.Left + btnOrder.Width + Margin;
btnDelete.Top := TopPos;
btnDelete.Images := MainForm.VirtualImageListMain;
btnDelete.ImageIndex := 26;
btnDelete.Hint := _('Drops sorting by this column.');
btnDelete.Tag := i+1;
btnDelete.OnClick := btnDeleteClick;
TopPos := comboColumns.Top + comboColumns.Height + Margin;
end;
LockWindowUpdate(0);
Inc(TopPos, MarginBig);
// Auto-adjust size of form
Height := TopPos +
btnReset.Height + Margin +
btnOK.Height + MarginBig +
pnlBevel.BorderWidth;
Width := pnlBevel.BorderWidth +
MarginBig + Width1 +
Margin + Width2 +
Margin + Width3 +
Margin + Width4 +
MarginBig + pnlBevel.BorderWidth;
// Auto-adjust width and position of main buttons at bottom
btnReset.Left := pnlBevel.BorderWidth + MarginBig;
btnReset.Top := TopPos;
btnReset.Width := Width - 2 * pnlBevel.BorderWidth - 2 * MarginBig;
btnReset.Enabled := Mainform.actDataResetSorting.Enabled;
btnOK.Left := pnlBevel.BorderWidth + MarginBig;
btnOK.Top := btnReset.Top + btnReset.Height + Margin;
btnOK.Width := Round(btnReset.Width / 3) - Margin;
btnCancel.Left := btnOK.Left + btnOK.Width + Margin;
btnCancel.Top := btnReset.Top + btnReset.Height + Margin;
btnCancel.Width := btnOK.Width;
btnAddCol.Left := btnCancel.Left + btnCancel.Width + Margin;
btnAddCol.Top := btnReset.Top + btnReset.Height + Margin;
btnAddCol.Width := btnOK.Width;
end;
{**
Dropdown for column selection was changed
}
procedure TfrmDataSorting.comboColumnsChange( Sender: TObject );
var
combo : TComboBox;
begin
combo := Sender as TComboBox;
FSortItems[combo.Tag-1].Column := combo.Text;
// Enables OK button
Modified;
end;
{**
Button for selecting sort-direction was clicked
}
procedure TfrmDataSorting.btnOrderClick( Sender: TObject );
var
btn: TSpeedButton;
begin
btn := Sender as TSpeedButton;
btn.Glyph := nil;
if FSortItems[btn.Tag-1].Order = sioAscending then begin
MainForm.VirtualImageListMain.GetBitmap(110, btn.Glyph);
FSortItems[btn.Tag-1].Order := sioDescending;
end else begin
MainForm.VirtualImageListMain.GetBitmap(109, btn.Glyph);
FSortItems[btn.Tag-1].Order := sioAscending;
end;
// Enables OK button
Modified;
end;
{**
Delete order column
}
procedure TfrmDataSorting.btnDeleteClick(Sender: TObject);
begin
FDeleteButtonPressed := Sender as TSpeedButton;
FDeleteTimer.Enabled := True;
end;
procedure TfrmDataSorting.DeleteTimerTimer(Sender: TObject);
begin
FDeleteTimer.Enabled := False;
FSortItems.Delete(FDeleteButtonPressed.Tag-1);
// Refresh controls
DisplaySortingControls(Self);
// Enables OK button
Modified;
end;
{**
Add a new order column
}
procedure TfrmDataSorting.btnAddColClick(Sender: TObject);
var
UnusedColumns: TStringList;
NewSortItem, SortItem: TSortItem;
begin
NewSortItem := FSortItems.AddNew;
// Take first unused column as default for new sort item
UnusedColumns := TStringList.Create;
UnusedColumns.AddStrings(FColumnNames);
for SortItem in FSortItems do begin
if UnusedColumns.IndexOf(SortItem.Column) > -1 then
UnusedColumns.Delete(UnusedColumns.IndexOf(SortItem.Column));
end;
if UnusedColumns.Count > 0 then
NewSortItem.Column := UnusedColumns[0]
else
NewSortItem.Column := FColumnNames[0];
MainForm.LogSQL('Created sorting for column '+NewSortItem.Column+'/'+Integer(NewSortItem.Order).ToString+' in TfrmDataSorting.btnAddColClick', lcDebug);
// Refresh controls
DisplaySortingControls(Sender);
// Enables OK button
Modified;
end;
{**
Gets called when any option has changed.
Enables the OK button if ORDER options have changed
}
procedure TfrmDataSorting.Modified;
begin
btnOk.Enabled := FSortItems.ComposeOrderClause(MainForm.ActiveConnection) <> FOldOrderClause;
end;
{**
OK clicked: Write ORDER clause to registry
}
procedure TfrmDataSorting.btnOKClick(Sender: TObject);
begin
// TODO: apply ordering
MainForm.DataGridSortItems.Assign(FSortItems);
InvalidateVT(Mainform.DataGrid, VTREE_NOTLOADED_PURGECACHE, False);
btnCancel.OnClick(Sender);
end;
procedure TfrmDataSorting.btnCancelClick(Sender: TObject);
begin
Mainform.tbtnDataSorting.Down := False;
Close;
end;
{**
Be sure the form is destroyed after closing.
}
procedure TfrmDataSorting.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
{**
Cancel this dialog if the user clicks elsewhere on mainform
}
procedure TfrmDataSorting.FormDeactivate(Sender: TObject);
begin
btnCancel.OnClick(Sender);
end;
end.

790
source/extra_controls.pas Normal file
View File

@ -0,0 +1,790 @@
unit extra_controls;
{$mode delphi}{$H+}
interface
uses
Classes, SysUtils, Forms, Types, StdCtrls, Clipbrd,
apphelpers, Graphics, Dialogs, ImgList, ComCtrls,
ExtCtrls, laz.VirtualTrees, RegExpr, Controls, EditBtn,
GraphUtil;
type
// Form with a sizegrip in the lower right corner, without the need for a statusbar
TExtForm = class(TForm)
private
//FSizeGrip: TSizeGripXP;
FPixelsPerInchDesigned: Integer;
//function GetHasSizeGrip: Boolean;
//procedure SetHasSizeGrip(Value: Boolean);
protected
//procedure DoShow; override;
//procedure DoBeforeMonitorDpiChanged(OldDPI, NewDPI: Integer); override;
//procedure DoAfterMonitorDpiChanged(OldDPI, NewDPI: Integer); override;
procedure FilterNodesByEdit(Edit: TEditButton; Tree: TVirtualStringTree);
public
constructor Create(AOwner: TComponent); override;
class procedure InheritFont(AFont: TFont);
//property HasSizeGrip: Boolean read GetHasSizeGrip write SetHasSizeGrip default False;
//class procedure FixControls(ParentComp: TComponent);
class procedure SaveListSetup(List: TVirtualStringTree);
class procedure RestoreListSetup(List: TVirtualStringTree);
function ScaleSize(x: Extended): Integer; overload;
class function ScaleSize(x: Extended; Control: TControl): Integer; overload;
class procedure PageControlTabHighlight(PageControl: TPageControl);
property PixelsPerInchDesigned: Integer read FPixelsPerInchDesigned;
end;
// Modern file-open-dialog with high DPI support and encoding selector
{TExtFileOpenDialog = class(TFileOpenDialog)
private
FEncodings: TStringList;
FEncodingIndex: Cardinal;
const idEncodingCombo = 1;
procedure FileOkClickNoOp(Sender: TObject; var CanClose: Boolean);
protected
procedure DoOnExecute; override;
function DoOnFileOkClick: Boolean; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure AddFileType(FileMask, DisplayName: String);
property Encodings: TStringList read FEncodings write FEncodings;
property EncodingIndex: Cardinal read FEncodingIndex write FEncodingIndex;
end;}
{TExtFileSaveDialog = class(TFileSaveDialog)
private
FLineBreaks: TStringList;
FLineBreakIndex: TLineBreaks;
const idLineBreakCombo = 1;
procedure FileOkClickNoOp(Sender: TObject; var CanClose: Boolean);
protected
procedure DoOnExecute; override;
function DoOnFileOkClick: Boolean; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure AddFileType(FileMask, DisplayName: String);
property LineBreaks: TStringList read FLineBreaks;
property LineBreakIndex: TLineBreaks read FLineBreakIndex write FLineBreakIndex;
end;}
{TExtSynHotKey = class(TSynHotKey)
private
FOnChange: TNotifyEvent;
FOnEnter: TNotifyEvent;
FOnExit: TNotifyEvent;
procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS;
procedure WMSetFocus(var Msg: TWMSetFocus); message WM_SETFOCUS;
protected
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure Paint; override;
published
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnEnter: TNotifyEvent read FOnEnter write FOnEnter;
property OnExit: TNotifyEvent read FOnExit write FOnExit;
end;}
{TExtComboBox = class(TComboBox)
private
FcbHintIndex: Integer;
FHintWindow: THintWindow;
protected
procedure Change; override;
procedure DropDown; override;
procedure CloseUp; override;
procedure InitiateAction; override;
end;}
{TExtHintWindow = class(THintWindow)
private
const Padding: Integer = 8;
protected
procedure Paint; override;
public
function CalcHintRect(MaxWidth: Integer; const AHint: string; AData: TCustomData): TRect; override;
end;}
implementation
{ TExtForm }
constructor TExtForm.Create(AOwner: TComponent);
var
OldImageList: TCustomImageList;
begin
inherited;
FPixelsPerInchDesigned := DesignTimePPI;
InheritFont(Font);
//HasSizeGrip := False;
// Reduce flicker on Windows 10
// See https://www.heidisql.com/forum.php?t=19141
//if CheckWin32Version(6, 2) then begin
// DoubleBuffered := True;
//end;
// Translation and related fixes
// Issue #557: Apply images *after* translating main menu, so top items don't get unused
// space left besides them.
if (Menu <> nil) and (Menu.Images <> nil) then begin
OldImageList := Menu.Images;
Menu.Images := nil;
//TranslateComponent(Self);
Menu.Images := OldImageList;
end else begin
//TranslateComponent(Self);
end;
end;
{procedure TExtForm.DoShow;
begin
// No need to fix anything
FixControls(Self);
inherited;
end;}
{procedure TExtForm.DoBeforeMonitorDpiChanged(OldDPI, NewDPI: Integer);
begin
// Reduce flicker
inherited;
LockWindowUpdate(Handle);
end;}
{procedure TExtForm.DoAfterMonitorDpiChanged(OldDPI, NewDPI: Integer);
begin
// Release window updates
LockWindowUpdate(0);
inherited;
end;}
{class procedure TExtForm.FixControls(ParentComp: TComponent);
var
i: Integer;
procedure ProcessSingleComponent(Cmp: TComponent);
begin
if (Cmp is TButton) and (TButton(Cmp).Style = bsSplitButton) then begin
// Work around broken dropdown (tool)button on Wine after translation:
// https://sourceforge.net/p/dxgettext/bugs/80/
TButton(Cmp).Style := bsPushButton;
TButton(Cmp).Style := bsSplitButton;
end;
if (Cmp is TToolButton) and (TToolButton(Cmp).Style = tbsDropDown) then begin
// similar fix as above
TToolButton(Cmp).Style := tbsButton;
TToolButton(Cmp).Style := tbsDropDown;
end;
end;
begin
// Passed component itself may also be some control to be fixed
// e.g. TInplaceEditorLink.MainControl
ProcessSingleComponent(ParentComp);
for i:=0 to ParentComp.ComponentCount-1 do begin
ProcessSingleComponent(ParentComp.Components[i]);
end;
end;}
{function TExtForm.GetHasSizeGrip: Boolean;
begin
Result := FSizeGrip <> nil;
end;}
{procedure TExtForm.SetHasSizeGrip(Value: Boolean);
begin
if Value then begin
FSizeGrip := TSizeGripXP.Create(Self);
FSizeGrip.Enabled := True;
end else begin
if FSizeGrip <> nil then
FreeAndNil(FSizeGrip);
end;
end;}
class procedure TExtForm.InheritFont(AFont: TFont);
var
GUIFontName: String;
begin
// Set custom font if set, or default system font.
// In high-dpi mode, the font *size* is increased automatically somewhere in the VCL,
// caused by a form's .Scaled property. So we don't increase it here again.
// To test this, you really need to log off/on Windows!
GUIFontName := AppSettings.ReadString(asGUIFontName);
if not GUIFontName.IsEmpty then begin
// Apply user specified font
AFont.Name := GUIFontName;
// Set size on top of automatic dpi-increased size
AFont.Size := AppSettings.ReadInt(asGUIFontSize);
end else begin
// Apply system font. See issue #3204.
AFont.Orientation := Screen.SystemFont.Orientation;
AFont.CharSet := Screen.SystemFont.CharSet;
AFont.Name := Screen.SystemFont.Name;
AFont.Pitch := Screen.SystemFont.Pitch;
end;
end;
{**
Save setup of a VirtualStringTree to registry
}
class procedure TExtForm.SaveListSetup( List: TVirtualStringTree );
var
i: Integer;
ColWidth: Int64;
ColWidths, ColsVisible, ColPos, Regname: String;
OwnerForm: TWinControl;
begin
// Prevent sporadic crash on startup
if List = nil then
Exit;
OwnerForm := GetParentFormOrFrame(List);
// On a windows shutdown, GetParentForm() seems sporadically unable to find the owner form
// In that case we would cause an exception when accessing it. Emergency break in that case.
// See issue #1462
// TODO: Test this, probably fixed by implementing GetParentFormOrFrame, and then again, probably not.
if not Assigned(OwnerForm) then
Exit;
ColWidths := '';
ColsVisible := '';
ColPos := '';
for i := 0 to List.Header.Columns.Count - 1 do
begin
// Column widths
if ColWidths <> '' then
ColWidths := ColWidths + ',';
ColWidth := List.Header.Columns[i].Width; // RoundCommercial(List.Header.Columns[i].Width / OwnerForm.ScaleFactor);
ColWidths := ColWidths + IntToStr(ColWidth);
// Column visibility
if coVisible in List.Header.Columns[i].Options then
begin
if ColsVisible <> '' then
ColsVisible := ColsVisible + ',';
ColsVisible := ColsVisible + IntToStr(i);
end;
// Column position
if ColPos <> '' then
ColPos := ColPos + ',';
ColPos := ColPos + IntToStr(List.Header.Columns[i].Position);
end;
// Lists can have the same name over different forms or frames. Find parent form or frame,
// so we can prepend its name into the registry value name.
Regname := OwnerForm.Name + '.' + List.Name;
AppSettings.ResetPath;
AppSettings.WriteString(asListColWidths, ColWidths, Regname);
AppSettings.WriteString(asListColsVisible, ColsVisible, Regname);
AppSettings.WriteString(asListColPositions, ColPos, Regname);
AppSettings.WriteString(asListColSort, IntToStr(List.Header.SortColumn) + ',' + IntToStr(Integer(List.Header.SortDirection)), RegName);
end;
{**
Restore setup of VirtualStringTree from registry
}
class procedure TExtForm.RestoreListSetup( List: TVirtualStringTree );
var
i : Byte;
colpos : Integer;
ColWidth: Int64;
Value : String;
ValueList : TStringList;
Regname: String;
OwnerForm: TWinControl;
begin
ValueList := TStringList.Create;
// Column widths
OwnerForm := GetParentFormOrFrame(List);
Regname := OwnerForm.Name + '.' + List.Name;
Value := AppSettings.ReadString(asListColWidths, Regname);
if Value <> '' then begin
ValueList := Explode( ',', Value );
for i := 0 to ValueList.Count - 1 do
begin
ColWidth := MakeInt(ValueList[i]);
//ColWidth := RoundCommercial(ColWidth * OwnerForm.ScaleFactor);
// Check if column number exists and width is at least 1 pixel
if (List.Header.Columns.Count > i) and (ColWidth > 0) and (ColWidth < 1000) then
List.Header.Columns[i].Width := ColWidth;
end;
end;
// Column visibility
Value := AppSettings.ReadString(asListColsVisible, Regname);
if Value <> '' then begin
ValueList := Explode( ',', Value );
for i:=0 to List.Header.Columns.Count-1 do begin
if ValueList.IndexOf( IntToStr(i) ) > -1 then
List.Header.Columns[i].Options := List.Header.Columns[i].Options + [coVisible]
else
List.Header.Columns[i].Options := List.Header.Columns[i].Options - [coVisible];
end;
end;
// Column position
Value := AppSettings.ReadString(asListColPositions, Regname);
if Value <> '' then begin
ValueList := Explode( ',', Value );
for i := 0 to ValueList.Count - 1 do
begin
colpos := MakeInt(ValueList[i]);
// Check if column number exists
if List.Header.Columns.Count > i then
List.Header.Columns[i].Position := colpos;
end;
end;
// Sort column and direction
Value := AppSettings.ReadString(asListColSort, Regname);
if Value <> '' then begin
ValueList := Explode(',', Value);
if ValueList.Count = 2 then begin
List.Header.SortColumn := MakeInt(ValueList[0]);
if MakeInt(ValueList[1]) = 0 then
List.Header.SortDirection := sdAscending
else
List.Header.SortDirection := sdDescending;
end;
end;
ValueList.Free;
end;
procedure TExtForm.FilterNodesByEdit(Edit: TEditButton; Tree: TVirtualStringTree);
var
rx: TRegExpr;
Node: PVirtualNode;
i: Integer;
match: Boolean;
CellText: String;
begin
// Loop through all tree nodes and hide non matching
Node := Tree.GetFirst;
rx := TRegExpr.Create;
rx.ModifierI := True;
rx.Expression := Edit.Text;
try
rx.Exec('abc');
except
on E:ERegExpr do begin
if rx.Expression <> '' then begin
//LogSQL('Filter text is not a valid regular expression: "'+rx.Expression+'"', lcError);
rx.Expression := '';
end;
end;
end;
Tree.BeginUpdate;
while Assigned(Node) do begin
if not Tree.HasChildren[Node] then begin
// Don't filter anything if the filter text is empty
match := rx.Expression = '';
// Search for given text in node's captions
if not match then for i := 0 to Tree.Header.Columns.Count - 1 do begin
CellText := Tree.Text[Node, i];
match := rx.Exec(CellText);
if match then
break;
end;
Tree.IsVisible[Node] := match;
if match and IsNotEmpty(Edit.Text) then
Tree.VisiblePath[Node] := True;
end;
Node := Tree.GetNext(Node);
end;
Tree.EndUpdate;
Tree.Invalidate;
rx.Free;
Edit.Button.Visible := IsNotEmpty(Edit.Text);
end;
function TExtForm.ScaleSize(x: Extended): Integer;
begin
// Shorthand for dpi scaling hardcoded width/height values of controls
Result := ScaleSize(x, Self);
end;
class function TExtForm.ScaleSize(x: Extended; Control: TControl): Integer;
begin
// Same as above for callers without a form
Result := Control.Scale96ToForm(Round(x));
end;
class procedure TExtForm.PageControlTabHighlight(PageControl: TPageControl);
var
i, CurrentImage, CountOriginals: Integer;
Images: TImageList;
GrayscaleMode: Integer;
IsQueryTab, DoGrayscale: Boolean;
begin
// Set grayscale icon on inactive tabs
if not (PageControl.Images is TImageList) then
Exit;
GrayscaleMode := AppSettings.ReadInt(asTabIconsGrayscaleMode);
Images := PageControl.Images as TImageList;
CountOriginals := Images.Count;
for i:=0 to PageControl.PageCount-1 do begin
CurrentImage := PageControl.Pages[i].ImageIndex;
if PageControl.ActivePageIndex = i then begin
if CurrentImage >= CountOriginals then begin
// Grayscaled => Color
PageControl.Pages[i].ImageIndex := CurrentImage - CountOriginals;
end;
end
else begin
if CurrentImage < CountOriginals then begin
// Color => Grayscaled
IsQueryTab := (PageControl.Owner.Name = 'MainForm') and ExecRegExpr('^tabQuery\d*$', PageControl.Pages[i].Name);
if ((GrayscaleMode = 1) and IsQueryTab) or (GrayscaleMode = 2) then
PageControl.Pages[i].ImageIndex := CurrentImage + CountOriginals;
end;
end;
end;;
end;
{ TExtFileOpenDialog }
{constructor TExtFileOpenDialog.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FEncodings := TStringList.Create;
FEncodingIndex := 0;
end;
destructor TExtFileOpenDialog.Destroy;
begin
FEncodings.Free;
inherited;
end;
procedure TExtFileOpenDialog.AddFileType(FileMask, DisplayName: String);
var
FileType: TFileTypeItem;
begin
// Shorthand for callers
FileType := FileTypes.Add;
FileType.DisplayName := DisplayName;
FileType.FileMask := FileMask;
end;
procedure TExtFileOpenDialog.DoOnExecute;
var
iCustomize: IFileDialogCustomize;
i: Integer;
begin
// Add encodings selector
if Dialog.QueryInterface(IFileDialogCustomize, iCustomize) = S_OK then
begin
iCustomize.StartVisualGroup(0, PChar(_('Encoding:')));
try
// note other controls available: AddCheckButton, AddEditBox, AddPushButton, AddRadioButtonList...
iCustomize.AddComboBox(idEncodingCombo);
for i:=0 to FEncodings.Count - 1 do begin
iCustomize.AddControlItem(idEncodingCombo, i, PChar(FEncodings[i]));
end;
iCustomize.SetSelectedControlItem(idEncodingCombo, FEncodingIndex);
if not Assigned(OnFileOkClick) then
OnFileOkClick := FileOkClickNoOp;
finally
iCustomize.EndVisualGroup;
end;
end;
end;
procedure TExtFileOpenDialog.FileOkClickNoOp(Sender: TObject; var CanClose: Boolean);
begin
// Dummy procedure, just makes sure parent class calls DoOnFileOkClick
end;
function TExtFileOpenDialog.DoOnFileOkClick: Boolean;
var
iCustomize: IFileDialogCustomize;
begin
Result := inherited;
if Dialog.QueryInterface(IFileDialogCustomize, iCustomize) = S_OK then
begin
iCustomize.GetSelectedControlItem(idEncodingCombo, FEncodingIndex);
end;
end;}
{ TExtFileSaveDialog }
{constructor TExtFileSaveDialog.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FLineBreaks := TStringList.Create;
FLineBreaks.Add(_('Windows linebreaks'));
FLineBreaks.Add(_('UNIX linebreaks'));
FLineBreaks.Add(_('Mac OS linebreaks'));
FLineBreakIndex := lbsWindows;
end;
destructor TExtFileSaveDialog.Destroy;
begin
FLineBreaks.Free;
inherited;
end;
procedure TExtFileSaveDialog.AddFileType(FileMask, DisplayName: String);
var
FileType: TFileTypeItem;
begin
// Shorthand for callers
FileType := FileTypes.Add;
FileType.DisplayName := DisplayName;
FileType.FileMask := FileMask;
end;
procedure TExtFileSaveDialog.DoOnExecute;
var
iCustomize: IFileDialogCustomize;
i, ComboIndex: Integer;
begin
// Add line break selector
if Dialog.QueryInterface(IFileDialogCustomize, iCustomize) = S_OK then
begin
iCustomize.StartVisualGroup(0, PChar(_('Linebreaks')+':'));
try
iCustomize.AddComboBox(idLineBreakCombo);
case FLineBreakIndex of
lbsUnix: ComboIndex := 1;
lbsMac: ComboIndex := 2;
else ComboIndex := 0;
end;
for i:=0 to FLineBreaks.Count - 1 do begin
iCustomize.AddControlItem(idLineBreakCombo, i, PChar(FLineBreaks[i]));
end;
iCustomize.SetSelectedControlItem(idLineBreakCombo, ComboIndex);
if not Assigned(OnFileOkClick) then
OnFileOkClick := FileOkClickNoOp;
finally
iCustomize.EndVisualGroup;
end;
end;
end;
procedure TExtFileSaveDialog.FileOkClickNoOp(Sender: TObject; var CanClose: Boolean);
begin
// Dummy procedure, just makes sure parent class calls DoOnFileOkClick
end;
function TExtFileSaveDialog.DoOnFileOkClick: Boolean;
var
iCustomize: IFileDialogCustomize;
ComboIndex: Cardinal;
begin
Result := inherited;
if Dialog.QueryInterface(IFileDialogCustomize, iCustomize) = S_OK then
begin
iCustomize.GetSelectedControlItem(idLineBreakCombo, ComboIndex);
case ComboIndex of
0: FLineBreakIndex := lbsWindows;
1: FLineBreakIndex := lbsUnix;
2: FLineBreakIndex := lbsMac;
end;
end;
end;}
{ TExtSynHotKey }
{procedure TExtSynHotKey.WMKillFocus(var Msg: TWMKillFocus);
begin
inherited;
if Assigned(FOnExit) then
FOnExit(Self);
end;
procedure TExtSynHotKey.WMSetFocus(var Msg: TWMSetFocus);
begin
inherited;
if Assigned(FOnEnter) then
FOnEnter(Self);
end;
procedure TExtSynHotKey.KeyDown(var Key: Word; Shift: TShiftState);
begin
inherited;
if Assigned(FOnChange) then
FOnChange(Self);
end;
procedure TExtSynHotKey.Paint;
var
r: TRect;
begin
r := ClientRect;
Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color := Color;
InflateRect(r, -BorderWidth, -BorderWidth);
Canvas.FillRect(r);
if Enabled then
Canvas.Font.Color := clWindowText
else
Canvas.Font.Color := clGrayText;
SynUnicode.TextRect(Canvas, r, BorderWidth + 1, BorderWidth + 1, Text);
end;}
{ TExtComboBox }
{procedure TExtComboBox.Change;
var
P: TPoint;
HintRect: TRect;
HintText: String;
HintWidth, Padding: Integer;
begin
inherited;
if (ItemIndex > -1) and DroppedDown and GetCursorPos(P) then begin
HintText := Items[ItemIndex];
HintWidth := Canvas.TextWidth(HintText);
if HintWidth > Width then begin
Padding := TExtForm.ScaleSize(10, Self);
HintRect := Rect(
P.X + Padding,
P.Y + Padding * 2,
P.X + HintWidth + Padding * 3,
P.Y + Padding * 4
);
FHintWindow.ActivateHint(HintRect, HintText);
end;
end;
end;
procedure TExtComboBox.CloseUp;
begin
inherited;
FHintWindow.Hide;
ControlStyle := ControlStyle - [csActionClient];
end;
procedure TExtComboBox.DropDown;
begin
inherited;
if not Assigned(FHintWindow) then
FHintWindow := THintWindow.Create(Self);
FcbHintIndex := -1;
ControlStyle := ControlStyle + [csActionClient];
end;
procedure TExtComboBox.InitiateAction;
var
Idx: Integer;
begin
inherited;
Idx := ItemIndex;
if Idx <> FcbHintIndex then
begin
FcbHintIndex := ItemIndex;
Change;
end;
end;}
{ TExtHintWindow }
{function TExtHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: string; AData: TCustomData): TRect;
begin
Result := inherited;
// Customized: enlarge surrounding rect to make space for padding
if AHint.Contains(SLineBreak) then begin
Result.Right := Result.Right + 2 * ScaleValue(Padding);
Result.Bottom := Result.Bottom + 2 * ScaleValue(Padding);
end;
end;
procedure TExtHintWindow.Paint;
var
R, ClipRect: TRect;
LColor: TColor;
LStyle: TCustomStyleServices;
LDetails: TThemedElementDetails;
LGradientStart, LGradientEnd, LTextColor: TColor;
begin
R := ClientRect;
LStyle := StyleServices(Screen.ActiveForm);
LTextColor := Screen.HintFont.Color;
if LStyle.Enabled then
begin
ClipRect := R;
InflateRect(R, 4, 4);
if TOSVersion.Check(6) and LStyle.IsSystemStyle then
begin
// Paint Windows gradient background
LStyle.DrawElement(Canvas.Handle, LStyle.GetElementDetails(tttStandardNormal), R, ClipRect);
end
else
begin
LDetails := LStyle.GetElementDetails(thHintNormal);
if LStyle.GetElementColor(LDetails, ecGradientColor1, LColor) and (LColor <> clNone) then
LGradientStart := LColor
else
LGradientStart := clInfoBk;
if LStyle.GetElementColor(LDetails, ecGradientColor2, LColor) and (LColor <> clNone) then
LGradientEnd := LColor
else
LGradientEnd := clInfoBk;
if LStyle.GetElementColor(LDetails, ecTextColor, LColor) and (LColor <> clNone) then
LTextColor := LColor
else
LTextColor := Screen.HintFont.Color;
GradientFillCanvas(Canvas, LGradientStart, LGradientEnd, R, gdVertical);
end;
R := ClipRect;
end;
Inc(R.Left, 2);
Inc(R.Top, 2);
// Customized: move inner rect right+down to add padding to outer edge
if String(Caption).Contains(SLineBreak) then begin
Inc(R.Left, ScaleValue(Padding));
Inc(R.Top, ScaleValue(Padding));
end;
Canvas.Font.Color := LTextColor;
DrawText(Canvas.Handle, Caption, -1, R, DT_LEFT or DT_NOPREFIX or
DT_WORDBREAK or DrawTextBiDiModeFlagsReadingOnly);
end;}
end.

View File

@ -1,28 +1,25 @@
object frmLogin: TfrmLogin
Left = 0
Height = 220
Top = 0
Width = 338
BorderStyle = bsDialog
Caption = 'Login'
ClientHeight = 176
ClientWidth = 270
ClientHeight = 220
ClientWidth = 338
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
DesignTimePPI = 120
Font.Color = clWindowText
Font.Height = -12
Font.Height = -15
Font.Name = 'Tahoma'
Font.Style = []
Position = poScreenCenter
OnCreate = FormCreate
OnShow = FormShow
DesignSize = (
270
176)
TextHeight = 14
Position = poScreenCenter
object btnOK: TButton
Left = 164
Top = 143
Width = 98
Height = 25
Left = 206
Height = 31
Top = 179
Width = 122
Anchors = [akRight, akBottom]
Caption = 'Login'
Default = True
@ -31,66 +28,68 @@ object frmLogin: TfrmLogin
end
object pnlBackground: TPanel
Left = 0
Height = 181
Top = 0
Width = 270
Height = 137
Width = 338
Align = alTop
Anchors = [akLeft, akTop, akRight, akBottom]
Anchors = [akTop, akLeft, akRight, akBottom]
BevelOuter = bvNone
Caption = 'pnlBackground'
ClientHeight = 181
ClientWidth = 338
Color = clWhite
ParentBackground = False
ShowCaption = False
ParentColor = False
TabOrder = 0
DesignSize = (
270
137)
object lblPrompt: TLabel
Left = 38
Top = 13
Width = 44
Height = 13
Left = 48
Height = 18
Top = 16
Width = 59
Caption = 'lblPrompt'
end
object lblUsername: TLabel
Left = 38
Top = 44
Width = 52
Height = 13
Left = 48
Height = 18
Top = 63
Width = 72
Anchors = [akLeft, akBottom]
Caption = '&Username:'
FocusControl = editUsername
end
object lblPassword: TLabel
Left = 38
Top = 90
Width = 50
Height = 13
Left = 48
Height = 18
Top = 121
Width = 66
Anchors = [akLeft, akBottom]
Caption = '&Password:'
FocusControl = editPassword
end
object imgIcon: TImage
Left = 10
Top = 13
Width = 16
Height = 16
Left = 12
Height = 20
Top = 16
Width = 20
ImageIndex = 144
Images = MainForm.ImageListIcons8
end
object editPassword: TEdit
Left = 38
Top = 109
Width = 224
Height = 21
Left = 48
Height = 26
Top = 146
Width = 280
Anchors = [akLeft, akRight, akBottom]
EchoMode = emPassword
PasswordChar = '*'
TabOrder = 1
Text = 'editPassword'
end
object editUsername: TEdit
Left = 38
Top = 63
Width = 224
Height = 21
Left = 48
Height = 26
Top = 89
Width = 280
Anchors = [akLeft, akRight, akBottom]
TabOrder = 0
Text = 'editUsername'

View File

@ -39,7 +39,6 @@ uses apphelpers, main;
procedure TfrmLogin.FormCreate(Sender: TObject);
begin
Caption := APPNAME + ' - Login';
//MainForm.VirtualImageListMain.GetBitmap(144, imgIcon.Picture.Bitmap);
lblPrompt.Font.Size := 10;
lblPrompt.Font.Color := GetThemeColor(clHotlight);
lblPrompt.Font.Style := lblPrompt.Font.Style + [fsBold];

View File

@ -962,11 +962,11 @@ object MainForm: TMainForm
Height = 316
Top = 0
Width = 780
ActivePage = tabDatabase
ActivePage = tabData
Align = alClient
Images = ImageListIcons8
PopupMenu = popupMainTabs
TabIndex = 1
TabIndex = 3
TabOrder = 1
OnChange = PageControlMainChange
OnChanging = PageControlMainChanging
@ -2019,8 +2019,8 @@ object MainForm: TMainForm
ImageIndex = 41
object lblSorryNoData: TLabel
Left = 0
Height = 192
Top = 91
Height = 185
Top = 98
Width = 772
Align = alClient
Alignment = taCenter
@ -2030,19 +2030,19 @@ object MainForm: TMainForm
end
object pnlDataTop: TPanel
Left = 0
Height = 25
Height = 32
Top = 0
Width = 772
Align = alTop
Alignment = taLeftJustify
BevelOuter = bvNone
BorderWidth = 1
ClientHeight = 25
ClientHeight = 32
ClientWidth = 772
TabOrder = 2
object lblDataTop: TLabel
Left = 1
Height = 23
Height = 30
Top = 1
Width = 215
Align = alLeft
@ -2053,56 +2053,48 @@ object MainForm: TMainForm
PopupMenu = popupDataTop
end
object tlbDataButtons: TToolBar
Left = 698
Height = 23
Left = 392
Height = 30
Top = 1
Width = 73
Width = 379
Align = alRight
AutoSize = True
ButtonHeight = 28
ButtonWidth = 72
Caption = 'tlbDataButtons'
Images = ImageListIcons8
List = True
ParentShowHint = False
ShowCaptions = True
ShowHint = True
TabOrder = 0
Wrapable = False
object tbtnDataNext: TToolButton
Left = 1
Top = 2
Action = actDataShowNext
end
object tbtnDataShowAll: TToolButton
Left = 1
Top = 31
Left = 73
Top = 2
Action = actDataShowAll
end
object ToolButton2: TToolButton
Left = 1
Top = 60
Caption = 'ToolButton2'
ImageIndex = 108
end
object tbtnDataSorting: TToolButton
Left = 1
Top = 89
Left = 145
Top = 2
AllowAllUp = True
Caption = 'Sorting'
ImageIndex = 107
OnClick = btnDataClick
end
object tbtnDataColumns: TToolButton
Left = 1
Top = 118
Left = 217
Top = 2
AllowAllUp = True
Caption = 'Columns'
ImageIndex = 107
OnClick = btnDataClick
end
object tbtnDataFilter: TToolButton
Left = 1
Top = 147
Left = 289
Top = 2
AllowAllUp = True
Caption = 'Filter'
ImageIndex = 107
@ -2113,7 +2105,7 @@ object MainForm: TMainForm
object pnlFilter: TPanel
Left = 0
Height = 66
Top = 25
Top = 32
Width = 772
Align = alTop
BevelOuter = bvNone
@ -2682,8 +2674,8 @@ object MainForm: TMainForm
end
object DataGrid: TLazVirtualStringTree
Left = 0
Height = 192
Top = 91
Height = 185
Top = 98
Width = 772
Align = alClient
DefaultText = 'Node'
@ -19849,7 +19841,7 @@ object MainForm: TMainForm
Top = 120
end
object SynCompletionProposal: TSynCompletion
Position = 0
Position = -1
LinesInWindow = 6
SelectedColor = clHighlight
CaseSensitive = False

View File

@ -508,7 +508,6 @@ type
actQueryFindAgain1: TMenuItem;
Replacetext1: TMenuItem;
menuExplainProcess: TMenuItem;
ToolButton2: TToolButton;
tbtnDataShowAll: TToolButton;
tbtnDataNext: TToolButton;
actDataShowNext: TAction;
@ -1412,7 +1411,7 @@ const
implementation
uses
FileInfo, winpeimagereader, elfreader, machoreader, About;
FileInfo, winpeimagereader, elfreader, machoreader, About, data_sorting;
{$R *.lfm}
@ -8520,7 +8519,7 @@ begin
if btn = tbtnDataColumns then
//frm := TfrmColumnSelection.Create(self)
else if btn = tbtnDataSorting then
//frm := TfrmDataSorting.Create(self)
frm := TfrmDataSorting.Create(self)
else
frm := TForm.Create(self); // Dummy fallback, should never get created
// Position new form relative to btn's position