OSDN Git Service

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