OSDN Git Service

クリップ時に保存確認しないように修正。
[winbottle/winbottle.git] / bottleclient / LogForm.pas
index 50e51d8..bfcb5e8 100755 (executable)
@@ -5,12 +5,17 @@ interface
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   ComCtrls, ToolWin, StdCtrls, ExtCtrls, SsParser, BottleDef, Menus,
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   ComCtrls, ToolWin, StdCtrls, ExtCtrls, SsParser, BottleDef, Menus,
-  Clipbrd, Logs, ShellAPI, Commctrl, DirectSstp, Contnrs, xmldom, XMLIntf,
-  msxmldom, XMLDoc;
+  Clipbrd, Logs, ShellAPI, Commctrl, DirectSstp, Contnrs, StrUtils,
+  TalkShowFrame, SppList, HtmlOutputConfig, HtmlOutputProgress,
+  SearchLog, IniFiles, BRegExp, RegexUtils;
 
 type
 
 type
+  // \83\8d\83O\82Ì\95Û\91\95û\96@
   TSaveLogType = (stLog, stLogWithChannels, stText, stXML);
 
   TSaveLogType = (stLog, stLogWithChannels, stText, stXML);
 
+  // \83\8a\83X\83g\83r\83\85\81[\82Ì\83X\83N\83\8d\81[\83\8b\95û\8cü
+  TLVScrollDir = (lvScrollUp, lvScrollDown);
+
   TfrmLog = class(TForm)
     ToolBar: TToolBar;
     tbtnClear: TToolButton;
   TfrmLog = class(TForm)
     ToolBar: TToolBar;
     tbtnClear: TToolButton;
@@ -26,7 +31,6 @@ type
     SaveDialog: TSaveDialog;
     pnlPanel: TPanel;
     Splitter: TSplitter;
     SaveDialog: TSaveDialog;
     pnlPanel: TPanel;
     Splitter: TSplitter;
-    edtScript: TRichEdit;
     mnPopUpCopyScript: TMenuItem;
     PopupMenuSaveLog: TPopupMenu;
     mnSaveLog: TMenuItem;
     mnPopUpCopyScript: TMenuItem;
     PopupMenuSaveLog: TPopupMenu;
     mnSaveLog: TMenuItem;
@@ -50,9 +54,33 @@ type
     PopupMenuTab: TPopupMenu;
     mnCloseTab: TMenuItem;
     tbtnFindBottle: TToolButton;
     PopupMenuTab: TPopupMenu;
     mnCloseTab: TMenuItem;
     tbtnFindBottle: TToolButton;
-    XMLDocument: TXMLDocument;
     tbtnOpenLog: TToolButton;
     OpenDialog: TOpenDialog;
     tbtnOpenLog: TToolButton;
     OpenDialog: TOpenDialog;
