(* Tux Commander - USearch - Search dialog 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 USearch; interface uses glib2, gdk2, gtk2, SyncObjs, SysUtils, Types, Classes, Variants, GTKControls, GTKForms, GTKStdCtrls, GTKExtCtrls, GTKConsts, GTKView, GTKMenus, UEngines, UGnome, ULibc; type TFSearch = class(TGTKDialog) BottomBox: TGTKVBox; HBox, HBox2: TGTKHBox; ButtonBox: TGTKHButtonBox; FileList: TGTKListView; FileListScrolledWindow: TGTKScrolledWindow; Notebook: TGTKNotebook; Table1, Table2: TGTKTable; ResultsLabel, StatusLabel: TGTKLabel; Label1, Label2, Label3, Label4, Label5, Label6: TGTKLabel; FileMaskEntry: TGTKCombo; SearchInEntry, FindTextEntry: TGTKEntry; ViewButton, NewSearchButton, GoToFileButton, FeedToListboxButton: TGTKButton; SearchArchivesCheckButton, CaseSensitiveCheckButton, StayCurrentFSCheckButton, CaseSensitiveMatchCheckButton: TGTKCheckButton; FindButton, StopButton, CloseButton: TGTKButton; BiggerThanCheckButton, SmallerThanCheckButton: TGTKCheckButton; BiggerThanEntry, SmallerThanEntry: TGTKSpinEdit; BiggerThanOptionMenu, SmallerThanOptionMenu, ModifiedLastOptionMenu, ModifiedNotLastOptionMenu: TGTKOptionMenu; ModifiedBetweenRadioButton, NotModifiedAfterRadioButton, ModifiedLastRadioButton, ModifiedNotLastRadionButton: TGTKCheckButton; ModifiedLastSpinEdit, ModifiedNotLastSpinEdit: TGTKSpinEdit; ModifiedBetweenEntry1, ModifiedBetweenEntry2, NotModifiedAfterEntry: TGTKEntry; ModifiedBetweenEntry1G, ModifiedBetweenEntry2G, NotModifiedAfterEntryG: TGnomeDateEdit; procedure FormCreate(Sender: TObject); override; procedure FormDestroy(Sender: TObject); procedure FormKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure FormResponse(Sender: TObject; const ResponseID: integer); procedure FileListSelectionChanged(Sender: TObject); procedure FindButtonClick(Sender: TObject); procedure StopButtonClick(Sender: TObject); procedure CloseButtonClick(Sender: TObject); procedure BiggerThanCheckButtonToggled(Sender: TObject); procedure NewSearchButtonClick(Sender: TObject); procedure ViewButtonClick(Sender: TObject); procedure GoToFileButtonClick(Sender: TObject); procedure FileListDblClick(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); procedure FileMaskEntryKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); private Processing, Stop, FUseGnomeWidgets: boolean; SavedData: string; procedure ConstructViews; procedure DoSearch; public Engine: TPanelEngine; AListView: TGTKListView; List: TList; GoToFile, GoToFileArchive: string; end; TSearchThread = class(TThread) private FEngine: TPanelEngine; Wilds: array of string; GUIMutex: TCriticalSection; procedure Rekurze(StartDir: string); function FindText(FileName: string): boolean; protected FStartPath, FFileMask, FStringFind: string; FDontLeaveFS, FCaseSensitiveMask, FCaseSensitiveStrings, FSearchArchives: boolean; FBiggerThan, FSmallerThan: integer; // -1 = skip FModifiedBetween1, FModifiedBetween2, FNotModifiedAfter: time_t; FModifiedLast, FModifiedNotLast: integer; FList: TList; procedure Execute; override; public Finished, CancelIt: boolean; CurrentDir: string; constructor Create(Engine: TPanelEngine); destructor Destroy; override; end; var FSearch: TFSearch; implementation uses Math, UMain, ULocale, UCoreUtils, UCore, DateUtils, UViewer, UConfig, UVFSCore; var SizeUnits: array[0..2] of string; DayUnits: array[0..3] of string; procedure TFSearch.FormCreate(Sender: TObject); var MenuItem: TGTKMenuItem; i: integer; begin // Set the constants SizeUnits[0] := LANGSearch_Bytes; SizeUnits[1] := LANGSearch_kB; SizeUnits[2] := LANGSearch_MB; DayUnits[0] := LANGSearch_days; DayUnits[1] := LANGSearch_weeks; DayUnits[2] := LANGSearch_months; DayUnits[3] := LANGSearch_years; // Initialization // WindowTypeHint := whNormal; List := TList.Create; List.Clear; Processing := False; Stop := False; GoToFile := ''; GoToFileArchive := ''; SetDefaultSize(650, 600); Caption := LANGSearch_Caption; Buttons := []; ShowSeparator := False; FUseGnomeWidgets := Assigned(@gnome_date_edit_new) and Assigned(@gnome_date_edit_set_time) and Assigned(@gnome_date_edit_get_time); Notebook := TGTKNotebook.Create(Self); Notebook.BorderWidth := 10; Table1 := TGTKTable.Create(Self); Table1.BorderWidth := 10; Table2 := TGTKTable.Create(Self); Table2.BorderWidth := 10; Notebook.AppendPage(Table1, LANGSearch_General); Notebook.AppendPage(Table2, LANGSearch_Advanced); ConstructViews; ResultsLabel := TGTKLabel.Create(Self); ResultsLabel.XAlign := 0; ResultsLabel.XPadding := 0; ResultsLabel.Caption := Format('%s', [LANGSearch_SearchResults]); ResultsLabel.FocusControl := FileList; ResultsLabel.UseMarkup := True; ResultsLabel.UseUnderline := True; ClientArea.AddControlEx(Notebook, False, False, 0); Label1 := TGTKLabel.Create(Self); Label1.XAlign := 0; Label1.XPadding := 0; Label1.Caption := Format('%s', [LANGSearch_SearchFor]); FileMaskEntry := TGTKCombo.Create(Self); FileMaskEntry.Tooltip := LANGSearch_FileMaskEntryTooltip; FileMaskEntry.Entry.OnKeyDown := FileMaskEntryKeyDown; if SearchHistory.Count > 0 then for i := 0 to SearchHistory.Count - 1 do FileMaskEntry.Items.Append(SearchHistory[i]); FileMaskEntry.Entry.Text := ''; Label1.FocusControl := FileMaskEntry.Entry; Label1.UseMarkup := True; Label1.UseUnderline := True; Label2 := TGTKLabel.Create(Self); Label2.XAlign := 0; Label2.XPadding := 0; Label2.Caption := LANGSearch_SearchIn; SearchInEntry := TGTKEntry.Create(Self); Label2.FocusControl := SearchInEntry; Label2.UseMarkup := True; Label2.UseUnderline := True; SearchArchivesCheckButton := TGTKCheckButton.CreateWithLabel(Self, LANGSearch_SearchArchivesCheckButton); SearchArchivesCheckButton.Enabled := PluginList.Count > 0; Label3 := TGTKLabel.Create(Self); Label3.XAlign := 0; Label3.XPadding := 0; Label3.Caption := LANGSearch_FindText; FindTextEntry := TGTKEntry.Create(Self); FindTextEntry.Tooltip := LANGSearch_FindTextEntryTooltip; Label3.FocusControl := FindTextEntry; Label3.UseMarkup := True; Label3.UseUnderline := True; CaseSensitiveCheckButton := TGTKCheckButton.CreateWithLabel(Self, LANGSearch_CaseSensitiveCheckButton); StayCurrentFSCheckButton := TGTKCheckButton.CreateWithLabel(Self, LANGSearch_StayCurrentFSCheckButton); CaseSensitiveMatchCheckButton := TGTKCheckButton.CreateWithLabel(Self, LANGSearch_CaseSensitiveMatchCheckButton); Table1.AddControlEx(0, 0, 1, 1, Label1, [taoShrink, taoFill], [taoShrink], 5, 2); Table1.AddControlEx(1, 0, 1, 1, FileMaskEntry, [taoExpand, taoFill], [taoShrink], 5, 2); Table1.AddControlEx(2, 0, 1, 1, CaseSensitiveMatchCheckButton, [taoShrink, taoFill], [taoShrink], 5, 2); Table1.AddControlEx(0, 1, 1, 1, Label2, [taoShrink, taoFill], [taoShrink], 5, 2); Table1.AddControlEx(1, 1, 1, 1, SearchInEntry, [taoExpand, taoFill], [taoShrink], 5, 2); Table1.AddControlEx(2, 1, 1, 1, StayCurrentFSCheckButton, [taoShrink], [taoShrink], 5, 2); Table1.AddControlEx(1, 2, 2, 1, SearchArchivesCheckButton, [taoShrink, taoFill], [taoShrink], 5, 2); Table1.AddControlEx(0, 3, 3, 1, TGTKHSeparator.Create(Self), [taoExpand, taoFill], [taoShrink], 5, 4); Table1.AddControlEx(0, 4, 1, 1, Label3, [taoShrink, taoFill], [taoShrink], 5, 2); Table1.AddControlEx(1, 4, 2, 1, FindTextEntry, [taoExpand, taoFill], [taoShrink], 5, 2); Table1.AddControlEx(1, 5, 2, 1, CaseSensitiveCheckButton, [taoShrink, taoFill], [taoShrink], 5, 2); Table1.AddControlEx(0, 6, 3, 1, TGTKVBox.Create(Self), [taoShrink, taoFill], [taoExpand, taoFill], 5, 2); Label4 := TGTKLabel.Create(Self); Label4.XAlign := 0; Label4.XPadding := 0; Label4.Caption := Format('%s', [LANGSearch_Size]); Label4.UseMarkup := True; Label5 := TGTKLabel.Create(Self); Label5.XAlign := 0; Label5.XPadding := 0; Label5.Caption := Format('%s', [LANGSearch_Date]); Label5.UseMarkup := True; BiggerThanCheckButton := TGTKCheckButton.CreateWithLabel(Self, LANGSearch_BiggerThan); SmallerThanCheckButton := TGTKCheckButton.CreateWithLabel(Self, LANGSearch_SmallerThan); BiggerThanEntry := TGTKSpinEdit.Create(Self); BiggerThanEntry.Min := 0; BiggerThanEntry.Max := 999999999; BiggerThanEntry.Digits := 0; BiggerThanEntry.IncrementStep := 1; BiggerThanEntry.IncrementPage := 10; SmallerThanEntry := TGTKSpinEdit.Create(Self); SmallerThanEntry.Min := 0; SmallerThanEntry.Max := 999999999; SmallerThanEntry.Digits := 0; SmallerThanEntry.IncrementStep := 1; SmallerThanEntry.IncrementPage := 10; BiggerThanOptionMenu := TGTKOptionMenu.Create(Self); for i := Low(SizeUnits) to High(SizeUnits) do begin MenuItem := TGTKMenuItem.Create(Self); MenuItem.Caption := SizeUnits[i]; BiggerThanOptionMenu.Items.Add(MenuItem); end; SmallerThanOptionMenu := TGTKOptionMenu.Create(Self); for i := Low(SizeUnits) to High(SizeUnits) do begin MenuItem := TGTKMenuItem.Create(Self); MenuItem.Caption := SizeUnits[i]; SmallerThanOptionMenu.Items.Add(MenuItem); end; ModifiedBetweenRadioButton := TGTKCheckButton.CreateWithLabel(Self, LANGSearch_ModifiedBetweenRadioButton); NotModifiedAfterRadioButton := TGTKCheckButton.CreateWithLabel(Self, LANGSearch_NotModifiedAfterRadioButton); ModifiedLastRadioButton := TGTKCheckButton.CreateWithLabel(Self, LANGSearch_ModifiedLastRadioButton); ModifiedNotLastRadionButton := TGTKCheckButton.CreateWithLabel(Self, LANGSearch_ModifiedNotLastRadionButton); ModifiedLastSpinEdit := TGTKSpinEdit.Create(Self); ModifiedLastSpinEdit.Min := 0; ModifiedLastSpinEdit.Max := 10000; ModifiedLastSpinEdit.Digits := 0; ModifiedLastSpinEdit.IncrementStep := 1; ModifiedLastSpinEdit.IncrementPage := 10; ModifiedNotLastSpinEdit := TGTKSpinEdit.Create(Self); ModifiedNotLastSpinEdit.Min := 0; ModifiedNotLastSpinEdit.Max := 10000; ModifiedNotLastSpinEdit.Digits := 0; ModifiedNotLastSpinEdit.IncrementStep := 1; ModifiedNotLastSpinEdit.IncrementPage := 10; ModifiedLastOptionMenu := TGTKOptionMenu.Create(Self); for i := Low(DayUnits) to High(DayUnits) do begin MenuItem := TGTKMenuItem.Create(Self); MenuItem.Caption := DayUnits[i]; ModifiedLastOptionMenu.Items.Add(MenuItem); end; ModifiedNotLastOptionMenu := TGTKOptionMenu.Create(Self); for i := Low(DayUnits) to High(DayUnits) do begin MenuItem := TGTKMenuItem.Create(Self); MenuItem.Caption := DayUnits[i]; ModifiedNotLastOptionMenu.Items.Add(MenuItem); end; Label6 := TGTKLabel.Create(Self); Label6.XAlign := 0; Label6.XPadding := 0; Label6.Caption := LANGSearch_And; Label6.UseMarkup := True; if not FUseGnomeWidgets then begin ModifiedBetweenEntry1 := TGTKEntry.Create(Self); ModifiedBetweenEntry1.Tooltip := SysUtils.FormatDateTime(LANGSearch_ModifiedBetweenEntry1, Date); ModifiedBetweenEntry2 := TGTKEntry.Create(Self); ModifiedBetweenEntry2.Tooltip := SysUtils.FormatDateTime(LANGSearch_ModifiedBetweenEntry1, Date); NotModifiedAfterEntry := TGTKEntry.Create(Self); NotModifiedAfterEntry.Tooltip := SysUtils.FormatDateTime(LANGSearch_ModifiedBetweenEntry1, Date); Table2.AddControlEx(2, 5, 2, 1, ModifiedBetweenEntry1, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 2); Table2.AddControlEx(5, 5, 2, 1, ModifiedBetweenEntry2, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 2); Table2.AddControlEx(2, 6, 2, 1, NotModifiedAfterEntry, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 2); end else begin ModifiedBetweenEntry1G := TGnomeDateEdit.Create(Self); ModifiedBetweenEntry2G := TGnomeDateEdit.Create(Self); NotModifiedAfterEntryG := TGnomeDateEdit.Create(Self); Table2.AddControlEx(2, 5, 2, 1, ModifiedBetweenEntry1G, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 2); Table2.AddControlEx(5, 5, 2, 1, ModifiedBetweenEntry2G, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 2); Table2.AddControlEx(2, 6, 2, 1, NotModifiedAfterEntryG, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 2); end; Table2.AddControlEx(0, 0, 3, 1, Label4, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 0); Table2.AddControlEx(0, 1, 1, 1, TGTKVBox.Create(Self), [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 8, 0); Table2.AddControlEx(1, 1, 1, 1, BiggerThanCheckButton, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 2, 0); Table2.AddControlEx(2, 1, 1, 1, BiggerThanEntry, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 0); Table2.AddControlEx(3, 1, 1, 1, BiggerThanOptionMenu, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 0, 0); Table2.AddControlEx(5, 1, 1, 1, SmallerThanCheckButton, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 0); Table2.AddControlEx(6, 1, 1, 1, SmallerThanEntry, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 0); Table2.AddControlEx(7, 1, 1, 1, SmallerThanOptionMenu, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 0, 0); Table2.AddControlEx(1, 3, 3, 1, TGTKVBox.Create(Self), [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 0, 6); Table2.AddControlEx(0, 4, 3, 1, Label5, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 1); Table2.AddControlEx(1, 5, 1, 1, ModifiedBetweenRadioButton, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 2, 2); Table2.AddControlEx(4, 5, 1, 1, Label6, [taoShrink], [taoShrink, taoExpand, taoFill], 5, 2); Table2.AddControlEx(1, 6, 1, 1, NotModifiedAfterRadioButton, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 2, 2); Table2.AddControlEx(1, 7, 1, 1, ModifiedLastRadioButton, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 2, 2); Table2.AddControlEx(2, 7, 1, 1, ModifiedLastSpinEdit, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 2); Table2.AddControlEx(3, 7, 1, 1, ModifiedLastOptionMenu, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 2, 2); Table2.AddControlEx(4, 7, 2, 1, ModifiedNotLastRadionButton, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 2); Table2.AddControlEx(6, 7, 1, 1, ModifiedNotLastSpinEdit, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 5, 2); Table2.AddControlEx(7, 7, 1, 1, ModifiedNotLastOptionMenu, [taoShrink, taoFill], [taoShrink, taoExpand, taoFill], 0, 2); BottomBox := TGTKVBox.Create(Self); BottomBox.Homogeneous := False; BottomBox.BorderWidth := 10; FileListScrolledWindow := TGTKScrolledWindow.Create(Self); FileListScrolledWindow.HorizScrollBarPolicy := sbAutomatic; FileListScrolledWindow.VertScrollBarPolicy := sbAutomatic; FileListScrolledWindow.ShadowType := stShadowIn; FileListScrolledWindow.AddControl(FileList); FileListScrolledWindow.SetSizeRequest(100, -1); FileList.SetSizeRequest(100, -1); HBox := TGTKHBox.Create(Self); HBox2 := TGTKHBox.Create(Self); HBox2.Homogeneous := False; HBox.Homogeneous := True; ViewButton := TGTKButton.Create(Self); ViewButton.Caption := LANGSearch_ViewButtonCaption; ViewButton.Visible := True; NewSearchButton := TGTKButton.Create(Self); NewSearchButton.Caption := LANGSearch_NewSearchButtonCaption; GoToFileButton := TGTKButton.Create(Self); GoToFileButton.Caption := LANGSearch_GoToFileButtonCaption; FeedToListboxButton := TGTKButton.Create(Self); FeedToListboxButton.Caption := LANGSearch_FeedToListboxButtonCaption; FeedToListboxButton.Enabled := False; FeedToListboxButton.Visible := True; StatusLabel := TGTKLabel.Create(Self); StatusLabel.XAlign := 0; StatusLabel.XPadding := 0; StatusLabel.Caption := Format('%s %s', [LANGSearch_StatusSC, LANGSearch_Ready]); StatusLabel.UseMarkup := True; StatusLabel.SetSizeRequest(100, -1); HBox.AddControlEx(ViewButton, True, True, 3); HBox.AddControlEx(NewSearchButton, True, True, 3); HBox.AddControlEx(GoToFileButton, True, True, 3); HBox.AddControlEx(FeedToListboxButton, True, True, 3); HBox2.AddControlEx(TGTKVBox.Create(Self), True, True, 20); HBox2.AddControlEx(HBox, False, False, 0); BottomBox.AddControlEx(ResultsLabel, False, False, 5); BottomBox.AddControlEx(FileListScrolledWindow, True, True, 0); BottomBox.AddControlEx(StatusLabel, False, False, 5); BottomBox.AddControlEx(HBox2, False, False, 2); ClientArea.AddControlEx(BottomBox, True, True, 5); FindButton := TGTKButton.CreateFromStock(Self, GTK_STOCK_FIND); FindButton.Default := True; StopButton := TGTKButton.CreateFromStock(Self, GTK_STOCK_STOP); StopButton.Visible := False; StopButton.Default := True; CloseButton := TGTKButton.CreateFromStock(Self, GTK_STOCK_CLOSE); CloseButton.Default := True; Default := FindButton; ButtonBox := TGTKHButtonBox.Create(Self); ButtonBox.Layout := blEnd; ButtonBox.Spacing := 10; ButtonBox.BorderWidth := 10; ButtonBox.AddControlEnd(FindButton); ButtonBox.AddControlEnd(StopButton); ButtonBox.AddControlEnd(CloseButton); ActionArea.AddControlEx(ButtonBox, False, False, 0); OnKeyDown := FormKeyDown; OnCloseQuery := FormCloseQuery; OnResponse := FormResponse; OnDestroy := FormDestroy; FileList.OnSelectionChanged := FileListSelectionChanged; FindButton.OnClick := FindButtonClick; StopButton.OnClick := StopButtonClick; CloseButton.OnClick := CloseButtonClick; BiggerThanCheckButton.OnToggled := BiggerThanCheckButtonToggled; SmallerThanCheckButton.OnToggled := BiggerThanCheckButtonToggled; ModifiedBetweenRadioButton.OnToggled := BiggerThanCheckButtonToggled; NotModifiedAfterRadioButton.OnToggled := BiggerThanCheckButtonToggled; ModifiedLastRadioButton.OnToggled := BiggerThanCheckButtonToggled; ModifiedNotLastRadionButton.OnToggled := BiggerThanCheckButtonToggled; NewSearchButton.OnClick := NewSearchButtonClick; ViewButton.OnClick := ViewButtonClick; GoToFileButton.OnClick := GoToFileButtonClick; FileList.OnDblClick := FileListDblClick; Notebook.PageIndex := 0; FileMaskEntry.Entry.SetFocus; BiggerThanCheckButtonToggled(Sender); FileListSelectionChanged(Sender); end; procedure TFSearch.ConstructViews; var Column: TGTKTreeViewColumn; i: integer; begin FileList := TGTKListView.CreateTyped(Self, True, [lcPointer, lcText]); FileList.SelectionMode := smSingle; FileList.RulesHint := True; FileList.ShowHeaders := False; Column := FileList.Columns.Add; Column.Caption := LANGFilenameColumnCaption; Column.AddAttribute('text', 1); Column.SortID := 0; for i := 0 to FileList.Columns.Count - 1 do begin FileList.Columns[i].SizingMode := smAutoSize; FileList.Columns[i].Resizable := True; FileList.Columns[i].SetProperty('ypad', 0); FileList.Columns[i].SetProperty('yalign', 0.5); end; end; procedure TFSearch.FormDestroy(Sender: TObject); var i: integer; begin Application.ProcessMessages; try if List.Count > 0 then for i := 0 to List.Count - 1 do FreeDataItem(PDataItem(List[i])); List.Free; except end; end; procedure TFSearch.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin Stop := True; end; procedure TFSearch.FormResponse(Sender: TObject; const ResponseID: integer); begin Stop := True; end; procedure TFSearch.FormKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); begin case Key of GDK_F3: if FileList.Focused and Assigned(FileList.Selected) then begin Accept := False; ViewButtonClick(Sender); end; GDK_RETURN, GDK_KP_ENTER: if FileList.Focused and Assigned(FileList.Selected) then begin Accept := False; GoToFileButtonClick(Sender); end else if FindButton.Visible then begin FindButtonClick(Sender); Accept := False; end; GDK_ESCAPE: begin Accept := False; if Processing then Stop := True else ModalResult := mbCancel; end; end; end; procedure TFSearch.FindButtonClick(Sender: TObject); var s: string; i: integer; begin s := Trim(FileMaskEntry.Entry.Text); if Length(s) > 0 then begin SaveItemToHistory(s, SearchHistory); if FileMaskEntry.Items.Count > 0 then for i := FileMaskEntry.Items.Count - 1 downto 0 do FileMaskEntry.Items.Delete(i); if SearchHistory.Count > 0 then for i := 0 to SearchHistory.Count - 1 do FileMaskEntry.Items.Append(SearchHistory[i]); end; DoSearch; end; procedure TFSearch.StopButtonClick(Sender: TObject); begin Stop := True; end; procedure TFSearch.CloseButtonClick(Sender: TObject); begin if Processing then Stop := True; ModalResult := mbClose; end; procedure TFSearch.BiggerThanCheckButtonToggled(Sender: TObject); begin BiggerThanEntry.Enabled := BiggerThanCheckButton.Checked; BiggerThanOptionMenu.Enabled := BiggerThanCheckButton.Checked; SmallerThanEntry.Enabled := SmallerThanCheckButton.Checked; SmallerThanOptionMenu.Enabled := SmallerThanCheckButton.Checked; Label6.Enabled := ModifiedBetweenRadioButton.Checked; ModifiedLastSpinEdit.Enabled := ModifiedLastRadioButton.Checked; ModifiedLastOptionMenu.Enabled := ModifiedLastRadioButton.Checked; ModifiedNotLastSpinEdit.Enabled := ModifiedNotLastRadionButton.Checked; ModifiedNotLastOptionMenu.Enabled := ModifiedNotLastRadionButton.Checked; if not FUseGnomeWidgets then begin ModifiedBetweenEntry1.Enabled := ModifiedBetweenRadioButton.Checked; ModifiedBetweenEntry2.Enabled := ModifiedBetweenRadioButton.Checked; NotModifiedAfterEntry.Enabled := NotModifiedAfterRadioButton.Checked; end else begin ModifiedBetweenEntry1G.Enabled := ModifiedBetweenRadioButton.Checked; ModifiedBetweenEntry2G.Enabled := ModifiedBetweenRadioButton.Checked; NotModifiedAfterEntryG.Enabled := NotModifiedAfterRadioButton.Checked; end; end; procedure TFSearch.FileListSelectionChanged(Sender: TObject); begin GoToFileButton.Enabled := Assigned(FileList.Selected) and Assigned(FileList.Selected.AsPointer(0)); ViewButton.Enabled := Assigned(FileList.Selected) and Assigned(FileList.Selected.AsPointer(0)); end; procedure TFSearch.NewSearchButtonClick(Sender: TObject); var i: integer; begin try FileMaskEntry.Entry.Text := ''; FileMaskEntry.Entry.SetFocus; FileList.Items.Clear; if List.Count > 0 then for i := List.Count - 1 downto 0 do FreeDataItem(PDataItem(List[i])); List.Clear; StatusLabel.Caption := Format('%s %s', [LANGSearch_StatusSC, LANGSearch_Ready]); StatusLabel.UseMarkup := True; except end; end; procedure TFSearch.ViewButtonClick(Sender: TObject); var AEngine: TPanelEngine; Plugin: TVFSPlugin; b: boolean; archive, s: string; i, j: integer; begin if not (Assigned(FileList.Selected) and Assigned(FileList.Selected.AsPointer(0))) then Exit; AEngine := nil; if Assigned(PDataItem(FileList.Selected.AsPointer(0))^.LnkPointTo) then begin archive := string(PDataItem(FileList.Selected.AsPointer(0))^.LnkPointTo); b := False; s := ANSIUpperCase(Trim(Copy(archive, LastDelimiter('.', archive) + 1, Length(archive) - LastDelimiter('.', archive)))); if (Length(s) > 1) and (s[1] = '.') then Delete(s, 1, 1); if PluginList.Count > 0 then begin Plugin := nil; 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 if AnsiCompareText(Plugin.Extensions[j], s) = 0 then begin b := True; Break; end; if b then Break; end; if b then begin DebugMsg(['Found plugin ''', Plugin.VFSName, ''', trying to open the archive ''', archive, '''']); AEngine := TVFSEngine.Create(Plugin); (AEngine as TVFSEngine).ArchiveMode := True; b := b and ((AEngine as TVFSEngine).VFSOpenEx(archive) = 0); end; end; end else begin AEngine := TLocalTreeEngine.Create; b := True; end; if b then FMain.EditViewFileInternal(Self, string(PDataItem(FileList.Selected.AsPointer(0))^.FDisplayName), AEngine, True, False) else Application.MessageBox(Format(LANGCannotLoadFile, [string(PDataItem(FileList.Selected.AsPointer(0))^.FDisplayName)]), [mbOK], mbError, mbNone, mbOK); if AEngine is TVFSEngine then (AEngine as TVFSEngine).VFSClose; AEngine.Free; end; procedure TFSearch.GoToFileButtonClick(Sender: TObject); begin if not (Assigned(FileList.Selected) and Assigned(FileList.Selected.AsPointer(0))) then Exit; GoToFile := string(PDataItem(FileList.Selected.AsPointer(0))^.FDisplayName); if Assigned(PDataItem(FileList.Selected.AsPointer(0))^.LnkPointTo) then GoToFileArchive := string(PDataItem(FileList.Selected.AsPointer(0))^.LnkPointTo); ModalResult := mbApply; end; procedure TFSearch.FileListDblClick(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); begin if Assigned(FileList.Selected) and Assigned(FileList.Selected.AsPointer(0)) then GoToFileButtonClick(Sender); end; procedure TFSearch.FileMaskEntryKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); var Orig, s: string; i: integer; begin case Key of GDK_UP, GDK_DOWN: if Shift = [] then begin Accept := False; if SearchHistory.Count > 0 then begin Orig := Trim(FileMaskEntry.Entry.Text); i := SearchHistory.IndexOf(Orig); if Key = GDK_DOWN then begin if i < 0 then begin SavedData := Orig; i := 0; end else if SearchHistory.Count > i + 1 then Inc(i); s := SearchHistory[i]; end else begin if i < 0 then Exit else if i = 0 then begin s := SavedData; SavedData := ''; end else if SearchHistory.Count > i then s := SearchHistory[i - 1]; end; FileMaskEntry.Entry.Text := s; FileMaskEntry.Entry.SetFocus; FileMaskEntry.Entry.SelectAll; end; end; end; end; (********************************************************************************************************************************) procedure TFSearch.DoSearch; var FSearchThread: TSearchThread; i, LastItems: integer; ListItem: TGTKListItem; x: Pointer; OldDir: string; begin try Processing := True; Stop := False; FindButton.Visible := False; StopButton.Visible := True; StopButton.SetFocus; Default := StopButton; StatusLabel.Caption := Format('%s %s', [LANGSearch_StatusSC, LANGSearch_PreparingToSearch]); StatusLabel.UseMarkup := True; // Disable the UI Table1.Enabled := False; Table2.Enabled := False; // Clear the items FileList.Items.Clear; if List.Count > 0 then for i := List.Count - 1 downto 0 do FreeDataItem(PDataItem(List[i])); List.Clear; // Save current directory OldDir := Engine.GetPath; // Set the parameters FSearchThread := TSearchThread.Create(Engine); with FSearchThread do begin FStartPath := UTF8ToStr(SearchInEntry.Text); FFileMask := UTF8ToStr(FileMaskEntry.Entry.Text); FStringFind := FindTextEntry.Text; FDontLeaveFS := not StayCurrentFSCheckButton.Checked; FCaseSensitiveMask := CaseSensitiveMatchCheckButton.Checked; FCaseSensitiveStrings := CaseSensitiveCheckButton.Checked; FSearchArchives := SearchArchivesCheckButton.Checked; if not BiggerThanCheckButton.Checked then FBiggerThan := -1 else begin i := StrToIntDef(BiggerThanEntry.Text, -1); i := i * Trunc(Power(1024, BiggerThanOptionMenu.ItemIndex)); FBiggerThan := i; end; if not SmallerThanCheckButton.Checked then FSmallerThan := -1 else begin i := StrToIntDef(SmallerThanEntry.Text, -1); i := i * Trunc(Power(1024, SmallerThanOptionMenu.ItemIndex)); FSmallerThan := i; end; FModifiedBetween1 := 0; FModifiedBetween2 := 0; FNotModifiedAfter := 0; if not FUseGnomeWidgets then begin if ModifiedBetweenRadioButton.Checked then begin FModifiedBetween1 := StrTotimetDef(ModifiedBetweenEntry1.Text, 0); FModifiedBetween2 := StrTotimetDef(ModifiedBetweenEntry2.Text, 0); end; if NotModifiedAfterRadioButton.Checked then FNotModifiedAfter := StrTotimetDef(NotModifiedAfterEntry.Text, 0); end else begin if ModifiedBetweenRadioButton.Checked then begin FModifiedBetween1 := ModifiedBetweenEntry1G.Time; FModifiedBetween2 := ModifiedBetweenEntry2G.Time; end; if NotModifiedAfterRadioButton.Checked then FNotModifiedAfter := NotModifiedAfterEntryG.Time; end; if not ModifiedLastRadioButton.Checked then FModifiedLast := -1 else begin i := StrToIntDef(ModifiedLastSpinEdit.Text, -1); case ModifiedLastOptionMenu.ItemIndex of 1: i := i * 7; // weeks 2: i := i * 30; // months 3: i := i * 365; // years end; FModifiedLast := i; end; if not ModifiedNotLastRadionButton.Checked then FModifiedNotLast := -1 else begin i := StrToIntDef(ModifiedNotLastSpinEdit.Text, -1); case ModifiedNotLastOptionMenu.ItemIndex of 1: i := i * 7; // weeks 2: i := i * 30; // months 3: i := i * 365; // years end; FModifiedNotLast := i; end; end; // UI loop FSearchThread.Resume; LastItems := 0; repeat Sleep(ConstInternalProgressTimer); FSearchThread.GUIMutex.Acquire; StatusLabel.Caption := Format('%s %s', [LANGSearch_SearchInProgress, StrToUTF8(FSearchThread.CurrentDir)]); FSearchThread.GUIMutex.Release; StatusLabel.UseMarkup := True; Application.ProcessMessages; FSearchThread.GUIMutex.Acquire; if Stop then FSearchThread.CancelIt := True; if LastItems < FSearchThread.FList.Count then begin for i := LastItems to FSearchThread.FList.Count - 1 do begin ListItem := FileList.Items.Add; ListItem.SetValue(0, FSearchThread.FList[i]); if PDataItem(FSearchThread.FList[i])^.LnkPointTo <> nil then ListItem.SetValue(1, Format('%s%s%s', [StrToUTF8(string(PDataItem(FSearchThread.FList[i])^.LnkPointTo)), ConstPathDelim, StrToUTF8(string(PDataItem(FSearchThread.FList[i])^.FDisplayName))])) else ListItem.SetValue(1, StrToUTF8(string(PDataItem(FSearchThread.FList[i])^.FDisplayName))); if i mod 30 = 0 then Application.ProcessMessages; // Refresh UI only after some amount of items added end; LastItems := FSearchThread.FList.Count; end; FSearchThread.GUIMutex.Release; until FSearchThread.Finished; if not Stop then StatusLabel.Caption := Format('%s (' + LANGSearch_FilesFound + ')', [LANGSearch_SearchFinished, FSearchThread.FList.Count]) else StatusLabel.Caption := Format('%s', [LANGSearch_UserCancelled]); StatusLabel.UseMarkup := True; // Save the list x := List; List := FSearchThread.FList; FSearchThread.FList := x; FSearchThread.Free; finally StopButton.Visible := False; FindButton.Visible := True; Default := FindButton; Processing := False; Stop := False; if FileList.Items.Count > 0 then FileList.SetFocus; if Engine.ChangeDir(OldDir) <> 0 then DebugMsg(['DoSearch: cannot change back to saved directory']); // Enable the UI Table1.Enabled := True; Table2.Enabled := True; end; end; (********************************************************************************************************************************) constructor TSearchThread.Create(Engine: TPanelEngine); begin inherited Create(True); FreeOnTerminate := False; GUIMutex := TCriticalSection.Create; CancelIt := False; Finished := False; CurrentDir := ''; FSearchArchives := False; FEngine := Engine; FList := TList.Create; end; destructor TSearchThread.Destroy; begin FList.Free; GUIMutex.Free; inherited Destroy; end; procedure TSearchThread.Execute; var i: integer; s: string; begin try // Prepare the wildcards SetLength(Wilds, 0); if Length(FFileMask) > 0 then begin while LastDelimiter(ConfSelItemsDelim, FFileMask) > 0 do begin i := LastDelimiter(ConfSelItemsDelim, FFileMask); if i < Length(FFileMask) then begin s := Copy(FFileMask, i + 1, Length(FFileMask) - i); Delete(FFileMask, i, Length(FFileMask) - i + 1); SetLength(Wilds, Length(Wilds) + 1); Wilds[Length(Wilds) - 1] := s; end; end; if Length(FFileMask) > 0 then begin SetLength(Wilds, Length(Wilds) + 1); Wilds[Length(Wilds) - 1] := FFileMask; end; // Check for strings with no jokers ---> allow searching by partial names if Length(Wilds) > 0 then for i := 0 to Length(Wilds) - 1 do if (Pos('*', Wilds[i]) < 1) and (Pos('?', Wilds[i]) < 1) then Wilds[i] := Format('*%s*', [Wilds[i]]); end; Rekurze(ExcludeTrailingPathDelimiter(FStartPath)); SetLength(Wilds, 0); finally Finished := True; end; end; procedure TSearchThread.Rekurze(StartDir: string); var LocalList: TList; i, j, k: integer; Matches, b: boolean; FileName, s: string; Data: PDataItem; Plugin: TVFSPlugin; xEngine: TVFSEngine; VFSOpenResult: integer; begin try if CancelIt then Exit; Plugin := nil; if Length(StartDir) < 1 then StartDir := '/'; DebugMsg(['++ Entering directory ', StartDir]); GUIMutex.Acquire; CurrentDir := StartDir; GUIMutex.Release; if FEngine.ChangeDir(StartDir) <> 0 then Exit; LocalList := TList.Create; if FEngine.GetListing(LocalList, True, StartDir) = 0 then begin // Processing... StartDir := IncludeTrailingPathDelimiter(StartDir); if LocalList.Count > 0 then for i := 0 to LocalList.Count - 1 do begin if CancelIt then Break; Data := LocalList[i]; // DebugMsg([' --- found ', Data^.AName]); FileName := Data^.FName; Matches := True; // Test if the file is on the same FS if Matches and FDontLeaveFS then Matches := Matches and FEngine.IsOnSameFS(FStartPath, StartDir + FileName); // File mask test if Matches and (Length(Wilds) > 0) then begin b := False; for j := 0 to Length(Wilds) - 1 do b := b or IsWild(FileName, Wilds[j], not FCaseSensitiveMask); Matches := Matches and b; end; if not Data^.IsDir then begin // Stop matching if file is directory // Size limiting if Matches and (FSmallerThan > 0) then Matches := Matches and (Data^.Size <= FSmallerThan); if Matches and (FBiggerThan > 0) then Matches := Matches and (Data^.Size >= FBiggerThan); // Date limiting if Matches and (FModifiedLast > 0) then Matches := Matches and (Data^.ModifyTime <= Now) and (Data^.ModifyTime >= Now - FModifiedLast); if Matches and (FModifiedNotLast > 0) then Matches := Matches and ((Data^.ModifyTime > Now) or (Data^.ModifyTime <= Now - FModifiedNotLast)); if Matches and (FNotModifiedAfter > 0) then Matches := Matches and (Data^.ModifyTime <= FNotModifiedAfter); if Matches and (FModifiedBetween1 > 0) and (FModifiedBetween2 > 0) then Matches := Matches and (Data^.ModifyTime >= FModifiedBetween1) and (Data^.ModifyTime <= FModifiedBetween2); // Find text in file if Matches and (Length(FStringFind) > 0) and (not (FEngine is TVFSEngine)) then begin GUIMutex.Acquire; CurrentDir := IncludeTrailingPathDelimiter(StartDir) + FileName; Matches := Matches and FindText(CurrentDir); CurrentDir := ExcludeTrailingPathDelimiter(StartDir); GUIMutex.Release; end; end else if (Length(FStringFind) > 0) then Matches := False; // When searching text in files, do not include directories // Add the record to the list if Matches then begin GUIMutex.Acquire; FList.Add(LocalList[i]); libc_free(Data^.FDisplayName); PDataItem(LocalList[i])^.FDisplayName := strdup(PChar(StartDir + FileName)); if Assigned(PDataItem(LocalList[i])^.LnkPointTo) then begin libc_free(PDataItem(LocalList[i])^.LnkPointTo); PDataItem(LocalList[i])^.LnkPointTo := nil; end; if FEngine is TVFSEngine then PDataItem(LocalList[i])^.LnkPointTo := strdup(PChar((FEngine as TVFSEngine).SavePath)); GUIMutex.Release; end; if Data^.IsDir and ((not FDontLeaveFS) or (FDontLeaveFS and FEngine.IsOnSameFS(FStartPath, StartDir + FileName))) then Rekurze(IncludeTrailingPathDelimiter(StartDir) + FileName); // Handle archives if (not Data^.IsDir) and FSearchArchives and (not (FEngine is TVFSEngine)) then begin b := False; s := WideUpperCase(Trim(Copy(FileName, LastDelimiter('.', FileName) + 1, Length(FileName) - LastDelimiter('.', FileName)))); if (Length(s) > 1) and (s[1] = '.') then Delete(s, 1, 1); if PluginList.Count > 0 then for k := 0 to PluginList.Count - 1 do begin Plugin := TVFSPlugin(PluginList[k]); if Length(Plugin.Extensions) > 0 then for j := 0 to Length(Plugin.Extensions) - 1 do if WideCompareText(Plugin.Extensions[j], s) = 0 then begin b := True; Break; end; if b then Break; end; if b then begin DebugMsg(['Found plugin ''', Plugin.VFSName, ''', trying to open the archive ''', FileName, '''']); xEngine := TVFSEngine.Create(Plugin); xEngine.ParentEngine := FEngine; xEngine.ArchiveMode := True; xEngine.SavePath := StartDir + FileName; FEngine := xEngine; VFSOpenResult := (FEngine as TVFSEngine).VFSOpenEx(IncludeTrailingPathDelimiter(StartDir) + FileName); if (VFSOpenResult = 0) and (not CancelIt) then Rekurze('/'); FEngine := FEngine.ParentEngine; if not (xEngine as TVFSEngine).VFSClose then DebugMsg(['Error closing the engine...']); xEngine.Free; end; end; if not Matches then libc_free(LocalList[i]); end; end; LocalList.Free; finally // DebugMsg(['-- Leaving directory ', StartDir]); end; end; function TSearchThread.FindText(FileName: string): boolean; const BlockSize = 65536; var fd: TEngineFileDes; i, Error, Read, Pos: integer; Buffer: PByteArray; x: boolean; begin Result := False; try Error := 0; Buffer := malloc(BlockSize); if Buffer = nil then Exit; memset(Buffer, 0, BlockSize); fd := FEngine.OpenFile(FileName, omRead, Error); if (fd = nil) or (Error <> 0) then Exit; Pos := 1; repeat Read := FEngine.ReadFile(fd, Buffer, BlockSize, Error); if Read > 0 then for i := 0 to Read - 1 do begin if FCaseSensitiveStrings then x := Buffer^[i] = byte(FStringFind[Pos]) else x := ANSIUpperCase(char(Buffer^[i])) = ANSIUpperCase(FStringFind[Pos]); if x then begin Inc(Pos); if Pos > Length(FStringFind) then begin Result := True; FEngine.CloseFile(fd); libc_free(Buffer); Exit; end; end else Pos := 1; end; DebugMsg(['Read : ', Read, ' bytes.']); if CancelIt then Break; until Read < BlockSize; FEngine.CloseFile(fd); libc_free(Buffer); except end; end; end.