Check-in [027e5c24c7]
Not logged in

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

Overview
Comment:ZPreview: made auto-updater a tad more generic (for re-use in other Fossil-based projects). Added button to open the webpage of a specific release, even if it's older than the current version.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | feature/auto-update
Files: files | file ages | folders
SHA1:027e5c24c7e9190f1e17d7f4c2193e8627d1e5f3
User & Date: tinus 2015-02-25 13:09:24
Context
2015-02-25
14:40
ZPreview auto-update appears to work: downloads the zip file, replaces the existing files by those in the zip, and restarts. check-in: 8b24d78ff0 user: tinus tags: feature/auto-update
13:09
ZPreview: made auto-updater a tad more generic (for re-use in other Fossil-based projects). Added button to open the webpage of a specific release, even if it's older than the current version. check-in: 027e5c24c7 user: tinus tags: feature/auto-update
11:45
ZPreview auto-updater lists all online versions. check-in: 661d9a8934 user: tinus tags: feature/auto-update
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ZPreview/src/Delphi/F_AutoUpdate.dfm.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
..
98
99
100
101
102
103
104










105

  DesignSize = (
    576
    448)
  PixelsPerInch = 96
  TextHeight = 13
  object lblVersions: TLabel
    Left = 8
    Top = 29
    Width = 90
    Height = 13
    Caption = '&Available versions:'
  end
  object lvwVersions: TListView
    Left = 8
    Top = 48
    Width = 560
    Height = 170
    Anchors = [akLeft, akTop, akRight]
    Columns = <
      item
        Caption = 'Publication date'
        Width = 100
      end
      item
................................................................................
      item
        Caption = 'Title'
        Width = 100
      end>
    ColumnClick = False
    Groups = <
      item
        Header = 'New version(s)'
        GroupID = 0
        State = [lgsNormal]
        HeaderAlign = taLeftJustify
        FooterAlign = taLeftJustify
        TitleImage = -1
      end
      item
................................................................................
    Height = 32
    Anchors = [akRight, akBottom]
    Cancel = True
    Caption = '&Cancel'
    ModalResult = 2
    TabOrder = 3
  end










end








|






|

|







 







|







 







>
>
>
>
>
>
>
>
>
>
|
>
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
..
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  DesignSize = (
    576
    448)
  PixelsPerInch = 96
  TextHeight = 13
  object lblVersions: TLabel
    Left = 8
    Top = 8
    Width = 90
    Height = 13
    Caption = '&Available versions:'
  end
  object lvwVersions: TListView
    Left = 8
    Top = 27
    Width = 560
    Height = 191
    Anchors = [akLeft, akTop, akRight]
    Columns = <
      item
        Caption = 'Publication date'
        Width = 100
      end
      item
................................................................................
      item
        Caption = 'Title'
        Width = 100
      end>
    ColumnClick = False
    Groups = <
      item
        Header = 'Newer versions'
        GroupID = 0
        State = [lgsNormal]
        HeaderAlign = taLeftJustify
        FooterAlign = taLeftJustify
        TitleImage = -1
      end
      item
................................................................................
    Height = 32
    Anchors = [akRight, akBottom]
    Cancel = True
    Caption = '&Cancel'
    ModalResult = 2
    TabOrder = 3
  end
  object btnWebpage: TButton
    Left = 8
    Top = 408
    Width = 112
    Height = 32
    Anchors = [akLeft, akBottom]
    Caption = '&View webpage'
    Enabled = False
    TabOrder = 4
    OnClick = btnWebpageClick
  end
end

Changes to ZPreview/src/Delphi/F_AutoUpdate.pas.

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
..
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
...
127
128
129
130
131
132
133





134
135
136
137
138
139
140
...
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
...
227
228
229
230
231
232
233

234
235
236

237



238

239

240
241














