Changes On Branch lazarus
Not logged in

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

Changes In Branch lazarus Excluding Merge-Ins

This is equivalent to a diff from b03b9b790d to 1905026b8c

2020-02-08
23:08
Confirm deletion of missing files. Properly set the branch or revision background-colour. Leaf check-in: 1905026b8c user: tinus tags: lazarus
22:07
Added button to stash changes, which works. New branch dialog also works. check-in: 89b16f2b3c user: tinus tags: lazarus
2014-07-23
16:24
Create new branch named "mono" check-in: 35fd697537 user: tinus tags: mono
15:03
Create new branch named "lazarus" check-in: bc5abb11a4 user: tinus tags: lazarus
2011-05-03
06:57
Eerste versie, werkt niet (goed) in Lazarus, begint te werken in Delphi check-in: 92007772e3 user: Martijn tags: trunk
2011-05-01
14:49
initial empty check-in check-in: b03b9b790d user: tinus tags: trunk

Added .fossil-settings/binary-glob.

















>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
*.png
*.jpg
*.jpeg
*.gif
*.bmp
*.ico
*.xcf
*.res

Added .fossil-settings/binary-glob.no-warn.

Added .fossil-settings/ignore-glob.

















>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
out/
*.bak
*.lps
*/__history/
*/backup/
GPATH
GTAGS
GRTAGS

Added .fossil-settings/ignore-glob.no-warn.

Added notes/fossil-help-commit.txt.



































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
Usage: f commit ?OPTIONS? ?FILE...?

Create a new version containing all of the changes in the current
checkout.  You will be prompted to enter a check-in comment unless
the comment has been specified on the command-line using "-m" or a
file containing the comment using -M.  The editor defined in the
"editor" fossil option (see f help set) will be used, or from
the "VISUAL" or "EDITOR" environment variables (in that order) if
no editor is set.

All files that have changed will be committed unless some subset of
files is specified on the command line.

The --branch option followed by a branch name causes the new
check-in to be placed in a newly-created branch with the name
passed to the --branch option.

Use the --branchcolor option followed by a color name (ex:
'#ffc0c0') to specify the background color of entries in the new
branch when shown in the web timeline interface.  The use of
the --branchcolor option is not recommend.  Instead, let Fossil
choose the branch color automatically.

The --bgcolor option works like --branchcolor but only sets the
background color for a single check-in.  Subsequent check-ins revert
to the default color.

A check-in is not permitted to fork unless the --allow-fork option
appears.  An empty check-in (i.e. with nothing changed) is not
allowed unless the --allow-empty option appears.  A check-in may not
be older than its ancestor unless the --allow-older option appears.
If any of files in the check-in appear to contain unresolved merge
conflicts, the check-in will not be allowed unless the
--allow-conflict option is present.  In addition, the entire
check-in process may be aborted if a file contains content that
appears to be binary, Unicode text, or text with CR/NL line endings
unless the interactive user chooses to proceed.  If there is no
interactive user or these warnings should be skipped for some other
reason, the --no-warnings option may be used.  A check-in is not
allowed against a closed leaf.

If a commit message is blank, you will be prompted:
("continue (y/N)?") to confirm you really want to commit with a
blank commit message.  The default value is "N", do not commit.

The --private option creates a private check-in that is never synced.
Children of private check-ins are automatically private.

The --tag option applies the symbolic tag name to the check-in.

The --sha1sum option detects edited files by computing each file's
SHA1 hash rather than just checking for changes to its size or mtime.

Options:
   --allow-conflict           allow unresolved merge conflicts
   --allow-empty              allow a commit with no changes
   --allow-fork               allow the commit to fork
   --allow-older              allow a commit older than its ancestor
   --baseline                 use a baseline manifest in the commit process
   --bgcolor COLOR            apply COLOR to this one check-in only
   --branch NEW-BRANCH-NAME   check in to this new branch
   --branchcolor COLOR        apply given COLOR to the branch
   --close                    close the branch being committed
   --delta                    use a delta manifest in the commit process
   --integrate                close all merged-in branches
   -m|--comment COMMENT-TEXT  use COMMENT-TEXT as commit comment
   -M|--message-file FILE     read the commit comment from given file
   --mimetype MIMETYPE        mimetype of check-in comment
   -n|--dry-run               If given, display instead of run actions
   --no-warnings              omit all warnings about file contents
   --nosign                   do not attempt to sign this commit with gpg
   --private                  do not sync changes and their descendants
   --sha1sum                  verify file status using SHA1 hashing rather
                              than relying on file mtimes
   --tag TAG-NAME             assign given tag TAG-NAME to the checkin

See also: branch, changes, checkout, extras, sync


^ +-(([a-z0-9])\|-)?(-([a-z0-9-]+))( ([A-Z0-9-]+))*  +(.*)
short: '\2'; long: '\4'; param: '\6'; help: '\7'

Added notes/utf-8 support.md.

















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# UTF-8 support in the Wide Diff view

## Steps

1. ✔ Make `FColDefs` a fixed-size array. Populate this as far as possible with the default values during construction.
2. ✔ Include both char and text indices (start, length) in `TSHColDef`.
3. ✔ In `SetLine`, when processing the header '=', adjust the char positions and lengths.
4. ✔ In `SetLine`, when processing a diff line ' ':
   * ✔ Scan the entire text line, keeping track of both byte and char indices. (This means we won't have
     to keep scanning for positions in text).
   * ✔ Populate the entire array of **FColDefs** as we go, only don't differentiate between the three content columns yet.
   * ✔ Put aside both left and right content.
   * In the case of a '|' modified line:
     * ✔ Define three subcolumns each for both left and right content.
     * ✔ `TSHSubColDef` contains the regular `TSHColDef` fields, ~~plus `Text: string`~~. _(not necessary)_
     * ✔ Compare the left and right content, and split both up in 3 same/diff/same subcols.
5. ✔ In `Next`, step through the **FColDef**s.
   * ✔ Only in the case of a '|' modified line, when in either content column, navigate the appropriate subcolumns instead.

## Performance

* ~~Our current implementation of CharLength, CopyChars etc. is quite slow.
  The steps outlined above ought to reduce the need for these functions, but once we know which ones we'll need,
  we should reimplement them. Probably using `PChar` and `Inc()`, while guarding for buffer overruns.~~ _We only need to scan every line once, in SetLine. This only requires `ByteType` function, and its performance is adequate._

Added src/a_commitmessage.lfm.

















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
object frameCommitMessage: TframeCommitMessage
  Left = 0
  Height = 240
  Top = 0
  Width = 631
  ClientHeight = 240
  ClientWidth = 631
  TabOrder = 0
  DesignLeft = 671
  DesignTop = 450
  object pnlOptions: TPanel
    Left = 0
    Height = 50
    Top = 0
    Width = 631
    Align = alTop
    BevelOuter = bvNone
    ClientHeight = 50
    ClientWidth = 631
    TabOrder = 0
    object tbrMessage: TToolBar
      Left = 0
      Height = 27
      Top = 0
      Width = 631
      Caption = 'tbrMessage'
      EdgeBorders = []
      Images = modMain.imlMain
      ShowCaptions = True
      TabOrder = 2
      object btnBranch: TToolButton
        Left = 1
        Top = 0
        AutoSize = True
        Caption = 'Branch'
        OnClick = btnBranchClick
      end
      object btnOptions: TToolButton
        AnchorSideRight.Control = tbrMessage
        AnchorSideRight.Side = asrBottom
        Left = 54
        Top = 0
        Caption = 'Options'
        DropdownMenu = pumOptions
        OnClick = btnOptionsClick
        Style = tbsDropDown
      end
      object btnMessage: TToolButton
        Left = 126
        Top = 0
        Caption = 'Message'
        DropdownMenu = pumMessages
        OnClick = btnOptionsClick
        ParentShowHint = False
        ShowHint = True
        Style = tbsDropDown
      end
      object btnTags: TToolButton
        Left = 204
        Top = 0
        Caption = 'Tags'
        DropdownMenu = pumTags
        OnClick = btnTagsClick
        Style = tbsDropDown
      end
      object btnBGColor: TColorButton
        Left = 279
        Height = 22
        Hint = 'Background color'
        Top = 0
        Width = 75
        BorderWidth = 2
        ButtonColorSize = 16
        ButtonColor = clNone
        ColorDialog = dlgColor
        Caption = 'BG'
        Flat = True
        Layout = blGlyphRight
        OnColorChanged = btnBGColorColorChanged
      end
      object chkBGColor: TCheckBox
        Left = 255
        Height = 23
        Top = 0
        Width = 24
        TabOrder = 0
      end
    end
    object btnCommit: TButton
      Left = 555
      Height = 25
      Top = 1
      Width = 75
      Anchors = [akTop, akRight]
      Caption = 'Commit'
      Enabled = False
      OnClick = btnCommitClick
      TabOrder = 0
    end
    object lblSelectedOptions: TLabel
      Left = 9
      Height = 20
      Top = 31
      Width = 114
      Caption = 'Selected options:'
      ParentColor = False
      ShowAccelChar = False
    end
    object btnStash: TButton
      Left = 471
      Height = 25
      Top = 1
      Width = 75
      Anchors = [akTop, akRight]
      Caption = 'Stash'
      OnClick = btnStashClick
      TabOrder = 1
    end
  end
  object sbrMessage: TStatusBar
    Left = 0
    Height = 29
    Top = 211
    Width = 631
    Panels = <>
  end
  inline synMessage: TSynEdit
    Left = 0
    Height = 161
    Top = 50
    Width = 631
    Align = alClient
    Font.Height = 15
    Font.Name = 'Monaco'
    Font.Pitch = fpFixed
    Font.Quality = fqCleartype
    ParentColor = False
    ParentFont = False
    TabOrder = 2
    OnKeyDown = synMessageKeyDown
    OnClickLink = synMessageClickLink
    OnMouseWheel = synMessageMouseWheel
    Gutter.Width = 10
    Gutter.MouseActions = <>
    RightGutter.Width = 0
    RightGutter.MouseActions = <>
    Keystrokes = <    
      item
        Command = ecUp
        ShortCut = 38
      end    
      item
        Command = ecSelUp
        ShortCut = 8230
      end    
      item
        Command = ecScrollUp
        ShortCut = 16422
      end    
      item
        Command = ecDown
        ShortCut = 40
      end    
      item
        Command = ecSelDown
        ShortCut = 8232
      end    
      item
        Command = ecScrollDown
        ShortCut = 16424
      end    
      item
        Command = ecLeft
        ShortCut = 37
      end    
      item
        Command = ecSelLeft
        ShortCut = 8229
      end    
      item
        Command = ecWordLeft
        ShortCut = 16421
      end    
      item
        Command = ecSelWordLeft
        ShortCut = 24613
      end    
      item
        Command = ecRight
        ShortCut = 39
      end    
      item
        Command = ecSelRight
        ShortCut = 8231
      end    
      item
        Command = ecWordRight
        ShortCut = 16423
      end    
      item
        Command = ecSelWordRight
        ShortCut = 24615
      end    
      item
        Command = ecPageDown
        ShortCut = 34
      end    
      item
        Command = ecSelPageDown
        ShortCut = 8226
      end    
      item
        Command = ecPageBottom
        ShortCut = 16418
      end    
      item
        Command = ecSelPageBottom
        ShortCut = 24610
      end    
      item
        Command = ecPageUp
        ShortCut = 33
      end    
      item
        Command = ecSelPageUp
        ShortCut = 8225
      end    
      item
        Command = ecPageTop
        ShortCut = 16417
      end    
      item
        Command = ecSelPageTop
        ShortCut = 24609
      end    
      item
        Command = ecLineStart
        ShortCut = 36
      end    
      item
        Command = ecSelLineStart
        ShortCut = 8228
      end    
      item
        Command = ecEditorTop
        ShortCut = 16420
      end    
      item
        Command = ecSelEditorTop
        ShortCut = 24612
      end    
      item
        Command = ecLineEnd
        ShortCut = 35
      end    
      item
        Command = ecSelLineEnd
        ShortCut = 8227
      end    
      item
        Command = ecEditorBottom
        ShortCut = 16419
      end    
      item
        Command = ecSelEditorBottom
        ShortCut = 24611
      end    
      item
        Command = ecToggleMode
        ShortCut = 45
      end    
      item
        Command = ecCopy
        ShortCut = 16429
      end    
      item
        Command = ecPaste
        ShortCut = 8237
      end    
      item
        Command = ecDeleteChar
        ShortCut = 46
      end    
      item
        Command = ecCut
        ShortCut = 8238
      end    
      item
        Command = ecDeleteLastChar
        ShortCut = 8
      end    
      item
        Command = ecDeleteLastChar
        ShortCut = 8200
      end    
      item
        Command = ecDeleteLastWord
        ShortCut = 16392
      end    
      item
        Command = ecUndo
        ShortCut = 32776
        ShortCut2 = 16474
      end    
      item
        Command = ecRedo
        ShortCut = 40968
        ShortCut2 = 24666
      end    
      item
        Command = ecLineBreak
        ShortCut = 13
      end    
      item
        Command = ecSelectAll
        ShortCut = 16449
      end    
      item
        Command = ecCopy
        ShortCut = 16451
      end    
      item
        Command = ecBlockIndent
        ShortCut = 24649
      end    
      item
        Command = ecLineBreak
        ShortCut = 16461
      end    
      item
        Command = ecInsertLine
        ShortCut = 16462
      end    
      item
        Command = ecDeleteWord
        ShortCut = 16468
      end    
      item
        Command = ecBlockUnindent
        ShortCut = 24661
      end    
      item
        Command = ecPaste
        ShortCut = 16470
      end    
      item
        Command = ecCut
        ShortCut = 16472
      end    
      item
        Command = ecDeleteLine
        ShortCut = 16473
      end    
      item
        Command = ecDeleteEOL
        ShortCut = 24665
      end    
      item
        Command = ecUndo
        ShortCut = 16474
      end    
      item
        Command = ecRedo
        ShortCut = 24666
      end    
      item
        Command = ecGotoMarker0
        ShortCut = 16432
      end    
      item
        Command = ecGotoMarker1
        ShortCut = 16433
      end    
      item
        Command = ecGotoMarker2
        ShortCut = 16434
      end    
      item
        Command = ecGotoMarker3
        ShortCut = 16435
      end    
      item
        Command = ecGotoMarker4
        ShortCut = 16436
      end    
      item
        Command = ecGotoMarker5
        ShortCut = 16437
      end    
      item
        Command = ecGotoMarker6
        ShortCut = 16438
      end    
      item
        Command = ecGotoMarker7
        ShortCut = 16439
      end    
      item
        Command = ecGotoMarker8
        ShortCut = 16440
      end    
      item
        Command = ecGotoMarker9
        ShortCut = 16441
      end    
      item
        Command = ecSetMarker0
        ShortCut = 24624
      end    
      item
        Command = ecSetMarker1
        ShortCut = 24625
      end    
      item
        Command = ecSetMarker2
        ShortCut = 24626
      end    
      item
        Command = ecSetMarker3
        ShortCut = 24627
      end    
      item
        Command = ecSetMarker4
        ShortCut = 24628
      end    
      item
        Command = ecSetMarker5
        ShortCut = 24629
      end    
      item
        Command = ecSetMarker6
        ShortCut = 24630
      end    
      item
        Command = ecSetMarker7
        ShortCut = 24631
      end    
      item
        Command = ecSetMarker8
        ShortCut = 24632
      end    
      item
        Command = ecSetMarker9
        ShortCut = 24633
      end    
      item
        Command = EcFoldLevel1
        ShortCut = 41009
      end    
      item
        Command = EcFoldLevel2
        ShortCut = 41010
      end    
      item
        Command = EcFoldLevel1
        ShortCut = 41011
      end    
      item
        Command = EcFoldLevel1
        ShortCut = 41012
      end    
      item
        Command = EcFoldLevel1
        ShortCut = 41013
      end    
      item
        Command = EcFoldLevel6
        ShortCut = 41014
      end    
      item
        Command = EcFoldLevel7
        ShortCut = 41015
      end    
      item
        Command = EcFoldLevel8
        ShortCut = 41016
      end    
      item
        Command = EcFoldLevel9
        ShortCut = 41017
      end    
      item
        Command = EcFoldLevel0
        ShortCut = 41008
      end    
      item
        Command = EcFoldCurrent
        ShortCut = 41005
      end    
      item
        Command = EcUnFoldCurrent
        ShortCut = 41003
      end    
      item
        Command = EcToggleMarkupWord
        ShortCut = 32845
      end    
      item
        Command = ecNormalSelect
        ShortCut = 24654
      end    
      item
        Command = ecColumnSelect
        ShortCut = 24643
      end    
      item
        Command = ecLineSelect
        ShortCut = 24652
      end    
      item
        Command = ecTab
        ShortCut = 9
      end    
      item
        Command = ecShiftTab
        ShortCut = 8201
      end    
      item
        Command = ecMatchBracket
        ShortCut = 24642
      end    
      item
        Command = ecColSelUp
        ShortCut = 40998
      end    
      item
        Command = ecColSelDown
        ShortCut = 41000
      end    
      item
        Command = ecColSelLeft
        ShortCut = 40997
      end    
      item
        Command = ecColSelRight
        ShortCut = 40999
      end    
      item
        Command = ecColSelPageDown
        ShortCut = 40994
      end    
      item
        Command = ecColSelPageBottom
        ShortCut = 57378
      end    
      item
        Command = ecColSelPageUp
        ShortCut = 40993
      end    
      item
        Command = ecColSelPageTop
        ShortCut = 57377
      end    
      item
        Command = ecColSelLineStart
        ShortCut = 40996
      end    
      item
        Command = ecColSelLineEnd
        ShortCut = 40995
      end    
      item
        Command = ecColSelEditorTop
        ShortCut = 57380
      end    
      item
        Command = ecColSelEditorBottom
        ShortCut = 57379
      end    
      item
        Command = ecCopy
        ShortCut = 4163
      end    
      item
        Command = ecCut
        ShortCut = 4184
      end    
      item
        Command = ecPaste
        ShortCut = 4182
      end>
    MouseActions = <>
    MouseTextActions = <>
    MouseSelActions = <>
    Options = [eoAutoIndent, eoBracketHighlight, eoGroupUndo, eoKeepCaretX, eoScrollByOneLess, eoSmartTabs, eoTabIndent, eoTrimTrailingSpaces, eoAltSetsColumnMode, eoDragDropEditing]
    Options2 = [eoCaretSkipTab, eoEnhanceEndKey, eoOverwriteBlock, eoAutoHideCursor]
    MouseOptions = [emAltSetsColumnMode, emDragDropEditing]
    VisibleSpecialChars = [vscSpace, vscTabAtLast]
    SelectedColor.BackPriority = 50
    SelectedColor.ForePriority = 50
    SelectedColor.FramePriority = 50
    SelectedColor.BoldPriority = 50
    SelectedColor.ItalicPriority = 50
    SelectedColor.UnderlinePriority = 50
    SelectedColor.StrikeOutPriority = 50
    BracketHighlightStyle = sbhsBoth
    BracketMatchColor.Background = clNone
    BracketMatchColor.Foreground = clNone
    BracketMatchColor.Style = [fsBold]
    FoldedCodeColor.Background = clNone
    FoldedCodeColor.Foreground = clGray
    FoldedCodeColor.FrameColor = clGray
    MouseLinkColor.Background = clNone
    MouseLinkColor.Foreground = clBlue
    LineHighlightColor.Background = clNone
    LineHighlightColor.Foreground = clNone
    TabWidth = 4
    WantTabs = False
    OnChange = synMessageChange
    inline SynLeftGutterPartList1: TSynGutterPartList
      object SynGutterLineNumber1: TSynGutterLineNumber
        Width = 15
        Visible = False
        MouseActions = <>
        MarkupInfo.Background = clBtnFace
        MarkupInfo.Foreground = clNone
        DigitCount = 2
        ShowOnlyLineNumbersMultiplesOf = 1
        ZeroStart = False
        LeadingZeros = False
      end
      object SynGutterCodeFolding1: TSynGutterCodeFolding
        MouseActions = <>
        MarkupInfo.Background = clNone
        MarkupInfo.Foreground = clGray
        MouseActionsExpanded = <>
        MouseActionsCollapsed = <>
      end
    end
  end
  object pumOptions: TPopupMenu
    left = 88
    top = 72
  end
  object pumTags: TPopupMenu
    left = 216
    top = 72
    object miNoTags: TMenuItem
      Caption = '(no tags)'
      Enabled = False
    end
  end
  object pumMessages: TPopupMenu
    OnPopup = pumMessagesPopup
    left = 152
    top = 112
    object miNoMessages: TMenuItem
      Caption = '(no previous messages)'
      Enabled = False
    end
  end
  object dlgColor: TColorDialog
    Title = 'Select background color'
    Color = clNone
    CustomColors.Strings = (
      'ColorA=dcdcf2'
      'ColorB=d6e5bd'
      'ColorC=a0a0a0'
      'ColorD=b0b0b0'
      'ColorE=c0c0c0'
      'ColorF=d0d0d0'
      'ColorG=e0e0e0'
      'ColorH=f0ffc0'
      'ColorG=fff0c0'
      'ColorI=ffc0d0'
      'ColorJ=ffc0ff'
      'ColorK=d0c0ff'
      'ColorL=c0f0ff'
      'ColorM=c0fff0'
      'ColorN=c0ffc0'
      'ColorO=c0d3a8'
      'ColorP=d3c7a8'
      'ColorQ=d3a8aa'
      'ColorR=d3a8cb'
      'ColorS=bca8d3'
    )
    left = 288
    top = 112
  end
  object scUUIDs: TSynCompletion
    Position = 0
    LinesInWindow = 16
    SelectedColor = clHighlight
    CaseSensitive = False
    Width = 262
    ShowSizeDrag = True
    AutoUseSingleIdent = True
    ShortCut = 16416
    EndOfTokenChr = '])., '
    OnCodeCompletion = scUUIDsCodeCompletion
    ExecCommandID = ecSynCompletionExecute
    Editor = synMessage
    left = 345
    top = 71
  end
end

Added src/a_commitmessage.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
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
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
unit A_CommitMessage;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, SynEdit, SynCompletion, Forms, Controls,
  ExtCtrls, ComCtrls, StdCtrls, Menus, Dialogs, types, M_Main, U_Fossil, LCLType;

