OSDN Git Service

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