From ecde167da74c86bc047aaf84c5e548cf65a5da98 Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sat, 7 Jun 2008 20:34:49 +0200 Subject: Initial commit --- UMain.pas | 6128 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 6128 insertions(+) create mode 100644 UMain.pas (limited to 'UMain.pas') diff --git a/UMain.pas b/UMain.pas new file mode 100644 index 0000000..3fb485a --- /dev/null +++ b/UMain.pas @@ -0,0 +1,6128 @@ +(* + 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; + 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; + 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 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 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); + private + LeftLastFocused, Editing, QuickFind, RedrawLeftInactive, RedrawRightInactive, StartUp, LeftTabPopup: boolean; + LastWidth, RunningEscSensitive: integer; + InplaceEditTimer, InactiveItemsTimer, RebuildListViewsTimer: TGTKTimer; + InplaceEditItem: TGTKListItem; + SavedCmdLine: string; + LastMounterButton: TGTKButton; + MounterTableList, MounterTableListLeft, MounterTableListRight: TList; + LeftNotebookBoxList, RightNotebookBoxList: TList; + LeftPathsHighlight, RightPathsHighlight: TStringList; + LeftTabEngines, RightTabEngines: TList; + LastUsedFilter: string; + procedure ConstructPanels; + procedure ConstructMenu; + procedure ConstructColumns(ListView: TGTKListView); + procedure AfterStart; + procedure ActivateItem(const ItemIndex: longint; const MouseActivate: boolean); + 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: boolean); + procedure SwitchTab(TabNo: integer; LeftPanel, SetFocus: boolean); + procedure CloseTab(TabNo: integer; LeftPanel: boolean); + procedure AddTabs(LeftPanel: boolean; TabList: TStringList; TabSortIDs, TabSortTypes: TList; SetTabActive: integer); + function HandleVFSArchive(const Ext, FullPath, HighlightItem, TargetPath: string): boolean; + procedure CloseVFS(LeftPanel, SurpressRefresh: boolean); + procedure ShowBookmarkQuick(LeftPanel: boolean); + procedure SetTabLabel(Notebook: TEphyNotebook; PageIndex: integer; ALabel, Tooltip: string); + procedure NewTabInternal(LeftPanel: boolean; _Engine: TPanelEngine; _Path: string; NewTabPosition: integer); + procedure CopyFilenamesToClipboard(FullPaths, LeftPanel: boolean); + function HandleRunFromArchive(var APath: string; Engine: TPanelEngine; Command, FileTypeDesc: string; BypassDialog: boolean): boolean; + function HandleSetPassword(Engine: TPanelEngine): 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 Libc, + 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, USetPassword, uVFSprototypes; + + + +(********************************************************************************************************************************) +(********************************************************************************************************************************) +(********************************************************************************************************************************) +(********************************************************************************************************************************) +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; + 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; + 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.PopupMenu := SplitterPopupMenu; + LeftListView.PopupMenu := FilePopupMenu; + RightListView.PopupMenu := FilePopupMenu; + + // 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; + 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; + RightBookmarkButton.SetSizeRequest(22, 22); RightBookmarkButton.Tooltip := LANGBookmarkButton_Tooltip; + 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; + LeftDisconnectButton.Visible := False; + RightDisconnectButton := TGTKButton.Create(Self); + RightDisconnectButton.Caption := '✖'; + RightDisconnectButton.CanFocus := False; + RightDisconnectButton.SetSizeRequest(22, 22); + RightDisconnectButton.Tooltip := LANGDisconnectButton_Tooltip; + 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; + LeftHomeButton.SetSizeRequest(22, 22); LeftHomeButton.Tooltip := LANGHomeButton_Tooltip; + 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; + RightHomeButton.SetSizeRequest(22, 22); RightHomeButton.Tooltip := LANGHomeButton_Tooltip; + 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)); + 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.Enabled := False; + 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('D'); + 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); +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; +// if not TVFSEngine(Engine).VFSClose then DebugMsg(['Error closing the engine...']); +// Engine.Free; + end; + +var i: integer; + b, DontShowAgain: boolean; + res: TMessageButton; +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); + if b and ConfOpenConnectionsWarning then begin + res := MessageBoxShowOnce(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; + + // Close all active connections + if b then begin + if (not LeftPanelNotebook.Visible) and (LeftPanelEngine is TVFSEngine) then CloseVFS(True, True) + else + for i := 0 to LeftTabEngines.Count - 1 do try + if LeftPanelNotebook.PageIndex = i then CloseVFS(True, True) else + if Assigned(LeftTabEngines[i]) and (TPanelEngine(LeftTabEngines[i]) is TVFSEngine) then begin + LeftPanelTabs[i] := TPanelEngine(LeftTabEngines[i]).SavePath; + LeftTabEngines[i] := InternalCloseEngine(LeftTabEngines[i], LeftLocalEngine); + end; + except end; + if (not RightPanelNotebook.Visible) and (RightPanelEngine is TVFSEngine) then CloseVFS(False, True) + else + for i := 0 to RightTabEngines.Count - 1 do try + if RightPanelNotebook.PageIndex = i then CloseVFS(False, True) else + if TPanelEngine(RightTabEngines[i]) is TVFSEngine then begin + RightPanelTabs[i] := TPanelEngine(RightTabEngines[i]).SavePath; + RightTabEngines[i] := InternalCloseEngine(RightTabEngines[i], RightLocalEngine); + end; + except end; + end; + + // Unset the columns changed signal because it is 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 + ChangingDir(True, ConfLeftPath, '', '', False, True); // AutoFallback + ChangingDir(False, ConfRightPath, '', '', False, True); + LeftListView.SetSortInfo(ConfMainWindowLeftSortColumn, TGTKTreeViewSortOrder(ConfMainWindowLeftSortType)); + RightListView.SetSortInfo(ConfMainWindowRightSortColumn, TGTKTreeViewSortOrder(ConfMainWindowRightSortType)); + + // 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.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: boolean; + vadj, hadj: PGtkAdjustment; + 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 begin + ANotebook := LeftPanelNotebook; + vadj := gtk_scrolled_window_get_vadjustment(PGtkScrolledWindow(LeftScrolledWindow.FWidget)); + hadj := gtk_scrolled_window_get_hadjustment(PGtkScrolledWindow(LeftScrolledWindow.FWidget)); + end else begin + ANotebook := RightPanelNotebook; + vadj := gtk_scrolled_window_get_vadjustment(PGtkScrolledWindow(RightScrolledWindow.FWidget)); + hadj := gtk_scrolled_window_get_hadjustment(PGtkScrolledWindow(RightScrolledWindow.FWidget)); + end; + FileListTipsHide; + case Key of + GDK_TAB, 65056 : if (ssCtrl in Shift) and ANotebook.Visible then begin + Accept := False; + 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; + 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 : 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, False); + GDK_BACKSPACE : if QuickFind then begin Accept := False; QuickFindSendKey(LeftPanel, Key); end + else if LeftPanel then PathButtonClick(LeftUpButton) + else PathButtonClick(RightUpButton); + GDK_Right : begin + if [ssCtrl] = Shift then SwitchPanelCtrlLeftRight(LeftPanel, False) else + if ConfLynxLikeMotion then begin + if Assigned(AListView.Selected) then ActivateItem(AListView.Selected.Index, False); + end; + Accept := False; + 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; + end; + GDK_INSERT : DoSelect(5); + GDK_SPACE : begin + Accept := False; + if QuickFind then QuickFindSendKey(LeftPanel, Key) + else 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 : if Shift = [ssAlt] then begin + ShowBookmarkQuick(True); + Accept := False; + end; + GDK_F2 : if Shift = [ssAlt] then begin + ShowBookmarkQuick(False); + Accept := False; + end else + if (Shift = [ssShift]) or (Shift = [ssCtrl]) then begin + CopyFilenamesToClipboard(Shift = [ssCtrl], LeftPanel); + end else begin + DeactivateQuickFind(LeftPanel); + Accept:= False; + F6ButtonClick(nil); + end; + GDK_F3 : begin + DeactivateQuickFind(LeftPanel); + Accept:= False; + F3F4ButtonClick(F3Button); + end; + GDK_F4 : begin + DeactivateQuickFind(LeftPanel); + Accept:= False; + if ssShift in Shift then EditViewFile(LeftPanel, AListView, False, True) + else F3F4ButtonClick(F4Button); + end; + GDK_F5 : begin + DeactivateQuickFind(LeftPanel); + Accept:= False; + if ssShift in Shift then F5ButtonClick(nil) + else F5ButtonClick(Sender); + end; + GDK_F6 : begin + DeactivateQuickFind(LeftPanel); + Accept:= False; + if ssShift in Shift then begin + if not Assigned(AListView.Columns[0].FColumn^.editable_widget) then Editing := False; + DoQuickRename(Sender = LeftListView, AListView, True) + end else F6ButtonClick(Sender); + end; + GDK_F7 : begin + DeactivateQuickFind(LeftPanel); + Accept:= False; + if ssAlt in Shift then miSearchClick(Sender) + else F7ButtonClick(Sender); + end; + GDK_F8, GDK_Delete_Key : begin + DeactivateQuickFind(LeftPanel); + Accept:= False; + F8ButtonClick(Sender); + end; + GDK_ESCAPE : begin + if not QuickFind then CommandLineCombo.Entry.Text := ''; + DeactivateQuickFind(LeftPanel); + if RunningEscSensitive > 0 then FMainEscPressed := True; + end; + GDK_S, GDK_Capital_S : if (ssAlt in Shift) or (ssCtrl in Shift) + then ActivateQuickFind(Sender = LeftListView) + else begin + Accept := False; + if QuickFind then QuickFindSendKey(LeftPanel, Key) + else ActivateCommandLine(Key); + end; + GDK_O, GDK_Capital_O : if ssAlt in Shift then SwitchOtherPanel(LeftPanel, False) + else begin + Accept := False; + if QuickFind then QuickFindSendKey(LeftPanel, Key) + else ActivateCommandLine(Key); + end; + + GDK_P, GDK_Capital_P, GDK_N, GDK_Capital_N: + if ((Shift = [ssAlt]) or (Shift = [ssCtrl]) or ((Shift = []) and (not QuickFind))) { and (CommandLineHistory.Count > 0) } + then CommandLineComboKeyDown(Sender, Key, Shift, Accept) + else if QuickFind then QuickFindSendKey(LeftPanel, Key); + GDK_A, GDK_Capital_A: if (Shift = [ssAlt]) or (Shift = [ssCtrl]) or ((Shift = []) and (not QuickFind)) + then CommandLineComboKeyDown(Sender, Key, Shift, Accept) + else if QuickFind then QuickFindSendKey(LeftPanel, Key); + GDK_WIN_POPUP : begin + Accept := False; + PopupFileMenuPos; + end; + GDK_HOME: 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; + GDK_END: if Assigned(AListView.Selected) and (AListView.ConvertToSorted(AListView.Selected.Index) = AListView.Items.Count - 1) then Accept := False; + GDK_SLASH, GDK_KP_SLASH: if Shift = [ssCtrl] then begin + if LeftPanel then PathButtonClick(LeftRootButton) + else PathButtonClick(RightRootButton); + Accept := False; + 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 : if [ssCtrl] = Shift then begin + Accept := False; + CommandLineCombo.Entry.SetFocus; + CommandLineCombo.Entry.SelectAll; + end else begin + if QuickFind then QuickFindSendKey(LeftPanel, Key) + else if Assigned(AListView.Selected) and (AListView.ConvertToSorted(AListView.Selected.Index) = AListView.Items.Count - 1) then Accept := False; + end; + GDK_Up : if Assigned(AListView.Selected) and (AListView.ConvertToSorted(AListView.Selected.Index) = 0) then Accept := False; + + GDK_Page_Up, GDK_Page_Down: 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; + + else if QuickFind then Accept := not QuickFindSendKey(LeftPanel, Key) + else Accept := not 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 = mbLeft then + if Sender = LeftPathLabelEventBox then LeftListView.SetFocus + else if Sender = RightPathLabelEventBox then RightListView.SetFocus; +end; + +procedure TFMain.ActivateItem(const ItemIndex: longint; const MouseActivate: boolean); +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^.AName]); + if not Assigned(Data) then Exit; + if Data^.UpDir then ChangingDir(LeftPanel, '..') else + if Data^.IsDir then begin + if MouseActivate then begin + if AListView.Items.Count > 0 then AListView.Items[0].Selected := True; + Application.ProcessMessages; + end; + ChangingDir(LeftPanel, String(Data^.AName)); + end else begin + Ext := ANSIUpperCase(Trim(Copy(String(Data^.AName), LastDelimiter('.', String(Data^.AName)) + 1, Length(String(Data^.AName)) - LastDelimiter('.', String(Data^.AName))))); + // Test for known internal functions + if (Ext = 'SFV') or (Ext = 'MD5') then miVerifyChecksumsClick(Self) else + if (Ext = 'CRC') or (Ext = '001') then miMergeFilesClick(Self) else + if not ((Engine is TLocalTreeEngine) and HandleVFSArchive(Ext, IncludeTrailingPathDelimiter(Engine.Path) + String(Data^.AName), String(Data^.AName), '/')) then + if (not ConfUseURI) or ((Engine is TVFSEngine) and TVFSEngine(Engine).ArchiveMode) + then RunFile(IncludeTrailingPathDelimiter(Engine.Path) + String(Data^.AName), Engine, -1) + else RunFile(ExcludeTrailingPathDelimiter(Engine.GetPrefix) + IncludeTrailingPathDelimiter(Engine.Path) + String(Data^.AName), 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, Err: integer; + b, bb: 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 (ANSICompareStr(string(PDataItem(DataList[i])^.AName), AName) = 0)) or + ((not CaseSensitive) and (ANSICompareText(string(PDataItem(DataList[i])^.AName), AName) = 0)) then + begin + Sel := i; + Result := True; + Break; + end; + end; + + procedure DoThread; + 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']); + OpenDirThread.Resume; + tt := Now; + b := False; + FRemoteWait := nil; + repeat + Sleep(ConstInternalProgressTimer); +// DebugMsg([' (II) TFMain.ChangingDir: sleep.']); + Application.ProcessMessages; + if not b and (MilliSecondsBetween(tt, Now) >= ConstRemoteWaitDialogDelay) then begin + FRemoteWait := TFRemoteWait.Create(Application); + FRemoteWait.ParentForm := FMain; + FRemoteWait.ShowModal; + b := True; + end; + if Assigned(FRemoteWait) then + if FRemoteWait.Cancelled then OpenDirThread.CancelIt := True; + until OpenDirThread.Finished; + if FRemoteWait <> nil then 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])^.AName); + end; + Editing := False; + DeactivateQuickFind(LeftPanel); + DeactivateQuickFind(not LeftPanel); + DirList := TList.Create; + + // Threading... + OpenDirThread := TOpenDirThread.Create; + DebugMsg(['TFMain.ChangingDir: Creating thread...']); + DoThread; + + bb := OpenDirThread.VFSOpenResult = 0; + if OpenDirThread.VFSOpenResult <> 0 then begin + DebugMsg(['(II) TFMain.ChangingDir: OpenDirThread.VFSOpenResult <> 0']); + if OpenDirThread.VFSOpenResult = cVFS_BadPassword then begin + // Handle password + if not HandleSetPassword(OpenDirThread.xEngine) then Exit; + ss := OpenDirThread.xEngine.Password; + if Assigned(OpenDirThread.xEngine) then OpenDirThread.xEngine.Free; + DebugMsg(['TFMain.ChangingDir: Freeing thread...']); + OpenDirThread.Free; + OpenDirThread := TOpenDirThread.Create; + DebugMsg(['TFMain.ChangingDir: Creating thread...']); + OpenDirThread.Password := ss; + DoThread; + bb := OpenDirThread.VFSOpenResult = 0; + end; + end; + if Plugin <> nil then begin + HiliString1 := ''; + HiliString2 := ''; + end; + if not bb then begin + Application.MessageBox(LANGCouldntOpenURIArchive, [mbOK], mbError, mbOK, mbOK); + DebugMsg(['TFMain.ChangingDir: Freeing thread...']); + OpenDirThread.Free; + end else + if OpenDirThread.ChDirResult <> 0 then begin + if OpenDirThread.ChDirResult = 1 then Application.MessageBox(Format(LANGErrorGettingListingForSPanelNoPath, [LANGPanelStrings[LeftPanel], 'Exception']), [mbOK], mbError, mbNone, mbOK) + else Application.MessageBox(Format(LANGErrorGettingListingForSPanel, [LANGPanelStrings[LeftPanel], ANSIToUTF8(GetErrorString(OpenDirThread.ChDirResult)), ANSIToUTF8(NewPath)]), [mbOK], mbError, mbNone, mbOK); + DebugMsg(['TFMain.ChangingDir: Freeing thread...']); + OpenDirThread.Free; + end else begin + if OpenDirThread.ListingResult <> 0 then begin + Application.MessageBox(Format(LANGErrorGettingListingForSPanel, [LANGPanelStrings[LeftPanel], ANSIToUTF8(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])^.AName) >= 0) + then PDataItem(DataList[i])^.Selected := True; + Sel := 0; + b := (NewPath = '..') and (Length(Trim(s)) > 0) and LookupItem(s, True); + if not b then b := (HiliString1 <> '') and LookupItem(HiliString1, True); + if (not b) and (HiliString1 <> '') then LookupItem(HiliString1, False); + if (not b) and (HiliString2 <> '') then LookupItem(HiliString2, True); + if (not b) and (HiliString2 <> '') then LookupItem(HiliString2, False); + if (Engine.Path = '/') and (NewPath = '/') 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, ANSIToUTF8(s), ANSIToUTF8(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; +begin + if LeftListView.Focused then LeftPanel := True else + if RightListView.Focused then LeftPanel := False else + LeftPanel := LeftLastFocused; + if LeftPanel then begin + if LeftPanelEngine.GetPrefix <> '' then s := ANSIToUTF8(Format(ConstFullPathFormatStr, [LeftPanelEngine.GetPrefix, LeftPanelEngine.Path])) + else s := ANSIToUTF8(LeftPanelEngine.Path); + end else + if RightPanelEngine.GetPrefix <> '' then s := ANSIToUTF8(Format(ConstFullPathFormatStr, [RightPanelEngine.GetPrefix, RightPanelEngine.Path])) + else s := ANSIToUTF8(RightPanelEngine.Path); + Caption := Format('Tux Commander [%s]', [s]); +end; + +procedure TFMain.UpdatePanelInfo; +var FSFree, FSSize: Int64; + FSName, s: string; + Time1, Time2: TDateTime; +begin + UpdateCaption; + Time1 := Now; + if LeftPanelEngine.GetPrefix <> '' + then LeftPathLabel.Caption := ANSIToUTF8(Format(ConstFullPathFormatStr, [LeftPanelEngine.GetPrefix, LeftPanelEngine.Path])) + else LeftPathLabel.Caption := ANSIToUTF8(LeftPanelEngine.Path); + if RightPanelEngine.GetPrefix <> '' + then RightPathLabel.Caption := ANSIToUTF8(Format(ConstFullPathFormatStr, [RightPanelEngine.GetPrefix, RightPanelEngine.Path])) + else RightPathLabel.Caption := ANSIToUTF8(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, + ANSIToUTF8(FormatSize(FSFree, 1024)), + ANSIToUTF8(FormatSize(FSSize, 1024))]) + else LeftDiskInfoLabel.Caption := Format(LANGDiskStatFmt, + [ANSIToUTF8(FormatSize(FSFree, 1024)), + ANSIToUTF8(FormatSize(FSSize, 1024))]); + RightPanelEngine.GetFileSystemInfo(RightPanelEngine.Path, FSSize, FSFree, FSName); + if FSName <> '' + then RightDiskInfoLabel.Caption := Format(LANGDiskStatVolNameFmt, [FSName, + ANSIToUTF8(FormatSize(FSFree, 1024)), + ANSIToUTF8(FormatSize(FSSize, 1024))]) + else RightDiskInfoLabel.Caption := Format(LANGDiskStatFmt, + [ANSIToUTF8(FormatSize(FSFree, 1024)), + ANSIToUTF8(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 > -1))) then begin + Inc(TotalSize, Data^.Size); + Inc(TotalFiles); + if Data^.Selected then begin + Inc(NumSel); + Inc(Size, Data^.Size); + end; + end; + end; + s := Format(LANGStatusLineFmt, [ANSIToUTF8(FormatSize(Size, 1024)), + ANSIToUTF8(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^.AName), 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; + +procedure TFMain.ProcessMarkKey(KeyType, Key: integer); +var LeftPanel: boolean; + ListView: TGTKListView; + Pos: integer; +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; + if Editing and Assigned(ListView.Columns[0].FColumn^.editable_widget) then begin + Pos := gtk_editable_get_position(PGtkEditable(ListView.Columns[0].FColumn^.editable_widget)); + gtk_editable_insert_text(PGtkEditable(ListView.Columns[0].FColumn^.editable_widget), PChar(UTF8Encode(WideChar(KeyValToUnicode(Key)))), 1, @Pos); + gtk_editable_set_position(PGtkEditable(ListView.Columns[0].FColumn^.editable_widget), 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 + AFGColor := nil; + ABGColor := nil; + 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 := PChar(ANSIToUTF8(string(AName))) 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 := Utf8ToAnsi(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); + 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 + AName; + 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, [ANSIToUTF8(string(Data^.AName))]) + else s := Format(LANGFileS, [ANSIToUTF8(string(Data^.AName))]); + 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, SelSingle, NextItem1, NextItem2: string; + AWorkingThread: TWorkerThread; + AFProgress: TFProgress; + CurrentEngine, OppositeEngine: TPanelEngine; + s: string; +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; + + SelSingle := ''; + if SelCount = 0 then begin + SelCount := 1; + SelSingle := PDataItem(ListView.Selected.Data)^.AName; + 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 FCopyMove.Entry.Text := ANSIToUTF8(SelSingle) + else FCopyMove.Entry.Text := '*.*'; + end else + if OppositeEngine is TLocalTreeEngine then FCopyMove.Entry.Text := ANSIToUTF8(OppositeEngine.Path) + else FCopyMove.Entry.Text := ANSIToUTF8(Format(ConstFullPathFormatStr, [OppositeEngine.GetPrefix, OppositeEngine.Path])); + FCopyMove.Entry.SelectAll; + if FCopyMove.Run <> mbOK then Exit; + NewPath := UTF8ToANSI(FCopyMove.Entry.Text); + finally + FCopyMove.Free; + end; + + // Handle password in archives + if (CurrentEngine is TVFSEngine) and (CurrentEngine as TVFSEngine).GetPasswordRequired and (Length((CurrentEngine as TVFSEngine).Password) < 1) then + if not HandleSetPassword(CurrentEngine) then Exit; + if (OppositeEngine is TVFSEngine) and (OppositeEngine as TVFSEngine).GetPasswordRequired and (Length((OppositeEngine as TVFSEngine).Password) < 1) then + if not HandleSetPassword(OppositeEngine) then Exit; + + FindNextSelected(ListView, DataList, NextItem1, NextItem2); + 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; + AWorkingThread.DestEngine := nil; + 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, True); +end; + +procedure TFMain.ListViewMouseDown(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); +var Item: TGTKListItem; + Click: TDateTime; +begin + try + InternalLock; +// 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; + end; + finally + Application.ProcessMessages; + InternalUnLock; + 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; +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); + 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; + if (AListView.Selected = InplaceEditItem) and (AListView.Selected.Data = InplaceEditItem.Data) and + (String(PDataItem(InplaceEditItem.Data)^.AName) <> 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 := UTF8ToANSI(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); + 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); + if Sender = LeftListView 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)^.AName; + 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)^.AName; +{ 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.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; + Found: boolean; + OldSelectionChangedEvent: TNotifyEvent; +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; + if Key = GDK_BACKSPACE then begin + if Length(Entry.Text) > 0 then Entry.Text := AnsiToUTF8(Copy(UTF8ToAnsi(Entry.Text), 1, Length(UTF8ToAnsi(Entry.Text)) - 1)); + NewText := UTF8ToAnsi(Entry.Text); + end else begin + s := UTF8ToANSI(UTF8Encode(WideChar(KeyValToUnicode(Key)))); + if (Length(s) = 0) or (s = #0) then Exit; + NewText := UTF8ToAnsi(Entry.Text) + s; + end; + if (DataList.Count > 0) and (Length(NewText) > 0) then begin + Found := False; + for i := 0 to DataList.Count - 1 do begin + Data := DataList[AListView.ConvertFromSorted(i)]; + if Assigned(Data) and (not Data^.UpDir) and (Pos(AnsiUpperCase(NewText), AnsiUpperCase(Data^.AName)) = 1) then begin + Found := True; + OldSelectionChangedEvent := AListView.OnSelectionChanged; + AListView.OnSelectionChanged := nil; + AListView.Selected := AListView.Items[AListView.ConvertFromSorted(i)]; + AListView.Items[AListView.ConvertFromSorted(i)].SetCursor(0, False, False, 0, 0); + AListView.OnSelectionChanged := OldSelectionChangedEvent; + Break; + end; + end; + if Found then Entry.Text := AnsiToUTF8(NewText) + 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)^.AName)) + 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(AName)); + {$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)^.AName)) + 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(AName)); + 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)^.AName); + 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, [ANSIToUTF8(PDataItem(AListView.Selected.Data)^.AName)]); + if LeftPanel then FNewDir.Entry.Text := ANSIToUTF8(RightPanelEngine.Path) + else FNewDir.Entry.Text := ANSIToUTF8(LeftPanelEngine.Path); + FNewDir.Entry.SelectAll; + if FNewDir.Run = mbOK + then FilePath := UTF8ToAnsi(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)^.AName; + 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)^.AName); + 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, [ANSIToUTF8(PDataItem(AListView.Selected.Data)^.AName)]); + FSplitFile.Label1.UseUnderline := True; + if LeftPanel then FSplitFile.Entry.Text := AnsiToUtf8(RightPanelEngine.Path) + else FSplitFile.Entry.Text := AnsiToUtf8(LeftPanelEngine.Path); + FSplitFile.Entry.SelectAll; + if FSplitFile.Run = mbOK + then FilePath := UTF8ToAnsi(FSplitFile.Entry.Text) + else Exit; + DeleteTarget := FSplitFile.DeleteTargetCheckBox.Checked; + MaxSize := 0; + for i := 1 to Length(SplitConsts) do + if Trim(AnsiUpperCase(SplitConsts[i].Title)) = Trim(AnsiUpperCase(FSplitFile.SizeCombo.Entry.Text)) then + begin + MaxSize := SplitConsts[i].PartSize; + Break; + end; + if MaxSize = 0 then MaxSize := GetStrSize(UTF8ToANSI(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)^.AName; + 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(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)^.AName); + 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, s: string; + Stat: PDataItemSL; + Error, x: integer; +// AViewer: TViewerThread; + AViewer: TFViewer; +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 := AnsiToUTF8(PDataItem(AListView.Selected.Data)^.AName) + else ANewDir.Entry.Text := ''; + if Length(ANewDir.Entry.Text) > 0 then ANewDir.Entry.SelectAll; + if ANewDir.Run <> mbOK then Exit; + AFile := IncludeTrailingPathDelimiter(Engine.Path) + UTF8ToAnsi(ANewDir.Entry.Text); + finally + ANewDir.Free; + end; + end else AFile := IncludeTrailingPathDelimiter(Engine.Path) + PDataItem(AListView.Selected.Data)^.AName; + + 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 Exit; + + 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, [ANSIToUTF8(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, [ANSIToUTF8(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, Ext, FileTypeDesc: string; + i, j, Last, ac: integer; + b, AutodetectGUI, RunInTerminal: boolean; + Stat: PDataItemSL; + s: string; +begin + try + InternalLock; + Command := ''; + FileTypeDesc := ''; + AutodetectGUI := True; + RunInTerminal := False; + if (Pos('.', ExtractFileName(Path)) > 0) and (LastDelimiter('.', ExtractFileName(Path)) < Length(ExtractFileName(Path))) then begin + Ext := ANSIUpperCase(Trim(Copy(ExtractFileName(Path), LastDelimiter('.', ExtractFileName(Path)) + 1, Length(ExtractFileName(Path)) - LastDelimiter('.', ExtractFileName(Path))))); + // Search in the association list + if AssocList.Count > 0 then + for i := 0 to AssocList.Count - 1 do + with TFileAssoc(AssocList[i]) do + if (ActionList.Count > 0) and (Length(Trim(Extensions)) > 0) then begin + b := False; + if Pos(';', Extensions) = 0 then b := ANSIUpperCase(Trim(Extensions)) = Ext else begin + Last := 0; + for j := 1 to Length(Extensions) do + if Extensions[j] = ';' then begin + if ANSIUpperCase(Trim(Copy(Extensions, Last + 1, j - Last - 1))) = Ext then begin + b := True; + Break; + end; + Last := j; + end; + if not b then b := ANSIUpperCase(Trim(Copy(Extensions, LastDelimiter(';', Extensions) + 1, Length(Extensions) - LastDelimiter(';', Extensions)))) = Ext; + end; + if b then begin + FileTypeDesc := FileTypeName; + if (CustomAction > ActionList.Count - 1) or (CustomAction = -1) + then ac := DefaultAction + else ac := CustomAction; + if ac > ActionList.Count - 1 then ac := 0; + if ActionList.Count >= ac then begin + Command := Trim(TAssocAction(ActionList[ac]).ActionCommand); + AutodetectGUI := TAssocAction(ActionList[ac]).AutodetectGUI; + RunInTerminal := TAssocAction(ActionList[ac]).RunInTerminal; + end; + if Command <> '' then Break; + end; + 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 + end else begin + if Engine is TVFSEngine then begin + HandleRunFromArchive(Path, Engine, Command, FileTypeDesc, False); + b := True; + end else + if Application.MessageBox(Format(LANGThereIsNoApplicationAssociatedWithS, [ANSIToUTF8(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, [ANSIToUTF8(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)^.AName)) + ' '; + 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 ANSIUpperCase(Orig) = 'CD' then begin + if LeftLastFocused then PathButtonClick(LeftHomeButton) + else PathButtonClick(RightHomeButton); + end else + if (Length(Orig) > 3) and (ANSIUpperCase(Copy(Orig, 1, 3)) = 'CD ') then begin + ChangingDir(LeftLastFocused, ProcessPattern(Engine, Copy(Orig, 4, Length(Orig) - 3), Engine.Path, '', True)); + end else begin + while not (Engine is TLocalTreeEngine) do Engine := Engine.ParentEngine; + ChDir(Engine.Path); + if not ExecuteProgram(Orig, Engine.Path, ConfCmdLineTerminalBehaviour = 0 , ConfCmdLineTerminalBehaviour = 1, Error) then + Application.MessageBox(LANGErrorExecutingCommand, [mbOK], mbError, mbNone, mbOK); + 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(ANSILeftStr(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 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)^.AName else + for i := 0 to DataList.Count - 1 do + with PDataItem(DataList[i])^ do + if Selected and (not UpDir) then begin + AFile := AName; + Break; + end; + + // Handle password + if (Engine is TVFSEngine) and TVFSEngine(Engine).GetPasswordRequired and (Length(TVFSEngine(Engine).Password) < 1) then + if not HandleSetPassword(Engine) then Exit; + + 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 + 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)^.AName else + for i := 0 to DataList.Count - 1 do + with PDataItem(DataList[i])^ do + if Selected and (not UpDir) then begin + AFile := AName; + Break; + end; + + // Handle password + if (Engine is TVFSEngine) and TVFSEngine(Engine).GetPasswordRequired and (Length(TVFSEngine(Engine).Password) < 1) then + if not HandleSetPassword(Engine) then Exit; + + 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 + 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)^.AName; + if Engine.Path <> EngineOpposite.Path + then s2 := IncludeTrailingPathDelimiter(EngineOpposite.Path) + PDataItem(AListView.Selected.Data)^.AName + else s2 := ''; + + // Handle password + if (Engine is TVFSEngine) and TVFSEngine(Engine).GetPasswordRequired and (Length(TVFSEngine(Engine).Password) < 1) then + if not HandleSetPassword(Engine) then Exit; + + 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; + + // Handle password + if (Engine is TVFSEngine) and TVFSEngine(Engine).GetPasswordRequired and (Length(TVFSEngine(Engine).Password) < 1) then + if not HandleSetPassword(Engine) then Exit; + + if EditSymlink(IncludeTrailingPathDelimiter(Engine.Path) + PDataItem(AListView.Selected.Data)^.AName, 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, Ext: string; + UpDir, b, Found: boolean; + i, j, Last: integer; +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)^.AName; + 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, [ANSIToUTF8(QuoteMarkupStr(ShortFName))]); + Item.StockIcon := 'gtk-execute'; + Item.Data := Pointer(1); + Item.OnClick := FilePopupMenuItemClick; + Item.Enabled := Engine.FileCanRun(FileName); + FilePopupMenu.Add(Item); + end else begin + Item := TGTKMenuItem.CreateTyped(Self, itImageText); + if UpDir then Item.Caption := LANGPopupGoUp + else Item.Caption := Format(LANGPopupOpenS, [ANSIToUTF8(QuoteMarkupStr(ShortFName))]); + Item.StockIcon := 'gtk-open'; + Item.Data := Pointer(1); + Item.OnClick := FilePopupMenuItemClick; + FilePopupMenu.Add(Item); + 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; + if Pos('.', ShortFName) > 0 then begin + Ext := ANSIUpperCase(Trim(Copy(ShortFName, LastDelimiter('.', ShortFName) + 1, Length(ShortFName) - LastDelimiter('.', ShortFName)))); + if AssocList.Count > 0 then + for i := 0 to AssocList.Count - 1 do + with TFileAssoc(AssocList[i]) do + if (ActionList.Count > 0) and (Length(Trim(Extensions)) > 0) then begin + b := False; + if Pos(';', Extensions) = 0 then b := ANSIUpperCase(Trim(Extensions)) = Ext else begin + Last := 0; + for j := 1 to Length(Extensions) do + if Extensions[j] = ';' then begin + if ANSIUpperCase(Trim(Copy(Extensions, Last + 1, j - Last - 1))) = Ext then begin + b := True; + Break; + end; + Last := j; + end; + if not b then b := ANSIUpperCase(Trim(Copy(Extensions, LastDelimiter(';', Extensions) + 1, Length(Extensions) - LastDelimiter(';', Extensions)))) = Ext; + end; + if b and (ActionList.Count > 0) then begin + Found := True; + for j := 0 to ActionList.Count - 1 do begin + Item := TGTKMenuItem.CreateTyped(Self, itImageText); + Item.Caption := Format(LANGPopupOpenWithS, [TAssocAction(ActionList[j]).ActionName]); + if ((j = 0) and (DefaultAction > ActionList.Count - 1)) or (j = DefaultAction) then + Item.Caption := Item.Caption + LANGPopupDefault; + Item.Data := ActionList[j]; + Item.OnClick := FilePopupMenuItemClick; + FilePopupMenu.Add(Item); + end; + end; + 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); +end; + +procedure TFMain.FilePopupMenuItemClick(Sender: TObject); +var Engine: TPanelEngine; + DataItem: PDataItemSL; + AListView: TGTKListView; + FileName, ShortFName, s, APath: 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)^.AName; + 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, False) + 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, [ANSIToUTF8(ShortFName)]), [mbYes, mbNo], mbQuestion, mbNone, mbNo) = mbYes + then miFileTypesClick(Self); + else begin + b := True; + s := 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, [ANSIToUTF8(FileName)]), [mbOK], mbError, mbNone, mbOK); + end; + end; + 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 + 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; +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 > 4 then + for i := mnuBookmarks.Count - 1 downto 4 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 + Item := TGTKMenuItem.CreateTyped(Self, itLabel); + Item.Caption := Format('_%s %s', [Chr(Ord('a') + i), ANSIToUTF8(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], mbError); + 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 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(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)^.AName); + 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, [ANSIToUTF8(MountPath), ANSIToUTF8(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; + FillMounterBar; + end; + 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; + + RebuildListViewsTimer.Interval := 100; + RebuildListViewsTimer.Enabled := 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 := ANSIToUTF8(TVFSPlugin(PluginList[i]).VFSName); + 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(UTF8ToANSI(FTestPlugin.CommandEntry.Text)) 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.VFSName, VFSItem.VFSAbout, VFSItem.VFSCopyright])) + else begin + AboutBox := gnome_about_new(VFSItem.VFSName, nil, VFSItem.VFSCopyright, VFSItem.VFSAbout, @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); +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; +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)^.AName); + 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], APathSave); + SetTabLabel(ANotebook, 0, APathSave, ATabList[0]); + end; + + InsertPos := NewTabPosition; + if InsertPos > ATabList.Count then InsertPos := ATabList.Count; + if not ANotebook.Visible then ANotebook.Visible := True; + i := ANotebook.InsertPage(InsertPos, VBox, APath); + SetTabLabel(ANotebook, i, APath, ATabList[InsertPos]); + ANotebook.PageIndex := i; +end; + +procedure TFMain.NewTab(LeftPanel: boolean); +var AEngine: TPanelEngine; + ATabList: TStringList; + APath: string; + DontShowAgain: boolean; +begin + if LeftPanel then begin + AEngine := LeftPanelEngine; + ATabList := LeftPanelTabs; + end else begin + AEngine := RightPanelEngine; + ATabList := RightPanelTabs; + end; + + 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(LANGDuplicateTabWarning, LANGDontShowAgain, DontShowAgain, [mbOK], mbInfo, mbOK, mbOK); + if DontShowAgain then begin + ConfDuplicateTabWarning := False; + WriteMainGUISettings; + end; + end; + end; + + NewTabInternal(LeftPanel, AEngine, APath, ATabList.Count + Ord(ATabList.Count = 0)); +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]); + 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) else + if RightListView.Focused then NewTab(False) else + if (Sender is TGTKMenuItem) and LeftTabPopup then NewTab(True) else + if (Sender is TGTKMenuItem) and (not LeftTabPopup) then NewTab(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) else + if RightListView.Focused then CloseTab(RightPanelNotebook.PageIndex, False) else + if (Sender is TGTKMenuItem) and LeftTabPopup then CloseTab(LeftPanelNotebook.PageIndex, True) else + if (Sender is TGTKMenuItem) and (not LeftTabPopup) then CloseTab(RightPanelNotebook.PageIndex, False) 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) else + if (Sender is TGTKMenuItem) and (not LeftTabPopup) then CloseTab(-1, False) else + DebugMsg(['Couldn''t close all tabs: No listview focused.']); +end; + +procedure TFMain.CloseTab(TabNo: integer; LeftPanel: boolean); +var ATabList: TStringList; + PathsHighlight: TStringList; + ANotebook: TEphyNotebook; + AVBoxList: TList; + i, NewPageIndex: integer; + TabEngines: TList; + Engine: 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 (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 Engine is TVFSEngine then + try + if not TVFSEngine(Engine).VFSClose then DebugMsg(['Error closing the engine...']); + Engine.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]); + 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 ((LeftPanel and (LeftPanelEngine <> TabEngines[i])) or + ((not LeftPanel) and (RightPanelEngine <> TabEngines[i]))) then + try + Engine := TabEngines[i]; + if not TVFSEngine(Engine).VFSClose then DebugMsg(['Error closing the engine...']); + Engine.Free; + except 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; +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, APath); + SetTabLabel(ANotebook, ANotebook.ChildrenCount - 1, APath, TabList[i]); + end; + + if not ANotebook.Visible then ANotebook.Visible := True; + ANotebook.PageIndex := SetTabActive; + TabNotebookSwitchPage(ANotebook, SetTabActive, True); +end; + +procedure TFMain.SetTabLabel(Notebook: TEphyNotebook; PageIndex: integer; ALabel, Tooltip: string); +begin + if (ConfTabMaxLength > 0) and (Length(ALabel) > ConfTabMaxLength) then + ALabel := Copy(ALabel, 1, ConfTabMaxLength) + '...'; + 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); + CanClose := False; +end; + +procedure TFMain.NotebookTabDoubleClick(Sender: TObject; const TabNum: integer); +begin + CloseTab(TabNum, Sender = LeftPanelNotebook); +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 + 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; + 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); + NewTabInternal(not LeftPanel, ATargetEngine, APath, dst_no); + + 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(const Ext, FullPath, HighlightItem, TargetPath: string): boolean; +var i, j: integer; + Plugin: TVFSPlugin; +begin + Result := False; + Plugin := nil; + + // Try to find a plugin which can handle open the archive type + if PluginList.Count > 0 then + for i := 0 to PluginList.Count - 1 do begin + Plugin := TVFSPlugin(PluginList[i]); + if Length(Plugin.Extensions) > 0 then + for j := 0 to Length(Plugin.Extensions) - 1 do begin +// DebugMsg(['Extension = "', Plugin.Extensions[j], '", Ext = "', Ext, '"']); + if AnsiCompareText(Plugin.Extensions[j], Ext) = 0 then begin + Result := True; + Break; + end; + end; + if Result then Break; + end; + if not Result then Exit; // we didn't find appropriate plugin + DebugMsg(['Found plugin ''', Plugin.VFSName, ''', trying to open the file ''', FullPath, '''']); + + ChangingDir(LeftLastFocused, TargetPath, FullPath, HighlightItem, False, False, Plugin); +end; + +procedure TFMain.CloseVFS(LeftPanel, SurpressRefresh: boolean); +var Engine: TPanelEngine; +begin + if LeftPanel then Engine := LeftPanelEngine + else Engine := RightPanelEngine; + if not Assigned(Engine.ParentEngine) or (not (Engine is TVFSEngine)) then Exit; + if LeftPanel then LeftPanelEngine := Engine.ParentEngine + else RightPanelEngine := Engine.ParentEngine; + if not SurpressRefresh then ChangingDir(LeftPanel, Engine.SavePath, Engine.ParentEngine.LastHighlightItem, '', False, True); + + if not TVFSEngine(Engine).VFSClose then DebugMsg(['Error closing the engine...']); + Engine.Free; +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; + Ext: string; + 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 := ANSIToUTF8(ExcludeTrailingPathDelimiter(Engine.Path)); + if Length(FSearch.SearchInEntry.Text) < 1 then FSearch.SearchInEntry.Text := '/'; + case FSearch.Run of + mbOK: ; + mbApply: begin + if Length(FSearch.GoToFileArchive) > 0 then begin + Ext := ANSIUpperCase(Trim(Copy(FSearch.GoToFileArchive, LastDelimiter('.', FSearch.GoToFileArchive) + 1, Length(FSearch.GoToFileArchive) - LastDelimiter('.', FSearch.GoToFileArchive)))); + HandleVFSArchive(Ext, 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 ANSICompareText(string(PDataItem(DataList[i])^.AName), 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 + FSearch.Free; + Application.ProcessMessages; + InternalLockInit(False); + end; +end; + +(********************************************************************************************************************************) +(********************************************************************************************************************************) +procedure TFMain.miOpenConnectionClick(Sender: TObject); +var Engine: TVFSEngine; + SourceEngine: TPanelEngine; + i, j: integer; + VFSPlugin: TVFSPlugin; + ConnInfo: TConnMgrItem; + b: boolean; +begin + try + InternalLock; + FConnectionManager := TFConnectionManager.Create(Self); + b := FConnectionManager.Run = mbOK; + WriteConnections; // Save the list to the file + + if b then begin + if PluginList.Count = 0 then begin + Application.MessageBox(LANGThereIsNoModuleAvailable, [mbOK], mbError, mbOK, mbOK); + Exit; + end; + if FConnectionManager.ListView.Selected = nil then Exit; + ConnInfo := FConnectionManager.ListView.Selected.AsPointer(0); + if ConnInfo = nil then Exit; + + // Find the VFS module to use for this connection + VFSPlugin := nil; + if Length(ConnInfo.PluginID) > 0 then + for i := 0 to PluginList.Count - 1 do + if TVFSPlugin(PluginList[i]).VFSName = ConnInfo.PluginID then begin + VFSPlugin := PluginList[i]; + Break; + end; + if VFSPlugin = nil then begin + for i := 0 to PluginList.Count - 1 do begin + if Length(TVFSPlugin(PluginList[i]).Services) > 0 then + for j := 0 to Length(TVFSPlugin(PluginList[i]).Services) - 1 do + if AnsiCompareText(TVFSPlugin(PluginList[i]).Services[j], ConnInfo.ServiceType) = 0 then begin + VFSPlugin := PluginList[i]; + Break; + end; + if VFSPlugin <> nil then Break; + end; + if VFSPlugin = nil then VFSPlugin := PluginList[0]; // Fallback in hope something else could handle it + end; + + if (LeftLastFocused and (LeftPanelEngine is TVFSEngine)) or ((not LeftLastFocused) and (RightPanelEngine is TVFSEngine)) then + begin + if Application.MessageBox(LANGCloseOpenConnection, [mbYes, mbNo], mbWarning, mbYes, mbNo) <> mbYes then Exit; + CloseVFS(LeftLastFocused, True); + end; + + // Construct the VFS Engine and try to open the connection + if LeftLastFocused then SourceEngine := LeftPanelEngine + else SourceEngine := RightPanelEngine; + Engine := TVFSEngine.Create(VFSPlugin); + Engine.ParentEngine := SourceEngine; + Engine.SavePath := SourceEngine.Path; + if not Engine.VFSOpenURI(ConnInfo.URI) then begin + Application.MessageBox(LANGCouldntOpenURI, [mbOK], mbError, mbOK, mbOK); + Exit; + end; + if LeftLastFocused then LeftPanelEngine := Engine + else RightPanelEngine := Engine; + DoRefresh(LeftLastFocused, False, False); + end; + finally + FConnectionManager.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; + ChDir(CurrentPath); + ExecuteProgram('bash', CurrentPath, False, True, Error); + 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)^.AName else + for i := 0 to DataList.Count - 1 do + with PDataItem(DataList[i])^ do + if Selected and (not UpDir) then begin + AFile := AName; + 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 + 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; + + 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 Exit; // Silently do nothing + + if SelCount = 0 then begin + s := PDataItem(AListView.Selected.Data)^.AName; + 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])^.AName + #10; + end; + end; + end; + + clip := gtk_clipboard_get(gdk_atom_intern('CLIPBOARD', False)); + gtk_clipboard_set_text(clip, StringToPgchar(s), Length(s)); +end; + + +(********************************************************************************************************************************) +function TFMain.HandleRunFromArchive(var APath: string; Engine: TPanelEngine; Command, FileTypeDesc: string; BypassDialog: boolean): boolean; +var Res: TMessageButton; + Stat: PDataItemSl; + s: string; + AWorkingThread: TWorkerThread; + AFProgress: TFProgress; + tmp: PChar; + LocalEngine: TLocalTreeEngine; + AListView: TGTKListView; + DataList: TList; + err: integer; +begin + Result := False; + try + if not BypassDialog then begin + Stat := Engine.GetFileInfoSL(APath); + FRunFromVFS := TFRunFromVFS.Create(Self); + FRunFromVFS.FileNameLabel2.Caption := Format('%s ', [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 ', [ANSIToUTF8(FormatSize(Stat^.Size, 0)), s]); + if (ConfSizeFormat < 5) or (Stat^.Size < 1024) then s := Format(' %s', [LANGHandleRunFromArchive_Bytes]); + FRunFromVFS.PackedSizeLabel2.Caption := Format('%s%s ', [ANSIToUTF8(FormatSize(Stat^.Size, 0)), s]); + FRunFromVFS.DateLabel2.Caption := Format('%s ', [FormatDateTime('ddddd tt', Stat^.ModifyTime)]); + 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; + 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; + + FreeDataItem(Stat); + + Res := FRunFromVFS.Run; + FRunFromVFS.Close; + FRunFromVFS.Free; + Application.ProcessMessages; + end else Res := mbYes; + if Res in [mbYes, mbNo] then begin + Result := False; + // Handle password + if (Engine is TVFSEngine) and TVFSEngine(Engine).GetPasswordRequired and (Length(TVFSEngine(Engine).Password) < 1) then + if not HandleSetPassword(Engine) then Exit; + + if Res = mbYes then DebugMsg(['(II) HandleRunFromArchive: Selected extract and execute single item']) + else DebugMsg(['(II) HandleRunFromArchive: Selected extract all and execute']); + s := ConfTempPath + '-XXXXXX'; + tmp := strdup(PChar(s)); + tmp := mkdtemp(tmp); + if tmp = nil then begin + err := errno; + DebugMsg(['(EE) HandleRunFromArchive: 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) HandleRunFromArchive: Using temporary directory: ', tmp]); + UsedTempPaths.Add(string(tmp)); + + Engine.Path := ExtractFilePath(APath); + // Extract the files + try + AWorkingThread := TWorkerThread.Create; + DebugMsg(['TFMain.HandleRunFromArchive: Creating thread...']); + AFProgress := TFProgress.Create(Self); + AFProgress.Label1.Caption := LANGCopySC; + AFProgress.SetNumBars(True); + AFProgress.ProgressBar.Value := 0; + AWorkingThread.ProgressForm := AFProgress; + LocalEngine := TLocalTreeEngine.Create; + LocalEngine.SetPath(tmp); + AWorkingThread.ExtractFromVFSMode := True; + AWorkingThread.DestEngine := LocalEngine; + AWorkingThread.SrcEngine := Engine; + if LeftLastFocused then begin + AListView := LeftListView; + DataList := LeftPanelData; + end else begin + AListView := RightListView; + DataList := RightPanelData; + end; + if Assigned(AListView.Selected) then AWorkingThread.SelectedItem := AListView.Selected.Data; + AWorkingThread.ExtractFromVFSAll := Res = mbNo; + AWorkingThread.LeftPanel := LeftLastFocused; + AWorkingThread.DataList := DataList; + AWorkingThread.WorkerProcedure := CopyFilesWorker; + AWorkingThread.ParamBool3 := True; + AWorkingThread.ParamBool4 := False; + AWorkingThread.ParamBool5 := True; + AWorkingThread.ParamString1 := string(tmp); + AWorkingThread.ParamString2 := APath; + 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.Cancelled) and} (not AWorkingThread.ErrorHappened); + finally + AFProgress.Free; + DebugMsg(['TFMain.HandleRunFromArchive: Freeing thread...']); + AWorkingThread.Free; + LocalEngine.Free; + end; + + DebugMsg(['(II) HandleRunFromArchive: Old path = ', APath]); + if Result then begin + if Res = mbYes then APath := IncludeTrailingPathDelimiter(string(tmp)) + ExtractFileName(APath) + else APath := ExcludeTrailingPathDelimiter(string(tmp)) + APath; + end; + DebugMsg(['(II) HandleRunFromArchive: New path = ', APath]); + + + Libc.free(tmp); + DebugMsg(['(II) HandleRunFromArchive: Copy OK, Result = ', Result]); + end; + + finally + Application.ProcessMessages; + end; +end; + +procedure TFMain.PasswordButtonClick(Sender: TObject); +begin + if Sender = LeftPasswordButton then HandleSetPassword(LeftPanelEngine) else + if Sender = RightPasswordButton then HandleSetPassword(RightPanelEngine); +end; + +function TFMain.HandleSetPassword(Engine: TPanelEngine): boolean; +begin + if not (Engine is TVFSEngine) then Exit; + try + FSetPassword := TFSetPassword.Create(Self); + FSetPassword.Entry.Text := TVFSEngine(Engine).Password; + if Length(FSetPassword.Entry.Text) > 0 then FSetPassword.Entry.SelectAll; + Result := FSetPassword.Run = mbOK; + if Result then TVFSEngine(Engine).Password := FSetPassword.Entry.Text; + FSetPassword.Free; + except + on E: Exception do DebugMsg(['*** Exception raised in FMain.HandleSetPassword: (', E.ClassName, '): ', E.Message]); + end; +end; + + + + + +end. + -- cgit v1.2.3