OSDN Git Service

Open XML file
[winbottle/winbottle.git] / bottleclient / LogForm.pas
index a082f4b..f9704cc 100755 (executable)
@@ -4,8 +4,9 @@ interface
 
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
-  ComCtrls, ToolWin, StdCtrls, ExtCtrls, SsParser, BottleDef, Menus, FastSstp,
-  Clipbrd, Logs, ShellAPI, Commctrl;
+  ComCtrls, ToolWin, StdCtrls, ExtCtrls, SsParser, BottleDef, Menus,
+  Clipbrd, Logs, ShellAPI, Commctrl, DirectSstp, Contnrs, xmldom, XMLIntf,
+  msxmldom, XMLDoc;
 
 type
   TSaveLogType = (stLog, stLogWithChannels, stText, stXML);
@@ -24,7 +25,6 @@ type
     mnPopUpVoteMessage: TMenuItem;
     SaveDialog: TSaveDialog;
     pnlPanel: TPanel;
-    lvwLog: TListView;
     Splitter: TSplitter;
     edtScript: TRichEdit;
     mnPopUpCopyScript: TMenuItem;
@@ -43,6 +43,16 @@ type
     mnPreviewStyleConversation: TMenuItem;
     mnPreviewStyleScript: TMenuItem;
     mnPreviewStyleScriptWithLineBreak: TMenuItem;
