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