OSDN Git Service

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