OSDN Git Service

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