+    tbtnInsertCue: TToolButton;
+    mnInsertCue: TMenuItem;
+    PopupMenuListPreviewStyle: TPopupMenu;
+    mnListPreviewStyleNormal: TMenuItem;
+    mnListPreviewStyleTagStripped: TMenuItem;
+    tbtnListPreviewStyle: TToolButton;
+    mnListPreviewStyleNoColor: TMenuItem;
+    SsParserForTalkShow: TSsParser;
+    mnPreviewStyleConversationImage: TMenuItem;
+    pnlPreviewArea: TPanel;
+    TalkShowFrame: TfrmTalkShow;
+    edtScript: TRichEdit;
+    tbtnSendEditor: TToolButton;
+    mnSendEditor: TMenuItem;
+    timScrollTimer: TTimer;
+    mnChangeTabName: TMenuItem;
+    N1: TMenuItem;
+    N2: TMenuItem;
+    mnDeleteLogItem: TMenuItem;
+    mnTabSaveXMLLog: TMenuItem;
+    mnSaveHTML: TMenuItem;
+    mnPopupCopyGhost: TMenuItem;
+    PopupMenuAction: TPopupMenu;
+    mnTestAction: TMenuItem;
+    mnSelAction: TMenuItem;
     procedure tbtnClearClick(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure lvwLogChange(Sender: TObject; Item: TListItem;
     procedure tbtnClearClick(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure lvwLogChange(Sender: TObject; Item: TListItem;
@@ -71,9 +99,6 @@ type
     procedure PopupMenuListViewPopup(Sender: TObject);
     procedure lvwLogCustomDrawItem(Sender: TCustomListView;
       Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
     procedure PopupMenuListViewPopup(Sender: TObject);
     procedure lvwLogCustomDrawItem(Sender: TCustomListView;
       Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
-    procedure lvwLogCustomDrawSubItem(Sender: TCustomListView;
-      Item: TListItem; SubItem: Integer; State: TCustomDrawState;
-      var DefaultDraw: Boolean);
     procedure PopupMenuPreviewStylePopup(Sender: TObject);
     procedure mnPreviewStyleClick(Sender: TObject);
     procedure tbtnPreviewStyleClick(Sender: TObject);
     procedure PopupMenuPreviewStylePopup(Sender: TObject);
     procedure mnPreviewStyleClick(Sender: TObject);
     procedure tbtnPreviewStyleClick(Sender: TObject);
@@ -90,6 +115,25 @@ type
     procedure tabBottleLogDragOver(Sender, Source: TObject; X, Y: Integer;
       State: TDragState; var Accept: Boolean);
     procedure tabBottleLogDragDrop(Sender, Source: TObject; X, Y: Integer);
     procedure tabBottleLogDragOver(Sender, Source: TObject; X, Y: Integer;
       State: TDragState; var Accept: Boolean);
     procedure tabBottleLogDragDrop(Sender, Source: TObject; X, Y: Integer);
+    procedure tabBottleLogEndDrag(Sender, Target: TObject; X, Y: Integer);
+    procedure lvwLogDrawItem(Sender: TCustomListView; Item: TListItem;
+      Rect: TRect; State: TOwnerDrawState);
+    procedure mnListPreviewStyleClick(Sender: TObject);
+    procedure tbtnListPreviewStyleClick(Sender: TObject);
+    procedure PopupMenuListPreviewStylePopup(Sender: TObject);
+    procedure lvwLogDragOver(Sender, Source: TObject; X, Y: Integer;
+      State: TDragState; var Accept: Boolean);
+    procedure lvwLogDragDrop(Sender, Source: TObject; X, Y: Integer);
+    procedure timScrollTimerTimer(Sender: TObject);
+    procedure mnChangeTabNameClick(Sender: TObject);
+    procedure lvwLogStartDrag(Sender: TObject;
+      var DragObject: TDragObject);
+    procedure lvwLogEndDrag(Sender, Target: TObject; X, Y: Integer);
+    procedure mnTabSaveXMLLogClick(Sender: TObject);
+    procedure mnSaveHTMLClick(Sender: TObject);
+    procedure mnPopupCopyGhostClick(Sender: TObject);
+    procedure mnTestActionClick(Sender: TObject);
+    procedure mnSelActionClick(Sender: TObject);
   private
     { Private \90é\8c¾ }
     FLastScript: String; //\83X\83N\83\8a\83v\83g\8dÄ\95`\89æ\97}\90§\97p
   private
     { Private \90é\8c¾ }
     FLastScript: String; //\83X\83N\83\8a\83v\83g\8dÄ\95`\89æ\97}\90§\97p
@@ -97,34 +141,64 @@ type
     //
     FDragTabIndex: integer; //\83^\83u\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
     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)
     //
     FDragTabIndex: integer; //\83^\83u\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
     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)
+    //
+    // \83\8a\83X\83g\83r\83\85\81[\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
+    FLVScrollDir: TLVScrollDir; // \83X\83N\83\8d\81[\83\8b\95û\8cü
+    FLVDragDest: integer;    //\83h\83\8d\83b\83v\82·\82é\88Ê\92u(\82·\82®\89º\82É\82­\82é\83A\83C\83e\83\80\82ÌIndex)
+    //
     procedure UpdateScript(const Script: String);
     procedure UpdateScriptConversationColor(const Script: String);
     procedure UpdateScript(const Script: String);
     procedure UpdateScriptConversationColor(const Script: String);
-    procedure UpdateScriptConversationNoColor(const Script: String);
     procedure UpdateScriptScript(const Script: String);
     procedure mnURLClick(Sender: TObject);
     procedure ExtractURLs(Script: String; Result: TStrings);
     function GetDefaultFileName(const Name: String; const Ext: String): String;
     function BottleLogTitled(const LogName: String): TBottleLogList;
     procedure UpdateScriptScript(const Script: String);
     procedure mnURLClick(Sender: TObject);
     procedure ExtractURLs(Script: String; Result: TStrings);
     function GetDefaultFileName(const Name: String; const Ext: String): String;
     function BottleLogTitled(const LogName: String): TBottleLogList;
+    procedure DrawSingleLineScript(LogItem: TLogItem; Rect: TRect;
+      Item: TListItem);
+    procedure PreviewStyleChange;
+    procedure DrawListViewDragBorder(const Rect: TRect);
+    function DoSaveLogXML(Log: TBottleLogList): integer;
+    procedure DoCloseTab(const Index: integer);
+    function DoSearchLog(Condition: TSearchCond): TBottleLogList;
+    procedure SearchLogIndivisual(Condition: TSearchCond;
+      LogList, Result: TBottleLogList; UntilIndex: integer = -1);
+    function CheckLogSave(const Index: integer): integer;
   protected
     procedure CreateParams(var Params: TCreateParams); override;
   public
     { Public \90é\8c¾ }
     function SelectedBottleLog: TBottleLogList;
     property BottleLogList: TObjectList read FBottleLogList;
   protected
     procedure CreateParams(var Params: TCreateParams); override;
   public
     { Public \90é\8c¾ }
     function SelectedBottleLog: TBottleLogList;
     property BottleLogList: TObjectList read FBottleLogList;
-    procedure AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String);
+    procedure AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String;
+      const LogTime: TDateTime; const Vote, Agree: integer);
     procedure AddCurrentSystemLog(const LogName, MessageString: String);
     procedure VoteLog(const MID: String; const Vote: integer);
     procedure AgreeLog(const MID: String; const Agree: integer);
     procedure AddCurrentSystemLog(const LogName, 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 SetBottleState(const MID: String; State: TLogState);
     procedure AllBottleOpened;
     procedure LogLoaded(Sender: TObject);
     procedure LogLoadFailure(Sender: TObject; const Message: String);
     procedure AllBottleOpened;
     procedure LogLoaded(Sender: TObject);
     procedure LogLoadFailure(Sender: TObject; const Message: String);
+    procedure LogLoadWork(Sender: TObject);
+    procedure HTMLOutputWork(Sender: TObject; const Count: integer;
+      var Canceled: boolean);
     procedure UpdateTab;
     procedure UpdateWindow;
     procedure SelAndFocusMessage(const MID: String);
     procedure UpdateTab;
     procedure UpdateWindow;
     procedure SelAndFocusMessage(const MID: String);
+    function CheckLog(Sender: TObject): integer;
   end;
 
   end;
 
+  TBottleLogDragObject = class(TDragControlObjectEx)
+  private
+    FBottleLogList: TBottleLogList;
+    FLogItem: TLogItem;
+    procedure SetBottleLogList(const Value: TBottleLogList);
+    procedure SetLogItem(const Value: TLogItem);
+  protected
+    function GetDragImages: TDragImageList; override;
+  public
+    property BottleLogList: TBottleLogList read FBottleLogList write SetBottleLogList;
+    property LogItem: TLogItem read FLogItem write SetLogItem;
+  end;
 
 var
   frmLog: TfrmLog;
 
 var
   frmLog: TfrmLog;
@@ -134,6 +208,7 @@ const
   IconOpened    = 30;
   IconPlaying   = 31;
   IconSystemLog = 26;
   IconOpened    = 30;
   IconPlaying   = 31;
   IconSystemLog = 26;
+  IconURL       = 43;
   SubChannel    = 0;
   SubGhost      = 1;
   SubVotes      = 2;
   SubChannel    = 0;
   SubGhost      = 1;
   SubVotes      = 2;
@@ -142,16 +217,18 @@ const
 
 implementation
 
 
 implementation
 
-uses MainForm, StrUtils;
+uses MainForm;
 
 {$R *.DFM}
 
 { TfrmLog }
 
 
 {$R *.DFM}
 
 { TfrmLog }
 
-procedure TfrmLog.AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String);
+procedure TfrmLog.AddCurrentScriptLog(const LogName, Script, Channel, MID, Ghost: String;
+  const LogTime: TDateTime; const Vote, Agree: integer);
 var Sel: integer;
 begin
 var Sel: integer;
 begin
-  BottleLogTitled(LogName).AddScriptLog(Script, Channel, MID, Ghost);
+  BottleLogTitled(LogName).AddScriptLog(Script, Channel, MID, Ghost, LogTime, Vote, Agree);
+  BottleLogTitled(LogName).LogModified := true; // \82±\82Ì\83\8a\83X\83g\82Í\95Ï\8dX\82³\82ê\82½
   if SelectedBottleLog <> BottleLogTitled(LogName) 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;
   if SelectedBottleLog <> BottleLogTitled(LogName) 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;
@@ -189,16 +266,13 @@ end;
 procedure TfrmLog.tbtnClearClick(Sender: TObject);
 begin
   if SelectedBottleLog = nil then Exit;
 procedure TfrmLog.tbtnClearClick(Sender: TObject);
 begin
   if SelectedBottleLog = nil then Exit;
-  FBottleLogList.Delete(tabBottleLog.TabIndex);
-  tabBottleLog.TabIndex := 0;
-  UpdateTab;
-  UpdateWindow;
-  lvwLogChange(Self, nil, ctState);
+  DoCloseTab(tabBottleLog.TabIndex);
 end;
 
 procedure TfrmLog.FormCreate(Sender: TObject);
 var i: integer;
 begin
 end;
 
 procedure TfrmLog.FormCreate(Sender: TObject);
 var i: integer;
 begin
+  FLVDragDest := -1; // \83\8a\83X\83g\83r\83\85\81[\82Ì\83h\83\89\83b\83O\92\86\82Å\82Í\82È\82¢
   FBottleLogList := TObjectList.Create;
 
   SsParser.TagPattern.Assign(frmSender.SsParser.TagPattern);
   FBottleLogList := TObjectList.Create;
 
   SsParser.TagPattern.Assign(frmSender.SsParser.TagPattern);
@@ -211,7 +285,7 @@ begin
     Self.Height := Bottom - Top + 1;
   end;
   lvwLog.DoubleBuffered := true;
     Self.Height := Bottom - Top + 1;
   end;
   lvwLog.DoubleBuffered := true;
-  edtScript.Height := Pref.LogWindowDividerPos;
+  pnlPreviewArea.Height := Pref.LogWindowDividerPos;
 
   i := 0;
   while Token(Pref.LogWindowColumnWidth, ',', i) <> '' do begin
 
   i := 0;
   while Token(Pref.LogWindowColumnWidth, ',', i) <> '' do begin
@@ -219,6 +293,18 @@ begin
     Inc(i);
   end;
 
     Inc(i);
   end;
 
+  SsParserForTalkShow.TagPattern.Assign(SsParser.TagPattern);
+  SsParserForTalkShow.MetaPattern.Assign(SsParser.MetaPattern);
+  SsParserForTalkShow.EscapeInvalidMeta := false;
+  SsParserForTalkShow.LeaveEscape := false;
+  TalkShowFrame.SsParser := self.SsParserForTalkShow;
+
+  TalkShowFrame.SetPreviewFont(edtScript.Font);
+  TalkShowFrame.PrevControl := lvwLog;
+
+  mnSelAction.Checked := Pref.LogAction; // \8f\89\8aú\89»
+
+  PreviewStyleChange;
   UpdateWindow; // Reset window color and enabled status of some buttons
 end;
 
   UpdateWindow; // Reset window color and enabled status of some buttons
 end;
 
@@ -239,79 +325,110 @@ begin
     Right  := Self.Left + Self.Width - 1;
     Bottom := Self.Top + Self.Height - 1;
   end;
     Right  := Self.Left + Self.Width - 1;
     Bottom := Self.Top + Self.Height - 1;
   end;
-  Pref.LogWindowDividerPos := edtScript.Height;
+  Pref.LogWindowDividerPos := pnlPreviewArea.Height;
 
   FreeAndNil(FBottleLogList);
 end;
 
 procedure TfrmLog.lvwLogChange(Sender: TObject; Item: TListItem;
   Change: TItemChange);
 
   FreeAndNil(FBottleLogList);
 end;
 
 procedure TfrmLog.lvwLogChange(Sender: TObject; Item: TListItem;
   Change: TItemChange);
-var Script: String;
+var Script, Text: String;
     Log: TLogItem;
     Log: TLogItem;
+    Selected, IsNormalBottle: boolean;
 begin
 begin
+  Selected := false;
+  IsNormalBottle := false;
   if SelectedBottleLog <> nil then begin
   if SelectedBottleLog <> nil then begin
-    StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
     if Change = ctState then begin
       Script := '';
       if lvwLog.Selected <> nil then begin
     if Change = ctState then begin
       Script := '';
       if lvwLog.Selected <> nil then begin
+        Selected := true;
+        StatusBar.Panels[0].Text := Format('%d/%d\8c\8f', [lvwLog.Selected.Index+1,
+          SelectedBottleLog.Count]);
         Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
         Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
-        if (Log.LogType = ltBottle) and not frmSender.Connecting then begin
+//        if (Log.LogType = ltBottle) and not frmSender.Connecting then begin
+        if Log.LogType = ltBottle then begin
+          IsNormalBottle := true;
           Script := Log.Script;
           Script := Log.Script;
-          frmSender.actVoteMessage.Enabled := true;
-          frmSender.actAgreeMessage.Enabled := true;
-          mnPopUpCopyScript.Enabled := true;
-          UpdateScript(Script);
+          Text := Format('%d\83o\83C\83g/%d\95b - \83_\83u\83\8b\83N\83\8a\83b\83N\82Å\8dÄ\90¶',
+            [Length(Log.Script), frmSender.SsPlayTime.PlayTime(Log.Script) div 1000]);
+          StatusBar.Panels[1].Text := Text;
+          if Pref.LogWindowPreviewStyle = psImageConversation then
+            TalkShowFrame.View(Log)
+          else
+            UpdateScript(Script);
         end else begin
         end else begin
-          frmSender.actVoteMessage.Enabled := false;
-          frmSender.actAgreeMessage.Enabled := false;
-          mnPopUpCopyScript.Enabled := false;
+          StatusBar.Panels[1].Text := '';
           UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\82ð\83N\83\8a\83A
         end;
       end else begin
           UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\82ð\83N\83\8a\83A
         end;
       end else begin
-        frmSender.actVoteMessage.Enabled := false;
-        frmSender.actAgreeMessage.Enabled := false;
-        mnPopUpCopyScript.Enabled := false;
+        StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
+        StatusBar.Panels[1].Text := '';
         UpdateScript(Script); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
       end;
     end;
     tbtnSaveLog.Enabled := lvwLog.Items.Count > 0;
   end else begin
         UpdateScript(Script); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
       end;
     end;
     tbtnSaveLog.Enabled := lvwLog.Items.Count > 0;
   end else begin
-    StatusBar.Panels[0].Text := '\83\8d\83O\82ª\82 \82è\82Ü\82¹\82ñ';
-    frmSender.actVoteMessage.Enabled := false;
-    frmSender.actAgreeMessage.Enabled := false;
-    mnPopUpCopyScript.Enabled := false;
+    StatusBar.Panels[0].Text := '';
     UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
   end;
     UpdateScript(''); // \83\8d\83O\83v\83\8c\83r\83\85\81[\95\94\83N\83\8a\83A
   end;
+  frmSender.actVoteMessage.Enabled := Selected and IsNormalBottle;
+  frmSender.actAgreeMessage.Enabled := Selected and IsNormalBottle;
+  frmSender.actSendToEditor.Enabled := Selected and IsNormalBottle;
+  frmSender.actInsertCue.Enabled := Selected;
+  frmSender.actDeleteLogItem.Enabled := Selected;
+  mnPopUpCopyScript.Enabled := Selected and IsNormalBottle;
+  mnPopupCopyGhost.Enabled := Selected and IsNormalBottle;
 end;
 
 procedure TfrmLog.lvwLogDblClick(Sender: TObject);
 end;
 
 procedure TfrmLog.lvwLogDblClick(Sender: TObject);
-var Script: String;
-    Opt: TScriptTransOptions;
-    SOpt: TSstpSendOptions;
-    Ghost: String;
-    Log: TLogItem;
+var Script, ErrorMes: String;
+    Log, CueItem: TLogItem;
+    Res: integer;
 begin
 begin
-  if lvwLog.Selected = nil then Exit;
-  //Log := TLogItem(lvwLog.Selected.Data);
+  if lvwLog.Selected = nil then
+    Exit;
   Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
   if Log = nil then Exit;
   Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
   if Log = nil then Exit;
-  if Log.LogType <> ltBottle then Exit;
-  Script := Log.Script;
-  Opt := [toConvertURL, toWaitScriptEnd];
-  if Pref.NoTransUrl then Opt := Opt + [toNoChoice];
-  if Pref.IgnoreFrequentYenS then Opt := Opt + [toIgnoreFrequentYenS];
-  if Pref.FixMessySurface then Opt := Opt + [toFixMessySurface];
-  frmSender.DoTrans(Script, Opt);
-
-  if ChannelList.Channel[Log.Channel] <> nil then
-    Ghost := ChannelList.Channel[Log.Channel].Ghost;
-  //\96Ú\95W\83S\81[\83X\83g\8c\88\92è
-  if Log.Ghost <> '' then Ghost := Log.Ghost;
-  //\83^\81[\83Q\83b\83g\83S\81[\83X\83g\8am\92è
-  Ghost := frmSender.SetHWndToFavoriteGhost(Ghost);
-  frmSender.DirectSstp.SstpSender := 'SSTP Bottle -\81y\83\8d\83O\8dÄ\90\81z';
-  if Pref.NoTranslate then SOpt := [soNoTranslate] else SOpt := [];
-  frmSender.DirectSstp.SstpSEND(Script, SOpt, frmSender.GhostNameToSetName(Ghost));
+  if Log.LogType <> ltBottle then
+    Exit;
+  //\92P\91Ì\83A\83N\83V\83\87\83\93\82ª\97L\8cø\82Å\82 \82ê\82Î\83X\83N\83\8a\83v\83g\82Ì\95Ï\8a·\82µ\82È\82¢
+  if Pref.LogAction then
+  begin
+    Script :=  Log.Script;
+    ErrorMes := '';
+  end else
+    Script := frmSender.ScriptTransForSSTP(Log.Script, ErrorMes);
+
+  if ErrorMes <> '' then
+  begin
+    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+
+        ErrorMes + #13#10 +
+        '\8b­\90§\93I\82É\8dÄ\90\82µ\82Ü\82·\82©?'#13#10,
+        mtWarning, mbOkCancel, 0
+      );
+    if Res = mrCancel then
+      Exit;
+  end;
+
+  CueItem := TLogItem.Create(Log);
+  try
+    //\83A\83N\83V\83\87\83\93\82ª\97L\8cø\82Å\82 \82ê\82Î\92Ê\8fí\8f\88\97\9d\8fÈ\97ª
+    if Pref.LogAction then
+      //\8c^\95Ï\8a·\82Æ\8eó\90M
+      frmSender.BottleCnv(CueItem)
+    else
+    begin
+      //\83`\83\83\83\93\83l\83\8b\83S\81[\83X\83g\91Î\8dô
+      if CueItem.Ghost = '' then
+        if ChannelList.Channel[CueItem.Channel] <> nil then
+          CueItem.Ghost := ChannelList.Channel[CueItem.Channel].Ghost;
+      CueItem.Script := Script;
+      frmSender.BottleSstp.Unshift(CueItem);
+    end;
+  except
+    CueItem.Free;
+  end;
 end;
 
 procedure TfrmLog.UpdateScriptConversationColor(const Script: String);
 end;
 
 procedure TfrmLog.UpdateScriptConversationColor(const Script: String);
@@ -323,7 +440,6 @@ begin
   frmSender.DoTrans(scr, [toConvertURL]);
   SsParser.LeaveEscape := false;
   SsParser.InputString := scr;
   frmSender.DoTrans(scr, [toConvertURL]);
   SsParser.LeaveEscape := false;
   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
   InSynchronized := false;
   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
   InSynchronized := false;
@@ -375,48 +491,6 @@ begin
   end;
 end;
 
   end;
 end;
 
-procedure TfrmLog.UpdateScriptConversationNoColor(const Script: String);
-var Scr: String;
-    i: integer;
-    UnyuTalking, Talked, LastUnyuTalked, InSynchronize, LastInSynchronize: boolean;
-begin
-  Scr := Script;
-  frmSender.DoTrans(Scr, [toConvertURL]);
-  SsParser.LeaveEscape := false;
-  SsParser.InputString := Scr;
-  SsParser.LeaveEscape := true;
-  edtScript.Text := '';
-  edtScript.Color := clWindow;
-  edtScript.DefAttributes.Color := clWindowText;
-  edtScript.SelAttributes.Color := clWindowText;
-  Talked := false;
-  UnyuTalking := false;
-  LastUnyuTalked := false;
-  InSynchronize := false;
-  LastInSynchronize := false;
-  for i := 0 to SsParser.Count-1 do begin
-    if SsParser[i] = '\u' then UnyuTalking := true;
-    if SsParser[i] = '\h' then UnyuTalking := false;
-    if SsParser[i] = '\_s' then InSynchronize := not InSynchronize;
-    if SsParser.MarkUpType[i] in [mtStr, mtMeta] then begin
-      if not Talked then begin // \83X\83N\83\8a\83v\83g\8dÅ\8f\89\82Ì\83Z\83\8a\83t
-        if InSynchronize then Scr := '\97¼\81F'
-        else if UnyuTalking then Scr := '\82¤:' else Scr := '\82³:';
-      end;
-      if Talked and ((UnyuTalking <> LastUnyuTalked) or (InSynchronize <> LastInSynchronize)) then begin
-        Scr := Scr + #13#10;
-        if InSynchronize then Scr := Scr + '\97¼\81F'
-        else if UnyuTalking then Scr := Scr + '\82¤:' else Scr := Scr + '\82³:';
-      end;
-      Scr := Scr + SsParser[i];
-      Talked := true;
-      LastInSynchronize := InSynchronize;
-      LastUnyuTalked := UnyuTalking;
-    end;
-  end;
-  edtScript.Text := Scr;
-end;
-
 procedure TfrmLog.lvwLogKeyPress(Sender: TObject; var Key: Char);
 begin
   if Key = #13 then lvwLogDblClick(Sender);
 procedure TfrmLog.lvwLogKeyPress(Sender: TObject; var Key: Char);
 begin
   if Key = #13 then lvwLogDblClick(Sender);
@@ -440,8 +514,12 @@ var SortType: TBottleLogSortType;
     SelectedMID: String;
     SortColumn: integer;
 begin
     SelectedMID: String;
     SortColumn: integer;
 begin
+  if SelectedBottleLog = nil then
+    Exit;
   if lvwLog.Selected <> nil then
   if lvwLog.Selected <> nil then
-    SelectedMID := SelectedBottleLog.Bottles[lvwLog.Selected.Index].MID;
+    SelectedMID := SelectedBottleLog.Bottles[lvwLog.Selected.Index].MID
+  else
+    SelectedMID := '';
 
   SortColumn := Column.Index;
   case SortColumn-1 of
 
   SortColumn := Column.Index;
   case SortColumn-1 of
@@ -451,12 +529,14 @@ begin
     subVotes:   SortType := stVote;
     subAgrees:  SortType := stAgree;
     subScript:  SortType := stScript;
     subVotes:   SortType := stVote;
     subAgrees:  SortType := stAgree;
     subScript:  SortType := stScript;
-  else SortType := stLogTime;
+  else
+    SortType := stLogTime;
   end;
 
   SelectedBottleLog.SortBottles(SortType);
   lvwLog.Invalidate;
   end;
 
   SelectedBottleLog.SortBottles(SortType);
   lvwLog.Invalidate;
-  SelAndFocusMessage(SelectedMID);
+  if Length(SelectedMID) > 0 then
+    SelAndFocusMessage(SelectedMID);
 end;
 
 
 end;
 
 
@@ -471,29 +551,14 @@ begin
   Clip.SetTextBuf(PChar(Log.Script));
 end;
 
   Clip.SetTextBuf(PChar(Log.Script));
 end;
 
-procedure TfrmLog.SetBottleStatusToOpened(const MID: String);
-var i: integer;
-    Bottle: TLogItem;
-begin
-  for i := 0 to FBottleLogList.Count-1 do begin
-    Bottle := (FBottleLogList[i] as TBottleLogList).Bottle(MID);
-    if Bottle <> nil then begin
-      Bottle.State := lsOpened;
-      lvwLog.OnChange := nil;
-      lvwLog.Invalidate;
-      lvwLog.OnChange := lvwLogChange;
-    end;
-  end;
-end;
-
-procedure TfrmLog.SetBottleStatusToPlaying(const MID: String);
+procedure TfrmLog.SetBottleState(const MID: String; State: TLogState);
 var i: integer;
     Bottle: TLogItem;
 begin
   for i := 0 to FBottleLogList.Count-1 do begin
     Bottle := (FBottleLogList[i] as TBottleLogList).Bottle(MID);
     if Bottle <> nil then begin
 var i: integer;
     Bottle: TLogItem;
 begin
   for i := 0 to FBottleLogList.Count-1 do begin
     Bottle := (FBottleLogList[i] as TBottleLogList).Bottle(MID);
     if Bottle <> nil then begin
-      Bottle.State := lsPlaying;
+      Bottle.State := State;
       lvwLog.OnChange := nil;
       lvwLog.Invalidate;
       lvwLog.OnChange := lvwLogChange;
       lvwLog.OnChange := nil;
       lvwLog.Invalidate;
       lvwLog.OnChange := lvwLogChange;
@@ -505,7 +570,7 @@ procedure TfrmLog.mnSaveLogClick(Sender: TObject);
 begin
   if SelectedBottleLog = nil then Exit;
   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
 begin
   if SelectedBottleLog = nil then Exit;
   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
-  SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
+  SaveDialog.InitialDir := Pref.LogDir;
   SaveDialog.DefaultExt := 'log';
   SaveDialog.FilterIndex := 1;
   if SaveDialog.Execute then
   SaveDialog.DefaultExt := 'log';
   SaveDialog.FilterIndex := 1;
   if SaveDialog.Execute then
@@ -516,7 +581,7 @@ procedure TfrmLog.mnSaveLogChannelClick(Sender: TObject);
 begin
   if SelectedBottleLog = nil then Exit;
   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
 begin
   if SelectedBottleLog = nil then Exit;
   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.log');
-  SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
+  SaveDialog.InitialDir := Pref.LogDir;
   SaveDialog.DefaultExt := 'log';
   SaveDialog.FilterIndex := 1;
   if SaveDialog.Execute then
   SaveDialog.DefaultExt := 'log';
   SaveDialog.FilterIndex := 1;
   if SaveDialog.Execute then
@@ -527,7 +592,7 @@ procedure TfrmLog.mnSaveLogScriptClick(Sender: TObject);
 begin
   if SelectedBottleLog = nil then Exit;
   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.txt');
 begin
   if SelectedBottleLog = nil then Exit;
   SaveDialog.FileName := GetDefaultFileName(SelectedBottleLog.Title, '.txt');
-  SaveDialog.InitialDir := ExtractFileDir(Application.ExeName);
+  SaveDialog.InitialDir := Pref.LogDir;
   SaveDialog.DefaultExt := 'txt';
   SaveDialog.FilterIndex := 2;
   if SaveDialog.Execute then
   SaveDialog.DefaultExt := 'txt';
   SaveDialog.FilterIndex := 2;
   if SaveDialog.Execute then
@@ -537,12 +602,7 @@ end;
 procedure TfrmLog.mnSaveLogXMLClick(Sender: TObject);
 begin
   if SelectedBottleLog = nil then Exit;
 procedure TfrmLog.mnSaveLogXMLClick(Sender: TObject);
 begin
   if SelectedBottleLog = nil then Exit;
-  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);
+  DoSaveLogXML(SelectedBottleLog);
 end;
 
 procedure TfrmLog.lvwLogData(Sender: TObject; Item: TListItem);
 end;
 
 procedure TfrmLog.lvwLogData(Sender: TObject; Item: TListItem);
