(* Tux Commander - UMain - Main form and window-related functions Copyright (C) 2008 Tomas Bzatek Check for updates on tuxcmd.sourceforge.net 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 UMain; interface uses gtk2, gdk2, gdk2pixbuf, glib2, pango, StrUtils, SysUtils, Types, Classes, DateUtils, GTKForms, GTKControls, GTKMenus, GTKStdCtrls, GTKExtCtrls, GTKView, GTKConsts, GTKUtils, GTKClasses, GTKPixbuf, UEngines, UConfig, UGnome, UVFSCore, UCoreClasses; type TFMain = class(TGTKForm) MainVBox : TGTKVBox; MainMenuHandleBox : TGTKHandleBox; MainMenu : TGTKMenuBar; LeftPanelBox, RightPanelBox : TGTKVBox; PanelSeparator : TGTKHPaned; LeftStatusBox, RightStatusBox, LeftPathLabelHBox, RightPathLabelHBox : TGTKHBox; LeftPathLabel, RightPathLabel : TGTKLabel; LeftPathLabelEventBox, RightPathLabelEventBox : TGTKEventBox; LeftScrolledWindow, RightScrolledWindow : TGTKScrolledWindow; LeftListView, RightListView: TGTKListView; LeftStatusLine, RightStatusLine : TGTKLabel; LeftUpButton, LeftRootButton, LeftHomeButton, RightUpButton, RightRootButton, RightHomeButton, LeftBookmarkButton, RightBookmarkButton : TGTKButton; LeftEqualButton, RightEqualButton : TGTKButton; LeftDiskInfoLabel, RightDiskInfoLabel : TGTKLabel; ButtonsBox : TGTKTable; F2Button, F3Button, F4Button, F5Button, F6Button, F7Button, F8Button : TGTKButton; mnuFile, mnuMark, mnuCommands, mnuShow, mnuSettings, mnuHelp : TGTKMenuItem; miExit : TGTKMenuItem; miSelectGroup, miUnselectGroup, miSelectAll, miUnselectAll, miInvertSelection : TGTKMenuItem; miRefresh : TGTKMenuItem; miShowDotFiles : TGTKMenuItem; miFileTypes: TGTKMenuItem; miAbout : TGTKMenuItem; miVerifyChecksums, miCreateChecksums : TGTKMenuItem; miSplitFile, miMergeFiles : TGTKMenuItem; miChangePermissions, miChangeOwner: TGTKMenuItem; miCreateSymlink, miEditSymlink: TGTKMenuItem; LeftQuickFindVBox, RightQuickFindVBox: TGTKVBox; LeftQuickFindHBox, RightQuickFindHBox: TGTKHBox; LeftQuickFindLabel, RightQuickFindLabel: TGTKLabel; LeftQuickFindEntry, RightQuickFindEntry: TGTKEntry; LeftQuickFindSeparator, RightQuickFindSeparator, ButtonBoxSeparator: TGTKHSeparator; CommandLineHBox: TGTKHBox; CommandLineCombo: TGTKCombo; CommandLineLabel: TGTKLabel; SplitterPopupMenu: TGTKMenuItem; FilePopupMenu: TGTKMenuItem; miPreferences: TGTKMenuItem; mnuBookmarks, miAddBookmark, miEditBookmarks, BookmarkPopup, BookmarkPopupDelete, miBookmarksSeparator: TGTKMenuItem; miShowDirectorySizes, miTargetSource: TGTKMenuItem; miCopyNames, miCopyFullPaths: TGTKMenuItem; ButtonBoxSpace: TGTKEventBox; MounterBarHandleBox : TGTKHandleBox; MounterBarTable: TGTKTable; MounterButtonPopupMenu, miMount, miUmount, miEject: TGTKMenuItem; miMounterSettings, miNoMounterBar, miShowOneMounterBar, miShowTwoMounterBar: TGTKMenuItem; LeftMounterTable, RightMounterTable: TGTKTable; miColumns: TGTKMenuItem; mnuPlugins, miTestPlugin: TGTKMenuItem; miSavePosition: TGTKMenuItem; LeftPanelNotebook, RightPanelNotebook: TEphyNotebook; LeftListBox, RightListBox: TGTKVBox; TabPopupMenu, miDuplicateTab, miCloseTab, miCloseAllTabs: TGTKMenuItem; mnuNetwork, miConnections, miOpenConnection, miQuickConnect, miDisconnect: TGTKMenuItem; miSearch: TGTKMenuItem; LeftDisconnectButton, RightDisconnectButton, LeftLeaveArchiveButton, RightLeaveArchiveButton: TGTKButton; OpenTerminalButton: TGTKButton; miNewTab: TGTKMenuItem; LeftPasswordButton, RightPasswordButton: TGTKImageButton; PathBoxPopupMenu: TGTKMenuItem; miPathBoxRefresh, miPathBoxCopyPath: TGTKMenuItem; procedure FormCreate(Sender: TObject); override; procedure FormDestroy(Sender: TObject); procedure FormResize(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure PanelSeparatorResize(Sender: TObject); procedure PanelSeparatorMouseUp(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); procedure miExitClick(Sender: TObject); procedure miAboutClick(Sender: TObject); procedure miRefreshClick(Sender: TObject); procedure mnuMarkClick(Sender: TObject); procedure ListViewKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); procedure ListViewEnter(Sender: TObject; var Accept: boolean); procedure PathLabelMouseDown(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); procedure PathButtonClick(Sender: TObject); function CompareFunc(Sender: TObject; var model: PGtkTreeModel; var a, b: PGtkTreeIter): integer; procedure F5ButtonClick(Sender: TObject); procedure F6ButtonClick(Sender: TObject); procedure F7ButtonClick(Sender: TObject); procedure F8ButtonClick(Sender: TObject); procedure ListViewDblClick(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); procedure ListViewMouseDown(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); procedure ListViewMouseUp(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); procedure ListViewMouseMove(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); procedure InplaceEditTimerTimer(Sender: TObject); procedure ListViewEdited(Sender: TObject; Column: TGTKTreeViewColumn; Item: TGTKListItem; var NewText: string; var AllowChange: boolean; var DataColumn: integer); procedure ListViewSelectionChanged(Sender: TObject); procedure QuickFindEntryEnter(Sender: TObject; var Accept: boolean); procedure miVerifyChecksumsClick(Sender: TObject); procedure miCreateChecksumsClick(Sender: TObject); procedure miMergeFilesClick(Sender: TObject); procedure miSplitFileClick(Sender: TObject); procedure miShowDotFilesClick(Sender: TObject); procedure F3F4ButtonClick(Sender: TObject); procedure miFileTypesClick(Sender: TObject); procedure CommandLineComboKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); procedure FormKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); procedure InactiveItemsTimerTimer(Sender: TObject); function OldGTKConvertToSorted(Sender: TObject; const Index: integer): integer; function OldGTKConvertFromSorted(Sender: TObject; const Index: integer): integer; procedure SplitterPopupMenuClick(Sender: TObject); procedure miChangePermissionsClick(Sender: TObject); procedure miChangeOwnerClick(Sender: TObject); procedure miCreateSymlinkClick(Sender: TObject); procedure miEditSymlinkClick(Sender: TObject); procedure FilePopupMenuPopup(Sender: TObject); procedure FilePopupMenuItemClick(Sender: TObject); procedure miPreferencesClick(Sender: TObject); procedure miAddBookmarkClick(Sender: TObject); procedure miBookmarkClick(Sender: TObject); procedure BookmarkPopupDeleteClick(Sender: TObject); procedure BookmarkItemMouseUp(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); procedure BookmarkButtonClick(Sender: TObject); procedure mnuBookmarksPopup(Sender: TObject); procedure miShowDirectorySizesClick(Sender: TObject); procedure miTargetSourceClick(Sender: TObject); procedure MounterButtonClick(Sender: TObject); procedure MounterButtonPopupMenuPopup(Sender: TObject); procedure MounterButtonMouseDown(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); procedure miMountClick(Sender: TObject); procedure miUmountClick(Sender: TObject); procedure miEjectClick(Sender: TObject); procedure miMounterSettingsClick(Sender: TObject); procedure miShowMounterBarClick(Sender: TObject); procedure miColumnsClick(Sender: TObject); procedure ListViewColumnsChanged(Sender: TObject); procedure RebuildListViewsTimerTimer(Sender: TObject); procedure miTestPluginClick(Sender: TObject); procedure miPluginAboutClick(Sender: TObject); procedure miSavePositionClick(Sender: TObject); procedure TabNotebookSwitchPage(Sender: TObject; const NewTabNum: integer; const ShouldFocus: boolean); procedure miDuplicateTabClick(Sender: TObject); procedure miCloseTabClick(Sender: TObject); procedure miCloseAllTabsClick(Sender: TObject); procedure TabPopupMenuPopup(Sender: TObject); procedure miOpenConnectionClick(Sender: TObject); procedure miSearchClick(Sender: TObject); procedure miDisconnectClick(Sender: TObject); procedure DisconnectButtonClick(Sender: TObject); procedure LeaveArchiveButtonClick(Sender: TObject); procedure OpenTerminalButtonClick(Sender: TObject); procedure ListViewColumnClicked(Sender: TObject); procedure NotebookReordered(Sender: TObject; const Source, Dest: integer); procedure NotebookTabClosed(Sender: TObject; const TabNum: integer; var CanClose: boolean); procedure NotebookTabDoubleClick(Sender: TObject; const TabNum: integer); function NotebookFindNotebookAtPointerEvent(Sender: TObject; const AbsX, AbsY: integer): TEphyNotebook; function NotebookMoveTabToAnotherNotebook(Sender: TObject; Destination: TEphyNotebook; const SourceTabNo, DestTabNo: integer): boolean; procedure NotebookTabFocusOnlyEvent(Sender: TObject; const TabNum: integer); procedure miFilePropertiesClick(Sender: TObject); procedure PasswordButtonClick(Sender: TObject); procedure miPathBoxCopyPathClick(Sender: TObject); procedure miCopyNamesClick(Sender: TObject); procedure RightMouseSelectPopupTimerTimer(Sender: TObject); procedure miQuickConnectClick(Sender: TObject); private LeftLastFocused, Editing, QuickFind, RedrawLeftInactive, RedrawRightInactive, StartUp, LeftTabPopup: boolean; LastWidth, RunningEscSensitive: integer; InplaceEditTimer, InactiveItemsTimer, RebuildListViewsTimer, RightMouseSelectPopupTimer: TGTKTimer; InplaceEditItem: TGTKListItem; SavedCmdLine: string; LastMounterButton: TGTKButton; MounterTableList, MounterTableListLeft, MounterTableListRight: TList; LeftNotebookBoxList, RightNotebookBoxList: TList; LeftPathsHighlight, RightPathsHighlight: TStringList; LeftTabEngines, RightTabEngines: TList; LastUsedFilter: string; PanelRightMouseSelMode, PanelRightMouseInProgress: boolean; procedure ConstructPanels; procedure ConstructMenu; procedure ConstructColumns(ListView: TGTKListView); procedure AfterStart; procedure ActivateItem(const ItemIndex: longint); procedure UpdatePanelInfo; procedure UpdatePanelInfoDown(LeftPanel: boolean); procedure UpdateCaption; procedure ChangingDir(LeftPanel: boolean; NewPath: string; HiliString1: string = ''; HiliString2: string = ''; const PreserveSelection: boolean = False; const AutoFallback: boolean = False; Plugin: TVFSPlugin = nil); procedure DoSelect(SelectType: integer); procedure ListViewCellDataFunc(Sender: TObject; tree_view: PGtkTreeView; tree_column : PGtkTreeViewColumn; cell : PGtkCellRenderer; tree_model : PGtkTreeModel; iter : PGtkTreeIter); procedure DoGetDirSize(AllItems: boolean); procedure DoDelete(LeftPanel: boolean; ListView: TGTKListView; Engine: TPanelEngine; DataList: TList); procedure DoCopyMove(LeftPanel, CopyMode, ShiftPressed: boolean; ListView: TGTKListView; Engine: TPanelEngine; DataList: TList); procedure DoRefresh(LeftPanel, StaySame, AutoFallback: boolean); procedure DoQuickRename(LeftPanel: boolean; ListView: TGTKListView; const CalledFromKey: boolean); procedure ActivateQuickFind(LeftPanel: boolean); procedure DeactivateQuickFind(LeftPanel: boolean); function QuickFindSendKey(LeftPanel: boolean; Key: word): boolean; procedure ProcessMarkKey(KeyType, Key: integer); procedure SwitchOtherPanel(LeftPanel, RequestNewAltO: boolean); procedure EditViewFile(LeftPanel: boolean; AListView: TGTKListView; View, NewFile: boolean); procedure RunFile(Path: string; Engine: TPanelEngine; CustomAction: integer); function ActivateCommandLine(Key: word; const ActualPosition: boolean = False): boolean; procedure ApplySettings(RebuildListViews, RebuildIcons, Startup: boolean); procedure RefreshBookmarksMenu; procedure PopupFileMenuPos; procedure HandleFormFocusIn; procedure SwitchPanelCtrlLeftRight(LeftPanel, LeftArrowPressed: boolean); procedure FillMounterBar; procedure RebuildListViews(DoRefresh: boolean); procedure FillPluginMenu; procedure NewTab(LeftPanel, SendSelectedDirToBg: boolean; CustomPath: string = ''); procedure SwitchTab(TabNo: integer; LeftPanel, SetFocus: boolean); procedure CloseTab(TabNo: integer; LeftPanel, CloseVFSEngine: boolean); procedure AddTabs(LeftPanel: boolean; TabList: TStringList; TabSortIDs, TabSortTypes: TList; SetTabActive: integer); function HandleVFSArchive(LeftPanel: boolean; const FullPath, HighlightItem, TargetPath: string): boolean; function CloseVFS(LeftPanel, SuppressRefresh: boolean): string; procedure ShowBookmarkQuick(LeftPanel: boolean); procedure SetTabLabel(Notebook: TEphyNotebook; PageIndex: integer; ALabel, Tooltip: string); procedure NewTabInternal(LeftPanel: boolean; _Engine: TPanelEngine; _Path: string; NewTabPosition: integer; SwitchToNewTab: boolean); procedure CopyFilenamesToClipboard(FullPaths, LeftPanel: boolean); function HandleRunFromArchive(var APath: string; Engine: TPanelEngine; Command, FileTypeDesc: string; BypassDialog: boolean): boolean; function ExtractFromArchive(var NewPath: string; Engine: TPanelEngine; const FilePath: string; ExtractAll: boolean): boolean; function HandleKey(Key: Word; Shift: TShiftState; LeftPanel: boolean): boolean; function IsEditing(AListView: TGTKListView): boolean; function PanelFindEditableWidget(AListView: TGTKListView): PGtkWidget; function CheckForUnsavedConnection(Engine: TVFSEngine; AllowCancel: boolean): boolean; // Returns False to Cancel procedure SaveCursorPositionTabbed(LeftPanel: boolean); public LeftPanelEngine, RightPanelEngine : TPanelEngine; ColumnSortIDs: array[1..ConstNumPanelColumns] of integer; LastClick: TDateTime; procedure EditViewFileInternal(ParentWindow: TGTKControl; Filename: string; Engine: TPanelEngine; View, NewFile: boolean); end; var FMain: TFMain; implementation uses ULibc, UCore, USelect, UNewDir, UDirDelete, UProgress, UCopyMove, UCoreUtils, ULocale, UChecksum, UChecksumDruid, USplitFile, UFileTypeSettings, UFileAssoc, UChmod, UChown, USymlink, UPreferences, UViewer, UToolTips, UMounterPrefs, UColumns, UTestPlugin, UConnectionManager, USearch, UProperties, URemoteWait, URunFromVFS, uVFSprototypes, UQuickConnect, UConnectionProperties; (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) function form_event_handler(widget: PGtkWidget; event: PGdkEvent; user_data: gpointer): gboolean; cdecl; forward; procedure TFMain.FormCreate(Sender: TObject); begin ReportGTKVersion; StartUp := True; RunningEscSensitive := 0; Editing := False; QuickFind := False; LeftTabPopup := True; LastClick := 0; PanelRightMouseSelMode := True; PanelRightMouseInProgress := False; LastUsedFilter := '*.*'; RedrawLeftInactive := False; RedrawRightInactive := False; MounterTableList := TList.Create; MounterTableListLeft := TList.Create; MounterTableListRight := TList.Create; LeftNotebookBoxList := TList.Create; RightNotebookBoxList := TList.Create; LeftPathsHighlight := TStringList.Create; RightPathsHighlight := TStringList.Create; LeftTabEngines := TList.Create; RightTabEngines := TList.Create; Caption := ConstAppTitle; WindowPosition := wpCenter; MainVBox := TGTKVBox.Create(Self); AddControl(MainVBox); MainMenuHandleBox := TGTKHandleBox.Create(Self); MainVBox.AddControlEx(MainMenuHandleBox, False, True, 0); MainMenu := TGTKMenuBar.Create(Self); MainMenuHandleBox.AddControl(MainMenu); ConstructMenu; InplaceEditTimer := TGTKTimer.Create(Self); InplaceEditTimer.Enabled := False; InplaceEditTimer.OnTimer := InplaceEditTimerTimer; InactiveItemsTimer := TGTKTimer.Create(Self); InactiveItemsTimer.Enabled := False; InactiveItemsTimer.Interval := ConfInactiveTimerDelay; if not Application.GTKVersion_2_6_0_Up then InactiveItemsTimer.OnTimer := InactiveItemsTimerTimer; RebuildListViewsTimer := TGTKTimer.Create(Self); RebuildListViewsTimer.Enabled := False; RebuildListViewsTimer.OnTimer := RebuildListViewsTimerTimer; RightMouseSelectPopupTimer := TGTKTimer.Create(Self); RightMouseSelectPopupTimer.Enabled := False; RightMouseSelectPopupTimer.OnTimer := RightMouseSelectPopupTimerTimer; MounterBarHandleBox := TGTKHandleBox.Create(Self); MounterBarHandleBox.SetSizeRequest(10, -1); MainVBox.AddControlEx(MounterBarHandleBox, False, True, 0); MounterBarTable := TGTKTable.Create(Self); MounterBarHandleBox.AddControl(MounterBarTable); LeftPanelBox := TGTKVBox.Create(Self); RightPanelBox := TGTKVBox.Create(Self); PanelSeparator := TGTKHPaned.Create(Self); MainVBox.AddControlEx(PanelSeparator, True, True, 0); PanelSeparator.Child1 := LeftPanelBox; PanelSeparator.Child2 := RightPanelBox; ConstructPanels; CommandLineHBox := TGTKHBox.Create(Self); CommandLineHBox.Homogeneous := False; CommandLineCombo := TGTKCombo.Create(Self); CommandLineCombo.DisableActivate; CommandLineCombo.MatchValue := False; CommandLineCombo.CaseSensitive := True; // CommandLineCombo.Entry.OnKeyDown := CommandLineComboKeyDown; CommandLineLabel := TGTKLabel.Create(Self); CommandLineLabel.Alignment := taRightJustify; CommandLineLabel.SetAlignment(1, 0.5); CommandLineLabel.SetSizeRequest(300, -1); OpenTerminalButton := TGTKButton.Create(Self); OpenTerminalButton.Caption := LANGOpenTerminalButton_Caption; OpenTerminalButton.BorderStyle := bsNone; OpenTerminalButton.CanFocus := False; OpenTerminalButton.Tooltip := LANGOpenTerminalButton_Tooltip; OpenTerminalButton.OnClick := OpenTerminalButtonClick; CommandLineHBox.AddControlEx(CommandLineLabel, False, False, 0); CommandLineHBox.AddControlEx(CommandLineCombo, True, True, 5); CommandLineHBox.AddControlEx(TGTKVSeparator.Create(Self), False, False, 2); CommandLineHBox.AddControlEx(OpenTerminalButton, False, False, 2); MainVBox.AddControlEx(TGTKHSeparator.Create(Self), False, False, 2); MainVBox.AddControlEx(CommandLineHBox, False, False, 0); ButtonsBox := TGTKTable.Create(Self); F2Button := TGTKButton.Create(Self); F3Button := TGTKButton.Create(Self); F4Button := TGTKButton.Create(Self); F5Button := TGTKButton.Create(Self); F6Button := TGTKButton.Create(Self); F7Button := TGTKButton.Create(Self); F8Button := TGTKButton.Create(Self); ButtonsBox.AddControlEx(0, 0, 1, 1, F2Button, [taoExpand, taoFill], [taoShrink, taoExpand, taoFill], 0, 0); ButtonsBox.AddControlEx(1, 0, 1, 1, TGTKVSeparator.Create(Self), [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 2, 4); ButtonsBox.AddControlEx(2, 0, 1, 1, F3Button, [taoExpand, taoFill], [taoShrink, taoExpand, taoFill], 0, 0); ButtonsBox.AddControlEx(3, 0, 1, 1, TGTKVSeparator.Create(Self), [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 2, 4); ButtonsBox.AddControlEx(4, 0, 1, 1, F4Button, [taoExpand, taoFill], [taoShrink, taoExpand, taoFill], 0, 0); ButtonsBox.AddControlEx(5, 0, 1, 1, TGTKVSeparator.Create(Self), [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 2, 4); ButtonsBox.AddControlEx(6, 0, 1, 1, F5Button, [taoExpand, taoFill], [taoShrink, taoExpand, taoFill], 0, 0); ButtonsBox.AddControlEx(7, 0, 1, 1, TGTKVSeparator.Create(Self), [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 2, 4); ButtonsBox.AddControlEx(8, 0, 1, 1, F6Button, [taoExpand, taoFill], [taoShrink, taoExpand, taoFill], 0, 0); ButtonsBox.AddControlEx(9, 0, 1, 1, TGTKVSeparator.Create(Self), [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 2, 4); ButtonsBox.AddControlEx(10, 0, 1, 1, F7Button, [taoExpand, taoFill], [taoShrink, taoExpand, taoFill], 0, 0); ButtonsBox.AddControlEx(11, 0, 1, 1, TGTKVSeparator.Create(Self), [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 2, 4); ButtonsBox.AddControlEx(12, 0, 1, 1, F8Button, [taoExpand, taoFill], [taoShrink, taoExpand, taoFill], 0, 0); ButtonBoxSeparator := TGTKHSeparator.Create(Self); ButtonBoxSpace := TGTKEventBox.Create(Self); MainVBox.AddControlEx(ButtonBoxSpace, False, False, 2); MainVBox.AddControlEx(ButtonBoxSeparator, False, False, 2); MainVBox.AddControlEx(ButtonsBox, False, False, 0); F2Button.BorderStyle := bsNone; F3Button.BorderStyle := bsNone; F4Button.BorderStyle := bsNone; F5Button.BorderStyle := bsNone; F6Button.BorderStyle := bsNone; F7Button.BorderStyle := bsNone; F8Button.BorderStyle := bsNone; F2Button.Caption := LANGF2Button_Caption; F3Button.Caption := LANGF3Button_Caption; F4Button.Caption := LANGF4Button_Caption; F5Button.Caption := LANGF5Button_Caption; F6Button.Caption := LANGF6Button_Caption; F7Button.Caption := LANGF7Button_Caption; F8Button.Caption := LANGF8Button_Caption; F2Button.OnClick := F6ButtonClick; F3Button.OnClick := F3F4ButtonClick; F4Button.OnClick := F3F4ButtonClick; F5Button.OnClick := F5ButtonClick; F6Button.OnClick := F6ButtonClick; F7Button.OnClick := F7ButtonClick; F8Button.OnClick := F8ButtonClick; F2Button.CanFocus := False; F3Button.CanFocus := False; F4Button.CanFocus := False; F5Button.CanFocus := False; F6Button.CanFocus := False; F7Button.CanFocus := False; F8Button.CanFocus := False; PanelSeparator.OnMouseUp := PanelSeparatorMouseUp; // Events OnResize := FormResize; OnDestroy := FormDestroy; OnKeyDown := FormKeyDown; OnClose := FormClose; g_signal_connect_after(FWidget, 'event-after', G_CALLBACK(@form_event_handler), nil); PanelSeparator.OnResize := PanelSeparatorResize; LeftListView.OnKeyDown := ListViewKeyDown; RightListView.OnKeyDown := ListViewKeyDown; LeftListView.OnEnter := ListViewEnter; RightListView.OnEnter := ListViewEnter; LeftPathLabelEventBox.OnMouseDown := PathLabelMouseDown; RightPathLabelEventBox.OnMouseDown := PathLabelMouseDown; LeftUpButton.OnClick := PathButtonClick; LeftRootButton.OnClick := PathButtonClick; LeftHomeButton.OnClick := PathButtonClick; RightUpButton.OnClick := PathButtonClick; RightRootButton.OnClick := PathButtonClick; RightHomeButton.OnClick := PathButtonClick; LeftEqualButton.OnClick := miTargetSourceClick; RightEqualButton.OnClick := miTargetSourceClick; LeftDisconnectButton.OnClick := DisconnectButtonClick; RightDisconnectButton.OnClick := DisconnectButtonClick; LeftLeaveArchiveButton.OnClick := LeaveArchiveButtonClick; RightLeaveArchiveButton.OnClick := LeaveArchiveButtonClick; LeftListView.CompareFunc := CompareFunc; RightListView.CompareFunc := CompareFunc; LeftListView.CellDataFunc := ListViewCellDataFunc; RightListView.CellDataFunc := ListViewCellDataFunc; LeftListView.OnMouseDown := ListViewMouseDown; RightListView.OnMouseDown := ListViewMouseDown; LeftListView.OnMouseUp := ListViewMouseUp; RightListView.OnMouseUp := ListViewMouseUp; { LeftListView.OnDblClick := ListViewDblClick; RightListView.OnDblClick := ListViewDblClick;} LeftListView.OnSelectionChanged := ListViewSelectionChanged; RightListView.OnSelectionChanged := ListViewSelectionChanged; LeftListView.OnMouseMove := ListViewMouseMove; RightListView.OnMouseMove := ListViewMouseMove; LeftQuickFindEntry.OnEnter := QuickFindEntryEnter; RightQuickFindEntry.OnEnter := QuickFindEntryEnter; LeftPanelNotebook.OnTabSwitched := TabNotebookSwitchPage; RightPanelNotebook.OnTabSwitched := TabNotebookSwitchPage; LeftPanelNotebook.PopupMenu := TabPopupMenu; RightPanelNotebook.PopupMenu := TabPopupMenu; AfterStart; end; procedure TFMain.ConstructPanels; begin LeftMounterTable := TGTKTable.Create(Self); LeftMounterTable.SetSizeRequest(10, -1); RightMounterTable := TGTKTable.Create(Self); RightMounterTable.SetSizeRequest(10, -1); LeftMounterTable.BorderWidth := 2; RightMounterTable.BorderWidth := 2; LeftPanelBox.AddControlEx(LeftMounterTable, False, False, 0); RightPanelBox.AddControlEx(RightMounterTable, False, False, 0); LeftStatusBox := TGTKHBox.Create(Self); RightStatusBox := TGTKHBox.Create(Self); LeftStatusBox.Homogeneous := False; RightStatusBox.Homogeneous := False; LeftPathLabel := TGTKLabel.Create(Self); RightPathLabel := TGTKLabel.Create(Self); LeftPathLabel.SetSizeRequest(10, -1); RightPathLabel.SetSizeRequest(10, -1); LeftPathLabelEventBox := TGTKEventBox.Create(Self); RightPathLabelEventBox := TGTKEventBox.Create(Self); LeftPathLabelHBox := TGTKHBox.Create(Self); RightPathLabelHBox := TGTKHBox.Create(Self); LeftPathLabelHBox.Homogeneous := False; RightPathLabelHBox.Homogeneous := False; LeftBookmarkButton := TGTKButton.Create(Self); LeftBookmarkButton.Caption := '❇'; RightBookmarkButton := TGTKButton.Create(Self); RightBookmarkButton.Caption := '❇'; LeftPasswordButton := TGTKImageButton.Create(Self); LeftPasswordButton.Icon := StockLock16; RightPasswordButton := TGTKImageButton.Create(Self); RightPasswordButton.Icon := StockLock16; LeftPasswordButton.BorderStyle := bsNone; RightPasswordButton.BorderStyle := bsNone; LeftBookmarkButton.SetSizeRequest(22, 22); LeftBookmarkButton.Tooltip := LANGBookmarkButton_Tooltip + ' (Ctrl+D)'; RightBookmarkButton.SetSizeRequest(22, 22); RightBookmarkButton.Tooltip := LANGBookmarkButton_Tooltip + ' (Ctrl+D)'; LeftPasswordButton.SetSizeRequest(28, 22); LeftPasswordButton.Tooltip := LANGPasswordButton_Tooltip; RightPasswordButton.SetSizeRequest(28, 22); RightPasswordButton.Tooltip := LANGPasswordButton_Tooltip; LeftPasswordButton.Visible := False; RightPasswordButton.Visible := False; LeftBookmarkButton.CanFocus := False; RightBookmarkButton.CanFocus := False; LeftPasswordButton.CanFocus := False; RightPasswordButton.CanFocus := False; LeftBookmarkButton.OnClick := BookmarkButtonClick; RightBookmarkButton.OnClick := BookmarkButtonClick; LeftPasswordButton.OnClick := PasswordButtonClick; RightPasswordButton.OnClick := PasswordButtonClick; LeftScrolledWindow := TGTKScrolledWindow.Create(Self); RightScrolledWindow := TGTKScrolledWindow.Create(Self); LeftListView := TGTKListView.CreateTyped(Self, True, [lcPointer]); RightListView := TGTKListView.CreateTyped(Self, True, [lcPointer]); if not Application.GTKVersion_2_0_5_Up then begin LeftListView.FromSortedCoversionFunc := OldGTKConvertFromSorted; LeftListView.ToSortedCoversionFunc := OldGTKConvertToSorted; RightListView.FromSortedCoversionFunc := OldGTKConvertFromSorted; RightListView.ToSortedCoversionFunc := OldGTKConvertToSorted; end; LeftStatusLine := TGTKLabel.Create(Self); RightStatusLine := TGTKLabel.Create(Self); LeftQuickFindVBox := TGTKVBox.Create(Self); LeftQuickFindHBox := TGTKHBox.Create(Self); LeftQuickFindLabel := TGTKLabel.Create(Self); LeftQuickFindEntry := TGTKEntry.Create(Self); LeftQuickFindEntry.CanFocus := False; LeftQuickFindSeparator := TGTKHSeparator.Create(Self); LeftQuickFindHBox.AddControlEx(LeftQuickFindLabel, False, False, 0); LeftQuickFindHBox.AddControlEx(LeftQuickFindEntry, True, True, 5); LeftQuickFindHBox.Homogeneous := False; LeftQuickFindVBox.AddControlEx(LeftQuickFindHBox, False, False, 5); LeftQuickFindVBox.AddControlEx(LeftQuickFindSeparator, False, False, 0); LeftQuickFindLabel.Caption := LANGQuickFind; LeftQuickFindVBox.Hide; RightQuickFindVBox := TGTKVBox.Create(Self); RightQuickFindHBox := TGTKHBox.Create(Self); RightQuickFindLabel := TGTKLabel.Create(Self); RightQuickFindEntry := TGTKEntry.Create(Self); RightQuickFindEntry.CanFocus := False; RightQuickFindSeparator := TGTKHSeparator.Create(Self); RightQuickFindHBox.AddControlEx(RightQuickFindLabel, False, False, 0); RightQuickFindHBox.AddControlEx(RightQuickFindEntry, True, True, 5); RightQuickFindHBox.Homogeneous := False; RightQuickFindVBox.AddControlEx(RightQuickFindHBox, False, False, 5); RightQuickFindVBox.AddControlEx(RightQuickFindSeparator, False, False, 0); RightQuickFindLabel.Caption := LANGQuickFind; RightQuickFindVBox.Hide; LeftDisconnectButton := TGTKButton.Create(Self); LeftDisconnectButton.Caption := '✖'; LeftDisconnectButton.CanFocus := False; LeftDisconnectButton.SetSizeRequest(22, 22); LeftDisconnectButton.Tooltip := LANGDisconnectButton_Tooltip + ' (Shift+Ctrl+F)'; LeftDisconnectButton.Visible := False; RightDisconnectButton := TGTKButton.Create(Self); RightDisconnectButton.Caption := '✖'; RightDisconnectButton.CanFocus := False; RightDisconnectButton.SetSizeRequest(22, 22); RightDisconnectButton.Tooltip := LANGDisconnectButton_Tooltip + ' (Shift+Ctrl+F)'; RightDisconnectButton.Visible := False; LeftLeaveArchiveButton := TGTKButton.Create(Self); LeftLeaveArchiveButton.Caption := '⇚'; LeftLeaveArchiveButton.CanFocus := False; LeftLeaveArchiveButton.SetSizeRequest(22, 22); LeftLeaveArchiveButton.Tooltip := LANGLeaveArchiveButton_Tooltip; LeftLeaveArchiveButton.Visible := False; RightLeaveArchiveButton := TGTKButton.Create(Self); RightLeaveArchiveButton.Caption := '⇚'; RightLeaveArchiveButton.CanFocus := False; RightLeaveArchiveButton.SetSizeRequest(22, 22); RightLeaveArchiveButton.Tooltip := LANGLeaveArchiveButton_Tooltip; RightLeaveArchiveButton.Visible := False; LeftPathLabelHBox.AddControlEx(LeftPathLabelEventBox, True, True, 0); LeftPathLabelHBox.AddControlEx(LeftPasswordButton, False, False, 0); LeftPathLabelHBox.AddControlEx(LeftDisconnectButton, False, True, 0); LeftPathLabelHBox.AddControlEx(LeftLeaveArchiveButton, False, True, 0); LeftPathLabelHBox.AddControlEx(LeftBookmarkButton, False, False, 0); RightPathLabelHBox.AddControlEx(RightPathLabelEventBox, True, True, 0); RightPathLabelHBox.AddControlEx(RightPasswordButton, False, False, 0); RightPathLabelHBox.AddControlEx(RightDisconnectButton, False, True, 0); RightPathLabelHBox.AddControlEx(RightLeaveArchiveButton, False, True, 0); RightPathLabelHBox.AddControlEx(RightBookmarkButton, False, False, 0); LeftPathLabelEventBox.AddControl(LeftPathLabel); RightPathLabelEventBox.AddControl(RightPathLabel); LeftPanelNotebook := TEphyNotebook.Create(Self); LeftPanelNotebook.Visible := False; LeftPanelNotebook.SetSizeRequest(10, -1); LeftPanelNotebook.Scrollable := True; LeftPanelNotebook.ShowBorder := False; LeftPanelNotebook.CanFocus := False; // Notebook should be focusable to allow scrolling when many tabs -- temporarily disabled LeftPanelNotebook.ShowCloseButtons := True; LeftPanelNotebook.AllowDragDrop := True; LeftPanelNotebook.AllowDragOutside := True; LeftPanelNotebook.ShowTooltips := True; LeftPanelNotebook.OnNotebookReordered := NotebookReordered; LeftPanelNotebook.OnTabClose := NotebookTabClosed; LeftPanelNotebook.OnTabDoubleClick := NotebookTabDoubleClick; LeftPanelNotebook.OnFindNotebookAtPointer := NotebookFindNotebookAtPointerEvent; LeftPanelNotebook.OnMoveTabToAnotherNotebook := NotebookMoveTabToAnotherNotebook; LeftPanelNotebook.OnTabFocusOnlyEvent := NotebookTabFocusOnlyEvent; RightPanelNotebook := TEphyNotebook.Create(Self); RightPanelNotebook.Visible := False; RightPanelNotebook.SetSizeRequest(10, -1); RightPanelNotebook.Scrollable := True; RightPanelNotebook.ShowBorder := False; RightPanelNotebook.CanFocus := False; RightPanelNotebook.ShowCloseButtons := True; RightPanelNotebook.AllowDragDrop := True; RightPanelNotebook.AllowDragOutside := True; RightPanelNotebook.ShowTooltips := True; RightPanelNotebook.OnNotebookReordered := NotebookReordered; RightPanelNotebook.OnTabClose := NotebookTabClosed; RightPanelNotebook.OnTabDoubleClick := NotebookTabDoubleClick; RightPanelNotebook.OnFindNotebookAtPointer := NotebookFindNotebookAtPointerEvent; RightPanelNotebook.OnMoveTabToAnotherNotebook := NotebookMoveTabToAnotherNotebook; RightPanelNotebook.OnTabFocusOnlyEvent := NotebookTabFocusOnlyEvent; LeftListBox := TGTKVBox.Create(Self); LeftListBox.AddControlEx(LeftScrolledWindow, True, True, 0); RightListBox := TGTKVBox.Create(Self); RightListBox.AddControlEx(RightScrolledWindow, True, True, 0); LeftPanelBox.AddControlEx(LeftStatusBox, False, True, 0); RightPanelBox.AddControlEx(RightStatusBox, False, True, 0); LeftPanelBox.AddControlEx(LeftPathLabelHBox, False, False, 1); RightPanelBox.AddControlEx(RightPathLabelHBox, False, False, 1); LeftPanelBox.AddControlEx(LeftPanelNotebook, True, True, 0); RightPanelBox.AddControlEx(RightPanelNotebook, True, True, 0); LeftPanelBox.AddControlEx(LeftListBox, True, True, 0); RightPanelBox.AddControlEx(RightListBox, True, True, 0); LeftPanelBox.AddControlEx(LeftQuickFindVBox, False, True, 0); RightPanelBox.AddControlEx(RightQuickFindVBox, False, True, 0); LeftPanelBox.AddControlEx(LeftStatusLine, False, True, 0); RightPanelBox.AddControlEx(RightStatusLine, False, True, 0); LeftScrolledWindow.AddControl(LeftListView); RightScrolledWindow.AddControl(RightListView); LeftScrolledWindow.HorizScrollBarPolicy := sbAutomatic; RightScrolledWindow.HorizScrollBarPolicy := sbAutomatic; LeftScrolledWindow.VertScrollBarPolicy := sbAutomatic; RightScrolledWindow.VertScrollBarPolicy := sbAutomatic; LeftScrolledWindow.ShadowType := stShadowIn; RightScrolledWindow.ShadowType := stShadowIn; LeftUpButton := TGTKButton.Create(Self); LeftUpButton.Caption := '..'; LeftRootButton := TGTKButton.Create(Self); LeftRootButton.Caption := '/'; LeftHomeButton := TGTKButton.Create(Self); LeftHomeButton.Caption := '~'; LeftEqualButton := TGTKButton.Create(Self); LeftEqualButton.Caption := '='; LeftUpButton.SetSizeRequest(22, 22); LeftUpButton.Tooltip := LANGUpButton_Tooltip; LeftRootButton.SetSizeRequest(22, 22); LeftRootButton.Tooltip := LANGRootButton_Tooltip + #10'(Ctrl+/)'; LeftHomeButton.SetSizeRequest(22, 22); LeftHomeButton.Tooltip := LANGHomeButton_Tooltip + #10'(Ctrl+Home)'; LeftEqualButton.SetSizeRequest(22, 22); LeftEqualButton.Tooltip := LANGLeftEqualButton_Tooltip; LeftDiskInfoLabel := TGTKLabel.Create(Self); LeftStatusBox.AddControlEx(LeftDiskInfoLabel, True, True, 0); LeftStatusBox.AddControlEndEx(LeftUpButton, False, False, 0); LeftStatusBox.AddControlEndEx(LeftRootButton, False, False, 0); LeftStatusBox.AddControlEndEx(LeftHomeButton, False, False, 0); LeftStatusBox.AddControlEndEx(LeftEqualButton, False, False, 0); RightUpButton := TGTKButton.Create(Self); RightUpButton.Caption := '..'; RightRootButton := TGTKButton.Create(Self); RightRootButton.Caption := '/'; RightHomeButton := TGTKButton.Create(Self); RightHomeButton.Caption := '~'; RightEqualButton := TGTKButton.Create(Self); RightEqualButton.Caption := '='; RightUpButton.SetSizeRequest(22, 22); RightUpButton.Tooltip := LANGUpButton_Tooltip; RightRootButton.SetSizeRequest(22, 22); RightRootButton.Tooltip := LANGRootButton_Tooltip + #10'(Ctrl+/)'; RightHomeButton.SetSizeRequest(22, 22); RightHomeButton.Tooltip := LANGHomeButton_Tooltip + #10'(Ctrl+Home)'; RightEqualButton.SetSizeRequest(22, 22); RightEqualButton.Tooltip := LANGRightEqualButton_Tooltip; LeftUpButton.CanFocus := False; LeftRootButton.CanFocus := False; LeftHomeButton.CanFocus := False; LeftEqualButton.CanFocus := False; RightUpButton.CanFocus := False; RightRootButton.CanFocus := False; RightHomeButton.CanFocus := False; RightEqualButton.CanFocus := False; RightDiskInfoLabel := TGTKLabel.Create(Self); RightStatusBox.AddControlEx(RightDiskInfoLabel, True, True, 0); RightStatusBox.AddControlEndEx(RightUpButton, False, False, 0); RightStatusBox.AddControlEndEx(RightRootButton, False, False, 0); RightStatusBox.AddControlEndEx(RightHomeButton, False, False, 0); RightStatusBox.AddControlEndEx(RightEqualButton, False, False, 0); ConstructColumns(LeftListView); ConstructColumns(RightListView); LeftPathLabel.XAlign := 0; LeftPathLabel.XPadding := 5; LeftPathLabel.YPadding := 1; RightPathLabel.XAlign := 0; RightPathLabel.XPadding := 5; RightPathLabel.YPadding := 1; LeftDiskInfoLabel.XAlign := 0; LeftDiskInfoLabel.XPadding := 5; LeftDiskInfoLabel.YAlign := 0.5; RightDiskInfoLabel.XAlign := 0; RightDiskInfoLabel.XPadding := 5; RightDiskInfoLabel.YAlign := 0.5; LeftStatusLine.XAlign := 0; LeftStatusLine.XPadding := 5; LeftStatusLine.YAlign := 0.5; RightStatusLine.XAlign := 0; RightStatusLine.XPadding := 5; RightStatusLine.YAlign := 0.5; { LeftStatusBox.SetSizeRequest(1, 18); RightStatusBox.SetSizeRequest(1, 18); } LeftStatusLine.SetSizeRequest(1, 18); RightStatusLine.SetSizeRequest(1, 18); LeftDiskInfoLabel.SetSizeRequest(1, -1); RightDiskInfoLabel.SetSizeRequest(1, -1); LeftQuickFindVBox.SetSizeRequest(1, -1); RightQuickFindVBox.SetSizeRequest(1, -1); end; procedure TFMain.ConstructMenu; const ShowDotFilesShortcut: TGDKShortCut = ( Key: 46; Locked: False; ModAlt: False; ModShift: False; ModCtrl: True); var i: integer; Item: TGTKMenuItem; Group: TGTKMenuItemGroup; begin mnuFile := TGTKMenuItem.Create(Self); mnuFile.Caption := LANGmnuFile_Caption; MainMenu.Items.Add(mnuFile); // mnuFile.Add(TGTKMenuItem.CreateTyped(Self, itTearOff)); miChangePermissions := TGTKMenuItem.CreateTyped(Self, itImageText); miChangePermissions.Caption := LANGmiChangePermissions_Caption; miChangePermissions.StockIcon := 'gtk-convert'; miChangePermissions.OnClick := miChangePermissionsClick; mnuFile.Add(miChangePermissions); miChangeOwner := TGTKMenuItem.Create(Self); miChangeOwner.Caption := LANGmiChangeOwner_Caption; miChangeOwner.OnClick := miChangeOwnerClick; mnuFile.Add(miChangeOwner); mnuFile.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miCreateSymlink := TGTKMenuItem.CreateTyped(Self, itImageText); miCreateSymlink.Caption := LANGmiCreateSymlink_Caption; miCreateSymlink.StockIcon := 'gtk-jump-to'; miCreateSymlink.OnClick := miCreateSymlinkClick; mnuFile.Add(miCreateSymlink); miEditSymlink := TGTKMenuItem.Create(Self); miEditSymlink.Caption := LANGmiEditSymlink_Caption; miEditSymlink.OnClick := miEditSymlinkClick; mnuFile.Add(miEditSymlink); mnuFile.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miSplitFile := TGTKMenuItem.Create(Self); miSplitFile.Caption := LANGmiSplitFileCaption; miSplitFile.OnClick := miSplitFileClick; mnuFile.Add(miSplitFile); miMergeFiles := TGTKMenuItem.Create(Self); miMergeFiles.Caption := LANGmiMergeFilesCaption; miMergeFiles.OnClick := miMergeFilesClick; mnuFile.Add(miMergeFiles); mnuFile.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miVerifyChecksums := TGTKMenuItem.Create(Self); miVerifyChecksums.Caption := LANGmiVerifyChecksums; miVerifyChecksums.OnClick := miVerifyChecksumsClick; mnuFile.Add(miVerifyChecksums); miCreateChecksums := TGTKMenuItem.Create(Self); miCreateChecksums.Caption := LANGmiCreateChecksumsCaption; miCreateChecksums.OnClick := miCreateChecksumsClick; mnuFile.Add(miCreateChecksums); mnuFile.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miExit := TGTKMenuItem.CreateTyped(Self, itImageText); miExit.Caption := LANGmiExit_Caption; miExit.StockIcon := 'gtk-quit'; miExit.OnClick := miExitClick; mnuFile.Add(miExit); mnuMark := TGTKMenuItem.Create(Self); mnuMark.Caption := LANGmnuMark_Caption; MainMenu.Items.Add(mnuMark); // mnuMark.Add(TGTKMenuItem.CreateTyped(Self, itTearOff)); miSelectGroup := TGTKMenuItem.CreateTyped(Self, itImageText); miSelectGroup.Caption := LANGmiSelectGroup_Caption; miSelectGroup.ShortCuts.Add(MakeGDKShortCut(GDK_KP_PLUS, False, False, False, False)); miSelectGroup.StockIcon := 'gtk-add'; miSelectGroup.OnClick := mnuMarkClick; mnuMark.Add(miSelectGroup); miUnselectGroup := TGTKMenuItem.CreateTyped(Self, itImageText); miUnselectGroup.Caption := LANGmiUnselectGroup_Caption; miUnselectGroup.ShortCuts.Add(MakeGDKShortCut(GDK_KP_MINUS, False, False, False, False)); miUnselectGroup.StockIcon := 'gtk-remove'; miUnselectGroup.OnClick := mnuMarkClick; mnuMark.Add(miUnselectGroup); miSelectAll := TGTKMenuItem.Create(Self); miSelectAll.Caption := LANGmiSelectAll_Caption; miSelectAll.ShortCuts.Add(MakeGDKShortCut(GDK_KP_PLUS, False, False, False, True)); miSelectAll.OnClick := mnuMarkClick; mnuMark.Add(miSelectAll); miUnselectAll := TGTKMenuItem.Create(Self); miUnselectAll.Caption := LANGmiUnselectAll_Caption; miUnselectAll.ShortCuts.Add(MakeGDKShortCut(GDK_KP_MINUS, False, False, False, True)); miUnselectAll.OnClick := mnuMarkClick; mnuMark.Add(miUnselectAll); miInvertSelection := TGTKMenuItem.Create(Self); miInvertSelection.Caption := LANGmiInvertSelection_Caption; miInvertSelection.ShortCuts.Add(MakeGDKShortCut(GDK_KP_ASTERISK, False, False, False, False)); miInvertSelection.OnClick := mnuMarkClick; mnuMark.Add(miInvertSelection); mnuCommands := TGTKMenuItem.Create(Self); mnuCommands.Caption := LANGmnuCommands_Caption; MainMenu.Items.Add(mnuCommands); // mnuCommands.Add(TGTKMenuItem.CreateTyped(Self, itTearOff)); miSearch := TGTKMenuItem.CreateTyped(Self, itImageText); miSearch.StockIcon := 'gtk-find'; miSearch.Caption := LANGmiSearchCaption2; miSearch.ShortCuts.AddName('F7'); miSearch.OnClick := miSearchClick; mnuCommands.Add(miSearch); mnuCommands.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miRefresh := TGTKMenuItem.CreateTyped(Self, itImageText); miRefresh.Caption := LANGmiRefresh_Caption; miRefresh.StockIcon := 'gtk-refresh'; miRefresh.ShortCuts.AddName('R'); miRefresh.OnClick := miRefreshClick; mnuCommands.Add(miRefresh); miShowDirectorySizes := TGTKMenuItem.CreateTyped(Self, itImageText); miShowDirectorySizes.Caption := LANGmiShowDirectorySizes_Caption; miShowDirectorySizes.OnClick := miShowDirectorySizesClick; mnuCommands.Add(miShowDirectorySizes); mnuCommands.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miCopyNames := TGTKMenuItem.CreateTyped(Self, itImageText); miCopyNames.Caption := LANGCopyFileNamesToClipboard; miCopyNames.ShortCuts.AddName('F2'); miCopyNames.OnClick := miCopyNamesClick; mnuCommands.Add(miCopyNames); miCopyFullPaths := TGTKMenuItem.CreateTyped(Self, itImageText); miCopyFullPaths.Caption := LANGCopyFullPathNamesToClipboard; miCopyFullPaths.ShortCuts.AddName('F2'); miCopyFullPaths.StockIcon := 'gtk-copy'; miCopyFullPaths.OnClick := miCopyNamesClick; mnuCommands.Add(miCopyFullPaths); mnuCommands.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miNewTab := TGTKMenuItem.CreateTyped(Self, itImageText); miNewTab.Caption := LANGmiNewTab_Caption; miNewTab.ShortCuts.AddName('T'); miNewTab.OnClick := miDuplicateTabClick; miNewTab.StockIcon := 'gtk-index'; mnuCommands.Add(miNewTab); mnuCommands.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miTargetSource := TGTKMenuItem.CreateTyped(Self, itImageText); miTargetSource.Caption := LANGmiTargetSource_Caption; // miTargetSource.ShortCuts.AddName('O'); miTargetSource.OnClick := miTargetSourceClick; mnuCommands.Add(miTargetSource); mnuShow := TGTKMenuItem.Create(Self); mnuShow.Caption := LANGmnuShow_Caption; MainMenu.Items.Add(mnuShow); // mnuShow.Add(TGTKMenuItem.CreateTyped(Self, itTearOff)); miShowDotFiles := TGTKMenuItem.CreateTyped(Self, itCheck); miShowDotFiles.Caption := LANGmiShowDotFiles_Caption; miShowDotFiles.Checked := ConfShowDotFiles; miShowDotFiles.OnClick := miShowDotFilesClick; miShowDotFiles.ShortCuts.Add(ShowDotFilesShortcut); mnuShow.Add(miShowDotFiles); mnuShow.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miNoMounterBar := TGTKMenuItem.CreateTyped(Self, itRadio, nil); miNoMounterBar.Caption := LANGmiNoMounterBarCaption; miNoMounterBar.Checked := ConfShowMounterBar = 0; Group := miNoMounterBar.Group; mnuShow.Add(miNoMounterBar); miShowOneMounterBar := TGTKMenuItem.CreateTyped(Self, itRadio, Group); miShowOneMounterBar.Caption := LANGmiShowOneMounterBarCaption; miShowOneMounterBar.Checked := ConfShowMounterBar = 1; Group := miShowOneMounterBar.Group; mnuShow.Add(miShowOneMounterBar); miShowTwoMounterBar := TGTKMenuItem.CreateTyped(Self, itRadio, Group); miShowTwoMounterBar.Caption := LANGmiShowTwoMounterBarCaption; miShowTwoMounterBar.Checked := ConfShowMounterBar = 2; mnuShow.Add(miShowTwoMounterBar); // Assign of the events has to be done after all radio items are created miNoMounterBar.OnClick := miShowMounterBarClick; miShowOneMounterBar.OnClick := miShowMounterBarClick; miShowTwoMounterBar.OnClick := miShowMounterBarClick; mnuBookmarks := TGTKMenuItem.Create(Self); mnuBookmarks.Caption := LANGmnuBookmarks_Caption; mnuBookmarks.OnPopup := mnuBookmarksPopup; mnuBookmarks.OnClick := mnuBookmarksPopup; MainMenu.Items.Add(mnuBookmarks); // mnuBookmarks.Add(TGTKMenuItem.CreateTyped(Self, itTearOff)); miAddBookmark := TGTKMenuItem.CreateTyped(Self, itImageText); miAddBookmark.Caption := LANGmiAddBookmark_Caption; miAddBookmark.StockIcon := 'gtk-add'; miAddBookmark.OnClick := miAddBookmarkClick; mnuBookmarks.Add(miAddBookmark); miEditBookmarks := TGTKMenuItem.CreateTyped(Self, itImageText); miEditBookmarks.Caption := LANGmiEditBookmarks_Caption; miEditBookmarks.Enabled := False; miEditBookmarks.Visible := False; miEditBookmarks.Tooltip := 'Currently not implemented - use the popup menu'; mnuBookmarks.Add(miEditBookmarks); miBookmarksSeparator := TGTKMenuItem.CreateTyped(Self, itSeparator); mnuBookmarks.Add(miBookmarksSeparator); BookmarkPopup := TGTKMenuItem.Create(Self); BookmarkPopupDelete := TGTKMenuItem.Create(Self); BookmarkPopupDelete.Caption := LANGBookmarkPopupDelete_Caption; BookmarkPopupDelete.OnClick := BookmarkPopupDeleteClick; BookmarkPopup.Add(BookmarkPopupDelete); mnuNetwork := TGTKMenuItem.Create(Self); mnuNetwork.Caption := LANGmnuNetworkCaption; MainMenu.Items.Add(mnuNetwork); // mnuNetwork.Add(TGTKMenuItem.CreateTyped(Self, itTearOff)); miConnections := TGTKMenuItem.CreateTyped(Self, itImageText); miConnections.Caption := LANGmiConnectionsCaption; miConnections.StockIcon := 'gtk-network'; miConnections.Enabled := False; mnuNetwork.Add(miConnections); mnuNetwork.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miOpenConnection := TGTKMenuItem.CreateTyped(Self, itImageText); miOpenConnection.Caption := LANGmiOpenConnectionCaption; miOpenConnection.OnClick := miOpenConnectionClick; miOpenConnection.StockIcon := 'gtk-connect'; miOpenConnection.ShortCuts.AddName('F'); mnuNetwork.Add(miOpenConnection); miQuickConnect := TGTKMenuItem.CreateTyped(Self, itImageText); miQuickConnect.Caption := LANGmiQuickConnectCaption; miQuickConnect.OnClick := miQuickConnectClick; miQuickConnect.ShortCuts.AddName('N'); mnuNetwork.Add(miQuickConnect); mnuNetwork.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miDisconnect := TGTKMenuItem.CreateTyped(Self, itImageText); miDisconnect.Caption := LANGmiDisconnect_Caption; miDisconnect.Enabled := False; miDisconnect.ShortCuts.AddName('F'); miDisconnect.StockIcon := 'gtk-disconnect'; miDisconnect.OnClick := miDisconnectClick; mnuNetwork.Add(miDisconnect); mnuPlugins := TGTKMenuItem.Create(Self); mnuPlugins.Caption := LANGmnuPluginsCaption; MainMenu.Items.Add(mnuPlugins); // mnuPlugins.Add(TGTKMenuItem.CreateTyped(Self, itTearOff)); miTestPlugin := TGTKMenuItem.CreateTyped(Self, itImageText); miTestPlugin.Caption := LANGmiTestPluginCaption; miTestPlugin.OnClick := miTestPluginClick; mnuPlugins.Add(miTestPlugin); mnuPlugins.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); mnuSettings := TGTKMenuItem.Create(Self); mnuSettings.Caption := LANGmnuSettings_Caption; MainMenu.Items.Add(mnuSettings); // mnuSettings.Add(TGTKMenuItem.CreateTyped(Self, itTearOff)); miPreferences := TGTKMenuItem.CreateTyped(Self, itImageText); miPreferences.Caption := LANGmiPreferences_Caption; miPreferences.StockIcon := 'gtk-preferences'; miPreferences.OnClick := miPreferencesClick; mnuSettings.Add(miPreferences); miFileTypes := TGTKMenuItem.CreateTyped(Self, itImageText); miFileTypes.Caption := LANGmiFileTypes_Caption; miFileTypes.OnClick := miFileTypesClick; mnuSettings.Add(miFileTypes); miMounterSettings := TGTKMenuItem.CreateTyped(Self, itImageText); miMounterSettings.Caption := LANGmiMounterSettingsCaption; miMounterSettings.OnClick := miMounterSettingsClick; mnuSettings.Add(miMounterSettings); miColumns := TGTKMenuItem.CreateTyped(Self, itImageText); miColumns.Caption := LANGmiColumnsCaption; miColumns.OnClick := miColumnsClick; mnuSettings.Add(miColumns); mnuSettings.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miSavePosition := TGTKMenuItem.CreateTyped(Self, itImageText); miSavePosition.Caption := LANGmiSavePositionCaption; miSavePosition.OnClick := miSavePositionClick; mnuSettings.Add(miSavePosition); mnuHelp := TGTKMenuItem.Create(Self); mnuHelp.Caption := LANGmnuHelp_Caption; MainMenu.Items.Add(mnuHelp); // mnuHelp.Add(TGTKMenuItem.CreateTyped(Self, itTearOff)); miAbout := TGTKMenuItem.CreateTyped(Self, itImageText); miAbout.Caption := LANGmiAbout_Caption; miAbout.StockIcon := 'gtk-about'; miAbout.OnClick := miAboutClick; mnuHelp.Add(miAbout); // Splitter popup menu SplitterPopupMenu := TGTKMenuItem.Create(Self); // SplitterPopupMenu.Add(TGTKMenuItem.CreateTyped(Self, itTearOff)); for i := 2 to 8 do begin Item := TGTKMenuItem.Create(Self); Item.Caption := Format('%d - %d', [i * 10, (10 - i) * 10]); Item.Data := Pointer(i * 10); Item.OnClick := SplitterPopupMenuClick; SplitterPopupMenu.Add(Item); end; // Files popup menu FilePopupMenu := TGTKMenuItem.Create(Self); FilePopupMenu.OnPopup := FilePopupMenuPopup; // Mounter popup menu MounterButtonPopupMenu := TGTKMenuItem.Create(Self); MounterButtonPopupMenu.OnPopup := MounterButtonPopupMenuPopup; miMount := TGTKMenuItem.CreateTyped(Self, itImageText); miMount.Caption := LANGmiMountCaption; miMount.StockIcon := 'gtk-connect'; miMount.OnClick := miMountClick; MounterButtonPopupMenu.Add(miMount); miUmount := TGTKMenuItem.CreateTyped(Self, itImageText); miUmount.Caption := LANGmiUmountCaption; miUmount.StockIcon := 'gtk-disconnect'; miUmount.OnClick := miUmountClick; MounterButtonPopupMenu.Add(miUmount); miEject := TGTKMenuItem.CreateTyped(Self, itImageText); miEject.Caption := LANGmiEjectCaption; // miEject.StockIcon := 'gtk-cdrom'; miEject.OnClick := miEjectClick; MounterButtonPopupMenu.Add(miEject); // Tab popup menu TabPopupMenu := TGTKMenuItem.Create(Self); TabPopupMenu.OnPopup := TabPopupMenuPopup; miDuplicateTab := TGTKMenuItem.CreateTyped(Self, itImageText); miDuplicateTab.Caption := LANGmiDuplicateTabCaption; miDuplicateTab.ShortCuts.AddName('T'); miDuplicateTab.OnClick := miDuplicateTabClick; miDuplicateTab.StockIcon := 'gtk-index'; TabPopupMenu.Add(miDuplicateTab); TabPopupMenu.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miCloseTab := TGTKMenuItem.CreateTyped(Self, itImageText); miCloseTab.Caption := LANGmiCloseTabCaption; miCloseTab.ShortCuts.AddName('W'); miCloseTab.OnClick := miCloseTabClick; miCloseTab.StockIcon := 'gtk-close'; TabPopupMenu.Add(miCloseTab); miCloseAllTabs := TGTKMenuItem.CreateTyped(Self, itImageText); miCloseAllTabs.Caption := LANGmiCloseAllTabsCaption; miCloseAllTabs.OnClick := miCloseAllTabsClick; TabPopupMenu.Add(miCloseAllTabs); // Path box popup menu PathBoxPopupMenu := TGTKMenuItem.Create(Self); miPathBoxRefresh := TGTKMenuItem.CreateTyped(Self, itImageText); miPathBoxRefresh.Caption := LANGmiRefresh_Caption; miPathBoxRefresh.StockIcon := 'gtk-refresh'; miPathBoxRefresh.ShortCuts.AddName('R'); miPathBoxRefresh.OnClick := miRefreshClick; PathBoxPopupMenu.Add(miPathBoxRefresh); PathBoxPopupMenu.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); miPathBoxCopyPath := TGTKMenuItem.CreateTyped(Self, itImageText); miPathBoxCopyPath.Caption := LANGCopyPathToClipboard; miPathBoxCopyPath.StockIcon := 'gtk-copy'; miPathBoxCopyPath.OnClick := miPathBoxCopyPathClick; PathBoxPopupMenu.Add(miPathBoxCopyPath); end; procedure TFMain.ConstructColumns(ListView: TGTKListView); var i, FirstColumn, LastColumn: integer; Column: TGTKTreeViewColumn; FontDesc: PPangoFontDescription; begin ListView.SelectionMode := smBrowse; GetFirstLastPanelColumn(FirstColumn, LastColumn); for i := 1 to ConstNumPanelColumns do if ConfColumnVisible[i] then begin // First column should have filetype icon if (i = FirstColumn) and ConfUseFileTypeIcons then begin Column := ListView.Columns.AddTyped(ctImageText); Column.SetImageProperty('ypad', 0); Column.SetImageProperty('yalign', 0.5); Column.SetImageProperty('xpad', 0); Column.SetImageProperty('xalign', 0.5); if (ConfRowHeight > 0) and ConfUseFileTypeIcons then begin Column.SetImageProperty('width', ConfRowHeight); Column.SetImageProperty('height', ConfRowHeight); end; end else Column := ListView.Columns.Add; Column.Caption := ConfColumnTitlesShort[ConfColumnIDs[i]]; if (i < LastColumn){ or Application.GTKVersion_2_4_0_Up} then begin { Column.MinWidth := 10; Column.MaxWidth := 500; } { Column.SizingMode := smFixed; Column.FixedWidth := ConfColumnSizes[i]; } g_object_set(G_OBJECT(Column.FColumn), 'sizing', 2, 'fixed-width', ConfColumnSizes[i], nil); end else g_object_set(G_OBJECT(Column.FColumn), 'sizing', 0, nil); // Column.SizingMode := smFixed; // smAutoSize; gtk_tree_view_column_set_spacing(Column.FColumn, 1); // Bug with column spacing? Column.Resizable := True; Column.Reorderable := True; // if not ConfUseSystemFont then ListView.Columns[i - 1].SetProperty('font', ConfPanelFont); Column.SetProperty('ypad', 0); Column.SetProperty('yalign', 0.5); Column.Tag := i; g_object_set_data(G_OBJECT(Column.FColumn), 'Column_ID', Pointer(i)); if ConfRowHeight > 0 then Column.SetProperty('height', ConfRowHeight); Column.SortID := ListView.Columns.Count - 1; ColumnSortIDs[Column.SortID + 1] := ConfColumnIDs[i]; if ConfRowHeight < 0 then gtk_cell_renderer_text_set_fixed_height_from_font(PGtkCellRendererText(Column.FRenderer), 1); { gtk_cell_renderer_text_set_fixed_height_from_font(PGtkCellRendererText(Column.FRenderer), 1); gtk_cell_renderer_set_fixed_size(PGtkCellRenderer(Column.FRenderer), ConfColumnSizes[i], ConfRowHeight); } if ConfColumnIDs[i] < 3 then begin // Filename column Column.OnEdited := ListViewEdited; if Application.GTKVersion_2_6_0_Up then Column.SetProperty('ellipsize', 3); end; if ConfColumnIDs[i] in [4, 8, 9] then begin Column.SetProperty('xalign', 1); Column.Alignment := 1; end; if Application.GTKVersion_2_4_0_Up then Column.SetProperty('single-paragraph-mode', 1); Column.OnClicked := ListViewColumnClicked; end; // Set the list font if not ConfUseSystemFont then begin FontDesc := pango_font_description_from_string(PChar(ConfPanelFont)); gtk_widget_modify_font(ListView.FWidget, FontDesc); end else gtk_widget_modify_font(ListView.FWidget, nil); // Set the fixed row height - temporarily disabled due to bug in custom drawing // if Application.GTKVersion_2_4_0_Up then g_object_set(ListView.FWidget, 'fixed_height_mode', integer(True), nil); end; procedure TFMain.FormDestroy(Sender: TObject); begin LeftNotebookBoxList.Free; RightNotebookBoxList.Free; end; procedure TFMain.FormClose(Sender: TObject; var Action: TCloseAction); function InternalCloseEngine(Engine, FallbackEngine: TPanelEngine): TPanelEngine; begin Result := FallbackEngine; if not Assigned(Engine.ParentEngine) or (not (Engine is TVFSEngine)) then Exit; Result := Engine.ParentEngine; CheckForUnsavedConnection(Engine as TVFSEngine, False); if not TVFSEngine(Engine).VFSClose then DebugMsg(['Error closing the engine...']); Engine.Free; end; var i: integer; b, DontShowAgain: boolean; res: TMessageButton; s: string; begin // Find all opened connections and warn user b := False; if LeftPanelNotebook.Visible and Assigned(LeftTabEngines) and (LeftTabEngines.Count > 0) then for i := 0 to LeftTabEngines.Count - 1 do if TPanelEngine(LeftTabEngines[i]) is TVFSEngine then begin b := True; Break; end; if (not b) or (RightPanelNotebook.Visible and Assigned(RightTabEngines) and (RightTabEngines.Count > 0)) then for i := 0 to RightTabEngines.Count - 1 do if TPanelEngine(RightTabEngines[i]) is TVFSEngine then begin b := True; Break; end; b := b or (LeftPanelEngine is TVFSEngine) or (RightPanelEngine is TVFSEngine); (* -- Disabled, makes users confused. We have that 'usaved connection' message now anyway if b and ConfOpenConnectionsWarning then begin res := MessageBoxShowOnce(PGtkWindow(FWidget), LANGOpenConnectionsWarning, LANGDontShowAgain, DontShowAgain, [mbYes, mbNo], mbWarning, mbYes, mbNo); if DontShowAgain then begin ConfOpenConnectionsWarning := False; WriteMainGUISettings; end; if res = mbNo then begin Action := caNone; Exit; end; end; *) ApplicationShuttingDown := True; // Avoid emitting config files refresh event // Close all active connections if b then begin if (not LeftPanelNotebook.Visible) and (LeftPanelEngine is TVFSEngine) then begin while LeftPanelEngine is TVFSEngine do s := CloseVFS(True, True); if LeftPanelEngine is TLocalTreeEngine then LeftPanelEngine.ChangeDir(s); end else for i := 0 to LeftTabEngines.Count - 1 do if (TPanelEngine(LeftTabEngines[i]) <> nil) and (TPanelEngine(LeftTabEngines[i]) is TVFSEngine) then try if LeftPanelNotebook.PageIndex = i then begin while LeftPanelEngine is TVFSEngine do s := CloseVFS(True, True); LeftPanelTabs[i] := s; end else while Assigned(LeftTabEngines[i]) and (TPanelEngine(LeftTabEngines[i]) is TVFSEngine) do begin s := TPanelEngine(LeftTabEngines[i]).SavePath; LeftTabEngines[i] := InternalCloseEngine(LeftTabEngines[i], LeftLocalEngine); if s <> '' then LeftPanelTabs[i] := s else LeftPanelTabs[i] := TPanelEngine(LeftTabEngines[i]).Path; end; except end; if (not RightPanelNotebook.Visible) and (RightPanelEngine is TVFSEngine) then begin while RightPanelEngine is TVFSEngine do s := CloseVFS(False, True); if RightPanelEngine is TLocalTreeEngine then RightPanelEngine.ChangeDir(s); end else for i := 0 to RightTabEngines.Count - 1 do if (TPanelEngine(RightTabEngines[i]) <> nil) and (TPanelEngine(RightTabEngines[i]) is TVFSEngine) then try if RightPanelNotebook.PageIndex = i then begin while RightPanelEngine is TVFSEngine do s := CloseVFS(False, True); RightPanelTabs[i] := s; end else while Assigned(RightTabEngines[i]) and (TPanelEngine(RightTabEngines[i]) is TVFSEngine) do begin s := TPanelEngine(RightTabEngines[i]).SavePath; RightTabEngines[i] := InternalCloseEngine(RightTabEngines[i], RightLocalEngine); if s <> '' then RightPanelTabs[i] := s else RightPanelTabs[i] := TPanelEngine(RightTabEngines[i]).Path; end; except end; end; // Unset the columns changed signal because it's called on window close LeftListView.OnColumnsChanged := nil; RightListView.OnColumnsChanged := nil; ConfMainWindowState := Integer(WindowState); if ConfMainWindowState <> 0 then begin ConfMainWindowPosLeft := TGTKControl(Self).Left; ConfMainWindowPosTop := TGTKControl(Self).Top; ConfMainWindowWidth := TGTKControl(Self).Width; ConfMainWindowHeight := TGTKControl(Self).Height; end else begin ConfMainWindowPosLeft := Left; ConfMainWindowPosTop := Top; ConfMainWindowWidth := Width; ConfMainWindowHeight := Height; end; for i := 0 to LeftListView.Columns.Count - 1 do ConfColumnSizes[LeftListView.Columns[i].Tag] := LeftListView.Columns[i].Width; ConfMainWindowLeftSortColumn := LeftListView.SortColumnID; ConfMainWindowLeftSortType := Integer(LeftListView.SortOrder); ConfMainWindowRightSortColumn := RightListView.SortColumnID; ConfMainWindowRightSortType := Integer(RightListView.SortOrder); ConfLeftTabBarTabIndex := LeftPanelNotebook.PageIndex; ConfRightTabBarTabIndex := RightPanelNotebook.PageIndex; end; (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.AfterStart; var i: integer; TmpList: TStringList; TmpList2, TmpList3: TList; begin LeftPanelEngine := LeftLocalEngine; RightPanelEngine := RightLocalEngine; // Apply the settings ApplySettings(False, False, True); MounterBarHandleBox.Visible := ConfShowMounterBar = 1; LeftMounterTable.Visible := ConfShowMounterBar = 2; RightMounterTable.Visible := ConfShowMounterBar = 2; FillMounterBar; LeftListView.SetFocus; if CommandLineHistory.Count > 0 then for i := 0 to CommandLineHistory.Count - 1 do CommandLineCombo.Items.Append(CommandLineHistory[i]); CommandLineCombo.Entry.Text := ''; RefreshBookmarksMenu; ButtonsBox.Visible := ConfShowFuncButtons; ButtonBoxSeparator.Visible := ConfShowFuncButtons; ButtonBoxSpace.Visible := not ConfShowFuncButtons; FillPluginMenu; FileListTipsInstall(PGtkTreeView(LeftListView.FWidget)); FileListTipsInstall(PGtkTreeView(RightListView.FWidget)); FileListTipsEnable; // Load and restore panel tabs if ConfSavePanelTabs then try TmpList := TStringList.Create; TmpList2 := TList.Create; TmpList3 := TList.Create; ReadTabs(True, TmpList, TmpList2, TmpList3); if (TmpList.Count > 0) and (TmpList2.Count > 0) and (TmpList3.Count > 0) then AddTabs(True, TmpList, TmpList2, TmpList3, ConfLeftTabBarTabIndex); TmpList.Clear; TmpList2.Clear; TmpList3.Clear; ReadTabs(False, TmpList, TmpList2, TmpList3); if (TmpList.Count > 0) and (TmpList2.Count > 0) and (TmpList3.Count > 0) then AddTabs(False, TmpList, TmpList2, TmpList3, ConfRightTabBarTabIndex); TmpList.Free; TmpList2.Free; TmpList3.Free; except on E: Exception do DebugMsg(['*** Exception raised in TFMain.AfterStart, loading tabs: (', E.ClassName, '): ', E.Message]); end; // Refresh the lists if not (LeftPanelNotebook.Visible and (LeftTabEngines.Count > 0)) then begin ChangingDir(True, ConfLeftPath, '', '', False, True); // AutoFallback LeftListView.SetSortInfo(ConfMainWindowLeftSortColumn, TGTKTreeViewSortOrder(ConfMainWindowLeftSortType)); end; if not (RightPanelNotebook.Visible and (RightTabEngines.Count > 0)) then begin ChangingDir(False, ConfRightPath, '', '', False, True); RightListView.SetSortInfo(ConfMainWindowRightSortColumn, TGTKTreeViewSortOrder(ConfMainWindowRightSortType)); end; // Set window position and size SetDefaultSize(ConfMainWindowWidth, ConfMainWindowHeight); if (ConfMainWindowPosLeft > -1) and (ConfMainWindowPosTop > -1) then WindowMove(ConfMainWindowPosLeft, ConfMainWindowPosTop); if ConfWMCompatMode then Show; case integer(ConfMainWindowState) of Ord(wsMaximized) : Maximize; Ord(wsMinimized) : Minimize; end; if not ConfWMCompatMode then Show; // Other things StartUp := False; // Set the flag to process Splitter repositioning PanelSeparator.Position := Round(Width * (ConfPanelSep / 100)); Application.ProcessMessages; // Need to process all messages before unlocking InternalLockInit(False); LeftListView.OnColumnsChanged := ListViewColumnsChanged; RightListView.OnColumnsChanged := ListViewColumnsChanged; LeftListView.SetFocus; end; (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.PanelSeparatorResize(Sender: TObject); begin if not StartUp then ConfPanelSep := Round((PanelSeparator.Position / Width) * 100); end; procedure TFMain.PanelSeparatorMouseUp(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); var XLeft, XRight: gint; begin if Button = mbRight then begin Accept := False; gtk_widget_get_pointer(LeftPanelBox.FWidget, @XLeft, nil); gtk_widget_get_pointer(RightPanelBox.FWidget, @XRight, nil); if (XLeft >= LeftPanelBox.Width) and (XRight < 0) then SplitterPopupMenu.PopUp; end; end; procedure TFMain.miExitClick(Sender: TObject); begin Close; end; procedure TFMain.miAboutClick(Sender: TObject); begin InternalLock; ShowAbout; Application.ProcessMessages; InternalLockInit(False); end; procedure TFMain.ListViewKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); var AListView: TGTKListView; ANotebook: TEphyNotebook; LeftPanel, KeyHandled: boolean; x: integer; begin if not Assigned(Sender) or not (Sender is TGTKListView) then DebugMsg(['**** ListViewKeyDown: Sender is not TGTKListView or not valid']); AListView := Sender as TGTKListView; if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then ANotebook := LeftPanelNotebook else ANotebook := RightPanelNotebook; FileListTipsHide; KeyHandled := False; case Key of GDK_TAB, 65056 : if (ssCtrl in Shift) and ANotebook.Visible then begin Accept := False; KeyHandled := True; x := (ANotebook.PageIndex + 1 - 2*Ord(ssShift in Shift)) mod ANotebook.ChildrenCount; if x < 0 then x := ANotebook.ChildrenCount - 1; ANotebook.PageIndex := x; end else begin Accept := False; KeyHandled := True; DeactivateQuickFind(LeftPanel); // Application.ProcessMessages; if InternalLockUnlocked then // prevent changing focus when busy if LeftPanel then RightListView.SetFocus else LeftListView.SetFocus; end; GDK_RETURN, GDK_KP_ENTER : begin KeyHandled := True; if {(Key = GDK_RETURN) and} (Shift = [ssAlt, ssShift]) then DoGetDirSize(True) else if ([ssAlt] = Shift) or ([ssCtrl] = Shift) then begin CommandLineComboKeyDown(Sender, Key, Shift, Accept); Accept := False; CommandLineCombo.Entry.SetFocus; CommandLineCombo.Entry.SelectRegion(Length(CommandLineCombo.Entry.Text), Length(CommandLineCombo.Entry.Text)); end else if Length(Trim(CommandLineCombo.Entry.Text)) > 0 then CommandLineComboKeyDown(Sender, Key, Shift, Accept) else if Assigned(AListView.Selected) then ActivateItem(AListView.Selected.Index); end; GDK_BACKSPACE : begin KeyHandled := True; if QuickFind then QuickFindSendKey(LeftPanel, Key) else begin if LeftPanel then PathButtonClick(LeftUpButton) else PathButtonClick(RightUpButton); end; end; GDK_Right : begin if [ssCtrl] = Shift then SwitchPanelCtrlLeftRight(LeftPanel, False) else if ConfLynxLikeMotion then begin if Assigned(AListView.Selected) then ActivateItem(AListView.Selected.Index); end; Accept := False; KeyHandled := True; end; GDK_Left : begin if [ssCtrl] = Shift then SwitchPanelCtrlLeftRight(LeftPanel, True) else if ConfLynxLikeMotion then if LeftPanel then PathButtonClick(LeftUpButton) else PathButtonClick(RightUpButton); Accept := False; KeyHandled := True; end; GDK_INSERT : begin KeyHandled := True; DoSelect(5); end; GDK_SPACE : if not QuickFind then begin Accept := False; KeyHandled := True; if Length(CommandLineCombo.Entry.Text) > 0 then ActivateCommandLine(Key) else begin if not PDataItem(AListView.Selected.Data)^.Selected then DoGetDirSize(False); DoSelect(8); end; end; GDK_F1 : begin KeyHandled := True; if Shift = [ssAlt] then begin ShowBookmarkQuick(True); Accept := False; end; end; GDK_F2 : begin KeyHandled := True; if Shift = [ssAlt] then begin ShowBookmarkQuick(False); Accept := False; end else begin DeactivateQuickFind(LeftPanel); Accept:= False; F6ButtonClick(nil); end; end; GDK_F3 : begin DeactivateQuickFind(LeftPanel); Accept:= False; KeyHandled := True; F3F4ButtonClick(F3Button); end; GDK_F4 : begin DeactivateQuickFind(LeftPanel); Accept:= False; KeyHandled := True; if ssShift in Shift then EditViewFile(LeftPanel, AListView, False, True) else F3F4ButtonClick(F4Button); end; GDK_F5 : begin DeactivateQuickFind(LeftPanel); Accept:= False; KeyHandled := True; if ssShift in Shift then F5ButtonClick(nil) else F5ButtonClick(Sender); end; GDK_F6 : begin DeactivateQuickFind(LeftPanel); Accept:= False; KeyHandled := True; if ssShift in Shift then begin Editing := IsEditing(AListView); DoQuickRename(LeftPanel, AListView, True); end else F6ButtonClick(Sender); end; GDK_F7 : begin DeactivateQuickFind(LeftPanel); Accept:= False; KeyHandled := True; if ssAlt in Shift then miSearchClick(Sender) else F7ButtonClick(Sender); end; GDK_F8, GDK_Delete_Key : begin DeactivateQuickFind(LeftPanel); Accept:= False; KeyHandled := True; F8ButtonClick(Sender); end; GDK_ESCAPE : begin if not QuickFind then CommandLineCombo.Entry.Text := ''; DeactivateQuickFind(LeftPanel); KeyHandled := True; if RunningEscSensitive > 0 then FMainEscPressed := True; end; GDK_WIN_POPUP : begin Accept := False; KeyHandled := True; PopupFileMenuPos; end; GDK_HOME: begin if Shift = [ssCtrl] then begin if LeftPanel then PathButtonClick(LeftHomeButton) else PathButtonClick(RightHomeButton); Accept := False; end else if Assigned(AListView.Selected) and (AListView.ConvertToSorted(AListView.Selected.Index) = 0) then Accept := False; KeyHandled := True; end; GDK_END: begin if Assigned(AListView.Selected) and (AListView.ConvertToSorted(AListView.Selected.Index) = AListView.Items.Count - 1) then Accept := False; KeyHandled := True; end; GDK_SLASH, GDK_KP_SLASH: begin if Shift = [ssCtrl] then begin if LeftPanel then PathButtonClick(LeftRootButton) else PathButtonClick(RightRootButton); Accept := False; end else if (Shift = []) then ActivateQuickFind(LeftPanel); KeyHandled := True; end; { GDK_0..GDK_9: if ConfBookmarkQuickJump and (Shift = [ssAlt]) then QuickJumpToBookmark(LeftPanel, Key - GDK_1) else begin Accept := False; if QuickFind then QuickFindSendKey(LeftPanel, Key) else ActivateCommandLine(Key); end; } GDK_Down : begin KeyHandled := False; if [ssCtrl] = Shift then begin Accept := False; CommandLineCombo.Entry.SetFocus; CommandLineCombo.Entry.SelectAll; KeyHandled := True; end else begin if QuickFind and (Shift = []) then begin KeyHandled := QuickFindSendKey(LeftPanel, Key); Accept := not KeyHandled; end; if not KeyHandled then begin KeyHandled := True; if Assigned(AListView.Selected) and (AListView.ConvertToSorted(AListView.Selected.Index) = AListView.Items.Count - 1) then Accept := False; end; end; end; GDK_Up : begin KeyHandled := False; if QuickFind and (Shift = []) then begin KeyHandled := QuickFindSendKey(LeftPanel, Key); Accept := not KeyHandled; end; if not KeyHandled then begin KeyHandled := True; if Assigned(AListView.Selected) and (AListView.ConvertToSorted(AListView.Selected.Index) = 0) then Accept := False; end; end; GDK_Page_Up, GDK_Page_Down: begin KeyHandled := True; if (Shift = [ssCtrl]) and ANotebook.Visible then begin Accept := False; x := (ANotebook.PageIndex + 1 - 2*Ord(Key = GDK_Page_Up)) mod ANotebook.ChildrenCount; if x < 0 then x := ANotebook.ChildrenCount - 1; ANotebook.PageIndex := x; end else begin if Assigned(AListView.Selected) and (((Key = GDK_Page_Up) and (AListView.ConvertToSorted(AListView.Selected.Index) = 0)) or ((Key = GDK_Page_Down) and (AListView.ConvertToSorted(AListView.Selected.Index) = AListView.Items.Count - 1))) then Accept := False; end; end; GDK_A, GDK_Capital_A: if ((Shift = [ssAlt]) and (ConfQuickSearchActivationKey <> 2)) or (Shift = [ssCtrl]) then begin KeyHandled := True; CommandLineComboKeyDown(Sender, Key, Shift, Accept); end; GDK_D, GDK_Capital_D: if Shift = [ssCtrl] then begin Accept := False; KeyHandled := True; ShowBookmarkQuick(LeftPanel); end; GDK_O, GDK_Capital_O : if (Shift = [ssAlt]) and (ConfQuickSearchActivationKey <> 2) then begin Accept := False; KeyHandled := True; SwitchOtherPanel(LeftPanel, False); end; GDK_P, GDK_Capital_P, GDK_N, GDK_Capital_N: if (((Shift = [ssAlt]) and (ConfQuickSearchActivationKey <> 2)) or (Shift = [ssCtrl])) { and (CommandLineHistory.Count > 0) } then begin KeyHandled := True; CommandLineComboKeyDown(Sender, Key, Shift, Accept); end; GDK_S, GDK_Capital_S : if ((Shift = [ssAlt]) and (ConfQuickSearchActivationKey <> 2)) or (Shift = [ssCtrl]) then begin KeyHandled := True; ActivateQuickFind(LeftPanel); end; end; if not KeyHandled then Accept := not HandleKey(Key, Shift, LeftPanel); end; function TFMain.HandleKey(Key: Word; Shift: TShiftState; LeftPanel: boolean): boolean; var s: string; b: boolean; begin Result := False; if Key = 0 then Exit; // Filter out all non-character keys s := UTF8Encode(WideChar(KeyValToUnicode(Key))); if (Length(s) = 0) or (s = #0) then begin // DebugMsg(['HandleKey: not a character key. Ignoring.']); Exit; end; // Triggers: // 0 = Ctrl+S/Alt+S and "/" only // 1 = Ctrl+Alt+letters // 2 = Alt+letters // 3 = letters directly if QuickFind then Result := QuickFindSendKey(LeftPanel, Key) else begin b := False; case ConfQuickSearchActivationKey of 1: b := Shift = [ssCtrl, ssAlt]; 2: b := Shift = [ssAlt]; 3: b := Shift = []; end; if b then begin ActivateQuickFind(LeftPanel); Result := QuickFindSendKey(LeftPanel, Key); end else Result := ActivateCommandLine(Key); end; end; (******************************************************************************************************************************************) procedure TFMain.ListViewEnter(Sender: TObject; var Accept: boolean); var s: string; begin LeftLastFocused := Sender = LeftListView; if LeftLastFocused then begin LeftPathLabelEventBox.ControlState := csSelected; RightPathLabelEventBox.ControlState := csActive; s := LeftPathLabel.Caption; end else begin LeftPathLabelEventBox.ControlState := csActive; RightPathLabelEventBox.ControlState := csSelected; s := RightPathLabel.Caption; end; CommandLineLabel.Caption := Format('%s@%s:%s>', [GetUserName, GetHostName, s]); UpdateCaption; miDisconnect.Enabled := (LeftLastFocused and (LeftPanelEngine is TVFSEngine) and (not TVFSEngine(LeftPanelEngine).ArchiveMode)) or ((not LeftLastFocused) and (RightPanelEngine is TVFSEngine) and (not TVFSEngine(RightPanelEngine).ArchiveMode)); end; procedure TFMain.FormResize(Sender: TObject); begin if Width <> LastWidth then begin // DebugMsg(['FormResize: ', Width, 'x', Height]); PanelSeparator.Position := Round(Width * (ConfPanelSep / 100)); CommandLineLabel.SetSizeRequest(Round(Width / 2.5), -1); LastWidth := Width; end; end; procedure TFMain.PathLabelMouseDown(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); begin if Button in [mbLeft, mbRight] then begin if Sender = LeftPathLabelEventBox then LeftListView.SetFocus else if Sender = RightPathLabelEventBox then RightListView.SetFocus; end; if Button = mbRight then begin Accept := False; PathBoxPopupMenu.PopUp; end; end; procedure TFMain.ActivateItem(const ItemIndex: longint); var Data: PDataItem; LeftPanel: boolean; DataList: TList; Engine: TPanelEngine; AListView: TGTKListView; Ext: string; begin if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then begin DataList := LeftPanelData; Engine := LeftPanelEngine; AListView := LeftListView; end else begin DataList := RightPanelData; Engine := RightPanelEngine; AListView := RightListView; end; DeactivateQuickFind(LeftPanel); if Application.GTKVersion_2_0_5_Up then Data := DataList[ItemIndex] else Data := AListView.Items[ItemIndex].AsPointer(0); DebugMsg(['Selected:', Data^.FDisplayName]); if not Assigned(Data) then Exit; if Data^.UpDir then ChangingDir(LeftPanel, '..') else if Data^.IsDir then ChangingDir(LeftPanel, Data^.FName) else begin Ext := WideUpperCase(Trim(Copy(String(Data^.FDisplayName), LastDelimiter('.', String(Data^.FDisplayName)) + 1, Length(String(Data^.FDisplayName)) - LastDelimiter('.', String(Data^.FDisplayName))))); // Test for known internal functions if ((Ext = 'SFV') or (Ext = 'MD5')) and (Engine is TLocalTreeEngine) then miVerifyChecksumsClick(Self) else if ((Ext = 'CRC') or (Ext = '001')) and (Engine is TLocalTreeEngine) then miMergeFilesClick(Self) else if not HandleVFSArchive(LeftPanel, IncludeTrailingPathDelimiter(Engine.Path) + String(Data^.FName), String(Data^.FName), '/') then if (not ConfUseURI) or ((Engine is TVFSEngine) and TVFSEngine(Engine).ArchiveMode) then RunFile(IncludeTrailingPathDelimiter(Engine.Path) + String(Data^.FName), Engine, -1) else RunFile(ExcludeTrailingPathDelimiter(Engine.GetPrefix) + IncludeTrailingPathDelimiter(Engine.Path) + String(Data^.FName), Engine, -1); end; end; procedure TFMain.ChangingDir(LeftPanel: boolean; NewPath: string; HiliString1: string = ''; HiliString2: string = ''; const PreserveSelection: boolean = False; const AutoFallback: boolean = False; Plugin: TVFSPlugin = nil); var ListView: TGTKListView; Engine: TPanelEngine; s, ss: string; i, Sel: integer; b: boolean; tt: TDateTime; DataList, DirList: TList; SelectedFiles: TStringList; ANotebook: TEphyNotebook; ATabList: TStringList; TabEngines: TList; OpenDirThread: TOpenDirThread; function LookupItem(const AName: string; const CaseSensitive: boolean): boolean; var i: integer; begin Result := False; for i := 0 to DataList.Count - 1 do if (CaseSensitive and (WideCompareStr(string(PDataItem(DataList[i])^.FName), AName) = 0)) or ((not CaseSensitive) and (WideCompareText(string(PDataItem(DataList[i])^.FName), AName) = 0)) then begin Sel := i; Result := True; Break; end; end; procedure DoThread; var DialogParent: PGtkWidget; begin try OpenDirThread.AEngine := Engine; OpenDirThread.APath := NewPath; OpenDirThread.ASelItem := ''; OpenDirThread.AAutoFallBack := AutoFallback; OpenDirThread.ADirList := DirList; if Plugin <> nil then begin DebugMsg(['Plugin <> nil']); OpenDirThread.APlugin := Plugin; OpenDirThread.AFullPath := HiliString1; OpenDirThread.AHighlightItem := HiliString2; end; DebugMsg(['(II) TFMain.ChangingDir: begin thread']); tt := Now; b := False; FRemoteWait := TFRemoteWait.Create(Application); FRemoteWait.ParentForm := FMain; // WARNING: For an unknown reason, there's a race condition in Kylix (FPC is fine). // The thread must be started *after* the FRemoteWait is created. OpenDirThread.Resume; repeat Sleep(ConstInternalProgressTimer); // DebugMsg([' (II) TFMain.ChangingDir: sleep.']); Application.ProcessMessages; if not b and (MilliSecondsBetween(tt, Now) >= ConstRemoteWaitDialogDelay) then begin FRemoteWait.ShowModal; b := True; end; if FRemoteWait.Cancelled then OpenDirThread.CancelIt := True; if OpenDirThread.VFSAskQuestion_Display then begin OpenDirThread.VFSAskQuestion_Display := False; DebugMsg(['Main thread: displaying question dialog']); if FRemoteWait.Visible then DialogParent := FRemoteWait.FWidget else DialogParent := FWidget; HandleVFSAskQuestionCallback(DialogParent, OpenDirThread.VFSAskQuestion_Message, OpenDirThread.VFSAskQuestion_Choices, OpenDirThread.VFSAskQuestion_Choice); OpenDirThread.VFSCallbackEvent.SetEvent; end; if OpenDirThread.VFSAskPassword_Display then begin OpenDirThread.VFSAskPassword_Display := False; DebugMsg(['Main thread: displaying password prompt']); if FRemoteWait.Visible then DialogParent := FRemoteWait.FWidget else DialogParent := FWidget; OpenDirThread.VFSAskPassword_Result := HandleVFSAskPasswordCallback(DialogParent, OpenDirThread.VFSAskPassword_Message, OpenDirThread.VFSAskPassword_default_user, OpenDirThread.VFSAskPassword_default_domain, OpenDirThread.VFSAskPassword_default_password, OpenDirThread.VFSAskPassword_flags, OpenDirThread.VFSAskPassword_username, OpenDirThread.VFSAskPassword_password, OpenDirThread.VFSAskPassword_anonymous, OpenDirThread.VFSAskPassword_domain, OpenDirThread.VFSAskPassword_password_save); OpenDirThread.VFSCallbackEvent.SetEvent; end; until OpenDirThread.Finished; FRemoteWait.Free; DebugMsg(['(II) TFMain.ChangingDir: end thread, running time = ', OpenDirThread.RunningTime, 'ms']); except on E: Exception do DebugMsg(['*** Exception raised in TFMain.ChangingDir:DoThread (', E.ClassName, '): ', E.Message]); end; end; begin DebugMsg(['*** Begin changing dir to ', NewPath]); if LeftPanel then begin ListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; ANotebook := LeftPanelNotebook; ATabList := LeftPanelTabs; TabEngines := LeftTabEngines; end else begin ListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; ANotebook := RightPanelNotebook; ATabList := RightPanelTabs; TabEngines := RightTabEngines; end; try if (NewPath = '..') and (Engine.ParentEngine <> nil) and (Engine.Path = '/') then begin CloseVFS(LeftPanel, False); Exit; end; InternalLock; FileListTipsDisable; SelectedFiles := nil; if PreserveSelection then begin SelectedFiles := TStringList.Create; if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do if PDataItem(DataList[i])^.Selected and (not PDataItem(DataList[i])^.UpDir) then SelectedFiles.Add(PDataItem(DataList[i])^.FName); end; Editing := False; DeactivateQuickFind(LeftPanel); DeactivateQuickFind(not LeftPanel); DirList := TList.Create; // Threading... OpenDirThread := TOpenDirThread.Create; DebugMsg(['TFMain.ChangingDir: Creating thread...']); DoThread; if Plugin <> nil then begin HiliString1 := ''; HiliString2 := ''; end; if OpenDirThread.VFSOpenResult <> 0 then begin // Silence the error if password dialog has been cancelled if not OpenDirThread.VFSCallbackCancelled then Application.MessageBox(LANGCouldntOpenURIArchive, [mbOK], mbError, mbOK, mbOK); DebugMsg(['TFMain.ChangingDir: Freeing thread...']); OpenDirThread.Free; end else if OpenDirThread.ChDirResult <> 0 then begin if not OpenDirThread.VFSCallbackCancelled then begin // Drop the error message if one of the callback dialogs were cancelled if OpenDirThread.ChDirResult = 1 then Application.MessageBox(Format(LANGErrorGettingListingForSPanelNoPath, [LANGPanelStrings[LeftPanel], 'Exception']), [mbOK], mbError, mbNone, mbOK) else Application.MessageBox(Format(LANGErrorGettingListingForSPanel, [LANGPanelStrings[LeftPanel], GetErrorString(OpenDirThread.ChDirResult), NewPath]), [mbOK], mbError, mbNone, mbOK); end; DebugMsg(['TFMain.ChangingDir: Freeing thread...']); OpenDirThread.Free; end else begin if OpenDirThread.ListingResult <> 0 then begin Application.MessageBox(Format(LANGErrorGettingListingForSPanel, [LANGPanelStrings[LeftPanel], GetErrorString(OpenDirThread.ListingResult), Engine.Path]), [mbOK], mbError, mbNone, mbOK); Exit; end; s := OpenDirThread.ASelItem; Engine := OpenDirThread.AEngine; // set current Engine from the thread (might have been modified due to VFS) if LeftPanel then LeftPanelEngine := Engine else RightPanelEngine := Engine; DebugMsg(['TFMain.ChangingDir: Freeing thread...']); OpenDirThread.Free; FillPanel(DirList, ListView, Engine, LeftPanel); // This is time consuming DirList.Free; if DataList.Count > 0 then begin if PreserveSelection and (SelectedFiles.Count > 0) and (DataList.Count > 0) then for i := 0 to DataList.Count - 1 do if (not PDataItem(DataList[i])^.UpDir) and (SelectedFiles.IndexOf(PDataItem(DataList[i])^.FName) >= 0) then PDataItem(DataList[i])^.Selected := True; Sel := 0; b := (NewPath = '..') and (Length(Trim(s)) > 0) and LookupItem(StrToUTF8(s), True); if not b then b := (HiliString1 <> '') and LookupItem(HiliString1, True); if (not b) and (HiliString1 <> '') then b := LookupItem(HiliString1, False); if (not b) and (HiliString2 <> '') then b := LookupItem(HiliString2, True); if (not b) and (HiliString2 <> '') then b := LookupItem(HiliString2, False); // DebugMsg(['TFMain.ChangingDir: Engine.Path = "', Engine.Path, '", NewPath = "', NewPath, '", HiliString1 = "', HiliString1, '", HiliString2 = "', HiliString2, '"']); if (not b) and ((Engine.Path = '/') or (NewPath = '/')) and (HiliString1 = '') and (HiliString2 = '') then Sel := ListView.ConvertFromSorted(0); ListView.Items[Sel].Selected := True; // Application.ProcessMessages; ListView.Items[Sel].SetCursor(0, False, not Application.GTKVersion_2_2_0_Up, 0.5, 0); // Application.ProcessMessages; end; UpdatePanelInfo; UpdatePanelInfoDown(LeftPanel); UpdatePanelInfoDown(not LeftPanel); if ANotebook.Visible then begin ATabList[ANotebook.PageIndex] := Engine.Path; TabEngines[ANotebook.PageIndex] := Engine; s := ExtractFileName(ExcludeTrailingPathDelimiter(Engine.Path)); if s = '' then s := '/'; SetTabLabel(ANotebook, ANotebook.PageIndex, StrToUTF8(s), StrToUTF8(Engine.Path)); end; end; // of Chdir, Listing, ... Engine.ExplicitChDir('/'); Application.ProcessMessages; InternalUnLock; FileListTipsEnable; except on E: Exception do DebugMsg(['*** Exception raised in TFMain.ChangingDir (', E.ClassName, '): ', E.Message]); end; end; procedure TFMain.DoRefresh(LeftPanel, StaySame, AutoFallback: boolean); var ListView: TGTKListView; Engine: TPanelEngine; DataList: TList; s1, s2: string; begin if LeftPanel then begin ListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin ListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; end; s1 := ''; s2 := ''; FindNextSelected(ListView, DataList, s1, s2); ChangingDir(LeftPanel, Engine.Path, s1, s2, StaySame, AutoFallback); end; procedure TFMain.UpdateCaption; var LeftPanel: boolean; s: string; Engine: TPanelEngine; begin if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then Engine := LeftPanelEngine else Engine := RightPanelEngine; if Engine.GetPrefix <> '' then s := Format(ConstFullPathFormatStr, [Engine.GetPrefix, Engine.Path]) else s := Engine.Path; Caption := Format('Tux Commander [%s]', [StrToUTF8(s)]); miVerifyChecksums.Enabled := Engine is TLocalTreeEngine; miCreateChecksums.Enabled := Engine is TLocalTreeEngine; miSplitFile.Enabled := Engine is TLocalTreeEngine; miMergeFiles.Enabled := Engine is TLocalTreeEngine; end; procedure TFMain.UpdatePanelInfo; var FSFree, FSSize: Int64; FSName, s: string; Time1, Time2: TDateTime; begin UpdateCaption; Time1 := Now; if LeftPanelEngine.GetPrefix <> '' then LeftPathLabel.Caption := StrToUTF8(Format(ConstFullPathFormatStr, [LeftPanelEngine.GetPrefix, LeftPanelEngine.Path])) else LeftPathLabel.Caption := StrToUTF8(LeftPanelEngine.Path); if RightPanelEngine.GetPrefix <> '' then RightPathLabel.Caption := StrToUTF8(Format(ConstFullPathFormatStr, [RightPanelEngine.GetPrefix, RightPanelEngine.Path])) else RightPathLabel.Caption := StrToUTF8(RightPanelEngine.Path); LeftPathLabel.UseMarkup := True; RightPathLabel.UseMarkup := True; if LeftLastFocused then s := LeftPathLabel.Caption else s := RightPathLabel.Caption; CommandLineLabel.Caption := Format('%s@%s:%s>', [GetUserName, GetHostName, s]); LeftPanelEngine.GetFileSystemInfo(LeftPanelEngine.Path, FSSize, FSFree, FSName); if FSName <> '' then LeftDiskInfoLabel.Caption := Format(LANGDiskStatVolNameFmt, [FSName, FormatSize(FSFree, 1024), FormatSize(FSSize, 1024)]) else LeftDiskInfoLabel.Caption := Format(LANGDiskStatFmt, [FormatSize(FSFree, 1024), FormatSize(FSSize, 1024)]); RightPanelEngine.GetFileSystemInfo(RightPanelEngine.Path, FSSize, FSFree, FSName); if FSName <> '' then RightDiskInfoLabel.Caption := Format(LANGDiskStatVolNameFmt, [FSName, FormatSize(FSFree, 1024), FormatSize(FSSize, 1024)]) else RightDiskInfoLabel.Caption := Format(LANGDiskStatFmt, [FormatSize(FSFree, 1024), FormatSize(FSSize, 1024)]); LeftDiskInfoLabel.UseMarkup := True; RightDiskInfoLabel.UseMarkup := True; // Update the visibility of VFS buttons LeftDisconnectButton.Visible := (LeftPanelEngine is TVFSEngine) and (not TVFSEngine(LeftPanelEngine).ArchiveMode); LeftLeaveArchiveButton.Visible := (LeftPanelEngine is TVFSEngine) and TVFSEngine(LeftPanelEngine).ArchiveMode; RightDisconnectButton.Visible := (RightPanelEngine is TVFSEngine) and (not TVFSEngine(RightPanelEngine).ArchiveMode); RightLeaveArchiveButton.Visible := (RightPanelEngine is TVFSEngine) and TVFSEngine(RightPanelEngine).ArchiveMode; LeftPasswordButton.Visible := (LeftPanelEngine is TVFSEngine) and TVFSEngine(LeftPanelEngine).GetPasswordRequired; RightPasswordButton.Visible := (RightPanelEngine is TVFSEngine) and TVFSEngine(RightPanelEngine).GetPasswordRequired; miDisconnect.Enabled := (LeftLastFocused and (LeftPanelEngine is TVFSEngine) and (not TVFSEngine(LeftPanelEngine).ArchiveMode)) or ((not LeftLastFocused) and (RightPanelEngine is TVFSEngine) and (not TVFSEngine(RightPanelEngine).ArchiveMode)); Time2 := Now; DebugMsg(['UpdatePanelInfo: ', SecondOf(Time2 - Time1), ':', MillisecondOf(Time2 - Time1)]); end; procedure TFMain.UpdatePanelInfoDown(LeftPanel: boolean); var Size, TotalSize: Int64; NumSel, TotalFiles: longint; i: integer; Data: PDataItem; s: string; DataList: TList; begin if LeftPanel then DataList := LeftPanelData else DataList := RightPanelData; Size := 0; TotalSize := 0; NumSel := 0; TotalFiles := 0; if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do begin Data := DataList[i]; if (not Data^.UpDir) and ((not Data^.IsDir) or (Data^.IsDir and (Data^.Size > 0))) then begin Inc(TotalSize, Data^.Size); if not Data^.IsDir then Inc(TotalFiles); if Data^.Selected then begin if not Data^.IsDir then Inc(NumSel); Inc(Size, Data^.Size); end; end; end; s := Format(LANGStatusLineFmt, [FormatSize(Size, 1024), FormatSize(TotalSize, 1024), NumSel, TotalFiles]); if LeftPanel then LeftStatusLine.Caption := s else RightStatusLine.Caption := s; end; procedure TFMain.PathButtonClick(Sender: TObject); var NewPath: string; LeftPanel: boolean; begin LeftPanel := (Sender = LeftUpButton) or (Sender = LeftHomeButton) or (Sender = LeftRootButton); if (Sender = LeftUpButton) or (Sender = RightUpButton) then NewPath := '..' else if (Sender = LeftRootButton) or (Sender = RightRootButton) then NewPath := '/' else if (Sender = LeftHomeButton) or (Sender = RightHomeButton) then NewPath := GetHomePath else Exit; // Close VFS connections if ((Sender = LeftRootButton) or (Sender = LeftHomeButton)) and (not (LeftPanelEngine is TLocalTreeEngine)) then while (LeftPanelEngine is TVFSEngine) do CloseVFS(LeftPanel, True); if ((Sender = RightRootButton) or (Sender = RightHomeButton)) and (not (RightPanelEngine is TLocalTreeEngine)) then while (RightPanelEngine is TVFSEngine) do CloseVFS(LeftPanel, True); ChangingDir(LeftPanel, NewPath); if LeftPanel then LeftListView.SetFocus else RightListView.SetFocus; end; procedure TFMain.miRefreshClick(Sender: TObject); begin DoRefresh(LeftListView.Focused, True, True); end; function TFMain.CompareFunc(Sender: TObject; var model: PGtkTreeModel; var a, b: PGtkTreeIter): integer; var Data1, Data2: PDataItem; Path: PGtkTreePath; DataList: TList; begin Result := 0; if not Application.GTKVersion_2_0_5_Up then begin gtk_tree_model_get(model, a, 0, @Data1, -1); gtk_tree_model_get(model, b, 0, @Data2, -1); end else begin if Sender = LeftListView then DataList := LeftPanelData else DataList := RightPanelData; Path := gtk_tree_model_get_path(model, a); if not Assigned(Path) then Exit; Data1 := DataList[gtk_tree_path_get_indices(Path)^]; gtk_tree_path_free(Path); Path := gtk_tree_model_get_path(model, b); if not Assigned(Path) then Exit; Data2 := DataList[gtk_tree_path_get_indices(Path)^]; gtk_tree_path_free(Path); end; Result := LVCompareItems(Data1, Data2, (Sender as TGTKView).SortOrder = soAscending, FMain.ColumnSortIDs[(Sender as TGTKView).SortColumnID + 1]); end; (********************************************************************************************************************************) procedure TFMain.DoSelect(SelectType: integer); var Filter, s: string; LeftPanel, ExpandSel, b, Found: boolean; ListView: TGTKListView; Engine: TPanelEngine; i, j: integer; Item: TGTKListItem; Data: PDataItem; DataList: TList; Wilds: array of string; begin try InternalLock; try if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else Exit; if LeftPanel then begin ListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin ListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; end; DeactivateQuickFind(LeftPanel); ExpandSel := False; if SelectType in [1, 2] then try FSelect := TFSelect.Create(Self); case SelectType of 1 : FSelect.Caption := LANGExpandSelection; 2 : FSelect.Caption := LANGShrinkSelection; end; { FSelect.TitleLabel.Caption := Format('%s', [FSelect.Caption]); FSelect.TitleLabel.UseMarkup := True; } ExpandSel := SelectType = 1; FSelect.ComboBox.Entry.Text := LastUsedFilter; FSelect.ComboBox.Entry.SelectAll; if FSelect.Run = mbOK then begin Filter := FSelect.ComboBox.Entry.Text; LastUsedFilter := Filter; if FSelect.ComboBox.Entry.Text <> '*.*' then SaveItemToHistory(FSelect.ComboBox.Entry.Text, SelectHistory); end else Exit; finally FSelect.Free; end; case SelectType of 1, 2 : if ListView.Items.Count > 1 then begin SetLength(Wilds, 0); while LastDelimiter(ConfSelItemsDelim, Filter) > 0 do begin i := LastDelimiter(ConfSelItemsDelim, Filter); if i < Length(Filter) then begin s := Copy(Filter, i + 1, Length(Filter) - i); Delete(Filter, i, Length(Filter) - i + 1); SetLength(Wilds, Length(Wilds) + 1); Wilds[Length(Wilds) - 1] := s; end; end; if Length(Filter) > 0 then begin SetLength(Wilds, Length(Wilds) + 1); Wilds[Length(Wilds) - 1] := Filter; end; Found := False; for i := 0 to DataList.Count - 1 do begin Data := DataList[i]; if Assigned(Data) and (not Data^.UpDir) and (ConfSelectAllDirs or (not Data^.IsDir)) {and (Data^.Selected <> ExpandSel)} then begin b := False; for j := 0 to Length(Wilds) - 1 do b := b or IsWild(String(Data^.FDisplayName), Wilds[j], True); if b then begin Data^.Selected := ExpandSel; Found := True; end; end; end; SetLength(Wilds, 0); if not Found then Application.MessageBox(LANGNoMatchesFound, [mbOK], mbWarning, mbNone, mbOK); end; 3, 4 : if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do if (not PDataItem(DataList[i])^.UpDir) and (ConfSelectAllDirs or (not PDataItem(DataList[i])^.IsDir)) then PDataItem(DataList[i])^.Selected := not PDataItem(DataList[i])^.Selected; 5, 8 : begin Item := ListView.Selected; if Assigned(Item) and Assigned(Item.Data) then begin Data := PDataItem(Item.Data); if (Engine.Path = '/') or (Item.Index > 0) then Data^.Selected := not Data^.Selected; if (ListView.ConvertToSorted(Item.Index) < ListView.Items.Count - 1) and (((SelectType = 5) and ConfInsMoveDown) or ((SelectType = 8) and ConfSpaceMovesDown)) then begin ListView.Selected := ListView.Items[ListView.ConvertFromSorted(ListView.ConvertToSorted(Item.Index) + 1)]; ListView.Selected.SetCursor(0, False, False, 0, 0); end else Item.RedrawRow; // Move to the next item will invalidate it automatically end; end; 6, 7: if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do if (not PDataItem(DataList[i])^.UpDir) and (ConfSelectAllDirs or (not PDataItem(DataList[i])^.IsDir)) then PDataItem(DataList[i])^.Selected := SelectType = 6; end; if SelectType in [1..4, 6..7] then ListView.Invalidate; // Make changes appear UpdatePanelInfoDown(LeftPanel); Application.ProcessMessages; except end; finally Application.ProcessMessages; InternalUnLock; end; end; function TFMain.IsEditing(AListView: TGTKListView): boolean; var i: integer; begin Result := False; for i := 0 to AListView.Columns.Count - 1 do if Assigned(AListView.Columns[i].FColumn^.editable_widget) then Result := True; end; function TFMain.PanelFindEditableWidget(AListView: TGTKListView): PGtkWidget; var i: integer; begin Result := nil; for i := 0 to AListView.Columns.Count - 1 do if Assigned(AListView.Columns[i].FColumn^.editable_widget) then begin Result := AListView.Columns[i].FColumn^.editable_widget; Break; end; end; procedure TFMain.ProcessMarkKey(KeyType, Key: integer); var LeftPanel: boolean; ListView: TGTKListView; Pos: integer; editable: PGtkEditable; begin if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then ListView := LeftListView else ListView := RightListView; editable := PanelFindEditableWidget(ListView); if Editing and Assigned(editable) then begin Pos := gtk_editable_get_position(editable); gtk_editable_insert_text(editable, PChar(UTF8Encode(WideChar(KeyValToUnicode(Key)))), 1, @Pos); gtk_editable_set_position(editable, Pos); end; if CommandLineCombo.Entry.Focused then ActivateCommandLine(Key, True) else if QuickFind then QuickFindSendKey(LeftPanel, Key) else DoSelect(KeyType); end; procedure TFMain.mnuMarkClick(Sender: TObject); begin if Sender = miSelectGroup then ProcessMarkKey(1, GDK_KP_PLUS) else if Sender = miUnselectGroup then ProcessMarkKey(2, GDK_KP_MINUS) else if Sender = miSelectAll then DoSelect(6) else if Sender = miUnselectAll then DoSelect(7) else if Sender = miInvertSelection then ProcessMarkKey(4, GDK_KP_ASTERISK); end; (********************************************************************************************************************************) procedure TFMain.ListViewCellDataFunc(Sender: TObject; tree_view: PGtkTreeView; tree_column : PGtkTreeViewColumn; cell : PGtkCellRenderer; tree_model : PGtkTreeModel; iter : PGtkTreeIter); var s: PChar; Sel, ImageCol: boolean; Data: PDataItem; i, ColumnID, ColumnIdx: integer; DataList: TList; TreePath: PGtkTreePath; AFGColor, ABGColor: PGdkColor; { Rect, VisibleRect: TGdkRectangle; } begin if Application.GTKVersion_2_0_5_Up then ColumnIdx := gtk_tree_view_column_get_sort_column_id(tree_column) else begin ColumnIdx := 0; for i := 0 to (Sender as TGTKListView).Columns.Count - 1 do if (cell = (Sender as TGTKListView).Columns[i].FRenderer) or (cell = (Sender as TGTKListView).Columns[i].FPixbufRenderer) then begin ColumnIdx := i; Break; end; end; ColumnID := ColumnSortIDs[ColumnIdx + 1] - 1; ImageCol := False; if ColumnIdx = 0 then ImageCol := GTK_IS_CELL_RENDERER_PIXBUF(cell); Data := nil; if not Application.GTKVersion_2_0_5_Up then gtk_tree_model_get(tree_model, iter, 0, @Data, -1) else begin if Sender = LeftListView then DataList := LeftPanelData else DataList := RightPanelData; TreePath := gtk_tree_model_get_path(tree_model, iter); if not Assigned(TreePath) then Exit; (Sender as TGTKListView).ConvertPathToChild(TreePath); Data := DataList[gtk_tree_path_get_indices(TreePath)^]; gtk_tree_path_free(TreePath); end; (* gtk_tree_view_get_cell_area(tree_view, TreePath, nil, @Rect); gtk_tree_view_get_visible_rect(tree_view, @VisibleRect); if (Rect.height = 0) or (Rect.height <> ConfRowHeight) or (Rect.y = 0) or (Rect.y > VisibleRect.y + VisibleRect.height) then Exit; *) // DebugMsg(['Rendering text ', Data^.ColumnData[ColumnID]]); if not Assigned(Data) then Exit; Sel := gtk_tree_selection_iter_is_selected((Sender as TGTKView).FSelection, iter); with Data^ do begin // ################ Prepare colors if Selected then AFGColor := SelectedItemGDKColor else begin if Sel then begin if (Sender as TGTKView).Focused then AFGColor := ActiveItemGDKColor else AFGColor := InactiveItemGDKColor; end else AFGColor := ItemColor; end; if not Sel then ABGColor := NormalItemGDKBackground else if (Sender as TGTKView).Focused then ABGColor := ActiveItemGDKBackground else ABGColor := InactiveItemGDKBackground; // ################ Setting the properties if not ImageCol then begin if Editing and (InplaceEditItem.Data = Data) and (ColumnID < 3) and ((ColumnID = 0) or (ColumnID = 1) or Assigned(tree_column^.editable_widget)) then begin if (ColumnID = 0) or (ColumnID = 1) then s := FDisplayName else s := nil; g_object_set(cell, 'text', s, 'foreground-gdk', AFGColor, nil); if Application.GTKVersion_2_2_0_Up or (not ConfUseFileTypeIcons) then g_object_set(cell, 'background-gdk', ABGColor, nil); end else begin // not editing if ConfDirsInBold then begin if IsDir or UpDir then g_object_set(cell, 'markup', PChar(Format('%s', [QuoteMarkupStr(ColumnData[ColumnID])])), 'foreground-gdk', AFGColor, nil) else g_object_set(cell, 'markup', PChar(QuoteMarkupStr(ColumnData[ColumnID])), 'foreground-gdk', AFGColor, nil); end else g_object_set(cell, 'text', ColumnData[ColumnID], 'foreground-gdk', AFGColor, nil); if Application.GTKVersion_2_2_0_Up or (not ConfUseFileTypeIcons) then g_object_set(cell, 'background-gdk', ABGColor, nil); // Older versions have bug in color filling end; end else // this is the image column if ConfUseFileTypeIcons then begin // Assign icons if Application.GTKVersion_2_2_0_Up then g_object_set(cell, 'cell-background-gdk', ABGColor, nil); // Older versions don't have this property if Sel and (not (Sender as TGTKView).Focused) and Application.GTKVersion_2_2_0_Up then begin if InactiveItemsTimer.Enabled then InactiveItemsTimer.Enabled := False; if Sender = LeftListView then RedrawLeftInactive := True else RedrawRightInactive := True; InactiveItemsTimer.Enabled := not Application.GTKVersion_2_6_0_Up; end; if Icon <> nil then g_object_set(cell, 'pixbuf', Icon, nil); end; end; end; procedure TFMain.F7ButtonClick(Sender: TObject); var LeftPanel: boolean; ListView: TGTKListView; Engine: TPanelEngine; NewDir: string; begin try InternalLock; try if Sender = F7Button then LeftPanel := LeftLastFocused else if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else Exit; if LeftPanel then ListView := LeftListView else ListView := RightListView; if LeftPanel then Engine := LeftPanelEngine else Engine := RightPanelEngine; try FNewDir := TFNewDir.Create(Self); if FNewDir.Run = mbOK then NewDir := UTF8ToStr(FNewDir.Entry.Text) else Exit; finally FNewDir.Free; end; if NewDir = '' then Exit; if not MakeDirectory(ListView, Engine, LeftPanel, NewDir) then Exit; ChangingDir(LeftPanel, Engine.Path, NewDir); DoRefresh(not LeftPanel, True, True); except end; finally Application.ProcessMessages; InternalLockInit(False); end; end; procedure TFMain.DoGetDirSize(AllItems: boolean); var LeftPanel: boolean; ListView: TGTKListView; Engine: TPanelEngine; DataList: TList; begin if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else Exit; if LeftPanel then begin ListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin ListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; end; RunningEscSensitive := 1; GetDirSize(ListView, Engine, DataList, AllItems); if ConfSortDirectoriesLikeFiles and (ColumnSortIDs[ListView.SortColumnID + 1] = 4) then begin if ListView.SortOrder = soAscending then begin ListView.SetSortInfo(ListView.SortColumnID, soDescending); ListView.SetSortInfo(ListView.SortColumnID, soAscending); end else begin ListView.SetSortInfo(ListView.SortColumnID, soAscending); ListView.SetSortInfo(ListView.SortColumnID, soDescending); end; ListView.Selected.SetCursor(0, False, False, 0, 0); end; FMainEscPressed := False; RunningEscSensitive := 0; end; procedure TFMain.DoDelete(LeftPanel: boolean; ListView: TGTKListView; Engine: TPanelEngine; DataList: TList); var i, j : integer; SelCount: longint; s, s2, smsg, NextItem1, NextItem2: string; Data: PDataItem; AWorkingThread: TWorkerThread; AFProgress: TFProgress; begin try InternalLock; SelCount := 0; if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if Selected and (not UpDir) then Inc(SelCount); if (SelCount = 0) and ((not Assigned(ListView.Selected)) or PDataItem(ListView.Selected.Data)^.UpDir) then begin // WriteLn(integer(mbApply), ', ', integer(Application.MessageBox(LANGNoFilesSelected, [mbOK], mbInfo, mbNone, mbApply))); Application.MessageBox(LANGNoFilesSelected, [mbOK], mbInfo, mbNone, mbOK); Exit; end; Data := nil; if Assigned(ListView.Selected) then Data := ListView.Selected.Data; if SelCount > 0 then begin j := 0; s2 := ''; for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if Selected and (not UpDir) then begin s2 := s2 + #10 + FDisplayName; Inc(j); if j = 5 then Break; end; if SelCount > j then s2 := s2 + #10 + '...'; s := Format(LANGSelectedFilesDirectories, [SelCount]); smsg := Format(LANGDoYouReallyWantToDeleteTheSS, [s, s2]); end else begin if Assigned(Data) then if Data^.IsDir then s := Format(LANGDirectoryS, [string(Data^.FDisplayName)]) else s := Format(LANGFileS, [string(Data^.FDisplayName)]); smsg := Format(LANGDoYouReallyWantToDeleteTheS, [s]); end; if Application.MessageBox(QuotePercentStr(smsg), [mbYes, mbNo], mbQuestion, mbNone, mbNo) <> mbYes then Exit; FindNextSelected(ListView, DataList, NextItem1, NextItem2); AWorkingThread := TWorkerThread.Create; DebugMsg(['TFMain.DoDelete: Creating thread...']); AFProgress := TFProgress.Create(Self); try AFProgress.SetNumBars(False); // AFProgress.ProgressBar.Max := 100; AFProgress.ProgressBar.Value := 0; AWorkingThread.ProgressForm := AFProgress; if Assigned(ListView.Selected) then AWorkingThread.SelectedItem := ListView.Selected.Data; AWorkingThread.Engine := Engine; AWorkingThread.LeftPanel := LeftPanel; AWorkingThread.DataList := DataList; AWorkingThread.WorkerProcedure := DeleteFilesWorker; AWorkingThread.Resume; // AFProgress.Run; AFProgress.ParentForm := FMain; AFProgress.ShowModal; ProcessProgressThread(AWorkingThread, AFProgress); AFProgress.Close; { FProgress.ShowModal; Application.ProcessMessages; repeat Sleep(100); Application.ProcessMessages; // FProgress.Run; until False; } { FProgress.ShowModal; DeleteFiles(FProgress, ListView, Engine, LeftPanel, DataList); } finally AFProgress.Free; AWorkingThread.Free; DebugMsg(['TFMain.DoDelete: Freeing thread...']); end; ChangingDir(LeftPanel, Engine.Path, NextItem1, NextItem2); DoRefresh(not LeftPanel, True, True); finally Application.ProcessMessages; InternalUnLock; end; end; procedure TFMain.F8ButtonClick(Sender: TObject); var LeftPanel: boolean; ListView: TGTKListView; Engine: TPanelEngine; DataList: TList; begin try if (Sender = F8Button) or (Sender is TGTKMenuItem) then LeftPanel := LeftLastFocused else if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else Exit; if LeftPanel then begin ListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin ListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; end; DoDelete(LeftPanel, ListView, Engine, DataList); except end; end; procedure TFMain.F5ButtonClick(Sender: TObject); var LeftPanel: boolean; ListView: TGTKListView; Engine: TPanelEngine; DataList: TList; begin try if Sender = F5Button then LeftPanel := LeftLastFocused else if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else Exit; if LeftPanel then begin ListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin ListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; end; DoCopyMove(LeftPanel, True, Sender = nil, ListView, Engine, DataList); except end; end; procedure TFMain.F6ButtonClick(Sender: TObject); var LeftPanel: boolean; ListView: TGTKListView; Engine: TPanelEngine; DataList: TList; begin try if (Sender = F6Button) or (Sender = F2Button) or (Sender is TGTKMenuItem) then LeftPanel := LeftLastFocused else if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else Exit; if LeftPanel then begin ListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin ListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; end; DoCopyMove(LeftPanel, False, (Sender = nil) or (Sender = F2Button) or (Sender is TGTKMenuItem), ListView, Engine, DataList); except end; end; procedure TFMain.DoCopyMove(LeftPanel, CopyMode, ShiftPressed: boolean; ListView: TGTKListView; Engine: TPanelEngine; DataList: TList); var i: integer; SelCount: longint; NewPath, NewPathx, SelSingle, NextItem1, NextItem2: string; AWorkingThread: TWorkerThread; AFProgress: TFProgress; CurrentEngine, OppositeEngine: TPanelEngine; p: PChar; BypassSelAll: boolean; begin try InternalLock; SelCount := 0; if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if Selected and (not UpDir) then Inc(SelCount); if (SelCount = 0) and ((not Assigned(ListView.Selected)) or PDataItem(ListView.Selected.Data)^.UpDir) then begin Application.MessageBox(LANGNoFilesSelected, [mbOK], mbInfo, mbNone, mbOK); Exit; end; BypassSelAll := False; SelSingle := ''; if SelCount = 0 then begin SelCount := 1; SelSingle := PDataItem(ListView.Selected.Data)^.FDisplayName; end; if LeftPanel then begin CurrentEngine := LeftPanelEngine; OppositeEngine := RightPanelEngine; end else begin CurrentEngine := RightPanelEngine; OppositeEngine := LeftPanelEngine; end; try FCopyMove := TFCopyMove.Create(Self); if CopyMode then begin FCopyMove.Caption := LANGCopyFiles; FCopyMove.Label1.Caption := Format(LANGCopyDFileDirectoriesTo, [SelCount]); end else begin FCopyMove.Caption := LANGMoveRenameFiles; FCopyMove.Label1.Caption := Format(LANGMoveRenameDFileDirectoriesTo, [SelCount]); end; if ShiftPressed then begin if SelSingle <> '' then begin FCopyMove.Entry.Text := SelSingle; if ConfQuickRenameSkipExt then begin p := gtk_entry_get_text(PGtkEntry(FCopyMove.Entry.FWidget)); if (p <> nil) and (g_utf8_strlen(p, -1) > 0) then begin // DebugMsg(['TFMain.DoCopyMove: p = "', p, '", g_utf8_strlen(p) = ', g_utf8_strlen(p, -1)]); if AnsiPos('.', p) > 0 then begin FCopyMove.Entry.SelectRegion(0, g_utf8_strlen(p, -1) - g_utf8_strlen(PChar(ExtractFileExt(p)), -1)); BypassSelAll := True; end; end; end; end else FCopyMove.Entry.Text := '*.*'; end else FCopyMove.Entry.Text := OppositeEngine.Path; { if OppositeEngine is TLocalTreeEngine then FCopyMove.Entry.Text := OppositeEngine.Path else FCopyMove.Entry.Text := Format(ConstFullPathFormatStr, [OppositeEngine.GetPrefix, OppositeEngine.Path]); } if not BypassSelAll then FCopyMove.Entry.SelectAll; if FCopyMove.Run <> mbOK then Exit; NewPathx := FCopyMove.Entry.Text; NewPath := UTF8ToStr(FCopyMove.Entry.Text); finally FCopyMove.Free; end; DebugMsg(['TFMain.DoCopyMove: NewPath = "', NewPath, '"']); NextItem1 := ''; NextItem2 := ''; FindNextSelected(ListView, DataList, NextItem1, NextItem2); DebugMsg(['TFMain.DoCopyMove: FindNextSelected, NextItem1 = "', NextItem1, '", NextItem2 = "', NextItem2, '"']); if ShiftPressed then NextItem1 := NewPathx; AWorkingThread := TWorkerThread.Create; DebugMsg(['TFMain.DoCopyMove: Creating thread...']); AFProgress := TFProgress.Create(Self); try if CopyMode then AFProgress.Label1.Caption := LANGCopySC else AFProgress.Label1.Caption := LANGMoveRenameSC; AFProgress.SetNumBars(True); AFProgress.ProgressBar.Value := 0; AWorkingThread.ProgressForm := AFProgress; if Assigned(ListView.Selected) then AWorkingThread.SelectedItem := ListView.Selected.Data; // Determine target engine according to absolute/relative path if (NewPath[1] = '/') or (NewPath[1] = '~') then AWorkingThread.DestEngine := OppositeEngine else AWorkingThread.DestEngine := Engine; AWorkingThread.SrcEngine := Engine; // Determine on which engine the entered path is { if Pos(ConstPathDelim, NewPath) = 0 then begin if OppositeEngine is TLocalTreeEngine then AWorkingThread.DestEngine := OppositeEngine else if CurrentEngine is TLocalTreeEngine then AWorkingThread.DestEngine := CurrentEngine else begin Application.MessageBox(LANGCannotDetermineDestinationEngine, [mbOK], mbError, mbOK, mbOK); Exit; end; end else begin s := Copy(NewPath, 1, Pos(ConstPathDelim, NewPath) - 1); if s = OppositeEngine.GetPrefix then AWorkingThread.DestEngine := OppositeEngine else if s = CurrentEngine.GetPrefix then AWorkingThread.DestEngine := CurrentEngine else begin Application.MessageBox(LANGCannotDetermineDestinationEngine, [mbOK], mbError, mbOK, mbOK); Exit; end; end; } if AWorkingThread = nil then begin // Something went terribly wrong Application.MessageBox(LANGCannotDetermineDestinationEngine, [mbOK], mbError, mbOK, mbOK); Exit; end; // Strip the engine prefix // if Pos(ConstPathDelim, NewPath) > 0 then Delete(NewPath, 1, Pos(ConstPathDelim, NewPath)); AWorkingThread.LeftPanel := LeftPanel; AWorkingThread.DataList := DataList; AWorkingThread.WorkerProcedure := CopyFilesWorker; // AWorkingThread.WorkerProcedure := DummyThreadWorker; AWorkingThread.ParamBool3 := CopyMode; AWorkingThread.ParamBool4 := False; AWorkingThread.ParamString1 := NewPath; AWorkingThread.ParamDataItem1 := nil; AFProgress.ParentForm := FMain; AFProgress.ShowModal; Application.ProcessMessages; DebugMsg(['*** Copy: AWorkingThread.Resume']); // DebugMsg(['*** Copy: xg_thread_supported = ', xg_thread_supported()]); AWorkingThread.Resume; // AWorkingThread.WorkerProcedure(AWorkingThread); DebugMsg(['*** Copy: AWorkingThread.Resumed.']); ProcessProgressThread(AWorkingThread, AFProgress); // DebugMsg(['***************************x1']); AFProgress.Close; // Beep; finally // DebugMsg(['** ddddddddddddddd ???']); AFProgress.Free; DebugMsg(['TFMain.DoCopyMove: Freeing thread...']); AWorkingThread.Free; end; ChangingDir(LeftPanel, Engine.Path, NextItem1, NextItem2); DoRefresh(not LeftPanel, True, False); finally DebugMsg(['** TFMain.DoCopyMove finished']); Application.ProcessMessages; InternalUnLock; end; end; procedure TFMain.ListViewDblClick(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); begin DebugMsg(['DblClick']); Accept := True; // This causes selecting the item if clicked is different than selected if (Sender as TGTKListView).GetItemAtPos(X, Y) <> (Sender as TGTKListView).Selected then Exit; Accept := False; DebugMsg(['DblClick OK']); if not (Sender as TGTKListView).Focused then (Sender as TGTKListView).SetFocus; if Assigned((Sender as TGTKListView).Selected) and Assigned((Sender as TGTKListView).Selected.Data) then ActivateItem((Sender as TGTKListView).Selected.Index); end; procedure TFMain.ListViewMouseDown(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); var Item: TGTKListItem; Click: TDateTime; Data: PDataItem; AEngine: TPanelEngine; begin try InternalLock; PanelRightMouseInProgress := False; // DebugMsg(['ListViewMouseDown, X = ', X, ', Y = ', Y]); if Button = mbLeft then begin Click := Now; if LastClick + ConfDblClickDelay/MSecsPerDay > Click then begin Accept := False; InplaceEditTimer.Enabled := False; LastClick := 0; ListViewDblClick(Sender, Button, Shift, X, Y, Accept); Exit; end; LastClick := Click; DebugMsg(['Click, Focus = ', (Sender as TGTKControl).Focused]); if not (Sender as TGTKListView).Focused then Exit; // (Sender as TGTKListView).SetFocus; Item := (Sender as TGTKListView).GetItemAtPos(X, Y); if (Item = (Sender as TGTKListView).Selected) and Assigned(Item) and Assigned((Sender as TGTKListView).Selected) and Assigned(Item.Data) and (not PDataItem(Item.Data)^.UpDir) and (Sender as TGTKListView).Focused and (not ConfDisableMouseRename) then begin DebugMsg(['Quick-Rename']); InplaceEditTimer.Interval := ConfQuickRenameDelay; InplaceEditTimer.Enabled := True; InplaceEditItem := Item; end; Accept := True; end else if Button = mbRight then begin if not (Sender as TGTKListView).Focused then (Sender as TGTKListView).SetFocus; Item := (Sender as TGTKListView).GetItemAtPos(X, Y); if Assigned(Item) then begin Item.Selected := True; Item.SetCursor(0, False, not Application.GTKVersion_2_2_0_Up, 0.5, 0); end; // Show file popup menu or start selection mode, depending on ConfRightClickSelect if ConfRightClickSelect then begin if (Item <> nil) and (Item.Data <> nil) then begin Data := PDataItem(Item.Data); if not Data^.UpDir then begin PanelRightMouseSelMode := not Data^.Selected; Data^.Selected := PanelRightMouseSelMode; Item.RedrawRow; UpdatePanelInfoDown(Sender = LeftListView); PanelRightMouseInProgress := True; RightMouseSelectPopupTimer.Interval := ConfQuickRenameDelay; RightMouseSelectPopupTimer.Enabled := True; end; end; end else if (Item <> nil) then FilePopupMenu.PopUp; Accept := False; end else if Button = mbMiddle then begin if not (Sender as TGTKListView).Focused then (Sender as TGTKListView).SetFocus; Item := (Sender as TGTKListView).GetItemAtPos(X, Y); if Assigned(Item) then begin Item.Selected := True; Item.SetCursor(0, False, not Application.GTKVersion_2_2_0_Up, 0.5, 0); end; // Open directory in background tab if (Item <> nil) and (Item.Data <> nil) then begin Data := PDataItem(Item.Data); if Sender = LeftListView then AEngine := LeftPanelEngine else AEngine := RightPanelEngine; if (not Data^.UpDir) and (Data^.IsDir) then NewTab(Sender = LeftListView, True, IncludeTrailingPathDelimiter(AEngine.Path) + Data^.FName); end; Accept := False; end; finally Application.ProcessMessages; InternalUnLock; end; end; procedure TFMain.ListViewMouseMove(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); var Item: TGTKListItem; Data: PDataItem; begin Accept := True; if not ((Sender is TGTKListView) and ConfRightClickSelect) then Exit; if (Button = mbRight) and PanelRightMouseInProgress then begin Item := (Sender as TGTKListView).GetItemAtPos(X, Y); if (Item <> nil) and (Item.Data <> nil) and (not Item.Selected) then begin Data := PDataItem(Item.Data); if not Data^.UpDir then begin RightMouseSelectPopupTimer.Enabled := False; Data^.Selected := PanelRightMouseSelMode; Item.Selected := True; Item.SetCursor(0, False, not Application.GTKVersion_2_2_0_Up, 0.5, 0); UpdatePanelInfoDown(Sender = LeftListView); end; end; end else PanelRightMouseInProgress := False; end; procedure TFMain.RightMouseSelectPopupTimerTimer(Sender: TObject); var AListView: TGTKListView; Data: PDataItem; begin RightMouseSelectPopupTimer.Enabled := False; if LeftListView.Focused then AListView := LeftListView else if RightListView.Focused then AListView := RightListView else Exit; if (AListView.Selected <> nil) then begin Data := AListView.Selected.Data; // Explicitly select the item before showing the popup menu if (Data <> nil) and (not Data^.UpDir) then begin Data^.Selected := True; AListView.Selected.RedrawRow; UpdatePanelInfoDown(LeftListView.Focused); FilePopupMenu.PopUp; end; end; end; procedure TFMain.InplaceEditTimerTimer(Sender: TObject); var LeftPanel: boolean; ListView: TGTKListView; begin InplaceEditTimer.Enabled := False; try LeftPanel := LeftLastFocused; if LeftPanel then begin ListView := LeftListView; end else begin ListView := RightListView; end; if ListView.Selected <> InplaceEditItem then Exit; DoQuickRename(LeftPanel, ListView, False); except end; end; procedure TFMain.DoQuickRename(LeftPanel: boolean; ListView: TGTKListView; const CalledFromKey: boolean); var i: integer; s: PChar; begin if (not Assigned(ListView.Selected)) or (not Assigned(ListView.Selected.Data)) or PDataItem(ListView.Selected.Data)^.UpDir or Editing then Exit; Editing := True; if CalledFromKey then InplaceEditItem := ListView.Selected; for i := 1 to ConstNumPanelColumns do if ColumnSortIDs[i] in [1, 2] then begin ListView.Columns[i - 1].SetProperty('editable', 1); ListView.StartEditing(i - 1); if ConfQuickRenameSkipExt and (ListView.Columns[i - 1].FColumn^.editable_widget <> nil) then begin s := gtk_entry_get_text(PGtkEntry(ListView.Columns[i - 1].FColumn^.editable_widget)); if (s <> nil) and (g_utf8_strlen(s, -1) > 0) then begin // DebugMsg(['TFMain.DoQuickRename: s = "', s, '", g_utf8_strlen(s) = ', g_utf8_strlen(s, -1)]); if AnsiPos('.', s) > 0 then gtk_editable_select_region(PGtkEditable(ListView.Columns[i - 1].FColumn^.editable_widget), 0, g_utf8_strlen(s, -1) - g_utf8_strlen(PChar(ExtractFileExt(s)), -1)); end; end; Break; end; end; procedure TFMain.ListViewEdited(Sender: TObject; Column: TGTKTreeViewColumn; Item: TGTKListItem; var NewText: string; var AllowChange: boolean; var DataColumn: integer); var AListView: TGTKListView; DataList: TList; Engine: TPanelEngine; AWorkingThread: TWorkerThread; AFProgress: TFProgress; i: integer; s1, s2: string; begin try InternalLock; AListView := (((Sender as TGTKTreeViewColumn).Parent as TGTKTreeViewColumns).Parent as TGTKListView); for i := 1 to ConstNumPanelColumns do if ColumnSortIDs[i] in [1, 2] then begin AListView.Columns[i - 1].SetProperty('editable', 0); Break; end; if AListView = LeftListView then begin Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin Engine := RightPanelEngine; DataList := RightPanelData; end; Editing := False; DebugMsg(['TFMain.ListViewEdited: FDisplayName = "', String(PDataItem(InplaceEditItem.Data)^.FDisplayName), '", NewText = "', NewText, '"']); if (AListView.Selected = InplaceEditItem) and (AListView.Selected.Data = InplaceEditItem.Data) and (String(PDataItem(InplaceEditItem.Data)^.FDisplayName) <> NewText) then begin AWorkingThread := TWorkerThread.Create; DebugMsg(['TFMain.ListViewEdited: Creating thread...']); AFProgress := TFProgress.Create(Self); try AFProgress.SetNumBars(True); AFProgress.ProgressBar.Value := 0; AFProgress.Label1.Caption := LANGMoveRenameSC; AWorkingThread.ProgressForm := AFProgress; if Assigned(AListView.Selected) then AWorkingThread.SelectedItem := AListView.Selected.Data; AWorkingThread.SrcEngine := Engine; AWorkingThread.DestEngine := Engine; AWorkingThread.LeftPanel := AListView = LeftListView; AWorkingThread.DataList := DataList; AWorkingThread.WorkerProcedure := CopyFilesWorker; AWorkingThread.ParamBool3 := False; AWorkingThread.ParamBool4 := True; AWorkingThread.ParamString1 := UTF8ToStr(NewText); AWorkingThread.ParamDataItem1 := InplaceEditItem.Data; AWorkingThread.Resume; // AWorkingThread.WorkerProcedure(AWorkingThread); AFProgress.ParentForm := FMain; AFProgress.ShowModal; ProcessProgressThread(AWorkingThread, AFProgress); AFProgress.Close; finally AFProgress.Free; DebugMsg(['TFMain.ListViewEdited: Freeing thread...']); AWorkingThread.Free; end; s1 := ''; s2 := ''; FindNextSelected(AListView, DataList, s1, s2); DebugMsg(['TFMain.ListViewEdited: FindNextSelected, s1 = "', s1, '", s2 = "', s2, '"']); ChangingDir(AListView = LeftListView, Engine.Path, {String(PDataItem(InplaceEditItem.Data)^.AName),} NewText, s2); DoRefresh(AListView <> LeftListView, True, True); end; AListView.SetFocus; finally Application.ProcessMessages; InternalUnLock; end; end; procedure TFMain.ListViewSelectionChanged(Sender: TObject); var i: integer; // AListView: TGTKListView; begin if Editing and ((Sender as TGTKListView).Selected <> InplaceEditItem) then begin Editing := False; for i := 1 to ConstNumPanelColumns do if ColumnSortIDs[i] in [1, 2] then begin (Sender as TGTKListView).Columns[i - 1].SetProperty('editable', 0); Break; end; end; if QuickFind then DeactivateQuickFind(Sender = LeftListView); SaveCursorPositionTabbed(Sender = LeftListView); { if Application.GTKVersion_2_6_0_Up then begin AListView := Sender as TGTKListView; if Assigned(AListView) and Assigned(AListView.Selected) and Assigned(AListView.Selected.Data) and Assigned(PDataItem(AListView.Selected.Data)^.ItemColor) then gtk_widget_modify_text(AListView.FWidget, GTK_STATE_SELECTED, PDataItem(AListView.Selected.Data)^.ItemColor); end; } end; procedure TFMain.SaveCursorPositionTabbed(LeftPanel: boolean); begin if LeftPanel then begin if LeftPanelNotebook.Visible and (LeftPathsHighlight.Count > LeftPanelNotebook.PageIndex) and (LeftPanelNotebook.PageIndex >= 0) and Assigned(LeftListView.Selected) and Assigned(LeftListView.Selected.Data) then LeftPathsHighlight[LeftPanelNotebook.PageIndex] := PDataItem(LeftListView.Selected.Data)^.FName; end else if RightPanelNotebook.Visible and (RightPathsHighlight.Count > RightPanelNotebook.PageIndex) and (RightPanelNotebook.PageIndex >= 0) and Assigned(RightListView.Selected) and Assigned(RightListView.Selected.Data) then RightPathsHighlight[RightPanelNotebook.PageIndex] := PDataItem(RightListView.Selected.Data)^.FName; end; procedure TFMain.ActivateQuickFind(LeftPanel: boolean); var Entry: TGTKEntry; begin QuickFind := True; if LeftPanel then begin Entry := LeftQuickFindEntry; LeftQuickFindVBox.Show; end else begin Entry := RightQuickFindEntry; RightQuickFindVBox.Show; end; Entry.Text := ''; end; procedure TFMain.QuickFindEntryEnter(Sender: TObject; var Accept: boolean); begin Accept := False; (Sender as TGTKControl).SetFocus; end; procedure TFMain.DeactivateQuickFind(LeftPanel: boolean); begin QuickFind := False; if LeftPanel then LeftQuickFindVBox.Hide else RightQuickFindVBox.Hide; end; function TFMain.QuickFindSendKey(LeftPanel: boolean; Key: word): boolean; var Entry: TGTKEntry; s, NewText: string; i: integer; AListView: TGTKListView; DataList: TList; Data: PDataItem; OldSelectionChangedEvent: TNotifyEvent; g: PChar; NewIndex, StartIndex: Longint; begin Result := False; if not QuickFind then Exit; if LeftPanel then begin Entry := LeftQuickFindEntry; AListView := LeftListView; DataList := LeftPanelData; end else begin Entry := RightQuickFindEntry; AListView := RightListView; DataList := RightPanelData; end; // DebugMsg(['TFMain.QuickFindSendKey: Key = ', Key, ', GDK_Down = ', GDK_Down, ', GDK_Up = ', GDK_Up]); if Key = GDK_BACKSPACE then begin if g_utf8_strlen(PChar(Entry.Text), -1) > 0 then begin // DebugMsg(['TFMain.QuickFindSendKey: before delete: "', Entry.Text, '", ansi = "', UTF8ToStr(Entry.Text), '"']); g := malloc(Length(Entry.Text)); memset(g, 0, Length(Entry.Text)); g_utf8_strncpy(g, PChar(Entry.Text), g_utf8_strlen(PChar(Entry.Text), -1) - 1); // DebugMsg(['TFMain.QuickFindSendKey: after delete: "', g, '", ansi = "', UTF8ToStr(g), '"']); Entry.Text := g; libc_free(g); end; NewText := Entry.Text; end else if (Key = GDK_Down) or (Key = GDK_Up) then begin if Length(Entry.Text) = 0 then begin DeactivateQuickFind(LeftPanel); Result := False; Exit; end else NewText := Entry.Text; end else begin s := UTF8Encode(WideChar(KeyValToUnicode(Key))); if (Length(s) = 0) or (s = #0) then Exit; NewText := Entry.Text + s; end; if (DataList.Count > 0) and (Length(NewText) > 0) then begin NewIndex := -1; StartIndex := (AListView.ConvertToSorted(AListView.Selected.Index) + Ord(Key = GDK_Down) - Ord(Key = GDK_Up)) mod DataList.Count; if StartIndex < 0 then StartIndex := 0; if StartIndex > DataList.Count - 1 then StartIndex := DataList.Count - 1; // DebugMsg(['TFMain.QuickFindSendKey: StartIndex = ', StartIndex]); if Key <> GDK_Up then begin // Search down for i := StartIndex to DataList.Count - 1 do begin Data := DataList[AListView.ConvertFromSorted(i)]; if Assigned(Data) and (not Data^.UpDir) and (Pos(WideUpperCase(NewText), WideUpperCase(Data^.FDisplayName)) = 1) then begin NewIndex := i; Break; end; end; if NewIndex < 0 then for i := 0 to StartIndex do begin Data := DataList[AListView.ConvertFromSorted(i)]; if Assigned(Data) and (not Data^.UpDir) and (Pos(WideUpperCase(NewText), WideUpperCase(Data^.FDisplayName)) = 1) then begin NewIndex := i; Break; end; end; end else begin // Search up for i := StartIndex downto 0 do begin Data := DataList[AListView.ConvertFromSorted(i)]; if Assigned(Data) and (not Data^.UpDir) and (Pos(WideUpperCase(NewText), WideUpperCase(Data^.FDisplayName)) = 1) then begin NewIndex := i; Break; end; end; if NewIndex < 0 then for i := DataList.Count - 1 downto StartIndex do begin Data := DataList[AListView.ConvertFromSorted(i)]; if Assigned(Data) and (not Data^.UpDir) and (Pos(WideUpperCase(NewText), WideUpperCase(Data^.FDisplayName)) = 1) then begin NewIndex := i; Break; end; end; end; if NewIndex >= 0 then begin OldSelectionChangedEvent := AListView.OnSelectionChanged; AListView.OnSelectionChanged := nil; AListView.Selected := AListView.Items[AListView.ConvertFromSorted(NewIndex)]; AListView.Items[AListView.ConvertFromSorted(NewIndex)].SetCursor(0, False, False, 0, 0); SaveCursorPositionTabbed(LeftPanel); AListView.OnSelectionChanged := OldSelectionChangedEvent; Entry.Text := NewText; end else Beep; Result := True; end; end; (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.miVerifyChecksumsClick(Sender: TObject); var i, SelCount: integer; b, LeftPanel: boolean; AListView: TGTKListView; Engine: TPanelEngine; DataList: TList; begin try InternalLock; if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then begin AListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin AListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; end; SelCount := 0; if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if Selected and (not UpDir) and (not IsDir) then Inc(SelCount); if (SelCount = 0) and ((not Assigned(AListView.Selected)) or PDataItem(AListView.Selected.Data)^.UpDir or PDataItem(AListView.Selected.Data)^.IsDir) then begin Application.MessageBox(LANGYouMustSelectAValidFile, [mbOK], mbError, mbNone, mbOK); Exit; end; try FChecksum := TFChecksum.Create(Self); FChecksum.Engine := Engine; FChecksum.DataList := DataList; FChecksum.AListView := AListView; if SelCount = 0 then b := FChecksum.ProcessFile(IncludeTrailingPathDelimiter(Engine.Path) + string(PDataItem(AListView.Selected.Data)^.FName)) else begin b := False; for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if (not UpDir) and (not IsDir) and Selected then {$B+} b := b or FChecksum.ProcessFile(IncludeTrailingPathDelimiter(Engine.Path) + string(FName)); {$B-} end; if b { and (FChecksum.List.Count > 0) } then FChecksum.Run; finally FChecksum.Free; end; finally Application.ProcessMessages; InternalLockInit(False); end; end; (********************************************************************************************************************************) procedure TFMain.miCreateChecksumsClick(Sender: TObject); var i, SelCount: integer; LeftPanel: boolean; AListView: TGTKListView; Engine: TPanelEngine; DataList: TList; begin try InternalLock; if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then begin AListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin AListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; end; SelCount := 0; if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if Selected and (not UpDir) and (not IsDir) then Inc(SelCount); if (SelCount = 0) and ((not Assigned(AListView.Selected)) or PDataItem(AListView.Selected.Data)^.UpDir or PDataItem(AListView.Selected.Data)^.IsDir) then begin Application.MessageBox(LANGYouMustSelectAtLeastOneFileToCalculateChecksum, [mbOK], mbError, mbNone, mbOK); Exit; end; try FChecksumDruid := TFChecksumDruid.Create(Self); Engine.ExplicitChDir(Engine.Path); FChecksumDruid.Engine := Engine; if Engine.Path = '/' then FChecksumDruid.DirName := 'root' else FChecksumDruid.DirName := ExtractFileName(ExcludeTrailingPathDelimiter(Engine.Path)); if SelCount = 0 then FChecksumDruid.FileNames.Add(IncludeTrailingPathDelimiter(Engine.Path) + string(PDataItem(AListView.Selected.Data)^.FName)) else for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if (not UpDir) and (not IsDir) and Selected then FChecksumDruid.FileNames.Add(IncludeTrailingPathDelimiter(Engine.Path) + string(FName)); FChecksumDruid.Run; finally if FChecksumDruid.SeparateFileCheckBox.Checked then DoRefresh(AListView = LeftListView, True, True) else ChangingDir(AListView = LeftListView, Engine.Path, FChecksumDruid.FileNameEntry.Text, PDataItem(AListView.Selected.Data)^.FName); DoRefresh(AListView <> LeftListView, True, True); FChecksumDruid.Free; Engine.ExplicitChDir('/'); end; finally Application.ProcessMessages; InternalLockInit(False); end; end; (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.miMergeFilesClick(Sender: TObject); var LeftPanel, HasInitialCRC: boolean; AListView: TGTKListView; Engine: TPanelEngine; FilePath, s, TargetName: string; TargetCRC: LongWord; TargetSize: Int64; AWorkingThread: TWorkerThread; AFProgress: TFProgress; begin try InternalLock; if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then begin AListView := LeftListView; Engine := LeftPanelEngine; end else begin AListView := RightListView; Engine := RightPanelEngine; end; if (not Assigned(AListView.Selected)) or (not Assigned(AListView.Selected.Data)) or PDataItem(AListView.Selected.Data)^.IsDir or PDataItem(AListView.Selected.Data)^.UpDir then begin Application.MessageBox(LANGYouMustSelectAValidFile, [mbOK], mbError, mbNone, mbOK); Exit; end; FilePath := ''; try FNewDir := TFNewDir.Create(Self); FNewDir.Caption := LANGMergeCaption; FNewDir.Label1.SetSizeRequest(500, -1); FNewDir.Label1.Caption := Format(LANGMergeSAndAllFilesWithAscendingNamesToTheFollowingDirectory, [PDataItem(AListView.Selected.Data)^.FDisplayName]); if LeftPanel then FNewDir.Entry.Text := StrToUTF8(RightPanelEngine.Path) else FNewDir.Entry.Text := StrToUTF8(LeftPanelEngine.Path); FNewDir.Entry.SelectAll; if FNewDir.Run = mbOK then FilePath := UTF8ToStr(FNewDir.Entry.Text) else Exit; finally FNewDir.Free; end; if FilePath = '' then Exit; AWorkingThread := TWorkerThread.Create; DebugMsg(['TFMain.miMergeFilesClick: Creating thread...']); AFProgress := TFProgress.Create(Self); try AWorkingThread.ParamString1 := FilePath; AWorkingThread.ParamString2 := IncludeTrailingPathDelimiter(Engine.Path) + PDataItem(AListView.Selected.Data)^.FName; HasInitialCRC := CRCGetInfo(AWorkingThread.ParamString2, Engine, TargetName, TargetCRC, TargetSize); AWorkingThread.ParamString3 := TargetName; AWorkingThread.ParamBool1 := HasInitialCRC; AWorkingThread.ParamLongWord1 := TargetCRC; AWorkingThread.ParamInt64 := TargetSize; AFProgress.SetNumBars(HasInitialCRC); AFProgress.ProgressBar.Value := 0; AFProgress.Label1.Caption := LANGMergeSC; AWorkingThread.ProgressForm := AFProgress; AWorkingThread.Engine := Engine; AWorkingThread.LeftPanel := LeftPanel; AWorkingThread.WorkerProcedure := MergeFilesWorker; AWorkingThread.Resume; AFProgress.ParentForm := FMain; AFProgress.ShowModal; ProcessProgressThread(AWorkingThread, AFProgress); AFProgress.Close; s := AWorkingThread.ParamString3; finally AFProgress.Free; DebugMsg(['TFMain.miMergeFilesClick: Freeing thread...']); AWorkingThread.Free; end; ChangingDir(LeftPanel, Engine.Path, s, PDataItem(AListView.Selected.Data)^.FName); DoRefresh(not LeftPanel, True, True); finally Application.ProcessMessages; InternalLockInit(False); end; end; (********************************************************************************************************************************) procedure TFMain.miSplitFileClick(Sender: TObject); var LeftPanel: boolean; AListView: TGTKListView; Engine: TPanelEngine; FilePath: string; DeleteTarget: boolean; MaxSize: Int64; i: integer; AWorkingThread: TWorkerThread; AFProgress: TFProgress; begin try InternalLock; if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then begin AListView := LeftListView; Engine := LeftPanelEngine; end else begin AListView := RightListView; Engine := RightPanelEngine; end; if (not Assigned(AListView.Selected)) or (not Assigned(AListView.Selected.Data)) or PDataItem(AListView.Selected.Data)^.IsDir or PDataItem(AListView.Selected.Data)^.UpDir then begin Application.MessageBox(LANGYouMustSelectAValidFile, [mbOK], mbError, mbNone, mbOK); Exit; end; FilePath := ''; try FSplitFile := TFSplitFile.Create(Self); FSplitFile.Label1.Caption := Format(LANGSplitTheFileSToDirectory, [PDataItem(AListView.Selected.Data)^.FDisplayName]); FSplitFile.Label1.UseUnderline := True; if LeftPanel then FSplitFile.Entry.Text := StrToUTF8(RightPanelEngine.Path) else FSplitFile.Entry.Text := StrToUTF8(LeftPanelEngine.Path); FSplitFile.Entry.SelectAll; if FSplitFile.Run = mbOK then FilePath := UTF8ToStr(FSplitFile.Entry.Text) else Exit; DeleteTarget := FSplitFile.DeleteTargetCheckBox.Checked; MaxSize := 0; for i := 1 to Length(SplitConsts) do if Trim(WideUpperCase(SplitConsts[i].Title)) = Trim(WideUpperCase(FSplitFile.SizeCombo.Entry.Text)) then begin MaxSize := SplitConsts[i].PartSize; Break; end; if MaxSize = 0 then MaxSize := GetStrSize(FSplitFile.SizeCombo.Entry.Text); finally FSplitFile.Free; end; if FilePath = '' then Exit; AWorkingThread := TWorkerThread.Create; DebugMsg(['TFMain.miSplitFileClick: Creating thread...']); AFProgress := TFProgress.Create(Self); try AWorkingThread.ParamString1 := IncludeTrailingPathDelimiter(Engine.Path) + PDataItem(AListView.Selected.Data)^.FName; AWorkingThread.ParamString2 := FilePath; AWorkingThread.ParamBool1 := DeleteTarget; AWorkingThread.ParamInt64 := MaxSize; AFProgress.SetNumBars(MaxSize > 0); AFProgress.ProgressBar.Value := 0; AFProgress.Label1.Caption := LANGSplitSC; AWorkingThread.ProgressForm := AFProgress; AWorkingThread.Engine := Engine; AWorkingThread.LeftPanel := LeftPanel; AWorkingThread.WorkerProcedure := SplitFilesWorker; AWorkingThread.Resume; AFProgress.ParentForm := FMain; AFProgress.ShowModal; ProcessProgressThread(AWorkingThread, AFProgress); AFProgress.Close; finally AFProgress.Free; DebugMsg(['TFMain.miSplitFileClick: Freeing thread...']); AWorkingThread.Free; end; DoRefresh(LeftPanel, True, True); DoRefresh(not LeftPanel, True, True); finally Application.ProcessMessages; InternalLockInit(False); end; end; (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.SwitchOtherPanel(LeftPanel, RequestNewAltO: boolean); var AListView: TGTKListView; SrcEngine, OrigSrcEngine, TargetEngine: TPanelEngine; s: string; DontShowAgain: boolean; begin if LeftPanel then begin AListView := LeftListView; SrcEngine := LeftPanelEngine; TargetEngine := RightPanelEngine; end else begin AListView := RightListView; SrcEngine := RightPanelEngine; TargetEngine := LeftPanelEngine; end; OrigSrcEngine := SrcEngine; if not (SrcEngine is TLocalTreeEngine) then begin if ConfSwitchOtherPanelBehaviour < 0 then begin MessageBoxShowOnce(PGtkWindow(FWidget), LANGSwitchOtherPanelWarning, LANGDontShowAgain, DontShowAgain, [mbOK], mbWarning, mbOK, mbOK); if DontShowAgain then begin ConfSwitchOtherPanelBehaviour := 1; WriteMainGUISettings; end; end; // Exit; end; // don't change dir in VFS engines while not (SrcEngine is TLocalTreeEngine) do SrcEngine := SrcEngine.ParentEngine; while not (TargetEngine is TLocalTreeEngine) do TargetEngine := TargetEngine.ParentEngine; if (not ConfNewStyleAltO) and (not RequestNewAltO) then begin s := ExcludeTrailingPathDelimiter(SrcEngine.Path); if OrigSrcEngine = SrcEngine then begin if (not Assigned(AListView.Selected)) or (not Assigned(AListView.Selected.Data)) or PDataItem(AListView.Selected.Data)^.UpDir or (not PDataItem(AListView.Selected.Data)^.IsDir) then begin if Length(s) > 1 then s := IncludeTrailingPathDelimiter(Copy(s, 1, LastDelimiter(PathDelim, s))); end else s := IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(s) + PDataItem(AListView.Selected.Data)^.FName); end; if IncludeTrailingPathDelimiter(TargetEngine.Path) <> s then begin if LeftPanel then RightPanelEngine := TargetEngine else LeftPanelEngine := TargetEngine; ChangingDir(not LeftPanel, s); end; // Move one item down if (OrigSrcEngine = SrcEngine) and Assigned(AListView.Selected) and (AListView.ConvertToSorted(AListView.Selected.Index) < AListView.Items.Count - 1) then begin AListView.Selected := AListView.Items[AListView.ConvertFromSorted(AListView.ConvertToSorted(AListView.Selected.Index) + 1)]; AListView.Selected.SetCursor(0, False, False, 0, 0); end; end else begin if IncludeTrailingPathDelimiter(TargetEngine.Path) <> IncludeTrailingPathDelimiter(SrcEngine.Path) then begin if LeftPanel then RightPanelEngine := TargetEngine else LeftPanelEngine := TargetEngine; ChangingDir(not LeftPanel, SrcEngine.Path); end; end; end; procedure TFMain.miShowDotFilesClick(Sender: TObject); begin ConfShowDotFiles := miShowDotFiles.Checked; DoRefresh(True, True, True); DoRefresh(False, True, True); end; procedure TFMain.F3F4ButtonClick(Sender: TObject); var LeftPanel: boolean; AListView: TGTKListView; begin try InternalLock; if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then begin AListView := LeftListView; end else begin AListView := RightListView; end; if (not Assigned(AListView.Selected)) or (not Assigned(AListView.Selected.Data)) or PDataItem(AListView.Selected.Data)^.IsDir or PDataItem(AListView.Selected.Data)^.UpDir then begin Application.MessageBox(LANGYouMustSelectAValidFile, [mbOK], mbError, mbNone, mbOK); Exit; end; EditViewFile(LeftPanel, AListView, (Sender = F3Button) or ((Sender is TGTKMenuItem) and (Integer((Sender as TGTKMenuItem).Data) = 200)), False); finally Application.ProcessMessages; InternalLockInit(False); end; end; procedure TFMain.EditViewFile(LeftPanel: boolean; AListView: TGTKListView; View, NewFile: boolean); var ANewDir: TFNewDir; Engine: TPanelEngine; AFile: string; begin try InternalLock; if LeftPanel then Engine := LeftPanelEngine else Engine := RightPanelEngine; if NewFile then begin ANewDir := TFNewDir.Create(Self); try ANewDir.Caption := LANGEdit; ANewDir.Label1.Caption := LANGEnterFilenameToEdit; ANewDir.Label1.UseUnderline := True; ANewDir.Label1.FocusControl := ANewDir.Entry; if Assigned(AListView.Selected) and Assigned(AListView.Selected.Data) and (not PDataItem(AListView.Selected.Data)^.IsDir) and (not PDataItem(AListView.Selected.Data)^.UpDir) then ANewDir.Entry.Text := PDataItem(AListView.Selected.Data)^.FDisplayName else ANewDir.Entry.Text := ''; if Length(ANewDir.Entry.Text) > 0 then ANewDir.Entry.SelectAll; if ANewDir.Run <> mbOK then Exit; AFile := IncludeTrailingPathDelimiter(Engine.Path) + UTF8ToStr(ANewDir.Entry.Text); finally ANewDir.Free; end; end else AFile := IncludeTrailingPathDelimiter(Engine.Path) + PDataItem(AListView.Selected.Data)^.FName; EditViewFileInternal(Self, AFile, Engine, View, NewFile); finally Application.ProcessMessages; InternalUnLock; end; end; procedure TFMain.EditViewFileInternal(ParentWindow: TGTKControl; Filename: string; Engine: TPanelEngine; View, NewFile: boolean); var s: string; Stat: PDataItemSL; Error, x: integer; // AViewer: TViewerThread; AViewer: TFViewer; begin Stat := Engine.GetFileInfoSL(Filename); if Assigned(Stat) and (Stat^.Size > ConfEditViewFileSizeLimit) and (Application.MessageBox(LANGTheFileYouAreTryingToOpenIsQuiteBig, [mbYes, mbNo], mbWarning, mbNone, mbNo) = mbNo) then begin FreeDataItem(Stat); Exit; end; FreeDataItem(Stat); if View then s := ConfViewer else s := ConfEditor; if (Engine is TVFSEngine) and (not NewFile) then if not HandleRunFromArchive(Filename, Engine, s, '', True) then Exit; if ConfUseInternalViewer and View then begin (* AViewer := TViewerThread.Create(Self); if not AViewer.LoadFile(AFile) then begin Application.MessageBox(Format('Cannot load file ''%s''. Please check the permissions.', [ANSIToUTF8(AFile)]), [mbOK], mbError); AViewer.Free; end else AViewer.Resume; *) AViewer := TFViewer.Create(ParentWindow); if not AViewer.LoadFile(Filename) then begin Application.MessageBox(Format(LANGCannotLoadFile, [Filename]), [mbOK], mbError, mbNone, mbOK); AViewer.Free; end else begin // gtk_window_set_transient_for(PGtkWindow(AViewer.FWidget), PGtkWindow(ParentWindow.FWidget)); AViewer.Show; end; end else begin if View then x := ConfViewerTerminalBehaviour else x := ConfEditorTerminalBehaviour; if not ExecuteProgram(Format('%s %s', [s, QuoteStr(Filename)]), ExtractFilePath(Filename), x = 0, x = 1, Error) then Application.MessageBox(Format(LANGCannotExecuteSPleaseCheckTheConfiguration, [s]), [mbOK], mbError, mbNone, mbOK); end; end; (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.miFileTypesClick(Sender: TObject); var x: pointer; begin try InternalLock; FFileTypeSettings := TFFileTypeSettings.Create(Self); ReadAssoc; FFileTypeSettings.AssignAssocList(AssocList); FFileTypeSettings.FillList; if FFileTypeSettings.Run = mbOK then begin RemoveIconRefs(FFileTypeSettings.IntAssocList, True); RemoveIconRefs(AssocList, False); FFileTypeSettings.CleanItems; x := AssocList; AssocList := FFileTypeSettings.IntAssocList; FFileTypeSettings.IntAssocList := x; RecreateIcons(AssocList); DoRefresh(True, True, True); DoRefresh(False, True, True); WriteAssoc; end; finally FFileTypeSettings.Free; Application.ProcessMessages; InternalLockInit(False); end; end; procedure TFMain.RunFile(Path: string; Engine: TPanelEngine; CustomAction: integer); var Command, FileTypeDesc: string; i, ac: integer; b, AutodetectGUI, RunInTerminal: boolean; Stat: PDataItemSL; s: string; Assoc: TFileAssoc; begin try InternalLock; Command := ''; FileTypeDesc := ''; AutodetectGUI := True; RunInTerminal := False; Assoc := FindAssoc(ExtractFileName(Path)); if Assoc <> nil then begin FileTypeDesc := Assoc.FileTypeName; if (CustomAction > Assoc.ActionList.Count - 1) or (CustomAction = -1) then ac := Assoc.DefaultAction else ac := CustomAction; if ac > Assoc.ActionList.Count - 1 then ac := 0; if Assoc.ActionList.Count > ac then begin Command := UTF8ToStr(Trim(TAssocAction(Assoc.ActionList[ac]).ActionCommand)); AutodetectGUI := TAssocAction(Assoc.ActionList[ac]).AutodetectGUI; RunInTerminal := TAssocAction(Assoc.ActionList[ac]).RunInTerminal; end; end; // Association not found, try to execute file itself if Command = '' then begin DebugMsg(['File association not found']); if CustomAction > -1 then begin DebugMsg(['Some strange error occured...']); Exit; end; Stat := Engine.GetFileInfoSL(Path); if Assigned(Stat) and Stat^.IsExecutable then begin b := True; if Engine is TVFSEngine then b := HandleRunFromArchive(Path, Engine, Command, FileTypeDesc, False); // not a local engine, extract to local first if b then begin libc_chdir(PChar(ExtractFilePath(Path))); b := ExecuteProgram(QuoteStr(Path), ExtractFilePath(Path), AutodetectGUI, RunInTerminal, i); libc_chdir(PChar('/')); end else b := True; // Mask cancelled extraction from VFS FreeDataItem(Stat); end else begin if Engine is TVFSEngine then begin HandleRunFromArchive(Path, Engine, Command, FileTypeDesc, False); // b := True; end else if Application.MessageBox(Format(LANGThereIsNoApplicationAssociatedWithS, [StrToUTF8(ExtractFileName(Path))]), [mbYes, mbNo], mbQuestion, mbNone, mbNo) = mbYes then miFileTypesClick(Self); Exit; end; end else begin DebugMsg(['File association found: ', Command]); s := Command; b := True; if Engine is TVFSEngine then b := HandleRunFromArchive(Path, Engine, Command, FileTypeDesc, False); // not a local engine, extract to local first if Pos('%s', s) > 0 then s := Format(s, ['''' + QuoteStr(Path) + '''']) else s := Format('%s %s', [s, QuoteStr(Path)]); // DebugMsg(['execute: ', s, ' , ', Command, ' , ', QuoteStr(Path)]); if b then begin libc_chdir(PChar(ExtractFilePath(Path))); b := ExecuteProgram(s, ExtractFilePath(Path), AutodetectGUI, RunInTerminal, i); libc_chdir(PChar('/')); end else b := True; // Mask cancelled extraction from VFS end; if not b then Application.MessageBox(Format(LANGCannotExecuteSPleaseCheckTheConfiguration, [s]), [mbOK], mbError, mbNone, mbOK); finally Application.ProcessMessages; InternalUnLock; end; end; procedure TFMain.CommandLineComboKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); var Error, OldPos, i: integer; AListView: TGTKListView; Engine: TPanelEngine; s, s2, Orig: string; begin try InternalLock; case Key of GDK_RETURN, GDK_KP_ENTER: begin Accept := False; if LeftLastFocused then Engine := LeftPanelEngine else Engine := RightPanelEngine; // Insert filename if (ssAlt in Shift) or (ssCtrl in Shift) then begin if LeftLastFocused then AListView := LeftListView else AListView := RightListView; if Assigned(AListView.Selected) and Assigned(AListView.Selected.Data) and (not PDataItem(AListView.Selected.Data)^.UpDir) then begin s2 := QuoteStr(String(PDataItem(AListView.Selected.Data)^.FDisplayName)) + ' '; if Length(CommandLineCombo.Entry.Text) = 0 then begin CommandLineCombo.Entry.Text := Format('./%s', [s2]); CommandLineCombo.Entry.CursorPosition := Length(s2); end else begin OldPos := CommandLineCombo.Entry.CursorPosition; s := CommandLineCombo.Entry.Text; Insert(s2, s, OldPos + 1); CommandLineCombo.Entry.Text := s; CommandLineCombo.Entry.CursorPosition := OldPos + Length(s2); end; end; Exit; end; Orig := Trim(CommandLineCombo.Entry.Text); if Length(Orig) > 0 then begin if WideUpperCase(Orig) = 'CD' then begin if LeftLastFocused then PathButtonClick(LeftHomeButton) else PathButtonClick(RightHomeButton); end else if (Length(Orig) > 3) and (WideUpperCase(Copy(Orig, 1, 3)) = 'CD ') then begin ChangingDir(LeftLastFocused, ProcessPattern(Engine, UTF8ToStr(Copy(Orig, 4, Length(Orig) - 3)), Engine.Path, '', True)); end else begin while not (Engine is TLocalTreeEngine) do Engine := Engine.ParentEngine; libc_chdir(PChar(Engine.Path)); if not ExecuteProgram(UTF8ToStr(Orig), Engine.Path, ConfCmdLineTerminalBehaviour = 0 , ConfCmdLineTerminalBehaviour = 1, Error) then Application.MessageBox(LANGErrorExecutingCommand, [mbOK], mbError, mbNone, mbOK); libc_chdir('/'); end; end; CommandLineCombo.Entry.Text := ''; if LeftLastFocused then LeftListView.SetFocus else RightListView.SetFocus; if Length(Orig) > 0 then begin SaveItemToHistory(Orig, CommandLineHistory); if CommandLineCombo.Items.Count > 0 then for i := CommandLineCombo.Items.Count - 1 downto 0 do CommandLineCombo.Items.Delete(i); if CommandLineHistory.Count > 0 then for i := 0 to CommandLineHistory.Count - 1 do CommandLineCombo.Items.Append(CommandLineHistory[i]); CommandLineCombo.Entry.Text := ''; end; end; GDK_Down, GDK_Up: begin Accept := False; if LeftLastFocused then AListView := LeftListView else AListView := RightListView; if (not (([ssCtrl] = Shift) and (Key = GDK_Up))) and ( ((Key = GDK_Down) and (AListView.ConvertToSorted(AListView.Selected.Index) < AListView.Items.Count - 1)) or ((Key = GDK_Up) and (AListView.ConvertToSorted(AListView.Selected.Index) > 0))) then begin AListView.Selected := AListView.Items[AListView.ConvertFromSorted(AListView.ConvertToSorted(AListView.Selected.Index) + (Ord(Key = GDK_Down) * 2) - 1)]; AListView.Selected.SetCursor(0, False, False, 0, 0); end; AListView.SetFocus; end; GDK_ESCAPE: begin Accept := False; CommandLineCombo.Entry.Text := ''; if LeftLastFocused then LeftListView.SetFocus else RightListView.SetFocus; end; GDK_TAB: begin Accept := False; if LeftLastFocused then RightListView.SetFocus else LeftListView.SetFocus; end; GDK_P, GDK_Capital_P: if ((Shift = [ssAlt]) or (Shift = [ssCtrl])) and (CommandLineHistory.Count > 0) then begin Accept := False; Orig := Trim(CommandLineCombo.Entry.Text); i := CommandLineHistory.IndexOf(Orig); if i < 0 then begin SavedCmdLine := Orig; i := 0; end else if CommandLineHistory.Count > i + 1 then Inc(i); CommandLineCombo.Entry.Text := CommandLineHistory[i]; CommandLineCombo.Entry.SetFocus; CommandLineCombo.Entry.SelectRegion(Length(CommandLineCombo.Entry.Text), Length(CommandLineCombo.Entry.Text)); end else if not CommandLineCombo.Entry.Focused then ActivateCommandLine(Key); GDK_N, GDK_Capital_N: if ((Shift = [ssAlt]) or (Shift = [ssCtrl])) and (CommandLineHistory.Count > 0) then begin Accept := False; Orig := Trim(CommandLineCombo.Entry.Text); i := CommandLineHistory.IndexOf(Orig); if i < 0 then Exit else if i = 0 then begin s := SavedCmdLine; SavedCmdLine := ''; end else if CommandLineHistory.Count > i then s := CommandLineHistory[i - 1]; CommandLineCombo.Entry.Text := s; CommandLineCombo.Entry.SetFocus; CommandLineCombo.Entry.SelectRegion(Length(CommandLineCombo.Entry.Text), Length(CommandLineCombo.Entry.Text)); end else if not CommandLineCombo.Entry.Focused then ActivateCommandLine(Key); GDK_A, GDK_Capital_A: if (Shift = [ssAlt]) or (Shift = [ssCtrl]) then begin Accept := False; if LeftLastFocused then Engine := LeftPanelEngine else Engine := RightPanelEngine; s2 := QuoteStr(IncludeTrailingPathDelimiter(Engine.Path)); OldPos := CommandLineCombo.Entry.CursorPosition; s := CommandLineCombo.Entry.Text; Insert(s2, s, OldPos + 1); CommandLineCombo.Entry.SetFocus; CommandLineCombo.Entry.Text := s; CommandLineCombo.Entry.CursorPosition := OldPos + Length(s2); end else if not CommandLineCombo.Entry.Focused then ActivateCommandLine(Key); end; finally Application.ProcessMessages; InternalUnLock; end; end; function TFMain.ActivateCommandLine(Key: word; const ActualPosition: boolean = False): boolean; var s, s2: string; OldPos: integer; begin Result := False; s := UTF8Encode(WideChar(KeyValToUnicode(Key))); if (Length(s) = 0) or (s = #0) then Exit; if ActualPosition and CommandLineCombo.Entry.Focused then begin OldPos := CommandLineCombo.Entry.CursorPosition; s2 := CommandLineCombo.Entry.Text; Insert(s, s2, Length(LeftStr(s2, OldPos)) + 1); CommandLineCombo.Entry.Text := s2; CommandLineCombo.Entry.CursorPosition := OldPos + 1; end else begin CommandLineCombo.Entry.Text := CommandLineCombo.Entry.Text + s; CommandLineCombo.Entry.SetFocus; CommandLineCombo.Entry.SelectRegion(Length(CommandLineCombo.Entry.Text), Length(CommandLineCombo.Entry.Text)); end; Result := True; end; procedure TFMain.FormKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); var AListView: TGTKListView; begin if (ConfQuickSearchActivationKey = 2) and (Shift = [ssAlt]) and (LeftListView.Focused or RightListView.Focused) then begin Accept := not HandleKey(Key, Shift, LeftListView.Focused); if not Accept then Exit; end; if CommandLineCombo.Entry.Focused then CommandLineComboKeyDown(Sender, Key, Shift, Accept); if Editing and (Key = GDK_ESCAPE) then begin Editing := False; if LeftLastFocused then AListView := LeftListView else AListView := RightListView; AListView.Columns[0].SetProperty('editable', 0); end; end; procedure TFMain.InactiveItemsTimerTimer(Sender: TObject); procedure Redraw(AListView: TGTKListView; tree_view: PGtkTreeView); var TreePath: PGtkTreePath; Iter: TGtkTreeIter; Rect: TGdkRectangle; PixBuf, Icon: PGdkPixbuf; cell_width, cell_height: integer; begin if (csDestroying in ComponentState) or (AListView = nil) or (AListView.FSelection = nil) then Exit; if not gtk_tree_selection_get_selected(AListView.FSelection, nil, @Iter) then Exit; TreePath := gtk_tree_model_get_path(gtk_tree_view_get_model(tree_view), @Iter); if not Assigned(TreePath) then Exit; gtk_tree_view_get_background_area(tree_view, TreePath, AListView.Columns[0].FColumn, @Rect); gtk_tree_path_free(TreePath); gtk_cell_renderer_get_size(AListView.Columns[0].FPixbufRenderer, PGtkWidget(tree_view), nil, nil, nil, @cell_width, @cell_height); Icon := PDataItem(AListView.Selected.Data)^.Icon; Rect.width := Rect.x + cell_width + AListView.Columns[0].FRenderer^.xpad * 2 + 1; PixBuf := gdk_pixbuf_new(GDK_COLORSPACE_RGB, True, 8, Rect.width, Rect.height); gdk_pixbuf_fill(PixBuf, InactiveItemBGColorNum); gdk_pixbuf_render_to_drawable_alpha(PixBuf, PGdkDrawable(gtk_tree_view_get_bin_window(tree_view)), 0, 0, Rect.x, Rect.y, Rect.width, Rect.height, GDK_PIXBUF_ALPHA_FULL, 0, GDK_RGB_DITHER_NORMAL, 0, 0); gdk_pixbuf_unref(PixBuf); gdk_pixbuf_render_to_drawable_alpha(Icon, PGdkDrawable(gtk_tree_view_get_bin_window(tree_view)), 0, 0, Rect.x + AListView.Columns[0].FRenderer^.xpad, Rect.y + Rect.height div 2 - (16 div 2), 16, 16, GDK_PIXBUF_ALPHA_FULL, 0, GDK_RGB_DITHER_NORMAL, 0, 0); end; begin if Assigned(Sender) and Assigned(InactiveItemsTimer) and Assigned(LeftListView) and Assigned(RightListView) and (not (csDestroying in ComponentState)) then try InactiveItemsTimer.Enabled := False; if RedrawLeftInactive and (not LeftListView.Focused) // and (not ConfInactiveItemDefaultColors) then Redraw(LeftListView, PGtkTreeView(LeftListView.FWidget)); if RedrawRightInactive and (not RightListView.Focused) // and (not ConfInactiveItemDefaultColors) then Redraw(RightListView, PGtkTreeView(RightListView.FWidget)); RedrawLeftInactive := False; RedrawRightInactive := False; except end; end; (********************************************************************************************************************************) function TFMain.OldGTKConvertToSorted(Sender: TObject; const Index: integer): integer; var DataList, List: TList; i: integer; AListView: TGTKListView; begin Result := -1; if Sender = LeftListView then begin DataList := LeftPanelData; AListView := LeftListView; end else begin DataList := RightPanelData; AListView := RightListView; end; List := nil; // Silent compiler warnings if Assigned(DataList) and (DataList.Count > 0) then try List := TList.Create; for i := 0 to DataList.Count - 1 do List.Add(DataList[i]); if (List.Count > 1) and (AListView.SortOrder <> soNone) then SortDataList(List, AListView.SortOrder = soAscending, AListView.SortColumnID); Result := List.IndexOf(DataList[Index]); finally List.Free; end; end; function TFMain.OldGTKConvertFromSorted(Sender: TObject; const Index: integer): integer; var DataList, List: TList; i: integer; AListView: TGTKListView; begin Result := -1; if Sender = LeftListView then begin DataList := LeftPanelData; AListView := LeftListView; end else begin DataList := RightPanelData; AListView := RightListView; end; List := nil; // Silent compiler warnings if Assigned(DataList) and (DataList.Count > 0) then try List := TList.Create; for i := 0 to DataList.Count - 1 do List.Add(DataList[i]); if (List.Count > 1) and (AListView.SortOrder <> soNone) then SortDataList(List, AListView.SortOrder = soAscending, AListView.SortColumnID); Result := DataList.IndexOf(List[Index]); finally List.Free; end; end; (********************************************************************************************************************************) procedure TFMain.SplitterPopupMenuClick(Sender: TObject); begin if not (Sender is TGTKMenuItem) then Exit; ConfPanelSep := Integer((Sender as TGTKMenuItem).Data); PanelSeparator.Position := Round(Width * (ConfPanelSep / 100)); end; (********************************************************************************************************************************) procedure TFMain.miChangePermissionsClick(Sender: TObject); var LeftPanel: boolean; AListView: TGTKListView; Engine: TPanelEngine; DataList: TList; i: longint; SelCount: longint; AFile, NextItem1, NextItem2: string; Stat: PDataItemSL; UsrManager: TUserManager; AWorkingThread: TWorkerThread; AFProgress: TFProgress; begin try InternalLock; if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then begin AListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin AListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; end; SelCount := 0; if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if Selected and (not UpDir) then Inc(SelCount); if (SelCount = 0) and ((not Assigned(AListView.Selected)) or PDataItem(AListView.Selected.Data)^.UpDir) then begin Application.MessageBox(LANGNoFilesSelected, [mbOK], mbInfo, mbNone, mbOK); Exit; end; AFile := ''; if SelCount = 0 then AFile := PDataItem(AListView.Selected.Data)^.FName else for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if Selected and (not UpDir) then begin AFile := FName; Break; end; if AFile <> '' then try FChmod := TFChmod.Create(Self); Stat := Engine.GetFileInfoSL(IncludeTrailingPathDelimiter(Engine.Path) + AFile); if not Assigned(Stat) then Exit; UsrManager := TUserManager.Create; try FChmod.AssignMode(Stat^.Mode, AFile, UsrManager.GetUserName(Stat^.UID), UsrManager.GetGroupName(Stat^.GID)); finally UsrManager.Free; end; if FChmod.Run = mbOK then begin FindNextSelected(AListView, DataList, NextItem1, NextItem2); AWorkingThread := TWorkerThread.Create; AFProgress := TFProgress.Create(Self); try AFProgress.SetNumBars(False); AFProgress.ProgressBar.Value := 0; AFProgress.Label1.Caption := LANGChmodProgress; AWorkingThread.ProgressForm := AFProgress; if Assigned(AListView.Selected) then AWorkingThread.SelectedItem := AListView.Selected.Data; AWorkingThread.ParamBool1 := FChmod.RecursiveCheckButton.Checked; AWorkingThread.ParamInt1 := FChmod.RecursiveOptionMenu.ItemIndex; AWorkingThread.ParamCardinal1 := FChmod.LastMode; AWorkingThread.Engine := Engine; AWorkingThread.LeftPanel := LeftPanel; AWorkingThread.DataList := DataList; AWorkingThread.WorkerProcedure := ChmodFilesWorker; AWorkingThread.Resume; AFProgress.ParentForm := FMain; if (SelCount > 1) or FChmod.RecursiveCheckButton.Checked then AFProgress.ShowModal; ProcessProgressThread(AWorkingThread, AFProgress); AFProgress.Close; finally AFProgress.Free; AWorkingThread.Free; end; ChangingDir(LeftPanel, Engine.Path, NextItem1, NextItem2); DoRefresh(not LeftPanel, True, True); end; finally FreeDataItem(Stat); FChmod.Free; end; finally Application.ProcessMessages; InternalLockInit(False); end; end; procedure TFMain.miChangeOwnerClick(Sender: TObject); var LeftPanel: boolean; AListView: TGTKListView; Engine: TPanelEngine; DataList: TList; i: integer; SelCount: longint; AFile, NextItem1, NextItem2: string; Stat: PDataItemSL; AWorkingThread: TWorkerThread; AFProgress: TFProgress; begin try InternalLock; if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then begin AListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin AListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; end; SelCount := 0; if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if Selected and (not UpDir) then Inc(SelCount); if (SelCount = 0) and ((not Assigned(AListView.Selected)) or PDataItem(AListView.Selected.Data)^.UpDir) then begin Application.MessageBox(LANGNoFilesSelected, [mbOK], mbInfo, mbNone, mbOK); Exit; end; AFile := ''; if SelCount = 0 then AFile := PDataItem(AListView.Selected.Data)^.FName else for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if Selected and (not UpDir) then begin AFile := FName; Break; end; if AFile <> '' then try FChown := TFChown.Create(Self); Stat := Engine.GetFileInfoSL(IncludeTrailingPathDelimiter(Engine.Path) + AFile); if not Assigned(Stat) then Exit; FChown.AssignMode(Stat^.Mode, AFile, Stat^.UID, Stat^.GID); if FChown.Run = mbOK then begin FindNextSelected(AListView, DataList, NextItem1, NextItem2); AWorkingThread := TWorkerThread.Create; AFProgress := TFProgress.Create(Self); try AFProgress.SetNumBars(False); AFProgress.ProgressBar.Value := 0; AFProgress.Label1.Caption := LANGChownProgress; AWorkingThread.ProgressForm := AFProgress; if Assigned(AListView.Selected) then AWorkingThread.SelectedItem := AListView.Selected.Data; AWorkingThread.ParamBool1 := FChown.RecursiveCheckButton.Checked; AWorkingThread.ParamCardinal1 := FChown.LastUID; AWorkingThread.ParamCardinal2 := FChown.LastGID; AWorkingThread.Engine := Engine; AWorkingThread.LeftPanel := LeftPanel; AWorkingThread.DataList := DataList; AWorkingThread.WorkerProcedure := ChownFilesWorker; AWorkingThread.Resume; AFProgress.ParentForm := FMain; if (SelCount > 1) or FChown.RecursiveCheckButton.Checked then AFProgress.ShowModal; ProcessProgressThread(AWorkingThread, AFProgress); AFProgress.Close; finally AFProgress.Free; AWorkingThread.Free; end; ChangingDir(LeftPanel, Engine.Path, NextItem1, NextItem2); DoRefresh(not LeftPanel, True, True); end; finally FreeDataItem(Stat); FChown.Free; end; finally Application.ProcessMessages; InternalLockInit(False); end; end; (********************************************************************************************************************************) procedure TFMain.miCreateSymlinkClick(Sender: TObject); var LeftPanel: boolean; AListView: TGTKListView; Engine, EngineOpposite: TPanelEngine; s1, s2: string; begin try InternalLock; if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then begin AListView := LeftListView; Engine := LeftPanelEngine; EngineOpposite := RightPanelEngine; end else begin AListView := RightListView; Engine := RightPanelEngine; EngineOpposite := LeftPanelEngine; end; if (not Assigned(AListView.Selected)) or (not Assigned(AListView.Selected.Data)) or PDataItem(AListView.Selected.Data)^.UpDir then s1 := ExcludeTrailingPathDelimiter(Engine.Path) else s1 := IncludeTrailingPathDelimiter(Engine.Path) + PDataItem(AListView.Selected.Data)^.FName; if Engine.Path <> EngineOpposite.Path then s2 := IncludeTrailingPathDelimiter(EngineOpposite.Path) + PDataItem(AListView.Selected.Data)^.FName else s2 := IncludeTrailingPathDelimiter(EngineOpposite.Path) + Format('Link to %s', [PDataItem(AListView.Selected.Data)^.FName]); if CreateSymlink(s1, s2, Engine) then begin DoRefresh(LeftPanel, True, True); DoRefresh(not LeftPanel, True, True); end; finally Application.ProcessMessages; InternalLockInit(False); end; end; procedure TFMain.miEditSymlinkClick(Sender: TObject); var LeftPanel: boolean; AListView: TGTKListView; Engine: TPanelEngine; begin try InternalLock; if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then begin AListView := LeftListView; Engine := LeftPanelEngine; end else begin AListView := RightListView; Engine := RightPanelEngine; end; if (not Assigned(AListView.Selected)) or (not Assigned(AListView.Selected.Data)) or (not PDataItem(AListView.Selected.Data)^.IsLnk) or PDataItem(AListView.Selected.Data)^.UpDir then begin Application.MessageBox(LANGYouMustSelectAValidSymbolicLink, [mbOK], mbError, mbNone, mbOK); Exit; end; if EditSymlink(IncludeTrailingPathDelimiter(Engine.Path) + PDataItem(AListView.Selected.Data)^.FName, Engine) then begin DoRefresh(LeftPanel, True, True); DoRefresh(not LeftPanel, True, True); end; finally Application.ProcessMessages; InternalLockInit(False); end; end; (********************************************************************************************************************************) procedure TFMain.FilePopupMenuPopup(Sender: TObject); procedure ClearPopupMenu(Popup: TGTKMenuItem); begin while Popup.Count > 0 do begin if Popup.Items[0].Count > 0 then ClearPopupMenu(Popup.Items[0]); Popup.Items[0].Free; Popup.Delete(0); end; end; var Item: TGTKMenuItem; DataItem: PDataItemSL; Engine: TPanelEngine; AListView: TGTKListView; FileName, ShortFName: string; UpDir, Found: boolean; i, j: integer; Assoc: TFileAssoc; begin ClearPopupMenu(FilePopupMenu); if LeftLastFocused then begin Engine := LeftPanelEngine; AListView := LeftListView; end else begin Engine := RightPanelEngine; AListView := RightListView; end; FileName := IncludeTrailingPathDelimiter(Engine.Path); if Assigned(AListView.Selected) and Assigned(AListView.Selected.Data) and (not PDataItem(AListView.Selected.Data)^.UpDir) then FileName := FileName + PDataItem(AListView.Selected.Data)^.FName; ShortFName := ExtractFileName(ExcludeTrailingPathDelimiter(FileName)); DataItem := Engine.GetFileInfoSL(FileName); if not Assigned(DataItem) then begin DebugMsg(['Error: File data not assigned. Bug ???! FileName = ', FileName]); Exit; end; UpDir := PDataItem(AListView.Selected.Data)^.UpDir; if not DataItem^.IsDir then begin Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := Format(LANGPopupRunS, [QuoteMarkupStr(StrToUTF8(ShortFName), True)]); Item.StockIcon := 'gtk-execute'; Item.Data := Pointer(1); Item.OnClick := FilePopupMenuItemClick; Item.Enabled := Engine.FileCanRun(FileName); FilePopupMenu.Add(Item); end else begin // Open directory Item := TGTKMenuItem.CreateTyped(Self, itImageText); if UpDir then Item.Caption := LANGPopupGoUp else Item.Caption := Format(LANGPopupOpenS, [QuoteMarkupStr(StrToUTF8(ShortFName), True)]); Item.StockIcon := 'gtk-open'; Item.Data := Pointer(1); Item.OnClick := FilePopupMenuItemClick; FilePopupMenu.Add(Item); // Open directory in background tab if not UpDir then begin Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := 'Open directory in _background tab'; Item.Data := Pointer(3); Item.OnClick := FilePopupMenuItemClick; FilePopupMenu.Add(Item); end; end; // Find actions for meta-item if AssocList.Count > 0 then for i := 0 to AssocList.Count - 1 do if ((DataItem^.IsDir and (TFileAssoc(AssocList[i]).FileTypeName = ConstFTAMetaDirectory)) or ((not DataItem^.IsDir) and (TFileAssoc(AssocList[i]).FileTypeName = ConstFTAMetaFile))) and (TFileAssoc(AssocList[i]).ActionList.Count > 0) then with TFileAssoc(AssocList[i]) do begin FilePopupMenu.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); for j := 0 to ActionList.Count - 1 do begin Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := Format(LANGPopupOpenWithS, [TAssocAction(ActionList[j]).ActionName]); Item.Data := ActionList[j]; Item.OnClick := FilePopupMenuItemClick; FilePopupMenu.Add(Item); end; Break; end; FilePopupMenu.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); // Find and add actions for this file type Found := False; Assoc := FindAssoc(ShortFName); if (Assoc <> nil) and (Assoc.ActionList.Count > 0) then begin Found := True; for j := 0 to Assoc.ActionList.Count - 1 do begin Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := Format(LANGPopupOpenWithS, [TAssocAction(Assoc.ActionList[j]).ActionName]); if ((j = 0) and (Assoc.DefaultAction > Assoc.ActionList.Count - 1)) or (j = Assoc.DefaultAction) then Item.Caption := Item.Caption + LANGPopupDefault; Item.Data := Assoc.ActionList[j]; Item.OnClick := FilePopupMenuItemClick; FilePopupMenu.Add(Item); end; end; if (not Found) and (not DataItem^.IsDir) then begin Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := LANGPopupOpenWith; Item.Data := Pointer(2); Item.OnClick := FilePopupMenuItemClick; FilePopupMenu.Add(Item); end; // Other items if not DataItem^.IsDir then begin FilePopupMenu.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := LANGPopupViewFile; Item.StockIcon := 'gtk-find'; Item.Data := Pointer(200); // This number HAVE to be here due to F3F4ButtonClick method using Item.OnClick := F3F4ButtonClick; FilePopupMenu.Add(Item); Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := LANGPopupEditFile; Item.Data := Pointer(201); // Here too Item.OnClick := F3F4ButtonClick; FilePopupMenu.Add(Item); end; if not DataItem^.IsDir then FilePopupMenu.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := LANGPopupMakeSymlink; Item.StockIcon := 'gtk-jump-to'; Item.OnClick := miCreateSymlinkClick; FilePopupMenu.Add(Item); if DataItem^.IsLnk then begin Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := LANGmiEditSymlink_Caption; Item.OnClick := miEditSymlinkClick; FilePopupMenu.Add(Item); end; FilePopupMenu.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := LANGmiChangePermissions_Caption; Item.StockIcon := 'gtk-convert'; Item.OnClick := miChangePermissionsClick; Item.Enabled := not UpDir; FilePopupMenu.Add(Item); Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := LANGmiChangeOwner_Caption; Item.OnClick := miChangeOwnerClick; Item.Enabled := not UpDir; FilePopupMenu.Add(Item); FilePopupMenu.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := LANGPopupRename; Item.OnClick := F6ButtonClick; Item.Enabled := not UpDir; FilePopupMenu.Add(Item); Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := LANGPopupDelete; Item.StockIcon := 'gtk-delete'; Item.OnClick := F8ButtonClick; Item.Enabled := not UpDir; FilePopupMenu.Add(Item); // FilePopupMenu.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); Item := TGTKMenuItem.CreateTyped(Self, itImageText); Item.Caption := LANGFilePopupMenu_Properties; Item.StockIcon := 'gtk-properties'; Item.OnClick := miFilePropertiesClick; Item.Enabled := False; Item.Visible := False; // Item.Enabled := not UpDir; FilePopupMenu.Add(Item); FreeDataItem(DataItem); end; procedure TFMain.FilePopupMenuItemClick(Sender: TObject); var Engine: TPanelEngine; DataItem: PDataItemSL; AListView: TGTKListView; FileName, ShortFName, s: string; Error: integer; b: boolean; begin try InternalLock; if (not Assigned(Sender)) or (not (Sender is TGTKMenuItem)) then begin DebugMsg(['Error: Popup menuitem is invalid']); end; if LeftLastFocused then begin Engine := LeftPanelEngine; AListView := LeftListView; end else begin Engine := RightPanelEngine; AListView := RightListView; end; FileName := IncludeTrailingPathDelimiter(Engine.Path); if Assigned(AListView.Selected) and Assigned(AListView.Selected.Data) and (not PDataItem(AListView.Selected.Data)^.UpDir) then FileName := FileName + PDataItem(AListView.Selected.Data)^.FName; ShortFName := ExtractFileName(ExcludeTrailingPathDelimiter(FileName)); DataItem := Engine.GetFileInfoSL(FileName); if not Assigned(DataItem) then begin DebugMsg(['Error: File data not assigned. Bug ???! FileName = ', FileName]); Exit; end; if ConfUseURI and ((Engine is TVFSEngine) and (not TVFSEngine(Engine).ArchiveMode)) then FileName := ExcludeTrailingPathDelimiter(Engine.GetPrefix) + FileName; case Integer((Sender as TGTKMenuItem).Data) of 1: if DataItem^.IsDir then ActivateItem(AListView.Selected.Index) else begin b := True; if Engine is TVFSEngine then b := HandleRunFromArchive(FileName, Engine, '', '', False); // not a local engine, extract to local first if b then begin libc_chdir(PChar(ExtractFilePath(FileName))); b := ExecuteProgram(QuoteStr(FileName), ExtractFilePath(FileName), True, False, Error); libc_chdir(PChar('/')); end else b := True; // Mask cancelled extraction from VFS if not b then Application.MessageBox(LANGErrorExecutingCommand, [mbOK], mbError, mbNone, mbOK); end; 2: if Application.MessageBox(Format(LANGThereIsNoApplicationAssociatedWithS, [ShortFName]), [mbYes, mbNo], mbQuestion, mbNone, mbNo) = mbYes then miFileTypesClick(Self); 3: NewTab(LeftLastFocused, True, FileName); else begin b := True; s := UTF8ToStr(Trim(TAssocAction((Sender as TGTKMenuItem).Data).ActionCommand)); if Engine is TVFSEngine then b := HandleRunFromArchive(FileName, Engine, s, '', False); // not a local engine, extract to local first if Pos('%s', s) > 0 then s := Format(s, ['''' + QuoteStr(FileName) + '''']) else s := Format('%s %s', [s, QuoteStr(FileName)]); if b then begin libc_chdir(PChar(ExtractFilePath(FileName))); b := ExecuteProgram(s, ExtractFilePath(FileName), TAssocAction((Sender as TGTKMenuItem).Data).AutodetectGUI, TAssocAction((Sender as TGTKMenuItem).Data).RunInTerminal, Error); libc_chdir(PChar('/')); end else b := True; // Mask cancelled extraction from VFS if not b then Application.MessageBox(Format(LANGCannotExecuteSPleaseCheckTheConfiguration, [FileName]), [mbOK], mbError, mbNone, mbOK); end; end; FreeDataItem(DataItem); finally Application.ProcessMessages; InternalUnLock; end; end; (********************************************************************************************************************************) procedure TFMain.ListViewMouseUp(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); var AListView: TGTKListView; i, FirstColumn, LastColumn: integer; LeftLV, b: boolean; begin PanelRightMouseInProgress := False; RightMouseSelectPopupTimer.Enabled := False; if Button = mbLeft then begin AListView := Sender as TGTKListView; LeftLV := AListView = LeftListView; GetFirstLastPanelColumn(FirstColumn, LastColumn); b := True; for i := 0 to AListView.Columns.Count - 1 do if (AListView.Columns[i].Tag <> LastColumn) or (FirstColumn = LastColumn) then b := b and (ConfColumnSizes[AListView.Columns[i].Tag] = AListView.Columns[i].Width); if not b then begin DebugMsg(['*** ListView Mouse Up -> resizing columns']); for i := 0 to AListView.Columns.Count - 1 do ConfColumnSizes[AListView.Columns[i].Tag] := AListView.Columns[i].Width; if LeftLV then AListView := RightListView else AListView := LeftListView; // Change target widths for i := 0 to AListView.Columns.Count - 1 do if ConfColumnSizes[AListView.Columns[i].Tag] <> AListView.Columns[i].Width then AListView.Columns[i].FixedWidth := ConfColumnSizes[AListView.Columns[i].Tag]; end; end; if Button = mbRight then Accept := False; end; procedure TFMain.ListViewColumnClicked(Sender: TObject); var ANotebook: TEphyNotebook; AListView: TGTKListView; LeftLV: boolean; begin AListView := ((Sender as TGTKTreeViewColumn).Parent as TGTKTreeViewColumns).Parent as TGTKListView; LeftLV := AListView = LeftListView; // Check for sort change if LeftLV then ANotebook := LeftPanelNotebook else ANotebook := RightPanelNotebook; try if ANotebook.Visible then begin DebugMsg(['*** ListView Mouse Up -> saving sort info to tab ', ANotebook.PageIndex, ', SortColumnID = ', AListView.SortColumnID]); if LeftLV then begin LeftTabSortIDs[ANotebook.PageIndex] := Pointer(AListView.SortColumnID); LeftTabSortTypes[ANotebook.PageIndex] := Pointer(Integer(AListView.SortOrder)); end else begin RightTabSortIDs[ANotebook.PageIndex] := Pointer(AListView.SortColumnID); RightTabSortTypes[ANotebook.PageIndex] := Pointer(Integer(AListView.SortOrder)); end; end; except on E: Exception do DebugMsg(['*** Exception raised in TFMain.ListViewColumnClicked(', E.ClassName, '): ', E.Message]); end; end; (********************************************************************************************************************************) procedure TFMain.miPreferencesClick(Sender: TObject); begin try InternalLock; FPreferences := TFPreferences.Create(Self); FPreferences.AssignDefaultValues; if FPreferences.Run = mbOK then begin FPreferences.SaveSettings; WriteMainGUISettings; ApplySettings(FPreferences.RebuildListViews, FPreferences.RebuildIcons, False); end; finally FPreferences.Free; Application.ProcessMessages; InternalLockInit(False); end; end; procedure TFMain.ApplySettings(RebuildListViews, RebuildIcons, Startup: boolean); var i: integer; begin InactiveItemsTimer.Enabled := False; ButtonsBox.Visible := ConfShowFuncButtons; ButtonBoxSeparator.Visible := ConfShowFuncButtons; ButtonBoxSpace.Visible := not ConfShowFuncButtons; // Rebuild Icons if RebuildIcons then begin if ConfRowHeight > 0 then ConfRowHeightReal := ConfRowHeight else ConfRowHeightReal := ConfDefaultRowHeight; FolderIcon.Free; FileIcon.Free; UpDirIcon.Free; SymLinkEmblem.Free; FolderIconLnk.Free; FileIconLnk.Free; LoadIcons; RecreateIcons(AssocList, False); end; // Rebuild ListViews if RebuildListViews then FMain.RebuildListViews(False); InactiveItemsTimer.Enabled := False; // Colors Section SetupColors; gtk_widget_modify_base(LeftListView.FWidget, GTK_STATE_NORMAL, NormalItemGDKBackground); gtk_widget_modify_base(RightListView.FWidget, GTK_STATE_NORMAL, NormalItemGDKBackground); gtk_widget_modify_base(LeftListView.FWidget, GTK_STATE_SELECTED, ActiveItemGDKBackground); gtk_widget_modify_base(RightListView.FWidget, GTK_STATE_SELECTED, ActiveItemGDKBackground); // if Application.GTKVersion_2_6_0_Up then begin gtk_widget_modify_base(LeftListView.FWidget, GTK_STATE_ACTIVE, InactiveItemGDKBackground); gtk_widget_modify_base(RightListView.FWidget, GTK_STATE_ACTIVE, InactiveItemGDKBackground); gtk_widget_modify_text(LeftListView.FWidget, GTK_STATE_NORMAL, NormalItemGDKColor); gtk_widget_modify_text(RightListView.FWidget, GTK_STATE_NORMAL, NormalItemGDKColor); gtk_widget_modify_text(LeftListView.FWidget, GTK_STATE_SELECTED, ActiveItemGDKColor); gtk_widget_modify_text(RightListView.FWidget, GTK_STATE_SELECTED, ActiveItemGDKColor); gtk_widget_modify_text(LeftListView.FWidget, GTK_STATE_ACTIVE, InactiveItemGDKColor); gtk_widget_modify_text(RightListView.FWidget, GTK_STATE_ACTIVE, InactiveItemGDKColor); // end; // Resize commandline history if ConfNumHistoryItems < CommandLineHistory.Count then begin try for i := CommandLineHistory.Count downto ConfNumHistoryItems + 1 do CommandLineHistory.Delete(i - 1); if CommandLineCombo.Items.Count > 0 then for i := CommandLineCombo.Items.Count - 1 downto 0 do CommandLineCombo.Items.Delete(i); if CommandLineHistory.Count > 0 then for i := 0 to CommandLineHistory.Count - 1 do CommandLineCombo.Items.Append(CommandLineHistory[i]); except end; CommandLineCombo.Entry.Text := ''; end; // Refresh the lists if not Startup then begin DoRefresh(True, True, True); DoRefresh(False, True, True); if RebuildListViews then begin LeftListView.SetSortInfo(ConfMainWindowLeftSortColumn, TGTKTreeViewSortOrder(ConfMainWindowLeftSortType)); RightListView.SetSortInfo(ConfMainWindowRightSortColumn, TGTKTreeViewSortOrder(ConfMainWindowRightSortType)); end; end; end; (********************************************************************************************************************************) procedure TFMain.RefreshBookmarksMenu; const ShortcutKeys = '1234567890'; var i: integer; Item: TGTKMenuItem; begin if mnuBookmarks.Count > 3 then for i := mnuBookmarks.Count - 1 downto 3 do begin mnuBookmarks.Items[i].Free; mnuBookmarks.Delete(i); end; miAddBookmark.Visible := True; miEditBookmarks.Visible := False; miBookmarksSeparator.Visible := False; if Bookmarks.Count > 0 then begin miBookmarksSeparator.Visible := True; for i := 0 to Bookmarks.Count - 1 do begin if Length(Trim(Bookmarks[i])) = 0 then Continue; Item := TGTKMenuItem.CreateTyped(Self, itLabel); Item.Caption := Format('_%s %s', [Chr(Ord('a') + i), StrToUTF8(QuoteMarkupStr(Bookmarks[i]))]); Item.Data := Pointer(i); Item.OnClick := miBookmarkClick; Item.OnMouseUp := BookmarkItemMouseUp; if i < Length(ShortcutKeys) - 1 then Item.ShortCuts.AddName(Format('%s', [ShortcutKeys[i + 1]])); mnuBookmarks.Add(Item); end; end; end; procedure TFMain.miAddBookmarkClick(Sender: TObject); var LeftPanel: boolean; s: string; begin try InternalLock; if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then s := LeftPanelEngine.Path else s := RightPanelEngine.Path; s := ExcludeTrailingPathDelimiter(s); if Bookmarks.IndexOf(s) > -1 then begin Application.MessageBox(LANGTheCurrentDirectoryAlreadyExistsInTheBookmarksList, [mbOK], mbWarning); Exit; end; Bookmarks.Add(s); WriteBookmarks; RefreshBookmarksMenu; finally Application.ProcessMessages; InternalLockInit(False); end; end; procedure TFMain.miBookmarkClick(Sender: TObject); var LeftPanel: boolean; begin if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; // Close VFS connections if LeftPanel then while (LeftPanelEngine is TVFSEngine) do CloseVFS(LeftPanel, True) else while (RightPanelEngine is TVFSEngine) do CloseVFS(LeftPanel, True); ChangingDir(LeftPanel, Bookmarks[Integer((Sender as TGTKMenuItem).Data)]); end; procedure TFMain.BookmarkPopupDeleteClick(Sender: TObject); begin try Bookmarks.Delete(Integer((Sender as TGTKMenuItem).Data)); WriteBookmarks; RefreshBookmarksMenu; except on E: Exception do DebugMsg(['*** Error deleting item: ', E.Message]); end; end; procedure TFMain.BookmarkItemMouseUp(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); begin if Button = mbRight then begin Accept := False; BookmarkPopup.PopUp; BookmarkPopupDelete.Data := (Sender as TGTKMenuItem).Data; end; end; (********************************************************************************************************************************) function form_event_handler(widget: PGtkWidget; event: PGdkEvent; user_data: gpointer): gboolean; cdecl; begin Result := False; if event^._type = GDK_FOCUS_CHANGE then if event^.focus_change._in = 1 then FMain.HandleFormFocusIn else FileListTipsHide; end; procedure TFMain.HandleFormFocusIn; var ChangedMainGUI, ChangedAssoc, ChangedBookmarks, ChangedMounter, ChangedConnMgr, APerformRefresh: boolean; begin try if (csDestroying in ComponentState) or (not Assigned(FMain)) then Exit; if ApplicationShuttingDown then begin DebugMsg([' *** TFMain.HandleFormFocusIn called when ApplicationShuttingDown, possible bug caught ***']); Exit; end; if InternalLockUnlocked then begin APerformRefresh := ConfFocusRefresh; if CheckConfFilesMod(ChangedMainGUI, ChangedAssoc, ChangedBookmarks, ChangedMounter, ChangedConnMgr) then begin if ChangedBookmarks then begin DebugMsg(['Bookmark file changed ---> performing refresh']); ReadBookmarks; RefreshBookmarksMenu; end; (* if ChangedConnMgr then begin DebugMsg(['Connection manager file changed ---> performing refresh']); ReadConnections; end; *) try InternalLock; if (ChangedMainGUI or ChangedAssoc or ChangedMounter) and (Application.MessageBox(LANGSomeOtherInstanceChanged, [mbYes, mbNo], mbWarning) = mbNo) then Exit; if ChangedMounter then begin DebugMsg(['Mounter file changed ---> performing refresh']); ReadMounter; FillMounterBar; end; if ChangedMainGUI then begin DebugMsg(['GUI file changed ---> performing refresh']); ReadMainGUISettings; ApplySettings(True, True, False); APerformRefresh := True; end; if ChangedAssoc then begin DebugMsg(['Assoc file changed ---> performing refresh']); ReadAssoc; LoadIcons; RemoveIconRefs(AssocList, False); RecreateIcons(AssocList); APerformRefresh := True; end; finally Application.ProcessMessages; InternalLockInit(False); end; end; if APerformRefresh then begin DebugMsg(['ANN: Form Focus ---> refresh']); DoRefresh(True, True, True); DoRefresh(False, True, True); if ConfFocusRefresh and ParamDebug then Beep; end; end; except on E: Exception do DebugMsg(['*** Exception raised in TFMain.HandleFormFocusIn (', E.ClassName, '): ', E.Message]); end; end; (********************************************************************************************************************************) function Max(Int1, Int2: integer): integer; begin if Int1 > Int2 then Result := Int1 else Result := Int2; end; procedure menu_position_cb(menu: PGtkMenu; x, y: Pgint; push_in: pgboolean; user_data: gpointer); cdecl; var menu_requisition: TGtkRequisition; max_x, max_y: integer; begin (* Calculate our preferred position. *) gdk_window_get_origin(PGtkWidget(user_data).Window, x, y); x^ := x^ + PGtkWidget(user_data).allocation.x + PGtkWidget(user_data).allocation.width; y^ := y^ + PGtkWidget(user_data).allocation.y + PGtkWidget(user_data).allocation.height; (* Now make sure we are on the screen. *) gtk_widget_size_request(PGtkWidget(menu), @menu_requisition); max_x := Max(0, gdk_screen_width () - menu_requisition.width); max_y := Max(0, gdk_screen_height () - menu_requisition.height); x^ := x^ - menu_requisition.width; x^ := CLAMP(x^, 0, max_x); y^ := CLAMP(y^, 0, max_y); end; procedure TFMain.BookmarkButtonClick(Sender: TObject); begin miAddBookmark.Visible := True; miEditBookmarks.Visible := False; miBookmarksSeparator.Visible := mnuBookmarks.Count > 3; gtk_menu_popup(PGtkMenu(mnuBookmarks.FMenu), nil, nil, menu_position_cb, (Sender as TGTKControl).FWidget, 1, GDK_CURRENT_TIME); if (Sender = LeftBookmarkButton) {and (not LeftLastFocused)} then LeftListView.SetFocus else if (Sender = RightBookmarkButton) {and LeftLastFocused} then RightListView.SetFocus; end; (********************************************************************************************************************************) procedure file_popup_position_cb(menu: PGtkMenu; x, y: Pgint; push_in: pgboolean; user_data: gpointer); cdecl; var menu_requisition: TGtkRequisition; max_x, max_y: integer; TreePath: PGtkTreePath; Iter: TGtkTreeIter; Rect: TGdkRectangle; TreeView: PGtkTreeView; begin TreeView := PGtkTreeView(TGTKListView(user_data).FWidget); if not gtk_tree_selection_get_selected(gtk_tree_view_get_selection(TreeView), nil, @Iter) then Exit; TreePath := gtk_tree_model_get_path(gtk_tree_view_get_model(TreeView), @iter); if not Assigned(TreePath) then Exit; gtk_tree_view_get_background_area(TreeView, TreePath, nil, @Rect); gdk_window_get_origin(gtk_tree_view_get_bin_window(TreeView), x, y); y^ := y^ + Rect.y + Rect.height; gtk_widget_size_request(PGtkWidget(menu), @menu_requisition); if y^ > gdk_screen_height - menu_requisition.height then begin gdk_window_get_origin(gtk_tree_view_get_bin_window(TreeView), x, y); y^ := y^ + Rect.y - menu_requisition.height; end; max_x := Max(0, gdk_screen_width () - menu_requisition.width); max_y := Max(0, gdk_screen_height () - menu_requisition.height); x^ := CLAMP(x^, 0, max_x); y^ := CLAMP(y^, 0, max_y); end; procedure TFMain.PopupFileMenuPos; var AListView: TGTKListView; LeftPanel: boolean; begin if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then AListView := LeftListView else AListView := RightListView; FilePopupMenuPopup(AListView); if not Application.GTKVersion_2_0_5_Up then FilePopupMenu.PopUp else gtk_menu_popup(PGtkMenu(FilePopupMenu.FMenu), nil, nil, file_popup_position_cb, AListView, 0, gtk_get_current_event_time()); end; (********************************************************************************************************************************) procedure TFMain.miShowDirectorySizesClick(Sender: TObject); begin DoGetDirSize(True); end; procedure TFMain.miTargetSourceClick(Sender: TObject); begin if Sender = LeftEqualButton then SwitchOtherPanel(True, True) else if Sender = RightEqualButton then SwitchOtherPanel(False, True) else SwitchOtherPanel(LeftLastFocused, True); end; procedure TFMain.SwitchPanelCtrlLeftRight(LeftPanel, LeftArrowPressed: boolean); var AListView: TGTKListView; SrcEngine, OrigSrcEngine, TargetEngine: TPanelEngine; s: string; DontShowAgain: boolean; begin if LeftPanel then begin AListView := LeftListView; SrcEngine := LeftPanelEngine; TargetEngine := RightPanelEngine; end else begin AListView := RightListView; SrcEngine := RightPanelEngine; TargetEngine := LeftPanelEngine; end; OrigSrcEngine := SrcEngine; if ((not (SrcEngine is TLocalTreeEngine)) and (LeftPanel <> LeftArrowPressed)) or ((not (TargetEngine is TLocalTreeEngine)) and (LeftPanel = LeftArrowPressed)) then begin if ConfSwitchOtherPanelBehaviour < 0 then begin MessageBoxShowOnce(PGtkWindow(FWidget), LANGSwitchOtherPanelWarning, LANGDontShowAgain, DontShowAgain, [mbOK], mbWarning, mbOK, mbOK); if DontShowAgain then begin ConfSwitchOtherPanelBehaviour := 1; WriteMainGUISettings; end; end; end; // don't change dir in VFS engines while not (SrcEngine is TLocalTreeEngine) do SrcEngine := SrcEngine.ParentEngine; while not (TargetEngine is TLocalTreeEngine) do TargetEngine := TargetEngine.ParentEngine; if LeftPanel <> LeftArrowPressed then begin s := ExcludeTrailingPathDelimiter(SrcEngine.Path); if (OrigSrcEngine = SrcEngine) and Assigned(AListView.Selected) and Assigned(AListView.Selected.Data) and (not PDataItem(AListView.Selected.Data)^.UpDir) and PDataItem(AListView.Selected.Data)^.IsDir then s := IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(s) + PDataItem(AListView.Selected.Data)^.FName); if IncludeTrailingPathDelimiter(TargetEngine.Path) <> s then begin if LeftPanel then RightPanelEngine := TargetEngine else LeftPanelEngine := TargetEngine; ChangingDir(not LeftPanel, s); end; end else begin // Close opened VFS engines if LeftPanel then begin while (LeftPanelEngine is TVFSEngine) do CloseVFS(LeftPanel, True); end else while (RightPanelEngine is TVFSEngine) do CloseVFS(LeftPanel, True); ChangingDir(LeftPanel, TargetEngine.Path); end; end; (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.FillMounterBar; procedure ClearButtons(List: TList; Table: TGTKTable); var i: integer; begin if List.Count > 0 then for i := List.Count - 1 downto 0 do Table.RemoveControl(TGTKControl(List[i])); List.Clear; end; procedure FillTableButtons(List: TList; Table: TGTKTable); var i: integer; Button: TGTKButton; Pixmap: TGDKPixbuf; b: boolean; Sep: TGTKVSeparator; begin if MounterList.Count > 0 then for i := 0 to MounterList.Count - 1 do with TMounterItem(MounterList[i]) do begin if ConfMounterPushDown then begin Button := TGTKImageToggleButton.Create(Self); if Length(DisplayText) > 0 then (Button as TGTKImageToggleButton).Caption := DisplayText else (Button as TGTKImageToggleButton).Caption := Copy(MountPath, LastDelimiter(PathDelim, ExcludeTrailingPathDelimiter(MountPath)) + 1, Length(ExcludeTrailingPathDelimiter(MountPath)) - LastDelimiter(PathDelim, ExcludeTrailingPathDelimiter(MountPath))); end else begin Button := TGTKImageButton.Create(Self); if Length(DisplayText) > 0 then (Button as TGTKImageButton).Caption := DisplayText else (Button as TGTKImageButton).Caption := Copy(MountPath, LastDelimiter(PathDelim, ExcludeTrailingPathDelimiter(MountPath)) + 1, Length(ExcludeTrailingPathDelimiter(MountPath)) - LastDelimiter(PathDelim, ExcludeTrailingPathDelimiter(MountPath))); end; Button.CanFocus := False; // DebugMsg(['Int64(MounterList[i]) = ', Int64(MounterList[i])]); // DebugMsg(['Integer(MounterList[i]) = ', Integer(MounterList[i])]); {$IFDEF CPU64} Button.Tag := QWORD(MounterList[i]); {$ELSE} Button.Tag := Longint(MounterList[i]); {$ENDIF} Button.Tooltip := Format(LANGMountPointDevice, [StrToUTF8(MountPath), StrToUTF8(Device)]); Button.BorderStyle := bsNone; Button.PopupMenu := MounterButtonPopupMenu; Button.OnMouseDown := MounterButtonMouseDown; // Check the icon b := FileExists(IconPath); Pixmap := nil; if b then begin Pixmap := TGDKPixbuf.Create(Self); Pixmap.LoadFromFile(IconPath); b := Pixmap.FPixbuf <> nil; if b then Pixmap.ScaleSimple(16, 16); end; if not b then case DeviceType of 0 : Pixmap := MounterHDD; 1 : Pixmap := MounterRemovable; 2 : Pixmap := MounterCD; 3 : Pixmap := MounterFloppy; 4 : Pixmap := MounterNetwork; end; if ConfMounterPushDown then begin (Button as TGTKImageToggleButton).Icon := Pixmap; (Button as TGTKImageToggleButton).Checked := Mounted; end else (Button as TGTKImageButton).Icon := Pixmap; Button.OnClick := MounterButtonClick; // It has to be here because setting the Checked property causes the signal emitting Table.AddControlEx(2*i + 1, 0, 1, 1, Button, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 0, 1); List.Add(Button); if i < MounterList.Count - 1 then begin Sep := TGTKVSeparator.Create(Self); Table.AddControlEx(2*(i + 1), 0, 1, 1, Sep, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 2, 6); List.Add(Sep); end; end; end; var Lab: TGTKLabel; begin if ConfMounterUseFSTab then FillDefaultFstabMounterItems; // Clean all items ClearButtons(MounterTableList, MounterBarTable); ClearButtons(MounterTableListLeft, LeftMounterTable); ClearButtons(MounterTableListRight, RightMounterTable); // Fill the new items Lab := TGTKLabel.Create(Self); Lab.Caption := Format('%s', [LANGMountSC]); Lab.UseMarkup := True; MounterTableList.Add(Lab); MounterBarTable.AddControlEx(0, 0, 1, 1, Lab, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 1); case ConfShowMounterBar of 1: FillTableButtons(MounterTableList, MounterBarTable); 2: begin FillTableButtons(MounterTableListLeft, LeftMounterTable); FillTableButtons(MounterTableListRight, RightMounterTable); end; end; end; procedure TFMain.MounterButtonClick(Sender: TObject); var Item: TMounterItem; LeftPanel, b: boolean; Engine: TPanelEngine; begin if not (Sender is TGTKButton) then Exit; try Item := Pointer((Sender as TGTKButton).Tag); if (not Assigned(Item)) or (Item.MountPath = '') then DebugMsg(['*** Error in mounter button: incorrect data']) else begin if MounterTableListLeft.IndexOf(Sender) > -1 then LeftPanel := True else if MounterTableListRight.IndexOf(Sender) > -1 then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then Engine := LeftPanelEngine else Engine := RightPanelEngine; try b := Item.Mounted; except b := False; end; if ConfMounterPushDown then begin if not b then b := Item.Mount else if Pos(Item.MountPath, Engine.Path) = 1 then b := Item.Eject; (Sender as TGTKToggleButton).OnClick := nil; (Sender as TGTKToggleButton).Checked := Item.Mounted; (Sender as TGTKToggleButton).OnClick := MounterButtonClick; end else if not b then b := Item.Mount; if b then begin if Engine is TVFSEngine then CloseVFS(LeftPanel, True); ChangingDir(LeftPanel, Item.MountPath); DoRefresh(LeftPanel, True, True); end; if LeftPanel then LeftListView.SetFocus else RightListView.SetFocus; end; except on E: Exception do DebugMsg(['*** Exception raised in FMain.MounterButtonClick: (', E.ClassName, '): ', E.Message]); end; end; procedure TFMain.MounterButtonPopupMenuPopup(Sender: TObject); begin // DebugMsg(['aaaa']); try // DebugMsg(['aaaa']); if (not Assigned(LastMounterButton)) or (LastMounterButton.Tag = 0) then begin // DebugMsg(['aaaa']); MounterButtonPopupMenu.PopDown; // DebugMsg(['aaaa']); Exit; end; // DebugMsg(['aaaaX']); // DebugMsg(['sizeof(LastMounterButton.Tag) = ', sizeof(LastMounterButton.Tag)]); // DebugMsg(['LastMounterButton.Tag = ', Int64(LastMounterButton.Tag)]); // DebugMsg(['TMounterItem(Pointer(QWord(LastMounterButton.Tag))).Device = ', TMounterItem(Pointer(QWord(LastMounterButton.Tag))).Device]); // DebugMsg(['TMounterItem(LastMounterButton.Tag).ClassName = ', TMounterItem(LastMounterButton.Tag).ClassName]); miMount.Enabled := not TMounterItem(LastMounterButton.Tag).Mounted; // DebugMsg(['aaaaX']); miUmount.Enabled := not miMount.Enabled; // DebugMsg(['aaaaX']); miEject.Enabled := not miMount.Enabled; except on E: Exception do begin DebugMsg(['*** Exception raised in FMain.MounterButtonClick: (', E.ClassName, '): ', E.Message]); MounterButtonPopupMenu.PopDown; end; end; end; procedure TFMain.MounterButtonMouseDown(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); begin LastMounterButton := nil; if (not (Sender is TGTKButton)) or ((Sender as TGTKButton).Tag = 0) then Exit; LastMounterButton := Sender as TGTKButton; end; procedure TFMain.miMountClick(Sender: TObject); begin if Assigned(LastMounterButton) then TMounterItem(LastMounterButton.Tag).Mount; DoRefresh(LeftLastFocused, True, True); DoRefresh(not LeftLastFocused, True, True); if ConfMounterPushDown then begin (LastMounterButton as TGTKToggleButton).OnClick := nil; (LastMounterButton as TGTKToggleButton).Checked := TMounterItem(LastMounterButton.Tag).Mounted; (LastMounterButton as TGTKToggleButton).OnClick := MounterButtonClick; end; end; procedure TFMain.miUmountClick(Sender: TObject); begin if Assigned(LastMounterButton) then TMounterItem(LastMounterButton.Tag).Umount; DoRefresh(LeftLastFocused, True, True); DoRefresh(not LeftLastFocused, True, True); if ConfMounterPushDown then begin (LastMounterButton as TGTKToggleButton).OnClick := nil; (LastMounterButton as TGTKToggleButton).Checked := TMounterItem(LastMounterButton.Tag).Mounted; (LastMounterButton as TGTKToggleButton).OnClick := MounterButtonClick; end; end; procedure TFMain.miEjectClick(Sender: TObject); begin if Assigned(LastMounterButton) then TMounterItem(LastMounterButton.Tag).Eject; DoRefresh(LeftLastFocused, True, True); DoRefresh(not LeftLastFocused, True, True); if ConfMounterPushDown then begin (LastMounterButton as TGTKToggleButton).OnClick := nil; (LastMounterButton as TGTKToggleButton).Checked := TMounterItem(LastMounterButton.Tag).Mounted; (LastMounterButton as TGTKToggleButton).OnClick := MounterButtonClick; end; end; (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.miMounterSettingsClick(Sender: TObject); var x: TList; begin try InternalLock; FMounterPrefs := TFMounterPrefs.Create(Self); ReadMounter; FMounterPrefs.AssignAssocList(MounterList); FMounterPrefs.FillList; if FMounterPrefs.Run = mbOK then begin FMounterPrefs.CleanItems; x := MounterList; MounterList := FMounterPrefs.InternalMounterList; FMounterPrefs.InternalMounterList := x; ConfMounterUseFSTab := FMounterPrefs.UseFSTabDefaultsCheckBox.Checked; ConfMounterPushDown := FMounterPrefs.ToggleModeCheckBox.Checked; WriteMounter; end; FillMounterBar; finally FMounterPrefs.Free; Application.ProcessMessages; InternalLockInit(False); end; end; procedure TFMain.miShowMounterBarClick(Sender: TObject); begin if miShowOneMounterBar.Checked then ConfShowMounterBar := 1 else if miShowTwoMounterBar.Checked then ConfShowMounterBar := 2 else ConfShowMounterBar := 0; MounterBarHandleBox.Visible := ConfShowMounterBar = 1; LeftMounterTable.Visible := ConfShowMounterBar = 2; RightMounterTable.Visible := ConfShowMounterBar = 2; FillMounterBar; end; procedure TFMain.miColumnsClick(Sender: TObject); begin try InternalLock; FColumns := TFColumns.Create(Self); // FColumns.Show; if FColumns.Run = mbOK then begin FColumns.ApplyColumnList; RebuildListViews(True); end; finally FColumns.Free; Application.ProcessMessages; InternalLockInit(False); end; end; procedure TFMain.ListViewColumnsChanged(Sender: TObject); var SourceListView: TGTKListView; i, j, k, ColIdx, ColumnID: integer; Column: PGtkTreeViewColumn; TempIDs, TempArrayI: array[1..ConstNumPanelColumns] of integer; TempArrayB: array[1..ConstNumPanelColumns] of boolean; begin DebugMsg(['*** Columns reordered ---> performing listview rebuild']); LeftListView.OnMouseUp := nil; RightListView.OnMouseUp := nil; LeftListView.OnColumnsChanged := nil; RightListView.OnColumnsChanged := nil; SourceListView := Sender as TGTKListView; // Copy the old items for i := 1 to ConstNumPanelColumns do begin TempIDs[i] := ConfColumnIDs[i]; TempArrayI[i] := ConfColumnSizes[i]; TempArrayB[i] := ConfColumnVisible[i]; end; ColIdx := 1; // Search for moved columns for i := 0 to SourceListView.Columns.Count - 1 do begin Column := gtk_tree_view_get_column(PGtkTreeView(SourceListView.FWidget), i); ColumnID := ColumnSortIDs[gtk_tree_view_column_get_sort_column_id(column) + 1]; for j := ColIdx to ConstNumPanelColumns do if TempArrayB[j] then begin for k := 1 to ConstNumPanelColumns do if ColumnID = TempIDs[k] then begin DebugMsg(['moving from ', k, ' to ', j]); ConfColumnIDs[j] := TempIDs[k]; ConfColumnSizes[j] := TempArrayI[k]; ConfColumnVisible[j] := TempArrayB[k]; Break; end; ColIdx := j + 1; Break; end; end; Application.ProcessMessages; { RebuildListViewsTimer.Interval := 100; RebuildListViewsTimer.Enabled := True; } RebuildListViewsTimerTimer(Self); gtk_tree_view_set_headers_visible(PGtkTreeView(SourceListView.FWidget), False); gtk_tree_view_set_headers_visible(PGtkTreeView(SourceListView.FWidget), True); end; procedure TFMain.RebuildListViews(DoRefresh: boolean); begin LeftListView.OnColumnsChanged := nil; RightListView.OnColumnsChanged := nil; ConfMainWindowLeftSortColumn := LeftListView.SortColumnID; ConfMainWindowLeftSortType := Integer(LeftListView.SortOrder); ConfMainWindowRightSortColumn := RightListView.SortColumnID; ConfMainWindowRightSortType := Integer(RightListView.SortOrder); LeftListView.Items.Clear; RightListView.Items.Clear; LeftListView.Columns.Clear; RightListView.Columns.Clear; ConstructColumns(LeftListView); ConstructColumns(RightListView); InactiveItemsTimer.Enabled := False; if DoRefresh then begin FMain.DoRefresh(True, True, True); FMain.DoRefresh(False, True, True); LeftListView.SetSortInfo(ConfMainWindowLeftSortColumn, TGTKTreeViewSortOrder(ConfMainWindowLeftSortType)); RightListView.SetSortInfo(ConfMainWindowRightSortColumn, TGTKTreeViewSortOrder(ConfMainWindowRightSortType)); end; LeftListView.OnColumnsChanged := ListViewColumnsChanged; RightListView.OnColumnsChanged := ListViewColumnsChanged; end; procedure TFMain.RebuildListViewsTimerTimer(Sender: TObject); begin RebuildListViewsTimer.Enabled := False; RebuildListViews(True); LeftListView.OnMouseUp := ListViewMouseUp; RightListView.OnMouseUp := ListViewMouseUp; end; (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.FillPluginMenu; var i: integer; MenuItem1, MenuItem2: TGTKMenuItem; begin if PluginList.Count = 0 then begin MenuItem1 := TGTKMenuItem.CreateTyped(Self, itLabel); MenuItem1.Caption := LANGNoPluginsFound; MenuItem1.Enabled := False; mnuPlugins.Add(MenuItem1); end else for i := 0 to PluginList.Count - 1 do begin MenuItem1 := TGTKMenuItem.CreateTyped(Self, itImageText); MenuItem1.Caption := TVFSPlugin(PluginList[i]).ModuleName; MenuItem2 := TGTKMenuItem.CreateTyped(Self, itImageText); MenuItem2.Caption := LANGPluginAbout; MenuItem2.Tag := i; MenuItem2.OnClick := miPluginAboutClick; mnuPlugins.Add(MenuItem1); MenuItem1.Add(MenuItem2); end; end; procedure TFMain.miTestPluginClick(Sender: TObject); var Engine: TVFSEngine; b: boolean; begin try InternalLock; FTestPlugin := TFTestPlugin.Create(Self); if (FTestPlugin.Run = mbOK) and (PluginList.Count > 0) then begin Engine := TVFSEngine.Create(PluginList[FTestPlugin.PluginOptionMenu.ItemIndex]); if not Engine.VFSOpenURI(FTestPlugin.CommandEntry.Text, nil, nil, nil, nil) then begin Application.MessageBox(LANGCouldntOpenURI, [mbOK], mbError, mbOK, mbOK); Exit; end; b := True; { if not FTestPlugin.AnonymousCheckButton.Checked then b := HandleLogin(FTestPlugin, Engine, FTestPlugin.UserEntry.Text, FTestPlugin.PasswordEntry.Text); } if b then begin if LeftLastFocused then LeftPanelEngine := Engine else RightPanelEngine := Engine; DoRefresh(LeftLastFocused, False, False); end; end; finally FTestPlugin.Free; Application.ProcessMessages; InternalLockInit(False); end; end; procedure TFMain.miPluginAboutClick(Sender: TObject); const Authors : array[0..1] of PChar = ('', nil); var AboutBox: PGtkWidget; VFSItem: TVFSPlugin; begin VFSItem := PluginList[(Sender as TGTKMenuItem).Tag]; InternalLock; if (libGnomeUI2Handle = nil) or (@gnome_about_new = nil) then Application.MessageBox(Format(LANGPluginAboutInside, [VFSItem.ModuleName, VFSItem.ModuleAbout, VFSItem.ModuleCopyright])) else begin AboutBox := gnome_about_new(PChar(VFSItem.ModuleName), nil, PChar(VFSItem.ModuleCopyright), PChar(VFSItem.ModuleAbout), @Authors, nil, nil, nil); gtk_window_set_transient_for(GTK_WINDOW(AboutBox), GTK_WINDOW(FMain.FWidget)); gtk_dialog_run(GTK_DIALOG(AboutBox)); end; Application.ProcessMessages; InternalLockInit(False); end; procedure TFMain.miSavePositionClick(Sender: TObject); begin WriteMainSettings; end; (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.NewTabInternal(LeftPanel: boolean; _Engine: TPanelEngine; _Path: string; NewTabPosition: integer; SwitchToNewTab: boolean); var AListView: TGTKListView; AEngine: TPanelEngine; ANotebook: TEphyNotebook; ATabList: TStringList; AVBoxList: TList; APath, APathSave: string; VBox: TGTKVBox; DockedToNotebook: boolean; PathsHighlight: TStringList; TabEngines: TList; TabSortIDs: TList; TabSortTypes: TList; i, InsertPos: integer; ForceReparent: boolean; begin if LeftPanel then begin AListView := LeftListView; AEngine := LeftPanelEngine; ANotebook := LeftPanelNotebook; ATabList := LeftPanelTabs; AVBoxList := LeftNotebookBoxList; PathsHighlight := LeftPathsHighlight; TabEngines := LeftTabEngines; TabSortIDs := LeftTabSortIDs; TabSortTypes := LeftTabSortTypes; end else begin AListView := RightListView; AEngine := RightPanelEngine; ANotebook := RightPanelNotebook; ATabList := RightPanelTabs; AVBoxList := RightNotebookBoxList; PathsHighlight := RightPathsHighlight; TabEngines := RightTabEngines; TabSortIDs := RightTabSortIDs; TabSortTypes := RightTabSortTypes; end; DockedToNotebook := ANotebook.Visible; VBox := nil; for i := 0 to 0 + Ord(not DockedToNotebook) do begin InsertPos := 0; if (i = 1) or DockedToNotebook then begin APath := _Path; AEngine := _Engine; InsertPos := NewTabPosition; if (i = 1) and (InsertPos > 1) then InsertPos := 1; end else APath := AEngine.Path; ATabList.Insert(InsertPos, APath); APath := ExtractFileName(ExcludeTrailingPathDelimiter(APath)); if APath = '' then APath := '/'; if i = 0 then APathSave := APath; VBox := TGTKVBox.Create(Self); AVBoxList.Insert(InsertPos, VBox); PathsHighlight.Insert(InsertPos, PDataItem(AListView.Selected.Data)^.FName); TabSortIDs.Insert(InsertPos, Pointer(AListView.SortColumnID)); TabSortTypes.Insert(InsertPos, Pointer(Integer(AListView.SortOrder))); TabEngines.Insert(InsertPos, AEngine); end; if not DockedToNotebook then begin ANotebook.InsertPage(0, AVBoxList[0], StrToUTF8(APathSave)); SetTabLabel(ANotebook, 0, StrToUTF8(APathSave), StrToUTF8(ATabList[0])); end; InsertPos := NewTabPosition; if InsertPos > ATabList.Count then InsertPos := ATabList.Count; ForceReparent := not ANotebook.Visible; if not ANotebook.Visible then ANotebook.Visible := True; i := ANotebook.InsertPage(InsertPos, VBox, StrToUTF8(APath)); SetTabLabel(ANotebook, i, StrToUTF8(APath), StrToUTF8(ATabList[InsertPos])); if SwitchToNewTab then ANotebook.PageIndex := i else if ForceReparent then SwitchTab(0, LeftPanel, True); // We use first page as there were no tabs before end; procedure TFMain.NewTab(LeftPanel, SendSelectedDirToBg: boolean; CustomPath: string = ''); var AEngine: TPanelEngine; ATabList: TStringList; ANotebook: TEphyNotebook; APath: string; DontShowAgain: boolean; NewTabPosition: integer; begin if LeftPanel then begin AEngine := LeftPanelEngine; ATabList := LeftPanelTabs; ANotebook := LeftPanelNotebook; end else begin AEngine := RightPanelEngine; ATabList := RightPanelTabs; ANotebook := RightPanelNotebook; end; if Length(Trim(CustomPath)) > 0 then APath := CustomPath else APath := AEngine.Path; if AEngine is TVFSEngine then begin while AEngine is TVFSEngine do begin APath := AEngine.SavePath; AEngine := AEngine.ParentEngine; end; // Show warning that we couldn't duplicate the VFS location if ConfDuplicateTabWarning then begin MessageBoxShowOnce(PGtkWindow(FWidget), LANGDuplicateTabWarning, LANGDontShowAgain, DontShowAgain, [mbOK], mbInfo, mbOK, mbOK); if DontShowAgain then begin ConfDuplicateTabWarning := False; WriteMainGUISettings; end; end; end; NewTabPosition := ATabList.Count + Ord(ATabList.Count = 0); // Append to end by default if SendSelectedDirToBg and ANotebook.Visible then NewTabPosition := ANotebook.PageIndex + 1; NewTabInternal(LeftPanel, AEngine, APath, NewTabPosition, not SendSelectedDirToBg); end; procedure TFMain.SwitchTab(TabNo: integer; LeftPanel, SetFocus: boolean); var ANotebook: TEphyNotebook; AListView: TGTKListView; AVBoxList: TList; ListBox: TGTKVBox; AScrolledWindow: TGTKScrolledWindow; i: integer; begin if LeftPanel then begin ANotebook := LeftPanelNotebook; AVBoxList := LeftNotebookBoxList; ListBox := LeftListBox; AScrolledWindow := LeftScrolledWindow; AListView := LeftListView; end else begin ANotebook := RightPanelNotebook; AVBoxList := RightNotebookBoxList; ListBox := RightListBox; AScrolledWindow := RightScrolledWindow; AListView := RightListView; end; if (AVBoxList.Count < TabNo) or (TabNo < -1) then Exit; // Remove any objects g_object_ref(AScrolledWindow.FWidget); if ListBox.ChildrenCount > 0 then ListBox.RemoveControl(AScrolledWindow); if AVBoxList.Count > 0 then for i := 0 to AVBoxList.Count - 1 do if TGTKVBox(AVBoxList[i]).ChildrenCount > 0 then TGTKVBox(AVBoxList[i]).RemoveControl(AScrolledWindow); if TabNo >= 0 then begin // Reparent to the tab TGTKVBox(AVBoxList[TabNo]).AddControlEx(AScrolledWindow, True, True, 0); ListBox.Visible := False; ANotebook.Visible := True; end else begin // Reparent back to the panel ListBox.AddControlEx(AScrolledWindow, True, True, 0); ListBox.Visible := True; ANotebook.Visible := False; end; g_object_unref(AScrolledWindow.FWidget); if SetFocus then AListView.SetFocus; end; procedure TFMain.TabNotebookSwitchPage(Sender: TObject; const NewTabNum: integer; const ShouldFocus: boolean); var LeftPanel: boolean; ATabList: TStringList; PathsHighlight: TStringList; TabEngines: TList; AListView: TGTKListView; TabSortIDs: TList; TabSortTypes: TList; begin DebugMsg(['Switch page']); LeftPanel := (Sender as TEphyNotebook) = LeftPanelNotebook; if LeftPanel then begin ATabList := LeftPanelTabs; PathsHighlight := LeftPathsHighlight; TabEngines := LeftTabEngines; TabSortIDs := LeftTabSortIDs; TabSortTypes := LeftTabSortTypes; AListView := LeftListView; end else begin ATabList := RightPanelTabs; PathsHighlight := RightPathsHighlight; TabEngines := RightTabEngines; TabSortIDs := RightTabSortIDs; TabSortTypes := RightTabSortTypes; AListView := RightListView; end; SwitchTab(NewTabNum, LeftPanel, ShouldFocus); if LeftPanel then LeftPanelEngine := TabEngines[NewTabNum] else RightPanelEngine := TabEngines[NewTabNum]; ChangingDir(LeftPanel, ATabList[NewTabNum], PathsHighlight[NewTabNum], '', False, True); AListView.SetSortInfo(Integer(TabSortIDs[NewTabNum]), TGTKTreeViewSortOrder(Integer(TabSortTypes[NewTabNum]))); end; procedure TFMain.miDuplicateTabClick(Sender: TObject); begin if not CommandLineCombo.Entry.Focused then if LeftListView.Focused then NewTab(True, False) else if RightListView.Focused then NewTab(False, False) else if (Sender is TGTKMenuItem) and LeftTabPopup then NewTab(True, False) else if (Sender is TGTKMenuItem) and (not LeftTabPopup) then NewTab(False, False) else DebugMsg(['Couldn''t duplicate tab: No listview focused.']); end; procedure TFMain.miCloseTabClick(Sender: TObject); begin if not CommandLineCombo.Entry.Focused then if LeftListView.Focused then CloseTab(LeftPanelNotebook.PageIndex, True, True) else if RightListView.Focused then CloseTab(RightPanelNotebook.PageIndex, False, True) else if (Sender is TGTKMenuItem) and LeftTabPopup then CloseTab(LeftPanelNotebook.PageIndex, True, True) else if (Sender is TGTKMenuItem) and (not LeftTabPopup) then CloseTab(RightPanelNotebook.PageIndex, False, True) else DebugMsg(['Couldn''t close tab: No listview focused.']); end; procedure TFMain.miCloseAllTabsClick(Sender: TObject); begin if Application.MessageBox(LANGAreYouSureCloseAllTabs, [mbYes, mbNo], mbQuestion, mbYes, mbNo) = mbYes then if (Sender is TGTKMenuItem) and LeftTabPopup then CloseTab(-1, True, True) else if (Sender is TGTKMenuItem) and (not LeftTabPopup) then CloseTab(-1, False, True) else DebugMsg(['Couldn''t close all tabs: No listview focused.']); end; procedure TFMain.CloseTab(TabNo: integer; LeftPanel, CloseVFSEngine: boolean); var ATabList: TStringList; PathsHighlight: TStringList; ANotebook: TEphyNotebook; AVBoxList: TList; i, NewPageIndex: integer; TabEngines: TList; Engine, xEngine: TPanelEngine; TabSortIDs: TList; TabSortTypes: TList; AListView: TGTKListView; begin if LeftPanel then begin ATabList := LeftPanelTabs; PathsHighlight := LeftPathsHighlight; ANotebook := LeftPanelNotebook; AVBoxList := LeftNotebookBoxList; TabEngines := LeftTabEngines; TabSortIDs := LeftTabSortIDs; TabSortTypes := LeftTabSortTypes; AListView := LeftListView; end else begin ATabList := RightPanelTabs; PathsHighlight := RightPathsHighlight; ANotebook := RightPanelNotebook; AVBoxList := RightNotebookBoxList; TabEngines := RightTabEngines; TabSortIDs := RightTabSortIDs; TabSortTypes := RightTabSortTypes; AListView := RightListView; end; DebugMsg(['Close tab, TabNo = ', TabNo, ', PageIndex = ', ANotebook.PageIndex]); if (ANotebook.ChildrenCount < TabNo) or (TabNo < -1) or (not ANotebook.Visible) then begin DebugMsg(['Couldn''t close tab: wrong TabNo']); Exit; end; if TabNo > 0 then begin Engine := TabEngines[TabNo]; if (Engine is TVFSEngine) and CloseVFSEngine and (not CheckForUnsavedConnection(Engine as TVFSEngine, True)) then Exit; end; if (ANotebook.ChildrenCount > 2) and (TabNo >= 0) then begin // Close one tab, leave tab bar visible Engine := TabEngines[TabNo]; // !!!!!!!! NewPageIndex := ANotebook.PageIndex - Ord((TabNo = ANotebook.PageIndex) and (TabNo = ANotebook.ChildrenCount - 1)) + Ord((TabNo = ANotebook.PageIndex) and (ANotebook.ChildrenCount > TabNo + 1)); // if (ANotebook.PageIndex = 0) and (NewPageIndex = 0) then NewPageIndex := 1; ANotebook.PageIndex := NewPageIndex; // Before remove, ensure some other page is active ANotebook.RemovePage(TabNo); ATabList.Delete(TabNo); PathsHighlight.Delete(TabNo); TabEngines.Delete(TabNo); // g_object_unref(TGTKVBox(AVBoxList[TabNo]).FWidget); AVBoxList.Delete(TabNo); TabSortIDs.Delete(TabNo); TabSortTypes.Delete(TabNo); // Try to close the VFS engine if CloseVFSEngine then while Engine is TVFSEngine do try xEngine := Engine; Engine := xEngine.ParentEngine; if not TVFSEngine(xEngine).VFSClose then DebugMsg(['Error closing the engine...']); xEngine.Free; except end; end else begin // Close last/all tabs, hide the tab bar // Change dir to the opposite if (TabNo >= 0) and (TabNo = ANotebook.PageIndex) then begin // we should not change directory while closing all tabs... i := Ord(not Boolean(ANotebook.PageIndex)); if LeftPanel then LeftPanelEngine := TabEngines[i] else RightPanelEngine := TabEngines[i]; ChangingDir(LeftPanel, ATabList[i], PathsHighlight[i]); AListView.SetSortInfo(Integer(TabSortIDs[i]), TGTKTreeViewSortOrder(Integer(TabSortTypes[i]))); end; // Remove the tabs SwitchTab(-1, LeftPanel, False); for i := ANotebook.ChildrenCount - 1 downto 0 do ANotebook.RemovePage(i); ATabList.Clear; PathsHighlight.Clear; for i := 0 to TabEngines.Count - 1 do if (TPanelEngine(TabEngines[i]) is TVFSEngine) and CloseVFSEngine and ((LeftPanel and (LeftPanelEngine <> TabEngines[i])) or ((not LeftPanel) and (RightPanelEngine <> TabEngines[i]))) then begin Engine := TabEngines[i]; while Engine is TVFSEngine do try if (i <> TabNo) and (not CheckForUnsavedConnection(Engine as TVFSEngine, False)) then Exit; xEngine := Engine; Engine := xEngine.ParentEngine; if not TVFSEngine(xEngine).VFSClose then DebugMsg(['Error closing the engine...']); xEngine.Free; except end; end; TabEngines.Clear; { for i := 0 to AVBoxList.Count - 1 do g_object_unref(TGTKVBox(AVBoxList[i]).FWidget); } AVBoxList.Clear; TabSortIDs.Clear; TabSortTypes.Clear; AListView.SetFocus; end; end; procedure TFMain.TabPopupMenuPopup(Sender: TObject); begin LeftTabPopup := Sender = LeftPanelNotebook; end; procedure TFMain.AddTabs(LeftPanel: boolean; TabList: TStringList; TabSortIDs, TabSortTypes: TList; SetTabActive: integer); var ANotebook: TEphyNotebook; ATabList: TStringList; AVBoxList: TList; APath: string; VBox: TGTKVBox; PathsHighlight: TStringList; TabEngines: TList; i: integer; ATabSortIDs: TList; ATabSortTypes: TList; OldEvent: TEphyNotebookTabSwitchedEvent; begin if LeftPanel then begin ANotebook := LeftPanelNotebook; ATabList := LeftPanelTabs; AVBoxList := LeftNotebookBoxList; PathsHighlight := LeftPathsHighlight; TabEngines := LeftTabEngines; ATabSortIDs := LeftTabSortIDs; ATabSortTypes := LeftTabSortTypes; end else begin ANotebook := RightPanelNotebook; ATabList := RightPanelTabs; AVBoxList := RightNotebookBoxList; PathsHighlight := RightPathsHighlight; TabEngines := RightTabEngines; ATabSortIDs := RightTabSortIDs; ATabSortTypes := RightTabSortTypes; end; if (not Assigned(TabList)) or (TabList.Count < 1) or (not Assigned(TabSortIDs)) or (TabSortIDs.Count < 1) or (not Assigned(TabSortTypes)) or (TabSortTypes.Count < 1) then Exit; for i := 0 to TabList.Count - 1 do begin ATabList.Add(TabList[i]); APath := ExtractFileName(ExcludeTrailingPathDelimiter(TabList[i])); if APath = '' then APath := '/'; VBox := TGTKVBox.Create(Self); AVBoxList.Add(VBox); PathsHighlight.Add(''); ATabSortIDs.Add(TabSortIDs[i]); ATabSortTypes.Add(TabSortTypes[i]); if LeftPanel then TabEngines.Add(LeftPanelEngine) else TabEngines.Add(RightPanelEngine); ANotebook.AppendPage(VBox, StrToUTF8(APath)); SetTabLabel(ANotebook, ANotebook.ChildrenCount - 1, StrToUTF8(APath), StrToUTF8(TabList[i])); end; if not ANotebook.Visible then ANotebook.Visible := True; OldEvent := ANotebook.OnTabSwitched; ANotebook.OnTabSwitched := nil; ANotebook.PageIndex := SetTabActive; ANotebook.OnTabSwitched := OldEvent; TabNotebookSwitchPage(ANotebook, SetTabActive, True); end; procedure TFMain.SetTabLabel(Notebook: TEphyNotebook; PageIndex: integer; ALabel, Tooltip: string); var g: PChar; begin if (ConfTabMaxLength > 0) and (g_utf8_strlen(PChar(ALabel), -1) > ConfTabMaxLength) then begin g := malloc(Length(ALabel) + 4); memset(g, 0, Length(ALabel) + 4); g_utf8_strncpy(g, PChar(ALabel), ConfTabMaxLength); ALabel := g + '...'; libc_free(g); end; Notebook.SetTabCaption(PageIndex, ALabel); Notebook.SetTabTooltip(PageIndex, Tooltip); end; procedure TFMain.NotebookReordered(Sender: TObject; const Source, Dest: integer); var ANotebook: TEphyNotebook; ATabList: TStringList; AVBoxList: TList; PathsHighlight: TStringList; TabEngines: TList; ATabSortIDs: TList; ATabSortTypes: TList; begin ANotebook := Sender as TEphyNotebook; if ANotebook = LeftPanelNotebook then begin ATabList := LeftPanelTabs; AVBoxList := LeftNotebookBoxList; PathsHighlight := LeftPathsHighlight; TabEngines := LeftTabEngines; ATabSortIDs := LeftTabSortIDs; ATabSortTypes := LeftTabSortTypes; end else begin ATabList := RightPanelTabs; AVBoxList := RightNotebookBoxList; PathsHighlight := RightPathsHighlight; TabEngines := RightTabEngines; ATabSortIDs := RightTabSortIDs; ATabSortTypes := RightTabSortTypes; end; ATabList.Move(Source, Dest); AVBoxList.Move(Source, Dest); PathsHighlight.Move(Source, Dest); TabEngines.Move(Source, Dest); ATabSortIDs.Move(Source, Dest); ATabSortTypes.Move(Source, Dest); end; procedure TFMain.NotebookTabClosed(Sender: TObject; const TabNum: integer; var CanClose: boolean); begin CloseTab(TabNum, Sender = LeftPanelNotebook, True); CanClose := False; end; procedure TFMain.NotebookTabDoubleClick(Sender: TObject; const TabNum: integer); begin CloseTab(TabNum, Sender = LeftPanelNotebook, True); end; function TFMain.NotebookFindNotebookAtPointerEvent(Sender: TObject; const AbsX, AbsY: integer): TEphyNotebook; var wx, wy: Integer; begin // DebugMsg([' *** NotebookFindNotebookAtPointerEvent(AbsX = ', AbsX, ', AbsY = ', AbsY, ')']); Result := nil; if LeftPanelNotebook.Visible then begin gtk_widget_get_pointer(LeftPanelNotebook.FWidget, @wx, @wy); if (wx > 0) and (wy > 0) and (wx < LeftPanelNotebook.FWidget^.allocation.width) and (wy < LeftPanelNotebook.FWidget^.allocation.height) then Result := LeftPanelNotebook; end else begin gtk_widget_get_pointer(LeftScrolledWindow.FWidget, @wx, @wy); if (wx > 0) and (wy > 0) and (wx < LeftScrolledWindow.FWidget^.allocation.width) and (wy < LeftScrolledWindow.FWidget^.allocation.height) then Result := LeftPanelNotebook; end; if Result = nil then begin if RightPanelNotebook.Visible then begin gtk_widget_get_pointer(RightPanelNotebook.FWidget, @wx, @wy); if (wx > 0) and (wy > 0) and (wx < RightPanelNotebook.FWidget^.allocation.width) and (wy < RightPanelNotebook.FWidget^.allocation.height) then Result := RightPanelNotebook; end else begin gtk_widget_get_pointer(RightScrolledWindow.FWidget, @wx, @wy); if (wx > 0) and (wy > 0) and (wx < RightScrolledWindow.FWidget^.allocation.width) and (wy < RightScrolledWindow.FWidget^.allocation.height) then Result := RightPanelNotebook; end; end; { if Result = LeftPanelNotebook then DebugMsg([' *** NotebookFindNotebookAtPointerEvent: inside LeftPanelNotebook']) else if Result = RightPanelNotebook then DebugMsg([' *** NotebookFindNotebookAtPointerEvent: inside RightPanelNotebook']); } end; function TFMain.NotebookMoveTabToAnotherNotebook(Sender: TObject; Destination: TEphyNotebook; const SourceTabNo, DestTabNo: integer): boolean; var LeftPanel: boolean; ATargetEngine, ASrcEngine: TPanelEngine; ATargetTabList, ASrcTabList: TStringList; ATargetPathsHighlight, ASrcPathsHighlight: TStringList; ATargetTabEngines, ASrcTabEngines: TList; ATargetTabSortIDs, ASrcTabSortIDs: TList; ATargetTabSortTypes, ASrcTabSortTypes: TList; APath: string; DontShowAgain: boolean; dst_no: integer; begin Result := False; DontShowAgain := False; DebugMsg([' *** NotebookMoveTabToAnotherNotebook(SourceTabNo = ', SourceTabNo, ', DestTabNo = ', DestTabNo, ')']); LeftPanel := Sender = LeftPanelNotebook; if LeftPanel then begin ATargetEngine := RightPanelEngine; ATargetTabList := RightPanelTabs; ASrcEngine := LeftPanelEngine; ASrcTabList := LeftPanelTabs; ATargetPathsHighlight := RightPathsHighlight; ATargetTabEngines := RightTabEngines; ATargetTabSortIDs := RightTabSortIDs; ATargetTabSortTypes := RightTabSortTypes; ASrcPathsHighlight := LeftPathsHighlight; ASrcTabEngines := LeftTabEngines; ASrcTabSortIDs := LeftTabSortIDs; ASrcTabSortTypes := LeftTabSortTypes; end else begin ATargetEngine := LeftPanelEngine; ATargetTabList := LeftPanelTabs; ASrcEngine := RightPanelEngine; ASrcTabList := RightPanelTabs; ATargetPathsHighlight := LeftPathsHighlight; ATargetTabEngines := LeftTabEngines; ATargetTabSortIDs := LeftTabSortIDs; ATargetTabSortTypes := LeftTabSortTypes; ASrcPathsHighlight := RightPathsHighlight; ASrcTabEngines := RightTabEngines; ASrcTabSortIDs := RightTabSortIDs; ASrcTabSortTypes := RightTabSortTypes; end; if (SourceTabNo < 0) or (SourceTabNo > ASrcTabList.Count - 1) then begin DebugMsg([' *** NotebookMoveTabToAnotherNotebook: invalid SourceTabNo']); Exit; end; APath := TPanelEngine(ASrcTabEngines[SourceTabNo]).Path; (* -- disabled, we allow to _move_ (not duplicate) non-local engine if TPanelEngine(ASrcTabEngines[SourceTabNo]) is TVFSEngine then begin while TPanelEngine(ASrcTabEngines[SourceTabNo]) is TVFSEngine do begin APath := TPanelEngine(ASrcTabEngines[SourceTabNo]).SavePath; ASrcTabEngines[SourceTabNo] := TPanelEngine(ASrcTabEngines[SourceTabNo]).ParentEngine; end; // Show warning that we couldn't duplicate the VFS location if ConfDuplicateTabWarning then begin MessageBoxShowOnce(LANGDuplicateTabWarning, LANGDontShowAgain, DontShowAgain, [mbOK], mbInfo, mbOK, mbOK); if DontShowAgain then begin ConfDuplicateTabWarning := False; WriteMainGUISettings; end; end; end; while ATargetEngine is TVFSEngine do ATargetEngine := ATargetEngine.ParentEngine; *) dst_no := DestTabNo; if dst_no < 0 then dst_no := ATargetTabList.Count + Ord(ATargetTabList.Count = 0); CloseTab(SourceTabNo, LeftPanel, False); if not (ASrcEngine is TLocalTreeEngine) then NewTabInternal(not LeftPanel, ASrcEngine, APath, dst_no, True) else NewTabInternal(not LeftPanel, ATargetEngine, APath, dst_no, True); Result := True; end; procedure TFMain.NotebookTabFocusOnlyEvent(Sender: TObject; const TabNum: integer); begin if Sender = LeftPanelNotebook then LeftListView.SetFocus else if Sender = RightPanelNotebook then RightListView.SetFocus else Exit; end; (********************************************************************************************************************************) (********************************************************************************************************************************) function TFMain.HandleVFSArchive(LeftPanel: boolean; const FullPath, HighlightItem, TargetPath: string): boolean; var Plugin: TVFSPlugin; Engine, NewEngine: TPanelEngine; NewPath: string; begin Plugin := FindVFSPlugin(ExtractFileName(FullPath)); Result := Plugin <> nil; if Result then begin if LeftPanel then Engine := LeftPanelEngine else Engine := RightPanelEngine; if Engine is TLocalTreeEngine then begin DebugMsg(['Found plugin ''', Plugin.ModuleID, ''', trying to open the file ''', FullPath, '''']); ChangingDir(LeftPanel, TargetPath, FullPath, HighlightItem, False, False, Plugin); end else begin DebugMsg(['Found plugin ''', Plugin.ModuleID, ''', archive is nested in another archive, extracting first.']); NewPath := ''; Result := ExtractFromArchive(NewPath, Engine, FullPath, False); if Result then begin DebugMsg(['Extract OK, trying to open the file ''', NewPath, '''']); ChangingDir(LeftPanel, TargetPath, NewPath, HighlightItem, False, False, Plugin); if LeftPanel then NewEngine := LeftPanelEngine else NewEngine := RightPanelEngine; if (NewEngine is TVFSEngine) and (NewEngine.ParentEngine = Engine) then (NewEngine as TVFSEngine).RemoveFileOnClose := NewPath; end; end; end; end; function TFMain.CloseVFS(LeftPanel, SuppressRefresh: boolean): string; var Engine: TPanelEngine; begin if LeftPanel then Engine := LeftPanelEngine else Engine := RightPanelEngine; if (not Assigned(Engine.ParentEngine)) or (not (Engine is TVFSEngine)) then begin if LeftPanel then begin LeftPanelEngine := LeftLocalEngine; Result := LeftPanelEngine.Path; end else begin RightPanelEngine := RightLocalEngine; Result := RightPanelEngine.Path; end; Exit; end; if not CheckForUnsavedConnection(Engine as TVFSEngine, (not SuppressRefresh) and (not ApplicationShuttingDown)) then Exit; if LeftPanel then LeftPanelEngine := Engine.ParentEngine else RightPanelEngine := Engine.ParentEngine; Result := Engine.SavePath; if not SuppressRefresh then ChangingDir(LeftPanel, Engine.SavePath, Engine.ParentEngine.LastHighlightItem, Engine.ParentEngine.LastHighlightItem, False, True); if not TVFSEngine(Engine).VFSClose then DebugMsg(['Error closing the engine...']); Engine.Free; end; function TFMain.CheckForUnsavedConnection(Engine: TVFSEngine; AllowCancel: boolean): boolean; // Returns False to Cancel var Buttons: TMessageButtons; CancelButton: TMessageButton; AFConnectionProperties: TFConnectionProperties; URI: string; ConnMgrItem: TConnMgrItem; i: integer; DontShowAgain: boolean; res: TMessageButton; begin Result := True; URI := Engine.GetPathURI; if Engine.OpenedFromQuickConnect and (Length(Trim(URI)) > 0) then begin Buttons := [mbYes, mbNo]; CancelButton := mbNo; if AllowCancel then begin Include(Buttons, mbCancel); CancelButton := mbCancel; end; if ConfWarnUnsavedConnection then begin DontShowAgain := False; res := MessageBoxShowOnce(PGtkWindow(FWidget), PChar(Format('The active connection has not been saved. Do you want to save it to Connection Manager?'#10#10'%s', [Engine.GetPathURI])), LANGDontShowAgain, DontShowAgain, Buttons, mbWarning, mbNo, CancelButton); if DontShowAgain then begin ConfWarnUnsavedConnection := False; WriteMainGUISettings; end; case res of mbYes: begin AFConnectionProperties := TFConnectionProperties.Create(Self); try AFConnectionProperties.URIEntry.Text := URI; if (Engine.CustomPluginIDSave <> '') and (AFConnectionProperties.PluginOptionMenu.Items.Count > 0) then for i := 0 to PluginList.Count - 1 do if TVFSPlugin(PluginList[i]).ModuleID = Engine.CustomPluginIDSave then AFConnectionProperties.PluginOptionMenu.ItemIndex := i + 1; if AFConnectionProperties.Run = mbOK then begin ReadConnections; ConnMgrItem := TConnMgrItem.Create; ConnMgrItem.ConnectionName := AFConnectionProperties.NameEntry.Text; ConnMgrItem.ServiceType := AFConnectionProperties.GetService; ConnMgrItem.Server := AFConnectionProperties.ServerEntry.Text; ConnMgrItem.Username := AFConnectionProperties.UserNameEntry.Text; ConnMgrItem.Password := AFConnectionProperties.PasswordEntry.Text; ConnMgrItem.TargetDir := AFConnectionProperties.TargetDirEntry.Text; ConnMgrItem.PluginID := ''; if AFConnectionProperties.PluginOptionMenu.ItemIndex <> 0 then ConnMgrItem.PluginID := TVFSPlugin(PluginList[AFConnectionProperties.PluginOptionMenu.ItemIndex - 1]).ModuleID; ConfConnMgrActiveItem := ConnectionMgrList.Add(ConnMgrItem); WriteConnections; end; finally AFConnectionProperties.Free; end; Result := True; end; mbNo: Result := True; mbCancel: Result := False; else {Cancel?} Result := not AllowCancel; end; end else Result := True; end; end; (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.ShowBookmarkQuick(LeftPanel: boolean); var SenderControl: TGTKControl; begin if LeftPanel then SenderControl := LeftBookmarkButton else SenderControl := RightBookmarkButton; miAddBookmark.Visible := False; miEditBookmarks.Visible := False; miBookmarksSeparator.Visible := False; gtk_menu_popup(PGtkMenu(mnuBookmarks.FMenu), nil, nil, menu_position_cb, SenderControl.FWidget, 1, GDK_CURRENT_TIME); if LeftPanel then LeftListView.SetFocus else RightListView.SetFocus; end; procedure TFMain.mnuBookmarksPopup(Sender: TObject); begin miAddBookmark.Visible := True; miEditBookmarks.Visible := False; miBookmarksSeparator.Visible := mnuBookmarks.Count > 3; end; (********************************************************************************************************************************) procedure TFMain.miSearchClick(Sender: TObject); var Engine: TPanelEngine; DataList: TList; AListView: TGTKListView; i: integer; begin if LeftLastFocused then Engine := LeftPanelEngine else Engine := RightPanelEngine; try FSearch := TFSearch.Create(Self); FSearch.ParentForm := FMain; FSearch.Engine := Engine; FSearch.SearchInEntry.Text := StrToUTF8(ExcludeTrailingPathDelimiter(Engine.Path)); if Length(FSearch.SearchInEntry.Text) < 1 then FSearch.SearchInEntry.Text := '/'; FSearch.CaseSensitiveMatchCheckButton.Checked := ConfSearchFilterCaseSensitive; FSearch.StayCurrentFSCheckButton.Checked := ConfSearchOtherFS; FSearch.SearchArchivesCheckButton.Checked := ConfSearchArchives; FSearch.CaseSensitiveCheckButton.Checked := ConfSearchTextCaseSensitive; case FSearch.Run of mbOK: ; mbApply: begin DebugMsg(['TFMain.miSearchClick: FSearch.GoToFileArchive = "', FSearch.GoToFileArchive, '", FSearch.GoToFile = "', FSearch.GoToFile, '"']); if Length(FSearch.GoToFileArchive) > 0 then begin HandleVFSArchive(LeftLastFocused, FSearch.GoToFileArchive, ExtractFileName(FSearch.GoToFileArchive), ExtractFilePath(FSearch.GoToFile)); if LeftLastFocused then begin Engine := LeftPanelEngine; DataList := LeftPanelData; AListView := LeftListView; end else begin Engine := RightPanelEngine; DataList := RightPanelData; AListView := RightListView; end; if Engine is TVFSEngine then begin Engine.SavePath := ExtractFilePath(FSearch.GoToFileArchive); // (Engine as TVFSEngine).ParentEngine.LastHighlightItem := ExtractFileName(FSearch.GoToFileArchive); for i := 0 to DataList.Count - 1 do if WideCompareText(string(PDataItem(DataList[i])^.FName), ExtractFileName(FSearch.GoToFile)) = 0 then begin AListView.Items[i].Selected := True; AListView.Items[i].SetCursor(0, False, not Application.GTKVersion_2_2_0_Up, 0.5, 0); Break; end; end; end else ChangingDir(LeftLastFocused, ExtractFilePath(FSearch.GoToFile), ExtractFileName(FSearch.GoToFile)); end; end; finally ConfSearchFilterCaseSensitive := FSearch.CaseSensitiveMatchCheckButton.Checked; ConfSearchOtherFS := FSearch.StayCurrentFSCheckButton.Checked; ConfSearchArchives := FSearch.SearchArchivesCheckButton.Checked; ConfSearchTextCaseSensitive := FSearch.CaseSensitiveCheckButton.Checked; FSearch.Free; Application.ProcessMessages; InternalLockInit(False); end; end; (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.miOpenConnectionClick(Sender: TObject); var b: boolean; begin try InternalLock; ReadConnections; FConnectionManager := TFConnectionManager.Create(Self); if LeftLastFocused then FConnectionManager.SourcePanelEngine := LeftPanelEngine else FConnectionManager.SourcePanelEngine := RightPanelEngine; b := FConnectionManager.Run = mbOK; WriteConnections; // Save connection manager data if b and (FConnectionManager.ConnectedEngine <> nil) then begin while FConnectionManager.ConnectedEngine.ParentEngine is TVFSEngine do FConnectionManager.ConnectedEngine.ParentEngine := FConnectionManager.ConnectedEngine.ParentEngine.ParentEngine; if FConnectionManager.SourcePanelEngine is TVFSEngine then CloseVFS(LeftLastFocused, True); if LeftLastFocused then LeftPanelEngine := FConnectionManager.ConnectedEngine else RightPanelEngine := FConnectionManager.ConnectedEngine; DoRefresh(LeftLastFocused, False, False); end; finally FConnectionManager.Free; Application.ProcessMessages; InternalLockInit(False); end; end; procedure TFMain.miQuickConnectClick(Sender: TObject); var b: boolean; begin try InternalLock; ReadConnections; FQuickConnect := TFQuickConnect.Create(Self); if LeftLastFocused then FQuickConnect.SourcePanelEngine := LeftPanelEngine else FQuickConnect.SourcePanelEngine := RightPanelEngine; b := FQuickConnect.Run = mbOK; WriteConnections; // Save connection manager data if b and (FQuickConnect.ConnectedEngine <> nil) then begin while FQuickConnect.ConnectedEngine.ParentEngine is TVFSEngine do FQuickConnect.ConnectedEngine.ParentEngine := FQuickConnect.ConnectedEngine.ParentEngine.ParentEngine; if FQuickConnect.SourcePanelEngine is TVFSEngine then CloseVFS(LeftLastFocused, True); if LeftLastFocused then LeftPanelEngine := FQuickConnect.ConnectedEngine else RightPanelEngine := FQuickConnect.ConnectedEngine; DoRefresh(LeftLastFocused, False, False); end; finally FQuickConnect.Free; Application.ProcessMessages; InternalLockInit(False); end; end; procedure TFMain.miDisconnectClick(Sender: TObject); begin CloseVFS(LeftLastFocused, False); end; procedure TFMain.DisconnectButtonClick(Sender: TObject); begin CloseVFS(Sender = LeftDisconnectButton, False); if (Sender = LeftDisconnectButton) and (not LeftListView.Focused) then LeftListView.SetFocus else if (Sender = RightDisconnectButton) and (not RightListView.Focused) then RightListView.SetFocus; end; procedure TFMain.LeaveArchiveButtonClick(Sender: TObject); begin CloseVFS(Sender = LeftLeaveArchiveButton, False); if (Sender = LeftLeaveArchiveButton) and (not LeftListView.Focused) then LeftListView.SetFocus else if (Sender = RightLeaveArchiveButton) and (not RightListView.Focused) then RightListView.SetFocus; end; procedure TFMain.OpenTerminalButtonClick(Sender: TObject); var CurrentPath: string; AEngine: TPanelEngine; Error: integer; begin Error := 0; if LeftLastFocused then AEngine := LeftPanelEngine else AEngine := RightPanelEngine; while not (AEngine is TLocalTreeEngine) do AEngine := AEngine.ParentEngine; CurrentPath := AEngine.Path; libc_chdir(PChar(CurrentPath)); ExecuteProgram('bash', CurrentPath, False, True, Error); libc_chdir('/'); end; (********************************************************************************************************************************) procedure TFMain.miFilePropertiesClick(Sender: TObject); var LeftPanel: boolean; AListView: TGTKListView; Engine: TPanelEngine; DataList: TList; i: integer; SelCount: longint; AFile, NextItem1, NextItem2: string; Stat: PDataItemSL; { AWorkingThread: TWorkerThread; AFProgress: TFProgress; } begin try InternalLock; if LeftListView.Focused then LeftPanel := True else if RightListView.Focused then LeftPanel := False else LeftPanel := LeftLastFocused; if LeftPanel then begin AListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin AListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; end; SelCount := 0; if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if Selected and (not UpDir) then Inc(SelCount); if (SelCount = 0) and ((not Assigned(AListView.Selected)) {or PDataItem(AListView.Selected.Data)^.UpDir}) then begin Application.MessageBox(LANGNoFilesSelected, [mbOK], mbInfo, mbNone, mbOK); Exit; end; AFile := ''; if SelCount = 0 then AFile := PDataItem(AListView.Selected.Data)^.FName else for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if Selected and (not UpDir) then begin AFile := FName; Break; end; if AFile <> '' then try FProperties := TFProperties.Create(Self); Stat := Engine.GetFileInfoSL(IncludeTrailingPathDelimiter(Engine.Path) + AFile); if not Assigned(Stat) then Exit; // FProperties.AssignMode(Stat^.Mode, AFile, Stat^.UID, Stat^.GID); FProperties.DisplayFileName := AFile; if FProperties.Run = mbOK then begin { FindNextSelected(AListView, DataList, NextItem1, NextItem2); AWorkingThread := TWorkerThread.Create; AFProgress := TFProgress.Create(Self); try AFProgress.SetNumBars(False); AFProgress.ProgressBar.Value := 0; AFProgress.Label1.Caption := LANGChownProgress; AWorkingThread.ProgressForm := AFProgress; if Assigned(AListView.Selected) then AWorkingThread.SelectedItem := AListView.Selected.Data; AWorkingThread.ParamBool1 := FChown.RecursiveCheckButton.Checked; AWorkingThread.ParamCardinal1 := FChown.LastUID; AWorkingThread.ParamCardinal2 := FChown.LastGID; AWorkingThread.Engine := Engine; AWorkingThread.LeftPanel := LeftPanel; AWorkingThread.DataList := DataList; AWorkingThread.WorkerProcedure := ChownFilesWorker; AWorkingThread.Resume; AFProgress.ParentForm := FMain; if (SelCount > 1) or FChown.RecursiveCheckButton.Checked then AFProgress.ShowModal; ProcessProgressThread(AWorkingThread, AFProgress); AFProgress.Close; finally AFProgress.Free; AWorkingThread.Free; end; } NextItem1 := ''; NextItem2 := ''; ChangingDir(LeftPanel, Engine.Path, NextItem1, NextItem2); DoRefresh(not LeftPanel, True, True); end; finally FreeDataItem(Stat); FProperties.Free; end; finally Application.ProcessMessages; InternalLockInit(False); end; end; (********************************************************************************************************************************) procedure TFMain.CopyFilenamesToClipboard(FullPaths, LeftPanel: boolean); var DataList: TList; Engine: TPanelEngine; AListView: TGTKListView; i, x: longint; SelCount: longint; clip: PGtkClipboard; s: string; begin if LeftPanel then begin AListView := LeftListView; Engine := LeftPanelEngine; DataList := LeftPanelData; end else begin AListView := RightListView; Engine := RightPanelEngine; DataList := RightPanelData; end; clip := gtk_clipboard_get(gdk_atom_intern('CLIPBOARD', False)); SelCount := 0; if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do with PDataItem(DataList[i])^ do if Selected and (not UpDir) then Inc(SelCount); if (SelCount = 0) and ((not Assigned(AListView.Selected)) or PDataItem(AListView.Selected.Data)^.UpDir) then begin // Empty the clipboard gtk_clipboard_clear(clip); Exit; end; if SelCount = 0 then begin s := PDataItem(AListView.Selected.Data)^.FDisplayName; if FullPaths then s := IncludeTrailingPathDelimiter(Engine.GetPath) + s; end else begin s := ''; if DataList.Count > 0 then for i := 0 to DataList.Count - 1 do begin x := AListView.ConvertFromSorted(i); if (x >= 0) and (x < DataList.Count) and PDataItem(DataList[x])^.Selected then begin if FullPaths then s := s + IncludeTrailingPathDelimiter(Engine.GetPath); s := s + PDataItem(DataList[x])^.FDisplayName + #10; end; end; end; gtk_clipboard_set_text(clip, PChar(s), Length(s)); end; procedure TFMain.miPathBoxCopyPathClick(Sender: TObject); var clip: PGtkClipboard; s: string; begin if LeftLastFocused then s := LeftPanelEngine.Path else s := RightPanelEngine.Path; clip := gtk_clipboard_get(gdk_atom_intern('CLIPBOARD', False)); gtk_clipboard_set_text(clip, PChar(StrToUTF8(s)), Length(StrToUTF8(s))); end; procedure TFMain.miCopyNamesClick(Sender: TObject); begin if LeftListView.Focused then CopyFilenamesToClipboard(Sender = miCopyFullPaths, True) else if RightListView.Focused then CopyFilenamesToClipboard(Sender = miCopyFullPaths, False); end; (********************************************************************************************************************************) (********************************************************************************************************************************) function TFMain.HandleRunFromArchive(var APath: string; Engine: TPanelEngine; Command, FileTypeDesc: string; BypassDialog: boolean): boolean; var Res: TMessageButton; Stat: PDataItemSl; s: string; AListView: TGTKListView; begin Result := False; try if not BypassDialog then begin Stat := Engine.GetFileInfoSL(APath); FRunFromVFS := TFRunFromVFS.Create(Self); FRunFromVFS.FileNameLabel2.Caption := Format('%s ', [StrToUTF8(APath)]); if FileTypeDesc = '' then FileTypeDesc := LANGHandleRunFromArchive_FileTypeDesc_Unknown; FRunFromVFS.FileTypeLabel2.Caption := Format('%s ', [FileTypeDesc]); if Assigned(Stat) then begin if (ConfSizeFormat < 5) or (Stat^.Size < 1024) then s := Format(' %s', [LANGHandleRunFromArchive_Bytes]); FRunFromVFS.SizeLabel2.Caption := Format('%s%s ', [FormatSize(Stat^.Size, 0), s]); if (ConfSizeFormat < 5) or (Stat^.Size < 1024) then s := Format(' %s', [LANGHandleRunFromArchive_Bytes]); if Stat^.PackedSize >= 0 then begin FRunFromVFS.PackedSizeLabel2.Caption := Format('%s%s ', [FormatSize(Stat^.PackedSize, 0), s]); end else begin FRunFromVFS.PackedSizeLabel2.Visible := False; FRunFromVFS.PackedSizeLabel.Visible := False; end; FRunFromVFS.DateLabel2.Caption := Format('%s ', [FormatDate(Stat^.ModifyTime, True, True)]); if (Command = '') and (not Stat^.IsExecutable) then begin FRunFromVFS.OpensWithLabel2.Caption := Format('%s ', [LANGHandleRunFromArchive_NotAssociated]); FRunFromVFS.ExecuteButton.Enabled := False; FRunFromVFS.ExecuteAllButton.Enabled := False; end else begin if Command = '' then Command := LANGHandleRunFromArchive_SelfExecutable; FRunFromVFS.OpensWithLabel2.Caption := Format('%s ', [Command]); end; FreeDataItem(Stat); end else begin FRunFromVFS.SizeLabel2.Caption := Format('%s ', ['??']); FRunFromVFS.PackedSizeLabel2.Caption := Format('%s ', ['??']); FRunFromVFS.DateLabel2.Caption := Format('%s ', ['??']); FRunFromVFS.OpensWithLabel2.Caption := Format('%s ', [Command]); end; FRunFromVFS.FileNameLabel2.UseMarkup := True; FRunFromVFS.FileTypeLabel2.UseMarkup := True; FRunFromVFS.SizeLabel2.UseMarkup := True; FRunFromVFS.PackedSizeLabel2.UseMarkup := True; FRunFromVFS.DateLabel2.UseMarkup := True; FRunFromVFS.OpensWithLabel2.UseMarkup := True; Res := FRunFromVFS.Run; FRunFromVFS.Close; FRunFromVFS.Free; Application.ProcessMessages; end else Res := mbYes; if Res in [mbYes, mbNo] then begin Result := False; if Res = mbYes then DebugMsg(['(II) HandleRunFromArchive: Selected extract and execute single item']) else DebugMsg(['(II) HandleRunFromArchive: Selected extract all and execute']); if LeftLastFocused then AListView := LeftListView else AListView := RightListView; Engine.Path := ExtractFilePath(APath); // Extract the files Result := ExtractFromArchive(APath, Engine, IncludeTrailingPathDelimiter(Engine.Path) + string(PDataItem(AListView.Selected.Data)^.FName), Res = mbNo); end; finally Application.ProcessMessages; end; end; function TFMain.ExtractFromArchive(var NewPath: string; Engine: TPanelEngine; const FilePath: string; ExtractAll: boolean): boolean; var s: string; AWorkingThread: TWorkerThread; AFProgress: TFProgress; tmp: PChar; LocalEngine: TLocalTreeEngine; DataList: TList; err: integer; begin Result := False; s := IncludeTrailingPathDelimiter(ConfTempPath) + 'tuxcmd-XXXXXX'; tmp := strdup(PChar(s)); tmp := mkdtemp(tmp); if tmp = nil then begin err := errno; DebugMsg(['(EE) ExtractFromArchive: Couldn''t create temporary directory: ', strerror(err)]); Application.MessageBox(PChar(Format(LANGHandleRunFromArchive_CouldntCreateTemporaryDirectory, [s, string(strerror(err))])), [mbOK], mbError, mbOK, mbOK); Result := False; Exit; end; DebugMsg(['(II) ExtractFromArchive: Using temporary directory: ', tmp]); UsedTempPaths.Add(string(tmp)); if LeftLastFocused then DataList := LeftPanelData else DataList := RightPanelData; LocalEngine := TLocalTreeEngine.Create; AFProgress := TFProgress.Create(Self); AWorkingThread := TWorkerThread.Create; try DebugMsg(['TFMain.ExtractFromArchive: Creating thread...']); AFProgress.Label1.Caption := LANGCopySC; AFProgress.SetNumBars(True); AFProgress.ProgressBar.Value := 0; AWorkingThread.ProgressForm := AFProgress; LocalEngine.SetPath(tmp); AWorkingThread.ExtractFromVFSMode := True; AWorkingThread.DestEngine := LocalEngine; AWorkingThread.SrcEngine := Engine; AWorkingThread.ExtractFromVFSAll := ExtractAll; AWorkingThread.LeftPanel := LeftLastFocused; AWorkingThread.DataList := DataList; AWorkingThread.WorkerProcedure := CopyFilesWorker; AWorkingThread.ParamBool3 := True; AWorkingThread.ParamBool4 := False; AWorkingThread.ParamBool5 := True; AWorkingThread.ParamString1 := string(tmp); AWorkingThread.ParamString2 := FilePath; AWorkingThread.ParamDataItem1 := nil; DebugMsg(['*** Copy: AWorkingThread.Resume']); AWorkingThread.Resume; DebugMsg(['*** Copy: AWorkingThread.Resumed.']); AFProgress.ParentForm := FMain; AFProgress.ShowModal; ProcessProgressThread(AWorkingThread, AFProgress); AFProgress.Close; Result := (not AWorkingThread.FCancelled) and (not AWorkingThread.ErrorHappened); finally DebugMsg(['TFMain.ExtractFromArchive: Freeing thread...']); LocalEngine.Free; AFProgress.Free; AWorkingThread.Free; end; DebugMsg(['(II) ExtractFromArchive: Old path = ', FilePath]); if Result then begin if not ExtractAll then NewPath := IncludeTrailingPathDelimiter(string(tmp)) + ExtractFileName(FilePath) else NewPath := ExcludeTrailingPathDelimiter(string(tmp)) + FilePath; DebugMsg(['(II) ExtractFromArchive: New path = ', NewPath]); // Test for read access to the new file if (not ExtractAll) and (access(PChar(NewPath), R_OK) <> 0) then begin Result := False; DebugMsg(['(EE) ExtractFromArchive: access test to the new file failed.']); end; end; libc_free(tmp); DebugMsg(['(II) ExtractFromArchive: Copy OK, Result = ', Result]); end; (********************************************************************************************************************************) procedure TFMain.PasswordButtonClick(Sender: TObject); var Engine: TPanelEngine; Password: PChar; begin if Sender = LeftPasswordButton then Engine := LeftPanelEngine else if Sender = RightPasswordButton then Engine := RightPanelEngine else Exit; if not (Engine is TVFSEngine) then Exit; try Password := nil; if HandleVFSAskPasswordCallback(FWidget, PChar('The archive is encrypted and requires password'), nil, nil, PChar(TVFSEngine(Engine).Password), VFS_ASK_PASSWORD_NEED_PASSWORD or VFS_ASK_PASSWORD_ARCHIVE_MODE, nil, @Password, nil, nil, nil) then if Password <> nil then begin TVFSEngine(Engine).Password := string(Password); TVFSEngine(Engine).PasswordUsed := False; TVFSEngine(Engine).ResetPassword; g_free(Password); end; except on E: Exception do DebugMsg(['*** Exception raised in FMain.HandleSetPassword: (', E.ClassName, '): ', E.Message]); end; end; end.