OSDN Git Service

(none)
[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
10 type
11   TSaveLogType = (stLog, stLogWithChannels, stText, stXML);
12
13   TfrmLog = class(TForm)
14     ToolBar: TToolBar;
15     tbtnClear: TToolButton;
16     pnlUpper: TPanel;
17     SsParser: TSsParser;
18     StatusBar: TStatusBar;
19     tbtnSaveLog: TToolButton;
20     PopupMenuPreview: TPopupMenu;
21     mnPopCopy: TMenuItem;
22     tbtnVoteMessage: TToolButton;
23     PopupMenuListView: TPopupMenu;
24     mnPopUpVoteMessage: TMenuItem;
25     SaveDialog: TSaveDialog;
26     pnlPanel: TPanel;
27     Splitter: TSplitter;
28     edtScript: TRichEdit;
29     mnPopUpCopyScript: TMenuItem;
30     PopupMenuSaveLog: TPopupMenu;
31     mnSaveLog: TMenuItem;
32     mnSaveLogChannel: TMenuItem;
33     mnSaveLogScript: TMenuItem;
34     mnSaveLogXML: TMenuItem;
35     ToolButton1: TToolButton;
36     mnJumpURL: TMenuItem;
37     mnPopUpAgreeMessage: TMenuItem;
38     tbtnAgreeMessage: TToolButton;
39     ToolButton2: TToolButton;
40     tbtnPreviewStyle: TToolButton;
41     PopupMenuPreviewStyle: TPopupMenu;
42     mnPreviewStyleConversation: TMenuItem;
43     mnPreviewStyleScript: TMenuItem;
44     mnPreviewStyleScriptWithLineBreak: TMenuItem;
45     Panel1: TPanel;
46     tabBottleLog: TTabControl;
47     lvwLog: TListView;
48     tbtnDownloadLog: TToolButton;
49     PopupMenuTab: TPopupMenu;
50     mnCloseTab: TMenuItem;
51     tbtnFindBottle: TToolButton;
52     tbtnOpenLog: TToolButton;
53     OpenDialog: TOpenDialog;
54     tbtnInsertCue: TToolButton;
55     mnInsertCue: TMenuItem;
56     procedure tbtnClearClick(Sender: TObject);
57     procedure FormCreate(Sender: TObject);
58     procedure lvwLogChange(Sender: TObject; Item: TListItem;
59       Change: TItemChange);
60     procedure lvwLogDblClick(Sender: TObject);
61     procedure lvwLogKeyPress(Sender: TObject; var Key: Char);
62     procedure FormDestroy(Sender: TObject);
63     procedure lvwLogClick(Sender: TObject);
64     procedure mnSaveLogClick(Sender: TObject);
65     procedure lvwLogColumnClick(Sender: TObject; Column: TListColumn);
66     procedure mnPopUpCopyScriptClick(Sender: TObject);
67     procedure mnSaveLogChannelClick(Sender: TObject);
68     procedure mnSaveLogScriptClick(Sender: TObject);
69     procedure mnSaveLogXMLClick(Sender: TObject);
70     procedure lvwLogData(Sender: TObject; Item: TListItem);
71     procedure PopupMenuListViewPopup(Sender: TObject);
72     procedure lvwLogCustomDrawItem(Sender: TCustomListView;
73       Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
74     procedure lvwLogCustomDrawSubItem(Sender: TCustomListView;
75       Item: TListItem; SubItem: Integer; State: TCustomDrawState;
76       var DefaultDraw: Boolean);
77     procedure PopupMenuPreviewStylePopup(Sender: TObject);
78     procedure mnPreviewStyleClick(Sender: TObject);
79     procedure tbtnPreviewStyleClick(Sender: TObject);
80     procedure tabBottleLogChange(Sender: TObject);
81     procedure tabBottleLogChanging(Sender: TObject;
82       var AllowChange: Boolean);
83     procedure tabBottleLogContextPopup(Sender: TObject; MousePos: TPoint;
84       var Handled: Boolean);
85     procedure mnCloseTabClick(Sender: TObject);
86     procedure tbtnFindBottleClick(Sender: TObject);
87     procedure tbtnOpenLogClick(Sender: TObject);
88     procedure tabBottleLogMouseDown(Sender: TObject; Button: TMouseButton;
89       Shift: TShiftState; X, Y: Integer);
90     procedure tabBottleLogDragOver(Sender, Source: TObject; X, Y: Integer;
91       State: TDragState; var Accept: Boolean);
92     procedure tabBottleLogDragDrop(Sender, Source: TObject; X, Y: Integer);
93     procedure tabBottleLogEndDrag(Sender, Target: TObject; X, Y: Integer);
94   private
95     { Private \90é\8c¾ }
96     FLastScript: String; //\83X\83N\83\8a\83v\83g\8dÄ\95`\89æ\97}\90§\97p
97     FBottleLogList: TObjectList;
98     //
99     FDragTabIndex: integer; //\83^\83u\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
100     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)
101     //
102     procedure UpdateScript(const Script: String);
103     procedure UpdateScriptConversationColor(const Script: String);
104     procedure UpdateScriptScript(const Script: String);
105     procedure mnURLClick(Sender: TObject);
106     procedure ExtractURLs(Script: String; Result: TStrings);
107     function GetDefaultFileName(const Name: String; const Ext: String): String;
108     function BottleLogTitled(const LogName: String): TBottleLogList;
109   protected
110     procedure CreateParams(var Params: TCreateParams); override;
111   public
112     { Public \90é\8c¾ }
113     function SelectedBottleLog: TBottleLogList;
114     property BottleLogList: TObjectList read FBottleLogList;
115     procedure AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String);
116     procedure AddCurrentSystemLog(const LogName, MessageString: String);
117     procedure VoteLog(const MID: String; const Vote: integer);
118     procedure AgreeLog(const MID: String; const Agree: integer);
119     procedure SetBottleState(const MID: String; State: TLogState);
120     procedure AllBottleOpened;
121     procedure LogLoaded(Sender: TObject);
122     procedure LogLoadFailure(Sender: TObject; const Message: String);
123     procedure LogLoadWork(Sender: TObject);
124     procedure UpdateTab;
125     procedure UpdateWindow;
126     procedure SelAndFocusMessage(const MID: String);
127   end;
128
129
130 var
131   frmLog: TfrmLog;
132
133 const
134   IconBottle    = 17;
135   IconOpened    = 30;
136   IconPlaying   = 31;
137   IconSystemLog = 26;
138   SubChannel    = 0;
139   SubGhost      = 1;
140   SubVotes      = 2;
141   SubAgrees     = 3;
142   SubScript     = 4;
143
144 implementation
145
146 uses MainForm;
147
148 {$R *.DFM}
149
150 { TfrmLog }
151
152 procedure TfrmLog.AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String);
153 var Sel: integer;
154 begin
155   BottleLogTitled(LogName).AddScriptLog(Script, Channel, MID, Ghost);
156   if SelectedBottleLog <> BottleLogTitled(LogName) then Exit;
157   lvwLog.OnChange := nil; //\83C\83x\83\93\83g\94­\90¶(\82¢\82ë\82¢\82ë\8dÄ\95`\89æ\82ª\8bN\82«\82é)\82Ì\97}\90§
158   if lvwLog.Selected <> nil then Sel := lvwLog.Selected.Index else Sel := -1;
159   lvwLog.Items.Count := SelectedBottleLog.Count;
160   UpdateWindow;
161   if Sel >= 0 then begin
162     lvwLog.Selected := lvwLog.Items[Sel + 1];
163     lvwLog.Selected.Focused := true;
164   end;
165   if not lvwLog.Focused then
166     ListView_Scroll(lvwLog.Handle, 0, High(integer));
167   lvwLog.OnChange := lvwLogChange;
168 end;
169
170 procedure TfrmLog.AddCurrentSystemLog(const LogName, MessageString: String);
171 var Sel: integer;
172 begin
173   BottleLogTitled(LogName).AddSystemLog(MessageString);
174   if SelectedBottleLog <> BottleLogTitled(LogName) then Exit;
175   lvwLog.OnChange := nil;
176   if lvwLog.Selected <> nil then Sel := lvwLog.Selected.Index else Sel := -1;
177   lvwLog.Items.Count := SelectedBottleLog.Count;
178   UpdateWindow;
179   if Sel >= 0 then begin
180     lvwLog.Selected := lvwLog.Items[Sel + 1];
181     lvwLog.Selected.Focused := true;
182   end;
183   if not lvwLog.Focused then
184     ListView_Scroll(lvwLog.Handle, 0, High(integer));
185   lvwLog.OnChange := lvwLogChange;
186 end;
187
188
189
190 procedure TfrmLog.tbtnClearClick(Sender: TObject);
191 begin
192   if SelectedBottleLog = nil then Exit;
193   FBottleLogList.Delete(tabBottleLog.TabIndex);
194   tabBottleLog.TabIndex := 0;
195   UpdateTab;
196   UpdateWindow;
197   lvwLogChange(Self, nil, ctState);
198 end;
199
200 procedure TfrmLog.FormCreate(Sender: TObject);
201 var i: integer;
202 begin
203   FBottleLogList := TObjectList.Create;
204
205   SsParser.TagPattern.Assign(frmSender.SsParser.TagPattern);
206   SsParser.MetaPattern.Assign(frmSender.SsParser.MetaPattern);
207
208   with Pref.LogWindowPosition do begin
209     Self.Left   := Left;
210     Self.Top    := Top;
211     Self.Width  := Right - Left + 1;
212     Self.Height := Bottom - Top + 1;
213   end;
214   lvwLog.DoubleBuffered := true;
215   edtScript.Height := Pref.LogWindowDividerPos;
216
217   i := 0;
218   while Token(Pref.LogWindowColumnWidth, ',', i) <> '' do begin
219     lvwLog.Columns[i].Width := StrToIntDef(Token(Pref.LogWindowColumnWidth, ',', i), 100);
220     Inc(i);
221   end;
222
223   UpdateWindow; // Reset window color and enabled status of some buttons
224 end;
225
226 procedure TfrmLog.FormDestroy(Sender: TObject);
227 var i: integer;
228     WidthStr: String;
229 begin
230   WidthStr := '';
231   for i := 0 to lvwLog.Columns.Count-1 do begin
232     if i > 0 then WidthStr := WidthStr + ',';
233     WidthStr := WidthStr + IntToStr(lvwLog.Column[i].Width);
234   end;
235   Pref.LogWindowColumnWidth := WidthStr;
236
237   with Pref.LogWindowPosition do begin
238     Left   := Self.Left;
239     Top    := Self.Top;
240     Right  := Self.Left + Self.Width - 1;
241     Bottom := Self.Top + Self.Height - 1;
242   end;
243   Pref.LogWindowDividerPos := edtScript.Height;
244
245   FreeAndNil(FBottleLogList);
246 end;
247
248 procedure TfrmLog.lvwLogChange(Sender: TObject; Item: TListItem;
249   Change: TItemChange);
250 var Script: String;
251     Log: TLogItem;
252 begin
253   if SelectedBottleLog <> nil then begin
254     StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
255     if Change = ctState then begin
256       Script := '';
257       if lvwLog.Selected <> nil then begin
258         Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
259         if (Log.LogType = ltBottle) and not frmSender.Connecting then begin
260           Script := Log.Script;
261           frmSender.actVoteMessage.Enabled := true;
262           frmSender.actAgreeMessage.Enabled := true;
263           frmSender.actInsertCue.Enabled := true;
264           mnPopUpCopyScript.Enabled := true;
265           StatusBar.Panels[1].Text := Format('%d\83o\83C\83g - \83_\83u\83\8b\83N\83\8a\83b\83N\82Å\8dÄ\90¶', [Length(Log.Script)]);
266           UpdateScript(Script);
267         end else begin
268           frmSender.actVoteMessage.Enabled := false;
269           frmSender.actAgreeMessage.Enabled := false;
270           frmSender.actInsertCue.Enabled := false;
271           mnPopUpCopyScript.Enabled := false;
272           StatusBar.Panels[1].Text := '';
273           UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\82ð\83N\83\8a\83A
274         end;
275       end else begin
276         frmSender.actVoteMessage.Enabled := false;
277         frmSender.actAgreeMessage.Enabled := false;
278         frmSender.actInsertCue.Enabled := false;
279         mnPopUpCopyScript.Enabled := false;
280         StatusBar.Panels[1].Text := '';
281         UpdateScript(Script); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
282       end;
283     end;
284     tbtnSaveLog.Enabled := lvwLog.Items.Count > 0;
285   end else begin
286     frmSender.actVoteMessage.Enabled := false;
287     frmSender.actAgreeMessage.Enabled := false;
288     frmSender.actInsertCue.Enabled := false;
289     mnPopUpCopyScript.Enabled := false;
290     StatusBar.Panels[0].Text := '';
291     UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
292   end;
293 end;
294
295 procedure TfrmLog.lvwLogDblClick(Sender: TObject);
296 var Script: String;
297     Log, CueItem: TLogItem;
298 begin
299   if lvwLog.Selected = nil then
300     Exit;
301   Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
302   if Log = nil then Exit;
303   if Log.LogType <> ltBottle then
304     Exit;
305   Script := frmSender.ScriptTransForSSTP(Log.Script);
306   if Script = '' then begin
307     ShowMessage('\96â\91è\82Ì\82 \82é\83X\83N\83\8a\83v\83g\82Å\82·\81B\8dÄ\90\82Å\82«\82Ü\82¹\82ñ\81B'+
308       '\83N\83\89\83C\83A\83\93\83g\82ð\8dÅ\90V\94Å\82É\82µ\82Ä\82Ý\82Ä\82­\82¾\82³\82¢\81B');
309     Exit;
310   end;
311
312   CueItem := TLogItem.Create(Log);
313   try
314     CueItem.Script := Script;
315     frmSender.BottleSstp.Unshift(CueItem);
316   except
317     CueItem.Free;
318   end;
319 end;
320
321 procedure TfrmLog.UpdateScriptConversationColor(const Script: String);
322 var i: integer;
323     scr: String;
324     UnyuTalking, Talked, InSynchronized: boolean;
325 begin
326   scr := Script;
327   frmSender.DoTrans(scr, [toConvertURL]);
328   SsParser.LeaveEscape := false;
329   SsParser.InputString := scr;
330   SsParser.LeaveEscape := true;
331   UnyuTalking := false;
332   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
333   InSynchronized := false;
334   edtScript.Text := '';
335   edtScript.Color := Pref.BgColor;
336   for i := 0 to SsParser.Count-1 do begin
337     if (SsParser[i] = '\_s') and not InSynchronized then begin
338       InSynchronized := true;
339       if Talked then begin
340         edtScript.SelText := #13#10;
341         Talked := false;
342       end;
343     end else if (SsParser[i] = '\_s') and InSynchronized then begin
344       InSynchronized := false;
345       if Talked then begin
346         edtScript.SelText := #13#10;
347         Talked := false;
348       end;
349     end;
350     if (SsParser[i] = '\u') and not UnyuTalking then begin
351       UnyuTalking := true;
352       if Talked then begin
353         edtScript.SelText := #13#10;
354         Talked := false;
355       end;
356     end;
357     if (SsParser[i] = '\h') and UnyuTalking then begin
358       UnyuTalking := false;
359       if Talked then begin
360         edtScript.SelText := #13#10;
361         Talked := false;
362       end;
363     end;
364     if SsParser.MarkUpType[i] = mtStr then begin
365       if InSynchronized then
366         edtScript.SelAttributes.Color := Pref.TalkColorS
367       else if UnyuTalking then
368         edtScript.SelAttributes.Color := Pref.TalkColorU
369       else
370         edtScript.SelAttributes.Color := Pref.TalkColorH;
371       edtScript.SelText := SsParser[i];
372       Talked := true;
373     end;
374     if SsParser.MarkUpType[i] = mtMeta then begin
375       edtScript.SelAttributes.Color := Pref.MetaWordColor;
376       edtScript.SelText := SsParser[i];
377       Talked := true;
378     end;
379   end;
380 end;
381
382 procedure TfrmLog.lvwLogKeyPress(Sender: TObject; var Key: Char);
383 begin
384   if Key = #13 then lvwLogDblClick(Sender);
385 end;
386
387 procedure TfrmLog.CreateParams(var Params: TCreateParams);
388 begin
389   inherited;
390   Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
391 end;
392
393 procedure TfrmLog.lvwLogClick(Sender: TObject);
394 begin
395   //\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ô
396   with lvwLog do
397     Selected := Selected;
398 end;
399
400 procedure TfrmLog.lvwLogColumnClick(Sender: TObject; Column: TListColumn);
401 var SortType: TBottleLogSortType;
402     SelectedMID: String;
403     SortColumn: integer;
404 begin
405   if lvwLog.Selected <> nil then
406     SelectedMID := SelectedBottleLog.Bottles[lvwLog.Selected.Index].MID;
407
408   SortColumn := Column.Index;
409   case SortColumn-1 of
410     -1: SortType := stLogTime;
411     subChannel: SortType := stChannel;
412     subGhost:   SortType := stGhost;
413     subVotes:   SortType := stVote;
414     subAgrees:  SortType := stAgree;
415     subScript:  SortType := stScript;
416   else SortType := stLogTime;
417   end;
418
419   SelectedBottleLog.SortBottles(SortType);
420   lvwLog.Invalidate;
421   SelAndFocusMessage(SelectedMID);
422 end;
423
424
425 procedure TfrmLog.mnPopUpCopyScriptClick(Sender: TObject);
426 var
427   Log: TLogItem;
428   Clip: TClipBoard;
429 begin
430   Log := SelectedBottleLog.Bottles[frmLog.lvwLog.Selected.Index];
431   if Log = nil then Exit;
432   Clip := ClipBoard();
433   Clip.SetTextBuf(PChar(Log.Script));
434 end;
435
436 procedure TfrmLog.SetBottleState(const MID: String; State: TLogState);
437 var i: integer;
438     Bottle: TLogItem;
439 begin
440   for i := 0 to FBottleLogList.Count-1 do begin
441     Bottle := (FBottleLogList[i] as TBottleLogList).Bottle(MID);
442     if Bottle <> nil then begin
443       Bottle.State := State;
444       lvwLog.OnChange := nil;
445       lvwLog.Invalidate;
446       lvwLog.OnChange := lvwLogChange;
447     end;
448   end;
449 end;
450
451 procedure TfrmLog.mnSaveLogClick(Sender: TObject);
452 begin
453   if SelectedBottleLog = nil then Exit;
454   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
455   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
456   SaveDialog.DefaultExt := 'log';
457   SaveDialog.FilterIndex := 1;
458   if SaveDialog.Execute then
459     SelectedBottleLog.SaveToSstpLog(SaveDialog.FileName, false);
460 end;
461
462 procedure TfrmLog.mnSaveLogChannelClick(Sender: TObject);
463 begin
464   if SelectedBottleLog = nil then Exit;
465   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
466   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
467   SaveDialog.DefaultExt := 'log';
468   SaveDialog.FilterIndex := 1;
469   if SaveDialog.Execute then
470     SelectedBottleLog.SaveToSstpLog(SaveDialog.FileName, true);
471 end;
472
473 procedure TfrmLog.mnSaveLogScriptClick(Sender: TObject);
474 begin
475   if SelectedBottleLog = nil then Exit;
476   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.txt');
477   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
478   SaveDialog.DefaultExt := 'txt';
479   SaveDialog.FilterIndex := 2;
480   if SaveDialog.Execute then
481     SelectedBottleLog.SaveToText(SaveDialog.FileName);
482 end;
483
484 procedure TfrmLog.mnSaveLogXMLClick(Sender: TObject);
485 begin
486   if SelectedBottleLog = nil then Exit;
487   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.xml');
488   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
489   SaveDialog.DefaultExt := 'xml';
490   SaveDialog.FilterIndex := 3;
491   if SaveDialog.Execute then
492     SelectedBottleLog.SaveToXmlFile(SaveDialog.FileName);
493 end;
494
495 procedure TfrmLog.lvwLogData(Sender: TObject; Item: TListItem);
496 var i: integer;
497     Log: TLogItem;
498 begin
499   if Item = nil then Exit;
500   i := Item.Index;
501   Log := SelectedBottleLog.Bottles[i];
502   with Item do begin
503     Caption := FormatDateTime('yy/mm/dd hh:nn:ss', Log.LogTime);
504     SubItems.Clear;
505     SubItems.Add(Log.Channel);
506     SubItems.Add(Log.Ghost);
507     if Log.LogType = ltBottle then begin
508       if Log.Votes > 0 then
509         SubItems.Add(IntToStr(Log.Votes))
510       else
511         SubItems.Add('');
512       if Log.Agrees > 0 then
513         SubItems.Add(IntToStr(Log.Agrees))
514       else
515         SubItems.Add('');
516     end else begin
517       // \83V\83X\83e\83\80\83\8d\83O\82È\82Ç\82Í\93\8a\95[\81E\93¯\88Ó\82ð\95\\8e¦\82µ\82È\82¢
518       SubItems.Add('');
519       SubItems.Add('');
520     end;
521     SubItems.Add(Log.Script);
522
523     if Log.LogType = ltBottle then begin
524       case Log.State of
525         lsUnopened: ImageIndex := IconBottle;
526         lsPlaying:  ImageIndex := IconPlaying;
527         lsOpened:   ImageIndex := IconOpened;
528       end;
529     end else
530       ImageIndex := IconSystemLog;
531   end;
532 end;
533
534 procedure TfrmLog.UpdateWindow;
535 var EnabledFlag: boolean;
536 begin
537   if true then begin // ColorScript
538     if lvwLog.Color <> Pref.BgColor then lvwLog.Color := Pref.BgColor;
539     if lvwLog.Font.Color <> Pref.TalkColorH then lvwLog.Font.Color := Pref.TalkColorH;
540   end else begin
541     if lvwLog.Color <> clWindow then lvwLog.Color := clWindow;
542     if lvwLog.Font.Color <> clWindowText then lvwLog.Font.Color := clWindowText;
543   end;
544   if SelectedBottleLog <> nil then begin
545     Caption := '\83\8d\83O - ' + SelectedBottleLog.Title;
546     StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
547     lvwLog.Items.Count := SelectedBottleLog.Count;
548   end else begin
549     Caption := '\83\8d\83O';
550     StatusBar.Panels[0].Text := '';
551     StatusBar.Panels[1].Text := '';
552     lvwLog.Items.Count := 0;
553   end;
554
555   EnabledFlag := SelectedBottleLog <> nil;
556   tbtnClear.Enabled := EnabledFlag;
557   tbtnSaveLog.Enabled := EnabledFlag;
558   tbtnFindBottle.Enabled := EnabledFlag;
559
560   lvwLog.Invalidate;
561 end;
562
563 procedure TfrmLog.PopupMenuListViewPopup(Sender: TObject);
564 var Log: TLogItem;
565     Child: TMenuItem;
566     Urls: TStringList;
567     i: integer;
568 begin
569   for i := mnJumpURL.Count-1 downto 0 do begin
570     mnJumpURL.Items[i].Free;
571   end;
572   mnJumpURL.Enabled := false;
573   if lvwLog.Selected = nil then Exit;
574   Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
575   if Log = nil then Exit;
576   Urls := TStringList.Create;
577   try
578     ExtractURLs(Log.Script, Urls);
579     for i := 0 to Urls.Count-1 do begin
580       Child := TMenuItem.Create(Self);
581       with Child do begin
582         Caption := Format('(&%d) %s', [i+1, StringReplace(Urls[i], '&', '&&', [rfReplaceAll])]);
583         Tag := i;
584         OnClick := mnURLClick;
585         AutoHotkeys := maManual;
586         mnJumpURL.Add(Child);
587       end;
588     end;
589     mnJumpURL.Enabled := Urls.Count > 0;
590   finally
591     Urls.Free;
592   end;
593 end;
594
595 procedure TfrmLog.mnURLClick(Sender: TObject);
596 var LogItem: TLogItem;
597     URL: String;
598     Urls: TStringList;
599 begin
600   if (lvwLog.Selected = nil) or (SelectedBottleLog = nil) then Exit;
601   LogItem := SelectedBottleLog[lvwLog.Selected.Index] as TLogItem;
602   Urls := TStringList.Create;
603   try
604     ExtractURLs(LogItem.Script, Urls);
605     URL := Urls[(Sender as TMenuItem).Tag];
606     ShellExecute(Handle, 'open', PChar(URL), nil, nil, SW_SHOW);
607   finally
608     Urls.Free;
609   end;
610 end;
611
612 procedure TfrmLog.ExtractURLs(Script: String; Result: TStrings);
613 var i, u, j: integer;
614     s: String;
615 begin
616   Result.Clear;
617   SsParser.InputString := Script;
618   for i := 0 to SsParser.Count-1 do begin
619     if (SsParser.Match(SsParser[i], '\URL%b') > 0) then begin
620       for u := 7 downto 1 do begin
621         if (SsParser.Match(SsParser[i],
622             '\URL%b'+StringReplace(StringOfChar('-', u*2),
623             '-', '%b', [rfReplaceAll]))) > 0 then begin
624           for j := 1 to u do begin
625             s := SsParser.GetParam(SsParser[i], j*2);
626             if Pos('http://', s) > 0 then Result.Add(s);
627           end;
628           Break;
629         end;
630       end;
631       if SsParser.Match(SsParser[i], '\URL%b%b') = 0 then begin //\8aÈ\88Õ\94ÅURL\95Ï\8a·
632         //\8aÈ\88Õ\8c`\8e®\URL\83^\83O\95Ï\8a·
633         s := SsParser.GetParam(SsParser[i], 1);
634         if Pos('http://', s) > 0 then Result.Add(s);
635       end;
636     end;
637   end;
638 end;
639
640 procedure TfrmLog.SelAndFocusMessage(const MID: String);
641 var i: integer;
642     Log: TLogItem;
643 begin
644   for i := 0 to SelectedBottleLog.Count-1 do begin
645     Log := SelectedBottleLog.Items[i] as TLogItem;
646     if Log.MID = MID then begin
647       lvwLog.Items[i].Selected := true;
648       lvwLog.Items[i].Focused := true;
649     end;
650   end;
651 end;
652
653 procedure TfrmLog.lvwLogCustomDrawItem(Sender: TCustomListView;
654   Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
655 begin
656   //
657 end;
658
659 procedure TfrmLog.lvwLogCustomDrawSubItem(Sender: TCustomListView;
660   Item: TListItem; SubItem: Integer; State: TCustomDrawState;
661   var DefaultDraw: Boolean);
662 {var
663   DestRect: TRect;
664   Script: String;
665   i, x, w: integer;
666   SavedDC: integer;
667   Mark: TSsMarkUpType;}
668 begin
669   Exit // !!
670   {if (SubItem <> SubScript+1) or (not Pref.ColorScript) then Exit; // DefaultDraw = true
671   // Custom Script Coloring
672   DefaultDraw := false;
673   SavedDC := SaveDC(lvwLog.Canvas.Handle);
674   try
675     ListView_GetSubItemRect(lvwLog.Handle, Item.Index, SubScript+1, LVIR_BOUNDS, @DestRect);
676
677     lvwLog.Canvas.Brush.Style := bsSolid;
678     if cdsSelected in State then begin
679       lvwLog.Canvas.Brush.Color := clHighlight
680     end else begin
681       lvwLog.Canvas.Brush.Color := Pref.BgColor;
682     end;
683     lvwLog.Canvas.FillRect(DestRect);
684     lvwLog.Canvas.Brush.Style := bsClear;
685
686     Script := Item.SubItems[SubScript];
687     // DrawTextEx(lvwLog.Canvas.Handle, PChar(Script), -1, DestRect, DT_END_ELLIPSIS, nil);
688     SsParser.InputString := Script;
689     x := 6;
690     for i := 0 to SsParser.Count - 1 do begin
691       Mark := SsParser.MarkUpType[i];
692       case Mark of
693         mtMeta:   lvwLog.Canvas.Font.Color := Pref.MetaWordColor;
694         mtTag:    lvwLog.Canvas.Font.Color := Pref.MarkUpColor;
695         mtTagErr: lvwLog.Canvas.Font.Color := Pref.MarkErrorColor;
696         else begin
697           lvwLog.Canvas.Font.Color := Pref.TalkColorH;
698         end;
699       end;
700       w := lvwLog.Canvas.TextWidth(SsParser[i]);
701       lvwLog.Canvas.TextRect(DestRect, DestRect.Left + x, DestRect.Top + 2, SsParser[i]);
702       x := x + w;
703       if DestRect.Right - DestRect.Left < x then Break;
704     end;
705   finally
706     RestoreDC(lvwLog.Canvas.Handle, SavedDC);
707   end;}
708 end;
709
710 procedure TfrmLog.UpdateScript(const Script: String);
711 begin
712   if Script <> FLastScript then begin
713     if Pref.LogWindowPreviewStyle = psConversation then begin
714       UpdateScriptConversationColor(Script);
715     end else begin
716       UpdateScriptScript(Script);
717     end;
718     SendMessage(edtScript.Handle, EM_LINESCROLL, Low(integer), Low(integer)); //\83X\83N\83\8d\81[\83\8b\96ß\82µ
719     FLastScript := Script;
720   end;
721 end;
722
723 procedure TfrmLog.PopupMenuPreviewStylePopup(Sender: TObject);
724 var i: integer;
725 begin
726   with PopupMenuPreviewStyle do
727     for i := 0 to Items.Count-1 do
728       Items[i].Checked := Items[i].Tag = Ord(Pref.LogWindowPreviewStyle)
729 end;
730
731 procedure TfrmLog.mnPreviewStyleClick(Sender: TObject);
732 var i: integer;
733 begin
734   with PopupMenuPreviewStyle do
735     for i := 0 to Items.Count-1 do
736       Items[i].Checked := (Sender as TMenuItem).Tag = Items[i].Tag;
737   Pref.LogWindowPreviewStyle := TLogWindowPreviewStyle((Sender as TMenuItem).Tag);
738   FLastScript := '';
739   lvwLogChange(self, lvwLog.Selected, ctState);
740 end;
741
742 procedure TfrmLog.UpdateScriptScript(const Script: String);
743 var
744   UnyuTalking, InSynchronized: boolean;
745   i: integer;
746 begin
747   edtScript.Color := Pref.BgColor;
748   SsParser.LeaveEscape := true;
749   SsParser.InputString := Script;
750   edtScript.Text := '';
751   edtScript.SelAttributes.Color := clWindowText;
752   UnyuTalking := false;
753   InSynchronized := false;
754   for i := 0 to SsParser.Count-1 do begin
755     case SsParser.MarkUpType[i] of
756       mtStr: begin
757         if InSynchronized then
758           edtScript.SelAttributes.Color := Pref.TalkColorS
759         else if UnyuTalking then
760           edtScript.SelAttributes.Color := Pref.TalkColorU
761         else
762           edtScript.SelAttributes.Color := Pref.TalkColorH;
763       end;
764       mtTag: begin
765         edtScript.SelAttributes.Color := Pref.MarkUpColor;
766         if SsParser[i] = '\h' then
767           UnyuTalking := false
768         else if SsParser[i] = '\u' then
769           UnyuTalking := true
770         else if SsParser[i] = '\_s' then
771           InSynchronized := not InSynchronized;
772       end;
773       mtMeta:   edtScript.SelAttributes.Color := Pref.MetaWordColor;
774       mtTagErr: edtScript.SelAttributes.Color := Pref.MarkErrorColor;
775     end;
776     edtScript.SelText := SsParser[i];
777     if (SsParser[i] = '\n') and (Pref.LogWindowPreviewStyle = psScriptWithLineBreak) then
778       edtScript.SelText := #13#10;
779   end;
780 end;
781
782 procedure TfrmLog.tbtnPreviewStyleClick(Sender: TObject);
783 var sel: integer;
784 begin
785   sel := Ord(Pref.LogWindowPreviewStyle);
786   sel := sel + 1;
787   if sel > Ord(High(TLogWindowPreviewStyle)) then sel := 0;
788   Pref.LogWindowPreviewStyle := TLogWindowPreviewStyle(sel);
789   FLastScript := '';
790   lvwLogChange(self, lvwLog.Selected, ctState);
791 end;
792
793 function TfrmLog.SelectedBottleLog: TBottleLogList;
794 begin
795   if tabBottleLog.TabIndex >= 0 then
796     Result := FBottleLogList.Items[tabBottleLog.TabIndex] as TBottleLogList
797   else
798     Result := nil;
799 end;
800
801 procedure TfrmLog.tabBottleLogChange(Sender: TObject);
802 begin
803   UpdateWindow;
804   if SelectedBottleLog.SelectedIndex >= 0 then begin
805     lvwLog.Items[SelectedBottleLog.SelectedIndex].Selected := true;
806     if lvwLog.Focused then lvwLog.Selected.Focused := true;
807   end;
808   lvwLogChange(Self, nil, ctState);
809 end;
810
811 procedure TfrmLog.LogLoaded(Sender: TObject);
812 begin
813   if SelectedBottleLog = Sender then begin
814     UpdateWindow;
815   end;
816 end;
817
818 procedure TfrmLog.UpdateTab;
819 var i, cur: integer;
820 begin
821   cur := tabBottleLog.tabIndex;
822   tabBottleLog.Tabs.Clear;
823   for i := 0 to FBottleLogList.Count - 1 do begin
824     tabBottleLog.Tabs.Add((FBottleLogList[i] as TBottleLogList).Title);
825   end;
826   if FBottleLogList.Count > 0 then begin
827     if cur < FBottleLogList.Count then
828       tabBottleLog.TabIndex := cur
829     else
830       tabBottleLog.TabIndex := FBottleLogList.Count-1;
831   end;
832 end;
833
834 procedure TfrmLog.LogLoadFailure(Sender: TObject; const Message: String);
835 begin
836   Beep;
837   ShowMessage(Message);
838   if Sender = SelectedBottleLog then UpdateWindow;
839 end;
840
841 procedure TfrmLog.AgreeLog(const MID: String; const Agree: integer);
842 var i: integer;
843     flag: boolean;
844 begin
845   flag := false;
846   for i := 0 to FBottleLogList.Count - 1 do begin
847     if (FBottleLogList[i] as TBottleLogList).Bottle(MID) <> nil then begin
848       (FBottleLogList[i] as TBottleLogList).Bottle(MID).Agrees := Agree;
849       flag := true;
850     end;
851   end;
852   if flag then lvwLog.Invalidate;
853 end;
854
855 procedure TfrmLog.VoteLog(const MID: String; const Vote: integer);
856 var i: integer;
857     flag: boolean;
858 begin
859   flag := false;
860   for i := 0 to FBottleLogList.Count - 1 do begin
861     if (FBottleLogList[i] as TBottleLogList).Bottle(MID) <> nil then begin
862       (FBottleLogList[i] as TBottleLogList).Bottle(MID).Votes := Vote;
863       flag := true;
864     end;
865   end;
866   if flag then lvwLog.Invalidate;
867 end;
868
869 procedure TfrmLog.tabBottleLogChanging(Sender: TObject;
870   var AllowChange: Boolean);
871 begin
872   // \8c»\8dÝ\91I\91ð\82³\82ê\82Ä\82¢\82é\83\8d\83O\82Ì\91I\91ð\8fó\91Ô\82ð\95Û\91
873   if SelectedBottleLog = nil then Exit;
874   if lvwLog.Selected <> nil then
875     SelectedBottleLog.SelectedIndex := lvwLog.Selected.Index
876   else
877     SelectedBottleLog.SelectedIndex := -1;
878 end;
879
880 procedure TfrmLog.tabBottleLogContextPopup(Sender: TObject;
881   MousePos: TPoint; var Handled: Boolean);
882 begin
883   with tabBottleLog do begin
884     Tag := IndexOfTabAt(MousePos.X, MousePos.Y);
885     if Tag < 0 then Handled := true;
886   end;
887 end;
888
889 procedure TfrmLog.mnCloseTabClick(Sender: TObject);
890 begin
891   FBottleLogList.Delete(tabBottleLog.Tag);
892   UpdateTab;
893   UpdateWindow;
894   lvwLogChange(Self, nil, ctState);
895 end;
896
897 procedure TfrmLog.tbtnFindBottleClick(Sender: TObject);
898 var Query: String;
899     ResultLog: TBottleLogList;
900     Item1, Item2: TLogItem;
901     i, matched: integer;
902 begin
903   if SelectedBottleLog = nil then Exit;
904   if SelectedBottleLog.Count = 0 then begin
905     ShowMessage('\8c\9f\8dõ\91Î\8fÛ\82ª\8bó\82Å\82·\81B');
906     Exit;
907   end;
908   Query := '';
909   matched := 0;
910   if InputQuery('\83X\83N\83\8a\83v\83g\96{\95\82ð\8c\9f\8dõ', '\8c\9f\8dõ\95\8e\9a\97ñ', Query) then begin
911     if Query = '' then Exit;
912     ResultLog := TBottleLogList.Create('\8c\9f\8dõ\8c\8b\89Ê');
913     for i := 0 to SelectedBottleLog.Count-1 do begin
914       Item1 := SelectedBottleLog.Items[i] as TLogItem;
915       if AnsiContainsText(Item1.Script, Query) and (Item1.LogType = ltBottle) then begin
916         matched := matched + 1;
917         Item2 := TLogItem.Create(ltBottle, Item1.MID, Item1.Channel,
918           Item1.Script, Item1.Ghost, Item1.LogTime);
919         Item2.State := lsOpened;
920         Item2.Votes := Item1.Votes;
921         Item2.Agrees := Item1.Agrees;
922         ResultLog.Add(Item2);
923       end;
924     end;
925     if matched = 0 then
926       ResultLog.AddSystemLog('\8c©\82Â\82©\82è\82Ü\82¹\82ñ\82Å\82µ\82½');
927     BottleLogList.Add(ResultLog);
928     UpdateTab;
929     tabBottleLog.TabIndex := BottleLogList.Count-1;
930     UpdateWindow;
931   end;
932 end;
933
934 procedure TfrmLog.tbtnOpenLogClick(Sender: TObject);
935 var BottleLog: TBottleLogList;
936     i, Index: integer;
937 begin
938   Index := -1;
939   if OpenDialog.Execute then begin
940     for i := 0 to OpenDialog.Files.Count-1 do begin
941       BottleLog := TBottleLogList.Create(ExtractFileName(OpenDialog.Files[i]));
942       try
943         BottleLog.LoadFromXMLFile(OpenDialog.Files[i]);
944       except
945         on E: EXMLFileOpenException do begin
946           Beep;
947           ShowMessage(E.Message);
948           FreeAndNil(BottleLog);
949         end;
950       end;
951       if BottleLog <> nil then Index := BottleLogList.Add(BottleLog); // \8dÅ\8cã\82É\8aJ\82¢\82½\83\8d\83O\82Ì\88Ê\92u\82ð\8bL\89¯
952     end;
953     UpdateTab;
954     if Index >= 0 then tabBottleLog.TabIndex := Index;
955     UpdateWindow;
956   end;
957 end;
958
959 function TfrmLog.GetDefaultFileName(const Name, Ext: String): String;
960 begin
961   Result := StringReplace(Name, '/', '', [rfReplaceAll]);
962   Result := StringReplace(Result, ' ', '', [rfReplaceAll]);
963   Result := ChangeFileExt(Result, Ext);
964 end;
965
966 function TfrmLog.BottleLogTitled(const LogName: String): TBottleLogList;
967 var i: integer;
968 begin
969   for i := 0 to FBottleLogList.Count-1 do begin
970     if (FBottleLogList[i] as TBottleLogList).Title = LogName then begin
971       Result := (FBottleLogList[i] as TBottleLogList);
972       Exit;
973     end;
974   end;
975   // \8c©\82Â\82©\82ç\82È\82¢\8fê\8d\87
976   Result := TBottleLogList.Create(LogName); // \90V\82µ\82­\8dì\82é
977   FBottleLogList.Add(Result);
978   UpdateTab;
979   if FBottleLogList.Count = 1 then tabBottleLog.TabIndex := 0;
980 end;
981
982 procedure TfrmLog.AllBottleOpened;
983 var i, j: integer;
984     Log: TBottleLogList;
985 begin
986   for i := 0 to FBottleLogList.Count-1 do begin
987     Log  := FBottleLogList[i] as TBottleLogList;
988     for j := 0 to Log.Count-1 do begin
989       Log.Bottles[j].State := lsOpened;
990     end;
991   end;
992 end;
993
994 procedure TfrmLog.tabBottleLogMouseDown(Sender: TObject;
995   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
996 var Index: integer;
997 begin
998   with tabBottleLog do begin
999     Index := IndexOfTabAt(X, Y);
1000     if Index = -1 then Exit; //\83^\83u\82ª\82È\82¢\82Ì\82Å\83h\83\89\83b\83O\82Å\82«\82È\82¢
1001     if Button = mbLeft then begin
1002       FDragTabIndex := Index; //\83h\83\89\83b\83O\82·\82é\83^\83u\82Ì\83C\83\93\83f\83b\83N\83X\82ð\95Û\91
1003       BeginDrag(False);
1004       FDragTabDest := -1;     //\83h\83\89\83b\83O\98g\90ü\95`\89æ\83t\83\89\83O\83N\83\8a\83A\82Ì\82½\82ß
1005     end;
1006   end;
1007 end;
1008
1009 procedure TfrmLog.tabBottleLogDragOver(Sender, Source: TObject; X,
1010   Y: Integer; State: TDragState; var Accept: Boolean);
1011 var TargetRect: TRect;
1012     OldDest: integer;
1013 begin
1014   Accept := Source = tabBottleLog;
1015   if not Accept then Exit;
1016   with tabBottleLog do begin
1017     OldDest := FDragTabDest;
1018     FDragTabDest := IndexOfTabAt(X, Y);
1019     if FDragTabDest = -1 then begin
1020       Accept := false; //\82±\82Ì\8fê\8d\87\82Í\83h\83\8d\83b\83v\82ð\94F\82ß\82È\82¢
1021       Exit;
1022     end;
1023     with Canvas do begin
1024       Pen.Mode := pmNot;
1025       Pen.Width := 3;
1026     end;
1027     if (OldDest <> FDragTabDest) and (OldDest >= 0) then begin
1028       //\88È\91O\82Ì\98g\90ü\8fÁ\8b\8e
1029       TargetRect := TabRect(OldDest);
1030       with Canvas do begin
1031         Brush.Style := bsClear;
1032         Rectangle(TargetRect.Left, TargetRect.Top,
1033                   TargetRect.Right, TargetRect.Bottom);
1034       end;
1035     end;
1036     if (OldDest <> FDragTabDest) then begin
1037       //\90V\82µ\82¢\98g\90ü\95`\89æ
1038       TargetRect := TabRect(FDragTabDest);
1039       with Canvas do begin
1040         Brush.Style := bsClear;
1041         Rectangle(TargetRect.Left, TargetRect.Top,
1042                   TargetRect.Right, TargetRect.Bottom);
1043       end;
1044     end;
1045   end;
1046 end;
1047
1048 procedure TfrmLog.tabBottleLogDragDrop(Sender, Source: TObject; X,
1049   Y: Integer);
1050 var DestIndex: integer;
1051 begin
1052   with tabBottleLog do begin
1053     DestIndex := IndexOfTabAt(X, Y);
1054     Tabs.Move(FDragTabIndex, DestIndex);
1055     FBottleLogList.Move(FDragTabIndex, DestIndex);
1056   end;
1057 end;
1058
1059 procedure TfrmLog.tabBottleLogEndDrag(Sender, Target: TObject; X,
1060   Y: Integer);
1061 begin
1062   //\8b­\90§\93I\82É\83^\83u\82ð\8dÄ\95`\89æ\82³\82¹\82é\81B\98g\90ü\8fÁ\82µ\91Î\8dô
1063   tabBottleLog.Tabs.BeginUpdate;
1064   tabBottleLog.Tabs.EndUpdate;
1065 end;
1066
1067 procedure TfrmLog.LogLoadWork(Sender: TObject);
1068 begin
1069   if Sender = SelectedBottleLog then lvwLog.Invalidate;
1070 end;
1071
1072 end.