type

  TNotifyGetFilesEvent = procedure(Sender: TObject; const AFiles: TFossilFileList) of object;

  { TframeCommitMessage }

  TframeCommitMessage = class(TFrame)
    btnCommit: TButton;
    btnBGColor: TColorButton;
    btnStash: TButton;
    chkBGColor: TCheckBox;
    dlgColor: TColorDialog;
    lblSelectedOptions: TLabel;
    miNoTags: TMenuItem;
    miNoMessages: TMenuItem;
    pnlOptions: TPanel;
    pumMessages: TPopupMenu;
    pumTags: TPopupMenu;
    pumOptions: TPopupMenu;
    sbrMessage: TStatusBar;
    scUUIDs: TSynCompletion;
    synMessage: TSynEdit;
    tbrMessage: TToolBar;
    btnBranch: TToolButton;
    btnOptions: TToolButton;
    btnMessage: TToolButton;
    btnTags: TToolButton;
    procedure btnBGColorColorChanged(Sender: TObject);
    procedure btnBranchClick(Sender: TObject);
    procedure btnCommitClick(Sender: TObject);
    procedure btnOptionsClick(Sender: TObject);
    procedure btnStashClick(Sender: Tobject);
    procedure btnTagsClick(Sender: TObject);
    procedure miMessageClick(Sender: TObject);
    procedure miOptionClick(Sender: TObject);
    procedure pumMessagesPopup(Sender: TObject);
    procedure scUUIDsCodeCompletion(var Value: string; SourceValue: string;
      var SourceStart, SourceEnd: TPoint; KeyChar: TUTF8Char; Shift: TShiftState
      );
    procedure synMessageChange(Sender: TObject);
    procedure synMessageClickLink(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure synMessageKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure synMessageMouseWheel(Sender: TObject; Shift: TShiftState;
      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
  private
    FWorkdir: TFossilWorkdir;
    FOptions: TCommitOptions;
    FOnGetFiles: TNotifyGetFilesEvent;
    FOnRefreshNeeded: TNotifyEvent;

    procedure PopulateCompletionFromTickets;
    procedure FillCommitOptions(var Options: TCommitOptions);
    procedure PopulateMessages;

    procedure SetWorkdir(AValue: TFossilWorkdir);
    { private declarations }
  public
    { public declarations }
    constructor Create(AOwner: TComponent); override;

    property WorkDir: TFossilWorkdir  read FWorkdir write SetWorkdir;

    property OnGetFiles: TNotifyGetFilesEvent read FOnGetFiles      write FOnGetFiles;
    property OnRefreshNeeded: TNotifyEvent    read FOnRefreshNeeded write FOnRefreshNeeded;
  end;

implementation
uses
  TypInfo, Graphics, StrUtils, Math,
  f_newbranch;

{$R *.lfm}

{ TframeCommitMessage }

function ProperCaseFromFlag(const Flag: TCommitFlag): string;
var
  Caps, Symbol: Boolean;
  i: Integer;
begin
  Result := Copy(GetEnumName(TypeInfo(TCommitFlag), Ord(Flag)), 3, 50);
  Caps := False;
  Symbol := False;
  for i := Length(Result) downto 2 do begin
    case Result[i] of
      'A'..'Z': begin
        if (Caps = False) and not Symbol then begin
          Result[i] := LowerCase(Result[i]);
          Insert(' ', Result, i);
        end;
        Caps := True;
        Symbol := False;
      end;
      'a'..'z': begin
        Caps := False;
        Symbol := False;
      end;
      else begin
        Caps := False;
        Symbol := True;
      end;
    end;
  end;
end;

constructor TframeCommitMessage.Create(AOwner: TComponent);
var
  s: string;
  cf: TCommitFlag;
  mi: TMenuItem;
begin
  inherited Create(AOwner);

  for s in cEditorFontNames do begin
    if Screen.Fonts.IndexOf(s) > -1 then begin
      synMessage.Font.Name := s;
      Break;
    end;
  end;

  FOptions := cDefaultCommitOptions;

  for cf in TCommitFlag do begin
    mi := TMenuItem.Create(pumOptions);
    mi.Caption := ProperCaseFromFlag(cf);
    mi.Tag := Ord(cf);
    mi.Checked := (cf in FOptions.Flags);
    mi.AutoCheck := True;
    mi.Visible := True;
    mi.OnClick := @miOptionClick;
    pumOptions.Items.Add(mi);
  end;
  miOptionClick(nil);
end {TframeCommitMessage.Create};

procedure TframeCommitMessage.SetWorkdir(AValue: TFossilWorkdir);
begin
  if FWorkdir = AValue then Exit;
  FWorkdir := AValue;

  FOptions.BranchName := FWorkdir.Branch;
  // TODO: figure out how to set the 'None' color.
  // TODO: try to determine if user has set a custom background color for this branch, and select that if relevant.
  btnBGColor.ButtonColor := FWorkdir.Checkout.Util.GetDefaultColor(FOptions.BranchName);
  chkBGColor.Checked := FOptions.BranchColor <> clNone;

  // Display current branch name
  sbrMessage.SimpleText := 'Current branch: ' + FWorkdir.Branch;
  btnBranch.Caption := 'Branch: ' + FWorkdir.Branch;

  PopulateMessages;

  scUUIDs.ItemList.BeginUpdate;
  try
    scUUIDs.ItemList.Clear;
    // fill the scUUIDs with open ticket-IDs
    PopulateCompletionFromTickets;

    scUUIDs.LinesInWindow := Min(16, scUUIDs.ItemList.Count);
  finally
    scUUIDs.ItemList.EndUpdate;
  end;
end;

procedure TframeCommitMessage.synMessageChange(Sender: TObject);
begin
  btnCommit.Enabled := Length(TrimRight(synMessage.Text)) > 0;
end;

procedure TframeCommitMessage.synMessageClickLink(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  // TODO: get the target of the link, and some something appropriate with it
end;

procedure TframeCommitMessage.synMessageKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (Key = VK_RETURN) and (Shift * [ssCtrl, ssMeta] <> []) then begin
    btnCommit.Click; // TODO: use an action for that
    Key := 0;
  end;
end;

procedure TframeCommitMessage.synMessageMouseWheel(Sender: TObject;
  Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
  var Handled: Boolean);
var
  SizeOffset: Integer;
begin
  if ssCtrl in Shift then begin
    // TODO: test this with user-set font sizes as well; both on Mac and Windows
    if WheelDelta > 0 then
      SizeOffset := -1
    else
      SizeOffset := 1;
    synMessage.BeginUpdate(False);
    try
      synMessage.Font.Size := synMessage.Font.Size + SizeOffset;
    finally
      synMessage.EndUpdate;
    end;
    Handled := True;
  end;
end;

procedure TframeCommitMessage.PopulateCompletionFromTickets;
var
  TicketClosedSQL: string = 'status=''Closed''';
  Tickets, Fields: TStringList;
  i: Integer;
  FieldUUID, FieldTitle: Integer;
  UUID, Title: string;
begin
  modMain.SetBusy(True);
  Tickets := TStringList.Create;
  try
    // TODO: retrieve the definition of 'closed' from the repo
    //       echo "SELECT value FROM config WHERE name='ticket-closed-expr';" | f sql
    //       (probably best to access the repo directly as an SQLite DB)
    // TODO: since we'll be accessing the repo DB directly anyway, we might as well read the
    //       ticket IDs and titles directly as well.
    Tickets.Text := FWorkdir.Checkout.Exe.Run('ticket show 0 "NOT (' + TicketClosedSQL + ')"');
    if Tickets.Count = 0 then
      Exit;

    Fields := TStringList.Create;
    try
      Fields.Delimiter := #9;
      Fields.StrictDelimiter := True;

      // read the first lines and extract the field names
      Fields.DelimitedText := Tickets[0];
      FieldUUID := Fields.IndexOf('tkt_uuid');
      FieldTitle := Fields.IndexOf('title');

      if (FieldUUID = -1) then
        Exit;

      // read each ticket, and extract the 'tkt_uuid' and the 'title'.
      for i := 1 to Tickets.Count - 1 do begin
        Fields.DelimitedText := Tickets[i];
        UUID := Copy(Fields[FieldUUID], 1, 10);
        if FieldTitle <> -1 then
          Title := Fields[FieldTitle]
        else
          Title := '';
        if Title <> '' then
          scUUIDs.ItemList.Add('[' + UUID + '|' + Title + ']')
        else
          scUUIDs.ItemList.Add('[' + UUID + ']');
      end;
    finally
      Fields.Free;
    end;
  finally
    Tickets.Free;
    modMain.SetBusy(False);
  end;
end {TframeCommitMessage.PopulateCompletionFromTickets};

procedure TframeCommitMessage.FillCommitOptions(var Options: TCommitOptions);
var
  i: Integer;
begin
  if (Options.BranchName <> '') and (Options.BranchName <> FWorkdir.Branch) then begin
    Options.RevColor := clNone;
    if chkBGColor.Checked then
      Options.BranchColor := btnBGColor.ButtonColor;
  end else if chkBGColor.Checked then begin
    Options.RevColor := btnBGColor.ButtonColor;
  end;

  // TODO
  //if Huppeldepup.Integrate then begin
  //  Options^.Integrate := True;
  //end;

  for i := 0 to pumOptions.Items.Count - 1 do begin
    if pumOptions.Items[i].Checked then
      Include(Options.Flags, TCommitFlag(pumOptions.Items[i].Tag))
    else
      Exclude(Options.Flags, TCommitFlag(pumOptions.Items[i].Tag));
  end;

  Options.Message := synMessage.Text;
  //Options.ContentType := ''; // TODO: provide dropdown to choose format
  // TODO (later): facilitate rich text editing?
end {TframeCommitMessage.FillCommitOptions};

procedure TframeCommitMessage.PopulateMessages;
const
  Limit: Integer = 20;
var
  miMessage: TMenuItem;
  i: Integer;
  Revision: TFossilRevision;
begin
  // TODO: this just loads the most recent commit entries. Shouldn't we be storing the messages
  //  after they've been committed in this editor?
  miMessage := miNoMessages.Parent;
  for i := miMessage.Count - 1 downto 0 do begin
    if miMessage.Items[i] <> miNoMessages then
      miMessage.Delete(i);
  end;
  i := 0;
  try
    FWorkdir.Checkout.LoadRevisions(Limit);
    for Revision in FWorkdir.Checkout.Revisions do begin
      miMessage := TMenuItem.Create(pumMessages);
      miMessage.Caption := Revision.Message.Substring(0, 99);
      if Revision.Message.Length > 99 then
        miMessage.Caption := miMessage.Caption + '…';
      miMessage.Hint := Revision.Message;
      miMessage.OnClick := @miMessageClick;
      miMessage.Visible := True;
      pumMessages.Items.Add(miMessage);
      i += 1;
      if i >= Limit then Break;
    end;
  finally
    miNoMessages.Visible := i = 0;
  end;
end;

procedure TframeCommitMessage.btnCommitClick(Sender: TObject);
var
  i: Integer;
  Tags: TStringList;
  Files: TFossilFileList;
  QFile: TFileVersion;
  QFiles: TStringList;
  Summary: string;
  Lines: TStringList;
begin
  if not btnCommit.Enabled then
    Exit;

  // Initialize the commit options
  FillCommitOptions({var}FOptions);

  // assemble tags
  Tags := TStringList.Create;
  try
    for i := 0 to pumTags.Items.Count - 1 do begin
      if pumTags.Items[i].Visible and pumTags.Items[i].Checked then begin
        Tags.Add(pumTags.Items[i].Caption);
      end;
    end;

    // assemble list of files
    Files := TFossilFileList.Create(False);
    try
      if Assigned(FOnGetFiles) then begin
        modMain.SetBusy(True);
        try
          FOnGetFiles(Self, Files);
        finally
          modMain.SetBusy(False);
        end;

        // Check if there's any NEW files, and ask if they really need
        //  to be added.
        QFiles := TStringList.Create;
        try
          for QFile in Files do begin
            if fsUntracked in QFile.Statuses then
              QFiles.Add(QFile.FullName);
          end;
          if QFiles.Count > 0 then begin
            case MessageDlg('Commit new files?',
                            'Are you sure you want to add the following untracked files?' + LineEnding
                            + LineEnding
                            + QFiles.Text,
                            mtConfirmation,
                            mbYesNoCancel,
                            'CommitNewFiles') of
              mrNo: begin
                // remove all new files from the list of files to commit
                for i := Files.Count - 1 downto 0 do begin
                  if fsUntracked in Files[i].Statuses then
                    Files.Delete(i);
                end;
              end;
              mrYes: begin
                // add all new files to the repository
                QFiles.QuoteChar := '"';
                QFiles.Delimiter := ' ';
                QFiles.StrictDelimiter := False;
                WorkDir.Checkout.Exe.Run('add --force ' + QFiles.DelimitedText);
              end {mrYes};
              mrCancel: begin
                Exit;
              end {mrCancel};
            end {case};
          end {if};
        finally
          QFiles.Free;
        end {finally};

        // Check if there's any MISSING files, and ask if they need to be removed.
        QFiles := TStringList.Create;
        try
          for QFile in Files do begin
            if fsMissing in QFile.Statuses then
              QFiles.Add(QFile.FullName);
          end;
          if QFiles.Count > 0 then begin
            case MessageDlg('Remove missing files?',
                            'Do you want to remove these missing files?' + LineEnding
                            + LineEnding
                            + QFiles.Text,
                            mtConfirmation,
                            mbYesNoCancel,
                            'RemoveMissingFiles') of
              mrNo: begin
                // remove all missing files from the list of files to commit
                for i := Files.Count - 1 downto 0 do begin
                  if fsMissing in Files[i].Statuses then
                    Files.Delete(i);
                end;
              end;
              mrYes: begin
                // marks all missing files as removed from the repository
                QFiles.QuoteChar := '"';
                QFiles.Delimiter := ' ';
                QFiles.StrictDelimiter := False;
                WorkDir.Checkout.Exe.Run('forget ' + QFiles.DelimitedText);
              end {mrYes};
              mrCancel: begin
                Exit;
              end {mrCancel};
            end {case};
          end {if};
        finally
          QFiles.Free;
        end {finally};

        if Files.Count = 0 then begin
          case MessageDlg('Empty commit',
                          'There are no files to commit!' + LineEnding
                          + LineEnding
                          + 'Do you want to continue anyway?',
                          mtConfirmation,
                          mbYesNo, 0 { TODO: help context id for empty commit }, mbNo) of
            mrNo: begin
              Exit;
            end;
          end {case};
        end;
      end {if};

      // TODO: get the command-line options, and show those instead
      i := MessageBoxFunction(PChar('To be done:'+LineEnding + FWorkdir.GetCommitOptions(FOptions)), 'Not implemented', MB_ICONQUESTION + MB_OKCANCEL + MB_DEFBUTTON2);
      if i = 1 then begin
        // if OK then perform the actual commit
        modMain.SetBusy(True);
        Summary := FWorkdir.Commit(@FOptions, Tags, Files);
        Lines := TStringList.Create;
        try
          Lines.Text := Summary;
          for i := Lines.Count - 2 downto 0 do begin
            if Lines[i].StartsWith('Round-trips', true) and Lines[i + 1].StartsWith('Round-trips', true) then
              Lines.Delete(i);
          end;
          Summary := Lines.Text.Trim();
        finally
          Lines.Free;
        end;
        MessageBoxFunction(PChar(Summary), 'Commit', IfThen(Summary.StartsWith('Warning:', True), MB_ICONWARNING, MB_ICONINFORMATION));
        synMessage.BeginUpdate(True);
        try
          synMessage.Lines.Clear;
        finally
          synMessage.EndUpdate;
        end;
        PopulateMessages;
      end;

    finally
      Files.Free;
    end;

    if Assigned(FOnRefreshNeeded) then begin
      FOnRefreshNeeded(Self);
    end;
  finally
    Tags.Free;
    modMain.SetBusy(False);
  end;
end;

procedure TframeCommitMessage.btnBranchClick(Sender: TObject);
var
  frm: TfrmNewBranch;
  Lines: TStringList;
  Line, TagName, HexColor: string;
  CharPos: Integer;
begin
  // TODO: do this via a TAction
  frm := TfrmNewBranch.Create(Self);
  try
    frm.Initialize(FWorkdir.Branch, FOptions);

    if frm.ShowModal = mrOK then begin
      FOptions.CloseBranch := frm.CloseBranch;
      if frm.BranchName <> FOptions.BranchName then begin // The choice has changed
        FOptions.BranchName := frm.BranchName;
        btnBranch.Caption := 'Branch: ' + FOptions.BranchName;

        // Disable the explicit branch color
        if (FOptions.BranchName <> FWorkdir.Branch) or not chkBGColor.Checked then begin
          btnBGColor.ButtonColor := FWorkdir.Checkout.Util.GetDefaultColor(FOptions.BranchName);
          chkBGColor.Checked := False;
        end;
      end;
    end;
  finally
    frm.Free;
  end;
end;

procedure TframeCommitMessage.btnBGColorColorChanged(Sender: TObject);
begin
  chkBGColor.Checked := (btnBGColor.ButtonColor <> clNone);
end;

procedure TframeCommitMessage.btnOptionsClick(Sender: TObject);
var
  Button: TToolButton;
  MenuPos: TPoint;
begin
  Button := Sender as TToolButton;
  MenuPos := Button.Parent.ClientToScreen(Point(Button.Left, Button.Top + Button.Height));
  Button.DropdownMenu.PopUp(MenuPos.X, MenuPos.Y);
end;

procedure TframeCommitMessage.btnStashClick(Sender: TObject);
var
  Files: TFossilFileList;
  Summary: string;
begin
  // assemble list of files
  Files := TFossilFileList.Create(False);
  try
    if Assigned(FOnGetFiles) then begin
      modMain.SetBusy(True);
      try
        FOnGetFiles(Self, Files);
      finally
        modMain.SetBusy(False);
      end;
    end;

    // remove all unselected files from the list of files to commit
    if Files.Count = 0 then begin
      case MessageDlg('No files',
                      'There are no files to stash!' + LineEnding
                      + LineEnding
                      + 'Do you want to continue anyway?',
                      mtConfirmation,
                      mbYesNo, 0 { TODO: help context id for no files to stash }, mbNo) of
        mrNo: begin
          Exit;
        end;
      end {case};
    end;

    modMain.SetBusy(True);
    try
      Summary := FWorkdir.MoveToStash(synMessage.Text, Files, False);
      MessageBoxFunction(PChar(Summary), 'Stash', MB_ICONINFORMATION);

      synMessage.BeginUpdate(True);
      try
        synMessage.Lines.Clear;
      finally
        synMessage.EndUpdate;
      end;
    finally
      modMain.SetBusy(False);
    end;
  finally
    Files.Free;
  end;

  if Assigned(FOnRefreshNeeded) then begin
    FOnRefreshNeeded(Self);
  end;
end;

procedure TframeCommitMessage.btnTagsClick(Sender: TObject);
begin
  // TODO: ask for new tag, then add it as a new menu item to pumTags
  MessageBoxFunction(PChar('(o) Assign new tag: [_____]' + LineEnding +
                           '(o) Assign/remove existing tag(s):' + LineEnding +
                           '    [x] v1.00' + LineEnding +
                           '    [ ] test-release' + LineEnding +
                           '    <...>'),
                      'Not Implemented', 64);
end;

procedure TframeCommitMessage.miMessageClick(Sender: TObject);
begin
  if synMessage.SelAvail then
    synMessage.SelText := (Sender as TMenuItem).Hint
  else
    synMessage.Text := (Sender as TMenuItem).Hint;
end;

procedure TframeCommitMessage.miOptionClick(Sender: TObject);
var
  Options: TCommitOptions;
begin
  Options := cDefaultCommitOptions;
  FillCommitOptions(Options);
  lblSelectedOptions.Caption := 'Selected options: ' + FWorkdir.GetCommitOptions(Options);
end;

procedure TframeCommitMessage.pumMessagesPopup(Sender: TObject);
begin
  if pumMessages.Items.Count <= 1 then
    PopulateMessages;
end;

procedure TframeCommitMessage.scUUIDsCodeCompletion(var Value: string;
  SourceValue: string; var SourceStart, SourceEnd: TPoint; KeyChar: TUTF8Char;
  Shift: TShiftState);
begin
  // Append instead of replacing
  Value := SourceValue + Value;

  // TODO: add the relevant ticket-UUID to the list of tickets affected by this commit
  // (by default, change its status to Fixed? ==> should be a setting)
end;

end.

Added src/a_filelist.lfm.





































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
object fraFileList: TfraFileList
  Left = 0
  Height = 411
  Top = 0
  Width = 514
  ClientHeight = 411
  ClientWidth = 514
  LCLVersion = '1.6.4.0'
  TabOrder = 0
  DesignLeft = 750
  DesignTop = 53
  object lvwFiles: TListView
    Left = 0
    Height = 385
    Top = 26
    Width = 514
    Align = alClient
    AutoWidthLastColumn = True
    Checkboxes = True
    Color = clWindow
    Columns = <    
      item
        Caption = 'Name'
        MinWidth = 50
        Width = 150
      end    
      item
        Caption = 'Path'
        ImageIndex = 11
        Width = 150
      end    
      item
        Caption = 'Status'
        Width = 20
      end    
      item
        Caption = 'Type'
        Width = 192
      end>
    HideSelection = False
    Items.LazData = {
      DA0000000300000009000000FFFFFFFFFFFFFFFF030000000C000000665F636F
      6D6D69742E6C666D050000007372632F61050000004558545241040000002E6C
      666D02000000FFFFFFFFFFFFFFFF030000000900000050616C656F2E6C706907
      0000007372632F70726A06000000454449544544040000002E6C706909000000
      FFFFFFFFFFFFFFFF030000000C000000665F636F6D6D69742E70617305000000
      7372632F7A050000004558545241040000002E706173FFFFFFFFFFFFFFFFFFFF
      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    }
    MultiSelect = True
    ReadOnly = True
    RowSelect = True
    ScrollBars = ssAutoBoth
    SmallImages = modMain.imlFileStatuses
    SortColumn = 1
    SortType = stBoth
    TabOrder = 0
    ViewStyle = vsReport
    OnColumnClick = lvwFilesColumnClick
    OnCompare = lvwFilesCompare
    OnItemChecked = lvwFilesItemChecked
    OnKeyPress = lvwFilesKeyPress
    OnSelectItem = lvwFilesSelectItem
  end
  object tbrFiles: TToolBar
    Left = 0
    Height = 26
    Top = 0
    Width = 514
    Caption = 'tbrFiles'
    EdgeBorders = []
    EdgeInner = esNone
    EdgeOuter = esNone
    Images = modMain.imlMain
    TabOrder = 1
    object chkAll: TCheckBox
      Left = 1
      Height = 21
      Top = 0
      Width = 21
      OnChange = chkAllChange
      State = cbGrayed
      TabOrder = 0
    end
    object btnSep1: TToolButton
      Left = 22
      Height = 22
      Top = 0
      Width = 5
      Style = tbsDivider
    end
    object btnStatus: TToolButton
      Left = 27
      Top = 0
      AllowAllUp = True
      Caption = 'Status'
      DropdownMenu = pumStatus
      ImageIndex = 7
      OnClick = btnStatusClick
      ShowCaption = False
      Style = tbsDropDown
    end
    object btnSep3: TToolButton
      Left = 220
      Height = 22
      Top = 0
      Width = 5
      Style = tbsDivider
    end
    object btnRefresh: TToolButton
      Left = 225
      Hint = 'Refresh'
      Top = 0
      Caption = 'Refresh'
      ImageIndex = 6
      OnClick = btnRefreshClick
    end
    object edtFilter: TEdit
      AnchorSideRight.Control = btnSep3
      Left = 67
      Height = 27
      Top = 0
      Width = 153
      OnChange = edtFilterChange
      TabOrder = 1
    end
    object btnSep2: TToolButton
      Left = 62
      Height = 22
      Top = 0
      Width = 5
      Caption = 'btnSep2'
      Style = tbsDivider
    end
  end
  object pumStatus: TPopupMenu
    Images = modMain.imlMain
    left = 74
    top = 183
    object miUntracked: TMenuItem
      AutoCheck = True
      Caption = 'Untracked'
      Checked = True
      OnClick = miStatusClick
    end
    object miAdded: TMenuItem
      AutoCheck = True
      Caption = 'Added'
      Checked = True
      ShowAlwaysCheckable = True
      OnClick = miStatusClick
    end
    object miModified: TMenuItem
      AutoCheck = True
      Caption = 'Modified'
      Checked = True
      OnClick = miStatusClick
    end
    object miDeleted: TMenuItem
      AutoCheck = True
      Caption = 'Deleted'
      Checked = True
      OnClick = miStatusClick
    end
    object miMissing: TMenuItem
      AutoCheck = True
      Caption = 'Missing'
      Checked = True
      OnClick = miStatusClick
    end
    object miMerged: TMenuItem
      AutoCheck = True
      Caption = 'Merged'
      Checked = True
      OnClick = miStatusClick
    end
    object miConflicted: TMenuItem
      AutoCheck = True
      Caption = 'Conflicted'
      Checked = True
      OnClick = miStatusClick
    end
    object miUnchanged: TMenuItem
      AutoCheck = True
      Caption = 'Unchanged'
      OnClick = miStatusClick
    end
    object miIgnored: TMenuItem
      AutoCheck = True
      Caption = 'Ignored'
      OnClick = miStatusClick
    end
  end
end

Added src/a_filelist.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
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
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
unit A_FileList;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, ComCtrls, ExtCtrls, ActnList,
  StdCtrls, Menus, M_Main, u_Fossil;

type
  TFileNameComparer = function(const FN1, FN2: TFileName): Integer;

  TFileSelectionEvent = procedure(Sender: TObject; AFile: TFileVersion; Selected: Boolean) of object;

  { TfraFileList }

  TfraFileList = class(TFrame)
    btnSep3: TToolButton;
    chkAll: TCheckBox;
    edtFilter: TEdit;
    lvwFiles: TListView;
    miUnchanged: TMenuItem;
    miIgnored: TMenuItem;
    miConflicted: TMenuItem;
    miMerged: TMenuItem;
    miUntracked: TMenuItem;
    miMissing: TMenuItem;
    miDeleted: TMenuItem;
    miAdded: TMenuItem;
    miModified: TMenuItem;
    pumStatus: TPopupMenu;
    tbrFiles: TToolBar;
    btnRefresh: TToolButton;
    btnSep1: TToolButton;
    btnStatus: TToolButton;
    btnSep2: TToolButton;
    procedure btnRefreshClick(Sender: TObject);
    procedure btnStatusClick(Sender: TObject);
    procedure chkAllChange(Sender: TObject);
    procedure edtFilterChange(Sender: TObject);
    procedure lvwFilesColumnClick(Sender: TObject; Column: TListColumn);
    procedure lvwFilesCompare(Sender: TObject; Item1, Item2: TListItem;
      Data: Integer; var Compare: Integer);
    procedure lvwFilesItemChecked(Sender: TObject; Item: TListItem);
    procedure lvwFilesKeyPress(Sender: TObject; var Key: char);
    procedure lvwFilesSelectItem(Sender: TObject; Item: TListItem;
      Selected: Boolean);
    procedure miStatusClick(Sender: TObject);
  private
    { private declarations }
    FRevision: TFossilRevision;
    //FRetrievedStatuses: TFileStatuses;
    FDisplayStatuses: TFileStatuses;
    FRequestStates: TFossilFileStates;

    FOnSelectionChanged: TFileSelectionEvent;
    FCompareFileNames: TFileNameComparer;

    procedure SetRevision(AValue: TFossilRevision);
    procedure DetermineDisplayStatuses;
    procedure SetStatuses(AValue: TFileStatuses);
  protected
    function  DoFilter(const oFile: TFileVersion): Boolean; virtual;
  public
    { public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor  Destroy; override;

    procedure PopulateList(ForceReload: Boolean = False);
    procedure LoadCheckedFiles(const Files: TFossilFileList);

    property DisplayStatuses: TFileStatuses read FDisplayStatuses write SetStatuses;
    property Revision: TFossilRevision      read FRevision        write SetRevision;
  published
    property OnSelectionChanged: TFileSelectionEvent  read FOnSelectionChanged  write FOnSelectionChanged;
  end;


implementation
uses
  Graphics, Math, StrUtils;

{$R *.lfm}

const
  cFileNameComparer: array[Boolean] of TFileNameComparer = (@CompareFilenamesIgnoreCase,
                                                            @CompareFilenames);
  {$IFDEF DARWIN}
  cSortAscending: string = '▼';
  cSortDescending: string = '▲';
  cSortDirection: array[TSortDirection] of string = ('▼', '▲');
  {$ELSE}
  cSortAscending: string = '▾';
  cSortDescending: string = '▴';
  cSortDirection: array[TSortDirection] of string = ('▾', '▴');
  {$ENDIF}

  cColFilename = 0;
  cColPath = 1;
  cColStatus = 2;
  cColType = 3;

{ TfraFileList }

constructor TfraFileList.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FCompareFileNames := cFileNameComparer[FileNameCaseSensitive];
  DetermineDisplayStatuses;
end;

destructor TfraFileList.Destroy;
begin
  inherited Destroy;
end;

procedure TfraFileList.PopulateList(ForceReload: Boolean = False);
const
  {$IFDEF MSWINDOWS}
  ResizableCols: set of Byte = [cColFilename,cColPath,cColType]; // don't resize the status column; that's an icon
  {$ELSE}
  ResizableCols: set of Byte = [cColFilename,cColPath,cColStatus, cColType]; // other platforms don't show the icon
  {$ENDIF}
var
  PrevStates: TFossilFileStates;
  Item: TListItem;
  FocusedFile: string;
  FileType: string;
  Checked, Unchecked: Boolean;
  SelectedFiles: TStringList;
  i, ci: Integer;
  oFile: TFileVersion;
  Bmp: TBitmap;
  ColWidths: array of Integer;
begin
  modMain.SetBusy(True);
  Bmp := TBitmap.Create;
  try
    Bmp.Canvas.Font.Assign(lvwFiles.Font);

    SetLength(ColWidths, lvwFiles.ColumnCount);
    for i := 0 to lvwFiles.ColumnCount - 1 do begin
      ColWidths[i] := Bmp.Canvas.TextWidth(lvwFiles.Column[i].Caption);
    end;

    // Remember the ItemFocused, the ItemIndex and the selected files, so we can restore all
    //  that after repopulating.
    Item := lvwFiles.ItemFocused;
    if Assigned(Item) then
      FocusedFile := TFileVersion(Item.Data).FullName
    else
      FocusedFile := '';

    Checked := False;
    Unchecked := False;
    SelectedFiles := TStringList.Create;
    try
      SelectedFiles.CaseSensitive := FRevision.Checkout.CaseSensitive;
      for Item in lvwFiles.Items do begin
        if Item.Selected then begin
          SelectedFiles.Add(TFileVersion(Item.Data).FullName);
          if Assigned(FOnSelectionChanged) then
            FOnSelectionChanged(Self, TFileVersion(Item.Data), False);
        end;
      end;

      lvwFiles.Items.BeginUpdate;
      try
        lvwFiles.Items.Clear;
        lvwFiles.SortType := stNone;

        // Check if we need to reload the list of files
        if not ForceReload then begin
          PrevStates := FRequestStates;
          DetermineDisplayStatuses;
        end;
        if ForceReload or ((FRequestStates - PrevStates) <> []) then begin
          FRevision.LoadFiles(FRequestStates, ForceReload);
        end;

        // Repopulate the listview
        for i := 0 to FRevision.Files.Count - 1 do begin
          oFile := FRevision.Files[i];
          if DoFilter(oFile) then begin
            Item := lvwFiles.Items.Add;
            Item.Data := Pointer(oFile);
            Item.Caption := oFile.Filename;
            Item.SubItems.Add(oFile.Path);
            Item.ImageIndex := modMain.ImageIndexFromStatuses(oFile.Statuses);
            Item.SubItems.Add(oFile.Status);
            // TODO: get type name from OS?
            FileType := ExtractFileExt(oFile.Filename);
            if (Length(FileType) > 0) and (FileType[1] = '.') then
              FileType := Copy(FileType, 2, Length(FileType));
            {$IFDEF MSWINDOWS}
            Item.SubItemImages[
              Item.SubItems.Add(FileType)
            ] := modMain.ImageIndexFromType(ExtractFileExt(oFile.Filename));
            {$ELSE}
            Item.SubItems.Add(FileType);
            {$ENDIF}

            if SelectedFiles.IndexOf(oFile.FullName) > -1 then begin
              Item.Selected := True;
              // TODO: test if the event triggers or not, otherwise trigger it ourselves
            end;
            if oFile.Selected then begin
              Item.Checked := True;
              Checked := True;
            end else begin
              Unchecked := True;
            end;
            if SameFileName(oFile.FullName, FocusedFile) then
              lvwFiles.ItemFocused := Item;

            ColWidths[0] := Max(ColWidths[0], Bmp.Canvas.TextWidth(Item.Caption));
            for ci := 1 to High(ColWidths) do begin
              if Item.SubItems.Count > ci then
                ColWidths[ci] := Max(ColWidths[ci], Bmp.Canvas.TextWidth(Item.SubItems[ci - 1]));
            end;
          end;
        end {for};

        for i := 0 to lvwFiles.ColumnCount - 1 do begin
          if i in ResizableCols then begin
            if i = 0 then // that column also harbours the icon and the checkbox
              lvwFiles.Column[i].Width := ColWidths[i] + 20 + 20 + 6
            else
              lvwFiles.Column[i].Width := ColWidths[i] + 6;
          end;
        end;
        lvwFiles.SortType := stData;

        {$IFNDEF MSWINDOWS}
        if (lvwFiles.SortColumn <> -1) and not (AnsiStartsStr(cSortAscending, lvwFiles.Column[lvwFiles.SortColumn].Caption) or AnsiStartsStr(cSortDescending, lvwFiles.Column[lvwFiles.SortColumn].Caption)) then
          lvwFiles.Column[lvwFiles.SortColumn].Caption := cSortDirection[lvwFiles.SortDirection] + ' ' + lvwFiles.Column[lvwFiles.SortColumn].Caption;
        {$ENDIF}
      finally
        lvwFiles.Items.EndUpdate;
      end;
    finally
      SelectedFiles.Free;
    end;
  finally
    Bmp.Free;
    modMain.SetBusy(False);
  end;

  if Checked and Unchecked then
    chkAll.State := cbGrayed
  else if Checked then
    chkAll.State := cbChecked
  else
    chkAll.State := cbUnchecked;
end;

procedure TfraFileList.LoadCheckedFiles(const Files: TFossilFileList);
var
  Item: TListItem;
begin
  for Item in lvwFiles.Items do begin
    if Item.Checked then begin
      Files.Add(TFileVersion(Item.Data));
    end;
  end;
end;

procedure TfraFileList.lvwFilesCompare(Sender: TObject; Item1,
  Item2: TListItem; Data: Integer; var Compare: Integer);
var
  File1, File2: TFileVersion;
begin
  if Item1.Data = nil then begin
    if Item2.Data = nil then
      Compare := 0
    else
      Compare := -1;
    Exit;
  end else if Item2.Data = nil then begin
    Compare := 1;
    Exit;
  end;
  File1 := TFileVersion(Item1.Data);
  File2 := TFileVersion(Item2.Data);
  case lvwFiles.SortColumn of
    cColFilename: begin
      Compare := FCompareFileNames(File1.Filename, File2.Filename);
    end;
    cColStatus: begin // file status
      Compare := CompareStr(File1.Status, File2.Status);
    end;
    cColPath: begin // path (+ name)
      Compare := FCompareFileNames(File1.Path, File2.Path);
      if Compare = 0 then
         Compare := FCompareFileNames(File1.Filename, File2.Filename);
    end;
    cColType: begin // extension, then name, then path
      Compare := FCompareFileNames(ExtractFileExt(File1.Filename), ExtractFileExt(File2.Filename));
      if Compare = 0 then begin
        Compare := FCompareFileNames(File1.Filename, File2.Filename);
        if Compare = 0 then
           Compare := FCompareFileNames(File1.Path, File2.Path);
      end;
    end;
  end;
  if lvwFiles.SortDirection = sdDescending then
     Compare := -Compare;
end;

procedure TfraFileList.lvwFilesItemChecked(Sender: TObject; Item: TListItem);
var
  Checked, Unchecked: Boolean;
  i: Integer;
begin
  if Assigned(Item) then
     TWorkFile(Item.Data).Selected := Item.Checked;

  Checked := False;
  Unchecked := False;
  for i := 0 to lvwFiles.Items.Count - 1 do begin
    if lvwFiles.Items[i].Checked then
      Checked := True
    else
      Unchecked := True;
  end;
  if Checked and Unchecked then
    chkAll.State := cbGrayed
  else if Checked then
    chkAll.State := cbChecked
  else
    chkAll.State := cbUnchecked;
end;

procedure TfraFileList.lvwFilesKeyPress(Sender: TObject; var Key: char);
var
  Checked: Boolean;
  i: Integer;
begin
  case Key of
    ' ': begin // space
      lvwFiles.Items.BeginUpdate;
      try
        Checked := Assigned(lvwFiles.Selected) and not lvwFiles.Selected.Checked;
        for i := 0 to lvwFiles.Items.Count - 1 do begin
          if (lvwFiles.Items[i].Selected) or (lvwFiles.Items[i] = lvwFiles.Selected) then
            lvwFiles.Items[i].Checked := Checked;
        end;
        lvwFilesItemChecked(Sender, nil);
      finally
        lvwFiles.Items.EndUpdate;
      end;
      lvwFiles.SetFocus;
      Key := #0;
    end;
  end;
end;

procedure TfraFileList.lvwFilesSelectItem(Sender: TObject; Item: TListItem;
  Selected: Boolean);
begin
  if Assigned(FOnSelectionChanged) then
    FOnSelectionChanged(Self, TFileVersion(Item.Data), Selected);
end;

procedure TfraFileList.miStatusClick(Sender: TObject);
begin
  // TODO: set up a timer (try 0.5 seconds) before we actually start reloading
  // TODO: create a thread that actually starts reloading the data from Fossil;
  //  if there is already such a thread, tell it to terminate.
  //  If Fossil needs to be run multiple times, the thread should refresh the file list
  //   (and repopulate) as soon as it has any results. This means there might be multiple
  //   listview refreshes. See how that performs.
  PopulateList(False);
end;

procedure TfraFileList.SetStatuses(AValue: TFileStatuses);
begin
  if FDisplayStatuses = AValue then Exit;
  FDisplayStatuses := AValue;
end;

procedure TfraFileList.DetermineDisplayStatuses;
begin
  FDisplayStatuses := []; // requested statuses this time
  if miUntracked.Checked then Include(FDisplayStatuses, fsUntracked);
  if miAdded.Checked then Include(FDisplayStatuses, fsAdded);
  if miModified.Checked then Include(FDisplayStatuses, fsModified);
  if miDeleted.Checked then Include(FDisplayStatuses, fsDeleted);
  if miMissing.Checked then Include(FDisplayStatuses, fsMissing);
  if miMerged.Checked then Include(FDisplayStatuses, fsMerged);
  if miConflicted.Checked then Include(FDisplayStatuses, fsConflict);
  if miIgnored.Checked then Include(FDisplayStatuses, fsIgnored);
  if miUnchanged.Checked then Include(FDisplayStatuses, fsUnchanged);

  FRequestStates := [];
  if fsUntracked in FDisplayStatuses then Include(FRequestStates, ffsExtra);
  if (FDisplayStatuses * [fsAdded, fsModified, fsDeleted, fsMissing, fsMerged, fsConflict]) <> [] then Include(FRequestStates, ffsChanged);
  if fsIgnored in FDisplayStatuses then Include(FRequestStates, ffsIgnored);
  if fsUnchanged in FDisplayStatuses then Include(FRequestStates, ffsUnchanged);
end;

procedure TfraFileList.SetRevision(AValue: TFossilRevision);
begin
  if FRevision = AValue then Exit;

  // TODO: clear the list; we're switching to a different revision anyway?
  //   otherwise we'll get SIGSEVs when trying to keep track of the selection
  //   although — we might not; the previous revision does still exist...
  FRevision := AValue;
  FCompareFileNames := cFileNameComparer[Revision.Checkout.CaseSensitive];

  // Repopulate the list for the new revision
  PopulateList(True);
end;

function TfraFileList.DoFilter(const oFile: TFileVersion): Boolean;
begin
  Result := (oFile.Statuses * FDisplayStatuses) <> [];
  if Result = False then
    Exit;

  Result := (edtFilter.Text = '') or (Pos(LowerCase(edtFilter.Text), LowerCase(oFile.FullName)) > 0);

  // TODO: test for matching file pattern
end;

procedure TfraFileList.chkAllChange(Sender: TObject);
var
  Checked: Boolean;
  i: Integer;
begin
  Checked := chkAll.Checked;
  lvwFiles.BeginUpdate;
  try
    for i := 0 to lvwFiles.Items.Count - 1 do begin
      lvwFiles.Items[i].Checked := Checked;
    end;
  finally
    lvwFiles.EndUpdate;
    lvwFiles.Repaint;
  end;
end;

procedure TfraFileList.btnStatusClick(Sender: TObject);
var
  Button: TToolButton;
  MenuPos: TPoint;
begin
  Button := Sender as TToolButton;
  MenuPos.X := Button.Left;
  MenuPos.Y := Button.Top + Button.Height;
  MenuPos := Button.Parent.ClientToScreen(MenuPos);
  Button.DropdownMenu.PopUp(MenuPos.X, MenuPos.Y);
end;

procedure TfraFileList.btnRefreshClick(Sender: TObject);
begin
  PopulateList(True);
end;

procedure TfraFileList.edtFilterChange(Sender: TObject);
begin
  PopulateList(False);
end;



procedure TfraFileList.lvwFilesColumnClick(Sender: TObject; Column: TListColumn);
begin
  // if the SortColumn is different from the Column, then assume the new column
  //  will be sorted ascending; otherwise, the other direction than now.
  {$IFDEF MSWINDOWS}
  lvwFiles.Column[lvwFiles.SortColumn].ImageIndex := -1;
  if lvwFiles.SortColumn <> Column.Index then begin
    Column.ImageIndex := 11;
  end else if lvwFiles.SortDirection = sdAscending then begin
    Column.ImageIndex := 10;
  end else begin
    Column.ImageIndex := 11;
  end;
  {$ELSE}
  if lvwFiles.SortColumn <> -1 then begin
    if AnsiStartsStr(cSortAscending, lvwFiles.Column[lvwFiles.SortColumn].Caption) or AnsiStartsStr(cSortDescending, lvwFiles.Column[lvwFiles.SortColumn].Caption) then
      lvwFiles.Column[lvwFiles.SortColumn].Caption := Copy(lvwFiles.Column[lvwFiles.SortColumn].Caption, Length(cSortAscending) + 2, 50);
  end;
  if (lvwFiles.SortColumn <> Column.Index) then begin
    Column.Caption := cSortAscending + ' ' + Column.Caption;
  end else if lvwFiles.SortDirection = sdDescending then begin
    Column.Caption := cSortAscending + ' ' + Column.Caption;
  end else begin
    Column.Caption := cSortDescending + ' ' + Column.Caption;
  end;
  {$ENDIF}
end;

end.

Added src/a_fileversioninfo.lfm.





















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
object fraFileVersionInfo: TfraFileVersionInfo
  Left = 0
  Height = 440
  Top = 0
  Width = 573
  ClientHeight = 440
  ClientWidth = 573
  Font.Quality = fqCleartype
  ParentFont = False
  TabOrder = 0
  DesignLeft = 565
  DesignTop = 297
  object tbcView: TTabControl
    Left = 0
    Height = 414
    Top = 26
    Width = 573
    Images = modMain.imlMain
    OnChange = tbcViewChange
    OnChanging = tbcViewChanging
    OnGetImageIndex = tbcViewGetImageIndex
    TabIndex = 0
    Tabs.Strings = (
      'Diff'
      'Side-by-side'
      'Contents'
      'Annotated'
      'Hex'
    )
    Align = alClient
    TabOrder = 0
    object imgContents: TImage
      Left = 2
      Height = 384
      Top = 28
      Width = 569
      Align = alClient
      Center = True
      Proportional = True
      Stretch = True
      Transparent = True
      Visible = False
    end
    inline synEdit: TSynEdit
      Cursor = crDefault
      Left = 2
      Height = 384
      Top = 28
      Width = 569
      Align = alClient
      Font.Height = 12
      Font.Name = 'Monaco'
      Font.Pitch = fpFixed
      Font.Quality = fqCleartype
      ParentColor = False
      ParentFont = False
      TabOrder = 1
      OnMouseWheel = synEditMouseWheel
      Gutter.Width = 34
      Gutter.MouseActions = <>
      RightGutter.Width = 0
      RightGutter.MouseActions = <>
      Keystrokes = <      
        item
          Command = ecUp
          ShortCut = 16422
        end      
        item
          Command = ecSelUp
          ShortCut = 8230
        end      
        item
          Command = ecScrollUp
          ShortCut = 38
        end      
        item
          Command = ecDown
          ShortCut = 16424
        end      
        item
          Command = ecSelDown
          ShortCut = 8232
        end      
        item
          Command = ecScrollDown
          ShortCut = 40
        end      
        item
          Command = ecScrollLeft
          ShortCut = 37
        end      
        item
          Command = ecSelLeft
          ShortCut = 8229
        end      
        item
          Command = ecWordLeft
          ShortCut = 16421
        end      
        item
          Command = ecSelWordLeft
          ShortCut = 24613
        end      
        item
          Command = ecScrollRight
          ShortCut = 39
        end      
        item
          Command = ecSelRight
          ShortCut = 8231
        end      
        item
          Command = ecWordRight
          ShortCut = 16423
        end      
        item
          Command = ecSelWordRight
          ShortCut = 24615
        end      
        item
          Command = ecPageDown
          ShortCut = 34
        end      
        item
          Command = ecSelPageDown
          ShortCut = 8226
        end      
        item
          Command = ecPageBottom
          ShortCut = 16418
        end      
        item
          Command = ecSelPageBottom
          ShortCut = 24610
        end      
        item
          Command = ecPageUp
          ShortCut = 33
        end      
        item
          Command = ecSelPageUp
          ShortCut = 8225
        end      
        item
          Command = ecPageTop
          ShortCut = 16417
        end      
        item
          Command = ecSelPageTop
          ShortCut = 24609
        end      
        item
          Command = ecLineStart
          ShortCut = 36
        end      
        item
          Command = ecSelLineStart
          ShortCut = 8228
        end      
        item
          Command = ecEditorTop
          ShortCut = 16420
        end      
        item
          Command = ecSelEditorTop
          ShortCut = 24612
        end      
        item
          Command = ecLineEnd
          ShortCut = 35
        end      
        item
          Command = ecSelLineEnd
          ShortCut = 8227
        end      
        item
          Command = ecEditorBottom
          ShortCut = 16419
        end      
        item
          Command = ecSelEditorBottom
          ShortCut = 24611
        end      
        item
          Command = ecToggleMode
          ShortCut = 45
        end      
        item
          Command = ecCopy
          ShortCut = 16429
        end      
        item
          Command = ecPaste
          ShortCut = 8237
        end      
        item
          Command = ecDeleteChar
          ShortCut = 46
        end      
        item
          Command = ecCut
          ShortCut = 8238
        end      
        item
          Command = ecDeleteLastChar
          ShortCut = 8
        end      
        item
          Command = ecDeleteLastChar
          ShortCut = 8200
        end      
        item
          Command = ecDeleteLastWord
          ShortCut = 16392
        end      
        item
          Command = ecUndo
          ShortCut = 32776
        end      
        item
          Command = ecRedo
          ShortCut = 40968
        end      
        item
          Command = ecLineBreak
          ShortCut = 13
        end      
        item
          Command = ecSelectAll
          ShortCut = 16449
        end      
        item
          Command = ecCopy
          ShortCut = 16451
        end      
        item
          Command = ecBlockIndent
          ShortCut = 24649
        end      
        item
          Command = ecLineBreak
          ShortCut = 16461
        end      
        item
          Command = ecInsertLine
          ShortCut = 16462
        end      
        item
          Command = ecDeleteWord
          ShortCut = 16468
        end      
        item
          Command = ecBlockUnindent
          ShortCut = 24661
        end      
        item
          Command = ecPaste
          ShortCut = 16470
        end      
        item
          Command = ecCut
          ShortCut = 16472
        end      
        item
          Command = ecDeleteLine
          ShortCut = 16473
        end      
        item
          Command = ecDeleteEOL
          ShortCut = 24665
        end      
        item
          Command = ecUndo
          ShortCut = 16474
        end      
        item
          Command = ecRedo
          ShortCut = 24666
        end      
        item
          Command = ecGotoMarker0
          ShortCut = 16432
        end      
        item
          Command = ecGotoMarker1
          ShortCut = 16433
        end      
        item
          Command = ecGotoMarker2
          ShortCut = 16434
        end      
        item
          Command = ecGotoMarker3
          ShortCut = 16435
        end      
        item
          Command = ecGotoMarker4
          ShortCut = 16436
        end      
        item
          Command = ecGotoMarker5
          ShortCut = 16437
        end      
        item
          Command = ecGotoMarker6
          ShortCut = 16438
        end      
        item
          Command = ecGotoMarker7
          ShortCut = 16439
        end      
        item
          Command = ecGotoMarker8
          ShortCut = 16440
        end      
        item
          Command = ecGotoMarker9
          ShortCut = 16441
        end      
        item
          Command = ecSetMarker0
          ShortCut = 24624
        end      
        item
          Command = ecSetMarker1
          ShortCut = 24625
        end      
        item
          Command = ecSetMarker2
          ShortCut = 24626
        end      
        item
          Command = ecSetMarker3
          ShortCut = 24627
        end      
        item
          Command = ecSetMarker4
          ShortCut = 24628
        end      
        item
          Command = ecSetMarker5
          ShortCut = 24629
        end      
        item
          Command = ecSetMarker6
          ShortCut = 24630
        end      
        item
          Command = ecSetMarker7
          ShortCut = 24631
        end      
        item
          Command = ecSetMarker8
          ShortCut = 24632
        end      
        item
          Command = ecSetMarker9
          ShortCut = 24633
        end      
        item
          Command = EcFoldLevel1
          ShortCut = 41009
        end      
        item
          Command = EcFoldLevel2
          ShortCut = 41010
        end      
        item
          Command = EcFoldLevel1
          ShortCut = 41011
        end      
        item
          Command = EcFoldLevel1
          ShortCut = 41012
        end      
        item
          Command = EcFoldLevel1
          ShortCut = 41013
        end      
        item
          Command = EcFoldLevel6
          ShortCut = 41014
        end      
        item
          Command = EcFoldLevel7
          ShortCut = 41015
        end      
        item
          Command = EcFoldLevel8
          ShortCut = 41016
        end      
        item
          Command = EcFoldLevel9
          ShortCut = 41017
        end      
        item
          Command = EcFoldLevel0
          ShortCut = 41008
        end      
        item
          Command = EcFoldCurrent
          ShortCut = 41005
        end      
        item
          Command = EcUnFoldCurrent
          ShortCut = 41003
        end      
        item
          Command = EcToggleMarkupWord
          ShortCut = 32845
        end      
        item
          Command = ecNormalSelect
          ShortCut = 24654
        end      
        item
          Command = ecColumnSelect
          ShortCut = 24643
        end      
        item
          Command = ecLineSelect
          ShortCut = 24652
        end      
        item
          Command = ecTab
          ShortCut = 9
        end      
        item
          Command = ecShiftTab
          ShortCut = 8201
        end      
        item
          Command = ecMatchBracket
          ShortCut = 24642
        end      
        item
          Command = ecColSelUp
          ShortCut = 40998
        end      
        item
          Command = ecColSelDown
          ShortCut = 41000
        end      
        item
          Command = ecColSelLeft
          ShortCut = 40997
        end      
        item
          Command = ecColSelRight
          ShortCut = 40999
        end      
        item
          Command = ecColSelPageDown
          ShortCut = 40994
        end      
        item
          Command = ecColSelPageBottom
          ShortCut = 57378
        end      
        item
          Command = ecColSelPageUp
          ShortCut = 40993
        end      
        item
          Command = ecColSelPageTop
          ShortCut = 57377
        end      
        item
          Command = ecColSelLineStart
          ShortCut = 40996
        end      
        item
          Command = ecColSelLineEnd
          ShortCut = 40995
        end      
        item
          Command = ecColSelEditorTop
          ShortCut = 57380
        end      
        item
          Command = ecColSelEditorBottom
          ShortCut = 57379
        end>
      MouseActions = <>
      MouseTextActions = <>
      MouseSelActions = <>
      Lines.Strings = (
        '--- C:\MC\Code\Projects\Voronwe\GUI\FrontEnd\LazPaleo\src\prj\laz\paleo.lpi'
        '+++ C:\MC\Code\Projects\Voronwe\GUI\FrontEnd\LazPaleo\src\prj\laz\paleo.lpi'
        '@@ -3,23 +3,111 @@'
        '   <ProjectOptions>'
        '     <Version Value="9"/>'
        '     <General>'
        '       <SessionStorage Value="InProjectDir"/>'
        '       <MainUnit Value="0"/>'
        '-      <Title Value="paleo"/>'
        '+      <Title Value="Paleo"/>'
        '       <ResourceType Value="res"/>'
        '       <UseXPManifest Value="True"/>'
        '+      <XPManifest>'
        '+        <DpiAware Value="True"/>'
        '+        <UIAccess Value="True"/>'
        '+      </XPManifest>'
        '       <Icon Value="0"/>'
        '     </General>'
        '     <i18n>'
        '       <EnableI18N LFM="False"/>'
        '     </i18n>'
        '     <VersionInfo>'
        '-      <StringTable ProductVersion=""/>'
        '+      <UseVersionInfo Value="True"/>'
        '+      <MinorVersionNr Value="2"/>'
        '+      <Language Value="0809"/>'
        '+      <StringTable CompanyName="Voronwë" FileDescription="Fossil UI" InternalName="paleo" LegalCopyright="© Martijn Coppoolse" OriginalFilename="paleo" ProductName="Stylemys" ProductVersion=""/>'
        '     </VersionInfo>'
        '-    <BuildModes Count="1">'
        '+    <BuildModes Count="3">'
        '       <Item1 Name="Default" Default="True"/>'
        '+      <Item2 Name="Debug">'
        '+        <CompilerOptions>'
        '+          <Version Value="11"/>'
        '+          <Target>'
        '+            <Filename Value="../../out/$(TargetCPU)-$(TargetOS)/paleo"/>'
        '+          </Target>'
        '+          <SearchPaths>'
        '+            <IncludeFiles Value="$(ProjOutDir)"/>'
        '+            <OtherUnitFiles Value="../.."/>'
        '+            <UnitOutputDirectory Value="../../out/lib/$(TargetCPU)-$(TargetOS)"/>'
        '+          </SearchPaths>'
        '+          <Parsing>'
        '+            <SyntaxOptions>'
        '+              <IncludeAssertionCode Value="True"/>'
        '+            </SyntaxOptions>'
        '+          </Parsing>'
        '+          <CodeGeneration>'
        '+            <Checks>'
        '+              <IOChecks Value="True"/>'
        '+              <RangeChecks Value="True"/>'
        '+              <OverflowChecks Value="True"/>'
        '+              <StackChecks Value="True"/>'
        '+            </Checks>'
        '+          </CodeGeneration>'
        '+          <Linking>'
        '+            <Debugging>'
        '+              <DebugInfoType Value="dsDwarf2Set"/>'
        '+              <UseHeaptrc Value="True"/>'
        '+              <UseExternalDbgSyms Value="True"/>'
        '+            </Debugging>'
        '+            <Options>'
        '+              <Win32>'
        '+                <GraphicApplication Value="True"/>'
        '+              </Win32>'
        '+            </Options>'
        '+          </Linking>'
        '+          <Other>'
        '+            <CompilerMessages>'
        '+              <MsgFileName Value=""/>'
        '+            </CompilerMessages>'
        '+            <CompilerPath Value="$(CompPath)"/>'
        '+          </Other>'
        '+        </CompilerOptions>'
        '+      </Item2>'
        '+      <Item3 Name="Release">'
        '+        <CompilerOptions>'
        '+          <Version Value="11"/>'
        '+          <Target>'
        '+            <Filename Value="../../../out/$(TargetCPU)-$(TargetOS)/paleo"/>'
        '+          </Target>'
        '+          <SearchPaths>'
        '+            <IncludeFiles Value="$(ProjOutDir)"/>'
        '+            <OtherUnitFiles Value="../.."/>'
        '+            <UnitOutputDirectory Value="../../../out/lib/$(TargetCPU)-$(TargetOS)"/>'
        '+          </SearchPaths>'
        '+          <CodeGeneration>'
        '+            <SmartLinkUnit Value="True"/>'
        '+            <Optimizations>'
        '+              <OptimizationLevel Value="3"/>'
        '+            </Optimizations>'
        '+            <SmallerCode Value="True"/>'
        '+          </CodeGeneration>'
        '+          <Linking>'
        '+            <Debugging>'
        '+              <GenerateDebugInfo Value="False"/>'
        '+            </Debugging>'
        '+            <LinkSmart Value="True"/>'
        '+            <Options>'
        '+              <Win32>'
        '+                <GraphicApplication Value="True"/>'
        '+              </Win32>'
        '+            </Options>'
        '+          </Linking>'
        '+          <Other>'
        '+            <CompilerMessages>'
        '+              <MsgFileName Value=""/>'
        '+            </CompilerMessages>'
        '+            <CompilerPath Value="$(CompPath)"/>'
        '+          </Other>'
        '+        </CompilerOptions>'
        '+      </Item3>'
        '     </BuildModes>'
        '     <PublishOptions>'
        '       <Version Value="2"/>'
        '     </PublishOptions>'
        '     <RunParams>'
        '@@ -76,16 +164,16 @@'
        '     </Units>'
        '   </ProjectOptions>'
        '   <CompilerOptions>'
        '     <Version Value="11"/>'
        '     <Target>'
        '-      <Filename Value="paleo"/>'
        '+      <Filename Value="../../out/$(TargetCPU)-$(TargetOS)/paleo"/>'
        '     </Target>'
        '     <SearchPaths>'
        '       <IncludeFiles Value="$(ProjOutDir)"/>'
        '       <OtherUnitFiles Value="../.."/>'
        '-      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>'
        '+      <UnitOutputDirectory Value="../../out/lib/$(TargetCPU)-$(TargetOS)"/>'
        '     </SearchPaths>'
        '     <Linking>'
        '       <Options>'
        '         <Win32>'
        '           <GraphicApplication Value="True"/>'
      )
      Options = [eoAutoIndent, eoBracketHighlight, eoEnhanceHomeKey, eoGroupUndo, eoHideRightMargin, eoKeepCaretX, eoNoCaret, eoShowScrollHint, eoTabIndent, eoAltSetsColumnMode, eoShowCtrlMouseLinks]
      Options2 = [eoCaretSkipTab, eoEnhanceEndKey, eoOverwriteBlock, eoAutoHideCursor]
      MouseOptions = [emAltSetsColumnMode, emShowCtrlMouseLinks]
      VisibleSpecialChars = [vscSpace, vscTabAtLast]
      OverwriteCaret = ctHalfBlock
      ReadOnly = True
      RightEdge = -1
      ScrollBars = ssAutoBoth
      SelectedColor.BackPriority = 50
      SelectedColor.ForePriority = 50
      SelectedColor.FramePriority = 50
      SelectedColor.BoldPriority = 50
      SelectedColor.ItalicPriority = 50
      SelectedColor.UnderlinePriority = 50
      SelectedColor.StrikeOutPriority = 50
      BracketHighlightStyle = sbhsBoth
      BracketMatchColor.Background = clNone
      BracketMatchColor.Foreground = clNone
      BracketMatchColor.Style = [fsBold]
      FoldedCodeColor.Background = clNone
      FoldedCodeColor.Foreground = clGray
      FoldedCodeColor.FrameColor = clGray
      MouseLinkColor.Background = clNone
      MouseLinkColor.Foreground = clBlue
      LineHighlightColor.Background = clNone
      LineHighlightColor.Foreground = clNone
      TabWidth = 4
      WantTabs = False
      inline SynLeftGutterPartList1: TSynGutterPartList
        object SynGutterMarks1: TSynGutterMarks
          Width = 24
          MouseActions = <>
        end
        object SynGutterLineNumber1: TSynGutterLineNumber
          Width = 19
          Visible = False
          MouseActions = <>
          MarkupInfo.Background = clBtnFace
          MarkupInfo.Foreground = clNone
          DigitCount = 2
          ShowOnlyLineNumbersMultiplesOf = 1
          ZeroStart = False
          LeadingZeros = False
        end
        object SynGutterSeparator1: TSynGutterSeparator
          Width = 2
          Visible = False
          MouseActions = <>
          MarkupInfo.Background = clWhite
          MarkupInfo.Foreground = clGray
        end
        object SynGutterCodeFolding1: TSynGutterCodeFolding
          MouseActions = <>
          MarkupInfo.Background = clNone
          MarkupInfo.Foreground = clGray
          MouseActionsExpanded = <>
          MouseActionsCollapsed = <>
        end
      end
    end
  end
  object tbrFileVersion: TToolBar
    Left = 0
    Height = 26
    Top = 0
    Width = 573
    Caption = 'tbrFileVersion'
    EdgeBorders = []
    EdgeInner = esNone
    EdgeOuter = esNone
    Images = modMain.imlMain
    TabOrder = 1
    object btnExternalDiff: TToolButton
      Left = 24
      Hint = 'View in external diff tool'
      Top = 0
      Caption = 'External diff'
      ImageIndex = 8
      OnClick = btnExternalDiffClick
    end
    object btnDiffTK: TToolButton
      Left = 1
      Hint = 'TK-based Diff viewer'
      Top = 0
      Caption = 'DiffTK'
      ImageIndex = 9
      OnClick = btnDiffTKClick
    end
  end
end

Added src/a_fileversioninfo.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
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
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
unit A_FileVersionInfo;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Types, Controls, ComCtrls, ExtCtrls,
  SynEdit, SynEditHighlighter,
  M_Main, M_Highlighters,
  U_Fossil;

type

  { TfraFileVersionInfo }

  TfraFileVersionInfo = class(TFrame)
    btnDiffTK: TToolButton;
    btnExternalDiff: TToolButton;
    imgContents: TImage;
    synEdit: TSynEdit;
    tbcView: TTabControl;
    tbrFileVersion: TToolBar;
    procedure btnDiffTKClick(Sender: TObject);
    procedure btnExternalDiffClick(Sender: TObject);
    procedure synEditMouseWheel(Sender: TObject; Shift: TShiftState;
      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure tbcViewChange(Sender: TObject);
    procedure tbcViewChanging(Sender: TObject; var AllowChange: Boolean);
    procedure tbcViewGetImageIndex(Sender: TObject; TabIndex: Integer;
      var ImageIndex: Integer);
  private
    FFile: TFileVersion;
    FExternalDiffCmd: string;

    function DetermineFileHighlighter(const AvoidMulti: Boolean = False): TSynCustomHighlighter;

    procedure SetFile(AValue: TFileVersion);
    { private declarations }
  public
    { public declarations }
    constructor Create(AOwner: TComponent); override;

    procedure SelectFile(const AFile: TFileVersion; const Selected: Boolean);
    procedure PopulateView;
  published
    property ActiveFile: TFileVersion read FFile  write SetFile;
  end;

implementation
uses
  StrUtils, Graphics,
  SynGutterLineNumber;

{$R *.lfm}

const
  cGraphicsClasses: array[0..8] of TGraphicClass = (TCursorImage, TBitmap, TIcon, TIcnsIcon,
                                                    TPixmap, TPortableNetworkGraphic, TJpegImage,
                                                    TPortableAnyMapGraphic, TTiffImage);


{ TfraFileVersionInfo }

constructor TfraFileVersionInfo.Create(AOwner: TComponent);
var
  s: string;
  GC: TGraphicClass;
  Exts: TStringList;
begin
  inherited Create(AOwner);

  for s in cEditorFontNames do begin
    if Screen.Fonts.IndexOf(s) > -1 then begin
      synEdit.Font.Name := s;
      Break;
    end;
  end;

  Exts := TStringList.Create;
  try
    Exts.CaseSensitive := False;
    Exts.Delimiter := ';';
    for GC in cGraphicsClasses do begin
      Exts.DelimitedText := GC.GetFileExtensions;
      for s in Exts do begin
        TPicture.RegisterFileFormat(s, GC.ClassName, GC);
      end;
    end;
  finally
    Exts.Free;
  end;

  tbcView.TabIndex := 0;

  if Assigned(modMain.Checkout) then
    FExternalDiffCmd := modMain.Checkout.Settings.Values['gdiff-command'];
  btnExternalDiff.Enabled := (FExternalDiffCmd <> '');
end;

procedure TfraFileVersionInfo.synEditMouseWheel(Sender: TObject;
  Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
  var Handled: Boolean);
var
  SizeOffset: Integer;
begin
  if ssCtrl in Shift then begin
    // TODO: test this with user-set font sizes as well; both on Mac and Windows
    if WheelDelta > 0 then
      SizeOffset := -1
    else
      SizeOffset := 1;
    synEdit.BeginUpdate(False);
    try
      synEdit.Font.Size := synEdit.Font.Size + SizeOffset;
    finally
      synEdit.EndUpdate;
    end;
    Handled := True;
  end;
end;

procedure TfraFileVersionInfo.tbcViewChange(Sender: TObject);
begin
  PopulateView;
end;

procedure TfraFileVersionInfo.tbcViewChanging(Sender: TObject;
  var AllowChange: Boolean);
begin
  // TODO: check if the current page can be left? Shame we don't know where we're going TO...
end;

procedure TfraFileVersionInfo.tbcViewGetImageIndex(Sender: TObject;
  TabIndex: Integer; var ImageIndex: Integer);
begin
  case TabIndex of
    0: begin // Diff
      ImageIndex := 8;
    end;
    1: begin // Wide diff
      ImageIndex := 9;
    end;
    2: begin // Content
      ImageIndex := 11;
    end;
    3: begin // Annotated
      ImageIndex := 10;
    end;
    4: begin // Hexadecimal
      ImageIndex := 12;
    end;
  end;
end;

procedure TfraFileVersionInfo.btnExternalDiffClick(Sender: TObject);
begin
  MessageBoxFunction(PChar('To be done:' + LineEnding + FExternalDiffCmd), 'Not implemented', 64);

  if not Assigned(FFile) then
    Exit;

  // TODO: FFile.RunDiffExternal();

  // TODO: copy and port (almost) code from existing project; see
  //        https://fossil.2of4.net/paleo/artifact/4bebe7bb2a5135e7cd4a9575760e7bf5b26f9bc1?ln=385-494

  // TODO: if we've got a file from the repo (not a local one), save its contents to a temp file.
  // TODO: Save a temp copy of the 'other' version to a temp file.

  // TODO: run the diff command

  // TODO: remember the temp files, to be deleted on exit of the program.
  // See https://fossil.2of4.net/paleo/artifact/4bebe7bb2a5135e7cd4a9575760e7bf5b26f9bc1?ln=116-126
  //  and https://fossil.2of4.net/paleo/artifact/4bebe7bb2a5135e7cd4a9575760e7bf5b26f9bc1?ln=1155-1192
end;

procedure TfraFileVersionInfo.btnDiffTKClick(Sender: TObject);
var
  DiffOpts: TDiffOptions;
begin
  if not Assigned(FFile) then
    Exit;

  DiffOpts := cDefaultDiffOptions;
  DiffOpts.External := True;
  DiffOpts.SideBySide := True;
  FFile.GetDiff({var}DiffOpts);
end;

procedure TfraFileVersionInfo.SelectFile(const AFile: TFileVersion;
  const Selected: Boolean);
begin
  // TODO: if selected, then add to list of files
  // TODO: if not selected, then remove from list of files
  // TODO: if more than one file selected, do diffs for all of them, but don't show contents or
  //       annotation. External diff is fine if they're all workfiles, otherwise no go.
  // TODO: if no file is selected, then clear everything.
  if Selected then
    SetFile(AFile)
  else if AFile = FFile then
    SetFile(nil);
end;

procedure TfraFileVersionInfo.SetFile(AValue: TFileVersion);
var
  HasDiff, HasHistory, HasContent: Boolean;
begin
  if (AValue = FFile) then
    Exit;

  FFile := AValue;

  if not Assigned(FFile) then begin
    HasDiff := False;
    HasHistory := False;
    HasContent := False;
  end else begin
    HasDiff := FFile.Statuses * (fsChanged - [fsAdded, fsMissing, fsDeleted]) <> [];
    HasHistory := FFile.Statuses * (fsChanged - [fsAdded] + [fsUnchanged]) <> [];
    HasContent := FileExistsUTF8(FFile.RealPath);
  end;
  // TODO: can the tabs be disabled or hidden?
  //btnViewDiff.Enabled := HasDiff;
  //btnViewDiffWide.Enabled := HasDiff;
  //btnViewAnnotate.Enabled := HasHistory;
  //btnViewContents.Enabled := HasContent;
  //btnViewHex.Enabled := HasContent;
  btnDiffTK.Enabled := HasDiff;
  btnExternalDiff.Enabled := HasDiff and (FExternalDiffCmd <> '');

  PopulateView;
end;

function IndexOfText(const Needle: string; const Haystack: array of string): Integer;
var
  i: Integer;
begin
  Result := -1;
  for i := Low(Haystack) to High(Haystack) do begin
    if SameText(Needle, Haystack[i]) then begin
      Result := i;
      Break;
    end;
  end;
end;

procedure TfraFileVersionInfo.PopulateView;
const
  cControlChars: array[0..31] of string = ('␀', '␁', '␂', '␃', '␄', '␅', '␆', '␇', '␈', '␉', '␊', '␋', '␌', '␍', '␎', '␏', '␐', '␑', '␒', '␓', '␔', '␕', '␖', '␗', '␘', '␙', '␚', '␛', '␜', '␝', '␞', '␟');
  cControlGlyphs: array[0..31] of widestring = (#$00A0, #$263A, #$263B, #$2665, #$2666, #$2663, #$2660, #$2022, #$25D8, #$25EF, #$25D9, #$2642, #$2640, #$266A, #$266C, #$263C, #$25BA, #$25C4, #$2195, #$203C, #$00B6, #$00A7, #$25AC, #$21A8, #$2191, #$2193, #$2192, #$2190, #$2319, #$2194, #$25B2, #$25BC);
var
  DiffOpts: TDiffOptions;
  Contents: TStream;
  sContents: string;
  CharPos: Integer;
  ContentWidth, NumberWidth: Integer;
  sPreview: string;
  B: Byte;
begin
  synEdit.Visible := Assigned(FFile);
  if not Assigned(FFile) then
    Exit;

  // TODO: can we disable a tab? If not, automatically switch to the most relevant one.

  modMain.SetBusy(True);
  synEdit.BeginUpdate(False);
  try
    // Disconnect the current highligher
    synEdit.Highlighter := nil;
    synEdit.Lines.Clear;
    try
      case tbcView.TabIndex of
        0, 1: begin // Diff, Side-by-side diff
          if tbcView.TabIndex = 1 { Wide diff } then begin
            synEdit.Highlighter := modHighlighters.shlDiffWide;
          end else begin
            synEdit.Highlighter := modHighlighters.shlDiff;
          end;

          DiffOpts := cDefaultDiffOptions;
          DiffOpts.SideBySide := (tbcView.TabIndex = 1); // btnViewDiffWide.Down;
          DiffOpts.Width := (synEdit.CharsInWindow div 2) - 9;
          synEdit.Lines.Text := FFile.GetDiff({var}DiffOpts);

          synEdit.Gutter.Parts.ByClass[TSynGutterLineNumber, 0].Visible := False;

        end;
        3: begin // Annotated
          // request GetAnnotated including full log
          sContents := FFile.GetAnnotated(True);
          CharPos := Pos(#10'-', sContents);
          if CharPos > 0 then begin
            modHighlighters.RecycleAnnotator;

            // parse the log, and feed it to the highlighter
            modHighlighters.shlAnnotate.SetVersionsFromLog(Copy(sContents, 1, CharPos - 1));

            // Remove the log from the contents
            CharPos := PosEx(#10, sContents, CharPos + 1);
            if CharPos > 0 then
              sContents := Copy(sContents, CharPos + 1, Length(sContents))
            else
              sContents := '';

            // The multi-highlighter has been known to cause crashes (SIGTRAP)
            modHighlighters.shlAnnotate.Highlighter := DetermineFileHighlighter(True);
            synEdit.Highlighter := modHighlighters.shlAnnotate;
          end;

          // put the remainder in the Edit.
          synEdit.Lines.Text := sContents;
          synEdit.Gutter.Parts.ByClass[TSynGutterLineNumber, 0].Visible := True;

        end;
        // else if btnViewContents.Down then begin
        2: begin
          synEdit.Highlighter := DetermineFileHighlighter();
          Contents := FFile.GetContents;
          try
            if synEdit.Highlighter = nil then begin
              // The file type was not recognized; we'll try the picture formats now
              try
                imgContents.Picture.LoadFromStreamWithFileExt(Contents, ExtractFileExt(FFile.Filename));
                imgContents.Stretch := (imgContents.Picture.Graphic.Width > imgContents.Width)
                                      or (imgContents.Picture.Graphic.Height > imgContents.Height);
                imgContents.Visible := True;
                synEdit.Visible := False;
              except
                on E: Exception do begin
                  imgContents.Visible := False;
                  synEdit.Visible := True;
                  //MessageBoxFunction(PChar(E.Message), PChar(string(E.ClassName)), $30);
                end {Exception};
              end {except};
            end {if};

            if synEdit.Visible then begin
              with TStringStream.Create('') do begin
                CopyFrom(Contents, 0);
                Position := 0;
                synEdit.Lines.Text := DataString;
                Free;
              end;
            end;
          finally
            Contents.Free;
          end;
          synEdit.Gutter.Parts.ByClass[TSynGutterLineNumber, 0].Visible := True;
        end {contents};

        4: begin // hexadecimal
          Contents := FFile.GetContents;
          try
            NumberWidth := Length(TrimLeftSet(IntToHex(Contents.Size, 16), ['0']));
            ContentWidth := (synEdit.CharsInWindow - NumberWidth - 4) div 4;
            ContentWidth -= ContentWidth div 4;
            case ContentWidth of
              Low(Integer)..7:  ContentWidth := 4;
              8..63:            ContentWidth := (ContentWidth div 8) * 8;
              else              ContentWidth := 64;
            end;
            while Contents.Position < Contents.Size - 1 do begin
              sContents += IntToHex(Contents.Position, NumberWidth);
              sContents += '   ';
              sPreview := '';
              CharPos := 0;
              repeat
                B := Contents.ReadByte;
                Inc(CharPos);
                sContents += IntToHex(B, 2) + ' ';
                if (CharPos mod 4) = 0 then
                  sContents += ' ';
                case B of
                  0:      sPreview += '·';
                  1..31:  sPreview += UTF8Encode(cControlGlyphs[B]);
                  else    sPreview += UTF8Encode(WideString(WideChar(B)));
                end;
              until (CharPos >= ContentWidth) or (Contents.Position >= Contents.Size);
              if CharPos < ContentWidth then
                sContents += StringOfChar(' ', (ContentWidth - CharPos + 1) * 3);
              sContents += ' ';
              sContents += sPreview;
              sContents += LineEnding;
            end {while};

            synEdit.Lines.Text := sContents;
          finally
            Contents.Free;
          end;
          synEdit.Gutter.Parts.ByClass[TSynGutterLineNumber, 0].Visible := False;
        end {hexadecimal};
      end {case};

      if synEdit.Gutter.Parts.ByClass[TSynGutterLineNumber, 0].Visible then
        synEdit.Options := synEdit.Options + [TSynEditorOption.eoShowScrollHint]
      else
        synEdit.Options := synEdit.Options - [TSynEditorOption.eoShowScrollHint];

    except
      on E: Exception do begin
        synEdit.Highlighter := modHighlighters.shlINI;
        synEdit.Lines.Add('[' + E.ClassName + ']');
        synEdit.Lines.Add(E.Message);
        synEdit.Gutter.Parts.ByClass[TSynGutterLineNumber, 0].Visible := False;
      end;
    end;
  finally
    synEdit.EndUpdate;
    modMain.SetBusy(False);
  end;
end {TfraFileVersionInfo.PopulateView};

function TfraFileVersionInfo.DetermineFileHighlighter(const AvoidMulti: Boolean = False): TSynCustomHighlighter;
var
  Ext: TFileName;
begin
  // TODO: use the DefaultFilter, split by ListSeparator, to determine which
  //  highlighter to use.
  // Or figure out a more easily configurable way to assign highlighters to extensions.

  Ext := ExtractFileExt(FFile.Filename);
  if IndexOfText(Ext, ['.pas', '.pp', '.lfm', '.lpr', '.lrs', '.dfm', '.dpr', '.fmx']) > -1 then begin
    Result := modHighlighters.shlPascal;
  end else if IndexOfText(Ext, ['.ini', '.dsk', '.vlb']) > -1 then begin
    Result := modHighlighters.shlINI;
  end else if IndexOfText(Ext, ['.xml', '.xsl', '.xslt', '.xsd', '.dtd', '.dproj', '.groupproj', '.deployproj', '.bdsproj', '.bdsgroup', '.config', '.manifest', '.lpi']) > -1 then begin
    Result := modHighlighters.shlXML;
  end else if IndexOfText(Ext, ['.htm', '.html', '.shtml', '.asp', '.aspx', '.asmx']) > -1 then begin
    if AvoidMulti then
      Result := modHighlighters.shlHTML
    else
      Result := modHighlighters.shlMulti;
  end else if IndexOfText(Ext, ['.js', '.json']) > -1 then begin
    Result := modHighlighters.shlJS;
  end else if IndexOfText(Ext, ['.css']) > -1 then begin
    Result := modHighlighters.shlCSS;
  end else if IndexOfText(Ext, ['.sql']) > -1 then begin
    Result := modHighlighters.shlSQL;
  end else if IndexOfText(Ext, ['.pl']) > -1 then begin
    Result := modHighlighters.shlPerl;
  end else if IndexOfText(Ext, ['.php', '.php4', '.php5']) > -1 then begin
      if AvoidMulti then
        Result := modHighlighters.shlPHP
      else
        Result := modHighlighters.shlMulti;
  end else if IndexOfText(Ext, ['.py']) > -1 then begin
    Result := modHighlighters.shlPython;
  end else if IndexOfText(Ext, ['.bas', '.vb', '.frm']) > -1 then begin
    Result := modHighlighters.shlBasic;
  end else if IndexOfText(Ext, ['.c', '.h', '.cpp', '.hpp']) > -1 then begin
    Result := modHighlighters.shlCpp;
  end else if IndexOfText(Ext, ['.sh']) > -1 then begin
    Result := modHighlighters.shlShell;
  end else if IndexOfText(Ext, ['.bat', '.cmd']) > -1 then begin
    Result := modHighlighters.shlBatch;
  end else if IndexOfText(Ext, ['.diff']) > -1 then begin
    Result := modHighlighters.shlDiff;
  end else if IndexOfText(Ext, ['.java', '.javaw']) > -1 then begin
    Result := modHighlighters.shlJava;
  end else if IndexOfText(Ext, ['.po']) > -1 then begin
    Result := modHighlighters.shlPO;

  // TODO: all other file types and highlighters


  end else begin
    Result := nil;
  end;
end;


end.

Added src/f_commit.lfm.

























































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
object frmCommit: TfrmCommit
  Left = 221
  Height = 470
  Top = 211
  Width = 946
  Caption = 'Commit'
  ClientHeight = 470
  ClientWidth = 946
  Menu = modMain.mnuMain
  OnCreate = FormCreate
  OnShow = FormShow
  Position = poScreenCenter
  SessionProperties = 'Height;Left;pssLeft.Width;pssTop.Height;Top;Width;WindowState;pssRight.Left;pssBottom.Top'
  ShowHint = True
  LCLVersion = '1.6.4.0'
  object pspLeftRight: TPairSplitter
    Left = 4
    Height = 462
    Top = 4
    Width = 938
    Align = alClient
    BorderSpacing.Around = 4
    Position = 260
    object pssLeft: TPairSplitterSide
      Cursor = crArrow
      Left = 0
      Height = 462
      Top = 0
      Width = 260
      ClientWidth = 260
      ClientHeight = 462
      inline frameFileList: TfraFileList
        Height = 462
        Width = 260
        Align = alClient
        ClientHeight = 462
        ClientWidth = 260
        inherited lvwFiles: TListView
          Height = 436
          Width = 260
          Columns = <          
            item
              Caption = 'Name'
              MinWidth = 50
              Width = 150
            end          
            item
              Caption = 'Path'
              ImageIndex = 11
              Width = 150
            end          
            item
              Caption = 'Status'
              Width = 20
            end          
            item
              Caption = 'Type'
              Width = 194
            end>
        end
        inherited tbrFiles: TToolBar
          Width = 260
          inherited chkAll: TCheckBox
            Height = 23
            Width = 24
          end
          inherited btnSep1: TToolButton
            Left = 25
          end
          inherited btnStatus: TToolButton
            Left = 30
          end
          inherited btnSep3: TToolButton
            Left = 223
          end
          inherited btnRefresh: TToolButton
            Left = 228
          end
          inherited edtFilter: TEdit
            Left = 70
            Height = 28
          end
          inherited btnSep2: TToolButton
            Left = 65
          end
        end
      end
    end
    object pssRight: TPairSplitterSide
      Cursor = crArrow
      Left = 265
      Height = 462
      Top = 0
      Width = 673
      ChildSizing.EnlargeHorizontal = crsHomogenousChildResize
      ClientWidth = 673
      ClientHeight = 462
      object pspTopBottom: TPairSplitter
        Cursor = crVSplit
        Left = 0
        Height = 462
        Top = 0
        Width = 673
        Align = alClient
        Position = 200
        SplitterType = pstVertical
        object pssTop: TPairSplitterSide
          Cursor = crArrow
          Left = 0
          Height = 200
          Top = 0
          Width = 673
          ClientWidth = 673
          ClientHeight = 200
          inline fraCommitMessage: TframeCommitMessage
            Height = 200
            Width = 673
            Align = alClient
            ClientHeight = 200
            ClientWidth = 673
          end
        end
        object pssBottom: TPairSplitterSide
          Cursor = crArrow
          Left = 0
          Height = 257
          Top = 205
          Width = 673
          ClientWidth = 673
          ClientHeight = 257
          inline frameFileVersionInfo: TfraFileVersionInfo
            Height = 257
            Width = 673
            Align = alClient
            ClientHeight = 257
            ClientWidth = 673
            inherited tbcView: TTabControl
              Height = 231
              Width = 673
              inherited imgContents: TImage
                Height = 201
                Width = 669
              end
              inherited synEdit: TSynEdit
                Height = 201
                Width = 669
                Lines.Strings = (                )
                OverwriteCaret = ctBlock
              end
            end
            inherited tbrFileVersion: TToolBar
              Width = 673
            end
          end
        end
      end
    end
  end
  object xpsCommit: TXMLPropStorage
    StoredValues = <>
    RootNodePath = 'Commit/Form'
    left = 240
    top = 152
  end
  object ApplicationProperties: TApplicationProperties
    ExceptionDialog = aedOkMessageBox
    OnActivate = ApplicationPropertiesActivate
    OnException = ApplicationPropertiesException
    left = 240
    top = 288
  end
end

Added src/f_commit.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
unit F_Commit;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, IDEWindowIntf, SynEdit, Forms, Controls,
  Graphics, Dialogs, PairSplitter, ExtCtrls, XMLPropStorage, ComCtrls, StdCtrls,
  M_Main, A_FileList, A_FileVersionInfo,
  U_Fossil, A_CommitMessage;

type

  { TfrmCommit }

  TfrmCommit = class(TForm)
    ApplicationProperties: TApplicationProperties;
    fraCommitMessage: TframeCommitMessage;
    frameFileVersionInfo: TfraFileVersionInfo;
    frameFileList: TfraFileList;
    pspTopBottom: TPairSplitter;
    pssTop: TPairSplitterSide;
    pssBottom: TPairSplitterSide;
    pspLeftRight: TPairSplitter;
    pssLeft: TPairSplitterSide;
    pssRight: TPairSplitterSide;
    xpsCommit: TXMLPropStorage;
    procedure ApplicationPropertiesActivate(Sender: TObject);
    procedure ApplicationPropertiesException(Sender: TObject; E: Exception);
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure frameFileListSelectionChanged(Sender: TObject; AFile: TFileVersion; Selected: Boolean);
    procedure fraCommitMessageGetFiles(Sender: TObject; const AFiles: TFossilFileList);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  frmCommit: TfrmCommit;

implementation

{$R *.lfm}

{ TfrmCommit }

procedure TfrmCommit.FormCreate(Sender: TObject);
begin
  frameFileList.OnSelectionChanged := @frameFileListSelectionChanged;
  fraCommitMessage.OnGetFiles := @fraCommitMessageGetFiles;
end;

procedure TfrmCommit.FormShow(Sender: TObject);
begin
  if Assigned(modMain.Checkout) then begin
    Caption := 'Commit - ' + modMain.Checkout.Info.Values['project-name'];
    frameFileList.Revision := modMain.Checkout.Workdir;
    fraCommitMessage.WorkDir := modMain.Checkout.Workdir;
  end;
end;

procedure TfrmCommit.ApplicationPropertiesActivate(Sender: TObject);
begin
  // TODO: autorefresh?
  //frameFileList.btnRefresh.Click;
end;

procedure TfrmCommit.ApplicationPropertiesException(Sender: TObject;
  E: Exception);
begin
  Application.ShowException(E); // TODO: use another dialog
end;

procedure TfrmCommit.frameFileListSelectionChanged(Sender: TObject;
  AFile: TFileVersion; Selected: Boolean);
begin
  frameFileVersionInfo.SelectFile(AFile, Selected);
end;

procedure TfrmCommit.fraCommitMessageGetFiles(Sender: TObject;
  const AFiles: TFossilFileList);
begin
  frameFileList.LoadCheckedFiles(AFiles);
end;

end.

Added src/f_newbranch.lfm.











































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
object frmNewBranch: TfrmNewBranch
  Left = 270
  Height = 132
  Top = 142
  Width = 437
  BorderIcons = [biSystemMenu]
  Caption = 'Next commit'
  ClientHeight = 132
  ClientWidth = 437
  OnCreate = FormCreate
  Position = poOwnerFormCenter
  ShowInTaskBar = stNever
  LCLVersion = '1.4.4.0'
  object rbnCurrentBranch: TRadioButton
    Left = 8
    Height = 18
    Top = 8
    Width = 156
    Caption = 'in current branch "%s"'
    Checked = True
    OnClick = rbnCurrentBranchClick
    TabOrder = 0
    TabStop = True
  end
  object chkCloseBranch: TCheckBox
    Left = 24
    Height = 18
    Top = 32
    Width = 98
    Caption = 'close branch'
    TabOrder = 1
  end
  object rbnNewBranch: TRadioButton
    Left = 8
    Height = 18
    Top = 64
    Width = 105
    Caption = 'in new branch'
    OnClick = rbnNewBranchClick
    TabOrder = 2
  end
  object edtBranchName: TEdit
    Left = 129
    Height = 22
    Top = 64
    Width = 302
    Anchors = [akTop, akLeft, akRight]
    Enabled = False
    TabOrder = 3
  end
  object pnlButtons: TButtonPanel
    Left = 6
    Height = 22
    Top = 104
    Width = 425
    OKButton.Name = 'OKButton'
    OKButton.DefaultCaption = True
    HelpButton.Name = 'HelpButton'
    HelpButton.DefaultCaption = True
    CloseButton.Name = 'CloseButton'
    CloseButton.DefaultCaption = True
    CancelButton.Name = 'CancelButton'
    CancelButton.DefaultCaption = True
    TabOrder = 4
    Spacing = 8
    ShowButtons = [pbOK, pbCancel]
    ShowBevel = False
  end
end

Added src/f_newbranch.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
unit F_NewBranch;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ButtonPanel,
  U_Fossil;

type

  { TfrmNewBranch }

  TfrmNewBranch = class(TForm)
    pnlButtons: TButtonPanel;
    chkCloseBranch: TCheckBox;
    edtBranchName: TEdit;
    rbnCurrentBranch: TRadioButton;
    rbnNewBranch: TRadioButton;
    procedure FormCreate(Sender: TObject);
    procedure rbnCurrentBranchClick(Sender: TObject);
    procedure rbnNewBranchClick(Sender: TObject);
  private
    FBranch: string;
    procedure EnableControls;
    function GetCloseBranch: Boolean;
    function GetBranchName: string;
    { private declarations }
  public
    { public declarations }
    procedure Initialize(const ACurrentBranch: string; const ACommitOptions: TCommitOptions);

    property CurrentBranch: string  read FBranch;
    property BranchName: string     read GetBranchName;
    property CloseBranch: Boolean   read GetCloseBranch;
  end;

implementation

{$R *.lfm}

{ TfrmNewBranch }

procedure TfrmNewBranch.FormCreate(Sender: TObject);
begin
  EnableControls;
end;

procedure TfrmNewBranch.rbnCurrentBranchClick(Sender: TObject);
begin
  EnableControls;
end;

procedure TfrmNewBranch.rbnNewBranchClick(Sender: TObject);
begin
  EnableControls;
end;

procedure TfrmNewBranch.EnableControls;
begin
  chkCloseBranch.Enabled := rbnCurrentBranch.Checked;
  edtBranchName.Enabled := rbnNewBranch.Checked;
end;

function TfrmNewBranch.GetCloseBranch: Boolean;
begin
  Result := rbnCurrentBranch.Checked and chkCloseBranch.Checked;
end;

function TfrmNewBranch.GetBranchName: string;
begin
  if rbnNewBranch.Checked then
    Result := Trim(edtBranchName.Text)
  else
    Result := FBranch;
end;

procedure TfrmNewBranch.Initialize(const ACurrentBranch: string; const ACommitOptions: TCommitOptions);
begin
  FBranch := ACurrentBranch;
  rbnCurrentBranch.Caption := Format('in current branch "%s"', [FBranch]);
  rbnCurrentBranch.Checked := (ACommitOptions.BranchName = FBranch);
  chkCloseBranch.Checked := ACommitOptions.CloseBranch;
  rbnNewBranch.Checked := not rbnCurrentBranch.Checked;
  if rbnNewBranch.Checked then
    edtBranchName.Text := ACommitOptions.BranchName;
end;

end.

Added src/f_opencheckout.lfm.





































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
object frmOpenCheckout: TfrmOpenCheckout
  Left = 278
  Height = 531
  Top = 72
  Width = 696
  Caption = 'Open checkout'
  ClientHeight = 531
  ClientWidth = 696
  OnCloseQuery = FormCloseQuery
  Position = poScreenCenter
  LCLVersion = '1.6.4.0'
  object pgcSource: TPageControl
    Left = 0
    Height = 344
    Top = 0
    Width = 696
    ActivePage = tshRecentCheckout
    Align = alClient
    TabIndex = 0
    TabOrder = 0
    object tshRecentCheckout: TTabSheet
      Caption = 'Recent checkouts'
      ClientHeight = 311
      ClientWidth = 688
      object lvwCheckouts: TListView
        Left = 0
        Height = 311
        Top = 0
        Width = 688
        Align = alClient
        BorderStyle = bsNone
        Columns = <        
          item
            AutoSize = True
            Caption = 'Project'
            Width = 66
          end        
          item
            AutoSize = True
            Caption = 'Branch'
            Width = 65
          end        
          item
            AutoSize = True
            Caption = 'Path'
            Width = 49
          end        
          item
            AutoSize = True
            Caption = 'Repository'
            Width = 91
          end        
          item
            Caption = 'Date'
          end>
        HideSelection = False
        RowSelect = True
        TabOrder = 0
        ViewStyle = vsReport
        OnColumnClick = lvwCheckoutsColumnClick
        OnCustomDrawItem = lvwCheckoutsCustomDrawItem
        OnDblClick = lvwCheckoutsDblClick
        OnSelectItem = lvwCheckoutsSelectItem
      end
    end
    object tshOpen: TTabSheet
      Caption = 'Select checkout'
      ClientHeight = 316
      ClientWidth = 688
      object tvwFolders: TShellTreeView
        Left = 0
        Height = 316
        Top = 0
        Width = 688
        Align = alClient
        BorderStyle = bsNone
        FileSortType = fstFoldersFirst
        ReadOnly = True
        RightClickSelect = True
        TabOrder = 0
        Options = [tvoAutoItemHeight, tvoHideSelection, tvoKeepCollapsedNodes, tvoReadOnly, tvoRightClickSelect, tvoShowButtons, tvoShowLines, tvoShowRoot, tvoToolTips, tvoThemedDraw]
        ObjectTypes = [otFolders]
      end
    end
  end
  object pnlButtons: TPanel
    Left = 0
    Height = 50
    Top = 481
    Width = 696
    Align = alBottom
    BevelOuter = bvNone
    ClientHeight = 50
    ClientWidth = 696
    TabOrder = 1
    object btnOK: TButton
      Left = 478
      Height = 33
      Top = 9
      Width = 104
      Anchors = [akRight, akBottom]
      Caption = '&Open'
      Default = True
      Enabled = False
      ModalResult = 1
      TabOrder = 0
    end
    object btnCancel: TButton
      Left = 590
      Height = 34
      Top = 8
      Width = 99
      Anchors = [akRight, akBottom]
      Cancel = True
      Caption = '&Cancel'
      ModalResult = 2
      TabOrder = 1
    end
  end
  object pnlRepoInfo: TPanel
    Left = 0
    Height = 129
    Top = 352
    Width = 696
    Align = alBottom
    ClientHeight = 129
    ClientWidth = 696
    TabOrder = 2
    object mmoRepoInfo: TMemo
      Left = 1
      Height = 127
      Top = 1
      Width = 694
      Align = alClient
      BorderStyle = bsNone
      Color = clBtnFace
      Font.Name = 'Monospace'
      Font.Pitch = fpFixed
      ParentFont = False
      ReadOnly = True
      ScrollBars = ssAutoVertical
      TabOrder = 0
      WantReturns = False
    end
  end
  object splRepo: TSplitter
    Cursor = crVSplit
    Left = 0
    Height = 8
    Top = 344
    Width = 696
    Align = alBottom
    ResizeAnchor = akBottom
  end
  object tmrPopulate: TTimer
    Enabled = False
    Interval = 100
    OnTimer = tmrPopulateTimer
    left = 616
    top = 88
  end
end

Added src/f_opencheckout.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
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
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
unit F_OpenCheckout;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,
  ShellCtrls, ExtCtrls, StdCtrls, Menus,
  M_Main, U_Fossil, types;

type
  { TCheckoutRepoReader }

  TCheckoutRepoReader = class(TThread)
  private
    FFossil: TFossil;
    FCheckouts: TStringList;
    FOnFound: TNotifyEvent;
    FCurrentPath: string;
    FCurrentTitle: string;
    FCurrentRepo: string;
    FCurrentBranch: string;
    procedure DoFound;
  public
    constructor Create(const AFossil: TFossil; const ACheckouts: TStringList; const AOnFound, AOnTerminate: TNotifyEvent);
    destructor  Destroy; override;

    procedure Execute; override;

    property OnFound: TNotifyEvent  read FOnFound write FOnFound;
    property CurrentPath: string    read FCurrentPath;
    property CurrentTitle: string   read FCurrentTitle;
    property CurrentRepo: string    read FCurrentRepo;
    property CurrentBranch: string  read FCurrentBranch;
  end;

  { TRepoReader }

  TRepoReader = class(TThread)
  private
    FFossil: TFossil;
    FRepos: TStringList;
    FOnFound: TNotifyEvent;
    FCurrentTitle: string;
    FCurrentRepo: string;
    FTipBranch: string;
    procedure DoFound;
  public
    constructor Create(const AFossil: TFossil; const ARepos: TStringList; const AOnFound, AOnTerminate: TNotifyEvent);
    destructor  Destroy; override;

    procedure Execute; override;

    property OnFound: TNotifyEvent  read FOnFound write FOnFound;
    property CurrentTitle: string   read FCurrentTitle;
    property CurrentRepo: string    read FCurrentRepo;
    property TipBranch: string      read FTipBranch;
  end;

type
  { TfrmOpenCheckout }

  TfrmOpenCheckout = class(TForm)
    btnOK: TButton;
    btnCancel: TButton;
    lvwCheckouts: TListView;
    mmoRepoInfo: TMemo;
    pnlRepoInfo: TPanel;
    pnlButtons: TPanel;
    pgcSource: TPageControl;
    splRepo: TSplitter;
    tmrPopulate: TTimer;
    tvwFolders: TShellTreeView;
    tshRecentCheckout: TTabSheet;
    tshOpen: TTabSheet;
    procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
    procedure lvwCheckoutsColumnClick(Sender: TObject; Column: TListColumn);
    procedure lvwCheckoutsCustomDrawItem(Sender: TCustomListView;
      Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
    procedure lvwCheckoutsDblClick(Sender: TObject);
    procedure lvwCheckoutsSelectItem(Sender: TObject; Item: TListItem;
      Selected: Boolean);
    procedure tmrPopulateTimer(Sender: TObject);
  private
    { private declarations }
    FDirName: string;
    FCheckoutReader: TCheckoutRepoReader;
    FRepoReader: TRepoReader;
    procedure PopulateCheckouts;
    procedure FossilCheckoutFound(Sender: TObject);
    procedure CheckoutReaderTerminate(Sender: TObject);
    procedure PopulateRepos;
    procedure FossilRepoFound(Sender: TObject);
    procedure RepoReaderTerminate(Sender: TObject);
  public
    { public declarations }
    function Execute: boolean;

    property SelectedDir: string  read FDirName write FDirName;
  end;

implementation

{$R *.lfm}

{ TCheckoutRepoReader }

constructor TCheckoutRepoReader.Create(const AFossil: TFossil; const ACheckouts: TStringList; const AOnFound, AOnTerminate: TNotifyEvent);
begin
  inherited Create(False);
  FFossil := TFossil.Create(AFossil.Executable);
  FCheckouts := TStringList.Create;
  FCheckouts.AddStrings(ACheckouts);
  Self.OnFound := AOnFound;
  Self.OnTerminate := AOnTerminate;
  FreeOnTerminate := True;
end;

destructor TCheckoutRepoReader.Destroy;
begin
  FCheckouts.Free;
  FFossil.Free;
  inherited Destroy;
end;

procedure TCheckoutRepoReader.Execute;
var
  Lines: TStringList;
  CheckoutPath: string;
  CharPos: integer;
begin
  Lines := TStringList.Create;
  try
    Lines.NameValueSeparator := ':';
    for CheckoutPath in FCheckouts do begin
      FCurrentPath := CheckoutPath;
      FCurrentTitle := '';
      FCurrentRepo := '';
      FCurrentBranch := '';
      try
        FFossil.CurrentDir := CheckoutPath;
        Lines.Text := FFossil.Run('info');
        if Self.Terminated then Break;
        FCurrentTitle := Trim(Lines.Values['project-name']);
        FCurrentRepo := Trim(Lines.Values['repository']);
        if DirectorySeparator <> '/' then
          FCurrentRepo := StringReplace(FCurrentRepo, '/', DirectorySeparator, [rfReplaceAll]);
        FCurrentBranch := Trim(Lines.Values['tags']);
        CharPos := Pos(', ', FCurrentBranch);
        if CharPos > 0 then
          FCurrentBranch := Copy(FCurrentBranch, 1, CharPos - 1);
      except
        on E: Exception do begin
          FCurrentRepo := '<' + E.Message + '>';
        end;
      end;
      if Self.Terminated then Break;
      Synchronize(@DoFound);
    end;
  finally
    Lines.Free;
  end;
end;

procedure TCheckoutRepoReader.DoFound;
begin
  FOnFound(Self);
end;


{ TRepoReader }

constructor TRepoReader.Create(const AFossil: TFossil;
  const ARepos: TStringList; const AOnFound, AOnTerminate: TNotifyEvent);
begin
  inherited Create(False);
  FFossil := TFossil.Create(AFossil.Executable);
  FRepos := TStringList.Create;
  FRepos.AddStrings(ARepos);
  Self.OnFound := AOnFound;
  Self.OnTerminate := AOnTerminate;
  FreeOnTerminate := True;
end;

destructor TRepoReader.Destroy;
begin
  FRepos.Free;
  FFossil.Free;
  inherited Destroy;
end;

procedure TRepoReader.Execute;
var
  Lines: TStringList;
  RepoPath: string;
  CharPos: integer;
begin
  Lines := TStringList.Create;
  try
    Lines.NameValueSeparator := ':';
    for RepoPath in FRepos do begin
      FCurrentRepo := RepoPath;
      if DirectorySeparator <> '/' then
        FCurrentRepo := StringReplace(FCurrentRepo, '/', DirectorySeparator, [rfReplaceAll]);
      FCurrentTitle := '';
      FTipBranch := '';
      try
        Lines.Text := FFossil.Run('info -R ' + AnsiQuotedStr(RepoPath, '"'));
        if Self.Terminated then Break;
        FCurrentTitle := Trim(Lines.Values['project-name']);

        Lines.Text := FFossil.Run('info tip -R ' + AnsiQuotedStr(RepoPath, '"'));
        if Self.Terminated then Break;
        FTipBranch := Trim(Lines.Values['tags']);
        CharPos := Pos(', ', FTipBranch);
        if CharPos > 0 then
          FTipBranch := Copy(FTipBranch, 1, CharPos - 1);
      except
        on E: Exception do begin
          FCurrentRepo := '<' + E.Message + '>';
        end;
      end;
      if Self.Terminated then Break;
      Synchronize(@DoFound);
    end;
  finally
    Lines.Free;
  end;
end;
procedure TRepoReader.DoFound;
begin
  FOnFound(Self);
end;


{ TfrmOpenCheckout }

procedure TfrmOpenCheckout.FormCloseQuery(Sender: TObject; var CanClose: boolean
  );
begin
  if Assigned(FCheckoutReader) then begin
    FCheckoutReader.Terminate;
    FCheckoutReader.OnTerminate := nil;
  end;
  if Assigned(FRepoReader) then begin
    FRepoReader.Terminate;
    FRepoReader.OnTerminate := nil;
  end;
end;

procedure TfrmOpenCheckout.lvwCheckoutsColumnClick(Sender: TObject;
  Column: TListColumn);
const
  cOtherDirection: array[TSortDirection] of TSortDirection = (sdAscending, sdDescending);
begin
  if lvwCheckouts.SortType = stNone then begin
    lvwCheckouts.SortDirection := sdDescending;
    lvwCheckouts.SortColumn := Column.Index;
    lvwCheckouts.SortType := stText;
  end else if lvwCheckouts.SortColumn = Column.Index then begin
    lvwCheckouts.SortDirection := cOtherDirection[lvwCheckouts.SortDirection];
  end else begin
    lvwCheckouts.SortColumn := Column.Index;
    lvwCheckouts.SortDirection := sdDescending;
  end;
end;

procedure TfrmOpenCheckout.lvwCheckoutsCustomDrawItem(Sender: TCustomListView;
  Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
  if Item.Cut then
    Sender.Canvas.Font.Color := clRed;
end;

function TfrmOpenCheckout.Execute: boolean;
begin
  tmrPopulate.Enabled := True;
  Result := (ShowModal = mrOK);
end;

procedure TfrmOpenCheckout.tmrPopulateTimer(Sender: TObject);
begin
  tmrPopulate.Enabled := False;
  PopulateCheckouts;
end;

procedure TfrmOpenCheckout.lvwCheckoutsSelectItem(Sender: TObject;
  Item: TListItem; Selected: Boolean);
var
  Branch: string;
  CharPos: integer;
  Repo: string;
begin
  if Selected then begin
    FDirName := Item.SubItems[1];
    try
      mmoRepoInfo.Lines.NameValueSeparator := ':';
      if FDirName <> '' then begin
        modMain.Fossil.CurrentDir := FDirName;
        mmoRepoInfo.Text := modMain.Fossil.Run('info');
        btnOK.Enabled := True;

        if Item.Caption = '' then
          Item.Caption := Trim(mmoRepoInfo.Lines.Values['project-name']);
        Branch := Trim(mmoRepoInfo.Lines.Values['tags']);
        CharPos := Pos(', ', Branch);
        if CharPos > 0 then
          Branch := Copy(Branch, 1, CharPos - 1);
        if Item.SubItems[0] = '' then begin
          Item.SubItems[0] := Branch;
        end;
        if Item.SubItems.Count < 3 then begin
          Item.SubItems.Add(Trim(mmoRepoInfo.Lines.Values['repository']));
        end else if Item.SubItems[2] = '' then begin
          Item.SubItems[2] := Trim(mmoRepoInfo.Lines.Values['repository']);
        end;
      end else begin
        btnOK.Enabled := False;
        if Item.SubItems.Count > 2 then begin
          Repo := Item.SubItems[2];
          mmoRepoInfo.Text := modMain.Fossil.Run('info --verbose -R ' + AnsiQuotedStr(Repo, '"'));
          if Item.Caption = '' then
            Item.Caption := Trim(mmoRepoInfo.Lines.Values['project-name']);
        end else begin
          mmoRepoInfo.Text := '';
        end;
      end;
    except
      on E: Exception do begin
        btnOK.Enabled := False;
        mmoRepoInfo.Text := E.Message;
      end;
    end;
  end else if FDirName = Item.SubItems[1] then begin
    FDirName := '';
    btnOK.Enabled := False;
    mmoRepoInfo.Text := '';
  end;
end;

procedure TfrmOpenCheckout.lvwCheckoutsDblClick(Sender: TObject);
begin
  if btnOK.Enabled then
    btnOK.Click;
end;

procedure TfrmOpenCheckout.PopulateCheckouts;
var
  RawLine, Line: string;
  Item: TListItem;
  Lines, Checkouts: TStringList;
begin
  Checkouts := TStringList.Create;
  try
    lvwCheckouts.Items.BeginUpdate;
    Lines := TStringList.Create;
    try
      Lines.Text := modMain.Fossil.Run('all list --ckout');
      for RawLine in Lines do begin
        if DirectorySeparator <> '/' then
          Line := StringReplace(RawLine, '/', DirectorySeparator, [rfReplaceAll])
        else
          Line := RawLine;
        Item := lvwCheckouts.Items.Add;
        Item.Caption := '';
        Item.SubItems.Add('');
        Item.SubItems.Add(Line);
        Checkouts.Add(Line);
      end;
    finally
      Lines.Free;
      lvwCheckouts.Items.EndUpdate;
    end;

    Self.Cursor := crAppStart;
    FCheckoutReader := TCheckoutRepoReader.Create(modMain.Fossil, Checkouts, @FossilCheckoutFound, @CheckoutReaderTerminate);
  finally
    Checkouts.Free;
  end;
end;

procedure TfrmOpenCheckout.PopulateRepos;
var
  RawLine, Line: string;
  Item: TListItem;
  Lines, Repos: TStringList;
begin
  Repos := TStringList.Create;
  try
    lvwCheckouts.Items.BeginUpdate;
    Lines := TStringList.Create;
    try
      Lines.Text := modMain.Fossil.Run('all list');
      for RawLine in Lines do begin
        if DirectorySeparator <> '/' then
          Line := StringReplace(RawLine, '/', DirectorySeparator, [rfReplaceAll])
        else
          Line := RawLine;
        Repos.Add(Line);
      end;
    finally
      Lines.Free;
      lvwCheckouts.Items.EndUpdate;
    end;

    Self.Cursor := crAppStart;
    FRepoReader := TRepoReader.Create(modMain.Fossil, Repos, @FossilRepoFound, @RepoReaderTerminate);
  finally
    Repos.Free;
  end;
end;

procedure TfrmOpenCheckout.FossilCheckoutFound(Sender: TObject);
var
  i: integer;
  Item: TListItem;
begin
  for i := 0 to lvwCheckouts.Items.Count - 1 do begin
    Item := lvwCheckouts.Items[i];
    if SameFileName(Item.SubItems[1], FCheckoutReader.CurrentPath) then begin
      Item.Caption := FCheckoutReader.CurrentTitle;
      Item.SubItems[0] := FCheckoutReader.CurrentBranch;
      Item.SubItems.Add(FCheckoutReader.CurrentRepo);
      Item.Cut := Copy(FCheckoutReader.CurrentRepo, 1, 1) = '<';
      Break;
    end;
  end;
end;

procedure TfrmOpenCheckout.CheckoutReaderTerminate(Sender: TObject);
begin
  FCheckoutReader := nil;
  Self.Cursor := crHourGlass;
  PopulateRepos;
end;

procedure TfrmOpenCheckout.FossilRepoFound(Sender: TObject);
var
  i: integer;
  Found: Boolean;
  Item: TListItem;
begin
  Found := False;
  for i := 0 to lvwCheckouts.Items.Count - 1 do begin
    Item := lvwCheckouts.Items[i];
    if SameFileName(Item.SubItems[2], FRepoReader.CurrentRepo) then begin
      Found := True;
      Break;
    end;
  end;
  if not Found then begin
    Item := lvwCheckouts.Items.Add;
    Item.Caption := FRepoReader.CurrentTitle;
    Item.SubItems.Add(FRepoReader.TipBranch);
    Item.SubItems.Add('');
    Item.SubItems.Add(FRepoReader.CurrentRepo);
    Item.Cut := Copy(FRepoReader.CurrentRepo, 1, 1) = '<';
  end;
end;

procedure TfrmOpenCheckout.RepoReaderTerminate(Sender: TObject);
begin
  FRepoReader := nil;
  Self.Cursor := crDefault;
end;

end.

Added src/lib/dirwatch.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
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
unit DirWatch;

// -----------------------------------------------------------------------------
// Component Name:  TDirectoryWatch                                            .
// Module:          DirWatch                                                   .
// Description:     Implements watching for file changes in a designated       .
//                  directory (or directories).                                .
// Version:         1.4                                                        .
// Date:            10-MAR-2003                                                .
// Target:          Win32, Delphi 3 - Delphi 7                                 .
// Author:          Angus Johnson, angusj-AT-myrealbox-DOT-com                 .
//                  A portion of code has been copied from the Drag & Drop     .
//                  Component Suite which I co-authored with Anders Melander.  .
// Copyright:       © 2003 Angus Johnson                                       .
//                  http://www.angusj.com/delphi/dirwatch.html                 .
//                                                                             .
// Usage:           1. Add a TDirectoryWatch component to your form.           .
//                  2. Set its Directory property                              .
//                  3. If you wish to watch its subdirectories too then set    .
//                  the WatchSubDir property to true                           .
//                  4. Assign the OnChange event                               .
//                  5. Set Active to true                                      .
// -----------------------------------------------------------------------------

interface

uses
  Windows, Messages, SysUtils, Forms, Classes;

type
  TNotifyFilters = set of (nfFilename, nfDirname, nfAttrib,
    nfSize, nfLastWrite, nfSecurity);

  TWatchThread = class;             //forward declaration

  TDirectoryWatch = class(TComponent)
  private
    fWindowHandle: THandle;
    fWatchThread: TWatchThread;
    fWatchSubDirs: boolean;
    fDirectory: string;
    fActive: boolean;
    fNotifyFilters: TNotifyFilters; //see FindFirstChangeNotification in winAPI
    fOnChangeEvent: TNotifyEvent;
    procedure SetActive(aActive: boolean);
    procedure SetDirectory(aDir: string);
    procedure SetWatchSubDirs(aWatchSubDirs: boolean);
    procedure SetNotifyFilters(aNotifyFilters: TNotifyFilters);
    procedure WndProc(var aMsg: TMessage);
  public
    constructor Create(aOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Directory: string read fDirectory write SetDirectory;
    property NotifyFilters: TNotifyFilters
      read fNotifyFilters write SetNotifyFilters;
    property WatchSubDirs: boolean read fWatchSubDirs write SetWatchSubDirs;
    property Active: boolean read fActive write SetActive;
    property OnChange: TNotifyEvent read fOnChangeEvent write fOnChangeEvent;
  end;

  TWatchThread = class(TThread)
  private
    fOwnerHdl: Thandle;
    fChangeNotify : THandle; //Signals whenever Windows detects a change in    .
                             //the watched directory                           .
    fBreakEvent: THandle;    //Signals when either the Directory property      .
                             //changes or when the thread terminates           .
    fDirectory: string;
    fWatchSubDirs: longbool;
    fNotifyFilters: dword;
    fFinished: boolean;
  protected
    procedure SetDirectory(const Value: string);
    procedure ProcessFilenameChanges;
    procedure Execute; override;
  public
    constructor Create( OwnerHdl: THandle;
      const InitialDir: string; WatchSubDirs: boolean; NotifyFilters: dword);
    destructor Destroy; override;
    procedure Terminate;
    property Directory: string write SetDirectory;
  end;

procedure Register;

implementation

const
  NOTIFYCHANGE_MESSAGE = WM_USER + 1;

resourcestring
  sInvalidDir = 'Invalid Directory: ';

//----------------------------------------------------------------------------
// Miscellaneous functions ...
//----------------------------------------------------------------------------

procedure Register;
begin
  RegisterComponents('Samples', [TDirectoryWatch]);
end;
//----------------------------------------------------------------------------

function DirectoryExists(const Name: string): Boolean;
var
  Code: Integer;
begin
  Code := GetFileAttributes(PChar(Name));
  Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
end;

//----------------------------------------------------------------------------
// TDirectoryWatch methods ...
//----------------------------------------------------------------------------

constructor TDirectoryWatch.Create(aOwner: TComponent);
begin
  inherited Create(aOwner);
  //default Notify values - notify if either a file name or a directory name
  //changes or if a file is modified ...
  fNotifyFilters := [nfFilename, nfDirname, nfLastWrite];
  fDirectory := 'C:\';
  //this non-visual control needs to handle messages, so ...
  if not (csDesigning in ComponentState) then
    fWindowHandle := AllocateHWnd(WndProc);
end;
//----------------------------------------------------------------------------

destructor TDirectoryWatch.Destroy;
begin
  Active := false;
  if not (csDesigning in ComponentState) then
    DeallocateHWnd(fWindowHandle);
  inherited Destroy;
end;
//----------------------------------------------------------------------------

procedure TDirectoryWatch.WndProc(var aMsg: TMessage);
begin
  with aMsg do
    if Msg = NOTIFYCHANGE_MESSAGE then
    begin
      if assigned(OnChange) then OnChange(self);
    end else
      Result := DefWindowProc(FWindowHandle, Msg, wParam, lParam);
end;
//------------------------------------------------------------------------------

procedure TDirectoryWatch.SetNotifyFilters(aNotifyFilters: TNotifyFilters);
begin
  if aNotifyFilters = fNotifyFilters then exit;
  fNotifyFilters := aNotifyFilters;
  if assigned(fWatchThread) then
  begin
    Active := false;
    Active := true;
  end;
end;
//------------------------------------------------------------------------------

procedure TDirectoryWatch.SetWatchSubDirs(aWatchSubDirs: boolean);
begin
  if aWatchSubDirs = fWatchSubDirs then exit;
  fWatchSubDirs := aWatchSubDirs;
  if assigned(fWatchThread) then
  begin
    Active := false;
    Active := true;
  end;
end;
//------------------------------------------------------------------------------

procedure TDirectoryWatch.SetDirectory(aDir: string);
begin
  if aDir = '' then
  begin
    Active := false;
    fDirectory := '';
    exit;
  end;
  if (aDir[length(aDir)] <> '\') then aDir := aDir + '\';
  if aDir = fDirectory then exit;
  if not (csDesigning in ComponentState) and not DirectoryExists(aDir) then
    raise Exception.Create( sInvalidDir + aDir);
  fDirectory := aDir;
  if assigned(fWatchThread) then
    fWatchThread.Directory := fDirectory;
end;
//------------------------------------------------------------------------------

procedure TDirectoryWatch.SetActive(aActive: boolean);
var
  nf: dword;
begin
  if aActive = fActive then exit;
  fActive := aActive;
  if csDesigning in ComponentState then exit;
  if fActive then
  begin
    if not DirectoryExists(fDirectory) then
    begin
      fActive := false;
      raise Exception.Create(sInvalidDir + fDirectory);
    end;
    nf := 0;
    if nfFilename in fNotifyFilters then
      nf := nf or FILE_NOTIFY_CHANGE_FILE_NAME;
    if nfDirname in fNotifyFilters then
      nf := nf or FILE_NOTIFY_CHANGE_DIR_NAME;
    if nfAttrib in fNotifyFilters then
      nf := nf or FILE_NOTIFY_CHANGE_ATTRIBUTES;
    if nfSize in fNotifyFilters then
      nf := nf or FILE_NOTIFY_CHANGE_SIZE;
    if nfLastWrite in fNotifyFilters then
      nf := nf or FILE_NOTIFY_CHANGE_LAST_WRITE;
    if nfSecurity in fNotifyFilters then
      nf := nf or FILE_NOTIFY_CHANGE_SECURITY;
    fWatchThread := TWatchThread.Create(
      fWindowHandle, fDirectory, fWatchSubDirs, nf);
  end else
  begin
    fWatchThread.Terminate;
    fWatchThread := nil;
  end;
end;

//----------------------------------------------------------------------------
// TWatchThread methods ...
//----------------------------------------------------------------------------

constructor TWatchThread.Create(OwnerHdl: THandle;
  const InitialDir: string; WatchSubDirs: boolean; NotifyFilters: dword);
begin
  inherited Create(True);
  fOwnerHdl := OwnerHdl;
  if WatchSubDirs then
    cardinal(fWatchSubDirs) := 1 //workaround a Win9x OS issue
  else
    fWatchSubDirs := false;
  FreeOnTerminate := true;
  Priority := tpLowest;
  fDirectory := InitialDir;
  fNotifyFilters := NotifyFilters;
  fBreakEvent := windows.CreateEvent(nil, False, False, nil);
  Resume;
end;
//------------------------------------------------------------------------------

destructor TWatchThread.Destroy;
begin
  CloseHandle(fBreakEvent);
  inherited Destroy;
end;
//------------------------------------------------------------------------------

procedure TWatchThread.SetDirectory(const Value: string);
begin
  if (Value = FDirectory) then exit;
  FDirectory := Value;
  SetEvent(fBreakEvent);
end;
//------------------------------------------------------------------------------

procedure TWatchThread.Terminate;
begin
  inherited Terminate;
  SetEvent(fBreakEvent);
  while not fFinished do sleep(10); //avoids a reported resource leak
                                    //if called while closing the application.
end;
//------------------------------------------------------------------------------

procedure TWatchThread.Execute;
begin
  //OUTER LOOP - manages Directory property reassignments
  while (not Terminated) do
  begin
    fChangeNotify := FindFirstChangeNotification(pchar(fDirectory),
      fWatchSubDirs, fNotifyFilters);
    if (fChangeNotify = INVALID_HANDLE_VALUE) then
      //Can't monitor the specified directory so we'll just wait for
      //a new Directory assignment or the thread terminating ...
      WaitForSingleObject(fBreakEvent, INFINITE)
    else
      try
        //Now do the INNER loop...
        ProcessFilenameChanges;
      finally
        FindCloseChangeNotification(fChangeNotify);
      end;
  end;
  fFinished := true;
end;
//------------------------------------------------------------------------------

procedure TWatchThread.ProcessFilenameChanges;
var
  WaitResult : DWORD;
  HandleArray : array[0..1] of THandle;
const
  TEN_MSECS = 10;
  HUNDRED_MSECS = 100;
begin
  HandleArray[0] := fBreakEvent;
  HandleArray[1] := fChangeNotify;
  //INNER LOOP - exits only when fBreakEvent signaled
  while (not Terminated) do
  begin
    //waits for either fChangeNotify or fBreakEvent ...
    WaitResult := WaitForMultipleObjects(2, @HandleArray, False, INFINITE);
    if (WaitResult = WAIT_OBJECT_0 + 1) then //fChangeNotify
    begin
      repeat //ie: if a number of files are changing in a block
             //just post the one notification message ...
        FindNextChangeNotification(fChangeNotify);
      until Terminated or
        (WaitForSingleObject(fChangeNotify, TEN_MSECS) <> WAIT_OBJECT_0);
      if Terminated then break;
      //OK, now notify the main thread (before restarting inner loop)...
      PostMessage(fOwnerHdl, NOTIFYCHANGE_MESSAGE, 0, 0);
    end else //fBreakEvent ...
    begin
      //If the Directory property is undergoing multiple rapid reassignments
      //wait 'til this stops before restarting monitoring of a new directory ...
      while (not Terminated) and
        (WaitForSingleObject(fBreakEvent, HUNDRED_MSECS) = WAIT_OBJECT_0) do;
      break; //EXIT LOOP HERE
    end;
  end;
end;
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

end.

Added src/m_highlighters.lfm.





















































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
object modHighlighters: TmodHighlighters
  OldCreateOrder = False
  Height = 526
  HorizontalOffset = 314
  VerticalOffset = 162
  Width = 455
  object shlPascal: TSynPasSyn
    Enabled = False
    CommentAttri.Foreground = clGreen
    KeyAttri.Foreground = clNavy
    KeyAttri.StyleMask = [fsBold]
    NumberAttri.Foreground = clBlue
    SpaceAttri.Foreground = clSilver
    StringAttri.Foreground = clMaroon
    SymbolAttri.Foreground = clRed
    DirectiveAttri.Foreground = clTeal
    DirectiveAttri.Style = []
    CompilerMode = pcmDelphi
    NestedComments = False
    D4syntax = False
    ExtendedKeywordsMode = True
    left = 24
    top = 208
  end
  object shlHTML: TSynHTMLSyn
    DefaultFilter = 'HTML Document (*.htm,*.html)|*.htm;*.html'
    Enabled = False
    ASPAttri.FrameColor = clYellow
    CDATAAttri.Foreground = 4227327
    DOCTYPEAttri.Background = clInfoBk
    DOCTYPEAttri.Foreground = clPurple
    DOCTYPEAttri.Style = []
    CommentAttri.Foreground = clGreen
    left = 24
    top = 80
  end
  object shlJS: TSynJScriptSyn
    Enabled = False
    CommentAttri.Foreground = clGreen
    KeyAttri.Foreground = clNavy
    NumberAttri.Foreground = clBlue
    StringAttri.Foreground = clMaroon
    SymbolAttri.Style = [fsBold]
    left = 80
    top = 80
  end
  object shlCSS: TSynCssSyn
    DefaultFilter = 'Cascading Stylesheets (*.css)|*.css'
    Enabled = False
    CommentAttri.Foreground = clGreen
    IdentifierAttri.Foreground = clNavy
    KeyAttri.Foreground = clBlue
    NumberAttri.Foreground = clTeal
    StringAttri.Foreground = clMaroon
    SymbolAttri.Foreground = clRed
    SymbolAttri.Style = [fsBold]
    left = 136
    top = 80
  end
  object shlXML: TSynXMLSyn
    DefaultFilter = 'XML Document (*.xml,*.xsd,*.xsl,*.xslt,*.dtd)|*.xml;*.xsd;*.xsl;*.xslt;*.dtd'
    Enabled = False
    WantBracesParsed = False
    left = 24
    top = 144
  end
  object shlSQL: TSynSQLSyn
    DefaultFilter = 'SQL Files (*.sql)|*.sql'
    Enabled = False
    CommentAttri.Foreground = clGreen
    DataTypeAttri.Foreground = clTeal
    KeyAttri.Foreground = clNavy
    NumberAttri.Foreground = clBlue
    StringAttri.Foreground = clMaroon
    SQLDialect = sqlStandard
    left = 24
    top = 272
  end
  object shlINI: TSynIniSyn
    DefaultFilter = 'INI Files (*.ini)|*.ini'
    Enabled = False
    left = 80
    top = 144
  end
  object shlDiff: TSynDiffSyn
    Enabled = False
    SpaceAttri.Foreground = clSilver
    OrigFileAttri.Background = clNone
    OrigFileAttri.Foreground = clRed
    NewFileAttri.Background = clNone
    NewFileAttri.Foreground = clGreen
    LineAddedAttri.Background = 12707275
    LineRemovedAttri.Background = 13421803
    LineRemovedAttri.Foreground = 4408225
    left = 24
    top = 16
  end
  object shlAny: TSynAnySyn
    Enabled = False
    Comments = []
    DetectPreprocessor = True
    IdentifierChars = '!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
    Markup = False
    Entity = False
    DollarVariables = False
    ActiveDot = False
    left = 328
    top = 16
  end
  object shlMulti: TSynMultiSyn
    Enabled = False
    Schemes = <    
      item
        CaseSensitive = False
        StartExpr = '<script\s?[^>]*>'
        EndExpr = '<\/script>'
        Highlighter = shlJS
        MarkerAttri.Background = clSkyBlue
        MarkerAttri.Style = []
        SchemeName = 'JavaScript'
      end    
      item
        CaseSensitive = False
        StartExpr = '<style\s?[^>]*>'
        EndExpr = '<\/style>'
        Highlighter = shlCSS
        MarkerAttri.Background = clNone
        MarkerAttri.Foreground = clPurple
        SchemeName = 'CSS'
      end    
      item
        CaseSensitive = False
        StartExpr = '<\?(php)?'
        EndExpr = '\?>'
        Highlighter = shlPHP
        MarkerAttri.Foreground = clRed
        SchemeName = 'PHP'
      end    
      item
        StartExpr = '<%'
        EndExpr = '%>'
        Highlighter = shlBasic
        MarkerAttri.Style = []
        SchemeName = 'ASP'
      end>
    DefaultHighlighter = shlHTML
    DefaultLanguageName = 'HTML'
    left = 392
    top = 16
  end
  object shlCpp: TSynCppSyn
    DefaultFilter = 'C++ Files (*.c,*.cpp,*.h,*.hpp,*.hh)|*.c;*.cpp;*.h;*.hpp;*.hh'
    Enabled = False
    left = 80
    top = 208
  end
  object shlJava: TSynJavaSyn
    DefaultFilter = 'Java Files (*.java)|*.java'
    Enabled = False
    left = 136
    top = 208
  end
  object shlPerl: TSynPerlSyn
    DefaultFilter = 'Perl Files (*.pl,*.pm,*.cgi)|*.pl;*.pm;*.cgi'
    Enabled = False
    left = 24
    top = 332
  end
  object shlShell: TSynUNIXShellScriptSyn
    DefaultFilter = 'UNIX Shell Scripts (*.sh)|*.sh'
    Enabled = False
    left = 24
    top = 394
  end
  object shlPHP: TSynPHPSyn
    DefaultFilter = 'PHP Files (*.php,*.php3,*.phtml,*.inc)|*.php;*.php3;*.phtml;*.inc'
    Enabled = False
    CommentAttri.Foreground = clGreen
    IdentifierAttri.Foreground = clNavy
    KeyAttri.Foreground = clBlue
    KeyAttri.Style = []
    NumberAttri.Foreground = clTeal
    StringAttri.Foreground = clMaroon
    SymbolAttri.Foreground = clRed
    VariableAttri.Foreground = clPurple
    left = 80
    top = 332
  end
  object shlPython: TSynPythonSyn
    DefaultFilter = 'Python Files (*.py)|*.py'
    Enabled = False
    left = 136
    top = 332
  end
  object shlBasic: TSynVBSyn
    Enabled = False
    CommentAttri.Foreground = clGreen
    IdentifierAttri.Foreground = clNavy
    KeyAttri.Foreground = clBlue
    KeyAttri.Style = []
    NumberAttri.Foreground = clMenuHighlight
    StringAttri.Foreground = clMaroon
    left = 192
    top = 208
  end
  object shlBatch: TSynBatSyn
    DefaultFilter = 'MS-DOS Batch Files (*.bat;*.cmd)|*.bat;*.cmd'
    Enabled = False
    left = 80
    top = 394
  end
  object shlPO: TSynPoSyn
    DefaultFilter = 'Po Files (*.po)|*.po'
    Enabled = False
    left = 24
    top = 458
  end
end

Added src/m_highlighters.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
unit M_Highlighters;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, SynHighlighterMulti, SynHighlighterDiff,
  SynHighlighterAny, SynHighlighterPas, SynHighlighterJScript,
  SynHighlighterHTML, SynHighlighterXML, SynHighlighterCss, SynHighlighterSQL,
  SynHighlighterIni, SynHighlighterCpp, SynHighlighterJava, SynHighlighterPerl,
  synhighlighterunixshellscript, SynHighlighterPHP, SynHighlighterPython,
  SynHighlighterVB, SynHighlighterBat, SynHighlighterPo,
  SynHighlighterDiffWide, SynHighlighterAnnotate;

type

  { TmodHighlighters }

  TmodHighlighters = class(TDataModule)
    shlAny: TSynAnySyn;
    shlCSS: TSynCssSyn;
    shlDiff: TSynDiffSyn;
    shlHTML: TSynHTMLSyn;
    shlINI: TSynIniSyn;
    shlJS: TSynJScriptSyn;
    shlMulti: TSynMultiSyn;
    shlPascal: TSynPasSyn;
    shlSQL: TSynSQLSyn;
    shlXML: TSynXMLSyn;
    shlCpp: TSynCppSyn;
    shlJava: TSynJavaSyn;
    shlPerl: TSynPerlSyn;
    shlShell: TSynUNIXShellScriptSyn;
    shlPHP: TSynPHPSyn;
    shlPython: TSynPythonSyn;
    shlBasic: TSynVBSyn;
    shlBatch: TSynBatSyn;
    shlPO: TSynPoSyn;
  private
    { private declarations }
    FshlDiffWide: TSynDiffWideSyn;
    FshlAnnotate: TSynAnnotateSyn;
  public
    { public declarations }
    constructor Create(AOwner: TComponent); override;

    procedure RecycleAnnotator;
  published
    property shlDiffWide: TSynDiffWideSyn read FshlDiffWide;
    property shlAnnotate: TSynAnnotateSyn read FshlAnnotate;
  end;

var
  modHighlighters: TmodHighlighters;

implementation

{$R *.lfm}

{ TmodHighlighters }

constructor TmodHighlighters.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FshlDiffWide := TSynDiffWideSyn.Create(Self);
  FshlAnnotate := TSynAnnotateSyn.Create(Self);
end;

procedure TmodHighlighters.RecycleAnnotator;
begin
  FshlAnnotate.Free;
  FshlAnnotate := TSynAnnotateSyn.Create(Self);
end;

end.

Added src/m_main.lfm.



















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
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
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
object modMain: TmodMain
  OldCreateOrder = False
  Height = 218
  HorizontalOffset = 974
  VerticalOffset = 519
  Width = 243
  object imlMain: TImageList
    left = 24
    top = 24
    Bitmap = {
      4C690D0000001000000010000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00CBCBCB63C7C7C7BFC4C4C4BFBFBFBF63FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00D1D1D129CECE
      CE0ECBCBCB02C7C7C7EAE5E5E5FFE4E4E4FFACACACEAB6B6B602B2B2B20EADAD
      AD29FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00D1D1D19BCECECEFDCACA
      CAE7C6C6C619C2C2C2E7DEDEDEFFDDDDDDFFB2B2B2E7B1B1B119ACACACE7A7A7
      A7FDA3A3A39BFFFFFF00FFFFFF00FFFFFF00D0D0D07BDCDCDCFFEDEDEDFFDBDB
      DBFFC2C2C2F4BEBEBEFED6D6D6FFD4D4D4FFB0B0B0FEACACACF4CBCBCBFFE7E7
      E7FFB7B7B7FF8B8B8B7BFFFFFF00FFFFFF00CACACA7DC4C4C4FEDCDCDCFFD4D4
      D4FFD9D9D9FFDBDBDBFFD6D6D6FFD4D4D4FFD9D9D9FFD2D2D2FFCBCBCBFFC8C8
      C8FF797979FE7171717DFFFFFF00FFFFFF00FFFFFF00C5C5C585D4D4D4FFCCCC
      CCFFC9C9C9FFBABABAFF9C9C9CFFA1A1A1FFC2C2C2FFC6C6C6FFC1C1C1FFB7B7
      B7FF89898985FFFFFF00FFFFFF00C8C8C8CDC4C4C4E3C0C0C0EED8D8D8FFCDCD
      CDFFBCBCBCFF828282C6777777447E7E7E448F8F8FC6C3C3C3FFC2C2C2FFCDCD
      CDFF8C8C8CEE878787E3838383CDC4C4C4FDE9E9E9FFD6D6D6FFC9C9C9FFCECE
      CEFFA5A5A5FF84848444FFFFFF00FFFFFF009A9A9A44ACACACFFC4C4C4FFBABA
      BAFFC6C6C6FFDDDDDDFF6B6B6BFDBFBFBFFDE2E2E2FFD2D2D2FFC6C6C6FFCDCD
      CDFFB1B1B1FF93939344FFFFFF00FFFFFF0095959544A8A8A8FFC2C2C2FFB7B7
      B7FFC0C0C0FFD2D2D2FF616161FDA3A3A3CD8F8F8FE3A0A0A0EECFCFCFFFC6C6
      C6FFCCCCCCFF9E9E9EC699999944949494448F8F8FC6C1C1C1FFBCBCBCFFB9B9
      B9FF646464EE585858E3535353CDFFFFFF00FFFFFF00ACACAC85C5C5C5FFC1C1
      C1FFC5C5C5FFC7C7C7FFAAAAAAFFA7A7A7FFC1C1C1FFBEBEBEFFB5B5B5FFAAAA
      AAFF69696985FFFFFF00FFFFFF00FFFFFF00ABABAB7DA6A6A6FED5D5D5FFC5C5
      C5FFCBCBCBFFD1D1D1FFC9C9C9FFC7C7C7FFCCCCCCFFC5C5C5FFBDBDBDFFCBCB
      CBFF6E6E6EFE6767677DFFFFFF00FFFFFF00A4A4A47BBCBCBCFFDEDEDEFFA6A6
      A6FF838383F4858585FEC4C4C4FFC2C2C2FF6D6D6DFE6E6E6EF4A6A6A6FFD2D2
      D2FF808080FF5252527BFFFFFF00FFFFFF00FFFFFF008181819B6F6F6FFD6464
      64E776767619838383E7CBCBCBFFC7C7C7FF626262E75A5A5A19585858E74E4E
      4EFD4949499BFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006F6F6F296A6A
      6A0E7A7A7A02818181EABDBDBDFFB2B2B2FF5B5B5BEA5A5A5A025C5C5C0E4E4E
      4E29FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF006D6D6D63585858BF515151BF52525263FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00D898
      5223D4964D7DD2924CDBCD8C45F3CB8B41F3C98B40DBC78B407DC5873D23FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00D6974F53D191
      49E6D0A06AFFE0BFA0FFE3C5AEFFE3C5AEFFDFBC9FFFC89762FFBD7D35E6BC7E
      3553FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00D4964D53CF8D47F4D9B2
      8CFFE6CDB8FFE0BA9DFFD7AB85FFD6A982FFD9B391FFE1C2ABFFD4AE86FFB16B
      35F4B16F3553FFFFFF00FFFFFF00FFFFFF00D2934C22CE8E47E5D9B28CFFE6CA
      B3FFD6A97DFFD1A579FFE2C4A8FFE1C3A8FFD0A276FFD1A477FFDDBDA2FFD0AC
      85FFAB6635E5A9653522FFFFFF00FFFFFF00CE91477ECD9C68FFE7CBB4FFD4A5
      7AFFD0A077FFCF9E74FFFBF8F5FFFBF8F5FFCB9E71FFCB9D71FFCDA177FFDFC0
      A5FFB98A5BFFA45C347EFFFFFF00FFFFFF00CB8E41DBE0BC9FFFDBB393FFCFA0
      75FFCD9E72FFCB9C71FFDDBFA3FFDDBFA2FFC5996BFFC5996BFFC4986BFFD1AB
      85FFD8BA97FF9E5635DBFFFFFF00FFFFFF00C5853BF6E4C9B0FFD0A37AFFCC9D
      71FFC79A6CFFC5986BFFFFFFFFFFFFFFFEFFC39669FFC19468FFC29468FFC398
      6DFFDFC5ABFF955334F6FFFFFF00FFFFFF00BF7E35F6E3C7AFFFD0A276FFC599
      6BFFC4976AFFC49669FFEEE0D4FFFBF7F4FFBF9066FFBE8F65FFBE8F64FFBE92
      69FFDFC6AAFF925034F6FFFFFF00FFFFFF00BC7E35DBDBBC9CFFD5AD89FFC798
      6CFFC39569FFC19367FFEDDFD3FFFAF7F4FFBB8B63FFB98A63FFB88A62FFC59D
      78FFD2B893FF905135DBFFFFFF00FFFFFF00B878357EBF915EFFE0C2A8FFC596
      6CFFC29169FFE1CBB8FFFEFDFCFFFFFFFEFFEADCD0FFB4855EFFB3855EFFD4B5
      99FFAE7B56FF8F51357EFFFFFF00FFFFFF00AF703522AB6935E5CFAA81FFDABC
      A2FFBE9166FFBA8C62FFB7895FFFB3845EFFB1835DFFB0835CFFCDAA8DFFC6A5
      79FF895034E589503522FFFFFF00FFFFFF00FFFFFF00A76234539F5533F4CBA7
      7DFFD8BB9FFFC39C77FFB68A62FFB48660FFBE9672FFD1B397FFC5A377FF844F
      35F489503553FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF009F5634539955
      34E6B28057FFD5B793FFDBC3A6FFDAC3A6FFD2B490FFAB7A52FF864F34E68850
      3553FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF009754
      35239453347D925234DB8A5034F3884F34F3895035DB8950357D84503623FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0040C9
      62233BC55E7D39C25BDB31BD54F32DBB52F32BB952DB2BB7527D28B44E23FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003DC7605336C2
      59E659C274FF96D7A3FFA5DCAEFFA5DCAEFF95D6A1FF50B96AFF1FAB42E61FA9
      4253FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003BC55E5334C055F47FCE
      90FFAFE1B7FF92D89DFF77CE83FF77CE83FF92D89DFFAEE1B5FF78C88BFF1D9D
      32F41D9D3653FFFFFF00FFFFFF00FFFFFF0039C25C2234BE55E57FCE90FFAEE1
      B5FF6DCC7AFF6ACA76FF68C872FF68C874FF68C875FF6BC979FFACDFB4FF76C4
      89FF1C962DE51C942D22FFFFFF00FFFFFF0034BE597E57BF70FFAFE1B7FF6DCC
      7AFF68C872FF65C770FF63C56EFF62C46EFF63C471FFB6E3BEFF6FC77EFFACDF
      B5FF48A95EFF1C8F267EFFFFFF00FFFFFF002DBB54DB95D7A1FF91D79BFF69C9
      76FF64C66FFF61C46EFF61C36FFF61C26FFFB9E4C0FFFFFFFFFFE3F4E6FF8BD1
      99FF8BCE9DFF1C8820DBFFFFFF00FFFFFF0026B44BF6A7DDB1FF72CC80FF66C7
      73FFB0E1B7FFD2EED6FF63C170FFB8E3BFFFFFFFFFFFFBFDFCFF8CD099FF69C1
      7EFFA1D7AEFF1B7F1EF6FFFFFF00FFFFFF001FAD42F6A6DCAFFF70CA7FFF73CA
      80FFF0F9F1FFFFFFFFFFEBF7EDFFFFFFFFFFFBFDFCFF88CD96FF5BB971FF67BE
      7DFFA0D7AFFF1B7A1EF6FFFFFF00FFFFFF001FA942DB91D29FFF8DD49AFF64C3
      74FF79C987FFF2FAF4FFFFFFFFFFFDFEFDFF86CB96FF57B76DFF5BB972FF85CC
      97FF87C79AFF1B781FDBFFFFFF00FFFFFF001EA43D7E4CB064FFAADDB4FF64C1
      79FF5FBE71FF75C585FFD4ECD9FF8ACD99FF56B66CFF58B56EFF5CB774FFA6DA
      B4FF419B4EFF1B771F7EFFFFFF00FFFFFF001D9B36221C962FE572C287FFA8DB
      B2FF60BC77FF5CBA73FF59B870FF59B56FFF58B56FFF5BB774FFA5D9B3FF69B8
      7FFF1A711EE51B711F22FFFFFF00FFFFFF00FFFFFF001C912B531B8A20F46DBE
      83FFA8DBB5FF87CC98FF66BC7DFF64BA7CFF86CB98FFA5D9B4FF66B77DFF1A6C
      1DF41B711F53FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF001C8A21531B83
      1FE642A052FF87CA9AFF9BD3ABFF9BD2ABFF83C796FF3D974CFF1A6E1EE61B70
      1F53FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF001C81
      1F231B7E1F7D1B7A1FDB1A731EF31A701EF31B711FDB1B711F7D1B6C1F23FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF003DBFEC033CBCEBD43ABAEAC339B8E803FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF003CBCEB9585D4F1FF82D1F0FF37B5E769FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF003CBCEB123ABAE9F2F1FAFDFFD5EFFAFF35B2E6ED33AFE412FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF003AB9E9B494D8F2FFF3FCFEFFE7FAFEFF8FD3F0FF31ACE396FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003AB9
      E93038B7E8FDF9FDFFFF94E9F9FF9EEBFAFFECFAFEFF2FA9E1FC2DA6E030FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0038B7
      E8D4A9DFF4FFEDF9FDFF3EA3D6FF3EA3D6FFD4F5FCFFA2D7F1FF2BA3DEC3FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0038B6E85A71C8
      EDFFF9FEFFFF5EDCF4FF3EA2D5FF3EA2D5FF5CD9F4FFEDFBFEFF68BBE5FF269C
      DB5AFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0038B6E80C36B4E6ECC8EA
      F7FFE6FAFDFF5DDAF4FF3DA1D5FF3DA1D5FF57D7F2FFC7F3FCFFC0E3F4FF2499
      D9E42296D80CFFFFFF00FFFFFF00FFFFFF00FFFFFF0036B3E6AA8CD2F0FFEAFB
      FEFF94E6F8FF5CDAF4FF47B1DDFF3DA1D5FF56D7F2FF5CDBF5FFDEF8FDFF7DC0
      E7FF1D8ED487FFFFFF00FFFFFF00FFFFFF0036B3E62434B1E5FBF3FBFEFFC3F2
      FBFF5CDCF6FF5CDAF4FF64DFF6FF57CBEBFF55D6F2FF54D9F5FF94E7F8FFE3F4
      FBFF1787D0F91380CD24FFFFFF00FFFFFF0034B0E5CA9DD7F1FFE7F9FDFF8BE5
      F8FF5ADBF6FF5BDAF4FF3DA1D5FF3DA1D5FF54D6F2FF52D8F5FF50D6F4FFD8F6
      FCFF88BFE5FF0E79C9B4FFFFFF0034B0E54B62BFE8FFF4FCFEFFB5EFFAFF58DA
      F5FF58DAF5FF57D8F3FF58D7F2FF58D6F2FF57D9F4FF51D8F5FF4ED7F4FF62DA
      F6FFEAFBFEFF4493D2FF066DC34B31ADE3E3BEE3F5FFF4FCFEFFEFFBFEFFEEFB
      FEFFEEFBFEFFEFFCFEFFEFFCFEFFEFFBFEFFEEFBFEFFEDFBFEFFEDFBFEFFECFB
      FEFFF2FCFEFFABCEEBFF0368C1D82FAAE2A22DA7E0FF2BA4DFFF29A1DDFF279E
      DCFF259BDAFF2398D9FF2093D6FF1B8CD3FF1685CFFF117ECCFF0D77C9FF0971
      C6FF066CC3FF0368C1FF0064BFA2FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006A85
      FC236984FA7D6782F9DB6580F7F3637EF5F3617CF3DB5F7AF17D5D77EF23FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006984FA536882
      F9E66E83EEFF92A6F4FFA0B4F8FFA0B4F8FF91A6F3FF687DE9FF5973EBE65671
      E953FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006883F9536681F8F48497
      F1FFA9BDFBFF8AA3F8FF6B89F6FF6B89F6FF89A2F8FFA8BCFAFF7F92ECFF526C
      E4F44F69E253FFFFFF00FFFFFF00FFFFFF006781F822657FF6E58397F0FFA9BD
      FBFF6382F5FF6382F5FFFFFFFFFFFFFFFFFF617EF3FF607CF3FFA6B9F9FF7B8D
      EAFF4A64DEE54862DB22FFFFFF00FFFFFF00637EF57E6C81ECFFA9BDFBFF6382
      F5FF6281F5FF6280F4FFFFFFFFFFFFFFFFFF5F7AF1FF5F78F0FF5D76EFFFA5B5
      F8FF5D70DDFF435DD77EFFFFFF00FFFFFF005F7AF1DB91A6F3FF88A1F8FF6280
      F4FF617EF3FF607CF3FFFFFFFFFFFFFFFFFF5D76EFFF5C73EEFF5B70ECFF8293
      F1FF8998ECFF3E58D2DBFFFFFF00FFFFFF005B76EDF6A1B6F8FF6784F4FF607C
      F3FF5F7AF1FF5F78F0FFFFFFFFFFFFFFFFFF5B70ECFF5A6EEBFF596CEAFF5F6F
      E9FF9BA8F1FF3A53CEF6FFFFFF00FFFFFF005771E9F6A0B3F7FF6580F2FF5F78
      F0FF5D76EFFF5C73EEFFD3D9FAFFFFFFFFFF596CEAFF5869E8FF5767E7FF5D6C
      E7FF99A5F1FF354FCAF6FFFFFF00FFFFFF00526DE5DB8E9FF0FF8499F4FF5C73
      EEFF5B70ECFF5A6EEBFF909DF1FFA6AFF3FF5767E7FF5665E6FF5562E5FF7D89
      EBFF8591E7FF314AC6DBFFFFFF00FFFFFF004E68E17E6073E0FFA4B3F7FF5A6E
      EBFF596CEAFF5869E8FFFFFFFFFFFFFFFFFF5562E5FF5461E3FF535FE2FF9FA9
      F2FF5061D1FF2D46C27EFFFFFF00FFFFFF004963DC224660DAE57888E6FFA3B0
      F5FF5767E7FF5665E6FF8992EDFF8892ECFF535FE2FF525DE1FF9FA9F2FF6F7D
      DDFF2B44C0E52942BE22FFFFFF00FFFFFF00FFFFFF00425CD5533F59D3F47584
      E3FFA1ACF4FF7F8BECFF5C67E4FF5B66E3FF7D87EAFF9FA8F1FF6F7CDDFF2943
      BFF42741BD53FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003A54CF533852
      CCE65264D4FF8490E7FF95A0EEFF959FEDFF838EE5FF4C5DCEFF2841BDE6263F
      BB53FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00334D
      C823314BC67D2F48C4DB2C46C2F32A44C0F32842BEDB2640BC7D243EBA23FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F9DB
      C423F8DAC27DF7D8C0DBF6D7BEF3F4D5BCF3F3D3B9DBF1D1B77DF0CFB423FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F6D8BF53F5D6
      BDE6F9E9DCFFF6E8DDFFF3E5DAFFF3E5DAFFF5E7DCFFF5E4D6FFEBC8ACE6E9C6
      A953FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F4D4BB53F2D2B8F4F7EA
      DFFFEEDED0FFE3C1A7FFD8AE89FFD7AC86FFDDBB9CFFEBD6C7FFF3E6D9FFE3BE
      9FF4E1BB9C53FFFFFF00FFFFFF00FFFFFF00F1D0B522EFCEB3E5F6E9DDFFECD8
      C6FFD7AC81FFDCBB9AFFF6ECE3FFF5ECE2FFE4C8AEFFD2A77BFFE6CEBAFFF1E2
      D5FFDBB391E5D9B08E22FFFFFF00FFFFFF00EBC9AD7EF4E3D4FFEFDCCDFFD5A8
      7EFFD0A077FFFBF8F5FFFCF8F5FFFCF8F5FFFBF8F5FFD1A881FFCFA47BFFEAD5
      C3FFEAD4C2FFD2A7837EFFFFFF00FFFFFF00E6C1A3DBF3E5D9FFDFBB9EFFCFA0
      75FFCD9E72FFF5EBE3FFE4CBB4FFE7D3BFFFFBF8F6FFE5D3BFFFC4986BFFD6B4
      91FFEEE0D2FFCC9E78DBFFFFFF00FFFFFF00E0B999F6F2E5DAFFD1A67EFFCC9D
      71FFC79A6CFFC5986BFFE2CCB6FFF8F3EEFFF6EEE8FFD9BDA1FFC29468FFC59B
      71FFF0E2D6FFC5956CF6FFFFFF00FFFFFF00D9B08FF6F2E4D9FFD1A57AFFC599
      6BFFC4976AFFC49669FFFAF6F2FFF3EAE1FFC2956DFFBE8F65FFBE8F64FFC095
      6DFFEFE3D5FFBF8C61F6FFFFFF00FFFFFF00D3A784DBEFE1D3FFD9B595FFC798
      6CFFC39569FFC19367FFBF9066FFBF9066FFBB8B63FFB98A63FFB88A62FFCBA7
      86FFEADCCCFFB88357DBFFFFFF00FFFFFF00CC9E787EE4CCB9FFEAD6C5FFC799
      71FFBF9066FFBF9066FFF7F1ECFFF6F0EAFFB7895FFFB7895FFFB58963FFE2CE
      BBFFD9BDA6FFB27B4D7EFFFFFF00FFFFFF00C6956D22C3926AE5EAD8C9FFE3CD
      BAFFC0946BFFBA8C62FFCFB094FFCFB094FFB7895FFFB28761FFDAC0AAFFE4D1
      C0FFAE7546E5AD734322FFFFFF00FFFFFF00FFFFFF00BD895F53BB875BF4E7D5
      C4FFE5D2BFFFC9A685FFB88E67FFB68A65FFC5A180FFE0CCBAFFE3D0BEFFAB70
      40F4A96E3D53FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00B57E5153B37C
      4EE6D7BBA3FFE9DACAFFECE0D1FFECE0D1FFE8D8C8FFD3B59CFFA76C3AE6A66A
      3853FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00AD74
      4423AC72417DAA703FDBA86D3CF3A76B3AF3A56937DBA468357DA3663323FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF004EA35778499B5103FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0047994FD24191499CFFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0063C06E1D5FBB6A705BB4
      65B756AD5FEA50A65AFF4B9E53FF45964DFF60A868FF5BA262FF347E3A90FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0062BE6D395EB968B079C383FF89CA
      92FF94D09CFF95D19EFF90CF99FF8CCB94FF87C98FFF80C487FF4E9554FF276D
      2C84FFFFFF00FFFFFF00FFFFFF0060BC6C1D5CB667B085C98EFF9BD4A4FF8FCE
      98FF92CF9AFF8DCC95FF88CA90FF83C68BFF7EC485FF79C17FFF478D4CFF2265
      258AFFFFFF00FFFFFF00FFFFFF005BB4657075BF7EFF98D2A1FF94CF9CFF86C7
      8DFF5EA765FF398640FF347E3AFF2E7633FF49904FFF458B4AFF20632493FFFF
      FF00FFFFFF00FFFFFF00FFFFFF0054AB5EB780C389FF8DCC95FF83C48AFF3D8B
      44E137833E8DFFFFFF00FFFFFF00FFFFFF00236627D21F61239FFFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF004DA155E847994FF8419149F63B8842ED3580
      3C83FFFFFF00FFFFFF00FFFFFF00FFFFFF001E5F217B1B5B1E0354AB5E614EA3
      5761499B516143934B613D8B446145964D613F8E466139864061347E3A612E76
      336167C6730364C2707BFFFFFF00FFFFFF00FFFFFF00FFFFFF004DA155834799
      4FED419149F63B8842F835803CE8FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF0063C06E9F5FBB6AD2FFFFFF00FFFFFF00FFFFFF004B9E538D45964DE186C6
      8EFF88C98FFF6FB376FF2E7633B7FFFFFF00FFFFFF00FFFFFF00FFFFFF0062BE
      6D937BC785FF77C281FF54AB5EFF4EA357FF499B51FF63AC6BFF83C38BFF87C9
      8FFF82C689FF509756FF276D2C70FFFFFF00FFFFFF00FFFFFF0060BC6C8A79C4
      83FF9ED7A7FF9BD4A4FF97D29FFF92CF9AFF8DCC95FF88CA90FF7AC282FF7EC4
      85FF5DA463FF266B2AB02265251DFFFFFF00FFFFFF00FFFFFF005BB4658473BD
      7CFF96D19FFF94CF9CFF8FCD96FF8ACA91FF85C78BFF7ABE81FF65AD6CFF4B92
      51FF246829B020632439FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF004EA3
      579066B06EFF61AA68FF3D8B44FF37833EFF327B37FF2C7432EA276D2CB72366
      27701F61231DFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF004191499C3B8842D2FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF0039864003347E3A78FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00EFC2A37EEFC1A2E3EDC09FFFEBBE9DFFEBBC
      9AFFE9BA96FFE7B793FFE6B590FFE4B28CFFE2AF88FFE0AC84FFDDA980FFDCA5
      7DFFDAA37ACAFFFFFF00FFFFFF00EEC1A1EBFBF7F4FFFBF7F4FFFBF7F4FFFBF7
      F4FFFBF7F4FFFBF7F4FFFBF7F4FFFBF7F4FFFBF7F4FFFBF7F4FFFBF7F4FFFBF7
      F4FFD7A074F8FFFFFF00FFFFFF00ECBF9EFFFBF7F4FF9CD5A5FF98D3A1FF94D0
      9DFF90CE98FF8BCB93FF87C98EFF82C689FF7EC384FF7AC180FF76BE7CFFFBF7
      F4FFD49B6FFFFFFFFF00FFFFFF00EBBD9BFFFBF7F4FFFFFFFFFFFFFFFFFFFFFF
      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF7
      F4FFD1976AFFFFFFFF00FFFFFF00E9BA98FFFBF7F4FFE9C3A6FFE9C3A6FFE9C3
      A6FFE9C3A6FFE9C3A6FFE9C3A6FFE9C3A6FFE9C3A6FFE9C3A6FFE9C3A6FFFBF7
      F4FFCE9364FFFFFFFF00FFFFFF00E7B794FFFBF7F4FFE9C3A6FFFFFFFFFFE8C4
      A9FFFFFFFFFFFFFFFFFFFFFFFFFFE8C7ACFFFFFFFFFFBABABAFFB4B4B4FFF7F1
      EBFFCB8F5FFFFFFFFF00FFFFFF00E5B48FFFFAF6F2FFE9C6AAFFE9C6ACFFEAC7
      ACFFE9C7ADFFE9C9AEFFD5C4B7FFBDBBBAFFDFC5B1FFC0C0C0FFBEBEBEFFE0D7
      D1FFABA39EFFA3A3A398FFFFFF00E3B18CFFFAF6F1FFEAC9AEFFFFFFFFFFEAC9
      B0FFFFFFFFFFFFFFFFFFBABABAFFDADADAFFBFBFBFFFD6D6D6FFD8D8D8FFB8B8
      B8FFD4D4D4FF888888FF8686860AE1AE87FFFAF4F0FFEACBB2FFEACCB3FFEACC
      B3FFEACCB3FFEACCB3FFCEC1B6FFD2D2D2FFABABABFF818181FF8A8A8AFFB4B4
      B4FFCACACAFF9393939EFFFFFF00DFAA82FFF9F3EFFFEACEB7FFFFFFFFFFEBD0
      BBFFFFFFFFFFB4B4B4FFCACACAFFE8E8E8FF868686FFFFFFFFFFFFFFFFFF9A99
      99FFE2E2E2FFB6B6B6FF878787FFDDA87EFFF9F3EFFFEBD0BAFFEBD0BBFFEBD0
      BBFFEBD0BBFFA5A5A5FFB7B7B7FFE1E1E1FF9C9C9CFFEACDB5FFEACDB5FF9A9A
      99FFD5D5D5FF999999FF656565FFD9A47AFFF9F3EEFFEBD2BEFFFFFFFFFFEBD3
      BFFFFFFFFFFFF9F9F9FFCDCDCDFFC9C9C9FFBDBDBDFF9C9C9CFF9A9A9AFFB5B5
      B5FFC2C2C2FF6D6D6D9C65656512D7A175FFF8F2EDFFF7F0EAFFF6EDE6FFF4EA
      E2FFF3E7DEFFF1E4DBFFA0A0A0FFC9C9C9FFA5A5A5FFCACACAFFC2C2C2FFA1A1
      A1FFC4C4C4FF6D6D6DFF6E6E6E03D69E72C4D3996EF4D19668FFCE9263FFCB8E
      5EFFC98A5BFFC78756FF9F8672FF696969FFAE815EFFA2A2A2FF8B8B8BFFA379
      58FF5D5D5DFE595959B9FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00727272FF5C5C5CFFFFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00777777FF777777FF7676
      76FF717171FF6C6C6CFF656565FF5E5E5EFF565656FF4D4D4DFF444444FF3C3C
      3CFF333333FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00737373FF6D6D6DFF6767
      67FF606060FF585858FF505050FF474747FF3E3E3EFF363636FFFFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00636363FF5B5B5BFF5353
      53FF4A4A4AFF414141FF393939FF303030FF272727FF1F1F1FFF171717FF1010
      10FF0A0A0AFFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF004D4D4DFF444444FF3C3C
      3CFF333333FF2A2A2AFF212121FF191919FF121212FF0B0B0BFFFFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00BCBCBCFFBCBCBCFFBCBC
      BCFFBCBCBCFFBCBCBCFFBCBCBCFFBBBBBBFFBBBBBBFFBABABAFFB9B9B9FFB7B7
      B7FFB7B7B7FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00BCBCBCFFF0F0F0FFF0F0
      F0FFEFEFEFFFEEEEEEFFEDEDEDFFEBEBEBFFEAEAEAFFE8E8E8FFE7E7E7FFE6E6
      E6FFB3B3B3FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00BBBBBBFFBBBBBBFFBABA
      BAFFB9B9B9FFB7B7B7FFB7B7B7FFB5B5B5FFB4B4B4FFB3B3B3FFB3B3B3FFB3B3
      B3FFB3B3B3FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00767676FF747474FF7070
      70FF6C6C6CFF686868FFFFFFFF00FFFFFF00595959FF535353FF4E4E4EFF4848
      48FF424242FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006F6F6FFF6B6B6BFF6666
      66FF616161FF5C5C5CFFFFFFFF00FFFFFF004B4B4BFF454545FF3F3F3FFF3939
      39FF333333FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00646464FF5F5F5FFF5A5A
      5AFF545454FF4F4F4FFFFFFFFF00FFFFFF003D3D3DFF373737FF313131FF2B2B
      2BFF252525FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00585858FF525252FF4C4C
      4CFF464646FF404040FFFFFFFF00FFFFFF002E2E2EFF282828FF232323FF1D1D
      1DFF181818FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF004A4A4AFF444444FF3E3E
      3EFF383838FF323232FFFFFFFF00FFFFFF00202020FF1B1B1BFF161616FF1111
      11FF0C0C0CFFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003C3C3CFF353535FF2F2F
      2FFF292929FF242424FFFFFFFF00FFFFFF00141414FF0F0F0FFF0B0B0BFF0707
      07FF030303FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00BCBCBCFFBCBCBCFFBCBC
      BCFFFFFFFF00777777FF747474FF707070FF6B6B6BFF656565FF606060FF5959
      59FF535353FF4C4C4C6BFFFFFF00FFFFFF00FFFFFF00BCBCBCFFF0F0F0FFBCBC
      BCFFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00BCBCBCFFF0F0F0FFBBBB
      BBFFFFFFFF006D6D6DFF686868FF636363FF5C5C5CFF565656FF4F4F4FFFFFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00BCBCBCFFEFEFEFFFBBBB
      BBFFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00BBBBBBFFEDEDEDFFB9B9
      B9FFFFFFFF00606060FF595959FF535353FF4C4C4CFF454545FF3D3D3DFF3636
      36FF2F2F2FFF2828286BFFFFFF00FFFFFF00FFFFFF00BABABAFFEBEBEBFFB7B7
      B7FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00B8B8B8FFE8E8E8FFB5B5
      B5FFFFFFFF004F4F4FFF484848FF414141FF3A3A3AFF323232FF2B2B2BFFFFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00B6B6B6FFE6E6E6FFB3B3
      B3FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00B4B4B4FFE5E5E5FFB3B3
      B3FFFFFFFF003D3D3DFF363636FF2F2F2FFF282828FF212121FF1B1B1BFF1414
      14FF0F0F0FFF0A0A0A6BFFFFFF00FFFFFF00FFFFFF00B3B3B3FFE4E4E4FFB3B3
      B3FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00B3B3B3FFB3B3B3FFB3B3
      B3FFFFFFFF002B2B2BFF242424FF1E1E1EFF171717FF121212FF0C0C0CFFFFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00767676FF747474FF7070
      70FF6C6C6CFF686868FF636363FF5E5E5EFF595959FF535353FF4E4E4EFF4848
      48FF424242FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006F6F6FFF6B6B6BFF6666
      66FF616161FF5C5C5CFF575757FF515151FF4B4B4BFF454545FFFFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00646464FF5F5F5FFF5A5A
      5AFF545454FF4F4F4FFF494949FF434343FF3D3D3DFF373737FF313131FF2B2B
      2BFF252525FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00585858FF525252FF4C4C
      4CFF464646FF404040FF3A3A3AFF343434FF2E2E2EFF282828FFFFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF004A4A4AFF444444FF3E3E
      3EFF383838FF323232FF2C2C2CFF262626FF202020FF1B1B1BFF161616FF1111
      11FF0C0C0CFFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003C3C3CFF353535FF2F2F
      2FFF292929FF242424FF1E1E1EFF191919FF141414FF0F0F0FFFFFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00A971516BDDB18DF4DCA77BFFDCA67AFFDAA4
      7AFFD8A279FFD5A076FFD49E75FFD29D73FFCF9A72FFCE9970FFCB966FFFC994
      6CFFC49A7AF4A971516BFFFFFF00DDAC85FDE8B992FFE8B992FFE8B992FFE8B9
      92FFE8B992FFE8B992FFE8B992FFE8B992FFE8B992FFE8B992FFE8B992FFE8B9
      92FFE8B992FFC1906FFDFFFFFF00DCA77BFFDCA77BFFDCA77BFFDCA77BFFDCA7
      7BFFDCA77BFFDCA77BFFDCA77BFFDCA77BFFDCA77BFFDCA77BFFDCA77BFFDCA7
      7BFFDCA77BFFC08B66FFFFFFFF00DBA47AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
      FFFFFFFFFFFFBD8763FFFFFFFF00D9A379FFFFFFFFFFFFBF68FFFFFFFFFFC7C7
      C7FFC6C6C6FFC3C3C3FFC1C1C1FFBFBFBFFFBDBDBDFFBBBBBBFFB9B9B9FFB9B9
      B9FFFFFFFFFFBA8560FFFFFFFF00D8A279FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFEFFFEFEFEFFFEFEFEFFFDFDFDFFFDFD
      FDFFFFFFFFFFB7815EFFFFFFFF00D5A076FFFFFFFFFFFFB95EFFFFFFFFFFC1C1
      C1FFBEBEBEFFBABABAFFB7B7B7FFB5B5B5FFB2B2B2FFB0B0B0FFAEAEAEFFADAD
      ADFFFFFFFFFFB57E5CFFFFFFFF00D49E75FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
      FFFFFEFEFEFFFEFEFEFFFEFEFEFFFDFDFDFFFDFDFDFFFDFDFDFFFDFDFDFFFCFC
      FCFFFFFFFFFFB27C5AFFFFFFFF00D19C73FFFFFFFFFFFFB252FFFEFEFEFFB9B9
      B9FFB5B5B5FFB1B1B1FFADADADFFA9A9A9FFA6A6A6FFA4A4A4FFA2A2A2FFA1A1
      A1FFFFFFFFFFB07A58FFFFFFFF00CC976FFFFFFFFFFFFEFEFEFFFDFDFDFFFDFD
      FDFFFDFDFDFFFCFCFCFFFCFCFCFFFCFCFCFFFCFCFCFFFBFBFBFFFBFBFBFFFBFB
      FBFFFFFFFFFFAC7554FFFFFFFF00CA946EFFFFFFFFFFFFAC48FFFDFDFDFFB2B2
      B2FFADADADFFA8A8A8FFA4A4A4FF9F9F9FFF9D9D9DFF9B9B9BFF999999FF9999
      99FFFFFFFFFFAA7353FFFFFFFF00C8926CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
      FFFFFFFFFFFFA97251FFFFFFFF00A97151A9C38E68FFC08B66FFBE8864FFBB85
      61FFB9835FFFB47E5CFFB27C5AFFB17B58FFAE7957FFAD7656FFAB7554FFA973
      53FFA97151FFA97151A9FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00
    }
  end
  object imlFileStatuses: TImageList
    left = 95
    top = 25
    Bitmap = {
      4C690C0000001000000010000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0040C9
      62233BC55E7D39C25BDB31BD54F32DBB52F32BB952DB2BB7527D28B44E23FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003DC7605336C2
      59E659C274FF96D7A3FFA5DCAEFFA5DCAEFF95D6A1FF50B96AFF1FAB42E61FA9
      4253FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003BC55E5334C055F47FCE
      90FFAFE1B7FF92D89DFF77CE83FF77CE83FF92D89DFFAEE1B5FF78C88BFF1D9D
      32F41D9D3653FFFFFF00FFFFFF00FFFFFF0039C25C2234BE55E57FCE90FFAEE1
      B5FF6DCC7AFF6ACA76FF68C872FF68C874FF68C875FF6BC979FFACDFB4FF76C4
      89FF1C962DE51C942D22FFFFFF00FFFFFF0034BE597E57BF70FFAFE1B7FF6DCC
      7AFF68C872FF65C770FF63C56EFF62C46EFF63C471FFB6E3BEFF6FC77EFFACDF
      B5FF48A95EFF1C8F267EFFFFFF00FFFFFF002DBB54DB95D7A1FF91D79BFF69C9
      76FF64C66FFF61C46EFF61C36FFF61C26FFFB9E4C0FFFFFFFFFFE3F4E6FF8BD1
      99FF8BCE9DFF1C8820DBFFFFFF00FFFFFF0026B44BF6A7DDB1FF72CC80FF66C7
      73FFB0E1B7FFD2EED6FF63C170FFB8E3BFFFFFFFFFFFFBFDFCFF8CD099FF69C1
      7EFFA1D7AEFF1B7F1EF6FFFFFF00FFFFFF001FAD42F6A6DCAFFF70CA7FFF73CA
      80FFF0F9F1FFFFFFFFFFEBF7EDFFFFFFFFFFFBFDFCFF88CD96FF5BB971FF67BE
      7DFFA0D7AFFF1B7A1EF6FFFFFF00FFFFFF001FA942DB91D29FFF8DD49AFF64C3
      74FF79C987FFF2FAF4FFFFFFFFFFFDFEFDFF86CB96FF57B76DFF5BB972FF85CC
      97FF87C79AFF1B781FDBFFFFFF00FFFFFF001EA43D7E4CB064FFAADDB4FF64C1
      79FF5FBE71FF75C585FFD4ECD9FF8ACD99FF56B66CFF58B56EFF5CB774FFA6DA
      B4FF419B4EFF1B771F7EFFFFFF00FFFFFF001D9B36221C962FE572C287FFA8DB
      B2FF60BC77FF5CBA73FF59B870FF59B56FFF58B56FFF5BB774FFA5D9B3FF69B8
      7FFF1A711EE51B711F22FFFFFF00FFFFFF00FFFFFF001C912B531B8A20F46DBE
      83FFA8DBB5FF87CC98FF66BC7DFF64BA7CFF86CB98FFA5D9B4FF66B77DFF1A6C
      1DF41B711F53FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF001C8A21531B83
      1FE642A052FF87CA9AFF9BD3ABFF9BD2ABFF83C796FF3D974CFF1A6E1EE61B70
      1F53FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF001C81
      1F231B7E1F7D1B7A1FDB1A731EF31A701EF31B711FDB1B711F7D1B6C1F23FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0074BB
      8B2371B9887D6EB684DB6AB380F367B17CF363AE77DB60AB737D5CA86E23FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0073BB8B5371B8
      87E694CEA4FFC3E6CBFFCFEBD4FFC9E9CEFFAFDDB8FF6DB97FFF58A569E654A1
      6553FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0073BB8B5370B887F4AFDC
      BBFFDCF2E0FFB6E4BDFF9BDBA5FF96D9A0FFA5DFAFFFC0E8C5FF79C28AFF509E
      5FF44C9B5B53FFFFFF00FFFFFF00FFFFFF0073BA8A2270B887E5AADAB7FFD8F1
      DCFF92D89DFF88CD93FF84CC8EFF8BD496FF8AD495FF83D28EFFAFE0B7FF6BB9
      7DFF489856E544945122FFFFFF00FFFFFF0070B8877E85C797FFD2EED7FF95D9
      A0FF8AD394FF7FC889FFFFFFFFFFFFFFFFFF79CD85FF6BC37CFF6FC77EFFACDF
      B5FF459E57FF40914C7EFFFFFF00FFFFFF006DB583DBACDDB6FFA6DFAFFF81CB
      8CFF7CC986FF6EBD79FFFFFFFFFFFFFFFFFF5BAC6AFF60BC77FF5CBA73FF8BD1
      99FF80C592FF3C8E47DBFFFFFF00FFFFFF0069B27EF6B6E2BEFF8BD597FF7AC9
      86FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59B870FF69C1
      7EFF9DD4AAFF388B42F6FFFFFF00FFFFFF0065AF7AF6A9DDB3FF7DCF8AFF75CC
      81FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59B870FF67BE
      7DFF9CD4ABFF34883DF6FFFFFF00FFFFFF0061AC75DB8ACC98FF89D396FF6BC6
      7AFF63C170FF55AB65FFFFFFFFFFFFFFFFFF59B870FF59B870FF5BB972FF85CC
      97FF7BBE8DFF308539DBFFFFFF00FFFFFF005DA9707E53AB68FFAADDB4FF64C1
      79FF5FBE71FF60BC77FFFFFFFFFFFFFFFFFF59B870FF58B56EFF5CB774FFA6DA
      B4FF388F43FF2C82347EFFFFFF00FFFFFF0059A66B2256A366E56AB97DFFA8DB
      B2FF60BC77FF5CBA73FF59B870FF59B56FFF58B56FFF5BB774FFA5D9B3FF5AAA
      6CFF2C8234E5297F3022FFFFFF00FFFFFF00FFFFFF00519F61534D9C5DF464B4
      78FFA8DBB5FF87CC98FF66BC7DFF64BA7CFF86CB98FFA5D9B4FF58AA6BFF2C81
      34F4297F3053FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00499958534596
      53E6419950FF7DC28FFF96D0A6FF96CFA6FF78BE89FF368D42FF2C8134E6297F
      3053FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF004192
      4E233D8F497D3A8C44DB368940F332873CF32F8437DB2C81337D287F3023FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF002FBAE4094FAADBEA5093
      CAFD4E90C8FF2F9DD2DF35A4DE19FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0050A8D9FF6AA5D8FFC9E1
      F7FFCBE3F8FF4295CAFF3182C2AEFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF002FBAE4FFA7D4F4FFC5E1F8FFCCE3
      F9FFCCE3F9FFBDDBF7FF4F90C9FDFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF002FBAE4FFC3EDF8FFA8E2F8FF6CAEDDFFA5CF
      F4FFA5CFF4FFBDDBF7FF5393CBF7FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF002FBAE4FFC3EDF8FFB3F4FCFF68D9F5FF6FCFF3FF599D
      D0FF73ABDDFF4F91C9FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF002FBAE4FFC3EDF8FFB3F4FCFF60DCF5FF44D6F4FF8EEEFAFF5DB4
      E6FF3B8FD9FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF002FBAE4FFC3EDF8FFB3F4FCFF60DCF5FF44D6F4FF8EEEFAFF5DB4E6FF3B8F
      D9FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF002790
      BFFFC3EDF8FFB3F4FCFF60DCF5FF44D6F4FF8EEEFAFF5DB4E6FF3B8FD9FFFFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF002689B9FFBEE6
      F2FFB3F4FCFF60DCF5FF44D6F4FF8EEEFAFF5DB4E6FF3B8FD9FFFFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00206398202689B9FFB0CBE1FF67A9
      C8FF60DCF5FF44D6F4FF8EEEFAFF5DB4E6FF3B8FD9FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF001E6D93FFC8E1F2FFD1E7FAFF347D
      B5FF3199C3FF6DC4DCFF4A9CCFFF3483C7FFFFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF001E6D93FFCBE3F9FF61AAECFF4098
      E8FF1567C2FF1660AAFF2C76B4FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00124259FF5D9CD4FFA6CFF5FFA9CF
      ECFF488BC1FF2C76B4FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00134058FF15425EFF25699CFF2C76
      B4FF3B8BBAADFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006360
      F80AFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF007774FF1F7774
      FF2BFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00625FF82B5D5B
      F76F5956F53EFFFFFF00FFFFFF00FFFFFF00FFFFFF007774FF1F7A77FFFF7976
      FEFF726FFD2BFFFFFF00FFFFFF00FFFFFF00FFFFFF00615EF82B6461F8FF6A68
      F9FF5451F3A84F4DF229FFFFFF00FFFFFF007774FF1F7A77FFFF817EFFFF817E
      FEFF7471FDFF6C69FB2BFFFFFF00FFFFFF00605DF72B625FF8FF6F6DFBFF7E7C
      FFFF625FF8FF4A47F06F4542EE02FFFFFF007673FF087471FEFD7D7AFEFF8A87
      FFFF7C79FDFF6C69FBFF6361F92B5F5CF72B615EF8FF6E6CFAFF7D7AFFFF615F
      F7FF4946F0FC4441EE05FFFFFF00FFFFFF00FFFFFF00716EFD086E6BFCFC7774
      FDFF8682FFFF7673FCFF6462F8FF605DF7FF6D6AFAFF7B79FFFF605DF7FF4845
      EFFC4341EE08FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006967FB086663
      F9FC706DFBFF807EFFFF7E7BFFFF7C79FFFF7977FFFF5E5CF7FF4744EFFC4240
      EE08FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00615E
      F8085D5AF6FD7D79FFFF5E5BFFFF5B58FFFF7674FFFF4643EFFD413FED08FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF005C59
      F62B5D5BF7FF7976FFFF5956FFFF5754FFFF7270FFFF4846F0FF3C39EB2BFFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF005B58F62B5C5A
      F6FF6764FAFF7472FFFF7370FFFF706EFFFF6E6CFFFF5755F7FF3F3DEEFF3230
      E82BFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF005A57F52B5B59F6FF6663
      FAFF7471FFFF5A58F6FF4341EEFC3E3CECFD504DF4FF6867FFFF504EF5FF3634
      EBFF2A27E52BFFFFFF00FFFFFF00FFFFFF005956F52B5B58F6FF6562FAFF7170
      FFFF5956F6FF4240EEFC3E3BEC083937EB083532E9FC4745F2FF6362FFFF4A48
      F4FF2F2DE9FF2220E32BFFFFFF00FFFFFF005451F3415856F5FF6361FAFF5855
      F6FF413FEDFC3D3AEC08FFFFFF00FFFFFF00302DE7082C2AE6FC413FF1FF4C4A
      F6FF312FEAFF1F1DE241FFFFFF00FFFFFF00FFFFFF004A47F0414F4CF2FF403E
      EDFD3C39EB08FFFFFF00FFFFFF00FFFFFF00FFFFFF002725E5082422E4FC312F
      EAFF1F1DE241FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003F3DED413B38
      EB08FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00211FE3081E1C
      E241FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006A85
      FC236984FA7D6782F9DB6580F7F3637EF5F3617CF3DB5F7AF17D5D77EF23FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006984FA536882
      F9E66E83EEFF92A6F4FFA0B4F8FFA0B4F8FF91A6F3FF687DE9FF5973EBE65671
      E953FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF006883F9536681F8F48497
      F1FFA9BDFBFF8AA3F8FF6B89F6FF6B89F6FF89A2F8FFA8BCFAFF7F92ECFF526C
      E4F44F69E253FFFFFF00FFFFFF00FFFFFF006781F822657FF6E58397F0FFA9BD
      FBFF6382F5FF6382F5FFFFFFFFFFFFFFFFFF617EF3FF607CF3FFA6B9F9FF7B8D
      EAFF4A64DEE54862DB22FFFFFF00FFFFFF00637EF57E6C81ECFFA9BDFBFF6382
      F5FF6281F5FF6280F4FFFFFFFFFFFFFFFFFF5F7AF1FF5F78F0FF5D76EFFFA5B5
      F8FF5D70DDFF435DD77EFFFFFF00FFFFFF005F7AF1DB91A6F3FF88A1F8FF6280
      F4FF617EF3FF607CF3FFFFFFFFFFFFFFFFFF5D76EFFF5C73EEFF5B70ECFF8293
      F1FF8998ECFF3E58D2DBFFFFFF00FFFFFF005B76EDF6A1B6F8FF6784F4FF607C
      F3FF5F7AF1FF5F78F0FFFFFFFFFFFFFFFFFF5B70ECFF5A6EEBFF596CEAFF5F6F
      E9FF9BA8F1FF3A53CEF6FFFFFF00FFFFFF005771E9F6A0B3F7FF6580F2FF5F78
      F0FF5D76EFFF5C73EEFFD3D9FAFFFFFFFFFF596CEAFF5869E8FF5767E7FF5D6C
      E7FF99A5F1FF354FCAF6FFFFFF00FFFFFF00526DE5DB8E9FF0FF8499F4FF5C73
      EEFF5B70ECFF5A6EEBFF909DF1FFA6AFF3FF5767E7FF5665E6FF5562E5FF7D89
      EBFF8591E7FF314AC6DBFFFFFF00FFFFFF004E68E17E6073E0FFA4B3F7FF5A6E
      EBFF596CEAFF5869E8FFFFFFFFFFFFFFFFFF5562E5FF5461E3FF535FE2FF9FA9
      F2FF5061D1FF2D46C27EFFFFFF00FFFFFF004963DC224660DAE57888E6FFA3B0
      F5FF5767E7FF5665E6FF8992EDFF8892ECFF535FE2FF525DE1FF9FA9F2FF6F7D
      DDFF2B44C0E52942BE22FFFFFF00FFFFFF00FFFFFF00425CD5533F59D3F47584
      E3FFA1ACF4FF7F8BECFF5C67E4FF5B66E3FF7D87EAFF9FA8F1FF6F7CDDFF2943
      BFF42741BD53FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003A54CF533852
      CCE65264D4FF8490E7FF95A0EEFF959FEDFF838EE5FF4C5DCEFF2841BDE6263F
      BB53FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00334D
      C823314BC67D2F48C4DB2C46C2F32A44C0F32842BEDB2640BC7D243EBA23FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF0066C471C464C16FC9FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF0065C370CF7DCA88FF7BC886FF5EBA69D1FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0066C4720664C2
      70D97CC987FFA0D7A9FF9ED6A7FF76C27FFF58B162DB55AD5F06FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0063C06FB861BE
      6CF779C684FF9DD5A6FF9BD4A4FF72BE7CFF54AB5EF751A75ABBFFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF005BB566FF9AD4A2FF98D2A1FF53AA5CFFFFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF0058B062FF97D29FFF94D09DFF4FA458FFFFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF0054AA5DFF94CF9CFF90CF99FF66B16FFF47994F71FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF004FA458FF90CE98FF84C88DFF7BBF82FF42924AC33F8E4671FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF004B9E53FF8CCB94FF70BE79FF87C98FFF74B97BFF58A15FFF37833EFF347E
      3AFFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF0046984EFF88CA90FF65B56EFF66B86FFF78C07FFF7EC485FF7DC282FF3078
      35FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00429149FF85C78BFF418946FF5BA061FF71B677FF79C080FF79C17FFF2C73
      31FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF003D8B44FF82C588FF71B778FF377D3CFF2D7433E92D7533F92A712FFF286D
      2CFFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF0038853FFF7EC384FF7BC282FF2F7735FF2B713008FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00347E3AFF7AC280FF78C07EFF2B7230FFFFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00307835FF77BF7DFF75BE7AFF276C2CFFFFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF002C7331FF296F2DFF266B2AFF246728FFFFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF003DBFEC033CBCEBD43ABAEAC339B8E803FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF003CBCEB9585D4F1FF82D1F0FF37B5E769FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF003CBCEB123ABAE9F2F1FAFDFFD5EFFAFF35B2E6ED33AFE412FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF003AB9E9B494D8F2FFF3FCFEFFE7FAFEFF8FD3F0FF31ACE396FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003AB9
      E93038B7E8FDF9FDFFFF94E9F9FF9EEBFAFFECFAFEFF2FA9E1FC2DA6E030FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0038B7
      E8D4A9DFF4FFEDF9FDFF3EA3D6FF3EA3D6FFD4F5FCFFA2D7F1FF2BA3DEC3FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0038B6E85A71C8
      EDFFF9FEFFFF5EDCF4FF3EA2D5FF3EA2D5FF5CD9F4FFEDFBFEFF68BBE5FF269C
      DB5AFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0038B6E80C36B4E6ECC8EA
      F7FFE6FAFDFF5DDAF4FF3DA1D5FF3DA1D5FF57D7F2FFC7F3FCFFC0E3F4FF2499
      D9E42296D80CFFFFFF00FFFFFF00FFFFFF00FFFFFF0036B3E6AA8CD2F0FFEAFB
      FEFF94E6F8FF5CDAF4FF47B1DDFF3DA1D5FF56D7F2FF5CDBF5FFDEF8FDFF7DC0
      E7FF1D8ED487FFFFFF00FFFFFF00FFFFFF0036B3E62434B1E5FBF3FBFEFFC3F2
      FBFF5CDCF6FF5CDAF4FF64DFF6FF57CBEBFF55D6F2FF54D9F5FF94E7F8FFE3F4
      FBFF1787D0F91380CD24FFFFFF00FFFFFF0034B0E5CA9DD7F1FFE7F9FDFF8BE5
      F8FF5ADBF6FF5BDAF4FF3DA1D5FF3DA1D5FF54D6F2FF52D8F5FF50D6F4FFD8F6
      FCFF88BFE5FF0E79C9B4FFFFFF0034B0E54B62BFE8FFF4FCFEFFB5EFFAFF58DA
      F5FF58DAF5FF57D8F3FF58D7F2FF58D6F2FF57D9F4FF51D8F5FF4ED7F4FF62DA
      F6FFEAFBFEFF4493D2FF066DC34B31ADE3E3BEE3F5FFF4FCFEFFEFFBFEFFEEFB
      FEFFEEFBFEFFEFFCFEFFEFFCFEFFEFFBFEFFEEFBFEFFEDFBFEFFEDFBFEFFECFB
      FEFFF2FCFEFFABCEEBFF0368C1D82FAAE2A22DA7E0FF2BA4DFFF29A1DDFF279E
      DCFF259BDAFF2398D9FF2093D6FF1B8CD3FF1685CFFF117ECCFF0D77C9FF0971
      C6FF066CC3FF0368C1FF0064BFA2FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C8C8C80CC3C3C36CBEBE
      BEBEB8B8B8F6B2B2B2BEACACAC6CA5A5A50CFFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00C2C2C276D3D4D4FFDDDE
      DEFFE0E3E4FFD8DADBFFC5C6C6FF9D9D9D76FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00C6C6C61AC1C1C180BBBBBBF7DBDEDFFFCCD1
      D3FFC6CDCFFFCAD0D3FFD7DBDCFF969696F38E8E8E6C8888880CFFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00C0C0C080D7D7D7FFDBDEDFFFCCD1D3FFC3CA
      CCFFC1C9CCFFC0C9CBFFC7CCCEFFC7C8C9FFBCBCBCFFB5B5B5F0B0B0B09DA6A6
      A641FFFFFF00FFFFFF00FFFFFF00B9B9B9D5DEE0E1FFCAD0D2FFC2CACCFFC1C9
      CCFFBEC7C9FFBEC4C6FFC0C2C2FFE3E3E3FFEAEAEAFFE9E9E9FFDEDEDEFFABAB
      ABD29F9F9F44FFFFFF00FFFFFF00B2B2B2D5DBDEDFFFC8CED0FFC2C9CCFFC5C8
      C9FFC3C4C5FFC3C4C4FFE5E5E5FFE6E6E6FFE2E2E2FFE2E2E2FFE7E7E7FFDCDC
      DCFFA7A7A7A7FFFFFF00FFFFFF00A7A7A780C5C6C6FFD2D5D5FFC9CBCBFFDCDC
      DCFFE8E8E8FFE8E8E8FFE6E6E6FFE3E3E3FFE0E0E0FFDFDFDFFFE0E0E0FFE9E9
      E9FFA9A9A9FD9E9E9EB0949494429F9F9F1A969696808E8E8ED5B9B9B9F8E9E9
      E9FFE2E2E2FFE0E0E0FFE3E3E3FFE5E5E5FFE2E2E2FFDFDFDFFFDFDFDFFFE2E2
      E2FFE8E8E8FFD8D8D8FF989898B4FFFFFF00FFFFFF00FFFFFF00BEBEBED8E9E9
      E9FFE3E3E3FFE1E1E1FFE2E2E2FFE4E4E4FFE2E2E2FFDFDFDFFFE0E0E0FFDFDF
      DFFFDEDEDEFFE7E7E7FF9A9A9AF5FFFFFF00FFFFFF00FFFFFF00B7B7B787DADA
      DAFFE7E7E7FFE4E4E4FFDADADAFFDCDCDCFFE5E5E5FFE1E1E1FFD7D7D7FFD9D9
      D9FFE4E4E4FFD3D3D3FF949494B8FFFFFF00FFFFFF00FFFFFF00ACACAC1CB4B4
      B491BABABAE2B7B7B7E3B1B1B1B8ADADADBBA3A3A3EA959595E9999999C39999
      99CC979797F5919191B88E8E8E4AFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F9DB
      C423F8DAC27DF7D8C0DBF6D7BEF3F4D5BCF3F3D3B9DBF1D1B77DF0CFB423FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F6D8BF53F5D6
      BDE6F9E9DCFFF6E8DDFFF3E5DAFFF3E5DAFFF5E7DCFFF5E4D6FFEBC8ACE6E9C6
      A953FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F4D4BB53F2D2B8F4F7EA
      DFFFEEDED0FFE3C1A7FFD8AE89FFD7AC86FFDDBB9CFFEBD6C7FFF3E6D9FFE3BE
      9FF4E1BB9C53FFFFFF00FFFFFF00FFFFFF00F1D0B522EFCEB3E5F6E9DDFFECD8
      C6FFD7AC81FFDCBB9AFFF6ECE3FFF5ECE2FFE4C8AEFFD2A77BFFE6CEBAFFF1E2
      D5FFDBB391E5D9B08E22FFFFFF00FFFFFF00EBC9AD7EF4E3D4FFEFDCCDFFD5A8
      7EFFD0A077FFFBF8F5FFFCF8F5FFFCF8F5FFFBF8F5FFD1A881FFCFA47BFFEAD5
      C3FFEAD4C2FFD2A7837EFFFFFF00FFFFFF00E6C1A3DBF3E5D9FFDFBB9EFFCFA0
      75FFCD9E72FFF5EBE3FFE4CBB4FFE7D3BFFFFBF8F6FFE5D3BFFFC4986BFFD6B4
      91FFEEE0D2FFCC9E78DBFFFFFF00FFFFFF00E0B999F6F2E5DAFFD1A67EFFCC9D
      71FFC79A6CFFC5986BFFE2CCB6FFF8F3EEFFF6EEE8FFD9BDA1FFC29468FFC59B
      71FFF0E2D6FFC5956CF6FFFFFF00FFFFFF00D9B08FF6F2E4D9FFD1A57AFFC599
      6BFFC4976AFFC49669FFFAF6F2FFF3EAE1FFC2956DFFBE8F65FFBE8F64FFC095
      6DFFEFE3D5FFBF8C61F6FFFFFF00FFFFFF00D3A784DBEFE1D3FFD9B595FFC798
      6CFFC39569FFC19367FFBF9066FFBF9066FFBB8B63FFB98A63FFB88A62FFCBA7
      86FFEADCCCFFB88357DBFFFFFF00FFFFFF00CC9E787EE4CCB9FFEAD6C5FFC799
      71FFBF9066FFBF9066FFF7F1ECFFF6F0EAFFB7895FFFB7895FFFB58963FFE2CE
      BBFFD9BDA6FFB27B4D7EFFFFFF00FFFFFF00C6956D22C3926AE5EAD8C9FFE3CD
      BAFFC0946BFFBA8C62FFCFB094FFCFB094FFB7895FFFB28761FFDAC0AAFFE4D1
      C0FFAE7546E5AD734322FFFFFF00FFFFFF00FFFFFF00BD895F53BB875BF4E7D5
      C4FFE5D2BFFFC9A685FFB88E67FFB68A65FFC5A180FFE0CCBAFFE3D0BEFFAB70
      40F4A96E3D53FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00B57E5153B37C
      4EE6D7BBA3FFE9DACAFFECE0D1FFECE0D1FFE8D8C8FFD3B59CFFA76C3AE6A66A
      3853FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00AD74
      4423AC72417DAA703FDBA86D3CF3A76B3AF3A56937DBA468357DA3663323FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00008AFF85008AFFEE008AFF85008AFF7F008A
      FFE8008AFF8B008AFFF4008AFFDA008AFFCB008AFFAB008AFF42008AFF66008A
      FF66008AFF6E008AFFD7008AFF6E008AFFEEEEF7FFFF44A9FFFF008AFFE8E8F4
      FFFF44A9FFFFF4FAFFFFDAEEFFFFCBE7FFFFABD8FFFF0C90FFFF66B9FFFF66B9
      FFFF008AFFD7D7EDFFFF1895FFFF008AFFF6F6FBFFFFEEF7FFFF008AFFF4F4FA
      FFFF77C1FFFFE8F4FFFF008AFFE80C90FFFFEDF7FFFF78C1FFFFBBE0FFFFBBE0
      FFFF56B1FFFFE3F2FFFF008AFFE3008AFFF4F4FAFFFF99D0FFFF77C1FFFFF4FA
      FFFF77C1FFFFF9FCFFFFDAEEFFFFC7E5FFFF9FD3FFFFE4F3FFFFDCEFFFFFE3F2
      FFFFCDE8FFFFBADFFFFF008AFFBA008AFFF4F4FAFFFF008AFFF4EEF7FFFFF4FA
      FFFF77C1FFFFE8F4FFFF008AFFE8008AFFC7038BFFFFF9FCFFFFB7DEFFFFAAD8
      FFFFFBFDFFFF279CFFFF008AFF51008AFFF4F4FAFFFF008AFFF477C1FFFFFFFF
      FFFF77C1FFFFF9FCFFFFDAEEFFFFC7E5FFFF008AFFF6F6FBFFFF98D0FFFF6FBD
      FFFFFFFFFFFF008AFFFFFFFFFF00008AFF8B008AFFF4008AFF8B008AFF96008A
      FFFF008AFF96008AFFF9008AFFDA008AFFC7008AFF8D008AFFF6008AFF98008A
      FF96008AFFFF008AFF96FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF0067C673C965C270C4FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF0066C572D17ECA88FF7BC885FF5DB868CFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0068C7
      740666C472DB7CCA87FF9ED6A7FF9CD4A5FF73C07DFF55AC5ED950A65906FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0065C3
      71BB62BF6EF779C683FF9AD4A3FF98D3A1FF7DC386FF4FA458F74A9E53B8FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF005BB465FF96D29FFF94D09CFF5DAC65FF499C5238FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF0056AD5FFF93CF9AFF90CE98FF489A50FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF0050A659FF8ECC95FF8BCB93FF42924AFFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF004A9E53FF8ACA91FF87C98EFF3C8A43FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF0045954CFF85C78CFF82C689FF36823DFFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF003F8D46FF81C587FF7EC385FF317A36FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF0039853FFF7DC282FF7AC180FF2B7230FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00337D39FF79C07EFF76BF7CFF266B2BFFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF002D7533FF74BD7AFF72BD78FF226526FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00286E2DFF256929FF216425FF1E6022FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF0068C774FF67C673FF65C270FF62BE6DFFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF0066C572FFA2D8ABFFA0D7A9FF5DB868FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF0063C06FFF9ED6A7FF9CD4A5FF59B263FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF005FBB6AFF9AD4A3FF98D3A1FF53AA5DFFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF005BB465FF96D29FFF94D09CFF4EA257FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF0056AD5FFF93CF9AFF90CE98FF489A50FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF0050A659FF8ECC95FF8BCB93FF42924AFFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF004A9E53FF8ACA91FF87C98EFF3C8A43FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF0045954CFF85C78CFF82C689FF36823DFFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF0043944B384E9A55FF81C587FF7EC385FF317A36FFFFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF004292
      4AB83D8C45F765AD6CFF7DC282FF7AC180FF4B9250FF276D2CF7246828BBFFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003C8A
      430638833ED9519957FF79C07EFF76BF7CFF468D4BFF236627DB20622306FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF002D7533CF488F4DFF458C4AFF226526D1FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00256929C4216425C9FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
      FF00FFFFFF00FFFFFF00FFFFFF00
    }
  end
  object mnuMain: TMainMenu
    Images = imlMain
    left = 24
    top = 104
    object miApplication: TMenuItem
      Caption = 'Paleo'
      Visible = False
      object miAppAbout: TMenuItem
        Caption = 'About Paleo'
        OnClick = miAppAboutClick
      end
      object MenuItem1: TMenuItem
        Caption = '-'
      end
      object miAppPreferences: TMenuItem
        Caption = 'Preferences...'
        ShortCut = 4284
      end
    end
  end
end

Added src/m_main.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
unit M_Main;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Controls, Dialogs, Menus,
  U_Fossil;

const
  cEditorFontNames: array[1..5] of string = ('Consolas', 'Monaco', 'Inconsolata', 'Monospace', 'Courier New');

  // TODO: add a DirWatcher here, and handle change notification for the given repo's files?
  //       Or should we set that up in TFossilCheckout?

type

  { TmodMain }

  TmodMain = class(TDataModule)
    imlFileStatuses: TImageList;
    imlMain: TImageList;
    MenuItem1: TMenuItem;
    miAppPreferences: TMenuItem;
    miAppAbout: TMenuItem;
    miApplication: TMenuItem;
    mnuMain: TMainMenu;
    procedure miAppAboutClick(Sender: TObject);
  private
    { private declarations }
    FFossil: TFossil;
    FCheckout: TFossilCheckout;
    FBusyLevel: Integer;
    function GetCheckout: TFossilCheckout;
    function GetFossil: TFossil;
  public
    { public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor  Destroy; override;

    function ImageIndexFromStatuses(const Statuses: TFileStatuses): Integer;
    function ImageIndexFromType(const FileExt: string): Integer;

    function SetBusy(const Busy: Boolean): Boolean;

    property Fossil: TFossil            read GetFossil;
    property Checkout: TFossilCheckout  read GetCheckout;
  end;

var
  modMain: TmodMain;

implementation
uses
  LazFileUtils, Forms,
  f_opencheckout;

{$R *.lfm}

{ TmodMain }

constructor TmodMain.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  {$IFDEF DARWIN}
  miApplication.Caption := #$EF#$A3#$BF; // The Apple logo
  miApplication.Visible := True;
  {$ENDIF}
end;

destructor TmodMain.Destroy;
begin
  FCheckout.Free;
  FFossil.Free;
  inherited Destroy;
end;

function TmodMain.GetFossil: TFossil;
var
  Dlg: TOpenDialog;
begin
  if FFossil = nil then begin
    try
      FFossil := TFossil.Create(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'fossil');
    except
      FFossil := nil;
    end;
    if FFossil = nil then begin
      try
        FFossil := TFossil.Create('fossil');
      except
        FFossil := nil;
      end;
    end;
    if FFossil = nil then begin
      Dlg := TOpenDialog.Create(Self);
      try
        Dlg.Title := 'Select fossil executable';
        {$IFDEF MSWINDOWS}
        Dlg.DefaultExt := 'exe';
        Dlg.FileName := 'fossil.exe';
        Dlg.Filter := 'Fossil executable|fossil.exe|All executables|*.exe|All files|*';
        {$ELSE}
        Dlg.FileName := 'fossil';
        Dlg.Filter := 'Fossil executable|fossil|All files|*';
        {$ENDIF}
        Dlg.FilterIndex := 1;
        Dlg.Options := [ofEnableSizing, ofFileMustExist];
        if Dlg.Execute then begin
          FFossil := TFossil.Create(Dlg.FileName);
        end else begin
          Halt(1);
        end;
      finally
        Dlg.Free;
      end;
    end;
  end;
  Result := FFossil;
end;

procedure TmodMain.miAppAboutClick(Sender: TObject);
begin
  MessageDlg(Application.Title, FFossil.VersionInfo, mtInformation, [mbOK], 'Version');
end;

function TmodMain.GetCheckout: TFossilCheckout;
var
  frm: TfrmOpenCheckout;
  Dlg: TSelectDirectoryDialog;
begin
  if not Assigned(FCheckout) and not Application.Terminated then begin
    // ask for checkout directory
    if (ParamCount >= 1) and DirectoryExistsUTF8(ParamStr(1)) then begin
      FCheckout := TFossilCheckout.Create(Fossil, ParamStr(1));
    end;

    if not Assigned(FCheckout) then begin
      frm := TfrmOpenCheckout.Create(Self);
      try
        if frm.Execute then begin
          FCheckout := TFossilCheckout.Create(Fossil, frm.SelectedDir);
        end;
      finally
        frm.Free;
      end;
    end;

    if not Assigned(FCheckout) then begin
      Dlg := TSelectDirectoryDialog.Create(Self);
      try
        Dlg.InitialDir := GetCurrentDir;
        Dlg.Title := 'Select checkout directory';
        Dlg.Options := [ofEnableSizing, ofPathMustExist];
        Dlg.Filter := 'All files|*';
        Dlg.FilterIndex := 1;
        if Dlg.Execute then begin
          FCheckout := TFossilCheckout.Create(Fossil, Dlg.FileName);
        end else begin
          Application.Terminate;
        end;
      finally
        Dlg.Free;
      end;
    end;
  end;
  Result := FCheckout;
end;

function TmodMain.ImageIndexFromStatuses(const Statuses: TFileStatuses): Integer;
const
  Prio: array[TFileStatus] of TFileStatus = (fsConflict, fsMerged, fsMissing, fsIgnored, fsUntracked, fsAdded, fsModified, fsDeleted, fsUnchanged);
var
  i, Status: TFileStatus;
begin
  for i := Low(TFileStatus) to High(TFileStatus) do begin
    Status := Prio[i];
    if Status in Statuses then begin
      Result := Ord(Status);
      Exit;
    end;
  end;
end;

function TmodMain.ImageIndexFromType(const FileExt: string): Integer;
begin
  Result := -1;
  // TODO: check if we've already got it; if so, return that
  // TODO: if not, retrieve the file type's icon
  // TODO: Windows: SHGetFileIcon
  // TODO: Linux?
  // TODO: MacOS?
  // TODO: add it to imlFileStatuses
end;

function TmodMain.SetBusy(const Busy: Boolean): Boolean;
begin
  if Busy then begin
    if FBusyLevel = 0 then begin
      Screen.Cursor := crHourGlass;
      // TODO: indicate busyness elsewhere?
    end;
    Inc(FBusyLevel);
  end else begin
    Dec(FBusyLevel);
    if FBusyLevel < 0 then
      FBusyLevel := 0;
    if FBusyLevel = 0 then begin
      Screen.Cursor := crDefault;
      // TODO: remove other indicators of busyness?
    end;
  end;
  Result := FBusyLevel > 0;
end {TmodMain.SetBusy};

end.

Added src/prj/laz/paleo.ico.

cannot compute difference between binary files

Added src/prj/laz/paleo.lpi.



























































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
  <ProjectOptions>
    <Version Value="10"/>
    <General>
      <SessionStorage Value="InProjectDir"/>
      <MainUnit Value="0"/>
      <Title Value="Paleo"/>
      <ResourceType Value="res"/>
      <UseXPManifest Value="True"/>
      <XPManifest>
        <DpiAware Value="True"/>
      </XPManifest>
      <Icon Value="0"/>
    </General>
    <i18n>
      <EnableI18N LFM="False"/>
    </i18n>
    <VersionInfo>
      <UseVersionInfo Value="True"/>
      <MinorVersionNr Value="2"/>
      <Language Value="0809"/>
      <CharSet Value="04B0"/>
      <StringTable CompanyName="Voronwë" FileDescription="Fossil UI" InternalName="paleo" LegalCopyright="© Martijn Coppoolse" OriginalFilename="paleo.exe" ProductName="Stylemys" ProductVersion="1.0.0.0"/>
    </VersionInfo>
    <BuildModes Count="3">
      <Item1 Name="Default" Default="True"/>
      <Item2 Name="Debug">
        <CompilerOptions>
          <Version Value="11"/>
          <Target>
            <Filename Value="../../../out/$(TargetCPU)-$(TargetOS)/paleo"/>
          </Target>
          <SearchPaths>
            <IncludeFiles Value="$(ProjOutDir)"/>
            <OtherUnitFiles Value="../..;../../lib"/>
            <UnitOutputDirectory Value="../../../out/lib/$(TargetCPU)-$(TargetOS)"/>
          </SearchPaths>
          <Parsing>
            <SyntaxOptions>
              <IncludeAssertionCode Value="True"/>
            </SyntaxOptions>
          </Parsing>
          <CodeGeneration>
            <Checks>
              <IOChecks Value="True"/>
              <RangeChecks Value="True"/>
              <OverflowChecks Value="True"/>
              <StackChecks Value="True"/>
            </Checks>
          </CodeGeneration>
          <Linking>
            <Debugging>
              <DebugInfoType Value="dsDwarf2Set"/>
              <UseHeaptrc Value="True"/>
              <UseExternalDbgSyms Value="True"/>
            </Debugging>
            <Options>
              <Win32>
                <GraphicApplication Value="True"/>
              </Win32>
            </Options>
          </Linking>
          <Other>
            <CompilerMessages>
              <IgnoredMessages idx5024="True"/>
            </CompilerMessages>
          </Other>
        </CompilerOptions>
      </Item2>
      <Item3 Name="Release">
        <CompilerOptions>
          <Version Value="11"/>
          <Target>
            <Filename Value="../../../out/$(TargetCPU)-$(TargetOS)/paleo"/>
          </Target>
          <SearchPaths>
            <IncludeFiles Value="$(ProjOutDir)"/>
            <OtherUnitFiles Value="../.."/>
            <UnitOutputDirectory Value="../../../out/lib/$(TargetCPU)-$(TargetOS)"/>
          </SearchPaths>
          <CodeGeneration>
            <SmartLinkUnit Value="True"/>
            <Optimizations>
              <OptimizationLevel Value="3"/>
            </Optimizations>
            <SmallerCode Value="True"/>
          </CodeGeneration>
          <Linking>
            <Debugging>
              <GenerateDebugInfo Value="False"/>
            </Debugging>
            <LinkSmart Value="True"/>
            <Options>
              <Win32>
                <GraphicApplication Value="True"/>
              </Win32>
            </Options>
          </Linking>
          <Other>
            <ExecuteAfter>
              <Command Value="&quot;C:/MC/Run/Util/Compression/UPX/upx.exe&quot; --best -vk C:/MC/Code/Projects/Voronwe/GUI/FrontEnd/LazPaleo/out/i386-win32/paleo.exe"/>
              <CompileReasons Compile="False" Run="False"/>
            </ExecuteAfter>
          </Other>
        </CompilerOptions>
      </Item3>
    </BuildModes>
    <PublishOptions>
      <Version Value="2"/>
    </PublishOptions>
    <RunParams>
      <local>
        <FormatVersion Value="1"/>
      </local>
    </RunParams>
    <RequiredPackages Count="4">
      <Item1>
        <PackageName Value="LazControls"/>
      </Item1>
      <Item2>
        <PackageName Value="IDEIntf"/>
      </Item2>
      <Item3>
        <PackageName Value="SynEdit"/>
      </Item3>
      <Item4>
        <PackageName Value="LCL"/>
      </Item4>
    </RequiredPackages>
    <Units Count="13">
      <Unit0>
        <Filename Value="paleo.lpr"/>
        <IsPartOfProject Value="True"/>
      </Unit0>
      <Unit1>
        <Filename Value="../../f_commit.pas"/>
        <IsPartOfProject Value="True"/>
        <ComponentName Value="frmCommit"/>
        <HasResources Value="True"/>
        <ResourceBaseClass Value="Form"/>
        <UnitName Value="F_Commit"/>
      </Unit1>
      <Unit2>
        <Filename Value="../../a_filelist.pas"/>
        <IsPartOfProject Value="True"/>
        <ComponentName Value="fraFileList"/>
        <HasResources Value="True"/>
        <ResourceBaseClass Value="Frame"/>
        <UnitName Value="A_FileList"/>
      </Unit2>
      <Unit3>
        <Filename Value="../../m_main.pas"/>
        <IsPartOfProject Value="True"/>
        <ComponentName Value="modMain"/>
        <HasResources Value="True"/>
        <ResourceBaseClass Value="DataModule"/>
        <UnitName Value="M_Main"/>
      </Unit3>
      <Unit4>
        <Filename Value="../../a_fileversioninfo.pas"/>
        <IsPartOfProject Value="True"/>
        <ComponentName Value="fraFileVersionInfo"/>
        <HasResources Value="True"/>
        <ResourceBaseClass Value="Frame"/>
        <UnitName Value="A_FileVersionInfo"/>
      </Unit4>
      <Unit5>
        <Filename Value="../../u_fossil.pas"/>
        <IsPartOfProject Value="True"/>
        <UnitName Value="U_Fossil"/>
      </Unit5>
      <Unit6>
        <Filename Value="../../a_commitmessage.pas"/>
        <IsPartOfProject Value="True"/>
        <ComponentName Value="frameCommitMessage"/>
        <HasResources Value="True"/>
        <ResourceBaseClass Value="Frame"/>
        <UnitName Value="A_CommitMessage"/>
      </Unit6>
      <Unit7>
        <Filename Value="../../m_highlighters.pas"/>
        <IsPartOfProject Value="True"/>
        <ComponentName Value="modHighlighters"/>
        <HasResources Value="True"/>
        <ResourceBaseClass Value="DataModule"/>
        <UnitName Value="M_Highlighters"/>
      </Unit7>
      <Unit8>
        <Filename Value="../../synhighlighterdiffwide.pas"/>
        <IsPartOfProject Value="True"/>
        <UnitName Value="SynHighlighterDiffWide"/>
      </Unit8>
      <Unit9>
        <Filename Value="../../synhighlighterannotate.pas"/>
        <IsPartOfProject Value="True"/>
        <UnitName Value="SynHighlighterAnnotate"/>
      </Unit9>
      <Unit10>
        <Filename Value="../../lib/dirwatch.pas"/>
        <IsPartOfProject Value="True"/>
        <UnitName Value="DirWatch"/>
      </Unit10>
      <Unit11>
        <Filename Value="../../f_opencheckout.pas"/>
        <IsPartOfProject Value="True"/>
        <ComponentName Value="frmOpenCheckout"/>
        <HasResources Value="True"/>
        <ResourceBaseClass Value="Form"/>
        <UnitName Value="F_OpenCheckout"/>
      </Unit11>
      <Unit12>
        <Filename Value="../../f_newbranch.pas"/>
        <IsPartOfProject Value="True"/>
        <ComponentName Value="frmNewBranch"/>
        <HasResources Value="True"/>
        <ResourceBaseClass Value="Form"/>
        <UnitName Value="F_NewBranch"/>
      </Unit12>
    </Units>
  </ProjectOptions>
  <CompilerOptions>
    <Version Value="11"/>
    <Target>
      <Filename Value="../../../out/$(TargetCPU)-$(TargetOS)/paleo"/>
    </Target>
    <SearchPaths>
      <IncludeFiles Value="$(ProjOutDir)"/>
      <OtherUnitFiles Value="../.."/>
      <UnitOutputDirectory Value="../../../out/lib/$(TargetCPU)-$(TargetOS)"/>
    </SearchPaths>
    <Linking>
      <Options>
        <Win32>
          <GraphicApplication Value="True"/>
        </Win32>
      </Options>
    </Linking>
  </CompilerOptions>
  <Debugging>
    <Exceptions Count="3">
      <Item1>
        <Name Value="EAbort"/>
      </Item1>
      <Item2>
        <Name Value="ECodetoolError"/>
      </Item2>
      <Item3>
        <Name Value="EFOpenError"/>
      </Item3>
    </Exceptions>
  </Debugging>
</CONFIG>

Added src/prj/laz/paleo.lpr.





























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
program paleo;

{$mode objfpc}{$H+}

uses
  {$DEFINE UseCThreads}
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms, SysUtils,
  M_Main,
  F_Commit,
  A_FileList,
  A_FileVersionInfo,
  A_CommitMessage,
  M_Highlighters;

{$R *.res}

(*
    TODO

    // In CommitMessage, get list of tickets to insert => [<tkt_uuid>|Subject]?
    //  also mark those tickets as closed;
    //  fossil ticket set '+TicketUUID+' status '+NewStatus+' icomment '+ TicketQuoted(NewStatus + ' by commit [' + GetParentUUID + ']' --quote;

    TFossilStashList
    TFossilStash

    TFossilTickets
    TFossilTicket
    TFossilTicketChanges

*)

begin
  Application.Title:='Paleo';
  RequireDerivedFormResource := True;
  Application.Initialize;
  Application.CreateForm(TmodMain, modMain);
  Application.CreateForm(TfrmCommit, frmCommit);
  Application.CreateForm(TmodHighlighters, modHighlighters);
  Application.Run;
end.

Added src/prj/laz/paleo.res.

cannot compute difference between binary files

Added src/synhighlighterannotate.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
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
unit SynHighlighterAnnotate;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Graphics,
  fgl,
  SynEditHighlighter;

type
  TSynHighlighterAttributesList = specialize TFPGList<TSynHighlighterAttributes>;

  TAnnFileVersion = record
    Version: Integer;
    Date: TDateTime;
    RevisionUUID: string;
    FileUUID: string;
  end;
  PAnnFileVersion = ^TAnnFileVersion;

  { TSynAnnotateSyn }

  TSynAnnotateSyn = class(TSynCustomHighlighter)
  private
    FColorAncient: TColor;
    FColorRecent: TColor;
    FSubHL: TSynCustomHighlighter;

    FStyles: TSynHighlighterAttributesList;
    FVersions: array of TAnnFileVersion;
    FRevisions: TStringList;
    FColorsAssigned: Boolean;

    FLineText: string;
    FLineIndex: Integer;

    FTokenPos: Integer;
    FTokenLength: Integer;
    FTokenStyle: Integer; // index in FStyles
    FDelegate: Boolean;

    procedure AssignColors;

    procedure SetColorAncient(AValue: TColor);
    procedure SetColorRecent(AValue: TColor);
  public
    procedure SetLine(const NewValue: String;
                  LineNumber:Integer // 0 based
                  ); override;
    procedure Next; override;
    function GetEol: Boolean; override;
    procedure GetTokenEx(out TokenStart: PChar; out TokenLength: integer); override;
    function GetTokenAttribute: TSynHighlighterAttributes; override;
    function GetToken: String; override;
    function GetTokenPos: Integer; override; // 0-based
    function GetTokenKind: integer; override;

    function GetDefaultAttribute(Index: integer): TSynHighlighterAttributes; override;
    function GetEndOfLineAttribute: TSynHighlighterAttributes; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    function SetVersionsFromLog(const AnnotateLog: string): Integer;
  published
    property ColorAncient: TColor               read FColorAncient  write SetColorAncient;
    property ColorRecent: TColor                read FColorRecent   write SetColorRecent;
    property Highlighter: TSynCustomHighlighter read FSubHL         write FSubHL;
  end;


implementation
uses
  Math;

{ TSynAnnotateSyn }

constructor TSynAnnotateSyn.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FColorAncient := RGBToColor(128, 128, 64);
  FColorRecent := RGBToColor(128, 255, 64);
  FStyles := TSynHighlighterAttributesList.Create;
  FStyles.Add(AddSpecialAttribute('regular', 'regular'));

  SetLength(FVersions, 0);
  FRevisions := TStringList.Create;
  FRevisions.CaseSensitive := True;
end;

destructor TSynAnnotateSyn.Destroy;
begin
  FRevisions.Free;
  FStyles.Free;
  inherited Destroy;
end;

function TSynAnnotateSyn.SetVersionsFromLog(const AnnotateLog: string): Integer;
var
  Lines, Fields: TStringList;
  Line: string;
  FV: PAnnFileVersion;
  Index: Integer;
begin
  // ASSERTION: This only gets called once in the lifetime of this instance
  Assert(FRevisions.Count = 0, 'TSynAnnotateSyn currently does not support re-use');

  FRevisions.BeginUpdate;
  try
    FRevisions.Sorted := False;
    FRevisions.Clear;
    Lines := TStringList.Create;
    Fields := TStringList.Create;
    try
      Fields.StrictDelimiter := False;
      Fields.Delimiter := ' ';
      Lines.Sorted := False;
      Lines.Text := AnnotateLog;
      SetLength(FVersions, Lines.Count);
      Result := 0;

      for Line in Lines do begin
        Fields.DelimitedText := Line;
        FV := @FVersions[Result];
        if not TryStrToInt(StringReplace(Fields[1], ':', '', []), FV^.Version) then
          Continue;
        if not TryStrToDate(Fields[2], FV^.Date, 'yyyy-MM-dd', '-') then
          Continue;
        FV^.RevisionUUID := Fields[3];
        if Fields[4] <> 'file' then
          Continue;
        FV^.FileUUID := Fields[5];

        Index := FStyles.Add(AddSpecialAttribute(FV^.RevisionUUID, FV^.RevisionUUID));
        Assert(Index = Result + 1);

        FRevisions.AddObject(FV^.RevisionUUID, FStyles.Items[Index]);

        // TODO?

        Inc(Result);
      end {for};
      if Result < Length(FVersions) then
        SetLength(FVersions, Result);
    finally
      Fields.Free;
      Lines.Free;
    end;
  finally
    FRevisions.Sorted := True;
    FRevisions.EndUpdate;
  end;

  FColorsAssigned := False;
end {TSynAnnotateSyn.SetVersionsFromLog};

procedure TSynAnnotateSyn.AssignColors;
var
  R1, G1, B1, R2, G2, B2: Byte;
  R, G, B: Byte;
  Span: Integer;
  i: Integer;
  Fraction, InvFraction: Double;
begin
  Span := High(FVersions);
  if Span > 0 then begin
    RedGreenBlue(FColorRecent, R1, G1, B1);
    RedGreenBlue(FColorAncient, R2, G2, B2);
    for i := Low(FVersions) to High(FVersions) do begin
      Fraction := i / Span;
      InvFraction := 1 - Fraction;
      R := Trunc((R1 * InvFraction) + (R2 * Fraction));
      G := Trunc((G1 * InvFraction) + (G2 * Fraction));
      B := Trunc((B1 * InvFraction) + (B2 * Fraction));
      FStyles[i + 1].Background := RGBToColor(R, G, B);
    end;
   end else begin
     FStyles[1].Background := FColorRecent;
  end;
  FColorsAssigned := True;
end;

procedure TSynAnnotateSyn.SetColorAncient(AValue: TColor);
begin
  if FColorAncient = AValue then Exit;
  FColorAncient := AValue;
  FColorsAssigned := False;
end;

procedure TSynAnnotateSyn.SetColorRecent(AValue: TColor);
begin
  if FColorRecent = AValue then Exit;
  FColorRecent := AValue;
  FColorsAssigned := False;
end;

procedure TSynAnnotateSyn.SetLine(const NewValue: String; LineNumber: Integer);
var
  UUID: string;
  Attr: TSynHighlighterAttributes;
  i: Integer;
begin
  inherited SetLine(NewValue, LineNumber);
  FLineText := NewValue;
  FLineIndex := LineNumber;

  if not FColorsAssigned then begin
    // assign colors to revisions or dates
    AssignColors;
  end;

  FTokenPos := 1;
  FTokenLength := Pos(':', FLineText) + 1;

  // determine which revision and date we're dealing with
  UUID := Copy(FLineText, 1, 10);
  // select the appropriate styler
  Attr := TSynHighlighterAttributes(FRevisions.Objects[FRevisions.IndexOf(UUID)]);
  FTokenStyle := 0;
  for i := 0 to FStyles.Count - 1 do begin
    if FStyles[i] = Attr then begin
      FTokenStyle := i;
      Break;
    end;
  end;

  FDelegate := False;
end;

procedure TSynAnnotateSyn.Next;
begin
  if not Assigned(FSubHL) then begin
    FTokenPos := FTokenPos + Max(1, FTokenLength);
    FTokenLength := Max(0, Length(FLineText) - FTokenPos + 1);
    // switch to the regular styler for the textual contents
    FTokenStyle := 0;
  end else if not FDelegate then begin
    // notify the subhighlighter
    FDelegate := True;
    try
      FSubHL.SetLine(Copy(FLineText, FTokenLength + 1, Length(FLineText)), FLineIndex);
    except
      // In case of boo-boo, immediately disable the sub-highlighter, and fall back to our default behaviour
      FSubHL := nil;
      FDelegate := False;
      Next;
    end;
  end else begin
    // delegate to the subhighlighter
    try
      FSubHL.Next;
    except
      // In case of boo-boo, immediately disable the sub-highlighter, and fall back to our default behaviour
      FSubHL := nil;
      FDelegate := False;
      Next;
    end;
  end;
end;

function TSynAnnotateSyn.GetEol: Boolean;
begin
  if not FDelegate then begin
    Result := FTokenPos > Length(FLineText);
  end else begin
    Result := FSubHL.GetEol;
  end;
end;

procedure TSynAnnotateSyn.GetTokenEx(out TokenStart: PChar; out
  TokenLength: integer);
begin
  if not FDelegate then begin
    TokenStart := @FLineText[FTokenPos];
    TokenLength := FTokenLength;
  end else begin
    FSubHL.GetTokenEx(TokenStart, TokenLength);
  end;
end;

function TSynAnnotateSyn.GetTokenAttribute: TSynHighlighterAttributes;
begin
  if not FDelegate then begin
    Result := FStyles[FTokenStyle];
  end else begin
    Result := FSubHL.GetTokenAttribute;
  end;
end;

function TSynAnnotateSyn.GetToken: String;
begin
  if not FDelegate then begin
    Result := Copy(FLineText, FTokenPos, FTokenLength);
  end else begin
    Result := FSubHL.GetToken;
  end;
end;

function TSynAnnotateSyn.GetTokenPos: Integer;
begin
  //  return the token position (0-based)
  if not FDelegate then begin
    Result := FTokenPos - 1;
  end else begin
    Result := FSubHL.GetTokenPos + Pos(':', FLineText);
  end;
end;

function TSynAnnotateSyn.GetTokenKind: integer;
begin
  if not FDelegate then begin
    Result := FTokenStyle;
  end else begin
    Result := FSubHL.GetTokenKind + FStyles.Count; // TODO: do we need to increment with FStyles.Count?
  end;
end;

function TSynAnnotateSyn.GetDefaultAttribute(Index: integer
  ): TSynHighlighterAttributes;
begin
  case Index of
    SYN_ATTR_WHITESPACE:
      Result := FStyles[0];
    else
      Result := nil;
  end;
end;

function TSynAnnotateSyn.GetEndOfLineAttribute: TSynHighlighterAttributes;
begin
  if not FDelegate then begin
    Result := inherited GetEndOfLineAttribute;
  end else begin
    Result := FSubHL.GetEndOfLineAttribute;
  end;
end;

end.

Added src/synhighlighterdiffwide.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
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
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
unit SynHighlighterDiffWide;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Graphics,
  SynEditHighlighter;

type

  THighlightStyle = (hsUnknown, hsHeader, hsDivider, hsLineNumber, hsSpace,
                      hsMarkerDeleted, hsMarkerEdited, hsMarkerAdded,
                      hsContent, hsContentDeleted, hsContentEdited, hsContentAdded);

  THSColDef = record
    ByteStart: Integer;
    CharStart: Integer;
    ByteLength: Integer;
    CharLength: Integer;
    Style: THighlightStyle;
  end;
  PHSColDef = ^THSColDef;

  { TSynDiffWideSyn }

  TSynDiffWideSyn = class(TSynCustomHighlighter)
  private
    FStyles: array[THighlightStyle] of TSynHighlighterAttributes;

    FLineText: string;
    FLineIndex: Integer;
    FLineType: Char;
    FFullWidth: Integer;
    FContentWidth: Integer;
    FColDefs: array[0..8] of THSColDef;
    FSubColDefs: array[Boolean,0..3] of THSColDef;

    FColIndex: Integer;
    FSubColIndex: Integer;

    FTokenPos: Integer;
    FTokenLength: Integer;
    FTokenStyle: THighlightStyle;

    procedure CompareContent(const Left, Right: String);

    function GetStyleAttrib(AIndex: THighlightStyle): TSynHighlighterAttributes;
    procedure SetStyleAttrib(AIndex: THighlightStyle; AValue: TSynHighlighterAttributes);
  protected
    procedure SetToken(const StartPos, ALength: Integer; const Style: THighlightStyle); overload;
    procedure SetToken(const ColDef: PHSColDef); overload; inline;
  public
    procedure SetLine(const NewValue: String;
                      LineNumber:Integer // 0 based
                      ); override;
    procedure Next; override;
    function GetEol: Boolean; override;
    procedure GetTokenEx(out TokenStart: PChar; out TokenLength: integer); override;
    function GetTokenAttribute: TSynHighlighterAttributes; override;
    function GetToken: String; override;
    function GetTokenPos: Integer; override; // 0-based
    function GetTokenKind: integer; override;

    function GetDefaultAttribute(Index: integer): TSynHighlighterAttributes; override;
    function GetEndOfLineAttribute: TSynHighlighterAttributes; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property UnknownAttrib: TSynHighlighterAttributes         index hsUnknown         read GetStyleAttrib write SetStyleAttrib;
    property HeaderAttrib: TSynHighlighterAttributes          index hsHeader          read GetStyleAttrib write SetStyleAttrib;
    property DividerAttrib: TSynHighlighterAttributes         index hsDivider         read GetStyleAttrib write SetStyleAttrib;
    property LineNumberAttrib: TSynHighlighterAttributes      index hsLineNumber      read GetStyleAttrib write SetStyleAttrib;
    property SpaceAttrib: TSynHighlighterAttributes           index hsSpace           read GetStyleAttrib write SetStyleAttrib;
    property MarkerDeletedAttrib: TSynHighlighterAttributes   index hsMarkerDeleted   read GetStyleAttrib write SetStyleAttrib;
    property MarkerEditedAttrib: TSynHighlighterAttributes    index hsMarkerEdited    read GetStyleAttrib write SetStyleAttrib;
    property MarkerAddedAttrib: TSynHighlighterAttributes     index hsMarkerAdded     read GetStyleAttrib write SetStyleAttrib;
    property ContentAttrib: TSynHighlighterAttributes         index hsContent         read GetStyleAttrib write SetStyleAttrib;
    property ContentDeletedAttrib: TSynHighlighterAttributes  index hsContentDeleted  read GetStyleAttrib write SetStyleAttrib;
    property ContentEditedAttrib: TSynHighlighterAttributes   index hsContentEdited   read GetStyleAttrib write SetStyleAttrib;
    property ContentAddedAttrib: TSynHighlighterAttributes    index hsContentAdded    read GetStyleAttrib write SetStyleAttrib;
  end;

implementation
uses
  TypInfo, Math, StrUtils;

type TByteType = (Invalid = 0, FirstOf1, FirstOf2, FirstOf3, FirstOf4, AfterFirst);

function ByteType(const B: Byte): TByteType; inline;
begin
  // https://en.wikipedia.org/wiki/UTF-8#Description
  if      B and %10000000 = %00000000 then
    Result := FirstOf1 // single-byte character
  else if B and %11000000 = %10000000 then
    Result := AfterFirst // 2nd, 3rd, or 4th byte of multiple-byte char
  else if B and %11100000 = %11000000 then
    Result := FirstOf2 // first byte of two-byte char
  else if B and %11110000 = %11100000 then
    Result := FirstOf3 // first byte of three-byte char
  else if B and %11111000 = %11110000 then
    Result := FirstOf4 // first byte of four-byte char
  else
    Result := Invalid; // invalid UTF-8 character
end;
function ByteType(const C: Char): TByteType; inline;
begin
  Result := ByteType(Byte(C));
end;
function ByteType(const C: PChar): TByteType; inline;
begin
  Result := ByteType(Byte(C^));
end;

function FirstByteOfChar(const C: PChar): PChar;
var
  BT: TByteType;
begin
  if (C = nil) or (C^ = #0) then
    Exit(C);

  Result := C;
  BT := ByteType(Result);
  while BT = AfterFirst do begin
    Dec(Result);
    BT := ByteType(Result);
  end;
end;

function FirstByteOfChar(const S: string; const Index: Integer): Integer;
var
  BT: TByteType;
begin
  if (Index < 1) or (Index > Length(S)) then
    Exit(Index);

  Result := Index;
  BT := ByteType(S[Result]);
  while (Result > 1) and (BT = AfterFirst) do begin
    Dec(Result);
    BT := ByteType(S[Result]);
  end;
end;

function LastByteOfChar(const C: PChar): PChar;
var
  BT: TByteType;
begin
  if (C = nil) or (C^ = #0) then
    Exit(C);

  Result := C;
  BT := ByteType(C);
  while BT in [FirstOf2, FirstOf3, FirstOf4] do begin
    Inc(Result);
    BT := ByteType(Result);
  end;
end;
function LastByteOfChar(const S: string; const Index: Integer): Integer;
var
  BT: TByteType;
begin
  if (Index < 1) or (Index > Length(S)) then
    Exit(Index);

  Result := Index;
  BT := ByteType(S[Result]);
  while (Result < Length(S)) and (BT in [FirstOf2, FirstOf3, FirstOf4]) do begin
    Inc(Result);
    BT := ByteType(S[Result]);
  end;
end;


{ TSynDiffWideSyn }

constructor TSynDiffWideSyn.Create(AOwner: TComponent);
var
  ColStart: Integer;
  procedure DefineCol(const ColIndex, Length: Integer; const Style: THighlightStyle);
  begin
    if ColIndex = 0 then ColStart := 1;
    FColDefs[ColIndex].ByteStart := ColStart;
    FColDefs[ColIndex].ByteLength := Length;
    FColDefs[ColIndex].CharStart := ColStart;
    FColDefs[ColIndex].CharLength := Length;
    FColDefs[ColIndex].Style := Style;
    ColStart += Length;
  end;
var
  hs: THighlightStyle;
  Attr: TSynHighlighterAttributes;
  StyleName: string;
begin
  inherited Create(AOwner);

  FLineText := '';
  FLineIndex := -1;
  FLineType := #0;

  FTokenPos := 1;
  FTokenLength := 0;
  FTokenStyle := hsUnknown;

  FColIndex := 0;
  DefineCol(0,  5, hsLineNumber);
  DefineCol(1,  2, hsSpace);
  DefineCol(2, 50, hsContentDeleted);
  DefineCol(3,  1, hsSpace);
  DefineCol(4,  1, hsMarkerEdited);
  DefineCol(5,  1, hsSpace);
  DefineCol(6,  5, hsLineNumber);
  DefineCol(7,  2, hsSpace);
  DefineCol(8, 50, hsContentAdded);

  for hs := Low(THighlightStyle) to High(THighlightStyle) do begin
    StyleName := GetEnumName(TypeInfo(THighlightStyle), Ord(hs));
    StyleName := Copy(StyleName, 3, Length(StyleName));
    Attr := AddSpecialAttribute(StyleName, StyleName);
    FStyles[hs] := Attr;
    case hs of
      hsUnknown: begin
        Attr.Style := [fsItalic];
      end;
      hsHeader: begin
        Attr.Style := [fsBold];
      end;
      hsDivider: begin
        Attr.Style := [fsBold];
        Attr.Foreground := clBlue;
      end;
      hsSpace, hsContent: begin
      end;
      hsLineNumber: begin
        Attr.Background := clBtnFace; // clGray;
        Attr.Foreground := clBtnText; // clWhite;
      end;
      hsMarkerAdded: begin
        Attr.Foreground := clGreen;
      end;
      hsMarkerEdited: begin
        Attr.Foreground := clNavy;
      end;
      hsMarkerDeleted: begin
        Attr.Foreground := clRed;
      end;
      hsContentAdded: begin
        Attr.Background := TColor($C0FFC0);
      end;
      hsContentEdited: begin
        Attr.Background := TColor($FFC0C0);
      end;
      hsContentDeleted: begin
        Attr.Background := TColor($C0C0FF);
      end;
    end;
  end;
end;

destructor TSynDiffWideSyn.Destroy;
begin
  inherited Destroy;
end;

function TSynDiffWideSyn.GetStyleAttrib(AIndex: THighlightStyle): TSynHighlighterAttributes;
begin
  Result := FStyles[AIndex];
end;

procedure TSynDiffWideSyn.SetStyleAttrib(AIndex: THighlightStyle; AValue: TSynHighlighterAttributes);
begin
  FStyles[AIndex].Assign(AValue);
end;

procedure TSynDiffWideSyn.SetToken(const StartPos, ALength: Integer; const Style: THighlightStyle);
begin
  FTokenPos := StartPos; // FTokenPos is 1-based
  if (ALength < 0) or (StartPos + ALength > Length(FLineText)) then begin
    FTokenLength := Length(FLineText) - FTokenPos + 1
  end else begin
    FTokenLength := StartPos + ALength - FTokenPos;
  end;
  FTokenStyle := Style;
end;
procedure TSynDiffWideSyn.SetToken(const ColDef: PHSColDef);
begin
  SetToken(ColDef^.ByteStart, ColDef^.ByteLength, ColDef^.Style);
end;

procedure TSynDiffWideSyn.SetLine(const NewValue: String; LineNumber: Integer);
var
  DiffType: string;
  ContentLeft, ContentRight: string;
  i: Integer;
  ColIndex, ByteIndex, CharIndex: Integer;
  PLastChar, PCurrChar: PChar;
  BytesToSkip: Integer;
begin
  inherited SetLine(NewValue, LineNumber);
  FLineText := NewValue;
  FLineIndex := LineNumber;
  if Length(NewValue) = 0 then begin
    FLineType := #0;
    SetToken(1, -1, hsUnknown);
    Exit;
  end;
  // keep track of the type of line we're on
  FLineType := NewValue[1];
  case FLineType of
    '=': begin // header
      // set the token to the entire line, and set the styler to HeaderAttri
      SetToken(1, -1, hsHeader);

      // Determine the content width
      FFullWidth := Length(FLineText);
      FContentWidth := (Length(FLineText) - 1) div 2 - 8;
      FColDefs[2].ByteLength := FContentWidth;
      FColDefs[8].ByteLength := FContentWidth;
      // Adjust the content columns
      for i := 2 to High(FColDefs) do begin
        with FColDefs[i] do begin
          ByteStart := FColDefs[i - 1].ByteStart + FColDefs[i - 1].ByteLength;
          CharStart := ByteStart;
          CharLength := ByteLength;
        end;
      end;
    end;
    '.': begin // divider
      // set the token to the entire line, and set the styler to DividerAttri
      SetToken(1, -1, hsDivider);
    end;
    ' ', '0'..'9': begin // a diff line
      FLineType := ' ';
      // set the token to the first 5 chars, and set the styler to LineNumberAttri
      FColIndex := 0;
      SetToken(@FColDefs[FColIndex]);

      // Walk through the entire line, *character by character*, and populate/adjust the
      //  ByteStart and ByteLength properties of each column as we go. Also isolate the left and
      //  right content while we're at it.
      DiffType := ' ';
      ColIndex := 0;
      PLastChar := @FLineText[Length(FLineText)];
      PCurrChar := @FLineText[1];
      ByteIndex := 1;
      CharIndex := 1;
      while PCurrChar <= PLastChar do begin
        if (PCurrChar = PLastChar) and (CharIndex < FFullWidth) then begin
          FLineText := FLineText + StringOfChar(' ', FFullWidth - CharIndex);
          PLastChar := @FLineText[Length(FLineText)];
          PCurrChar := @FLineText[ByteIndex];
        end;
        // Skip bytes in case of multibyte characters
        case ByteType(PCurrChar) of
          FirstOf2: BytesToSkip := 1;
          FirstOf3: BytesToSkip := 2;
          FirstOf4: BytesToSkip := 3;
          else      BytesToSkip := 0;
        end;
        Inc(PCurrChar, BytesToSkip);
        Inc(ByteIndex, BytesToSkip);

        // Figure out if we've moved into the next column
        if CharIndex >= FColDefs[ColIndex].CharStart + FColDefs[ColIndex].CharLength then begin
          FColDefs[ColIndex].ByteLength := ByteIndex - FColDefs[ColIndex].ByteStart;

          // Set aside the content
          case ColIndex of
            2: begin // left-hand content
              ContentLeft := Copy(FLineText, FColDefs[ColIndex].ByteStart, FColDefs[ColIndex].ByteLength);
              ContentLeft := TrimRight(ContentLeft);
            end;
            4: begin // diff marker
              DiffType := Copy(FLineText, FColDefs[ColIndex].ByteStart, FColDefs[ColIndex].ByteLength);
            end;
            // There is no column 9, so we can only check for the right-hand content after the end of the loop
          end;

          // Move to next column
          if ColIndex < High(FColDefs) then begin
            Inc(ColIndex);
            FColDefs[ColIndex].ByteStart := ByteIndex;
          end;
        end;

        // Next byte please!
        Inc(PCurrChar);
        Inc(ByteIndex);
        Inc(CharIndex);
      end;
      if ColIndex = 8 then begin // right-hand content
        ContentRight := Copy(FLineText, FColDefs[ColIndex].ByteStart, FColDefs[ColIndex].ByteLength);
        ContentRight := TrimRight(ContentRight);
      end;

      case DiffType of
        '<': begin
          FColDefs[2].Style := hsContentDeleted;
          FColDefs[4].Style := hsMarkerDeleted;
          FColDefs[8].Style := hsContent;
        end;
        '>': begin
          FColDefs[2].Style := hsContent;
          FColDefs[4].Style := hsMarkerAdded;
          FColDefs[8].Style := hsContentAdded;
        end;
        '|': begin
          FColDefs[2].Style := hsContentEdited;
          FColDefs[4].Style := hsMarkerEdited;
          FColDefs[8].Style := hsContentEdited;
        end
        else begin
          FColDefs[2].Style := hsContent;
          FColDefs[4].Style := hsMarkerEdited;
          FColDefs[8].Style := hsContent;
        end;
      end;

      if DiffType = '|' then begin
        // compare left and right content, and adjust their respective subcols
        FLineType := '|';
        CompareContent(ContentLeft, ContentRight);
      end;
    end;
    else begin // unknown
      FLineType := '?';
      // set the token to the entire line, and set the styler to UnknownAttri
      SetToken(1, -1, hsUnknown);
    end;
  end;
end;

procedure TSynDiffWideSyn.CompareContent(const Left, Right: string);
  procedure DefineSubCols(const IsLeft: Boolean; const DiffStart, DiffEnd, MaxLength: Integer; const Style: THighlightStyle);
  const
    ColIndex: array[Boolean] of Integer = (8, 2);
  begin
    FSubColDefs[IsLeft, 0].ByteStart := FColDefs[ColIndex[IsLeft]].ByteStart;
    FSubColDefs[IsLeft, 0].ByteLength := DiffStart - 1;
    FSubColDefs[IsLeft, 0].Style := hsContent;
    FSubColDefs[IsLeft, 1].ByteStart := FSubColDefs[IsLeft, 0].ByteStart + FSubColDefs[IsLeft, 0].ByteLength;
    FSubColDefs[IsLeft, 1].ByteLength := DiffEnd - DiffStart + 1;
    FSubColDefs[IsLeft, 1].Style := Style;
    FSubColDefs[IsLeft, 2].ByteStart := FSubColDefs[IsLeft, 1].ByteStart + FSubColDefs[IsLeft, 1].ByteLength;
    FSubColDefs[IsLeft, 2].ByteLength := MaxLength - DiffEnd + 1;
    FSubColDefs[IsLeft, 2].Style := hsContent;
    FSubColDefs[IsLeft, 3].ByteStart := FSubColDefs[IsLeft, 2].ByteStart + FSubColDefs[IsLeft, 2].ByteLength;
    FSubColDefs[IsLeft, 3].ByteLength := FColDefs[ColIndex[IsLeft]].ByteLength - MaxLength - 1;
    FSubColDefs[IsLeft, 3].Style := hsSpace;
  end;
var
  MinLast: Integer;
  FirstDiff, RevFirstDiff, LastDiffLeft, LastDiffRight: Integer;
  i: Integer;
  LastLeft, LastRight: Integer;
  StyleLeft, StyleRight: THighlightStyle;
  LeftHasContent, RightHasContent: Boolean;
begin
  // Scan Left and Right to see how far they're equal from the left;
  LastLeft := Length(Left);
  LastRight := Length(Right);
  MinLast := Min(LastLeft, LastRight);
  FirstDiff := MinLast + 1;
  for i := 1 to MinLast do begin
    if Left[i] <> Right[i] then begin
      // We've found a different byte!
      FirstDiff := FirstByteOfChar(Left, i);
      Break;
    end;
  end;

  // Scan Left and Right to see how far they're equal from the right;
  RevFirstDiff := MinLast - FirstDiff + 1;
  for i := 0 to MinLast - FirstDiff do begin
    if Left[LastLeft - i] <> Right[LastRight - i] then begin
      // We've found a different byte!
      RevFirstDiff := i;
      Break;
    end;
  end;
  LastDiffLeft := LastByteOfChar(Left, LastLeft - RevFirstDiff);
  LastDiffRight := LastByteOfChar(Right, LastRight - RevFirstDiff);

  // Figure out if the diff part has content on either side, or not,
  //  and determine the styling based on that.
  LeftHasContent := FirstDiff <= LastDiffLeft;
  RightHasContent := FirstDiff <= LastDiffRight;
  if LeftHasContent = RightHasContent then begin
    StyleLeft := hsContentEdited;
    StyleRight := hsContentEdited;
  end else begin
    StyleLeft := hsContentDeleted;
    StyleRight := hsContentAdded;
  end;

  if not LeftHasContent and not RightHasContent then begin
    FLineType := ' ';
  end else begin
    // Split up the left and right contents into same|diff|same|trailing-whitespace
    DefineSubCols(True, FirstDiff, LastDiffLeft, LastLeft, StyleLeft);
    DefineSubCols(False, FirstDiff, LastDiffRight, LastRight, StyleRight);
  end;
end;

procedure TSynDiffWideSyn.Next;
begin
  // 0:linenum[5], 1:space[2], 2:content[FContentWidth], 3:space[1], 4:marker[1], 5:space[1], 6:linenum[5], 7:space[2], 8:content[FContentWidth]
  case FLineType of
    ' ': begin
      Inc(FColIndex);
      if FColIndex < Length(FColDefs) then begin
        SetToken(@FColDefs[FColIndex]);
      end else begin
        SetToken(Length(FLineText) + 1, 0, hsUnknown);
      end;
    end;
  '|': begin
      if (FColIndex in [2, 8]) and (FSubColIndex < 3) then begin
        Inc(FSubColIndex);
      end else begin
        Inc(FColIndex);
        FSubColIndex := 0;
      end;
      case FColIndex of
        2: begin // left content column
          SetToken(@FSubColDefs[True, FSubColIndex]);
        end;
        8: begin // right content column
          SetToken(@FSubColDefs[False, FSubColIndex]);
        end;
        else begin // normal column(s)
          if FColIndex < Length(FColDefs) then begin
            SetToken(@FColDefs[FColIndex]);
          end else begin
            SetToken(Length(FLineText) + 1, 0, hsUnknown);
          end;
        end;
      end;
    end;
  else
    SetToken(Length(FLineText) + 1, 0, hsSpace);
  end;
end;

function TSynDiffWideSyn.GetEol: Boolean;
begin
  Result := FTokenPos > Length(FLineText);
end;

procedure TSynDiffWideSyn.GetTokenEx(out TokenStart: PChar; out TokenLength: Integer);
begin
  TokenStart := @FLineText[FTokenPos];
  TokenLength := FTokenLength;
end;

function TSynDiffWideSyn.GetEndOfLineAttribute: TSynHighlighterAttributes;
begin
  Result := inherited GetEndOfLineAttribute; // TODO?
end;

function TSynDiffWideSyn.GetTokenAttribute: TSynHighlighterAttributes;
begin
  Result := FStyles[FTokenStyle];
end;

function TSynDiffWideSyn.GetToken: String;
begin
  Result := Copy(FLineText, FTokenPos, FTokenLength);
end;

function TSynDiffWideSyn.GetTokenPos: Integer;
begin
  //  return the token position (0-based)
  Result := FTokenPos - 1;
end;

function TSynDiffWideSyn.GetTokenKind: Integer;
begin
  Result := Ord(FTokenStyle);
end;

function TSynDiffWideSyn.GetDefaultAttribute(Index: Integer): TSynHighlighterAttributes;
begin
  case Index of
    SYN_ATTR_WHITESPACE: Result := Self.SpaceAttrib;
    else
      Result := nil;
  end;
end;

end.

Added src/u_fossil.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
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
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
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
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
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
896
897
898
899
900
901
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
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
unit U_Fossil;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Contnrs, Graphics,
  fgl;

type
  TFileStatus = (fsUnchanged, fsAdded, fsModified, fsDeleted,
                fsMissing, fsMerged, fsConflict, fsIgnored, fsUntracked);
  TFileStatuses = set of TFileStatus;
const
  fsChanged: TFileStatuses = [fsAdded, fsModified, fsDeleted, fsMissing, fsMerged, fsConflict];

type
  TFossilFileState = (ffsUnknown, ffsChanged, ffsUnchanged, ffsExtra, ffsIgnored);
  TFossilFileStates = set of TFossilFileState;

const
  ffsPending = [ffsChanged, ffsExtra];

type
  // forward declarations
  TFossilObject = class;
  TFossilRevision = class;
  TFossilWorkdir = class;
  TFileVersion = class;

  TFossilObjectClass = class of TFossilObject;

  { TFossil }

  TFossil = class
    // TODO: this object represents a fossil executable, and all that can be
    //  done with it. It's a very low-level interface (returns strings, at most)
    // Or does it just have a class var with fossil's path, and only class methods?
  private
    FExePath: TFileName;
    FCurDir: TFileName;
    FVersionInfo: string;
  public
    constructor Create(const AExecutable: TFileName; const ACurrentDir: TFileName = '');

    function Run(const Command: string; const TrimTrailingSpaces: Boolean = True): string;

    property Executable: TFileName  read FExePath;
    property CurrentDir: TFileName  read FCurDir  write FCurDir;
    property VersionInfo: string    read FVersionInfo;
  end;

  TFossilRevisionList = specialize TFPGList<TFossilRevision>;

  { TFossilUtilities }

  TFossilUtilities = class
  private
    FFossil: TFossil;
  public
    constructor Create(const Fossil: TFossil);

    function GetDefaultColor(const BranchName: string): TColor;
  end;

  { TFossilCheckout }

  TFossilCheckout = class
    // TODO: this object represents a entire checkout. It also manages all this checkout's objects
  private
    FFossil: TFossil;
    FOwnFossil: Boolean;
    FPath: TFileName;
    FObjects: TFPObjectList;
    FRevisions: TFossilRevisionList;
    FWorkdir: TFossilWorkdir;
    FInfo: TStringList;
    FSettings: TStringList;
    FCurrentRev: TFossilRevision;
    FUtilities: TFossilUtilities;

    function GetCaseSensitive: Boolean;
    function GetCurrentRevision: TFossilRevision;
    function GetInfo: TStringList;
    function GetRevisions: TFossilRevisionList;
    function GetSettings: TStringList;
    procedure SetCurrentRevision(AValue: TFossilRevision);
  public
    constructor Create(const Fossil: TFossil; Path: TFileName = '');
    destructor  Destroy; override;

    function PathFromFossil(const Path: TFileName): TFileName;
    function FindObjectByUUID(UUID: string; const ObjectClass: TFossilObjectClass = nil): TFossilObject;

    function LoadRevisions(Limit: Integer = 200; Offset: Integer = 0): Integer;

    property Exe: TFossil                     read FFossil;
    property Path: TFileName                  read FPath              write FPath;
    property Workdir: TFossilWorkdir          read FWorkdir;
    property Revisions: TFossilRevisionList   read GetRevisions;
    property Info: TStringList                read GetInfo;
    property Settings: TStringList            read GetSettings;
    property CurrentRevision: TFossilRevision read GetCurrentRevision write SetCurrentRevision;
    property Util: TFossilUtilities           read FUtilities;

    // TODO
    //property RepositoryPath: TFileName read; // the path of the repository
    //property RemoteURL: string read write; // the synchronisation URL


    property CaseSensitive: Boolean   read GetCaseSensitive;
  end;


  { TFossilObject }

  TFossilObject = class
  private
    FUUID: string;
    FCheckout: TFossilCheckout;
    FInfo: TStringList;
  protected
    function GetInfo: TStringList; virtual;
  public
    constructor Create(const ACheckout: TFossilCheckout); virtual;
    destructor  Destroy; override;

    function CompareUUID(const OtherUUID: string): Integer;

    property Checkout: TFossilCheckout  read FCheckout;
    property UUID: string               read FUUID      write FUUID;
    property Info: TStringList          read GetInfo;
  end;


  { TFossilFileList }

  TFossilFileList = specialize TFPGObjectList<TFileVersion>;


  { TFossilRevision }

  TFossilRevision = class(TFossilObject)
  private
    FParentUUID: string;
    FFiles: TFossilFileList;
    FTags: TStringList;
    FDateTimeUTC: TDateTime;
    FDateTime: TDateTime;
    FUser: string;
    FMessage: string;
    FBranch: string;

    function GetCurrent: Boolean; virtual;
    function GetTags: TStringList;
    procedure SetCurrent(AValue: Boolean); virtual; // TODO: checkout? update?
    procedure SetDateTime(AValue: TDateTime);
    procedure SetDateTimeUTC(AValue: TDateTime);
  protected
    FLoadedStates: TFossilFileStates;

    function GetParentUUID: string; virtual;
    function GetParent: TFossilRevision; virtual;
    function GetBranch: string; virtual;
    function GetFiles: TFossilFileList; virtual;
  public
    constructor Create(const ACheckout: TFossilCheckout); override;
    destructor  Destroy; override;

    function LoadFilesFromTimeline(const Timeline: TStrings; var AIndex: Integer; const LeaveExisting: Boolean): Integer;

    property UUID;
    property ParentUUID: string       read GetParentUUID  write FParentUUID;
    property Parent: TFossilRevision  read GetParent;
    property IsCurrent: Boolean       read GetCurrent     write SetCurrent;
    property Files: TFossilFileList   read GetFiles;

    property DateTimeUTC: TDateTime   read FDateTimeUTC   write SetDateTimeUTC;
    property DateTimeLocal: TDateTime read FDateTime      write SetDateTime;
    property Message: string          read FMessage       write FMessage;
    property User: string             read FUser          write FUser;
    property Branch: string           read GetBranch      write FBranch;
    property Tags: TStringList        read GetTags;
  public
    function  LoadFiles(const States: TFossilFileStates = ffsPending; const ForceRefresh: Boolean = False): Integer; virtual;
  end;


type
  TCommitFlag = (cfAllowConflict, cfAllowEmpty, cfAllowFork, cfAllowOlder, cfBaselineManifest,
                  cfDeltaManifest, cfDryRun, cfNoSign, cfNoWarnings, cfPrivate, cfSHA1Sum);
  TCommitFlags = set of TCommitFlag;
  TCommitOptions = packed record
    Message: string;
    ContentType: string;
    BranchName: string;
    BranchColor: TColor;
    CloseBranch: Boolean;
    Integrate: Boolean;
    RevColor: TColor;
    Flags: TCommitFlags;
  end;
  PCommitOptions = ^TCommitOptions;
const
  cCommitFlagNames: array[TCommitFlag] of string = ('allow-conflict', 'allow-empty', 'allow-fork',
                                                    'allow-older', 'baseline', 'delta', 'dry-run',
                                                    'nosign', 'no-warnings', 'private', 'sha1sum');
  cDefaultCommitOptions: TCommitOptions = (
      Message: '';
      ContentType: '';
      BranchName: '';
      BranchColor: clNone;
      CloseBranch: False;
      Integrate: False;
      RevColor: clNone;
      Flags: [cfNoWarnings]
    );

type
  { TFossilWorkdir }

  TFossilWorkdir = class(TFossilRevision)
  protected
    function GetInfo: TStringList; override;
    function GetParentUUID: string; override;
  public
    function  LoadFiles(const States: TFossilFileStates = ffsPending; const ForceRefresh: Boolean = False): Integer; override;

    function  GetCommitOptions(var Options: TCommitOptions): string;
    function  Commit(const Options: PCommitOptions; const ATags: TStrings = nil; const AFiles: TFossilFileList = nil): string;

    function  MoveToStash(AMessage: string = ''; const AFiles: TFossilFileList = nil; const ACopy: Boolean = False): string;
  end;


type
  TDiffWhitespace = (dwsProcess, dwsIgnoreTrailing, dwsIgnoreAll);
  TDiffOptions = packed record
    External: Boolean;
    SideBySide: Boolean;
    Width: Integer;
    ContextLines: Integer;
    Whitespace: TDiffWhitespace;
  end;
  PDiffOptions = ^TDiffOptions;

const
  cDefaultDiffOptions: TDiffOptions = (External: False;
                                       SideBySide: False;
                                       Width: 0;
                                       ContextLines: -1;
                                       Whitespace: dwsProcess);

type

  { TFileVersion }

  TFileVersion = class
  private
    FRevision: TFossilRevision;
    FFullName: string;
    FStatus: string;

    // Derived data
    FName: string;
    FPath: string;
    FStatuses: TFileStatuses;

    // User data
    FSelected: Boolean;

    function GetRealPath: string;
    procedure SetFullName(AValue: string);
    procedure SetStatus(AValue: string);
  protected
    function GetDiffInternal(const ToRevision: string;
                             const FromRevision: string;
                             var Options: TDiffOptions): string; virtual;
  public
    constructor Create(const ARevision: TFossilRevision);
    destructor  Destroy; override;

    function GetContents: TStream; virtual;
    function GetDiff(var Options: TDiffOptions; const FromRevision: string = ''): string; virtual;
    function GetAnnotated(const IncludeLog: Boolean): string; virtual;

    property Revision: TFossilRevision  read FRevision;
    property FullName: string           read FFullName      write SetFullName;
    property Filename: string           read FName;
    property Path: string               read FPath;
    property Status: string             read FStatus    write SetStatus;
    property Statuses: TFileStatuses    read FStatuses;

    property Selected: Boolean          read FSelected  write FSelected;

    property RealPath: string           read GetRealPath;
  end;

  { TWorkFile }

  TWorkFile = class(TFileVersion)
  private
    function GetWorkdir: TFossilWorkdir;
  public
    function GetContents: TStream; override;
    function GetDiff(var Options: TDiffOptions; const FromRevision: string = ''): string; override;

    property Workdir: TFossilWorkdir    read GetWorkdir;
  end;

type
  EFossilError = class(Exception);
  ETimelineError = class(EFossilError);


////////////////////////////////////////////////////////////////////////////////
implementation
uses
  StrUtils, Pipes, utf8process, process, LazFileUtils;


{ TFossilUtilities }

constructor TFossilUtilities.Create(const Fossil: TFossil);
begin
  FFossil := Fossil;
end;

function TFossilUtilities.GetDefaultColor(const BranchName: string): TColor;
var
  Lines: TStringList;
  Line, TagName, HexColor: string;
  CharPos: Integer;
begin
  // Figure out the branch color for the given branch name
  Lines := TStringList.Create;
  try
    Lines.StrictDelimiter := True;
    Lines.Text := FFossil.Run('test-hash-color ' + AnsiQuotedStr(BranchName, '"'));
    for Line in Lines do begin
      CharPos := Pos(': #', Line);
      if CharPos > 0 then begin
        TagName := Trim(Copy(Line, 1, CharPos - 1));
        if CompareText(TagName, BranchName) = 0 then begin
          HexColor := TrimRight(Copy(Line, CharPos + 3, Length(Line)));
          // convert hex color to TColor
          Result := Hex2Dec(HexColor[5] + HexColor[6] + HexColor[3] + HexColor[4] + HexColor[1] + HexColor[2]);
          Exit;
        end;
      end;
    end;
  finally
    Lines.Free;
  end;
  Result := clNone;
end {TFossilUtilities.GetDefaultColor};


{ TFossil }

constructor TFossil.Create(const AExecutable: TFileName;
  const ACurrentDir: TFileName);
begin
  FExePath := AExecutable;
  if ACurrentDir <> '' then
    FCurDir := ACurrentDir
  else
    FCurDir := GetCurrentDir;
  FVersionInfo := Run('version');
end;

{ ------------------------------------------------------------------------------------------------ }
function TFossil.Run(const Command: string; const TrimTrailingSpaces: Boolean = True): string;
const
  READ_BYTES = 2048;
  { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - }
  {$POINTERMATH ON}
  function CacheStream(Input: TInputPipeStream; Cache: TMemoryStream; var BytesRead: LongInt): LongInt;
  var
    CacheMem: PByte;
  begin
    if Input.NumBytesAvailable > 0 then begin
      // make sure we have room
      Cache.SetSize(BytesRead + READ_BYTES);

      // try reading it
      CacheMem := Cache.Memory;
      Inc(CacheMem, BytesRead);
      Result := Input.Read(CacheMem^, READ_BYTES);
      if Result > 0 then begin
        Inc(BytesRead, Result);
      end;
    end else begin
      Result := 0;
    end;
  end{CacheStream};
  {$POINTERMATH OFF}
  { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - }
var
  OMS, EMS: TMemoryStream;
  S: TStringStream;
  P: TProcessUTF8;
  n: LongInt;
  BytesRead, ErrBytesRead: LongInt;
  Msg: string;
begin
  // We cannot use poWaitOnExit here since we don't
  // know the size of the output. On Linux the size of the
  // output pipe is 2 kB. If the output data is more, we
  // need to read the data. This isn't possible since we are
  // waiting. So we get a deadlock here.
  //
  // A temp Memorystream is used to buffer the output

  BytesRead := 0;
  OMS := TMemoryStream.Create;
  ErrBytesRead := 0;
  EMS := TMemoryStream.Create;
  try
    P := TProcessUTF8.Create(nil);
    try
      P.CurrentDirectory := FCurDir;
      P.CommandLine := FExePath + ' ' + Command;
      //P.Executable := FExePath;
      //P.Parameters.Text := FExePath + ' ' + Command;
      P.Options := P.Options + [poUsePipes];
      P.StartupOptions := [suoUseShowWindow];
      P.ShowWindow := swoHIDE;

      P.Execute;
      while P.Running do begin
        n := CacheStream(P.Output, OMS, BytesRead);
        // TODO: if Assigned(OnFossilOutput) then OnFossilOutput(Self, False, OMS, n, P.Input);
        Inc(n, CacheStream(P.Stderr, EMS, ErrBytesRead));
        // TODO: if Assigned(OnFossilOutput) then OnFossilOutput(Self, True, EMS, n, P.Input);
        if n <= 0 then begin
          // no data, wait 100 ms
          Sleep(100);
        end;
      end;
      // read last part
      repeat
        n := CacheStream(P.Output, OMS, BytesRead);
        // TODO: if Assigned(OnFossilOutput) then OnFossilOutput(Self, False, OMS, n, P.Input);
        Inc(n, CacheStream(P.Stderr, EMS, ErrBytesRead));
        // TODO: if Assigned(OnFossilOutput) then OnFossilOutput(Self, True, EMS, n, P.Input);
      until n <= 0;

      OMS.SetSize(BytesRead);
      EMS.SetSize(ErrBytesRead);
    finally
      P.Free;
    end;

    S := TStringStream.Create('');
    try
      S.CopyFrom(OMS, OMS.Size);
      S.Position := 0;
      Result := S.DataString;
    finally
      S.Free;
    end;

    // MessageBoxFunction(PChar(FCurDir + '> TFossil.Run(' + Command + ')'), PChar(Result), 64);

    if EMS.Size > 0 then begin
      S := TStringStream.Create('');
      try
        S.CopyFrom(EMS, EMS.Size);
        S.Position := 0;
        Msg := S.DataString;
      finally
        S.Free;
      end;
      if SameFileName(Copy(Msg, 2, Length(FExePath)), FExePath) then begin
        Msg := Copy(Msg, Length(FExePath) + 3, Length(Msg));
      end;
      raise EFossilError.Create(Trim(Msg + sLineBreak + sLineBreak + Result));
    end;
  finally
    OMS.Free;
    EMS.Free;
  end;

  // Trim all whitespace, including tabs and newlines
  if TrimTrailingSpaces then begin
    for n := Length(Result) downto 1 do
      if not (Result[n] in [' ', #13, #10, #9]) then begin
        if n < Length(Result) then
          SetLength(Result, n);
        Break;
      end;
  end;
end {TFossil.Run};


{ TFossilCheckout }

constructor TFossilCheckout.Create(const Fossil: TFossil; Path: TFileName);
begin
  inherited Create;
  FObjects := TFPObjectList.Create(True);
  FRevisions := TFossilRevisionList.Create;
  if Assigned(Fossil) then begin
    FFossil := Fossil;
    FOwnFossil := False;
    if Path <> '' then
      FFossil.CurrentDir := Path;
  end else begin
    FFossil := TFossil.Create(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'fossil', Path);
    FOwnFossil := True;
  end;
  FUtilities := TFossilUtilities.Create(FFossil);

  // query Fossil for its root path, so we don't have to worry about absolute and relative paths
  FPath := IncludeTrailingPathDelimiter(PathFromFossil(Info.Values['local-root']));
  FFossil.CurrentDir := FPath;

  FWorkdir := TFossilWorkdir.Create(Self);
end;

destructor TFossilCheckout.Destroy;
begin
  if Assigned(FSettings) then
    FSettings.Free;
  if Assigned(FInfo) then
    FInfo.Free;
  FWorkdir.Free;
  FRevisions.Free;
  FObjects.Free;
  FUtilities.Free;
  if FOwnFossil then
    FFossil.Free;
  inherited Destroy;
end;

function TFossilCheckout.PathFromFossil(const Path: TFileName): TFileName;
begin
  if DirectorySeparator = '/' then
    Result := Path
  else
    Result := StringReplace(Path, '/', DirectorySeparator, [rfReplaceAll]);
end;

function TFossilCheckout.FindObjectByUUID(UUID: string;
  const ObjectClass: TFossilObjectClass): TFossilObject;
var
  i: Integer;
begin
  // TODO: implement binary search, to speed this up?
  // loop through our list of objects
  Result := nil;
  i := 0;
  while (i < FObjects.Count - 1) do begin
    case TFossilObject(FObjects[i]).CompareUUID(UUID) of
      0: begin
        Result := TFossilObject(FObjects[i]);
        Exit;
      end;
      1: begin // that list is sorted; this means we're past where it would be
        Break;
      end;
    end;
    Inc(i);
  end;
  // Coming here means we haven't got it yet; which means we'll have to create it
  if ObjectClass = nil then
    Exit;

  // First, check if fossil knows that UUID (`fossil info UUID` will give an exception if it doesn't exist)
  FFossil.Run('info ' + UUID);

  // TODO: check the object class, and use the appropriate constructor to create it?
  Result := ObjectClass.Create(Self);
  Result.UUID := UUID;

  // Insert the object to our list, at the expected index
  FObjects.Insert(i, Result);
end {TFossilCheckout.FindObjectByUUID};

function TFossilCheckout.LoadRevisions(Limit: Integer; Offset: Integer): Integer;
const
  DBFS: TFormatSettings = (CurrencyFormat: 0; NegCurrFormat: 0; ThousandSeparator: ',';
                            DecimalSeparator: '.'; CurrencyDecimals: 4;
                            DateSeparator: '-'; TimeSeparator: ':'; ListSeparator: ' ';
                            CurrencyString: '€';
                            ShortDateFormat: 'YYYY-MM-dd'; LongDateFormat: 'YYYY-MM-dd';
                            TimeAMString: ''; TimePMString: '';
                            ShortTimeFormat: 'HH:nn:ss'; LongTimeFormat: 'HH:nn:ss';
                            ShortMonthNames: ('','','','','','','','','','','','');
                            LongMonthNames: ('','','','','','','','','','','','');
                            ShortDayNames: ('','','','','','','');
                            LongDayNames: ('','','','','','','');
                            TwoDigitYearCenturyWindow: 30);
var
  Timeline: TStringList;
  i, CharPos: Integer;
  Line, Marker: string;
  CurDate: TDate;
  RevTime: TTime;
  RevUUID, Description, User: string;
  NewRevision: TFossilRevision;
begin
  Result := 0;

  Timeline := TStringList.Create;
  try
    // TODO: get the limit from a setting (defaulting to 100)
    // TODO: take paging offset into account
    Timeline.Text := FFossil.Run(Format('timeline --verbose --type ci --width 0 --limit %d --offset %d', [Limit, Offset]));
    // --verbose    so we get a list of the changed files
    // --type ci    so we only get the checkins
    // -- width 0   so no wrapping occurs, and each revision is located on a single line
    // --limit 100  to prevent having to wait for too long
    // --offset n   for paging

    // Parse the timeline:
    // === YYYY-MM-dd ===
    // HH:mm:ss [UUID] *CURRENT* Description, all on one line.
    // >>> >>> >>> this is where AddFilesFromTimeline should start <<< <<< <<<
    //    STATUS path/filename.pas
    //    STATUS path/filename.lfm
    // --- entry limit (1) reached ---
    // OR
    // +++ no more data (1) +++
    i := 0;
    while i < Timeline.Count do begin
      Line := Timeline[i];
      case Line[1] of
        '=': begin // New date
          CurDate := StrToDate(Copy(Line, 5, 10), DBFS);
          Inc(i);
        end;
        '0'..'2': begin // new time, ergo new revision
          RevTime := StrToTime(Copy(Line, 1, 8), DBFS);
          Delete(Line, 1, 10);
          CharPos := Pos(']', Line);
          Assert(CharPos > 0, 'No UUID found in timeline: ' + TimeLine[i]);
          RevUUID := Copy(Line, 1, CharPos - 1);
          Delete(Line, 1, CharPos + 1);
          NewRevision := TFossilRevision(FindObjectByUUID(RevUUID, TFossilRevision));
          Inc(Result);
          NewRevision.DateTimeUTC := Trunc(CurDate) + Frac(RevTime);
          if Line[1] = '*' then begin
            CharPos := PosEx('*', Line, 2);
            Marker := Copy(Line, 2, CharPos - 2);
            if Marker = 'CURRENT' then begin
              Self.CurrentRevision := NewRevision;
            end; // TODO: also remember other markers (like BRANCH and MERGE)
            Delete(Line, 1, CharPos);
          end;

          // TODO: extract user, branch?, and tags from the description line?
          CharPos := LastDelimiter('(', Line);
          // TODO: read user, branch (first tag) and other tags
          Line := Copy(Line, 1, CharPos - 1).Trim();

          NewRevision.Message := Line;

          // TODO: Check that it's not already present?
          if Revisions.IndexOf(NewRevision) < 0 then
             Revisions.Add(NewRevision);

          Inc(i);
          NewRevision.LoadFilesFromTimeline(Timeline, i, NewRevision.Files.Count = 0);
        end;
        '-': begin // Limit reached; there may be more data available
          // TODO
          Break;
        end;
        '+': begin // End reached; no more data.
          // TODO
          Break;
        end;
        else begin
          raise ETimelineError.CreateFmt('Error retrieving timeline: expected [=012-+], encountered "%s" of "%s"', [Line[1], Line]);
        end;
      end;
    end;
  finally
    Timeline.Free;
  end;

  // TODO: keep track of which section we've loaded (first, last)
end {TFossilCheckout.LoadRevisions};

function TFossilCheckout.GetInfo: TStringList;
var
  i: Integer;
begin
  if not Assigned(FInfo) then begin
    FInfo := TStringList.Create;
    FInfo.NameValueSeparator := ':';
    FInfo.Text := FFossil.Run('info');

    // TODO: correctly process wrapped lines!

    for i := FInfo.Count - 1 downto 0 do begin
      FInfo[i] := TrimRight(FInfo.Names[i])
                  + FInfo.NameValueSeparator
                  + TrimLeft(FInfo.ValueFromIndex[i]);
    end;
  end;
  Result := FInfo;
end;

function TFossilCheckout.GetRevisions: TFossilRevisionList;
begin
  if not Assigned(FRevisions) then begin
    FRevisions := TFossilRevisionList.Create;
  end;
  Result := FRevisions;
end;

function TFossilCheckout.GetSettings: TStringList;
var
  Lines: TStringList;
  Line, Name, Scope, Value: string;
  i, CharPos: Integer;
  iScope: NativeInt;
  Index: Integer = -1;
begin
  if not Assigned(FSettings) then begin
    FSettings := TStringList.Create;
    FSettings.CaseSensitive := True;

    Lines := TStringList.Create;
    try
      Lines.Text := FFossil.Run('settings');

      // <setting-name>   (local|global) <value>
      //   (overridden by contents of file .fossil-settings/<setting-name>)

      // Objects value: 0 = no value; 1 = global, 2 = local, 4 = file (so 5 = global overridden by file, 6 = local overridden by file)
      for i := 0 to Lines.Count - 1 do begin
        Line := TrimRight(Lines[i]);
        if Length(Line) = 0 then
          Continue;
        if (Line[1] = ' ') then begin
          if Index > -1 then
            FSettings.Objects[Index] := TObject(4 or NativeInt(FSettings.Objects[Index]));
          Continue;
        end;
        CharPos := Pos(' ', Line);
        if CharPos = 0 then begin
          Name := Line;
          Scope := '';
          Value := '';
        end else begin
          Name := Copy(Line, 1, CharPos - 1);
          Line := TrimLeft(Copy(Line, CharPos + 1, Length(Line)));
          CharPos := Pos(' ', Line);
          if CharPos > 0 then begin
            Scope := Copy(Line, 1, CharPos - 1);
            Value := TrimLeft(Copy(Line, CharPos + 1, Length(Line)));
          end else begin
            Scope := Line;
            Value := '';
          end;
          FSettings.Values[Name] := Value;
          Index := FSettings.IndexOfName(Name);
          Assert((Scope = '') or (Scope = '(global)') or (Scope = '(local)'), 'Unexpected scope: "' + Scope + '"');
          if Scope = '' then
            iScope := 0
          else if Scope = '(global)' then
            iScope := 1
          else if Scope = '(local)' then
            iScope := 2
          else
            iScope := -1;
          FSettings.Objects[Index] := TObject(iScope);
        end;
      end;
    finally
      Lines.Free;
    end;
  end;
  Result := FSettings;
end;

function TFossilCheckout.GetCaseSensitive: Boolean;
begin
  Result := StrToBoolDef(Settings.Values['case-sensitive'], FileNameCaseSensitive);
end;

function TFossilCheckout.GetCurrentRevision: TFossilRevision;
begin
  Result := FCurrentRev;
end;

procedure TFossilCheckout.SetCurrentRevision(AValue: TFossilRevision);
begin
  FCurrentRev := AValue;
end;



{ TFossilObject }

constructor TFossilObject.Create(const ACheckout: TFossilCheckout);
begin
  inherited Create;
  FCheckout := ACheckout;
end;

destructor TFossilObject.Destroy;
begin
  if Assigned(FInfo) then
    FInfo.Free;
  inherited Destroy;
end;

function TFossilObject.GetInfo: TStringList;
var
  i: Integer;
begin
  if not Assigned(FInfo) then begin
    Assert(UUID <> '', 'No UUID known for this object!');
    FInfo := TStringList.Create;
    FInfo.NameValueSeparator := ':';
    FInfo.Text := FCheckout.Exe.Run('info ' + UUID);

    // TODO: wrapped lines!

    for i := FInfo.Count - 1 downto 0 do begin
      FInfo[i] := TrimRight(FInfo.Names[i])
                  + FInfo.NameValueSeparator
                  + TrimLeft(FInfo.ValueFromIndex[i]);
    end;
  end;
  Result := FInfo;
end;

function TFossilObject.CompareUUID(const OtherUUID: string): Integer;
var count, count1, count2: integer;
begin
  result := 0;
  Count1 := Length(FUUID);
  Count2 := Length(OtherUUID);
  if Count1 > Count2 then
    Count := Count2
  else
    Count := Count1;
  result := CompareMemRange(Pointer(FUUID), Pointer(OtherUUID), Count);
end;


{ TFossilRevision }

constructor TFossilRevision.Create(const ACheckout: TFossilCheckout);
begin
  inherited Create(ACheckout);
  FTags := TStringList.Create;
end;

destructor TFossilRevision.Destroy;
begin
  if Assigned(FFiles) then
    FFiles.Free;
  FTags.Free;
  inherited Destroy;
end;

function TFossilRevision.GetParentUUID: string;
var
  CharPos: Integer;
begin
  if FParentUUID = '' then begin
    FParentUUID := Info.Values['parent'];
    CharPos := Pos(' ', FParentUUID);
    if CharPos > 0 then
      FParentUUID := Copy(FParentUUID, 1, CharPos - 1);
  end;
  Result := FParentUUID;
end;

function TFossilRevision.GetParent: TFossilRevision;
var
  FO: TFossilObject;
begin
  FO := Checkout.FindObjectByUUID(ParentUUID, TFossilRevision);
  Assert(not Assigned(FO) or (FO is TFossilRevision));
  Result := TFossilRevision(FO);
end;

function TFossilRevision.GetFiles: TFossilFileList;
begin
  if not Assigned(FFiles) then begin
    FFiles := TFossilFileList.Create;
  end;
  Result := FFiles;
end;

function TFossilRevision.GetCurrent: Boolean;
begin
  Result := (FCheckout.CurrentRevision = Self);
end;

function TFossilRevision.GetTags: TStringList;
begin
  if not Assigned(FTags) then begin
    FTags := TStringList.Create;
  end;
  if FTags.Count = 0 then begin
    FTags.Delimiter := ',';
    FTags.DelimitedText := Info.Values['tags'];
  end;
  Result := FTags;
end;

function TFossilRevision.GetBranch: string;
begin
  Result := Trim(Tags[0]);
end;

procedure TFossilRevision.SetCurrent(AValue: Boolean);
begin
  if AValue then
    FCheckout.CurrentRevision := Self
  else if IsCurrent then
    FCheckout.CurrentRevision := nil;
end;

procedure TFossilRevision.SetDateTime(AValue: TDateTime);
begin
  if FDateTime = AValue then Exit;
  FDateTime := AValue;
  // TODO: calculate FDateTimeUTC
end;

procedure TFossilRevision.SetDateTimeUTC(AValue: TDateTime);
begin
  if FDateTimeUTC = AValue then Exit;
  FDateTimeUTC := AValue;
  // TODO: calculate FDateTime (local)
end;

function TFossilRevision.LoadFilesFromTimeline(const Timeline: TStrings; var AIndex: Integer; const LeaveExisting: Boolean): Integer;
var
  Line: string;
  OldFile, NewFile: TFileVersion;
  i, CharPos: Integer;
  Existing: TStringList;
  bReused: Boolean;
begin
  Result := 0;

  // Parse the timeline:
  // === YYYY-MM-dd ===
  // HH:mm:ss [UUID] Message, all on one line.
  // >>> >>> >>> this is where LoadFilesFromTimeline should start <<< <<< <<<
  //    STATUS path/filename.pas
  //    STATUS path/filename.lfm
  // --- entry limit (1) reached ---
  // OR
  // +++ <no more data (1)> +++

  Existing := nil;
  try
    if LeaveExisting then begin
      //  Copy the files over to a sorted TStringList (for faster lookup), and move them back
      //  over when found. We can then free any remaining files in the TStringList.
      Existing := TStringList.Create;
      Existing.CaseSensitive := Checkout.CaseSensitive;
      for i := Files.Count - 1 downto 0 do begin
        OldFile := Files[i];
        // Extract it; delete would (probably?) Free it
        Existing.AddObject(OldFile.FullName, Files.Extract(OldFile));
      end;
      Existing.Sorted := True;
    end;
    while AIndex < Timeline.Count do begin
      Line := Timeline[AIndex];
      case Line[1] of
        '=': begin // new date; === <YYYY-MM-DD> ===
          Break;
        end;
        '0'..'2': begin // new revision; <HH:mm:ss> [<UUID>] <Message>
          Break;
        end;
        ' ': begin // STATUS <filename>
          bReused := False;
          NewFile := TFileVersion.Create(Self);
          try
            Line := TrimLeft(Line);
            CharPos := Pos(' ', Line);
            if CharPos > 0 then begin
              NewFile.Status := Copy(Line, 1, CharPos - 1);
              NewFile.FullName := Trim(Copy(Line, CharPos + 1, Length(Line)));
            end else begin
              raise ETimelineError.CreateFmt('Unexpected data while parsing timeline. Expected: STATUS FILENAME; received: "%s"', [Timeline[0]]);
            end;
          except
            NewFile.Free;
            raise;
          end;
          if LeaveExisting then begin
            i := Existing.IndexOf(NewFile.FullName);
            if i > -1 then begin
              OldFile := TFileVersion(Existing.Objects[i]);
              OldFile.Status := NewFile.Status;
              if not Existing.CaseSensitive then
                OldFile.FullName := NewFile.FullName;
              Existing.Delete(i);
              // TODO: can we notify the file (or rather, its users) that it's about to be freed?
              NewFile.Free;
              NewFile := OldFile;
              bReused := True;
            end;
          end;

          if not bReused then
            NewFile.Selected := (NewFile.Statuses * (fsChanged + [fsUntracked])) <> [];

          Files.Add(NewFile);
          Inc(Result);
        end;
        '-': begin // --- entry limit (<n>) reached ---; there may be more!
          Break;
        end;
        '+': begin // +++ done +++
          Break;
        end;
      end;

      // 'Eat' the line we just read, and carry on
      Inc(AIndex);
    end;

    if LeaveExisting then begin
      // Free any remaining old files
      for i := Existing.Count - 1 downto 0 do begin
        // TODO: notify the file('s users) of its imminent freedom?
        Existing.Objects[i].Free;
      end;
    end;

  finally
    Existing.Free;
  end;
end {TFossilRevision.LoadFilesFromTimeline};

function TFossilRevision.LoadFiles(const States: TFossilFileStates; const ForceRefresh: Boolean): Integer;
var
  Timeline: TStringList;
  Index: Integer;
begin
  Timeline := TStringList.Create;
  try
    Timeline.Text := FCheckout.Exe.Run('timeline ' + UUID + ' --limit 1 --width 0 --verbose');
    // --limit 1  only retrieve this revision's info
    // --width 0  so the commit message only takes up a single line
    // --verbose  so we also get the list of changed files in this revision

    // Skip the header of the timeline, which describes the revision
    Index := 2;
    Result := LoadFilesFromTimeline(Timeline, Index, ForceRefresh or True);
  finally
    Timeline.Free;
  end;

  FLoadedStates := States * [ffsChanged, ffsUnchanged];
end {TFossilRevision.LoadFiles};


{ TFossilWorkdir }

function TFossilWorkdir.GetInfo: TStringList;
var
  i: Integer;
begin
  if not Assigned(FInfo) then begin
    FInfo := TStringList.Create;
    FInfo.NameValueSeparator := ':';
    FInfo.Text := FCheckout.Exe.Run('info');

    // TODO: correctly process wrapped lines!

    // Remove extraneous spaces
    for i := FInfo.Count - 1 downto 0 do begin
      FInfo[i] := TrimRight(FInfo.Names[i])
                  + FInfo.NameValueSeparator
                  + TrimLeft(FInfo.ValueFromIndex[i]);
    end;
  end;
  Result := FInfo;
end;

function TFossilWorkdir.GetParentUUID: string;
var
  CharPos: Integer;
begin
  if FParentUUID = '' then begin
    FParentUUID := Checkout.Info.Values['checkout'];
    CharPos := Pos(' ', FParentUUID);
    if CharPos > 0 then
      FParentUUID := Copy(FParentUUID, 1, CharPos - 1);
  end;
  Result := FParentUUID;
end;

function TFossilWorkdir.LoadFiles(const States: TFossilFileStates; const ForceRefresh: Boolean): Integer;
var
  NewStates: TFossilFileStates;
  OldFile, NewFile: TWorkFile;
  Existing, List, Extras, Ignored: TStringList;
  Line: string;
  i, CharPos: Integer;
  { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - }
  procedure CheckOldFile(var NewFile: TWorkFile);
  var
    Index: Integer;
  begin
    Index := Existing.IndexOf(NewFile.FullName);
    if Index > -1 then begin
      OldFile := TWorkFile(Existing.Objects[Index]);
      OldFile.Status := NewFile.Status;
      if not Existing.CaseSensitive then
        OldFile.FullName := NewFile.FullName;
      Existing.Delete(Index);
      // TODO: can we notify the file (or rather, its users) that it's about to be freed?
      NewFile.Free;
      NewFile := OldFile;
    end else begin
      NewFile.Selected := (NewFile.Statuses * (fsChanged + [fsUntracked])) <> [];
    end;
  end;
  { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - }
begin
  Result := 0;
  If ForceRefresh then begin
    NewStates := States;
  end else begin
    NewStates := States - FLoadedStates; // don't re-request known files if we already know them
  end;

  // Keep a list of all old files and their objects
  Existing := TStringList.Create;
  try
    Existing.CaseSensitive := Checkout.CaseSensitive;
    for i := Files.Count - 1 downto 0 do begin
      OldFile := TWorkFile(Files[i]);
      // Extract it; delete would (probably?) Free it
      Existing.AddObject(OldFile.FullName, Files.Extract(OldFile));
    end;
    Existing.Sorted := True;


    // Depending on the requested states, either do
    //  fossil changes (CHANGED)
    //  fossil ls --verbose (CHANGED + UNCHANGED)
    //  fossil extras [--dotfiles] (EXTRA)
    //  fossil extras --ignore "" (EXTRA + IGNORED)

    List := nil;
    if ffsUnchanged in NewStates then begin
      Include(NewStates, ffsChanged);

      List := TStringList.Create;
      List.Text := FCheckout.Exe.Run('ls --verbose');

    end else if ffsChanged in NewStates then begin

      List := TStringList.Create;
      List.Text := FCheckout.Exe.Run('changes');

    end;
    if Assigned(List) then
    try
      for i := 0 to List.Count - 1 do begin
        Line := List[i];
        CharPos := Pos(' ', Line);
        Assert(CharPos > 0, 'Expected space in list of versioned files: "' + Line + '"');
        NewFile := TWorkFile.Create(Self);
        NewFile.Status := UpperCase(Copy(Line, 1, CharPos - 1));
        NewFile.FullName := TrimLeft(Copy(Line, CharPos + 1, Length(Line)));
        CheckOldFile(NewFile);
        Files.Add(NewFile);
        Inc(Result);
      end;
    finally
      List.Free;
    end;

    if ([ffsExtra, ffsIgnored] * NewStates) <> [] then begin
      Extras := TStringList.Create;
      Ignored := nil;
      try
        Extras.CaseSensitive := Checkout.CaseSensitive;
        Extras.Sorted := False;

        if ffsIgnored in NewStates then begin
          Include(NewStates, ffsExtra);
          Ignored := TStringList.Create;
          Ignored.Text := FCheckout.Exe.Run('extras --dotfiles --ignore ""');
        end;

        Extras.Text := FCheckout.Exe.Run('extras');
        Extras.Sorted := True; // speeds up lookups later on

        if ffsIgnored in NewStates then begin
          // all files present in Ignored but not in Extras are, indeed, ignored.
          for i := 0 to Ignored.Count - 1 do begin
            NewFile := TWorkFile.Create(Self);
            NewFile.FullName := Ignored[i];
            if Extras.IndexOf(Ignored[i]) = -1 then
              NewFile.Status := 'IGNORED'
            else
              NewFile.Status := 'EXTRA';
            CheckOldFile(NewFile);
            Files.Add(NewFile);
            Inc(Result);
          end;
        end else begin
          // all files present in Extras are ...extra.
          for i := 0 to Extras.Count - 1 do begin
            NewFile := TWorkFile.Create(Self);
            NewFile.FullName := Extras[i];
            NewFile.Status := 'EXTRA';
            CheckOldFile(NewFile);
            Files.Add(NewFile);
            Inc(Result);
          end;
        end;
      finally
        Extras.Free;
        Ignored.Free;
      end;
    end;

    // Free any remaining old files
    for i := Existing.Count - 1 downto 0 do begin
      OldFile := TWorkFile(Existing.Objects[i]);
      // TODO: if its status matches FLoadedStates but not NewStates,
      //  then we shouldn't free it, but add it to the list.
//      if not ForceRefresh {and ...} then begin
//        Files.Add(OldFile);
//      end else begin
        // TODO: notify the file('s users) of its imminent freedom?
        OldFile.Free;
//      end;
    end;
  finally
    Existing.Free;
  end;

  // keep track of the requested states, so we know to only request what's new when ForceRefresh = False
  if ForceRefresh then
    FLoadedStates := NewStates
  else
    FLoadedStates := FLoadedStates + NewStates;
end;

function ColorToHex(const Color: TColor): string;
var
  R, G, B: Byte;
begin
  // return HTML hex-notation of Color (HTML has the colors in the inverse direction of Pascal)
  RedGreenBlue(Color, R, G, B);
  Result := '#' + HexStr(RGBToColor(G, B, R), 6);
end;

function TFossilWorkdir.GetCommitOptions(var Options: TCommitOptions): string;
var
  Flag: TCommitFlag;
begin
  Result := '';

  if Options.ContentType <> '' then
    Result += ' --mimetype ' + Options.ContentType;

  if Options.BranchName <> '' then begin
    Result += ' --branch ' + Options.BranchName;
    if Options.BranchColor <> clNone then begin
      Result += ' --branchcolor ' + ColorToHex(Options.BranchColor);
    end;
  end;
  if Options.Integrate then begin
    Result += ' --integrate';
  end else if Options.CloseBranch then begin
    Result += ' --close';
  end;

  if Options.RevColor <> clNone then begin
    Result += ' --bgcolor ' + ColorToHex(Options.RevColor);
  end;

  for Flag := Low(TCommitFlag) to High(TCommitFlag) do begin
    if Flag in Options.Flags then begin
      Result += ' --' + cCommitFlagNames[Flag];
    end;
  end;
end {TFossilWorkdir.GetCommitOptions};

function TFossilWorkdir.Commit(const Options: PCommitOptions;
  const ATags: TStrings = nil; const AFiles: TFossilFileList = nil): string;
var
  Cmd: string;
  MsgFile: TFileName;
  SS: TStringStream;
  FS: TFileStream;
  i: Integer;
begin
  Cmd := 'commit';

  // save the commit message to a temp file, and refer to that
  MsgFile := GetTempFilenameUTF8(GetTempDir, 'msg');
  SS := TStringStream.Create(Options^.Message);
  try
    FS := TFileStream.Create(MsgFile, fmCreate or fmOpenWrite or fmShareDenyWrite);
    try
      FS.CopyFrom(SS, 0);
    finally
      FS.Free;
    end;
  finally
    SS.Free;
  end;
  try
    Cmd += ' --message-file ' + AnsiQuotedStr(MsgFile, '"');

    Cmd += GetCommitOptions(Options^);

    if Assigned(ATags) and (ATags.Count > 0) then begin
      for i := 0 to ATags.Count - 1 do begin
        Cmd += ' --tag ' + AnsiQuotedStr(ATags[i], '"');
      end;
    end;

    // Only for the given files (if requested)
    if Assigned(AFiles) and (AFiles.Count > 0) then begin
      for i := 0 to AFiles.Count - 1 do begin
        Cmd += ' ' + AnsiQuotedStr(AFiles[i].FullName, '"');
      end;
    end;

    // TODO: how do we handle Fossil asking questions? TFossil.Exec: TProcess!
    try
      Result := Checkout.Exe.Run(Cmd);
    except
      on E: EFossilError do begin
        if E.Message.StartsWith('Warning:', True) then
          Result := E.Message
        else
          raise;
      end;
    end;
  finally
    DeleteFile(MsgFile);
  end;

  // TODO: clear/invalidate ParentUUID, files etc.
  FParentUUID := '';
  // TODO: notify listeners that we've committed, and they might want to refresh!

end {TFossilWorkdir.Commit};

function TFossilWorkdir.MoveToStash(AMessage: string; const AFiles: TFossilFileList;
  const ACopy: Boolean): string;
var
  Cmd: string;
  oFile: TFileVersion;
begin
  Cmd := 'stash ';
  if ACopy then
    Cmd += 'snapshot '
  else
    Cmd += 'save ';

  Cmd += '--message ' + AnsiQuotedStr(ReplaceStr(AMessage, LineEnding, ' '), '"');

  if Assigned(AFiles) then begin
    for oFile in AFiles do begin
      if oFile.Selected then begin
        Cmd += ' ' + AnsiQuotedStr(oFile.FullName, '"');
      end;
    end;
  end;

  Result := Checkout.Exe.Run(Cmd);
end {TFossilWorkdir.MoveToStash};


{ TFileVersion }

constructor TFileVersion.Create(const ARevision: TFossilRevision);
begin
  FRevision := ARevision;
end;

destructor TFileVersion.Destroy;
begin
  inherited Destroy;
end;

procedure TFileVersion.SetFullName(AValue: string);
begin
  FFullName := AValue;
  FName := ExtractFileName(AValue);
  FPath := ExtractFilePath(AValue);
end;

function TFileVersion.GetRealPath: string;
var
  Checkout: TFossilCheckout;
begin
  Checkout := FRevision.Checkout;
  Result := IncludeTrailingPathDelimiter(Checkout.Path) + Checkout.PathFromFossil(FFullName);
end;

procedure TFileVersion.SetStatus(AValue: string);
const
  // from src/checkin.c # 328, fossil checkout [d7d265502a660e1dc5ef41fbe6978f32f507a00e] of 2014-07-24 21:27:08 UTC
  //  http://fossil-scm.org/fossil/artifact/86d2e5cd9f361c988d9c01a8dfc9d12466d15587?ln=328-354
  cStatusNames: array[0..13] of string = ('ADDED',
                                          'DELETED',
                                          'NOT_A_FILE',
                                          'MISSING',
                                          'UPDATED_BY_MERGE',
                                          'ADDED_BY_MERGE',
                                          'UPDATED_BY_INTEGRATE',
                                          'ADDED_BY_INTEGRATE',
                                          'CONFLICT',
                                          'EDITED',
                                          'RENAMED',
                                          'UNCHANGED',
                                          'EXTRA',
                                          'IGNORED');
  cStatuses: array[0..13] of TFileStatuses = ( [fsAdded],
                                               [fsDeleted],
                                               [],
                                               [fsMissing],
                                               [fsModified, fsMerged],
                                               [fsAdded, fsMerged],
                                               [fsModified, fsMerged],
                                               [fsAdded, fsMerged],
                                               [fsConflict],
                                               [fsModified],
                                               [fsModified], // fsRenamed
                                               [fsUnchanged],
                                               [fsUntracked],
                                               [fsUntracked, fsIgnored]);
var
  i: Integer;
begin
  if FStatus = AValue then Exit;
  FStatus := UpperCase(AValue);

  for i := Low(cStatusNames) to High(cStatusNames) do begin
    if FStatus = cStatusNames[i] then begin
      FStatuses := cStatuses[i];
      Exit;
    end;
  end;
  FStatuses := [];
end;

function TFileVersion.GetContents: TStream;
var
  Cmd: string;
begin
  Cmd := 'fossil cat '+ AnsiQuotedStr(FFullName, '"') +' -r '+ Revision.UUID;
  Result := TStringStream.Create(Revision.Checkout.Exe.Run(Cmd));
end;

function TFileVersion.GetDiffInternal(const ToRevision: string;
  const FromRevision: string; var Options: TDiffOptions): string;
var
  Cmd: string;
begin
  // fossil diff <--internal|--tk> --verbose --to '+Revision.UUID+' [--from '+FromUUID+'] [--side-by-side --width <n>]
  //            [--context <n>] [--ignore-all-space] [--ignore-trailing-space] ?FILE1? ?FILE2?

  Cmd := 'diff --verbose';
  if Options.External then
    Cmd += ' --tk'
  else
    Cmd += ' --internal';
  if ToRevision <> '' then
   Cmd += ' --to ' + ToRevision;
  if FromRevision <> '' then
    Cmd += ' --from ' + FromRevision;
  if Options.SideBySide then begin
    Cmd += ' --side-by-side';
    if Options.Width > 0 then
      Cmd += ' --width ' + IntToStr(Options.Width);
  end;
  if Options.ContextLines > -1 then
    Cmd += ' --context ' + IntToStr(Options.ContextLines);
  case Options.Whitespace of
    dwsIgnoreTrailing:
      Cmd += ' --ignore-trailing-space';
    dwsIgnoreAll:
      Cmd += ' --ignore-all-space';
  end;
  Cmd += ' ' + AnsiQuotedStr(FullName, '"');

  Result := Revision.Checkout.Exe.Run(Cmd);
  {$IFDEF MSWINDOWS}
  Result := StringReplace(Result, #13#13#10, #13#10, [rfReplaceAll]);
  {$ENDIF}
end {TFileVersion.GetDiffInternal};

function TFileVersion.GetDiff(var Options: TDiffOptions; const FromRevision: string): string;
begin
  Result := GetDiffInternal(Revision.UUID, FromRevision, {var}Options);
end;

function TFileVersion.GetAnnotated(const IncludeLog: Boolean): string;
var
  Cmd: string;
begin
  Cmd := 'praise --ignore-all-space ';
  if IncludeLog then
    Cmd += '--log ';
  Result := Revision.Checkout.Exe.Run(Cmd + AnsiQuotedStr(FullName, '"'));
end;


{ TWorkFile }

function TWorkFile.GetWorkdir: TFossilWorkdir;
begin
  if Revision is TFossilWorkdir then
    Result := TFossilWorkdir(Revision)
  else
    Result := nil;
end;

function TWorkFile.GetContents: TStream;
var
  sFile: TFileName;
begin
  sFile := IncludeTrailingPathDelimiter(Revision.Checkout.Path) + Revision.Checkout.PathFromFossil(FullName);
  Result := TFileStream.Create(sFile, fmOpenRead or fmShareDenyNone);
end;

function TWorkFile.GetDiff(var Options: TDiffOptions; const FromRevision: string): string;
begin
  // from fossil help diff:
  //  If there is no "--to" option then the (possibly edited) files in the current check-out are used.
  Result := GetDiffInternal('', FromRevision, Options);
end;



end.