OSDN Git Service

Fix: Access violation when clicking list view column when no log is active
[winbottle/winbottle.git] / bottleclient / LogForm.pas
1 unit LogForm;
2
3 interface
4
5 uses
6   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
7   ComCtrls, ToolWin, StdCtrls, ExtCtrls, SsParser, BottleDef, Menus,
8   Clipbrd, Logs, ShellAPI, Commctrl, DirectSstp, Contnrs, StrUtils;
9
10 type
11   TSaveLogType = (stLog, stLogWithChannels, stText, stXML);
12
13   TfrmLog = class(TForm)
14     ToolBar: TToolBar;
15     tbtnClear: TToolButton;
16     pnlUpper: TPanel;
17     SsParser: TSsParser;
18     StatusBar: TStatusBar;
19     tbtnSaveLog: TToolButton;
20     PopupMenuPreview: TPopupMenu;
21     mnPopCopy: TMenuItem;
22     tbtnVoteMessage: TToolButton;
23     PopupMenuListView: TPopupMenu;
24     mnPopUpVoteMessage: TMenuItem;
25     SaveDialog: TSaveDialog;
26     pnlPanel: TPanel;
27     Splitter: TSplitter;
28     edtScript: TRichEdit;
29     mnPopUpCopyScript: TMenuItem;
30     PopupMenuSaveLog: TPopupMenu;
31     mnSaveLog: TMenuItem;
32     mnSaveLogChannel: TMenuItem;
33     mnSaveLogScript: TMenuItem;
34     mnSaveLogXML: TMenuItem;
35     ToolButton1: TToolButton;
36     mnJumpURL: TMenuItem;
37     mnPopUpAgreeMessage: TMenuItem;
38     tbtnAgreeMessage: TToolButton;
39     ToolButton2: TToolButton;
40     tbtnPreviewStyle: TToolButton;
41     PopupMenuPreviewStyle: TPopupMenu;
42     mnPreviewStyleConversation: TMenuItem;
43     mnPreviewStyleScript: TMenuItem;
44     mnPreviewStyleScriptWithLineBreak: TMenuItem;
45     Panel1: TPanel;
46     tabBottleLog: TTabControl;
47     lvwLog: TListView;
48     tbtnDownloadLog: TToolButton;
49     PopupMenuTab: TPopupMenu;
50     mnCloseTab: TMenuItem;
51     tbtnFindBottle: TToolButton;
52     tbtnOpenLog: TToolButton;
53     OpenDialog: TOpenDialog;
54     tbtnInsertCue: TToolButton;
55     mnInsertCue: TMenuItem;
56     PopupMenuListPreviewStyle: TPopupMenu;
57     mnListPreviewStyleNormal: TMenuItem;
58     mnListPreviewStyleTagStripped: TMenuItem;
59     tbtnListPreviewStyle: TToolButton;
60     procedure tbtnClearClick(Sender: TObject);
61     procedure FormCreate(Sender: TObject);
62     procedure lvwLogChange(Sender: TObject; Item: TListItem;
63       Change: TItemChange);
64     procedure lvwLogDblClick(Sender: TObject);
65     procedure lvwLogKeyPress(Sender: TObject; var Key: Char);
66     procedure FormDestroy(Sender: TObject);
67     procedure lvwLogClick(Sender: TObject);
68     procedure mnSaveLogClick(Sender: TObject);
69     procedure lvwLogColumnClick(Sender: TObject; Column: TListColumn);
70     procedure mnPopUpCopyScriptClick(Sender: TObject);
71     procedure mnSaveLogChannelClick(Sender: TObject);
72     procedure mnSaveLogScriptClick(Sender: TObject);
73     procedure mnSaveLogXMLClick(Sender: TObject);
74     procedure lvwLogData(Sender: TObject; Item: TListItem);
75     procedure PopupMenuListViewPopup(Sender: TObject);
76     procedure lvwLogCustomDrawItem(Sender: TCustomListView;
77       Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
78     procedure PopupMenuPreviewStylePopup(Sender: TObject);
79     procedure mnPreviewStyleClick(Sender: TObject);
80     procedure tbtnPreviewStyleClick(Sender: TObject);
81     procedure tabBottleLogChange(Sender: TObject);
82     procedure tabBottleLogChanging(Sender: TObject;
83       var AllowChange: Boolean);
84     procedure tabBottleLogContextPopup(Sender: TObject; MousePos: TPoint;
85       var Handled: Boolean);
86     procedure mnCloseTabClick(Sender: TObject);
87     procedure tbtnFindBottleClick(Sender: TObject);
88     procedure tbtnOpenLogClick(Sender: TObject);
89     procedure tabBottleLogMouseDown(Sender: TObject; Button: TMouseButton;
90       Shift: TShiftState; X, Y: Integer);
91     procedure tabBottleLogDragOver(Sender, Source: TObject; X, Y: Integer;
92       State: TDragState; var Accept: Boolean);
93     procedure tabBottleLogDragDrop(Sender, Source: TObject; X, Y: Integer);
94     procedure tabBottleLogEndDrag(Sender, Target: TObject; X, Y: Integer);
95     procedure lvwLogDrawItem(Sender: TCustomListView; Item: TListItem;
96       Rect: TRect; State: TOwnerDrawState);
97     procedure mnListPreviewStyleTagStrippedClick(Sender: TObject);
98     procedure tbtnListPreviewStyleClick(Sender: TObject);
99     procedure PopupMenuListPreviewStylePopup(Sender: TObject);
100   private
101     { Private \90é\8c¾ }
102     FLastScript: String; //\83X\83N\83\8a\83v\83g\8dÄ\95`\89æ\97}\90§\97p
103     FBottleLogList: TObjectList;
104     //
105     FDragTabIndex: integer; //\83^\83u\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
106     FDragTabDest: integer;  //\83h\83\8d\83b\83v\82·\82é\88Ê\92u(\82·\82®\89E\82É\82­\82é\83^\83u\82Ì\83C\83\93\83f\83b\83N\83X)
107     //
108     procedure UpdateScript(const Script: String);
109     procedure UpdateScriptConversationColor(const Script: String);
110     procedure UpdateScriptScript(const Script: String);
111     procedure mnURLClick(Sender: TObject);
112     procedure ExtractURLs(Script: String; Result: TStrings);
113     function GetDefaultFileName(const Name: String; const Ext: String): String;
114     function BottleLogTitled(const LogName: String): TBottleLogList;
115     procedure DrawSingleLineScript(const Script: String; const Rect: TRect;
116       Item: TListItem);
117   protected
118     procedure CreateParams(var Params: TCreateParams); override;
119   public
120     { Public \90é\8c¾ }
121     function SelectedBottleLog: TBottleLogList;
122     property BottleLogList: TObjectList read FBottleLogList;
123     procedure AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String);
124     procedure AddCurrentSystemLog(const LogName, MessageString: String);
125     procedure VoteLog(const MID: String; const Vote: integer);
126     procedure AgreeLog(const MID: String; const Agree: integer);
127     procedure SetBottleState(const MID: String; State: TLogState);
128     procedure AllBottleOpened;
129     procedure LogLoaded(Sender: TObject);
130     procedure LogLoadFailure(Sender: TObject; const Message: String);
131     procedure LogLoadWork(Sender: TObject);
132     procedure UpdateTab;
133     procedure UpdateWindow;
134     procedure SelAndFocusMessage(const MID: String);
135   end;
136
137
138 var
139   frmLog: TfrmLog;
140
141 const
142   IconBottle    = 17;
143   IconOpened    = 30;
144   IconPlaying   = 31;
145   IconSystemLog = 26;
146   SubChannel    = 0;
147   SubGhost      = 1;
148   SubVotes      = 2;
149   SubAgrees     = 3;
150   SubScript     = 4;
151
152 implementation
153
154 uses MainForm;
155
156 {$R *.DFM}
157
158 { TfrmLog }
159
160 procedure TfrmLog.AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String);
161 var Sel: integer;
162 begin
163   BottleLogTitled(LogName).AddScriptLog(Script, Channel, MID, Ghost);
164   if SelectedBottleLog <> BottleLogTitled(LogName) then Exit;
165   lvwLog.OnChange := nil; //\83C\83x\83\93\83g\94­\90¶(\82¢\82ë\82¢\82ë\8dÄ\95`\89æ\82ª\8bN\82«\82é)\82Ì\97}\90§
166   if lvwLog.Selected <> nil then Sel := lvwLog.Selected.Index else Sel := -1;
167   lvwLog.Items.Count := SelectedBottleLog.Count;
168   UpdateWindow;
169   if Sel >= 0 then begin
170     lvwLog.Selected := lvwLog.Items[Sel + 1];
171     lvwLog.Selected.Focused := true;
172   end;
173   if not lvwLog.Focused then
174     ListView_Scroll(lvwLog.Handle, 0, High(integer));
175   lvwLog.OnChange := lvwLogChange;
176 end;
177
178 procedure TfrmLog.AddCurrentSystemLog(const LogName, MessageString: String);
179 var Sel: integer;
180 begin
181   BottleLogTitled(LogName).AddSystemLog(MessageString);
182   if SelectedBottleLog <> BottleLogTitled(LogName) then Exit;
183   lvwLog.OnChange := nil;
184   if lvwLog.Selected <> nil then Sel := lvwLog.Selected.Index else Sel := -1;
185   lvwLog.Items.Count := SelectedBottleLog.Count;
186   UpdateWindow;
187   if Sel >= 0 then begin
188     lvwLog.Selected := lvwLog.Items[Sel + 1];
189     lvwLog.Selected.Focused := true;
190   end;
191   if not lvwLog.Focused then
192     ListView_Scroll(lvwLog.Handle, 0, High(integer));
193   lvwLog.OnChange := lvwLogChange;
194 end;
195
196
197
198 procedure TfrmLog.tbtnClearClick(Sender: TObject);
199 begin
200   if SelectedBottleLog = nil then Exit;
201   FBottleLogList.Delete(tabBottleLog.TabIndex);
202   tabBottleLog.TabIndex := 0;
203   UpdateTab;
204   UpdateWindow;
205   lvwLogChange(Self, nil, ctState);
206 end;
207
208 procedure TfrmLog.FormCreate(Sender: TObject);
209 var i: integer;
210 begin
211   FBottleLogList := TObjectList.Create;
212
213   SsParser.TagPattern.Assign(frmSender.SsParser.TagPattern);
214   SsParser.MetaPattern.Assign(frmSender.SsParser.MetaPattern);
215
216   with Pref.LogWindowPosition do begin
217     Self.Left   := Left;
218     Self.Top    := Top;
219     Self.Width  := Right - Left + 1;
220     Self.Height := Bottom - Top + 1;
221   end;
222   lvwLog.DoubleBuffered := true;
223   edtScript.Height := Pref.LogWindowDividerPos;
224
225   i := 0;
226   while Token(Pref.LogWindowColumnWidth, ',', i) <> '' do begin
227     lvwLog.Columns[i].Width := StrToIntDef(Token(Pref.LogWindowColumnWidth, ',', i), 100);
228     Inc(i);
229   end;
230
231   UpdateWindow; // Reset window color and enabled status of some buttons
232 end;
233
234 procedure TfrmLog.FormDestroy(Sender: TObject);
235 var i: integer;
236     WidthStr: String;
237 begin
238   WidthStr := '';
239   for i := 0 to lvwLog.Columns.Count-1 do begin
240     if i > 0 then WidthStr := WidthStr + ',';
241     WidthStr := WidthStr + IntToStr(lvwLog.Column[i].Width);
242   end;
243   Pref.LogWindowColumnWidth := WidthStr;
244
245   with Pref.LogWindowPosition do begin
246     Left   := Self.Left;
247     Top    := Self.Top;
248     Right  := Self.Left + Self.Width - 1;
249     Bottom := Self.Top + Self.Height - 1;
250   end;
251   Pref.LogWindowDividerPos := edtScript.Height;
252
253   FreeAndNil(FBottleLogList);
254 end;
255
256 procedure TfrmLog.lvwLogChange(Sender: TObject; Item: TListItem;
257   Change: TItemChange);
258 var Script: String;
259     Log: TLogItem;
260 begin
261   if SelectedBottleLog <> nil then begin
262     StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
263     if Change = ctState then begin
264       Script := '';
265       if lvwLog.Selected <> nil then begin
266         Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
267         if (Log.LogType = ltBottle) and not frmSender.Connecting then begin
268           Script := Log.Script;
269           frmSender.actVoteMessage.Enabled := true;
270           frmSender.actAgreeMessage.Enabled := true;
271           frmSender.actInsertCue.Enabled := true;
272           mnPopUpCopyScript.Enabled := true;
273           StatusBar.Panels[1].Text := Format('%d\83o\83C\83g - \83_\83u\83\8b\83N\83\8a\83b\83N\82Å\8dÄ\90¶', [Length(Log.Script)]);
274           UpdateScript(Script);
275         end else begin
276           frmSender.actVoteMessage.Enabled := false;
277           frmSender.actAgreeMessage.Enabled := false;
278           frmSender.actInsertCue.Enabled := false;
279           mnPopUpCopyScript.Enabled := false;
280           StatusBar.Panels[1].Text := '';
281           UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\82ð\83N\83\8a\83A
282         end;
283       end else begin
284         frmSender.actVoteMessage.Enabled := false;
285         frmSender.actAgreeMessage.Enabled := false;
286         frmSender.actInsertCue.Enabled := false;
287         mnPopUpCopyScript.Enabled := false;
288         StatusBar.Panels[1].Text := '';
289         UpdateScript(Script); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
290       end;
291     end;
292     tbtnSaveLog.Enabled := lvwLog.Items.Count > 0;
293   end else begin
294     frmSender.actVoteMessage.Enabled := false;
295     frmSender.actAgreeMessage.Enabled := false;
296     frmSender.actInsertCue.Enabled := false;
297     mnPopUpCopyScript.Enabled := false;
298     StatusBar.Panels[0].Text := '';
299     UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
300   end;
301 end;
302
303 procedure TfrmLog.lvwLogDblClick(Sender: TObject);
304 var Script: String;
305     Log, CueItem: TLogItem;
306 begin
307   if lvwLog.Selected = nil then
308     Exit;
309   Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
310   if Log = nil then Exit;
311   if Log.LogType <> ltBottle then
312     Exit;
313   Script := frmSender.ScriptTransForSSTP(Log.Script);
314   if Script = '' then begin
315     ShowMessage('\96â\91è\82Ì\82 \82é\83X\83N\83\8a\83v\83g\82Å\82·\81B\8dÄ\90\82Å\82«\82Ü\82¹\82ñ\81B'+
316       '\83N\83\89\83C\83A\83\93\83g\82ð\8dÅ\90V\94Å\82É\82µ\82Ä\82Ý\82Ä\82­\82¾\82³\82¢\81B');
317     Exit;
318   end;
319
320   CueItem := TLogItem.Create(Log);
321   try
322     CueItem.Script := Script;
323     frmSender.BottleSstp.Unshift(CueItem);
324   except
325     CueItem.Free;
326   end;
327 end;
328
329 procedure TfrmLog.UpdateScriptConversationColor(const Script: String);
330 var i: integer;
331     scr: String;
332     UnyuTalking, Talked, InSynchronized: boolean;
333 begin
334   scr := Script;
335   frmSender.DoTrans(scr, [toConvertURL]);
336   SsParser.LeaveEscape := false;
337   SsParser.InputString := scr;
338   SsParser.LeaveEscape := true;
339   UnyuTalking := false;
340   Talked := false; //'\h\u\h\u'\82Ì\82æ\82¤\82È\83X\83N\83\8a\83v\83g\82Å\8bó\82«\8ds\82ð\8dì\82ç\82È\82¢\82½\82ß\82Ì\91[\92u
341   InSynchronized := false;
342   edtScript.Text := '';
343   edtScript.Color := Pref.BgColor;
344   for i := 0 to SsParser.Count-1 do begin
345     if (SsParser[i] = '\_s') and not InSynchronized then begin
346       InSynchronized := true;
347       if Talked then begin
348         edtScript.SelText := #13#10;
349         Talked := false;
350       end;
351     end else if (SsParser[i] = '\_s') and InSynchronized then begin
352       InSynchronized := false;
353       if Talked then begin
354         edtScript.SelText := #13#10;
355         Talked := false;
356       end;
357     end;
358     if (SsParser[i] = '\u') and not UnyuTalking then begin
359       UnyuTalking := true;
360       if Talked then begin
361         edtScript.SelText := #13#10;
362         Talked := false;
363       end;
364     end;
365     if (SsParser[i] = '\h') and UnyuTalking then begin
366       UnyuTalking := false;
367       if Talked then begin
368         edtScript.SelText := #13#10;
369         Talked := false;
370       end;
371     end;
372     if SsParser.MarkUpType[i] = mtStr then begin
373       if InSynchronized then
374         edtScript.SelAttributes.Color := Pref.TalkColorS
375       else if UnyuTalking then
376         edtScript.SelAttributes.Color := Pref.TalkColorU
377       else
378         edtScript.SelAttributes.Color := Pref.TalkColorH;
379       edtScript.SelText := SsParser[i];
380       Talked := true;
381     end;
382     if SsParser.MarkUpType[i] = mtMeta then begin
383       edtScript.SelAttributes.Color := Pref.MetaWordColor;
384       edtScript.SelText := SsParser[i];
385       Talked := true;
386     end;
387   end;
388 end;
389
390 procedure TfrmLog.lvwLogKeyPress(Sender: TObject; var Key: Char);
391 begin
392   if Key = #13 then lvwLogDblClick(Sender);
393 end;
394
395 procedure TfrmLog.CreateParams(var Params: TCreateParams);
396 begin
397   inherited;
398   Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
399 end;
400
401 procedure TfrmLog.lvwLogClick(Sender: TObject);
402 begin
403   //\89E\83N\83\8a\83b\83N\82Å\83\81\83j\83\85\81[\8fo\82·\82Æ\82«\82É\94­\90\82·\82é\95s\8bï\8d\87\91Î\8dô
404   with lvwLog do
405     Selected := Selected;
406 end;
407
408 procedure TfrmLog.lvwLogColumnClick(Sender: TObject; Column: TListColumn);
409 var SortType: TBottleLogSortType;
410     SelectedMID: String;
411     SortColumn: integer;
412 begin
413   if SelectedBottleLog = nil then
414     Exit;
415   if lvwLog.Selected <> nil then
416     SelectedMID := SelectedBottleLog.Bottles[lvwLog.Selected.Index].MID
417   else
418     SelectedMID := '';
419
420   SortColumn := Column.Index;
421   case SortColumn-1 of
422     -1: SortType := stLogTime;
423     subChannel: SortType := stChannel;
424     subGhost:   SortType := stGhost;
425     subVotes:   SortType := stVote;
426     subAgrees:  SortType := stAgree;
427     subScript:  SortType := stScript;
428   else
429     SortType := stLogTime;
430   end;
431
432   SelectedBottleLog.SortBottles(SortType);
433   lvwLog.Invalidate;
434   if Length(SelectedMID) > 0 then
435     SelAndFocusMessage(SelectedMID);
436 end;
437
438
439 procedure TfrmLog.mnPopUpCopyScriptClick(Sender: TObject);
440 var
441   Log: TLogItem;
442   Clip: TClipBoard;
443 begin
444   Log := SelectedBottleLog.Bottles[frmLog.lvwLog.Selected.Index];
445   if Log = nil then Exit;
446   Clip := ClipBoard();
447   Clip.SetTextBuf(PChar(Log.Script));
448 end;
449
450 procedure TfrmLog.SetBottleState(const MID: String; State: TLogState);
451 var i: integer;
452     Bottle: TLogItem;
453 begin
454   for i := 0 to FBottleLogList.Count-1 do begin
455     Bottle := (FBottleLogList[i] as TBottleLogList).Bottle(MID);
456     if Bottle <> nil then begin
457       Bottle.State := State;
458       lvwLog.OnChange := nil;
459       lvwLog.Invalidate;
460       lvwLog.OnChange := lvwLogChange;
461     end;
462   end;
463 end;
464
465 procedure TfrmLog.mnSaveLogClick(Sender: TObject);
466 begin
467   if SelectedBottleLog = nil then Exit;
468   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
469   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
470   SaveDialog.DefaultExt := 'log';
471   SaveDialog.FilterIndex := 1;
472   if SaveDialog.Execute then
473     SelectedBottleLog.SaveToSstpLog(SaveDialog.FileName, false);
474 end;
475
476 procedure TfrmLog.mnSaveLogChannelClick(Sender: TObject);
477 begin
478   if SelectedBottleLog = nil then Exit;
479   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
480   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
481   SaveDialog.DefaultExt := 'log';
482   SaveDialog.FilterIndex := 1;
483   if SaveDialog.Execute then
484     SelectedBottleLog.SaveToSstpLog(SaveDialog.FileName, true);
485 end;
486
487 procedure TfrmLog.mnSaveLogScriptClick(Sender: TObject);
488 begin
489   if SelectedBottleLog = nil then Exit;
490   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.txt');
491   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
492   SaveDialog.DefaultExt := 'txt';
493   SaveDialog.FilterIndex := 2;
494   if SaveDialog.Execute then
495     SelectedBottleLog.SaveToText(SaveDialog.FileName);
496 end;
497
498 procedure TfrmLog.mnSaveLogXMLClick(Sender: TObject);
499 begin
500   if SelectedBottleLog = nil then Exit;
501   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.xml');
502   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
503   SaveDialog.DefaultExt := 'xml';
504   SaveDialog.FilterIndex := 3;
505   if SaveDialog.Execute then
506     SelectedBottleLog.SaveToXmlFile(SaveDialog.FileName);
507 end;
508
509 procedure TfrmLog.lvwLogData(Sender: TObject; Item: TListItem);
510 var i: integer;
511     Log: TLogItem;
512 begin
513   if Item = nil then Exit;
514   i := Item.Index;
515   Log := SelectedBottleLog.Bottles[i];
516   with Item do begin
517     Caption := FormatDateTime('yy/mm/dd hh:nn:ss', Log.LogTime);
518     SubItems.Clear;
519     SubItems.Add(Log.Channel);
520     SubItems.Add(Log.Ghost);
521     if Log.LogType = ltBottle then begin
522       if Log.Votes > 0 then
523         SubItems.Add(IntToStr(Log.Votes))
524       else
525         SubItems.Add('');
526       if Log.Agrees > 0 then
527         SubItems.Add(IntToStr(Log.Agrees))
528       else
529         SubItems.Add('');
530     end else begin
531       // \83V\83X\83e\83\80\83\8d\83O\82È\82Ç\82Í\93\8a\95[\81E\93¯\88Ó\82ð\95\\8e¦\82µ\82È\82¢
532       SubItems.Add('');
533       SubItems.Add('');
534     end;
535     SubItems.Add(Log.Script);
536
537     if Log.LogType = ltBottle then begin
538       case Log.State of
539         lsUnopened: ImageIndex := IconBottle;
540         lsPlaying:  ImageIndex := IconPlaying;
541         lsOpened:   ImageIndex := IconOpened;
542       end;
543     end else
544       ImageIndex := IconSystemLog;
545   end;
546 end;
547
548 procedure TfrmLog.UpdateWindow;
549 var EnabledFlag: boolean;
550 begin
551   if true then begin // ColorScript
552     if lvwLog.Color <> Pref.BgColor then lvwLog.Color := Pref.BgColor;
553     if lvwLog.Font.Color <> Pref.TalkColorH then lvwLog.Font.Color := Pref.TalkColorH;
554   end else begin
555     if lvwLog.Color <> clWindow then lvwLog.Color := clWindow;
556     if lvwLog.Font.Color <> clWindowText then lvwLog.Font.Color := clWindowText;
557   end;
558   if SelectedBottleLog <> nil then begin
559     Caption := '\83\8d\83O - ' + SelectedBottleLog.Title;
560     StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
561     lvwLog.Items.Count := SelectedBottleLog.Count;
562   end else begin
563     Caption := '\83\8d\83O';
564     StatusBar.Panels[0].Text := '';
565     StatusBar.Panels[1].Text := '';
566     lvwLog.Items.Count := 0;
567   end;
568
569   EnabledFlag := SelectedBottleLog <> nil;
570   tbtnClear.Enabled := EnabledFlag;
571   tbtnSaveLog.Enabled := EnabledFlag;
572   tbtnFindBottle.Enabled := EnabledFlag;
573
574   lvwLog.Invalidate;
575 end;
576
577 procedure TfrmLog.PopupMenuListViewPopup(Sender: TObject);
578 var Log: TLogItem;
579     Child: TMenuItem;
580     Urls: TStringList;
581     i: integer;
582 begin
583   for i := mnJumpURL.Count-1 downto 0 do begin
584     mnJumpURL.Items[i].Free;
585   end;
586   mnJumpURL.Enabled := false;
587   if lvwLog.Selected = nil then Exit;
588   Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
589   if Log = nil then Exit;
590   Urls := TStringList.Create;
591   try
592     ExtractURLs(Log.Script, Urls);
593     for i := 0 to Urls.Count-1 do begin
594       Child := TMenuItem.Create(Self);
595       with Child do begin
596         Caption := Format('(&%d) %s', [i+1, StringReplace(Urls[i], '&', '&&', [rfReplaceAll])]);
597         Tag := i;
598         OnClick := mnURLClick;
599         AutoHotkeys := maManual;
600         mnJumpURL.Add(Child);
601       end;
602     end;
603     mnJumpURL.Enabled := Urls.Count > 0;
604   finally
605     Urls.Free;
606   end;
607 end;
608
609 procedure TfrmLog.mnURLClick(Sender: TObject);
610 var LogItem: TLogItem;
611     URL: String;
612     Urls: TStringList;
613 begin
614   if (lvwLog.Selected = nil) or (SelectedBottleLog = nil) then Exit;
615   LogItem := SelectedBottleLog[lvwLog.Selected.Index] as TLogItem;
616   Urls := TStringList.Create;
617   try
618     ExtractURLs(LogItem.Script, Urls);
619     URL := Urls[(Sender as TMenuItem).Tag];
620     ShellExecute(Handle, 'open', PChar(URL), nil, nil, SW_SHOW);
621   finally
622     Urls.Free;
623   end;
624 end;
625
626 procedure TfrmLog.ExtractURLs(Script: String; Result: TStrings);
627 var i, u, j: integer;
628     s: String;
629 begin
630   Result.Clear;
631   SsParser.InputString := Script;
632   for i := 0 to SsParser.Count-1 do begin
633     if (SsParser.Match(SsParser[i], '\URL%b') > 0) then begin
634       for u := 7 downto 1 do begin
635         if (SsParser.Match(SsParser[i],
636             '\URL%b'+StringReplace(StringOfChar('-', u*2),
637             '-', '%b', [rfReplaceAll]))) > 0 then begin
638           for j := 1 to u do begin
639             s := SsParser.GetParam(SsParser[i], j*2);
640             if Pos('http://', s) > 0 then Result.Add(s);
641           end;
642           Break;
643         end;
644       end;
645       if SsParser.Match(SsParser[i], '\URL%b%b') = 0 then begin //\8aÈ\88Õ\94ÅURL\95Ï\8a·
646         //\8aÈ\88Õ\8c`\8e®\URL\83^\83O\95Ï\8a·
647         s := SsParser.GetParam(SsParser[i], 1);
648         if Pos('http://', s) > 0 then Result.Add(s);
649       end;
650     end;
651   end;
652 end;
653
654 procedure TfrmLog.SelAndFocusMessage(const MID: String);
655 var i: integer;
656     Log: TLogItem;
657 begin
658   for i := 0 to SelectedBottleLog.Count-1 do begin
659     Log := SelectedBottleLog.Items[i] as TLogItem;
660     if Log.MID = MID then begin
661       lvwLog.Items[i].Selected := true;
662       lvwLog.Items[i].Focused := true;
663     end;
664   end;
665 end;
666
667 procedure TfrmLog.lvwLogCustomDrawItem(Sender: TCustomListView;
668   Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
669 begin
670   //
671 end;
672
673 procedure TfrmLog.UpdateScript(const Script: String);
674 begin
675   if Script <> FLastScript then begin
676     if Pref.LogWindowPreviewStyle = psConversation then begin
677       UpdateScriptConversationColor(Script);
678     end else begin
679       UpdateScriptScript(Script);
680     end;
681     SendMessage(edtScript.Handle, EM_LINESCROLL, Low(integer), Low(integer)); //\83X\83N\83\8d\81[\83\8b\96ß\82µ
682     FLastScript := Script;
683   end;
684 end;
685
686 procedure TfrmLog.PopupMenuPreviewStylePopup(Sender: TObject);
687 var i: integer;
688 begin
689   with PopupMenuPreviewStyle do
690     for i := 0 to Items.Count-1 do
691       Items[i].Checked := Items[i].Tag = Ord(Pref.LogWindowPreviewStyle)
692 end;
693
694 procedure TfrmLog.mnPreviewStyleClick(Sender: TObject);
695 var i: integer;
696 begin
697   with PopupMenuPreviewStyle do
698     for i := 0 to Items.Count-1 do
699       Items[i].Checked := (Sender as TMenuItem).Tag = Items[i].Tag;
700   Pref.LogWindowPreviewStyle := TLogWindowPreviewStyle((Sender as TMenuItem).Tag);
701   FLastScript := '';
702   lvwLogChange(self, lvwLog.Selected, ctState);
703 end;
704
705 procedure TfrmLog.UpdateScriptScript(const Script: String);
706 var
707   UnyuTalking, InSynchronized: boolean;
708   i: integer;
709 begin
710   edtScript.Color := Pref.BgColor;
711   SsParser.LeaveEscape := true;
712   SsParser.InputString := Script;
713   edtScript.Text := '';
714   edtScript.SelAttributes.Color := clWindowText;
715   UnyuTalking := false;
716   InSynchronized := false;
717   for i := 0 to SsParser.Count-1 do begin
718     case SsParser.MarkUpType[i] of
719       mtStr: begin
720         if InSynchronized then
721           edtScript.SelAttributes.Color := Pref.TalkColorS
722         else if UnyuTalking then
723           edtScript.SelAttributes.Color := Pref.TalkColorU
724         else
725           edtScript.SelAttributes.Color := Pref.TalkColorH;
726       end;
727       mtTag: begin
728         edtScript.SelAttributes.Color := Pref.MarkUpColor;
729         if SsParser[i] = '\h' then
730           UnyuTalking := false
731         else if SsParser[i] = '\u' then
732           UnyuTalking := true
733         else if SsParser[i] = '\_s' then
734           InSynchronized := not InSynchronized;
735       end;
736       mtMeta:   edtScript.SelAttributes.Color := Pref.MetaWordColor;
737       mtTagErr: edtScript.SelAttributes.Color := Pref.MarkErrorColor;
738     end;
739     edtScript.SelText := SsParser[i];
740     if (SsParser[i] = '\n') and (Pref.LogWindowPreviewStyle = psScriptWithLineBreak) then
741       edtScript.SelText := #13#10;
742   end;
743 end;
744
745 procedure TfrmLog.tbtnPreviewStyleClick(Sender: TObject);
746 var sel: integer;
747 begin
748   sel := Ord(Pref.LogWindowPreviewStyle);
749   sel := sel + 1;
750   if sel > Ord(High(TLogWindowPreviewStyle)) then sel := 0;
751   Pref.LogWindowPreviewStyle := TLogWindowPreviewStyle(sel);
752   FLastScript := '';
753   lvwLogChange(self, lvwLog.Selected, ctState);
754 end;
755
756 function TfrmLog.SelectedBottleLog: TBottleLogList;
757 begin
758   if tabBottleLog.TabIndex >= 0 then
759     Result := FBottleLogList.Items[tabBottleLog.TabIndex] as TBottleLogList
760   else
761     Result := nil;
762 end;
763
764 procedure TfrmLog.tabBottleLogChange(Sender: TObject);
765 begin
766   UpdateWindow;
767   if SelectedBottleLog.SelectedIndex >= 0 then begin
768     lvwLog.Items[SelectedBottleLog.SelectedIndex].Selected := true;
769     if lvwLog.Focused then lvwLog.Selected.Focused := true;
770   end;
771   lvwLogChange(Self, nil, ctState);
772 end;
773
774 procedure TfrmLog.LogLoaded(Sender: TObject);
775 begin
776   if SelectedBottleLog = Sender then begin
777     UpdateWindow;
778   end;
779 end;
780
781 procedure TfrmLog.UpdateTab;
782 var i, cur: integer;
783 begin
784   cur := tabBottleLog.tabIndex;
785   tabBottleLog.Tabs.Clear;
786   for i := 0 to FBottleLogList.Count - 1 do begin
787     tabBottleLog.Tabs.Add((FBottleLogList[i] as TBottleLogList).Title);
788   end;
789   if FBottleLogList.Count > 0 then begin
790     if cur < FBottleLogList.Count then
791       tabBottleLog.TabIndex := cur
792     else
793       tabBottleLog.TabIndex := FBottleLogList.Count-1;
794   end;
795 end;
796
797 procedure TfrmLog.LogLoadFailure(Sender: TObject; const Message: String);
798 begin
799   Beep;
800   ShowMessage(Message);
801   if Sender = SelectedBottleLog then UpdateWindow;
802 end;
803
804 procedure TfrmLog.AgreeLog(const MID: String; const Agree: integer);
805 var i: integer;
806     flag: boolean;
807 begin
808   flag := false;
809   for i := 0 to FBottleLogList.Count - 1 do begin
810     if (FBottleLogList[i] as TBottleLogList).Bottle(MID) <> nil then begin
811       (FBottleLogList[i] as TBottleLogList).Bottle(MID).Agrees := Agree;
812       flag := true;
813     end;
814   end;
815   if flag then lvwLog.Invalidate;
816 end;
817
818 procedure TfrmLog.VoteLog(const MID: String; const Vote: integer);
819 var i: integer;
820     flag: boolean;
821 begin
822   flag := false;
823   for i := 0 to FBottleLogList.Count - 1 do begin
824     if (FBottleLogList[i] as TBottleLogList).Bottle(MID) <> nil then begin
825       (FBottleLogList[i] as TBottleLogList).Bottle(MID).Votes := Vote;
826       flag := true;
827     end;
828   end;
829   if flag then lvwLog.Invalidate;
830 end;
831
832 procedure TfrmLog.tabBottleLogChanging(Sender: TObject;
833   var AllowChange: Boolean);
834 begin
835   // \8c»\8dÝ\91I\91ð\82³\82ê\82Ä\82¢\82é\83\8d\83O\82Ì\91I\91ð\8fó\91Ô\82ð\95Û\91
836   if SelectedBottleLog = nil then Exit;
837   if lvwLog.Selected <> nil then
838     SelectedBottleLog.SelectedIndex := lvwLog.Selected.Index
839   else
840     SelectedBottleLog.SelectedIndex := -1;
841 end;
842
843 procedure TfrmLog.tabBottleLogContextPopup(Sender: TObject;
844   MousePos: TPoint; var Handled: Boolean);
845 begin
846   with tabBottleLog do begin
847     Tag := IndexOfTabAt(MousePos.X, MousePos.Y);
848     if Tag < 0 then Handled := true;
849   end;
850 end;
851
852 procedure TfrmLog.mnCloseTabClick(Sender: TObject);
853 begin
854   FBottleLogList.Delete(tabBottleLog.Tag);
855   UpdateTab;
856   UpdateWindow;
857   lvwLogChange(Self, nil, ctState);
858 end;
859
860 procedure TfrmLog.tbtnFindBottleClick(Sender: TObject);
861 var Query: String;
862     ResultLog: TBottleLogList;
863     Item1, Item2: TLogItem;
864     i, matched: integer;
865 begin
866   if SelectedBottleLog = nil then Exit;
867   if SelectedBottleLog.Count = 0 then begin
868     ShowMessage('\8c\9f\8dõ\91Î\8fÛ\82ª\8bó\82Å\82·\81B');
869     Exit;
870   end;
871   Query := '';
872   matched := 0;
873   if InputQuery('\83X\83N\83\8a\83v\83g\96{\95\82ð\8c\9f\8dõ', '\8c\9f\8dõ\95\8e\9a\97ñ', Query) then begin
874     if Query = '' then Exit;
875     ResultLog := TBottleLogList.Create('\8c\9f\8dõ\8c\8b\89Ê');
876     for i := 0 to SelectedBottleLog.Count-1 do begin
877       Item1 := SelectedBottleLog.Items[i] as TLogItem;
878       if AnsiContainsText(Item1.Script, Query) and (Item1.LogType = ltBottle) then begin
879         matched := matched + 1;
880         Item2 := TLogItem.Create(ltBottle, Item1.MID, Item1.Channel,
881           Item1.Script, Item1.Ghost, Item1.LogTime);
882         Item2.State := lsOpened;
883         Item2.Votes := Item1.Votes;
884         Item2.Agrees := Item1.Agrees;
885         ResultLog.Add(Item2);
886       end;
887     end;
888     if matched = 0 then
889       ResultLog.AddSystemLog('\8c©\82Â\82©\82è\82Ü\82¹\82ñ\82Å\82µ\82½');
890     BottleLogList.Add(ResultLog);
891     UpdateTab;
892     tabBottleLog.TabIndex := BottleLogList.Count-1;
893     UpdateWindow;
894   end;
895 end;
896
897 procedure TfrmLog.tbtnOpenLogClick(Sender: TObject);
898 var BottleLog: TBottleLogList;
899     i, Index: integer;
900 begin
901   Index := -1;
902   if OpenDialog.Execute then begin
903     for i := 0 to OpenDialog.Files.Count-1 do begin
904       BottleLog := TBottleLogList.Create(ExtractFileName(OpenDialog.Files[i]));
905       try
906         BottleLog.LoadFromXMLFile(OpenDialog.Files[i]);
907       except
908         on E: EXMLFileOpenException do begin
909           Beep;
910           ShowMessage(E.Message);
911           FreeAndNil(BottleLog);
912         end;
913       end;
914       if BottleLog <> nil then Index := BottleLogList.Add(BottleLog); // \8dÅ\8cã\82É\8aJ\82¢\82½\83\8d\83O\82Ì\88Ê\92u\82ð\8bL\89¯
915     end;
916     UpdateTab;
917     if Index >= 0 then tabBottleLog.TabIndex := Index;
918     UpdateWindow;
919   end;
920 end;
921
922 function TfrmLog.GetDefaultFileName(const Name, Ext: String): String;
923 begin
924   Result := StringReplace(Name, '/', '', [rfReplaceAll]);
925   Result := StringReplace(Result, ' ', '', [rfReplaceAll]);
926   Result := ChangeFileExt(Result, Ext);
927 end;
928
929 function TfrmLog.BottleLogTitled(const LogName: String): TBottleLogList;
930 var i: integer;
931 begin
932   for i := 0 to FBottleLogList.Count-1 do begin
933     if (FBottleLogList[i] as TBottleLogList).Title = LogName then begin
934       Result := (FBottleLogList[i] as TBottleLogList);
935       Exit;
936     end;
937   end;
938   // \8c©\82Â\82©\82ç\82È\82¢\8fê\8d\87
939   Result := TBottleLogList.Create(LogName); // \90V\82µ\82­\8dì\82é
940   FBottleLogList.Add(Result);
941   UpdateTab;
942   if FBottleLogList.Count = 1 then tabBottleLog.TabIndex := 0;
943 end;
944
945 procedure TfrmLog.AllBottleOpened;
946 var i, j: integer;
947     Log: TBottleLogList;
948 begin
949   for i := 0 to FBottleLogList.Count-1 do begin
950     Log  := FBottleLogList[i] as TBottleLogList;
951     for j := 0 to Log.Count-1 do begin
952       Log.Bottles[j].State := lsOpened;
953     end;
954   end;
955 end;
956
957 procedure TfrmLog.tabBottleLogMouseDown(Sender: TObject;
958   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
959 var Index: integer;
960 begin
961   with tabBottleLog do begin
962     Index := IndexOfTabAt(X, Y);
963     if Index = -1 then Exit; //\83^\83u\82ª\82È\82¢\82Ì\82Å\83h\83\89\83b\83O\82Å\82«\82È\82¢
964     if Button = mbLeft then begin
965       FDragTabIndex := Index; //\83h\83\89\83b\83O\82·\82é\83^\83u\82Ì\83C\83\93\83f\83b\83N\83X\82ð\95Û\91
966       BeginDrag(False);
967       FDragTabDest := -1;     //\83h\83\89\83b\83O\98g\90ü\95`\89æ\83t\83\89\83O\83N\83\8a\83A\82Ì\82½\82ß
968     end;
969   end;
970 end;
971
972 procedure TfrmLog.tabBottleLogDragOver(Sender, Source: TObject; X,
973   Y: Integer; State: TDragState; var Accept: Boolean);
974 var TargetRect: TRect;
975     OldDest: integer;
976 begin
977   Accept := Source = tabBottleLog;
978   if not Accept then Exit;
979   with tabBottleLog do begin
980     OldDest := FDragTabDest;
981     FDragTabDest := IndexOfTabAt(X, Y);
982     if FDragTabDest = -1 then begin
983       Accept := false; //\82±\82Ì\8fê\8d\87\82Í\83h\83\8d\83b\83v\82ð\94F\82ß\82È\82¢
984       Exit;
985     end;
986     with Canvas do begin
987       Pen.Mode := pmNot;
988       Pen.Width := 3;
989     end;
990     if (OldDest <> FDragTabDest) and (OldDest >= 0) then begin
991       //\88È\91O\82Ì\98g\90ü\8fÁ\8b\8e
992       TargetRect := TabRect(OldDest);
993       with Canvas do begin
994         Brush.Style := bsClear;
995         Rectangle(TargetRect.Left, TargetRect.Top,
996                   TargetRect.Right, TargetRect.Bottom);
997       end;
998     end;
999     if (OldDest <> FDragTabDest) then begin
1000       //\90V\82µ\82¢\98g\90ü\95`\89æ
1001       TargetRect := TabRect(FDragTabDest);
1002       with Canvas do begin
1003         Brush.Style := bsClear;
1004         Rectangle(TargetRect.Left, TargetRect.Top,
1005                   TargetRect.Right, TargetRect.Bottom);
1006       end;
1007     end;
1008   end;
1009 end;
1010
1011 procedure TfrmLog.tabBottleLogDragDrop(Sender, Source: TObject; X,
1012   Y: Integer);
1013 var DestIndex: integer;
1014 begin
1015   with tabBottleLog do begin
1016     DestIndex := IndexOfTabAt(X, Y);
1017     Tabs.Move(FDragTabIndex, DestIndex);
1018     FBottleLogList.Move(FDragTabIndex, DestIndex);
1019   end;
1020 end;
1021
1022 procedure TfrmLog.tabBottleLogEndDrag(Sender, Target: TObject; X,
1023   Y: Integer);
1024 begin
1025   //\8b­\90§\93I\82É\83^\83u\82ð\8dÄ\95`\89æ\82³\82¹\82é\81B\98g\90ü\8fÁ\82µ\91Î\8dô
1026   tabBottleLog.Tabs.BeginUpdate;
1027   tabBottleLog.Tabs.EndUpdate;
1028 end;
1029
1030 procedure TfrmLog.LogLoadWork(Sender: TObject);
1031 begin
1032   if Sender = SelectedBottleLog then lvwLog.Invalidate;
1033 end;
1034
1035 procedure TfrmLog.lvwLogDrawItem(Sender: TCustomListView; Item: TListItem;
1036   Rect: TRect; State: TOwnerDrawState);
1037 var
1038   DestRect: TRect;
1039   Script: String;
1040   Ico: TIcon;
1041   sub, Ex: integer;
1042 begin
1043   // \94w\8ci\8fÁ\8b\8e
1044   ListView_GetItemRect(lvwLog.Handle, Item.Index, DestRect, LVIR_BOUNDS);
1045
1046   lvwLog.Canvas.Brush.Style := bsSolid;
1047   if Item.Selected then begin
1048     lvwLog.Canvas.Brush.Color := clHighlight
1049   end else begin
1050     lvwLog.Canvas.Brush.Color := Pref.BgColor;
1051   end;
1052   lvwLog.Canvas.FillRect(DestRect);
1053   lvwLog.Canvas.Brush.Style := bsClear;
1054   if Item.Focused and lvwLog.Focused then
1055     lvwLog.Canvas.DrawFocusRect(DestRect);
1056
1057   if Item.Selected then
1058     lvwLog.Canvas.Font.Color := clHighlightText
1059   else
1060     lvwLog.Canvas.Font.Color := Pref.TalkColorH;
1061   lvwLog.Canvas.Refresh;
1062
1063
1064   // \83L\83\83\83v\83V\83\87\83\93(\93ú\95t)
1065   ListView_GetItemRect(lvwLog.Handle, Item.Index, DestRect, LVIR_LABEL);
1066   Inc(DestRect.Left, 2);
1067   Inc(DestRect.Top, 2);
1068   Dec(DestRect.Right, 2);
1069   DrawTextEx(lvwLog.Canvas.Handle, PChar(Item.Caption), -1, DestRect,
1070     DT_SINGLELINE or DT_END_ELLIPSIS, nil);
1071   ListView_GetItemRect(lvwLog.Handle, Item.Index, DestRect, LVIR_ICON);
1072   Ico := TIcon.Create;
1073   try
1074     lvwLog.SmallImages.GetIcon(Item.ImageIndex, Ico);
1075     lvwLog.Canvas.Draw(DestRect.Left, DestRect.Top, Ico);
1076   finally
1077     Ico.Free;
1078   end;
1079   
1080   // \83L\83\83\83v\83V\83\87\83\93\88È\8aO
1081   for sub := 0 to Item.SubItems.Count-1 do
1082   begin
1083     if sub = SubScript then Continue;
1084     ListView_GetSubItemRect(lvwLog.Handle, Item.Index, sub + 1,
1085       LVIR_BOUNDS, @DestRect);
1086     Inc(DestRect.Left, 2);
1087     Inc(DestRect.Top, 2);
1088     Dec(DestRect.Right, 2);
1089     if lvwLog.Columns[sub+1].Alignment = taRightJustify then
1090       Ex := DT_SINGLELINE or DT_END_ELLIPSIS or DT_RIGHT
1091     else
1092       Ex := DT_SINGLELINE or DT_END_ELLIPSIS;
1093     DrawTextEx(lvwLog.Canvas.Handle, PChar(Item.SubItems[sub]), -1, DestRect,
1094       Ex, nil);
1095   end;
1096
1097   // \83X\83N\83\8a\83v\83g
1098   ListView_GetSubItemRect(lvwLog.Handle, Item.Index, SubScript + 1,
1099     LVIR_BOUNDS, @DestRect);
1100   Script := Item.SubItems[SubScript];
1101   DrawSingleLineScript(Script, DestRect, Item);
1102 end;
1103
1104 procedure TfrmLog.DrawSingleLineScript(const Script: String;
1105   const Rect: TRect; Item: TListItem);
1106 var
1107   i, x, w: integer;
1108   UnyuTalking, Synchronized, Spaced: boolean;
1109   Mark: TSsMarkUpType;
1110   procedure ScopeChange;
1111   begin
1112     if (not Spaced) and (Pref.LogListPreviewStyle = psTagStripped) then
1113     begin
1114       Inc(x, 10);
1115       Spaced := true;
1116     end;
1117   end;
1118 begin
1119   SsParser.InputString := Script;
1120   x := 6;
1121   UnyuTalking := false;
1122   Synchronized := false;
1123   Spaced := true; // \83^\83O\8fÈ\97ª\95\\8e¦\8e\9e\82É\95s\95K\97v\82É\83X\83R\81[\83v\95Ï\8a·\8e\9e\82Ì\83X\83y\81[\83X\82ð\8bó\82¯\82È\82¢
1124                   // \82½\82ß\82Ì\83t\83\89\83O
1125   for i := 0 to SsParser.Count - 1 do begin
1126     if SsParser[i] = '\h' then
1127     begin
1128       UnyuTalking := false;
1129       ScopeChange;
1130     end else if SsParser[i] = '\u' then
1131     begin
1132       UnyuTalking := true;
1133       ScopeChange;
1134     end else if SsParser[i] = '\_s' then
1135     begin
1136       Synchronized := not Synchronized;
1137       ScopeChange;
1138     end;
1139     Mark := SsParser.MarkUpType[i];
1140     case Mark of
1141       mtMeta:
1142         begin
1143           lvwLog.Canvas.Font.Color := Pref.MetaWordColor;
1144           Spaced := false;
1145         end;
1146       mtTag:
1147         if Pref.LogListPreviewStyle = psNormal then
1148           lvwLog.Canvas.Font.Color := Pref.MarkUpColor
1149         else
1150         begin
1151           Continue;
1152         end;
1153       mtTagErr:
1154         lvwLog.Canvas.Font.Color := Pref.MarkErrorColor;
1155       else begin
1156         Spaced := false;
1157         if Synchronized then
1158           lvwLog.Canvas.Font.Color := Pref.TalkColorS
1159         else if UnyuTalking then
1160           lvwLog.Canvas.Font.Color := Pref.TalkColorU
1161         else
1162           lvwLog.Canvas.Font.Color := Pref.TalkColorH;
1163       end;
1164     end;
1165     if Item.Selected then lvwLog.Canvas.Font.Color := clHighlightText;
1166     lvwLog.Canvas.Refresh;
1167     w := lvwLog.Canvas.TextWidth(SsParser[i]);
1168     lvwLog.Canvas.TextRect(Rect, Rect.Left + x, Rect.Top + 2, SsParser[i]);
1169     x := x + w;
1170     if Rect.Right - Rect.Left < x then Break;
1171   end;
1172 end;
1173
1174 procedure TfrmLog.mnListPreviewStyleTagStrippedClick(Sender: TObject);
1175 var i: integer;
1176 begin
1177   with PopupMenuListPreviewStyle do
1178     for i := 0 to Items.Count-1 do
1179       Items[i].Checked := (Sender as TMenuItem).Tag = Items[i].Tag;
1180   Pref.LogListPreviewStyle := TLogListPreviewStyle((Sender as TMenuItem).Tag);
1181   lvwLog.Invalidate;
1182 end;
1183
1184 procedure TfrmLog.tbtnListPreviewStyleClick(Sender: TObject);
1185 var sel: integer;
1186 begin
1187   sel := Ord(Pref.LogListPreviewStyle);
1188   sel := sel + 1;
1189   if sel > Ord(High(TLogListPreviewStyle)) then sel := 0;
1190   Pref.LogListPreviewStyle := TLogListPreviewStyle(sel);
1191   lvwLog.Invalidate;
1192 end;
1193
1194 procedure TfrmLog.PopupMenuListPreviewStylePopup(Sender: TObject);
1195 var i: integer;
1196 begin
1197   with PopupMenuListPreviewStyle do
1198     for i := 0 to Items.Count-1 do
1199       Items[i].Checked := Items[i].Tag = Ord(Pref.LogListPreviewStyle)
1200 end;
1201
1202 end.