OSDN Git Service

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