@@ -587,18 +647,16 @@ end;
 procedure TfrmLog.UpdateWindow;
 var EnabledFlag: boolean;
 begin
 procedure TfrmLog.UpdateWindow;
 var EnabledFlag: boolean;
 begin
-  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;
-  end else begin
-    if lvwLog.Color <> clWindow then lvwLog.Color := clWindow;
-    if lvwLog.Font.Color <> clWindowText then lvwLog.Font.Color := clWindowText;
-  end;
+  lvwLog.Color := Pref.BgColor;
+  lvwLog.Font.Color := Pref.TextColor;
   if SelectedBottleLog <> nil then begin
   if SelectedBottleLog <> nil then begin
+    Caption := '\83\8d\83O - ' + SelectedBottleLog.Title;
     StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
     lvwLog.Items.Count := SelectedBottleLog.Count;
   end else begin
     StatusBar.Panels[0].Text := IntToStr(SelectedBottleLog.Count) + '\8c\8f';
     lvwLog.Items.Count := SelectedBottleLog.Count;
   end else begin
-    StatusBar.Panels[0].Text := '\83\8d\83O\82ª\82 \82è\82Ü\82¹\82ñ';
+    Caption := '\83\8d\83O';
+    StatusBar.Panels[0].Text := '';
+    StatusBar.Panels[1].Text := '';
     lvwLog.Items.Count := 0;
   end;
 
     lvwLog.Items.Count := 0;
   end;
 
@@ -623,14 +681,14 @@ begin
   if lvwLog.Selected = nil then Exit;
   Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
   if Log = nil then Exit;
   if lvwLog.Selected = nil then Exit;
   Log := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
   if Log = nil then Exit;
