doublecmd/src/ftreeviewmenu.pas
Denis Bisson 92565d38a5 ADD: Tree View Menu implementation.
ADD: The possibility to use a concept of "Tree View Menu" where the possible actions offered to the user are placed into a tree view. There is above an edit box where user might enter a few letters to eliminate the non-matching choices from the visual field so only the matching ones are remaining visible. User might use arrows keys to move up and down through possible selectable items from the three. He might even use ALT+KEY shorcut to select item. This way, user might choose quicker when list are long without having to give much attention.
ADD: The "Tree View Menu" are not offered by default so it won't impact on user by default. User needs to activate the "Tree View Menu" presentation from "Tree View Menu" configuration page.
ADD: "Tree View Menu" may be activated for selection from hot directories configured independently if it was called from internal command "cm_DirHotList" or from a double click for the top of the panel.
ADD: "Tree View Menu" may be activated for selection from favorite tabs configured independently if it was called from internal command "cm_LoadFavoriteTabs" or from a double click on a tabs.
ADD: "Tree View Menu" may be activated for selection from directories in dir history.
ADD: "Tree View Menu" may be activated for selection from directories in fileview history, 
ADD: "Tree View Menu" may be activated for selection  of commands when looking at the command line history. 
UPD: The internal commands "cm_ShowMainMenu" may now supports the parameter "treeview" with boolean possible values to offer the possibility to choose item from main menu items through a "Tree View Menu" look.
UPD: Put back in action the internal command "cm_ShowButtonMenu" to make visible or not the toolbar with the parameter "toolbar" with boolean possible values.
UPD: The internal commands "cm_ShowButtonMenu" may now supports the parameter "treeview" with boolean possible values to offer the possibility to choose item from toolbar items through a "Tree View Menu" look.
ADD: In the TKASToolBar, add a "PublicExecuteToolItem" function so given a "TKASToolItem", we may call its execution directly from it.
ADD: Two new configuration pages related with the "Tree View Menus" which are a page for basic settings "fOptionsTreeViewMenu" and one for colors "fOptionsTreeViewMenuColor".
ADD: Add new 32x32 icon for the commands "cm_configtreeviewmenus" and "cm_configtreeviewmenuscolors".
UPD: Change 32x32 icon for the "cm_showbuttonmenu" so it looks like a little more to a DC toolbar.
UPD: New parameter "position=" for the internal command "cm_DirHotList" with possible values "panel" or "cursor" to determine where it will be shown. Previous one was not documented and use only internally so no need to respect legacy here.
2016-04-17 23:24:04 +00:00

1347 lines
51 KiB
ObjectPascal

