OSDN Git Service

ログ画面の描画をリストビュー使用から由緒正しきリストボックスに変更。
[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, SsParser, BottleDef, Menus,
8   Clipbrd, Logs, ShellAPI, Commctrl, DirectSstp, Contnrs, StrUtils,
9   TalkShowFrame, SppList, HtmlOutputConfig, HtmlOutputProgress,
10   SearchLog, IniFiles, BRegExp, RegexUtils, ExtCtrls;
11
12 type
13   // \83\8d\83O\82Ì\95Û\91\95û\96@
14   TSaveLogType = (stLog, stLogWithChannels, stText, stXML);
15
16   // \83\8a\83X\83g\83r\83\85\81[\82Ì\83X\83N\83\8d\81[\83\8b\95û\8cü
17   TLVScrollDir = (lvScrollUp, lvScrollDown);
18
19   TfrmLog = class(TForm)
20     ToolBar: TToolBar;
21     tbtnClear: TToolButton;
22     pnlUpper: TPanel;
23     SsParser: TSsParser;
24     StatusBar: TStatusBar;
25     tbtnSaveLog: TToolButton;
26     PopupMenuPreview: TPopupMenu;
27     mnPopCopy: TMenuItem;
28     tbtnVoteMessage: TToolButton;
29     PopupMenuListView: TPopupMenu;
30     mnPopUpVoteMessage: TMenuItem;
31     SaveDialog: TSaveDialog;
32     pnlPanel: TPanel;
33     Splitter: TSplitter;
34     mnPopUpCopyScript: TMenuItem;
35     PopupMenuSaveLog: TPopupMenu;
36     mnSaveLog: TMenuItem;
37     mnSaveLogChannel: TMenuItem;
38     mnSaveLogScript: TMenuItem;
39     mnSaveLogXML: TMenuItem;
40     ToolButton1: TToolButton;
41     mnJumpURL: TMenuItem;
42     mnPopUpAgreeMessage: TMenuItem;
43     tbtnAgreeMessage: TToolButton;
44     ToolButton2: TToolButton;
45     tbtnPreviewStyle: TToolButton;
46     PopupMenuPreviewStyle: TPopupMenu;
47     mnPreviewStyleConversation: TMenuItem;
48     mnPreviewStyleScript: TMenuItem;
49     mnPreviewStyleScriptWithLineBreak: TMenuItem;
50     Panel1: TPanel;
51     tabBottleLog: TTabControl;
52     tbtnDownloadLog: TToolButton;
53     PopupMenuTab: TPopupMenu;
54     mnCloseTab: TMenuItem;
55     tbtnFindBottle: TToolButton;
56     tbtnOpenLog: TToolButton;
57     OpenDialog: TOpenDialog;
58     tbtnInsertCue: TToolButton;
59     mnInsertCue: TMenuItem;
60     PopupMenuListPreviewStyle: TPopupMenu;
61     mnListPreviewStyleNormal: TMenuItem;
62     mnListPreviewStyleTagStripped: TMenuItem;
63     tbtnListPreviewStyle: TToolButton;
64     mnListPreviewStyleNoColor: TMenuItem;
65     SsParserForTalkShow: TSsParser;
66     mnPreviewStyleConversationImage: TMenuItem;
67     pnlPreviewArea: TPanel;
68     TalkShowFrame: TfrmTalkShow;
69     edtScript: TRichEdit;
70     tbtnSendEditor: TToolButton;
71     mnSendEditor: TMenuItem;
72     timScrollTimer: TTimer;
73     mnChangeTabName: TMenuItem;
74     lvwLog: TListBox;
75     N1: TMenuItem;
76     N2: TMenuItem;
77     mnDeleteLogItem: TMenuItem;
78     mnTabSaveXMLLog: TMenuItem;
79     mnSaveHTML: TMenuItem;
80     mnPopupCopyGhost: TMenuItem;
81     Header: THeaderControl;
82     procedure tbtnClearClick(Sender: TObject);
83     procedure FormCreate(Sender: TObject);
84     procedure lvwLogDblClick(Sender: TObject);
85     procedure lvwLogKeyPress(Sender: TObject; var Key: Char);
86     procedure FormDestroy(Sender: TObject);
87     procedure lvwLogClick(Sender: TObject);
88     procedure mnSaveLogClick(Sender: TObject);
89     procedure mnPopUpCopyScriptClick(Sender: TObject);
90     procedure mnSaveLogChannelClick(Sender: TObject);
91     procedure mnSaveLogScriptClick(Sender: TObject);
92     procedure mnSaveLogXMLClick(Sender: TObject);
93     procedure PopupMenuListViewPopup(Sender: TObject);
94     procedure PopupMenuPreviewStylePopup(Sender: TObject);
95     procedure mnPreviewStyleClick(Sender: TObject);
96     procedure tbtnPreviewStyleClick(Sender: TObject);
97     procedure tabBottleLogChange(Sender: TObject);
98     procedure tabBottleLogChanging(Sender: TObject;
99       var AllowChange: Boolean);
100     procedure tabBottleLogContextPopup(Sender: TObject; MousePos: TPoint;
101       var Handled: Boolean);
102     procedure mnCloseTabClick(Sender: TObject);
103     procedure tbtnFindBottleClick(Sender: TObject);
104     procedure tbtnOpenLogClick(Sender: TObject);
105     procedure tabBottleLogMouseDown(Sender: TObject; Button: TMouseButton;
106       Shift: TShiftState; X, Y: Integer);
107     procedure tabBottleLogDragOver(Sender, Source: TObject; X, Y: Integer;
108       State: TDragState; var Accept: Boolean);
109     procedure tabBottleLogDragDrop(Sender, Source: TObject; X, Y: Integer);
110     procedure tabBottleLogEndDrag(Sender, Target: TObject; X, Y: Integer);
111     procedure mnListPreviewStyleClick(Sender: TObject);
112     procedure tbtnListPreviewStyleClick(Sender: TObject);
113     procedure PopupMenuListPreviewStylePopup(Sender: TObject);
114     procedure lvwLogDragOver(Sender, Source: TObject; X, Y: Integer;
115       State: TDragState; var Accept: Boolean);
116     procedure lvwLogDragDrop(Sender, Source: TObject; X, Y: Integer);
117     procedure timScrollTimerTimer(Sender: TObject);
118     procedure mnChangeTabNameClick(Sender: TObject);
119     procedure lvwLogStartDrag(Sender: TObject;
120       var DragObject: TDragObject);
121     procedure lvwLogEndDrag(Sender, Target: TObject; X, Y: Integer);
122     procedure mnTabSaveXMLLogClick(Sender: TObject);
123     procedure mnSaveHTMLClick(Sender: TObject);
124     procedure mnPopupCopyGhostClick(Sender: TObject);
125     procedure lvwLogDrawItem(Control: TWinControl; Index: Integer;
126       Rect: TRect; State: TOwnerDrawState);
127     procedure lvwLogData(Control: TWinControl; Index: Integer;
128       var Data: String);
129     procedure HeaderSectionClick(HeaderControl: THeaderControl;
130       Section: THeaderSection);
131     procedure HeaderSectionTrack(HeaderControl: THeaderControl;
132       Section: THeaderSection; Width: Integer; State: TSectionTrackState);
133     procedure lvwLogMouseDown(Sender: TObject; Button: TMouseButton;
134       Shift: TShiftState; X, Y: Integer);
135   private
136     { Private \90é\8c¾ }
137     FLastScript: String; //\83X\83N\83\8a\83v\83g\8dÄ\95`\89æ\97}\90§\97p
138     FBottleLogList: TObjectList;
139     //
140     FDragTabIndex: integer; //\83^\83u\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
141     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)
142     //
143     // \83\8a\83X\83g\83r\83\85\81[\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
144     FLVScrollDir: TLVScrollDir; // \83X\83N\83\8d\81[\83\8b\95û\8cü
145     FLVDragDest: integer;    //\83h\83\8d\83b\83v\82·\82é\88Ê\92u(\82·\82®\89º\82É\82­\82é\83A\83C\83e\83\80\82ÌIndex)
146     //
147     procedure UpdateScript(const Script: String);
148     procedure UpdateScriptConversationColor(const Script: String);
149     procedure UpdateScriptScript(const Script: String);
150     procedure mnURLClick(Sender: TObject);
151     function ExtractURLs(Script: String; Urls: TStrings; Labels: TStrings): Boolean;
152     function GetDefaultFileName(const Name: String; const Ext: String): String;
153     function BottleLogTitled(const LogName: String): TBottleLogList;
154     procedure DrawSingleLineScript(LogItem: TLogItem; Rect: TRect;
155       State: TOwnerDrawState);
156     procedure PreviewStyleChange;
157     procedure DrawListViewDragBorder(const Rect: TRect);
158     procedure DoSaveLogXML(Log: TBottleLogList);
159     procedure DoCloseTab(const Index: integer);
160     function DoSearchLog(Condition: TSearchCond): TBottleLogList;
161     procedure SearchLogIndivisual(Condition: TSearchCond;
162       LogList, Result: TBottleLogList; UntilIndex: integer = -1);
163     procedure DrawListSection(const SectionIndex, Top: Integer;
164       const Text: string; const LeftMargin: Integer = 0);
165   protected
166     procedure CreateParams(var Params: TCreateParams); override;
167   public
168     { Public \90é\8c¾ }
169     function SelectedBottleLog: TBottleLogList;
170     property BottleLogList: TObjectList read FBottleLogList;
171     procedure AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String);
172     procedure AddCurrentSystemLog(const LogName, MessageString: String);
173     procedure VoteLog(const MID: String; const Vote: integer);
174     procedure AgreeLog(const MID: String; const Agree: integer);
175     procedure SetBottleState(const MID: String; State: TLogState);
176     procedure AllBottleOpened;
177     procedure LogLoaded(Sender: TObject);
178     procedure LogLoadFailure(Sender: TObject; const Message: String);
179     procedure LogLoadWork(Sender: TObject);
180     procedure HTMLOutputWork(Sender: TObject; const Count: integer;
181       var Canceled: boolean);
182     procedure UpdateTab;
183     procedure UpdateWindow;
184     procedure SelAndFocusMessage(const MID: String);
185     procedure CheckBottleURL(Bottle: TLogItem);
186   end;
187
188   TBottleLogDragObject = class(TDragControlObjectEx)
189   private
190     FBottleLogList: TBottleLogList;
191     FLogItem: TLogItem;
192     procedure SetBottleLogList(const Value: TBottleLogList);
193     procedure SetLogItem(const Value: TLogItem);
194   protected
195     function GetDragImages: TDragImageList; override;
196   public
197     property BottleLogList: TBottleLogList read FBottleLogList write SetBottleLogList;
198     property LogItem: TLogItem read FLogItem write SetLogItem;
199   end;
200
201 var
202   frmLog: TfrmLog;
203
204 const
205   IconBottle    = 17;
206   IconOpened    = 30;
207   IconPlaying   = 31;
208   IconSystemLog = 26;
209   IconURL       = 43;
210   SubTime       = 0;
211   SubChannel    = 1;
212   SubGhost      = 2;
213   SubVotes      = 3;
214   SubAgrees     = 4;
215   SubScript     = 5;
216
217 implementation
218
219 uses MainForm;
220
221 {$R *.DFM}
222
223 { TfrmLog }
224
225 procedure TfrmLog.AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String);
226 var Sel: integer;
227 begin
228   BottleLogTitled(LogName).AddScriptLog(Script, Channel, MID, Ghost);
229   if SelectedBottleLog <> BottleLogTitled(LogName) then Exit;
230   //lvwLog.OnChange := nil; //\83C\83x\83\93\83g\94­\90¶(\82¢\82ë\82¢\82ë\8dÄ\95`\89æ\82ª\8bN\82«\82é)\82Ì\97}\90§
231   if lvwLog.Itemindex <> -1 then
232     Sel := lvwLog.ItemIndex
233   else
234     Sel := -1;
235   lvwLog.Count := SelectedBottleLog.Count;
236   UpdateWindow;
237   if Sel >= 0 then begin
238     lvwLog.ItemIndex := Sel + 1;
239     lvwLog.SetFocus;
240   end;
241   if not lvwLog.Focused then
242     ListView_Scroll(lvwLog.Handle, 0, High(integer));
243   //lvwLog.OnChange := lvwLogChange;
244 end;
245
246 procedure TfrmLog.AddCurrentSystemLog(const LogName, MessageString: String);
247 var Sel: integer;
248 begin
249   BottleLogTitled(LogName).AddSystemLog(MessageString);
250   if SelectedBottleLog <> BottleLogTitled(LogName) then Exit;
251   //lvwLog.OnChange := nil;
252   if lvwLog.ItemIndex <> -1 then
253     Sel := lvwLog.ItemIndex
254   else
255     Sel := -1;
256   lvwLog.Count := SelectedBottleLog.Count;
257   UpdateWindow;
258   if Sel >= 0 then begin
259     lvwLog.ItemIndex := Sel + 1;
260     lvwLog.SetFocus;
261   end;
262   if not lvwLog.Focused then
263     ListView_Scroll(lvwLog.Handle, 0, High(integer));
264   //lvwLog.OnChange := lvwLogChange;
265 end;
266
267
268
269 procedure TfrmLog.tbtnClearClick(Sender: TObject);
270 begin
271   if SelectedBottleLog = nil then Exit;
272   DoCloseTab(tabBottleLog.TabIndex);
273 end;
274
275 procedure TfrmLog.FormCreate(Sender: TObject);
276 var i: integer;
277 begin
278   FLVDragDest := -1; // \83\8a\83X\83g\83r\83\85\81[\82Ì\83h\83\89\83b\83O\92\86\82Å\82Í\82È\82¢
279   FBottleLogList := TObjectList.Create;
280
281   SsParser.TagPattern.Assign(frmSender.SsParser.TagPattern);
282   SsParser.MetaPattern.Assign(frmSender.SsParser.MetaPattern);
283
284   with Pref.LogWindowPosition do begin
285     Self.Left   := Left;
286     Self.Top    := Top;
287     Self.Width  := Right - Left + 1;
288     Self.Height := Bottom - Top + 1;
289   end;
290   lvwLog.DoubleBuffered := true;
291   pnlPreviewArea.Height := Pref.LogWindowDividerPos;
292
293   i := 0;
294   while Token(Pref.LogWindowColumnWidth, ',', i) <> '' do begin
295     Header.Sections[i].Width := StrToIntDef(Token(Pref.LogWindowColumnWidth, ',', i), 100);
296     Inc(i);
297   end;
298
299   SsParserForTalkShow.TagPattern.Assign(SsParser.TagPattern);
300   SsParserForTalkShow.MetaPattern.Assign(SsParser.MetaPattern);
301   SsParserForTalkShow.EscapeInvalidMeta := false;
302   SsParserForTalkShow.LeaveEscape := false;
303   TalkShowFrame.SsParser := self.SsParserForTalkShow;
304
305   TalkShowFrame.SetPreviewFont(edtScript.Font);
306   TalkShowFrame.PrevControl := lvwLog;
307
308   PreviewStyleChange;
309   UpdateWindow; // Reset window color and enabled status of some buttons
310 end;
311
312 procedure TfrmLog.FormDestroy(Sender: TObject);
313 var i: integer;
314     WidthStr: String;
315 begin
316   WidthStr := '';
317   for i := 0 to Header.Sections.Count-1 do begin
318     if i > 0 then WidthStr := WidthStr + ',';
319     WidthStr := WidthStr + IntToStr(Header.Sections[i].Width);
320   end;
321   Pref.LogWindowColumnWidth := WidthStr;
322
323   with Pref.LogWindowPosition do begin
324     Left   := Self.Left;
325     Top    := Self.Top;
326     Right  := Self.Left + Self.Width - 1;
327     Bottom := Self.Top + Self.Height - 1;
328   end;
329   Pref.LogWindowDividerPos := pnlPreviewArea.Height;
330
331   FreeAndNil(FBottleLogList);
332 end;
333
334 procedure TfrmLog.lvwLogDblClick(Sender: TObject);
335 var Script, ErrorMes: String;
336     Log, CueItem: TLogItem;
337     Res: integer;
338 begin
339   if lvwLog.ItemIndex = -1 then
340     Exit;
341   Log := SelectedBottleLog.Bottles[lvwLog.ItemIndex];
342   if Log = nil then Exit;
343   if Log.LogType <> ltBottle then
344     Exit;
345   Script := frmSender.ScriptTransForSSTP(Log.Script, ErrorMes);
346   if ErrorMes <> '' then
347   begin
348     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+
349         ErrorMes + #13#10 +
350         '\8b­\90§\93I\82É\8dÄ\90\82µ\82Ü\82·\82©?'#13#10,
351         mtWarning, mbOkCancel, 0
352       );
353     if Res = mrCancel then
354       Exit;
355   end;
356
357   CueItem := TLogItem.Create(Log);
358   try
359     CueItem.Script := Script;
360     frmSender.BottleSstp.Unshift(CueItem);
361   except
362     CueItem.Free;
363   end;
364 end;
365
366 procedure TfrmLog.UpdateScriptConversationColor(const Script: String);
367 var i: integer;
368     scr: String;
369     UnyuTalking, Talked, InSynchronized: boolean;
370 begin
371   scr := Script;
372   frmSender.DoTrans(scr, [toConvertURL]);
373   SsParser.LeaveEscape := false;
374   SsParser.InputString := scr;
375   UnyuTalking := false;
376   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
377   InSynchronized := false;
378   edtScript.Text := '';
379   edtScript.Color := Pref.BgColor;
380   for i := 0 to SsParser.Count-1 do begin
381     if (SsParser[i] = '\_s') and not InSynchronized then begin
382       InSynchronized := true;
383       if Talked then begin
384         edtScript.SelText := #13#10;
385         Talked := false;
386       end;
387     end else if (SsParser[i] = '\_s') and InSynchronized then begin
388       InSynchronized := false;
389       if Talked then begin
390         edtScript.SelText := #13#10;
391         Talked := false;
392       end;
393     end;
394     if (SsParser[i] = '\u') and not UnyuTalking then begin
395       UnyuTalking := true;
396       if Talked then begin
397         edtScript.SelText := #13#10;
398         Talked := false;
399       end;
400     end;
401     if (SsParser[i] = '\h') and UnyuTalking then begin
402       UnyuTalking := false;
403       if Talked then begin
404         edtScript.SelText := #13#10;
405         Talked := false;
406       end;
407     end;
408     if SsParser.MarkUpType[i] = mtStr then begin
409       if InSynchronized then
410         edtScript.SelAttributes.Color := Pref.TalkColorS
411       else if UnyuTalking then
412         edtScript.SelAttributes.Color := Pref.TalkColorU
413       else
414         edtScript.SelAttributes.Color := Pref.TalkColorH;
415       edtScript.SelText := SsParser[i];
416       Talked := true;
417     end;
418     if SsParser.MarkUpType[i] = mtMeta then begin
419       edtScript.SelAttributes.Color := Pref.MetaWordColor;
420       edtScript.SelText := SsParser[i];
421       Talked := true;
422     end;
423   end;
424 end;
425
426 procedure TfrmLog.lvwLogKeyPress(Sender: TObject; var Key: Char);
427 begin
428   if Key = #13 then lvwLogDblClick(Sender);
429 end;
430
431 procedure TfrmLog.CreateParams(var Params: TCreateParams);
432 begin
433   inherited;
434   Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
435 end;
436
437 procedure TfrmLog.lvwLogClick(Sender: TObject);
438 var Script, Text: String;
439     Log: TLogItem;
440     Selected, IsNormalBottle: boolean;
441 begin
442   Selected := false;
443   IsNormalBottle := false;
444   if SelectedBottleLog <> nil then begin
445     Script := '';
446     if lvwLog.ItemIndex <> -1 then begin
447       Selected := true;
448       StatusBar.Panels[0].Text := Format('%d/%d\8c\8f', [lvwLog.ItemIndex+1,
449         SelectedBottleLog.Count]);
450       Log := SelectedBottleLog.Bottles[lvwLog.ItemIndex];
451       if (Log.LogType = ltBottle) and not frmSender.Connecting then begin
452         IsNormalBottle := true;
453         Script := Log.Script;
454         Text := Format('%d\83o\83C\83g/%d\95b - \83_\83u\83\8b\83N\83\8a\83b\83N\82Å\8dÄ\90¶',
455           [Length(Log.Script), frmSender.SsPlayTime.PlayTime(Log.Script) div 1000]);
456         StatusBar.Panels[1].Text := Text;
457         if Pref.LogWindowPreviewStyle = psImageConversation then
458           TalkShowFrame.View(Log)
459         else
460           UpdateScript(Script);
461       end else begin
462         StatusBar.Panels[1].Text := '';
463         UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\82ð\83N\83\8a\83A
464       end;
465     end else begin
466       StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
467       StatusBar.Panels[1].Text := '';
468       UpdateScript(Script); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
469     end;
470     tbtnSaveLog.Enabled := lvwLog.Items.Count > 0;
471   end else begin
472     StatusBar.Panels[0].Text := '';
473     UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
474   end;
475   frmSender.actVoteMessage.Enabled := Selected and IsNormalBottle;
476   frmSender.actAgreeMessage.Enabled := Selected and IsNormalBottle;
477   frmSender.actSendToEditor.Enabled := Selected and IsNormalBottle;
478   frmSender.actInsertCue.Enabled := Selected;
479   frmSender.actDeleteLogItem.Enabled := Selected;
480   mnPopUpCopyScript.Enabled := Selected and IsNormalBottle;
481   mnPopupCopyGhost.Enabled := Selected and IsNormalBottle;
482 end;
483
484 procedure TfrmLog.mnPopUpCopyScriptClick(Sender: TObject);
485 var
486   Log: TLogItem;
487   Clip: TClipBoard;
488 begin
489   Log := SelectedBottleLog.Bottles[frmLog.lvwLog.ItemIndex];
490   if Log = nil then Exit;
491   Clip := ClipBoard();
492   Clip.SetTextBuf(PChar(Log.Script));
493 end;
494
495 procedure TfrmLog.SetBottleState(const MID: String; State: TLogState);
496 var i: integer;
497     Bottle: TLogItem;
498 begin
499   for i := 0 to FBottleLogList.Count-1 do begin
500     Bottle := (FBottleLogList[i] as TBottleLogList).Bottle(MID);
501     if Bottle <> nil then begin
502       Bottle.State := State;
503       // lvwLog.OnChange := nil;
504       lvwLog.Invalidate;
505       // lvwLog.OnChange := lvwLogChange;
506     end;
507   end;
508 end;
509
510 procedure TfrmLog.mnSaveLogClick(Sender: TObject);
511 begin
512   if SelectedBottleLog = nil then Exit;
513   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
514   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
515   SaveDialog.DefaultExt := 'log';
516   SaveDialog.FilterIndex := 1;
517   if SaveDialog.Execute then
518     SelectedBottleLog.SaveToSstpLog(SaveDialog.FileName, false);
519 end;
520
521 procedure TfrmLog.mnSaveLogChannelClick(Sender: TObject);
522 begin
523   if SelectedBottleLog = nil then Exit;
524   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
525   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
526   SaveDialog.DefaultExt := 'log';
527   SaveDialog.FilterIndex := 1;
528   if SaveDialog.Execute then
529     SelectedBottleLog.SaveToSstpLog(SaveDialog.FileName, true);
530 end;
531
532 procedure TfrmLog.mnSaveLogScriptClick(Sender: TObject);
533 begin
534   if SelectedBottleLog = nil then Exit;
535   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.txt');
536   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
537   SaveDialog.DefaultExt := 'txt';
538   SaveDialog.FilterIndex := 2;
539   if SaveDialog.Execute then
540     SelectedBottleLog.SaveToText(SaveDialog.FileName);
541 end;
542
543 procedure TfrmLog.mnSaveLogXMLClick(Sender: TObject);
544 begin
545   if SelectedBottleLog = nil then Exit;
546   DoSaveLogXML(SelectedBottleLog);
547 end;
548
549 procedure TfrmLog.UpdateWindow;
550 var EnabledFlag: boolean;
551 begin
552   lvwLog.Color := Pref.BgColor;
553   lvwLog.Font.Color := Pref.TextColor;
554   if SelectedBottleLog <> nil then begin
555     Caption := '\83\8d\83O - ' + SelectedBottleLog.Title;
556     StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
557     lvwLog.Count := SelectedBottleLog.Count;
558   end else begin
559     Caption := '\83\8d\83O';
560     StatusBar.Panels[0].Text := '';
561     StatusBar.Panels[1].Text := '';
562     lvwLog.Count := 0;
563   end;
564
565   EnabledFlag := SelectedBottleLog <> nil;
566   tbtnClear.Enabled := EnabledFlag;
567   tbtnSaveLog.Enabled := EnabledFlag;
568   tbtnFindBottle.Enabled := EnabledFlag;
569
570   lvwLog.Invalidate;
571 end;
572
573 procedure TfrmLog.PopupMenuListViewPopup(Sender: TObject);
574 var Log: TLogItem;
575     Child: TMenuItem;
576     Urls: TStringList;
577     Labels: TStringList;
578     ProcessedUrl: String;
579     ProcessedLabel: String;
580     i: Integer;
581 begin
582   for i := mnJumpURL.Count-1 downto 0 do begin
583     mnJumpURL.Items[i].Free;
584   end;
585   mnJumpURL.Enabled := false;
586   if lvwLog.ItemIndex = -1 then
587     Exit;
588   Log := SelectedBottleLog.Bottles[lvwLog.ItemIndex];
589   if Log = nil then
590     Exit;
591   Urls := TStringList.Create;
592   Labels := TStringList.Create;
593   try
594     ExtractURLs(Log.Script, Urls, Labels);
595     for i := 0 to Urls.Count-1 do begin
596       Child := TMenuItem.Create(Self);
597       with Child do begin
598         ProcessedUrl := StringReplace(Urls[i], '&', '&&', [rfReplaceAll]);
599         ProcessedLabel := StringReplace(Labels[i], '&', '&&', [rfReplaceAll]);
600         if Length(ProcessedLabel) > 0 then begin
601           Caption := Format('[%s] %s (&%d)', [ProcessedLabel, ProcessedUrl, i+1]);
602         end else begin
603           Caption := Format('%s (&%d)', [ProcessedUrl, i+1]);
604         end;
605         Tag := i;
606         OnClick := mnURLClick;
607         AutoHotkeys := maManual;
608         mnJumpURL.Add(Child);
609       end;
610     end;
611     mnJumpURL.Enabled := Urls.Count > 0;
612   finally
613     Urls.Free;
614     Labels.Free;
615   end;
616 end;
617
618 procedure TfrmLog.mnURLClick(Sender: TObject);
619 var LogItem: TLogItem;
620     URL: string;
621     Urls: TStringList;
622
623 begin
624   if (lvwLog.ItemIndex <> -1) or (SelectedBottleLog = nil) then Exit;
625   LogItem := SelectedBottleLog[lvwLog.ItemIndex] as TLogItem;
626   Urls := TStringList.Create;
627   try
628     ExtractURLs(LogItem.Script, Urls, nil);
629     URL := Urls[(Sender as TMenuItem).Tag];
630     OpenBrowser(URL);
631   finally
632     Urls.Free;
633   end;
634 end;
635
636 function TfrmLog.ExtractURLs(Script: String; Urls: TStrings; Labels: TStrings): Boolean;
637 var i, u, j, count: integer;
638     s: String;
639 begin
640   count := 0;
641   SsParser.InputString := Script;
642   SsParser.LeaveEscape := true;
643   for i := 0 to SsParser.Count-1 do begin
644     if (SsParser.Match(SsParser[i], '\URL%b') > 0)
645     and (SsParser.MarkUpType[i] = mtTag) then
646     begin
647       for u := 7 downto 1 do begin
648         if (SsParser.Match(SsParser[i],
649             '\URL%b'+StringReplace(StringOfChar('-', u*2),
650             '-', '%b', [rfReplaceAll]))) > 0 then begin
651           for j := 1 to u do begin
652             s := SsParser.GetParam(SsParser[i], j*2);
653             if Pos('http://', s) > 0 then begin
654               if Urls <> nil then Urls.Add(s);
655               count := count + 1;
656             end;
657             if Labels <> nil then begin
658               s := SsParser.GetParam(SsParser[i], j*2+1);
659               Labels.Add(s);
660             end;
661           end;
662           Break;
663         end;
664       end;
665       if SsParser.Match(SsParser[i], '\URL%b%b') = 0 then begin
666         //\8aÈ\88Õ\8c`\8e®\URL\83^\83O\95Ï\8a·
667         s := SsParser.GetParam(SsParser[i], 1);
668         if Pos('http://', s) > 0 then begin
669           if Urls <> nil then Urls.Add(s);
670           count := count + 1;
671         end
672       end;
673     end;
674   end;
675
676   //\83\89\83x\83\8b\82Ì\90\94\82ðURL\82Ì\90\94\82É\82 \82í\82¹\82é - \82¢\82¿\82¢\82¿\94»\92è\82µ\82È\82­\82Ä\82à\82¢\82¢\82æ\82¤\82É
677   if Urls <> nil then begin
678     if Labels <> nil then begin
679       while Urls.Count > Labels.Count do Labels.Add('');
680     end;
681   end;
682   Result := count > 0;
683 end;
684
685 procedure TfrmLog.SelAndFocusMessage(const MID: String);
686 var i: integer;
687     Log: TLogItem;
688 begin
689   for i := 0 to SelectedBottleLog.Count-1 do begin
690     Log := SelectedBottleLog.Items[i] as TLogItem;
691     if Log.MID = MID then begin
692       lvwLog.ItemIndex := i;
693       lvwLog.SetFocus;
694     end;
695   end;
696 end;
697
698 procedure TfrmLog.UpdateScript(const Script: String);
699 begin
700   if Script <> FLastScript then begin
701     if Pref.LogWindowPreviewStyle = psConversation then begin
702       UpdateScriptConversationColor(Script);
703     end else begin
704       UpdateScriptScript(Script);
705     end;
706     SendMessage(edtScript.Handle, EM_LINESCROLL, Low(integer), Low(integer)); //\83X\83N\83\8d\81[\83\8b\96ß\82µ
707     FLastScript := Script;
708   end;
709 end;
710
711 procedure TfrmLog.PopupMenuPreviewStylePopup(Sender: TObject);
712 var i: integer;
713 begin
714   with PopupMenuPreviewStyle do
715     for i := 0 to Items.Count-1 do
716       Items[i].Checked := Items[i].Tag = Ord(Pref.LogWindowPreviewStyle)
717 end;
718
719 procedure TfrmLog.mnPreviewStyleClick(Sender: TObject);
720 var i: integer;
721 begin
722   with PopupMenuPreviewStyle do
723     for i := 0 to Items.Count-1 do
724       Items[i].Checked := (Sender as TMenuItem).Tag = Items[i].Tag;
725   Pref.LogWindowPreviewStyle := TLogWindowPreviewStyle((Sender as TMenuItem).Tag);
726   PreviewStyleChange;
727   FLastScript := '';
728   {TODO: \82È\82ñ\82Æ\82©\82·\82é}
729   //lvwLogChange(self, lvwLog.Selected, ctState);
730 end;
731
732 procedure TfrmLog.UpdateScriptScript(const Script: String);
733 var
734   UnyuTalking, InSynchronized: boolean;
735   i: integer;
736 begin
737   edtScript.Color := Pref.BgColor;
738   SsParser.LeaveEscape := true;
739   SsParser.InputString := Script;
740   edtScript.Text := '';
741   edtScript.SelAttributes.Color := clWindowText;
742   UnyuTalking := false;
743   InSynchronized := false;
744   for i := 0 to SsParser.Count-1 do begin
745     case SsParser.MarkUpType[i] of
746       mtStr: begin
747         if InSynchronized then
748           edtScript.SelAttributes.Color := Pref.TalkColorS
749         else if UnyuTalking then
750           edtScript.SelAttributes.Color := Pref.TalkColorU
751         else
752           edtScript.SelAttributes.Color := Pref.TalkColorH;
753       end;
754       mtTag: begin
755         edtScript.SelAttributes.Color := Pref.MarkUpColor;
756         if SsParser[i] = '\h' then
757           UnyuTalking := false
758         else if SsParser[i] = '\u' then
759           UnyuTalking := true
760         else if SsParser[i] = '\_s' then
761           InSynchronized := not InSynchronized;
762       end;
763       mtMeta:   edtScript.SelAttributes.Color := Pref.MetaWordColor;
764       mtTagErr: edtScript.SelAttributes.Color := Pref.MarkErrorColor;
765     end;
766     edtScript.SelText := SsParser[i];
767     if (SsParser[i] = '\n') and (Pref.LogWindowPreviewStyle = psScriptWithLineBreak) then
768       edtScript.SelText := #13#10;
769   end;
770 end;
771
772 procedure TfrmLog.tbtnPreviewStyleClick(Sender: TObject);
773 var sel: integer;
774 begin
775   sel := Ord(Pref.LogWindowPreviewStyle);
776   sel := sel + 1;
777   if sel > Ord(High(TLogWindowPreviewStyle)) then sel := 0;
778   Pref.LogWindowPreviewStyle := TLogWindowPreviewStyle(sel);
779   FLastScript := '';
780   PreviewStyleChange;
781   {TODO: \82È\82ñ\82Æ\82©\82·\82é}
782   //lvwLogChange(self, lvwLog.Selected, ctState);
783 end;
784
785 function TfrmLog.SelectedBottleLog: TBottleLogList;
786 begin
787   if tabBottleLog.TabIndex >= 0 then
788     Result := FBottleLogList.Items[tabBottleLog.TabIndex] as TBottleLogList
789   else
790     Result := nil;
791 end;
792
793 procedure TfrmLog.tabBottleLogChange(Sender: TObject);
794 begin
795   // StatusBar\82Ì\8c\8f\90\94\95\\8e¦\82âListView.Items.Count\82ð\8dX\90V\82·\82é
796   UpdateWindow;
797   // \83A\83C\83e\83\80\82Ì\91I\91ð\8fó\91Ô\82ð\95\9c\8bA\82·\82é
798   with SelectedBottleLog do
799     if (SelectedIndex >= 0) and (Count > SelectedIndex) then
800     begin
801       lvwLog.ItemIndex := SelectedIndex;
802     end;
803   {TODO: \82È\82ñ\82Æ\82©\82·\82é}
804   //lvwLogChange(Self, nil, ctState);
805 end;
806
807 procedure TfrmLog.LogLoaded(Sender: TObject);
808 begin
809   if SelectedBottleLog = Sender then begin
810     UpdateWindow;
811   end;
812 end;
813
814 procedure TfrmLog.UpdateTab;
815 var i, cur: integer;
816 begin
817   cur := tabBottleLog.tabIndex;
818   tabBottleLog.Tabs.Clear;
819   for i := 0 to FBottleLogList.Count - 1 do begin
820     tabBottleLog.Tabs.Add((FBottleLogList[i] as TBottleLogList).Title);
821   end;
822   if FBottleLogList.Count > 0 then begin
823     if cur < FBottleLogList.Count then
824       tabBottleLog.TabIndex := cur
825     else
826       tabBottleLog.TabIndex := FBottleLogList.Count-1;
827   end;
828 end;
829
830 procedure TfrmLog.LogLoadFailure(Sender: TObject; const Message: String);
831 begin
832   Beep;
833   ShowMessage(Message);
834   if Sender = SelectedBottleLog then UpdateWindow;
835 end;
836
837 procedure TfrmLog.AgreeLog(const MID: String; const Agree: integer);
838 var i: integer;
839     flag: boolean;
840 begin
841   flag := false;
842   for i := 0 to FBottleLogList.Count - 1 do begin
843     if (FBottleLogList[i] as TBottleLogList).Bottle(MID) <> nil then begin
844       (FBottleLogList[i] as TBottleLogList).Bottle(MID).Agrees := Agree;
845       flag := true;
846     end;
847   end;
848   if flag then lvwLog.Invalidate;
849 end;
850
851 procedure TfrmLog.VoteLog(const MID: String; const Vote: integer);
852 var i: integer;
853     flag: boolean;
854 begin
855   flag := false;
856   for i := 0 to FBottleLogList.Count - 1 do begin
857     if (FBottleLogList[i] as TBottleLogList).Bottle(MID) <> nil then begin
858       (FBottleLogList[i] as TBottleLogList).Bottle(MID).Votes := Vote;
859       flag := true;
860     end;
861   end;
862   if flag then lvwLog.Invalidate;
863 end;
864
865 procedure TfrmLog.tabBottleLogChanging(Sender: TObject;
866   var AllowChange: Boolean);
867 begin
868   // \8c»\8dÝ\91I\91ð\82³\82ê\82Ä\82¢\82é\83\8d\83O\82Ì\91I\91ð\8fó\91Ô\82ð\95Û\91
869   if SelectedBottleLog = nil then
870     Exit;
871   SelectedBottleLog.SelectedIndex := lvwLog.ItemIndex
872 end;
873
874 procedure TfrmLog.tabBottleLogContextPopup(Sender: TObject;
875   MousePos: TPoint; var Handled: Boolean);
876 begin
877   with tabBottleLog do begin
878     Tag := IndexOfTabAt(MousePos.X, MousePos.Y);
879     if Tag < 0 then Handled := true;
880   end;
881 end;
882
883 procedure TfrmLog.mnCloseTabClick(Sender: TObject);
884 begin
885   DoCloseTab(tabBottleLog.Tag);
886 end;
887
888 procedure TfrmLog.tbtnFindBottleClick(Sender: TObject);
889 var ResultLog: TBottleLogList;
890     Cond: TSearchCond;
891     i: integer;
892     CList, GList: THashedStringList;
893 begin
894   Application.CreateForm(TfrmSearchLog, frmSearchLog);
895   Cond := TSearchCond.Create(nil);
896   try
897     try
898       with frmSearchLog do
899       begin
900         // \8c»\8dÝ\83\8d\83O\82É\82 \82é\83S\81[\83X\83g\82Æ\83`\83\83\83\93\83l\83\8b\82Ì\83\8a\83X\83g\82ð\8eæ\93¾
901         // \8fd\82½\82¢\82©\82à??
902         CList := THashedStringList.Create;
903         GList := THashedStringList.Create;
904         try
905           for i := 0 to BottleLogList.Count-1 do
906           begin
907             with BottleLogList[i] as TBottleLogList do
908             begin
909               ExtractUniqueChannels(CList);
910               ExtractUniqueGhosts(GList);
911             end;
912           end;
913           CList.Sort;
914           GList.Sort;
915           ChannelList := CList;
916           GhostList   := GList;
917         finally
918           CList.Free;
919           GList.Free;
920         end;
921         if not Execute then
922           Exit
923         else
924           Cond.Assign(Condition);
925       end;
926     finally
927       frmSearchLog.Release;
928     end;
929     // \8c\9f\8dõ\8eÀ\8ds
930     ResultLog := DoSearchLog(Cond);
931     // \90V\83^\83u\82ð\8dì\90¬\82µ\82Ä\89æ\96Ê\8dX\90V
932     BottleLogList.Add(ResultLog);
933     UpdateTab;
934     tabBottleLog.TabIndex := BottleLogList.Count-1;
935     UpdateWindow;
936   finally
937     Cond.Free;
938   end;
939 end;
940
941 procedure TfrmLog.tbtnOpenLogClick(Sender: TObject);
942 var BottleLog: TBottleLogList;
943     i, Index: integer;
944 begin
945   Index := -1;
946   if OpenDialog.Execute then begin
947     for i := 0 to OpenDialog.Files.Count-1 do begin
948       BottleLog := TBottleLogList.Create(ExtractFileName(OpenDialog.Files[i]));
949       try
950         with BottleLog do
951         begin
952           OnLoaded := LogLoaded;
953           OnLoadFailure := LogLoadFailure;
954           OnLoadWork := LogLoadWork;
955           BottleLog.LoadFromXMLFile(OpenDialog.Files[i]);
956         end;
957         Index := BottleLogList.Add(BottleLog); // \8dÅ\8cã\82É\8aJ\82¢\82½\83\8d\83O\82Ì\88Ê\92u\82ð\8bL\89¯
958       except
959         BottleLog.Free;
960       end;
961     end;
962     UpdateTab;
963     if Index >= 0 then tabBottleLog.TabIndex := Index;
964     UpdateWindow;
965   end;
966 end;
967
968 function TfrmLog.GetDefaultFileName(const Name, Ext: String): String;
969 begin
970   Result := StringReplace(Name, '/', '', [rfReplaceAll]);
971   Result := StringReplace(Result, ' ', '', [rfReplaceAll]);
972   Result := SafeFileName(Result);
973   Result := ChangeFileExt(Result, Ext);
974 end;
975
976 function TfrmLog.BottleLogTitled(const LogName: String): TBottleLogList;
977 var i: integer;
978 begin
979   for i := 0 to FBottleLogList.Count-1 do begin
980     if (FBottleLogList[i] as TBottleLogList).Title = LogName then begin
981       Result := (FBottleLogList[i] as TBottleLogList);
982       Exit;
983     end;
984   end;
985   // \8c©\82Â\82©\82ç\82È\82¢\8fê\8d\87
986   Result := TBottleLogList.Create(LogName); // \90V\82µ\82­\8dì\82é
987   FBottleLogList.Add(Result);
988   UpdateTab;
989   if FBottleLogList.Count = 1 then tabBottleLog.TabIndex := 0;
990 end;
991
992 procedure TfrmLog.AllBottleOpened;
993 var i, j: integer;
994     Log: TBottleLogList;
995 begin
996   for i := 0 to FBottleLogList.Count-1 do begin
997     Log  := FBottleLogList[i] as TBottleLogList;
998     for j := 0 to Log.Count-1 do begin
999       Log.Bottles[j].State := lsOpened;
1000     end;
1001   end;
1002 end;
1003
1004 procedure TfrmLog.tabBottleLogMouseDown(Sender: TObject;
1005   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
1006 var Index: integer;
1007 begin
1008   if Button = mbMiddle then
1009   begin
1010     //\92\86\83{\83^\83\93\83N\83\8a\83b\83N\82Å\83^\83u\8dí\8f\9c
1011     DoCloseTab(tabBottleLog.IndexOfTabAt(X, Y));
1012   end else
1013   begin
1014     with tabBottleLog do begin
1015       Index := IndexOfTabAt(X, Y);
1016       if Index = -1 then Exit; //\83^\83u\82ª\82È\82¢\82Ì\82Å\83h\83\89\83b\83O\82Å\82«\82È\82¢
1017       if Button = mbLeft then begin
1018         FDragTabIndex := Index; //\83h\83\89\83b\83O\82·\82é\83^\83u\82Ì\83C\83\93\83f\83b\83N\83X\82ð\95Û\91
1019         BeginDrag(False);
1020         FDragTabDest := -1;     //\83h\83\89\83b\83O\98g\90ü\95`\89æ\83t\83\89\83O\83N\83\8a\83A\82Ì\82½\82ß
1021       end;
1022     end;
1023   end;
1024 end;
1025
1026 procedure TfrmLog.tabBottleLogDragOver(Sender, Source: TObject; X,
1027   Y: Integer; State: TDragState; var Accept: Boolean);
1028 var TargetRect: TRect;
1029     OldDest, Index: integer;
1030     dummy: boolean;
1031 begin
1032   // \83^\83u\82Ì\83h\83\89\83b\83O(\83^\83u\82Ì\8f\87\94Ô\93ü\82ê\91Ö\82¦)\82Ü\82½\82Í\81A
1033   // \83\8d\83O\83A\83C\83e\83\80\82Ì\83h\83\89\83b\83O(\83\8d\83O\82ð\95Ê\82Ì\83^\83u\82É\88Ú\93®)\82Ì
1034   // \97¼\95û\82Ì\83h\83\89\83b\83O\82ð\8eó\82¯\95t\82¯\82é
1035   Accept := false;
1036   if Source = tabBottleLog then
1037   begin
1038     // \83^\83u\82Ì\8f\87\94Ô\93ü\82ê\91Ö\82¦\82Ì\8fê\8d\87
1039     Accept := true;
1040     with tabBottleLog do begin
1041       OldDest := FDragTabDest;
1042       FDragTabDest := IndexOfTabAt(X, Y);
1043       if FDragTabDest = -1 then begin
1044         Accept := false; //\82±\82Ì\8fê\8d\87\82Í\83h\83\8d\83b\83v\82ð\94F\82ß\82È\82¢
1045         Exit;
1046       end;
1047       with Canvas do begin
1048         Pen.Mode := pmNot;
1049         Pen.Width := 3;
1050       end;
1051       if (OldDest <> FDragTabDest) and (OldDest >= 0) then begin
1052         //\88È\91O\82Ì\98g\90ü\8fÁ\8b\8e
1053         TargetRect := TabRect(OldDest);
1054         with Canvas do begin
1055           Brush.Style := bsClear;
1056           Rectangle(TargetRect.Left, TargetRect.Top,
1057                     TargetRect.Right, TargetRect.Bottom);
1058         end;
1059       end;
1060       if (OldDest <> FDragTabDest) then begin
1061         //\90V\82µ\82¢\98g\90ü\95`\89æ
1062         TargetRect := TabRect(FDragTabDest);
1063         with Canvas do begin
1064           Brush.Style := bsClear;
1065           Rectangle(TargetRect.Left, TargetRect.Top,
1066                     TargetRect.Right, TargetRect.Bottom);
1067         end;
1068       end;
1069     end;
1070   end else if Source is TBottleLogDragObject then
1071   begin
1072     // \83\8d\83O\8d\80\96Ú\82Ì\83h\83\89\83b\83O(\83\8d\83O\82ð\95Ê\82Ì\83^\83u\82É\88Ú\93®\82·\82é)\82Ì\8fê\8d\87
1073     Index := tabBottleLog.IndexOfTabAt(X, Y);
1074     if tabBottleLog.TabIndex <> Index then
1075     begin
1076       FLVDragDest := -1; // \98g\90ü\82Í\82Ü\82¾\95\\8e¦\82³\82ê\82È\82¢\82Í\82¸
1077       // \83^\83u\82ð\90Ø\91Ö\82¦\82é
1078       tabBottleLogChanging(Self, dummy);
1079       tabBottleLog.TabIndex := Index;
1080       UpdateWindow;
1081     end;
1082   end;
1083 end;
1084
1085 procedure TfrmLog.tabBottleLogDragDrop(Sender, Source: TObject; X,
1086   Y: Integer);
1087 var DestIndex: integer;
1088 begin
1089   with tabBottleLog do begin
1090     DestIndex := IndexOfTabAt(X, Y);
1091     Tabs.Move(FDragTabIndex, DestIndex);
1092     FBottleLogList.Move(FDragTabIndex, DestIndex);
1093   end;
1094 end;
1095
1096 procedure TfrmLog.tabBottleLogEndDrag(Sender, Target: TObject; X,
1097   Y: Integer);
1098 begin
1099   //\8b­\90§\93I\82É\83^\83u\82ð\8dÄ\95`\89æ\82³\82¹\82é\81B\98g\90ü\8fÁ\82µ\91Î\8dô
1100   tabBottleLog.Tabs.BeginUpdate;
1101   tabBottleLog.Tabs.EndUpdate;
1102 end;
1103
1104 procedure TfrmLog.LogLoadWork(Sender: TObject);
1105 begin
1106   if Sender = SelectedBottleLog then
1107   begin
1108     lvwLog.Invalidate;
1109     lvwLog.Count := SelectedBottleLog.Count;
1110   end;
1111 end;
1112
1113 procedure TfrmLog.DrawSingleLineScript(LogItem: TLogItem;
1114   Rect: TRect; State: TOwnerDrawState);
1115 var
1116   i, x, w: integer;
1117   UnyuTalking, Synchronized, Spaced: boolean;
1118   Mark: TSsMarkUpType;
1119   Script: String;
1120   Ico: TIcon;
1121   procedure ScopeChange;
1122   begin
1123     if (not Spaced) and (Pref.LogListPreviewStyle = psTagStripped) then
1124     begin
1125       Inc(x, 7);
1126       Spaced := true;
1127     end;
1128   end;
1129 begin
1130   Script := LogItem.Script;
1131   x := 3;
1132
1133   if LogItem.HasURL = huYes then
1134   begin
1135     Ico := TIcon.Create;
1136     try
1137       frmSender.imgIcon.GetIcon(IconURL, Ico);
1138       lvwLog.Canvas.Draw(Rect.Left + x, Rect.Top, Ico);
1139       Inc(x, 20);
1140     finally
1141       Ico.Free;
1142     end;
1143   end;
1144
1145   if Pref.LogListPreviewStyle = psNoColor then
1146   begin
1147     Inc(Rect.Left, x);
1148     Inc(Rect.Top, 2);
1149     Dec(Rect.Right, 2);
1150     DrawTextEx(lvwLog.Canvas.Handle, PChar(Script), -1, Rect,
1151       DT_SINGLELINE or DT_END_ELLIPSIS or DT_NOPREFIX, nil);
1152     Exit;
1153   end;
1154
1155   SsParser.LeaveEscape := Pref.LogListPreviewStyle = psNormal;
1156   SsParser.InputString := Script;
1157
1158   UnyuTalking := false;
1159   Synchronized := false;
1160   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¢
1161                   // \82½\82ß\82Ì\83t\83\89\83O
1162   for i := 0 to SsParser.Count - 1 do begin
1163     if SsParser[i] = '\h' then
1164     begin
1165       UnyuTalking := false;
1166       ScopeChange;
1167     end else if SsParser[i] = '\u' then
1168     begin
1169       UnyuTalking := true;
1170       ScopeChange;
1171     end else if SsParser[i] = '\_s' then
1172     begin
1173       Synchronized := not Synchronized;
1174       ScopeChange;
1175     end else if (Pos('\n', SsParser[i]) = 1) or (SsParser[i] = '\c') then
1176     begin
1177       ScopeChange;
1178     end;
1179     Mark := SsParser.MarkUpType[i];
1180     case Mark of
1181       mtMeta:
1182         begin
1183           lvwLog.Canvas.Font.Color := Pref.MetaWordColor;
1184           Spaced := false;
1185         end;
1186       mtTag:
1187         if Pref.LogListPreviewStyle = psNormal then
1188           lvwLog.Canvas.Font.Color := Pref.MarkUpColor
1189         else
1190         begin
1191           Continue;
1192         end;
1193       mtTagErr:
1194         lvwLog.Canvas.Font.Color := Pref.MarkErrorColor;
1195       else begin
1196         Spaced := false;
1197         if Synchronized then
1198           lvwLog.Canvas.Font.Color := Pref.TalkColorS
1199         else if UnyuTalking then
1200           lvwLog.Canvas.Font.Color := Pref.TalkColorU
1201         else
1202           lvwLog.Canvas.Font.Color := Pref.TalkColorH;
1203       end;
1204     end;
1205     if odSelected in State then
1206     begin
1207       if odFocused in State then
1208         lvwLog.Canvas.Font.Color := clHighlightText
1209       else
1210         lvwLog.Canvas.Font.Color := clWindowText;
1211     end;
1212     w := lvwLog.Canvas.TextWidth(SsParser[i]);
1213     lvwLog.Canvas.TextRect(Rect, Rect.Left + x, Rect.Top + 2, SsParser[i]);
1214     x := x + w;
1215     if Rect.Right - Rect.Left < x then
1216       Break;
1217   end;
1218 end;
1219
1220 procedure TfrmLog.mnListPreviewStyleClick(Sender: TObject);
1221 var i: integer;
1222 begin
1223   with PopupMenuListPreviewStyle do
1224     for i := 0 to Items.Count-1 do
1225       Items[i].Checked := (Sender as TMenuItem).Tag = Items[i].Tag;
1226   Pref.LogListPreviewStyle := TLogListPreviewStyle((Sender as TMenuItem).Tag);
1227   lvwLog.Invalidate;
1228 end;
1229
1230 procedure TfrmLog.tbtnListPreviewStyleClick(Sender: TObject);
1231 var sel: integer;
1232 begin
1233   sel := Ord(Pref.LogListPreviewStyle);
1234   sel := sel + 1;
1235   if sel > Ord(High(TLogListPreviewStyle)) then sel := 0;
1236   Pref.LogListPreviewStyle := TLogListPreviewStyle(sel);
1237   lvwLog.Invalidate;
1238 end;
1239
1240 procedure TfrmLog.PopupMenuListPreviewStylePopup(Sender: TObject);
1241 var i: integer;
1242 begin
1243   with PopupMenuListPreviewStyle do
1244     for i := 0 to Items.Count-1 do
1245       Items[i].Checked := Items[i].Tag = Ord(Pref.LogListPreviewStyle)
1246 end;
1247
1248 procedure TfrmLog.PreviewStyleChange;
1249 begin
1250   if Pref.LogWindowPreviewStyle = psImageConversation then
1251   begin
1252     if Spps.Count = 0 then
1253       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');
1254     edtScript.Visible := false;
1255     TalkShowFrame.Visible := true;
1256   end else
1257   begin
1258     edtScript.Visible := true;
1259     TalkShowFrame.Visible := false;
1260   end;
1261 end;
1262
1263 procedure TfrmLog.lvwLogDragOver(Sender, Source: TObject; X, Y: Integer;
1264   State: TDragState; var Accept: Boolean);
1265 var
1266   OldDest, Target: Integer;
1267   Rec: TRect; // \83_\83~\81[\81B
1268 begin
1269   Accept := False;
1270   // \82Æ\82è\82 \82¦\82¸\8eó\82¯\95t\82¯\82é\89Â\94\\90«\82ª\82 \82é\82Ì\82ÍTBottleLogDragObject\82¾\82¯
1271   if not (Source is TBottleLogDragObject) then
1272     Exit;
1273
1274   Target := lvwLog.ItemAtPos(Point(X, Y), true);
1275
1276   // \82±\82ê\88È\91O\82É\95`\89æ\82³\82ê\82Ä\82¢\82½\98g\82Ì\83C\83\93\83f\83b\83N\83X
1277   OldDest := FLVDragDest;
1278
1279   // \83h\83\8d\83b\83v\88Ê\92u\82É Item \82ª\82 \82ê\82Î\83h\83\8d\83b\83v\82ð\8b\96\89Â\82·\82é
1280   if Target <> -1 then
1281   begin
1282     Accept := true;
1283     FLVDragDest := Target;
1284   end else
1285   begin
1286     Accept := true;
1287     FLVDragDest := -1;
1288   end;
1289
1290   // \88È\91O\82Ì\98g\90ü\82ð\8dí\8f\9c
1291   if (OldDest > -1) and (FLVDragDest <> OldDest) then
1292   begin
1293     Rec := lvwLog.ItemRect(OldDest);
1294     DrawListViewDragBorder(Rec);
1295   end;
1296   // \83h\83\89\83b\83O\90æ\82Ì\98g\90ü\82ð\95`\89æ
1297   if (Target <> -1) and (FLVDragDest <> OldDest) then
1298   begin
1299     Rec := lvwLog.ItemRect(Target);
1300     DrawListViewDragBorder(Rec);
1301   end;
1302
1303   // \83X\83N\83\8d\81[\83\8b\8aÖ\8cW
1304   {TODO: \83X\83N\83\8d\81[\83\8b\95\9c\8a\88\82³\82¹\82é}
1305   {if lvwLog.Count > 0 then
1306   begin
1307     if (lvwLog.topItem <> nil) and (Y - lvwLog.TopItem.Top < 10) then
1308     begin
1309       FLVScrollDir := lvScrollDown;
1310       if not timScrollTimer.Enabled then
1311         timScrollTimer.Enabled := true;
1312     end else if (lvwLog.Height - Y) < 10 then
1313     begin
1314       FLVScrollDir := lvScrollUp;
1315       if not timScrollTimer.Enabled then
1316         timScrollTimer.Enabled := true;
1317     end
1318     else
1319       timScrollTimer.Enabled := false;
1320   end else
1321     timScrollTimer.Enabled := false;}
1322 end;
1323
1324 procedure TfrmLog.lvwLogDragDrop(Sender, Source: TObject; X, Y: Integer);
1325 var
1326   TargetItem: integer;
1327   Src: TBottleLogDragObject;
1328   SrcLog: TObject;
1329 begin
1330   timScrollTimer.Enabled := false;
1331
1332   if not (Source is TBottleLogDragObject) then
1333     Exit;
1334   Src := Source as TBottleLogDragObject;
1335
1336   TargetItem := lvwLog.ItemAtPos(Point(X, Y), true);
1337
1338   try
1339     // \83h\83\8d\83b\83v\88Ê\92u\82É Item \82ð\88Ú\93®\82·\82é
1340     if (GetAsyncKeyState(VK_CONTROL) and $8000) > 0 then
1341     begin // \83R\83s\81[\88Ú\93®\82Ì\8fê\8d\87
1342       SrcLog := TLogItem.Create(Src.LogItem);
1343     end else // \88Ú\93®\82¾\82¯\82·\82é\8fê\8d\87
1344     begin
1345       SrcLog := Src.BottleLogList.Extract(Src.LogItem);
1346     end;
1347     if TargetItem >= 0 then
1348     begin
1349       // \82·\82Å\82É\91\8dÝ\82·\82é\83A\83C\83e\83\80\82Ì\8fã\82É\83h\83\8d\83b\83v\82µ\82½\8fê\8d\87
1350       SelectedBottleLog.Insert(TargetItem, SrcLog);
1351     end else
1352     begin
1353       // ListView\82Ì\97]\94\92\82É\83h\83\8d\83b\83v\82µ\82½\8fê\8d\87(Insert\82Å\82«\82È\82¢)
1354       TargetItem := SelectedBottleLog.Add(SrcLog);
1355     end;
1356     lvwLog.ItemIndex := TargetItem;
1357   finally
1358     UpdateWindow;
1359   end;
1360 end;
1361
1362 procedure TfrmLog.timScrollTimerTimer(Sender: TObject);
1363 var
1364   ScrollHeight: Integer;
1365 begin
1366   // \83X\83N\83\8d\81[\83\8b\97Ê\82ð\8b\81\82ß\82é
1367   ScrollHeight := lvwLog.ItemHeight;
1368
1369   {TODO: \83X\83N\83\8d\81[\83\8b\95\9c\8a\88}
1370   case FLVScrollDir of
1371     lvScrollUp: ;//lvwLog.Scroll(0, ScrollHeight);
1372     lvSCrollDown: ;//lvwLog.Scroll(0, -ScrollHeight);
1373   end;
1374   lvwLog.Invalidate;  // \8dÅ\90V\82Ì\8fó\91Ô\82É\8dÄ\95`\89æ\82·\82é
1375   lvwLog.Update;
1376 end;
1377
1378 procedure TfrmLog.mnChangeTabNameClick(Sender: TObject);
1379 var Name: String;
1380 begin
1381   Name := (FBottleLogList[tabBottleLog.Tag] as TBottleLogList).Title;
1382   InputQuery('\96¼\91O\82Ì\95Ï\8dX', '\90V\82µ\82¢\83^\83u\82Ì\96¼\91O', Name);
1383   (FBottleLogList[tabBottleLog.Tag] as TBottleLogList).Title := Name;
1384   UpdateTab;
1385 end;
1386
1387 procedure TfrmLog.lvwLogStartDrag(Sender: TObject;
1388   var DragObject: TDragObject);
1389 var Drag: TBottleLogDragObject;
1390 begin
1391   // TDragObject\82©\82ç\92¼\90Ú\8cp\8f³\82µ\82½\82¾\82¯\82Ì\82à\82Ì(\83C\83\81\81[\83W\82ð\8e\9d\82Á\82Ä\82¢\82È\82¢)\82ð\8eg\82¤\82Æ
1392   // \83h\83\89\83b\83O\83C\83\81\81[\83W\82Ì\95`\89æ\82Í\97}\90§\82Å\82«\82é\81B
1393   Drag := TBottleLogDragObject.Create(lvwLog);
1394   Drag.BottleLogList := SelectedBottleLog;
1395   Drag.LogItem := SelectedBottleLog.Bottles[lvwLog.ItemIndex];
1396   DragObject := Drag;
1397 end;
1398
1399 procedure TfrmLog.lvwLogEndDrag(Sender, Target: TObject; X, Y: Integer);
1400 begin
1401   // \98g\90ü\8fÁ\82µ\97p\82É\8b­\90§\93I\82É\8dÄ\95`\89æ\82³\82¹\82é
1402   timScrollTimer.Enabled := false;
1403   FLVDragDest := -1;
1404   UpdateWindow;
1405 end;
1406
1407 procedure TfrmLog.DrawListViewDragBorder(const Rect: TRect);
1408 var Rec: TRect;
1409 begin
1410   Rec := Rect;
1411   InflateRect(Rec, -1, -1);
1412   with lvwLog.Canvas do
1413   begin
1414     Pen.Mode := pmNot;
1415     Pen.Width := 3;
1416     Brush.Style := bsClear;
1417     Refresh; // \95K\97v
1418     Rectangle(Rec);
1419   end;
1420 end;
1421
1422 procedure TfrmLog.DoSaveLogXML(Log: TBottleLogList);
1423 begin
1424   SaveDialog.FileName := GetDefaultFileName(Log.Title, '.xml');
1425   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
1426   SaveDialog.DefaultExt := 'xml';
1427   SaveDialog.FilterIndex := 3;
1428   if SaveDialog.Execute then
1429     Log.SaveToXmlFile(SaveDialog.FileName);
1430 end;
1431
1432 procedure TfrmLog.DoCloseTab(const Index: integer);
1433 var
1434   Confirm: String;
1435   PrevSelection: TBottleLogList; // \95Â\82\82½\82Æ\82«\83^\83u\82ª\82¸\82ê\82È\82¢\82æ\82¤\82É\82·\82é\8f\88\97\9d\97p
1436   i: integer;
1437 begin
1438   if Pref.ConfirmOnTabClose then
1439   begin
1440     Confirm := Format('\83^\83u"%s"\82ð\95Â\82\82Ü\82·\82©?', [(FBottleLogList[Index] as TBottleLogList).Title]);
1441     if MessageDlg(Confirm, mtConfirmation, mbOkCancel, 0) = mrCancel then
1442       Exit;
1443   end;
1444   PrevSelection := SelectedBottleLog;
1445   FBottleLogList.Delete(Index);
1446   UpdateTab;
1447   // \83^\83u\82¸\82ê\96h\8e~\8f\88\97\9d
1448   for i := 0 to FBottleLogList.Count-1 do
1449     if FBottleLogList[i] = PrevSelection then
1450       tabBottleLog.TabIndex := i;
1451   UpdateWindow;
1452 end;
1453
1454 procedure TfrmLog.HTMLOutputWork(Sender: TObject; const Count: integer;
1455   var Canceled: boolean);
1456 begin
1457   frmHTMLOutputProgress.ProgressBar.Position := Count;
1458   Application.ProcessMessages;
1459   if frmHTMLOutputProgress.Canceled then
1460     Canceled := true;
1461 end;
1462
1463 function TfrmLog.DoSearchLog(Condition: TSearchCond): TBottleLogList;
1464 var i, UntilIndex: integer;
1465 begin
1466   Result := TBottleLogList.Create('\8c\9f\8dõ\8c\8b\89Ê');
1467   if Condition.SearchLogRange in [srSelectedLogList, srAboveSelectedLog] then
1468   begin
1469     if SelectedBottleLog = nil then
1470     begin
1471       ShowMessage('\8c\9f\8dõ\91Î\8fÛ\82ª\82 \82è\82Ü\82¹\82ñ');
1472       Result.Free;
1473       Result := nil;
1474       Exit;
1475     end else
1476     begin
1477       if Condition.SearchLogRange = srSelectedLogList then
1478         UntilIndex := -1
1479       else
1480         UntilIndex := lvwLog.ItemIndex;
1481       SearchLogIndivisual(Condition, SelectedBottleLog, Result, UntilIndex);
1482     end;
1483   end else if Condition.SearchLogRange = srAllLogLists then
1484   begin
1485     for i := 0 to BottleLogList.Count-1 do
1486     begin
1487       SearchLogIndivisual(Condition, BottleLogList[i] as TBottleLogList,
1488         Result);
1489     end;
1490   end;
1491
1492   if Result.Count = 0 then
1493     Result.AddSystemLog('\8c©\82Â\82©\82è\82Ü\82¹\82ñ\82Å\82µ\82½\81B');
1494 end;
1495
1496 procedure TfrmLog.SearchLogIndivisual(Condition: TSearchCond; LogList,
1497   Result: TBottleLogList; UntilIndex: integer = -1);
1498 var
1499   i, Max: integer;
1500   Bottle, New: TLogItem;
1501   Ok: boolean;
1502 begin
1503   // 1\8cÂ\82Ì\83\8d\83O\83^\83u\82É\91Î\82µ\82Ä\8c\9f\8dõ\82ð\82©\82¯\82é\81BUntilIndex\82Å\94Í\88Í\8ew\92è(\8fÈ\97ª\8e\9e\82»\82Ì\83^\83u\91S\91Ì)
1504   if UntilIndex >= 0 then
1505     Max := UntilIndex
1506   else
1507     Max := LogList.Count-1;
1508   for i := 0 to Max do
1509   begin
1510     // \8fð\8c\8f\94»\92è
1511     Bottle := LogList.Bottles[i];
1512     if Bottle.LogType <> ltBottle then
1513       Continue;
1514     Ok := true;
1515     // \83X\83N\83\8a\83v\83g\83p\83^\81[\83\93\82Å\89ð\90Í
1516     if Condition.ScriptPattern <> '' then
1517     begin
1518       if Condition.ScriptRegExp then
1519       begin
1520         try
1521           if not RegExp.Match(Condition.ScriptPattern, Bottle.Script) then
1522             Ok := false;
1523         except
1524           on EBRegExpError do
1525             Ok := false; //\96­\82È\90³\8bK\95\\8c»\82ð\8fR\82é
1526         end;
1527       end else
1528       begin
1529         if not AnsiContainsText(Bottle.Script, Condition.ScriptPattern) then
1530           Ok := false;
1531       end;
1532     end;
1533     // \83`\83\83\83\93\83l\83\8b\96¼\81A\83S\81[\83X\83g\96¼\81A\93\8a\95[\93¯\88Ó
1534     if Condition.Channel <> '' then
1535       if not AnsiContainsText(Bottle.Channel, Condition.Channel) then
1536         Ok := false;
1537     if Condition.Ghost <> '' then
1538       if not AnsiContainsText(Bottle.Ghost, Condition.Ghost) then
1539         Ok := false;
1540     if Condition.MinVote > Bottle.Votes then
1541       Ok := false;
1542     if Condition.MinAgree > Bottle.Agrees then
1543       Ok := false;
1544     // \8fð\8c\8f\82É\88ê\92v\82µ\82½\82à\82Ì\82ð\8c\8b\89Ê\83\8a\83X\83g\82É\92Ç\89Á
1545     if Ok then
1546     begin
1547       New := TLogItem.Create(Bottle); // \83R\83s\81[\83R\83\93\83X\83g\83\89\83N\83^
1548       New.State := lsOpened;
1549       Result.Add(New);
1550     end;
1551   end;
1552 end;
1553
1554 procedure TfrmLog.CheckBottleURL(Bottle: TLogItem);
1555 var
1556   DummyStr: TStringList;
1557 begin
1558   if Bottle.HasURL = huUndefined then
1559   begin
1560     DummyStr := TStringList.Create;
1561     try
1562       ExtractURLs(Bottle.Script, DummyStr, nil);
1563       if DummyStr.Count > 0 then
1564         Bottle.HasURL := huYes
1565       else
1566         Bottle.HasURL := huNo;
1567     finally
1568       DummyStr.Free;
1569     end;
1570   end;
1571 end;
1572
1573 procedure TfrmLog.DrawListSection(const SectionIndex, Top: Integer;
1574   const Text: string; const LeftMargin: Integer = 0);
1575 var
1576   Sec: THeaderSection;
1577   Ex: Integer;
1578   Rec: TRect;
1579 const
1580   MinimumTextWidth = 16; //\82±\82ê\82æ\82è\8b·\82¢\82È\82ç\82»\82à\82»\82à\95`\89æ\82µ\82È\82¢
1581 begin
1582   Sec := Header.Sections[SectionIndex];
1583
1584   Rec.Left   := Sec.Left + 2 + LeftMargin;
1585   Rec.Right  := Sec.Right - 2;
1586   if Rec.Right - Rec.Left < MinimumTextWidth then
1587     Exit;
1588   Rec.Top    := Top + 2;
1589   Rec.Bottom := Top + lvwLog.ItemHeight;
1590
1591   Ex := DT_NOPREFIX or DT_SINGLELINE or DT_END_ELLIPSIS;
1592   if Sec.Alignment = taRightJustify then
1593     Ex := (Ex or DT_RIGHT) and not (DT_END_ELLIPSIS);
1594   DrawTextEx(lvwLog.Canvas.Handle, PChar(Text), -1, Rec, Ex, nil);
1595 end;
1596
1597 { TBottleLogDragObject }
1598
1599 function TBottleLogDragObject.GetDragImages: TDragImageList;
1600 begin
1601   // \92\86\93r\94¼\92[\82È\83h\83\89\83b\83O\83C\83\81\81[\83W\82ð\95\\8e¦\82µ\82È\82¢\82æ\82¤\82É\82·\82é
1602   Result := nil;
1603 end;
1604
1605 procedure TBottleLogDragObject.SetBottleLogList(
1606   const Value: TBottleLogList);
1607 begin
1608   FBottleLogList := Value;
1609 end;
1610
1611 procedure TBottleLogDragObject.SetLogItem(const Value: TLogItem);
1612 begin
1613   FLogItem := Value;
1614 end;
1615
1616 procedure TfrmLog.mnTabSaveXMLLogClick(Sender: TObject);
1617 begin
1618   DoSaveLogXML(FBottleLogList[tabBottleLog.Tag] as TBottleLogList);
1619 end;
1620
1621 procedure TfrmLog.mnSaveHTMLClick(Sender: TObject);
1622 var
1623   LogList, SB: TBottleLogList;
1624   i: integer;
1625   Options: THTMLOutputOptions;
1626 begin
1627   SB := SelectedBottleLog;
1628   if SB = nil then
1629     Exit;
1630   if SB.Count = 0 then
1631     Exit;
1632   Application.CreateForm(TfrmHTMLOutputConfig, frmHTMLOutputConfig);
1633   with frmHTMLOutputConfig do
1634     try
1635       // Show HTML save option dialog
1636       if not Execute then
1637         Exit;
1638       LogList := TBottleLogList.Create('');
1639       try
1640         case Range of
1641           orAll:
1642             for i := SB.Count-1 downto 0 do
1643               if SB.Bottles[i].LogType = ltBottle then
1644                 LogList.Add(TLogItem.Create(SB.Bottles[i]));
1645           orSelected:
1646             if SB.Bottles[lvwLog.ItemIndex].LogType = ltBottle then
1647               LogList.Add(TLogItem.Create(SB.Bottles[lvwLog.ItemIndex]))
1648             else
1649               ShowMessage('\82±\82Ì\83\81\83b\83Z\81[\83W\82Í\95Û\91\82Å\82«\82Ü\82¹\82ñ');
1650           orUpward:
1651             for i := lvwLog.ItemIndex downto 0 do
1652               if SB.Bottles[i].LogType = ltBottle then
1653                 LogList.Add(TLogItem.Create(SB.Bottles[i]));
1654         end;
1655         Options.ImageDir := ImageDir;
1656         Options.UseColor := UseColor;
1657         Options.ImageType := ImageType;
1658         Application.CreateForm(TfrmHTMLOutputProgress, frmHTMLOutputProgress);
1659         try
1660           frmHTMLOutputProgress.Show;
1661           LogList.OnHTMLOutputWork := HTMLOutputWork;
1662           LogList.SaveToHTML(FileName, Options, SsParser);
1663         finally
1664           frmHTMLOutputProgress.Release;
1665         end;
1666       finally
1667         LogList.Free;
1668       end;
1669     finally
1670       Release;
1671     end;
1672 end;
1673
1674 procedure TfrmLog.mnPopupCopyGhostClick(Sender: TObject);
1675 var
1676   Log: TLogItem;
1677   Clip: TClipBoard;
1678 begin
1679   Log := SelectedBottleLog.Bottles[frmLog.lvwLog.ItemIndex];
1680   if Log = nil then Exit;
1681   Clip := ClipBoard();
1682   Clip.SetTextBuf(PChar(Log.Ghost));
1683 end;
1684
1685 procedure TfrmLog.lvwLogDrawItem(Control: TWinControl; Index: Integer;
1686   Rect: TRect; State: TOwnerDrawState);
1687 var
1688   DestRect: TRect;
1689   Ico: TIcon;
1690   Bottle: TLogItem;
1691 begin
1692   Bottle := SelectedBottleLog.Bottles[Index];
1693   CheckBottleURL(Bottle);
1694
1695   DestRect := lvwLog.ItemRect(Index);
1696
1697   // \94w\8ci\82Ì\90F\82Í\91I\91ð\8fó\91Ô\81E\91I\91ð\94ñ\83A\83N\83e\83B\83u\8fó\91Ô\81E\92Ê\8fí\8fó\91Ô\82Ì3\92Ê\82è
1698   lvwLog.Canvas.Brush.Style := bsSolid;
1699   if odSelected in State then
1700   begin
1701     if odFocused in State then
1702       lvwLog.Canvas.Brush.Color := clHighlight
1703     else
1704       lvwLog.Canvas.Brush.Color := clBtnFace;
1705   end else
1706   begin
1707     lvwLog.Canvas.Brush.Color := Pref.BgColor;
1708   end;
1709   lvwLog.Canvas.FillRect(DestRect);
1710   lvwLog.Canvas.Brush.Style := bsClear;
1711   // \83t\83H\81[\83J\83X\82ª\82 \82é\8fê\8d\87\82É\82Í\83t\83H\81[\83J\83X\82Ì\98g\90ü\82ð\88ø\82­
1712   //if lvwLog.Focused then
1713   //  lvwLog.Canvas.DrawFocusRect(DestRect);
1714
1715   // \83h\83\89\83b\83O\92\86\82È\82ç\98g\90ü\82ð\95`\89æ\82·\82é
1716   if FLVDragDest = Index then
1717   begin
1718     DrawListViewDragBorder(DestRect);
1719   end;
1720
1721   if odSelected in State then
1722   begin
1723     if odFocused in State then
1724       lvwLog.Canvas.Font.Color := clHighlightText
1725     else
1726       lvwLog.Canvas.Font.Color := clWindowText;
1727   end else
1728   lvwLog.Canvas.Font.Color := Pref.TextColor;
1729
1730   //lvwLog.Canvas.TextOut(DestRect.Left, DestRect.Top, Bottle.Script);
1731   // \93ú\95t
1732   DrawListSection(SubTime, DestRect.Top,
1733     FormatDateTime('yy/mm/dd hh:nn:ss', Bottle.LogTime), 16);
1734   // \93ú\95t\97\93\83{\83g\83\8b\83A\83C\83R\83\93
1735   Ico := TIcon.Create;
1736   try
1737     if Bottle.LogType = ltSystemLog then
1738       frmSender.imgIcon.GetIcon(IconSystemLog, Ico)
1739     else
1740       case Bottle.State of
1741         lsUnopened:
1742           frmSender.imgIcon.GetIcon(IconBottle, Ico);
1743         lsOpened:
1744           frmSender.imgIcon.GetIcon(IconOpened, Ico);
1745         lsPlaying:
1746           frmSender.imgIcon.GetIcon(IconPlaying, Ico);
1747       end;
1748     lvwLog.Canvas.Draw(2, DestRect.Top, Ico);
1749   finally
1750     Ico.Free;
1751   end;
1752
1753   // \83`\83\83\83\93\83l\83\8b
1754   DrawListSection(SubChannel, DestRect.Top, Bottle.Channel);
1755   // \83S\81[\83X\83g
1756   DrawListSection(SubGhost, DestRect.Top, Bottle.Ghost);
1757   // \93\8a\95[\81E\93¯\88Ó
1758   DrawListSection(SubVotes, DestRect.Top, IntToStr(Bottle.Votes));
1759   DrawListSection(SubAgrees, DestRect.Top, IntToStr(Bottle.Agrees));
1760   // \83X\83N\83\8a\83v\83g
1761   DestRect.Left := Header.Sections[SubScript].Left;
1762   DrawSingleLineScript(Bottle, DestRect, State);
1763 end;
1764
1765 procedure TfrmLog.lvwLogData(Control: TWinControl; Index: Integer;
1766   var Data: String);
1767 begin
1768   Data := '';
1769 end;
1770
1771 procedure TfrmLog.HeaderSectionClick(HeaderControl: THeaderControl;
1772   Section: THeaderSection);
1773 var SortType: TBottleLogSortType;
1774     SelectedMID: String;
1775 begin
1776   if SelectedBottleLog = nil then
1777     Exit;
1778   if lvwLog.ItemIndex <> -1 then
1779     SelectedMID := SelectedBottleLog.Bottles[lvwLog.ItemIndex].MID
1780   else
1781     SelectedMID := '';
1782
1783   case Section.Index of
1784     SubTime:    SortType := stLogTime;
1785     SubChannel: SortType := stChannel;
1786     SubGhost:   SortType := stGhost;
1787     SubVotes:   SortType := stVote;
1788     SubAgrees:  SortType := stAgree;
1789     SubScript:  SortType := stScript;
1790   else
1791     SortType := stLogTime;
1792   end;
1793
1794   SelectedBottleLog.SortBottles(SortType);
1795   lvwLog.Invalidate;
1796   if Length(SelectedMID) > 0 then
1797     SelAndFocusMessage(SelectedMID);
1798 end;
1799
1800 procedure TfrmLog.HeaderSectionTrack(HeaderControl: THeaderControl;
1801   Section: THeaderSection; Width: Integer; State: TSectionTrackState);
1802 begin
1803   lvwLog.Invalidate;
1804 end;
1805
1806 procedure TfrmLog.lvwLogMouseDown(Sender: TObject; Button: TMouseButton;
1807   Shift: TShiftState; X, Y: Integer);
1808 var
1809   Index: Integer;
1810 begin
1811   if Button = mbRight then
1812   begin
1813     Index := lvwLog.ItemAtPos(Point(X, Y), true);
1814     if Index > -1 then
1815     begin
1816       lvwLog.ItemIndex := Index;
1817       lvwLogClick(Self);
1818     end;
1819   end;
1820 end;
1821
1822 end.