OSDN Git Service

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