+    Panel1: TPanel;
+    tabBottleLog: TTabControl;
+    lvwLog: TListView;
+    tbtnDownloadLog: TToolButton;
+    PopupMenuTab: TPopupMenu;
+    mnCloseTab: TMenuItem;
+    tbtnFindBottle: TToolButton;
+    XMLDocument: TXMLDocument;
+    tbtnOpenLog: TToolButton;
+    OpenDialog: TOpenDialog;
     procedure tbtnClearClick(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure lvwLogChange(Sender: TObject; Item: TListItem;
@@ -67,27 +77,42 @@ type
     procedure PopupMenuPreviewStylePopup(Sender: TObject);
     procedure mnPreviewStyleClick(Sender: TObject);
     procedure tbtnPreviewStyleClick(Sender: TObject);
+    procedure tabBottleLogChange(Sender: TObject);
+    procedure tabBottleLogChanging(Sender: TObject;
+      var AllowChange: Boolean);
+    procedure tabBottleLogContextPopup(Sender: TObject; MousePos: TPoint;
+      var Handled: Boolean);
+    procedure mnCloseTabClick(Sender: TObject);
+    procedure tbtnFindBottleClick(Sender: TObject);
+    procedure tbtnOpenLogClick(Sender: TObject);
   private
     { Private \90é\8c¾ }
-    SortColumn: integer;  //\97ñ\83N\83\8a\83b\83N\83\\81[\83g\97p
-    SortDesc: boolean;    //\97ñ\83N\83\8a\83b\83N\83\\81[\83g\97p
     FLastScript: String; //\83X\83N\83\8a\83v\83g\8dÄ\95`\89æ\97}\90§\97p
+    FBottleLogList: TObjectList;
     procedure UpdateScript(const Script: String);
     procedure UpdateScriptConversationColor(const Script: String);
     procedure UpdateScriptConversationNoColor(const Script: String);
     procedure UpdateScriptScript(const Script: String);
-    procedure DoSaveLog(SaveType: TSaveLogType; Ext: String;
-      Filter: integer);
     procedure mnURLClick(Sender: TObject);
     procedure ExtractURLs(Script: String; Result: TStrings);
+    function GetCurrentBottleLog: TBottleLogList;
+    function GetDefaultFileName(const Name: String; const Ext: String): String;
   protected
     procedure CreateParams(var Params: TCreateParams); override;
   public
     { Public \90é\8c¾ }
-    procedure AddScriptLog(const Script, Channel, MID, Ghost: String);
-    procedure AddSystemLog(const MessageString: String);
+    function SelectedBottleLog: TBottleLogList;
+    property CurrentBottleLog: TBottleLogList read GetCurrentBottleLog;
+    property BottleLogList: TObjectList read FBottleLogList;
+    procedure AddCurrentScriptLog(const Script, Channel, MID, Ghost: String);
+    procedure AddCurrentSystemLog(const MessageString: String);
+    procedure VoteLog(const MID: String; const Vote: integer);
+    procedure AgreeLog(const MID: String; const Agree: integer);
     procedure SetBottleStatusToPlaying(const MID: String);
     procedure SetBottleStatusToOpened(const MID: String);
+    procedure LogLoaded(Sender: TObject);
+    procedure LogLoadFailure(Sender: TObject; const Message: String);
+    procedure UpdateTab;
     procedure UpdateWindow;
     procedure SelAndFocusMessage(const MID: String);
   end;
@@ -96,6 +121,8 @@ type
 var
   frmLog: TfrmLog;
 
+function CurrentBottleLog: TBottleLogList;
+
 const
   IconBottle    = 17;
   IconOpened    = 30;
@@ -108,56 +135,76 @@ const
 
 implementation
 
-uses MainForm;
+uses MainForm, StrUtils;
 
 {$R *.DFM}
 
+function CurrentBottleLog: TBottleLogList;
+begin
+  Result := frmLog.CurrentBottleLog;
+end;
+
 { TfrmLog }
 
-procedure TfrmLog.AddScriptLog(const Script, Channel, MID, Ghost: String);
-var LogItem: TLogItem;
-    Sel: integer;
+procedure TfrmLog.AddCurrentScriptLog(const Script, Channel, MID, Ghost: String);
+var Sel: integer;
 begin
+  CurrentBottleLog.AddScriptLog(Script, Channel, MID, Ghost);
+  if SelectedBottleLog <> CurrentBottleLog then Exit;
   lvwLog.OnChange := nil; //\83C\83x\83\93\83g\94­\90¶(\82¢\82ë\82¢\82ë\8dÄ\95`\89æ\82ª\8bN\82«\82é)\82Ì\97}\90§
   if lvwLog.Selected <> nil then Sel := lvwLog.Selected.Index else Sel := -1;
-  LogItem := TLogItem.Create(ltBottle, MID, Channel, Script, Ghost, Now());
-  BottleLog.Insert(0, LogItem);
-  lvwLog.Items.Count := BottleLog.Count;
+  lvwLog.Items.Count := CurrentBottleLog.Count;
   UpdateWindow;
   if Sel >= 0 then begin
     lvwLog.Selected := lvwLog.Items[Sel + 1];
     lvwLog.Selected.Focused := true;
   end;
+  if not lvwLog.Focused then
+    ListView_Scroll(lvwLog.Handle, 0, High(integer));
   lvwLog.OnChange := lvwLogChange;
 end;
 
-procedure TfrmLog.AddSystemLog(const MessageString: String);
-var LogItem: TLogItem;
-    Sel: integer;
+procedure TfrmLog.AddCurrentSystemLog(const MessageString: String);
+var Sel: integer;
 begin
+  CurrentBottleLog.AddSystemLog(MessageString);
+  if SelectedBottleLog <> CurrentBottleLog then Exit;
   lvwLog.OnChange := nil;
   if lvwLog.Selected <> nil then Sel := lvwLog.Selected.Index else Sel := -1;
-  LogItem := TLogItem.Create(ltSystemLog, '', '', MessageString, '', Now());
-  BottleLog.Insert(0, LogItem);
-  lvwLog.Items.Count := BottleLog.Count;
+  lvwLog.Items.Count := CurrentBottleLog.Count;
   UpdateWindow;
   if Sel >= 0 then begin
     lvwLog.Selected := lvwLog.Items[Sel + 1];
     lvwLog.Selected.Focused := true;
   end;
+  if not lvwLog.Focused then
+    ListView_Scroll(lvwLog.Handle, 0, High(integer));
   lvwLog.OnChange := lvwLogChange;
 end;
 
+
+
 procedure TfrmLog.tbtnClearClick(Sender: TObject);
 begin
-  BottleLog.Clear;
-  lvwLog.Items.Count := 0;
-  lvwLog.Invalidate;
-  lvwLogChange(Self, nil, ctState);
+  if SelectedBottleLog = CurrentBottleLog then begin
+    CurrentBottleLog.Clear;
+    lvwLog.Items.Count := 0;
+    lvwLog.Invalidate;
+    lvwLogChange(Self, nil, ctState);
+  end else begin
+    FBottleLogList.Delete(tabBottleLog.TabIndex);
+    tabBottleLog.TabIndex := 0;
+    UpdateTab;
+    UpdateWindow;
+    lvwLogChange(Self, nil, ctState);
+  end;
 end;
 
 procedure TfrmLog.FormCreate(Sender: TObject);
 begin
+  FBottleLogList := TObjectList.Create;
+  FBottleLogList.Add(TBottleLogList.Create('\83J\83\8c\83\93\83g')); // CurrentBottleLog
+
   SsParser.TagPattern.Assign(frmSender.SsParser.TagPattern);
   SsParser.MetaPattern.Assign(frmSender.SsParser.MetaPattern);
 
@@ -181,6 +228,8 @@ begin
     Bottom := Self.Top + Self.Height - 1;
   end;
   Pref.LogWindowDividerPos := edtScript.Height;
+
+  FreeAndNil(FBottleLogList);
 end;
 
 procedure TfrmLog.lvwLogChange(Sender: TObject; Item: TListItem;
@@ -188,17 +237,16 @@ procedure TfrmLog.lvwLogChange(Sender: TObject; Item: TListItem;
 var Script: String;
     Log: TLogItem;
 begin
-  StatusBar.Panels[0].Text := IntToStr(lvwLog.Items.Count) + '\8c\8f';
+  StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
   if Change = ctState then begin
     Script := '';
     if lvwLog.Selected <> nil then begin
-      Log := BottleLog.Bottles[lvwLog.Selected.Index];
-      if Log.LogType = ltBottle then begin
+      Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
+      if (Log.LogType = ltBottle) and not frmSender.Connecting then begin
         Script := Log.Script;
         frmSender.actVoteMessage.Enabled := true;
         frmSender.actAgreeMessage.Enabled := true;
         mnPopUpCopyScript.Enabled := true;
-        frmSender.DoTrans(Script, [toConvertURL]);
         UpdateScript(Script);
       end else begin
         frmSender.actVoteMessage.Enabled := false;
@@ -225,7 +273,7 @@ var Script: String;
 begin
   if lvwLog.Selected = nil then Exit;
   //Log := TLogItem(lvwLog.Selected.Data);
-  Log := BottleLog.Bottles[lvwLog.Selected.Index];
+  Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
   if Log = nil then Exit;
   if Log.LogType <> ltBottle then Exit;
   Script := Log.Script;
@@ -245,17 +293,20 @@ begin
     Ghost := frmSender.GetChannelPrefs(Log.Channel).TargetGhost;
   //\83^\81[\83Q\83b\83g\83S\81[\83X\83g\8am\92è
   Ghost := frmSender.SetHWndToFavoriteGhost(Ghost);
-  frmSender.FastSstp.SstpSender := 'SSTP Bottle -\81y\83\8d\83O\8dÄ\90\81z';
+  frmSender.DirectSstp.SstpSender := 'SSTP Bottle -\81y\83\8d\83O\8dÄ\90\81z';
   if Pref.NoTranslate then SOpt := [soNoTranslate] else SOpt := [];
-  frmSender.FastSstp.SstpSEND(Script, SOpt, frmSender.GhostNameToSetName(Ghost));
+  frmSender.DirectSstp.SstpSEND(Script, SOpt, frmSender.GhostNameToSetName(Ghost));
 end;
 
 procedure TfrmLog.UpdateScriptConversationColor(const Script: String);
 var i: integer;
+    scr: String;
     UnyuTalking, Talked: boolean;
 begin
+  scr := Script;
+  frmSender.DoTrans(scr, [toConvertURL]);
   SsParser.LeaveEscape := false;
-  SsParser.InputString := Script;
+  SsParser.InputString := scr;
   SsParser.LeaveEscape := true;
   UnyuTalking := false;
   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
@@ -297,8 +348,10 @@ var Scr: String;
     i: integer;
     UnyuTalking, Talked, LastUnyuTalked: boolean;
 begin
+  Scr := Script;
+  frmSender.DoTrans(Scr, [toConvertURL]);
   SsParser.LeaveEscape := false;
-  SsParser.InputString := Script;
+  SsParser.InputString := Scr;
   SsParser.LeaveEscape := true;
   edtScript.Text := '';
   edtScript.Color := clWindow;
@@ -348,23 +401,15 @@ begin
     Selected := Selected;
 end;
 
-procedure TfrmLog.mnSaveLogClick(Sender: TObject);
-begin
-  DoSaveLog(stLog, 'log', 1);
-end;
-
 procedure TfrmLog.lvwLogColumnClick(Sender: TObject; Column: TListColumn);
 var SortType: TBottleLogSortType;
     SelectedMID: String;
+    SortColumn: integer;
 begin
   if lvwLog.Selected <> nil then
-    SelectedMID := BottleLog.Bottles[lvwLog.Selected.Index].MID;
-  if SortColumn = Column.Index then
-    SortDesc := not SortDesc
-  else begin
-    SortColumn := Column.Index;
-    SortDesc := false;
-  end;
+    SelectedMID := SelectedBottleLog.Bottles[lvwLog.Selected.Index].MID;
+
+  SortColumn := Column.Index;
   case SortColumn-1 of
     -1: SortType := stLogTime;
     subChannel: SortType := stChannel;
@@ -373,7 +418,8 @@ begin
     subScript:  SortType := stScript;
   else SortType := stLogTime;
   end;
-  BottleLog.SortBottles(SortType, SortDesc);
+
+  SelectedBottleLog.SortBottles(SortType);
   lvwLog.Invalidate;
   SelAndFocusMessage(SelectedMID);
 end;
@@ -384,7 +430,7 @@ var
   Log: TLogItem;
   Clip: TClipBoard;
 begin
-  Log := BottleLog.Bottles[frmLog.lvwLog.Selected.Index];
+  Log := SelectedBottleLog.Bottles[frmLog.lvwLog.Selected.Index];
   if Log = nil then Exit;
   Clip := ClipBoard();
   Clip.SetTextBuf(PChar(Log.Script));
@@ -392,117 +438,71 @@ end;
 
 procedure TfrmLog.SetBottleStatusToOpened(const MID: String);
 begin
-  if BottleLog.Bottle(MID) <> nil then begin
-    BottleLog.Bottle(MID).State := lsOpened;
+  if CurrentBottleLog.Bottle(MID) <> nil then begin
+    CurrentBottleLog.Bottle(MID).State := lsOpened;
+    lvwLog.OnChange := nil;
     lvwLog.Invalidate;
+    lvwLog.OnChange := lvwLogChange;
   end;
 end;
 
 procedure TfrmLog.SetBottleStatusToPlaying(const MID: String);
 begin
-  if BottleLog.Bottle(MID) <> nil then begin
-    BottleLog.Bottle(MID).State := lsPlaying;
+  if CurrentBottleLog.Bottle(MID) <> nil then begin
+    CurrentBottleLog.Bottle(MID).State := lsPlaying;
+    lvwLog.OnChange := nil;
     lvwLog.Invalidate;
+    lvwLog.OnChange := lvwLogChange;
   end;
 end;
 
-procedure TfrmLog.DoSaveLog(SaveType: TSaveLogType; Ext: String; Filter: integer);
-var i: integer;
-    Log: TStringList;
-    LogItem: TLogItem;
-    Date: String;
-    TmpScript: String;
-const
-  DayStr: array[1..7] of String = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
+procedure TfrmLog.mnSaveLogClick(Sender: TObject);
 begin
+  SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
   SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
-  SaveDialog.DefaultExt := Ext;
-  SaveDialog.FilterIndex := Filter;
-  if SaveDialog.Execute then begin
-    Log := nil;
-    try
-      Log := TStringList.Create;
-      case SaveType of
-        stLog, stLogWithChannels: begin
-          for i := 0 to BottleLog.Count -1 do begin
-            LogItem := BottleLog.Bottles[i];
-            if LogItem = nil then Continue;
-            if LogItem.LogType <> ltBottle then Continue;
-            Date := FormatDateTime('yyyy/mm/dd hh:nn:ss ', LogItem.LogTime);
-            Date := Date + '(' + DayStr[DayOfWeek(LogItem.LogTime)] + ')';
-            if SaveType = stLogWithChannels then
-              Date := Date + ',' + LogItem.Channel +',SEND,' + LogItem.Script
-            else
-              Date := Date + ',0.0.0.0,SEND,' + LogItem.Script;
-            Log.Add(Date);
-          end;
-        end;
-        stText: begin
-          for i := 0 to BottleLog.Count -1 do begin
-            LogItem := BottleLog.Bottles[i];
-            if LogItem = nil then Continue;
-            if LogItem.LogType <> ltBottle then Continue;
-            Log.Add(LogItem.Script);
-          end;
-        end;
-        stXML: begin
-          Log.Add('<?xml version=''1.0'' encoding=''Shift_JIS''?>');
-          Log.Add('<bottlelog>');
-          for i := 0 to BottleLog.Count -1 do begin
-            LogItem := BottleLog.Bottles[i];
-            if LogItem = nil then Continue;
-            if LogItem.LogType <> ltBottle then Continue;
-            Date := FormatDateTime('yyyy/mm/dd hh:nn:ss', LogItem.LogTime);
-            Log.Add(Format('<message mid=''%s''>', [LogItem.MID]));
-            Log.Add('<date>' + Date + '</date>');
-            Log.Add('<channel>' + LogItem.Channel + '</channel>');
-            //
-            TmpScript := LogItem.Script;
-            TmpScript := StringReplace(TmpScript, '<', '&lt;', [rfReplaceAll]);
-            TmpScript := StringReplace(TmpScript, '>', '&gt;', [rfReplaceAll]);
-            Log.Add('<script>' + TmpScript + '</script>');
-            //
-            if LogItem.Ghost = '' then
-              Log.Add('<ghost />')
-            else begin
-              TmpScript := LogItem.Ghost;
-              TmpScript := StringReplace(TmpScript, '<', '&lt;', [rfReplaceAll]);
-              TmpScript := StringReplace(TmpScript, '>', '&gt;', [rfReplaceAll]);
-              Log.Add(Format('<ghost>%s</ghost>', [TmpScript]));
-            end;
-            Log.Add('</message>');
-          end;
-          Log.Add('</bottlelog>');
-        end;
-      end;
-      Log.SaveToFile(SaveDialog.FileName);
-    finally
-      Log.Free;
-    end;
-  end;
+  SaveDialog.DefaultExt := 'log';
+  SaveDialog.FilterIndex := 1;
+  if SaveDialog.Execute then
+    SelectedBottleLog.SaveToSstpLog(SaveDialog.FileName, false);
 end;
 
 procedure TfrmLog.mnSaveLogChannelClick(Sender: TObject);
 begin
-  DoSaveLog(stLogWithChannels, 'log', 1);
+  SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
+  SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
+  SaveDialog.DefaultExt := 'log';
+  SaveDialog.FilterIndex := 1;
+  if SaveDialog.Execute then
+    SelectedBottleLog.SaveToSstpLog(SaveDialog.FileName, true);
 end;
 
 procedure TfrmLog.mnSaveLogScriptClick(Sender: TObject);
 begin
-  DoSaveLog(stText, 'txt', 2);
+  SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.txt');
+  SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
+  SaveDialog.DefaultExt := 'txt';
+  SaveDialog.FilterIndex := 2;
+  if SaveDialog.Execute then
+    SelectedBottleLog.SaveToText(SaveDialog.FileName);
 end;
 
 procedure TfrmLog.mnSaveLogXMLClick(Sender: TObject);
 begin
-  DoSaveLog(stXML, 'xml', 3);
+  SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.xml');
+  SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
+  SaveDialog.DefaultExt := 'xml';
+  SaveDialog.FilterIndex := 3;
+  if SaveDialog.Execute then
+    SelectedBottleLog.SaveToXmlFile(SaveDialog.FileName, XMLDocument);
 end;
 
 procedure TfrmLog.lvwLogData(Sender: TObject; Item: TListItem);
 var i: integer;
     Log: TLogItem;
 begin
+  if Item = nil then Exit;
   i := Item.Index;
-  Log := BottleLog.Bottles[i];
+  Log := SelectedBottleLog.Bottles[i];
   with Item do begin
     Caption := FormatDateTime('yy/mm/dd hh:nn:ss', Log.LogTime);
     SubItems.Clear;
@@ -510,8 +510,14 @@ begin
       SubItems.Add(Log.Channel + '/' + Log.Ghost)
     else
       SubItems.Add(Log.Channel);
-    SubItems.Add(IntToStr(Log.Votes));
-    SubItems.Add(IntToStr(Log.Agrees));
+    if Log.LogType = ltBottle then begin
+      SubItems.Add(IntToStr(Log.Votes));
+      SubItems.Add(IntToStr(Log.Agrees));
+    end else begin
+      // \83V\83X\83e\83\80\83\8d\83O\82È\82Ç\82Í\93\8a\95[\81E\93¯\88Ó\82ð\95\\8e¦\82µ\82È\82¢
+      SubItems.Add('-');
+      SubItems.Add('-');
+    end;
     SubItems.Add(Log.Script);
 
     if Log.LogType = ltBottle then begin
@@ -527,6 +533,7 @@ end;
 
 procedure TfrmLog.UpdateWindow;
 begin
+  StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
   if Pref.ColorScript then begin
     if lvwLog.Color <> Pref.BgColor then lvwLog.Color := Pref.BgColor;
     if lvwLog.Font.Color <> Pref.TalkColorH then lvwLog.Font.Color := Pref.TalkColorH;
@@ -534,7 +541,7 @@ begin
     if lvwLog.Color <> clWindow then lvwLog.Color := clWindow;
     if lvwLog.Font.Color <> clWindowText then lvwLog.Font.Color := clWindowText;
   end;
-  lvwLog.Items.Count := BottleLog.Count;
+  lvwLog.Items.Count := SelectedBottleLog.Count;
   lvwLog.Invalidate;
   //lvwLogChange(Self, lvwLog.Selected, ctState);
 end;
@@ -545,12 +552,12 @@ var Log: TLogItem;
     Urls: TStringList;
     i: integer;
 begin
-  for i := 0 to mnJumpURL.Count-1 do begin
+  for i := mnJumpURL.Count-1 downto 0 do begin
     mnJumpURL.Items[i].Free;
   end;
   mnJumpURL.Enabled := false;
   if lvwLog.Selected = nil then Exit;
-  Log := BottleLog.Bottles[lvwLog.Selected.Index];
+  Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
   if Log = nil then Exit;
   Urls := nil;
   try
@@ -613,8 +620,8 @@ procedure TfrmLog.SelAndFocusMessage(const MID: String);
 var i: integer;
     Log: TLogItem;
 begin
-  for i := 0 to BottleLog.Count-1 do begin
-    Log := BottleLog.Items[i] as TLogItem;
+  for i := 0 to SelectedBottleLog.Count-1 do begin
+    Log := SelectedBottleLog.Items[i] as TLogItem;
     if Log.MID = MID then begin
       lvwLog.Items[i].Selected := true;
       lvwLog.Items[i].Focused := true;
@@ -631,12 +638,12 @@ end;
 procedure TfrmLog.lvwLogCustomDrawSubItem(Sender: TCustomListView;
   Item: TListItem; SubItem: Integer; State: TCustomDrawState;
   var DefaultDraw: Boolean);
-var
+{var
   DestRect: TRect;
   Script: String;
   i, x, w: integer;
   SavedDC: integer;
-  Mark: TSsMarkUpType;
+  Mark: TSsMarkUpType;}
 begin
   Exit // !!
   {if (SubItem <> SubScript+1) or (not Pref.ColorScript) then Exit; // DefaultDraw = true
@@ -691,7 +698,7 @@ begin
     end else begin
       UpdateScriptScript(Script);
     end;
-    SendMessage(edtScript.Handle, EM_LINESCROLL, 0, 0); //\83X\83N\83\8d\81[\83\8b\96ß\82µ
+    SendMessage(edtScript.Handle, EM_LINESCROLL, Low(integer), Low(integer)); //\83X\83N\83\8d\81[\83\8b\96ß\82µ
     FLastScript := Script;
   end;
 end;
@@ -769,4 +776,175 @@ begin
   lvwLogChange(self, lvwLog.Selected, ctState);
 end;
 
+function TfrmLog.SelectedBottleLog: TBottleLogList;
+begin
+  Result := FBottleLogList.Items[tabBottleLog.TabIndex] as TBottleLogList;
+end;
+
+function TfrmLog.GetCurrentBottleLog: TBottleLogList;
+begin
+  Result := FBottleLogList.Items[0] as TBottleLogList;
+end;
+
+procedure TfrmLog.tabBottleLogChange(Sender: TObject);
+begin
+  UpdateWindow;
+  if SelectedBottleLog.SelectedIndex >= 0 then begin
+    lvwLog.Items[SelectedBottleLog.SelectedIndex].Selected := true;
+    if lvwLog.Focused then lvwLog.Selected.Focused := true;
+  end;
+  lvwLogChange(Self, nil, ctState);
+end;
+
+procedure TfrmLog.LogLoaded(Sender: TObject);
+begin
+  if SelectedBottleLog = Sender then begin
+    UpdateWindow;
+  end;
+end;
+
+procedure TfrmLog.UpdateTab;
+var i: integer;
+    cur: TBottleLogList;
+begin
+  cur := SelectedBottleLog;
+  tabBottleLog.Tabs.Clear;
+  for i := 0 to FBottleLogList.Count - 1 do begin
+    tabBottleLog.Tabs.Add((FBottleLogList[i] as TBottleLogList).Title);
+  end;
+  tabBottleLog.TabIndex := FBottleLogList.IndexOf(cur);
+end;
+
+procedure TfrmLog.LogLoadFailure(Sender: TObject; const Message: String);
+begin
+  Beep;
+  ShowMessage(Message);
+  (Sender as TBottleLogList).AddSystemLog(Message);
+  lvwLog.Invalidate;
+end;
+
+procedure TfrmLog.AgreeLog(const MID: String; const Agree: integer);
+var i: integer;
+    flag: boolean;
+begin
+  flag := false;
+  for i := 0 to FBottleLogList.Count - 1 do begin
+    if (FBottleLogList[i] as TBottleLogList).Bottle(MID) <> nil then begin
+      (FBottleLogList[i] as TBottleLogList).Bottle(MID).Agrees := Agree;
+      flag := true;
+    end;
+  end;
+  if flag then lvwLog.Invalidate;
+end;
+
+procedure TfrmLog.VoteLog(const MID: String; const Vote: integer);
+var i: integer;
+    flag: boolean;
+begin
+  flag := false;
+  for i := 0 to FBottleLogList.Count - 1 do begin
+    if (FBottleLogList[i] as TBottleLogList).Bottle(MID) <> nil then begin
+      (FBottleLogList[i] as TBottleLogList).Bottle(MID).Votes := Vote;
+      flag := true;
+    end;
+  end;
+  if flag then lvwLog.Invalidate;
+end;
+
+procedure TfrmLog.tabBottleLogChanging(Sender: TObject;
+  var AllowChange: Boolean);
+begin
+  if lvwLog.Selected <> nil then
+    SelectedBottleLog.SelectedIndex := lvwLog.Selected.Index
+  else
+    SelectedBottleLog.SelectedIndex := -1;
+end;
+
+procedure TfrmLog.tabBottleLogContextPopup(Sender: TObject;
+  MousePos: TPoint; var Handled: Boolean);
+begin
+  with tabBottleLog do begin
+    Tag := IndexOfTabAt(MousePos.X, MousePos.Y);
+    if Tag < 0 then Handled := true;
+    mnCloseTab.Enabled := Tag > 0;
+  end;
+end;
+
+procedure TfrmLog.mnCloseTabClick(Sender: TObject);
+begin
+  if tabBottleLog.Tag = 0 then Exit;
+  FBottleLogList.Delete(tabBottleLog.Tag);
+  tabBottleLog.TabIndex := 0;
+  UpdateTab;
+  UpdateWindow;
+  lvwLogChange(Self, nil, ctState);
+end;
+
+procedure TfrmLog.tbtnFindBottleClick(Sender: TObject);
+var Query: String;
+    ResultLog: TBottleLogList;
+    Item1, Item2: TLogItem;
+    i, matched: integer;
+begin
+  if SelectedBottleLog.Count = 0 then begin
+    ShowMessage('\8c\9f\8dõ\91Î\8fÛ\82ª\8bó\82Å\82·\81B');
+    Exit;
+  end;
+  Query := '';
+  matched := 0;
+  if InputQuery('\83X\83N\83\8a\83v\83g\96{\95\82ð\8c\9f\8dõ', '\8c\9f\8dõ\95\8e\9a\97ñ', Query) then begin
+    if Query = '' then Exit;
+    ResultLog := TBottleLogList.Create('\8c\9f\8dõ\8c\8b\89Ê');
+    for i := 0 to SelectedBottleLog.Count-1 do begin
+      Item1 := SelectedBottleLog.Items[i] as TLogItem;
+      if AnsiContainsText(Item1.Script, Query) and (Item1.LogType = ltBottle) then begin
+        matched := matched + 1;
+        Item2 := TLogItem.Create(ltBottle, Item1.MID, Item1.Channel,
+          Item1.Script, Item1.Ghost, Item1.LogTime);
+        Item2.State := lsOpened;
+        Item2.Votes := Item1.Votes;
+        Item2.Agrees := Item1.Agrees;
+        ResultLog.Add(Item2);
+      end;
+    end;
+    if matched = 0 then
+      ResultLog.AddSystemLog('\8c©\82Â\82©\82è\82Ü\82¹\82ñ\82Å\82µ\82½');
+    BottleLogList.Add(ResultLog);
+    UpdateTab;
+    tabBottleLog.TabIndex := BottleLogList.Count-1;
+    UpdateWindow;
+  end;
+end;
+
+procedure TfrmLog.tbtnOpenLogClick(Sender: TObject);
+var BottleLog: TBottleLogList;
+    i: integer;
+begin
+  BottleLog := nil;
+  if OpenDialog.Execute then begin
+    try
+      for i := 0 to OpenDialog.Files.Count-1 do begin
+        BottleLog := TBottleLogList.Create(ExtractFileName(OpenDialog.Files[i]));
+        BottleLog.LoadFromXMLFile(OpenDialog.Files[i], XMLDocument);
+        BottleLogList.Add(BottleLog);
+      end;
+    except
+      on E: EXMLFileOpenException do begin
+        Beep;
+        ShowMessage(E.Message);
+        BottleLog.Free;
+      end;
+    end;
+    UpdateTab;
+    UpdateWindow;
+  end;
+end;
+
+function TfrmLog.GetDefaultFileName(const Name, Ext: String): String;
+begin
+  Result := StringReplace(Name, '/', '', [rfReplaceAll]);
+  Result := StringReplace(Result, ' ', '', [rfReplaceAll]);
+  Result := ChangeFileExt(Result, Ext);
+end;
+
 end.