OSDN Git Service

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