1 unit editor_markup_userdefined;
2 
3 {$mode objfpc}{$H+}
4 
5 interface
6 
7 uses
8   Classes, sysutils, math,
9   // LCL
10   LCLType, StdCtrls, ComCtrls, Graphics, EditorOptions, Spin, ExtCtrls,
11   Menus, Grids, Controls, Dialogs, Buttons, Forms,
12   // LazControls
13   DividerBevel,
14   // LazUtils
15   LazLoggerBase,
16   // SynEdit
17   SynEditKeyCmds, SynEditMarkupHighAll,
18   // IdeIntf
19   IDEOptionsIntf, IDEOptEditorIntf, IDECommands, IDEDialogs,
20   // IDE
21   LazarusIDEStrConsts, SynColorAttribEditor, KeyMapping, KeyMapShortCutDlg,
22   editor_keymapping_options;
23 
24 type
25 
26   { TColorStringGrid }
27 
28   TColorStringGrid = class(TStringGrid)
29   private
30     FRowFontColor: Array of TColor;
GetRowFontColornull31     function GetRowFontColor(AIndex: Integer): TColor;
GetUserWordIndexnull32     function GetUserWordIndex(ARowIndex: Integer): Integer;
33     procedure SetRowFontColor(AIndex: Integer; AValue: TColor);
34     procedure SetUserWordIndex(ARowIndex: Integer; AValue: Integer);
35   protected
36     procedure PrepareCanvas(aCol, aRow: Integer; aState: TGridDrawState); override;
37     procedure ColRowDeleted(IsColumn: Boolean; index: Integer); override;
38     procedure ColRowInserted(IsColumn: boolean; index: integer); override;
39     // no exchanged or move
40     procedure SizeChanged(OldColCount, OldRowCount: Integer); override;
41     procedure DrawTextInCell(aCol, aRow: Integer; aRect: TRect;
42       aState: TGridDrawState); override;
43     procedure CalcCellExtent(acol, aRow: Integer; var aRect: TRect); override;
44   public
45     procedure DefaultDrawCell(aCol, aRow: Integer; var aRect: TRect;
46       aState: TGridDrawState); override;
47     property RowFontColor[AIndex: Integer]: TColor read GetRowFontColor write SetRowFontColor;
48     property UserWordIndex[ARowIndex: Integer]: Integer read GetUserWordIndex write SetUserWordIndex;
49   end;
50 
51   { TEditorMarkupUserDefinedFrame }
52 
53   TEditorMarkupUserDefinedFrame = class(TAbstractIDEOptionsEditor)
54     cbCaseSense: TCheckBox;
55     cbMatchEndBound: TCheckBox;
56     cbMatchStartBound: TCheckBox;
57     cbKeyCase: TCheckBox;
58     cbKeyBoundStart: TCheckBox;
59     cbKeyBoundEnd: TCheckBox;
60     cbSmartSelectBound: TCheckBox;
61     cbGlobalList: TCheckBox;
62     divKeyAdd: TDividerBevel;
63     divKeyRemove: TDividerBevel;
64     divKeyToggle: TDividerBevel;
65     edListName: TEdit;
66     HCenter: TLabel;
67     lbWordMin: TLabel;
68     lbSelectMin: TLabel;
69     HQuarter: TLabel;
70     lbKeyBoundMinLen: TLabel;
71     lbNewKeyOptions: TLabel;
72     HCenterKey: TLabel;
73     lbKeyAdd1: TLabel;
74     lbKeyAdd2: TLabel;
75     lbKeyRemove1: TLabel;
76     lbKeyRemove2: TLabel;
77     lbKeyToggle1: TLabel;
78     lbKeyToggle2: TLabel;
79     lbListName: TLabel;
80     ListMenu: TPopupMenu;
81     MainPanel: TPanel;
82     Notebook1: TNotebook;
83     PageMain: TPage;
84     PageKeys: TPage;
85     Panel1: TPanel;
86     btnKeyAdd: TSpeedButton;
87     btnKeyRemove: TSpeedButton;
88     btnKeyToggle: TSpeedButton;
89     edWordMin: TSpinEdit;
90     edSelectMin: TSpinEdit;
91     SynColorAttrEditor1: TSynColorAttrEditor;
92     ToolBar1: TToolBar;
93     tbSelectList: TToolButton;
94     tbNewList: TToolButton;
95     tbDeleteList: TToolButton;
96     ToolButton2: TToolButton;
97     tbMainPage: TToolButton;
98     tbKeyPage: TToolButton;
99     WordList: TColorStringGrid;
100     procedure edListNameEditingDone(Sender: TObject);
101     procedure edListNameKeyPress(Sender: TObject; var Key: char);
102     procedure KeyEditClicked(Sender: TObject);
103     procedure tbDeleteListClick(Sender: TObject);
104     procedure tbNewListClick(Sender: TObject);
105     procedure tbSelectListClick(Sender: TObject);
106     procedure GeneralCheckBoxChange(Sender: TObject);
107     procedure tbSelectPageClicked(Sender: TObject);
108     procedure WordListButtonClick(Sender: TObject; {%H-}aCol, aRow: Integer);
109     procedure WordListColRowDeleted(Sender: TObject; {%H-}IsColumn: Boolean; {%H-}sIndex,
110       {%H-}tIndex: Integer);
111     procedure WordListEditingDone(Sender: TObject);
112     procedure WordListExit(Sender: TObject);
113     procedure WordListKeyUp(Sender: TObject; var {%H-}Key: Word; {%H-}Shift: TShiftState);
114     procedure WordListSelection(Sender: TObject; {%H-}aCol, aRow: Integer);
115   private
116     { private declarations }
117     FGlobalColors: TEditorUserDefinedWordsList;
118     FUserWordsList: TEditorUserDefinedWordsList;
119     FUserWords: TEditorUserDefinedWords;
120     FKeyOptFrame: TEditorKeymappingOptionsFrame;
121     FSelectedListIdx: Integer;  // In List of Lists
122     FSelectedRow: Integer;
123     FUpdatingDisplay: Integer;
124     procedure CheckDuplicate(AnIndex: Integer);
125     procedure DoListSelected(Sender: TObject);
126     procedure MaybeCleanEmptyRow(aRow: Integer);
127     procedure UpdateKeys;
128     procedure UpdateTermOptions;
129     procedure UpdateListDropDownFull;
130     procedure UpdateListDropDownCaption;
131     procedure UpdateListDisplay(KeepDuplicates: Boolean = False);
132   public
133     constructor Create(AOwner: TComponent); override;
134     destructor Destroy; override;
GetTitlenull135     function GetTitle: String; override;
136     procedure Setup(ADialog: TAbstractOptionsEditorDialog); override;
137     procedure ReadSettings(AOptions: TAbstractIDEOptions); override;
138     procedure WriteSettings(AOptions: TAbstractIDEOptions); override;
SupportedOptionsClassnull139     class function SupportedOptionsClass: TAbstractIDEOptionsClass; override;
140   end;
141 
142 implementation
143 
144 { TColorStringGrid }
145 
GetRowFontColornull146 function TColorStringGrid.GetRowFontColor(AIndex: Integer): TColor;
147 begin
148   assert(AIndex < Length(FRowFontColor), 'GetRowFontColor');
149   Result := FRowFontColor[AIndex];
150 end;
151 
TColorStringGrid.GetUserWordIndexnull152 function TColorStringGrid.GetUserWordIndex(ARowIndex: Integer): Integer;
153 begin
154   Result := PtrInt(Objects[0, ARowIndex])-1;
155 end;
156 
157 procedure TColorStringGrid.SetRowFontColor(AIndex: Integer; AValue: TColor);
158 begin
159   assert(AIndex < Length(FRowFontColor), 'SetRowFontColor');
160   if FRowFontColor[AIndex] = AValue then
161     exit;
162   FRowFontColor[AIndex] := AValue;
163   Invalidate;
164 end;
165 
166 procedure TColorStringGrid.SetUserWordIndex(ARowIndex: Integer; AValue: Integer
167   );
168 begin
169   Objects[0, ARowIndex] := TObject(PtrInt(AValue + 1));
170 end;
171 
172 procedure TColorStringGrid.PrepareCanvas(aCol, aRow: Integer; aState: TGridDrawState);
173 begin
174   assert(aRow < Length(FRowFontColor));
175   inherited PrepareCanvas(aCol, aRow, aState);
176   Canvas.Font.Color := FRowFontColor[aRow];
177 end;
178 
179 procedure TColorStringGrid.ColRowDeleted(IsColumn: Boolean; index: Integer);
180 begin
181   inherited ColRowDeleted(IsColumn, index);
182   if IsColumn then exit;
183   assert(index < Length(FRowFontColor), 'ColRowDeleted');
184   if index < Length(FRowFontColor) - 1 then
185     move(FRowFontColor[index+1], FRowFontColor[index],
186       (Length(FRowFontColor)-index) * SizeOf(TColor));
187   SetLength(FRowFontColor, Length(FRowFontColor) - 1);
188 end;
189 
190 procedure TColorStringGrid.ColRowInserted(IsColumn: boolean; index: integer);
191 begin
192   inherited ColRowInserted(IsColumn, index);
193   if IsColumn then exit;
194   SetLength(FRowFontColor, Length(FRowFontColor) + 1);
195   assert(index < Length(FRowFontColor), 'ColRowInserted');
196   if index < Length(FRowFontColor) - 1 then
197     move(FRowFontColor[index], FRowFontColor[index+1],
198       (Length(FRowFontColor)-index) * SizeOf(TColor));
199   FRowFontColor[index] := Font.Color;
200 end;
201 
202 procedure TColorStringGrid.SizeChanged(OldColCount, OldRowCount: Integer);
203 var
204   i: Integer;
205 begin
206   inherited SizeChanged(OldColCount, OldRowCount);
207   i := Length(FRowFontColor);
208   SetLength(FRowFontColor, RowCount);
209   while i < RowCount do begin
210     FRowFontColor[i] := Font.Color;
211     inc(i);
212   end;
213 end;
214 
215 procedure TColorStringGrid.DrawTextInCell(aCol, aRow: Integer; aRect: TRect;
216   aState: TGridDrawState);
217 var
218   c: TColor;
219 begin
220   if (aRow = RowCount - 1) and (Cells[0, aRow] = '') then begin
221     c := Canvas.Font.Color;
222     Canvas.Font.Color := clGrayText;
223     DrawCellText(aCol, aRow, aRect, aState, rsAddNewTerm);
224     Canvas.Font.Color := c;
225   end
226   else
227     inherited DrawTextInCell(aCol, aRow, aRect, aState);
228 end;
229 
230 procedure TColorStringGrid.CalcCellExtent(acol, aRow: Integer; var aRect: TRect
231   );
232 var
233   dummy: Integer;
234 begin
235   if (aRow = RowCount - 1) and (acol = 0) then
236     ColRowToOffset(True, True, 1, dummy, aRect.Right);
237 end;
238 
239 procedure TColorStringGrid.DefaultDrawCell(aCol, aRow: Integer;
240   var aRect: TRect; aState: TGridDrawState);
241 var
242   w, h, x, y: LongInt;
243 begin
244   if (aRow = RowCount - 1) and (aCol = 1) then
245     exit;
246 
247   inherited DefaultDrawCell(aCol, aRow, aRect, aState);
248   if aCol = 1 then begin
249     Canvas.Pen.Color := clRed;
250     Canvas.Pen.Width := 2;
251     Canvas.Pen.Style := psSolid;
252     w := (aRect.Right - aRect.Left) div 2;
253     h := (aRect.Bottom - aRect.Top) div 2;
254     x := aRect.Left + w;
255     y := aRect.Top + h;
256     w := min(w, h) div 2;
257     Canvas.Line(x - w, y - w, x + w, y + w);
258     Canvas.Line(x - w, y + w, x + w, y - w);
259   end;
260 end;
261 
262 {$R *.lfm}
263 
264 { TEditorMarkupUserDefinedFrame }
265 
266 procedure TEditorMarkupUserDefinedFrame.edListNameEditingDone(Sender: TObject);
267 begin
268   if (FUpdatingDisplay > 0) or (FUserWords = nil) then exit;
269   if FUserWords.Name = edListName.Text then
270     exit;
271   FUserWords.Name := edListName.Text;
272   UpdateListDropDownFull;
273 end;
274 
275 procedure TEditorMarkupUserDefinedFrame.edListNameKeyPress(Sender: TObject; var Key: char);
276 begin
277   if key in [#10,#13] then edListNameEditingDone(nil);
278 end;
279 
280 procedure TEditorMarkupUserDefinedFrame.KeyEditClicked(Sender: TObject);
281 var
282   i: Integer;
283 begin
284   if FUserWords = nil then exit;
285 
286   i := -1;
287   if Sender = btnKeyAdd then
288     i := (FUserWordsList.KeyCommandList as TKeyCommandRelationList).IndexOf(FUserWords.AddTermCmd as TKeyCommandRelation);
289   if Sender = btnKeyRemove then
290     i := (FUserWordsList.KeyCommandList as TKeyCommandRelationList).IndexOf(FUserWords.RemoveTermCmd as TKeyCommandRelation);
291   if Sender = btnKeyToggle then
292     i := (FUserWordsList.KeyCommandList as TKeyCommandRelationList).IndexOf(FUserWords.ToggleTermCmd as TKeyCommandRelation);
293 
294   if i < 0 then exit;
295 
296   ShowKeyMappingEditForm(i, (FUserWordsList.KeyCommandList as TKeyCommandRelationList));
297   FKeyOptFrame.UpdateTree;
298   UpdateKeys;
299 end;
300 
301 procedure TEditorMarkupUserDefinedFrame.tbDeleteListClick(Sender: TObject);
302 begin
303   if FUserWords = nil then exit;
304   if WordList.EditorMode then
305     WordList.EditingDone;
306 
307   if IDEMessageDialog(dlgMarkupUserDefinedDelCaption,
308                 Format(dlgMarkupUserDefinedDelPrompt, [FUserWords.Name]),
309                 mtConfirmation, mbYesNo) = mrNo
310   then
311     exit;
312 
313   FUserWordsList.Remove(FUserWords, True);
314   FUserWords := nil;
315   if FSelectedListIdx > 0 then
316     dec(FSelectedListIdx);
317 
318   UpdateListDropDownFull;
319   UpdateListDisplay;
320 end;
321 
322 procedure TEditorMarkupUserDefinedFrame.tbNewListClick(Sender: TObject);
323 begin
324   if WordList.EditorMode then
325     WordList.EditingDone;
326 
327   FUserWords := FUserWordsList.Add(dlgMarkupUserDefinedNewName);
328   FSelectedListIdx := FUserWordsList.IndexOf(FUserWords);
329   UpdateListDropDownFull;
330   UpdateListDisplay;
331 end;
332 
333 procedure TEditorMarkupUserDefinedFrame.tbSelectListClick(Sender: TObject);
334 begin
335   tbSelectList.CheckMenuDropdown;
336 end;
337 
338 procedure TEditorMarkupUserDefinedFrame.GeneralCheckBoxChange(Sender: TObject);
339 var
340   i: PtrInt;
341 begin
342   if (FUpdatingDisplay > 0) or (FUserWords = nil) then exit;
343 
344   if WordList.EditorMode then
345     WordList.EditingDone;
346 
347   if Sender = cbKeyCase then begin
348     FUserWords.KeyAddCase := cbKeyCase.Checked;
349   end;
350 
351   if (Sender = cbKeyBoundStart) or (Sender = cbKeyBoundEnd) then begin
352     if cbKeyBoundStart.Checked then begin
353       if cbKeyBoundEnd.Checked
354       then FUserWords.KeyAddTermBounds := soBothBounds
355       else FUserWords.KeyAddTermBounds := soBoundsAtStart;
356     end
357     else begin
358       if cbKeyBoundEnd.Checked
359       then FUserWords.KeyAddTermBounds := soBoundsAtEnd
360       else FUserWords.KeyAddTermBounds := soNoBounds;
361     end;
362 
363     cbSmartSelectBound.Enabled := FUserWords.KeyAddTermBounds <> soNoBounds;
364     edWordMin.Enabled   := FUserWords.KeyAddTermBounds <> soNoBounds;
365     edSelectMin.Enabled := FUserWords.KeyAddTermBounds <> soNoBounds;
366   end;
367 
368   if Sender = cbSmartSelectBound then begin
369     FUserWords.KeyAddSelectSmart := cbSmartSelectBound.Checked;
370   end;
371 
372   if Sender = edWordMin then begin
373     FUserWords.KeyAddWordBoundMaxLen := edWordMin.Value;
374   end;
375 
376   if Sender = edSelectMin then begin
377     FUserWords.KeyAddSelectBoundMaxLen := edSelectMin.Value;
378   end;
379 
380   if Sender = cbGlobalList then
381     FUserWords.GlobalList := cbGlobalList.Checked;
382 
383 
384   // Related to current word
385   i := WordList.UserWordIndex[FSelectedRow];
386   if (i < 0) or (i >= FUserWords.Count) then
387     exit;
388 
389   if Sender = cbCaseSense then begin
390     FUserWords.Items[i].MatchCase := cbCaseSense.Checked;
391     CheckDuplicate(FSelectedRow);
392   end;
393 
394   if (Sender = cbMatchStartBound) or (Sender = cbMatchEndBound) then begin
395     if cbMatchStartBound.Checked then begin
396       if cbMatchEndBound.Checked
397       then FUserWords.Items[i].MatchWordBounds := soBothBounds
398       else FUserWords.Items[i].MatchWordBounds := soBoundsAtStart;
399     end
400     else begin
401       if cbMatchEndBound.Checked
402       then FUserWords.Items[i].MatchWordBounds := soBoundsAtEnd
403       else FUserWords.Items[i].MatchWordBounds := soNoBounds;
404     end;
405   end;
406 
407 end;
408 
409 procedure TEditorMarkupUserDefinedFrame.tbSelectPageClicked(Sender: TObject);
410 begin
411   if WordList.EditorMode then
412     WordList.EditingDone;
413 
414   if tbMainPage.Down then
415     Notebook1.PageIndex :=  0
416   else
417     Notebook1.PageIndex :=  1;
418 end;
419 
420 procedure TEditorMarkupUserDefinedFrame.WordListButtonClick(Sender: TObject;
421   aCol, aRow: Integer);
422 var
423   i: LongInt;
424 begin
425   if (FUserWords = nil) or (aRow = WordList.RowCount - 1) then
426     exit;
427 
428   inc(FUpdatingDisplay);
429   try
430     i := WordList.UserWordIndex[aRow];
431     if (i >= 0) and (i < FUserWords.Count) then begin
432       FUserWords.Delete(i);
433       i := FSelectedRow;
434       UpdateListDisplay(True);
435       FSelectedRow := i;
436       if (FSelectedRow > aRow) and (FSelectedRow > 0) then
437         dec(FSelectedRow);
438       WordList.Row := FSelectedRow;
439       UpdateTermOptions;
440     end;
441   finally
442     dec(FUpdatingDisplay);
443   end;
444   UpdateTermOptions;
445 end;
446 
447 procedure TEditorMarkupUserDefinedFrame.WordListColRowDeleted(Sender: TObject;
448   IsColumn: Boolean; sIndex, tIndex: Integer);
449 begin
450   if (FUpdatingDisplay > 0) or (FUserWords = nil) then exit;
451   UpdateListDisplay(True);
452 end;
453 
454 procedure TEditorMarkupUserDefinedFrame.WordListEditingDone(Sender: TObject);
455 var
456   i: Integer;
457 begin
458   if (FUpdatingDisplay > 0) or (FUserWords = nil) then exit;
459 
460   i := WordList.UserWordIndex[FSelectedRow];
461   if (i = -1) and (WordList.Cells[0, FSelectedRow] <> '') then begin
462     i := FUserWords.Add.Index;
463     WordList.UserWordIndex[FSelectedRow] := i;
464     WordList.RowCount := WordList.RowCount + 1;
465   end;
466 
467   if (i < 0) or (i >= FUserWords.Count) then
468     exit;
469 
470   if FUserWords.Items[i].SearchTerm = WordList.Cells[0, FSelectedRow] then
471     exit;
472 
473   FUserWords.Items[i].SearchTerm := WordList.Cells[0, FSelectedRow];
474   UpdateTermOptions;
475   CheckDuplicate(FSelectedRow);
476 end;
477 
478 procedure TEditorMarkupUserDefinedFrame.WordListExit(Sender: TObject);
479 begin
480   if WordList.EditorMode then
481     WordList.EditingDone;
482   MaybeCleanEmptyRow(FSelectedRow);
483 end;
484 
485 procedure TEditorMarkupUserDefinedFrame.WordListKeyUp(Sender: TObject; var Key: Word;
486   Shift: TShiftState);
487 var
488   i: PtrInt;
489 begin
490   if (FUpdatingDisplay > 0) or (FUserWords = nil) then exit;
491 
492   i := WordList.UserWordIndex[FSelectedRow];
493   if (i = -1) and (WordList.Cells[0, FSelectedRow] <> '') then begin
494     // Editing in the newly added cell
495     i := FUserWords.Add.Index;
496     WordList.UserWordIndex[FSelectedRow] := i;
497     WordList.RowCount := WordList.RowCount + 1;
498     UpdateTermOptions;
499   end;
500 end;
501 
502 procedure TEditorMarkupUserDefinedFrame.WordListSelection(Sender: TObject; aCol,
503   aRow: Integer);
504 var
505   i: Integer;
506 begin
507   if (FUpdatingDisplay > 0) or (FUserWords = nil) then exit;
508 
509   if WordList.EditorMode then
510     WordList.EditingDone;
511 
512   i := FSelectedRow;
513   FSelectedRow := aRow;
514 
515   MaybeCleanEmptyRow(i);
516   UpdateTermOptions;
517 end;
518 
519 procedure TEditorMarkupUserDefinedFrame.CheckDuplicate(AnIndex: Integer);
520 
521   procedure UpdateDupErrors;
522   var
523     i: Integer;
524   begin
525     i := 0;
526     while (i < FUserWords.Count) do begin
527       if (FUserWords.FindSimilarMatchFor(FUserWords[i], 0, i) >= 0) then
528         WordList.RowFontColor[i] := clRed
529       else
530         WordList.RowFontColor[i] := clDefault;
531       inc(i);
532     end;
533   end;
534 
535 var
536   j: Integer;
537 begin
538   if AnIndex < 0 then begin
539     UpdateDupErrors;
540     exit;
541   end;
542 
543   j := FUserWords.FindSimilarMatchFor(FUserWords[AnIndex], 0, AnIndex);
544   if (j >= 0) then begin
545     if WordList.RowFontColor[FSelectedRow] <> clRed then begin
546       UpdateDupErrors;
547       IDEMessageDialog(dlgMarkupUserDefinedDuplicate,
548                  Format(dlgMarkupUserDefinedDuplicateMsg, [FUserWords[AnIndex].SearchTerm]),
549                  mtConfirmation, [mbOK]);
550     end;
551   end
552   else
553   if WordList.RowFontColor[FSelectedRow] <> clDefault then
554     UpdateDupErrors;
555 end;
556 
557 procedure TEditorMarkupUserDefinedFrame.DoListSelected(Sender: TObject);
558 begin
559   if WordList.EditorMode then
560     WordList.EditingDone;
561 
562   FSelectedListIdx := TMenuItem(Sender).Tag;
563   UpdateListDisplay;
564 end;
565 
566 procedure TEditorMarkupUserDefinedFrame.MaybeCleanEmptyRow(aRow: Integer);
567 var
568   i: PtrInt;
569 begin
570   if (FUpdatingDisplay > 0) or (FUserWords = nil) then exit;
571 
572   inc(FUpdatingDisplay);
573   try
574     i := WordList.UserWordIndex[aRow];
575     if (i < 0) or (i >= FUserWords.Count) then
576       exit;
577 
578     if FUserWords.Items[i].SearchTerm = '' then begin
579       FUserWords.Delete(i);
580       if FSelectedRow > aRow then
581         dec(FSelectedRow);
582       i := FSelectedRow;
583       UpdateListDisplay(True);
584       if i >= WordList.RowCount then
585         dec(i);
586       WordList.Row := i;
587       FSelectedRow := i;
588       UpdateTermOptions; // WordListSelection
589     end;
590 
591   finally
592     dec(FUpdatingDisplay);
593   end;
594 end;
595 
596 procedure TEditorMarkupUserDefinedFrame.UpdateKeys;
597 const
598   NoKey: TIDEShortCut = (Key1: VK_UNKNOWN; Shift1: []; Key2: VK_UNKNOWN; Shift2: [];);
599 begin
600   if (FUserWords = nil) then begin
601     lbKeyAdd1.Caption    := KeyAndShiftStateToEditorKeyString(NoKey);
602     lbKeyAdd2.Caption    := KeyAndShiftStateToEditorKeyString(NoKey);
603     lbKeyRemove1.Caption := KeyAndShiftStateToEditorKeyString(NoKey);
604     lbKeyRemove2.Caption := KeyAndShiftStateToEditorKeyString(NoKey);
605     lbKeyToggle1.Caption := KeyAndShiftStateToEditorKeyString(NoKey);
606     lbKeyToggle2.Caption := KeyAndShiftStateToEditorKeyString(NoKey);
607     exit;
608   end;
609 
610   lbKeyAdd1.Caption    := KeyAndShiftStateToEditorKeyString(FUserWords.AddTermCmd.ShortcutA);
611   lbKeyAdd2.Caption    := KeyAndShiftStateToEditorKeyString(FUserWords.AddTermCmd.ShortcutB);
612   lbKeyRemove1.Caption := KeyAndShiftStateToEditorKeyString(FUserWords.RemoveTermCmd.ShortcutA);
613   lbKeyRemove2.Caption := KeyAndShiftStateToEditorKeyString(FUserWords.RemoveTermCmd.ShortcutB);
614   lbKeyToggle1.Caption := KeyAndShiftStateToEditorKeyString(FUserWords.ToggleTermCmd.ShortcutA);
615   lbKeyToggle2.Caption := KeyAndShiftStateToEditorKeyString(FUserWords.ToggleTermCmd.ShortcutB);
616 end;
617 
618 procedure TEditorMarkupUserDefinedFrame.UpdateTermOptions;
619 var
620   i: PtrInt;
621 begin
622   if (FUserWords = nil) then exit;
623 
624   inc(FUpdatingDisplay);
625   try
626     i := WordList.UserWordIndex[FSelectedRow];
627     cbCaseSense.Enabled       := (i >= 0) and (i < FUserWords.Count);
628     cbMatchStartBound.Enabled := (i >= 0) and (i < FUserWords.Count);
629     cbMatchEndBound.Enabled   := (i >= 0) and (i < FUserWords.Count);
630 
631     if (i < 0) or (i >= FUserWords.Count) then begin
632       cbCaseSense.Checked       := False;
633       cbMatchStartBound.Checked := False;
634       cbMatchEndBound.Checked   := False;
635       exit;
636     end;
637 
638     cbCaseSense.Checked       := FUserWords.Items[i].MatchCase;
639     cbMatchStartBound.Checked := FUserWords.Items[i].MatchWordBounds in [soBoundsAtStart, soBothBounds];
640     cbMatchEndBound.Checked   := FUserWords.Items[i].MatchWordBounds in [soBoundsAtEnd, soBothBounds];
641   finally
642     dec(FUpdatingDisplay);
643   end;
644 end;
645 
646 procedure TEditorMarkupUserDefinedFrame.UpdateListDropDownFull;
647 var
648   m: TMenuItem;
649   i: Integer;
650 begin
651   ListMenu.Items.Clear;
652   if FUserWordsList.Count > 0 then begin
653     for i := 0 to FUserWordsList.Count - 1 do begin
654       m := TMenuItem.Create(ListMenu);
655       m.Caption := FUserWordsList.Lists[i].Name;
656       m.Tag := i;
657       m.OnClick := @DoListSelected;
658       ListMenu.Items.Add(m);
659     end;
660   end;
661   UpdateListDropDownCaption;
662 end;
663 
664 procedure TEditorMarkupUserDefinedFrame.UpdateListDropDownCaption;
665 begin
666   if FUserWordsList.Count = 0 then begin
667     tbSelectList.Enabled := False;
668     tbSelectList.Caption := dlgMarkupUserDefinedNoLists;
669   end
670   else begin
671     tbSelectList.Enabled := True;
672     if (FSelectedListIdx >= 0) and (FSelectedListIdx < FUserWordsList.Count) then
673       tbSelectList.Caption := FUserWordsList.Lists[FSelectedListIdx].Name
674     else
675       tbSelectList.Caption := dlgMarkupUserDefinedNoListsSel;
676   end;
677 end;
678 
679 procedure TEditorMarkupUserDefinedFrame.UpdateListDisplay(KeepDuplicates: Boolean);
680 var
681   i: Integer;
682 begin
683   WordList.EditorMode := False;
684   inc(FUpdatingDisplay);
685 
686   if (FUserWords <> nil) and not (KeepDuplicates) then
687     FUserWords.ClearSimilarMatches;
688 
689   UpdateListDropDownCaption;
690   try
691     if (FSelectedListIdx < 0) or (FSelectedListIdx >= FUserWordsList.Count) then begin
692       FUserWords := nil;
693       MainPanel.Enabled := False;
694       WordList.RowCount := 0;
695       UpdateKeys;
696       exit;
697     end;
698 
699     FUserWords := FUserWordsList.Lists[FSelectedListIdx];
700     MainPanel.Enabled := True;
701     edListName.Text := FUserWords.Name;
702     SynColorAttrEditor1.CurHighlightElement := FUserWords.ColorAttr;
703     WordList.RowCount := FUserWords.Count + 1;
704     WordList.Cells[0, 0] := '';
705     WordList.UserWordIndex[0] := -1;
706     for i := 0 to FUserWords.Count - 1 do begin
707       WordList.Cells[0, i] := FUserWords.Items[i].SearchTerm;
708       WordList.UserWordIndex[i] := FUserWords.Items[i].Index;
709     end;
710     WordList.Cells[0, FUserWords.Count] := '';
711     WordList.UserWordIndex[FUserWords.Count] := -1;
712     FSelectedRow := 0;
713     WordList.Col := 0;
714     WordList.Row := 0;
715 
716     CheckDuplicate(-1);
717     UpdateKeys;
718     cbKeyCase.Checked          := FUserWords.KeyAddCase;
719     cbKeyBoundStart.Checked    := FUserWords.KeyAddTermBounds in [soBoundsAtStart, soBothBounds];
720     cbKeyBoundEnd.Checked      := FUserWords.KeyAddTermBounds in [soBoundsAtEnd, soBothBounds];
721     cbSmartSelectBound.Checked := FUserWords.KeyAddSelectSmart;
722     cbGlobalList.Checked       := FUserWords.GlobalList;
723     edWordMin.Value   := FUserWords.KeyAddWordBoundMaxLen;
724     edSelectMin.Value := FUserWords.KeyAddSelectBoundMaxLen;
725     cbSmartSelectBound.Enabled := FUserWords.KeyAddTermBounds <> soNoBounds;
726     edWordMin.Enabled          := FUserWords.KeyAddTermBounds <> soNoBounds;
727     edSelectMin.Enabled        := FUserWords.KeyAddTermBounds <> soNoBounds;
728   finally
729     dec(FUpdatingDisplay)
730   end;
731   WordListSelection(nil, 0, 0);
732 end;
733 
734 constructor TEditorMarkupUserDefinedFrame.Create(AOwner: TComponent);
735 begin
736   inherited Create(AOwner);
737   FUserWordsList := TEditorUserDefinedWordsList.Create;
738   FUpdatingDisplay := 0;
739 end;
740 
741 destructor TEditorMarkupUserDefinedFrame.Destroy;
742 begin
743   inherited Destroy;
744   FreeAndNil(FUserWordsList);
745 end;
746 
TEditorMarkupUserDefinedFrame.GetTitlenull747 function TEditorMarkupUserDefinedFrame.GetTitle: String;
748 begin
749   Result := dlgMarkupUserDefined;
750 end;
751 
752 procedure TEditorMarkupUserDefinedFrame.Setup(ADialog: TAbstractOptionsEditorDialog);
753 begin
754   SynColorAttrEditor1.Setup;
755   SynColorAttrEditor1.ShowPrior := True;
756   tbNewList.Caption          := dlgMarkupUserDefinedListNew;
757   tbDeleteList.Caption       := dlgMarkupUserDefinedListDel;
758   lbListName.Caption         := dlgMarkupUserDefinedListName;
759   tbMainPage.Caption         := dlgMarkupUserDefinedPageMain;
760   tbKeyPage.Caption          := dlgMarkupUserDefinedPageKeys;
761   cbCaseSense.Caption        := dlgMarkupUserDefinedMatchCase;
762   cbMatchStartBound.Caption  := dlgMarkupUserDefinedMatchStartBound;
763   cbMatchEndBound.Caption    := dlgMarkupUserDefinedMatchEndBound;
764   divKeyAdd.Caption          := dlgMarkupUserDefinedDivKeyAdd;
765   divKeyRemove.Caption       := dlgMarkupUserDefinedDivKeyRemove;
766   divKeyToggle.Caption       := dlgMarkupUserDefinedDivKeyToggle;
767 
768   lbNewKeyOptions.Caption    := dlgMarkupUserDefinedNewByKeyOpts;
769   cbKeyCase.Caption          := dlgMarkupUserDefinedMatchCase;
770   cbKeyBoundStart.Caption    := dlgMarkupUserDefinedMatchStartBound;
771   cbKeyBoundEnd.Caption      := dlgMarkupUserDefinedMatchEndBound;
772   lbKeyBoundMinLen.Caption   := dlgMarkupUserDefinedNewByKeyLen;
773   lbWordMin.Caption          := dlgMarkupUserDefinedNewByKeyLenWord;
774   lbSelectMin.Caption        := dlgMarkupUserDefinedNewByKeyLenSelect;
775   cbSmartSelectBound.Caption := dlgMarkupUserDefinedNewByKeySmartSelect;
776   cbGlobalList.Caption       := dlgMarkupUserDefinedGlobalList;
777 
778   FKeyOptFrame := TEditorKeymappingOptionsFrame(ADialog.FindEditor(TEditorKeymappingOptionsFrame));
779   FUserWordsList.KeyCommandList := FKeyOptFrame.EditingKeyMap;
780   edListName.Text := '';
781 end;
782 
783 procedure TEditorMarkupUserDefinedFrame.ReadSettings(AOptions: TAbstractIDEOptions);
784 begin
785   FGlobalColors := TEditorOptions(AOptions).UserDefinedColors;
786   FSelectedListIdx := 0;
787 
788   FKeyOptFrame.ReadSettings(AOptions);
789 
790   FUserWordsList.Assign(FGlobalColors);
791   FSelectedListIdx := 0;
792   UpdateListDropDownFull;
793   UpdateListDisplay;
794   tbDeleteList.Enabled := FUserWordsList.Count > 0;
795   FGlobalColors := nil;
796   UpdateKeys;
797 end;
798 
799 procedure TEditorMarkupUserDefinedFrame.WriteSettings(AOptions: TAbstractIDEOptions);
800 begin
801   if FGlobalColors <> nil then
802     exit;
803 
804   if FUserWords <> nil then
805     FUserWords.ClearSimilarMatches;
806   TEditorOptions(AOptions).UserDefinedColors.Assign(FUserWordsList);
807 end;
808 
TEditorMarkupUserDefinedFrame.SupportedOptionsClassnull809 class function TEditorMarkupUserDefinedFrame.SupportedOptionsClass: TAbstractIDEOptionsClass;
810 begin
811   Result := TEditorOptions;
812 end;
813 
814 initialization
815   RegisterIDEOptionsEditor(GroupEditor, TEditorMarkupUserDefinedFrame,
816     EdtOptionsUserDefined, EdtOptionsDisplay);
817 end.
818 
819