OSDN Git Service

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