1{ $Id$ }
2{               ----------------------------------------------
3                 breakpointsdlg.pp  -  Overview of breakpoints
4                ----------------------------------------------
5
6 @created(Fri Dec 14st WET 2001)
7 @lastmod($Date$)
8 @author(Shane Miller)
9 @author(Marc Weustink <marc@@dommelstein.net>)
10
11 This unit contains the Breakpoint dialog.
12
13
14 ***************************************************************************
15 *                                                                         *
16 *   This source is free software; you can redistribute it and/or modify   *
17 *   it under the terms of the GNU General Public License as published by  *
18 *   the Free Software Foundation; either version 2 of the License, or     *
19 *   (at your option) any later version.                                   *
20 *                                                                         *
21 *   This code is distributed in the hope that it will be useful, but      *
22 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
23 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
24 *   General Public License for more details.                              *
25 *                                                                         *
26 *   A copy of the GNU General Public License is available on the World    *
27 *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
28 *   obtain it by writing to the Free Software Foundation,                 *
29 *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
30 *                                                                         *
31 ***************************************************************************
32}
33
34unit BreakPointsDlg;
35
36{$mode objfpc}{$H+}
37
38interface
39
40uses
41  Classes, SysUtils, LazFileUtils, Forms, Controls, Dialogs,
42  IDEWindowIntf, DebuggerStrConst, Menus, ComCtrls, Debugger, DebuggerDlg, ActnList, MainBase,
43  IDEImagesIntf, DbgIntfDebuggerBase, DbgIntfMiscClasses, SourceEditor, MainIntf;
44
45type
46  TBreakPointsDlgState = (
47    bpdsItemsNeedUpdate
48    );
49  TBreakPointsDlgStates = set of TBreakPointsDlgState;
50
51  { TBreakPointsDlg }
52
53  TBreakPointsDlg = class(TDebuggerDlg)
54    actAddSourceBP: TAction;
55    actAddAddressBP: TAction;
56    actAddWatchPoint: TAction;
57    actGroupSetNone: TAction;
58    actGroupSetNew: TAction;
59    actShow: TAction;
60    actProperties: TAction;
61    actToggleCurrentEnable: TAction;
62    actDeleteAllInSrc: TAction;
63    actEnableSelected: TAction;
64    actDisableSelected: TAction;
65    actDeleteSelected: TAction;
66    actEnableAll: TAction;
67    actDisableAll: TAction;
68    actDeleteAll: TAction;
69    actEnableAllInSrc: TAction;
70    actDisableAllInSrc: TAction;
71    ActionList1: TActionList;
72    lvBreakPoints: TListView;
73    popGroupSep: TMenuItem;
74    popGroupSetNew: TMenuItem;
75    popGroupSetNone: TMenuItem;
76    popGroup: TMenuItem;
77    popAddWatchPoint: TMenuItem;
78    popAddAddressBP: TMenuItem;
79    N0: TMenuItem;
80    popShow: TMenuItem;
81    mnuPopup: TPopupMenu;
82    popAdd: TMenuItem;
83    popAddSourceBP: TMenuItem;
84    N1: TMenuItem; //--------------
85    popProperties: TMenuItem;
86    popEnabled: TMenuItem;
87    popDelete: TMenuItem;
88    N2: TMenuItem; //--------------
89    popDisableAll: TMenuItem;
90    popEnableAll: TMenuItem;
91    popDeleteAll: TMenuItem;
92    N3: TMenuItem; //--------------
93    popDisableAllSameSource: TMenuItem;
94    popEnableAllSameSource: TMenuItem;
95    popDeleteAllSameSource: TMenuItem;
96    ToolBar1: TToolBar;
97    ToolButtonAdd: TToolButton;
98    ToolButtonProperties: TToolButton;
99    ToolSep2: TToolButton;
100    ToolButtonEnable: TToolButton;
101    ToolButtonDisable: TToolButton;
102    ToolButtonTrash: TToolButton;
103    ToolSep1: TToolButton;
104    ToolButtonEnableAll: TToolButton;
105    ToolButtonDisableAll: TToolButton;
106    ToolButtonTrashAll: TToolButton;
107    procedure actAddAddressBPExecute(Sender: TObject);
108    procedure actAddSourceBPExecute(Sender: TObject);
109    procedure actAddWatchPointExecute(Sender: TObject);
110    procedure actDisableSelectedExecute(Sender: TObject);
111    procedure actEnableSelectedExecute(Sender: TObject);
112    procedure actGroupSetNoneExecute(Sender: TObject);
113    procedure actGroupSetNewExecute(Sender: TObject);
114    procedure actShowExecute(Sender: TObject);
115    procedure BreakpointsDlgCREATE(Sender: TObject);
116    procedure lvBreakPointsClick(Sender: TObject);
117    procedure lvBreakPointsDBLCLICK(Sender: TObject);
118    procedure lvBreakPointsSelectItem(Sender: TObject; {%H-}Item: TListItem; {%H-}Selected: Boolean);
119    procedure mnuPopupPopup(Sender: TObject);
120    procedure popDeleteAllSameSourceCLICK(Sender: TObject);
121    procedure popDisableAllSameSourceCLICK(Sender: TObject);
122    procedure popEnableAllSameSourceCLICK(Sender: TObject);
123    procedure popPropertiesClick(Sender: TObject);
124    procedure popEnabledClick(Sender: TObject);
125    procedure popDeleteClick(Sender: TObject);
126    procedure popDisableAllClick(Sender: TObject);
127    procedure popEnableAllClick(Sender: TObject);
128    procedure popDeleteAllClick(Sender: TObject);
129  private
130    FBaseDirectory: string;
131    FStates: TBreakPointsDlgStates;
132    FLockActionUpdate: Integer;
133    procedure BreakPointAdd(const {%H-}ASender: TIDEBreakPoints;
134                            const ABreakpoint: TIDEBreakPoint);
135    procedure BreakPointUpdate(const ASender: TIDEBreakPoints;
136                               const ABreakpoint: TIDEBreakPoint);
137    procedure BreakPointRemove(const {%H-}ASender: TIDEBreakPoints;
138                               const ABreakpoint: TIDEBreakPoint);
139    procedure SetBaseDirectory(const AValue: string);
140    procedure popSetGroupItemClick(Sender: TObject);
141    procedure SetGroup(const NewGroup: TIDEBreakPointGroup);
142
143    procedure UpdateItem(const AnItem: TListItem;
144                         const ABreakpoint: TIDEBreakPoint);
145    procedure UpdateAll;
146
147    procedure DeleteSelectedBreakpoints;
148    procedure JumpToCurrentBreakPoint;
149    procedure ShowProperties;
150  protected
151    procedure DoBreakPointsChanged; override;
152    procedure DoBeginUpdate; override;
153    procedure DoEndUpdate; override;
154    procedure DisableAllActions;
155    function  ColSizeGetter(AColId: Integer; var ASize: Integer): Boolean;
156    procedure ColSizeSetter(AColId: Integer; ASize: Integer);
157  public
158    constructor Create(AOwner: TComponent); override;
159  public
160    property BaseDirectory: string read FBaseDirectory write SetBaseDirectory;
161    property BreakPoints;
162  end;
163
164function GetBreakPointStateDescription(ABreakpoint: TBaseBreakpoint): string;
165function GetBreakPointActionsDescription(ABreakpoint: TBaseBreakpoint): string;
166
167
168implementation
169
170{$R *.lfm}
171uses
172  LazarusIDEStrConsts, BaseDebugManager;
173
174var
175  BreakPointDlgWindowCreator: TIDEWindowCreator;
176
177const
178  COL_BREAK_STATE     = 1;
179  COL_BREAK_FILE      = 2;
180  COL_BREAK_LINE      = 3;
181  COL_BREAK_CONDITION = 4;
182  COL_BREAK_ACTION    = 5;
183  COL_BREAK_PASS      = 6;
184  COL_BREAK_GROUP     = 7;
185  COL_WIDTHS: Array[0..6] of integer = ( 50, 150, 100,  75, 150, 100, 80);
186
187function BreakPointDlgColSizeGetter(AForm: TCustomForm; AColId: Integer; var ASize: Integer): Boolean;
188begin
189  Result := AForm is TBreakPointsDlg;
190  if Result then
191    Result := TBreakPointsDlg(AForm).ColSizeGetter(AColId, ASize);
192end;
193
194procedure BreakPointDlgColSizeSetter(AForm: TCustomForm; AColId: Integer; ASize: Integer);
195begin
196  if AForm is TBreakPointsDlg then
197    TBreakPointsDlg(AForm).ColSizeSetter(AColId, ASize);
198end;
199
200function GetBreakPointStateDescription(ABreakpoint: TBaseBreakpoint): string;
201var
202  DEBUG_STATE: array[Boolean, TValidState] of ShortString;
203begin
204  DEBUG_STATE[false, vsUnknown]:=lisOff;
205  DEBUG_STATE[false, vsValid]:=lisBPSDisabled;
206  DEBUG_STATE[false, vsInvalid]:=lisInvalidOff;
207  DEBUG_STATE[false, vsPending]:=lisInvalidOff;
208  DEBUG_STATE[true, vsUnknown]:=lisOn;
209  DEBUG_STATE[true, vsValid]:=lisBPSEnabled;
210  DEBUG_STATE[true, vsInvalid]:=lisInvalidOn;
211  DEBUG_STATE[true, vsPending]:=lisPendingOn;
212  Result:=DEBUG_STATE[ABreakpoint.Enabled,ABreakpoint.Valid];
213end;
214
215function GetBreakPointActionsDescription(ABreakpoint: TBaseBreakpoint): string;
216var
217  DEBUG_ACTION: array[TIDEBreakPointAction] of ShortString;
218  CurBreakPoint: TIDEBreakPoint;
219  Action: TIDEBreakPointAction;
220begin
221  Result := '';
222
223  DEBUG_ACTION[bpaStop]:=lisBreak;
224  DEBUG_ACTION[bpaEnableGroup]:=lisEnableGroups;
225  DEBUG_ACTION[bpaDisableGroup]:=lisDisableGroups;
226  DEBUG_ACTION[bpaLogMessage]:=lisLogMessage;
227  DEBUG_ACTION[bpaEValExpression]:=lisLogEvalExpression;
228  DEBUG_ACTION[bpaLogCallStack]:=lisLogCallStack;
229  DEBUG_ACTION[bpaTakeSnapshot]:=lisTakeSnapshot;
230
231  if ABreakpoint is TIDEBreakPoint then begin
232    CurBreakPoint:=TIDEBreakPoint(ABreakpoint);
233    for Action := Low(TIDEBreakPointAction) to High(TIDEBreakPointAction) do
234      if Action in CurBreakpoint.Actions
235      then begin
236        if Result <> '' then Result := Result + ', ';
237        Result := Result + DEBUG_ACTION[Action]
238      end;
239  end;
240end;
241
242procedure TBreakPointsDlg.BreakPointAdd(const ASender: TIDEBreakPoints;
243  const ABreakpoint: TIDEBreakPoint);
244var
245  Item: TListItem;
246  n: Integer;
247begin
248  Item := lvBreakPoints.Items.FindData(ABreakpoint);
249  if Item = nil
250  then begin
251    Item := lvBreakPoints.Items.Add;
252    Item.Data := ABreakPoint;
253    for n := 0 to 5 do
254      Item.SubItems.Add('');
255  end;
256
257  UpdateItem(Item, ABreakPoint);
258end;
259
260procedure TBreakPointsDlg.BreakPointUpdate(const ASender: TIDEBreakPoints;
261  const ABreakpoint: TIDEBreakPoint);
262var
263  Item: TListItem;
264begin
265  if ABreakpoint = nil then Exit;
266
267  Item := lvBreakPoints.Items.FindData(ABreakpoint);
268  if Item = nil
269  then BreakPointAdd(ASender, ABreakPoint)
270  else begin
271    if UpdateCount>0 then begin
272      Include(FStates,bpdsItemsNeedUpdate);
273      exit;
274    end;
275    UpdateItem(Item, ABreakPoint);
276  end;
277end;
278
279procedure TBreakPointsDlg.BreakPointRemove(const ASender: TIDEBreakPoints;
280  const ABreakpoint: TIDEBreakPoint);
281begin
282  lvBreakPoints.Items.FindData(ABreakpoint).Free;
283end;
284
285procedure TBreakPointsDlg.SetBaseDirectory(const AValue: string);
286begin
287  if FBaseDirectory=AValue then exit;
288  FBaseDirectory:=AValue;
289  UpdateAll;
290end;
291
292procedure TBreakPointsDlg.SetGroup(const NewGroup: TIDEBreakPointGroup);
293var
294  OldGroup: TIDEBreakPointGroup;
295  OldGroups: TList;
296  i: Integer;
297  PrevChoice: TModalResult;
298begin
299  PrevChoice := mrNone;
300  OldGroups := TList.Create;
301  try
302    for i := 0 to lvBreakPoints.Items.Count - 1 do
303      if lvBreakPoints.Items[i].Selected then
304      begin
305        OldGroup := TIDEBreakPoint(lvBreakPoints.Items[i].Data).Group;
306        TIDEBreakPoint(lvBreakPoints.Items[i].Data).Group := NewGroup;
307        if (OldGroup <> nil) and (OldGroup.Count = 0) and (OldGroups.IndexOf(OldGroup) < 0) then
308          OldGroups.Add(OldGroup);
309      end;
310  finally
311    while OldGroups.Count > 0 do begin
312      OldGroup := TIDEBreakPointGroup(OldGroups[0]);
313      OldGroups.Delete(0);
314      if not (PrevChoice in [mrYesToAll, mrNoToAll]) then
315      begin
316        if OldGroups.Count > 0 then
317          PrevChoice := MessageDlg(Caption, Format(lisGroupEmptyDelete + lisGroupEmptyDeleteMore,
318            [OldGroup.Name, LineEnding, OldGroups.Count]),
319            mtConfirmation, mbYesNo + [mbYesToAll, mbNoToAll], 0)
320        else
321          PrevChoice := MessageDlg(Caption, Format(lisGroupEmptyDelete,
322            [OldGroup.Name]), mtConfirmation, mbYesNo, 0);
323      end;
324      if PrevChoice in [mrYes, mrYesToAll] then
325        OldGroup.Free;
326    end;
327    OldGroups.Free;
328  end;
329end;
330
331constructor TBreakPointsDlg.Create(AOwner: TComponent);
332var
333  i: Integer;
334begin
335  inherited;
336  Name:='BreakPointsDlg';
337  BreakpointsNotification.OnAdd    := @BreakPointAdd;
338  BreakpointsNotification.OnUpdate := @BreakPointUpdate;
339  BreakpointsNotification.OnRemove := @BreakPointRemove;
340
341  ActionList1.Images := IDEImages.Images_16;
342  ToolBar1.Images := IDEImages.Images_16;
343  mnuPopup.Images := IDEImages.Images_16;
344  lvBreakPoints.SmallImages := IDEImages.Images_16;
345
346  ToolButtonAdd.ImageIndex := IDEImages.LoadImage('laz_add');
347
348  actEnableSelected.Caption := lisDbgItemEnable;
349  actEnableSelected.Hint    := lisDbgItemEnableHint;
350  actEnableSelected.ImageIndex := IDEImages.LoadImage('debugger_enable');
351
352  actDisableSelected.Caption := lisDbgItemDisable;
353  actDisableSelected.Hint    := lisDbgItemDisableHint;
354  actDisableSelected.ImageIndex := IDEImages.LoadImage('debugger_disable');
355
356  actDeleteSelected.Caption := lisBtnDelete;
357  actDeleteSelected.Hint    := lisDbgItemDeleteHint;
358  actDeleteSelected.ImageIndex := IDEImages.LoadImage('laz_delete');
359
360  actEnableAll.Caption := lisEnableAll;
361  actEnableAll.Hint    := lisDbgAllItemEnableHint;
362  actEnableAll.ImageIndex := IDEImages.LoadImage('debugger_enable_all');
363
364  actDisableAll.Caption := liswlDIsableAll;
365  actDisableAll.Hint    := lisDbgAllItemDisableHint;
366  actDisableAll.ImageIndex := IDEImages.LoadImage('debugger_disable_all');
367
368  actDeleteAll.Caption := lisDeleteAll;
369  actDeleteAll.Hint    := lisDbgAllItemDeleteHint;
370  actDeleteAll.ImageIndex := IDEImages.LoadImage('menu_clean');
371
372  actProperties.Caption:= liswlProperties;
373  actProperties.Hint := lisDbgBreakpointPropertiesHint;
374  actProperties.ImageIndex := IDEImages.LoadImage('menu_environment_options');
375
376  actToggleCurrentEnable.Caption:= lisBtnEnabled;
377
378  actEnableAllInSrc.Caption:= lisEnableAllInSameSource;
379  actDisableAllInSrc.Caption:= lisDisableAllInSameSource;
380  actDeleteAllInSrc.Caption:= lisDeleteAllInSameSource;
381  for i := low(COL_WIDTHS) to high(COL_WIDTHS) do
382    lvBreakPoints.Column[i].Width := COL_WIDTHS[i];
383
384  FLockActionUpdate := 0;
385end;
386
387procedure TBreakPointsDlg.BreakpointsDlgCREATE(Sender: TObject);
388begin
389  Caption:= lisMenuViewBreakPoints;
390  lvBreakPoints.Align:=alClient;
391  lvBreakPoints.Columns[0].Caption:= lisOIPState;
392  lvBreakPoints.Columns[1].Caption:= lisFilenameAddress;
393  lvBreakPoints.Columns[2].Caption:= lisLineLength;
394  lvBreakPoints.Columns[3].Caption:= lisCondition;
395  lvBreakPoints.Columns[4].Caption:= lisLazBuildABOAction;
396  lvBreakPoints.Columns[5].Caption:= lisPassCount;
397  lvBreakPoints.Columns[6].Caption:= lisGroup;
398  actShow.Caption := lisViewSource;
399  popAdd.Caption:= lisAdd;
400  actAddSourceBP.Caption := lisSourceBreakpoint;
401  actAddAddressBP.Caption := lisAddressBreakpoint;
402  actAddWatchPoint.Caption := lisWatchPoint;
403  popGroup.Caption := lisGroup;
404  actGroupSetNew.Caption := lisGroupSetNew;
405  actGroupSetNone.Caption := lisGroupSetNone;
406end;
407
408procedure TBreakPointsDlg.actEnableSelectedExecute(Sender: TObject);
409var
410  n: Integer;
411  Item: TListItem;
412begin
413  try
414    DisableAllActions;
415    for n := 0 to lvBreakPoints.Items.Count -1 do
416    begin
417      Item := lvBreakPoints.Items[n];
418      if Item.Selected then
419        TIDEBreakPoint(Item.Data).Enabled := True;
420    end;
421  finally
422    lvBreakPointsSelectItem(nil, nil, False);
423  end;
424end;
425
426procedure TBreakPointsDlg.actGroupSetNewExecute(Sender: TObject);
427var
428  GroupName: String;
429  NewGroup: TIDEBreakPointGroup;
430begin
431  GroupName := '';
432  if not InputQuery(Caption, lisGroupNameInput, GroupName) then Exit;
433  if GroupName = '' then
434  begin
435    if MessageDlg(Caption, lisGroupNameEmptyClearInstead,
436      mtConfirmation, mbYesNo, 0) = mrYes then Exit;
437    NewGroup := nil;
438  end
439  else begin
440    NewGroup := DebugBoss.BreakPointGroups.GetGroupByName(GroupName);
441    if NewGroup = nil then
442    begin
443      if not TIDEBreakPointGroup.CheckName(GroupName) then
444      begin
445        MessageDlg(Caption, lisGroupNameInvalid, mtError, [mbOk], 0);
446        Exit;
447      end;
448      NewGroup := TIDEBreakPointGroup(DebugBoss.BreakPointGroups.Add);
449      try
450        NewGroup.Name := GroupName;
451      except
452        NewGroup.Free;
453        raise;
454      end;
455    end
456    else if MessageDlg(Caption, Format(lisGroupAssignExisting,
457        [GroupName]), mtConfirmation, mbYesNo, 0) <> mrYes
458      then
459        Exit;
460  end;
461
462  SetGroup(NewGroup);
463end;
464
465procedure TBreakPointsDlg.actGroupSetNoneExecute(Sender: TObject);
466begin
467  SetGroup(nil);
468end;
469
470procedure TBreakPointsDlg.popSetGroupItemClick(Sender: TObject);
471var
472  Group: TIDEBreakPointGroup;
473begin
474  Group := DebugBoss.BreakPointGroups.GetGroupByName((Sender as TMenuItem).Caption);
475  if Group = nil then
476    raise Exception.CreateFmt('Group %s not found', [(Sender as TMenuItem).Caption]);
477  SetGroup(Group);
478end;
479
480procedure TBreakPointsDlg.actShowExecute(Sender: TObject);
481begin
482  JumpToCurrentBreakPoint;
483end;
484
485procedure TBreakPointsDlg.actDisableSelectedExecute(Sender: TObject);
486var
487  n: Integer;
488  Item: TListItem;
489begin
490  try
491    DisableAllActions;
492    for n := 0 to lvBreakPoints.Items.Count -1 do
493    begin
494      Item := lvBreakPoints.Items[n];
495      if Item.Selected then
496        TIDEBreakPoint(Item.Data).Enabled := False;
497    end;
498  finally
499    lvBreakPointsSelectItem(nil, nil, False);
500  end;
501end;
502
503procedure TBreakPointsDlg.actAddSourceBPExecute(Sender: TObject);
504var
505  NewBreakpoint: TIDEBreakPoint;
506  SrcEdit: TSourceEditor;
507begin
508  SrcEdit := SourceEditorManager.GetActiveSE;
509  if SrcEdit <> nil then
510    NewBreakpoint := BreakPoints.Add(SrcEdit.FileName, SrcEdit.CurrentCursorYLine, True)
511  else
512    NewBreakpoint := BreakPoints.Add('', 0, True);
513  if DebugBoss.ShowBreakPointProperties(NewBreakpoint) = mrOk then begin
514    NewBreakpoint.EndUpdate;
515    UpdateAll;
516  end
517  else
518    ReleaseRefAndNil(NewBreakpoint);
519end;
520
521procedure TBreakPointsDlg.actAddWatchPointExecute(Sender: TObject);
522var
523  NewBreakpoint: TIDEBreakPoint;
524begin
525  NewBreakpoint := BreakPoints.Add('', wpsGlobal, wpkWrite, True);
526  if DebugBoss.ShowBreakPointProperties(NewBreakpoint) = mrOk then begin
527    NewBreakpoint.EndUpdate;
528    UpdateAll;
529  end
530  else
531    ReleaseRefAndNil(NewBreakpoint);
532end;
533
534procedure TBreakPointsDlg.actAddAddressBPExecute(Sender: TObject);
535var
536  NewBreakpoint: TIDEBreakPoint;
537begin
538  NewBreakpoint := BreakPoints.Add(0, True);
539  if DebugBoss.ShowBreakPointProperties(NewBreakpoint) = mrOk then begin
540    NewBreakpoint.EndUpdate;
541    UpdateAll;
542  end
543  else
544    ReleaseRefAndNil(NewBreakpoint);
545end;
546
547procedure TBreakPointsDlg.lvBreakPointsClick(Sender: TObject);
548begin
549  lvBreakPointsSelectItem(nil, nil, False);
550end;
551
552procedure TBreakPointsDlg.lvBreakPointsDBLCLICK(Sender: TObject);
553begin
554  lvBreakPointsSelectItem(nil, nil, False);
555  JumpToCurrentBreakPoint;
556end;
557
558procedure TBreakPointsDlg.lvBreakPointsSelectItem(Sender: TObject; Item: TListItem;
559  Selected: Boolean);
560var
561  ItemSelected: Boolean;
562  SelCanEnable, SelCanDisable: Boolean;
563  AllCanEnable, AllCanDisable: Boolean;
564  CurBreakPoint: TIDEBreakPoint;
565  i: Integer;
566begin
567  if FLockActionUpdate > 0 then exit;
568
569  ItemSelected := lvBreakPoints.Selected <> nil;
570  if ItemSelected then
571    CurBreakPoint := TIDEBreakPoint(lvBreakPoints.Selected.Data)
572  else
573    CurBreakPoint := nil;
574  SelCanEnable := False;
575  SelCanDisable := False;
576  AllCanEnable := False;
577  allCanDisable := False;
578  for i := 0 to lvBreakPoints.Items.Count - 1 do begin
579    if lvBreakPoints.Items[i].Data = nil then
580      continue;
581    if lvBreakPoints.Items[i].Selected then begin
582      SelCanEnable := SelCanEnable or not TIDEBreakPoint(lvBreakPoints.Items[i].Data).Enabled;
583      SelCanDisable := SelCanDisable or TIDEBreakPoint(lvBreakPoints.Items[i].Data).Enabled;
584    end;
585    AllCanEnable := AllCanEnable or not TIDEBreakPoint(lvBreakPoints.Items[i].Data).Enabled;
586    AllCanDisable := AllCanDisable or TIDEBreakPoint(lvBreakPoints.Items[i].Data).Enabled;
587  end;
588
589  actToggleCurrentEnable.Enabled := ItemSelected;
590  actToggleCurrentEnable.Checked := (CurBreakPoint <> nil) and CurBreakPoint.Enabled;
591
592  actEnableSelected.Enabled := SelCanEnable;
593  actDisableSelected.Enabled := SelCanDisable;
594  actDeleteSelected.Enabled := ItemSelected;
595
596  actEnableAll.Enabled := AllCanEnable;
597  actDisableAll.Enabled := AllCanDisable;
598  actDeleteAll.Enabled := lvBreakPoints.Items.Count > 0;
599
600  actEnableAllInSrc.Enabled := ItemSelected;
601  actDisableAllInSrc.Enabled := ItemSelected;
602  actDeleteAllInSrc.Enabled := ItemSelected;
603
604  actProperties.Enabled := ItemSelected;
605  actShow.Enabled := ItemSelected;
606
607  popGroup.Enabled := ItemSelected;
608  actGroupSetNew.Enabled := ItemSelected;
609  actGroupSetNone.Enabled := ItemSelected;
610end;
611
612procedure TBreakPointsDlg.mnuPopupPopup(Sender: TObject);
613var
614  i: Integer;
615  MenuItem: TMenuItem;
616begin
617  for i := popGroup.Count - 1 downto popGroup.IndexOf(popGroupSep) +1 do
618    popGroup.Items[i].Free;
619  for i := 0 to DebugBoss.BreakPointGroups.Count - 1 do
620  begin
621    MenuItem := TMenuItem.Create(popGroup);
622    MenuItem.Caption := DebugBoss.BreakPointGroups[i].Name;
623    MenuItem.OnClick := @popSetGroupItemClick;
624    popGroup.Add(MenuItem);
625  end;
626  popGroupSep.Visible := DebugBoss.BreakPointGroups.Count <> 0;
627end;
628
629procedure TBreakPointsDlg.popDeleteAllSameSourceCLICK(Sender: TObject);
630var
631  n: Integer;
632  Item: TListItem;
633  CurItem: TListItem;
634  CurBreakPoint: TIDEBreakPoint;
635  Filename: String;
636begin
637  try
638    DisableAllActions;
639    CurItem:=lvBreakPoints.Selected;
640    if (CurItem=nil) then exit;
641    Filename:=TIDEBreakpoint(CurItem.Data).Source;
642    if MessageDlg(lisDeleteAllBreakpoints,
643      Format(lisDeleteAllBreakpoints2, [Filename]),
644      mtConfirmation,[mbYes,mbCancel],0)<>mrYes
645    then exit;
646    for n := lvBreakPoints.Items.Count - 1 downto 0 do
647    begin
648      Item := lvBreakPoints.Items[n];
649      CurBreakPoint:=TIDEBreakPoint(Item.Data);
650      if CompareFilenames(CurBreakPoint.Source,Filename)=0
651      then ReleaseRefAndNil(CurBreakPoint);
652    end;
653  finally
654    lvBreakPointsSelectItem(nil, nil, False);
655  end;
656end;
657
658procedure TBreakPointsDlg.popDisableAllSameSourceCLICK(Sender: TObject);
659var
660  n: Integer;
661  Item: TListItem;
662  CurItem: TListItem;
663  CurBreakPoint: TIDEBreakPoint;
664  Filename: String;
665begin
666  try
667    DisableAllActions;
668    CurItem:=lvBreakPoints.Selected;
669    if (CurItem=nil) then exit;
670    Filename:=TIDEBreakpoint(CurItem.Data).Source;
671    for n := 0 to lvBreakPoints.Items.Count - 1 do
672    begin
673      Item := lvBreakPoints.Items[n];
674      CurBreakPoint:=TIDEBreakPoint(Item.Data);
675      if CompareFilenames(CurBreakPoint.Source,Filename)=0
676      then CurBreakPoint.Enabled := False;
677    end;
678  finally
679    lvBreakPointsSelectItem(nil, nil, False);
680  end;
681end;
682
683procedure TBreakPointsDlg.popEnableAllSameSourceCLICK(Sender: TObject);
684var
685  n: Integer;
686  Item: TListItem;
687  CurItem: TListItem;
688  CurBreakPoint: TIDEBreakPoint;
689  Filename: String;
690begin
691  try
692    DisableAllActions;
693    CurItem:=lvBreakPoints.Selected;
694    if (CurItem=nil) then exit;
695    Filename:=TIDEBreakpoint(CurItem.Data).Source;
696    for n := 0 to lvBreakPoints.Items.Count - 1 do
697    begin
698      Item := lvBreakPoints.Items[n];
699      CurBreakPoint:=TIDEBreakPoint(Item.Data);
700      if CompareFilenames(CurBreakPoint.Source,Filename)=0
701      then CurBreakPoint.Enabled := True;
702    end;
703  finally
704    lvBreakPointsSelectItem(nil, nil, False);
705  end;
706end;
707
708procedure TBreakPointsDlg.popDeleteAllClick(Sender: TObject);
709var
710  n: Integer;
711begin
712  try
713    DisableAllActions;
714    if MessageDlg(lisDeleteAllBreakpoints,
715      lisDeleteAllBreakpoints,
716      mtConfirmation,[mbYes,mbCancel],0)<>mrYes
717    then exit;
718    lvBreakPoints.BeginUpdate;
719    try
720      for n := lvBreakPoints.Items.Count - 1 downto 0 do
721        TIDEBreakPoint(lvBreakPoints.Items[n].Data).ReleaseReference;
722    finally
723      lvBreakPoints.EndUpdate;
724    end;
725  finally
726    lvBreakPointsSelectItem(nil, nil, False);
727  end;
728end;
729
730procedure TBreakPointsDlg.popDeleteClick(Sender: TObject);
731begin
732  try
733    DisableAllActions;
734    DeleteSelectedBreakpoints;
735  finally
736    lvBreakPointsSelectItem(nil, nil, False);
737  end;
738end;
739
740procedure TBreakPointsDlg.popDisableAllClick(Sender: TObject);
741var
742  n: Integer;
743  Item: TListItem;
744begin
745  try
746    DisableAllActions;
747    for n := 0 to lvBreakPoints.Items.Count - 1 do
748    begin
749      Item := lvBreakPoints.Items[n];
750      if Item.Data <> nil
751      then TIDEBreakPoint(Item.Data).Enabled := False;
752    end;
753  finally
754    lvBreakPointsSelectItem(nil, nil, False);
755  end;
756end;
757
758procedure TBreakPointsDlg.popEnableAllClick(Sender: TObject);
759var
760  n: Integer;
761  Item: TListItem;
762begin
763  try
764    DisableAllActions;
765    for n := 0 to lvBreakPoints.Items.Count - 1 do
766    begin
767      Item := lvBreakPoints.Items[n];
768      if Item.Data <> nil
769      then TIDEBreakPoint(Item.Data).Enabled := True;
770    end;
771  finally
772    lvBreakPointsSelectItem(nil, nil, False);
773  end;
774end;
775
776procedure TBreakPointsDlg.popEnabledClick(Sender: TObject);
777var
778  n: Integer;
779  Item: TListItem;
780  Enable: Boolean;
781begin
782  try
783    DisableAllActions;
784    Item:=lvBreakPoints.Selected;
785    if (Item=nil) then exit;
786
787    Enable := not TIDEBreakPoint(Item.Data).Enabled;
788
789    if lvBreakPoints.SelCount > 1
790    then begin
791      for n := 0 to lvBreakPoints.Items.Count -1 do
792      begin
793        Item := lvBreakPoints.Items[n];
794        if Item.Selected then
795          TIDEBreakPoint(Item.Data).Enabled := Enable;
796      end;
797    end
798    else begin
799      TIDEBreakPoint(Item.Data).Enabled:= Enable;
800    end;
801  finally
802    lvBreakPointsSelectItem(nil, nil, False);
803  end;
804end;
805
806procedure TBreakPointsDlg.popPropertiesClick(Sender: TObject);
807begin
808  try
809    DisableAllActions;
810    ShowProperties;
811  finally
812    lvBreakPointsSelectItem(nil, nil, False);
813  end;
814end;
815
816procedure TBreakPointsDlg.DoEndUpdate;
817begin
818  inherited DoEndUpdate;
819  if bpdsItemsNeedUpdate in FStates then UpdateAll;
820  lvBreakPointsSelectItem(nil, nil, False);
821end;
822
823procedure TBreakPointsDlg.DisableAllActions;
824var
825  i: Integer;
826begin
827  for i := 0 to ActionList1.ActionCount - 1 do
828    (ActionList1.Actions[i] as TAction).Enabled := False;
829  actAddSourceBP.Enabled := True;
830  actAddAddressBP.Enabled := True;
831  actAddWatchPoint.Enabled := True;
832end;
833
834function TBreakPointsDlg.ColSizeGetter(AColId: Integer; var ASize: Integer): Boolean;
835begin
836  if (AColId - 1 >= 0) and (AColId - 1 < lvBreakPoints.ColumnCount) then begin
837    ASize := lvBreakPoints.Column[AColId - 1].Width;
838    Result := ASize <> COL_WIDTHS[AColId - 1];
839  end
840  else
841    Result := False;
842end;
843
844procedure TBreakPointsDlg.ColSizeSetter(AColId: Integer; ASize: Integer);
845begin
846  case AColId of
847    COL_BREAK_STATE:     lvBreakPoints.Column[0].Width := ASize;
848    COL_BREAK_FILE:      lvBreakPoints.Column[1].Width := ASize;
849    COL_BREAK_LINE:      lvBreakPoints.Column[2].Width := ASize;
850    COL_BREAK_CONDITION: lvBreakPoints.Column[3].Width := ASize;
851    COL_BREAK_ACTION:    lvBreakPoints.Column[4].Width := ASize;
852    COL_BREAK_PASS:      lvBreakPoints.Column[5].Width := ASize;
853    COL_BREAK_GROUP:     lvBreakPoints.Column[6].Width := ASize;
854  end;
855end;
856
857procedure TBreakPointsDlg.UpdateItem(const AnItem: TListItem;
858  const ABreakpoint: TIDEBreakPoint);
859var
860  s, Filename: String;
861begin
862  // Filename/Address
863  // Line/Length
864  // Condition
865  // Action
866  // Pass Count
867  // Group
868
869  // state
870  AnItem.Caption := GetBreakPointStateDescription(ABreakpoint);
871  AnItem.ImageIndex := GetBreakPointImageIndex(ABreakpoint);
872
873  // filename/address
874  case ABreakpoint.Kind of
875    bpkSource:
876      begin
877        Filename:=ABreakpoint.Source;
878        if BaseDirectory<>'' then
879          Filename:=CreateRelativePath(Filename,BaseDirectory);
880        AnItem.SubItems[0] := Filename;
881        // line
882        if ABreakpoint.Line > 0
883        then AnItem.SubItems[1] := IntToStr(ABreakpoint.Line)
884        else AnItem.SubItems[1] := '';
885      end;
886    bpkAddress:
887      begin
888        // todo: how to define digits count? 8 or 16 depends on gdb pointer size for platform
889        AnItem.SubItems[0] := '$' + IntToHex(ABreakpoint.Address, 8);
890      end;
891    bpkData:
892      begin
893        AnItem.SubItems[0] := ABreakpoint.WatchData;
894        case ABreakpoint.WatchScope of
895          wpsGlobal: s:= lisWatchScopeGlobal;
896          wpsLocal:  s:= lisWatchScopeLocal;
897          else s := '';
898        end;
899        s := s +' / ';
900        case ABreakpoint.WatchKind of
901          wpkRead:      s := s + lisWatchKindRead;
902          wpkReadWrite: s := s + lisWatchKindReadWrite;
903          wpkWrite:     s := s + lisWatchKindWrite;
904        end;
905        AnItem.SubItems[1] := s;
906      end;
907  end;
908
909  // expression
910  AnItem.SubItems[2] := ABreakpoint.Expression;
911
912  // actions
913  AnItem.SubItems[3]  := GetBreakPointActionsDescription(ABreakpoint);
914
915  // hitcount
916  AnItem.SubItems[4] := IntToStr(ABreakpoint.HitCount);
917
918  // group
919  if ABreakpoint.Group = nil
920  then AnItem.SubItems[5] := ''
921  else AnItem.SubItems[5] := ABreakpoint.Group.Name;
922
923  lvBreakPointsSelectItem(nil, nil, False);
924end;
925
926procedure TBreakPointsDlg.UpdateAll;
927var
928  i: Integer;
929  CurItem: TListItem;
930begin
931  if UpdateCount>0 then begin
932    Include(FStates,bpdsItemsNeedUpdate);
933    exit;
934  end;
935  Exclude(FStates,bpdsItemsNeedUpdate);
936  inc(FLockActionUpdate);
937  for i:=0 to lvBreakPoints.Items.Count-1 do begin
938    CurItem:=lvBreakPoints.Items[i];
939    UpdateItem(CurItem,TIDEBreakPoint(CurItem.Data));
940  end;
941  dec(FLockActionUpdate);
942  lvBreakPointsSelectItem(nil, nil, False);
943end;
944
945procedure TBreakPointsDlg.DeleteSelectedBreakpoints;
946var
947  Item: TListItem;
948  CurBreakPoint: TIDEBreakPoint;
949  Msg: String;
950  List: TList;
951  n, Idx: Integer;
952begin
953  Idx := lvBreakPoints.ItemIndex;
954  Item:=lvBreakPoints.Selected;
955  if Item = nil then exit;
956
957  if lvBreakPoints.SelCount = 1 then
958  begin
959    CurBreakPoint:=TIDEBreakPoint(Item.Data);
960    case CurBreakPoint.Kind of
961      bpkSource: Msg := Format(lisDeleteBreakpointAtLine,
962                           [LineEnding, CurBreakPoint.Source, CurBreakPoint.Line]);
963      bpkAddress: Msg := Format(lisDeleteBreakpointForAddress, ['$' + IntToHex(CurBreakPoint.Address, 8)]);
964      bpkData: Msg := Format(lisDeleteBreakpointForWatch, [CurBreakPoint.WatchData]);
965    end;
966  end
967  else
968    Msg := lisDeleteAllSelectedBreakpoints;
969  if MessageDlg(Msg, mtConfirmation, [mbYes,mbCancel],0) <> mrYes then exit;
970
971  if lvBreakPoints.SelCount = 1
972  then begin
973    TBaseBreakPoint(Item.Data).ReleaseReference;
974  end
975  else begin
976    List := TList.Create;
977    for n := 0 to lvBreakPoints.Items.Count - 1 do
978    begin
979      Item := lvBreakPoints.Items[n];
980      if Item.Selected
981      then List.Add(Item.Data);
982    end;
983
984    lvBreakPoints.BeginUpdate;
985    try
986      for n := 0 to List.Count - 1 do
987        TBaseBreakPoint(List[n]).ReleaseReference;
988    finally
989      lvBreakPoints.EndUpdate;
990    end;
991    List.Free;
992  end;
993  if Idx > lvBreakPoints.Items.Count - 1 then
994    Idx := lvBreakPoints.Items.Count - 1;
995  if Idx >= 0 then
996    lvBreakPoints.ItemIndex := Idx;
997end;
998
999procedure TBreakPointsDlg.JumpToCurrentBreakPoint;
1000var
1001  CurItem: TListItem;
1002  CurBreakPoint: TIDEBreakPoint;
1003begin
1004  CurItem:=lvBreakPoints.Selected;
1005  if CurItem=nil then exit;
1006  CurBreakPoint:=TIDEBreakPoint(CurItem.Data);
1007  if CurBreakPoint.Kind = bpkSource then
1008    MainIDE.DoJumpToSourcePosition(CurBreakPoint.Source, 0, CurBreakPoint.Line, 0,
1009      [jfAddJumpPoint, jfFocusEditor, jfMarkLine, jfSearchVirtualFullPath]);
1010end;
1011
1012procedure TBreakPointsDlg.ShowProperties;
1013var
1014  Item: TListItem;
1015  CurBreakPoint: TIDEBreakPoint;
1016begin
1017  Item:=lvBreakPoints.Selected;
1018  if Item = nil then exit;
1019
1020  CurBreakPoint:=TIDEBreakPoint(Item.Data);
1021
1022  DebugBoss.ShowBreakPointProperties(CurBreakPoint);
1023end;
1024
1025procedure TBreakPointsDlg.DoBreakPointsChanged;
1026var
1027  i: Integer;
1028begin
1029  lvBreakPoints.Items.Clear;
1030  if BreakPoints <> nil
1031  then begin
1032    for i:=0 to BreakPoints.Count-1 do
1033      BreakPointUpdate(BreakPoints, BreakPoints.Items[i]);
1034  end;
1035end;
1036
1037procedure TBreakPointsDlg.DoBeginUpdate;
1038begin
1039  inherited DoBeginUpdate;
1040  DisableAllActions;
1041end;
1042
1043initialization
1044
1045  BreakPointDlgWindowCreator := IDEWindowCreators.Add(DebugDialogNames[ddtBreakpoints]);
1046  BreakPointDlgWindowCreator.OnCreateFormProc := @CreateDebugDialog;
1047  BreakPointDlgWindowCreator.OnSetDividerSize := @BreakPointDlgColSizeSetter;
1048  BreakPointDlgWindowCreator.OnGetDividerSize := @BreakPointDlgColSizeGetter;
1049  BreakPointDlgWindowCreator.DividerTemplate.Add('ColumnBreakState',     COL_BREAK_STATE,     @drsColWidthState);
1050  BreakPointDlgWindowCreator.DividerTemplate.Add('ColumnBreakFile',      COL_BREAK_FILE,      @drsBreakPointColWidthFile);
1051  BreakPointDlgWindowCreator.DividerTemplate.Add('ColumnBreakLine',      COL_BREAK_LINE,      @drsBreakPointColWidthLine);
1052  BreakPointDlgWindowCreator.DividerTemplate.Add('ColumnBreakCondition', COL_BREAK_CONDITION, @drsBreakPointColWidthCondition);
1053  BreakPointDlgWindowCreator.DividerTemplate.Add('ColumnBreakAction',    COL_BREAK_ACTION,    @drsBreakPointColWidthAction);
1054  BreakPointDlgWindowCreator.DividerTemplate.Add('ColumnBreakPassCnt',   COL_BREAK_PASS,      @drsBreakPointColWidthPassCount);
1055  BreakPointDlgWindowCreator.DividerTemplate.Add('ColumnBreakGroup',     COL_BREAK_GROUP,     @drsBreakPointColWidthGroup);
1056  BreakPointDlgWindowCreator.CreateSimpleLayout;
1057
1058end.
1059
1060