1 unit EditorFileManager;
2 
3 {$mode objfpc}{$H+}
4 
5 interface
6 
7 uses
8   Classes, sysutils, Forms, Controls, CheckLst, ButtonPanel, StdCtrls, Buttons,
9   ExtCtrls, Menus, LCLProc, LCLType, IDEImagesIntf, LazIDEIntf, IDEHelpIntf,
10   SrcEditorIntf, IDEWindowIntf, SourceEditor, LazarusIDEStrConsts,
11   ListFilterEdit, IDEOptionDefs;
12 
13 type
14 
15   { TEditorFileManagerForm }
16 
17   TEditorFileManagerForm = class(TForm)
18     ActivateMenuItem: TMenuItem;
19     FileCountLabel: TLabel;
20     MoveDownBtn: TSpeedButton;
21     MoveUpBtn: TSpeedButton;
22     FilterPanel: TPanel;
23     OpenButton: TSpeedButton;
24     SaveCheckedButton: TBitBtn;
25     ButtonPanel1: TButtonPanel;
26     CloseCheckedButton: TBitBtn;
27     CloseMenuItem: TMenuItem;
28     Panel1: TPanel;
29     PopupMenu1: TPopupMenu;
30     CheckAllCheckBox: TCheckBox;
31     CheckListBox1: TCheckListBox;
32     FilterEdit: TListFilterEdit;
33     SortAlphabeticallyButton: TSpeedButton;
34     procedure ActivateMenuItemClick(Sender: TObject);
35     procedure CheckListBox1DblClick(Sender: TObject);
36     procedure CheckListBox1KeyDown(Sender: TObject; var Key: Word;
37       Shift: TShiftState);
38     procedure CheckListBox1KeyPress(Sender: TObject; var Key: char);
39     procedure CloseButtonClick(Sender: TObject);
40     procedure DoEditorsChanged(Sender: TObject);
41     procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
42     procedure HelpButtonClick(Sender: TObject);
43     procedure MoveDownBtnClick(Sender: TObject);
44     procedure MoveUpBtnClick(Sender: TObject);
45     procedure CheckListBox1Click(Sender: TObject);
46     procedure CheckListBox1ItemClick(Sender: TObject; Index: integer);
47     procedure CloseCheckedButtonClick(Sender: TObject);
48     procedure PopupMenu1Popup(Sender: TObject);
49     procedure SaveCheckedButtonClick(Sender: TObject);
50     procedure CloseMenuItemClick(Sender: TObject);
51     procedure FormCreate(Sender: TObject);
52     procedure ActivateButtonClick(Sender: TObject);
53     procedure CheckAllCheckBoxClick(Sender: TObject);
54     procedure SortAlphabeticallyButtonClick(Sender: TObject);
55   private
56     FSortAlphabetically: boolean;
57     procedure CloseListItem(ListIndex: integer);
58     procedure PopulateList;
SrcEditorByListItemnull59     function SrcEditorByListItem(ListIndex: integer): TSourceEditor;
60     procedure SetSortAlphabetically(AValue: boolean);
61     procedure UpdateCheckAllCaption;
62     procedure UpdateButtons;
63     procedure UpdateMoveButtons(ListIndex: integer);
64   public
65     destructor Destroy; override;
66     property SortAlphabetically: boolean read FSortAlphabetically write SetSortAlphabetically;
67   end;
68 
69 var
70   EditorFileManagerForm: TEditorFileManagerForm;
71 
72 procedure ShowEditorFileManagerForm(State: TIWGetFormState = iwgfShowOnTop);
73 
74 implementation
75 
76 {$R *.lfm}
77 
78 procedure ShowEditorFileManagerForm(State: TIWGetFormState);
79 begin
80   if EditorFileManagerForm = Nil then
81     IDEWindowCreators.CreateForm(EditorFileManagerForm,TEditorFileManagerForm,
82        State=iwgfDisabled,LazarusIDE.OwningComponent)
83   else if State=iwgfDisabled then
84     EditorFileManagerForm.DisableAlign;
85   if State>=iwgfShow then
86     IDEWindowCreators.ShowForm(EditorFileManagerForm,State=iwgfShowOnTop);
87 end;
88 
89 { TEditorFileManagerForm }
90 
91 procedure TEditorFileManagerForm.FormCreate(Sender: TObject);
92 begin
93   Name := NonModalIDEWindowNames[nmiwEditorFileManager];
94 
95   SourceEditorManager.RegisterChangeEvent(semEditorCreate, @DoEditorsChanged);
96   SourceEditorManager.RegisterChangeEvent(semEditorDestroy, @DoEditorsChanged);
97 
98   PopulateList;          // Populate the list with all open editor file names
99   // Captions
100   Caption:=lisSourceEditorWindowManager;
101   ActivateMenuItem.Caption:=lisActivate;
102   CloseMenuItem.Caption:=lisClose;
103   CheckAllCheckBox.Caption:=lisCheckAll;
104   SaveCheckedButton.Caption:=lisSaveAllChecked;
105   CloseCheckedButton.Caption:=lisCloseAllChecked;
106   MoveUpBtn.Hint:=lisMoveSelectedUp;
107   MoveDownBtn.Hint:=lisMoveSelectedDown;
108   // Icons
109   PopupMenu1.Images:=IDEImages.Images_16;
110   ActivateMenuItem.ImageIndex:=IDEImages.LoadImage('laz_open');
111   CloseMenuItem.ImageIndex:=IDEImages.LoadImage('menu_close');
112   IDEImages.AssignImage(CloseCheckedButton, 'menu_close_all');
113   IDEImages.AssignImage(SaveCheckedButton, 'menu_save_all');
114   IDEImages.AssignImage(MoveUpBtn, 'arrow_up');
115   IDEImages.AssignImage(MoveDownBtn, 'arrow_down');
116   // Buttons on FilterPanel
117   IDEImages.AssignImage(OpenButton, 'laz_open');
118   OpenButton.Hint:=lisActivateSelected;
119   SortAlphabeticallyButton.Hint:=lisPESortFilesAlphabetically;
120   IDEImages.AssignImage(SortAlphabeticallyButton, 'pkg_sortalphabetically');
121 end;
122 
123 procedure TEditorFileManagerForm.CheckListBox1Click(Sender: TObject);
124 var
125   clb: TCheckListBox;
126 begin
127   clb:=Sender as TCheckListBox;
128   // Enable Activate when there is a selected item.
129   OpenButton.Enabled:=clb.SelCount>0;
130   UpdateMoveButtons(clb.ItemIndex);
131 end;
132 
133 procedure TEditorFileManagerForm.CheckListBox1ItemClick(Sender: TObject; Index: integer);
134 var
135   clb: TCheckListBox;
136   i: Integer;
137   HasChecked: Boolean;
138 begin
139   clb:=Sender as TCheckListBox;
140   // Notify the filter edit that item's checked state has changed.
141   if Index>-1 then
142     FilterEdit.ItemWasClicked(clb.Items[Index], clb.Checked[Index]);
143   // Enable save and close buttons when there are checked items.
144   HasChecked:=False;
145   for i:=clb.Count-1 downto 0 do
146     if clb.Checked[i] then begin
147       HasChecked:=True;
148       Break;
149     end;
150   SaveCheckedButton.Enabled:=HasChecked;
151   CloseCheckedButton.Enabled:=HasChecked;
152   CloseMenuItem.Enabled:=HasChecked;
153   CheckAllCheckBox.Enabled:=clb.Count>0;
154   // If all items were unchecked, change CheckAllCheckBox state.
155   if CheckAllCheckBox.Checked and not HasChecked then begin
156     CheckAllCheckBox.Checked:=HasChecked;
157     UpdateCheckAllCaption;
158   end;
159   CheckListBox1Click(CheckListBox1); // Call also OnClick handler for other controls.
160 end;
161 
162 procedure TEditorFileManagerForm.CheckAllCheckBoxClick(Sender: TObject);
163 var
164   cb: TCheckBox;
165   i: Integer;
166 begin
167   cb:=Sender as TCheckBox;
168   UpdateCheckAllCaption;
169   // Set / reset all CheckListBox1 items.
170   for i:=0 to CheckListBox1.Count-1 do begin
171     if CheckListBox1.Checked[i]<>cb.Checked then begin
172       CheckListBox1.Checked[i]:=cb.Checked;
173       FilterEdit.ItemWasClicked(CheckListBox1.Items[i], cb.Checked); // Notify the filter
174     end;
175   end;
176   CheckListBox1ItemClick(CheckListBox1, CheckListBox1.Count-1);
177 end;
178 
179 procedure TEditorFileManagerForm.SortAlphabeticallyButtonClick(Sender: TObject);
180 begin
181   SortAlphabetically:=SortAlphabeticallyButton.Down;
182 end;
183 
184 procedure TEditorFileManagerForm.SaveCheckedButtonClick(Sender: TObject);
185 var
186   i: Integer;
187   SrcEdit: TSourceEditor;
188 begin
189   for i:=CheckListBox1.Count-1 downto 0 do
190     if CheckListBox1.Checked[i] then begin
191       SrcEdit:=SrcEditorByListItem(i);
192       Assert(Assigned(SrcEdit), 'TEditorFileManagerForm.SaveCheckedButtonClick: SrcEdit is not assigned.');
193       if (not SrcEdit.CodeBuffer.IsVirtual) and (LazarusIDE.DoSaveEditorFile(SrcEdit, []) <> mrOk) then
194         DebugLn(['TSourceNotebook.EncodingClicked LazarusIDE.DoSaveEditorFile failed']);
195     end;
196 end;
197 
198 procedure TEditorFileManagerForm.CloseCheckedButtonClick(Sender: TObject);
199 var
200   i: Integer;
201 begin
202   for i:=CheckListBox1.Count-1 downto 0 do
203     if CheckListBox1.Checked[i] then
204       CloseListItem(i);
205   PopulateList;
206   UpdateButtons;
207 end;
208 
209 procedure TEditorFileManagerForm.PopupMenu1Popup(Sender: TObject);
210 var
211   HasSelected: Boolean;
212 begin
213   HasSelected:=CheckListBox1.SelCount>0;
214   ActivateMenuItem.Enabled:=HasSelected;
215   CloseMenuItem.Enabled:=HasSelected;
216 end;
217 
218 procedure TEditorFileManagerForm.CloseMenuItemClick(Sender: TObject);
219 begin
220   CloseListItem(CheckListBox1.ItemIndex);
221   PopulateList;
222   UpdateButtons;
223 end;
224 
225 procedure TEditorFileManagerForm.ActivateMenuItemClick(Sender: TObject);
226 begin
227   ActivateButtonClick(nil);
228 end;
229 
230 procedure TEditorFileManagerForm.CheckListBox1DblClick(Sender: TObject);
231 begin
232   ActivateButtonClick(nil);
233 end;
234 
235 procedure TEditorFileManagerForm.CheckListBox1KeyDown(Sender: TObject;
236   var Key: Word; Shift: TShiftState);
237 begin
238   if (ssCtrl in Shift ) and ((Key = VK_UP) or (Key = VK_DOWN)) then begin
239     if Key = VK_UP then
240       MoveUpBtnClick(nil)
241     else
242       MoveDownBtnClick(nil);
243     Key:=VK_UNKNOWN;
244   end;
245 end;
246 
247 procedure TEditorFileManagerForm.CheckListBox1KeyPress(Sender: TObject; var Key: char);
248 begin
249   if Key = char(VK_RETURN) then
250     ActivateButtonClick(nil);
251 end;
252 
253 procedure TEditorFileManagerForm.CloseButtonClick(Sender: TObject);
254 begin
255   Close;
256 end;
257 
258 procedure TEditorFileManagerForm.DoEditorsChanged(Sender: TObject);
259 begin
260   PopulateList;
261 end;
262 
263 procedure TEditorFileManagerForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
264 begin
265   if (Key = VK_ESCAPE) and (Shift = []) then
266     Close;
267 end;
268 
269 procedure TEditorFileManagerForm.HelpButtonClick(Sender: TObject);
270 begin
271   LazarusHelp.ShowHelpForIDEControl(Self);
272 end;
273 
274 procedure TEditorFileManagerForm.MoveDownBtnClick(Sender: TObject);
275 var
276   SrcEdit: TSourceEditor;
277   i: Integer;
278 begin
279   i:=CheckListBox1.ItemIndex;
280   if (i>-1) and (i<CheckListBox1.Items.Count-1)
281   and (FilterEdit.Filter='') and not SortAlphabetically then begin
282     SrcEdit:=SrcEditorByListItem(i);
283     Assert(Assigned(SrcEdit), 'TEditorFileManagerForm.MoveDownBtnClick: SrcEdit is not assigned.');
284     if SrcEdit.PageIndex < SrcEdit.SourceNotebook.PageCount-1 then begin
285       // First move the source editor tab
286       SrcEdit.SourceNotebook.MoveEditor(SrcEdit.PageIndex, SrcEdit.PageIndex+1);
287       // Then switch the list items
288       FilterEdit.Items.Exchange(i, i+1);
289       FilterEdit.InvalidateFilter;
290       UpdateMoveButtons(i+1);
291     end;
292   end;
293 end;
294 
295 procedure TEditorFileManagerForm.MoveUpBtnClick(Sender: TObject);
296 var
297   SrcEdit: TSourceEditor;
298   i: Integer;
299 begin
300   i := CheckListBox1.ItemIndex;
301   if (i > 0) and (FilterEdit.Filter='') and not SortAlphabetically then begin
302     SrcEdit:=SrcEditorByListItem(i);
303     Assert(Assigned(SrcEdit), 'TEditorFileManagerForm.MoveUpBtnClick: SrcEdit is not assigned.');
304     if SrcEdit.PageIndex > 0 then begin
305       // First move the source editor tab
306       SrcEdit.SourceNotebook.MoveEditor(SrcEdit.PageIndex, SrcEdit.PageIndex-1);
307       // Then switch the list items
308       FilterEdit.Items.Exchange(i, i-1);
309       FilterEdit.InvalidateFilter;
310       UpdateMoveButtons(i-1);
311     end;
312   end;
313 end;
314 
315 procedure TEditorFileManagerForm.ActivateButtonClick(Sender: TObject);
316 var
317   i: Integer;
318   SrcEdit: TSourceEditor;
319 begin
320   for i:=0 to CheckListBox1.Count-1 do
321     if CheckListBox1.Selected[i] then begin       // Find first selected.
322       SrcEdit:=SrcEditorByListItem(CheckListBox1.ItemIndex);
323       Assert(Assigned(SrcEdit), 'TEditorFileManagerForm.ActivateButtonClick: SrcEdit is not assigned.');
324       SrcEdit.Activate;
325       Break;
326     end;
327 end;
328 
329 // Private methods
330 
331 procedure TEditorFileManagerForm.CloseListItem(ListIndex: integer);
332 var
333   SrcEdit: TSourceEditor;
334 begin
335   SrcEdit:=SrcEditorByListItem(ListIndex);
336   Assert(Assigned(SrcEdit), 'TEditorFileManagerForm.CloseListItem: SrcEdit is not assigned.');
337   LazarusIDE.DoCloseEditorFile(SrcEdit, [cfSaveFirst]);
338 end;
339 
340 procedure TEditorFileManagerForm.PopulateList;
341 // Populate the list with all open editor file names
342 var
343   SrcEdit: TSourceEditor;
344   i, j: Integer;
345   sw: TSourceNotebook;
346   Modi: String;
347 begin
348   FilterEdit.Items.Clear;
349   with SourceEditorManager do
350     for i:=0 to SourceWindowCount-1 do begin
351       sw:=SourceWindows[i];
352       Assert(sw.PageCount=sw.EditorCount, 'sw.PageCount<>sw.EditorCount');
353       for j:=0 to sw.EditorCount-1 do begin
354         SrcEdit:=sw.FindSourceEditorWithPageIndex(j);
355         if SrcEdit.Modified then
356           Modi:='* '
357         else
358           Modi:='';
359         FilterEdit.Items.Add(Modi+SrcEdit.FileName);
360       end;
361     end;
362   FilterEdit.InvalidateFilter;
363   FileCountLabel.Caption:=Format(dlgFiles,[IntToStr(SourceEditorManager.SourceEditorCount)]);
364 end;
365 
SrcEditorByListItemnull366 function TEditorFileManagerForm.SrcEditorByListItem(ListIndex: integer): TSourceEditor;
367 var
368   s: String;
369 begin
370   s:=CheckListBox1.Items[ListIndex];
371   if (s<>'') and (s[1]='*') then        // Modified indicator
372     delete(s, 1, 2);
373   Result:=SourceEditorManager.SourceEditorIntfWithFilename(s);
374 end;
375 
376 procedure TEditorFileManagerForm.SetSortAlphabetically(AValue: boolean);
377 begin
378   if FSortAlphabetically=AValue then exit;
379   FSortAlphabetically:=AValue;
380   SortAlphabeticallyButton.Down:=FSortAlphabetically;
381   FilterEdit.SortData:=FSortAlphabetically;
382   FilterEdit.InvalidateFilter;
383 end;
384 
385 procedure TEditorFileManagerForm.UpdateCheckAllCaption;
386 // Caption text: check all / uncheck all
387 begin
388   if CheckAllCheckBox.Checked then
389     CheckAllCheckBox.Caption:=lisUncheckAll
390   else
391     CheckAllCheckBox.Caption:=lisCheckAll;
392 end;
393 
394 procedure TEditorFileManagerForm.UpdateButtons;
395 // Update the filter and buttons. Reuse event handlers for it.
396 begin
397   FilterEdit.InvalidateFilter;
398   CheckListBox1ItemClick(CheckListBox1, CheckListBox1.Count-1);
399 end;
400 
401 procedure TEditorFileManagerForm.UpdateMoveButtons(ListIndex: integer);
402 var
403   SrcEdit: TSourceEditor;
404   UpEnabled, DownEnabled: Boolean;
405 begin
406   UpEnabled:=False;
407   DownEnabled:=False;
408   if (ListIndex>-1) and (ListIndex<CheckListBox1.Items.Count)
409   and (FilterEdit.Filter='') and not SortAlphabetically then begin
410     //DebugLn(['TEditorFileManagerForm.UpdateMoveButtons: Filename', CheckListBox1.Items[ListIndex], ', ListIndex:', ListIndex]);
411     SrcEdit:=SrcEditorByListItem(ListIndex);
412     if Assigned(SrcEdit) then begin
413       DownEnabled:=(ListIndex<CheckListBox1.Items.Count-1)
414                and (SrcEdit.PageIndex<SrcEdit.SourceNotebook.PageCount-1);
415       UpEnabled:=(ListIndex>0) and (SrcEdit.PageIndex>0);
416     end;
417   end;
418   MoveUpBtn.Enabled:=UpEnabled;
419   MoveDownBtn.Enabled:=DownEnabled;
420 end;
421 
422 destructor TEditorFileManagerForm.Destroy;
423 begin
424   if SourceEditorManager <> nil then begin
425     SourceEditorManager.UnRegisterChangeEvent(semEditorCreate, @DoEditorsChanged);
426     SourceEditorManager.UnRegisterChangeEvent(semEditorDestroy, @DoEditorsChanged);
427   end;
428   inherited Destroy;
429 end;
430 
431 end.
432 
433