OSDN Git Service

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