OSDN Git Service

ToDo266
[winbottle/winbottle.git] / bottleclient / MainForm.pas
1 unit MainForm;
2
3 {
4 \83A\83v\83\8a\83P\81[\83V\83\87\83\93\82Ì\83\81\83C\83\93\83t\83H\81[\83\80\81B
5 \91\97\90M\81E\8eó\90M\81E\83{\83g\83\8b\94z\91\97\8aÖ\8cW\82Ì\82¢\82ë\82¢\82ë\82È\8f\88\97\9d\82ð\8ds\82¤\81B
6 }
7
8 interface
9
10 uses
11   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
12   Menus, StdCtrls, ComCtrls, BRegExp, BottleDef, BottleSstp, DirectSstp,
13 //  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdSLPP20,
14   SsParser, ImgList, AppEvnts, TaskTray, StdActns,
15   ActnList, MPlayer, MenuBar, ToolWin,
16   IniFiles, ExtCtrls, ShellAPI, Contnrs,
17   ConstEditor, Buttons, Clipbrd, HeadValue, Logs, MultipleChoiceEditor,
18 //  IdException, HttpThread, IdHTTP, LogDownload,
19   ScriptConsts, DateUtils, BottleChainRule, BottleChainEvent,
20   SakuraSeekerInstance, HEditor, HTSearch, heClasses, heFountain,
21   SakuraScriptFountain, SppList, SurfacePreview, XDOM_2_3_J3, SsPlayTime,
22   RegexUtils, StrReplace, StrReplaceDialog, ReplacePresetEditor;
23
24 type
25   TSurfacePreviewType = (spHint, spEditor);
26
27   TfrmSender = class(TForm)
28     MainMenu: TMainMenu;
29     mnFile: TMenuItem;
30     mnExit: TMenuItem;
31     mnRegister: TMenuItem;
32     mnStart: TMenuItem;
33     mnScript: TMenuItem;
34     StatusBar: TStatusBar;
35     mnHelp: TMenuItem;
36     mnAbout: TMenuItem;
37     mnEditConst: TMenuItem;
38     ActionList: TActionList;
39     mnPopUp: TPopupMenu;
40     mnPopPaste: TMenuItem;
41     mnPopCut: TMenuItem;
42     mnPopCopy: TMenuItem;
43     mnPopSelectAll: TMenuItem;
44     N6: TMenuItem;
45     mnPopConst: TMenuItem;
46     mnEdit: TMenuItem;
47     mnPaste: TMenuItem;
48     mnCopy: TMenuItem;
49     mnCut: TMenuItem;
50     mnSelectAll: TMenuItem;
51     TaskTray: TTaskTray;
52     ApplicationEvents: TApplicationEvents;
53     mnPopUpTaskTray: TPopupMenu;
54     mnTaskStart: TMenuItem;
55     mnTaskEnd: TMenuItem;
56     mnTaskRestore: TMenuItem;
57     mnTaskNewMessage: TMenuItem;
58     actStart: TAction;
59     actStop: TAction;
60     N8: TMenuItem;
61     mnTaskExit: TMenuItem;
62     actSend: TAction;
63     actConfirm: TAction;
64     actClear: TAction;
65     mnSend: TMenuItem;
66     mnConfirm: TMenuItem;
67     mnClear: TMenuItem;
68     imgIcon: TImageList;
69     mnPopupConst: TPopupMenu;
70     actEditConst: TAction;
71     mnView: TMenuItem;
72     mnShowToolBar: TMenuItem;
73     mnShowConstBar: TMenuItem;
74     ConstBarMenu: TMainMenu;
75     ToolBar: TToolBar;
76     tbtnClear: TToolButton;
77     tbtnConfirm: TToolButton;
78     tbtnSend: TToolButton;
79     tbtnSeparator: TToolButton;
80     tbtnStart: TToolButton;
81     tbtnSeparator2: TToolButton;
82     tbtnInsertConst: TToolButton;
83     ConstMenuBar: TMenuBar;
84     mnGoToHP: TMenuItem;
85     LabelTimer: TTimer;
86     mnCopyAll: TMenuItem;
87     actCopyAll: TAction;
88     actCopyAllNoReturn: TAction;
89     mnCopyAllNoReturn: TMenuItem;
90     mnPopCopyAll: TMenuItem;
91     mnPopCopyAllNoReturn: TMenuItem;
92     actSetting: TAction;
93     tbtnSetting: TToolButton;
94     mnStayOnTop: TMenuItem;
95     mnSetting: TMenuItem;
96     actExitClient: TAction;
97     SsParser: TSsParser;
98     tbtnEditConst: TToolButton;
99     actClearBottles: TAction;
100     mnClearBottles: TMenuItem;
101     MediaPlayer: TMediaPlayer;
102     mnGetNewId: TMenuItem;
103     actNextChannel: TAction;
104     actPrevChannel: TAction;
105     N2: TMenuItem;
106     mnNextChannel: TMenuItem;
107     mnPrevChannel: TMenuItem;
108     actShowLog: TAction;
109     N3: TMenuItem;
110     tbtnShowLog: TToolButton;
111     tbtnSleep: TToolButton;
112     actSleep: TAction;
113     N1: TMenuItem;
114     mnSleep: TMenuItem;
115     mnTaskSleep: TMenuItem;
116     pnlEditor: TPanel;
117     tabChannel: TTabControl;
118     pnlPanel: TPanel;
119     lblMessage: TLabel;
120     cbxTargetGhost: TComboBox;
121     actVoteMessage: TAction;
122     mnPopUpChannelTab: TPopupMenu;
123     mnLeaveThisChannel: TMenuItem;
124     N4: TMenuItem;
125     mnGotoVote: TMenuItem;
126     mnGoToHelp: TMenuItem;
127     btnSend: TButton;
128     btnConfirm: TButton;
129     btnClear: TButton;
130     mnExitAllChannels: TMenuItem;
131     actAgreeMessage: TAction;
132     btnIfGhost: TButton;
133     actPrevGhost: TAction;
134     actNextGhost: TAction;
135     mnPrevGhost: TMenuItem;
136     mnNextGhost: TMenuItem;
137     actResetGhost: TAction;
138     mnResetGhost: TMenuItem;
139     DirectSstp: TDirectSstp;
140     actDownloadLog: TAction;
141     actFMOExplorer: TAction;
142     tbtnFMOExplorer: TToolButton;
143     mnFMOExplorer: TMenuItem;
144     mnLine: TMenuItem;
145     actInsertCue: TAction;
146     SakuraScriptFountain: TSakuraScriptFountain;
147     memScript: TEditor;
148     actCopy: TAction;
149     actPaste: TAction;
150     actCut: TAction;
151     actSelectAll: TAction;
152     actRecallScriptBuffer: TAction;
153     N5: TMenuItem;
154     mnRecallScriptBuffer: TMenuItem;
155     tbtnEditorPreview: TToolButton;
156     actEditorPreview: TAction;
157     mnEditorPreview: TMenuItem;
158     actResetPlugins: TAction;
159     N7: TMenuItem;
160     mnResetPlugins: TMenuItem;
161     actReplace: TAction;
162     N10: TMenuItem;
163     mnReplace: TMenuItem;
164     actSendToEditor: TAction;
165     actSendToLogWindow: TAction;
166     mnSendLogWindow: TMenuItem;
167     actDeleteLogItem: TAction;
168     actAbout: TAction;
169     actEditCopy: TEditCopy;
170     tbtnSendToLogWindow: TToolButton;
171     SsPlayTime: TSsPlayTime;
172     actUndo: TAction;
173     actRedo: TAction;
174     mnUndo: TMenuItem;
175     mnRedo: TMenuItem;
176     N9: TMenuItem;
177     mnPresetReplaceRoot: TMenuItem;
178     ScriptBackup: TTimer;
179     OpenDialog: TOpenDialog;
180     mnFileOpen: TMenuItem;
181     actFileOpen: TAction;
182     actFileSaveAs: TAction;
183     mnSaveAs: TMenuItem;
184     SaveDialog: TSaveDialog;
185     N11: TMenuItem;
186     mnSave: TMenuItem;
187     actFileSave: TAction;
188     procedure actConfirmExecute(Sender: TObject);
189     procedure FormCreate(Sender: TObject);
190     procedure FormDestroy(Sender: TObject);
191     procedure actSendExecute(Sender: TObject);
192 //    procedure HTTPSuccess(Sender: TObject);
193 //    procedure HTTPFailure(Sender: TObject);
194     procedure actStartClick(Sender: TObject);
195     procedure actStopExecute(Sender: TObject);
196     procedure FormShow(Sender: TObject);
197     procedure actAboutClick(Sender: TObject);
198     procedure actExitClientExecute(Sender: TObject);
199     procedure actClearExecute(Sender: TObject);
200     procedure memScriptChange(Sender: TObject);
201     procedure mnStayOnTopClick(Sender: TObject);
202     procedure actEditConstExecute(Sender: TObject);
203     procedure mnTaskBarClick(Sender: TObject);
204     procedure FormClose(Sender: TObject; var Action: TCloseAction);
205     procedure ApplicationEventsMinimize(Sender: TObject);
206     procedure ApplicationEventsRestore(Sender: TObject);
207     procedure mnTaskRestoreClick(Sender: TObject);
208     procedure TaskTrayDblClick(Seft: TObject; Button: TMouseButton);
209     procedure FormActivate(Sender: TObject);
210     procedure mnTaskNewMessageClick(Sender: TObject);
211     procedure ApplicationEventsHint(Sender: TObject);
212     procedure memScriptKeyDown(Sender: TObject; var Key: Word;
213       Shift: TShiftState);
214     procedure mnShowToolBarClick(Sender: TObject);
215     procedure mnShowConstBarClick(Sender: TObject);
216     procedure mnGoToHPClick(Sender: TObject);
217     procedure LabelTimerTimer(Sender: TObject);
218     procedure actCopyAllExecute(Sender: TObject);
219     procedure actCopyAllNoReturnExecute(Sender: TObject);
220 //    procedure Slpp20SlppEvent(Sender: TObject; EventType: TIdSlppEventType;
221 //      const Param: String);
222     procedure actSettingExecute(Sender: TObject);
223     procedure memScriptKeyPress(Sender: TObject; var Key: Char);
224 //    procedure Slpp20Disconnect(Sender: TObject);
225     procedure actClearBottlesExecute(Sender: TObject);
226     procedure SakuraSeekerDetectResultChanged(Sender: TObject);
227     procedure mnGetNewIdClick(Sender: TObject);
228     procedure tabChannelChange(Sender: TObject);
229     procedure actPrevChannelExecute(Sender: TObject);
230     procedure actNextChannelExecute(Sender: TObject);
231     procedure cbxTargetGhostDropDown(Sender: TObject);
232     procedure actShowLogExecute(Sender: TObject);
233     procedure actSleepExecute(Sender: TObject);
234     procedure actVoteMessageExecute(Sender: TObject);
235     procedure tabChannelContextPopup(Sender: TObject; MousePos: TPoint;
236       var Handled: Boolean);
237     procedure mnLeaveThisChannelClick(Sender: TObject);
238     procedure mnGotoVoteClick(Sender: TObject);
239     procedure tabChannelMouseMove(Sender: TObject; Shift: TShiftState; X,
240       Y: Integer);
241     procedure mnGoToHelpClick(Sender: TObject);
242     procedure tabChannelMouseDown(Sender: TObject; Button: TMouseButton;
243       Shift: TShiftState; X, Y: Integer);
244     procedure tabChannelDragOver(Sender, Source: TObject; X, Y: Integer;
245       State: TDragState; var Accept: Boolean);
246     procedure tabChannelDragDrop(Sender, Source: TObject; X, Y: Integer);
247     procedure tabChannelEndDrag(Sender, Target: TObject; X, Y: Integer);
248     procedure cbxTargetGhostDrawItem(Control: TWinControl; Index: Integer;
249       Rect: TRect; State: TOwnerDrawState);
250     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
251     procedure actAgreeMessageExecute(Sender: TObject);
252     procedure actPrevGhostExecute(Sender: TObject);
253     procedure actNextGhostExecute(Sender: TObject);
254     procedure actResetGhostExecute(Sender: TObject);
255 //    procedure timDisconnectCheckTimerTimer(Sender: TObject);
256     procedure actDownloadLogExecute(Sender: TObject);
257     procedure cbxTargetGhostChange(Sender: TObject);
258     procedure actFMOExplorerExecute(Sender: TObject);
259     // \8eÀ\8dÛ\82Ì\8f\88\97\9d\82ÍLogInsertCue\82Ö\88Ú\93®
260     procedure actInsertCueExecute(Sender: TObject);
261     procedure FormResize(Sender: TObject);
262     procedure actCopyExecute(Sender: TObject);
263     procedure actPasteExecute(Sender: TObject);
264     procedure actCutExecute(Sender: TObject);
265     procedure actSelectAllExecute(Sender: TObject);
266     procedure memScriptMouseMove(Sender: TObject; Shift: TShiftState; X,
267       Y: Integer);
268     procedure actRecallScriptBufferExecute(Sender: TObject);
269     procedure actEditorPreviewExecute(Sender: TObject);
270     procedure actResetPluginsExecute(Sender: TObject);
271 //    procedure IdSLPP20Connect(Sender: TObject);
272     procedure actReplaceExecute(Sender: TObject);
273     procedure actSendToEditorExecute(Sender: TObject);
274     procedure actSendToLogWindowExecute(Sender: TObject);
275     procedure memScriptDragOver(Sender, Source: TObject; X, Y: Integer;
276       State: TDragState; var Accept: Boolean);
277     procedure memScriptDragDrop(Sender, Source: TObject; X, Y: Integer);
278     procedure actDeleteLogItemExecute(Sender: TObject);
279     procedure memScriptSelectionChange(Sender: TObject; Selected: Boolean);
280     procedure actUndoExecute(Sender: TObject);
281     procedure actRedoExecute(Sender: TObject);
282 //    procedure IdSLPP20ConnectFailed(Sender: TObject);
283     procedure ScriptBackupTimer(Sender: TObject);
284     procedure actFileOpenExecute(Sender: TObject);
285     procedure actFileSaveAsExecute(Sender: TObject);
286     procedure actFileSaveExecute(Sender: TObject);
287   private
288     FSleeping: boolean;  // \94z\91\97\83X\83\8a\81[\83v\92\86\82©\82Ç\82¤\82©
289     FStatusText: String;
290 //    FConnecting: boolean;
291 //    FAdded: boolean;
292     FBooted: boolean; //\8f\89\89ñ\8bN\93®\92Ê\90M\97p
293 //    FEndSession: Boolean; // Windows\8fI\97¹\82ð\8c\9f\92m\82µ\82Ätrue\82É\82È\82é
294     FOriginalCaption: String;
295 //    FAutoAddAfterGetChannel: boolean; //\83`\83\83\83\93\83l\83\8b\8eæ\93¾\8cã\82É\83_\83C\83A\83\8d\83O\82È\82µ\82É
296                                       //\83`\83\83\83\93\83l\83\8b\82É\8eQ\89Á\82·\82é\82©\82Ç\82¤\82©
297     FConstDir: String;
298     FSppDir: String;
299     //
300 //    FNowChannel: String; //\8c»\8dÝ\91I\91ð\82³\82ê\82Ä\82¢\82é\83`\83\83\83\93\83l\83\8b
301 //    JoinChannelsBackup: TStringList; //\88ê\8e\9e\8eg\97p
302     //
303 //    FScriptModified: boolean; // \83X\83N\83\8a\83v\83g\82ª\95Ï\8dX\82³\82ê\82Ä\82¢\82é\82©\82Ç\82¤\82©\81B
304 //                              // \83\8d\81[\83J\83\8b\8am\94F\8b­\90§\97p\83t\83\89\83O\81BTRichEdit.Modified\82Í
305 //                              //\95Ê\82Ì\97p\93r\82Å\8eg\82Á\82Ä\82¢\82é\82Ì\82Å\8eg\82¦\82È\82¢
306     //
307     FScriptBackupModified: boolean; // \83X\83N\83\8a\83v\83g\83o\83b\83N\83A\83b\83v\83t\83\89\83O
308     FScriptBackupFile: string;      // \8c»\8dÝ\82Ì\83o\83b\83N\83A\83b\83v\83t\83@\83C\83\8b\96¼
309     FScriptBackupDir: string;       // \83o\83b\83N\83A\83b\83v\90æ\82Ì\83t\83H\83\8b\83_\96¼
310     //
311     FFileModified: boolean;       //\83t\83@\83C\83\8b\93à\97e\82ª\95Ï\8dX\82³\82ê\82Ä\82¢\82é\82©\81B
312     FFileName: string;            //\90V\8bK\82©\8aù\91\83t\83@\83C\83\8b\82Ì\94»\92f\81A\82Ü\82½\82Í\8aù\91\83t\83@\83C\83\8b\82Ì\8fê\8f\8a
313     //
314 //    FDragTabIndex: integer; //\83^\83u\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
315 //    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)
316     //
317     FBottleSstp: TBottleSstp; // \8dÄ\91\97\83v\83\8d\83O\83\89\83\80
318     //
319 //    FHttp: THTTPDownloadThread; //HTTP\83_\83E\83\93\83\8d\81[\83h\83X\83\8c\83b\83h(\83C\83\93\83X\83^\83\93\83X\82Í1\8cÂ\82Ì\82Ý)
320 //    FBeginConnectFailCount: integer; //\89½\93x\82à\90Ú\91±\8e¸\94s\82µ\82½\82ç\83\8a\83g\83\89\83C\92\86\8e~
321     //
322     FVisiblePreviewGhost: String;
323     FVisiblePreviewSurface: integer; //\83T\81[\83t\83B\83X\83v\83\8c\83r\83\85\81[\82Å\8c©\82¦\82Ä\82¢\82é\82à\82Ì
324     FVisibleMenuItem: TMenuItem; //\83N\83\8a\83b\83N\82³\82ê\82½\83\81\83j\83\85\81[\83A\83C\83e\83\80\81B
325                                  //\83T\81[\83t\83B\83X\83v\83\8c\83r\83\85\81[\82ª\83\81\83j\83\85\81[\82Ì\89º\82É
326                                  //\89B\82ê\82È\82¢\82æ\82¤\82É\95\\8e¦\82³\82ê\82Ä\82¢\82é\82à\82Ì\82ð\8bL\89¯\82µ\82Ä\82¨\82­
327     //
328     FTargetGhostExpand: boolean; //\83S\81[\83X\83g\91I\91ð\83{\83b\83N\83X\82ª\81A
329                                  //\88ê\8e\9e\93I\82É\91S\95\94\95\\8e¦\82³\82ê\82Ä\82¢\82é\82©\82Ç\82¤\82©\82Ì\83t\83\89\83O
330     //
331     FScriptBuffer: TObjectList;  //\83X\83N\83\8a\83v\83g\83N\83\8a\83A\83o\83b\83t\83@
332     //
333     FWM_TaskBarCreated: WORD; // \83^\83X\83N\83o\81[\93o\98^\97p\83E\83B\83\93\83h\83E\83\81\83b\83Z\81[\83WID
334     //
335 //    procedure SetStatusText(const Value: String);
336     procedure SetSleeping(const Value: boolean);
337     procedure YenETrans;
338 //    procedure SetConnecting(const Value: boolean);
339 //    procedure SetAdded(const Value: boolean);
340     procedure mnConstClick(Sender: TObject);
341     procedure mnConstGroupClick(Sender: TObject);
342     procedure mnPresetReplaceClick(Sender: TObject);
343 //    property Added: boolean read FAdded write SetAdded;
344     property Sleeping: boolean read FSleeping write SetSleeping;
345 //    property StatusText: String read FStatusText write SetStatusText;
346     function GetScriptText: String;
347     procedure ChangeTaskIcon;
348     procedure ShowHintLabel(const Mes: String; Col: TColor = clBlue);
349     procedure UpdateLayout;
350 //    procedure DispatchBottle(EventType: TIdSlppEventType; Dat: THeadValue);
351     procedure DispatchBottle(Dat: THeadValue; LogTime: TDateTime;
352       Vote, Agree: integer);
353     //\83`\83\83\83\93\83l\83\8b\8aÖ\8cW
354 //    procedure UpdateChannelInfo(Dat: THeadValue);
355 //    procedure UpdateJoinChannelList(Dat: THeadValue);
356     procedure UpdateJoinChannelList;
357     procedure UpdateChannelList;
358 //    procedure NoLuidError;
359     procedure UpdateIfGhostBox;
360     function BuildMenuConditionCheck(const IfGhost, Ghost: String): boolean;
361     procedure BuildMenu(Root: TMenuItem; Simple: boolean);
362     procedure BuildReplaceMenu(Root: TMenuItem);
363     procedure PlaySound(const FileName: String);
364     //TBottleSstp\8aÖ\8cW\83C\83x\83\93\83g\83n\83\93\83h\83\89
365     procedure BottleSstpResendCountChange(Sender: TObject);
366     procedure BottleSstpResendTrying(Sender: TObject; MID: String);
367     procedure BottleSstpResendEnd(Sender: TObject; MID: String);
368     function IsSurfaceTag(const Script: String; var ID: integer): boolean;
369     procedure DoSurfacePreview(Surface: integer; const Kind:
370       TSurfacePreviewType);
371     function GetSurfacePreviewPositionHint(w, h: integer): TPoint;
372     function GetSurfacePreviewPositionScriptPoint(w, h: integer): TPoint;
373     procedure EditorPreview;
374     // \83^\83O\82Ì\95\8e\9a\97ñ\82ð\95Ï\8a·\82·\82é
375     function TagReplace(Script: String;
376       Before, After: array of String): String; overload;
377     function TagReplace(Script: String;
378       Before, After: TStrings): String; overload;
379     // \83T\81[\83t\83B\83X\82ð\95Ï\8a·\82·\82é
380     function ReplaceSurface(Script: String; Params: TCollection): String;
381     procedure ClearEditor;
382     procedure CopyFromLogToEditor(Log: TLogItem);
383     //
384     procedure AppendTextLog(const FileName, Line: String; FAppend: boolean);
385     procedure AppendXMLLog(const FileName: String; Args: THeadValue);
386     //\8e©\93®\95Û\91\90Ý\92è
387     procedure SetScriptAutoBackup;
388     //\92P\91Ì\83t\83@\83C\83\8b\93ü\8fo\97Í\8aÖ\8cW
389     procedure PerformFileOpen(const AFileName: string);
390     procedure PerformFileSave(const AFileName: string);
391     procedure SetFileName(const FileName: string; Reset, SetNewFile: boolean);
392     function CheckFileModified(Sender: TObject): integer;
393     function FileSave(Sender: TObject): integer;
394     function FileSaveAs(Sender: TObject): integer;
395     procedure EditerStatusChange;
396     procedure SetFileModified(Value: boolean);
397     function CheckFileExt(const FileName: string): boolean;
398     procedure DeleteBackupFile(const FileName: string);
399
400   protected
401     procedure WndProc(var Message: TMessage); override;
402 //    procedure WMQueryEndSession(var msg: TWMQueryEndSession);
403 //      message WM_QUERYENDSESSION;
404   public
405     function DoTrans(var Script: String;
406       Options: TScriptTransOptions): String; overload;
407     function DoTrans(var Script: String;
408       Options: TScriptTransOptions; out FoundURL: boolean): String; overload;
409     function ScriptTransForSSTP(const Script: String;
410       out Error: String): String; overload;
411 //    procedure BeginConnect;
412 //    procedure RetryBeginConnect;
413 //    procedure EndConnect;
414     procedure ConstructMenu(Simple: boolean);
415 //    property Connecting: boolean read FConnecting write SetConnecting;
416     property BottleSstp: TBottleSstp read FBottleSstp;
417     function GhostNameToSetName(const Ghost: String): String;
418 //    procedure PostCommand(const Command: array of String); overload;
419 //    procedure PostCommand(Command: TStrings); overload;
420 //    procedure PostSetChannel(Channels: TStrings);
421     procedure SaveChainRuleList;
422     //DispatchBottle()\82Ö\83o\83C\83p\83X
423     procedure BottleCnv(Log: TLogItem);
424     //\83u\83\89\83E\83U\94»\92f\82ÆURL\88ø\93n\82µ
425     procedure OpenBrowser(const Url: string);
426     //Log\83E\83B\83\93\83h\83E\82©\82ç\8fð\8c\8f\95t\82Å\8cÄ\82Î\82ê\82é
427     procedure LogInsertCue(TestAction, SelectAll: boolean);
428   end;
429
430
431 var
432   frmSender: TfrmSender;
433
434 const
435   PanelConnecting = 0;  //\81u\90Ú\91±\92\86\81v\95\\8e¦\97p
436   PanelBytes      = 1;  //\81\9b\81\9b\83o\83C\83g
437   PanelCount      = 2;  //Local Proxy\81A\8c»\8dÝ\81\9b\8c\8f\91Ò\82¿
438   PanelMembers    = 3;  //\81\9b\81\9b\90l
439   PanelStatus     = 4;  //SSTP Bottle\83T\81[\83o\82É\93o\98^\82³\82ê\82Ä\82¢\82Ü\82·\81c\82È\82Ç
440
441   IconConnected    = 17;
442   IconDisconnected = 18;
443   IconSleep        = 19;
444   IconSleepDisconnected = 20;
445   IconYC                = 47;
446
447   WarningColor = clRed;
448
449   SendButtonLongHint = 'Bottle\82Ì\91\97\90M';
450   NewFileTitle       = '\96¼\8fÌ\96¢\92è';     // \90V\8bK\83^\83C\83g\83\8b
451   StdBackupFile      = 'Script.bak';   // FileCheck\8ew\92è\82Ì\8ag\92£\8eq\82ð\8eg\82¤
452
453 function Token(const Str: String; const Delimiter: char;
454   const Index: integer): String;
455
456 function StringReplaceEx(const Before: String; List: THeadValue): String;
457
458 implementation
459
460 uses SettingForm, LogForm,
461   MessageBox, FMOExplorer, EditorTalkShow;
462 //SendConfirm, ChannelListForm,
463
464 {$R *.DFM}
465
466 // \92P\8f\83\82É\83o\83C\83g\92P\88Ê\82Å\95\8e\9a\97ñ\82ð\8c©\82Ä\82¢\82«\95ª\89ð\82·\82é\83\86\81[\83e\83B\83\8a\83e\83B\8aÖ\90\94
467 function Token(const Str: String; const Delimiter: char;
468   const Index: integer): String;
469 var i, c, len: integer;
470 begin
471   i := 1;
472   c := 0;
473   len := length(Str);
474   Result := '';
475   while i <= len do begin
476     if (Str[i] = Delimiter) and (StrByteType(PChar(Str), i) <> mbTrailByte) then begin
477       Inc(c);
478       if c > Index then Break;
479     end else if c = Index then Result := Result + Str[i];
480     Inc(i);
481   end;
482 end;
483
484 // \95\8e\9a\97ñ\82ð\92u\82«\8a·\82¦\82é\83\86\81[\83e\83B\83\8a\83e\83B\8aÖ\90\94
485 function StringReplaceEx(const Before: String; List: THeadValue): String;
486 var
487   i, MinPos, MinKey, p: integer;
488   Work: String;
489 begin
490   Work := Before;
491   Result := '';
492   MinKey := -1;
493   while Work <> '' do
494   begin
495     MinPos := -1;
496     for i := 0 to List.Count-1 do
497     begin
498       p := Pos(List.KeyAt[i], Work);
499       if (p > 0) and ((p < MinPos) or (MinPos < 0)) then
500       begin
501         MinPos := p;
502         MinKey := i;
503       end;
504     end;
505     if MinPos < 0 then
506     begin
507       Result := Result + Work;
508       Break;
509     end else
510     begin
511       Result := Result + Copy(Work, 1, MinPos-1) + List.ValueAt[MinKey];
512       Delete(Work, 1, (MinPos - 1) + Length(List.KeyAt[MinKey]));
513     end;
514   end;
515 end;
516
517 {TfrmSender}
518
519 procedure TfrmSender.actConfirmExecute(Sender: TObject);
520 var
521   AScript, Err, AGhost: String;
522   Item: TLogItem;
523   Choice: integer;
524 begin
525   // Partial Confirmation
526   if memScript.SelText <> '' then
527   begin
528     Choice := 0;
529     if not Pref.AutoPartialConfirm then
530       if not MultipleChoiceEdit('\8am\94F', ['\91I\91ð\95\94\95ª\82Ì\82Ý', '\83X\83N\83\8a\83v\83g\91S\91Ì'], Choice) then
531         Exit;
532     if Choice = 0 then
533     begin
534       AScript := memScript.SelText;
535       AScript := StringReplace(Pref.PartialConfirmFormat, '|', AScript, []);
536     end else
537       AScript := GetScriptText;
538   end else
539     AScript := GetScriptText;
540   AScript := StringReplace(AScript, #13#10, '', [rfReplaceAll]);
541
542   if Length(AScript) = 0 then Exit;
543   YenETrans;
544   AScript := ScriptTransForSSTP(AScript, Err);
545   if Err <> '' then
546   begin
547     ShowMessage(Err);
548     Exit;
549   end;
550
551   if cbxTargetGhost.ItemIndex > 0 then
552     AGhost := cbxTargetGhost.Text
553\95K\82¸\8ew\92è\82³\82ê\82é\82Í\82¸
554   else if FNowChannel <> '' then
555     AGhost := ChannelList.Channel[FNowChannel].Ghost
556 }
557   else
558     AGhost := '';
559
560   if Pref.IgnoreTimeCritical then
561     AScript := TagReplace(AScript, ['\t'], ['']);
562
563   Item := TLogItem.Create;
564   try
565     with Item do
566     begin
567       LogType := ltBottle;
568       Script := AScript;
569       Channel := '\81y\8am\94F\81z';
570       Ghost := AGhost;
571     end;
572     BottleSstp.Unshift(Item);
573   except
574     Item.Free;
575   end;
576
577 //  FScriptModified := false;
578 end;
579
580 procedure TfrmSender.FormCreate(Sender: TObject);
581 var Str: TStringList;
582 begin
583   FScriptBuffer := TObjectList.Create(true);
584
585   SakuraSeeker.OnDetectResultChanged := SakuraSeekerDetectResultChanged;
586   FConstDir := ExtractFileDir(Application.ExeName)+'\consts';
587   ScriptConstList.LoadFromDir(FConstDir);
588   FSppDir := ExtractFileDir(Application.ExeName)+'\spp';
589   Spps.LoadFromDirectory(FSppDir);
590   ConstructMenu(false);
591   BuildReplaceMenu(mnPresetReplaceRoot);
592   FScriptBackupDir := ExtractFileDir(Application.ExeName) + '\temp\';
593
594   Str := TStringList.Create;
595   try
596     try
597       Str.LoadFromFile(ExtractFilePath(Application.ExeName)+'rule.txt');
598       BottleChainRuleList := StringToComponent(Str.Text) as TBottleChainRuleList;
599     except
600       try
601         Str.LoadFromFile(ExtractFilePath(Application.ExeName)+'defrule.txt');
602         BottleChainRuleList := StringToComponent(Str.Text) as TBottleChainRuleList;
603       except
604         ShowMessage('defrule.txt\93Ç\82Ý\8d\9e\82Ý\92\86\82É\92v\96½\93I\83G\83\89\81[\82ª\94­\90\82µ\82Ü\82µ\82½\81Bdefrule.txt\82ð\8dÄ\83C\83\93\83X\83g\81[\83\8b\82µ\82Ä\82­\82¾\82³\82¢\81B');
605         Application.Terminate;
606         Application.ProcessMessages;
607         Exit;
608       end;
609     end;
610   finally
611     Str.Free;
612   end;
613
614   FOriginalCaption := Self.Caption;
615
616   UpdateLayout;
617   mnShowToolBar.Checked := Pref.ShowToolBar;
618   mnShowConstBar.Checked := Pref.ShowConstBar;
619   if Pref.StayOnTop then begin
620     FormStyle := fsStayOnTop;
621     mnStayOnTop.Checked := true;
622   end else begin
623     FormStyle := fsNormal;
624     mnStayOnTop.Checked := false;
625   end;
626
627   // URL\83W\83\83\83\93\83v\90æ\82ð\83q\83\93\83g\95\8e\9a\97ñ\82Æ\82µ\82Ä\90Ý\92è
628   mnGoToHP.Hint := Pref.HomePage;
629   mnGotoVote.Hint := Pref.VotePage;
630   mnGotoHelp.Hint := Pref.HelpPage;
631
632   mnGetNewId.Enabled := (Pref.LUID = '');
633
634   // \82³\82­\82ç\83X\83N\83\8a\83v\83g\89ð\90Í\83p\83^\81[\83\93\82ð\93Ç\82Ý\8d\9e\82Ý
635   try
636     SsParser.TagPattern.LoadFromFile(ExtractFilePath(Application.Exename) + 'tagpat.txt');
637     SsParser.MetaPattern.LoadFromFile(ExtractFilePath(Application.ExeName) + 'metapat.txt');
638   except
639     ShowMessage('tagpat.txt, metapat.txt\82ª\8c©\82Â\82©\82è\82Ü\82¹\82ñ\81B');
640     Application.Terminate;
641   end;
642
643   // \8dÄ\90\8e\9e\8aÔ\90\84\92è\83R\83\93\83|\81[\83l\83\93\83g\82É\83p\83\89\83\81\81[\83^\82ð\8ew\92è
644   SsPlayTime.PlayTimeParams := Pref.PlayTimeParams;
645
646   // \83\81\83C\83\93\83E\83B\83\93\83h\83E\82Ì\88Ê\92u\82Æ\83T\83C\83Y\82ð\95\9c\8bA
647   with Pref.SenderWindowPosition do begin
648     Self.Left   := Left;
649     Self.Top    := Top;
650     Self.Width  := Right - Left + 1;
651     Self.Height := Bottom - Top + 1;
652   end;
653
654   // \83^\83X\83N\83o\81[\82Ì\8dÄ\8bN\93®(Explorer\82ª\97\8e\82¿\82½\82Æ\82«)\82ð\8c\9f\8fo\82·\82é
655   FWM_TaskBarCreated := RegisterWindowMessage('TaskBarCreated');
656
657   // \83X\83N\83\8a\83v\83g\95\8e\9a\97ñ\82Ì\8f\89\8aú\89»
658   actClearExecute(Sender);
659   // \83^\83X\83N\83g\83\8c\83C\82É\83A\83C\83R\83\93\82ð\92Ç\89Á
660   ChangeTaskIcon;
661   // \83`\83\83\83\93\83l\83\8b\8eQ\89Á\8aÖ\8cW\82Ì\83^\83u\82Ì\8f\88\97\9d\82È\82Ç(\83`\83\83\83\93\83l\83\8b\95s\8eQ\89Á\82Å\8f\89\8aú\89»)
662 //  UpdateJoinChannelList(nil);
663   //\8f\89\8aú\89»
664   UpdateJoinChannelList;
665
666   // SSTP\8dÄ\91\97\83I\83u\83W\83F\83N\83g
667   FBottleSstp := TBottleSstp.Create(false);
668   with FBottleSstp do begin
669     OnResendCountChange := BottleSstpResendCountChange;
670     OnResendTrying := BottleSstpResendTrying;
671     OnResendEnd := BottleSstpResendEnd;
672   end;
673 end;
674
675 procedure TfrmSender.FormDestroy(Sender: TObject);
676 begin
677   if FScriptBuffer <> nil then
678     FScriptBuffer.Free;
679
680   if FBottleSstp <> nil then begin
681     FBottleSstp.Terminate;
682     FBottleSstp.Free;
683   end;
684
685   with Pref.SenderWindowPosition do begin
686     Left   := Self.Left;
687     Top    := Self.Top;
688     Right  := Self.Left + Self.Width - 1;
689     Bottom := Self.Top + Self.Height - 1;
690   end;
691
692 //  if JoinChannelsBackup <> nil then JoinChannelsBackup.Free;
693
694   ScriptConstList.Save;
695
696   SaveChainRuleList;
697   BottleChainRuleList.Free;
698
699 end;
700
701
702 {
703 procedure TfrmSender.SetConnecting(const Value: boolean);
704 begin
705   FConnecting := Value;
706   if Value then begin
707     StatusBar.Panels[PanelConnecting].Text := '\92Ê\90M\92\86';
708     actStart.Enabled := false;
709     actStop.Enabled := false;
710     actSend.Enabled := false;
711     actVoteMessage.Enabled := false;
712     actAgreeMessage.Enabled := false;
713     mnGetNewId.Enabled := false;
714     Screen.Cursor := crAppStart;
715   end else begin
716     StatusBar.Panels[PanelConnecting].Text := '';
717     actStart.Enabled := true;
718     actStop.Enabled := true;
719     actSend.Enabled := true;
720     frmLog.lvwLogChange(Self, nil, ctState);
721     mnGetNewId.Enabled := Pref.LUID = '';
722     Screen.Cursor := crDefault;
723   end;
724 end;
725 }
726
727 // \83\81\83b\83Z\81[\83W\91\97\90M
728 procedure TfrmSender.actSendExecute(Sender: TObject);
729 var
730   Talk, AGhost: String;
731 //  Command: TStringList;
732   Err: String;
733 //  F: TextFile;
734   Item: TLogItem;
735 begin
736   //\83t\83\89\83O
737   if not Pref.SendAction then
738   begin
739     ShowHintLabel('\93®\8dì\82ª\96¢\90Ý\92è\82Å\82·');
740     Exit;
741   end;
742   if Length(GetScriptText) = 0 then
743   begin
744     ShowMessage('\83X\83N\83\8a\83v\83g\82ª\8bó\82Å\82·\81B');
745     Exit;
746   end;
747
748 {
749   if Pref.LUID = '' then begin
750     NoLuidError;
751     Exit;
752   end;
753   if tabChannel.TabIndex < 0 then begin
754     ShowMessage('\83`\83\83\83\93\83l\83\8b\82É\8eQ\89Á\82µ\82Ä\82¢\82Ü\82¹\82ñ\81B'#13#10+
755       '\83\81\83j\83\85\81[\82©\82ç\81u\83`\83\83\83\93\83l\83\8b\8eQ\89Á\81v\82ð\8ds\82Á\82Ä\82­\82¾\82³\82¢\81B');
756     Exit;
757   end;
758   if ChannelList.Channel[FNowChannel].NoPost then begin
759     Beep;
760     ShowMessage(FNowChannel + ' \82Í\8eó\90M\90ê\97p\82Å\82·');
761     Exit;
762   end;
763 }
764
765   YenETrans;
766   Talk := StringReplace(GetScriptText, #13#10, '', [rfReplaceAll]);
767   Err := DoTrans(Talk, [toWarnMessySurface, toWarnCheck]);
768   if Err <> '' then
769   begin
770     MessageDlg(Err, mtWarning, [mbOk], 0);
771     Exit;
772   end;
773
774 {
775   if Pref.NeedConfirmBeforeSend and FScriptModified then
776   begin
777     MessageDlg('\91\97\90M\91O\82É\83\8d\81[\83J\83\8b\8am\94F\82µ\82Ä\82­\82¾\82³\82¢\81B', mtError, [mbOk], 0);
778     Exit;
779   end;
780
781   if not Pref.NoConfirm then begin
782     if not SendConfirmDialog(FNowChannel, cbxTargetGhost.Text) then Exit;
783   end;
784 }
785
786   AGhost := '';
787   if cbxTargetGhost.ItemIndex > 0 then AGhost := cbxTargetGhost.Text;
788   //\92P\91Ì\83A\83N\83V\83\87\83\93
789   Item := TLogItem.Create;
790   try
791     with Item do
792     begin
793       MID := '';
794       Channel := 'local';
795       LogTime := now();
796       Script := Talk;
797       Ghost := AGhost;
798       Votes := 0;
799       Agrees := 0;
800     end;
801     BottleCnv(Item);
802   except
803     Item.Free;
804   end;
805   ShowHintLabel('[local]\82Ö\91\97\90M\82µ\82Ü\82µ\82½');
806
807 {
808   Command := TStringList.Create;
809   try
810     with Command do begin
811       Add('Command: sendBroadcast');
812       Add('Channel: ' + FNowChannel);
813       Add('LUID: ' + Pref.LUID);
814       Add('Agent: ' + VersionString);
815       if Ghost <> '' then Add('Ghost: ' + Ghost);
816       Add('Talk: ' + Talk);
817     end;
818     PostCommand(Command);
819   finally
820     Command.Free;
821   end;
822
823   //\91\97\90M\83\8d\83O\95Û\91
824   AssignFile(F, ExtractFilePath(Application.ExeName) + SentLogFile);
825   if FileExists(ExtractFilePath(Application.ExeName) + SentLogFile) then
826     Append(F)
827   else
828     Rewrite(F);
829   WriteLn(F, Format('%s,%s,%s,%s', [FNowChannel, Ghost, FormatDateTime('yy/mm/dd hh:nn:ss', Now), Talk]));
830   Flush(F);
831   CloseFile(F);
832 }
833 end;
834
835 {
836 procedure TfrmSender.BeginConnect;
837 begin
838   if Pref.LUID = '' then begin
839     NoLuidError;
840     Exit;
841   end;
842   IdSlpp20.LUID := Pref.LUID;
843   self.Cursor := crHourGlass;
844   if IdSlpp20.Connected then IdSlpp20.Disconnect;
845   if Pref.UseHttpProxy then begin
846     IdSlpp20.Host := Pref.ProxyAddress;
847     IdSlpp20.Port := Pref.ProxyPort;
848     IdSlpp20.ProxyMode := true;
849   end else begin
850     IdSlpp20.Host := Pref.BottleServer;
851     IdSlpp20.Port := Pref.BottleServerPort;
852     IdSlpp20.ProxyMode := false;
853   end;
854   IdSlpp20.ConnectServer;
855   self.Cursor := crDefault;
856 end;
857 }
858
859 {
860 procedure TfrmSender.EndConnect;
861 begin
862   IdSlpp20.OnDisconnect := nil;
863   IdSlpp20.Disconnect;
864 end;
865 }
866
867 {
868 procedure TfrmSender.SetAdded(const Value: boolean);
869 begin
870   if FAdded = Value then Exit;
871   FAdded := Value;
872   if Value then begin
873     StatusText := 'SSTP Bottle \83T\81[\83o\82É\90Ú\91±\82³\82ê\82Ä\82¢\82Ü\82·';
874     ChangeTaskIcon;
875   end else begin
876     StatusText := '\83T\81[\83o\82Æ\82Ì\90Ú\91±\82ª\90Ø\82ê\82Ä\82¢\82Ü\82·!';
877     ChangeTaskIcon;
878     ShowHintLabel('SSTP Bottle\83T\81[\83o\82©\82ç\90Ø\92f\82³\82ê\82Ü\82µ\82½!', WarningColor);
879   end;
880 end;
881 }
882
883 {
884 procedure TfrmSender.HTTPSuccess(Sender: TObject);
885 var Str, ResStr, Command: String;
886     HeadValue: THeadValue;
887     i: integer;
888     SetChannel: TStringList;
889 begin
890   Connecting := false;
891   Str := (Sender as THttpDownloadThread).RecvString;
892   HeadValue := nil;
893   try
894     try
895       HeadValue := THeadValue.Create(Str);
896     except
897       Beep;
898       frmMessageBox.ShowMessage('SSTP Bottle\83T\81[\83o\82ª\89ð\90Í\82Å\82«\82È\82¢\83G\83\89\81[\82ð\95Ô\82µ\82Ü\82µ\82½\81B');
899       Exit;
900     end;
901     Command := HeadValue['Command'];
902     ResStr  := HeadValue['Result'];
903     if ResStr = 'Err' then begin
904       if HeadValue['ExtraMessage'] <> '' then begin
905         Beep;
906         frmMessageBox.ShowMessage('SSTP Bottle\83T\81[\83o\82ª\8e\9f\82Ì\83G\83\89\81[\82ð\95Ô\82µ\82Ü\82µ\82½:'#13#10 +
907                      HeadValue['ExtraMessage']);
908       end else begin
909         Beep;
910         frmMessageBox.ShowMessage('SSTP Bottle\83T\81[\83o\82ª\89½\82ç\82©\82Ì\83G\83\89\81[\82ð\95Ô\82µ\82Ü\82µ\82½\81B');
911       end;
912     end;
913     if (Command = 'sendBroadcast') and (ResStr = 'OK') then begin
914       ShowHintLabel(HeadValue['Channel'] + ' \82Ì ' + HeadValue['SentNum'] +
915                     '\90l\82É\91\97\90M\82µ\82Ü\82µ\82½');
916       //\83S\81[\83X\83g\82ð\83f\83t\83H\83\8b\83g\82É\96ß\82·
917       if Pref.ResetIfGhostAfterSend then begin
918         actResetGhostExecute(Self);
919       end;
920       //\83X\83N\83\8a\83v\83g\82ð\83N\83\8a\83A
921       if Pref.ClearAfterSend then begin
922         actClear.Execute;
923       end;
924     end else if (Command = 'sendBroadcast') and (ResStr <> 'OK') then begin
925       ShowHintLabel('\83\81\83b\83Z\81[\83W\82ð\91\97\90M\82Å\82«\82Ü\82¹\82ñ\82Å\82µ\82½', WarningColor);
926     end;
927     if (Command = 'getNewId') then begin
928       if ResStr = 'OK' then begin
929         Pref.LUID := HeadValue['NewID'];
930         ShowHintLabel('LUID\8eæ\93¾\8a®\97¹\81B');
931         frmMessageBox.ShowMessage('\8f\89\89ñ\8bN\93®\82Ì\82½\82ß\81A' +
932           'SSTP Bottle\83T\81[\83o\90Ú\91±\97p\82ÌID(LUID)\82ð\90V\8bK\8eæ\93¾\82µ\82Ü\82µ\82½\81B'#13#10 +
933           'LUID: ' + Pref.LUID +  #13#10 +
934           '\90Ý\92è\83t\83@\83C\83\8b\82ð\8dí\8f\9c\82·\82é\82±\82Æ\82Å\81ALUID\82Í\8e©\97R\82É\8eæ\93¾\82Å\82«\82Ü\82·\82ª\81A' +
935           'LUID\82Ì\8eg\97p\8eÀ\90Ñ\82É\89\9e\82\82Ä\93Á\93T\82ª\82 \82é\82©\82à\82µ\82ê\82Ü\82¹\82ñ\82Ì\82Å\81A' +
936           '\8fo\97\88\82é\82¾\82¯\93¯\82\82à\82Ì\82ð\8eg\82Á\82Ä\82­\82¾\82³\82¢\81B\8fÚ\8d×\82Í\83w\83\8b\83v\82ð\82²\97\97\82­\82¾\82³\82¢\81B');
937         mnGetNewId.Enabled := false;
938         BeginConnect;
939       end else begin
940         ShowHintLabel('LUID\8eæ\93¾\82É\8e¸\94s\82µ\82Ü\82µ\82½');
941       end;
942     end;
943     if (Command = 'voteMessage') then begin
944       if ResStr = 'OK' then begin
945         ShowHintLabel('\83\81\83b\83Z\81[\83W\82É\93\8a\95[/\93¯\88Ó\82µ\82Ü\82µ\82½\81B\95[\90\94: ' + HeadValue['Votes']);
946       end;
947     end;
948     if (Command = 'getChannels') and (ResStr = 'OK') then begin
949       UpdateChannelInfo(HeadValue);
950       SetChannel := nil;
951       try
952         if FAutoAddAfterGetChannel then begin
953           SetChannel := TStringList.Create;
954           if JoinChannelsBackup <> nil then begin
955             //\88ê\92U\83`\83\83\83\93\83l\83\8b\8eQ\89Á\82É\90¬\8c÷\82µ\82½\8cã\82È\82ç\8dÅ\8cã\82É\8eQ\89Á\82µ\82Ä\82¢\82½\83`\83\83\83\93\83l\83\8b
956             SetChannel.Assign(JoinChannelsBackup);
957           end else begin
958             //\8a®\91S\82É\8f\89\89ñ\8bN\93®\82Ì\8fê\8d\87\82Í\83v\83\8c\83t\83@\83\8c\83\93\83X\82©\82ç\93o\98^\95ª\82ð\8eæ\93¾
959             for i := 0 to Pref.AutoJoinChannels.Count-1 do begin
960               if ChannelList.Channel[Pref.AutoJoinChannels[i]] <> nil then
961                 SetChannel.Add(Pref.AutoJoinChannels[i]);
962             end;
963           end;
964         end else begin
965           Application.CreateForm(TfrmChannelList, frmChannelList);
966           try
967             if frmChannelList.Execute(ChannelList, JoinChannels) then begin
968               SetChannel := TStringList.Create;
969               SetChannel.Assign(frmChannelList.JoinList);
970             end;
971           finally
972             frmChannelList.Release;
973           end;
974         end;
975         if SetChannel <> nil then PostSetChannel(SetChannel);
976       finally
977         if SetChannel <> nil then FreeAndNil(SetChannel);
978       end;
979     end;
980     if (Command = 'setChannels') then begin
981       if ResStr <> 'OK' then begin
982         Beep;
983         frmMessageBox.ShowMessage('\83`\83\83\83\93\83l\83\8b\90Ý\92è\82É\8e¸\94s\82µ\82Ü\82µ\82½\81B\82à\82¤\88ê\93x\93o\98^\82µ\82È\82¨\82µ\82Ä\82­\82¾\82³\82¢');
984         ShowHintLabel('\83`\83\83\83\93\83l\83\8b\90Ý\92è\82É\8e¸\94s\82µ\82Ü\82µ\82½', WarningColor);
985       end;
986     end;
987     if HeadValue['ExtraTip'] <> '' then ShowHintLabel(HeadValue['ExtraTip']);
988   finally
989     HeadValue.Free;
990   end;
991 end;
992 }
993
994 procedure TfrmSender.actStartClick(Sender: TObject);
995 begin
996   ShowHintLabel('SSTP Bottle\83T\81[\83o\82É\82Í\90Ú\91±\82Å\82«\82Ü\82¹\82ñ', WarningColor);
997 {
998   if Pref.LUID = '' then begin
999     NoLuidError;
1000     Exit;
1001   end;
1002   if not IdSlpp20.Connected then begin
1003     FBeginConnectFailCount := 0; // \8e©\93®\8dÄ\90Ú\91±\83J\83E\83\93\83^\83\8a\83Z\83b\83g
1004     BeginConnect;
1005   end;
1006   if Added then begin
1007     FAutoAddAfterGetChannel := false;
1008     PostCommand(['Command: getChannels']);
1009   end;
1010 }
1011 end;
1012
1013 procedure TfrmSender.actStopExecute(Sender: TObject);
1014 begin
1015   ShowHintLabel('SSTP Bottle\83T\81[\83o\82É\82Í\90Ú\91±\82Å\82«\82Ü\82¹\82ñ', WarningColor);
1016 {
1017   // \8b­\90§\8dÄ\90Ú\91±\82ð\8ds\82¤
1018   FBeginConnectFailCount := 0; // \8e©\93®\8dÄ\90Ú\91±\83J\83E\83\93\83^\83\8a\83Z\83b\83g
1019   RetryBeginConnect;
1020 }
1021 end;
1022
1023 procedure TfrmSender.FormShow(Sender: TObject);
1024 begin
1025   if FBooted or Application.Terminated then Exit;
1026 {
1027   //LUID\82ª\8eæ\93¾\82³\82ê\82Ä\82¢\82ê\82Î\91\81\91¬\93o\98^\81B\82»\82¤\82Å\82È\82¯\82ê\82ÎLUID\8eæ\93¾\81B
1028   if Pref.LUID <> '' then BeginConnect
1029   else mnGetNewIdClick(Self);
1030   FAutoAddAfterGetChannel := Pref.AutoStart;
1031 }
1032   FBooted := true;
1033   if NOT Pref.NotShowLog then frmLog.Show;  // \8bN\93®\8e\9e\82É\83\8d\83O\83E\83B\83\93\83h\83E\82ð\95\\8e¦\82·\82é\82©
1034   frmSurfacePreview.Show;
1035   Self.Show;
1036   SakuraSeeker.BeginDetect;
1037   SakuraSeekerDetectResultChanged(self);
1038   SetScriptAutoBackup;                      // \8e©\93®\95Û\91\90Ý\92è\82Ì\83\8d\81[\83h
1039 {
1040   if (SakuraSeeker.Count = 0) and (Pref.NotWarnGhostIsntExists = False) then
1041     frmMessageBox.ShowMessage('\83S\81[\83X\83g(SSTP\83T\81[\83o)\82ª1\82Â\82à\8bN\93®\82µ\82Ä\82¢\82Ü\82¹\82ñ\81B'#13#10 +
1042       'SSTP Bottle\82ð\97\98\97p\82·\82é\82½\82ß\82É\82Í\81A\83S\81[\83X\83g\82ð\93¯\8e\9e\82É\8bN\93®\82µ\82Ä\82­\82¾\82³\82¢\81B'#13#10 +
1043       '\8fÚ\8d×\82Í\83w\83\8b\83v\82ð\82²\97\97\89º\82³\82¢\81B');
1044 }
1045 end;
1046
1047 procedure TfrmSender.actAboutClick(Sender: TObject);
1048 var Str: String;
1049 begin
1050   Str := VersionString + #13#10 + BottleDisclaimer + #13#10#13#10;
1051   Str := Str + Format('Compiler Version: %f', [CompilerVersion]);
1052   frmMessageBox.ShowMessage(Str);
1053 end;
1054
1055 procedure TfrmSender.actExitClientExecute(Sender: TObject);
1056 begin
1057   Close;
1058 end;
1059
1060 procedure TfrmSender.actClearExecute(Sender: TObject);
1061 var
1062   Script, Default: String;
1063 begin
1064   Script := StringReplace(GetScriptText, #13#10, '', [rfReplaceAll]);
1065   Default := StringReplace(Pref.DefaultScript, '|', '', [rfReplaceAll]);
1066   Default := StringReplace(Default, #13#10, '', [rfReplaceAll]);
1067
1068   if (Pref.AutoClip) and (Length(GetScriptText) > 0) and (Script <> Default) then
1069     actSendToLogWindow.Execute
1070   else
1071     ClearEditor;
1072 end;
1073
1074 procedure TfrmSender.memScriptChange(Sender: TObject);
1075 begin
1076   //\83G\83f\83B\83^\93à\97e\82ª\95Ï\8dX\82³\82ê\82½\82Æ\82«
1077   EditerStatusChange;             // \83X\83e\81[\83^\83X\8dX\90V\82Ö
1078   FScriptBackupModified := true;  // \8e©\93®\95Û\91\8dÄ\8aJ
1079   SetFileModified(true);          // \83t\83@\83C\83\8b\95Ï\8dX\83t\83\89\83O
1080 end;
1081
1082 procedure TfrmSender.EditerStatusChange;
1083 var
1084   Script: String;
1085   Text: String;
1086 begin
1087   // \83X\83e\81[\83^\83X\95Ï\8dX
1088   // \93à\97e\82ª\83h\83\89\83b\83O\82³\82ê\82½\82Æ\82«\82à\8cÄ\82Ñ\8fo\82³\82ê\82é
1089   Script := StringReplace(GetScriptText, #13#10, '', [rfReplaceAll]);
1090   if Pref.NotScriptTime then
1091     // \83o\83C\83g\90\94\82¾\82¯\95\\8e¦
1092     Text := Format('%d\83o\83C\83g', [Length(Script)])
1093   else
1094     // \83o\83C\83g\90\94\81{\97\\91ª\8e\9e\8aÔ\95\\8e¦
1095     Text := Format('%d\83o\83C\83g/%d\95b', [Length(Script), SsPlayTime.PlayTime(Script) div 1000]);
1096   StatusBar.Panels[PanelBytes].Text := Text;
1097 //  FScriptModified := true;
1098   EditorPreview;
1099 end;
1100
1101 procedure TfrmSender.mnStayOnTopClick(Sender: TObject);
1102 begin
1103   Pref.StayOnTop := not Pref.StayOnTop;
1104   mnStayOnTop.Checked := Pref.StayOnTop;
1105   if Pref.StayOnTop then begin
1106     FormStyle := fsStayOnTop;
1107   end else begin
1108     FormStyle := fsNormal;
1109   end;
1110   Show;
1111 end;
1112
1113 function TfrmSender.GetScriptText: String;
1114 begin
1115   Result := memScript.Lines.Text;
1116 end;
1117
1118 procedure TfrmSender.mnConstClick(Sender: TObject);
1119 var i: integer;
1120 begin
1121   i := (Sender as TMenuItem).Tag;
1122   memScript.SelText := ScriptConstList.GetConstByID(i).ConstText;
1123 end;
1124
1125 procedure TfrmSender.actEditConstExecute(Sender: TObject);
1126 begin
1127   ScriptConstList.LoadFromDir(FConstDir);
1128   try
1129     Application.CreateForm(TfrmConstEditor, frmConstEditor);
1130     frmConstEditor.Execute;
1131     ScriptConstList.Save;
1132   finally
1133     frmConstEditor.Release;
1134   end;
1135   ConstructMenu(false);
1136 end;
1137
1138 procedure TfrmSender.mnTaskBarClick(Sender: TObject);
1139 begin
1140   Application.Minimize;
1141   WindowState := wsNormal;
1142 end;
1143
1144 procedure TfrmSender.FormClose(Sender: TObject; var Action: TCloseAction);
1145 begin
1146 //  EndConnect;
1147   TaskTray.Registered := false;
1148 end;
1149
1150 procedure TfrmSender.ApplicationEventsMinimize(Sender: TObject);
1151 begin
1152   Visible := false;
1153   Application.ShowMainForm := false;
1154   ShowWindow(Application.Handle, SW_HIDE);
1155 end;
1156
1157 procedure TfrmSender.ApplicationEventsRestore(Sender: TObject);
1158 begin
1159   Application.ShowMainForm := true;
1160   Visible := true;
1161 end;
1162
1163 procedure TfrmSender.mnTaskRestoreClick(Sender: TObject);
1164 begin
1165   Application.Restore;
1166 end;
1167
1168 procedure TfrmSender.TaskTrayDblClick(Seft: TObject; Button: TMouseButton);
1169 begin
1170   Application.Restore;
1171 end;
1172
1173 procedure TfrmSender.FormActivate(Sender: TObject);
1174 begin
1175   memScript.SetFocus;
1176 end;
1177
1178 procedure TfrmSender.mnTaskNewMessageClick(Sender: TObject);
1179 begin
1180   Application.Restore;
1181   actClearExecute(Sender);
1182 end;
1183
1184 procedure TfrmSender.ChangeTaskIcon;
1185 var Ico: TIcon;
1186 //    IcoNum: integer;
1187 begin
1188 {
1189   if Added then begin
1190     if Sleeping then IcoNum := IconSleep else IcoNum := IconConnected;
1191   end else begin
1192     if Sleeping then IcoNum := IconSleepDisconnected
1193     else IcoNum := IconDisconnected;
1194   end;
1195 }
1196   try
1197     Ico := TIcon.Create;
1198     try
1199       imgIcon.GetIcon(IconYC, Ico);  // IconNum
1200       TaskTray.Icon := Ico;
1201       TaskTray.Registered := true;
1202     finally
1203       Ico.Free;
1204     end;
1205   except
1206     ; // PC\82Ì\95\89\89×\82ª\8d\82\82¢\82Æ4\95b\88È\93à\82É\83^\83X\83N\83g\83\8c\83C\93o\98^\82Å\82«\82¸\81A
1207       // \83V\83F\83\8b\82ª\83n\83\93\83O\82µ\82Ä\82¢\82é\82Æ\94»\92f\82³\82ê\82Ä\97á\8aO\82ª\94­\90\82·\82é\81B
1208       // \82»\82Ì\8fê\8d\87\82Í\83G\83\89\81[\82ð\96³\8e\8b\82·\82é
1209   end;
1210 end;
1211
1212 {
1213 procedure TfrmSender.SetStatusText(const Value: String);
1214 begin
1215   FStatusText := Value;
1216   StatusBar.Panels[PanelStatus].Text := Value;
1217 end;
1218 }
1219
1220 procedure TfrmSender.ApplicationEventsHint(Sender: TObject);
1221 var id: integer;
1222 begin
1223   if Length(Application.Hint) > 0 then
1224   begin
1225     StatusBar.Panels[PanelStatus].Text := GetLongHint(Application.Hint);
1226     Application.HintColor := clInfoBk;
1227\91\97\90M\83{\83^\83\93\8aÖ\98A
1228     if (Application.Hint = SendButtonLongHint)
1229     and (FNowChannel <> '') then
1230     begin
1231       //\91\97\90M\83{\83^\83\93\82Ì\8fê\8d\87\82Í\91¬\8dU\8fo\82·
1232       Application.HintColor := clYellow;
1233       Application.ActivateHint(Mouse.CursorPos);
1234     end;
1235 }
1236     if IsSurfaceTag(Application.Hint, id) and Pref.SurfacePreviewOnHint then
1237     begin
1238       // \83T\81[\83t\83B\83X\83v\83\8c\83r\83\85\81[
1239       DoSurfacePreview(id, spHint);
1240     end;
1241   end else
1242   begin
1243     StatusBar.Panels[PanelStatus].Text := FStatusText;
1244     frmSurfacePreview.HideAway;
1245   end;
1246 end;
1247
1248 procedure TfrmSender.ConstructMenu(Simple: boolean);
1249 begin
1250   BuildMenu(mnPopConst, Simple);
1251   BuildMenu(mnPopUpConst.Items, Simple);
1252   BuildMenu(ConstBarMenu.Items, Simple);
1253   //ConstMenuBar.Menu := nil;
1254   ConstMenuBar.AutoSize := false;
1255   ConstMenuBar.Width := 1000;
1256   // \83\81\83j\83\85\81[\82ð\88ê\93x\8fÁ\82·
1257   // \82È\82º\82©2\89ñ\96Ú\88È\8d~\82Ì\95\\8e¦\82ª\82¨\82©\82µ\82­\82È\82é
1258   ConstMenuBar.Menu := nil;
1259   ConstMenuBar.Menu := ConstBarMenu;
1260   ConstMenuBar.AutoSize := true;
1261 end;
1262
1263 procedure TfrmSender.memScriptKeyDown(Sender: TObject; var Key: Word;
1264   Shift: TShiftState);
1265 var Pos: TPoint;
1266     Func: TReturnKeyFunction;
1267 begin
1268   if (Key = VK_RETURN) then begin
1269     if (ssShift in Shift) then
1270       Func := Pref.WhenShiftReturn
1271     else if (ssCtrl in Shift) then
1272       Func := Pref.WhenCtrlReturn
1273     else
1274       Func := Pref.WhenReturn;
1275     case Func of
1276       kfConstText: begin
1277         with tbtnInsertConst do
1278           Pos := tbtnInsertConst.ClientToScreen(Point(0, Height));
1279         mnPopUpConst.Popup(Pos.X, Pos.Y);
1280       end;
1281       kfYenN: begin
1282         memScript.SelText := '\n';
1283         Key := 0;
1284       end;
1285       kfYenNReturn: begin
1286         memScript.SelText := '\n'#13#10;
1287       end;
1288       kfReturn: begin
1289         memScript.SelText := #13#10
1290       end;
1291     end;
1292   end;
1293 end;
1294
1295 procedure TfrmSender.mnShowToolBarClick(Sender: TObject);
1296 begin
1297   mnShowToolBar.Checked := not mnShowToolBar.Checked;
1298   Pref.ShowToolBar := mnShowToolBar.Checked;
1299   UpdateLayout;
1300 end;
1301
1302 procedure TfrmSender.mnShowConstBarClick(Sender: TObject);
1303 begin
1304   mnShowConstBar.Checked := not mnShowConstBar.Checked;
1305   Pref.ShowConstBar := mnShowConstBar.Checked;
1306   UpdateLayout;
1307 end;
1308
1309 procedure TfrmSender.UpdateLayout;
1310 begin
1311   with SakuraScriptFountain do begin
1312     TagColor.Color := Pref.MarkUpColor;
1313     TagErrorColor.Color := Pref.MarkErrorColor;
1314     Scope0Color.Color := Pref.TalkColorH;
1315     Scope1Color.Color := Pref.TalkColorU;
1316     SynchronizedColor.Color := Pref.TalkColorS;
1317   end;
1318   memScript.Ruler.Visible := Pref.ShowRuler;
1319   memScript.Ruler.Color := Pref.TextColor;
1320   memScript.Color := Pref.BgColor;
1321
1322   ToolBar.Visible := Pref.ShowToolBar;
1323   ConstMenuBar.Visible := Pref.ShowConstBar;
1324   ToolBar.Top := 0;
1325   //
1326   with tabChannel do begin
1327     TabPosition := Pref.TabPosition;
1328     case Pref.TabPosition of
1329       tpTop:    Align  := alTop;
1330       tpBottom: Align := alBottom;
1331     end;
1332     TabWidth := Pref.TabWidth;
1333   end;
1334 end;
1335
1336 function TfrmSender.DoTrans(var Script: String;
1337   Options: TScriptTransOptions; out FoundURL: boolean): String;
1338 var UrlCancel, Mark: String;
1339     Url, UrlName: array[0..6] of String;
1340     i, j, u, UrlCount: integer;
1341     LastSurfaceH, LastSurfaceU: integer;
1342     UnyuTalking: boolean;
1343     QuickSection, Synchronize: boolean;
1344     Warnings: TStringList;
1345 begin
1346   UrlCount := 0;
1347   LastSurfaceH := 0;
1348   LastSurfaceU := 10;
1349   UnyuTalking := false;
1350   QuickSection := false;
1351   Synchronize := false;
1352   SsParser.LeaveEscape := true;
1353   SsParser.EscapeInvalidMeta := false;
1354   SsParser.InputString := Script;
1355   Script := '';
1356   Warnings := TStringList.Create;
1357   try
1358     for i := 0 to SsParser.Count-1 do begin
1359       if SsParser[i] = '\t' then begin
1360         if not(toIgnoreTimeCritical in Options) then
1361           Script := Script + '\t';
1362       end else if SsParser[i] = '\e' then begin
1363         Continue;
1364       end else if (SsParser.Match(SsParser[i], '\URL%b') > 0) then begin
1365         if toConvertURL in Options then begin
1366           UrlCount := 0; //\91O\82ÌURL\83^\83O\82Ì\89e\8b¿\82ð\96³\8e\8b\81B
1367           for u := 7 downto 1 do begin
1368             if (SsParser.Match(SsParser[i],
1369                 '\URL%b'+StringReplace(StringOfChar('-', u*2),
1370                 '-', '%b', [rfReplaceAll]))) > 0 then begin
1371               for j := 1 to u do begin
1372                 Url[UrlCount] := SsParser.GetParam(SsParser[i], UrlCount*2+2);
1373                 UrlName[UrlCount] := SsParser.GetParam(SsParser[i], UrlCount*2+3);
1374                 if UrlName[UrlCount] = '' then UrlName[UrlCount] := Url[UrlCount];
1375                 if Pos('http://', Url[UrlCount]) > 0 then Inc(UrlCount);
1376               end;
1377             end;
1378             if UrlCount > 0 then UrlCancel := SsParser.GetParam(SsParser[i], 1);
1379             if UrlCancel = '' then UrlCancel := '\8ds\82©\82È\82¢\81@\81@\81@\81@';
1380           end;
1381           if SsParser.Match(SsParser[i], '\URL%b%b') = 0 then begin //\8aÈ\88Õ\94ÅURL\95Ï\8a·
1382             //\8aÈ\88Õ\8c`\8e®\URL\83^\83O\95Ï\8a·
1383             Url[0] := SsParser.GetParam(SsParser[i], 1);
1384             UrlName[0] := '\8ds\82­\81@\81@\81@\81@\81@\81@';
1385             UrlCancel  := '\8ds\82©\82È\82¢\81@\81@\81@\81@';
1386             if Pos('http://', Url[0]) > 0 then begin
1387               UrlCount := 1;
1388               if not QuickSection then
1389                 Script := Script + '\_q' + Url[0] + '\_q'
1390               else
1391                 Script := Script + Url[0];
1392             end;
1393           end;
1394         end else Script := Script + SsParser[i];
1395       end else begin
1396         Mark := SsParser[i];
1397         if Mark = '\h' then begin
1398           UnyuTalking := false;
1399           if toHUTagTo01Tag in Options then Mark := '\0';
1400           if Synchronize and Pref.WarnScopeChangeInSynchronize then
1401             Warnings.Add('\83V\83\93\83N\83\8d\83i\83C\83Y\83h\83Z\83N\83V\83\87\83\93\92\86\82É\h\82ª\82 \82è\82Ü\82·\81B');
1402         end else if Mark = '\u' then begin
1403           UnyuTalking := true;
1404           if toHUTagTo01Tag in Options then Mark := '\1';
1405           if Synchronize and Pref.WarnScopeChangeInSynchronize then
1406             Warnings.Add('\83V\83\93\83N\83\8d\83i\83C\83Y\83h\83Z\83N\83V\83\87\83\93\92\86\82É\u\82ª\82 \82è\82Ü\82·\81B');
1407         end else if Mark = '\_q' then begin
1408           QuickSection := not QuickSection;
1409         end else if Mark = '\_s' then begin
1410           Synchronize := not Synchronize;
1411         end else if SsParser.Match(Mark, '\s%b') > 0 then begin
1412           if UnyuTalking then begin
1413             LastSurfaceU := StrToIntDef(SsParser.GetParam(Mark, 1),
1414                                         LastSurfaceU);
1415           end else begin
1416             LastSurfaceH := StrToIntDef(SsParser.GetParam(Mark, 1),
1417                                         LastSurfaceH);
1418           end;
1419         end else if SsParser.Match(Mark, '\s%d') > 0 then begin
1420           if UnyuTalking then begin
1421             LastSurfaceU := StrToIntDef(Mark[3], LastSurfaceU);
1422           end else begin
1423             LastSurfaceH := StrToIntDef(Mark[3], LastSurfaceH);
1424           end;
1425         end;
1426         Script := Script + Mark;
1427       end;
1428     end;
1429     if UrlCount > 0 then begin
1430       FoundUrl := true;
1431       Script := Script + '\h\n';
1432       if not (toNoChoice in Options) then begin
1433         for i := 0 to UrlCount-1 do begin
1434           Script := Script + Format('\q%d[%s][%s]',
1435                       [i, SsParser.EscapeParam(Url[i]), UrlName[i]]);
1436         end;
1437         Script := Script + Format('\q%d[#cancel][%s]', [UrlCount, UrlCancel]);
1438         //Script := Script + '\z'; //\8dÅ\90Vphase\82Å\82Í\8dí\8f\9c
1439       end else begin
1440         Script := Script + '\h';
1441         for i := 0 to UrlCount-1 do begin
1442           Script := Script + Format('\n{%s}(%s)', [UrlName[i], Url[i]]);
1443         end;
1444         Script := Script + Format('\n{%s}', [UrlCancel]);
1445       end;
1446     end else
1447       FoundUrl := false;
1448     //\83X\83N\83\8a\83v\83g\82Ì\8dÅ\8cã\82É\83E\83F\83C\83g\91}\93ü
1449     if toWaitScriptEnd in Options then begin
1450       i := Pref.WaitScriptEnd;
1451       while i > 0 do begin
1452         if i > 9 then begin
1453           Script := Script + '\w9';
1454           Dec(i, 9);
1455         end else begin
1456           Script := Script + '\w' + IntToStr(i);
1457           i := 0;
1458         end;
1459       end;
1460     end;
1461
1462     Script := Script + '\e';
1463     RegExp.Subst('s/\r\n//gk', Script);
1464
1465     //\83^\83O\83`\83F\83b\83N\8aÖ\98A
1466     for i := 0 to SsParser.Count-1 do begin
1467       if SsParser.MarkUpType[i] = mtTagErr then begin
1468         Result := '"' + SsParser[i] + '"'#13#10 +
1469                   '\82Í\81ASSTP Bottle\82Å\94F\82ß\82ç\82ê\82È\82¢\82©\81A\94F\8e¯\82Å\82«\82È\82¢\83^\83O\82Å\82·\81B';
1470         Exit;
1471       end;
1472     end;
1473     if (SsParser[0] <> '\t') and Pref.WarnYenTNotExist then begin
1474       Warnings.Add('\83X\83N\83\8a\83v\83g\82ª\t\82©\82ç\8en\82Ü\82Á\82Ä\82¢\82Ü\82¹\82ñ\81B');
1475     end;
1476
1477     //\83`\83F\83b\83N
1478     if (Warnings.Count > 0) and (toWarnCheck in Options) then begin
1479       if MessageDlg(Warnings.Text + #13#10 + '\91\97\90M\82µ\82Ü\82·\82©?', mtWarning,
1480                     mbOkCancel, 0) = mrCancel then
1481         Result := Warnings.Text;
1482     end;
1483   finally
1484     Warnings.Free;
1485   end;
1486 end;
1487
1488 function TfrmSender.DoTrans(var Script: String;
1489   Options: TScriptTransOptions): String;
1490 var dum: boolean;
1491 begin
1492   Result := DoTrans(Script, Options, dum);
1493 end;
1494
1495 procedure TfrmSender.mnGoToHPClick(Sender: TObject);
1496 begin
1497   OpenBrowser(Pref.HomePage);
1498 end;
1499
1500 procedure TfrmSender.ShowHintLabel(const Mes: String; Col: TColor);
1501 begin
1502   lblMessage.Caption := Mes;
1503   lblMessage.Font.Color := Col;
1504   lblMessage.Visible := true;
1505   LabelTimer.Enabled := false;
1506   LabelTimer.Enabled := true;
1507 end;
1508
1509 procedure TfrmSender.LabelTimerTimer(Sender: TObject);
1510 begin
1511   LabelTimer.Enabled := false;
1512   lblmessage.Visible := false;
1513 end;
1514
1515 procedure TfrmSender.actCopyAllExecute(Sender: TObject);
1516 var Str: String;
1517     Clip: TClipBoard;
1518 begin
1519   Str := memScript.Lines.Text;
1520   Clip := ClipBoard();
1521   Clip.SetTextBuf(PChar(Str));
1522 end;
1523
1524 procedure TfrmSender.actCopyAllNoReturnExecute(Sender: TObject);
1525 var Str: String;
1526     Clip: TClipBoard;
1527 begin
1528   Str := memScript.Lines.Text;
1529   RegExp.Subst('s/\r\n//gk', Str);
1530   Clip := ClipBoard();
1531   Clip.SetTextBuf(PChar(Str));
1532 end;
1533
1534 {
1535 procedure TfrmSender.Slpp20SlppEvent(Sender: TObject; EventType: TIdSlppEventType;
1536   const Param: String);
1537 var HeadValue: THeadValue;
1538 begin
1539   HeadValue := THeadValue.Create(Param);
1540   try
1541     case EventType of
1542       etScript, etForceBroadcast, etUnicast: begin
1543         //\83\81\83b\83Z\81[\83W\8eó\90M
1544         DispatchBottle(EventType, HeadValue);
1545       end;
1546
1547       etMemberCount: begin
1548         //\91\8d\8eQ\89Á\8eÒ\90\94
1549         StatusBar.Panels[PanelMembers].Text := HeadValue['Num'] + '\90l'
1550       end;
1551
1552       etChannelCount: begin
1553         //\83`\83\83\83\93\83l\83\8b\95Ê\8eQ\89Á\8eÒ\90\94
1554         try
1555           if HeadValue['Channel'] <> '' then begin
1556             if ChannelList.Channel[HeadValue['Channel']] <> nil then begin
1557               ChannelList.Channel[HeadValue['Channel']].Members := StrToInt(HeadValue['Num']);
1558             end;
1559           end;
1560         except
1561         end;
1562       end;
1563
1564       etConnectOk: begin
1565         ShowHintLabel('SSTP Bottle\83T\81[\83o\82Æ\92Ê\90M\8am\97§\81B');
1566         Added := true;
1567         FBeginConnectFailCount := 0;
1568         //\83`\83\83\83\93\83l\83\8b\8e©\93®\93o\98^
1569         if not Connecting then
1570           PostCommand(['Command: getChannels']);
1571         try
1572           SakuraSeeker.BeginDetect;
1573         except
1574           on E: Exception do ShowHintLabel(E.Message,WarningColor);
1575         end;
1576       end;
1577
1578       etChannelList: begin
1579         UpdateJoinChannelList(HeadValue);
1580         // \8dÅ\8cã\82É\8eQ\89Á\82µ\82Ä\82¢\82½\83`\83\83\83\93\83l\83\8b\82ð\8bL\98^\82·\82é
1581         if JoinChannelsBackup = nil then JoinChannelsBackup := TStringList.Create;
1582         JoinChannelsBackup.Assign(JoinChannels);
1583       end;
1584
1585       etCloseChannel: begin
1586         //\83`\83\83\83\93\83l\83\8b\94p\8e~
1587         if HeadValue['Channel'] <> '' then begin
1588           with JoinChannels do
1589             if IndexOf(HeadValue['Channel']) >= 0 then
1590               Delete(IndexOf(HeadValue['Channel']));
1591           with tabChannel do begin
1592             if Tabs.IndexOf(HeadValue['Channel']) >= 0 then
1593               Tabs.Delete(Tabs.IndexOf(HeadValue['Channel']));
1594             if Tabs.Count > 0 then TabIndex := 0 else TabIndex := -1;
1595             tabChannelChange(self);
1596           end;
1597           ShowHintLabel(HeadValue['Channel'] + '\83`\83\83\83\93\83l\83\8b\82Í\94p\8e~\82³\82ê\82Ü\82µ\82½',
1598                         WarningColor);
1599           frmLog.AddCurrentSystemLog('SYSTEM', HeadValue['Channel'] + '\83`\83\83\83\93\83l\83\8b\82Í\94p\8e~\82³\82ê\82Ü\82µ\82½');
1600           frmMessageBox.ShowMessage(HeadValue['Channel'] + '\83`\83\83\83\93\83l\83\8b\82Í\94p\8e~\82³\82ê\82Ü\82µ\82½');
1601         end;
1602       end;
1603
1604       etForceBroadcastInformation: begin
1605         //\93\8a\95[\81^\93¯\88Ó\81^\82»\82Ì\91¼\83u\83\8d\81[\83h\83L\83\83\83X\83g\8fî\95ñ
1606         if HeadValue['MID'] <> '' then begin
1607           try //Num\82ª\90\94\92l\82Å\82È\82©\82Á\82½\82Æ\82«\91Î\8dô
1608             if HeadValue['Type'] = 'Vote' then begin
1609               frmLog.VoteLog(HeadValue['MID'], StrToIntDef(HeadValue['Num'], 0));
1610             end else if HeadValue['Type'] = 'Agree' then begin
1611               frmLog.AgreeLog(HeadValue['MID'], StrToIntDef(HeadValue['Num'], 0));
1612             end;
1613           except
1614           end;
1615         end;
1616       end;
1617     end;
1618   finally
1619     HeadValue.Free;
1620   end;
1621 end;
1622 }
1623
1624 procedure TfrmSender.BottleSstpResendCountChange(Sender: TObject);
1625 begin
1626   StatusBar.Panels[PanelCount].Text := IntToStr(FBottleSstp.CueCount) + '\8c\8f';
1627   try
1628     TaskTray.TipString := 'Yasagure Client (' +
1629                           IntToStr(FBottleSstp.CueCount) + '\8c\8f)';
1630   except
1631     ; // \83^\83X\83N\83g\83\8c\83C\93o\98^\8e¸\94s\82ð\96³\8e\8b\82·\82é
1632   end;
1633   actClearBottles.Enabled := (FBottleSstp.CueCount > 0);
1634 end;
1635
1636 procedure TfrmSender.actSettingExecute(Sender: TObject);
1637 var
1638   FTBackupMode: boolean;
1639 begin
1640   FTBackupMode := Pref.ScriptBackupMode;
1641   Application.CreateForm(TfrmSetting, frmSetting);
1642   try
1643     frmSetting.Execute;
1644     Pref.SaveSettings;
1645     SaveChainRuleList;
1646   finally
1647     frmSetting.Release;
1648     frmSetting := nil;
1649   end;
1650   //
1651   BuildReplaceMenu(mnPresetReplaceRoot);
1652   UpdateLayout;
1653   tabChannel.Repaint;
1654   frmLog.UpdateWindow;
1655   SetScriptAutoBackup;  //\83X\83N\83\8a\83v\83g\8e©\93®\95Û\91\90Ý\92è\8dX\90V
1656   UpdateChannelList;    //\83`\83\83\83\93\83l\83\8b\83S\81[\83X\83g\8dÄ\83\8d\81[\83h
1657
1658   // \83o\83b\83N\83A\83b\83v\83\82\81[\83h\82ª\95Ï\8dX\82³\82ê\82½\82Æ\82«FScriptBackupFile\82à\95Ï\8dX\82·\82é
1659   if FTBackupMode <> Pref.ScriptBackupMode then
1660     SetFileName(FFileName, false, false);
1661 end;
1662
1663 procedure TfrmSender.memScriptKeyPress(Sender: TObject; var Key: Char);
1664 begin
1665   if (Key = #13) or (Key = #10) then Key := Char(0);
1666 end;
1667
1668 {
1669 procedure TfrmSender.Slpp20Disconnect(Sender: TObject);
1670 begin
1671   Added := false;
1672   UpdateJoinChannelList(nil);
1673   frmLog.AddCurrentSystemLog('SYSTEM', '\83T\81[\83o\82©\82ç\90Ø\92f\82³\82ê\82Ü\82µ\82½');
1674   if not Application.Terminated then RetryBeginConnect;
1675 end;
1676 }
1677
1678 procedure TfrmSender.SetSleeping(const Value: boolean);
1679 begin
1680   FSleeping := Value;
1681   FBottleSstp.ResendSleep := Value;
1682   ChangeTaskIcon;
1683 end;
1684
1685 procedure TfrmSender.actClearBottlesExecute(Sender: TObject);
1686 var Re: integer;
1687 begin
1688   Re := MessageDlg(Format('\96¢\94z\91\97\82Ì%d\8c\8f\82ÌBottle\82ð\91S\95\94\83N\83\8a\83A\82µ\82Ü\82·', [FBottleSstp.CueCount]),
1689                    mtWarning, mbOkCancel, 0);
1690   if Re = mrOk then begin
1691     FBottleSstp.Clear;
1692     frmLog.AllBottleOpened;
1693     frmLog.UpdateWindow;
1694   end;
1695 end;
1696
1697 procedure TfrmSender.SakuraSeekerDetectResultChanged(Sender: TObject);
1698 begin
1699   UpdateIfGhostBox; // \83h\83\8d\83b\83v\83_\83E\83\93\82Ì\92\86\90g\82ð\8f\91\82«\8a·\82¦\82é
1700 end;
1701
1702 {
1703 procedure TfrmSender.UpdateChannelInfo(Dat: THeadValue);
1704 var i: integer;
1705     Ch: TChannelListItem;
1706 begin
1707   ChannelList.Clear;
1708   for i := 1 to Dat.IntData['Count'] do begin
1709     Ch := TChannelListItem.Create;
1710     Ch.Name    := Dat[Format('CH%d_name', [i])];
1711     Ch.Ghost   := Dat[Format('CH%d_ghost', [i])];
1712     Ch.Info    := Dat[Format('CH%d_info', [i])];
1713     Ch.NoPost  := Dat[Format('CH%d_nopost', [i])] = '1';
1714     Ch.Members := Dat.IntData[Format('CH%d_count', [i])];
1715     Ch.WarnPost:= Dat[Format('CH%d_warnpost', [i])] = '1';
1716     ChannelList.Add(Ch);
1717   end;
1718   UpdateLayout;
1719 end;
1720 }
1721
1722 procedure TfrmSender.mnGetNewIdClick(Sender: TObject);
1723 begin
1724   ShowHintLabel('LUID\82Ì\8eæ\93¾\82Í\82Å\82«\82Ü\82¹\82ñ', WarningColor);
1725 //  PostCommand(['Command: getNewId', 'Agent: ' + VersionString]);
1726 end;
1727
1728 {
1729 procedure TfrmSender.NoLuidError;
1730 begin
1731   Beep;
1732   ShowMessage('SSTP Bottle ID\82Ì\8eæ\93¾\82ª\82Ü\82¾\8a®\97¹\82µ\82Ä\82¢\82Ü\82¹\82ñ\81B'#13#10+
1733               '\83w\83\8b\83v\83\81\83j\83\85\81[\82Ì[LUID\8eæ\93¾]\82©\82çID\82ð\8eæ\93¾\82µ\82Ä\82­\82¾\82³\82¢\81B'#13#10+
1734               '\82±\82Ì\91\80\8dì\82ÍClient\8f\89\89ñ\8bN\93®\8e\9e\82É1\89ñ\82¾\82¯\95K\97v\82Å\82·\81B');
1735 end;
1736 }
1737
1738 procedure TfrmSender.tabChannelChange(Sender: TObject);
1739 begin
1740 {
1741   if tabChannel.TabIndex >= 0 then begin
1742     FNowChannel := tabChannel.Tabs[tabChannel.TabIndex];
1743     actSend.Hint := Format('\81u%s\81v\82É\91\97\90M|%s', [FNowChannel, SendButtonLongHint]);
1744   end else begin
1745     FNowChannel := '';
1746     actSend.Hint := '';
1747   end;
1748   tabChannel.Repaint; //\82±\82ê\82ª\82È\82¢\82Æ\90F\82ª\95Ï\82í\82ç\82È\82¢\82±\82Æ\82ª\82 \82é
1749   ConstructMenu(true);
1750 }
1751 end;
1752
1753 procedure TfrmSender.actPrevChannelExecute(Sender: TObject);
1754 begin
1755 {
1756   with tabChannel do begin
1757     if Tabs.Count = 0 then Exit;
1758     if TabIndex=0 then TabIndex := Tabs.Count-1
1759     else TabIndex := TabIndex-1;
1760   end;
1761   tabChannelChange(Self);
1762 }
1763 end;
1764
1765 procedure TfrmSender.actNextChannelExecute(Sender: TObject);
1766 begin
1767 {
1768   with tabChannel do begin
1769     if Tabs.Count = 0 then Exit;
1770     if TabIndex=Tabs.Count-1 then TabIndex := 0
1771     else TabIndex := TabIndex+1;
1772   end;
1773   tabChannelChange(Self);
1774 }
1775 end;
1776
1777 {
1778 procedure TfrmSender.UpdateJoinChannelList(Dat: THeadValue);
1779 var i: integer;
1780     nodat: boolean;
1781 begin
1782   nodat := Dat = nil; //nil\82È\82ç\83`\83\83\83\93\83l\83\8b\91S\89ð\8f\9c
1783   if nodat then Dat := THeadValue.Create('');
1784   JoinChannels.Clear;
1785   for i := 0 to Dat.Count-1 do
1786     if Dat.KeyAt[i] = 'Entry' then begin
1787       if RegExp.Match('m/^(.+?) \((\d+?)\)$/', Dat.ValueAt[i]) then
1788         JoinChannels.Add(RegExp[1]);
1789     end;
1790   with tabChannel do begin
1791     OnChange := nil;
1792     JoinChannels.Sort;
1793     Tabs.BeginUpdate;
1794     Tabs.Clear;
1795     for i := 0 to JoinChannels.Count-1 do begin
1796       //\8eó\90M\90ê\97p\83`\83\83\83\93\83l\83\8b\82Í\95\\8e¦\82µ\82È\82¢
1797       if ChannelList.Channel[JoinChannels[i]] <> nil then
1798         if not ChannelList.Channel[JoinChannels[i]].NoPost then
1799           Tabs.Add(JoinChannels[i]);
1800     end;
1801     Tabs.EndUpdate;
1802     // \8c³\82©\82ç\83`\83\83\83\93\83l\83\8b\82É\8eQ\89Á\82µ\82Ä\82¢\82½\8fê\8d\87\82Í
1803     // \91I\91ð\82³\82ê\82Ä\82¢\82½\83`\83\83\83\93\83l\83\8b\82ª\95Ï\82í\82ç\82È\82¢\82æ\82¤\82É\82·\82é(\83^\83u\82ª\82¸\82ê\82È\82¢\8f\88\97\9d)
1804     TabIndex := 0;
1805     for i := 0 to Tabs.Count-1 do
1806       if Tabs[i] = FNowChannel then TabIndex := i;
1807     if Tabs.Count > 0 then begin
1808       FNowChannel := Tabs[TabIndex];
1809       actSend.Hint := Format('\81u%s\81v\82É\91\97\90M|%s', [FNowChannel, SendButtonLongHint]);
1810     end else begin
1811       FNowChannel := '';
1812       actSend.Hint := Format('\91\97\90M|%s', [SendButtonLongHint]);
1813     end;
1814     Visible := Tabs.Count > 0;
1815     if Tabs.Count > 1 then begin
1816       actNextChannel.Enabled := true;
1817       actPrevChannel.Enabled := true;
1818     end else begin
1819       actNextChannel.Enabled := false;
1820       actPrevChannel.Enabled := false;
1821     end;
1822     OnChange := tabChannelChange;
1823   end;
1824   if nodat then Dat.Free;
1825   if JoinChannels.Count = 0 then begin
1826     Self.Caption := FOriginalCaption + ' - \83`\83\83\83\93\83l\83\8b\82É\8eQ\89Á\82µ\82Ä\82¢\82Ü\82¹\82ñ';
1827     actSend.Enabled := false;
1828   end else begin
1829     Self.Caption := FOriginalCaption;
1830     actSend.Enabled := true;
1831   end;
1832 end;
1833 }
1834
1835 procedure TfrmSender.cbxTargetGhostDropDown(Sender: TObject);
1836 begin
1837   SakuraSeeker.BeginDetect;
1838   UpdateIfGhostBox;
1839 end;
1840
1841 procedure TfrmSender.actShowLogExecute(Sender: TObject);
1842 begin
1843   frmLog.Show;
1844   if frmLog.WindowState = wsMinimized then frmLog.WindowState := wsNormal;
1845 end;
1846
1847 procedure TfrmSender.actSleepExecute(Sender: TObject);
1848 begin
1849   if actSleep.Checked then begin
1850     actSleep.Checked := false;
1851     ShowHintLabel('\83X\83\8a\81[\83v\82ð\89ð\8f\9c\82µ\82Ü\82µ\82½');
1852   end else begin
1853     actSleep.Checked := true;
1854     ShowHintLabel('\83X\83\8a\81[\83v\82ð\90Ý\92è\82µ\82Ü\82µ\82½');
1855   end;
1856   Sleeping := actSleep.Checked;
1857   ChangeTaskIcon;
1858 end;
1859
1860
1861 procedure TfrmSender.DispatchBottle(Dat: THeadValue; LogTime: TDateTime;
1862   Vote, Agree: integer);
1863 //procedure TfrmSender.DispatchBottle(EventType: TIdSlppEventType;
1864 //  Dat: THeadValue);
1865 var Opt: TSstpSendOptions;
1866     Event: TBottleChainBottleEvent;
1867     Script, Sender, Ghost, Channel, ErrorMes: String;
1868     BreakFlag, NoDispatch: boolean;
1869     Sound, LogName: String;
1870     i, j, k, SkipCount: integer;
1871     Rule: TBottleChainRule;
1872     Action: TBottleChainAction;
1873     LogNameList: TStringList;
1874     CueItem: TLogItem;
1875     ReplaceHash: THeadValue;
1876 begin
1877   Channel := Dat['Channel'];
1878
1879   Opt := [];
1880   if Pref.NoTranslate then Opt := Opt + [soNoTranslate];
1881   if Pref.NoDescript  then Opt := Opt + [soNoDescript];
1882 {
1883   case EventType of
1884     etScript: Sender := Channel;
1885     etForceBroadcast: Sender := '\81y\82¨\92m\82ç\82¹\81z';
1886     etUnicast: Sender := Dat['SenderUID'];
1887   end;
1888 }
1889   Sender := Channel; //\92Ê\8fí
1890
1891   //\96Ú\95W\83S\81[\83X\83g(\83I\81[\83o\81[\83\89\83C\83h\88È\91O)\8c\88\92è
1892   if Dat['IfGhost'] <> '' then begin
1893     Ghost := Dat['IfGhost'];
1894   end else begin
1895     if ChannelList.Channel[Channel] <> nil then
1896       Ghost := ChannelList.Channel[Channel].Ghost
1897     else
1898       Ghost := '';
1899   end;
1900   Dat['TargetGhost'] := Ghost;
1901
1902   // \83\81\83^\95\8e\9a\8f\80\94õ
1903   ReplaceHash := THeadValue.Create;
1904   ReplaceHash['%channel%'] := SafeFileName(Dat['Channel']);
1905   ReplaceHash['%ghost%'] := SafeFileName(Dat['IfGhost']);
1906   ReplaceHash['%date%'] := FormatDateTime('yy-mm-dd', LogTime);
1907   ReplaceHash['%year%'] := FormatDateTime('yyyy', LogTime);
1908   ReplaceHash['%yy%'] := FormatDateTime('yy', LogTime);
1909   ReplaceHash['%month%'] := FormatDateTime('mm', LogTime);
1910   ReplaceHash['%day%'] := FormatDateTime('dd', LogTime);
1911   ReplaceHash['%hour%'] := FormatDateTime('hh', LogTime);
1912   ReplaceHash['%minute%'] := FormatDateTime('nn', LogTime);
1913
1914   Event := TBottleChainBottleEvent.Create;
1915   try
1916     Event.Data := Dat;
1917 {
1918     if EventType = etScript then Event.LogType := ltBottle
1919     else Event.LogType := ltSystemLog;
1920 }
1921     Event.LogType := ltBottle; //\92Ê\8fí
1922
1923     //\83X\83N\83\8a\83v\83g\95Ï\8a·
1924     Script := ScriptTransForSSTP(Dat['Script'], ErrorMes);
1925     //\96â\91è\82Ì\97L\82é\95r\82É\91Î\82·\82é\93®\8dì
1926     if (ErrorMes <> '') AND (NOT Pref.ErrScript) then
1927     begin
1928       frmLog.AddCurrentSystemLog('SYSTEM', '\96â\91è\82Ì\82 \82é\89Â\94\\90«\82Ì\82 \82é\83X\83N\83\8a\83v\83g\82ª\93Í\82¢\82½\82½\82ß\81A'+
1929                     '\94z\91\97\82³\82ê\82Ü\82¹\82ñ\82Å\82µ\82½\81@\81c '+Dat['Script']);
1930       Exit;
1931     end;
1932
1933     BreakFlag := false;
1934     NoDispatch := false;
1935     Sound := '';
1936     LogNameList := TStringList.Create;
1937     SkipCount := 0;
1938     try
1939       for i := 0 to BottleChainRuleList.Count-1 do begin
1940         if SkipCount > 0 then begin
1941           Dec(SkipCount);
1942           Continue;
1943         end;
1944         Rule := BottleChainRuleList[i];
1945         if not Rule.Enabled then Continue;
1946         if not Rule.Check(Event) then Continue;
1947         for j := 0 to Rule.Actions.Count-1 do begin
1948           Action := (Rule.Actions[j] as TBottleChainAction);
1949           if Action is TBottleChainAbortRuleAction then
1950             BreakFlag := true;
1951           if Action is TBottleChainSkipRuleAction then
1952             SkipCount := (Action as TBottleChainSkipRuleAction).SkipCount;
1953           if (Action is TBottleChainSoundAction) and (Sound = '') then
1954           begin
1955             Sound := (Action as TBottleChainSoundAction).SoundFile;
1956             Sound := StringReplaceEx(Sound, ReplaceHash);
1957           end;
1958           if Action is TBottleChainNoDispatchAction then
1959             NoDispatch := true;
1960           if Action is TBottleChainLogAction then
1961           begin
1962             for k := 0 to (Action as TBottleChainLogAction).LogNames.Count-1 do begin
1963               LogName := (Action as TBottleChainLogAction).LogNames[k];
1964               LogName := StringReplaceEx(LogName, ReplaceHash);
1965               LogNameList.Add(LogName);
1966             end;
1967           end;
1968           if Action is TBottleChainOverrideGhostAction then
1969           begin
1970             Dat['TargetGhost'] := (Action as TBottleChainOverrideGhostAction).TargetGhost;
1971           end;
1972           if Action is TBottleChainQuitAction then
1973             Application.Terminate;
1974           if Action is TBottleChainSaveTextLogAction then
1975             AppendTextLog(StringReplaceEx((Action as TBottleChainSaveTextLogAction).FileName, ReplaceHash),
1976               Format('%s,%s,%s,%s', [Dat['Channel'], Dat['IfGhost'],
1977                 FormatDateTime('yy/mm/dd hh:nn:ss', LogTime), Dat['Script']]), true);
1978           if Action is TBottleChainSaveXMLLogAction then
1979             AppendXMLLog(StringReplaceEx((Action as TBottleChainSaveXMLLogAction).FileName, ReplaceHash),
1980               Dat);
1981           if Action is TBottleChainSurfaceReplaceAction then
1982             Script := ReplaceSurface(Script, (Action as TBottleChainSurfaceReplaceAction).Params);
1983         end;
1984         if BreakFlag then Break;
1985       end;
1986
1987       if Dat['Script'] <> '' then begin
1988         for i := 0 to LogNameList.Count-1 do
1989           frmLog.AddCurrentScriptLog(LogNameList[i], Dat['Script'], Sender, Dat['MID'], Dat['IfGhost'],
1990             LogTime, Vote, Agree);
1991         if NoDispatch or Pref.NotSSTP then begin
1992           frmLog.SetBottleState(Dat['MID'], lsOpened);
1993         end else begin
1994           Ghost := Dat['TargetGhost']; // \83I\81[\83o\81[\83\89\83C\83h\82³\82ê\82Ä\82¢\82é\89Â\94\\90«\82ª\82 \82é
1995           CueItem := TLogItem.Create(ltBottle, Dat['MID'], Dat['Channel'],
1996             Script, Ghost, LogTime);
1997           try
1998             FBottleSstp.Push(CueItem);
1999           except
2000             CueItem.Free;
2001           end;
2002         end;
2003       end;
2004
2005 {
2006       if Dat['DialogMessage'] <> '' then begin
2007         Beep;
2008         frmMessageBox.ShowMessage(
2009           DateTimeToStr(LogTime) + #13#10 +
2010           'SSTP Bottle\83T\81[\83o\82©\82ç\82¨\92m\82ç\82¹'#13#10+Dat['DialogMessage']);
2011         for i := 0 to LogNameList.Count-1 do
2012           frmLog.AddCurrentSystemLog(LogNameList[i], Dat['DialogMessage']);
2013       end;
2014 }
2015
2016       //\89¹\82Ì\8dÄ\90
2017       if (Sound <> '') then PlaySound(Sound);
2018     finally
2019       LogNameList.Free;
2020     end;
2021   finally
2022     Event.Free;
2023     ReplaceHash.Free;
2024   end;
2025 end;
2026
2027 procedure TfrmSender.YenETrans;
2028 var St, Le, i: integer;
2029     Orig, Text: String;
2030 begin
2031   if Pref.NotYenETrans then exit; // \e\83`\83F\83b\83N\82µ\82È\82¢
2032   St := memScript.SelStart;
2033   Le := memScript.SelLength;
2034   Orig := GetScriptText;
2035   RegExp.Subst('s/(\r\n)+$//kg', Orig);
2036
2037   with SsParser do begin
2038     LeaveEscape := true;
2039     EscapeInvalidMeta := false;
2040     InputString := Orig;
2041   end;
2042   for i := 0 to SsParser.Count-1 do begin
2043     if SsParser[i] <> '\e' then Text := Text + SsParser[i];
2044   end;
2045
2046   Text := Text + '\e';
2047
2048   if Orig <> Text then memScript.Lines.Text := Text;
2049   SsParser.InputString := Text;
2050
2051   RegExp.Subst('s/\r\n//kg', Text);
2052   memScript.SetFocus;
2053   memScript.SelStart := St;
2054   memScript.SelLength := Le;
2055 end;
2056
2057 {
2058 procedure TfrmSender.PostCommand(const Command: array of String);
2059 var PostStr: TStringList;
2060     i: integer;
2061 begin
2062   PostStr := nil;
2063   try
2064     PostStr := TStringList.Create;
2065     for i := Low(Command) to High(Command) do begin
2066       PostStr.Add(Command[i]);
2067     end;
2068     PostCommand(PostStr);
2069   finally
2070     PostStr.Free;
2071   end;
2072 end;
2073 }
2074
2075 {
2076 procedure TfrmSender.PostCommand(Command: TStrings);
2077 var PostStr: String;
2078 begin
2079   Connecting := true;
2080   PostStr := Command.Text;
2081   PostStr := ParamsEncode(PostStr);
2082   try
2083     FHttp := THTTPDownloadThread.Create(Pref.BottleServer, Pref.CgiName, PostStr);
2084     if Pref.UseHttpProxy then begin
2085       FHttp.ProxyServer := Pref.ProxyAddress;
2086       FHttp.ProxyPort   := Pref.ProxyPort;
2087     end;
2088     FHttp.OnSuccess := HttpSuccess;
2089     FHttp.OnConnectionFailed := HttpFailure;
2090     FHttp.FreeOnTerminate := true; // \8f\9f\8eè\82É\8e©\95ª\82ÅFree\82µ\82Ä\82­\82¾\82³\82¢
2091     FHTTP.Resume;
2092   except
2093     on EHeapException do begin
2094       Connecting := false;
2095       FHttp.Free;
2096     end;
2097   end;
2098 end;
2099 }
2100
2101 procedure TfrmSender.actVoteMessageExecute(Sender: TObject);
2102 //var Log: TLogItem;
2103 begin
2104   ShowHintLabel('\93\8a\95[\82Å\82«\82Ü\82¹\82ñ', WarningColor);
2105 {
2106   if frmLog.lvwLog.Selected = nil then Exit;
2107   Log := frmLog.SelectedBottleLog[frmLog.lvwLog.Selected.Index] as TLogItem;
2108   if Log = nil then Exit;
2109   if Log.LogType <> ltBottle then Exit;
2110   PostCommand([
2111     'Command: voteMessage',
2112     'VoteType: Vote',
2113     'LUID: ' + Pref.LUID,
2114     'MID: ' + Log.MID
2115   ]);
2116 }
2117 end;
2118
2119
2120 procedure TfrmSender.actAgreeMessageExecute(Sender: TObject);
2121 //var Log: TLogItem;
2122 begin
2123   ShowHintLabel('\93¯\88Ó\82Å\82«\82Ü\82¹\82ñ', WarningColor);
2124 {
2125   if frmLog.lvwLog.Selected = nil then Exit;
2126   Log := frmLog.SelectedBottleLog[frmLog.lvwLog.Selected.Index] as TLogItem;
2127   if Log = nil then Exit;
2128   if Log.LogType <> ltBottle then Exit;
2129   PostCommand([
2130     'Command: voteMessage',
2131     'VoteType: Agree',
2132     'LUID: ' + Pref.LUID,
2133     'MID: ' + Log.MID
2134   ]);
2135 }
2136 end;
2137
2138
2139 function TfrmSender.GhostNameToSetName(const Ghost: String): String;
2140 begin
2141   if SakuraSeeker.ProcessByName[Ghost] <> nil then
2142     Result := SakuraSeeker.ProcessByName[Ghost].SetName
2143   else
2144     Result := '';
2145 end;
2146
2147 procedure TfrmSender.tabChannelContextPopup(Sender: TObject;
2148   MousePos: TPoint; var Handled: Boolean);
2149 //var Ch: String;
2150 begin
2151 {
2152   with tabChannel do begin
2153     Tag := IndexOfTabAt(MousePos.X, MousePos.Y);
2154     if Tag < 0 then Handled := true;
2155     Ch := Tabs[Tag];
2156   end;
2157 }
2158 end;
2159
2160 {
2161 procedure TfrmSender.PostSetChannel(Channels: TStrings);
2162 var PostStr: TStringList;
2163     i: integer;
2164 begin
2165   PostStr := TStringList.Create;
2166   try
2167     with PostStr do begin
2168       Add('Command: setChannels');
2169       Add('Agent: ' + VersionString);
2170       Add('LUID: ' + Pref.LUID);
2171       if Channels <> nil then
2172         for i := 0 to Channels.Count-1 do begin
2173           Add(Format('Ch%d: %s'#13#10, [i+1, Channels[i]]));
2174         end;
2175     end;
2176     PostCommand(PostStr);
2177   finally
2178     PostStr.Free;
2179   end;
2180 end;
2181 }
2182
2183 procedure TfrmSender.mnLeaveThisChannelClick(Sender: TObject);
2184 //var Ch: String;
2185 //    Chs: TStringList;
2186 begin
2187 {
2188   // \8ew\92è\82µ\82½\83`\83\83\83\93\83l\83\8b\82©\82ç\94²\82¯\82é
2189   with tabChannel do
2190     Ch := Tabs[Tag]; // \94²\82¯\82½\82¢\83`\83\83\83\93\83l\83\8b\96¼
2191   Chs := TStringList.Create;
2192
2193   // \8c»\8dÝ\8eQ\89Á\92\86\82Ì\83`\83\83\83\93\83l\83\8b\82©\82ç\81A\94²\82¯\82½\82¢\83`\83\83\83\93\83l\83\8b\82ð
2194   // \8aO\82µ\82½\83\8a\83X\83g\82Å\81A\90V\82½\82É\83`\83\83\83\93\83l\83\8b\8eQ\89Á\83R\83}\83\93\83h\82ð\91\97\82é
2195   try
2196     Chs.Assign(JoinChannels);
2197     while Chs.IndexOf(Ch) >= 0 do
2198       Chs.Delete(Chs.IndexOf(Ch));
2199     PostSetChannel(Chs);
2200   finally
2201     Chs.Free;
2202   end;
2203 }
2204 end;
2205
2206 procedure TfrmSender.mnGotoVoteClick(Sender: TObject);
2207 begin
2208   OpenBrowser(Pref.VotePage);
2209 end;
2210
2211 procedure TfrmSender.tabChannelMouseMove(Sender: TObject;
2212   Shift: TShiftState; X, Y: Integer);
2213 //var Index: integer;
2214 //    Ch: String;
2215 begin
2216 {
2217   with tabChannel do begin
2218     Index := IndexOfTabAt(X, Y);
2219     Ch := Tabs[Index];
2220     Hint := Ch + ': ' + IntToStr(ChannelList.Channel[Ch].Members) + '\90l';
2221   end;
2222 }
2223 end;
2224
2225 procedure TfrmSender.mnGoToHelpClick(Sender: TObject);
2226 begin
2227   OpenBrowser(Pref.HelpPage);
2228 end;
2229
2230 procedure TfrmSender.tabChannelMouseDown(Sender: TObject;
2231   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
2232 //var Index: integer;
2233 begin
2234 {
2235   with tabChannel do begin
2236     Index := IndexOfTabAt(X, Y);
2237     if Index = -1 then Exit; //\83^\83u\82ª\82È\82¢\82Ì\82Å\83h\83\89\83b\83O\82Å\82«\82È\82¢
2238     if Button = mbLeft then begin
2239       FDragTabIndex := Index; //\83h\83\89\83b\83O\82·\82é\83^\83u\82Ì\83C\83\93\83f\83b\83N\83X\82ð\95Û\91
2240       BeginDrag(False);
2241       FDragTabDest := -1;     //\83h\83\89\83b\83O\98g\90ü\95`\89æ\83t\83\89\83O\83N\83\8a\83A\82Ì\82½\82ß
2242     end;
2243   end;
2244 }
2245 end;
2246
2247 procedure TfrmSender.tabChannelDragOver(Sender, Source: TObject; X,
2248   Y: Integer; State: TDragState; var Accept: Boolean);
2249 //var TargetRect: TRect;
2250 //    OldDest: integer;
2251 begin
2252 {
2253   Accept := Source = tabChannel;
2254   if not Accept then Exit;
2255   with tabChannel do begin
2256     OldDest := FDragTabDest;
2257     FDragTabDest := IndexOfTabAt(X, Y);
2258     if FDragTabDest = -1 then begin
2259       Accept := false; //\82±\82Ì\8fê\8d\87\82Í\83h\83\8d\83b\83v\82ð\94F\82ß\82È\82¢
2260       Exit;
2261     end;
2262     with Canvas do begin
2263       Pen.Mode := pmNot;
2264       Pen.Width := 3;
2265     end;
2266     if (OldDest <> FDragTabDest) and (OldDest >= 0) then begin
2267       //\88È\91O\82Ì\98g\90ü\8fÁ\8b\8e
2268       TargetRect := TabRect(OldDest);
2269       with Canvas do begin
2270         Brush.Style := bsClear;
2271         Rectangle(TargetRect.Left, TargetRect.Top,
2272                   TargetRect.Right, TargetRect.Bottom);
2273       end;
2274     end;
2275     if (OldDest <> FDragTabDest) then begin
2276       //\90V\82µ\82¢\98g\90ü\95`\89æ
2277       TargetRect := TabRect(FDragTabDest);
2278       with Canvas do begin
2279         Brush.Style := bsClear;
2280         Rectangle(TargetRect.Left, TargetRect.Top,
2281                   TargetRect.Right, TargetRect.Bottom);
2282       end;
2283     end;
2284   end;
2285 }
2286 end;
2287
2288 procedure TfrmSender.tabChannelDragDrop(Sender, Source: TObject; X,
2289   Y: Integer);
2290 //var DestIndex: integer;
2291 begin
2292 {
2293   with tabChannel do begin
2294     DestIndex := IndexOfTabAt(X, Y);
2295     Tabs.Move(FDragTabIndex, DestIndex);
2296   end;
2297 }
2298 end;
2299
2300 procedure TfrmSender.tabChannelEndDrag(Sender, Target: TObject; X,
2301   Y: Integer);
2302 begin
2303 {
2304   //\8b­\90§\93I\82É\83^\83u\82ð\8dÄ\95`\89æ\82³\82¹\82é\81B\98g\90ü\8fÁ\82µ\91Î\8dô
2305   tabChannel.Tabs.BeginUpdate;
2306   tabChannel.Tabs.EndUpdate;
2307 }
2308 end;
2309
2310 procedure TfrmSender.cbxTargetGhostDrawItem(Control: TWinControl;
2311   Index: Integer; Rect: TRect; State: TOwnerDrawState);
2312 var AlignRight: boolean;
2313     w: integer;
2314 begin
2315   //\83S\81[\83X\83g\91I\91ð\83{\83b\83N\83X\82Ì\83I\81[\83i\81[\83h\83\8d\81[
2316   with cbxTargetGhost do begin
2317     AlignRight := false;
2318     if Pref.HideGhosts and not FTargetGhostExpand and
2319        (Index = Items.Count-1) and (Index > 0) then
2320     begin
2321       // \81u\82·\82×\82Ä\95\\8e¦\81v
2322       Canvas.Font.Color := clWindowText;
2323       Canvas.Font.Style := [];
2324       AlignRight := true;
2325     end else if (Index > 0) then
2326     begin
2327       // \83S\81[\83X\83g\96¼\82ð\91I\91ð
2328       if SakuraSeeker.ProcessByName[Items[Index]] = nil then
2329         Canvas.Font.Color := clRed // \91\8dÝ\82µ\82È\82¢\83S\81[\83X\83g
2330       else
2331         Canvas.Font.Color := clBlue; // \91\8dÝ\82·\82é\83S\81[\83X\83g
2332       Canvas.Font.Style := [fsBold];
2333     end else
2334     begin
2335       Canvas.Font.Color := clWindowText;
2336       Canvas.Font.Style := [];
2337     end;
2338     if odSelected in State then
2339       Canvas.Font.Color := clHighlightText;
2340     // \95`\89æ
2341     if AlignRight then
2342     begin
2343       w := Canvas.TextWidth(cbxTargetGhost.Items[Index]);
2344       Canvas.TextRect(Rect, Rect.Right - w, Rect.Top,
2345         cbxTargetGhost.Items[Index]);
2346     end else
2347       Canvas.TextRect(Rect, Rect.Left, Rect.Top,
2348         cbxTargetGhost.Items[Index]);
2349   end;
2350 end;
2351
2352 procedure TfrmSender.FormCloseQuery(Sender: TObject;
2353   var CanClose: Boolean);
2354 begin
2355 {
2356   if (not Pref.ConfirmOnExit) or FEndSession then
2357   begin
2358     Exit;
2359   end;
2360 }
2361   if Pref.ConfirmOnExit then
2362     if MessageDlg('Yasagure Client\82ð\8fI\97¹\82µ\82Ü\82·', mtConfirmation,
2363                 mbOkCancel, 0) = mrCancel then
2364     begin
2365       CanClose := false;
2366       exit;
2367     end;
2368
2369   //\83t\83@\83C\83\8b\82Ì\8dX\90V\83`\83F\83b\83N\81i\83G\83f\83B\83^\95\94\81j
2370   if CheckFileModified(Self) = idCancel then
2371   begin
2372     CanClose := false;
2373     exit;
2374   end;
2375   //\83\8d\83O\81i\83^\83u\81j\93à\97e\82Ì\95Ï\8dX\83`\83F\83b\83N
2376   if frmLog.CheckLog(Self) = idCancel then
2377   begin
2378     CanClose := false;
2379     exit;
2380   end;
2381   DeleteBackupFile(FScriptBackupFile);   // \88ê\8e\9e\83t\83@\83C\83\8b\8dí\8f\9c\82Ö
2382 end;
2383
2384 procedure TfrmSender.UpdateIfGhostBox;
2385 var
2386   Selected: String;
2387   i: integer;
2388   HiddenCount: integer;
2389 begin
2390   cbxTargetGhost.DropDownCount := Pref.GhostDropDownCount;
2391   Selected := cbxTargetGhost.Text;
2392   HiddenCount := 0;
2393   with cbxTargetGhost do begin
2394     Items.BeginUpdate;
2395     Items.Clear;
2396     Items.Add(ChannelDefault);
2397     for i := 0 to SakuraSeeker.Count-1 do begin
2398       // \94j\91¹FMO\91Î\8dô\81BHWND\82Ì\92f\95Ð\82ª\8ec\82Á\82Ä\82¢\82é\82ªName\82ª\8fÁ\82¦\82Ä\82¢\82é\8fê\8d\87\82ª\82 \82é
2399       if Length(SakuraSeeker[i].Name) = 0 then Continue;
2400       if Pref.HideGhosts and not FTargetGhostExpand then
2401         if Pref.VisibleGhostsList.IndexOf(SakuraSeeker[i].Name) < 0 then
2402         begin
2403           Inc(HiddenCount);
2404           Continue;
2405         end;
2406       if cbxTargetGhost.Items.IndexOf(SakuraSeeker[i].Name) < 0 then
2407         cbxTargetGhost.Items.Add(SakuraSeeker[i].Name);
2408     end;
2409     cbxTargetGhost.ItemIndex := 0;
2410     if (Length(Selected) > 0) and (Selected <> ChannelDefault) then begin
2411       with cbxTargetGhost do begin
2412         for i := 1 to Items.Count-1 do begin
2413           if Items[i] = Selected then
2414             ItemIndex := i;
2415         end;
2416         //\83S\81[\83X\83g\82ª\93Ë\91R\91\8dÝ\82µ\82È\82­\82È\82Á\82½\8fê\8d\87\91Î\8dô
2417         if ItemIndex = 0 then begin
2418           Items.Add(Selected);
2419           ItemIndex := Items.Count - 1;
2420         end;
2421       end;
2422     end;
2423     if Pref.HideGhosts and not FTargetGhostExpand then
2424       Items.Add(Format('\82·\82×\82Ä(%d)...', [HiddenCount]));
2425     Items.EndUpdate;
2426   end;
2427 end;
2428
2429 {
2430 procedure TfrmSender.HTTPFailure(Sender: TObject);
2431 begin
2432   SysUtils.Beep;
2433   Beep;
2434   ShowHintLabel('SSTP Bottle\83T\81[\83o\82Æ\82Ì\90Ú\91±\82É\8e¸\94s\82µ\82Ü\82µ\82½', WarningColor);
2435   frmMessageBox.ShowMessage('SSTP Bottle\83T\81[\83o\82Æ\82Ì\90Ú\91±\82É\8e¸\94s\82µ\82Ü\82µ\82½'#13#10 +
2436     (Sender as THTTPDownloadThread).LastErrorMessage);
2437   Connecting := false;
2438 end;
2439 }
2440
2441 procedure TfrmSender.actPrevGhostExecute(Sender: TObject);
2442 var i: integer;
2443 begin
2444   SakuraSeeker.BeginDetect;
2445   UpdateIfGhostBox;
2446   i := cbxTargetGhost.ItemIndex;
2447   Dec(i);
2448   if i <= -1 then
2449   begin
2450     i := cbxTargetGhost.Items.Count-1;
2451     if Pref.HideGhosts and not FTargetGhostExpand then
2452       i := i - 1;
2453   end;
2454   cbxTargetGhost.ItemIndex := i;
2455   cbxTargetGhostChange(self);
2456 end;
2457
2458 procedure TfrmSender.actNextGhostExecute(Sender: TObject);
2459 var i: integer;
2460 begin
2461   SakuraSeeker.BeginDetect;
2462   UpdateIfGhostBox;
2463   i := cbxTargetGhost.ItemIndex;
2464   Inc(i);
2465   if Pref.HideGhosts and not FTargetGhostExpand then
2466   begin
2467     if  i > cbxTargetGhost.Items.Count-2 then i := 0;
2468   end else
2469   begin
2470     if  i > cbxTargetGhost.Items.Count-1 then i := 0;
2471   end;
2472   cbxTargetGhost.ItemIndex := i;
2473   cbxTargetGhostChange(self);
2474 end;
2475
2476 procedure TfrmSender.actResetGhostExecute(Sender: TObject);
2477 begin
2478   cbxTargetGhost.ItemIndex := 0; // (CH\90\84\8f§)\82É\96ß\82·
2479   FTargetGhostExpand := false;
2480   if Visible then memScript.SetFocus;
2481   cbxTargetGhostChange(self);
2482 end;
2483
2484 {
2485 procedure TfrmSender.timDisconnectCheckTimerTimer(Sender: TObject);
2486 begin
2487   if (IdSlpp20.LastReadTimeInterval > Pref.ReconnectWait * 60000) then begin
2488     SysUtils.Beep;
2489     frmLog.AddCurrentSystemLog('SYSTEM', 'SSTP Bottle\83T\81[\83o\82Æ\82Ì\90Ú\91±\82ª\83^\83C\83\80\83A\83E\83g\82µ\82Ü\82µ\82½');
2490     if IdSlpp20.Connected then IdSlpp20.Disconnect;
2491   end;
2492   if not IdSlpp20.Connected then begin
2493     if Added then begin
2494       Slpp20Disconnect(self); //\82È\82º\82©Disconnect\83C\83x\83\93\83g\82ª\8bN\82±\82ç\82¸\82É\90Ø\92f\82µ\82½\8fê\8d\87
2495     end else begin
2496       //\90Ø\92f\82µ\82½\82Ü\82Ü\8dÄ\90Ú\91±\82Å\82«\82¸\95ú\92u\82³\82ê\82Ä\82¢\82é\8fê\8d\87\82à\88ê\92è\8e\9e\8aÔ\92u\82«\82É\8dÄ\90Ú\91±\83g\83\89\83C
2497       //\82½\82¾\82µ\89ñ\90\94\90§\8cÀ\82 \82è
2498       RetryBeginConnect;
2499     end;
2500   end;
2501 end;
2502 }
2503
2504 {
2505 procedure TfrmSender.RetryBeginConnect;
2506 begin
2507   if FBeginConnectFailCount < 3 then begin
2508     // \90Ø\92f\82³\82ê\82Ä\82¢\82ê\82Î\8dÄ\90Ú\91±
2509     IdSlpp20.OnDisconnect := nil;
2510     if IdSlpp20.Connected then IdSlpp20.Disconnect;
2511     FAutoAddAfterGetChannel := true;
2512     BeginConnect;
2513     IdSlpp20.OnDisconnect := Slpp20Disconnect;
2514   end else if FBeginConnectFailCount = 3 then begin
2515     frmLog.AddCurrentSystemLog('SYSTEM', '\8dÄ\90Ú\91±\8e©\93®\83\8a\83g\83\89\83C\82ð\92\86\8e~\82µ\82Ü\82·');
2516     frmMessageBox.ShowMessage(
2517       'SSTP Bottle\83T\81[\83o\82É\90Ú\91±\82Å\82«\82Ü\82¹\82ñ\81B'#13#10+
2518       '\83l\83b\83g\83\8f\81[\83N\82É\90Ú\91±\82µ\82Ä\82¢\82é\82±\82Æ\82ð\8am\94F\82µ\82½\8cã\82Å\81A\83`\83\83\83\93\83l\83\8b\8eQ\89Á\83{\83^\83\93\82ð\89\9f\82µ\82Ä\82­\82¾\82³\82¢\81B'#13#10+
2519       'SSTP Bottle\83T\81[\83o\82ª\83_\83E\83\93\82µ\82Ä\82¢\82é\8fê\8d\87\82Í\81A\82µ\82Î\82ç\82­\82µ\82Ä\82©\82ç\8dÄ\90Ú\91±\82µ\82Ä\82­\82¾\82³\82¢\81B'
2520     );
2521     Inc(FBeginConnectFailCount);
2522   end;
2523 end;
2524 }
2525
2526 procedure TfrmSender.actDownloadLogExecute(Sender: TObject);
2527 {
2528 var BottleLog: TBottleLogList;
2529     Title: String;
2530     Cond: TBottleLogDownloadCondition;
2531     NewIndex: integer;
2532   function TimeStr(Mins: integer): String;
2533   var day, hour, min: integer;
2534   begin
2535     day := Mins div (60 * 24);
2536     hour := (Mins div 60) mod 24;
2537     min := Mins mod 60;
2538     Result := '';
2539     if day  > 0 then Result := Result + Format('%d\93ú', [day]);
2540     if hour > 0 then Result := Result + Format('%d\8e\9e\8aÔ', [hour]);
2541     if (min  > 0) or (Result = '') then Result := Result + Format('%d\95ª', [min]);
2542   end;
2543 }
2544 begin
2545   ShowHintLabel('\83\8d\83O\82Ì\83_\83E\83\93\83\8d\81[\83h\82Í\82Å\82«\82Ü\82¹\82ñ', WarningColor);
2546 {
2547   Application.CreateForm(TfrmLogDownload, frmLogDownload);
2548   try
2549     if frmLogDownload.Execute then begin
2550       with frmLogDownload do begin
2551         if IsRange then begin
2552           if CompareDate(DateLo, DateHi) = 0 then
2553             Title := FormatDateTime('yy-mm-dd', DateLo)
2554           else
2555             Title := FormatDateTime('yy-mm-dd', DateLo) + ' - ' + FormatdateTime('yy-mm-dd', DateHi);
2556         end else begin
2557           Title := Format('\89ß\8b\8e%s', [TimeStr(RecentCount)]);
2558         end;
2559         if Channel <> '' then Title := Title + '(' + Channel + ')';
2560       end;
2561       BottleLog := TBottleLogList.Create(Title);
2562       try
2563         BottleLog.OnLoaded := frmLog.LogLoaded;
2564         BottleLog.OnLoadFailure := frmLog.LogLoadFailure;
2565         BottleLog.OnLoadWork := frmLog.LogLoadWork;
2566         with frmLogDownload do begin
2567           Cond.IsRange := IsRange;
2568           Cond.RecentCount := RecentCount;
2569           Cond.DateLo := DateLo;
2570           Cond.DateHi := DateHi;
2571           Cond.MinVote := MinVote;
2572           Cond.MinAgree := MinAgree;
2573           Cond.Channel := Channel;
2574         end;
2575         BottleLog.LoadFromWeb(Cond);
2576       except
2577         FreeAndNil(BottleLog);
2578       end;
2579       if BottleLog <> nil then begin
2580         NewIndex := frmLog.BottleLogList.Add(BottleLog);
2581         frmLog.UpdateTab;
2582         frmLog.tabBottleLog.TabIndex := NewIndex;
2583         frmLog.UpdateWindow;
2584       end;
2585     end;
2586   finally
2587     frmLogDownload.Release;
2588   end;
2589 }
2590 end;
2591
2592 function TfrmSender.BuildMenuConditionCheck(const IfGhost,
2593   Ghost: String): boolean;
2594 var i: integer;
2595     Cond: String;
2596 begin
2597   i := 0;
2598   Result := true;
2599   repeat
2600     Cond := Token(IfGhost, ',', i);
2601     if Cond <> '' then begin
2602       if Cond[1] = '!' then begin
2603         Cond := Copy(Cond, 2, High(integer));
2604         if Cond = Ghost then Result := false;
2605       end else begin
2606         if Cond <> Ghost then Result := false;
2607       end;
2608     end;
2609     Inc(i);
2610   until Cond = '';
2611 end;
2612
2613 procedure TfrmSender.BuildMenu(Root: TMenuItem; Simple: boolean);
2614 var i, j, k, count: integer;
2615     ConstData: TScriptConst;
2616     Menu1, Menu2: TMenuItem;
2617     Ghost: String;
2618 begin
2619   // Simple = false \82Ì\8fê\8d\87\82Í\83\81\83j\83\85\81[\82ð\8a®\91S\82É\8dÄ\8d\\92z\82·\82é\81B
2620   // Simple = true \82Ì\8fê\8d\87\82Í\83S\81[\83X\83g\8aÖ\8cW\82Ì\82Ý\8dÄ\8d\\92z\82·\82é\82Ì\82Å\8d\82\91¬\81B
2621   if cbxTargetGhost.ItemIndex > 0 then Ghost := cbxTargetGhost.Text
2622 //  else if FNowChannel <> '' then Ghost := ChannelList.Channel[FNowChannel].Ghost;
2623   else
2624     Ghost := '';
2625
2626   // \8aù\91\82Ì\83\81\83j\83\85\81[\8dí\8f\9c
2627   if Simple then begin
2628     // IfGhost\8fð\8c\8f\95t\82«\83\81\83j\83\85\81[\82Ì\82Ý\8dí\8f\9c
2629     for i := Root.Count-1 downto 0 do begin
2630       if ScriptConstList.GetMenuByID(Root.Items[i].Tag).IfGhost <> '' then
2631         Root.Items[i].Free;
2632     end;
2633   end else
2634     // \91S\95\94\8dí\8f\9c
2635     Root.Clear;
2636
2637   count := -1;
2638   for i := 0 to ScriptConstList.Count-1 do begin
2639     for j := 0 to ScriptConstList[i].Count-1 do begin
2640       // \83S\81[\83X\83g\88á\82¢\82Ì\8fê\8d\87\82Í\83X\83L\83b\83v
2641       if not BuildMenuConditionCheck(ScriptConstList[i][j].IfGhost, Ghost) then Continue;
2642       Inc(count);
2643       // Simple\82Ì\8fê\8d\87\82Í\8aù\82É\8aY\93\96\83\81\83j\83\85\81[\82ª\91\8dÝ\82·\82é\82±\82Æ\82ª\82 \82é\82Ì\82Å\83X\83L\83b\83v
2644       if Simple and (count < Root.Count) then
2645         if (Root.Items[count].Tag = ScriptConstList[i][j].ID) then begin
2646           Continue;
2647         end;
2648
2649       Menu1 := TMenuItem.Create(Root);
2650       with Menu1 do
2651       begin
2652         Caption := ScriptConstList[i][j].Caption;
2653         Hint    := ScriptConstList[i][j].Caption;
2654         AutoHotkeys := maManual;
2655         Tag := ScriptConstList[i][j].ID;
2656         OnClick := mnConstGroupClick;
2657       end;
2658
2659       if not Simple then begin
2660         Root.Add(Menu1);
2661       end else begin
2662         if count < Root.Count-1 then
2663           Root.Insert(count, Menu1)
2664         else
2665           Root.Add(Menu1);
2666       end;
2667
2668       Menu1.Enabled := ScriptConstList[i][j].Count > 0;
2669       for k := 0 to ScriptConstList[i][j].Count-1 do begin
2670         ConstData := ScriptConstList[i][j][k];
2671         Menu2 := TMenuItem.Create(Root);
2672         Menu2.Caption := ConstData.Caption;
2673         Menu2.Hint := ConstData.ConstText;
2674         // if ConstData.ShortCut <> 0 then Menu2.Hint := Menu2.Hint
2675         //   + ' (' + ShortCutToText(ConstData.ShortCut) + ')';
2676         // \83T\81[\83t\83B\83X\83v\83\8c\83r\83\85\81[\8eÀ\8c»\82Ì\82½\82ß\8fã\8dí\8f\9c
2677         Menu2.ShortCut := ConstData.ShortCut;
2678         Menu2.OnClick := mnConstClick;
2679         Menu2.AutoHotkeys := maManual;
2680         Menu2.Tag := ConstData.ID;
2681         if (k mod 15 = 0) and (k > 0) then Menu2.Break := mbBarBreak;
2682         Menu1.Add(Menu2);
2683       end;
2684     end;
2685   end;
2686 end;
2687
2688 procedure TfrmSender.cbxTargetGhostChange(Sender: TObject);
2689 begin
2690   // \81u\82·\82×\82Ä\95\\8e¦\81v\82ð\91I\91ð\82³\82ê\82½\8fê\8d\87\82Í\83S\81[\83X\83g\91I\91ð\83{\83b\83N\83X\82ð\8dÄ\8d\\92z
2691   if Pref.HideGhosts and not FTargetGhostExpand then
2692   begin
2693     with cbxTargetGhost do
2694     begin
2695       // \88ê\94Ô\89º\82Ì\83A\83C\83e\83\80\82ª\91I\91ð\82³\82ê\82½\82Æ\82«\81B
2696       // \82½\82¾\82µItemIndex=0\82Ì\8fê\8d\87(\83S\81[\83X\83g\82ª\8bN\93®\82µ\82Ä\82¢\82È\82¢\8fê\8d\87)\82Í\97á\8aO
2697       if (ItemIndex = Items.Count-1) and (ItemIndex > 0) then
2698       begin
2699         FTargetGhostExpand := true;
2700         UpdateIfGhostBox;
2701         ItemIndex := 0;
2702         DroppedDown := true;
2703       end;
2704     end;
2705   end;
2706   // \92è\8c^\8bå\83\81\83j\83\85\81[\82ð\8dÄ\8d\\92z
2707   ConstructMenu(true);
2708   // \83v\83\8c\83r\83\85\81[\82ª\82 \82é\8fê\8d\87\82Í\83v\83\8c\83r\83\85\81[
2709   EditorPreview;
2710 end;
2711
2712 procedure TfrmSender.PlaySound(const FileName: String);
2713 begin
2714   if Pref.SilentWhenHidden and not Application.ShowMainForm then Exit;
2715   try
2716     MediaPlayer.FileName := FileName;
2717     MediaPlayer.Open;
2718     MediaPlayer.Play;
2719   except
2720     on E: EMCIDeviceError do begin
2721       ShowMessage('\83T\83E\83\93\83h\8dÄ\90\83G\83\89\81[:'#13#10 + FileName + #13#10#13#10 + E.Message);
2722     end;
2723   end;
2724 end;
2725
2726 procedure TfrmSender.actFMOExplorerExecute(Sender: TObject);
2727 begin
2728   try
2729     if not Assigned(frmFMOExplorer) then
2730       Application.CreateForm(TfrmFMOExplorer, frmFMOExplorer);
2731   except
2732     on E: Exception do
2733       ShowMessage('FMO\83G\83N\83X\83v\83\8d\81[\83\89\82ð\95\\8e¦\82Å\82«\82Ü\82¹\82ñ\81B'#13#10#13#10 +
2734         E.Message);
2735   end;
2736   frmFMOExplorer.Show;
2737 end;
2738
2739 procedure TfrmSender.SaveChainRuleList;
2740 var Str: TStringList;
2741 begin
2742   Str := TStringList.Create;
2743   try
2744     Str.Text := ComponentToString(BottleChainRuleList);
2745     Str.SaveToFile(ExtractFileDir(Application.ExeName)+'\rule.txt');
2746   finally
2747     Str.Free;
2748   end;
2749 end;
2750
2751 procedure TfrmSender.BottleSstpResendEnd(Sender: TObject; MID: String);
2752 begin
2753   frmLog.SetBottleState(MID, lsOpened);
2754 end;
2755
2756 procedure TfrmSender.BottleSstpResendTrying(Sender: TObject; MID: String);
2757 begin
2758   frmLog.SetBottleState(MID, lsPlaying);
2759 end;
2760
2761 procedure TfrmSender.LogInsertCue(TestAction, SelectAll: boolean);
2762 var InsertItem: TLogItem;
2763     i, errCount, Res, Tstart: integer;
2764     Log: TBottleLogList;
2765     ErrorMes: String; // \83X\83N\83\8a\83v\83g\82Ì\83G\83\89\81[\82Ì\93à\97e
2766 begin
2767   if FBottleSstp.CueCount > 0 then begin
2768     if MessageDlg(Format('\8c»\8dÝ\8dÄ\91\97\83L\83\85\81[\82É\93ü\82Á\82Ä\82¢\82é%d\8c\8f\82Ì\96¢\94z\91\97\83{\83g\83\8b\82ð\83N\83\8a\83A\82µ\82Ä\81A'+
2769       '\83\8d\83O\83E\83B\83\93\83h\83E\82É\82 \82é\83{\83g\83\8b\82ð\98A\91±\8dÄ\90\82µ\82Ü\82·\81B'#13#10+
2770       '\90V\92\85\83\81\83b\83Z\81[\83W\82Í\82»\82Ì\8cã\82É\8dÄ\90\82³\82ê\82Ü\82·\81B', [FBottleSstp.CueCount]),
2771       mtWarning, mbOkCancel, 0) = mrCancel then Exit;
2772   end;
2773   FBottleSstp.Clear;
2774   frmLog.AllBottleOpened;
2775   if frmLog.lvwLog.Selected = nil then Exit;
2776   Log := frmLog.SelectedBottleLog;
2777   if Log = nil then Exit;
2778   FBottleSSTP.OnResendCountChange := nil;
2779   errCount := 0;
2780   if SelectAll then Tstart := frmLog.lvwLog.Items.Count-1  // \8ew\92è\82³\82ê\82½\83^\83u\82Ì\91S\90\94
2781   else Tstart := frmLog.lvwLog.Selected.Index;             // \91I\91ð\82³\82ê\82Ä\82¢\82é\8fê\8f\8a\82©\82ç
2782   for i := Tstart downto 0 do begin
2783     if (Log[i] as TLogItem).LogType <> ltBottle then Continue;
2784     InsertItem := TLogItem.Create(Log[i] as TLogItem);
2785     try
2786       //\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¢
2787       if TestAction then
2788       begin
2789         BottleCnv(InsertItem); // \8c^\95Ï\8a·\82Æ\8eó\90M
2790         frmLog.StatusBar.Panels[1].Text := Format('\8ec\82è%d\8c\8f', [i]);
2791         frmLog.StatusBar.Update;
2792       end else
2793       begin
2794         InsertItem.Script := ScriptTransForSSTP(InsertItem.Script, ErrorMes);
2795         if ErrorMes <> '' then
2796         begin
2797           Res := MessageDlg('\83X\83N\83\8a\83v\83g\82É\96â\91è\82ª\82 \82é\89Â\94\\90«\82ª\82 \82è\82Ü\82·\81B' +
2798             '\8dÄ\90\82µ\82Ü\82·\82©?'#13#10 + ErrorMes, mtWarning,
2799             mbYesNoCancel, 0);
2800           if Res = mrNo then
2801             raise Exception.Create('Script Syntax Error')
2802           else if Res = mrCancel then
2803           begin
2804             InsertItem.Free;
2805             FBottleSstp.Clear;
2806             frmLog.AllBottleOpened;
2807             Break;
2808           end;
2809         end;
2810         //\83`\83\83\83\93\83l\83\8b\83S\81[\83X\83g\91Î\8dô
2811         if InsertItem.Ghost = '' then
2812           if ChannelList.Channel[InsertItem.Channel] <> nil then
2813             InsertItem.Ghost := ChannelList.Channel[InsertItem.Channel].Ghost;
2814         FBottleSSTP.Push(InsertItem);
2815         frmLog.SetBottleState(InsertItem.MID, lsUnopened);
2816       end;
2817     except
2818       InsertItem.Free;
2819       Inc(errCount);
2820     end;
2821   end;
2822   if TestAction then
2823     frmLog.StatusBar.Panels[1].Text := '\8a®\97¹';
2824   if errCount > 0 then
2825     ShowMessage(Format('%d\8c\8f\82Ì\83{\83g\83\8b\82É\96â\91è\82ª\82 \82Á\82½\82½\82ß\8dÄ\90\82Å\82«\82Ü\82¹\82ñ\81B', [errCount]));
2826   FBottleSSTP.OnResendCountChange := BottleSstpResendCountChange;
2827   BottleSstpResendCountChange(self);
2828   frmLog.lvwLog.Invalidate;
2829 end;
2830
2831 procedure TfrmSender.actInsertCueExecute(Sender: TObject);
2832 begin
2833   LogInsertCue(false, false); // \92Ê\8fí\82Ì\98A\91±\8dÄ\90\82ð\82·\82é
2834 end;
2835
2836 function TfrmSender.ScriptTransForSSTP(const Script: String;
2837   out Error: String): String;
2838 var TransOpt: TScriptTransOptions;
2839 begin
2840   if Pref.NoTransURL then
2841     TransOpt := [toConvertURL, toNoChoice, toWaitScriptEnd]
2842   else
2843     TransOpt := [toConvertURL, toWaitScriptEnd];
2844   if Pref.IgnoreFrequentYenS then TransOpt := TransOpt + [toIgnoreFrequentYenS];
2845   if Pref.FixMessySurface then TransOpt := TransOpt + [toFixMessySurface];
2846   if Pref.HUTagTo01Tag then TransOpt := TransOpt + [toHUTagTo01Tag];
2847   Result := Script;
2848   Error := DoTrans(Result, TransOpt);
2849 end;
2850
2851 procedure TfrmSender.FormResize(Sender: TObject);
2852 var w: integer;
2853 begin
2854   // \83G\83f\83B\83^\81[\95\94\95ª\82Ì\83\8f\81[\83h\83\89\83b\83v\95\9d\82ð\92²\90®\82·\82é
2855   if memScript.ColWidth <> 0 then
2856   begin
2857     with memScript do
2858     begin
2859       w := Width - LeftMargin - ColWidth div 2;
2860       if ScrollBars in [ssVertical, ssBoth] then
2861         w := w - GetSystemMetrics(SM_CYVSCROLL);
2862       WrapOption.WrapByte := w div ColWidth;
2863     end;
2864   end;
2865 end;
2866
2867 procedure TfrmSender.actCopyExecute(Sender: TObject);
2868 begin
2869   memScript.CopyToClipboard;
2870 end;
2871
2872 procedure TfrmSender.actPasteExecute(Sender: TObject);
2873 begin
2874   memScript.PasteFromClipboard;
2875 end;
2876
2877 procedure TfrmSender.actCutExecute(Sender: TObject);
2878 begin
2879   memScript.CutToClipboard;
2880 end;
2881
2882 procedure TfrmSender.actSelectAllExecute(Sender: TObject);
2883 begin
2884   memScript.SelectAll;
2885 end;
2886
2887 procedure TfrmSender.actUndoExecute(Sender: TObject);
2888 begin
2889   memScript.Undo;
2890 end;
2891
2892 procedure TfrmSender.actRedoExecute(Sender: TObject);
2893 begin
2894   memScript.Redo;
2895 end;
2896
2897 function TfrmSender.IsSurfaceTag(const Script: String;
2898   var ID: integer): boolean;
2899 begin
2900   Result := false;
2901   if SsParser.Match(Script, '\s%d') = 3 then
2902   begin
2903     Result := true;
2904     ID := Ord(Script[3]) - Ord('0')
2905   end
2906   else if (Length(Script) > 0) and (SsParser.Match(Script, '\s[%D]') = Length(Script)) then
2907   begin
2908     Result := true;
2909     ID := StrToIntDef(SsParser.GetParam(Script, 1), 0);
2910   end;
2911 end;
2912
2913 procedure TfrmSender.memScriptMouseMove(Sender: TObject;
2914   Shift: TShiftState; X, Y: Integer);
2915 var id: integer;
2916     token: String;
2917 begin
2918   // \95Ò\8fW\83E\83B\83\93\83h\83E\82Å\83}\83E\83X\83|\83C\83\93\83g\82·\82é\82Æ\83T\81[\83t\83B\83X\83v\83\8c\83r\83\85\81[
2919   if not Pref.SurfacePreviewOnScriptPoint then
2920     Exit;
2921   token := memScript.TokenStringFromPos(Point(X, Y));
2922   if IsSurfaceTag(token, id) then
2923   begin
2924     DoSurfacePreview(id, spEditor);
2925   end else
2926   begin
2927     frmSurfacePreview.HideAway;
2928   end;
2929 end;
2930
2931 procedure TfrmSender.DoSurfacePreview(Surface: integer;
2932   const Kind: TSurfacePreviewType);
2933 var Ghost: String;
2934     Bmp: TBitmap;
2935     CPos: TPoint;
2936 begin
2937   if cbxTargetGhost.ItemIndex > 0 then
2938     ghost := cbxTargetGhost.Text
2939   else
2940     ghost := '';
2941 {  \8eè\93®\82Ì\82Í\82¸
2942   else if FNowChannel <> '' then
2943     ghost := ChannelList.Channel[FNowChannel].Ghost;
2944 }
2945
2946   // \93ñ\8fd\95\\8e¦\82Ì\97}\90§
2947   if (FVisiblePreviewGhost = Ghost) and (FVisiblePreviewSurface = Surface) and
2948     not (frmSurfacePreview.IsHidden) then
2949   begin
2950     Exit;
2951   end;
2952
2953   if ghost <> '' then
2954   begin
2955     Bmp := TBitmap.Create;
2956     try
2957       if Spps.TryGetImage(ghost, Surface, Bmp) then
2958       begin
2959         case Kind of
2960           spHint:
2961             CPos := GetSurfacePreviewPositionHint(Bmp.Width, Bmp.Height);
2962           spEditor:
2963             CPos := GetSurfacePreviewPositionScriptPoint(Bmp.Width, Bmp.Height);
2964           else
2965             CPos := Point(0, 0);
2966         end;
2967         frmSurfacePreview.ShowPreview(Bmp, CPos.X, CPos.Y);
2968         FVisiblePreviewGhost := Ghost;
2969         FVisiblePreviewSurface := Surface;
2970       end else
2971         frmSurfacePreview.HideAway;
2972     finally
2973       Bmp.Free;
2974     end;
2975   end;
2976 end;
2977
2978 function TfrmSender.GetSurfacePreviewPositionHint(w, h: integer): TPoint;
2979 {var MousePoint: TPoint;
2980     MenuRect: TRect;}
2981 begin
2982   // \83T\81[\83t\83B\83X\83v\83\8c\83r\83\85\81[\83E\83B\83\93\83h\83E\82Ì\95\\8e¦\88Ê\92u\82ð\8c\88\92è\82·\82é
2983   // \91\97\90M\83E\83B\83\93\83h\83E\82Ì\88Ê\92u\82É\82æ\82Á\82Ä\82Í\96­\82È\82Æ\82±\82ë\82É\83\81\83j\83\85\81[\82ª\95\\8e¦\82³\82ê\82Ä\82¢\82é\82Ì\82Å
2984   // \88Ä\8aO\82â\82â\82±\82µ\82¢
2985   {GetCursorPos(MousePoint);
2986   OutputDebugString(PChar(IntToStr(FVisibleMenuItem.Count)));
2987   //if GetMenuItemRect(Self.Handle, FVisibleMenuItem.Items[0].Handle, 1, MenuRect) then ;
2988   Result := Point(MenuRect.Left-10, MenuRect.Top-10);}
2989   Result := GetSurfacePreviewPositionScriptPoint(w, h);
2990 end;
2991
2992 function TfrmSender.GetSurfacePreviewPositionScriptPoint(w, h: integer): TPoint;
2993 var MousePoint: TPoint;
2994 begin
2995   GetCursorPos(MousePoint);
2996   case Pref.SurfacePreviewOnScriptPointPosition of
2997     spspMainWindowRight:
2998       Result := Point(Self.Left + Self.Width, MousePoint.Y);
2999   else
3000       Result := Point(Self.Left - w, MousePoint.Y);
3001   end;
3002 end;
3003
3004 procedure TfrmSender.mnConstGroupClick(Sender: TObject);
3005 begin
3006   if (Sender is TMenuItem) then
3007     FVisibleMenuItem := Sender as TMenuItem;
3008 end;
3009
3010 procedure TfrmSender.actRecallScriptBufferExecute(Sender: TObject);
3011 begin
3012   if FScriptBuffer.Count = 0 then
3013     Exit;
3014   memScript.Lines.Assign(FScriptBuffer[0] as TStringList);
3015   EditerStatusChange;
3016   ShowHintLabel('\83X\83N\83\8a\83v\83g\82ð\8cÄ\82Ñ\8fo\82µ\82Ü\82µ\82½');
3017 end;
3018
3019 procedure TfrmSender.EditorPreview;
3020 var Ghost, Script: String;
3021 begin
3022   if frmEditorTalkShow <> nil then
3023     if frmEditorTalkShow.Visible then
3024     begin
3025       Script := StringReplace(GetScriptText, #13#10, '', [rfReplaceAll]);
3026       Ghost := '';
3027       if cbxTargetGhost.ItemIndex > 0 then
3028         Ghost := cbxTargetGhost.Text;
3029\8eè\93®\82Å\8ew\92è\82³\82ê\82é
3030       else if FNowChannel <> '' then
3031         Ghost := ChannelList.Channel[FNowChannel].Ghost;
3032 }
3033       frmEditorTalkShow.TalkShowFrame.View(Script, Ghost);
3034     end;
3035 end;
3036
3037 procedure TfrmSender.actEditorPreviewExecute(Sender: TObject);
3038 begin
3039   if frmEditorTalkShow <> nil then
3040     frmEditorTalkShow.Show
3041   else
3042   begin
3043     Application.CreateForm(TfrmEditorTalkShow, frmEditorTalkShow);
3044     frmEditorTalkShow.TalkShowFrame.SetPreviewFont(memScript.Font);
3045     frmEditorTalkShow.Show;
3046   end;
3047   EditorPreview;
3048 end;
3049
3050 // \83v\83\89\83O\83C\83\93\83\8a\83Z\83b\83g
3051 procedure TfrmSender.actResetPluginsExecute(Sender: TObject);
3052 begin
3053   Spps.ClearImagePool;
3054   Spps.LoadFromDirectory(FSppDir);
3055 end;
3056
3057 {
3058 procedure TfrmSender.IdSLPP20Connect(Sender: TObject);
3059 begin
3060   ShowHintLabel('SSTP Bottle\83T\81[\83o\82ª\8c©\82Â\82©\82è\82Ü\82µ\82½');
3061 end;
3062 }
3063
3064 // \83X\83N\83\8a\83v\83g\92\86\82Ì\83^\83O\82ð\92u\8a·\82·\82é
3065 // \83T\83C\83Y\89Â\95Ï\82Ì\94z\97ñ\83p\83\89\83\81\81[\83^\94Å
3066 function TfrmSender.TagReplace(Script: String; Before,
3067   After: array of String): String;
3068 var BeforeList, AfterList: TStringList;
3069     i: integer;
3070 begin
3071   BeforeList := TStringList.Create;
3072   AfterList  := TStringList.Create;
3073   try
3074     for i := Low(Before) to High(Before) do
3075       BeforeList.Add(Before[i]);
3076     for i := Low(After) to High(After) do
3077       AfterList.Add(After[i]);
3078     Result := TagReplace(Script, BeforeList, AfterList);
3079   finally
3080     BeforeList.Free;
3081     AfterList.Free;
3082   end;
3083 end;
3084
3085 // \83X\83N\83\8a\83v\83g\92\86\82Ì\83^\83O\82ð\92u\8a·\82·\82é
3086 // StringReplace\82Æ\88á\82Á\82Ä\90³\8am\82É\83^\83O\82É\83}\83b\83`\82µ\81A
3087 // \82Ü\82½\83p\83^\81[\83\93\82ð\95¡\90\94\8ew\92è\82Å\82«\82é(\92u\8a·\8cã\82Ì\8c\8b\89Ê\82ª\82Ü\82½\92u\8a·\82³\82ê\82½\82è\82µ\82È\82¢)
3088 function TfrmSender.TagReplace(Script: String; Before,
3089   After: TStrings): String;
3090 var i, j: integer;
3091     Flag, OldLeaveEscape, OldEscapeInvalidMeta: boolean;
3092     OldStr: String;
3093 begin
3094   Result := '';
3095   with SsParser do
3096   begin
3097     OldStr := InputString;
3098     OldLeaveEscape := LeaveEscape;
3099     OldEscapeInvalidMeta := EscapeInvalidMeta;
3100     LeaveEscape := true;
3101     EscapeInvalidMeta := false;
3102     InputString := Script;
3103   end;
3104   for i := 0 to SsParser.Count-1 do
3105   begin
3106     Flag := false;
3107     for j := 0 to Before.Count-1 do
3108     begin
3109       if (SsParser.MarkUpType[i] = mtTag) and (SsParser[i] = Before[j]) then
3110       begin
3111         Flag := true;
3112         Result := Result + After[j];
3113       end;
3114     end;
3115     if not Flag then
3116       Result := Result + SsParser[i];
3117   end;
3118   with SsParser do
3119   begin
3120     LeaveEscape := OldLeaveEscape;
3121     EscapeInvalidMeta := OldEscapeInvalidMeta;
3122     InputString := OldStr;
3123   end;
3124 end;
3125
3126 // WndProc\82ð\83I\81[\83o\81[\83\89\83C\83h\82µ\82Ä\81AFWM_TaskBarCraeted\82É
3127 // \91Î\89\9e\82·\82é
3128 procedure TfrmSender.WndProc(var Message: TMessage);
3129 begin
3130   if (Message.Msg = FWM_TaskBarCreated) and (FWM_TaskBarCreated <> 0) then
3131   begin
3132     TaskTray.Registered := false; // TTaskTray\82É\83^\83X\83N\83g\83\8c\83C\82ª\8fÁ\82¦\82½\82±\82Æ\82ð
3133                                   // \8bC\82Ã\82©\82¹\82é
3134     ChangeTaskIcon;
3135   end
3136   else
3137     inherited;
3138 end;
3139
3140 // \92u\8a·
3141 procedure TfrmSender.actReplaceExecute(Sender: TObject);
3142 var
3143   Form: TfrmStrReplaceDialog;
3144   Lines: String;
3145   Options: TReplaceFlags;
3146 begin
3147   Application.CreateForm(TfrmStrReplaceDialog, Form);
3148   try
3149     if Form.Execute then
3150     begin
3151       with Form.Pair do
3152       begin
3153         Lines := memScript.Lines.Text;
3154         Options := [rfReplaceAll];
3155         if IgnoreCase then
3156           Options := Options + [rfIgnoreCase];
3157         if UseRegExp then
3158           Lines := StringReplaceRegExp(Lines, BeforeStr, AfterStr, Options)
3159         else
3160           Lines := StringReplace(Lines, BeforeStr, AfterStr, Options);
3161       end;
3162       if Lines <> memScript.Lines.Text then
3163       begin
3164         memScript.SelectAll;
3165         memScript.SelText := Lines;
3166       end;
3167     end;
3168   finally
3169     Form.Release;
3170   end;
3171 end;
3172
3173 procedure TfrmSender.actSendToEditorExecute(Sender: TObject);
3174 var Log: TLogItem;
3175 begin
3176   if frmLog.lvwLog.Selected = nil then Exit;
3177   Log := frmLog.SelectedBottleLog[frmLog.lvwLog.Selected.Index] as TLogItem;
3178   if Log = nil then Exit;
3179   CopyFromLogToEditor(Log);
3180 end;
3181
3182 procedure TfrmSender.actSendToLogWindowExecute(Sender: TObject);
3183 var Ghost, Script: String;
3184 begin
3185   YenETrans;
3186   Script := StringReplace(GetScriptText, #13#10, '', [rfReplaceAll]);
3187   if cbxTargetGhost.ItemIndex > 0 then
3188     Ghost := cbxTargetGhost.Text
3189   else
3190     Ghost := '';
3191   frmLog.AddCurrentScriptLog('\83N\83\8a\83b\83v', Script, ClipChannel, '', Ghost, Now(), 0, 0);
3192   SetFileModified(false); // \95Ï\8dX\82³\82ê\82Ä\82¢\82È\82¢\82±\82Æ\82É\82µ\82Ä\83N\83\8a\83A\82³\82¹\82é
3193   ClearEditor;
3194 end;
3195
3196 procedure TfrmSender.memScriptDragOver(Sender, Source: TObject; X,
3197   Y: Integer; State: TDragState; var Accept: Boolean);
3198 begin
3199   // \83\8d\83O\83E\83B\83\93\83h\83E\82©\82ç\82Ì\83\8d\83O\83A\83C\83e\83\80\82Ì\92¼\90ÚD&D\82ð\8b\96\89Â\82·\82é
3200   if Source is TBottleLogDragObject then
3201     Accept := (Source as TBottleLogDragObject).LogItem.LogType = ltBottle
3202 end;
3203
3204 procedure TfrmSender.memScriptDragDrop(Sender, Source: TObject; X,
3205   Y: Integer);
3206 var Src: TBottleLogDragObject;
3207     Log: TLogItem;
3208 begin
3209   // \83\8d\83O\83E\83B\83\93\83h\83E\82©\82ç\83\8d\83O\83A\83C\83e\83\80\82ðD&D\82µ\82Ä\82­\82é
3210   if not (Source is TBottleLogDragObject) then
3211     Exit;
3212   if (Source as TBottleLogDragObject).LogItem.LogType <> ltBottle then
3213     Exit;
3214   Src := Source as TBottleLogDragObject;
3215   Log := Src.LogItem;
3216   CopyFromLogToEditor(Log);
3217 end;
3218
3219 procedure TfrmSender.CopyFromLogToEditor(Log: TLogItem);
3220 var
3221   Ghost: string;
3222 begin
3223   if Log.LogType <> ltBottle then Exit;
3224   frmSender.actClear.Execute; // \8c»\8dÝ\82Ì\83X\83N\83\8a\83v\83g\82ð\83N\83\8a\83b\83v\82·\82é(\90Ý\92è\82É\82æ\82Á\82Ä)
3225   memScript.Lines.Clear;
3226   memScript.Lines.Add(Log.Script);
3227   SetFileName(Log.MID, true, false);  // \81ª\82Å\93à\97e\82ª\8dX\90V\82³\82ê\82é\82Ì\82Å\8dÄ\93x\90V\8bK\82É
3228   Ghost := Log.Ghost;
3229   //\83`\83\83\83\93\83l\83\8b\83S\81[\83X\83g\91Î\8dô
3230   if Ghost = '' then
3231     if ChannelList.Channel[Log.Channel] <> nil then
3232       Ghost := ChannelList.Channel[Log.Channel].Ghost;
3233   if Ghost <> '' then
3234   begin
3235     // \83S\81[\83X\83g\96¼\82ð\83{\83b\83N\83X\82É\93ü\82ê\82é
3236     // \96³\97\9d\96î\97\9d\83S\81[\83X\83g\96¼\82ð\92Ç\89Á\82µ\82Ä\82©\82ç\8dÄ\8d\\92z\82·\82é\82±\82Æ\82Å
3237     // \96³\97\9d\96î\97\9d\83S\81[\83X\83g\96¼\82ª\83{\83b\83N\83X\82É\93ü\82é
3238     cbxTargetGhost.Items.Add(Ghost);
3239     cbxTargetGhost.ItemIndex := cbxTargetGhost.Items.Count-1;
3240     UpdateIfGhostBox;
3241     cbxTargetGhost.Invalidate;
3242   end else
3243     cbxTargetGhost.ItemIndex := 0; // 'CH\90\84\8f§'\82É\82·\82é
3244   memScript.SetFocus;
3245 end;
3246
3247 procedure TfrmSender.actDeleteLogItemExecute(Sender: TObject);
3248 begin
3249   // \83\8d\83O\83E\83B\83\93\83h\83E\82Ì\8cÂ\95Ê\83\8d\83O\82ð\8dí\8f\9c\82·\82é
3250   if frmLog.SelectedBottleLog = nil then
3251     Exit;
3252   if frmLog.lvwLog.Selected = nil then
3253     Exit;
3254   frmLog.SelectedBottleLog.Delete(frmLog.lvwLog.Selected.Index);
3255   frmLog.UpdateWindow;
3256   frmLog.lvwLogChange(Self, nil, ctState);
3257   frmLog.SelectedBottleLog.LogModified := true; // \95Ï\8dX\82³\82ê\82½
3258 end;
3259
3260 procedure TfrmSender.ClearEditor;
3261 var TmpScript: String;
3262     Position: Integer;
3263     DoSaveBuffer: boolean;
3264     SavedScript: TStringList;
3265 begin
3266   // \90Ý\92è\82É\82æ\82è\83N\83\8a\83A\8e\9e\82É\83t\83@\83C\83\8b\83Z\81[\83u\82·\82é\82©\8am\94F\82·\82é
3267   // \8e©\93®\83N\83\8a\83b\83v\82ª\97L\8cø\82Å\82 \82ê\82Î\83X\83L\83b\83v\82·\82é
3268   if (NOT Pref.NotCheckClear) AND (NOT Pref.AutoClip) then
3269     if CheckFileModified(Self) = idCancel then abort;
3270   // \83X\83N\83\8a\83v\83g\82Ì\83N\83\8a\83A
3271   // \82Ü\82¸\81A\83X\83N\83\8a\83v\83g\83N\83\8a\83A\83o\83b\83t\83@\82É\8c»\8dÝ\82Ì\83X\83N\83\8a\83v\83g\82ð\95Û\91\82·\82é
3272   DoSaveBuffer := false;
3273   if FScriptBuffer.Count = 0 then
3274     DoSaveBuffer := true
3275   else if (FScriptBuffer[0] as TStringList).Text <> GetScriptText then
3276     DoSaveBuffer := true;
3277   if (GetScriptText = Pref.DefaultScript) or (GetScriptText = '') then
3278     DoSaveBuffer := false;
3279   if DoSaveBuffer then
3280   begin
3281     SavedScript := TStringList.Create;
3282     SavedScript.Text := GetScriptText;
3283     FScriptBuffer.Insert(0, SavedScript);
3284   end;
3285   if FScriptBuffer.Count >= 4 then
3286     FScriptBuffer.Delete(FScriptBuffer.Count-1);
3287   actRecallScriptBuffer.Enabled := FScriptBuffer.Count > 0;
3288
3289   TmpScript := Pref.DefaultScript;
3290   Position := Pos('|', TmpScript);
3291   if Position < 1 then Position := 1;
3292   TmpScript := StringReplace(TmpScript, '|', '', []);
3293   memScript.Lines.Text := TmpScript;
3294   Sendmessage(memScript.Handle, WM_VSCROLL, SB_LINEUP, 0);
3295   memScript.SelStart := Position-1;
3296
3297   if Visible then memScript.SetFocus;
3298 //  FScriptModified := false;
3299
3300   SetFileName(NewFileTitle, true, true);    // \83t\83@\83C\83\8b\82ð\90V\8bK\88µ\82¢\82É\82·\82é
3301   EditerStatusChange;                       // \83X\83e\81[\83^\83X\8dX\90V
3302 end;
3303
3304 procedure TfrmSender.AppendTextLog(const FileName, Line: String; FAppend: boolean);
3305 var
3306   F: TextFile;
3307 begin
3308   //\91\97\90M\83\8d\83O\95Û\91
3309   try
3310     ForceDirectories(ExtractFileDir(FileName));
3311     AssignFile(F, FileName);
3312     if FileExists(FileName) AND FAppend then  // \92Ç\8bL\82©\8fã\8f\91\82«\94»\92è
3313       Append(F)
3314     else
3315       Rewrite(F);
3316     WriteLn(F, Line);
3317     Flush(F);
3318     CloseFile(F);
3319   except
3320     on E: Exception do
3321       frmLog.AddCurrentSystemLog('SYSTEM', '\83e\83L\83X\83g\83\8d\83O\95Û\91\82É\8e¸\94s\81F'+E.Message);
3322   end;
3323 end;
3324
3325 procedure TfrmSender.AppendXMLLog(const FileName: String; Args: THeadValue);
3326 var
3327   F: TFileStream;
3328   Buf: String;
3329   P: integer;
3330   Impl: TDomImplementation;
3331   Parser: TXmlToDomParser;
3332   DOM: TdomDocument;
3333 begin
3334   try // Long try block to handle all kinds of exception in this method
3335     if not FileExists(FileName) then
3336     begin
3337       // Create empty XML log file
3338       Impl := TDomImplementation.create(nil);
3339       try
3340         ForceDirectories(ExtractFileDir(FileName));
3341         Parser := TXmlToDomParser.create(nil);
3342         Parser.DOMImpl := Impl;
3343         try
3344           try
3345             DOM := Parser.fileToDom(ExtractFilePath(Application.ExeName)+'xbtl.dat');
3346             with DOM do
3347             begin
3348               documentElement.setAttribute('saved',
3349                 FormatDateTime('yy/mm/dd hh:nn:ss', Now));
3350               documentElement.setAttribute('generator', VersionString);
3351               documentElement.setAttribute('version', '1.0');
3352             end;
3353             // \82±\82ê\82Í\96¾\8e¦\93I\82ÉFree\82µ\82È\82­\82Ä\82æ\82¢
3354             F := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
3355             try
3356               DOM.writeCodeAsShiftJIS(F);
3357             finally
3358               F.Free;
3359             end;
3360           except
3361             frmLog.AddCurrentSystemLog('SYSTEM', 'XML\83\8d\83O\95Û\91\82É\8e¸\94s\82µ\82Ü\82µ\82½');
3362           end;
3363         finally
3364           Parser.DOMImpl.freeDocument(DOM);
3365           Parser.Free;
3366         end;
3367       finally;
3368         Impl.Free;
3369       end;
3370     end;
3371     F := TFileStream.Create(FileName, fmOpenReadWrite or fmShareExclusive);
3372     try
3373       P := -11;
3374       SetLength(Buf, 12);
3375       while P > -100 do
3376       begin
3377         F.Seek(P, soFromEnd);
3378         F.Read(Buf[1], 12);
3379         if Buf = '</bottlelog>' then
3380           Break;
3381         Dec(P);
3382       end;
3383       if P = -100 then
3384         raise Exception.Create(FileName + ' is not a valid XML bottle log file')
3385       else
3386       begin
3387         F.Seek(P, soFromEnd);
3388         Buf := Format('<message mid="%s">', [Args['MID']]);
3389         Buf := Buf + Format('<date>%s</date>', [FormatDateTime('yy/mm/dd hh:nn:ss', Now)]);
3390         Buf := Buf + Format('<channel>%s</channel>', [XMLEntity(Args['Channel'])]);
3391         Buf := Buf + Format('<script>%s</script>', [XMLEntity(Args['Script'])]);
3392         Buf := Buf + '<votes>0</votes><agrees>0</agrees>';
3393         Buf := Buf + Format('<ghost>%s</ghost>', [XMLEntity(Args['IfGhost'])]);
3394         Buf := Buf + '</message>';
3395         Buf := Buf + '</bottlelog>';
3396         F.Write(Buf[1], Length(Buf));
3397       end;
3398     finally
3399       F.Free;
3400     end;
3401   except
3402     on E: Exception do
3403       frmLog.AddCurrentSystemLog('SYSTEM', 'XML\83\8d\83O\95Û\91\82É\8e¸\94s\82µ\82Ü\82µ\82½:'+E.Message);
3404   end;
3405 end;
3406
3407 procedure TfrmSender.memScriptSelectionChange(Sender: TObject;
3408   Selected: Boolean);
3409 var
3410   SelText: String;
3411 begin
3412   SelText := memScript.SelText;
3413   if SelText <> '' then
3414   begin
3415     StatusBar.Panels[PanelBytes].Text := Format('(%d\83o\83C\83g)', [Length(SelText)]);
3416   end else
3417   begin
3418     EditerStatusChange;
3419   end;
3420 end;
3421
3422 function TfrmSender.ReplaceSurface(Script: String;
3423   Params: TCollection): String;
3424 var
3425   Flag, OldLeaveEscape, OldEscapeInvalidMeta: boolean;
3426   OldStr, Tag: String;
3427   i, j, k, Cur: integer;
3428   Item: TSurfaceReplaceItem;
3429   Before: TSurfaceReplaceBeforeItem;
3430 begin
3431   Result := '';
3432   Cur := 0;
3433   with SsParser do
3434   begin
3435     OldStr := InputString;
3436     OldLeaveEscape := LeaveEscape;
3437     OldEscapeInvalidMeta := EscapeInvalidMeta;
3438     LeaveEscape := true;
3439     EscapeInvalidMeta := false;
3440     InputString := Script;
3441   end;
3442   for i := 0 to SsParser.Count-1 do
3443   begin
3444     if SsParser.MarkUpType[i] <> mtTag then
3445     begin
3446       Result := Result + SsParser.Str[i];
3447       Continue;
3448     end;
3449     Tag := SsParser.Str[i];
3450     if SsParser.Match(Tag, '\s%d') = 2 then
3451       Cur := Ord(Tag[3]) - Ord('0')
3452     else if SsParser.Match(Tag, '\s[%D]') > 0 then
3453       Cur := StrToInt(SsParser.GetParam(Tag, 1))
3454     else
3455     begin
3456       Result := Result + Tag;
3457       Continue;
3458     end;
3459     Flag := false;
3460     for j := 0 to Params.Count-1 do
3461     begin
3462       Item := Params.Items[j] as TSurfaceReplaceItem;
3463       for k := 0 to Item.Before.Count-1 do
3464       begin
3465         Before := Item.Before.Items[k] as TSurfaceReplaceBeforeItem;
3466         if (Cur >= Before.FromNo) and (Cur <= Before.ToNo) then
3467         begin
3468           Flag := true;
3469           Result := Result + Format('\s[%d]', [Item.After]);
3470           Break;
3471         end;
3472       end;
3473       if Flag then
3474         Break;
3475     end;
3476     if not Flag then
3477       Result := Result + Tag;
3478   end;
3479   with SsParser do
3480   begin
3481     LeaveEscape := OldLeaveEscape;
3482     EscapeInvalidMeta := OldEscapeInvalidMeta;
3483     InputString := OldStr;
3484   end;
3485 end;
3486
3487 {
3488 procedure TfrmSender.WMQueryEndSession(var msg: TWMQueryEndSession);
3489 begin
3490   // Windows\82ª\8fI\97¹\82µ\82æ\82¤\82Æ\82µ\82Ä\82¢\82é\82Ì\82ð\8a´\92m\82·\82é
3491   FEndSession := true;
3492   inherited;
3493 end;
3494 }
3495
3496 {
3497 procedure TfrmSender.IdSLPP20ConnectFailed(Sender: TObject);
3498 begin
3499   Added := false;
3500   if FBeginConnectFailCount = 0 then
3501   begin
3502     Beep;
3503     if Pref.UseHttpProxy then
3504       frmMessageBox.ShowMessage('HTTP Proxy\82ð\92Ê\82\82ÄSSTP Bottle\83T\81[\83o\82É\90Ú\91±\82Å\82«\82Ü\82¹\82ñ\82Å\82µ\82½\81B'#13#10 +
3505                   '\83l\83b\83g\83\8f\81[\83N\82Ì\8fó\91Ô\81EProxy\82Ì\8fó\91Ô\82ð\8am\94F\82µ\82Ä\82­\82¾\82³\82¢\81B'#13#10 +
3506                   '\82 \82é\82¢\82Í\83T\81[\83o\82ª\83_\83E\83\93\82µ\82Ä\82¢\82é\89Â\94\\90«\82ª\82 \82è\82Ü\82·\81B')
3507     else
3508       frmMessageBox.ShowMessage('SSTP Bottle\83T\81[\83o\82É\90Ú\91±\82Å\82«\82Ü\82¹\82ñ\82Å\82µ\82½\81B'#13#10 +
3509                   '\83l\83b\83g\83\8f\81[\83N\82É\8cq\82ª\82Á\82Ä\82¢\82é\82©\8am\94F\82µ\82Ä\82­\82¾\82³\82¢\81B'#13#10 +
3510                   '\82 \82é\82¢\82Í\83T\81[\83o\82ª\83_\83E\83\93\82µ\82Ä\82¢\82é\89Â\94\\90«\82ª\82 \82è\82Ü\82·\81B');
3511   end;
3512   ShowHintLabel('SSTP Bottle Server\90Ú\91±\82É\8e¸\94s\82µ\82Ü\82µ\82½', WarningColor);
3513   Inc(FBeginConnectFailCount);
3514 end;
3515 }
3516
3517 procedure TfrmSender.BuildReplaceMenu(Root: TMenuItem);
3518 var
3519   i: integer;
3520   Presets: TReplacePresetCollection;
3521   NewItem: TMenuItem;
3522 begin
3523   Root.Clear;
3524   Presets := Pref.ReplacePresets.Presets;
3525   for i := 0 to Presets.Count-1 do
3526   begin
3527     NewItem := TMenuItem.Create(Root);
3528     with NewItem do
3529     begin
3530       Caption := Presets[i].Title;
3531       ShortCut := Presets[i].ShortCut;
3532       AutoHotkeys := maManual;
3533       Hint := Presets[i].Pairs.StringExpression;
3534       Tag := i;
3535       OnClick := mnPresetReplaceClick;
3536     end;
3537     Root.Add(NewItem);
3538   end;
3539   Root.Enabled := Presets.Count > 0;
3540 end;
3541
3542 procedure TfrmSender.mnPresetReplaceClick(Sender: TObject);
3543 var
3544   Preset: TReplacePreset;
3545   Lines, New: string;
3546 begin
3547   // \83v\83\8a\83Z\83b\83g\92u\8a·\82ð\8eÀ\8ds
3548   Preset := Pref.ReplacePresets.Presets[(Sender as TMenuItem).Tag];
3549   Lines := memScript.Lines.Text;
3550   New := Preset.Pairs.ExecuteReplace(Lines);
3551   if New <> Lines then
3552   begin
3553     memScript.SelectAll;
3554     memScript.SelText := New;
3555   end;
3556   if Preset.ConfirmAfterReplace then
3557   begin
3558     btnConfirm.Click;
3559   end;
3560 end;
3561
3562 procedure TfrmSender.BottleCnv(Log: TLogItem);
3563 var
3564   HeadValue: THeadValue;
3565 begin
3566   //HeadValue\8c^\82Ö
3567   HeadValue := THeadValue.Create;
3568   HeadValue['MID'] := Log.MID;
3569   HeadValue['Channel'] := Log.Channel;
3570 //  HeadValue['SEND'] := 'SSTP/1.4';
3571 //  HeadValue['Sender'] := 'SSTP Bottle ' + Log.Channel;
3572   HeadValue['IfGhost'] := Log.Ghost;
3573   HeadValue['Script'] := Log.Script;
3574 //  HeadValue['Charset'] := 'Shift_JIS';
3575 //  HeadValue['DialogMessage'] := Log.Script;
3576   try
3577     //\83\81\83b\83Z\81[\83W\8eó\90M\8f\88\97\9d\82Ö
3578     DispatchBottle(HeadValue, Log.LogTime, Log.Votes, Log.Agrees);
3579   finally
3580     HeadValue.Free;
3581   end;
3582 end;
3583
3584 procedure TfrmSender.SetScriptAutoBackup;
3585 begin
3586   //\83o\83b\83N\83A\83b\83v\83^\83C\83}\81[\82Ö\92l\82Ì\83Z\83b\83g
3587   frmSender.ScriptBackup.Enabled := Pref.ScriptBackup;
3588   frmSender.ScriptBackup.Interval := Pref.ScriptBackupTime * 60000;
3589 end;
3590
3591 procedure TfrmSender.ScriptBackupTimer(Sender: TObject);
3592 var
3593   Script: string;
3594 begin
3595   // \83X\83N\83\8a\83v\83g\82Ì\83o\83b\83N\83A\83b\83v
3596   if FScriptBackupModified then
3597   begin
3598     Script := FormatDateTime('yy/mm/dd hh:nn:ss', Now) + #13#10 + GetScriptText;
3599
3600     AppendTextLog(FScriptBackupDir + FScriptBackupFile, Script,
3601       not Pref.ScriptBackupMode);
3602
3603     ShowHintLabel('\83o\83b\83N\83A\83b\83v\8eÀ\8ds');
3604     FScriptBackupModified := False;
3605   end;
3606 end;
3607
3608 procedure TfrmSender.UpdateChannelList;
3609 var
3610   Ch: TChannelListItem;
3611   i: integer;
3612 begin
3613   // \83`\83\83\83\93\83l\83\8b\83S\81[\83X\83g\92è\8b`
3614   ChannelList.Clear;
3615   // \83t\83\89\83O\82ð\8c©\82Ä\94»\92f
3616   if NOT Pref.GhostCh then exit;
3617   if (Pref.GhostChList.Count mod 2) = 1 then
3618   begin
3619     Pref.GhostChList.Add(''); // \95s\91«\82µ\82Ä\82¢\82é\82Ì\82Å\93K\93\96\82É\92Ç\89Á
3620     ShowMessage('\83`\83\83\83\93\83l\83\8b\82ð\8dÄ\90Ý\92è\82µ\82Ä\82­\82¾\82³\82¢');
3621     exit;
3622   end;
3623   i := 0;
3624   while i < Pref.GhostChList.Count-1 do
3625   begin
3626     Ch := TChannelListItem.Create;
3627     Ch.Name    := Pref.GhostChList.Strings[i];
3628     Ch.Ghost   := Pref.GhostChList.Strings[i+1];
3629     Ch.Info    := '';
3630     Ch.NoPost  := false;
3631     Ch.Members := 0;
3632     Ch.WarnPost:= false;
3633     ChannelList.Add(Ch);
3634     i := i + 2;
3635   end;
3636 end;
3637
3638 procedure TfrmSender.UpdateJoinChannelList;
3639 begin
3640   UpdateChannelList;  // \83`\83\83\83\93\83l\83\8b\90Ý\92è
3641   with tabChannel do  // \96¢\8eg\97p
3642   begin
3643     OnChange := nil;
3644     Tabs.BeginUpdate;
3645     Tabs.Clear;
3646     Tabs.EndUpdate;
3647     actSend.Hint := Format('\91\97\90M|%s', [SendButtonLongHint]);
3648     actNextChannel.Enabled := false;
3649     actPrevChannel.Enabled := false;
3650     OnChange := tabChannelChange;
3651   end;
3652 end;
3653
3654 procedure TfrmSender.PerformFileOpen(const AFileName: string);
3655 begin
3656   // \8eÀ\8dÛ\82É\83t\83@\83C\83\8b\82ð\8aJ\82­
3657   Screen.Cursor := crHourGlass;
3658   try
3659     memScript.Lines.LoadFromFile(AFileName);
3660     SetFileName(AFileName, true, true);
3661     Pref.TextDir := ExtractFileDir(AFileName);
3662   except
3663     on E: Exception do
3664     begin
3665       ShowMessage('\83G\83\89\81[:'#13#10#13#10 + E.Message);
3666       SetFileName(NewFileTitle, true, true);  // \8e¸\94s\82µ\82½\8e\9e\82Í\83^\83C\83g\83\8b\8f\89\8aú\89»\81B
3667     end;
3668   end;
3669   Screen.Cursor := crDefault;
3670   memScript.SetFocus;
3671   EditerStatusChange;
3672 end;
3673
3674 procedure TfrmSender.PerformFileSave(const AFileName: string);
3675 begin
3676   // \8eÀ\8dÛ\82É\95Û\91\82·\82é
3677   Screen.Cursor := crHourGlass;
3678   try
3679     memScript.Lines.SaveToFile(AFileName);
3680     SetFileModified(false);
3681     Pref.TextDir := ExtractFileDir(AFileName);
3682   except
3683     on E: Exception do
3684       ShowMessage('\83G\83\89\81[:'#13#10#13#10 + E.Message);
3685   end;
3686   Screen.Cursor := crDefault;
3687 end;
3688
3689 procedure TfrmSender.actFileOpenExecute(Sender: TObject);
3690 begin
3691   // \8aJ\82­
3692   if CheckFileModified(Self) = idCancel then exit;
3693   OpenDialog.InitialDir := Pref.TextDir;
3694   if OpenDialog.Execute then
3695     if CheckFileExt(OpenDialog.FileName) then // \8ag\92£\8eq\82Ì\83`\83F\83b\83N
3696       PerformFileOpen(OpenDialog.FileName);
3697 end;
3698
3699 procedure TfrmSender.SetFileName(const FileName: string; Reset, SetNewFile: boolean);
3700 var
3701   FPath: string;
3702 begin
3703   // \83^\83C\83g\83\8b\95Ï\8dX\82Æ\83t\83@\83C\83\8b\83p\83X\83Z\83b\83g
3704   // \82à\82µ\81A\91æ\93ñ\88ø\90\94\82ªfalse\82È\82ç\83^\83C\83g\83\8b\82¾\82¯\95Ï\8dX
3705   if Reset then
3706   begin
3707     SetFileModified(false);               // \95Ï\8dX\96³\82µ\82É
3708     FScriptBackupModified := false;       // \8e©\93®\95Û\91\88ê\8e\9e\92â\8e~
3709     DeleteBackupFile(FScriptBackupFile);  // \88ê\8e\9e\83t\83@\83C\83\8b\8dí\8f\9c\82Ö
3710   end;
3711
3712   if SetNewFile then
3713     FFileName := FileName;                // \8d¡\8c»\8dÝ\82Ì\95Ò\8fW\83t\83@\83C\83\8b\96¼
3714
3715   FPath := ExtractFileName(FileName);
3716
3717   if Pref.ScriptBackupMode then
3718     FScriptBackupFile := FormatDateTime('yymmddhhnnss', Now) + FPath
3719   else
3720     FScriptBackupFile := StdBackupFile;
3721   Self.Caption := Format('%s - %s', [FPath, FOriginalCaption]);
3722 end;
3723
3724 function TfrmSender.CheckFileModified(Sender: TObject): integer;
3725 var
3726   Res: integer;
3727 begin
3728   // \83t\83@\83C\83\8b\82Ì\95Ï\8dX\82ð\8am\94F
3729   // Result := idCancel\82Å\8fI\97¹\91j\8e~
3730   if FFileModified then
3731   begin
3732     Res := MessageDlg(Format('\83t\83@\83C\83\8b %s \82Í\95Ï\8dX\82³\82ê\82Ä\82¢\82Ü\82·\81B'#13#10#13#10 +
3733       '\95Û\91\82µ\82Ü\82·\82©\81H', [FFileName]), mtConfirmation, mbYesNoCancel, 0);
3734     if Res = idYes then Res := FileSave(Self);
3735   end else
3736     Res := idNo;
3737   Result := Res;
3738 end;
3739
3740 function TfrmSender.FileSave(Sender: TObject): integer;
3741 var
3742   Res: integer;
3743 begin
3744   // \8fã\8f\91\82«
3745   if FFileName = NewFileTitle then
3746     Res := FileSaveAs(Sender)       // \96¼\91O\82ð\95t\82¯\82Ä\95Û\91\82Ö
3747   else
3748   begin
3749     PerformFileSave(FFileName);     // \8eÀ\8dÛ\82É\95Û\91\82·\82é\82Ö
3750     Res := idNo;
3751   end;
3752   Result := Res;
3753 end;
3754
3755 function TfrmSender.FileSaveAs(Sender: TObject): integer;
3756 var
3757   Res: integer;
3758 begin
3759   // \96¼\91O\82ð\95t\82¯\82Ä\95Û\91
3760   Res := idYes;
3761   SaveDialog.FileName := ExtractFileName(FFileName);
3762   SaveDialog.InitialDir := Pref.TextDir;
3763   if SaveDialog.Execute then
3764   begin
3765     if CheckFileExt(SaveDialog.FileName) = false then // \8ag\92£\8eq\83`\83F\83b\83N
3766     begin
3767       Res := idCancel;
3768       abort;
3769     end;
3770     if FileExists(SaveDialog.FileName) then
3771       Res := MessageDlg(Format('%s \82Í\8aù\82É\91\8dÝ\82µ\82Ä\82¢\82Ü\82·\81B'#13#10 + '\8fã\8f\91\82«\82µ\82Ü\82·\82©\81H',
3772         [SaveDialog.FileName]), mtConfirmation, mbYesNoCancel, 0);
3773     if Res = idYes then
3774     begin
3775       PerformFileSave(SaveDialog.FileName);
3776       SetFileName(SaveDialog.FileName, true, true);
3777     end;
3778   end else
3779     Res := idCancel;
3780   Result := Res;
3781 end;
3782
3783 procedure TfrmSender.actFileSaveAsExecute(Sender: TObject);
3784 begin
3785   // \96¼\91O\82ð\95t\82¯\82Ä\95Û\91\8cÄ\82Ñ\8fo\82µ
3786   FileSaveAs(Self);
3787 end;
3788
3789 procedure TfrmSender.actFileSaveExecute(Sender: TObject);
3790 begin
3791   // \8fã\8f\91\82«\95Û\91\8cÄ\82Ñ\8fo\82µ
3792   FileSave(Self);
3793 end;
3794
3795 procedure TfrmSender.SetFileModified(Value: boolean);
3796 begin
3797   // \83t\83@\83C\83\8b\95Ï\8dX\83t\83\89\83O\82Æ\95\\8e¦\95Ï\8dX
3798   if Value then
3799     StatusBar.Panels[PanelConnecting].Text := '\95Ï\8dX'
3800   else
3801     StatusBar.Panels[PanelConnecting].Text := '';
3802   FFileModified := Value;
3803 end;
3804
3805 procedure TfrmSender.OpenBrowser(const Url: string);
3806 var
3807   Command: string;
3808 begin
3809   if Pref.BrowserExeName = '' then
3810   begin
3811     ShellExecute(Handle, 'open', PChar(Url), nil, nil, SW_SHOW);
3812   end else
3813   begin
3814     Command := Pref.BrowserExeName + ' ' + Url;
3815     WinExec(PChar(Command), SW_SHOW);
3816   end;
3817 end;
3818
3819 function TfrmSender.CheckFileExt(const FileName: string): boolean;
3820 var
3821   Res: boolean;
3822 begin
3823   // \8ag\92£\8eq.bak\82Ì\83t\83@\83C\83\8b\82Í\93Ç\8f\91\82«\82µ\82È\82¢\81B
3824   Res := true;
3825   if LowerCase(ExtractFileExt(FileName)) = '.bak' then  // StdBackupFile\82Æ\93¯\8ag\92£\8eq
3826   begin
3827     beep;
3828     ShowMessage('\8ag\92£\8eqbak\82Í\88µ\82¦\82Ü\82¹\82ñ\81B'#13#10 + '\91¼\82Ì\83G\83f\83B\83^\82ð\8eg\97p\82µ\82Ä\82­\82¾\82³\82¢\81B');
3829     Res := false;
3830   end;
3831   Result := Res;
3832 end;
3833
3834 procedure TfrmSender.DeleteBackupFile(const FileName: string);
3835 var
3836   FPath: string;
3837 begin
3838   // \8fã\8f\91\95Û\91\82Ì\88ê\8e\9e\83t\83@\83C\83\8b\82ð\8dí\8f\9c
3839   if Pref.NoDeleteBackupFile or (FileName = '') or
3840     (StdBackupFile = FileName) then exit;
3841
3842   FPath := FScriptBackupDir + FileName;
3843   try
3844     if FileExists(FPath) then
3845       DeleteFile(FPath);
3846   except
3847     on E: Exception do
3848       ShowMessage('\88ê\8e\9e\83t\83@\83C\83\8b\8dí\8f\9c\83G\83\89\81[:'#13#10#13#10 + E.Message);
3849   end;
3850 end;
3851
3852 end.