-  Urls := nil;
+  Urls := TStringList.Create;
   try
   try
-    Urls := TStringList.Create;
     ExtractURLs(Log.Script, Urls);
     for i := 0 to Urls.Count-1 do begin
       Child := TMenuItem.Create(Self);
       with Child do begin
     ExtractURLs(Log.Script, Urls);
     for i := 0 to Urls.Count-1 do begin
       Child := TMenuItem.Create(Self);
       with Child do begin
-        Caption := Format('(&%d) %s', [i+1, Urls[i]]);
+        Caption := Format('(&%d) %s', [i+1, StringReplace(Urls[i], '&', '&&', [rfReplaceAll])]);
+        Tag := i;
         OnClick := mnURLClick;
         AutoHotkeys := maManual;
         mnJumpURL.Add(Child);
         OnClick := mnURLClick;
         AutoHotkeys := maManual;
         mnJumpURL.Add(Child);
@@ -643,11 +701,20 @@ begin
 end;
 
 procedure TfrmLog.mnURLClick(Sender: TObject);
 end;
 
 procedure TfrmLog.mnURLClick(Sender: TObject);
-var URL: String;
+var LogItem: TLogItem;
+    URL: string;
+    Urls: TStringList;
 begin
 begin
-  URL := (Sender as TMenuItem).Caption;
-  RegExp.Subst('s/^\(&?\d\) //', URL);
-  ShellExecute(Handle, 'open', PChar(URL), nil, nil, SW_SHOW);
+  if (lvwLog.Selected = nil) or (SelectedBottleLog = nil) then Exit;
+  LogItem := SelectedBottleLog[lvwLog.Selected.Index] as TLogItem;
+  Urls := TStringList.Create;
+  try
+    ExtractURLs(LogItem.Script, Urls);
+    URL := Urls[(Sender as TMenuItem).Tag];
+    frmSender.OpenBrowser(URL);
+  finally
+    Urls.Free;
+  end;
 end;
 
 procedure TfrmLog.ExtractURLs(Script: String; Result: TStrings);
 end;
 
 procedure TfrmLog.ExtractURLs(Script: String; Result: TStrings);
@@ -655,11 +722,12 @@ var i, u, j: integer;
     s: String;
 begin
   Result.Clear;
     s: String;
 begin
   Result.Clear;
-  SsParser.LeaveEscape := false;
   SsParser.InputString := Script;
   SsParser.LeaveEscape := true;
   for i := 0 to SsParser.Count-1 do begin
   SsParser.InputString := Script;
   SsParser.LeaveEscape := true;
   for i := 0 to SsParser.Count-1 do begin
