OSDN Git Service

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