OSDN Git Service

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