OSDN Git Service

Fix: Ampersand in script is handled as escape
[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     mnListPreviewStyleNoColor: TMenuItem;
61     procedure tbtnClearClick(Sender: TObject);
62     procedure FormCreate(Sender: TObject);
63     procedure lvwLogChange(Sender: TObject; Item: TListItem;
64       Change: TItemChange);
65     procedure lvwLogDblClick(Sender: TObject);
66     procedure lvwLogKeyPress(Sender: TObject; var Key: Char);
67     procedure FormDestroy(Sender: TObject);
68     procedure lvwLogClick(Sender: TObject);
69     procedure mnSaveLogClick(Sender: TObject);
70     procedure lvwLogColumnClick(Sender: TObject; Column: TListColumn);
71     procedure mnPopUpCopyScriptClick(Sender: TObject);
72     procedure mnSaveLogChannelClick(Sender: TObject);
73     procedure mnSaveLogScriptClick(Sender: TObject);
74     procedure mnSaveLogXMLClick(Sender: TObject);
75     procedure lvwLogData(Sender: TObject; Item: TListItem);
76     procedure PopupMenuListViewPopup(Sender: TObject);
77     procedure lvwLogCustomDrawItem(Sender: TCustomListView;
78       Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
79     procedure PopupMenuPreviewStylePopup(Sender: TObject);
80     procedure mnPreviewStyleClick(Sender: TObject);
81     procedure tbtnPreviewStyleClick(Sender: TObject);
82     procedure tabBottleLogChange(Sender: TObject);
83     procedure tabBottleLogChanging(Sender: TObject;
84       var AllowChange: Boolean);
85     procedure tabBottleLogContextPopup(Sender: TObject; MousePos: TPoint;
86       var Handled: Boolean);
87     procedure mnCloseTabClick(Sender: TObject);
88     procedure tbtnFindBottleClick(Sender: TObject);
89     procedure tbtnOpenLogClick(Sender: TObject);
90     procedure tabBottleLogMouseDown(Sender: TObject; Button: TMouseButton;
91       Shift: TShiftState; X, Y: Integer);
92     procedure tabBottleLogDragOver(Sender, Source: TObject; X, Y: Integer;
93       State: TDragState; var Accept: Boolean);
94     procedure tabBottleLogDragDrop(Sender, Source: TObject; X, Y: Integer);
95     procedure tabBottleLogEndDrag(Sender, Target: TObject; X, Y: Integer);
96     procedure lvwLogDrawItem(Sender: TCustomListView; Item: TListItem;
97       Rect: TRect; State: TOwnerDrawState);
98     procedure mnListPreviewStyleClick(Sender: TObject);
99     procedure tbtnListPreviewStyleClick(Sender: TObject);
100     procedure PopupMenuListPreviewStylePopup(Sender: TObject);
101   private
102     { Private \90é\8c¾ }
103     FLastScript: String; //\83X\83N\83\8a\83v\83g\8dÄ\95`\89æ\97}\90§\97p
104     FBottleLogList: TObjectList;
105     //
106     FDragTabIndex: integer; //\83^\83u\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
107     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)
108     //
109     procedure UpdateScript(const Script: String);
110     procedure UpdateScriptConversationColor(const Script: String);
111     procedure UpdateScriptScript(const Script: String);
112     procedure mnURLClick(Sender: TObject);
113     procedure ExtractURLs(Script: String; Result: TStrings);
114     function GetDefaultFileName(const Name: String; const Ext: String): String;
115     function BottleLogTitled(const LogName: String): TBottleLogList;
116     procedure DrawSingleLineScript(const Script: String; Rect: TRect;
117       Item: TListItem);
118   protected
119     procedure CreateParams(var Params: TCreateParams); override;
120   public
121     { Public \90é\8c¾ }
122     function SelectedBottleLog: TBottleLogList;
123     property BottleLogList: TObjectList read FBottleLogList;
124     procedure AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String);
125     procedure AddCurrentSystemLog(const LogName, MessageString: String);
126     procedure VoteLog(const MID: String; const Vote: integer);
127     procedure AgreeLog(const MID: String; const Agree: integer);
128     procedure SetBottleState(const MID: String; State: TLogState);
129     procedure AllBottleOpened;
130     procedure LogLoaded(Sender: TObject);
131     procedure LogLoadFailure(Sender: TObject; const Message: String);
132     procedure LogLoadWork(Sender: TObject);
133     procedure UpdateTab;
134     procedure UpdateWindow;
135     procedure SelAndFocusMessage(const MID: String);
136   end;
137
138
139 var
140   frmLog: TfrmLog;
141
142 const
143   IconBottle    = 17;
144   IconOpened    = 30;
145   IconPlaying   = 31;
146   IconSystemLog = 26;
147   SubChannel    = 0;
148   SubGhost      = 1;
149   SubVotes      = 2;
150   SubAgrees     = 3;
151   SubScript     = 4;
152
153 implementation
154
155 uses MainForm;
156
157 {$R *.DFM}
158
159 { TfrmLog }
160
161 procedure TfrmLog.AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String);
162 var Sel: integer;
163 begin
164   BottleLogTitled(LogName).AddScriptLog(Script, Channel, MID, Ghost);
165   if SelectedBottleLog <> BottleLogTitled(LogName) then Exit;
166   lvwLog.OnChange := nil; //\83C\83x\83\93\83g\94­\90¶(\82¢\82ë\82¢\82ë\8dÄ\95`\89æ\82ª\8bN\82«\82é)\82Ì\97}\90§
167   if lvwLog.Selected <> nil then Sel := lvwLog.Selected.Index else Sel := -1;
168   lvwLog.Items.Count := SelectedBottleLog.Count;
169   UpdateWindow;
170   if Sel >= 0 then begin
171     lvwLog.Selected := lvwLog.Items[Sel + 1];
172     lvwLog.Selected.Focused := true;
173   end;
174   if not lvwLog.Focused then
175     ListView_Scroll(lvwLog.Handle, 0, High(integer));
176   lvwLog.OnChange := lvwLogChange;
177 end;
178
179 procedure TfrmLog.AddCurrentSystemLog(const LogName, MessageString: String);
180 var Sel: integer;
181 begin
182   BottleLogTitled(LogName).AddSystemLog(MessageString);
183   if SelectedBottleLog <> BottleLogTitled(LogName) then Exit;
184   lvwLog.OnChange := nil;
185   if lvwLog.Selected <> nil then Sel := lvwLog.Selected.Index else Sel := -1;
186   lvwLog.Items.Count := SelectedBottleLog.Count;
187   UpdateWindow;
188   if Sel >= 0 then begin
189     lvwLog.Selected := lvwLog.Items[Sel + 1];
190     lvwLog.Selected.Focused := true;
191   end;
192   if not lvwLog.Focused then
193     ListView_Scroll(lvwLog.Handle, 0, High(integer));
194   lvwLog.OnChange := lvwLogChange;
195 end;
196
197
198
199 procedure TfrmLog.tbtnClearClick(Sender: TObject);
200 begin
201   if SelectedBottleLog = nil then Exit;
202   FBottleLogList.Delete(tabBottleLog.TabIndex);
203   tabBottleLog.TabIndex := 0;
204   UpdateTab;
205   UpdateWindow;
206   lvwLogChange(Self, nil, ctState);
207 end;
208
209 procedure TfrmLog.FormCreate(Sender: TObject);
210 var i: integer;
211 begin
212   FBottleLogList := TObjectList.Create;
213
214   SsParser.TagPattern.Assign(frmSender.SsParser.TagPattern);
215   SsParser.MetaPattern.Assign(frmSender.SsParser.MetaPattern);
216
217   with Pref.LogWindowPosition do begin
218     Self.Left   := Left;
219     Self.Top    := Top;
220     Self.Width  := Right - Left + 1;
221     Self.Height := Bottom - Top + 1;
222   end;
223   lvwLog.DoubleBuffered := true;
224   edtScript.Height := Pref.LogWindowDividerPos;
225
226   i := 0;
227   while Token(Pref.LogWindowColumnWidth, ',', i) <> '' do begin
228     lvwLog.Columns[i].Width := StrToIntDef(Token(Pref.LogWindowColumnWidth, ',', i), 100);
229     Inc(i);
230   end;
231
232   UpdateWindow; // Reset window color and enabled status of some buttons
233 end;
234
235 procedure TfrmLog.FormDestroy(Sender: TObject);
236 var i: integer;
237     WidthStr: String;
238 begin
239   WidthStr := '';
240   for i := 0 to lvwLog.Columns.Count-1 do begin
241     if i > 0 then WidthStr := WidthStr + ',';
242     WidthStr := WidthStr + IntToStr(lvwLog.Column[i].Width);
243   end;
244   Pref.LogWindowColumnWidth := WidthStr;
245
246   with Pref.LogWindowPosition do begin
247     Left   := Self.Left;
248     Top    := Self.Top;
249     Right  := Self.Left + Self.Width - 1;
250     Bottom := Self.Top + Self.Height - 1;
251   end;
252   Pref.LogWindowDividerPos := edtScript.Height;
253
254   FreeAndNil(FBottleLogList);
255 end;
256
257 procedure TfrmLog.lvwLogChange(Sender: TObject; Item: TListItem;
258   Change: TItemChange);
259 var Script: String;
260     Log: TLogItem;
261 begin
262   if SelectedBottleLog <> nil then begin
263     StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
264     if Change = ctState then begin
265       Script := '';
266       if lvwLog.Selected <> nil then begin
267         Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
268         if (Log.LogType = ltBottle) and not frmSender.Connecting then begin
269           Script := Log.Script;
270           frmSender.actVoteMessage.Enabled := true;
271           frmSender.actAgreeMessage.Enabled := true;
272           frmSender.actInsertCue.Enabled := true;
273           mnPopUpCopyScript.Enabled := true;
274           StatusBar.Panels[1].Text := Format('%d\83o\83C\83g - \83_\83u\83\8b\83N\83\8a\83b\83N\82Å\8dÄ\90¶', [Length(Log.Script)]);
275           UpdateScript(Script);
276         end else begin
277           frmSender.actVoteMessage.Enabled := false;
278           frmSender.actAgreeMessage.Enabled := false;
279           frmSender.actInsertCue.Enabled := false;
280           mnPopUpCopyScript.Enabled := false;
281           StatusBar.Panels[1].Text := '';
282           UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\82ð\83N\83\8a\83A
283         end;
284       end else begin
285         frmSender.actVoteMessage.Enabled := false;
286         frmSender.actAgreeMessage.Enabled := false;
287         frmSender.actInsertCue.Enabled := false;
288         mnPopUpCopyScript.Enabled := false;
289         StatusBar.Panels[1].Text := '';
290         UpdateScript(Script); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
291       end;
292     end;
293     tbtnSaveLog.Enabled := lvwLog.Items.Count > 0;
294   end else begin
295     frmSender.actVoteMessage.Enabled := false;
296     frmSender.actAgreeMessage.Enabled := false;
297     frmSender.actInsertCue.Enabled := false;
298     mnPopUpCopyScript.Enabled := false;
299     StatusBar.Panels[0].Text := '';
300     UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
301   end;
302 end;
303
304 procedure TfrmLog.lvwLogDblClick(Sender: TObject);
305 var Script: String;
306     Log, CueItem: TLogItem;
307 begin
308   if lvwLog.Selected = nil then
309     Exit;
310   Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
311   if Log = nil then Exit;
312   if Log.LogType <> ltBottle then
313     Exit;
314   Script := frmSender.ScriptTransForSSTP(Log.Script);
315   if Script = '' then begin
316     ShowMessage('\96â\91è\82Ì\82 \82é\83X\83N\83\8a\83v\83g\82Å\82·\81B\8dÄ\90\82Å\82«\82Ü\82¹\82ñ\81B'+
317       '\83N\83\89\83C\83A\83\93\83g\82ð\8dÅ\90V\94Å\82É\82µ\82Ä\82Ý\82Ä\82­\82¾\82³\82¢\81B');
318     Exit;
319   end;
320
321   CueItem := TLogItem.Create(Log);
322   try
323     CueItem.Script := Script;
324     frmSender.BottleSstp.Unshift(CueItem);
325   except
326     CueItem.Free;
327   end;
328 end;
329
330 procedure TfrmLog.UpdateScriptConversationColor(const Script: String);
331 var i: integer;
332     scr: String;
333     UnyuTalking, Talked, InSynchronized: boolean;
334 begin
335   scr := Script;
336   frmSender.DoTrans(scr, [toConvertURL]);
337   SsParser.LeaveEscape := false;
338   SsParser.InputString := scr;
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         with BottleLog do
907         begin
908           OnLoaded := LogLoaded;
909           OnLoadFailure := LogLoadFailure;
910           OnLoadWork := LogLoadWork;
911           BottleLog.LoadFromXMLFile(OpenDialog.Files[i]);
912         end;
913         Index := BottleLogList.Add(BottleLog); // \8dÅ\8cã\82É\8aJ\82¢\82½\83\8d\83O\82Ì\88Ê\92u\82ð\8bL\89¯
914       except
915         BottleLog.Free;
916       end;
917     end;
918     UpdateTab;
919     if Index >= 0 then tabBottleLog.TabIndex := Index;
920     UpdateWindow;
921   end;
922 end;
923
924 function TfrmLog.GetDefaultFileName(const Name, Ext: String): String;
925 begin
926   Result := StringReplace(Name, '/', '', [rfReplaceAll]);
927   Result := StringReplace(Result, ' ', '', [rfReplaceAll]);
928   Result := ChangeFileExt(Result, Ext);
929 end;
930
931 function TfrmLog.BottleLogTitled(const LogName: String): TBottleLogList;
932 var i: integer;
933 begin
934   for i := 0 to FBottleLogList.Count-1 do begin
935     if (FBottleLogList[i] as TBottleLogList).Title = LogName then begin
936       Result := (FBottleLogList[i] as TBottleLogList);
937       Exit;
938     end;
939   end;
940   // \8c©\82Â\82©\82ç\82È\82¢\8fê\8d\87
941   Result := TBottleLogList.Create(LogName); // \90V\82µ\82­\8dì\82é
942   FBottleLogList.Add(Result);
943   UpdateTab;
944   if FBottleLogList.Count = 1 then tabBottleLog.TabIndex := 0;
945 end;
946
947 procedure TfrmLog.AllBottleOpened;
948 var i, j: integer;
949     Log: TBottleLogList;
950 begin
951   for i := 0 to FBottleLogList.Count-1 do begin
952     Log  := FBottleLogList[i] as TBottleLogList;
953     for j := 0 to Log.Count-1 do begin
954       Log.Bottles[j].State := lsOpened;
955     end;
956   end;
957 end;
958
959 procedure TfrmLog.tabBottleLogMouseDown(Sender: TObject;
960   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
961 var Index: integer;
962 begin
963   with tabBottleLog do begin
964     Index := IndexOfTabAt(X, Y);
965     if Index = -1 then Exit; //\83^\83u\82ª\82È\82¢\82Ì\82Å\83h\83\89\83b\83O\82Å\82«\82È\82¢
966     if Button = mbLeft then begin
967       FDragTabIndex := Index; //\83h\83\89\83b\83O\82·\82é\83^\83u\82Ì\83C\83\93\83f\83b\83N\83X\82ð\95Û\91
968       BeginDrag(False);
969       FDragTabDest := -1;     //\83h\83\89\83b\83O\98g\90ü\95`\89æ\83t\83\89\83O\83N\83\8a\83A\82Ì\82½\82ß
970     end;
971   end;
972 end;
973
974 procedure TfrmLog.tabBottleLogDragOver(Sender, Source: TObject; X,
975   Y: Integer; State: TDragState; var Accept: Boolean);
976 var TargetRect: TRect;
977     OldDest: integer;
978 begin
979   Accept := Source = tabBottleLog;
980   if not Accept then Exit;
981   with tabBottleLog do begin
982     OldDest := FDragTabDest;
983     FDragTabDest := IndexOfTabAt(X, Y);
984     if FDragTabDest = -1 then begin
985       Accept := false; //\82±\82Ì\8fê\8d\87\82Í\83h\83\8d\83b\83v\82ð\94F\82ß\82È\82¢
986       Exit;
987     end;
988     with Canvas do begin
989       Pen.Mode := pmNot;
990       Pen.Width := 3;
991     end;
992     if (OldDest <> FDragTabDest) and (OldDest >= 0) then begin
993       //\88È\91O\82Ì\98g\90ü\8fÁ\8b\8e
994       TargetRect := TabRect(OldDest);
995       with Canvas do begin
996         Brush.Style := bsClear;
997         Rectangle(TargetRect.Left, TargetRect.Top,
998                   TargetRect.Right, TargetRect.Bottom);
999       end;
1000     end;
1001     if (OldDest <> FDragTabDest) then begin
1002       //\90V\82µ\82¢\98g\90ü\95`\89æ
1003       TargetRect := TabRect(FDragTabDest);
1004       with Canvas do begin
1005         Brush.Style := bsClear;
1006         Rectangle(TargetRect.Left, TargetRect.Top,
1007                   TargetRect.Right, TargetRect.Bottom);
1008       end;
1009     end;
1010   end;
1011 end;
1012
1013 procedure TfrmLog.tabBottleLogDragDrop(Sender, Source: TObject; X,
1014   Y: Integer);
1015 var DestIndex: integer;
1016 begin
1017   with tabBottleLog do begin
1018     DestIndex := IndexOfTabAt(X, Y);
1019     Tabs.Move(FDragTabIndex, DestIndex);
1020     FBottleLogList.Move(FDragTabIndex, DestIndex);
1021   end;
1022 end;
1023
1024 procedure TfrmLog.tabBottleLogEndDrag(Sender, Target: TObject; X,
1025   Y: Integer);
1026 begin
1027   //\8b­\90§\93I\82É\83^\83u\82ð\8dÄ\95`\89æ\82³\82¹\82é\81B\98g\90ü\8fÁ\82µ\91Î\8dô
1028   tabBottleLog.Tabs.BeginUpdate;
1029   tabBottleLog.Tabs.EndUpdate;
1030 end;
1031
1032 procedure TfrmLog.LogLoadWork(Sender: TObject);
1033 begin
1034   if Sender = SelectedBottleLog then lvwLog.Invalidate;
1035 end;
1036
1037 procedure TfrmLog.lvwLogDrawItem(Sender: TCustomListView; Item: TListItem;
1038   Rect: TRect; State: TOwnerDrawState);
1039 var
1040   DestRect: TRect;
1041   Script: String;
1042   Ico: TIcon;
1043   sub, Ex: integer;
1044 begin
1045   // \94w\8ci\8fÁ\8b\8e
1046   ListView_GetItemRect(lvwLog.Handle, Item.Index, DestRect, LVIR_BOUNDS);
1047
1048   lvwLog.Canvas.Brush.Style := bsSolid;
1049   if Item.Selected then begin
1050     if lvwLog.Focused then
1051       lvwLog.Canvas.Brush.Color := clHighlight
1052     else
1053       lvwLog.Canvas.Brush.Color := clBtnFace;
1054   end else begin
1055     lvwLog.Canvas.Brush.Color := Pref.BgColor;
1056   end;
1057   lvwLog.Canvas.FillRect(DestRect);
1058   lvwLog.Canvas.Brush.Style := bsClear;
1059   if Item.Focused and lvwLog.Focused then
1060     lvwLog.Canvas.DrawFocusRect(DestRect);
1061
1062   if Item.Selected then
1063   begin
1064     if lvwLog.Focused then
1065       lvwLog.Canvas.Font.Color := clHighlightText
1066     else
1067       lvwLog.Canvas.Font.Color := clWindowText;
1068   end else
1069     lvwLog.Canvas.Font.Color := Pref.TalkColorH;
1070   lvwLog.Canvas.Refresh;
1071
1072   // \83L\83\83\83v\83V\83\87\83\93(\93ú\95t)
1073   ListView_GetItemRect(lvwLog.Handle, Item.Index, DestRect, LVIR_LABEL);
1074   Inc(DestRect.Left, 2);
1075   Inc(DestRect.Top, 2);
1076   Dec(DestRect.Right, 2);
1077   DrawTextEx(lvwLog.Canvas.Handle, PChar(Item.Caption), -1, DestRect,
1078     DT_SINGLELINE or DT_END_ELLIPSIS, nil);
1079   ListView_GetItemRect(lvwLog.Handle, Item.Index, DestRect, LVIR_ICON);
1080   Ico := TIcon.Create;
1081   try
1082     lvwLog.SmallImages.GetIcon(Item.ImageIndex, Ico);
1083     lvwLog.Canvas.Draw(DestRect.Left, DestRect.Top, Ico);
1084   finally
1085     Ico.Free;
1086   end;
1087   // \83L\83\83\83v\83V\83\87\83\93\82Å\82à\83X\83N\83\8a\83v\83g\82Å\82à\82È\82¢\82à\82Ì
1088   for sub := 0 to Item.SubItems.Count-1 do
1089   begin
1090     if sub = SubScript then Continue;
1091     ListView_GetSubItemRect(lvwLog.Handle, Item.Index, sub + 1,
1092       LVIR_BOUNDS, @DestRect);
1093     Inc(DestRect.Left, 2);
1094     Inc(DestRect.Top, 2);
1095     Dec(DestRect.Right, 2);
1096     Ex := DT_NOPREFIX or DT_SINGLELINE or DT_END_ELLIPSIS;
1097     if lvwLog.Columns[sub+1].Alignment = taRightJustify then
1098       Ex := Ex or DT_RIGHT;
1099     DrawTextEx(lvwLog.Canvas.Handle, PChar(Item.SubItems[sub]), -1, DestRect,
1100       Ex, nil);
1101   end;
1102   // \83X\83N\83\8a\83v\83g
1103   ListView_GetSubItemRect(lvwLog.Handle, Item.Index, SubScript + 1,
1104     LVIR_BOUNDS, @DestRect);
1105   Script := Item.SubItems[SubScript];
1106   DrawSingleLineScript(Script, DestRect, Item);
1107 end;
1108
1109 procedure TfrmLog.DrawSingleLineScript(const Script: String;
1110   Rect: TRect; Item: TListItem);
1111 var
1112   i, x, w: integer;
1113   UnyuTalking, Synchronized, Spaced: boolean;
1114   Mark: TSsMarkUpType;
1115   procedure ScopeChange;
1116   begin
1117     if (not Spaced) and (Pref.LogListPreviewStyle = psTagStripped) then
1118     begin
1119       Inc(x, 7);
1120       Spaced := true;
1121     end;
1122   end;
1123 begin
1124   if Pref.LogListPreviewStyle = psNoColor then
1125   begin
1126     Inc(Rect.Left, 6);
1127     Inc(Rect.Top, 2);
1128     Dec(Rect.Right, 2);
1129     DrawTextEx(lvwLog.Canvas.Handle, PChar(Script), -1, Rect,
1130       DT_SINGLELINE or DT_END_ELLIPSIS or DT_NOPREFIX, nil);
1131     Exit;
1132   end;
1133
1134   SsParser.LeaveEscape := false;
1135   SsParser.InputString := Script;
1136
1137   x := 6;
1138   UnyuTalking := false;
1139   Synchronized := false;
1140   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¢
1141                   // \82½\82ß\82Ì\83t\83\89\83O
1142   for i := 0 to SsParser.Count - 1 do begin
1143     if SsParser[i] = '\h' then
1144     begin
1145       UnyuTalking := false;
1146       ScopeChange;
1147     end else if SsParser[i] = '\u' then
1148     begin
1149       UnyuTalking := true;
1150       ScopeChange;
1151     end else if SsParser[i] = '\_s' then
1152     begin
1153       Synchronized := not Synchronized;
1154       ScopeChange;
1155     end else if (Pos('\n', SsParser[i]) = 1) or (SsParser[i] = '\c') then
1156     begin
1157       ScopeChange;
1158     end;
1159     Mark := SsParser.MarkUpType[i];
1160     case Mark of
1161       mtMeta:
1162         begin
1163           lvwLog.Canvas.Font.Color := Pref.MetaWordColor;
1164           Spaced := false;
1165         end;
1166       mtTag:
1167         if Pref.LogListPreviewStyle = psNormal then
1168           lvwLog.Canvas.Font.Color := Pref.MarkUpColor
1169         else
1170         begin
1171           Continue;
1172         end;
1173       mtTagErr:
1174         lvwLog.Canvas.Font.Color := Pref.MarkErrorColor;
1175       else begin
1176         Spaced := false;
1177         if Synchronized then
1178           lvwLog.Canvas.Font.Color := Pref.TalkColorS
1179         else if UnyuTalking then
1180           lvwLog.Canvas.Font.Color := Pref.TalkColorU
1181         else
1182           lvwLog.Canvas.Font.Color := Pref.TalkColorH;
1183       end;
1184     end;
1185     if Item.Selected then
1186     begin
1187       if lvwLog.Focused then
1188         lvwLog.Canvas.Font.Color := clHighlightText
1189       else
1190         lvwLog.Canvas.Font.Color := clWindowText;
1191     end;
1192     lvwLog.Canvas.Refresh;
1193     w := lvwLog.Canvas.TextWidth(SsParser[i]);
1194     lvwLog.Canvas.TextRect(Rect, Rect.Left + x, Rect.Top + 2, SsParser[i]);
1195     x := x + w;
1196     if Rect.Right - Rect.Left < x then Break;
1197   end;
1198 end;
1199
1200 procedure TfrmLog.mnListPreviewStyleClick(Sender: TObject);
1201 var i: integer;
1202 begin
1203   with PopupMenuListPreviewStyle do
1204     for i := 0 to Items.Count-1 do
1205       Items[i].Checked := (Sender as TMenuItem).Tag = Items[i].Tag;
1206   Pref.LogListPreviewStyle := TLogListPreviewStyle((Sender as TMenuItem).Tag);
1207   lvwLog.Invalidate;
1208 end;
1209
1210 procedure TfrmLog.tbtnListPreviewStyleClick(Sender: TObject);
1211 var sel: integer;
1212 begin
1213   sel := Ord(Pref.LogListPreviewStyle);
1214   sel := sel + 1;
1215   if sel > Ord(High(TLogListPreviewStyle)) then sel := 0;
1216   Pref.LogListPreviewStyle := TLogListPreviewStyle(sel);
1217   lvwLog.Invalidate;
1218 end;
1219
1220 procedure TfrmLog.PopupMenuListPreviewStylePopup(Sender: TObject);
1221 var i: integer;
1222 begin
1223   with PopupMenuListPreviewStyle do
1224     for i := 0 to Items.Count-1 do
1225       Items[i].Checked := Items[i].Tag = Ord(Pref.LogListPreviewStyle)
1226 end;
1227
1228 end.