OSDN Git Service

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