242

  TfrmAutoUpdate = class(TForm)
    lvwVersions: TListView;
    mmoChanges: TMemo;
    btnInstall: TButton;
    btnCancel: TButton;
    lblVersions: TLabel;

    procedure lvwVersionsSelectItem(Sender: TObject; Item: TListItem;
      Selected: Boolean);

  private
    { Private declarations }
  public
    { Public declarations }
    procedure Populate(const Versions: TDictionary<TDateTime,TXmlNode>);
  end;

  TUpdateChecker = class
  private


    FXmlFeed: TNativeXml;
    FVersions: TDictionary<TDateTime,TXmlNode>;
    FForm: TfrmAutoUpdate;

    procedure LoadFeed;
  public

    destructor  Destroy; override;

    function  CheckForUpdate: boolean;
    function  AskUserForUpdate(out UUID: string): boolean;
    procedure UpdateToVersion(const UUID: string);


    property XmlFeed: TNativeXml  read FXmlFeed;
  end;

implementation
uses
  System.RegularExpressions, Types;


{$R *.dfm}

{ ================================================================================================ }
{ TUpdateChecker }








{ ------------------------------------------------------------------------------------------------ }
destructor TUpdateChecker.Destroy;
begin
  if Assigned(FForm) then
    FForm.Free;
  if Assigned(FVersions) then
    FVersions.Free;
................................................................................
    FXmlFeed.Free;
  inherited;
end {TUpdateChecker.Destroy};

{ ------------------------------------------------------------------------------------------------ }
procedure TUpdateChecker.LoadFeed;
var
  URL: UTF8String;
begin
  if not Assigned(FXmlFeed) then begin
    FVersions := TDictionary<TDateTime,TXmlNode>.Create;

    FXmlFeed := TNativeXml.Create(nil);
    try
      URL := 'http://fossil.2of4.net/zaap/timeline.rss?y=ci&tag=zpreview&name=ZPreview.'+{$IFDEF WIN32}'32'{$ELSE}'64'{$ENDIF}+'.exe';





      FXmlFeed.LoadFromURL(URL);
    except
      FreeAndNil(FXmlFeed);
      raise;
    end;
  end;
end {TUpdateChecker.LoadFeed};

{ ------------------------------------------------------------------------------------------------ }
function TUpdateChecker.CheckForUpdate: boolean;
var



  rexDateTime: TRegEx;
  xmlChannel: TXmlNode;
  xmlItems: TList;
  i, mi: Integer;
  xmlItem: TXmlNode;
  sPubDate: string;
  PubDate: TDateTime;
  Match: TMatch;
  Day, Month, Year: Integer;
  en_US: TFormatSettings;
begin
  LoadFeed;






  rexDateTime := TRegEx.Create('(\d{1,2})\s+(\w{2,})\s+(\d{4})\s+(\d?\d:\d\d:\d\d)', [roCompiled]);
  en_US := TFormatSettings.Create('en-US');

  Result := False;
  xmlChannel := FXmlFeed.Root.NodeByName('channel');
  xmlItems := TList.Create;
  try
    xmlChannel.NodesByName('item', xmlItems);
    for i := 0 to xmlItems.Count - 1 do begin
      xmlItem := TXmlNode(xmlItems[i]);
      // TODO: keep track of the relevant XML nodes, including some pre-parsed

      //  data, like the publication date as TDateTime, the author, the link,
      //  the title and the description.
      sPubDate := xmlItem.ReadUnicodeString('pubDate');

      {$MESSAGE WARN 'TODO: PROPERLY read pubDate; if more recent than our executable’s last-modified date, result is true — MCO 25-02-2015'}
      Match := rexDateTime.Match(sPubDate);
      if Match.Success then begin
        Day := StrToInt(Match.Groups[1].Value);
        Month := 0;
        for mi := Low(en_US.ShortMonthNames) to High(en_US.ShortMonthNames) do begin
          if SameText(Match.Groups[2].Value, en_US.ShortMonthNames[mi]) then begin
            Month := mi;
................................................................................
        end;
        Year := StrToInt(Match.Groups[3].Value);
        PubDate := EncodeDate(Year, Month, Day) + StrToTime(Match.Groups[4].Value);
      end else begin
        PubDate := StrToDateTime(sPubDate);
      end;







      FVersions.Add(PubDate, xmlItem);
    end;
  finally
    xmlItems.Free;
  end;
end {TUpdateChecker.CheckForUpdate};
................................................................................
var
  ExeModified, PubDate: TDateTime;
  xmlItem: TXmlNode;
  ListItem: TListItem;
  Title: string;
  Index: Integer;
  DTI: TDateTimeInfoRec;

