Menu

[r2299]: / trunk / Src / FmDependenciesDlg.pas  Maximize  Restore  History

Download this file

453 lines (416 with data), 14.4 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
{
* FmDependenciesDlg.pas
*
* Implements a dialog box that displays all the dependencies and dependents of
* a snippet.
*
* $Rev$
* $Date$
*
* ***** BEGIN LICENSE BLOCK *****
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Original Code is FmDependenciesDlg.pas
*
* The Initial Developer of the Original Code is Peter Johnson
* (http://www.delphidabbler.com/).
*
* Portions created by the Initial Developer are Copyright (C) 2009-2012 Peter
* Johnson. All Rights Reserved.
*
* Contributor(s)
* NONE
*
* ***** END LICENSE BLOCK *****
}
unit FmDependenciesDlg;
interface
uses
// Delphi
ComCtrls, StdCtrls, Controls, ExtCtrls, Classes, Windows,
// Project
DB.USnippet, FmGenericViewDlg, UBaseObjects, USnippetIDs, USnippetsTVDraw;
type
{
TDependenciesDlg:
Tabbed dialog box that displays all the dependencies and dependents of a
snippet.
}
TDependenciesDlg = class(TGenericViewDlg, INoPublicConstruct)
lbDependents: TListBox;
lblCircularRef: TLabel;
lblNoDependencies: TLabel;
lblNoDependents: TLabel;
pcBody: TPageControl;
tsDependsUpon: TTabSheet;
tsRequiredBy: TTabSheet;
tvDependencies: TTreeView;
procedure FormDestroy(Sender: TObject);
procedure lbDependentsDrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure pcBodyMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure tvDependenciesCollapsing(Sender: TObject; Node: TTreeNode;
var AllowCollapse: Boolean);
public
type
TTabID = (tiDependsUpon, tiRequiredBy);
TTabIDs = set of TTabID;
strict private
type
{
TTVDraw:
Object used to draw snippets tree view nodes.
}
TTVDraw = class(TSnippetsTVDraw)
strict private
fRootID: TSnippetID; // ID of snippet whose dependency nodes displayed
strict protected
function IsUserDefinedNode(const Node: TTreeNode): Boolean;
override;
{Checks if a node represents a user defined snippets object.
@param Node [in] Node to be checked.
@return True if node represents user defined object, False if not.
}
function IsErrorNode(const Node: TTreeNode): Boolean;
override;
{Checks if a node represents an error condition.
@param Node [in] Node to be checked.
@return True if node represents error condition, False if not.
}
public
constructor Create(const RootID: TSnippetID);
{Class constructor. Sets up object.
@param ID [in] ID snippet for which dependencies are displayed.
}
end;
var
fSnippetID: TSnippetID; // Snippet whose dependencies are displayed
fDisplayName: string; // Display name of snippet
fDependsList: TSnippetList; // List of dependencies to be displayed
fTVDraw: TTVDraw; // Customises appearance of tree view}
fTabs: TTabIDs;
procedure PopulateRequiredByList;
procedure PopulateTreeView;
{Populates treeview with nodes for each snippet in dependency list.
}
procedure AddDependencies(const Parent: TTreeNode;
const DependsList: TSnippetList);
{Adds tree nodes for snippets in a dependency list.
@param Parent [in] Parent node for nodes from dependency list.
@param DependsList [in] Dependency list containing snippets to be added
to treeview.
}
procedure DisplayCircularRefWarning;
{Displays circular reference warning label.
}
function GetDisplayName: string;
{Gets display name for snippet for which dependencies are being displayed.
@return Required display name: snippet's name if available, otherwise an
untitled string.
}
strict protected
procedure ConfigForm; override;
{Configure controls on form.
}
procedure ArrangeForm; override;
{Arranges controls on form.
}
public
class procedure Execute(const AOwner: TComponent;
const SnippetID: TSnippetID; const DisplayName: string;
const DependsList: TSnippetList; const Tabs: TTabIDs); overload;
{Displays dialogue box containing details of a snippet's dependencies.
@param AOwner [in] Component that owns the dialog box.
@param SnippetID [in] ID of snippet for which dependencies are to be
displayed.
@param DisplayName [in] Display name of snippet for which dependencies
are to be displayed.
@param DependsList [in] List of dependencies.
@param Tabs [in] Tabs to be displayed in dialogue box.
}
class procedure Execute(const AOwner: TComponent; const Snippet: TSnippet;
const Tabs: TTabIDs); overload;
{Displays dialogue box containing details of a snippet's dependencies.
@param AOwner [in] Component that owns the dialog box.
@param Snippet [in] Snippet for which dependencies are to be displayed.
@param Tabs [in] Tabs to be displayed in dialogue box.
}
end;
implementation
uses
// Delphi
SysUtils, Graphics,
// Project
DB.UMain, DB.USnippetKind, UBox, UColours, UFontHelper, UPreferences;
{$R *.dfm}
{ TDependenciesDlg }
procedure TDependenciesDlg.AddDependencies(const Parent: TTreeNode;
const DependsList: TSnippetList);
{Adds tree nodes for snippets in a dependency list.
@param Parent [in] Parent node for nodes from dependency list.
@param DependsList [in] Dependency list containing snippets to be added to
treeview.
}
var
RequiredSnippet: TSnippet; // iterates through snippets in DependsList
ChildNode: TTreeNode; // a node added to treeview
begin
for RequiredSnippet in DependsList do
begin
// Add node for snippet from dependency list
ChildNode := tvDependencies.Items.AddChild(
Parent, RequiredSnippet.DisplayName
);
ChildNode.Data := RequiredSnippet; // reference to associated snippet
// Check for circular reference. If detetected display warning otherwise
// recursively add child nodes for snippet's dependency list
if (RequiredSnippet.ID <> fSnippetID) then
AddDependencies(ChildNode, RequiredSnippet.Depends)
else
DisplayCircularRefWarning;
end;
end;
procedure TDependenciesDlg.ArrangeForm;
{Arranges controls on form.
}
begin
inherited;
// Position "no dependencies" and "no dependents" message labels in form
lblNoDependencies.Left :=
(tsDependsUpon.ClientWidth - lblNoDependencies.Width) div 2;
lblNoDependencies.Top :=
(tsDependsUpon.ClientHeight - lblNoDependencies.Height) div 2;
lblNoDependents.Left :=
(tsRequiredBy.ClientWidth - lblNoDependents.Width) div 2;
lblNoDependents.Top :=
(tsRequiredBy.ClientHeight - lblNoDependents.Height) div 2;
// Adjust size of treeview
if lblCircularRef.Visible then
begin
// move label
lblCircularRef.Top := tsDependsUpon.ClientHeight
- lblCircularRef.Height - 8;
// circular reference: make room to see circular reference label
tvDependencies.Align := alTop;
tvDependencies.Height := lblCircularRef.Top - 6;
end
else
// no circular reference: use all space for treeview
tvDependencies.Align := alClient;
end;
procedure TDependenciesDlg.ConfigForm;
{Configure controls on form.
}
resourcestring
// no dependents and no dependencies message templates
sNoDepends = '%s has no dependencies';
sNoRequires = '%s has no dependents';
// form caption template
sTitle = 'Dependencies for %s';
begin
inherited;
// Set form caption
Caption := Format(sTitle, [GetDisplayName]);
// Determine which tabs are visible
tsDependsUpon.TabVisible := tiDependsUpon in fTabs;
tsRequiredBy.TabVisible := tiRequiredBy in fTabs;
// Set "no dependencies" and "no dependents" labels in case needed
lblNoDependencies.Caption := Format(sNoDepends, [GetDisplayName]);
lblNoDependencies.Font.Style := [fsBold];
lblNoDependents.Caption := Format(sNoRequires, [GetDisplayName]);
lblNoDependents.Font.Style := [fsBold];
// Set "circular reference" label's colour and visibility
lblCircularRef.Font.Color := clWarningText;
lblCircularRef.Visible := False;
// Provide custom draw support for treeview
fTVDraw := TTVDraw.Create(fSnippetID);
tvDependencies.OnCustomDrawItem := fTVDraw.CustomDrawItem;
// Populate treeview with dependency information
PopulateTreeView;
// Populate Required By list view with dependent snippets
PopulateRequiredByList;
// Hide treeview, revealing "no dependencies" label if no dependencies
if tvDependencies.Items.Count = 0 then
tvDependencies.Visible := False;
// Hide list box, revealing "no dependents" label if no dependents
if lbDependents.Count = 0 then
lbDependents.Visible := False;
end;
procedure TDependenciesDlg.DisplayCircularRefWarning;
{Displays circular reference warning label.
}
begin
lblCircularRef.Visible := True;
end;
class procedure TDependenciesDlg.Execute(const AOwner: TComponent;
const Snippet: TSnippet; const Tabs: TTabIDs);
begin
Execute(AOwner, Snippet.ID, Snippet.DisplayName, Snippet.Depends, Tabs);
end;
class procedure TDependenciesDlg.Execute(const AOwner: TComponent;
const SnippetID: TSnippetID; const DisplayName: string;
const DependsList: TSnippetList; const Tabs: TTabIDs);
begin
Assert(Tabs <> [], ClassName + '.Execute: Tabs is []');
with InternalCreate(AOwner) do
try
fSnippetID := SnippetID;
fDisplayName := DisplayName;
fDependsList := DependsList;
fTabs := Tabs;
ShowModal;
finally
Free;
end;
end;
procedure TDependenciesDlg.FormDestroy(Sender: TObject);
{Form destruction event handler. Frees owned object.
@param Sender [in] Not used.
}
var
Idx: Integer;
begin
inherited;
fTVDraw.Free;
for Idx := Pred(lbDependents.Items.Count) downto 0 do
lbDependents.Items.Objects[Idx].Free;
end;
function TDependenciesDlg.GetDisplayName: string;
{Gets display name for snippet for which dependencies are being displayed.
@return Required display name: snippet's name if available, otherwise an
untitled string.
}
resourcestring
sUntitled = '<Untitled Snippet>'; // display name when snippet has no name
begin
if fDisplayName <> '' then
Exit(fDisplayName);
if fSnippetID.Name <> '' then
Exit(fSnippetID.Name);
Result := sUntitled;
end;
procedure TDependenciesDlg.lbDependentsDrawItem(Control: TWinControl;
Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
LB: TListBox;
Canvas: TCanvas;
function IsUserDefinedItem: Boolean;
begin
Result := (LB.Items.Objects[Index] as TBox<Boolean>).Value;
end;
begin
LB := Control as TListBox;
Canvas := LB.Canvas;
if not (odSelected in State) then
Canvas.Font.Color := Preferences.DBHeadingColours[IsUserDefinedItem];
Canvas.TextRect(
Rect,
Rect.Left + 2,
(Rect.Top + Rect.Bottom - Canvas.TextHeight(LB.Items[Index])) div 2,
LB.Items[Index]
);
end;
procedure TDependenciesDlg.pcBodyMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if htOnItem in pcBody.GetHitTestInfoAt(X, Y) then
pcBody.SetFocus;
end;
procedure TDependenciesDlg.PopulateRequiredByList;
var
Dependents: ISnippetIDList;
SnippetID: TSnippetID;
ThisSnippet: TSnippet;
ASnippet: TSnippet;
begin
lbDependents.Items.BeginUpdate;
try
lbDependents.Clear;
ThisSnippet := Database.Snippets.Find(fSnippetID);
// must only try to get dependents for snippet if it is in database
if (tiRequiredBy in fTabs) and Assigned(ThisSnippet) then
begin
Dependents := (Database as IDatabaseEdit).GetDependents(ThisSnippet);
for SnippetID in Dependents do
begin
ASnippet := Database.Snippets.Find(SnippetID);
Assert(Assigned(ASnippet),
ClassName + '.PopulateRequiredByList: Snippet id not found');
lbDependents.Items.AddObject(
ASnippet.DisplayName, TBox<Boolean>.Create(ASnippet.UserDefined)
);
end;
end;
finally
lbDependents.Items.EndUpdate;
end;
end;
procedure TDependenciesDlg.PopulateTreeView;
{Populates treeview with nodes for each snippet in dependency list.
}
begin
tvDependencies.Items.BeginUpdate;
try
tvDependencies.Items.Clear;
if tiDependsUpon in fTabs then
begin
AddDependencies(nil, fDependsList);
tvDependencies.FullExpand;
end;
finally
tvDependencies.Items.EndUpdate;
end;
end;
procedure TDependenciesDlg.tvDependenciesCollapsing(Sender: TObject;
Node: TTreeNode; var AllowCollapse: Boolean);
{Handles event triggered when tree view wants to collapse. We don't allow
this.
@param Sender [in] Not used.
@param Node [in] Not used.
@param AllowChange [in/out] Set false to prevent tree view collapsing.
}
begin
AllowCollapse := False;
end;
{ TDependenciesDlg.TTVDraw }
constructor TDependenciesDlg.TTVDraw.Create(
const RootID: TSnippetID);
{Class constructor. Sets up object.
@param ID [in] ID snippet for which dependencies are displayed.
}
begin
inherited Create;
fRootID := RootID;
end;
function TDependenciesDlg.TTVDraw.IsErrorNode(
const Node: TTreeNode): Boolean;
{Checks if a node represents an error condition.
@param Node [in] Node to be checked.
@return True if node represents error condition, False if not.
}
begin
Result := Assigned(Node.Data) and (TSnippet(Node.Data).ID = fRootID);
end;
function TDependenciesDlg.TTVDraw.IsUserDefinedNode(
const Node: TTreeNode): Boolean;
{Checks if a node represents a user defined snippets object.
@param Node [in] Node to be checked.
@return True if node represents user defined object, False if not.
}
begin
if not Assigned(Node.Data) then
Result := True
else
Result := TSnippet(Node.Data).UserDefined;
end;
end.
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.