OSDN Git Service

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