OSDN Git Service

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