OSDN Git Service

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