Check-in [d118e46707]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Renamed AppSettings.pas to UAppSettings.pas so the global var can be named AppSettings. Added L_StoreFormPos, and made frmCommit a TRestorableForm, so it automatically remembers its position. History diffs now also support viewing with external diff tool. The diff viewer can now display exceptions via the SetException method. Made GetTempFilename more robust; if no filename was specified, it will generate a random filename; if one is specified, any invalid characters will be replaced by underscores.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: d118e46707ea9ccff39da54f866978fddb26d579
User & Date: MCO 2011-09-22 12:34:03.693
Context
2011-09-22
20:59
Adjusted L_StoreFormPos so it compiles with Delphi XE2. Added GetUpdates method to TFossil, to retrieve list of pending updates. Added property RemoteURL to TFossil. RefreshFiles now also checks for pending updates, as per ticket [27fb0b4408]. check-in: d129f6bbb4 user: Martijn tags: trunk
12:34
Renamed AppSettings.pas to UAppSettings.pas so the global var can be named AppSettings. Added L_StoreFormPos, and made frmCommit a TRestorableForm, so it automatically remembers its position. History diffs now also support viewing with external diff tool. The diff viewer can now display exceptions via the SetException method. Made GetTempFilename more robust; if no filename was specified, it will generate a random filename; if one is specified, any invalid characters will be replaced by underscores. check-in: d118e46707 user: MCO tags: trunk
2011-09-21
20:23
Added separate project for Delphi XE2 check-in: d7787cc81b user: Martijn tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/FCommit.dfm.
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
          item
            Header = 'Versioned files'
            GroupID = 0
            State = [lgsNormal, lgsNoHeader]
            HeaderAlign = taLeftJustify
            FooterAlign = taLeftJustify
            TitleImage = -1

          end
          item
            Header = 'Unversioned files'
            GroupID = 1
            State = [lgsCollapsible]
            HeaderAlign = taLeftJustify
            FooterAlign = taLeftJustify
            TitleImage = -1

          end>
        HideSelection = False
        MultiSelect = True
        GroupHeaderImages = imlFiles
        GroupView = True
        ReadOnly = True
        RowSelect = True







>








>







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
          item
            Header = 'Versioned files'
            GroupID = 0
            State = [lgsNormal, lgsNoHeader]
            HeaderAlign = taLeftJustify
            FooterAlign = taLeftJustify
            TitleImage = -1
            ExtendedImage = -1
          end
          item
            Header = 'Unversioned files'
            GroupID = 1
            State = [lgsCollapsible]
            HeaderAlign = taLeftJustify
            FooterAlign = taLeftJustify
            TitleImage = -1
            ExtendedImage = -1
          end>
        HideSelection = False
        MultiSelect = True
        GroupHeaderImages = imlFiles
        GroupView = True
        ReadOnly = True
        RowSelect = True
208
209
210
211
212
213
214




215
216
217
218
219
220
221
      Height = 343
      ActivePage = tsDiff
      Align = alClient
      TabOrder = 0
      Visible = False
      object tsContents: TTabSheet
        Caption = 'Contents'




      end
      object tsDiff: TTabSheet
        Caption = 'Diff'
        ImageIndex = 1
        OnShow = tsDiffShow
      end
      object tshHistory: TTabSheet







>
>
>
>