{
Double Commander
-------------------------------------------------------------------------
Menu offered to user via a Tree View look where user might type sequence of letters
Copyright (C) 2016 Alexander Koblov (alexx2000@mail.ru)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
}
unit fTreeViewMenu;
{$mode objfpc}{$H+}
interface
uses
//Lazarus, Free-Pascal, etc.
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,
StdCtrls, ExtCtrls, Menus,
//DC
kastoolitems, KASToolBar, uKASToolItemsExtended;
type
// *IMPORTANT: "tvmcLASTONE" always must be the last one as it is used to give the number of element here.
tvmContextMode = (tvmcHotDirectory, tvmcFavoriteTabs, tvmcDirHistory, tvmcViewHistory, tvmcKASToolBar, tvmcMainMenu, tvmcCommandLineHistory, tvmcFileSelectAssistant, tvmcLASTONE);
TTreeViewMenuOptions = record
CaseSensitive: boolean;
IgnoreAccents: boolean;
ShowWholeBranchIfMatch: boolean;
end;
{ TTreeMenuItem }
// In out TreeView, the "pointer" will actually point this type of element where the "FPointerSourceData" might actually point the actual vital items user actually choose.
TTreeMenuItem = class
private
FPointerSourceData: Pointer;
FTypeDispatcher: integer;
FSecondaryText: string;
FKeyboardShortcut: char;
public
constructor Create(PointerSourceData: Pointer);
property PointerSourceData: Pointer read FPointerSourceData;
property KeyboardShortcut: char read FKeyboardShortcut write FKeyboardShortcut;
property SecondaryText: string read FSecondaryText write FSecondaryText;
property TypeDispatcher: integer read FTypeDispatcher write FTypeDispatcher;
end;
{ TTreeViewMenuGenericRoutineAndVarHolder }
// Everything could have been placed into the "TfrmTreeViewMenu" form.
// But this "sub-object" exists just to allow the configuration form to use the *same* routine to draw the tree so the test color could be tested this way.
TTreeViewMenuGenericRoutineAndVarHolder = class(TObject)
private
FContextMode: tvmContextMode;
FCaseSensitive: boolean;
FIgnoreAccents: boolean;
FShowWholeBranchIfMatch: boolean;
FSearchingText: string;
FShowShortcut: boolean;
FMayStopOnNode: boolean;
FBackgroundColor: TColor;
FShortcutColor: TColor;
FNormalTextColor: TColor;
FSecondaryTextColor: TColor;
FFoundTextColor: TColor;
FUnselectableTextColor: TColor;
FCursorColor: TColor;
FShortcutUnderCursor: TColor;
FNormalTextUnderCursor: TColor;
FSecondaryTextUnderCursor: TColor;
FFoundTextUnderCursor: TColor;
FUnselectableUnderCursor: TColor;
public
function AddTreeViewMenuItem(ATreeView: TTreeView; ParentNode: TTreeNode; const S: string; const SecondaryText: string = ''; TypeDispatcher: integer = 0; Data: Pointer = nil): TTreeNode;
procedure TreeViewMenuAdvancedCustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState; Stage: TCustomDrawStage; var {%H-}PaintImages, DefaultDraw: boolean);
property ContextMode: tvmContextMode read FContextMode write FContextMode;
property CaseSensitive: boolean read FCaseSensitive write FCaseSensitive;
property IgnoreAccents: boolean read FIgnoreAccents write FIgnoreAccents;
property ShowWholeBranchIfMatch: boolean read FShowWholeBranchIfMatch write FShowWholeBranchIfMatch;
property SearchingText: string read FSearchingText write FSearchingText;
property ShowShortcut: boolean read FShowShortcut write FShowShortcut;
property MayStopOnNode: boolean read FMayStopOnNode write FMayStopOnNode;
property BackgroundColor: TColor read FBackgroundColor write FBackgroundColor;
property ShortcutColor: TColor read FShortcutColor write FShortcutColor;
property NormalTextColor: TColor read FNormalTextColor write FNormalTextColor;
property SecondaryTextColor: TColor read FSecondaryTextColor write FSecondaryTextColor;
property FoundTextColor: TColor read FFoundTextColor write FFoundTextColor;
property UnselectableTextColor: TColor read FUnselectableTextColor write FUnselectableTextColor;
property CursorColor: TColor read FCursorColor write FCursorColor;
property ShortcutUnderCursor: TColor read FShortcutUnderCursor write FShortcutUnderCursor;
property NormalTextUnderCursor: TColor read FNormalTextUnderCursor write FNormalTextUnderCursor;
property SecondaryTextUnderCursor: TColor read FSecondaryTextUnderCursor write FSecondaryTextUnderCursor;
property FoundTextUnderCursor: TColor read FFoundTextUnderCursor write FFoundTextUnderCursor;
property UnselectableUnderCursor: TColor read FUnselectableUnderCursor write FUnselectableUnderCursor;
end;
{ TfrmTreeViewMenu }
TfrmTreeViewMenu = class(TForm)
pnlAll: TPanel;
lblSearchingEntry: TLabel;
edSearchingEntry: TEdit;
tvMainMenu: TTreeView;
tbOptions: TToolBar;
tbCaseSensitive: TToolButton;
tbIgnoreAccents: TToolButton;
tbShowWholeBranchOrNot: TToolButton;
tbDivider: TToolButton;
tbFullExpandOrNot: TToolButton;
tbClose: TToolBar;
tbConfigurationTreeViewMenus: TToolButton;
tbConfigurationTreeViewMenusColors: TToolButton;
tbCancelAndQuit: TToolButton;
pmCaseSensitiveOrNot: TPopupMenu;
pmiCaseSensitive: TMenuItem;
pmiNotCaseSensitive: TMenuItem;
pmIgnoreAccentsOrNot: TPopupMenu;
pmiIgnoreAccents: TMenuItem;
pmiNotIgnoreAccents: TMenuItem;
pmShowWholeBranchIfMatchOrNot: TPopupMenu;
pmiShowWholeBranchIfMatch: TMenuItem;
pmiNotShowWholeBranchIfMatch: TMenuItem;
pmFullExpandOrNot: TPopupMenu;
pmiFullExpand: TMenuItem;
pmiFullCollapse: TMenuItem;
imgListButton: TImageList;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
procedure FormCloseQuery(Sender: TObject; var {%H-}CanClose: boolean);
procedure FormDestroy(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
procedure tbCaseSensitiveClick(Sender: TObject);
procedure pmiCaseSensitiveOrNotClick(Sender: TObject);
procedure tbIgnoreAccentsClick(Sender: TObject);
procedure pmiIgnoreAccentsOrNotClick(Sender: TObject);
procedure tbShowWholeBranchOrNotClick(Sender: TObject);
procedure pmiShowWholeBranchIfMatchOrNotClick(Sender: TObject);
procedure tbFullExpandOrNotClick(Sender: TObject);
procedure pmiFullExpandOrNotClick(Sender: TObject);
procedure tbConfigurationTreeViewMenusClick(Sender: TObject);
procedure tbConfigurationTreeViewMenusColorsClick(Sender: TObject);
procedure tbCancelAndQuitClick(Sender: TObject);
procedure edSearchingEntryChange(Sender: TObject);
procedure tvMainMenuClick(Sender: TObject);
procedure tvMainMenuDblClick(Sender: TObject);
procedure tvMainMenuEnter(Sender: TObject);
procedure tvMainMenuMouseMove(Sender: TObject; {%H-}Shift: TShiftState; X, Y: integer);
procedure tvMainMenuSelectionChanged(Sender: TObject);
procedure tvMainMenuExpandOrCollapseClick(Sender: TObject; {%H-}Node: TTreeNode);
function isAtLeastOneItemVisibleAndSelectable: boolean;
procedure SelectNextVisibleItem;
procedure SelectPreviousVisibleItem;
procedure SelectFirstVisibleItem;
procedure SelectLastVisibleItem;
procedure SetShortcuts;
function WasAbleToSelectShortCutLetter(SearchKey: char): boolean;
function AttemptToExitWithCurrentSelection: boolean;
procedure SetSizeToLargestElement;
private
{ private declarations }
bTargetFixedWidth: boolean;
LastMousePos: TPoint;
public
{ public declarations }
iFinalSelectedIndex: integer;
TreeViewMenuGenericRoutineAndVarHolder: TTreeViewMenuGenericRoutineAndVarHolder;
procedure SetContextMode(WantedContextMode: tvmContextMode; WantedPosX, WantedPosY: integer; WantedWidth: integer = 0; WantedHeight: integer = 0);
procedure HideUnmatchingNode;
end;
// Actual routine called from the outside to help user to quickly select something using the "Tree View Menu" concept.
function GetUserChoiceFromTStrings(ATStrings: TStrings; ContextMode: tvmContextMode; WantedPosX, WantedPosY: integer; WantedWidth: integer = 0; WantedHeight: integer = 0): string;
function GetUserChoiceFromTreeViewMenuLoadedFromPopupMenu(pmAnyMenu: TMenu; ContextMode: tvmContextMode; WantedPosX, WantedPosY: integer; WantedWidth: integer = 0; WantedHeight: integer = 0): TMenuItem;
function GetUserChoiceFromKASToolBar(AKASToolBar: TKASToolBar; ContextMode: tvmContextMode; WantedPosX, WantedPosY, WantedWidth, WantedHeight: integer; var ReturnedTypeDispatcher: integer): Pointer;
var
frmTreeViewMenu: TfrmTreeViewMenu;
implementation
{$R *.lfm}
uses
//Lazarus, Free-Pascal, etc.
LCLType, LCLIntf, LazUTF8,
//DC
uLng, fMain, uGlobs, uAccentsUtils;
const
CONST_CANCEL_ACTION = -1;
CONST_CONFIG_ACTION = -2;
CONST_CONFIG_COLOR_ACTION = -3;
var
sTreeViewMenuShortcutString: string = '0123456789abcdefghijklmnopqrstuvwxyz';
{ TTreeMenuItem.Create }
constructor TTreeMenuItem.Create(PointerSourceData: Pointer);
begin
FPointerSourceData := PointerSourceData;
FTypeDispatcher := 0;
FSecondaryText := '';
FKeyboardShortcut := ' ';
end;
{ TTreeViewMenuGenericRoutineAndVarHolder.AddTreeViewMenuItem }
function TTreeViewMenuGenericRoutineAndVarHolder.AddTreeViewMenuItem(ATreeView: TTreeView; ParentNode: TTreeNode; const S: string; const SecondaryText: string = ''; TypeDispatcher: integer = 0; Data: Pointer = nil): TTreeNode;
var
ATreeMenuItem: TTreeMenuItem;
begin
ATreeMenuItem := TTreeMenuItem.Create(Data);
ATreeMenuItem.TypeDispatcher := TypeDispatcher;
ATreeMenuItem.KeyboardShortcut := ' ';
ATreeMenuItem.SecondaryText := SecondaryText;
Result := ATreeView.Items.AddChildObject(ParentNode, S, ATreeMenuItem);
end;
{ TTreeViewMenuGenericRoutineAndVarHolder.TreeViewMenuAdvancedCustomDrawItem }
procedure TTreeViewMenuGenericRoutineAndVarHolder.TreeViewMenuAdvancedCustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState; Stage: TCustomDrawStage; var PaintImages, DefaultDraw: boolean);
var
NodeRect: TRect;
sPart, sStringToShow: string;
iRenduX: integer;
iPosNormal: integer = 0;
iMatchingLengthInSource: integer = 0;
iTotalWidth: integer;
local_TextColor: TColor;
local_ShortcutColor: TColor;
local_SecondaryTextColor: TColor;
local_FoundTextColor: TColor;
begin
if TCustomTreeView(Sender).BackgroundColor <> BackgroundColor then
TCustomTreeView(Sender).BackgroundColor := BackgroundColor;
if TCustomTreeView(Sender).Color <> BackgroundColor then
TCustomTreeView(Sender).Color := BackgroundColor;
if Stage = cdPostPaint then
begin
if Node <> nil then
begin
NodeRect := Node.DisplayRect(True);
iTotalWidth := ((TCustomTreeView(Sender).Width - Node.DisplayTextLeft) - 25);
NodeRect.Right := NodeRect.Left + iTotalWidth;
if cdsSelected in State then
begin
// Draw something under selection.
TTreeView(Sender).Canvas.Brush.Color := CursorColor;
local_ShortcutColor := ShortcutUnderCursor;
local_SecondaryTextColor := SecondaryTextUnderCursor;
if (Node.Count = 0) or (FMayStopOnNode) then
local_TextColor := NormalTextUnderCursor
else
local_TextColor := UnselectableUnderCursor;
local_FoundTextColor := FoundTextUnderCursor;
end
else
begin
// Draw something unselected.
TTreeView(Sender).Canvas.Brush.Color := BackgroundColor;
local_ShortcutColor := ShortcutColor;
local_SecondaryTextColor := SecondaryTextColor;
if (Node.Count = 0) or (FMayStopOnNode) then
local_TextColor := NormalTextColor
else
local_TextColor := UnselectableTextColor;
local_FoundTextColor := FoundTextColor;
end;
TTreeView(Sender).Canvas.Brush.Style := bsSolid;
TTreeView(Sender).Canvas.FillRect(NodeRect);
TTreeView(Sender).Canvas.Brush.Style := bsClear;
sStringToShow := Node.Text;
iRenduX := NodeRect.Left + 3;
// Short the shortcut name if config wants it AND if we have one to give.
if (FShowShortcut) and (TTreeMenuItem(Node.Data).KeyboardShortcut <> ' ') then
begin
TTreeView(Sender).Canvas.Font.Color := local_ShortcutColor;
sPart := '[' + TTreeMenuItem(Node.Data).KeyboardShortcut + '] ';
TTreeView(Sender).Canvas.TextOut(iRenduX, NodeRect.Top + 1, sPart);
iRenduX := iRenduX + TTreeView(Sender).Canvas.TextWidth(sPart);
end;
if (Node.Count = 0) or (FMayStopOnNode or ShowWholeBranchIfMatch) then
begin
while sStringToShow <> '' do
begin
iPosNormal := PosOfSubstrWithVersatileOptions(FSearchingText, sStringToShow, CaseSensitive, IgnoreAccents, iMatchingLengthInSource);
if iPosNormal > 0 then
begin
if iPosNormal > 1 then
begin
// What we have in black prior the red...
TTreeView(Sender).Canvas.Font.Color := local_TextColor;
sPart := UTF8LeftStr(sStringToShow, pred(iPosNormal));
TTreeView(Sender).Canvas.TextOut(iRenduX, NodeRect.Top + 1, sPart);
iRenduX := iRenduX + TTreeView(Sender).Canvas.TextWidth(sPart);
sStringToShow := UTF8RightStr(sStringToShow, ((UTF8Length(sStringToShow) - iPosNormal) + 1));
end;
// What we have in red...
TTreeView(Sender).Canvas.Font.Style := TTreeView(Sender).Canvas.Font.Style + [fsUnderline, fsBold];
TTreeView(Sender).Canvas.Font.Color := local_FoundTextColor;
sPart := UTF8Copy(sStringToShow, 1, iMatchingLengthInSource);
TTreeView(Sender).Canvas.TextOut(iRenduX, NodeRect.Top + 1, sPart);
iRenduX := iRenduX + TTreeView(Sender).Canvas.TextWidth(sPart);
TTreeView(Sender).Canvas.Font.Style := TTreeView(Sender).Canvas.Font.Style - [fsUnderline, fsBold];
sStringToShow := UTF8RightStr(sStringToShow, ((UTF8Length(sStringToShow) - iMatchingLengthInSource)));
end
else
begin
TTreeView(Sender).Canvas.Font.Color := local_TextColor;
TTreeView(Sender).Canvas.TextOut(iRenduX, NodeRect.Top + 1, sStringToShow);
iRenduX := iRenduX + TTreeView(Sender).Canvas.TextWidth(sStringToShow);
sStringToShow := '';
end;
end;
end
else
begin
TTreeView(Sender).Canvas.Font.Color := local_TextColor;
TTreeView(Sender).Canvas.TextOut(iRenduX, NodeRect.Top + 1, sStringToShow);
iRenduX := iRenduX + TTreeView(Sender).Canvas.TextWidth(sStringToShow);
end;
if TTreeMenuItem(Node.Data).SecondaryText <> '' then
begin
TTreeView(Sender).Canvas.Font.Color := local_SecondaryTextColor;
TTreeView(Sender).Canvas.Font.Style := TTreeView(Sender).Canvas.Font.Style + [fsItalic];
TTreeView(Sender).Canvas.TextOut(iRenduX + 4, NodeRect.Top + 1 + 1, TTreeMenuItem(Node.Data).SecondaryText);
//If we ever add something else after one day: iRenduX := iRenduX+4 + TTreeView(Sender).Canvas.TextWidth(TTreeMenuItem(Node.Data).SecondaryText);
TTreeView(Sender).Canvas.Font.Style := TTreeView(Sender).Canvas.Font.Style - [fsItalic];
end;
DefaultDraw := False;
end;
end;
end;
{ TfrmTreeViewMenu.FormCreate }
procedure TfrmTreeViewMenu.FormCreate(Sender: TObject);
begin
bTargetFixedWidth := False;
LastMousePos.x := -1;
LastMousePos.y := -1;
iFinalSelectedIndex := CONST_CANCEL_ACTION;
TreeViewMenuGenericRoutineAndVarHolder := TTreeViewMenuGenericRoutineAndVarHolder.Create;
TreeViewMenuGenericRoutineAndVarHolder.BackgroundColor := gTVMBackgroundColor;
TreeViewMenuGenericRoutineAndVarHolder.ShortcutColor := gTVMShortcutColor;
TreeViewMenuGenericRoutineAndVarHolder.NormalTextColor := gTVMNormalTextColor;
TreeViewMenuGenericRoutineAndVarHolder.SecondaryTextColor := gTVMSecondaryTextColor;
TreeViewMenuGenericRoutineAndVarHolder.FoundTextColor := gTVMFoundTextColor;
TreeViewMenuGenericRoutineAndVarHolder.UnselectableTextColor := gTVMUnselectableTextColor;
TreeViewMenuGenericRoutineAndVarHolder.CursorColor := gTVMCursorColor;
TreeViewMenuGenericRoutineAndVarHolder.ShortcutUnderCursor := gTVMShortcutUnderCursor;
TreeViewMenuGenericRoutineAndVarHolder.NormalTextUnderCursor := gTVMNormalTextUnderCursor;
TreeViewMenuGenericRoutineAndVarHolder.SecondaryTextUnderCursor := gTVMSecondaryTextUnderCursor;
TreeViewMenuGenericRoutineAndVarHolder.FoundTextUnderCursor := gTVMFoundTextUnderCursor;
TreeViewMenuGenericRoutineAndVarHolder.UnselectableUnderCursor := gTVMUnselectableUnderCursor;
tvMainMenu.BackgroundColor := gTVMBackgroundColor;
tvMainMenu.Color := gTVMBackgroundColor;
tvMainMenu.OnAdvancedCustomDrawItem := @TreeViewMenuGenericRoutineAndVarHolder.TreeViewMenuAdvancedCustomDrawItem;
edSearchingEntryChange(nil);
end;
{ TfrmTreeViewMenu.FormClose }
procedure TfrmTreeViewMenu.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
case iFinalSelectedIndex of
CONST_CANCEL_ACTION: ModalResult := mrCancel;
CONST_CONFIG_ACTION: ModalResult := mrYes;
CONST_CONFIG_COLOR_ACTION: ModalResult := mrAll;
else
ModalResult := mrOk;
end;
end;
{ TfrmTreeViewMenu.FormCloseQuery }
procedure TfrmTreeViewMenu.FormCloseQuery(Sender: TObject; var CanClose: boolean);
begin
tvMainMenu.OnExpanded := nil;
tvMainMenu.OnCollapsed := nil;
tvMainMenu.OnSelectionChanged := nil;
Application.ProcessMessages;
//We saved our options. We're aware it will save it even if user CANCEL the action but after a few test, the author of these lines feels it is better this way.
gTreeViewMenuOptions[Ord(TreeViewMenuGenericRoutineAndVarHolder.ContextMode)].CaseSensitive := TreeViewMenuGenericRoutineAndVarHolder.CaseSensitive;
gTreeViewMenuOptions[Ord(TreeViewMenuGenericRoutineAndVarHolder.ContextMode)].IgnoreAccents := TreeViewMenuGenericRoutineAndVarHolder.IgnoreAccents;
gTreeViewMenuOptions[Ord(TreeViewMenuGenericRoutineAndVarHolder.ContextMode)].ShowWholeBranchIfMatch := TreeViewMenuGenericRoutineAndVarHolder.ShowWholeBranchIfMatch;
end;
{ TfrmTreeViewMenu.FormDestroy }
procedure TfrmTreeViewMenu.FormDestroy(Sender: TObject);
begin
FreeAndNil(TreeViewMenuGenericRoutineAndVarHolder);
inherited;
end;
{ TfrmTreeViewMenu.FormKeyDown }
procedure TfrmTreeViewMenu.FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
var
ChoiceNode: TTreeNode;
begin
if edSearchingEntry.Focused then
begin
case Key of
VK_HOME: // Home Key
begin
if SSCTRL in Shift then
begin
SelectFirstVisibleItem;
Key := 0;
end;
end;
VK_END: // End Key
begin
if SSCTRL in Shift then
begin
SelectLastVisibleItem;
Key := 0;
end;
end;
end;
end;
if ssALT in Shift then
begin
case Key of
VK_0..VK_9, VK_A..VK_Z: if WasAbleToSelectShortCutLetter(char(Key)) then
Key := 0;
end;
if (Key = 0) and gTreeViewMenuShortcutExit then
AttemptToExitWithCurrentSelection;
end;
case Key of
VK_UP: // Up Arrow Key
begin
SelectPreviousVisibleItem;
Key := 0;
end;
VK_DOWN: // Down Arrow Key
begin
SelectNextVisibleItem;
Key := 0;
end;
VK_END: // End Key - Let's play tricky: if cursor is at the end into the edit box, let's assume user pressed the "end" key to go to the end in the list.
begin
if edSearchingEntry.SelStart >= utf8Length(edSearchingEntry.Text) then
begin
SelectLastVisibleItem;
Key := 0;
end;
end;
VK_HOME: // Home Key - Let's play tricky: if cursor is at the beginning into the edit box, let's assume user pressed the "home" key to go to the first in the list.
begin
if edSearchingEntry.SelStart = 0 then
begin
SelectFirstVisibleItem;
Key := 0;
end;
end;
VK_RETURN: // Enter key
begin
ChoiceNode := tvMainMenu.Selected;
if ChoiceNode <> nil then
begin
if (TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode) or (ChoiceNode.Count = 0) then
begin
Key := 0;
AttemptToExitWithCurrentSelection;
end;
end;
end;
VK_ESCAPE: // Escape key
begin
Key := 0;
Close;
end;
end;
end;
{ TfrmTreeViewMenu.tbCaseSensitiveClick }
procedure TfrmTreeViewMenu.tbCaseSensitiveClick(Sender: TObject);
var
pmiToSwitchTo: TMenuItem = nil;
begin
if pmiNotCaseSensitive.Checked then pmiToSwitchTo := pmiCaseSensitive
else if pmiCaseSensitive.Checked then pmiToSwitchTo := pmiNotCaseSensitive;
if pmiToSwitchTo <> nil then
begin
pmiToSwitchTo.Checked := True;
pmiCaseSensitiveOrNotClick(pmiToSwitchTo);
end;
end;
{ TfrmTreeViewMenu.pmiCaseSensitiveOrNotClick }
procedure TfrmTreeViewMenu.pmiCaseSensitiveOrNotClick(Sender: TObject);
begin
begin
with Sender as TMenuItem do
begin
tbCaseSensitive.ImageIndex := ImageIndex;
tbCaseSensitive.Hint := Caption;
end;
edSearchingEntryChange(edSearchingEntry);
end;
end;
{ TfrmTreeViewMenu.tbIgnoreAccentsClick }
procedure TfrmTreeViewMenu.tbIgnoreAccentsClick(Sender: TObject);
var
pmiToSwitchTo: TMenuItem = nil;
begin
if pmiIgnoreAccents.Checked then pmiToSwitchTo := pmiNotIgnoreAccents
else if pmiNotIgnoreAccents.Checked then pmiToSwitchTo := pmiIgnoreAccents;
if pmiToSwitchTo <> nil then
begin
pmiToSwitchTo.Checked := True;
pmiIgnoreAccentsOrNotClick(pmiToSwitchTo);
end;
end;
{ TfrmTreeViewMenu.pmiIgnoreAccentsOrNotClick }
procedure TfrmTreeViewMenu.pmiIgnoreAccentsOrNotClick(Sender: TObject);
begin
with Sender as TMenuItem do
begin
tbIgnoreAccents.ImageIndex := ImageIndex;
tbIgnoreAccents.Hint := Caption;
end;
edSearchingEntryChange(edSearchingEntry);
end;
{ TfrmTreeViewMenu.tbShowWholeBranchOrNotClick }
procedure TfrmTreeViewMenu.tbShowWholeBranchOrNotClick(Sender: TObject);
var
pmiToSwitchTo: TMenuItem = nil;
begin
if pmiShowWholeBranchIfMatch.Checked then pmiToSwitchTo := pmiNotShowWholeBranchIfMatch
else if pmiNotShowWholeBranchIfMatch.Checked then pmiToSwitchTo := pmiShowWholeBranchIfMatch;
if pmiToSwitchTo <> nil then
begin
pmiToSwitchTo.Checked := True;
pmiShowWholeBranchIfMatchOrNotClick(pmiToSwitchTo);
end;
end;
{ TfrmTreeViewMenu.pmiShowWholeBranchIfMatchOrNotClick }
procedure TfrmTreeViewMenu.pmiShowWholeBranchIfMatchOrNotClick(Sender: TObject);
begin
with Sender as TMenuItem do
begin
tbShowWholeBranchOrNot.ImageIndex := ImageIndex;
tbShowWholeBranchOrNot.Hint := Caption;
end;
edSearchingEntryChange(edSearchingEntry);
end;
{ TfrmTreeViewMenu.tbFullExpandOrNotClick }
procedure TfrmTreeViewMenu.tbFullExpandOrNotClick(Sender: TObject);
var
pmiToSwitchTo: TMenuItem = nil;
begin
if pmiFullExpand.Checked then pmiToSwitchTo := pmiFullCollapse
else if pmiFullCollapse.Checked then pmiToSwitchTo := pmiFullExpand;
if pmiToSwitchTo <> nil then
begin
pmiToSwitchTo.Checked := True;
pmiFullExpandOrNotClick(pmiToSwitchTo);
end;
end;
{ TfrmTreeViewMenu.pmiFullExpandOrNotClick }
procedure TfrmTreeViewMenu.pmiFullExpandOrNotClick(Sender: TObject);
begin
with Sender as TMenuItem do
begin
tbFullExpandOrNot.ImageIndex := ImageIndex;
tbFullExpandOrNot.Hint := Caption;
end;
if pmiFullExpand.Checked then
tvMainMenu.FullExpand
else
tvMainMenu.FullCollapse;
end;
{ TfrmTreeViewMenu.tbConfigurationTreeViewMenusClick }
procedure TfrmTreeViewMenu.tbConfigurationTreeViewMenusClick(Sender: TObject);
begin
iFinalSelectedIndex := CONST_CONFIG_ACTION;
Close;
end;
{ TfrmTreeViewMenu.tbConfigurationTreeViewMenusColorsClick }
procedure TfrmTreeViewMenu.tbConfigurationTreeViewMenusColorsClick(Sender: TObject);
begin
iFinalSelectedIndex := CONST_CONFIG_COLOR_ACTION;
Close;
end;
{ TfrmTreeViewMenu.tbCancelAndQuitClick }
procedure TfrmTreeViewMenu.tbCancelAndQuitClick(Sender: TObject);
begin
Close;
end;
{ TfrmTreeViewMenu.edSearchingEntryChange }
procedure TfrmTreeViewMenu.edSearchingEntryChange(Sender: TObject);
begin
TreeViewMenuGenericRoutineAndVarHolder.CaseSensitive := pmiCaseSensitive.Checked;
TreeViewMenuGenericRoutineAndVarHolder.IgnoreAccents := pmiIgnoreAccents.Checked;
TreeViewMenuGenericRoutineAndVarHolder.ShowWholeBranchIfMatch := pmiShowWholeBranchIfMatch.Checked;
TreeViewMenuGenericRoutineAndVarHolder.SearchingText := edSearchingEntry.Text;
TreeViewMenuGenericRoutineAndVarHolder.ShowShortcut := gTreeViewMenuUseKeyboardShortcut;
if pmiIgnoreAccents.Checked then TreeViewMenuGenericRoutineAndVarHolder.SearchingText := NormalizeAccentedChar(TreeViewMenuGenericRoutineAndVarHolder.SearchingText);
if not pmiCaseSensitive.Checked then TreeViewMenuGenericRoutineAndVarHolder.SearchingText := UTF8UpperCase(TreeViewMenuGenericRoutineAndVarHolder.SearchingText);
HideUnmatchingNode;
end;
{ TfrmTreeViewMenu.tvMainMenuClick }
procedure TfrmTreeViewMenu.tvMainMenuClick(Sender: TObject);
begin
if gTreeViewMenuSingleClickExit then AttemptToExitWithCurrentSelection;
end;
{ TfrmTreeViewMenu.tvMainMenuDblClick }
procedure TfrmTreeViewMenu.tvMainMenuDblClick(Sender: TObject);
begin
if gTreeViewMenuDoubleClickExit then AttemptToExitWithCurrentSelection;
end;
{ TfrmTreeViewMenu.tvMainMenuEnter }
procedure TfrmTreeViewMenu.tvMainMenuEnter(Sender: TObject);
begin
if edSearchingEntry.CanFocus then edSearchingEntry.SetFocus;
end;
{ TfrmTreeViewMenu.tvMainMenuMouseMove }
procedure TfrmTreeViewMenu.tvMainMenuMouseMove(Sender: TObject; Shift: TShiftState; X, Y: integer);
var
ANode: TTreeNode;
begin
if (LastMousePos.x <> -1) and (LastMousePos.y <> -1) then
begin
ANode := tvMainMenu.GetNodeAt(X, Y);
if ANode <> nil then
if not ANode.Selected then
ANode.Selected := True;
end;
LastMousePos.x := X;
LastMousePos.y := Y;
end;
{ TfrmTreeViewMenu.tvMainMenuSelectionChanged }
procedure TfrmTreeViewMenu.tvMainMenuSelectionChanged(Sender: TObject);
begin
tvMainMenu.BeginUpdate;
SetShortcuts;
tvMainMenu.EndUpdate;
end;
{ TfrmTreeViewMenu.ExpandOrCollapseClick }
procedure TfrmTreeViewMenu.tvMainMenuExpandOrCollapseClick(Sender: TObject; Node: TTreeNode);
begin
if edSearchingEntry.Text = '' then
begin
tvMainMenu.BeginUpdate;
SetShortcuts;
tvMainMenu.EndUpdate;
end;
end;
{ TfrmTreeViewMenu.isAtLeastOneItemVisibleAndSelectable }
function TfrmTreeViewMenu.isAtLeastOneItemVisibleAndSelectable: boolean;
var
iSearchIndex: integer;
begin
Result := False;
if tvMainMenu.Items.Count > 0 then
begin
iSearchIndex := 0;
while (not Result) and (iSearchIndex < tvMainMenu.Items.Count) do
begin
if tvMainMenu.Items[iSearchIndex].Visible then
Result := ((tvMainMenu.Items[iSearchIndex].Count = 0) or TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode);
Inc(iSearchIndeX);
end;
end;
end;
{ TfrmTreeViewMenu.SelectNextVisibleItem }
procedure TfrmTreeViewMenu.SelectNextVisibleItem;
var
iCurrentIndex: integer;
begin
if isAtLeastOneItemVisibleAndSelectable then
begin
if tvMainMenu.Selected = nil then
iCurrentIndex := -1
else
iCurrentIndex := tvMainMenu.Selected.AbsoluteIndex;
begin
repeat
iCurrentIndex := ((iCurrentIndex + 1) mod tvMainMenu.Items.Count);
until (tvMainMenu.Items[iCurrentIndex].Visible and ((tvMainMenu.Items[iCurrentIndex].Count = 0) or TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode));
tvMainMenu.Items[iCurrentIndex].Selected := True;
end;
end;
end;
{ TfrmTreeViewMenu.SelectPreviousVisibleItem }
procedure TfrmTreeViewMenu.SelectPreviousVisibleItem;
var
iCurrentIndex: integer;
begin
if isAtLeastOneItemVisibleAndSelectable then
begin
if tvMainMenu.Selected = nil then
iCurrentIndex := -1
else
iCurrentIndex := tvMainMenu.Selected.AbsoluteIndex;
begin
repeat
if iCurrentIndex = 0 then
iCurrentIndex := pred(tvMainMenu.Items.Count)
else
Dec(iCurrentIndex);
until (tvMainMenu.Items[iCurrentIndex].Visible and ((tvMainMenu.Items[iCurrentIndex].Count = 0) or TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode));
tvMainMenu.Items[iCurrentIndex].Selected := True;
end;
end;
end;
{ TfrmTreeViewMenu.SelectFirstVisibleItem }
procedure TfrmTreeViewMenu.SelectFirstVisibleItem;
var
iCurrentIndex: integer;
begin
if isAtLeastOneItemVisibleAndSelectable then
begin
iCurrentIndex := -1;
repeat
iCurrentIndex := ((iCurrentIndex + 1) mod tvMainMenu.Items.Count);
until (tvMainMenu.Items[iCurrentIndex].Visible and ((tvMainMenu.Items[iCurrentIndex].Count = 0) or TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode));
tvMainMenu.Items[iCurrentIndex].Selected := True;
end;
end;
{ TfrmTreeViewMenu.SelectLastVisibleItem }
procedure TfrmTreeViewMenu.SelectLastVisibleItem;
var
iCurrentIndex: integer;
begin
if isAtLeastOneItemVisibleAndSelectable then
begin
iCurrentIndex := tvMainMenu.Items.Count;
repeat
if iCurrentIndex = 0 then
iCurrentIndex := pred(tvMainMenu.Items.Count)
else
Dec(iCurrentIndex);
until (tvMainMenu.Items[iCurrentIndex].Visible and ((tvMainMenu.Items[iCurrentIndex].Count = 0) or TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode));
tvMainMenu.Items[iCurrentIndex].Selected := True;
end;
end;
{ TfrmTreeViewMenu.SetShortcuts }
procedure TfrmTreeViewMenu.SetShortcuts;
var
iCurrentShortcut: integer = 1;
function GetCurrentShortcutLetter: char;
begin
if iCurrentShortcut > 0 then
begin
Result := sTreeViewMenuShortcutString[iCurrentShortcut];
Inc(iCurrentShortcut);
if iCurrentShortcut > length(sTreeViewMenuShortcutString) then
iCurrentShortcut := 0;
end
else
begin
Result := ' ';
end;
end;
function GetShortcutLetterForThisNode(paramNode: TTreeNode): char;
begin
Result := ' ';
if paramNode.Visible then
if (paramNode.Count = 0) or TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode then
Result := GetCurrentShortcutLetter;
end;
var
iNode, iNbOfVisibleNode: integer;
ANode: TTreeNode;
begin
for iNode := 0 to pred(tvMainMenu.Items.Count) do
TTreeMenuItem(tvMainMenu.Items[iNode].Data).KeyboardShortcut := ' ';
iNbOfVisibleNode := tvMainMenu.Height div tvMainMenu.DefaultItemHeight;
iNode := 0;
while iNode < iNbOfVisibleNode do
begin
ANode := tvMainMenu.GetNodeAt(100, (iNode * tvMainMenu.DefaultItemHeight));
if ANode <> nil then
begin
if (ANode.Count = 0) or TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode then
TTreeMenuItem(ANode.Data).KeyboardShortcut :=
GetShortcutLetterForThisNode(ANode);
end;
Inc(iNode);
end;
end;
{ TfrmTreeViewMenu.WasAbleToSelectShortCutLetter }
function TfrmTreeViewMenu.WasAbleToSelectShortCutLetter(SearchKey: char): boolean;
var
iSearchIndex: integer;
begin
Result := False;
if tvMainMenu.Items.Count > 0 then
begin
iSearchIndex := 0;
while (not Result) and (iSearchIndex < tvMainMenu.Items.Count) do
begin
if (LowerCase(TTreeMenuItem(tvMainMenu.Items[iSearchIndex].Data).KeyboardShortcut) = LowerCase(SearchKey)) and (tvMainMenu.Items[iSearchIndex].Visible) then
Result := True
else
Inc(iSearchIndeX);
end;
end;
if Result then
tvMainMenu.Items[iSearchIndex].Selected := True;
end;
{ TfrmTreeViewMenu.AttemptToExitWithCurrentSelection }
function TfrmTreeViewMenu.AttemptToExitWithCurrentSelection: boolean;
begin
Result := False;
if tvMainMenu.Selected <> nil then
if (TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode) or (tvMainMenu.Selected.Count = 0) then
begin
Result := True;
iFinalSelectedIndex := tvMainMenu.Selected.AbsoluteIndex;
Close;
end;
end;
{ TfrmTreeViewMenu.SetSizeToLargestElement }
procedure TfrmTreeViewMenu.SetSizeToLargestElement;
var
iNode, iLargest: integer;
begin
iLargest := 0;
for iNode := 0 to pred(tvMainMenu.Items.Count) do
if tvMainMenu.Items[iNode].DisplayRect(True).Right > iLargest then
iLargest := tvMainMenu.Items[iNode].DisplayRect(True).Right;
Width := iLargest + 50;
end;
{ TfrmTreeViewMenu.SetContextMode }
procedure TfrmTreeViewMenu.SetContextMode(WantedContextMode: tvmContextMode; WantedPosX, WantedPosY: integer; WantedWidth: integer = 0; WantedHeight: integer = 0);
var
pmiToSwitchTo: TMenuItem = nil;
begin
TreeViewMenuGenericRoutineAndVarHolder.ContextMode := WantedContextMode;
// Let's set our option checked menu item AND our internal options according to settings saved previously for that context.
if gTreeViewMenuOptions[Ord(TreeViewMenuGenericRoutineAndVarHolder.ContextMode)].CaseSensitive then pmiToSwitchTo := pmiCaseSensitive else pmiToSwitchTo := pmiNotCaseSensitive;
pmiToSwitchTo.Checked := True;
pmiCaseSensitiveOrNotClick(pmiToSwitchTo);
if gTreeViewMenuOptions[Ord(TreeViewMenuGenericRoutineAndVarHolder.ContextMode)].IgnoreAccents then pmiToSwitchTo := pmiIgnoreAccents else pmiToSwitchTo := pmiNotIgnoreAccents;
pmiToSwitchTo.Checked := True;
pmiIgnoreAccentsOrNotClick(pmiToSwitchTo);
if gTreeViewMenuOptions[Ord(TreeViewMenuGenericRoutineAndVarHolder.ContextMode)].ShowWholeBranchIfMatch then pmiToSwitchTo := pmiShowWholeBranchIfMatch else pmiToSwitchTo := pmiNotShowWholeBranchIfMatch;
pmiToSwitchTo.Checked := True;
pmiShowWholeBranchIfMatchOrNotClick(pmiToSwitchTo);
// We set the appropriate title to give feedback to user.
case TreeViewMenuGenericRoutineAndVarHolder.ContextMode of
tvmcHotDirectory: lblSearchingEntry.Caption := rsStrTVMChooseHotDirectory;
tvmcFavoriteTabs: lblSearchingEntry.Caption := rsStrTVMChooseFavoriteTabs;
tvmcDirHistory: lblSearchingEntry.Caption := rsStrTVMChooseDirHistory;
tvmcViewHistory: lblSearchingEntry.Caption := rsStrTVMChooseViewHistory;
tvmcKASToolBar: lblSearchingEntry.Caption := rsStrTVMChooseFromToolbar;
tvmcMainMenu: lblSearchingEntry.Caption := rsStrTVMChooseFromMainMenu;
tvmcCommandLineHistory: lblSearchingEntry.Caption := rsStrTVMChooseFromCmdLineHistory;
tvmcFileSelectAssistant: lblSearchingEntry.Caption := rsStrTVMChooseYourFileOrDir;
else
raise Exception.Create(rsMsgUnexpectedUsageTreeViewMenu);
end;
// We set the "look and feel" of the form for the user.
case TreeViewMenuGenericRoutineAndVarHolder.ContextMode of
tvmcHotDirectory, tvmcFavoriteTabs, tvmcKASToolBar, tvmcMainMenu: TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode := False;
tvmcDirHistory, tvmcViewHistory, tvmcCommandLineHistory: TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode := False;
tvmcFileSelectAssistant: TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode := True; // But on first revision, won't happen
else
raise Exception.Create(rsMsgUnexpectedUsageTreeViewMenu);
end;
case TreeViewMenuGenericRoutineAndVarHolder.ContextMode of
tvmcHotDirectory, tvmcFavoriteTabs, tvmcDirHistory, tvmcViewHistory,
tvmcKASToolBar, tvmcMainMenu, tvmcCommandLineHistory, tvmcFileSelectAssistant:
begin
Left := WantedPosX;
Top := WantedPosY;
if WantedHeight <> 0 then Height := WantedHeight;
if (WantedWidth <> 0) and (WantedHeight <> 0) then
begin
bTargetFixedWidth := True;
Width := WantedWidth;
end;
BorderStyle := bsNone;
end;
else
begin
raise Exception.Create(rsMsgUnexpectedUsageTreeViewMenu);
end;
end;
end;
{ TfrmTreeViewMenu.HideUnmatchingNode }
// The *key* routine off all this.
// Routine will make visible in tree view the items that match with what the user has typed.
// Eliminating from the view the non matching item helps user to quickly see what he was looking for.
// So choosing it through a lot of data speed up things.
procedure TfrmTreeViewMenu.HideUnmatchingNode;
var
iDummy: integer = 0;
iNode: integer;
nFirstMatchingNode: TTreeNode = nil;
//WARNING: The following procedure is recursive and so may call itself back!
procedure KeepMeThisWholeBranch(paramNode: TTreeNode);
begin
while paramNode <> nil do
begin
paramNode.Visible := True;
if paramNode.Count > 0 then KeepMeThisWholeBranch(paramNode.Items[0]);
paramNode := paramNode.GetNextSibling;
end;
end;
//WARNING: The following procedure is recursive and so may call itself back!
function UpdateVisibilityAccordingToSearchingString(paramNode: TTreeNode): boolean;
begin
Result := False;
while paramNode <> nil do
begin
if paramNode.Count = 0 then
begin
paramNode.Visible := (PosOfSubstrWithVersatileOptions(TreeViewMenuGenericRoutineAndVarHolder.SearchingText, paramNode.Text, pmiCaseSensitive.Checked, pmiIgnoreAccents.Checked, iDummy) <> 0);
end
else
begin
if pmiShowWholeBranchIfMatch.Checked then
begin
paramNode.Visible := (PosOfSubstrWithVersatileOptions(TreeViewMenuGenericRoutineAndVarHolder.SearchingText, paramNode.Text, pmiCaseSensitive.Checked, pmiIgnoreAccents.Checked, iDummy) <> 0);
if paramNode.Visible then
begin
KeepMeThisWholeBranch(paramNode);
end
else
begin
paramNode.Visible := UpdateVisibilityAccordingToSearchingString(paramNode.Items[0]);
end;
end
else
begin
paramNode.Visible := UpdateVisibilityAccordingToSearchingString(paramNode.Items[0]);
if not paramNode.Visible then
begin
if TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode then
paramNode.Visible := (PosOfSubstrWithVersatileOptions(TreeViewMenuGenericRoutineAndVarHolder.SearchingText, paramNode.Text, pmiCaseSensitive.Checked, pmiIgnoreAccents.Checked, iDummy) <> 0);
end;
end;
end;
if paramNode.Visible then
begin
Result := True;
if nFirstMatchingNode = nil then
if (paramNode.Count = 0) or TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode then
nFirstMatchingNode := paramNode;
end;
paramNode := paramNode.GetNextSibling;
end;
end;
begin
tbFullExpandOrNot.Visible := (TreeViewMenuGenericRoutineAndVarHolder.SearchingText = '');
if tvMainMenu.Items.Count > 0 then
begin
tvMainMenu.BeginUpdate;
try
if TreeViewMenuGenericRoutineAndVarHolder.SearchingText <> '' then
begin
UpdateVisibilityAccordingToSearchingString(tvMainMenu.Items.Item[0]);
end
else
begin
for iNode := 0 to pred(tvMainMenu.Items.Count) do
begin
tvMainMenu.Items.Item[iNode].Visible := True;
if nFirstMatchingNode = nil then
if (tvMainMenu.Items.Item[iNode].Count = 0) or TreeViewMenuGenericRoutineAndVarHolder.MayStopOnNode then
nFirstMatchingNode := tvMainMenu.Items.Item[iNode];
end;
end;
if TreeViewMenuGenericRoutineAndVarHolder.SearchingText <> '' then
begin
for iNode := pred(tvMainMenu.Items.Count) downto 0 do
tvMainMenu.Items.Item[iNode].MakeVisible;
end
else
begin
pmiFullExpand.Checked := True;
pmiFullExpandOrNotClick(pmiFullExpand);
end;
if nFirstMatchingNode <> nil then
nFirstMatchingNode.Selected := True;
SetShortcuts;
finally
tvMainMenu.EndUpdate;
end;
end;
end;
{ GetUserChoiceFromTStrings }
// We provide a "TStrings" for input.
// Function will show strings into a ttreeview.
// User select the one he wants.
// Function returns the chosen string.
// If user cancel action, returned string is empty.
function GetUserChoiceFromTStrings(ATStrings: TStrings; ContextMode: tvmContextMode; WantedPosX, WantedPosY: integer; WantedWidth: integer = 0; WantedHeight: integer = 0): string;
var
iIndex: integer;
local_Result: integer;
begin
Result := '';
if ATStrings.Count > 0 then
begin
frmTreeViewMenu := TfrmTreeViewMenu.Create(frmMain);
try
frmTreeViewMenu.SetContextMode(ContextMode, WantedPosX, WantedPosY, WantedWidth, WantedHeight);
frmTreeViewMenu.tvMainMenu.BeginUpdate;
for iIndex := 0 to pred(ATStrings.Count) do
frmTreeViewMenu.TreeViewMenuGenericRoutineAndVarHolder.AddTreeViewMenuItem(frmTreeViewMenu.tvMainMenu, nil, ATStrings.Strings[iIndex], '', 0, nil);
frmTreeViewMenu.HideUnmatchingNode;
if not frmTreeViewMenu.bTargetFixedWidth then frmTreeViewMenu.SetSizeToLargestElement;
frmTreeViewMenu.tvMainMenu.EndUpdate;
local_Result := frmTreeViewMenu.ShowModal;
case local_Result of
mrOk: Result := frmTreeViewMenu.tvMainMenu.Items[frmTreeViewMenu.iFinalSelectedIndex].Text;
mrYes: frmMain.Commands.cm_ConfigTreeViewMenus([]);
mrAll: frmMain.Commands.cm_ConfigTreeViewMenusColors([]);
end;
finally
FreeAndNil(frmTreeViewMenu);
end;
end;
end;
{ GetUserChoiceFromTreeViewMenuLoadedFromPopupMenu }
// We provide a "TMenu" for input (either a popup menu or a mainmenu).
// Function will show items into a ttreeview.
// User select the one he wants.
// Function returns the chosen TMenuItem.
// If user cancel action, returned TMenuItem is nil.
function GetUserChoiceFromTreeViewMenuLoadedFromPopupMenu(pmAnyMenu: TMenu; ContextMode: tvmContextMode; WantedPosX, WantedPosY: integer; WantedWidth: integer = 0; WantedHeight: integer = 0): TMenuItem;
var
RootNode: TTreeNode;
iMenuItem: integer;
local_Result: integer;
function NormalizeMenuCaption(sMenuCaption: string): string;
var
iChar: integer;
begin
if UTF8Pos('&', sMenuCaption) = 0 then
begin
Result := sMenuCaption;
end
else
begin
Result := '';
iChar := 1;
while iChar <= UTF8Length(sMenuCaption) do
begin
if copy(sMenuCaption, iChar, 1) <> '&' then
Result := Result + copy(sMenuCaption, iChar, 1)
else
begin
if iChar < UTF8Length(sMenuCaption) then
begin
if copy(sMenuCaption, iChar + 1, 1) = '&' then
begin
Result := Result + '&';
Inc(iChar);
end;
end;
end;
Inc(iChar);
end;
end;
end;
//WARNING: This procedure is recursive and may call itself!
procedure RecursiveAddMenuBranch(AMenuItem: TMenuItem; ANode: TTreeNode);
var
iIndexSubMenuItem: integer;
ASubNode: TTreeNode;
begin
for iIndexSubMenuItem := 0 to pred(AMenuItem.Count) do
begin
if AMenuItem.Items[iIndexSubMenuItem].Caption <> '-' then
begin
if (AMenuItem.Items[iIndexSubMenuItem].Enabled) and (AMenuItem.Items[iIndexSubMenuItem].Visible) then
begin
ASubNode := frmTreeViewMenu.TreeViewMenuGenericRoutineAndVarHolder.AddTreeViewMenuItem(frmTreeViewMenu.tvMainMenu, ANode, NormalizeMenuCaption(AMenuItem.Items[iIndexSubMenuItem].Caption),
'', 0, AMenuItem.Items[iIndexSubMenuItem]);
if AMenuItem.Items[iIndexSubMenuItem].Count > 0 then
RecursiveAddMenuBranch(AMenuItem.Items[iIndexSubMenuItem], ASubNode);
end;
end;
end;
end;
begin
Result := nil;
frmTreeViewMenu := TfrmTreeViewMenu.Create(frmMain);
try
frmTreeViewMenu.SetContextMode(ContextMode, WantedPosX, WantedPosY,
WantedWidth, WantedHeight);
frmTreeViewMenu.tvMainMenu.BeginUpdate;
for iMenuItem := 0 to pred(pmAnyMenu.Items.Count) do
begin
if pmAnyMenu.Items[iMenuItem].Caption <> '-' then
begin
if (pmAnyMenu.Items[iMenuItem].Enabled) and (pmAnyMenu.Items[iMenuItem].Visible) then
begin
RootNode := frmTreeViewMenu.TreeViewMenuGenericRoutineAndVarHolder.AddTreeViewMenuItem(frmTreeViewMenu.tvMainMenu, nil, NormalizeMenuCaption(pmAnyMenu.Items[iMenuItem].Caption), '', 0, pmAnyMenu.Items[iMenuItem]);
if pmAnyMenu.Items[iMenuItem].Count > 0 then
RecursiveAddMenuBranch(pmAnyMenu.Items[iMenuItem], RootNode);
end;
end;
end;
frmTreeViewMenu.HideUnmatchingNode;
if not frmTreeViewMenu.bTargetFixedWidth then frmTreeViewMenu.SetSizeToLargestElement;
frmTreeViewMenu.tvMainMenu.EndUpdate;
local_Result := frmTreeViewMenu.ShowModal;
case local_Result of
mrOk: Result := TMenuItem(TTreeMenuItem(frmTreeViewMenu.tvMainMenu.Items[frmTreeViewMenu.iFinalSelectedIndex].Data).PointerSourceData);
mrYes: frmMain.Commands.cm_ConfigTreeViewMenus([]);
mrAll: frmMain.Commands.cm_ConfigTreeViewMenusColors([]);
end;
finally
FreeAndNil(frmTreeViewMenu);
end;
end;
{ GetUserChoiceFromKASToolBar }
function GetUserChoiceFromKASToolBar(AKASToolBar: TKASToolBar; ContextMode: tvmContextMode; WantedPosX, WantedPosY, WantedWidth, WantedHeight: integer; var ReturnedTypeDispatcher: integer): Pointer;
var
frmTreeViewMenu: TfrmTreeViewMenu;
sSimiliCaptionToAddToMenu: string;
sSecondaryText: string;
procedure AddToSecondyText(sInfo: string);
begin
if sInfo <> '' then
begin
if sSecondaryText <> '' then
sSecondaryText := sSecondaryText + ' / ';
sSecondaryText := sSecondaryText + sInfo;
end;
end;
//WARNING: This procedure is recursive and may call itself!
procedure RecursiveAddTheseTKASToolItems(AKASMenuItem: TKASMenuItem; ANode: TTreeNode);
var
ASubNode: TTreeNode;
iIndexKASMenuItem: integer;
AKASToolItem: TKASToolItem;
begin
for iIndexKASMenuItem := 0 to pred(AKASMenuItem.SubItems.Count) do
begin
sSimiliCaptionToAddToMenu := '';
sSecondaryText := '';
AKASToolItem := AKASMenuItem.SubItems.Items[iIndexKASMenuItem];
if AKASToolItem is TKASNormalItem then
sSimiliCaptionToAddToMenu := TKASNormalItem(AKASToolItem).Hint;
if AKASToolItem is TKASCommandItem then
begin
AddToSecondyText(TKASCommandItem(AKASToolItem).Command);
frmTreeViewMenu.TreeViewMenuGenericRoutineAndVarHolder.AddTreeViewMenuItem(frmTreeViewMenu.tvMainMenu, ANode, sSimiliCaptionToAddToMenu, sSecondaryText, 2, AKASToolItem);
end
else
begin
if AKASToolItem is TKASProgramItem then
begin
AddToSecondyText(TKASProgramItem(AKASToolItem).Command);
frmTreeViewMenu.TreeViewMenuGenericRoutineAndVarHolder.AddTreeViewMenuItem(
frmTreeViewMenu.tvMainMenu, ANode, sSimiliCaptionToAddToMenu,
sSecondaryText, 2, AKASToolItem);
end
else
begin
if AKASToolItem is TKASMenuItem then
begin
if TKASMenuItem(AKASToolItem).SubItems.Count > 0 then
begin
ASubNode := frmTreeViewMenu.TreeViewMenuGenericRoutineAndVarHolder.AddTreeViewMenuItem(frmTreeViewMenu.tvMainMenu, ANode, sSimiliCaptionToAddToMenu, sSecondaryText, 0, nil);
RecursiveAddTheseTKASToolItems(TKASMenuItem(AKASToolItem), ASubNode);
end;
end;
end;
end;
end;
end;
var
// Variables declared *afer* the recursive block to make sure we won't use it.
RootNode: TTreeNode;
iKASToolButton: integer;
local_Result: integer;
AKASToolButton: TKASToolButton;
begin
Result := nil;
ReturnedTypeDispatcher := -1;
frmTreeViewMenu := TfrmTreeViewMenu.Create(frmMain);
try
frmTreeViewMenu.SetContextMode(ContextMode, WantedPosX, WantedPosY,
WantedWidth, WantedHeight);
frmTreeViewMenu.tvMainMenu.BeginUpdate;
for iKASToolButton := 0 to pred(AKASToolBar.ButtonList.Count) do
begin
sSimiliCaptionToAddToMenu := '';
sSecondaryText := '';
AKASToolButton := TKASToolButton(AKASToolBar.ButtonList.Items[iKASToolButton]);
if AKASToolButton.ToolItem is TKASNormalItem then
sSimiliCaptionToAddToMenu := TKASNormalItem(AKASToolButton.ToolItem).Hint;
if AKASToolButton.ToolItem is TKASCommandItem then
begin
AddToSecondyText(TKASCommandItem(AKASToolButton.ToolItem).Command);
frmTreeViewMenu.TreeViewMenuGenericRoutineAndVarHolder.AddTreeViewMenuItem(frmTreeViewMenu.tvMainMenu, nil, sSimiliCaptionToAddToMenu, sSecondaryText, 1, AKASToolButton);
end
else
begin
if AKASToolButton.ToolItem is TKASProgramItem then
begin
AddToSecondyText(TKASProgramItem(AKASToolButton.ToolItem).Command);
frmTreeViewMenu.TreeViewMenuGenericRoutineAndVarHolder.AddTreeViewMenuItem(frmTreeViewMenu.tvMainMenu, nil, sSimiliCaptionToAddToMenu, sSecondaryText, 1, AKASToolButton);
end
else
begin
if AKASToolButton.ToolItem is TKASMenuItem then
begin
if TKASMenuItem(AKASToolButton.ToolItem).SubItems.Count > 0 then
begin
RootNode := frmTreeViewMenu.TreeViewMenuGenericRoutineAndVarHolder.AddTreeViewMenuItem(frmTreeViewMenu.tvMainMenu, nil, sSimiliCaptionToAddToMenu, sSecondaryText, 0, nil);
RecursiveAddTheseTKASToolItems(TKASMenuItem(AKASToolButton.ToolItem), RootNode);
end;
end;
end;
end;
end;
frmTreeViewMenu.HideUnmatchingNode;
if not frmTreeViewMenu.bTargetFixedWidth then frmTreeViewMenu.SetSizeToLargestElement;
frmTreeViewMenu.tvMainMenu.EndUpdate;
local_Result := frmTreeViewMenu.ShowModal;
case local_Result of
mrOk:
begin
ReturnedTypeDispatcher := TTreeMenuItem(frmTreeViewMenu.tvMainMenu.Items[frmTreeViewMenu.iFinalSelectedIndex].Data).TypeDispatcher;
Result := TTreeMenuItem(frmTreeViewMenu.tvMainMenu.Items[frmTreeViewMenu.iFinalSelectedIndex].Data).PointerSourceData;
end;
mrYes: frmMain.Commands.cm_ConfigTreeViewMenus([]);
mrAll: frmMain.Commands.cm_ConfigTreeViewMenusColors([]);
end;
finally
FreeAndNil(frmTreeViewMenu);
end;
end;
end.