-    if (SsParser.Match(SsParser[i], '\URL%b') > 0) then begin
+    if (SsParser.Match(SsParser[i], '\URL%b') > 0)
+    and (SsParser.MarkUpType[i] = mtTag) then
+    begin
       for u := 7 downto 1 do begin
         if (SsParser.Match(SsParser[i],
             '\URL%b'+StringReplace(StringOfChar('-', u*2),
       for u := 7 downto 1 do begin
         if (SsParser.Match(SsParser[i],
             '\URL%b'+StringReplace(StringOfChar('-', u*2),
@@ -699,66 +767,11 @@ begin
   //
 end;
 
   //
 end;
 
-procedure TfrmLog.lvwLogCustomDrawSubItem(Sender: TCustomListView;
-  Item: TListItem; SubItem: Integer; State: TCustomDrawState;
-  var DefaultDraw: Boolean);
-{var
-  DestRect: TRect;
-  Script: String;
-  i, x, w: integer;
-  SavedDC: integer;
-  Mark: TSsMarkUpType;}
-begin
-  Exit // !!
-  {if (SubItem <> SubScript+1) or (not Pref.ColorScript) then Exit; // DefaultDraw = true
-  // Custom Script Coloring
-  DefaultDraw := false;
-  SavedDC := SaveDC(lvwLog.Canvas.Handle);
-  try
-    ListView_GetSubItemRect(lvwLog.Handle, Item.Index, SubScript+1, LVIR_BOUNDS, @DestRect);
-
-    lvwLog.Canvas.Brush.Style := bsSolid;
-    if cdsSelected in State then begin
-      lvwLog.Canvas.Brush.Color := clHighlight
-    end else begin
-      lvwLog.Canvas.Brush.Color := Pref.BgColor;
-    end;
-    lvwLog.Canvas.FillRect(DestRect);
-    lvwLog.Canvas.Brush.Style := bsClear;
-
-    Script := Item.SubItems[SubScript];
-    // DrawTextEx(lvwLog.Canvas.Handle, PChar(Script), -1, DestRect, DT_END_ELLIPSIS, nil);
-    SsParser.InputString := Script;
-    x := 6;
-    for i := 0 to SsParser.Count - 1 do begin
-      Mark := SsParser.MarkUpType[i];
-      case Mark of
-        mtMeta:   lvwLog.Canvas.Font.Color := Pref.MetaWordColor;
-        mtTag:    lvwLog.Canvas.Font.Color := Pref.MarkUpColor;
-        mtTagErr: lvwLog.Canvas.Font.Color := Pref.MarkErrorColor;
-        else begin
-          lvwLog.Canvas.Font.Color := Pref.TalkColorH;
-        end;
-      end;
-      w := lvwLog.Canvas.TextWidth(SsParser[i]);
-      lvwLog.Canvas.TextRect(DestRect, DestRect.Left + x, DestRect.Top + 2, SsParser[i]);
-      x := x + w;
-      if DestRect.Right - DestRect.Left < x then Break;
-    end;
-  finally
-    RestoreDC(lvwLog.Canvas.Handle, SavedDC);
-  end;}
-end;
-
 procedure TfrmLog.UpdateScript(const Script: String);
 begin
   if Script <> FLastScript then begin
     if Pref.LogWindowPreviewStyle = psConversation then begin
 procedure TfrmLog.UpdateScript(const Script: String);
 begin
   if Script <> FLastScript then begin
     if Pref.LogWindowPreviewStyle = psConversation then begin
-      if Pref.ColorScript then begin
-        UpdateScriptConversationColor(Script);
-      end else begin
-        UpdateScriptConversationNoColor(Script);
-      end;
+      UpdateScriptConversationColor(Script);
     end else begin
       UpdateScriptScript(Script);
     end;
     end else begin
       UpdateScriptScript(Script);
     end;
@@ -782,6 +795,7 @@ begin
     for i := 0 to Items.Count-1 do
       Items[i].Checked := (Sender as TMenuItem).Tag = Items[i].Tag;
   Pref.LogWindowPreviewStyle := TLogWindowPreviewStyle((Sender as TMenuItem).Tag);
     for i := 0 to Items.Count-1 do
       Items[i].Checked := (Sender as TMenuItem).Tag = Items[i].Tag;
   Pref.LogWindowPreviewStyle := TLogWindowPreviewStyle((Sender as TMenuItem).Tag);
+  PreviewStyleChange;
   FLastScript := '';
   lvwLogChange(self, lvwLog.Selected, ctState);
 end;
   FLastScript := '';
   lvwLogChange(self, lvwLog.Selected, ctState);
 end;
@@ -791,13 +805,7 @@ var
   UnyuTalking, InSynchronized: boolean;
   i: integer;
 begin
   UnyuTalking, InSynchronized: boolean;
   i: integer;
 begin
-  if Pref.ColorScript then begin
-    edtScript.Color := Pref.BgColor;
-  end else begin
-    edtScript.Color := clWindow;
-    edtScript.DefAttributes.Color := clWindowText;
-    edtScript.SelAttributes.Color := clWindowText;
-  end;
+  edtScript.Color := Pref.BgColor;
   SsParser.LeaveEscape := true;
   SsParser.InputString := Script;
   edtScript.Text := '';
   SsParser.LeaveEscape := true;
   SsParser.InputString := Script;
   edtScript.Text := '';
@@ -805,28 +813,26 @@ begin
   UnyuTalking := false;
   InSynchronized := false;
   for i := 0 to SsParser.Count-1 do begin
   UnyuTalking := false;
   InSynchronized := false;
   for i := 0 to SsParser.Count-1 do begin
-    if Pref.ColorScript then begin
-      case SsParser.MarkUpType[i] of
-        mtStr: begin
-          if InSynchronized then
-            edtScript.SelAttributes.Color := Pref.TalkColorS
-          else if UnyuTalking then
-            edtScript.SelAttributes.Color := Pref.TalkColorU
-          else
-            edtScript.SelAttributes.Color := Pref.TalkColorH;
-        end;
-        mtTag: begin
-          edtScript.SelAttributes.Color := Pref.MarkUpColor;
-          if SsParser[i] = '\h' then
-            UnyuTalking := false
-          else if SsParser[i] = '\u' then
-            UnyuTalking := true
-          else if SsParser[i] = '\_s' then
-            InSynchronized := not InSynchronized;
-        end;
-        mtMeta:   edtScript.SelAttributes.Color := Pref.MetaWordColor;
-        mtTagErr: edtScript.SelAttributes.Color := Pref.MarkErrorColor;
+    case SsParser.MarkUpType[i] of
+      mtStr: begin
+        if InSynchronized then
+          edtScript.SelAttributes.Color := Pref.TalkColorS
+        else if UnyuTalking then
+          edtScript.SelAttributes.Color := Pref.TalkColorU
+        else
+          edtScript.SelAttributes.Color := Pref.TalkColorH;
       end;
       end;
+      mtTag: begin
+        edtScript.SelAttributes.Color := Pref.MarkUpColor;
+        if SsParser[i] = '\h' then
+          UnyuTalking := false
+        else if SsParser[i] = '\u' then
+          UnyuTalking := true
+        else if SsParser[i] = '\_s' then
+          InSynchronized := not InSynchronized;
+      end;
+      mtMeta:   edtScript.SelAttributes.Color := Pref.MetaWordColor;
+      mtTagErr: edtScript.SelAttributes.Color := Pref.MarkErrorColor;
     end;
     edtScript.SelText := SsParser[i];
     if (SsParser[i] = '\n') and (Pref.LogWindowPreviewStyle = psScriptWithLineBreak) then
     end;
     edtScript.SelText := SsParser[i];
     if (SsParser[i] = '\n') and (Pref.LogWindowPreviewStyle = psScriptWithLineBreak) then
@@ -842,6 +848,7 @@ begin
   if sel > Ord(High(TLogWindowPreviewStyle)) then sel := 0;
   Pref.LogWindowPreviewStyle := TLogWindowPreviewStyle(sel);
   FLastScript := '';
   if sel > Ord(High(TLogWindowPreviewStyle)) then sel := 0;
   Pref.LogWindowPreviewStyle := TLogWindowPreviewStyle(sel);
   FLastScript := '';
+  PreviewStyleChange;
   lvwLogChange(self, lvwLog.Selected, ctState);
 end;
 
   lvwLogChange(self, lvwLog.Selected, ctState);
 end;
 
@@ -855,11 +862,15 @@ end;
 
 procedure TfrmLog.tabBottleLogChange(Sender: TObject);
 begin
 
 procedure TfrmLog.tabBottleLogChange(Sender: TObject);
 begin
+  // StatusBar\82Ì\8c\8f\90\94\95\\8e¦\82âListView.Items.Count\82ð\8dX\90V\82·\82é
   UpdateWindow;
   UpdateWindow;
-  if SelectedBottleLog.SelectedIndex >= 0 then begin
-    lvwLog.Items[SelectedBottleLog.SelectedIndex].Selected := true;
-    if lvwLog.Focused then lvwLog.Selected.Focused := true;
-  end;
+  // \83A\83C\83e\83\80\82Ì\91I\91ð\8fó\91Ô\82ð\95\9c\8bA\82·\82é
+  with SelectedBottleLog do
+    if (SelectedIndex >= 0) and (Count > SelectedIndex) then
+    begin
+      lvwLog.Items[SelectedIndex].Selected := true;
+      if lvwLog.Focused then lvwLog.Selected.Focused := true;
+    end;
   lvwLogChange(Self, nil, ctState);
 end;
 
   lvwLogChange(Self, nil, ctState);
 end;
 
@@ -890,8 +901,7 @@ procedure TfrmLog.LogLoadFailure(Sender: TObject; const Message: String);
 begin
   Beep;
   ShowMessage(Message);
 begin
   Beep;
   ShowMessage(Message);
-  (Sender as TBottleLogList).AddSystemLog(Message);
-  lvwLog.Invalidate;
+  if Sender = SelectedBottleLog then UpdateWindow;
 end;
 
 procedure TfrmLog.AgreeLog(const MID: String; const Agree: integer);
 end;
 
 procedure TfrmLog.AgreeLog(const MID: String; const Agree: integer);
@@ -944,46 +954,59 @@ end;
 
 procedure TfrmLog.mnCloseTabClick(Sender: TObject);
 begin
 
 procedure TfrmLog.mnCloseTabClick(Sender: TObject);
 begin
-  FBottleLogList.Delete(tabBottleLog.Tag);
-  UpdateTab;
-  UpdateWindow;
-  lvwLogChange(Self, nil, ctState);
+  DoCloseTab(tabBottleLog.Tag);
 end;
 
 procedure TfrmLog.tbtnFindBottleClick(Sender: TObject);
 end;
 
 procedure TfrmLog.tbtnFindBottleClick(Sender: TObject);
-var Query: String;
-    ResultLog: TBottleLogList;
-    Item1, Item2: TLogItem;
-    i, matched: integer;
+var ResultLog: TBottleLogList;
+    Cond: TSearchCond;
+    i: integer;
+    CList, GList: THashedStringList;
 begin
 begin
-  if SelectedBottleLog = nil then Exit;
-  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);
+  Application.CreateForm(TfrmSearchLog, frmSearchLog);
+  Cond := TSearchCond.Create(nil);
+  try
+    try
+      with frmSearchLog do
+      begin
+        // \8c»\8dÝ\83\8d\83O\82É\82 \82é\83S\81[\83X\83g\82Æ\83`\83\83\83\93\83l\83\8b\82Ì\83\8a\83X\83g\82ð\8eæ\93¾
+        // \8fd\82½\82¢\82©\82à??
+        CList := THashedStringList.Create;
+        GList := THashedStringList.Create;
+        try
+          for i := 0 to BottleLogList.Count-1 do
+          begin
+            with BottleLogList[i] as TBottleLogList do
+            begin
+              ExtractUniqueChannels(CList);
+              ExtractUniqueGhosts(GList);
+            end;
+          end;
+          CList.Sort;
+          GList.Sort;
+          ChannelList := CList;
+          GhostList   := GList;
+        finally
+          CList.Free;
+          GList.Free;
+        end;
+        if not Execute then
+          Exit
+        else
+          Cond.Assign(Condition);
       end;
       end;
+    finally
+      frmSearchLog.Release;
     end;
     end;
-    if matched = 0 then
-      ResultLog.AddSystemLog('\8c©\82Â\82©\82è\82Ü\82¹\82ñ\82Å\82µ\82½');
+    // \8c\9f\8dõ\8eÀ\8ds
+    ResultLog := DoSearchLog(Cond);
+    // \90V\83^\83u\82ð\8dì\90¬\82µ\82Ä\89æ\96Ê\8dX\90V
     BottleLogList.Add(ResultLog);
     UpdateTab;
     tabBottleLog.TabIndex := BottleLogList.Count-1;
     UpdateWindow;
     BottleLogList.Add(ResultLog);
     UpdateTab;
     tabBottleLog.TabIndex := BottleLogList.Count-1;
     UpdateWindow;
+  finally
+    Cond.Free;
   end;
 end;
 
   end;
 end;
 
@@ -992,19 +1015,22 @@ var BottleLog: TBottleLogList;
     i, Index: integer;
 begin
   Index := -1;
     i, Index: integer;
 begin
   Index := -1;
+  OpenDialog.InitialDir := Pref.LogDir;
   if OpenDialog.Execute then begin
     for i := 0 to OpenDialog.Files.Count-1 do begin
       BottleLog := TBottleLogList.Create(ExtractFileName(OpenDialog.Files[i]));
       try
   if OpenDialog.Execute then begin
     for i := 0 to OpenDialog.Files.Count-1 do begin
       BottleLog := TBottleLogList.Create(ExtractFileName(OpenDialog.Files[i]));
       try
-        BottleLog.LoadFromXMLFile(OpenDialog.Files[i], XMLDocument);
-      except
-        on E: EXMLFileOpenException do begin
-          Beep;
-          ShowMessage(E.Message);
-          FreeAndNil(BottleLog);
+        with BottleLog do
+        begin
+          OnLoaded := LogLoaded;
+          OnLoadFailure := LogLoadFailure;
+          OnLoadWork := LogLoadWork;
+          BottleLog.LoadFromXMLFile(OpenDialog.Files[i]);
         end;
         end;
+        Index := BottleLogList.Add(BottleLog); // \8dÅ\8cã\82É\8aJ\82¢\82½\83\8d\83O\82Ì\88Ê\92u\82ð\8bL\89¯
+      except
+        BottleLog.Free;
       end;
       end;
-      if BottleLog <> nil then Index := BottleLogList.Add(BottleLog); // \8dÅ\8cã\82É\8aJ\82¢\82½\83\8d\83O\82Ì\88Ê\92u\82ð\8bL\89¯
     end;
     UpdateTab;
     if Index >= 0 then tabBottleLog.TabIndex := Index;
     end;
     UpdateTab;
     if Index >= 0 then tabBottleLog.TabIndex := Index;
@@ -1016,6 +1042,7 @@ function TfrmLog.GetDefaultFileName(const Name, Ext: String): String;
 begin
   Result := StringReplace(Name, '/', '', [rfReplaceAll]);
   Result := StringReplace(Result, ' ', '', [rfReplaceAll]);
 begin
   Result := StringReplace(Name, '/', '', [rfReplaceAll]);
   Result := StringReplace(Result, ' ', '', [rfReplaceAll]);
+  Result := SafeFileName(Result);
   Result := ChangeFileExt(Result, Ext);
 end;
 
   Result := ChangeFileExt(Result, Ext);
 end;
 
@@ -1051,13 +1078,20 @@ procedure TfrmLog.tabBottleLogMouseDown(Sender: TObject;
   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
 var Index: integer;
 begin
   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
 var Index: integer;
 begin
-  with tabBottleLog do begin
-    Index := IndexOfTabAt(X, Y);
-    if Index = -1 then Exit; //\83^\83u\82ª\82È\82¢\82Ì\82Å\83h\83\89\83b\83O\82Å\82«\82È\82¢
-    if Button = mbLeft then begin
-      FDragTabIndex := Index; //\83h\83\89\83b\83O\82·\82é\83^\83u\82Ì\83C\83\93\83f\83b\83N\83X\82ð\95Û\91
-      BeginDrag(False);
-      FDragTabDest := -1;     //\83h\83\89\83b\83O\98g\90ü\95`\89æ\83t\83\89\83O\83N\83\8a\83A\82Ì\82½\82ß
+  if Button = mbMiddle then
+  begin
+    //\92\86\83{\83^\83\93\83N\83\8a\83b\83N\82Å\83^\83u\8dí\8f\9c
+    DoCloseTab(tabBottleLog.IndexOfTabAt(X, Y));
+  end else
+  begin
+    with tabBottleLog do begin
+      Index := IndexOfTabAt(X, Y);
+      if Index = -1 then Exit; //\83^\83u\82ª\82È\82¢\82Ì\82Å\83h\83\89\83b\83O\82Å\82«\82È\82¢
+      if Button = mbLeft then begin
+        FDragTabIndex := Index; //\83h\83\89\83b\83O\82·\82é\83^\83u\82Ì\83C\83\93\83f\83b\83N\83X\82ð\95Û\91
+        BeginDrag(False);
+        FDragTabDest := -1;     //\83h\83\89\83b\83O\98g\90ü\95`\89æ\83t\83\89\83O\83N\83\8a\83A\82Ì\82½\82ß
+      end;
     end;
   end;
 end;
     end;
   end;
 end;
@@ -1065,37 +1099,58 @@ end;
 procedure TfrmLog.tabBottleLogDragOver(Sender, Source: TObject; X,
   Y: Integer; State: TDragState; var Accept: Boolean);
 var TargetRect: TRect;
 procedure TfrmLog.tabBottleLogDragOver(Sender, Source: TObject; X,
   Y: Integer; State: TDragState; var Accept: Boolean);
 var TargetRect: TRect;
-    OldDest: integer;
+    OldDest, Index: integer;
+    dummy: boolean;
 begin
 begin
-  Accept := Source = tabBottleLog;
-  with tabBottleLog do begin
-    OldDest := FDragTabDest;
-    FDragTabDest := IndexOfTabAt(X, Y);
-    if FDragTabDest = -1 then begin
-      Accept := false; //\82±\82Ì\8fê\8d\87\82Í\83h\83\8d\83b\83v\82ð\94F\82ß\82È\82¢
-      Exit;
-    end;
-    with Canvas do begin
-      Pen.Mode := pmNot;
-      Pen.Width := 3;
-    end;
-    if (OldDest <> FDragTabDest) and (OldDest >= 0) then begin
-      //\88È\91O\82Ì\98g\90ü\8fÁ\8b\8e
-      TargetRect := TabRect(OldDest);
-      with Canvas do begin
-        Brush.Style := bsClear;
-        Rectangle(TargetRect.Left, TargetRect.Top,
-                  TargetRect.Right, TargetRect.Bottom);
+  // \83^\83u\82Ì\83h\83\89\83b\83O(\83^\83u\82Ì\8f\87\94Ô\93ü\82ê\91Ö\82¦)\82Ü\82½\82Í\81A
+  // \83\8d\83O\83A\83C\83e\83\80\82Ì\83h\83\89\83b\83O(\83\8d\83O\82ð\95Ê\82Ì\83^\83u\82É\88Ú\93®)\82Ì
+  // \97¼\95û\82Ì\83h\83\89\83b\83O\82ð\8eó\82¯\95t\82¯\82é
+  Accept := false;
+  if Source = tabBottleLog then
+  begin
+    // \83^\83u\82Ì\8f\87\94Ô\93ü\82ê\91Ö\82¦\82Ì\8fê\8d\87
+    Accept := true;
+    with tabBottleLog do begin
+      OldDest := FDragTabDest;
+      FDragTabDest := IndexOfTabAt(X, Y);
+      if FDragTabDest = -1 then begin
+        Accept := false; //\82±\82Ì\8fê\8d\87\82Í\83h\83\8d\83b\83v\82ð\94F\82ß\82È\82¢
+        Exit;
       end;
       end;
-    end;
-    if (OldDest <> FDragTabDest) then begin
-      //\90V\82µ\82¢\98g\90ü\95`\89æ
-      TargetRect := TabRect(FDragTabDest);
       with Canvas do begin
       with Canvas do begin
-        Brush.Style := bsClear;
-        Rectangle(TargetRect.Left, TargetRect.Top,
-                  TargetRect.Right, TargetRect.Bottom);
+        Pen.Mode := pmNot;
+        Pen.Width := 3;
+      end;
+      if (OldDest <> FDragTabDest) and (OldDest >= 0) then begin
+        //\88È\91O\82Ì\98g\90ü\8fÁ\8b\8e
+        TargetRect := TabRect(OldDest);
+        with Canvas do begin
+          Brush.Style := bsClear;
+          Rectangle(TargetRect.Left, TargetRect.Top,
+                    TargetRect.Right, TargetRect.Bottom);
+        end;
       end;
       end;
+      if (OldDest <> FDragTabDest) then begin
+        //\90V\82µ\82¢\98g\90ü\95`\89æ
+        TargetRect := TabRect(FDragTabDest);
+        with Canvas do begin
+          Brush.Style := bsClear;
+          Rectangle(TargetRect.Left, TargetRect.Top,
+                    TargetRect.Right, TargetRect.Bottom);
+        end;
+      end;
+    end;
+  end else if Source is TBottleLogDragObject then
+  begin
+    // \83\8d\83O\8d\80\96Ú\82Ì\83h\83\89\83b\83O(\83\8d\83O\82ð\95Ê\82Ì\83^\83u\82É\88Ú\93®\82·\82é)\82Ì\8fê\8d\87
+    Index := tabBottleLog.IndexOfTabAt(X, Y);
+    if tabBottleLog.TabIndex <> Index then
+    begin
+      FLVDragDest := -1; // \98g\90ü\82Í\82Ü\82¾\95\\8e¦\82³\82ê\82È\82¢\82Í\82¸
+      // \83^\83u\82ð\90Ø\91Ö\82¦\82é
+      tabBottleLogChanging(Self, dummy);
+      tabBottleLog.TabIndex := Index;
+      UpdateWindow;
     end;
   end;
 end;
     end;
   end;
 end;
@@ -1111,4 +1166,734 @@ begin
   end;
 end;
 
   end;
 end;
 
+procedure TfrmLog.tabBottleLogEndDrag(Sender, Target: TObject; X,
+  Y: Integer);
+begin
+  //\8b­\90§\93I\82É\83^\83u\82ð\8dÄ\95`\89æ\82³\82¹\82é\81B\98g\90ü\8fÁ\82µ\91Î\8dô
+  tabBottleLog.Tabs.BeginUpdate;
+  tabBottleLog.Tabs.EndUpdate;
+end;
+
+procedure TfrmLog.LogLoadWork(Sender: TObject);
+begin
+  if Sender = SelectedBottleLog then
+  begin
+    lvwLog.Invalidate;
+    lvwLog.Items.Count := SelectedBottleLog.Count;
+  end;
+end;
+
+procedure TfrmLog.lvwLogDrawItem(Sender: TCustomListView; Item: TListItem;
+  Rect: TRect; State: TOwnerDrawState);
+var
+  DestRect: TRect;
+  Script: String;
+  Ico: TIcon;
+  sub, Ex: integer;
+  Bottle: TLogItem;
+  DummyStr: TStringList;
+begin
+  Bottle := SelectedBottleLog.Bottles[Item.Index];
+  if Bottle.HasURL = huUndefined then
+  begin
+    DummyStr := TStringList.Create;
+    try
+      ExtractURLs(Bottle.Script, DummyStr);
+      if DummyStr.Count > 0 then
+        Bottle.HasURL := huYes
+      else
+        Bottle.HasURL := huNo;
+    finally
+      DummyStr.Free;
+    end;
+  end;
+
+  // \94w\8ci\8fÁ\8b\8e
+  ListView_GetItemRect(lvwLog.Handle, Item.Index, DestRect, LVIR_BOUNDS);
+
+  // \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è
+  lvwLog.Canvas.Brush.Style := bsSolid;
+  if Item.Selected then begin
+    if lvwLog.Focused then
+      lvwLog.Canvas.Brush.Color := clHighlight
+    else
+      lvwLog.Canvas.Brush.Color := clBtnFace;
+  end else begin
+    lvwLog.Canvas.Brush.Color := Pref.BgColor;
+  end;
+  lvwLog.Canvas.FillRect(DestRect);
+  lvwLog.Canvas.Brush.Style := bsClear;
+  // \83t\83H\81[\83J\83X\82ª\82 \82é\8fê\8d\87\82É\82Í\83t\83H\81[\83J\83X\82Ì\98g\90ü\82ð\88ø\82­
+  if Item.Focused and lvwLog.Focused then
+    lvwLog.Canvas.DrawFocusRect(DestRect);
+
+  // \83h\83\89\83b\83O\92\86\82È\82ç\98g\90ü\82ð\95`\89æ\82·\82é
+  if FLVDragDest = Item.Index then
+  begin
+    DestRect := Item.DisplayRect(drBounds);
+    DrawListViewDragBorder(DestRect);
+  end;
+
+  if Item.Selected then
+  begin
+    if lvwLog.Focused then
+      lvwLog.Canvas.Font.Color := clHighlightText
+    else
+      lvwLog.Canvas.Font.Color := clWindowText;
+  end else
+  lvwLog.Canvas.Font.Color := Pref.TextColor;
+  lvwLog.Canvas.Refresh;
+
+  // \83L\83\83\83v\83V\83\87\83\93(\93ú\95t)
+  ListView_GetItemRect(lvwLog.Handle, Item.Index, DestRect, LVIR_LABEL);
+  Inc(DestRect.Left, 2);
+  Inc(DestRect.Top, 2);
+  Dec(DestRect.Right, 2);
+  DrawTextEx(lvwLog.Canvas.Handle, PChar(Item.Caption), -1, DestRect,
+    DT_SINGLELINE or DT_RIGHT, nil);
+  ListView_GetItemRect(lvwLog.Handle, Item.Index, DestRect, LVIR_ICON);
+  Ico := TIcon.Create;
+  try
+    lvwLog.SmallImages.GetIcon(Item.ImageIndex, Ico);
+    lvwLog.Canvas.Draw(DestRect.Left, DestRect.Top, Ico);
+  finally
+    Ico.Free;
+  end;
+  // \83L\83\83\83v\83V\83\87\83\93\82Å\82à\83X\83N\83\8a\83v\83g\82Å\82à\82È\82¢\82à\82Ì
+  for sub := 0 to Item.SubItems.Count-1 do
+  begin
+    if sub = SubScript then Continue;
+    ListView_GetSubItemRect(lvwLog.Handle, Item.Index, sub + 1,
+      LVIR_BOUNDS, @DestRect);
+    if DestRect.Right - DestRect.Left <= 16 then
+    begin
+      // \8b·\82·\82¬\82é\8fê\8d\87\82Í\95\8e\9a\97ñ\82ð\95`\89æ\82µ\82È\82¢\81B
+      // 16\82Æ\82¢\82¤\90\94\8e\9a\82Í\8eÀ\91ª\92l\81B\89½\82©\82Ì\83o\83O\82Á\82Û
+      lvwLog.Canvas.FillRect(DestRect);
+      Continue;
+    end;
+    Inc(DestRect.Left, 2);
+    Inc(DestRect.Top, 2);
+    Dec(DestRect.Right, 2);
+    Ex := DT_NOPREFIX or DT_SINGLELINE or DT_END_ELLIPSIS;
+    if lvwLog.Columns[sub+1].Alignment = taRightJustify then
+      Ex := Ex or DT_RIGHT;
+    DrawTextEx(lvwLog.Canvas.Handle, PChar(Item.SubItems[sub]), -1, DestRect,
+      Ex, nil);
+  end;
+  // \83X\83N\83\8a\83v\83g
+  ListView_GetSubItemRect(lvwLog.Handle, Item.Index, SubScript + 1,
+    LVIR_BOUNDS, @DestRect);
+  Script := Item.SubItems[SubScript];
+  DrawSingleLineScript(Bottle, DestRect, Item);
+
+end;
+
+procedure TfrmLog.DrawSingleLineScript(LogItem: TLogItem;
+  Rect: TRect; Item: TListItem);
+var
+  i, x, w: integer;
+  UnyuTalking, Synchronized, Spaced: boolean;
+  Mark: TSsMarkUpType;
+  Script: String;
+  Ico: TIcon;
+  procedure ScopeChange;
+  begin
+    if (not Spaced) and (Pref.LogListPreviewStyle = psTagStripped) then
+    begin
+      Inc(x, 7);
+      Spaced := true;
+    end;
+  end;
+begin
+  Script := LogItem.Script;
+  x := 3;
+
+  if LogItem.HasURL = huYes then
+  begin
+    Ico := TIcon.Create;
+    try
+      lvwLog.SmallImages.GetIcon(IconURL, Ico);
+      lvwLog.Canvas.Draw(Rect.Left + x, Rect.Top, Ico);
+      Inc(x, 20);
+    finally
+      Ico.Free;
+    end;
+  end;
+
+  if Pref.LogListPreviewStyle = psNoColor then
+  begin
+    Inc(Rect.Left, x);
+    Inc(Rect.Top, 2);
+    Dec(Rect.Right, 2);
+    DrawTextEx(lvwLog.Canvas.Handle, PChar(Script), -1, Rect,
+      DT_SINGLELINE or DT_END_ELLIPSIS or DT_NOPREFIX, nil);
+    Exit;
+  end;
+
+  SsParser.LeaveEscape := Pref.LogListPreviewStyle = psNormal;
+  SsParser.InputString := Script;
+
+  UnyuTalking := false;
+  Synchronized := false;
+  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¢
+                  // \82½\82ß\82Ì\83t\83\89\83O
+  for i := 0 to SsParser.Count - 1 do begin
+    if SsParser[i] = '\h' then
+    begin
+      UnyuTalking := false;
+      ScopeChange;
+    end else if SsParser[i] = '\u' then
+    begin
+      UnyuTalking := true;
+      ScopeChange;
+    end else if SsParser[i] = '\_s' then
+    begin
+      Synchronized := not Synchronized;
+      ScopeChange;
+    end else if (Pos('\n', SsParser[i]) = 1) or (SsParser[i] = '\c') then
+    begin
+      ScopeChange;
+    end;
+    Mark := SsParser.MarkUpType[i];
+    case Mark of
+      mtMeta:
+        begin
+          lvwLog.Canvas.Font.Color := Pref.MetaWordColor;
+          Spaced := false;
+        end;
+      mtTag:
+        if Pref.LogListPreviewStyle = psNormal then
+          lvwLog.Canvas.Font.Color := Pref.MarkUpColor
+        else
+        begin
+          Continue;
+        end;
+      mtTagErr:
+        lvwLog.Canvas.Font.Color := Pref.MarkErrorColor;
+      else begin
+        Spaced := false;
+        if Synchronized then
+          lvwLog.Canvas.Font.Color := Pref.TalkColorS
+        else if UnyuTalking then
+          lvwLog.Canvas.Font.Color := Pref.TalkColorU
+        else
+          lvwLog.Canvas.Font.Color := Pref.TalkColorH;
+      end;
+    end;
+    if Item.Selected then
+    begin
+      if lvwLog.Focused then
+        lvwLog.Canvas.Font.Color := clHighlightText
+      else
+        lvwLog.Canvas.Font.Color := clWindowText;
+    end;
+    lvwLog.Canvas.Refresh;
+    w := lvwLog.Canvas.TextWidth(SsParser[i]);
+    lvwLog.Canvas.TextRect(Rect, Rect.Left + x, Rect.Top + 2, SsParser[i]);
+    x := x + w;
+    if Rect.Right - Rect.Left < x then Break;
+  end;
+end;
+
+procedure TfrmLog.mnListPreviewStyleClick(Sender: TObject);
+var i: integer;
+begin
+  with PopupMenuListPreviewStyle do
+    for i := 0 to Items.Count-1 do
+      Items[i].Checked := (Sender as TMenuItem).Tag = Items[i].Tag;
+  Pref.LogListPreviewStyle := TLogListPreviewStyle((Sender as TMenuItem).Tag);
+  lvwLog.Invalidate;
+end;
+
+procedure TfrmLog.tbtnListPreviewStyleClick(Sender: TObject);
+var sel: integer;
+begin
+  sel := Ord(Pref.LogListPreviewStyle);
+  sel := sel + 1;
+  if sel > Ord(High(TLogListPreviewStyle)) then sel := 0;
+  Pref.LogListPreviewStyle := TLogListPreviewStyle(sel);
+  lvwLog.Invalidate;
+end;
+
+procedure TfrmLog.PopupMenuListPreviewStylePopup(Sender: TObject);
+var i: integer;
+begin
+  with PopupMenuListPreviewStyle do
+    for i := 0 to Items.Count-1 do
+      Items[i].Checked := Items[i].Tag = Ord(Pref.LogListPreviewStyle)
+end;
+
+procedure TfrmLog.PreviewStyleChange;
+begin
+  if Pref.LogWindowPreviewStyle = psImageConversation then
+  begin
+    if Spps.Count = 0 then
+      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');
+    edtScript.Visible := false;
+    TalkShowFrame.Visible := true;
+  end else
+  begin
+    edtScript.Visible := true;
+    TalkShowFrame.Visible := false;
+  end;
+end;
+
+procedure TfrmLog.lvwLogDragOver(Sender, Source: TObject; X, Y: Integer;
+  State: TDragState; var Accept: Boolean);
+var
+  Target: TListItem;
+  OldDest: integer;
+  Rec: TRect; // \83_\83~\81[\81B
+begin
+  Accept := False;
+  // \82Æ\82è\82 \82¦\82¸\8eó\82¯\95t\82¯\82é\89Â\94\\90«\82ª\82 \82é\82Ì\82ÍTBottleLogDragObject\82¾\82¯
+  if not (Source is TBottleLogDragObject) then
+    Exit;
+
+  Target := lvwLog.GetItemAt(X, Y);
+
+  // \82±\82ê\88È\91O\82É\95`\89æ\82³\82ê\82Ä\82¢\82½\98g\82Ì\83C\83\93\83f\83b\83N\83X
+  OldDest := FLVDragDest;
+
+  // \83h\83\8d\83b\83v\88Ê\92u\82É Item \82ª\82 \82ê\82Î\83h\83\8d\83b\83v\82ð\8b\96\89Â\82·\82é
+  if Target <> nil then
+  begin
+    Accept := true;
+    FLVDragDest := Target.Index;
+  end else
+  begin
+    Accept := true;
+    FLVDragDest := -1;
+  end;
+
+  // \88È\91O\82Ì\98g\90ü\82ð\8dí\8f\9c
+  if (OldDest > -1) and (FLVDragDest <> OldDest) then
+  begin
+    Rec := lvwLog.Items[OldDest].DisplayRect(drBounds);
+    DrawListViewDragBorder(Rec);
+  end;
+  // \83h\83\89\83b\83O\90æ\82Ì\98g\90ü\82ð\95`\89æ
+  if (Target <> nil) and (FLVDragDest <> OldDest) then
+  begin
+    Rec := Target.DisplayRect(drBounds);
+    DrawListViewDragBorder(Rec);
+  end;
+
+  // \83X\83N\83\8d\81[\83\8b\8aÖ\8cW
+  if lvwLog.Items.Count > 0 then
+  begin
+    if (lvwLog.topItem <> nil) and (Y - lvwLog.TopItem.Top < 10) then
+    begin
+      FLVScrollDir := lvScrollDown;
+      if not timScrollTimer.Enabled then
+        timScrollTimer.Enabled := true;
+    end else if (lvwLog.Height - Y) < 10 then
+    begin
+      FLVScrollDir := lvScrollUp;
+      if not timScrollTimer.Enabled then
+        timScrollTimer.Enabled := true;
+    end
+    else
+      timScrollTimer.Enabled := false;
+  end else
+    timScrollTimer.Enabled := false;
+end;
+
+procedure TfrmLog.lvwLogDragDrop(Sender, Source: TObject; X, Y: Integer);
+var
+  TargetItem: integer;
+  Src: TBottleLogDragObject;
+  SrcLog: TObject;
+begin
+  timScrollTimer.Enabled := false;
+
+  if not (Source is TBottleLogDragObject) then
+    Exit;
+  Src := Source as TBottleLogDragObject;
+
+  if lvwLog.GetItemAt(X, Y) <> nil then
+    TargetItem := lvwLog.GetItemAt(X, Y).Index
+  else
+    TargetItem := -1;
+
+  lvwLog.Items.BeginUpdate; // \83h\83\8d\83b\83v\92\86\82Í\95\\8e¦\82ð\97}\8e~\82·\82é\81@\8fd\97v\81I
+  try
+    // \83h\83\8d\83b\83v\88Ê\92u\82É Item \82ð\88Ú\93®\82·\82é
+    if (GetAsyncKeyState(VK_CONTROL) and $8000) > 0 then
+    begin // \83R\83s\81[\88Ú\93®\82Ì\8fê\8d\87
+      SrcLog := TLogItem.Create(Src.LogItem);
+      SelectedBottleLog.LogModified := true; // \95Ï\8dX\88µ\82¢\82É\82·\82é
+    end else // \88Ú\93®\82¾\82¯\82·\82é\8fê\8d\87
+    begin
+      SrcLog := Src.BottleLogList.Extract(Src.LogItem);
+      // \88Ú\93®\8c³\82Æ\88Ú\93®\90æ\82ª\88á\82Á\82Ä\82¢\82ê\82Î\97¼\95û\82Ì\83t\83\89\83O\82ð\97§\82Ä\82é
+      if SelectedBottleLog.SelectedIndex <> Src.BottleLogList.SelectedIndex then
+      begin
+        Src.BottleLogList.LogModified := true; // \88Ú\93®\8c³
+        SelectedBottleLog.LogModified := true; // \88Ú\93®\90æ
+      end;
+    end;
+    if TargetItem >= 0 then
+    begin
+      // \82·\82Å\82É\91\8dÝ\82·\82é\83A\83C\83e\83\80\82Ì\8fã\82É\83h\83\8d\83b\83v\82µ\82½\8fê\8d\87
+      SelectedBottleLog.Insert(TargetItem, SrcLog);
+    end else
+    begin
+      // ListView\82Ì\97]\94\92\82É\83h\83\8d\83b\83v\82µ\82½\8fê\8d\87(Insert\82Å\82«\82È\82¢)
+      TargetItem := SelectedBottleLog.Add(SrcLog);
+    end;
+    lvwLog.Items[TargetItem].Selected := true;
+    lvwLog.Items[TargetItem].Focused := true;
+  finally
+    lvwLog.Items.EndUpdate;
+    UpdateWindow;
+  end;
+end;
+
+procedure TfrmLog.timScrollTimerTimer(Sender: TObject);
+var
+  ScrollHeight: Integer;
+begin
+  // \83X\83N\83\8d\81[\83\8b\97Ê\82ð\8b\81\82ß\82é
+  ScrollHeight := 0;
+  if lvwLog.Items.Count > 2 then
+  begin
+    ScrollHeight := lvwLog.Items[1].Top - lvwLog.Items[0].Top;
+  end;
+
+  case FLVScrollDir of
+    lvScrollUp: lvwLog.Scroll(0, ScrollHeight);
+    lvSCrollDown: lvwLog.Scroll(0, -ScrollHeight);
+  end;
+  lvwLog.Invalidate;  // \8dÅ\90V\82Ì\8fó\91Ô\82É\8dÄ\95`\89æ\82·\82é
+  lvwLog.Update;
+end;
+
+procedure TfrmLog.mnChangeTabNameClick(Sender: TObject);
+var Name: String;
+begin
+  Name := (FBottleLogList[tabBottleLog.Tag] as TBottleLogList).Title;
+  InputQuery('\96¼\91O\82Ì\95Ï\8dX', '\90V\82µ\82¢\83^\83u\82Ì\96¼\91O', Name);
+  (FBottleLogList[tabBottleLog.Tag] as TBottleLogList).Title := Name;
+  UpdateTab;
+end;
+
+procedure TfrmLog.lvwLogStartDrag(Sender: TObject;
+  var DragObject: TDragObject);
+var Drag: TBottleLogDragObject;
+begin
+  // \92Ê\8fí\82ÌListView\97p\82Ì\83h\83\89\83b\83O\83I\83u\83W\83F\83N\83g\82Í
+  // OS\82É\82æ\82Á\82Ä\82Í\81A\88Ú\93®\82·\82é\82Æ\82«\82É\83A\83C\83e\83\80\82Ì\83C\83\81\81[\83W\82ð\94¼\93§\96¾\82Å\95`\89æ\82µ\82Ä\82µ\82Ü\82¤\81B
+  // TDragObject\82©\82ç\92¼\90Ú\8cp\8f³\82µ\82½\82¾\82¯\82Ì\82à\82Ì(\83C\83\81\81[\83W\82ð\8e\9d\82Á\82Ä\82¢\82È\82¢)\82ð\8eg\82¤\82Æ
+  // \94¼\93§\96¾\83C\83\81\81[\83W\82Ì\95`\89æ\82Í\97}\90§\82Å\82«\82é\81B
+  Drag := TBottleLogDragObject.Create(lvwLog);
+  Drag.BottleLogList := SelectedBottleLog;
+  Drag.LogItem := SelectedBottleLog.Bottles[lvwLog.Selected.Index];
+  DragObject := Drag;
+end;
+
+procedure TfrmLog.lvwLogEndDrag(Sender, Target: TObject; X, Y: Integer);
+begin
+  // \98g\90ü\8fÁ\82µ\97p\82É\8b­\90§\93I\82É\8dÄ\95`\89æ\82³\82¹\82é
+  timScrollTimer.Enabled := false;
+  FLVDragDest := -1;
+  UpdateWindow;
+end;
+
+procedure TfrmLog.DrawListViewDragBorder(const Rect: TRect);
+var Rec: TRect;
+begin
+  Rec := Rect;
+  InflateRect(Rec, -1, -1);
+  with lvwLog.Canvas do
+  begin
+    Pen.Mode := pmNot;
+    Pen.Width := 3;
+    Brush.Style := bsClear;
+    Refresh; // \95K\97v
+    Rectangle(Rec);
+  end;
+end;
+
+function TfrmLog.DoSaveLogXML(Log: TBottleLogList): integer;
+var
+  Res: integer;
+begin
+  Res := idYes;
+  SaveDialog.FileName := GetDefaultFileName(Log.Title, '.xml');
+  SaveDialog.InitialDir := Pref.LogDir;
+  SaveDialog.DefaultExt := 'xml';
+  SaveDialog.FilterIndex := 3;
+  if SaveDialog.Execute then
+    Log.SaveToXmlFile(SaveDialog.FileName)
+  else
+    Res := idCancel;
+  Result := Res;
+end;
+
+procedure TfrmLog.DoCloseTab(const Index: integer);
+var
+  Confirm: String;
+  PrevSelection: TBottleLogList; // \95Â\82\82½\82Æ\82«\83^\83u\82ª\82¸\82ê\82È\82¢\82æ\82¤\82É\82·\82é\8f\88\97\9d\97p
+  i: integer;
+begin
+  if CheckLogSave(Index) = idCancel then exit; // \83\8d\83O\82Ì\95Û\91\8am\94F
+  if Pref.ConfirmOnTabClose then
+  begin
+    Confirm := Format('\83^\83u"%s"\82ð\95Â\82\82Ü\82·\82©?', [(FBottleLogList[Index] as TBottleLogList).Title]);
+    if MessageDlg(Confirm, mtConfirmation, mbOkCancel, 0) = mrCancel then
+      Exit;
+  end;
+  PrevSelection := SelectedBottleLog;
+  FBottleLogList.Delete(Index);
+  UpdateTab;
+  // \83^\83u\82¸\82ê\96h\8e~\8f\88\97\9d
+  for i := 0 to FBottleLogList.Count-1 do
+    if FBottleLogList[i] = PrevSelection then
+      tabBottleLog.TabIndex := i;
+  UpdateWindow;
+  lvwLogChange(Self, nil, ctState);
+end;
+
+procedure TfrmLog.HTMLOutputWork(Sender: TObject; const Count: integer;
+  var Canceled: boolean);
+begin
+  frmHTMLOutputProgress.ProgressBar.Position := Count;
+  Application.ProcessMessages;
+  if frmHTMLOutputProgress.Canceled then
+    Canceled := true;
+end;
+
+function TfrmLog.DoSearchLog(Condition: TSearchCond): TBottleLogList;
+var i, UntilIndex: integer;
+begin
+  Result := TBottleLogList.Create('\8c\9f\8dõ\8c\8b\89Ê');
+  if Condition.SearchLogRange in [srSelectedLogList, srAboveSelectedLog] then
+  begin
+    if SelectedBottleLog = nil then
+    begin
+      ShowMessage('\8c\9f\8dõ\91Î\8fÛ\82ª\82 \82è\82Ü\82¹\82ñ');
+      Result.Free;
+      Result := nil;
+      Exit;
+    end else
+    begin
+      if Condition.SearchLogRange = srSelectedLogList then
+        UntilIndex := -1
+      else if lvwLog.Selected = nil then
+        UntilIndex := -1
+      else
+        UntilIndex := lvwLog.Selected.Index;
+      SearchLogIndivisual(Condition, SelectedBottleLog, Result, UntilIndex);
+    end;
+  end else if Condition.SearchLogRange = srAllLogLists then
+  begin
+    for i := 0 to BottleLogList.Count-1 do
+    begin
+      SearchLogIndivisual(Condition, BottleLogList[i] as TBottleLogList,
+        Result);
+    end;
+  end;
+
+  if Result.Count = 0 then
+    Result.AddSystemLog('\8c©\82Â\82©\82è\82Ü\82¹\82ñ\82Å\82µ\82½\81B');
+end;
+
+procedure TfrmLog.SearchLogIndivisual(Condition: TSearchCond; LogList,
+  Result: TBottleLogList; UntilIndex: integer = -1);
+var
+  i, Max: integer;
+  Bottle, New: TLogItem;
+  Ok: boolean;
+begin
+  // 1\8cÂ\82Ì\83\8d\83O\83^\83u\82É\91Î\82µ\82Ä\8c\9f\8dõ\82ð\82©\82¯\82é\81BUntilIndex\82Å\94Í\88Í\8ew\92è(\8fÈ\97ª\8e\9e\82»\82Ì\83^\83u\91S\91Ì)
+  if UntilIndex >= 0 then
+    Max := UntilIndex
+  else
+    Max := LogList.Count-1;
+  for i := 0 to Max do
+  begin
+    // \8fð\8c\8f\94»\92è
+    Bottle := LogList.Bottles[i];
+    if Bottle.LogType <> ltBottle then
+      Continue;
+    Ok := true;
+    // \83X\83N\83\8a\83v\83g\83p\83^\81[\83\93\82Å\89ð\90Í
+    if Condition.ScriptPattern <> '' then
+    begin
+      if Condition.ScriptRegExp then
+      begin
+        try
+          if not RegExp.Match(Condition.ScriptPattern, Bottle.Script) then
+            Ok := false;
+        except
+          on EBRegExpError do
+            Ok := false; //\96­\82È\90³\8bK\95\\8c»\82ð\8fR\82é
+        end;
+      end else
+      begin
+        if not AnsiContainsText(Bottle.Script, Condition.ScriptPattern) then
+          Ok := false;
+      end;
+    end;
+    // \83`\83\83\83\93\83l\83\8b\96¼\81A\83S\81[\83X\83g\96¼\81A\93\8a\95[\93¯\88Ó
+    if Condition.Channel <> '' then
+      if not AnsiContainsText(Bottle.Channel, Condition.Channel) then
+        Ok := false;
+    if Condition.Ghost <> '' then
+      if not AnsiContainsText(Bottle.Ghost, Condition.Ghost) then
+        Ok := false;
+    if Condition.MinVote > Bottle.Votes then
+      Ok := false;
+    if Condition.MinAgree > Bottle.Agrees then
+      Ok := false;
+    // \8fð\8c\8f\82É\88ê\92v\82µ\82½\82à\82Ì\82ð\8c\8b\89Ê\83\8a\83X\83g\82É\92Ç\89Á
+    if Ok then
+    begin
+      New := TLogItem.Create(Bottle); // \83R\83s\81[\83R\83\93\83X\83g\83\89\83N\83^
+      New.State := lsOpened;
+      Result.Add(New);
+    end;
+  end;
+end;
+
+{ TBottleLogDragObject }
+
+function TBottleLogDragObject.GetDragImages: TDragImageList;
+begin
+  // \92\86\93r\94¼\92[\82È\83h\83\89\83b\83O\83C\83\81\81[\83W\82ð\95\\8e¦\82µ\82È\82¢\82æ\82¤\82É\82·\82é
+  Result := nil;
+end;
+
+procedure TBottleLogDragObject.SetBottleLogList(
+  const Value: TBottleLogList);
+begin
+  FBottleLogList := Value;
+end;
+
+procedure TBottleLogDragObject.SetLogItem(const Value: TLogItem);
+begin
+  FLogItem := Value;
+end;
+
+procedure TfrmLog.mnTabSaveXMLLogClick(Sender: TObject);
+begin
+  DoSaveLogXML(FBottleLogList[tabBottleLog.Tag] as TBottleLogList);
+end;
+
+procedure TfrmLog.mnSaveHTMLClick(Sender: TObject);
+var
+  LogList, SB: TBottleLogList;
+  i: integer;
+  Options: THTMLOutputOptions;
+begin
+  SB := SelectedBottleLog;
+  if SB = nil then
+    Exit;
+  if SB.Count = 0 then
+    Exit;
+  Application.CreateForm(TfrmHTMLOutputConfig, frmHTMLOutputConfig);
+  with frmHTMLOutputConfig do
+    try
+      // Show HTML save option dialog
+      if not Execute then
+        Exit;
+      LogList := TBottleLogList.Create('');
+      try
+        case Range of
+          orAll:
+            for i := SB.Count-1 downto 0 do
+              if SB.Bottles[i].LogType = ltBottle then
+                LogList.Add(TLogItem.Create(SB.Bottles[i]));
+          orSelected:
+            if SB.Bottles[lvwLog.Selected.Index].LogType = ltBottle then
+              LogList.Add(TLogItem.Create(SB.Bottles[lvwLog.Selected.Index]))
+            else
+              ShowMessage('\82±\82Ì\83\81\83b\83Z\81[\83W\82Í\95Û\91\82Å\82«\82Ü\82¹\82ñ');
+          orUpward:
+            for i := lvwLog.Selected.Index downto 0 do
+              if SB.Bottles[i].LogType = ltBottle then
+                LogList.Add(TLogItem.Create(SB.Bottles[i]));
+        end;
+        Options.ImageDir := ImageDir;
+        Options.UseColor := UseColor;
+        Options.ImageType := ImageType;
+        Application.CreateForm(TfrmHTMLOutputProgress, frmHTMLOutputProgress);
+        try
+          frmHTMLOutputProgress.Show;
+          LogList.OnHTMLOutputWork := HTMLOutputWork;
+          LogList.SaveToHTML(FileName, Options, SsParser);
+        finally
+          frmHTMLOutputProgress.Release;
+        end;
+      finally
+        LogList.Free;
+      end;
+    finally
+      Release;
+    end;
+end;
+
+procedure TfrmLog.mnPopupCopyGhostClick(Sender: TObject);
+var
+  Log: TLogItem;
+  Clip: TClipBoard;
+begin
+  Log := SelectedBottleLog.Bottles[frmLog.lvwLog.Selected.Index];
+  if Log = nil then Exit;
+  Clip := ClipBoard();
+  Clip.SetTextBuf(PChar(Log.Ghost));
+end;
+
+function TfrmLog.CheckLog(Sender: TObject): integer;
+var
+  i, Res: integer;
+begin
+  // \91S\82Ä\82Ì\83\8a\83X\83g\81i\83^\83u\81j\83`\83F\83b\83N\82·\82é
+  // frmSender\82©\82ç\8fI\97¹\8e\9e\82É\8cÄ\82Ñ\8fo\82³\82ê\82é
+  Res := idNo;
+  for i := 0 to BottleLogList.Count-1 do
+  begin
+    Res := CheckLogSave(i);
+    if Res = idCancel then break;
+  end;
+  Result := Res;
+end;
+
+function TfrmLog.CheckLogSave(const Index: integer): integer;
+var
+  Res: integer;
+  Confirm: string;
+begin
+  // \83\8a\83X\83g\82ð\83`\83F\83b\83N\82µ\81A\95Û\91\8f\88\97\9d\82ð\8cÄ\82Ñ\8fo\82·
+  Res := idNo;
+  if (BottleLogList[Index] as TBottleLogList).LogModified then
+  begin
+    Confirm := Format('\83^\83u %s \82Ì\93à\97e\82Í\95Ï\8dX\82³\82ê\82Ä\82¢\82Ü\82·\81B'#13#10#13#10 +
+      '\95Û\91\82µ\82Ü\82·\82©\81H', [(FBottleLogList[Index] as TBottleLogList).Title]);
+    Res := MessageDlg(Confirm, mtConfirmation, mbYesNoCancel, 0);
+    if Res = idYes then
+      Res := DoSaveLogXML(FBottleLogList[Index] as TBottleLogList);
+  end;
+  Result := Res;
+end;
+
+procedure TfrmLog.mnTestActionClick(Sender: TObject);
+begin
+  Screen.Cursor := crHourGlass;
+  frmSender.LogInsertCue(true); // \83A\83N\83V\83\87\83\93\83e\83X\83g\81i\98A\91±\8dÄ\90\81j\97L\8cø
+  Screen.Cursor := crDefault;
+end;
+
+procedure TfrmLog.mnSelActionClick(Sender: TObject);
+begin
+  // \92P\91Ì\83A\83N\83V\83\87\83\93\82Ì\97L\96³\82ð\90Ý\92è
+  if mnSelAction.Checked then
+    mnSelAction.Checked := false
+  else
+    mnSelAction.Checked := true;
+  Pref.LogAction := mnSelAction.Checked;
+end;
+
 end.
 end.