210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
      Height = 343
      ActivePage = tsDiff
      Align = alClient
      TabOrder = 0
      Visible = False
      object tsContents: TTabSheet
        Caption = 'Contents'
        ExplicitLeft = 0
        ExplicitTop = 0
        ExplicitWidth = 0
        ExplicitHeight = 0
      end
      object tsDiff: TTabSheet
        Caption = 'Diff'
        ImageIndex = 1
        OnShow = tsDiffShow
      end
      object tshHistory: TTabSheet
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
    end
  end
  object imlFiles: TImageList
    ColorDepth = cd32Bit
    Left = 24
    Top = 256
    Bitmap = {
      494C010108002800880010001000FFFFFFFF2110FFFFFFFFFFFFFFFF424D3600
      0000000000003600000028000000400000003000000001002000000000000030
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000







|







360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
    end
  end
  object imlFiles: TImageList
    ColorDepth = cd32Bit
    Left = 24
    Top = 256
    Bitmap = {
      494C0101080028008C0010001000FFFFFFFF2110FFFFFFFFFFFFFFFF424D3600
      0000000000003600000028000000400000003000000001002000000000000030
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
      0000000000000000000000000000000000000000000000000000000000000000
Changes to src/FCommit.pas.
1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16
17
18
unit FCommit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls, ImgList,

  u_fossil, fviewcontents, Buttons, fviewdiff;

type
  TfrmCommit = class(TForm)
    pnlLeft: TPanel;
    pnlRight: TPanel;
    splLeftRight: TSplitter;
    pnlTop: TPanel;
    pnlBottom: TPanel;
    splTopBottom: TSplitter;
    lvwFiles: TListView;






|
>
|


|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
unit FCommit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls, ImgList, Buttons,
  L_StoreFormPos,
  u_fossil, fviewcontents, fviewdiff;

type
  TfrmCommit = class(TRestorableForm)
    pnlLeft: TPanel;
    pnlRight: TPanel;
    splLeftRight: TSplitter;
    pnlTop: TPanel;
    pnlBottom: TPanel;
    splTopBottom: TSplitter;
    lvwFiles: TListView;
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

implementation

uses
  Math, FileCtrl, RichEdit, DateUtils, StrUtils, IOUtils,
  PNGImage, JPEG, GIFImg,
  RegExpr, L_SysParamInfo, FileIcon, Process, L_FileUtils,
  AppSettings;

{$R *.dfm}

type
  TLauncherCleaner = class(TThread)
  private
    FProcess: TProcess;







|







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

implementation

uses
  Math, FileCtrl, RichEdit, DateUtils, StrUtils, IOUtils,
  PNGImage, JPEG, GIFImg,
  RegExpr, L_SysParamInfo, FileIcon, Process, L_FileUtils,
  UAppSettings;

{$R *.dfm}

type
  TLauncherCleaner = class(TThread)
  private
    FProcess: TProcess;
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
  FDiffViewer.Parent := tsDiff;
  FDiffViewer.OnExternalDiff := DiffExternal;

  FHistoryViewer := TfrmViewContents.Create(Self);
  FHistoryViewer.Parent := tshHistory;
  FHistoryDiff := TfrmViewDiff.Create(Self);
  FHistoryDiff.Parent := tshHistory;

  FHistoryDiff.Visible := False;
  lvwHistory.Clear;
  lvwHistory.Tag := lvwHistory.Height;
  btnViewFileRevision.Down := Settings.ShowHistoryContents;

  if Settings.FossilExePath = '' then
    FossilExePath := ChangeFilePath('fossil.exe', ExtractFilePath(GetDllOrExePath));

  // TODO: get the target path from somewhere else
  CheckoutPath := ParamStr(1);
  if not DirectoryExists(CheckoutPath) then begin
    CheckoutPath := Settings.LastCheckoutFolder;
    if not SelectDirectory('Select the code path', '', CheckoutPath, [sdNewUI, sdShowEdit, sdValidateDir]) then begin
      PostMessage(Self.Handle, WM_CLOSE, 0, 0);
      Exit;
    end;
  end;
  Settings.LastCheckoutFolder := CheckoutPath;
  FFossil := TFossil.Create(FossilExePath, CheckoutPath);

  Self.Caption := 'Commit changes in ' + FFossil.RootDir;
  RefreshFiles;
end;
{ ------------------------------------------------------------------------------------------------ }
procedure TfrmCommit.FormDestroy(Sender: TObject);







>



|

|





|





|







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
  FDiffViewer.Parent := tsDiff;
  FDiffViewer.OnExternalDiff := DiffExternal;

  FHistoryViewer := TfrmViewContents.Create(Self);
  FHistoryViewer.Parent := tshHistory;
  FHistoryDiff := TfrmViewDiff.Create(Self);
  FHistoryDiff.Parent := tshHistory;
  FHistoryDiff.OnExternalDiff := DiffExternal;
  FHistoryDiff.Visible := False;
  lvwHistory.Clear;
  lvwHistory.Tag := lvwHistory.Height;
  btnViewFileRevision.Down := AppSettings.ShowHistoryContents;

  if AppSettings.FossilExePath = '' then
    FossilExePath := ChangeFilePath('fossil.exe', ExtractFilePath(GetDllOrExePath));

  // TODO: get the target path from somewhere else
  CheckoutPath := ParamStr(1);
  if not DirectoryExists(CheckoutPath) then begin
    CheckoutPath := AppSettings.LastCheckoutFolder;
    if not SelectDirectory('Select the code path', '', CheckoutPath, [sdNewUI, sdShowEdit, sdValidateDir]) then begin
      PostMessage(Self.Handle, WM_CLOSE, 0, 0);
      Exit;
    end;
  end;
  AppSettings.LastCheckoutFolder := CheckoutPath;
  FFossil := TFossil.Create(FossilExePath, CheckoutPath);

  Self.Caption := 'Commit changes in ' + FFossil.RootDir;
  RefreshFiles;
end;
{ ------------------------------------------------------------------------------------------------ }
procedure TfrmCommit.FormDestroy(Sender: TObject);
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
procedure TfrmCommit.btnViewFileRevisionClick(Sender: TObject);
begin
  if Assigned(lvwHistory.Selected) then begin
    lvwHistorySelectItem(lvwHistory, lvwHistory.Selected, lvwHistory.Selected.Selected);
  end else begin
    lvwHistorySelectItem(lvwHistory, lvwHistory.Selected, True);
  end;
  Settings.ShowHistoryContents := btnViewFileRevision.Down;
end {TfrmCommit.btnViewFileRevisionClick};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmCommit.chkBranchClick(Sender: TObject);
begin
  EnableControls;
end {TfrmCommit.chkBranchClick};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmCommit.chkExtrasClick(Sender: TObject);
begin
  RefreshFiles;
end {TfrmCommit.chkExtrasClick};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmCommit.DiffExternal(Sender: TObject);
var

  OldFile, NewFile, DiffCommand: TFileName;
  SS: TStringStream;
  FS: TFileStream;
  Contents: string;
  P: TProcess;

  TempFiles: TStringList;

begin
  OldFile := '';
  NewFile := '';
  TempFiles := TStringList.Create;
  try

    if (Sender = FDiffViewer) then begin
      NewFile := FFossil.RootDir + lvwFiles.Selected.Caption;

      // save latest revision to temp
      // create temp file name
      OldFile := GetTempFilename(ChangeFileExt(NewFile, '~' + ExtractFileExt(NewFile)));

      // Fetch the contents of the checked out file
      Contents := FFossil.Run('finfo --print "' + NewFile + '"');
      Contents := StringReplace(Contents, #13#13#10, #13#10, [rfReplaceAll]);
      SS := TStringStream.Create(Contents, 0);
      try
        FS := TFileStream.Create(OldFile, fmCreate);
        try
          FS.CopyFrom(SS, SS.Size);
        finally
          FS.Free;
          TempFiles.Add(OldFile);
        end;
        TFile.SetAttributes(OldFile, [IOUtils.TFileAttribute.faReadOnly, IOUtils.TFileAttribute.faTemporary]);
      finally
        SS.Free;
      end;



































    end;


    // Go compare the two files
    if FileExists(NewFile) and FileExists(OldFile) then begin




      // TODO: if no external diff viewer is defined, use 'gdiff'
      DiffCommand := FFossil.Settings.Values['gdiff-command'];
      if not FileExists(DiffCommand) then begin
        // TODO: ask for executable
        MessageBeep(MB_ICONWARNING);

        Exit;
      end;

      // Launch external diff viewer
      P := TProcess.Create(nil);
      try
        P.CurrentDirectory := FFossil.CurrentDir;







|

















>





>
|
>









<



|















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





>
>
>
>





>







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
procedure TfrmCommit.btnViewFileRevisionClick(Sender: TObject);
begin
  if Assigned(lvwHistory.Selected) then begin
    lvwHistorySelectItem(lvwHistory, lvwHistory.Selected, lvwHistory.Selected.Selected);
  end else begin
    lvwHistorySelectItem(lvwHistory, lvwHistory.Selected, True);
  end;
  AppSettings.ShowHistoryContents := btnViewFileRevision.Down;
end {TfrmCommit.btnViewFileRevisionClick};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmCommit.chkBranchClick(Sender: TObject);
begin
  EnableControls;
end {TfrmCommit.chkBranchClick};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmCommit.chkExtrasClick(Sender: TObject);
begin
  RefreshFiles;
end {TfrmCommit.chkExtrasClick};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmCommit.DiffExternal(Sender: TObject);
var
  TempFiles: TStringList;
  OldFile, NewFile, DiffCommand: TFileName;
  SS: TStringStream;
  FS: TFileStream;
  Contents: string;
  P: TProcess;
  i: Integer;
  Item: TListItem;
  OlderArtifact, NewerArtifact: string;
begin
  OldFile := '';
  NewFile := '';
  TempFiles := TStringList.Create;
  try

    if (Sender = FDiffViewer) then begin
      NewFile := FFossil.RootDir + lvwFiles.Selected.Caption;


      // create temp file name
      OldFile := GetTempFilename(ChangeFileExt(NewFile, '~' + ExtractFileExt(NewFile)));

      // Fetch the contents of the latest checked-in revision
      Contents := FFossil.Run('finfo --print "' + NewFile + '"');
      Contents := StringReplace(Contents, #13#13#10, #13#10, [rfReplaceAll]);
      SS := TStringStream.Create(Contents, 0);
      try
        FS := TFileStream.Create(OldFile, fmCreate);
        try
          FS.CopyFrom(SS, SS.Size);
        finally
          FS.Free;
          TempFiles.Add(OldFile);
        end;
        TFile.SetAttributes(OldFile, [IOUtils.TFileAttribute.faReadOnly, IOUtils.TFileAttribute.faTemporary]);
      finally
        SS.Free;
      end;

    end else if (Sender = FHistoryDiff) then begin
      // Find out the first and last selected revisions;
      //  then show a diff of those two revisions
      OlderArtifact := '';
      NewerArtifact := '';
      for i := 0 to lvwHistory.Items.Count - 1 do begin
        Item := lvwHistory.Items[i];
        if Item.Selected then begin
          if NewerArtifact = '' then begin
            NewerArtifact := Item.Subitems[3];
            NewFile := lvwFiles.Selected.Caption;
            NewFile := ChangeFileExt(NewFile, '-' + Item.SubItems[0] + '-' + Item.Caption + ExtractFileExt(NewFile));
          end else begin
            OlderArtifact := Item.Subitems[3];
            OldFile := lvwFiles.Selected.Caption;
            OldFile := ChangeFileExt(OldFile, '-' + Item.SubItems[0] + '-' + Item.Caption + ExtractFileExt(OldFile));
          end;
        end;
      end;

      if (OlderArtifact <> '') and (NewerArtifact <> '') then begin
        // Fetch the contents of the older revision
        OldFile := GetTempFilename(OldFile);
        FFossil.Run('artifact ' + OlderArtifact + ' "' + OldFile + '"');
        TempFiles.Add(OldFile);

        // Fetch the contents of the Newer revision
        NewFile := GetTempFilename(NewFile);
        FFossil.Run('artifact ' + NewerArtifact + ' "' + NewFile + '"');
        TempFiles.Add(NewFile);
      end else begin
        OldFile := '';
        NewFile := '';
      end;
    end;


    // Go compare the two files
    if FileExists(NewFile) and FileExists(OldFile) then begin
      for i := 0 to TempFiles.Count - 1 do begin
        TFile.SetAttributes(TempFiles[i], [IOUtils.TFileAttribute.faReadOnly, IOUtils.TFileAttribute.faTemporary]);
      end;

      // TODO: if no external diff viewer is defined, use 'gdiff'
      DiffCommand := FFossil.Settings.Values['gdiff-command'];
      if not FileExists(DiffCommand) then begin
        // TODO: ask for executable
        MessageBeep(MB_ICONWARNING);
        TLauncherCleaner.Create(nil, TempFiles);
        Exit;
      end;

      // Launch external diff viewer
      P := TProcess.Create(nil);
      try
        P.CurrentDirectory := FFossil.CurrentDir;
747
748
749
750
751
752
753

754
755
756
757
758





759
760
761
762
763
764
765
  if not tsDiff.TabVisible then
    Exit;
  if Length(FDiffViewer.rteDiff.Text) > 0 then
    Exit;
  if not Assigned(lvwFiles.Selected) then
    Exit;


  Output := FFossil.Run('diff -i -N "' + FFossil.RootDir + lvwFiles.Selected.Caption + '"');
  Output := StringReplace(Output, #13#13#10, #13#10, [rfReplaceAll]);

  // Apply highlighting to the output
  FDiffViewer.SetContents(Output);





end {TfrmCommit.tsDiffShow};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmCommit.tshHistoryShow(Sender: TObject);
var
  rxHistory: TRegExpr;
  i: Integer;







>
|
|

|
|
>
>
>
>
>







791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
  if not tsDiff.TabVisible then
    Exit;
  if Length(FDiffViewer.rteDiff.Text) > 0 then
    Exit;
  if not Assigned(lvwFiles.Selected) then
    Exit;

  try
    Output := FFossil.Run('diff -i -N "' + FFossil.RootDir + lvwFiles.Selected.Caption + '"');
    Output := StringReplace(Output, #13#13#10, #13#10, [rfReplaceAll]);

    // Apply highlighting to the output
    FDiffViewer.SetContents(Output);
  except
    on E: Exception do begin
      FDiffViewer.SetException(E);
    end;
  end;
end {TfrmCommit.tsDiffShow};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmCommit.tshHistoryShow(Sender: TObject);
var
  rxHistory: TRegExpr;
  i: Integer;
852
853
854
855
856
857
858

859
860

861
862
863
864
865
866
867
868
869
870
871
872
873

874
875
876
877
878

879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
    lvwHistory.Items.EndUpdate;
  end;
end {TfrmCommit.tshHistoryShow};





{ TLauncherCleaner }


constructor TLauncherCleaner.Create(const Process: TProcess; const TempFiles: TStrings = nil);
begin
  FreeOnTerminate := True;

  FTempFiles := TStringList.Create;
  FTempFiles.Assign(TempFiles);
  FProcess := Process;

  inherited Create(False);
end {TLauncherCleaner.Create};

destructor TLauncherCleaner.Destroy;
begin

  FProcess.Free;
  FTempFiles.Free;
  inherited;
end {TLauncherCleaner.Destroy};


procedure TLauncherCleaner.Execute;
var
  i: Integer;
begin
  // Now wait until either the thread or the process is terminated
  while FProcess.Running and not Self.Terminated do begin
    Sleep(250);
  end;

  // Delete the temporary files
  for i := 0 to FTempFiles.Count - 1 do begin
    TFile.SetAttributes(FTempFiles[i], []);
    TFile.Delete(FTempFiles[i]);
  end;
end {TLauncherCleaner.Execute};

end.







>


>










|


>
|




>





|











902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
    lvwHistory.Items.EndUpdate;
  end;
end {TfrmCommit.tshHistoryShow};




{ ================================================================================================ }
{ TLauncherCleaner }

{ ------------------------------------------------------------------------------------------------ }
constructor TLauncherCleaner.Create(const Process: TProcess; const TempFiles: TStrings = nil);
begin
  FreeOnTerminate := True;

  FTempFiles := TStringList.Create;
  FTempFiles.Assign(TempFiles);
  FProcess := Process;

  inherited Create(False);
end {TLauncherCleaner.Create};
{ ------------------------------------------------------------------------------------------------ }
destructor TLauncherCleaner.Destroy;
begin
  if Assigned(FProcess) then
    FProcess.Free;
  FTempFiles.Free;
  inherited;
end {TLauncherCleaner.Destroy};

{ ------------------------------------------------------------------------------------------------ }
procedure TLauncherCleaner.Execute;
var
  i: Integer;
begin
  // Now wait until either the thread or the process is terminated
  while Assigned(FProcess) and FProcess.Running and not Self.Terminated do begin
    Sleep(250);
  end;

  // Delete the temporary files
  for i := 0 to FTempFiles.Count - 1 do begin
    TFile.SetAttributes(FTempFiles[i], []);
    TFile.Delete(FTempFiles[i]);
  end;
end {TLauncherCleaner.Execute};

end.
Name change from src/AppSettings.pas to src/UAppSettings.pas.
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
unit AppSettings;

////////////////////////////////////////////////////////////////////////////////////////////////////
interface
uses
  L_AppSettings;

type
  TAppSettings = class(TCustomAppSettings)
  private
    FLastCheckoutFolder: string;
    FFossilExePath: string;
    FShowHistoryContents: boolean;
  published
    property FossilExePath: string read FFossilExePath write FFossilExePath;
    property LastCheckoutFolder: string read FLastCheckoutFolder write FLastCheckoutFolder;
    property ShowHistoryContents: boolean read FShowHistoryContents write FShowHistoryContents;
  end;

var
  Settings: TAppSettings;

////////////////////////////////////////////////////////////////////////////////////////////////////
implementation

uses
  SysUtils,
  L_FileUtils;

{ TAppSettings }



////////////////////////////////////////////////////////////////////////////////////////////////////
var
  Filename: string;

initialization
  // TODO: use writeable SpecialFolder specific to current user AND machine
  Filename := ChangeFileExt(GetDllOrExePath, '.settings');
  if FileExists(Filename) then begin
    Settings := TAppSettings.Create(Filename);
  end else begin
    Settings := TAppSettings.Create;
  end;

finalization
  Settings.SaveToFile(Filename);
  FreeAndNil(Settings);

end.
|



















|




















|

|



|
|


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
unit UAppSettings;

////////////////////////////////////////////////////////////////////////////////////////////////////
interface
uses
  L_AppSettings;

type
  TAppSettings = class(TCustomAppSettings)
  private
    FLastCheckoutFolder: string;
    FFossilExePath: string;
    FShowHistoryContents: boolean;
  published
    property FossilExePath: string read FFossilExePath write FFossilExePath;
    property LastCheckoutFolder: string read FLastCheckoutFolder write FLastCheckoutFolder;
    property ShowHistoryContents: boolean read FShowHistoryContents write FShowHistoryContents;
  end;

var
  AppSettings: TAppSettings;

////////////////////////////////////////////////////////////////////////////////////////////////////
implementation

uses
  SysUtils,
  L_FileUtils;

{ TAppSettings }



////////////////////////////////////////////////////////////////////////////////////////////////////
var
  Filename: string;

initialization
  // TODO: use writeable SpecialFolder specific to current user AND machine
  Filename := ChangeFileExt(GetDllOrExePath, '.settings');
  if FileExists(Filename) then begin
    AppSettings := TAppSettings.Create(Filename);
  end else begin
    AppSettings := TAppSettings.Create;
  end;

finalization
  AppSettings.SaveToFile(Filename);
  FreeAndNil(AppSettings);

end.
Changes to src/fviewcontents.dfm.
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
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate


  PixelsPerInch = 96
  TextHeight = 13
  object pgcContentTypes: TPageControl
    Left = 0
    Top = 0
    Width = 434
    Height = 320
    ActivePage = tshText
    Align = alClient
    Style = tsFlatButtons
    TabOrder = 0
    object tshText: TTabSheet
      Caption = 'Text'




      object mmoContents: TMemo
        AlignWithMargins = True
        Left = 3
        Top = 3
        Width = 420
        Height = 283
        Align = alClient







>
>













>
>
>
>







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
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  ExplicitWidth = 320
  ExplicitHeight = 240
  PixelsPerInch = 96
  TextHeight = 13
  object pgcContentTypes: TPageControl
    Left = 0
    Top = 0
    Width = 434
    Height = 320
    ActivePage = tshText
    Align = alClient
    Style = tsFlatButtons
    TabOrder = 0
    object tshText: TTabSheet
      Caption = 'Text'
      ExplicitLeft = 0
      ExplicitTop = 0
      ExplicitWidth = 0
      ExplicitHeight = 0
      object mmoContents: TMemo
        AlignWithMargins = True
        Left = 3
        Top = 3
        Width = 420
        Height = 283
        Align = alClient
Changes to src/fviewcontents.pas.
100
101
102
103
104
105
106

107
108
109
110
111
112
113

  // Populate the controls
  mmoContents.Clear;
  imgContents.Picture := nil;
  mmoHexContents.Clear;

  // try reading the content as an image

  try
    FContent.Position := 0;

    // If it’s an existing file, try loading it from its original location
    if (FContent is TFileStream) then begin
      if SameText(ExtractFileExt(Filename), ExtractFileExt(TFileStream(FContent).FileName)) then begin
        imgContents.Picture.LoadFromFile(Filename);







>







100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

  // Populate the controls
  mmoContents.Clear;
  imgContents.Picture := nil;
  mmoHexContents.Clear;

  // try reading the content as an image
  TempFilename := '';
  try
    FContent.Position := 0;

    // If it’s an existing file, try loading it from its original location
    if (FContent is TFileStream) then begin
      if SameText(ExtractFileExt(Filename), ExtractFileExt(TFileStream(FContent).FileName)) then begin
        imgContents.Picture.LoadFromFile(Filename);
129
130
131
132
133
134
135


136
137
138
139
140
141
142
    end;
    tshImage.TabVisible := True;
    pgcContentTypes.ActivePage := tshImage;
  except
    imgContents.Picture := nil;
    tshImage.TabVisible := False;
  end;



  tshText.TabVisible := not tshImage.TabVisible;
  if tshText.TabVisible then begin
    mmoContents.Lines.BeginUpdate;
    try
      // Read the contents as text
      FContent.Position := 0;







>
>







130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
    end;
    tshImage.TabVisible := True;
    pgcContentTypes.ActivePage := tshImage;
  except
    imgContents.Picture := nil;
    tshImage.TabVisible := False;
  end;
  if TempFilename <> '' then
    DeleteFile(TempFilename);

  tshText.TabVisible := not tshImage.TabVisible;
  if tshText.TabVisible then begin
    mmoContents.Lines.BeginUpdate;
    try
      // Read the contents as text
      FContent.Position := 0;
Changes to src/fviewdiff.dfm.
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
    Font.Name = 'Consolas'
    Font.Style = []
    ParentFont = False
    PlainText = True
    ReadOnly = True
    ScrollBars = ssBoth
    TabOrder = 1
    ExplicitLeft = 8
    ExplicitTop = 112
    ExplicitHeight = 200
  end
  object pnlButtons: TPanel
    Left = 0
    Top = 0
    Width = 434
    Height = 41
    Align = alTop
    BevelOuter = bvLowered
    TabOrder = 0
    Visible = False
    ExplicitLeft = 160
    ExplicitTop = 24
    ExplicitWidth = 185
    object btnExternal: TButton
      Left = 16
      Top = 8
      Width = 121
      Height = 25
      Caption = 'E&xternal compare...'
      TabOrder = 0







<
<
<










<
<
<







27
28
29
30
31
32
33



34
35
36
37
38
39
40
41
42
43



44
45
46
47
48
49
50
    Font.Name = 'Consolas'
    Font.Style = []
    ParentFont = False
    PlainText = True
    ReadOnly = True
    ScrollBars = ssBoth
    TabOrder = 1



  end
  object pnlButtons: TPanel
    Left = 0
    Top = 0
    Width = 434
    Height = 41
    Align = alTop
    BevelOuter = bvLowered
    TabOrder = 0
    Visible = False



    object btnExternal: TButton
      Left = 16
      Top = 8
      Width = 121
      Height = 25
      Caption = 'E&xternal compare...'
      TabOrder = 0
Changes to src/fviewdiff.pas.
16
17
18
19
20
21
22

23
24
25
26
27
28
29
    { Private declarations }
    FOnExternalDiff: TNotifyEvent;
    procedure SetOnExternalDiff(const Value: TNotifyEvent);
  public
    { Public declarations }
    procedure SetContents(const DiffText: string);
    procedure ClearContents;


    property OnExternalDiff: TNotifyEvent read FOnExternalDiff  write SetOnExternalDiff;
  end;


implementation
uses







>







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    { Private declarations }
    FOnExternalDiff: TNotifyEvent;
    procedure SetOnExternalDiff(const Value: TNotifyEvent);
  public
    { Public declarations }
    procedure SetContents(const DiffText: string);
    procedure ClearContents;
    procedure SetException(const E: Exception);

    property OnExternalDiff: TNotifyEvent read FOnExternalDiff  write SetOnExternalDiff;
  end;


implementation
uses
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
      SR.Free;
    end;
  finally
    rteDiff.Lines.EndUpdate;
    SS.Free;
  end;


  Visible := True;
end {TfrmViewDiff.SetContents};











{ ------------------------------------------------------------------------------------------------ }
procedure TfrmViewDiff.SetOnExternalDiff(const Value: TNotifyEvent);
begin
  FOnExternalDiff := Value;
  pnlButtons.Visible := Assigned(FOnExternalDiff);
end {TfrmViewDiff.SetOnExternalDiff};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmViewDiff.btnExternalClick(Sender: TObject);
begin
  if Assigned(FOnExternalDiff) then begin
    FOnExternalDiff(Self);
  end;

end {TfrmViewDiff.btnExternalClick};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmViewDiff.ClearContents;
begin
  rteDiff.Clear;
  Visible := False;
end {TfrmViewDiff.ClearContents};

end.







>


>
>
>
>
>
>
>
>
>
>














>










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
      SR.Free;
    end;
  finally
    rteDiff.Lines.EndUpdate;
    SS.Free;
  end;

  pnlButtons.Visible := Assigned(FOnExternalDiff);
  Visible := True;
end {TfrmViewDiff.SetContents};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmViewDiff.SetException(const E: Exception);
begin
  rteDiff.Clear;
  rteDiff.SelAttributes.Color := clRed;
  rteDiff.SelText := E.Message;
  pnlButtons.Visible := False;
  Visible := True;
end {TfrmViewDiff.SetException};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmViewDiff.SetOnExternalDiff(const Value: TNotifyEvent);
begin
  FOnExternalDiff := Value;
  pnlButtons.Visible := Assigned(FOnExternalDiff);
end {TfrmViewDiff.SetOnExternalDiff};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmViewDiff.btnExternalClick(Sender: TObject);
begin
  if Assigned(FOnExternalDiff) then begin
    FOnExternalDiff(Self);
  end;
  rteDiff.SetFocus;
end {TfrmViewDiff.btnExternalClick};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmViewDiff.ClearContents;
begin
  rteDiff.Clear;
  Visible := False;
end {TfrmViewDiff.ClearContents};

end.
Changes to src/lib/L_FileUtils.pas.
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
unit L_FileUtils;


interface

uses
  SysUtils;

function GetExePath: string;
function GetDllOrExePath: string;
function GetModulePath(const HModule: HINST): string;
function GetTempPath(const DllSpecific: Boolean = True): string;
function CreateNewFilename(const Filename: TFilename): string;
function GetTempFilename(const Filename: string; const DllSpecific: Boolean = True): string;


implementation
uses
  Windows, IOUtils, Types, Classes;



function GetExePath: string;
begin
  Result := GetModulePath(0);
end {GetExePath};

function GetDllOrExePath: string;
begin
  Result := GetModulePath(HInstance);
end {GetDllOrExePath};

function GetModulePath(const HModule: HINST): string;
var
  PBuffer: PChar;
  BufferSize, RetVal: Cardinal;
begin
  BufferSize := MAX_PATH;
  repeat


>










|

>





>




|




|







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
unit L_FileUtils;

////////////////////////////////////////////////////////////////////////////////////////////////////
interface

uses
  SysUtils;

function GetExePath: string;
function GetDllOrExePath: string;
function GetModulePath(const HModule: HINST): string;
function GetTempPath(const DllSpecific: Boolean = True): string;
function CreateNewFilename(const Filename: TFilename): string;
function GetTempFilename(Filename: string = ''; const DllSpecific: Boolean = True): string;

////////////////////////////////////////////////////////////////////////////////////////////////////
implementation
uses
  Windows, IOUtils, Types, Classes;


{ ------------------------------------------------------------------------------------------------ }
function GetExePath: string;
begin
  Result := GetModulePath(0);
end {GetExePath};
{ ------------------------------------------------------------------------------------------------ }
function GetDllOrExePath: string;
begin
  Result := GetModulePath(HInstance);
end {GetDllOrExePath};
{ ------------------------------------------------------------------------------------------------ }
function GetModulePath(const HModule: HINST): string;
var
  PBuffer: PChar;
  BufferSize, RetVal: Cardinal;
begin
  BufferSize := MAX_PATH;
  repeat
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
    end else begin
      RaiseLastOSError;
    end;
  until False;
end {GetModulePath};



function GetTempPath(const DllSpecific: Boolean = True): string;
var
  HModule: HINST;
begin
  if DllSpecific then begin
    HModule := HInstance;
  end else begin
    HModule := 0;
  end;
  Result := TPath.GetFileNameWithoutExtension(GetModulePath(HModule));
  Result := TPath.Combine(TPath.GetTempPath, Result);
  ForceDirectories(Result);
  Result := IncludeTrailingPathDelimiter(Result);
end {GetTempPath};



function CreateNewFilename(const Filename: TFilename): string;
var
  Base, Ext, Seq: string;
  Sequence, Index: integer;
begin
  Ext := TPath.GetExtension(Filename);
  Base := Copy(Filename, 1, Length(Filename) - Length(Ext));







>
















>







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
    end else begin
      RaiseLastOSError;
    end;
  until False;
end {GetModulePath};


{ ------------------------------------------------------------------------------------------------ }
function GetTempPath(const DllSpecific: Boolean = True): string;
var
  HModule: HINST;
begin
  if DllSpecific then begin
    HModule := HInstance;
  end else begin
    HModule := 0;
  end;
  Result := TPath.GetFileNameWithoutExtension(GetModulePath(HModule));
  Result := TPath.Combine(TPath.GetTempPath, Result);
  ForceDirectories(Result);
  Result := IncludeTrailingPathDelimiter(Result);
end {GetTempPath};


{ ------------------------------------------------------------------------------------------------ }
function CreateNewFilename(const Filename: TFilename): string;
var
  Base, Ext, Seq: string;
  Sequence, Index: integer;
begin
  Ext := TPath.GetExtension(Filename);
  Base := Copy(Filename, 1, Length(Filename) - Length(Ext));
93
94
95
96
97
98
99

100


101










102
103
104
105
106

107
108
109
110
111
112
113
  while TFile.Exists(Result) do begin
    Inc(Sequence);
    Result := Base + ' (' + IntToStr(Sequence) + ')' + Ext;
  end;
end {CreateNewFilename};



function GetTempFilename(const Filename: string; const DllSpecific: Boolean = True): string;


begin










  Result := TPath.Combine(GetTempPath(DllSpecific), TPath.GetFileName(Filename));
  Result := CreateNewFilename(Result);
end {GetTempFilename};



procedure EmptyDirectory(const Path: string);
var
  Entries: TStringDynArray;
  i: Integer;
begin
  // Delete all the files
  Entries := TDirectory.GetFiles(Path, '*', TSearchOption.soAllDirectories);







>
|
>
>

>
>
>
>
>
>
>
>
>
>
|




>







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
  while TFile.Exists(Result) do begin
    Inc(Sequence);
    Result := Base + ' (' + IntToStr(Sequence) + ')' + Ext;
  end;
end {CreateNewFilename};


{ ------------------------------------------------------------------------------------------------ }
function GetTempFilename(Filename: string = ''; const DllSpecific: Boolean = True): string;
var
  i: Integer;
begin
  Filename := Trim(TPath.GetFileName(Filename));
  if Length(Filename) = 0 then begin
    Filename := TPath.GetRandomFileName;
  end else begin
    for i := 1 to Length(Filename) do begin
      if not TPath.IsValidFileNameChar(Filename[i]) then
        Filename[i] := '_';
    end;
  end;

  Result := TPath.Combine(GetTempPath(DllSpecific), Filename);
  Result := CreateNewFilename(Result);
end {GetTempFilename};


{ ------------------------------------------------------------------------------------------------ }
procedure EmptyDirectory(const Path: string);
var
  Entries: TStringDynArray;
  i: Integer;
begin
  // Delete all the files
  Entries := TDirectory.GetFiles(Path, '*', TSearchOption.soAllDirectories);
127
128
129
130
131
132
133

134
135
136
137
138
139
140
141
142
143
144
145
      // Register the directory to be deleted on reboot
      MoveFileEx(PChar(Entries[i]), nil, MOVEFILE_DELAY_UNTIL_REBOOT);
    end;
  end;
end {EmptyDirectory};



initialization

finalization
  try
    EmptyDirectory(GetTempPath(True));
  except
    on E: Exception do begin // Ignore the error
      OutputDebugString(PChar(E.Message + ' [' + E.ClassName + ']'));
    end;
  end;

end.







>












146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
      // Register the directory to be deleted on reboot
      MoveFileEx(PChar(Entries[i]), nil, MOVEFILE_DELAY_UNTIL_REBOOT);
    end;
  end;
end {EmptyDirectory};


////////////////////////////////////////////////////////////////////////////////////////////////////
initialization

finalization
  try
    EmptyDirectory(GetTempPath(True));
  except
    on E: Exception do begin // Ignore the error
      OutputDebugString(PChar(E.Message + ' [' + E.ClassName + ']'));
    end;
  end;

end.
Added src/lib/L_StoreFormPos.pas.






















































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
unit L_StoreFormPos;

interface

uses
  Forms;

function StoreFormPosition(AForm: TForm; const AInstanceName: string = ''): boolean;
function StoreFormPositionShort(AForm: TForm; const AInstanceName: string = ''): boolean;
function RestoreFormPosition(AForm: TForm; const AInstanceName: string = ''): boolean;
function RestoreFormPositionShort(AForm: TForm; const AInstanceName: string = ''): boolean;

function GetFormLongRegistryKey(const AForm: TForm; const AInstanceName: string = ''): string;
function GetFormShortRegistryKey(const AForm: TForm; const AInstanceName: string = ''): string;

type
  TRestorableForm = class (TForm)
  public
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  end;

implementation

uses
  Types, Registry, Windows, Math,
   L_Data;

type
  TFormData = record
  public
    Key            : String;
    NormalPosition : TRect;
    Maximized      : Boolean;

  public
    function SaveToRegistry: Boolean;
    function LoadFromRegistry: Boolean;
  end;

function TFormData.LoadFromRegistry: Boolean;
var
  Registry: TRegistry;
begin
  Result := False;
  Registry := TRegistry.Create;
  try
    Registry.RootKey := HKEY_CURRENT_USER;
    if Registry.OpenKey(Key, False) then begin
      NormalPosition.Left := Registry.ReadInteger('Left');
      NormalPosition.Top := Registry.ReadInteger('Top');
      NormalPosition.Right := Registry.ReadInteger('Right');
      NormalPosition.Bottom := Registry.ReadInteger('Bottom');
      Maximized := Registry.ReadBool('Maximized');
      Result := True;
    end;
  finally
    Registry.Free;
  end;
end;

function TFormData.SaveToRegistry: Boolean;
var
  Registry: TRegistry;
begin
  Registry := TRegistry.Create;
  try
    Registry.RootKey := HKEY_CURRENT_USER;
    if Registry.OpenKey(Key, True) then begin
      Registry.WriteInteger('Left',   NormalPosition.Left);
      Registry.WriteInteger('Top',    NormalPosition.Top);
      Registry.WriteInteger('Right',  NormalPosition.Right);
      Registry.WriteInteger('Bottom', NormalPosition.Bottom);
      Registry.WriteBool('Maximized', Maximized);
    end;
    Result := True;
  finally
    Registry.Free;
  end;
end;

///////////////////////////////////////////////////////////////////////////////
procedure TestFormPos(AForm :TForm);
const
  MIN_PIXELS_VISIBLE = 150;
var
  bValid :Boolean;
begin
  bValid := (AForm.Left + AForm.Width - MIN_PIXELS_VISIBLE) >= (Screen.DesktopRect.Left);
  bValid := bValid and ((AForm.Left + MIN_PIXELS_VISIBLE) <= (Screen.DesktopRect.Right));
  bValid := bValid and ((AForm.Top + MIN_PIXELS_VISIBLE) <= (Screen.DesktopRect.Bottom));
  bValid := bValid and (AForm.Top >= Screen.DesktopRect.Top);

  if not bValid then begin
    AForm.Left := (Screen.Width div 2) - (AForm.Width div 2);
    AForm.Top := (Screen.Height div 2) - (AForm.Height div 2);
  end; {if}
end; {TestFormPos}

///////////////////////////////////////////////////////////////////////////////
function DoStoreFormPosition(AForm: TForm; const AKey: string): boolean;
var
  FormData        : TFormData;
  WindowPlacement : TWindowPlacement;
begin
  Result := False;
  if not Assigned(AForm) then
    Exit;

  if not GetWindowPlacement(AForm.Handle, @WindowPlacement) then
    Exit;

  FormData.Key            := AKey;
  FormData.NormalPosition := WindowPlacement.rcNormalPosition;
  FormData.Maximized      := AForm.WindowState = wsMaximized;

  Result := FormData.SaveToRegistry;
end;

const
  cDefaultKey = '\Software\Dgmr\';

function GetFormRegistryKey(const AForm: TForm; const AProgramName, AInstanceName: string): string;
begin
  Result := cDefaultKey + AProgramName;
  if AForm=nil then
    Result := Result + '\'
  else if AInstanceName = '' then
    Result := Result + '\' + AForm.ClassName
  else
    Result := Result + '\' + AForm.ClassName + '#' + AInstanceName;
end;

function GetFormLongRegistryKey(const AForm: TForm; const AInstanceName: string = ''): string;
begin
  Result := GetFormRegistryKey(AForm, L_Data.ProgramName, AInstanceName);
end;

function GetFormShortRegistryKey(const AForm: TForm; const AInstanceName: string = ''): string;
begin
  Result := GetFormRegistryKey(AForm, L_Data.ProgramShort, AInstanceName);
end;

function StoreFormPositionShort(AForm: TForm; const AInstanceName: string = ''): boolean;
begin
  Result := DoStoreFormPosition(AForm, GetFormShortRegistryKey(AForm, AInstanceName));
end;

function StoreFormPosition(AForm: TForm; const AInstanceName: string = ''): boolean;
begin
  Result := DoStoreFormPosition(AForm, GetFormLongRegistryKey(AForm, AInstanceName));
end;

///////////////////////////////////////////////////////////////////////////////

function DoRestoreFormPosition(AForm: TForm; const AKey: string): boolean;
var
  FormData : TFormData;
begin
  Result := False;

  if not Assigned(AForm) then
    Exit;

  FormData.Key := AKey;

  if not FormData.LoadFromRegistry then begin
    AForm.Left := (Screen.Width div 2) - (AForm.Width div 2);
    AForm.Top := (Screen.Height div 2) - (AForm.Height div 2);

    Exit;
  end; {if}

  AForm.Position := poDesigned; //disables automatic OnCreate positioning
  AForm.DefaultMonitor := dmDesktop; //disables automatic OnCreate positioning

  AForm.Left   := FormData.NormalPosition.Left;
  AForm.Top    := FormData.NormalPosition.Top;
  AForm.Width  := FormData.NormalPosition.Right - FormData.NormalPosition.Left;
  AForm.Height := FormData.NormalPosition.Bottom - FormData.NormalPosition.Top;

  TestFormPos(AForm); //make sure its visible

  if FormData.Maximized then
    AForm.WindowState := wsMaximized;

  Result := True;
end;

function RestoreFormPositionShort(AForm: TForm; const AInstanceName: string = ''): boolean;
begin
  Result := DoRestoreFormPosition(AForm, GetFormShortRegistryKey(AForm, AInstanceName));
end;

function RestoreFormPosition(AForm: TForm; const AInstanceName: string = ''): boolean;
begin
  Result := DoRestoreFormPosition(AForm, GetFormLongRegistryKey(AForm, AInstanceName));
end;

///////////////////////////////////////////////////////////////////////////////
{ TRestorableForm }

procedure TRestorableForm.AfterConstruction;
begin
  inherited;
  RestoreFormPosition(self);
end;


procedure TRestorableForm.BeforeDestruction;
begin
  StoreFormPosition(self);
  inherited;
end;

{ TFormData }


end.
Changes to src/prj/d2009/Paleo.dpr.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
program Paleo;

uses
  Forms,
  FCommit in '..\..\FCommit.pas' {frmCommit},
  fviewcontents in '..\..\fviewcontents.pas' {frmViewContents},
  fviewdiff in '..\..\fviewdiff.pas' {frmViewDiff},
  AppSettings in '..\..\AppSettings.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.Title := 'Fossil UI';







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
program Paleo;

uses
  Forms,
  FCommit in '..\..\FCommit.pas' {frmCommit},
  fviewcontents in '..\..\fviewcontents.pas' {frmViewContents},
  fviewdiff in '..\..\fviewdiff.pas' {frmViewDiff},
  UAppSettings in '..\..\UAppSettings.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.Title := 'Fossil UI';
Changes to src/prj/d2009/Paleo.dproj.
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
				<FormType>dfm</FormType>
				<Form>frmViewContents</Form>
			</DCCReference>
			<DCCReference Include="..\..\fviewdiff.pas">
				<FormType>dfm</FormType>
				<Form>frmViewDiff</Form>
			</DCCReference>
			<DCCReference Include="..\..\AppSettings.pas"/>
			<BuildConfiguration Include="Base">
				<Key>Base</Key>
			</BuildConfiguration>
			<BuildConfiguration Include="Debug">
				<Key>Cfg_2</Key>
				<CfgParent>Base</CfgParent>
			</BuildConfiguration>







|







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
				<FormType>dfm</FormType>
				<Form>frmViewContents</Form>
			</DCCReference>
			<DCCReference Include="..\..\fviewdiff.pas">
				<FormType>dfm</FormType>
				<Form>frmViewDiff</Form>
			</DCCReference>
			<DCCReference Include="..\..\UAppSettings.pas"/>
			<BuildConfiguration Include="Base">
				<Key>Base</Key>
			</BuildConfiguration>
			<BuildConfiguration Include="Debug">
				<Key>Cfg_2</Key>
				<CfgParent>Base</CfgParent>
			</BuildConfiguration>
Changes to src/u_fossil.pas.
8
9
10
11
12
13
14


15
16
17
18
19
20
21

uses
  Classes, SysUtils;

type
  TFossilStatus = (fsUnversioned, fsUnchanged, fsAdded, fsEdited, fsDeleted, fsIgnored);



const
  cFossilStatuses: array[0..5] of string = ('extra', 'unchanged', 'added', 'edited', 'deleted', 'ignored');


type
{ ------------------------------------------------------------------------------------------------ }
  { TFossil }







>
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

uses
  Classes, SysUtils;

type
  TFossilStatus = (fsUnversioned, fsUnchanged, fsAdded, fsEdited, fsDeleted, fsIgnored);

  EFossilError = class(Exception);

const
  cFossilStatuses: array[0..5] of string = ('extra', 'unchanged', 'added', 'edited', 'deleted', 'ignored');


type
{ ------------------------------------------------------------------------------------------------ }
  { TFossil }
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
      try
        S.CopyFrom(EMS, EMS.Size);
        S.Position := 0;
        Msg := S.ReadString(S.Size);
        if SameFileName(Copy(Msg, 2, Length(FExePath)), FExePath) then begin
          Msg := Copy(Msg, Length(FExePath) + 3, Length(Msg));
        end;
        raise Exception.Create(Msg);
      finally
        S.Free;
      end;
    end;
  finally
    OMS.Free;
    EMS.Free;







|







180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
      try
        S.CopyFrom(EMS, EMS.Size);
        S.Position := 0;
        Msg := S.ReadString(S.Size);
        if SameFileName(Copy(Msg, 2, Length(FExePath)), FExePath) then begin
          Msg := Copy(Msg, Length(FExePath) + 3, Length(Msg));
        end;
        raise EFossilError.Create(Msg);
      finally
        S.Free;
      end;
    end;
  finally
    OMS.Free;
    EMS.Free;
276
277
278
279
280
281
282

283
284
285
286
287
288
289
  try
    Options := '';
    if Opened and Closed then
      Options := '--all'
    else if Closed then
      Options := '--closed';
    Result.Text := Run('branch list' + Options);

  except
    FreeAndNil(Result);
    raise;
  end;
end;

{ ------------------------------------------------------------------------------------------------ }







>







278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
  try
    Options := '';
    if Opened and Closed then
      Options := '--all'
    else if Closed then
      Options := '--closed';
    Result.Text := Run('branch list' + Options);
    // TODO: filter something; remove the asterisk from the active branch
  except
    FreeAndNil(Result);
    raise;
  end;
end;

{ ------------------------------------------------------------------------------------------------ }