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 FormDestroy(Sender: TObject);
86     procedure lvwLogClick(Sender: TObject);
87     procedure mnSaveLogClick(Sender: TObject);
88     procedure mnPopUpCopyScriptClick(Sender: TObject);
89     procedure mnSaveLogChannelClick(Sender: TObject);
90     procedure mnSaveLogScriptClick(Sender: TObject);
91     procedure mnSaveLogXMLClick(Sender: TObject);
92     procedure PopupMenuListViewPopup(Sender: TObject);
93     procedure PopupMenuPreviewStylePopup(Sender: TObject);
94     procedure mnPreviewStyleClick(Sender: TObject);
95     procedure tbtnPreviewStyleClick(Sender: TObject);
96     procedure tabBottleLogChange(Sender: TObject);
97     procedure tabBottleLogChanging(Sender: TObject;
98       var AllowChange: Boolean);
99     procedure tabBottleLogContextPopup(Sender: TObject; MousePos: TPoint;
100       var Handled: Boolean);
101     procedure mnCloseTabClick(Sender: TObject);
102     procedure tbtnFindBottleClick(Sender: TObject);
103     procedure tbtnOpenLogClick(Sender: TObject);
104     procedure tabBottleLogMouseDown(Sender: TObject; Button: TMouseButton;
105       Shift: TShiftState; X, Y: Integer);
106     procedure tabBottleLogDragOver(Sender, Source: TObject; X, Y: Integer;
107       State: TDragState; var Accept: Boolean);
108     procedure tabBottleLogDragDrop(Sender, Source: TObject; X, Y: Integer);
109     procedure tabBottleLogEndDrag(Sender, Target: TObject; X, Y: Integer);
110     procedure mnListPreviewStyleClick(Sender: TObject);
111     procedure tbtnListPreviewStyleClick(Sender: TObject);
112     procedure PopupMenuListPreviewStylePopup(Sender: TObject);
113     procedure lvwLogDragOver(Sender, Source: TObject; X, Y: Integer;
114       State: TDragState; var Accept: Boolean);
115     procedure lvwLogDragDrop(Sender, Source: TObject; X, Y: Integer);
116     procedure timScrollTimerTimer(Sender: TObject);
117     procedure mnChangeTabNameClick(Sender: TObject);
118     procedure lvwLogStartDrag(Sender: TObject;
119       var DragObject: TDragObject);
120     procedure lvwLogEndDrag(Sender, Target: TObject; X, Y: Integer);
121     procedure mnTabSaveXMLLogClick(Sender: TObject);
122     procedure mnSaveHTMLClick(Sender: TObject);
123     procedure mnPopupCopyGhostClick(Sender: TObject);
124     procedure lvwLogDrawItem(Control: TWinControl; Index: Integer;
125       Rect: TRect; State: TOwnerDrawState);
126     procedure lvwLogData(Control: TWinControl; Index: Integer;
127       var Data: String);
128     procedure HeaderSectionClick(HeaderControl: THeaderControl;
129       Section: THeaderSection);
130     procedure HeaderSectionTrack(HeaderControl: THeaderControl;
131       Section: THeaderSection; Width: Integer; State: TSectionTrackState);
132     procedure lvwLogMouseDown(Sender: TObject; Button: TMouseButton;
133       Shift: TShiftState; X, Y: Integer);
134   private
135     { Private \90é\8c¾ }
136     FLastScript: String; //\83X\83N\83\8a\83v\83g\8dÄ\95`\89æ\97}\90§\97p
137     FBottleLogList: TObjectList;
138     //
139     FDragTabIndex: integer; //\83^\83u\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
140     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)
141     //
142     // \83\8a\83X\83g\83r\83\85\81[\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
143     FLVScrollDir: TLVScrollDir; // \83X\83N\83\8d\81[\83\8b\95û\8cü
144     FLVDragDest: integer;    //\83h\83\8d\83b\83v\82·\82é\88Ê\92u(\82·\82®\89º\82É\82­\82é\83A\83C\83e\83\80\82ÌIndex)
145     // \83\8a\83X\83g\83{\83b\83N\83X\82Ì\83L\81[\83C\83x\83\93\83g\83t\83b\83N\97p
146     FLVWndMethod: TWndMethod;
147     //
148     procedure UpdateScript(const Script: String);
149     procedure UpdateScriptConversationColor(const Script: String);
150     procedure UpdateScriptScript(const Script: String);
151     procedure mnURLClick(Sender: TObject);
152     function ExtractURLs(Script: String; Urls: TStrings; Labels: TStrings): Boolean;
153     function GetDefaultFileName(const Name: String; const Ext: String): String;
154     function BottleLogTitled(const LogName: String): TBottleLogList;
155     procedure DrawSingleLineScript(LogItem: TLogItem; Rect: TRect;
156       State: TOwnerDrawState);
157     procedure PreviewStyleChange;
158     procedure DrawListViewDragBorder(const Rect: TRect);
159     procedure DoSaveLogXML(Log: TBottleLogList);
160     procedure DoCloseTab(const Index: integer);
161     function DoSearchLog(Condition: TSearchCond): TBottleLogList;
162     procedure SearchLogIndivisual(Condition: TSearchCond;
163       LogList, Result: TBottleLogList; UntilIndex: integer = -1);
164     procedure DrawListSection(const SectionIndex, Top: Integer;
165       const Text: string; const LeftMargin: Integer = 0);
166     procedure lvwLogWindowProc(var Msg: TMessage);
167   protected
168     procedure CreateParams(var Params: TCreateParams); override;
169   public
170     { Public \90é\8c¾ }
171     function SelectedBottleLog: TBottleLogList;
172     property BottleLogList: TObjectList read FBottleLogList;
173     procedure AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String);
174     procedure AddCurrentSystemLog(const LogName, MessageString: String);
175     procedure VoteLog(const MID: String; const Vote: integer);
176     procedure AgreeLog(const MID: String; const Agree: integer);
177     procedure SetBottleState(const MID: String; State: TLogState);
178     procedure AllBottleOpened;
179     procedure LogLoaded(Sender: TObject);
180     procedure LogLoadFailure(Sender: TObject; const Message: String);
181     procedure LogLoadWork(Sender: TObject);
182     procedure HTMLOutputWork(Sender: TObject; const Count: integer;
183       var Canceled: boolean);
184     procedure UpdateTab;
185     procedure UpdateWindow;
186     procedure SelAndFocusMessage(const MID: String);
187     procedure CheckBottleURL(Bottle: TLogItem);
188   end;
189
190   TBottleLogDragObject = class(TDragControlObjectEx)
191   private
192     FBottleLogList: TBottleLogList;
193     FLogItem: TLogItem;
194     procedure SetBottleLogList(const Value: TBottleLogList);
195     procedure SetLogItem(const Value: TLogItem);
196   protected
197     function GetDragImages: TDragImageList; override;
198   public
199     property BottleLogList: TBottleLogList read FBottleLogList write SetBottleLogList;
200     property LogItem: TLogItem read FLogItem write SetLogItem;
201   end;
202
203 var
204   frmLog: TfrmLog;
205
206 const
207   IconBottle    = 17;
208   IconOpened    = 30;
209   IconPlaying   = 31;
210   IconSystemLog = 26;
211   IconURL       = 43;
212   SubTime       = 0;
213   SubChannel    = 1;
214   SubGhost      = 2;
215   SubVotes      = 3;
216   SubAgrees     = 4;
217   SubScript     = 5;
218
219 implementation
220
221 uses MainForm;
222
223 {$R *.DFM}
224
225 { TfrmLog }
226
227 procedure TfrmLog.AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String);
228 var Sel: integer;
229 begin
230   BottleLogTitled(LogName).AddScriptLog(Script, Channel, MID, Ghost);
231   if SelectedBottleLog <> BottleLogTitled(LogName) then Exit;
232   if lvwLog.Itemindex <> -1 then
233     Sel := lvwLog.ItemIndex
234   else
235     Sel := -1;
236   lvwLog.Count := SelectedBottleLog.Count;
237   UpdateWindow;
238   if Sel >= 0 then begin
239     lvwLog.ItemIndex := Sel + 1;
240   end;
241   if not lvwLog.Focused then
242     PostMessage(lvwLog.Handle,WM_VSCROLL,SB_TOP,0);
243 end;
244
245 procedure TfrmLog.AddCurrentSystemLog(const LogName, MessageString: String);
246 var Sel: integer;
247 begin
248   BottleLogTitled(LogName).AddSystemLog(MessageString);
249   if SelectedBottleLog <> BottleLogTitled(LogName) then Exit;
250   if lvwLog.ItemIndex <> -1 then
251     Sel := lvwLog.ItemIndex
252   else
253     Sel := -1;
254   lvwLog.Count := SelectedBottleLog.Count;
255   UpdateWindow;
256   if Sel >= 0 then begin
257     lvwLog.ItemIndex := Sel + 1;
258     //lvwLog.SetFocus;
259   end;
260   if not lvwLog.Focused then
261     PostMessage(lvwLog.Handle,WM_VSCROLL,SB_TOP,0);
262 end;
263
264
265
266 procedure TfrmLog.tbtnClearClick(Sender: TObject);
267 begin
268   if SelectedBottleLog = nil then Exit;
269   DoCloseTab(tabBottleLog.TabIndex);
270 end;
271
272 procedure TfrmLog.FormCreate(Sender: TObject);
273 var i: integer;
274 begin
275   FLVWndMethod := lvwLog.WindowProc;
276   lvwLog.WindowProc := lvwLogWindowProc;
277
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   lvwLog.DoubleBuffered := true;
309
310   PreviewStyleChange;
311   UpdateWindow; // Reset window color and enabled status of some buttons
312 end;
313
314 procedure TfrmLog.FormDestroy(Sender: TObject);
315 var i: integer;
316     WidthStr: String;
317 begin
318   WidthStr := '';
319   for i := 0 to Header.Sections.Count-1 do begin
320     if i > 0 then WidthStr := WidthStr + ',';
321     WidthStr := WidthStr + IntToStr(Header.Sections[i].Width);
322   end;
323   Pref.LogWindowColumnWidth := WidthStr;
324
325   with Pref.LogWindowPosition do begin
326     Left   := Self.Left;
327     Top    := Self.Top;
328     Right  := Self.Left + Self.Width - 1;
329     Bottom := Self.Top + Self.Height - 1;
330   end;
331   Pref.LogWindowDividerPos := pnlPreviewArea.Height;
332
333   FreeAndNil(FBottleLogList);
334
335   lvwLog.WindowProc := FLVWndMethod;
336 end;
337
338 procedure TfrmLog.lvwLogDblClick(Sender: TObject);
339 var Script, ErrorMes: String;
340     Log, CueItem: TLogItem;
341     Res: integer;
342 begin
343   if lvwLog.ItemIndex = -1 then
344     Exit;
345   Log := SelectedBottleLog.Bottles[lvwLog.ItemIndex];
346   if Log = nil then Exit;
347   if Log.LogType <> ltBottle then
348     Exit;
349   Script := frmSender.ScriptTransForSSTP(Log.Script, ErrorMes);
350   if ErrorMes <> '' then
351   begin
352     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+
353         ErrorMes + #13#10 +
354         '\8b­\90§\93I\82É\8dÄ\90\82µ\82Ü\82·\82©?'#13#10,
355         mtWarning, mbOkCancel, 0
356       );
357     if Res = mrCancel then
358       Exit;
359   end;
360
361   CueItem := TLogItem.Create(Log);
362   try
363     CueItem.Script := Script;
364     frmSender.BottleSstp.Unshift(CueItem);
365   except
366     CueItem.Free;
367   end;
368 end;
369
370 procedure TfrmLog.UpdateScriptConversationColor(const Script: String);
371 var i: integer;
372     scr: String;
373     UnyuTalking, Talked, InSynchronized: boolean;
374 begin
375   scr := Script;
376   frmSender.DoTrans(scr, [toConvertURL]);
377   SsParser.LeaveEscape := false;
378   SsParser.InputString := scr;
379   UnyuTalking := false;
380   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
381   InSynchronized := false;
382   edtScript.Text := '';
383   edtScript.Color := Pref.BgColor;
384   for i := 0 to SsParser.Count-1 do begin
385     if (SsParser[i] = '\_s') and not InSynchronized then begin
386       InSynchronized := true;
387       if Talked then begin
388         edtScript.SelText := #13#10;
389         Talked := false;
390       end;
391     end else if (SsParser[i] = '\_s') and InSynchronized then begin
392       InSynchronized := false;
393       if Talked then begin
394         edtScript.SelText := #13#10;
395         Talked := false;
396       end;
397     end;
398     if (SsParser[i] = '\u') and not UnyuTalking then begin
399       UnyuTalking := true;
400       if Talked then begin
401         edtScript.SelText := #13#10;
402         Talked := false;
403       end;
404     end;
405     if (SsParser[i] = '\h') and UnyuTalking then begin
406       UnyuTalking := false;
407       if Talked then begin
408         edtScript.SelText := #13#10;
409         Talked := false;
410       end;
411     end;
412     if SsParser.MarkUpType[i] = mtStr then begin
413       if InSynchronized then
414         edtScript.SelAttributes.Color := Pref.TalkColorS
415       else if UnyuTalking then
416         edtScript.SelAttributes.Color := Pref.TalkColorU
417       else
418         edtScript.SelAttributes.Color := Pref.TalkColorH;
419       edtScript.SelText := SsParser[i];
420       Talked := true;
421     end;
422     if SsParser.MarkUpType[i] = mtMeta then begin
423       edtScript.SelAttributes.Color := Pref.MetaWordColor;
424       edtScript.SelText := SsParser[i];
425       Talked := true;
426     end;
427   end;
428 end;
429
430 procedure TfrmLog.CreateParams(var Params: TCreateParams);
431 begin
432   inherited;
433   Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
434 end;
435
436 procedure TfrmLog.lvwLogClick(Sender: TObject);
437 var Script, Text: String;
438     Log: TLogItem;
439     Selected, IsNormalBottle: boolean;
440 begin
441   Selected := false;
442   IsNormalBottle := false;
443   if SelectedBottleLog <> nil then begin
444     Script := '';
445     if lvwLog.ItemIndex <> -1 then begin
446       Selected := true;
447       StatusBar.Panels[0].Text := Format('%d/%d\8c\8f', [lvwLog.ItemIndex+1,
448         SelectedBottleLog.Count]);
449       Log := SelectedBottleLog.Bottles[lvwLog.ItemIndex];
450       if (Log.LogType = ltBottle) and not frmSender.Connecting then begin
451         IsNormalBottle := true;
452         Script := Log.Script;
453         Text := Format('%d\83o\83C\83g/%d\95b - \83_\83u\83\8b\83N\83\8a\83b\83N\82Å\8dÄ\90¶',
454           [Length(Log.Script), frmSender.SsPlayTime.PlayTime(Log.Script) div 1000]);
455         StatusBar.Panels[1].Text := Text;
456         if Pref.LogWindowPreviewStyle = psImageConversation then
457           TalkShowFrame.View(Log)
458         else
459           UpdateScript(Script);
460       end else begin
461         StatusBar.Panels[1].Text := '';
462         UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\82ð\83N\83\8a\83A
463       end;
464     end else begin
465       StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
466       StatusBar.Panels[1].Text := '';
467       UpdateScript(Script); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
468     end;
469     tbtnSaveLog.Enabled := lvwLog.Items.Count > 0;
470   end else begin
471     StatusBar.Panels[0].Text := '';
472     UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
473   end;
474   frmSender.actVoteMessage.Enabled := Selected and IsNormalBottle;
475   frmSender.actAgreeMessage.Enabled := Selected and IsNormalBottle;
476   frmSender.actSendToEditor.Enabled := Selected and IsNormalBottle;
477   frmSender.actInsertCue.Enabled := Selected;
478   frmSender.actDeleteLogItem.Enabled := Selected;
479   mnPopUpCopyScript.Enabled := Selected and IsNormalBottle;
480   mnPopupCopyGhost.Enabled := Selected and IsNormalBottle;
481 end;
482
483 procedure TfrmLog.mnPopUpCopyScriptClick(Sender: TObject);
484 var
485   Log: TLogItem;
486   Clip: TClipBoard;
487 begin
488   Log := SelectedBottleLog.Bottles[frmLog.lvwLog.ItemIndex];
489   if Log = nil then Exit;
490   Clip := ClipBoard();
491   Clip.SetTextBuf(PChar(Log.Script));
492 end;
493
494 procedure TfrmLog.SetBottleState(const MID: String; State: TLogState);
495 var i: integer;
496     Bottle: TLogItem;
497 begin
498   for i := 0 to FBottleLogList.Count-1 do begin
499     Bottle := (FBottleLogList[i] as TBottleLogList).Bottle(MID);
500     if Bottle <> nil then begin
501       Bottle.State := State;
502       // lvwLog.OnChange := nil;
503       lvwLog.Invalidate;
504       // lvwLog.OnChange := lvwLogChange;
505     end;
506   end;
507 end;
508
509 procedure TfrmLog.mnSaveLogClick(Sender: TObject);
510 begin
511   if SelectedBottleLog = nil then Exit;
512   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
513   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
514   SaveDialog.DefaultExt := 'log';
515   SaveDialog.FilterIndex := 1;
516   if SaveDialog.Execute then
517     SelectedBottleLog.SaveToSstpLog(SaveDialog.FileName, false);
518 end;
519
520 procedure TfrmLog.mnSaveLogChannelClick(Sender: TObject);
521 begin
522   if SelectedBottleLog = nil then Exit;
523   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
524   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
525   SaveDialog.DefaultExt := 'log';
526   SaveDialog.FilterIndex := 1;
527   if SaveDialog.Execute then
528     SelectedBottleLog.SaveToSstpLog(SaveDialog.FileName, true);
529 end;
530
531 procedure TfrmLog.mnSaveLogScriptClick(Sender: TObject);
532 begin
533   if SelectedBottleLog = nil then Exit;
534   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.txt');
535   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
536   SaveDialog.DefaultExt := 'txt';
537   SaveDialog.FilterIndex := 2;
538   if SaveDialog.Execute then
539     SelectedBottleLog.SaveToText(SaveDialog.FileName);
540 end;
541
542 procedure TfrmLog.mnSaveLogXMLClick(Sender: TObject);
543 begin
544   if SelectedBottleLog = nil then Exit;
545   DoSaveLogXML(SelectedBottleLog);
546 end;
547
548 procedure TfrmLog.UpdateWindow;
549 var
550   EnabledFlag: boolean;
551   Index: Integer;
552 begin
553   lvwLog.Color := Pref.BgColor;
554   lvwLog.Font.Color := Pref.TextColor;
555   if SelectedBottleLog <> nil then begin
556     Caption := '\83\8d\83O - ' + SelectedBottleLog.Title;
557     StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
558     Index := lvwLog.ItemIndex;
559     lvwLog.Count := SelectedBottleLog.Count;
560   end else begin
561     Caption := '\83\8d\83O';
562     StatusBar.Panels[0].Text := '';
563     StatusBar.Panels[1].Text := '';
564     lvwLog.Count := 0;
565   end;
566
567   EnabledFlag := SelectedBottleLog <> nil;
568   tbtnClear.Enabled := EnabledFlag;
569   tbtnSaveLog.Enabled := EnabledFlag;
570   tbtnFindBottle.Enabled := EnabledFlag;
571
572   lvwLog.Invalidate;
573 end;
574
575 procedure TfrmLog.PopupMenuListViewPopup(Sender: TObject);
576 var Log: TLogItem;
577     Child: TMenuItem;
578     Urls: TStringList;
579     Labels: TStringList;
580     ProcessedUrl: String;
581     ProcessedLabel: String;
582     i: Integer;
583 begin
584   for i := mnJumpURL.Count-1 downto 0 do begin
585     mnJumpURL.Items[i].Free;
586   end;
587   mnJumpURL.Enabled := false;
588   if lvwLog.ItemIndex = -1 then
589     Exit;
590   Log := SelectedBottleLog.Bottles[lvwLog.ItemIndex];
591   if Log = nil then
592     Exit;
593   Urls := TStringList.Create;
594   Labels := TStringList.Create;
595   try
596     ExtractURLs(Log.Script, Urls, Labels);
597     for i := 0 to Urls.Count-1 do begin
598       Child := TMenuItem.Create(Self);
599       with Child do begin
600         ProcessedUrl := StringReplace(Urls[i], '&', '&&', [rfReplaceAll]);
601         ProcessedLabel := StringReplace(Labels[i], '&', '&&', [rfReplaceAll]);
602         if Length(ProcessedLabel) > 0 then begin
603           Caption := Format('[%s] %s (&%d)', [ProcessedLabel, ProcessedUrl, i+1]);
604         end else begin
605           Caption := Format('%s (&%d)', [ProcessedUrl, i+1]);
606         end;
607         Tag := i;
608         OnClick := mnURLClick;
609         AutoHotkeys := maManual;
610         mnJumpURL.Add(Child);
611       end;
612     end;
613     mnJumpURL.Enabled := Urls.Count > 0;
614   finally
615     Urls.Free;
616     Labels.Free;
617   end;
618 end;
619
620 procedure TfrmLog.mnURLClick(Sender: TObject);
621 var LogItem: TLogItem;
622     URL: string;
623     Urls: TStringList;
624
625 begin
626   if (lvwLog.ItemIndex < 0) or (SelectedBottleLog = nil) then Exit;
627   LogItem := SelectedBottleLog[lvwLog.ItemIndex] as TLogItem;
628   Urls := TStringList.Create;
629   try
630     ExtractURLs(LogItem.Script, Urls, nil);
631     URL := Urls[(Sender as TMenuItem).Tag];
632     OpenBrowser(URL);
633   finally
634     Urls.Free;
635   end;
636 end;
637
638 function TfrmLog.ExtractURLs(Script: String; Urls: TStrings; Labels: TStrings): Boolean;
639 var i, u, j, count: integer;
640     s: String;
641 begin
642   count := 0;
643   SsParser.InputString := Script;
644   SsParser.LeaveEscape := true;
645   for i := 0 to SsParser.Count-1 do begin
646     if (SsParser.Match(SsParser[i], '\URL%b') > 0)
647     and (SsParser.MarkUpType[i] = mtTag) then
648     begin
649       for u := 7 downto 1 do begin
650         if (SsParser.Match(SsParser[i],
651             '\URL%b'+StringReplace(StringOfChar('-', u*2),
652             '-', '%b', [rfReplaceAll]))) > 0 then begin
653           for j := 1 to u do begin
654             s := SsParser.GetParam(SsParser[i], j*2);
655             //https\82È\82Ç\82ª\92Ç\89Á\82³\82ê\82é\8e\9e\91Î\8dô\81c\81c://\82ð\8c\9f\8dõ\82·\82ê\82Î\82½\82Ô\82ñ\91å\8fä\95v
656             if Pos('://', s) > 0 then begin
657               if Urls <> nil then Urls.Add(s);
658               count := count + 1;
659             end;
660             if Labels <> nil then begin
661               s := SsParser.GetParam(SsParser[i], j*2+1);
662               Labels.Add(s);
663             end;
664           end;
665           Break;
666         end;
667       end;
668       if SsParser.Match(SsParser[i], '\URL%b%b') = 0 then begin
669         //\8aÈ\88Õ\8c`\8e®\URL\83^\83O\95Ï\8a·
670         s := SsParser.GetParam(SsParser[i], 1);
671         //https\82È\82Ç\82ª\92Ç\89Á\82³\82ê\82é\8e\9e\91Î\8dô\81c\81c://\82ð\8c\9f\8dõ\82·\82ê\82Î\82½\82Ô\82ñ\91å\8fä\95v
672         if Pos('://', s) > 0 then begin
673           if Urls <> nil then Urls.Add(s);
674           count := count + 1;
675         end
676       end;
677     end;
678   end;
679
680   //\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É
681   if Urls <> nil then begin
682     if Labels <> nil then begin
683       while Urls.Count > Labels.Count do Labels.Add('');
684     end;
685   end;
686   Result := count > 0;
687 end;
688
689 procedure TfrmLog.SelAndFocusMessage(const MID: String);
690 var i: integer;
691     Log: TLogItem;
692 begin
693   for i := 0 to SelectedBottleLog.Count-1 do begin
694     Log := SelectedBottleLog.Items[i] as TLogItem;
695     if Log.MID = MID then begin
696       lvwLog.ItemIndex := i;
697       lvwLog.SetFocus;
698     end;
699   end;
700 end;
701
702 procedure TfrmLog.UpdateScript(const Script: String);
703 begin
704   if Script <> FLastScript then begin
705     if Pref.LogWindowPreviewStyle = psConversation then begin
706       UpdateScriptConversationColor(Script);
707     end else begin
708       UpdateScriptScript(Script);
709     end;
710     SendMessage(edtScript.Handle, EM_LINESCROLL, Low(integer), Low(integer)); //\83X\83N\83\8d\81[\83\8b\96ß\82µ
711     FLastScript := Script;
712   end;
713 end;
714
715 procedure TfrmLog.PopupMenuPreviewStylePopup(Sender: TObject);
716 var i: integer;
717 begin
718   with PopupMenuPreviewStyle do
719     for i := 0 to Items.Count-1 do
720       Items[i].Checked := Items[i].Tag = Ord(Pref.LogWindowPreviewStyle)
721 end;
722
723 procedure TfrmLog.mnPreviewStyleClick(Sender: TObject);
724 var i: integer;
725 begin
726   with PopupMenuPreviewStyle do
727     for i := 0 to Items.Count-1 do
728       Items[i].Checked := (Sender as TMenuItem).Tag = Items[i].Tag;
729   Pref.LogWindowPreviewStyle := TLogWindowPreviewStyle((Sender as TMenuItem).Tag);
730   PreviewStyleChange;
731   FLastScript := '';
732   lvwLogClick(self);
733 end;
734
735 procedure TfrmLog.UpdateScriptScript(const Script: String);
736 var
737   UnyuTalking, InSynchronized: boolean;
738   i: integer;
739 begin
740   edtScript.Color := Pref.BgColor;
741   SsParser.LeaveEscape := true;
742   SsParser.InputString := Script;
743   edtScript.Text := '';
744   edtScript.SelAttributes.Color := clWindowText;
745   UnyuTalking := false;
746   InSynchronized := false;
747   for i := 0 to SsParser.Count-1 do begin
748     case SsParser.MarkUpType[i] of
749       mtStr: begin
750         if InSynchronized then
751           edtScript.SelAttributes.Color := Pref.TalkColorS
752         else if UnyuTalking then
753           edtScript.SelAttributes.Color := Pref.TalkColorU
754         else
755           edtScript.SelAttributes.Color := Pref.TalkColorH;
756       end;
757       mtTag: begin
758         edtScript.SelAttributes.Color := Pref.MarkUpColor;
759         if SsParser[i] = '\h' then
760           UnyuTalking := false
761         else if SsParser[i] = '\u' then
762           UnyuTalking := true
763         else if SsParser[i] = '\_s' then
764           InSynchronized := not InSynchronized;
765       end;
766       mtMeta:   edtScript.SelAttributes.Color := Pref.MetaWordColor;
767       mtTagErr: edtScript.SelAttributes.Color := Pref.MarkErrorColor;
768     end;
769     edtScript.SelText := SsParser[i];
770     if (SsParser[i] = '\n') and (Pref.LogWindowPreviewStyle = psScriptWithLineBreak) then
771       edtScript.SelText := #13#10;
772   end;
773 end;
774
775 procedure TfrmLog.tbtnPreviewStyleClick(Sender: TObject);
776 var sel: integer;
777 begin
778   sel := Ord(Pref.LogWindowPreviewStyle);
779   sel := sel + 1;
780   if sel > Ord(High(TLogWindowPreviewStyle)) then sel := 0;
781   Pref.LogWindowPreviewStyle := TLogWindowPreviewStyle(sel);
782   FLastScript := '';
783   PreviewStyleChange;
784   lvwLogClick(self);
785 end;
786
787 function TfrmLog.SelectedBottleLog: TBottleLogList;
788 begin
789   if tabBottleLog.TabIndex >= 0 then
790     Result := FBottleLogList.Items[tabBottleLog.TabIndex] as TBottleLogList
791   else
792     Result := nil;
793 end;
794
795 procedure TfrmLog.tabBottleLogChange(Sender: TObject);
796 begin
797   // StatusBar\82Ì\8c\8f\90\94\95\\8e¦\82âListView.Items.Count\82ð\8dX\90V\82·\82é
798   UpdateWindow;
799   // \83A\83C\83e\83\80\82Ì\91I\91ð\8fó\91Ô\82ð\95\9c\8bA\82·\82é
800   with SelectedBottleLog do
801     if (SelectedIndex >= 0) and (Count > SelectedIndex) then
802     begin
803       lvwLog.ItemIndex := SelectedIndex;
804     end;
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   
1271   if not Pref.LogItemDragDrop then
1272     Exit;
1273   // \82Æ\82è\82 \82¦\82¸\8eó\82¯\95t\82¯\82é\89Â\94\\90«\82ª\82 \82é\82Ì\82ÍTBottleLogDragObject\82¾\82¯
1274   if not (Source is TBottleLogDragObject) then
1275     Exit;
1276   // \82µ\82©\82à\8d\80\96Ú\82ª\82 \82é\8fê\8d\87\82Ì\82Ý
1277   if (Source as TBottleLogDragObject).LogItem = nil then
1278     Exit; 
1279
1280   Target := lvwLog.ItemAtPos(Point(X, Y), true);
1281
1282   // \82±\82ê\88È\91O\82É\95`\89æ\82³\82ê\82Ä\82¢\82½\98g\82Ì\83C\83\93\83f\83b\83N\83X
1283   OldDest := FLVDragDest;
1284
1285   // \83h\83\8d\83b\83v\88Ê\92u\82É Item \82ª\82 \82ê\82Î\83h\83\8d\83b\83v\82ð\8b\96\89Â\82·\82é
1286   if Target <> -1 then
1287   begin
1288     Accept := true;
1289     FLVDragDest := Target;
1290   end else
1291   begin
1292     Accept := true;
1293     FLVDragDest := -1;
1294   end;
1295
1296   // \88È\91O\82Ì\98g\90ü\82ð\8dí\8f\9c
1297   if (OldDest > -1) and (FLVDragDest <> OldDest) then
1298   begin
1299     Rec := lvwLog.ItemRect(OldDest);
1300     DrawListViewDragBorder(Rec);
1301   end;
1302   // \83h\83\89\83b\83O\90æ\82Ì\98g\90ü\82ð\95`\89æ
1303   if (Target <> -1) and (FLVDragDest <> OldDest) then
1304   begin
1305     Rec := lvwLog.ItemRect(Target);
1306     DrawListViewDragBorder(Rec);
1307   end;
1308
1309   // \83X\83N\83\8d\81[\83\8b\8aÖ\8cW
1310   if lvwLog.Count > 0 then
1311   begin
1312     if Y < 10 then
1313     begin
1314       FLVScrollDir := lvScrollDown;
1315       if not timScrollTimer.Enabled then
1316         timScrollTimer.Enabled := true;
1317     end else if (lvwLog.Height - Y) < 10 then
1318     begin
1319       FLVScrollDir := lvScrollUp;
1320       if not timScrollTimer.Enabled then
1321         timScrollTimer.Enabled := true;
1322     end
1323     else
1324       timScrollTimer.Enabled := false;
1325   end else
1326     timScrollTimer.Enabled := false;
1327 end;
1328
1329 procedure TfrmLog.lvwLogDragDrop(Sender, Source: TObject; X, Y: Integer);
1330 var
1331   TargetItem: integer;
1332   Src: TBottleLogDragObject;
1333   SrcLog: TObject;
1334 begin
1335   timScrollTimer.Enabled := false;
1336
1337   if not Pref.LogItemDragDrop then
1338     Exit;
1339   if not (Source is TBottleLogDragObject) then
1340     Exit;
1341   Src := Source as TBottleLogDragObject;
1342   if Src.LogItem = nil then
1343     Exit;
1344
1345   TargetItem := lvwLog.ItemAtPos(Point(X, Y), true);
1346
1347   // \83h\83\8d\83b\83v\88Ê\92u\82É Item \82ð\88Ú\93®\82·\82é
1348   if (GetAsyncKeyState(VK_CONTROL) and $8000) > 0 then
1349   begin // \83R\83s\81[\88Ú\93®\82Ì\8fê\8d\87
1350     SrcLog := TLogItem.Create(Src.LogItem);
1351   end else // \88Ú\93®\82¾\82¯\82·\82é\8fê\8d\87
1352   begin
1353     SrcLog := Src.BottleLogList.Extract(Src.LogItem);
1354   end;
1355   if TargetItem >= 0 then
1356   begin
1357     // \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
1358     SelectedBottleLog.Insert(TargetItem, SrcLog);
1359   end else
1360   begin
1361     // ListView\82Ì\97]\94\92\82É\83h\83\8d\83b\83v\82µ\82½\8fê\8d\87(Insert\82Å\82«\82È\82¢)
1362     TargetItem := SelectedBottleLog.Add(SrcLog);
1363   end;
1364   UpdateWindow;
1365   lvwLog.ItemIndex := TargetItem;
1366 end;
1367
1368 procedure TfrmLog.timScrollTimerTimer(Sender: TObject);
1369 var
1370   ScrollHeight: Integer;
1371 begin
1372   // \83X\83N\83\8d\81[\83\8b\97Ê\82ð\8b\81\82ß\82é
1373   ScrollHeight := lvwLog.ItemHeight;
1374
1375   {TODO: \83X\83N\83\8d\81[\83\8b\95\9c\8a\88}
1376   case FLVScrollDir of
1377     lvScrollUp:
1378       PostMessage(lvwLog.Handle, WM_VSCROLL, SB_LINEDOWN, 0);
1379     lvSCrollDown:
1380       PostMessage(lvwLog.Handle, WM_VSCROLL, SB_LINEUP, 0);
1381   end;
1382   lvwLog.Invalidate;  // \8dÅ\90V\82Ì\8fó\91Ô\82É\8dÄ\95`\89æ\82·\82é
1383   lvwLog.Update;
1384 end;
1385
1386 procedure TfrmLog.mnChangeTabNameClick(Sender: TObject);
1387 var Name: String;
1388 begin
1389   Name := (FBottleLogList[tabBottleLog.Tag] as TBottleLogList).Title;
1390   InputQuery('\96¼\91O\82Ì\95Ï\8dX', '\90V\82µ\82¢\83^\83u\82Ì\96¼\91O', Name);
1391   (FBottleLogList[tabBottleLog.Tag] as TBottleLogList).Title := Name;
1392   UpdateTab;
1393 end;
1394
1395 procedure TfrmLog.lvwLogStartDrag(Sender: TObject;
1396   var DragObject: TDragObject);
1397 var Drag: TBottleLogDragObject;
1398 begin
1399   // 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Æ
1400   // \83h\83\89\83b\83O\83C\83\81\81[\83W\82Ì\95`\89æ\82Í\97}\90§\82Å\82«\82é\81B
1401   Drag := TBottleLogDragObject.Create(lvwLog);
1402   Drag.BottleLogList := SelectedBottleLog;
1403   if lvwLog.ItemIndex <> -1 then
1404     Drag.LogItem := SelectedBottleLog.Bottles[lvwLog.ItemIndex]
1405   else
1406     Drag.LogItem := nil;
1407   DragObject := Drag;
1408 end;
1409
1410 procedure TfrmLog.lvwLogEndDrag(Sender, Target: TObject; X, Y: Integer);
1411 begin
1412   // \98g\90ü\8fÁ\82µ\97p\82É\8b­\90§\93I\82É\8dÄ\95`\89æ\82³\82¹\82é
1413   timScrollTimer.Enabled := false;
1414   FLVDragDest := -1;
1415   lvwLog.Invalidate;
1416 end;
1417
1418 procedure TfrmLog.DrawListViewDragBorder(const Rect: TRect);
1419 var Rec: TRect;
1420 begin
1421   Rec := Rect;
1422   InflateRect(Rec, -1, -1);
1423   with lvwLog.Canvas do
1424   begin
1425     Pen.Mode := pmNot;
1426     Pen.Width := 3;
1427     Brush.Style := bsClear;
1428     Refresh; // \95K\97v
1429     Rectangle(Rec);
1430   end;
1431 end;
1432
1433 procedure TfrmLog.DoSaveLogXML(Log: TBottleLogList);
1434 begin
1435   SaveDialog.FileName := GetDefaultFileName(Log.Title, '.xml');
1436   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
1437   SaveDialog.DefaultExt := 'xml';
1438   SaveDialog.FilterIndex := 3;
1439   if SaveDialog.Execute then
1440     Log.SaveToXmlFile(SaveDialog.FileName);
1441 end;
1442
1443 procedure TfrmLog.DoCloseTab(const Index: integer);
1444 var
1445   Confirm: String;
1446   PrevSelection: TBottleLogList; // \95Â\82\82½\82Æ\82«\83^\83u\82ª\82¸\82ê\82È\82¢\82æ\82¤\82É\82·\82é\8f\88\97\9d\97p
1447   i: integer;
1448 begin
1449   if Pref.ConfirmOnTabClose then
1450   begin
1451     Confirm := Format('\83^\83u"%s"\82ð\95Â\82\82Ü\82·\82©?', [(FBottleLogList[Index] as TBottleLogList).Title]);
1452     if MessageDlg(Confirm, mtConfirmation, mbOkCancel, 0) = mrCancel then
1453       Exit;
1454   end;
1455   PrevSelection := SelectedBottleLog;
1456   FBottleLogList.Delete(Index);
1457   UpdateTab;
1458   // \83^\83u\82¸\82ê\96h\8e~\8f\88\97\9d
1459   for i := 0 to FBottleLogList.Count-1 do
1460     if FBottleLogList[i] = PrevSelection then
1461       tabBottleLog.TabIndex := i;
1462   UpdateWindow;
1463 end;
1464
1465 procedure TfrmLog.HTMLOutputWork(Sender: TObject; const Count: integer;
1466   var Canceled: boolean);
1467 begin
1468   frmHTMLOutputProgress.ProgressBar.Position := Count;
1469   Application.ProcessMessages;
1470   if frmHTMLOutputProgress.Canceled then
1471     Canceled := true;
1472 end;
1473
1474 function TfrmLog.DoSearchLog(Condition: TSearchCond): TBottleLogList;
1475 var i, UntilIndex: integer;
1476 begin
1477   Result := TBottleLogList.Create('\8c\9f\8dõ\8c\8b\89Ê');
1478   if Condition.SearchLogRange in [srSelectedLogList, srAboveSelectedLog] then
1479   begin
1480     if SelectedBottleLog = nil then
1481     begin
1482       ShowMessage('\8c\9f\8dõ\91Î\8fÛ\82ª\82 \82è\82Ü\82¹\82ñ');
1483       Result.Free;
1484       Result := nil;
1485       Exit;
1486     end else
1487     begin
1488       if Condition.SearchLogRange = srSelectedLogList then
1489         UntilIndex := -1
1490       else
1491         UntilIndex := lvwLog.ItemIndex;
1492       SearchLogIndivisual(Condition, SelectedBottleLog, Result, UntilIndex);
1493     end;
1494   end else if Condition.SearchLogRange = srAllLogLists then
1495   begin
1496     for i := 0 to BottleLogList.Count-1 do
1497     begin
1498       SearchLogIndivisual(Condition, BottleLogList[i] as TBottleLogList,
1499         Result);
1500     end;
1501   end;
1502
1503   if Result.Count = 0 then
1504     Result.AddSystemLog('\8c©\82Â\82©\82è\82Ü\82¹\82ñ\82Å\82µ\82½\81B');
1505 end;
1506
1507 procedure TfrmLog.SearchLogIndivisual(Condition: TSearchCond; LogList,
1508   Result: TBottleLogList; UntilIndex: integer = -1);
1509 var
1510   i, Max: integer;
1511   Bottle, New: TLogItem;
1512   Ok: boolean;
1513 begin
1514   // 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Ì)
1515   if UntilIndex >= 0 then
1516     Max := UntilIndex
1517   else
1518     Max := LogList.Count-1;
1519   for i := 0 to Max do
1520   begin
1521     // \8fð\8c\8f\94»\92è
1522     Bottle := LogList.Bottles[i];
1523     if Bottle.LogType <> ltBottle then
1524       Continue;
1525     Ok := true;
1526     // \83X\83N\83\8a\83v\83g\83p\83^\81[\83\93\82Å\89ð\90Í
1527     if Condition.ScriptPattern <> '' then
1528     begin
1529       if Condition.ScriptRegExp then
1530       begin
1531         try
1532           if not RegExp.Match(Condition.ScriptPattern, Bottle.Script) then
1533             Ok := false;
1534         except
1535           on EBRegExpError do
1536             Ok := false; //\96­\82È\90³\8bK\95\\8c»\82ð\8fR\82é
1537         end;
1538       end else
1539       begin
1540         if not AnsiContainsText(Bottle.Script, Condition.ScriptPattern) then
1541           Ok := false;
1542       end;
1543     end;
1544     // \83`\83\83\83\93\83l\83\8b\96¼\81A\83S\81[\83X\83g\96¼\81A\93\8a\95[\93¯\88Ó
1545     if Condition.Channel <> '' then
1546       if not AnsiContainsText(Bottle.Channel, Condition.Channel) then
1547         Ok := false;
1548     if Condition.Ghost <> '' then
1549       if not AnsiContainsText(Bottle.Ghost, Condition.Ghost) then
1550         Ok := false;
1551     if Condition.MinVote > Bottle.Votes then
1552       Ok := false;
1553     if Condition.MinAgree > Bottle.Agrees then
1554       Ok := false;
1555     // \8fð\8c\8f\82É\88ê\92v\82µ\82½\82à\82Ì\82ð\8c\8b\89Ê\83\8a\83X\83g\82É\92Ç\89Á
1556     if Ok then
1557     begin
1558       New := TLogItem.Create(Bottle); // \83R\83s\81[\83R\83\93\83X\83g\83\89\83N\83^
1559       New.State := lsOpened;
1560       Result.Add(New);
1561     end;
1562   end;
1563 end;
1564
1565 procedure TfrmLog.CheckBottleURL(Bottle: TLogItem);
1566 begin
1567   if Bottle.HasURL = huUndefined then
1568   begin
1569     try
1570       //nil,nil\82ð\88ø\82«\93n\82µ\82Ä\82àTRUE/FALSE\82ÅURL\82ª\82 \82é\82©\82Ç\82¤\82©\82ª\82«\82¿\82ñ\82Æ\95Ô\82é
1571       if ExtractURLs(Bottle.Script, nil, nil) then
1572         Bottle.HasURL := huYes
1573       else
1574         Bottle.HasURL := huNo;
1575     finally
1576     end;
1577   end;
1578 end;
1579
1580 procedure TfrmLog.DrawListSection(const SectionIndex, Top: Integer;
1581   const Text: string; const LeftMargin: Integer = 0);
1582 var
1583   Sec: THeaderSection;
1584   Ex: Integer;
1585   Rec: TRect;
1586 const
1587   MinimumTextWidth = 16; //\82±\82ê\82æ\82è\8b·\82¢\82È\82ç\82»\82à\82»\82à\95`\89æ\82µ\82È\82¢
1588 begin
1589   Sec := Header.Sections[SectionIndex];
1590
1591   Rec.Left   := Sec.Left + 2 + LeftMargin;
1592   Rec.Right  := Sec.Right - 2;
1593   if Rec.Right - Rec.Left < MinimumTextWidth then
1594     Exit;
1595   Rec.Top    := Top + 2;
1596   Rec.Bottom := Top + lvwLog.ItemHeight;
1597
1598   Ex := DT_NOPREFIX or DT_SINGLELINE or DT_END_ELLIPSIS;
1599   if Sec.Alignment = taRightJustify then
1600     Ex := (Ex or DT_RIGHT) and not (DT_END_ELLIPSIS);
1601   DrawTextEx(lvwLog.Canvas.Handle, PChar(Text), -1, Rec, Ex, nil);
1602 end;
1603
1604 { TBottleLogDragObject }
1605
1606 function TBottleLogDragObject.GetDragImages: TDragImageList;
1607 begin
1608   // \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é
1609   Result := nil;
1610 end;
1611
1612 procedure TBottleLogDragObject.SetBottleLogList(
1613   const Value: TBottleLogList);
1614 begin
1615   FBottleLogList := Value;
1616 end;
1617
1618 procedure TBottleLogDragObject.SetLogItem(const Value: TLogItem);
1619 begin
1620   FLogItem := Value;
1621 end;
1622
1623 procedure TfrmLog.mnTabSaveXMLLogClick(Sender: TObject);
1624 begin
1625   DoSaveLogXML(FBottleLogList[tabBottleLog.Tag] as TBottleLogList);
1626 end;
1627
1628 procedure TfrmLog.mnSaveHTMLClick(Sender: TObject);
1629 var
1630   LogList, SB: TBottleLogList;
1631   i: integer;
1632   Options: THTMLOutputOptions;
1633 begin
1634   SB := SelectedBottleLog;
1635   if SB = nil then
1636     Exit;
1637   if SB.Count = 0 then
1638     Exit;
1639   Application.CreateForm(TfrmHTMLOutputConfig, frmHTMLOutputConfig);
1640   with frmHTMLOutputConfig do
1641     try
1642       // Show HTML save option dialog
1643       if not Execute then
1644         Exit;
1645       LogList := TBottleLogList.Create('');
1646       try
1647         case Range of
1648           orAll:
1649             for i := SB.Count-1 downto 0 do
1650               if SB.Bottles[i].LogType = ltBottle then
1651                 LogList.Add(TLogItem.Create(SB.Bottles[i]));
1652           orSelected:
1653             if SB.Bottles[lvwLog.ItemIndex].LogType = ltBottle then
1654               LogList.Add(TLogItem.Create(SB.Bottles[lvwLog.ItemIndex]))
1655             else
1656               ShowMessage('\82±\82Ì\83\81\83b\83Z\81[\83W\82Í\95Û\91\82Å\82«\82Ü\82¹\82ñ');
1657           orUpward:
1658             for i := lvwLog.ItemIndex downto 0 do
1659               if SB.Bottles[i].LogType = ltBottle then
1660                 LogList.Add(TLogItem.Create(SB.Bottles[i]));
1661         end;
1662         Options.ImageDir := ImageDir;
1663         Options.UseColor := UseColor;
1664         Options.ImageType := ImageType;
1665         Application.CreateForm(TfrmHTMLOutputProgress, frmHTMLOutputProgress);
1666         try
1667           frmHTMLOutputProgress.Show;
1668           LogList.OnHTMLOutputWork := HTMLOutputWork;
1669           LogList.SaveToHTML(FileName, Options, SsParser);
1670         finally
1671           frmHTMLOutputProgress.Release;
1672         end;
1673       finally
1674         LogList.Free;
1675       end;
1676     finally
1677       Release;
1678     end;
1679 end;
1680
1681 procedure TfrmLog.mnPopupCopyGhostClick(Sender: TObject);
1682 var
1683   Log: TLogItem;
1684   Clip: TClipBoard;
1685 begin
1686   Log := SelectedBottleLog.Bottles[frmLog.lvwLog.ItemIndex];
1687   if Log = nil then Exit;
1688   Clip := ClipBoard();
1689   Clip.SetTextBuf(PChar(Log.Ghost));
1690 end;
1691
1692 procedure TfrmLog.lvwLogDrawItem(Control: TWinControl; Index: Integer;
1693   Rect: TRect; State: TOwnerDrawState);
1694 var
1695   DestRect: TRect;
1696   Ico: TIcon;
1697   Bottle: TLogItem;
1698 begin
1699   Bottle := SelectedBottleLog.Bottles[Index];
1700   CheckBottleURL(Bottle);
1701
1702   DestRect := lvwLog.ItemRect(Index);
1703
1704   // \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è
1705   lvwLog.Canvas.Brush.Style := bsSolid;
1706   if odSelected in State then
1707   begin
1708     if odFocused in State then
1709       lvwLog.Canvas.Brush.Color := clHighlight
1710     else
1711       lvwLog.Canvas.Brush.Color := clBtnFace;
1712   end else
1713   begin
1714     lvwLog.Canvas.Brush.Color := Pref.BgColor;
1715   end;
1716   lvwLog.Canvas.FillRect(DestRect);
1717   lvwLog.Canvas.Brush.Style := bsClear;
1718
1719   // \83h\83\89\83b\83O\92\86\82È\82ç\98g\90ü\82ð\95`\89æ\82·\82é
1720   if FLVDragDest = Index then
1721   begin
1722     DrawListViewDragBorder(DestRect);
1723   end;
1724
1725   if odSelected in State then
1726   begin
1727     if odFocused in State then
1728       lvwLog.Canvas.Font.Color := clHighlightText
1729     else
1730       lvwLog.Canvas.Font.Color := clWindowText;
1731   end else
1732   lvwLog.Canvas.Font.Color := Pref.TextColor;
1733
1734   //lvwLog.Canvas.TextOut(DestRect.Left, DestRect.Top, Bottle.Script);
1735   // \93ú\95t
1736   DrawListSection(SubTime, DestRect.Top,
1737     FormatDateTime('yy/mm/dd hh:nn:ss', Bottle.LogTime), 16);
1738   // \93ú\95t\97\93\83{\83g\83\8b\83A\83C\83R\83\93
1739   Ico := TIcon.Create;
1740   try
1741     if Bottle.LogType = ltSystemLog then
1742       frmSender.imgIcon.GetIcon(IconSystemLog, Ico)
1743     else
1744       case Bottle.State of
1745         lsUnopened:
1746           frmSender.imgIcon.GetIcon(IconBottle, Ico);
1747         lsOpened:
1748           frmSender.imgIcon.GetIcon(IconOpened, Ico);
1749         lsPlaying:
1750           frmSender.imgIcon.GetIcon(IconPlaying, Ico);
1751       end;
1752     lvwLog.Canvas.Draw(2, DestRect.Top, Ico);
1753   finally
1754     Ico.Free;
1755   end;
1756
1757   // \83`\83\83\83\93\83l\83\8b
1758   DrawListSection(SubChannel, DestRect.Top, Bottle.Channel);
1759   // \83S\81[\83X\83g
1760   DrawListSection(SubGhost, DestRect.Top, Bottle.Ghost);
1761   // \93\8a\95[\81E\93¯\88Ó
1762   if Bottle.Votes <> 0 then
1763     DrawListSection(SubVotes, DestRect.Top, IntToStr(Bottle.Votes))
1764   else
1765     DrawListSection(SubVotes, DestRect.Top, '');
1766
1767   if Bottle.Agrees <> 0 then
1768     DrawListSection(SubAgrees, DestRect.Top, IntToStr(Bottle.Agrees))
1769   else
1770     DrawListSection(SubVotes, DestRect.Top, '');
1771   
1772   // \83X\83N\83\8a\83v\83g
1773   DestRect.Left := Header.Sections[SubScript].Left;
1774   DrawSingleLineScript(Bottle, DestRect, State);
1775 end;
1776
1777 procedure TfrmLog.lvwLogData(Control: TWinControl; Index: Integer;
1778   var Data: String);
1779 begin
1780   Data := '';
1781 end;
1782
1783 procedure TfrmLog.HeaderSectionClick(HeaderControl: THeaderControl;
1784   Section: THeaderSection);
1785 var SortType: TBottleLogSortType;
1786     SelectedMID: String;
1787 begin
1788   if SelectedBottleLog = nil then
1789     Exit;
1790   if lvwLog.ItemIndex <> -1 then
1791     SelectedMID := SelectedBottleLog.Bottles[lvwLog.ItemIndex].MID
1792   else
1793     SelectedMID := '';
1794
1795   case Section.Index of
1796     SubTime:    SortType := stLogTime;
1797     SubChannel: SortType := stChannel;
1798     SubGhost:   SortType := stGhost;
1799     SubVotes:   SortType := stVote;
1800     SubAgrees:  SortType := stAgree;
1801     SubScript:  SortType := stScript;
1802   else
1803     SortType := stLogTime;
1804   end;
1805
1806   SelectedBottleLog.SortBottles(SortType);
1807   lvwLog.Invalidate;
1808   if Length(SelectedMID) > 0 then
1809     SelAndFocusMessage(SelectedMID);
1810 end;
1811
1812 procedure TfrmLog.HeaderSectionTrack(HeaderControl: THeaderControl;
1813   Section: THeaderSection; Width: Integer; State: TSectionTrackState);
1814 begin
1815   lvwLog.Invalidate;
1816 end;
1817
1818 procedure TfrmLog.lvwLogMouseDown(Sender: TObject; Button: TMouseButton;
1819   Shift: TShiftState; X, Y: Integer);
1820 var
1821   Index: Integer;
1822 begin
1823   if Button = mbRight then
1824   begin
1825     Index := lvwLog.ItemAtPos(Point(X, Y), true);
1826     if Index > -1 then
1827     begin
1828       lvwLog.ItemIndex := Index;
1829       lvwLogClick(Self);
1830     end;
1831   end;
1832 end;
1833
1834 procedure TfrmLog.lvwLogWindowProc(var Msg: TMessage);
1835 begin
1836   if Msg.Msg = WM_CHAR then begin
1837     if Msg.WParam = 13 then begin
1838       lvwLogDblClick(lvwLog);
1839       Msg.Result := 0;
1840     end;
1841     Exit;
1842   end;
1843
1844   FLVWndMethod(Msg);
1845 end;
1846
1847 end.