OSDN Git Service

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