OSDN Git Service

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