1{ $Id: watchesdlg.pp 60850 2019-04-06 08:12:17Z mattias $ } 2{ ---------------------------------------------- 3 watchesdlg.pp - Overview of watches 4 ---------------------------------------------- 5 6 @created(Fri Dec 14st WET 2001) 7 @lastmod($Date: 2019-04-06 10:12:17 +0200 (Sa, 06 Apr 2019) $) 8 @author(Shane Miller) 9 @author(Marc Weustink <marc@@dommelstein.net>) 10 11 This unit contains the watches 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 WatchesDlg; 35 36{$mode objfpc}{$H+} 37 38interface 39 40uses 41 Classes, Forms, Controls, math, sysutils, LazLoggerBase, Clipbrd, 42 IDEWindowIntf, Menus, ComCtrls, ActnList, ExtCtrls, StdCtrls, LCLType, 43 IDEImagesIntf, LazarusIDEStrConsts, DebuggerStrConst, Debugger, DebuggerDlg, 44 DbgIntfBaseTypes, DbgIntfDebuggerBase, DbgIntfMiscClasses, SynEdit, 45 BaseDebugManager; 46 47type 48 49 TWatchesDlgStateFlags = set of ( 50 wdsfUpdating, 51 wdsfNeedDeleteAll, 52 wdsfNeedDeleteCurrent 53 ); 54 55 { TWatchesDlg } 56 57 TWatchesDlg = class(TDebuggerDlg) 58 actDeleteAll: TAction; 59 actDeleteSelected: TAction; 60 actDisableAll: TAction; 61 actDisableSelected: TAction; 62 actEnableAll: TAction; 63 actEnableSelected: TAction; 64 actAddWatch: TAction; 65 actAddWatchPoint: TAction; 66 actCopyName: TAction; 67 actCopyValue: TAction; 68 actInspect: TAction; 69 actEvaluate: TAction; 70 actToggleInspectSite: TAction; 71 actToggleCurrentEnable: TAction; 72 actPower: TAction; 73 ActionList1: TActionList; 74 actProperties: TAction; 75 InspectLabel: TLabel; 76 lvWatches: TListView; 77 InspectMemo: TMemo; 78 MenuItem1: TMenuItem; 79 nbInspect: TNotebook; 80 Page1: TPage; 81 popInspect: TMenuItem; 82 popEvaluate: TMenuItem; 83 popCopyName: TMenuItem; 84 popCopyValue: TMenuItem; 85 N3: TMenuItem; 86 popAddWatchPoint: TMenuItem; 87 mnuPopup: TPopupMenu; 88 popAdd: TMenuItem; 89 N1: TMenuItem; //-------------- 90 popProperties: TMenuItem; 91 popEnabled: TMenuItem; 92 popDelete: TMenuItem; 93 N2: TMenuItem; //-------------- 94 popDisableAll: TMenuItem; 95 popEnableAll: TMenuItem; 96 popDeleteAll: TMenuItem; 97 InspectSplitter: TSplitter; 98 ToolBar1: TToolBar; 99 ToolButtonInspectView: TToolButton; 100 ToolButtonProperties: TToolButton; 101 ToolButtonAdd: TToolButton; 102 ToolButtonPower: TToolButton; 103 ToolButton10: TToolButton; 104 ToolButton2: TToolButton; 105 ToolButtonEnable: TToolButton; 106 ToolButtonDisable: TToolButton; 107 ToolButtonTrash: TToolButton; 108 ToolButton6: TToolButton; 109 ToolButtonEnableAll: TToolButton; 110 ToolButtonDisableAll: TToolButton; 111 ToolButtonTrashAll: TToolButton; 112 procedure actAddWatchPointExecute(Sender: TObject); 113 procedure actCopyNameExecute(Sender: TObject); 114 procedure actCopyValueExecute(Sender: TObject); 115 procedure actDisableSelectedExecute(Sender: TObject); 116 procedure actEnableSelectedExecute(Sender: TObject); 117 procedure actEvaluateExecute(Sender: TObject); 118 procedure actInspectExecute(Sender: TObject); 119 procedure actPowerExecute(Sender: TObject); 120 procedure actToggleInspectSiteExecute(Sender: TObject); 121 procedure FormDestroy(Sender: TObject); 122 procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 123 procedure FormShow(Sender: TObject); 124 procedure lvWatchesDblClick(Sender: TObject); 125 procedure lvWatchesDragDrop(Sender, Source: TObject; {%H-}X, {%H-}Y: Integer); 126 procedure lvWatchesDragOver(Sender, Source: TObject; {%H-}X, {%H-}Y: Integer; 127 {%H-}State: TDragState; var Accept: Boolean); 128 procedure lvWatchesSelectItem(Sender: TObject; {%H-}AItem: TListItem; {%H-}Selected: Boolean); 129 procedure popAddClick(Sender: TObject); 130 procedure popPropertiesClick(Sender: TObject); 131 procedure popEnabledClick(Sender: TObject); 132 procedure popDeleteClick(Sender: TObject); 133 procedure popDisableAllClick(Sender: TObject); 134 procedure popEnableAllClick(Sender: TObject); 135 procedure popDeleteAllClick(Sender: TObject); 136 private 137 function GetWatches: TIdeWatches; 138 procedure ContextChanged(Sender: TObject); 139 procedure SnapshotChanged(Sender: TObject); 140 private 141 FWatchesInView: TIdeWatches; 142 FPowerImgIdx, FPowerImgIdxGrey: Integer; 143 FUpdateAllNeeded, FUpdatingAll: Boolean; 144 FStateFlags: TWatchesDlgStateFlags; 145 function GetSelected: TCurrentWatch; 146 function GetThreadId: Integer; 147 function GetSelectedThreads(Snap: TSnapshot): TIdeThreads; 148 function GetStackframe: Integer; 149 procedure WatchAdd(const {%H-}ASender: TIdeWatches; const AWatch: TIdeWatch); 150 procedure WatchUpdate(const ASender: TIdeWatches; const AWatch: TIdeWatch); 151 procedure WatchRemove(const {%H-}ASender: TIdeWatches; const AWatch: TIdeWatch); 152 153 procedure UpdateInspectPane; 154 procedure UpdateItem(const AItem: TListItem; const AWatch: TIdeWatch); 155 procedure UpdateAll; 156 procedure DisableAllActions; 157 function GetSelectedSnapshot: TSnapshot; 158 property Watches: TIdeWatches read GetWatches; 159 protected 160 procedure DoEndUpdate; override; 161 procedure DoWatchesChanged; override; 162 function ColSizeGetter(AColId: Integer; var ASize: Integer): Boolean; 163 procedure ColSizeSetter(AColId: Integer; ASize: Integer); 164 public 165 constructor Create(AOwner: TComponent); override; 166 property WatchesMonitor; 167 property ThreadsMonitor; 168 property CallStackMonitor; 169 property BreakPoints; 170 property SnapshotManager; 171 end; 172 173 174implementation 175 176{$R *.lfm} 177 178var 179 DBG_DATA_MONITORS: PLazLoggerLogGroup; 180 WatchWindowCreator: TIDEWindowCreator; 181const 182 COL_WATCH_EXPR = 1; 183 COL_WATCH_VALUE = 2; 184 COL_SPLITTER_INSPECT = 3; 185 COL_WIDTHS: Array[0..2] of integer = ( 100, 200, 200); 186 187function WatchesDlgColSizeGetter(AForm: TCustomForm; AColId: Integer; var ASize: Integer): Boolean; 188begin 189 Result := AForm is TWatchesDlg; 190 if Result then 191 Result := TWatchesDlg(AForm).ColSizeGetter(AColId, ASize); 192end; 193 194procedure WatchesDlgColSizeSetter(AForm: TCustomForm; AColId: Integer; ASize: Integer); 195begin 196 if AForm is TWatchesDlg then 197 TWatchesDlg(AForm).ColSizeSetter(AColId, ASize); 198end; 199 200{ TWatchesDlg } 201 202constructor TWatchesDlg.Create(AOwner: TComponent); 203begin 204 inherited Create(AOwner); 205 FWatchesInView := nil; 206 FStateFlags := []; 207 nbInspect.Visible := False; 208 209 WatchesNotification.OnAdd := @WatchAdd; 210 WatchesNotification.OnUpdate := @WatchUpdate; 211 WatchesNotification.OnRemove := @WatchRemove; 212 ThreadsNotification.OnCurrent := @ContextChanged; 213 CallstackNotification.OnCurrent := @ContextChanged; 214 SnapshotNotification.OnCurrent := @SnapshotChanged; 215 216 ActionList1.Images := IDEImages.Images_16; 217 ToolBar1.Images := IDEImages.Images_16; 218 mnuPopup.Images := IDEImages.Images_16; 219 220 FPowerImgIdx := IDEImages.LoadImage('debugger_power'); 221 FPowerImgIdxGrey := IDEImages.LoadImage('debugger_power_grey'); 222 actPower.ImageIndex := FPowerImgIdx; 223 actPower.Caption := lisDbgWinPower; 224 actPower.Hint := lisDbgWinPowerHint; 225 226 actAddWatch.Caption:=lisBtnAdd; 227 actAddWatch.ImageIndex := IDEImages.LoadImage('laz_add'); 228 229 actToggleCurrentEnable.Caption := lisBtnEnabled; 230 231 actEnableSelected.Caption := lisDbgItemEnable; 232 actEnableSelected.Hint := lisDbgItemEnableHint; 233 actEnableSelected.ImageIndex := IDEImages.LoadImage('debugger_enable'); 234 235 actDisableSelected.Caption := lisDbgItemDisable; 236 actDisableSelected.Hint := lisDbgItemDisableHint; 237 actDisableSelected.ImageIndex := IDEImages.LoadImage('debugger_disable'); 238 239 actDeleteSelected.Caption := lisBtnDelete; 240 actDeleteSelected.Hint := lisDbgItemDeleteHint; 241 actDeleteSelected.ImageIndex := IDEImages.LoadImage('laz_delete'); 242 243 actEnableAll.Caption := liswlENableAll; //lisDbgAllItemEnable; 244 actEnableAll.Hint := lisDbgAllItemEnableHint; 245 actEnableAll.ImageIndex := IDEImages.LoadImage('debugger_enable_all'); 246 247 actDisableAll.Caption := liswlDIsableAll; //lisDbgAllItemDisable; 248 actDisableAll.Hint := lisDbgAllItemDisableHint; 249 actDisableAll.ImageIndex := IDEImages.LoadImage('debugger_disable_all'); 250 251 actDeleteAll.Caption := liswlDeLeteAll; //lisDbgAllItemDelete; 252 actDeleteAll.Hint := lisDbgAllItemDeleteHint; 253 actDeleteAll.ImageIndex := IDEImages.LoadImage('menu_clean'); 254 255 actProperties.Caption:= liswlProperties; 256 actProperties.ImageIndex := IDEImages.LoadImage('menu_environment_options'); 257 258 actToggleInspectSite.Caption:= liswlInspectPane; 259 actToggleInspectSite.ImageIndex := IDEImages.LoadImage('debugger_inspect'); 260 261 actAddWatchPoint.Caption := lisWatchToWatchPoint; 262 263 actCopyName.Caption := lisLocalsDlgCopyName; 264 actCopyValue.Caption := lisLocalsDlgCopyValue; 265 266 actInspect.Caption := lisInspect; 267 actEvaluate.Caption := lisEvaluateModify; 268 269 Caption:=liswlWatchList; 270 271 lvWatches.Columns[0].Caption:=liswlExpression; 272 lvWatches.Columns[1].Caption:=dlgValueColor; 273 274 lvWatches.Column[0].Width := COL_WIDTHS[COL_WATCH_EXPR]; 275 lvWatches.Column[1].Width := COL_WIDTHS[COL_WATCH_VALUE]; 276end; 277 278function TWatchesDlg.GetSelected: TCurrentWatch; 279var 280 Item: TListItem; 281begin 282 Item := lvWatches.Selected; 283 if Item = nil 284 then Result := nil 285 else Result := TCurrentWatch(Item.Data); 286end; 287 288function TWatchesDlg.GetThreadId: Integer; 289var 290 Threads: TIdeThreads; 291begin 292 Result := -1; 293 if (ThreadsMonitor = nil) then exit; 294 Threads := GetSelectedThreads(GetSelectedSnapshot); 295 if Threads <> nil 296 then Result := Threads.CurrentThreadId 297 else Result := 1; 298end; 299 300function TWatchesDlg.GetSelectedThreads(Snap: TSnapshot): TIdeThreads; 301begin 302 if ThreadsMonitor = nil then exit(nil); 303 if Snap = nil 304 then Result := ThreadsMonitor.CurrentThreads 305 else Result := ThreadsMonitor.Snapshots[Snap]; 306end; 307 308function TWatchesDlg.GetStackframe: Integer; 309var 310 Snap: TSnapshot; 311 Threads: TIdeThreads; 312 tid: LongInt; 313 Stack: TIdeCallStack; 314begin 315 if (CallStackMonitor = nil) or (ThreadsMonitor = nil) 316 then begin 317 Result := 0; 318 exit; 319 end; 320 321 Snap := GetSelectedSnapshot; 322 Threads := GetSelectedThreads(Snap); 323 if Threads <> nil 324 then tid := Threads.CurrentThreadId 325 else tid := 1; 326 327 if (Snap <> nil) 328 then Stack := CallStackMonitor.Snapshots[Snap].EntriesForThreads[tid] 329 else Stack := CallStackMonitor.CurrentCallStackList.EntriesForThreads[tid]; 330 331 if Stack <> nil 332 then Result := Stack.CurrentIndex 333 else Result := 0; 334end; 335 336procedure TWatchesDlg.lvWatchesSelectItem(Sender: TObject; AItem: TListItem; Selected: Boolean); 337var 338 ItemSelected: Boolean; 339 Watch: TCurrentWatch; 340 SelCanEnable, SelCanDisable: Boolean; 341 AllCanEnable, AllCanDisable: Boolean; 342 i: Integer; 343begin 344 if FUpdatingAll then exit; 345 if GetSelectedSnapshot <> nil then begin 346 actToggleCurrentEnable.Enabled := False; 347 actToggleCurrentEnable.Checked := False; 348 actEnableSelected.Enabled := False; 349 actDisableSelected.Enabled := False; 350 actDeleteSelected.Enabled := False; 351 actEnableAll.Enabled := False; 352 actDisableAll.Enabled := False; 353 actDeleteAll.Enabled := False; 354 actProperties.Enabled := False; 355 actAddWatch.Enabled := False; 356 actPower.Enabled := False; 357 actAddWatchPoint.Enabled := False; 358 actEvaluate.Enabled := False; 359 actInspect.Enabled := False; 360 UpdateInspectPane; 361 exit; 362 end; 363 364 ItemSelected := lvWatches.Selected <> nil; 365 if ItemSelected then 366 Watch:=TCurrentWatch(lvWatches.Selected.Data) 367 else 368 Watch:=nil; 369 SelCanEnable := False; 370 SelCanDisable := False; 371 AllCanEnable := False; 372 AllCanDisable := False; 373 for i := 0 to lvWatches.Items.Count - 1 do begin 374 if lvWatches.Items[i].Data = nil then 375 continue; 376 if lvWatches.Items[i].Selected then begin 377 SelCanEnable := SelCanEnable or not TCurrentWatch(lvWatches.Items[i].Data).Enabled; 378 SelCanDisable := SelCanDisable or TCurrentWatch(lvWatches.Items[i].Data).Enabled; 379 end; 380 AllCanEnable := AllCanEnable or not TCurrentWatch(lvWatches.Items[i].Data).Enabled; 381 AllCanDisable := AllCanDisable or TCurrentWatch(lvWatches.Items[i].Data).Enabled; 382 end; 383 384 actToggleCurrentEnable.Enabled := ItemSelected; 385 actToggleCurrentEnable.Checked := ItemSelected and Watch.Enabled; 386 387 actEnableSelected.Enabled := SelCanEnable; 388 actDisableSelected.Enabled := SelCanDisable; 389 actDeleteSelected.Enabled := ItemSelected; 390 391 actAddWatchPoint.Enabled := ItemSelected; 392 actEvaluate.Enabled := ItemSelected; 393 actInspect.Enabled := ItemSelected; 394 395 actEnableAll.Enabled := AllCanEnable; 396 actDisableAll.Enabled := AllCanDisable; 397 actDeleteAll.Enabled := lvWatches.Items.Count > 0; 398 399 actCopyName.Enabled := ItemSelected; 400 actCopyValue.Enabled := ItemSelected; 401 402 actProperties.Enabled := ItemSelected; 403 actAddWatch.Enabled := True; 404 actPower.Enabled := True; 405 406 actToggleInspectSite.Enabled := True; 407 408 UpdateInspectPane; 409end; 410 411procedure TWatchesDlg.lvWatchesDblClick(Sender: TObject); 412begin 413 if GetSelectedSnapshot <> nil then exit; 414 if lvWatches.SelCount >= 0 then 415 popPropertiesClick(Sender) 416 else 417 popAddClick(Sender); 418end; 419 420procedure TWatchesDlg.lvWatchesDragDrop(Sender, Source: TObject; X, Y: Integer); 421var 422 s: String; 423 NewWatch: TCurrentWatch; 424begin 425 s := ''; 426 if (Source is TSynEdit) then s := TSynEdit(Source).SelText; 427 if (Source is TCustomEdit) then s := TCustomEdit(Source).SelText; 428 429 if s <> '' then begin 430 NewWatch := DebugBoss.Watches.CurrentWatches.Add(s); 431 NewWatch.DisplayFormat := wdfDefault; 432 NewWatch.EvaluateFlags := [defClassAutoCast]; 433 NewWatch.Enabled := True; 434 end; 435end; 436 437procedure TWatchesDlg.lvWatchesDragOver(Sender, Source: TObject; X, Y: Integer; 438 State: TDragState; var Accept: Boolean); 439begin 440 Accept := ( (Source is TSynEdit) and (TSynEdit(Source).SelAvail) ) or 441 ( (Source is TCustomEdit) and (TCustomEdit(Source).SelText <> '') ); 442end; 443 444procedure TWatchesDlg.FormDestroy(Sender: TObject); 445begin 446 //DebugLn('TWatchesDlg.FormDestroy ',DbgSName(Self)); 447end; 448 449procedure TWatchesDlg.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 450var 451 s: String; 452 NewWatch: TCurrentWatch; 453begin 454 if (Shift * [ssShift, ssAlt, ssAltGr, ssCtrl] = [ssCtrl]) and (Key = VK_V) 455 then begin 456 Key := 0; 457 s := Clipboard.AsText; 458 if s <> '' then begin 459 NewWatch := DebugBoss.Watches.CurrentWatches.Add(s); 460 NewWatch.DisplayFormat := wdfDefault; 461 NewWatch.EvaluateFlags := [defClassAutoCast]; 462 NewWatch.Enabled := True; 463 end; 464 465 exit; 466 end; 467 468 inherited FormKeyDown(Sender, Key, Shift); 469end; 470 471procedure TWatchesDlg.FormShow(Sender: TObject); 472begin 473 UpdateAll; 474end; 475 476procedure TWatchesDlg.actPowerExecute(Sender: TObject); 477begin 478 if ToolButtonPower.Down 479 then begin 480 actPower.ImageIndex := FPowerImgIdx; 481 ToolButtonPower.ImageIndex := FPowerImgIdx; 482 UpdateAll; 483 end 484 else begin 485 actPower.ImageIndex := FPowerImgIdxGrey; 486 ToolButtonPower.ImageIndex := FPowerImgIdxGrey; 487 end; 488end; 489 490procedure TWatchesDlg.actToggleInspectSiteExecute(Sender: TObject); 491begin 492 InspectSplitter.Visible := ToolButtonInspectView.Down; 493 nbInspect.Visible := ToolButtonInspectView.Down; 494 InspectSplitter.Left := nbInspect.Left - 1; 495 if ToolButtonInspectView.Down then 496 UpdateInspectPane; 497end; 498 499procedure TWatchesDlg.ContextChanged(Sender: TObject); 500begin 501 DebugLn(DBG_DATA_MONITORS, ['DebugDataWindow: TWatchesDlg.ContextChanged ', DbgSName(Sender), ' Upd:', IsUpdating]); 502 UpdateAll; 503end; 504 505procedure TWatchesDlg.actEnableSelectedExecute(Sender: TObject); 506var 507 n: Integer; 508 Item: TListItem; 509begin 510 try 511 DisableAllActions; 512 for n := 0 to lvWatches.Items.Count -1 do 513 begin 514 Item := lvWatches.Items[n]; 515 if Item.Selected then 516 TCurrentWatch(Item.Data).Enabled := True; 517 end; 518 finally 519 lvWatchesSelectItem(nil, nil, False); 520 end; 521end; 522 523procedure TWatchesDlg.actEvaluateExecute(Sender: TObject); 524begin 525 DebugBoss.EvaluateModify(lvWatches.Selected.Caption); 526end; 527 528procedure TWatchesDlg.actInspectExecute(Sender: TObject); 529begin 530 DebugBoss.Inspect(lvWatches.Selected.Caption); 531end; 532 533procedure TWatchesDlg.actDisableSelectedExecute(Sender: TObject); 534var 535 n: Integer; 536 Item: TListItem; 537begin 538 try 539 DisableAllActions; 540 for n := 0 to lvWatches.Items.Count -1 do 541 begin 542 Item := lvWatches.Items[n]; 543 if Item.Selected then 544 TCurrentWatch(Item.Data).Enabled := False; 545 end; 546 finally 547 lvWatchesSelectItem(nil, nil, False); 548 end; 549end; 550 551procedure TWatchesDlg.actAddWatchPointExecute(Sender: TObject); 552var 553 NewBreakpoint: TIDEBreakPoint; 554 Watch: TCurrentWatch; 555begin 556 Watch := GetSelected; 557 if Watch = nil then Exit; 558 NewBreakpoint := BreakPoints.Add(Watch.Expression, wpsGlobal, wpkWrite); 559 if DebugBoss.ShowBreakPointProperties(NewBreakpoint) <> mrOk then 560 ReleaseRefAndNil(NewBreakpoint); 561end; 562 563procedure TWatchesDlg.actCopyNameExecute(Sender: TObject); 564begin 565 Clipboard.Open; 566 Clipboard.AsText := lvWatches.Selected.Caption; 567 Clipboard.Close; 568end; 569 570procedure TWatchesDlg.actCopyValueExecute(Sender: TObject); 571begin 572 Clipboard.Open; 573 Clipboard.AsText := lvWatches.Selected.SubItems[0]; 574 Clipboard.Close; 575end; 576 577procedure TWatchesDlg.popAddClick(Sender: TObject); 578begin 579 try 580 DisableAllActions; 581 DebugBoss.ShowWatchProperties(nil); 582 finally 583 lvWatchesSelectItem(nil, nil, False); 584 end; 585end; 586 587procedure TWatchesDlg.popDeleteAllClick(Sender: TObject); 588var 589 n: Integer; 590begin 591 Include(FStateFlags, wdsfNeedDeleteAll); 592 if wdsfUpdating in FStateFlags then exit; 593 Exclude(FStateFlags, wdsfNeedDeleteAll); 594 try 595 DisableAllActions; 596 for n := lvWatches.Items.Count - 1 downto 0 do 597 TCurrentWatch(lvWatches.Items[n].Data).Free; 598 finally 599 lvWatchesSelectItem(nil, nil, False); 600 end; 601end; 602 603procedure TWatchesDlg.SnapshotChanged(Sender: TObject); 604var 605 NewWatches: TIdeWatches; 606begin 607 DebugLn(DBG_DATA_MONITORS, ['DebugDataWindow: TWatchesDlg.SnapshotChanged ', DbgSName(Sender), ' Upd:', IsUpdating]); 608 lvWatches.BeginUpdate; 609 // prevent lvWatchesSelectItem when deleting the itews. Snapsot may have been deleted 610 FUpdatingAll := True; // will be reset by UpdateAll 611 try 612 NewWatches := Watches; 613 if FWatchesInView <> NewWatches 614 then lvWatches.Items.Clear; 615 FWatchesInView := NewWatches; 616 UpdateAll; 617 finally 618 FUpdatingAll := False; // wan reset by UpdateAll anyway 619 lvWatches.EndUpdate; 620 end; 621end; 622 623function TWatchesDlg.GetWatches: TIdeWatches; 624var 625 Snap: TSnapshot; 626begin 627 Result := nil; 628 if WatchesMonitor = nil then exit; 629 630 Snap := GetSelectedSnapshot; 631 632 if Snap <> nil 633 then Result := WatchesMonitor.Snapshots[Snap] 634 else Result := WatchesMonitor.CurrentWatches; 635end; 636 637procedure TWatchesDlg.DoEndUpdate; 638begin 639 inherited DoEndUpdate; 640 if FUpdateAllNeeded then begin 641 FUpdateAllNeeded := False; 642 UpdateAll; 643 end; 644end; 645 646procedure TWatchesDlg.DoWatchesChanged; 647begin 648 UpdateAll; 649end; 650 651function TWatchesDlg.ColSizeGetter(AColId: Integer; var ASize: Integer): Boolean; 652begin 653 if AColId = COL_SPLITTER_INSPECT then begin 654 ASize := nbInspect.Width; 655 Result := ASize <> COL_WIDTHS[AColId - 1]; 656 end 657 else 658 if (AColId - 1 >= 0) and (AColId - 1 < lvWatches.ColumnCount) then begin 659 ASize := lvWatches.Column[AColId - 1].Width; 660 Result := ASize <> COL_WIDTHS[AColId - 1]; 661 end 662 else 663 Result := False; 664end; 665 666procedure TWatchesDlg.ColSizeSetter(AColId: Integer; ASize: Integer); 667begin 668 case AColId of 669 COL_WATCH_EXPR: lvWatches.Column[0].Width := ASize; 670 COL_WATCH_VALUE: lvWatches.Column[1].Width := ASize; 671 COL_SPLITTER_INSPECT: nbInspect.Width := ASize; 672 end; 673end; 674 675procedure TWatchesDlg.popDeleteClick(Sender: TObject); 676var 677 Item: TCurrentWatch; 678begin 679 Include(FStateFlags, wdsfNeedDeleteCurrent); 680 if (wdsfUpdating in FStateFlags) then exit; 681 Exclude(FStateFlags, wdsfNeedDeleteCurrent); 682 try 683 DisableAllActions; 684 repeat 685 Item := GetSelected; 686 Item.Free; 687 until Item = nil; 688 //GetSelected.Free; 689 finally 690 lvWatchesSelectItem(nil, nil, False); 691 end; 692end; 693 694procedure TWatchesDlg.popDisableAllClick(Sender: TObject); 695var 696 n: Integer; 697 Item: TListItem; 698begin 699 try 700 DisableAllActions; 701 for n := 0 to lvWatches.Items.Count - 1 do 702 begin 703 Item := lvWatches.Items[n]; 704 if Item.Data <> nil 705 then TCurrentWatch(Item.Data).Enabled := False; 706 end; 707 finally 708 lvWatchesSelectItem(nil, nil, False); 709 end; 710end; 711 712procedure TWatchesDlg.popEnableAllClick(Sender: TObject); 713var 714 n: Integer; 715 Item: TListItem; 716begin 717 try 718 DisableAllActions; 719 for n := 0 to lvWatches.Items.Count - 1 do 720 begin 721 Item := lvWatches.Items[n]; 722 if Item.Data <> nil 723 then TCurrentWatch(Item.Data).Enabled := True; 724 end; 725 finally 726 lvWatchesSelectItem(nil, nil, False); 727 end; 728end; 729 730procedure TWatchesDlg.popEnabledClick(Sender: TObject); 731var 732 Watch: TCurrentWatch; 733begin 734 try 735 DisableAllActions; 736 Watch := GetSelected; 737 if Watch = nil then Exit; 738 popEnabled.Checked := not popEnabled.Checked; 739 Watch.Enabled := popEnabled.Checked; 740 finally 741 lvWatchesSelectItem(nil, nil, False); 742 end; 743end; 744 745procedure TWatchesDlg.popPropertiesClick(Sender: TObject); 746begin 747 try 748 DisableAllActions; 749 DebugBoss.ShowWatchProperties(GetSelected); 750 finally 751 lvWatchesSelectItem(nil, nil, False); 752 end; 753end; 754 755procedure TWatchesDlg.UpdateInspectPane; 756var 757 Watch: TCurrentWatch; 758 i: integer; 759 d: TIdeWatchValue; 760 t: TDBGType; 761begin 762 if not nbInspect.Visible then exit; 763 DebugBoss.LockCommandProcessing; 764 try 765 766 InspectMemo.Clear; 767 InspectLabel.Caption := '...'; 768 if (lvWatches.Selected = nil) then begin 769 exit; 770 end; 771 772 Watch:=TCurrentWatch(lvWatches.Selected.Data); 773 InspectLabel.Caption := Watch.Expression; 774 775 d := Watch.Values[GetThreadId, GetStackframe]; 776 if d = nil then begin 777 InspectMemo.WordWrap := True; 778 InspectMemo.Text := '<evaluating>'; 779 exit; 780 end; 781 782 t := d.TypeInfo; 783 784 if (t <> nil) and (t.Fields <> nil) and (t.Fields.Count > 0) and 785 (d.DisplayFormat in [wdfDefault, wdfStructure]) 786 then begin 787 InspectMemo.WordWrap := False; 788 InspectMemo.Lines.BeginUpdate; 789 try 790 for i := 0 to t.Fields.Count - 1 do 791 case t.Fields[i].DBGType.Kind of 792 skSimple: 793 begin 794 if t.Fields[i].DBGType.Value.AsString='$0' then begin 795 if t.Fields[i].DBGType.TypeName='ANSISTRING' then 796 InspectMemo.Append(t.Fields[i].Name + ': ''''') 797 else 798 InspectMemo.Append(t.Fields[i].Name + ': nil'); 799 end 800 else 801 InspectMemo.Append(t.Fields[i].Name + ': ' + t.Fields[i].DBGType.Value.AsString); 802 end; 803 skProcedure, skFunction: ; 804 else 805 InspectMemo.Append(t.Fields[i].Name + ': ' + t.Fields[i].DBGType.Value.AsString); 806 end; 807 finally 808 InspectMemo.Lines.EndUpdate; 809 end; 810 exit; 811 end; 812 813 InspectMemo.WordWrap := True; 814 InspectMemo.Text := d.Value; 815 finally 816 DebugBoss.UnLockCommandProcessing; 817 end; 818end; 819 820procedure TWatchesDlg.UpdateItem(const AItem: TListItem; const AWatch: TIdeWatch); 821 function ClearMultiline(const AValue: ansistring): ansistring; 822 var 823 j: SizeInt; 824 ow: SizeInt; 825 NewLine: Boolean; 826 begin 827 ow:=0; 828 SetLength(Result,Length(AValue)); 829 NewLine:=true; 830 for j := 1 to Length(AValue) do begin 831 if (AValue[j]=#13) or (AValue[j]=#10) then begin 832 NewLine:=true; 833 inc(ow); 834 Result[ow]:=#32; // insert one space instead of new line 835 end 836 else if Avalue[j] in [#9,#32] then begin 837 if not NewLine then begin // strip leading spaces after new line 838 inc(ow); 839 Result[ow]:=#32; 840 end; 841 end else begin 842 inc(ow); 843 Result[ow]:=AValue[j]; 844 NewLine:=false; 845 end; 846 end; 847 If ow>255 then begin 848 //Limit watch to 255 chars in length 849 Result:=Copy(Result,1,252)+'...'; 850 end else begin 851 SetLength(Result,ow); 852 end; 853 end; 854var 855 WatchValue: TIdeWatchValue; 856 WatchValueStr: string; 857begin 858 DebugBoss.LockCommandProcessing; 859 try 860// Expression 861// Result 862 if (not ToolButtonPower.Down) or (not Visible) then exit; 863 if (ThreadsMonitor = nil) or (CallStackMonitor = nil) then exit; 864 if GetStackframe < 0 then exit; // TODO need dedicated validity property 865 866 include(FStateFlags, wdsfUpdating); 867 AItem.Caption := AWatch.Expression; 868 WatchValue := AWatch.Values[GetThreadId, GetStackframe]; 869 if (WatchValue <> nil) and 870 ( (GetSelectedSnapshot = nil) or not(WatchValue.Validity in [ddsUnknown, ddsEvaluating, ddsRequested]) ) 871 then begin 872 WatchValueStr := ClearMultiline(DebugBoss.FormatValue(WatchValue.TypeInfo, WatchValue.Value)); 873 if (WatchValue.TypeInfo <> nil) and 874 (WatchValue.TypeInfo.Attributes * [saArray, saDynArray] <> []) and 875 (WatchValue.TypeInfo.Len >= 0) 876 then AItem.SubItems[0] := Format(drsLen, [WatchValue.TypeInfo.Len]) + WatchValueStr 877 else AItem.SubItems[0] := WatchValueStr; 878 end 879 else 880 AItem.SubItems[0] := '<not evaluated>'; 881 exclude(FStateFlags, wdsfUpdating); 882 if wdsfNeedDeleteCurrent in FStateFlags then 883 popDeleteClick(nil); 884 if wdsfNeedDeleteAll in FStateFlags then 885 popDeleteAllClick(nil); 886 finally 887 DebugBoss.UnLockCommandProcessing; 888 end; 889end; 890 891procedure TWatchesDlg.UpdateAll; 892var 893 i, l: Integer; 894 Snap: TSnapshot; 895begin 896 if Watches = nil then exit; 897 if IsUpdating then begin 898 DebugLn(DBG_DATA_MONITORS, ['DebugDataWindow: TWatchesDlg.UpdateAll: TWatchesDlg.UpdateAll in IsUpdating:']); 899 FUpdateAllNeeded := True; 900 exit; 901 end; 902 try DebugLnEnter(DBG_DATA_MONITORS, ['DebugDataWindow: TWatchesDlg.UpdateAll: >>ENTER: TWatchesDlg.UpdateAll ']); 903 904 Snap := GetSelectedSnapshot; 905 if Snap <> nil 906 then Caption:= liswlWatchList + ' (' + Snap.LocationAsText + ')' 907 else Caption:= liswlWatchList; 908 909 FUpdatingAll := True; 910 DebugBoss.LockCommandProcessing; 911 lvWatches.BeginUpdate; 912 try 913 l := Watches.Count; 914 i := 0; 915 while i < l do begin 916 WatchUpdate(Watches, Watches.Items[i]); 917 if l <> Watches.Count then begin 918 i := Max(0, i - Max(0, Watches.Count - l)); 919 l := Watches.Count; 920 end; 921 inc(i); 922 end; 923 finally 924 FUpdatingAll := False; 925 lvWatches.EndUpdate; 926 DebugBoss.UnLockCommandProcessing; 927 lvWatchesSelectItem(nil, nil, False); 928 end; 929 finally DebugLnExit(DBG_DATA_MONITORS, ['DebugDataWindow: TWatchesDlg.UpdateAll: <<EXIT: TWatchesDlg.UpdateAll ']); end; 930end; 931 932procedure TWatchesDlg.DisableAllActions; 933var 934 i: Integer; 935begin 936 for i := 0 to ActionList1.ActionCount - 1 do 937 (ActionList1.Actions[i] as TAction).Enabled := False; 938end; 939 940function TWatchesDlg.GetSelectedSnapshot: TSnapshot; 941begin 942 Result := nil; 943 if (SnapshotManager <> nil) and (SnapshotManager.SelectedEntry <> nil) 944 then Result := SnapshotManager.SelectedEntry; 945end; 946 947procedure TWatchesDlg.WatchAdd(const ASender: TIdeWatches; const AWatch: TIdeWatch); 948var 949 Item: TListItem; 950begin 951 Item := lvWatches.Items.FindData(AWatch); 952 if Item = nil 953 then begin 954 Item := lvWatches.Items.Add; 955 Item.Data := AWatch; 956 Item.SubItems.Add(''); 957 Item.Selected := True; 958 end; 959 960 UpdateItem(Item, AWatch); 961 lvWatchesSelectItem(nil, nil, False); 962end; 963 964procedure TWatchesDlg.WatchUpdate(const ASender: TIdeWatches; const AWatch: TIdeWatch); 965var 966 Item: TListItem; 967begin 968 if AWatch = nil then Exit; // TODO: update all 969 if AWatch.Collection <> FWatchesInView then exit; 970 try DebugLnEnter(DBG_DATA_MONITORS, ['DebugDataWindow: TWatchesDlg.WatchUpdate Upd:', IsUpdating, ' Watch=',AWatch.Expression]); 971 972 Item := lvWatches.Items.FindData(AWatch); 973 if Item = nil 974 then WatchAdd(ASender, AWatch) 975 else UpdateItem(Item, AWatch); 976 977 // TODO: if AWatch <> Selected, then prevent UpdateInspectPane 978 // Selected may update later, and calling UpdateInspectPane will schedule an unsucesful attemptn to fetch data 979 if not FUpdatingAll 980 then lvWatchesSelectItem(nil, nil, False); 981 finally DebugLnExit(DBG_DATA_MONITORS, ['DebugDataWindow: TWatchesDlg.WatchUpdate']); end; 982end; 983 984procedure TWatchesDlg.WatchRemove(const ASender: TIdeWatches; const AWatch: TIdeWatch); 985begin 986 lvWatches.Items.FindData(AWatch).Free; 987 lvWatchesSelectItem(nil, nil, False); 988end; 989 990initialization 991 992 WatchWindowCreator := IDEWindowCreators.Add(DebugDialogNames[ddtWatches]); 993 WatchWindowCreator.OnCreateFormProc := @CreateDebugDialog; 994 WatchWindowCreator.OnSetDividerSize := @WatchesDlgColSizeSetter; 995 WatchWindowCreator.OnGetDividerSize := @WatchesDlgColSizeGetter; 996 WatchWindowCreator.DividerTemplate.Add('ColumnWatchExpr', COL_WATCH_EXPR, @drsColWidthExpression); 997 WatchWindowCreator.DividerTemplate.Add('ColumnWatchValue', COL_WATCH_VALUE, @drsColWidthValue); 998 WatchWindowCreator.DividerTemplate.Add('SplitterWatchInspect', COL_SPLITTER_INSPECT, @drsWatchSplitterInspect); 999 WatchWindowCreator.CreateSimpleLayout; 1000 1001 DBG_DATA_MONITORS := DebugLogger.FindOrRegisterLogGroup('DBG_DATA_MONITORS' {$IFDEF DBG_DATA_MONITORS} , True {$ENDIF} ); 1002 1003end. 1004 1005