begin
  if FileGetDateTimeInfo(Application.ExeName, DTI) then begin
    ExeModified := DTI.TimeStamp;
  end else begin
    ExeModified := 0;
  end;

  lvwVersions.Items.BeginUpdate;
  try
    lvwVersions.Items.Clear;

    for PubDate in Versions.Keys do begin
      xmlItem := Versions.Items[PubDate];
      ListItem := lvwVersions.Items.Add;
      ListItem.Data := xmlItem;
      ListItem.Caption := DateTimeToStr(PubDate);
      ListItem.SubItems.Add(xmlItem.ReadUnicodeString('dc:creator'));

      Title := xmlItem.ReadUnicodeString('title');
      Index := Pos(#10, Title);
      if Index > 0 then
        Title := Copy(Title, 1, Index - 1);
      ListItem.SubItems.Add(Title);

      if PubDate <= ExeModified then
        ListItem.GroupID := 1 // old versions
      else
        ListItem.GroupID := 0; // recent versions


    end;
    for Index := 0 to lvwVersions.Columns.Count - 1 do
      lvwVersions.Columns[Index].Width := -2; // autosize by content + headers







    if lvwVersions.Items.Count > 0 then
      lvwVersions.ItemIndex := 0;
  finally
    lvwVersions.Items.EndUpdate;
  end;
end {TfrmAutoUpdate.Populate};
................................................................................
procedure TfrmAutoUpdate.lvwVersionsSelectItem(Sender: TObject; Item: TListItem;
  Selected: Boolean);
var
  xmlItem: TXmlNode;
begin
  if Selected = False then begin
    btnInstall.Enabled := False;

    mmoChanges.Clear;
  end else begin
    xmlItem := TXmlNode(Item.Data);

    mmoChanges.Lines.Text := xmlItem.ReadUnicodeString('description', '(no description provided)');



    btnInstall.Enabled := True;

  end;

end {TfrmAutoUpdate.lvwVersionsSelectItem};















end.







>


>









>
>






>






>





|
>






>
>
>
>
>
>
>







 







|






|
>
>
>
>
>
|










>
>
>









<



>
>
>
>
>










<
>
|
<

<
<







 







>
>
>
>
>







 







>










>













|
|
|
|
>
>



>
>
>
>
>
>







 







>



>
|
>
>
>
|
>
|
>


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

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
..
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
...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
...
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
...
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

  TfrmAutoUpdate = class(TForm)
    lvwVersions: TListView;
    mmoChanges: TMemo;
    btnInstall: TButton;
    btnCancel: TButton;
    lblVersions: TLabel;
    btnWebpage: TButton;
    procedure lvwVersionsSelectItem(Sender: TObject; Item: TListItem;
      Selected: Boolean);
    procedure btnWebpageClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure Populate(const Versions: TDictionary<TDateTime,TXmlNode>);
  end;

  TUpdateChecker = class
  private
    FBaseURL: string;
    FTagName: string;
    FXmlFeed: TNativeXml;
    FVersions: TDictionary<TDateTime,TXmlNode>;
    FForm: TfrmAutoUpdate;

    procedure LoadFeed;
  public
    constructor Create(const BaseURL, BranchName: string);
    destructor  Destroy; override;

    function  CheckForUpdate: boolean;
    function  AskUserForUpdate(out UUID: string): boolean;
    procedure UpdateToVersion(const UUID: string);

    property BaseURL: string      read FBaseURL;
    property XmlFeed: TNativeXml  read FXmlFeed;
  end;

implementation
uses
  System.RegularExpressions, System.Types,
  Winapi.ShellAPI;

{$R *.dfm}

{ ================================================================================================ }
{ TUpdateChecker }

{ ------------------------------------------------------------------------------------------------ }
constructor TUpdateChecker.Create(const BaseURL, BranchName: string);
begin
  inherited Create;
  FBaseURL := BaseURL;
  FTagName := BranchName;
end {TUpdateChecker.Create};
{ ------------------------------------------------------------------------------------------------ }
destructor TUpdateChecker.Destroy;
begin
  if Assigned(FForm) then
    FForm.Free;
  if Assigned(FVersions) then
    FVersions.Free;
................................................................................
    FXmlFeed.Free;
  inherited;
end {TUpdateChecker.Destroy};

{ ------------------------------------------------------------------------------------------------ }
procedure TUpdateChecker.LoadFeed;
var
  URL: string;
begin
  if not Assigned(FXmlFeed) then begin
    FVersions := TDictionary<TDateTime,TXmlNode>.Create;

    FXmlFeed := TNativeXml.Create(nil);
    try
//      URL := 'http://fossil.2of4.net/zaap/timeline.rss?y=ci&tag=zpreview&name=ZPreview.'+{$IFDEF WIN32}'32'{$ELSE}'64'{$ENDIF}+'.exe';
      URL := FBaseURL + 'timeline.rss?y=ci';
      if FTagName <> '' then
        URL := URL + '&tag=' + FTagName; // TODO: URLEncode
      URL := URL + '&name=' + ExtractFileName(Application.ExeName); // TODO: URLEncode;

      FXmlFeed.LoadFromURL(UTF8String(URL));
    except
      FreeAndNil(FXmlFeed);
      raise;
    end;
  end;
end {TUpdateChecker.LoadFeed};

{ ------------------------------------------------------------------------------------------------ }
function TUpdateChecker.CheckForUpdate: boolean;
var
  DTI: TDateTimeInfoRec;
  ExeModified: TDateTime;
  en_US: TFormatSettings;
  rexDateTime: TRegEx;
  xmlChannel: TXmlNode;
  xmlItems: TList;
  i, mi: Integer;
  xmlItem: TXmlNode;
  sPubDate: string;
  PubDate: TDateTime;
  Match: TMatch;
  Day, Month, Year: Integer;

begin
  LoadFeed;

  if FileGetDateTimeInfo(Application.ExeName, DTI) then begin
    ExeModified := DTI.TimeStamp;
  end else begin
    ExeModified := 0;
  end;
  rexDateTime := TRegEx.Create('(\d{1,2})\s+(\w{2,})\s+(\d{4})\s+(\d?\d:\d\d:\d\d)', [roCompiled]);
  en_US := TFormatSettings.Create('en-US');

  Result := False;
  xmlChannel := FXmlFeed.Root.NodeByName('channel');
  xmlItems := TList.Create;
  try
    xmlChannel.NodesByName('item', xmlItems);
    for i := 0 to xmlItems.Count - 1 do begin
      xmlItem := TXmlNode(xmlItems[i]);


      // Parse the publication date.

      sPubDate := xmlItem.ReadUnicodeString('pubDate');


      Match := rexDateTime.Match(sPubDate);
      if Match.Success then begin
        Day := StrToInt(Match.Groups[1].Value);
        Month := 0;
        for mi := Low(en_US.ShortMonthNames) to High(en_US.ShortMonthNames) do begin
          if SameText(Match.Groups[2].Value, en_US.ShortMonthNames[mi]) then begin
            Month := mi;
................................................................................
        end;
        Year := StrToInt(Match.Groups[3].Value);
        PubDate := EncodeDate(Year, Month, Day) + StrToTime(Match.Groups[4].Value);
      end else begin
        PubDate := StrToDateTime(sPubDate);
      end;

      {--- MCO 25-02-2015: If it's more recent than our executable's date, then return true. ---}
      {$MESSAGE HINT 'TODO: Better check for version would be to actually compare version numbers. — MCO 25-02-2015'}
      if PubDate > ExeModified then begin
        Result := True;
      end;

      FVersions.Add(PubDate, xmlItem);
    end;
  finally
    xmlItems.Free;
  end;
end {TUpdateChecker.CheckForUpdate};
................................................................................
var
  ExeModified, PubDate: TDateTime;
  xmlItem: TXmlNode;
  ListItem: TListItem;
  Title: string;
  Index: Integer;
  DTI: TDateTimeInfoRec;
  bNew: boolean;
begin
  if FileGetDateTimeInfo(Application.ExeName, DTI) then begin
    ExeModified := DTI.TimeStamp;
  end else begin
    ExeModified := 0;
  end;

  lvwVersions.Items.BeginUpdate;
  try
    lvwVersions.Items.Clear;
    bNew := False;
    for PubDate in Versions.Keys do begin
      xmlItem := Versions.Items[PubDate];
      ListItem := lvwVersions.Items.Add;
      ListItem.Data := xmlItem;
      ListItem.Caption := DateTimeToStr(PubDate);
      ListItem.SubItems.Add(xmlItem.ReadUnicodeString('dc:creator'));

      Title := xmlItem.ReadUnicodeString('title');
      Index := Pos(#10, Title);
      if Index > 0 then
        Title := Copy(Title, 1, Index - 1);
      ListItem.SubItems.Add(Title);

      if PubDate <= ExeModified then begin
        ListItem.GroupID := 1 // older versions
      end else begin
        ListItem.GroupID := 0; // newer versions
        bNew := True;
      end;
    end;
    for Index := 0 to lvwVersions.Columns.Count - 1 do
      lvwVersions.Columns[Index].Width := -2; // autosize by content + headers

    if not bNew then begin
      ListItem := lvwVersions.Items.Add;
      ListItem.GroupID := 0;
      ListItem.Caption := '(no newer versions)';
    end;

    if lvwVersions.Items.Count > 0 then
      lvwVersions.ItemIndex := 0;
  finally
    lvwVersions.Items.EndUpdate;
  end;
end {TfrmAutoUpdate.Populate};
................................................................................
procedure TfrmAutoUpdate.lvwVersionsSelectItem(Sender: TObject; Item: TListItem;
  Selected: Boolean);
var
  xmlItem: TXmlNode;
begin
  if Selected = False then begin
    btnInstall.Enabled := False;
    btnWebpage.Enabled := False;
    mmoChanges.Clear;
  end else begin
    xmlItem := TXmlNode(Item.Data);
    if Assigned(xmlItem) then begin
      mmoChanges.Lines.Text := xmlItem.ReadUnicodeString('description', '(no description provided)');
      btnInstall.Enabled := (Item.GroupID = 0);
      btnWebpage.Enabled := True;
    end else begin
      btnInstall.Enabled := False;
      btnWebpage.Enabled := False;
    end;
  end;
end {TfrmAutoUpdate.lvwVersionsSelectItem};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmAutoUpdate.btnWebpageClick(Sender: TObject);
var
  xmlItem: TXmlNode;
begin
  if Assigned(lvwVersions.Selected) then begin
    xmlItem := TXmlNode(lvwVersions.Selected.Data);
    if Assigned(xmlItem) then
      ShellExecute(Handle, 'open', PChar(xmlItem.ReadUnicodeString('link')), nil, nil, SW_SHOW);
  end;
end {TfrmAutoUpdate.btnWebpageClick};



end.

Changes to ZPreview/src/Delphi/F_Main.dfm.

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
..
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
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object tbcPreview: TTabControl
    AlignWithMargins = True
    Left = 3
    Top = 70
    Width = 806
    Height = 412
    Align = alClient
    DoubleBuffered = True
    ParentDoubleBuffered = False
    TabOrder = 0
    Tabs.Strings = (
      'Sophie.jpg')
    TabIndex = 0


  end
  object pnlError: TPanel
    Left = 0
    Top = 26
    Width = 816
    Height = 41
    Align = alTop
    TabOrder = 1
    Visible = False
    OnDblClick = pnlErrorDblClick

    object lblError: TLabel
      AlignWithMargins = True
      Left = 4
      Top = 4
      Width = 808
      Height = 33
      Align = alClient
      Alignment = taCenter
      ShowAccelChar = False
      WordWrap = True
      ExplicitWidth = 3
      ExplicitHeight = 13
    end
  end
  object acttbMain: TActionToolBar
    Left = 0
    Top = 0
    Width = 816
    Height = 26
    ActionManager = ActionManager
    Caption = 'acttbMain'
    Color = clMenuBar
    ColorMap.DisabledFontColor = 7171437
    ColorMap.HighlightColor = clWhite
    ColorMap.BtnSelectedFont = clBlack
    ColorMap.UnusedColor = clWhite
................................................................................
    Font.Height = -11
    Font.Name = 'Tahoma'
    Font.Style = []
    ParentFont = False
    ParentShowHint = False
    ShowHint = True
    Spacing = 0

  end
  object pnlRight: TPanel
    Left = 812
    Top = 67
    Width = 4
    Height = 418
    Align = alRight
    AutoSize = True
    Constraints.MaxWidth = 4
    DockSite = True
    TabOrder = 3


  end
  object sbrMain: TStatusBar
    Left = 0
    Top = 485
    Width = 816
    Height = 20
    DoubleBuffered = True







|

|







>
>



|






>




|
|




<
<






|







 







>



|

|





>
>







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
..
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
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object tbcPreview: TTabControl
    AlignWithMargins = True
    Left = 3
    Top = 67
    Width = 806
    Height = 415
    Align = alClient
    DoubleBuffered = True
    ParentDoubleBuffered = False
    TabOrder = 0
    Tabs.Strings = (
      'Sophie.jpg')
    TabIndex = 0
    ExplicitTop = 70
    ExplicitHeight = 412
  end
  object pnlError: TPanel
    Left = 0
    Top = 23
    Width = 816
    Height = 41
    Align = alTop
    TabOrder = 1
    Visible = False
    OnDblClick = pnlErrorDblClick
    ExplicitTop = 26
    object lblError: TLabel
      AlignWithMargins = True
      Left = 4
      Top = 4
      Width = 3
      Height = 13
      Align = alClient
      Alignment = taCenter
      ShowAccelChar = False
      WordWrap = True


    end
  end
  object acttbMain: TActionToolBar
    Left = 0
    Top = 0
    Width = 816
    Height = 23
    ActionManager = ActionManager
    Caption = 'acttbMain'
    Color = clMenuBar
    ColorMap.DisabledFontColor = 7171437
    ColorMap.HighlightColor = clWhite
    ColorMap.BtnSelectedFont = clBlack
    ColorMap.UnusedColor = clWhite
................................................................................
    Font.Height = -11
    Font.Name = 'Tahoma'
    Font.Style = []
    ParentFont = False
    ParentShowHint = False
    ShowHint = True
    Spacing = 0
    ExplicitHeight = 26
  end
  object pnlRight: TPanel
    Left = 812
    Top = 64
    Width = 4
    Height = 421
    Align = alRight
    AutoSize = True
    Constraints.MaxWidth = 4
    DockSite = True
    TabOrder = 3
    ExplicitTop = 67
    ExplicitHeight = 418
  end
  object sbrMain: TStatusBar
    Left = 0
    Top = 485
    Width = 816
    Height = 20
    DoubleBuffered = True

Changes to ZPreview/src/Delphi/F_Main.pas.

279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
...
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
  frmInfoSentinel.Visible := actViewSentinel.Checked;
  if frmInfoSentinel.Visible and Assigned(FSentinel) then
    frmInfoSentinel.Populate(FSentinel);
end {TfrmMain.actViewSentinelExecute};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmMain.actViewTopmostExecute(Sender: TObject);
var
  i: integer;
begin
  if actViewTopmost.Checked then begin
    Self.FormStyle := fsStayOnTop;
    ApplicationDeactivate(Application);
  end else begin
    Self.FormStyle := fsNormal;
  end;
................................................................................

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmMain.actHelpCheckForUpdatesExecute(Sender: TObject);
var
  UC: TUpdateChecker;
  UUID: string;
begin
  UC := TUpdateChecker.Create;
  try
    if UC.CheckForUpdate or True then begin
      Self.FormStyle := fsNormal;
      try
        if UC.AskUserForUpdate(UUID) then begin
          UC.UpdateToVersion(UUID);
        end;







<
<







 







|







279
280
281
282
283
284
285


286
287
288
289
290
291
292
...
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
  frmInfoSentinel.Visible := actViewSentinel.Checked;
  if frmInfoSentinel.Visible and Assigned(FSentinel) then
    frmInfoSentinel.Populate(FSentinel);
end {TfrmMain.actViewSentinelExecute};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmMain.actViewTopmostExecute(Sender: TObject);


begin
  if actViewTopmost.Checked then begin
    Self.FormStyle := fsStayOnTop;
    ApplicationDeactivate(Application);
  end else begin
    Self.FormStyle := fsNormal;
  end;
................................................................................

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmMain.actHelpCheckForUpdatesExecute(Sender: TObject);
var
  UC: TUpdateChecker;
  UUID: string;
begin
  UC := TUpdateChecker.Create('http://fossil.2of4.net/zaap/', 'zpreview');
  try
    if UC.CheckForUpdate or True then begin
      Self.FormStyle := fsNormal;
      try
        if UC.AskUserForUpdate(UUID) then begin
          UC.UpdateToVersion(UUID);
        end;