OSDN Git Service

ヘルプページ、SSTP Bottleホームページ、投票ランキングページの各メニューでウェブページを
[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 var
1366   Command: string;
1367 begin
1368   if Pref.BrowserExeName='' then
1369     begin
1370       ShellExecute(Handle, 'open', PChar(Pref.HomePage), nil, nil, SW_SHOW);
1371     end else
1372     begin
1373       Command := Pref.BrowserExeName+' '+Pref.HomePage;
1374       WinExec(PChar(Command), SW_SHOW);
1375     end;
1376 end;
1377
1378 procedure TfrmSender.ShowHintLabel(const Mes: String; Col: TColor);
1379 begin
1380   lblMessage.Caption := Mes;
1381   lblMessage.Font.Color := Col;
1382   lblMessage.Visible := true;
1383   LabelTimer.Enabled := false;
1384   LabelTimer.Enabled := true;
1385 end;
1386
1387 procedure TfrmSender.LabelTimerTimer(Sender: TObject);
1388 begin
1389   LabelTimer.Enabled := false;
1390   lblmessage.Visible := false;
1391 end;
1392
1393 procedure TfrmSender.actCopyAllExecute(Sender: TObject);
1394 var Str: String;
1395     Clip: TClipBoard;
1396 begin
1397   Str := memScript.Lines.Text;
1398   Clip := ClipBoard();
1399   Clip.SetTextBuf(PChar(Str));
1400 end;
1401
1402 procedure TfrmSender.actCopyAllNoReturnExecute(Sender: TObject);
1403 var Str: String;
1404     Clip: TClipBoard;
1405 begin
1406   Str := memScript.Lines.Text;
1407   RegExp.Subst('s/\r\n//gk', Str);
1408   Clip := ClipBoard();
1409   Clip.SetTextBuf(PChar(Str));
1410 end;
1411
1412 procedure TfrmSender.Slpp20SlppEvent(Sender: TObject; EventType: TIdSlppEventType;
1413   const Param: String);
1414 var HeadValue: THeadValue;
1415 begin
1416   HeadValue := THeadValue.Create(Param);
1417   try
1418     case EventType of
1419       etScript, etForceBroadcast, etUnicast: begin
1420         //\83\81\83b\83Z\81[\83W\8eó\90M
1421         DispatchBottle(EventType, HeadValue);
1422       end;
1423       etMemberCount: begin
1424         StatusBar.Panels[PanelMembers].Text := HeadValue['Num'] + '\90l'
1425       end;
1426       etChannelCount: begin
1427         try
1428           ChannelList.Channel[HeadValue['Channel']].Members := StrToInt(HeadValue['Num']);
1429         except
1430         end;
1431       end;
1432       etConnectOk: begin
1433         ShowHintLabel('SSTP Bottle\83T\81[\83o\82Æ\92Ê\90M\8am\97§\81B');
1434         Added := true;
1435         FBeginConnectFailCount := 0;
1436         //\83`\83\83\83\93\83l\83\8b\8e©\93®\93o\98^
1437         if not Connecting then
1438           PostCommand(['Command: getChannels']);
1439         SakuraSeeker.BeginDetect;
1440       end;
1441       etChannelList: begin
1442         UpdateJoinChannelList(HeadValue);
1443         // \8dÅ\8cã\82É\8eQ\89Á\82µ\82Ä\82¢\82½\83`\83\83\83\93\83l\83\8b\82ð\8bL\98^\82·\82é
1444         if JoinChannelsBackup = nil then JoinChannelsBackup := TStringList.Create;
1445         JoinChannelsBackup.Assign(JoinChannels);
1446       end;
1447       etCloseChannel: begin
1448         with JoinChannels do
1449           if IndexOf(HeadValue['Channel']) >= 0 then
1450             Delete(IndexOf(HeadValue['Channel']));
1451         with tabChannel do begin
1452           if Tabs.IndexOf(HeadValue['Channel']) >= 0 then
1453             Tabs.Delete(Tabs.IndexOf(HeadValue['Channel']));
1454           if Tabs.Count > 0 then TabIndex := 0 else TabIndex := -1;
1455           tabChannelChange(self);
1456         end;
1457         ShowHintLabel(HeadValue['Channel'] + '\83`\83\83\83\93\83l\83\8b\82Í\94p\8e~\82³\82ê\82Ü\82µ\82½',
1458                       WarningColor);
1459         frmLog.AddCurrentSystemLog('SYSTEM', HeadValue['Channel'] + '\83`\83\83\83\93\83l\83\8b\82Í\94p\8e~\82³\82ê\82Ü\82µ\82½');
1460         frmMessageBox.ShowMessage(HeadValue['Channel'] + '\83`\83\83\83\93\83l\83\8b\82Í\94p\8e~\82³\82ê\82Ü\82µ\82½');
1461       end;
1462       etForceBroadcastInformation: begin
1463         if HeadValue['Type'] = 'Vote' then begin
1464           frmLog.VoteLog(HeadValue['MID'], StrToIntDef(HeadValue['Num'], 0));
1465         end else if HeadValue['Type'] = 'Agree' then begin
1466           frmLog.AgreeLog(HeadValue['MID'], StrToIntDef(HeadValue['Num'], 0));
1467         end;
1468       end;
1469     end;
1470   finally
1471     HeadValue.Free;
1472   end;
1473 end;
1474
1475 procedure TfrmSender.BottleSstpResendCountChange(Sender: TObject);
1476 begin
1477   StatusBar.Panels[PanelCount].Text := IntToStr(FBottleSstp.CueCount) + '\8c\8f';
1478   try
1479     TaskTray.TipString := 'SSTP Bottle Client (' +
1480                           IntToStr(FBottleSstp.CueCount) + '\8c\8f)';
1481   except
1482     ; // \83^\83X\83N\83g\83\8c\83C\93o\98^\8e¸\94s\82ð\96³\8e\8b\82·\82é
1483   end;
1484   actClearBottles.Enabled := (FBottleSstp.CueCount > 0);
1485 end;
1486
1487 procedure TfrmSender.actSettingExecute(Sender: TObject);
1488 begin
1489   Application.CreateForm(TfrmSetting, frmSetting);
1490   try
1491     frmSetting.Execute;
1492     Pref.SaveSettings;
1493     SaveChainRuleList;
1494   finally
1495     frmSetting.Release;
1496     frmSetting := nil;
1497   end;
1498   //
1499   BuildReplaceMenu(mnPresetReplaceRoot);
1500   UpdateLayout;
1501   tabChannel.Repaint;
1502   frmLog.UpdateWindow;
1503 end;
1504
1505 procedure TfrmSender.memScriptKeyPress(Sender: TObject; var Key: Char);
1506 begin
1507   if (Key = #13) or (Key = #10) then Key := Char(0);
1508 end;
1509
1510 procedure TfrmSender.Slpp20Disconnect(Sender: TObject);
1511 begin
1512   Added := false;
1513   UpdateJoinChannelList(nil);
1514   frmLog.AddCurrentSystemLog('SYSTEM', '\83T\81[\83o\82©\82ç\90Ø\92f\82³\82ê\82Ü\82µ\82½');
1515   if not Application.Terminated then RetryBeginConnect;
1516 end;
1517
1518 procedure TfrmSender.SetSleeping(const Value: boolean);
1519 begin
1520   FSleeping := Value;
1521   FBottleSstp.ResendSleep := Value;
1522   ChangeTaskIcon;
1523 end;
1524
1525 procedure TfrmSender.actClearBottlesExecute(Sender: TObject);
1526 var Re: integer;
1527 begin
1528   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]),
1529                    mtWarning, mbOkCancel, 0);
1530   if Re = mrOk then begin
1531     FBottleSstp.Clear;
1532     frmLog.AllBottleOpened;
1533     frmLog.UpdateWindow;
1534   end;
1535 end;
1536
1537 procedure TfrmSender.SakuraSeekerDetectResultChanged(Sender: TObject);
1538 begin
1539   UpdateIfGhostBox; // \83h\83\8d\83b\83v\83_\83E\83\93\82Ì\92\86\90g\82ð\8f\91\82«\8a·\82¦\82é
1540 end;
1541
1542 procedure TfrmSender.UpdateChannelInfo(Dat: THeadValue);
1543 var i: integer;
1544     Ch: TChannelListItem;
1545 begin
1546   ChannelList.Clear;
1547   for i := 1 to Dat.IntData['Count'] do begin
1548     Ch := TChannelListItem.Create;
1549     Ch.Name    := Dat[Format('CH%d_name', [i])];
1550     Ch.Ghost   := Dat[Format('CH%d_ghost', [i])];
1551     Ch.Info    := Dat[Format('CH%d_info', [i])];
1552     Ch.NoPost  := Dat[Format('CH%d_nopost', [i])] = '1';
1553     Ch.Members := Dat.IntData[Format('CH%d_count', [i])];
1554     Ch.WarnPost:= Dat[Format('CH%d_warnpost', [i])] = '1';
1555     ChannelList.Add(Ch);
1556   end;
1557   UpdateLayout;
1558 end;
1559
1560 procedure TfrmSender.mnGetNewIdClick(Sender: TObject);
1561 begin
1562   PostCommand(['Command: getNewId', 'Agent: ' + VersionString]);
1563 end;
1564
1565 procedure TfrmSender.NoLuidError;
1566 begin
1567   Beep;
1568   ShowMessage('SSTP Bottle ID\82Ì\8eæ\93¾\82ª\82Ü\82¾\8a®\97¹\82µ\82Ä\82¢\82Ü\82¹\82ñ\81B'#13#10+
1569               '\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+
1570               '\82±\82Ì\91\80\8dì\82ÍClient\8f\89\89ñ\8bN\93®\8e\9e\82É1\89ñ\82¾\82¯\95K\97v\82Å\82·\81B');
1571 end;
1572
1573 procedure TfrmSender.tabChannelChange(Sender: TObject);
1574 begin
1575   if tabChannel.TabIndex >= 0 then begin
1576     FNowChannel := tabChannel.Tabs[tabChannel.TabIndex];
1577     actSend.Hint := Format('\81u%s\81v\82É\91\97\90M|%s', [FNowChannel, SendButtonLongHint]);
1578   end else begin
1579     FNowChannel := '';
1580     actSend.Hint := '';
1581   end;
1582   tabChannel.Repaint; //\82±\82ê\82ª\82È\82¢\82Æ\90F\82ª\95Ï\82í\82ç\82È\82¢\82±\82Æ\82ª\82 \82é
1583   ConstructMenu(true);
1584 end;
1585
1586 procedure TfrmSender.actPrevChannelExecute(Sender: TObject);
1587 begin
1588   with tabChannel do begin
1589     if Tabs.Count = 0 then Exit;
1590     if TabIndex=0 then TabIndex := Tabs.Count-1
1591     else TabIndex := TabIndex-1;
1592   end;
1593   tabChannelChange(Self);
1594 end;
1595
1596 procedure TfrmSender.actNextChannelExecute(Sender: TObject);
1597 begin
1598   with tabChannel do begin
1599     if Tabs.Count = 0 then Exit;
1600     if TabIndex=Tabs.Count-1 then TabIndex := 0
1601     else TabIndex := TabIndex+1;
1602   end;
1603   tabChannelChange(Self);
1604 end;
1605
1606 procedure TfrmSender.UpdateJoinChannelList(Dat: THeadValue);
1607 var i: integer;
1608     nodat: boolean;
1609 begin
1610   nodat := Dat = nil; //nil\82È\82ç\83`\83\83\83\93\83l\83\8b\91S\89ð\8f\9c
1611   if nodat then Dat := THeadValue.Create('');
1612   JoinChannels.Clear;
1613   for i := 0 to Dat.Count-1 do
1614     if Dat.KeyAt[i] = 'Entry' then begin
1615       if RegExp.Match('m/^(.+?) \((\d+?)\)$/', Dat.ValueAt[i]) then
1616         JoinChannels.Add(RegExp[1]);
1617     end;
1618   with tabChannel do begin
1619     OnChange := nil;
1620     JoinChannels.Sort;
1621     Tabs.BeginUpdate;
1622     Tabs.Clear;
1623     for i := 0 to JoinChannels.Count-1 do begin
1624       //\8eó\90M\90ê\97p\83`\83\83\83\93\83l\83\8b\82Í\95\\8e¦\82µ\82È\82¢
1625       if not ChannelList.Channel[JoinChannels[i]].NoPost then
1626         Tabs.Add(JoinChannels[i]);
1627     end;
1628     Tabs.EndUpdate;
1629     // \8c³\82©\82ç\83`\83\83\83\93\83l\83\8b\82É\8eQ\89Á\82µ\82Ä\82¢\82½\8fê\8d\87\82Í
1630     // \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)
1631     TabIndex := 0;
1632     for i := 0 to Tabs.Count-1 do
1633       if Tabs[i] = FNowChannel then TabIndex := i;
1634     if Tabs.Count > 0 then begin
1635       FNowChannel := Tabs[TabIndex];
1636       actSend.Hint := Format('\81u%s\81v\82É\91\97\90M|%s', [FNowChannel, SendButtonLongHint]);
1637     end else begin
1638       FNowChannel := '';
1639       actSend.Hint := Format('\91\97\90M|%s', [SendButtonLongHint]);
1640     end;
1641     Visible := Tabs.Count > 0;
1642     if Tabs.Count > 1 then begin
1643       actNextChannel.Enabled := true;
1644       actPrevChannel.Enabled := true;
1645     end else begin
1646       actNextChannel.Enabled := false;
1647       actPrevChannel.Enabled := false;
1648     end;
1649     OnChange := tabChannelChange;
1650   end;
1651   if nodat then Dat.Free;
1652   if JoinChannels.Count = 0 then begin
1653     Self.Caption := FOriginalCaption + ' - \83`\83\83\83\93\83l\83\8b\82É\8eQ\89Á\82µ\82Ä\82¢\82Ü\82¹\82ñ';
1654     actSend.Enabled := false;
1655   end else begin
1656     Self.Caption := FOriginalCaption;
1657     actSend.Enabled := true;
1658   end;
1659 end;
1660
1661 procedure TfrmSender.cbxTargetGhostDropDown(Sender: TObject);
1662 begin
1663   SakuraSeeker.BeginDetect;
1664   UpdateIfGhostBox;
1665 end;
1666
1667 procedure TfrmSender.actShowLogExecute(Sender: TObject);
1668 begin
1669   frmLog.Show;
1670   if frmLog.WindowState = wsMinimized then frmLog.WindowState := wsNormal;
1671 end;
1672
1673 procedure TfrmSender.actSleepExecute(Sender: TObject);
1674 begin
1675   if actSleep.Checked then begin
1676     actSleep.Checked := false;
1677     ShowHintLabel('\83X\83\8a\81[\83v\82ð\89ð\8f\9c\82µ\82Ü\82µ\82½');
1678   end else begin
1679     actSleep.Checked := true;
1680     ShowHintLabel('\83X\83\8a\81[\83v\82ð\90Ý\92è\82µ\82Ü\82µ\82½');
1681   end;
1682   Sleeping := actSleep.Checked;
1683   ChangeTaskIcon;
1684 end;
1685
1686
1687 procedure TfrmSender.DispatchBottle(EventType: TIdSlppEventType;
1688   Dat: THeadValue);
1689 var Opt: TSstpSendOptions;
1690     Event: TBottleChainBottleEvent;
1691     Script, Sender, Ghost, Channel, ErrorMes: String;
1692     BreakFlag, NoDispatch: boolean;
1693     Sound, LogName: String;
1694     i, j, k, SkipCount: integer;
1695     Rule: TBottleChainRule;
1696     Action: TBottleChainAction;
1697     LogNameList: TStringList;
1698     CueItem: TLogItem;
1699     ReplaceHash: THeadValue; 
1700 begin
1701   Opt := [];
1702   if Pref.NoTranslate then Opt := Opt + [soNoTranslate];
1703   if Pref.NoDescript  then Opt := Opt + [soNoDescript];
1704   Channel := Dat['Channel'];
1705   case EventType of
1706     etScript: Sender := Channel;
1707     etForceBroadcast: Sender := '\81y\82¨\92m\82ç\82¹\81z';
1708     etUnicast: Sender := Dat['SenderUID'];
1709   end;
1710
1711   //\96Ú\95W\83S\81[\83X\83g(\83I\81[\83o\81[\83\89\83C\83h\88È\91O)\8c\88\92è
1712   if Dat['IfGhost'] <> '' then begin
1713     Ghost := Dat['IfGhost'];
1714   end else begin
1715     if ChannelList.Channel[Channel] <> nil then
1716       Ghost := ChannelList.Channel[Channel].Ghost;
1717   end;
1718   Dat['TargetGhost'] := Ghost;
1719
1720   // \83\81\83^\95\8e\9a\8f\80\94õ
1721   ReplaceHash := THeadValue.Create;
1722   ReplaceHash['%channel%'] := SafeFileName(Dat['Channel']);
1723   ReplaceHash['%ghost%'] := SafeFileName(Dat['IfGhost']);
1724   ReplaceHash['%date%'] := FormatDateTime('yy-mm-dd', Now());
1725   ReplaceHash['%year%'] := FormatDateTime('yyyy', Now());
1726   ReplaceHash['%yy%'] := FormatDateTime('yy', Now());
1727   ReplaceHash['%month%'] := FormatDateTime('mm', Now());
1728   ReplaceHash['%day%'] := FormatDateTime('dd', Now());
1729   ReplaceHash['%hour%'] := FormatDateTime('hh', Now());
1730   ReplaceHash['%minute%'] := FormatDateTime('nn', Now());
1731
1732   Event := TBottleChainBottleEvent.Create;
1733   try
1734     Event.Data := Dat;
1735     if EventType = etScript then Event.LogType := ltBottle
1736     else Event.LogType := ltSystemLog;
1737
1738     //\83X\83N\83\8a\83v\83g\95Ï\8a·
1739     Script := ScriptTransForSSTP(Dat['Script'], ErrorMes);
1740     if ErrorMes <> '' then begin
1741       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'+
1742                     '\94z\91\97\82³\82ê\82Ü\82¹\82ñ\82Å\82µ\82½\81@\81c '+Dat['Script']);
1743       Exit;
1744     end;
1745
1746     BreakFlag := false;
1747     NoDispatch := false;
1748     Sound := '';
1749     LogNameList := TStringList.Create;
1750     SkipCount := 0;
1751     try
1752       for i := 0 to BottleChainRuleList.Count-1 do begin
1753         if SkipCount > 0 then begin
1754           Dec(SkipCount);
1755           Continue;
1756         end;
1757         Rule := BottleChainRuleList[i];
1758         if not Rule.Enabled then Continue;
1759         if not Rule.Check(Event) then Continue;
1760         for j := 0 to Rule.Actions.Count-1 do begin
1761           Action := (Rule.Actions[j] as TBottleChainAction);
1762           if Action is TBottleChainAbortRuleAction then
1763             BreakFlag := true;
1764           if Action is TBottleChainSkipRuleAction then
1765             SkipCount := (Action as TBottleChainSkipRuleAction).SkipCount;
1766           if (Action is TBottleChainSoundAction) and (Sound = '') then
1767           begin
1768             Sound := (Action as TBottleChainSoundAction).SoundFile;
1769             Sound := StringReplaceEx(Sound, ReplaceHash);
1770           end;
1771           if Action is TBottleChainNoDispatchAction then
1772             NoDispatch := true;
1773           if Action is TBottleChainLogAction then
1774           begin
1775             for k := 0 to (Action as TBottleChainLogAction).LogNames.Count-1 do begin
1776               LogName := (Action as TBottleChainLogAction).LogNames[k];
1777               LogName := StringReplaceEx(LogName, ReplaceHash);
1778               LogNameList.Add(LogName);
1779             end;
1780           end;
1781           if Action is TBottleChainOverrideGhostAction then
1782           begin
1783             Dat['TargetGhost'] := (Action as TBottleChainOverrideGhostAction).TargetGhost;
1784           end;
1785           if Action is TBottleChainQuitAction then
1786             Application.Terminate;
1787           if Action is TBottleChainSaveTextLogAction then
1788             AppendTextLog(StringReplaceEx((Action as TBottleChainSaveTextLogAction).FileName, ReplaceHash),
1789               Format('%s,%s,%s,%s', [Dat['Channel'], Dat['IfGhost'],
1790                 FormatDateTime('yy/mm/dd hh:nn:ss', Now), Dat['Script']]));
1791           if Action is TBottleChainSaveXMLLogAction then
1792             AppendXMLLog(StringReplaceEx((Action as TBottleChainSaveXMLLogAction).FileName, ReplaceHash),
1793               Dat);
1794           if Action is TBottleChainSurfaceReplaceAction then
1795             Script := ReplaceSurface(Script, (Action as TBottleChainSurfaceReplaceAction).Params);
1796         end;
1797         if BreakFlag then Break;
1798       end;
1799
1800       if Dat['Script'] <> '' then begin
1801         for i := 0 to LogNameList.Count-1 do
1802           frmLog.AddCurrentScriptLog(LogNameList[i], Dat['Script'], Sender, Dat['MID'], Dat['IfGhost']);
1803         if NoDispatch then begin
1804           frmLog.SetBottleState(Dat['MID'], lsOpened);
1805         end else begin
1806           Ghost := Dat['TargetGhost']; // \83I\81[\83o\81[\83\89\83C\83h\82³\82ê\82Ä\82¢\82é\89Â\94\\90«\82ª\82 \82é
1807           CueItem := TLogItem.Create(ltBottle, Dat['MID'], Dat['Channel'],
1808             Script, Ghost, Now());
1809           try
1810             FBottleSstp.Push(CueItem);
1811           except
1812             CueItem.Free;
1813           end;
1814         end;
1815       end;
1816
1817       if Dat['DialogMessage'] <> '' then begin
1818         Beep;
1819         frmMessageBox.ShowMessage(
1820           DateTimeToStr(Now) + #13#10 +
1821           'SSTP Bottle\83T\81[\83o\82©\82ç\82¨\92m\82ç\82¹'#13#10+Dat['DialogMessage']);
1822         for i := 0 to LogNameList.Count-1 do
1823           frmLog.AddCurrentSystemLog(LogNameList[i], Dat['DialogMessage']);
1824       end;
1825
1826       //\89¹\82Ì\8dÄ\90
1827       if (Sound <> '') then PlaySound(Sound);
1828     finally
1829       LogNameList.Free;
1830     end;
1831   finally
1832     Event.Free;
1833     ReplaceHash.Free;
1834   end;
1835 end;
1836
1837 procedure TfrmSender.YenETrans;
1838 var St, Le, i: integer;
1839     Orig, Text: String;
1840 begin
1841   St := memScript.SelStart;
1842   Le := memScript.SelLength;
1843   Orig := GetScriptText;
1844   RegExp.Subst('s/(\r\n)+$//kg', Orig);
1845
1846   with SsParser do begin
1847     LeaveEscape := true;
1848     EscapeInvalidMeta := false;
1849     InputString := Orig;
1850   end;
1851   for i := 0 to SsParser.Count-1 do begin
1852     if SsParser[i] <> '\e' then Text := Text + SsParser[i];
1853   end;
1854
1855   Text := Text + '\e';
1856
1857   if Orig <> Text then memScript.Lines.Text := Text;
1858   SsParser.InputString := Text;
1859
1860   RegExp.Subst('s/\r\n//kg', Text);
1861   memScript.SetFocus;
1862   memScript.SelStart := St;
1863   memScript.SelLength := Le;
1864 end;
1865
1866 procedure TfrmSender.PostCommand(const Command: array of String);
1867 var PostStr: TStringList;
1868     i: integer;
1869 begin
1870   PostStr := nil;
1871   try
1872     PostStr := TStringList.Create;
1873     for i := Low(Command) to High(Command) do begin
1874       PostStr.Add(Command[i]);
1875     end;
1876     PostCommand(PostStr);
1877   finally
1878     PostStr.Free;
1879   end;
1880 end;
1881
1882 procedure TfrmSender.PostCommand(Command: TStrings);
1883 var PostStr: String;
1884 begin
1885   Connecting := true;
1886   PostStr := Command.Text;
1887   PostStr := ParamsEncode(PostStr);
1888   try
1889     FHttp := THTTPDownloadThread.Create(Pref.BottleServer, Pref.CgiName, PostStr);
1890     if Pref.UseHttpProxy then begin
1891       FHttp.ProxyServer := Pref.ProxyAddress;
1892       FHttp.ProxyPort   := Pref.ProxyPort;
1893     end;
1894     FHttp.OnSuccess := HttpSuccess;
1895     FHttp.OnConnectionFailed := HttpFailure;
1896     FHttp.FreeOnTerminate := true; // \8f\9f\8eè\82É\8e©\95ª\82ÅFree\82µ\82Ä\82­\82¾\82³\82¢
1897     FHTTP.Resume;
1898   except
1899     on EHeapException do begin
1900       Connecting := false;
1901       FHttp.Free;
1902     end;
1903   end;
1904 end;
1905
1906 procedure TfrmSender.actVoteMessageExecute(Sender: TObject);
1907 var Log: TLogItem;
1908 begin
1909   if frmLog.lvwLog.Selected = nil then Exit;
1910   Log := frmLog.SelectedBottleLog[frmLog.lvwLog.Selected.Index] as TLogItem;
1911   if Log = nil then Exit;
1912   if Log.LogType <> ltBottle then Exit;
1913   PostCommand([
1914     'Command: voteMessage',
1915     'VoteType: Vote',
1916     'LUID: ' + Pref.LUID,
1917     'MID: ' + Log.MID
1918   ]);
1919 end;
1920
1921
1922 procedure TfrmSender.actAgreeMessageExecute(Sender: TObject);
1923 var Log: TLogItem;
1924 begin
1925   if frmLog.lvwLog.Selected = nil then Exit;
1926   Log := frmLog.SelectedBottleLog[frmLog.lvwLog.Selected.Index] as TLogItem;
1927   if Log = nil then Exit;
1928   if Log.LogType <> ltBottle then Exit;
1929   PostCommand([
1930     'Command: voteMessage',
1931     'VoteType: Agree',
1932     'LUID: ' + Pref.LUID,
1933     'MID: ' + Log.MID
1934   ]);
1935 end;
1936
1937
1938 function TfrmSender.GhostNameToSetName(const Ghost: String): String;
1939 begin
1940   if SakuraSeeker.ProcessByName[Ghost] <> nil then
1941     Result := SakuraSeeker.ProcessByName[Ghost].SetName
1942   else
1943     Result := '';
1944 end;
1945
1946 procedure TfrmSender.tabChannelContextPopup(Sender: TObject;
1947   MousePos: TPoint; var Handled: Boolean);
1948 var Ch: String;
1949 begin
1950   with tabChannel do begin
1951     Tag := IndexOfTabAt(MousePos.X, MousePos.Y);
1952     if Tag < 0 then Handled := true;
1953     Ch := Tabs[Tag];
1954   end;
1955 end;
1956
1957 procedure TfrmSender.PostSetChannel(Channels: TStrings);
1958 var PostStr: TStringList;
1959     i: integer;
1960 begin
1961   PostStr := TStringList.Create;
1962   try
1963     with PostStr do begin
1964       Add('Command: setChannels');
1965       Add('Agent: ' + VersionString);
1966       Add('LUID: ' + Pref.LUID);
1967       if Channels <> nil then
1968         for i := 0 to Channels.Count-1 do begin
1969           Add(Format('Ch%d: %s'#13#10, [i+1, Channels[i]]));
1970         end;
1971     end;
1972     PostCommand(PostStr);
1973   finally
1974     PostStr.Free;
1975   end;
1976 end;
1977
1978 procedure TfrmSender.mnLeaveThisChannelClick(Sender: TObject);
1979 var Ch: String;
1980     Chs: TStringList;
1981 begin
1982   // \8ew\92è\82µ\82½\83`\83\83\83\93\83l\83\8b\82©\82ç\94²\82¯\82é
1983   with tabChannel do
1984     Ch := Tabs[Tag]; // \94²\82¯\82½\82¢\83`\83\83\83\93\83l\83\8b\96¼
1985   Chs := TStringList.Create;
1986
1987   // \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ð
1988   // \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é
1989   try
1990     Chs.Assign(JoinChannels);
1991     while Chs.IndexOf(Ch) >= 0 do
1992       Chs.Delete(Chs.IndexOf(Ch));
1993     PostSetChannel(Chs);
1994   finally
1995     Chs.Free;
1996   end;
1997 end;
1998
1999 procedure TfrmSender.mnGotoVoteClick(Sender: TObject);
2000 var
2001   Command: string;
2002 begin
2003   if Pref.BrowserExeName='' then
2004     begin
2005       ShellExecute(Handle, 'open', PChar(Pref.VotePage), nil, nil, SW_SHOW);
2006     end else
2007     begin
2008       Command := Pref.BrowserExeName+' '+Pref.VotePage;
2009       WinExec(PChar(Command), SW_SHOW);
2010     end;
2011 end;
2012
2013 procedure TfrmSender.tabChannelMouseMove(Sender: TObject;
2014   Shift: TShiftState; X, Y: Integer);
2015 var Index: integer;
2016     Ch: String;
2017 begin
2018   with tabChannel do begin
2019     Index := IndexOfTabAt(X, Y);
2020     Ch := Tabs[Index];
2021     Hint := Ch + ': ' + IntToStr(ChannelList.Channel[Ch].Members) + '\90l';
2022   end;
2023 end;
2024
2025 procedure TfrmSender.mnGoToHelpClick(Sender: TObject);
2026 var
2027   Command: string;
2028 begin
2029   if Pref.BrowserExeName='' then
2030     begin
2031       ShellExecute(Handle, 'open', PChar(Pref.HelpPage), nil, nil, SW_SHOW);
2032     end else
2033     begin
2034       Command := Pref.BrowserExeName+' '+Pref.HelpPage;
2035       WinExec(PChar(Command), SW_SHOW);
2036     end;
2037 end;
2038
2039 procedure TfrmSender.tabChannelMouseDown(Sender: TObject;
2040   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
2041 var Index: integer;
2042 begin
2043   with tabChannel do begin
2044     Index := IndexOfTabAt(X, Y);
2045     if Index = -1 then Exit; //\83^\83u\82ª\82È\82¢\82Ì\82Å\83h\83\89\83b\83O\82Å\82«\82È\82¢
2046     if Button = mbLeft then begin
2047       FDragTabIndex := Index; //\83h\83\89\83b\83O\82·\82é\83^\83u\82Ì\83C\83\93\83f\83b\83N\83X\82ð\95Û\91
2048       BeginDrag(False);
2049       FDragTabDest := -1;     //\83h\83\89\83b\83O\98g\90ü\95`\89æ\83t\83\89\83O\83N\83\8a\83A\82Ì\82½\82ß
2050     end;
2051   end;
2052 end;
2053
2054 procedure TfrmSender.tabChannelDragOver(Sender, Source: TObject; X,
2055   Y: Integer; State: TDragState; var Accept: Boolean);
2056 var TargetRect: TRect;
2057     OldDest: integer;
2058 begin
2059   Accept := Source = tabChannel;
2060   if not Accept then Exit;
2061   with tabChannel do begin
2062     OldDest := FDragTabDest;
2063     FDragTabDest := IndexOfTabAt(X, Y);
2064     if FDragTabDest = -1 then begin
2065       Accept := false; //\82±\82Ì\8fê\8d\87\82Í\83h\83\8d\83b\83v\82ð\94F\82ß\82È\82¢
2066       Exit;
2067     end;
2068     with Canvas do begin
2069       Pen.Mode := pmNot;
2070       Pen.Width := 3;
2071     end;
2072     if (OldDest <> FDragTabDest) and (OldDest >= 0) then begin
2073       //\88È\91O\82Ì\98g\90ü\8fÁ\8b\8e
2074       TargetRect := TabRect(OldDest);
2075       with Canvas do begin
2076         Brush.Style := bsClear;
2077         Rectangle(TargetRect.Left, TargetRect.Top,
2078                   TargetRect.Right, TargetRect.Bottom);
2079       end;
2080     end;
2081     if (OldDest <> FDragTabDest) then begin
2082       //\90V\82µ\82¢\98g\90ü\95`\89æ
2083       TargetRect := TabRect(FDragTabDest);
2084       with Canvas do begin
2085         Brush.Style := bsClear;
2086         Rectangle(TargetRect.Left, TargetRect.Top,
2087                   TargetRect.Right, TargetRect.Bottom);
2088       end;
2089     end;
2090   end;
2091 end;
2092
2093 procedure TfrmSender.tabChannelDragDrop(Sender, Source: TObject; X,
2094   Y: Integer);
2095 var DestIndex: integer;
2096 begin
2097   with tabChannel do begin
2098     DestIndex := IndexOfTabAt(X, Y);
2099     Tabs.Move(FDragTabIndex, DestIndex);
2100   end;
2101 end;
2102
2103 procedure TfrmSender.tabChannelEndDrag(Sender, Target: TObject; X,
2104   Y: Integer);
2105 begin
2106   //\8b­\90§\93I\82É\83^\83u\82ð\8dÄ\95`\89æ\82³\82¹\82é\81B\98g\90ü\8fÁ\82µ\91Î\8dô
2107   tabChannel.Tabs.BeginUpdate;
2108   tabChannel.Tabs.EndUpdate;
2109 end;
2110
2111 procedure TfrmSender.cbxTargetGhostDrawItem(Control: TWinControl;
2112   Index: Integer; Rect: TRect; State: TOwnerDrawState);
2113 var AlignRight: boolean;
2114     w: integer;
2115 begin
2116   //\83S\81[\83X\83g\91I\91ð\83{\83b\83N\83X\82Ì\83I\81[\83i\81[\83h\83\8d\81[
2117   with cbxTargetGhost do begin
2118     AlignRight := false;
2119     if Pref.HideGhosts and not FTargetGhostExpand and
2120        (Index = Items.Count-1) and (Index > 0) then
2121     begin
2122       // \81u\82·\82×\82Ä\95\\8e¦\81v
2123       Canvas.Font.Color := clWindowText;
2124       Canvas.Font.Style := [];
2125       AlignRight := true;
2126     end else if (Index > 0) then
2127     begin
2128       // \83S\81[\83X\83g\96¼\82ð\91I\91ð
2129       if SakuraSeeker.ProcessByName[Items[Index]] = nil then
2130         Canvas.Font.Color := clRed // \91\8dÝ\82µ\82È\82¢\83S\81[\83X\83g
2131       else
2132         Canvas.Font.Color := clBlue; // \91\8dÝ\82·\82é\83S\81[\83X\83g
2133       Canvas.Font.Style := [fsBold];
2134     end else
2135     begin
2136       Canvas.Font.Color := clWindowText;
2137       Canvas.Font.Style := [];
2138     end;
2139     if odSelected in State then
2140       Canvas.Font.Color := clHighlightText;
2141     // \95`\89æ
2142     if AlignRight then
2143     begin
2144       w := Canvas.TextWidth(cbxTargetGhost.Items[Index]);
2145       Canvas.TextRect(Rect, Rect.Right - w, Rect.Top,
2146         cbxTargetGhost.Items[Index]);
2147     end else
2148       Canvas.TextRect(Rect, Rect.Left, Rect.Top,
2149         cbxTargetGhost.Items[Index]);
2150   end;
2151 end;
2152
2153 procedure TfrmSender.FormCloseQuery(Sender: TObject;
2154   var CanClose: Boolean);
2155 begin
2156   if (not Pref.ConfirmOnExit) or FEndSession then
2157     Exit;
2158   if MessageDlg('SSTP Bottle Client\82ð\8fI\97¹\82µ\82Ü\82·', mtConfirmation,
2159                 mbOkCancel, 0) = mrCancel then
2160     CanClose := false;
2161 end;
2162
2163 procedure TfrmSender.UpdateIfGhostBox;
2164 var
2165   Selected: String;
2166   i: integer;
2167   HiddenCount: integer;
2168 begin
2169   cbxTargetGhost.DropDownCount := Pref.GhostDropDownCount;
2170   Selected := cbxTargetGhost.Text;
2171   HiddenCount := 0;
2172   with cbxTargetGhost do begin
2173     Items.BeginUpdate;
2174     Items.Clear;
2175     Items.Add(ChannelDefault);
2176     for i := 0 to SakuraSeeker.Count-1 do begin
2177       // \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é
2178       if Length(SakuraSeeker[i].Name) = 0 then Continue;
2179       if Pref.HideGhosts and not FTargetGhostExpand then
2180         if Pref.VisibleGhostsList.IndexOf(SakuraSeeker[i].Name) < 0 then
2181         begin
2182           Inc(HiddenCount);
2183           Continue;
2184         end;
2185       if cbxTargetGhost.Items.IndexOf(SakuraSeeker[i].Name) < 0 then
2186         cbxTargetGhost.Items.Add(SakuraSeeker[i].Name);
2187     end;
2188     cbxTargetGhost.ItemIndex := 0;
2189     if (Length(Selected) > 0) and (Selected <> ChannelDefault) then begin
2190       with cbxTargetGhost do begin
2191         for i := 1 to Items.Count-1 do begin
2192           if Items[i] = Selected then
2193             ItemIndex := i;
2194         end;
2195         //\83S\81[\83X\83g\82ª\93Ë\91R\91\8dÝ\82µ\82È\82­\82È\82Á\82½\8fê\8d\87\91Î\8dô
2196         if ItemIndex = 0 then begin
2197           Items.Add(Selected);
2198           ItemIndex := Items.Count - 1;
2199         end;
2200       end;
2201     end;
2202     if Pref.HideGhosts and not FTargetGhostExpand then
2203       Items.Add(Format('\82·\82×\82Ä(%d)...', [HiddenCount]));
2204     Items.EndUpdate;
2205   end;
2206 end;
2207
2208 procedure TfrmSender.HTTPFailure(Sender: TObject);
2209 begin
2210   SysUtils.Beep;
2211   Beep;
2212   ShowHintLabel('SSTP Bottle\83T\81[\83o\82Æ\82Ì\90Ú\91±\82É\8e¸\94s\82µ\82Ü\82µ\82½', WarningColor);
2213   ShowMessage('SSTP Bottle\83T\81[\83o\82Æ\82Ì\90Ú\91±\82É\8e¸\94s\82µ\82Ü\82µ\82½'#13#10 +
2214     (Sender as THTTPDownloadThread).LastErrorMessage);
2215   Connecting := false;
2216 end;
2217
2218 procedure TfrmSender.actPrevGhostExecute(Sender: TObject);
2219 var i: integer;
2220 begin
2221   SakuraSeeker.BeginDetect;
2222   UpdateIfGhostBox;
2223   i := cbxTargetGhost.ItemIndex;
2224   Dec(i);
2225   if i <= -1 then
2226   begin
2227     i := cbxTargetGhost.Items.Count-1;
2228     if Pref.HideGhosts and not FTargetGhostExpand then
2229       i := i - 1;
2230   end;
2231   cbxTargetGhost.ItemIndex := i;
2232   cbxTargetGhostChange(self);
2233 end;
2234
2235 procedure TfrmSender.actNextGhostExecute(Sender: TObject);
2236 var i: integer;
2237 begin
2238   SakuraSeeker.BeginDetect;
2239   UpdateIfGhostBox;
2240   i := cbxTargetGhost.ItemIndex;
2241   Inc(i);
2242   if Pref.HideGhosts and not FTargetGhostExpand then
2243   begin
2244     if  i > cbxTargetGhost.Items.Count-2 then i := 0;
2245   end else
2246   begin
2247     if  i > cbxTargetGhost.Items.Count-1 then i := 0;
2248   end;
2249   cbxTargetGhost.ItemIndex := i;
2250   cbxTargetGhostChange(self);
2251 end;
2252
2253 procedure TfrmSender.actResetGhostExecute(Sender: TObject);
2254 begin
2255   cbxTargetGhost.ItemIndex := 0; // (CH\90\84\8f§)\82É\96ß\82·
2256   FTargetGhostExpand := false;
2257   if Visible then memScript.SetFocus;
2258   cbxTargetGhostChange(self);
2259 end;
2260
2261 procedure TfrmSender.timDisconnectCheckTimerTimer(Sender: TObject);
2262 begin
2263   if (IdSlpp20.LastReadTimeInterval > Pref.ReconnectWait * 60000) then begin
2264     SysUtils.Beep;
2265     frmLog.AddCurrentSystemLog('SYSTEM', 'SSTP Bottle\83T\81[\83o\82Æ\82Ì\90Ú\91±\82ª\83^\83C\83\80\83A\83E\83g\82µ\82Ü\82µ\82½');
2266     if IdSlpp20.Connected then IdSlpp20.Disconnect;
2267   end;
2268   if not IdSlpp20.Connected then begin
2269     if Added then begin
2270       Slpp20Disconnect(self); //\82È\82º\82©Disconnect\83C\83x\83\93\83g\82ª\8bN\82±\82ç\82¸\82É\90Ø\92f\82µ\82½\8fê\8d\87
2271     end else begin
2272       //\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
2273       //\82½\82¾\82µ\89ñ\90\94\90§\8cÀ\82 \82è
2274       RetryBeginConnect;
2275     end;
2276   end;
2277 end;
2278
2279 procedure TfrmSender.RetryBeginConnect;
2280 begin
2281   if FBeginConnectFailCount < 3 then begin
2282     // \90Ø\92f\82³\82ê\82Ä\82¢\82ê\82Î\8dÄ\90Ú\91±
2283     FAutoAddAfterGetChannel := true;
2284     BeginConnect;
2285   end else if FBeginConnectFailCount = 3 then begin
2286     frmLog.AddCurrentSystemLog('SYSTEM', '\8dÄ\90Ú\91±\8e©\93®\83\8a\83g\83\89\83C\82ð\92\86\8e~\82µ\82Ü\82·');
2287     frmMessageBox.ShowMessage(
2288       'SSTP Bottle\83T\81[\83o\82É\90Ú\91±\82Å\82«\82Ü\82¹\82ñ\81B'#13#10+
2289       '\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+
2290       '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'
2291     );
2292     Inc(FBeginConnectFailCount);
2293   end;
2294 end;
2295
2296 procedure TfrmSender.actDownloadLogExecute(Sender: TObject);
2297 var BottleLog: TBottleLogList;
2298     Title: String;
2299     Cond: TBottleLogDownloadCondition;
2300     NewIndex: integer;
2301   function TimeStr(Mins: integer): String;
2302   var day, hour, min: integer;
2303   begin
2304     day := Mins div (60 * 24);
2305     hour := (Mins div 60) mod 24;
2306     min := Mins mod 60;
2307     Result := '';
2308     if day  > 0 then Result := Result + Format('%d\93ú', [day]);
2309     if hour > 0 then Result := Result + Format('%d\8e\9e\8aÔ', [hour]);
2310     if (min  > 0) or (Result = '') then Result := Result + Format('%d\95ª', [min]);
2311   end;
2312 begin
2313   Application.CreateForm(TfrmLogDownload, frmLogDownload);
2314   try
2315     if frmLogDownload.Execute then begin
2316       with frmLogDownload do begin
2317         if IsRange then begin
2318           if CompareDate(DateLo, DateHi) = 0 then
2319             Title := FormatDateTime('yy-mm-dd', DateLo)
2320           else
2321             Title := FormatDateTime('yy-mm-dd', DateLo) + ' - ' + FormatdateTime('yy-mm-dd', DateHi);
2322         end else begin
2323           Title := Format('\89ß\8b\8e%s', [TimeStr(RecentCount)]);
2324         end;
2325         if Channel <> '' then Title := Title + '(' + Channel + ')';
2326       end;
2327       BottleLog := TBottleLogList.Create(Title);
2328       try
2329         BottleLog.OnLoaded := frmLog.LogLoaded;
2330         BottleLog.OnLoadFailure := frmLog.LogLoadFailure;
2331         BottleLog.OnLoadWork := frmLog.LogLoadWork;
2332         with frmLogDownload do begin
2333           Cond.IsRange := IsRange;
2334           Cond.RecentCount := RecentCount;
2335           Cond.DateLo := DateLo;
2336           Cond.DateHi := DateHi;
2337           Cond.MinVote := MinVote;
2338           Cond.MinAgree := MinAgree;
2339           Cond.Channel := Channel;
2340         end;
2341         BottleLog.LoadFromWeb(Cond);
2342       except
2343         FreeAndNil(BottleLog);
2344       end;
2345       if BottleLog <> nil then begin
2346         NewIndex := frmLog.BottleLogList.Add(BottleLog);
2347         frmLog.UpdateTab;
2348         frmLog.tabBottleLog.TabIndex := NewIndex;
2349         frmLog.UpdateWindow;
2350       end;
2351     end;
2352   finally
2353     frmLogDownload.Release;
2354   end;
2355 end;
2356
2357 function TfrmSender.BuildMenuConditionCheck(const IfGhost,
2358   Ghost: String): boolean;
2359 var i: integer;
2360     Cond: String;
2361 begin
2362   i := 0;
2363   Result := true;
2364   repeat
2365     Cond := Token(IfGhost, ',', i);
2366     if Cond <> '' then begin
2367       if Cond[1] = '!' then begin
2368         Cond := Copy(Cond, 2, High(integer));
2369         if Cond = Ghost then Result := false;
2370       end else begin
2371         if Cond <> Ghost then Result := false;
2372       end;
2373     end;
2374     Inc(i);
2375   until Cond = '';
2376 end;
2377
2378 procedure TfrmSender.BuildMenu(Root: TMenuItem; Simple: boolean);
2379 var i, j, k, count: integer;
2380     ConstData: TScriptConst;
2381     Menu1, Menu2: TMenuItem;
2382     Ghost: String;
2383 begin
2384   // Simple = false \82Ì\8fê\8d\87\82Í\83\81\83j\83\85\81[\82ð\8a®\91S\82É\8dÄ\8d\\92z\82·\82é\81B
2385   // 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
2386   if cbxTargetGhost.ItemIndex > 0 then Ghost := cbxTargetGhost.Text
2387   else if FNowChannel <> '' then Ghost := ChannelList.Channel[FNowChannel].Ghost;
2388
2389   // \8aù\91\82Ì\83\81\83j\83\85\81[\8dí\8f\9c
2390   if Simple then begin
2391     // IfGhost\8fð\8c\8f\95t\82«\83\81\83j\83\85\81[\82Ì\82Ý\8dí\8f\9c
2392     for i := Root.Count-1 downto 0 do begin
2393       if ScriptConstList.GetMenuByID(Root.Items[i].Tag).IfGhost <> '' then
2394         Root.Items[i].Free;
2395     end;
2396   end else
2397     // \91S\95\94\8dí\8f\9c
2398     Root.Clear;
2399
2400   count := -1;
2401   for i := 0 to ScriptConstList.Count-1 do begin
2402     for j := 0 to ScriptConstList[i].Count-1 do begin
2403       // \83S\81[\83X\83g\88á\82¢\82Ì\8fê\8d\87\82Í\83X\83L\83b\83v
2404       if not BuildMenuConditionCheck(ScriptConstList[i][j].IfGhost, Ghost) then Continue;
2405       Inc(count);
2406       // 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
2407       if Simple and (count < Root.Count) then
2408         if (Root.Items[count].Tag = ScriptConstList[i][j].ID) then begin
2409           Continue;
2410         end;
2411
2412       Menu1 := TMenuItem.Create(Root);
2413       with Menu1 do
2414       begin
2415         Caption := ScriptConstList[i][j].Caption;
2416         Hint    := ScriptConstList[i][j].Caption;
2417         AutoHotkeys := maManual;
2418         Tag := ScriptConstList[i][j].ID;
2419         OnClick := mnConstGroupClick;
2420       end;
2421
2422       if not Simple then begin
2423         Root.Add(Menu1);
2424       end else begin
2425         if count < Root.Count-1 then
2426           Root.Insert(count, Menu1)
2427         else
2428           Root.Add(Menu1);
2429       end;
2430
2431       Menu1.Enabled := ScriptConstList[i][j].Count > 0;
2432       for k := 0 to ScriptConstList[i][j].Count-1 do begin
2433         ConstData := ScriptConstList[i][j][k];
2434         Menu2 := TMenuItem.Create(Root);
2435         Menu2.Caption := ConstData.Caption;
2436         Menu2.Hint := ConstData.ConstText;
2437         // if ConstData.ShortCut <> 0 then Menu2.Hint := Menu2.Hint
2438         //   + ' (' + ShortCutToText(ConstData.ShortCut) + ')';
2439         // \83T\81[\83t\83B\83X\83v\83\8c\83r\83\85\81[\8eÀ\8c»\82Ì\82½\82ß\8fã\8dí\8f\9c
2440         Menu2.ShortCut := ConstData.ShortCut;
2441         Menu2.OnClick := mnConstClick;
2442         Menu2.AutoHotkeys := maManual;
2443         Menu2.Tag := ConstData.ID;
2444         if (k mod 15 = 0) and (k > 0) then Menu2.Break := mbBarBreak;
2445         Menu1.Add(Menu2);
2446       end;
2447     end;
2448   end;
2449 end;
2450
2451 procedure TfrmSender.cbxTargetGhostChange(Sender: TObject);
2452 begin
2453   // \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
2454   if Pref.HideGhosts and not FTargetGhostExpand then
2455   begin
2456     with cbxTargetGhost do
2457     begin
2458       // \88ê\94Ô\89º\82Ì\83A\83C\83e\83\80\82ª\91I\91ð\82³\82ê\82½\82Æ\82«\81B
2459       // \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
2460       if (ItemIndex = Items.Count-1) and (ItemIndex > 0) then
2461       begin
2462         FTargetGhostExpand := true;
2463         UpdateIfGhostBox;
2464         ItemIndex := 0;
2465         DroppedDown := true;
2466       end;
2467     end;
2468   end;
2469   // \92è\8c^\8bå\83\81\83j\83\85\81[\82ð\8dÄ\8d\\92z
2470   ConstructMenu(true);
2471   // \83v\83\8c\83r\83\85\81[\82ª\82 \82é\8fê\8d\87\82Í\83v\83\8c\83r\83\85\81[
2472   EditorPreview;
2473 end;
2474
2475 procedure TfrmSender.PlaySound(const FileName: String);
2476 begin
2477   if Pref.SilentWhenHidden and not Application.ShowMainForm then Exit;
2478   try
2479     MediaPlayer.FileName := FileName;
2480     MediaPlayer.Open;
2481     MediaPlayer.Play;
2482   except
2483     on E: EMCIDeviceError do begin
2484       ShowMessage('\83T\83E\83\93\83h\8dÄ\90\83G\83\89\81[:'#13#10 + FileName + #13#10#13#10 + E.Message);
2485     end;
2486   end;
2487 end;
2488
2489 procedure TfrmSender.actFMOExplorerExecute(Sender: TObject);
2490 begin
2491   try
2492     if not Assigned(frmFMOExplorer) then
2493       Application.CreateForm(TfrmFMOExplorer, frmFMOExplorer);
2494   except
2495     on E: Exception do
2496       ShowMessage('FMO\83G\83N\83X\83v\83\8d\81[\83\89\82ð\95\\8e¦\82Å\82«\82Ü\82¹\82ñ\81B'#13#10#13#10 +
2497         E.Message);
2498   end;
2499   frmFMOExplorer.Show;
2500 end;
2501
2502 procedure TfrmSender.SaveChainRuleList;
2503 var Str: TStringList;
2504 begin
2505   Str := TStringList.Create;
2506   try
2507     Str.Text := ComponentToString(BottleChainRuleList);
2508     Str.SaveToFile(ExtractFileDir(Application.ExeName)+'\rule.txt');
2509   finally
2510     Str.Free;
2511   end;
2512 end;
2513
2514 procedure TfrmSender.BottleSstpResendEnd(Sender: TObject; MID: String);
2515 begin
2516   frmLog.SetBottleState(MID, lsOpened);
2517 end;
2518
2519 procedure TfrmSender.BottleSstpResendTrying(Sender: TObject; MID: String);
2520 begin
2521   frmLog.SetBottleState(MID, lsPlaying);
2522 end;
2523
2524 procedure TfrmSender.actInsertCueExecute(Sender: TObject);
2525 var InsertItem: TLogItem;
2526     i, errCount, Res: integer;
2527     Log: TBottleLogList;
2528     ErrorMes: String; // \83X\83N\83\8a\83v\83g\82Ì\83G\83\89\81[\82Ì\93à\97e
2529 begin
2530   if FBottleSstp.CueCount > 0 then begin
2531     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'+
2532       '\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+
2533       '\90V\92\85\83\81\83b\83Z\81[\83W\82Í\82»\82Ì\8cã\82É\8dÄ\90\82³\82ê\82Ü\82·\81B', [FBottleSstp.CueCount]),
2534       mtWarning, mbOkCancel, 0) = mrCancel then Exit;
2535   end;
2536   FBottleSstp.Clear;
2537   frmLog.AllBottleOpened;
2538   if frmLog.lvwLog.Selected = nil then Exit;
2539   Log := frmLog.SelectedBottleLog;
2540   if Log = nil then Exit;
2541   FBottleSSTP.OnResendCountChange := nil;
2542   errCount := 0;
2543   for i := frmLog.lvwLog.Selected.Index downto 0 do begin
2544     if (Log[i] as TLogItem).LogType <> ltBottle then Continue;
2545     InsertItem := TLogItem.Create(Log[i] as TLogItem);
2546     try
2547       InsertItem.Script := ScriptTransForSSTP(InsertItem.Script, ErrorMes);
2548       if ErrorMes <> '' then
2549       begin
2550         Res := MessageDlg('\83X\83N\83\8a\83v\83g\82É\96â\91è\82ª\82 \82é\89Â\94\\90«\82ª\82 \82è\82Ü\82·\81B' +
2551           '\8dÄ\90\82µ\82Ü\82·\82©?'#13#10 + ErrorMes, mtWarning,
2552           mbYesNoCancel, 0);
2553         if Res = mrNo then
2554           raise Exception.Create('Script Syntax Error')
2555         else if Res = mrCancel then
2556         begin
2557           InsertItem.Free;
2558           FBottleSstp.Clear;
2559           frmLog.AllBottleOpened;
2560           Break;
2561         end;
2562       end;
2563       if InsertItem.Ghost = '' then begin
2564         if ChannelList.Channel[InsertItem.Channel] <> nil then
2565         InsertItem.Ghost := ChannelList.Channel[InsertItem.Channel].Ghost;
2566       end;
2567       FBottleSSTP.Push(InsertItem);
2568       frmLog.SetBottleState(InsertItem.MID, lsUnopened);
2569     except
2570       InsertItem.Free;
2571       Inc(errCount);
2572     end;
2573   end;
2574   if errCount > 0 then
2575     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]));
2576   FBottleSSTP.OnResendCountChange := BottleSstpResendCountChange;
2577   BottleSstpResendCountChange(self);
2578   frmLog.lvwLog.Invalidate;
2579 end;
2580
2581 function TfrmSender.ScriptTransForSSTP(const Script: String;
2582   out Error: String): String;
2583 var TransOpt: TScriptTransOptions;
2584 begin
2585   if Pref.NoTransURL then
2586     TransOpt := [toConvertURL, toNoChoice, toWaitScriptEnd]
2587   else
2588     TransOpt := [toConvertURL, toWaitScriptEnd];
2589   if Pref.IgnoreFrequentYenS then TransOpt := TransOpt + [toIgnoreFrequentYenS];
2590   if Pref.FixMessySurface then TransOpt := TransOpt + [toFixMessySurface];
2591   if Pref.HUTagTo01Tag then TransOpt := TransOpt + [toHUTagTo01Tag];
2592   Result := Script;
2593   Error := DoTrans(Result, TransOpt);
2594 end;
2595
2596 procedure TfrmSender.FormResize(Sender: TObject);
2597 var w: integer;
2598 begin
2599   // \83G\83f\83B\83^\81[\95\94\95ª\82Ì\83\8f\81[\83h\83\89\83b\83v\95\9d\82ð\92²\90®\82·\82é
2600   if memScript.ColWidth <> 0 then
2601   begin
2602     with memScript do
2603     begin
2604       w := Width - LeftMargin - ColWidth div 2;
2605       if ScrollBars in [ssVertical, ssBoth] then
2606         w := w - GetSystemMetrics(SM_CYVSCROLL);
2607       WrapOption.WrapByte := w div ColWidth;
2608     end;
2609   end;
2610 end;
2611
2612 procedure TfrmSender.actCopyExecute(Sender: TObject);
2613 begin
2614   memScript.CopyToClipboard;
2615 end;
2616
2617 procedure TfrmSender.actPasteExecute(Sender: TObject);
2618 begin
2619   memScript.PasteFromClipboard;
2620 end;
2621
2622 procedure TfrmSender.actCutExecute(Sender: TObject);
2623 begin
2624   memScript.CutToClipboard;
2625 end;
2626
2627 procedure TfrmSender.actSelectAllExecute(Sender: TObject);
2628 begin
2629   memScript.SelectAll;
2630 end;
2631
2632 procedure TfrmSender.actUndoExecute(Sender: TObject);
2633 begin
2634   memScript.Undo;
2635 end;
2636
2637 procedure TfrmSender.actRedoExecute(Sender: TObject);
2638 begin
2639   memScript.Redo;
2640 end;
2641
2642 function TfrmSender.IsSurfaceTag(const Script: String;
2643   var ID: integer): boolean;
2644 begin
2645   Result := false;
2646   if SsParser.Match(Script, '\s%d') = 3 then
2647   begin
2648     Result := true;
2649     ID := Ord(Script[3]) - Ord('0')
2650   end
2651   else if (Length(Script) > 0) and (SsParser.Match(Script, '\s[%D]') = Length(Script)) then
2652   begin
2653     Result := true;
2654     ID := StrToIntDef(SsParser.GetParam(Script, 1), 0);
2655   end;
2656 end;
2657
2658 procedure TfrmSender.memScriptMouseMove(Sender: TObject;
2659   Shift: TShiftState; X, Y: Integer);
2660 var id: integer;
2661     token: String;
2662 begin
2663   // \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[
2664   if not Pref.SurfacePreviewOnScriptPoint then
2665     Exit;
2666   token := memScript.TokenStringFromPos(Point(X, Y));
2667   if IsSurfaceTag(token, id) then
2668   begin
2669     DoSurfacePreview(id, spEditor);
2670   end else
2671   begin
2672     frmSurfacePreview.HideAway;
2673   end;
2674 end;
2675
2676 procedure TfrmSender.DoSurfacePreview(Surface: integer;
2677   const Kind: TSurfacePreviewType);
2678 var Ghost: String;
2679     Bmp: TBitmap;
2680     CPos: TPoint;
2681 begin
2682   if cbxTargetGhost.ItemIndex > 0 then
2683     ghost := cbxTargetGhost.Text
2684   else if FNowChannel <> '' then
2685     ghost := ChannelList.Channel[FNowChannel].Ghost;
2686
2687   // \93ñ\8fd\95\\8e¦\82Ì\97}\90§
2688   if (FVisiblePreviewGhost = Ghost) and (FVisiblePreviewSurface = Surface) and
2689     not (frmSurfacePreview.IsHidden) then
2690   begin
2691     Exit;
2692   end;
2693
2694   if ghost <> '' then
2695   begin
2696     Bmp := TBitmap.Create;
2697     try
2698       if Spps.TryGetImage(ghost, Surface, Bmp) then
2699       begin
2700         case Kind of
2701           spHint:
2702             CPos := GetSurfacePreviewPositionHint(Bmp.Width, Bmp.Height);
2703           spEditor:
2704             CPos := GetSurfacePreviewPositionScriptPoint(Bmp.Width, Bmp.Height);
2705           else
2706             CPos := Point(0, 0);
2707         end;
2708         frmSurfacePreview.ShowPreview(Bmp, CPos.X, CPos.Y);
2709         FVisiblePreviewGhost := Ghost;
2710         FVisiblePreviewSurface := Surface;
2711       end else
2712         frmSurfacePreview.HideAway;
2713     finally
2714       Bmp.Free;
2715     end;
2716   end;
2717 end;
2718
2719 function TfrmSender.GetSurfacePreviewPositionHint(w, h: integer): TPoint;
2720 {var MousePoint: TPoint;
2721     MenuRect: TRect;}
2722 begin
2723   // \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é
2724   // \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Å
2725   // \88Ä\8aO\82â\82â\82±\82µ\82¢
2726   {GetCursorPos(MousePoint);
2727   OutputDebugString(PChar(IntToStr(FVisibleMenuItem.Count)));
2728   //if GetMenuItemRect(Self.Handle, FVisibleMenuItem.Items[0].Handle, 1, MenuRect) then ;
2729   Result := Point(MenuRect.Left-10, MenuRect.Top-10);}
2730   Result := GetSurfacePreviewPositionScriptPoint(w, h);
2731 end;
2732
2733 function TfrmSender.GetSurfacePreviewPositionScriptPoint(w, h: integer): TPoint;
2734 var MousePoint: TPoint;
2735 begin
2736   GetCursorPos(MousePoint);
2737   case Pref.SurfacePreviewOnScriptPointPosition of
2738     spspMainWindowRight:
2739       Result := Point(Self.Left + Self.Width, MousePoint.Y);
2740   else
2741       Result := Point(Self.Left - w, MousePoint.Y);
2742   end;
2743 end;
2744
2745 procedure TfrmSender.mnConstGroupClick(Sender: TObject);
2746 begin
2747   if (Sender is TMenuItem) then
2748     FVisibleMenuItem := Sender as TMenuItem;
2749 end;
2750
2751 procedure TfrmSender.actRecallScriptBufferExecute(Sender: TObject);
2752 begin
2753   if FScriptBuffer.Count = 0 then
2754     Exit;
2755   memScript.Lines.Assign(FScriptBuffer[0] as TStringList);
2756   memScriptChange(Self);
2757   ShowHintLabel('\83X\83N\83\8a\83v\83g\82ð\8cÄ\82Ñ\8fo\82µ\82Ü\82µ\82½');
2758 end;
2759
2760 procedure TfrmSender.EditorPreview;
2761 var Ghost, Script: String;
2762 begin
2763   if frmEditorTalkShow <> nil then
2764     if frmEditorTalkShow.Visible then
2765     begin
2766       Script := StringReplace(GetScriptText, #13#10, '', [rfReplaceAll]);
2767       Ghost := '';
2768       if cbxTargetGhost.ItemIndex > 0 then
2769         Ghost := cbxTargetGhost.Text
2770       else if FNowChannel <> '' then
2771         Ghost := ChannelList.Channel[FNowChannel].Ghost;
2772       frmEditorTalkShow.TalkShowFrame.View(Script, Ghost);
2773     end;
2774 end;
2775
2776 procedure TfrmSender.actEditorPreviewExecute(Sender: TObject);
2777 begin
2778   if frmEditorTalkShow <> nil then
2779     frmEditorTalkShow.Show
2780   else
2781   begin
2782     Application.CreateForm(TfrmEditorTalkShow, frmEditorTalkShow);
2783     frmEditorTalkShow.Show;
2784   end;
2785   EditorPreview;
2786 end;
2787
2788 // \83v\83\89\83O\83C\83\93\83\8a\83Z\83b\83g
2789 procedure TfrmSender.actResetPluginsExecute(Sender: TObject);
2790 begin
2791   Spps.ClearImagePool;
2792   Spps.LoadFromDirectory(FSppDir);
2793 end;
2794
2795 procedure TfrmSender.IdSLPP20Connect(Sender: TObject);
2796 begin
2797   ShowHintLabel('SSTP Bottle\83T\81[\83o\82ª\8c©\82Â\82©\82è\82Ü\82µ\82½');
2798 end;
2799
2800 // \83X\83N\83\8a\83v\83g\92\86\82Ì\83^\83O\82ð\92u\8a·\82·\82é
2801 // \83T\83C\83Y\89Â\95Ï\82Ì\94z\97ñ\83p\83\89\83\81\81[\83^\94Å
2802 function TfrmSender.TagReplace(Script: String; Before,
2803   After: array of String): String;
2804 var BeforeList, AfterList: TStringList;
2805     i: integer;
2806 begin
2807   BeforeList := TStringList.Create;
2808   AfterList  := TStringList.Create;
2809   try
2810     for i := Low(Before) to High(Before) do
2811       BeforeList.Add(Before[i]);
2812     for i := Low(After) to High(After) do
2813       AfterList.Add(After[i]);
2814     Result := TagReplace(Script, BeforeList, AfterList);
2815   finally
2816     BeforeList.Free;
2817     AfterList.Free;
2818   end;
2819 end;
2820
2821 // \83X\83N\83\8a\83v\83g\92\86\82Ì\83^\83O\82ð\92u\8a·\82·\82é
2822 // StringReplace\82Æ\88á\82Á\82Ä\90³\8am\82É\83^\83O\82É\83}\83b\83`\82µ\81A
2823 // \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¢)
2824 function TfrmSender.TagReplace(Script: String; Before,
2825   After: TStrings): String;
2826 var i, j: integer;
2827     Flag, OldLeaveEscape, OldEscapeInvalidMeta: boolean;
2828     OldStr: String;
2829 begin
2830   Result := '';
2831   with SsParser do
2832   begin
2833     OldStr := InputString;
2834     OldLeaveEscape := LeaveEscape;
2835     OldEscapeInvalidMeta := EscapeInvalidMeta;
2836     LeaveEscape := true;
2837     EscapeInvalidMeta := false;
2838     InputString := Script;
2839   end;
2840   for i := 0 to SsParser.Count-1 do
2841   begin
2842     Flag := false;
2843     for j := 0 to Before.Count-1 do
2844     begin
2845       if (SsParser.MarkUpType[i] = mtTag) and (SsParser[i] = Before[j]) then
2846       begin
2847         Flag := true;
2848         Result := Result + After[j];
2849       end;
2850     end;
2851     if not Flag then
2852       Result := Result + SsParser[i];
2853   end;
2854   with SsParser do
2855   begin
2856     LeaveEscape := OldLeaveEscape;
2857     EscapeInvalidMeta := OldEscapeInvalidMeta;
2858     InputString := OldStr;
2859   end;
2860 end;
2861
2862 // WndProc\82ð\83I\81[\83o\81[\83\89\83C\83h\82µ\82Ä\81AFWM_TaskBarCraeted\82É
2863 // \91Î\89\9e\82·\82é
2864 procedure TfrmSender.WndProc(var Message: TMessage);
2865 begin
2866   if (Message.Msg = FWM_TaskBarCreated) and (FWM_TaskBarCreated <> 0) then
2867   begin
2868     TaskTray.Registered := false; // TTaskTray\82É\83^\83X\83N\83g\83\8c\83C\82ª\8fÁ\82¦\82½\82±\82Æ\82ð
2869                                   // \8bC\82Ã\82©\82¹\82é
2870     ChangeTaskIcon;
2871   end
2872   else
2873     inherited;
2874 end;
2875
2876 // \92u\8a·
2877 procedure TfrmSender.actReplaceExecute(Sender: TObject);
2878 var
2879   Form: TfrmStrReplaceDialog;
2880   Lines: String;
2881   Options: TReplaceFlags;
2882 begin
2883   Application.CreateForm(TfrmStrReplaceDialog, Form);
2884   try
2885     if Form.Execute then
2886     begin
2887       with Form.Pair do
2888       begin
2889         Lines := memScript.Lines.Text;
2890         Options := [rfReplaceAll];
2891         if IgnoreCase then
2892           Options := Options + [rfIgnoreCase];
2893         if UseRegExp then
2894           Lines := StringReplaceRegExp(Lines, BeforeStr, AfterStr, Options)
2895         else
2896           Lines := StringReplace(Lines, BeforeStr, AfterStr, Options);
2897       end;
2898       if Lines <> memScript.Lines.Text then
2899       begin
2900         memScript.SelectAll;
2901         memScript.SelText := Lines;
2902       end;
2903     end;
2904   finally
2905     Form.Release;
2906   end;
2907 end;
2908
2909 procedure TfrmSender.actSendToEditorExecute(Sender: TObject);
2910 var Log: TLogItem;
2911 begin
2912   if frmLog.lvwLog.Selected = nil then Exit;
2913   Log := frmLog.SelectedBottleLog[frmLog.lvwLog.Selected.Index] as TLogItem;
2914   if Log = nil then Exit;
2915   CopyFromLogToEditor(Log);
2916 end;
2917
2918 procedure TfrmSender.actSendToLogWindowExecute(Sender: TObject);
2919 var Ghost, Script: String;
2920 begin
2921   YenETrans;
2922   Script := StringReplace(GetScriptText, #13#10, '', [rfReplaceAll]);
2923   if cbxTargetGhost.ItemIndex > 0 then
2924     Ghost := cbxTargetGhost.Text
2925   else
2926     Ghost := '';
2927   frmLog.AddCurrentScriptLog('\83N\83\8a\83b\83v', Script, ClipChannel, '', Ghost);
2928   ClearEditor;
2929 end;
2930
2931 procedure TfrmSender.memScriptDragOver(Sender, Source: TObject; X,
2932   Y: Integer; State: TDragState; var Accept: Boolean);
2933 begin
2934   // \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é
2935   if Source is TBottleLogDragObject then
2936     Accept := (Source as TBottleLogDragObject).LogItem.LogType = ltBottle
2937 end;
2938
2939 procedure TfrmSender.memScriptDragDrop(Sender, Source: TObject; X,
2940   Y: Integer);
2941 var Src: TBottleLogDragObject;
2942     Log: TLogItem;
2943 begin
2944   // \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é
2945   if not (Source is TBottleLogDragObject) then
2946     Exit;
2947   if (Source as TBottleLogDragObject).LogItem.LogType <> ltBottle then
2948     Exit;
2949   Src := Source as TBottleLogDragObject;
2950   Log := Src.LogItem;
2951   CopyFromLogToEditor(Log);
2952 end;
2953
2954 procedure TfrmSender.CopyFromLogToEditor(Log: TLogItem);
2955 begin
2956   if Log.LogType <> ltBottle then Exit;
2957   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Ä)
2958   memScript.Lines.Clear;
2959   memScript.Lines.Add(Log.Script);
2960   if Log.Ghost <> '' then
2961   begin
2962     // \83S\81[\83X\83g\96¼\82ð\83{\83b\83N\83X\82É\93ü\82ê\82é
2963     // \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Å
2964     // \96³\97\9d\96î\97\9d\83S\81[\83X\83g\96¼\82ª\83{\83b\83N\83X\82É\93ü\82é
2965     cbxTargetGhost.Items.Add(Log.Ghost);
2966     cbxTargetGhost.ItemIndex := cbxTargetGhost.Items.Count-1;
2967     UpdateIfGhostBox;
2968     cbxTargetGhost.Invalidate;
2969   end else
2970     cbxTargetGhost.ItemIndex := 0; // 'CH\90\84\8f§'\82É\82·\82é
2971   memScript.SetFocus;
2972 end;
2973
2974 procedure TfrmSender.actDeleteLogItemExecute(Sender: TObject);
2975 begin
2976   // \83\8d\83O\83E\83B\83\93\83h\83E\82Ì\8cÂ\95Ê\83\8d\83O\82ð\8dí\8f\9c\82·\82é
2977   if frmLog.SelectedBottleLog = nil then
2978     Exit;
2979   if frmLog.lvwLog.Selected = nil then
2980     Exit;
2981   frmLog.SelectedBottleLog.Delete(frmLog.lvwLog.Selected.Index);
2982   frmLog.UpdateWindow;
2983   frmLog.lvwLogChange(Self, nil, ctState);
2984 end;
2985
2986 procedure TfrmSender.ClearEditor;
2987 var TmpScript: String;
2988     Position: Integer;
2989     DoSaveBuffer: boolean;
2990     SavedScript: TStringList;
2991 begin
2992   // \83X\83N\83\8a\83v\83g\82Ì\83N\83\8a\83A
2993   // \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é
2994   DoSaveBuffer := false;
2995   if FScriptBuffer.Count = 0 then
2996     DoSaveBuffer := true
2997   else if (FScriptBuffer[0] as TStringList).Text <> GetScriptText then
2998     DoSaveBuffer := true;
2999   if (GetScriptText = Pref.DefaultScript) or (GetScriptText = '') then
3000     DoSaveBuffer := false;
3001   if DoSaveBuffer then
3002   begin
3003     SavedScript := TStringList.Create;
3004     SavedScript.Text := GetScriptText;
3005     FScriptBuffer.Insert(0, SavedScript);
3006   end;
3007   if FScriptBuffer.Count >= 4 then
3008     FScriptBuffer.Delete(FScriptBuffer.Count-1);
3009   actRecallScriptBuffer.Enabled := FScriptBuffer.Count > 0;
3010
3011   TmpScript := Pref.DefaultScript;
3012   Position := Pos('|', TmpScript);
3013   if Position < 1 then Position := 1;
3014   TmpScript := StringReplace(TmpScript, '|', '', []);
3015   memScript.Lines.Text := TmpScript;
3016   Sendmessage(memScript.Handle, WM_VSCROLL, SB_LINEUP, 0);
3017   memScript.SelStart := Position-1;
3018
3019   if Visible then memScript.SetFocus;
3020   FScriptModified := false;
3021   memScriptChange(self);
3022 end;
3023
3024 procedure TfrmSender.AppendTextLog(const FileName, Line: String);
3025 var
3026   F: TextFile;
3027 begin
3028   //\91\97\90M\83\8d\83O\95Û\91
3029   try
3030     ForceDirectories(ExtractFileDir(FileName));
3031     AssignFile(F, FileName);
3032     if FileExists(FileName) then
3033       Append(F)
3034     else
3035       Rewrite(F);
3036     WriteLn(F, Line);
3037     Flush(F);
3038     CloseFile(F);
3039   except
3040     on E: Exception do
3041       frmLog.AddCurrentSystemLog('SYSTEM', '\83e\83L\83X\83g\83\8d\83O\95Û\91\82É\8e¸\94s\81F'+E.Message);
3042   end;
3043 end;
3044
3045 procedure TfrmSender.AppendXMLLog(const FileName: String; Args: THeadValue);
3046 var
3047   F: TFileStream;
3048   Buf: String;
3049   P: integer;
3050   Impl: TDomImplementation;
3051   Parser: TXmlToDomParser;
3052   DOM: TdomDocument;
3053 begin
3054   try // Long try block to handle all kinds of exception in this method
3055     if not FileExists(FileName) then
3056     begin
3057       // Create empty XML log file
3058       Impl := TDomImplementation.create(nil);
3059       try
3060         ForceDirectories(ExtractFileDir(FileName));
3061         Parser := TXmlToDomParser.create(nil);
3062         Parser.DOMImpl := Impl;
3063         try
3064           try
3065             DOM := Parser.fileToDom(ExtractFilePath(Application.ExeName)+'xbtl.dat');
3066             with DOM do
3067             begin
3068               documentElement.setAttribute('saved',
3069                 FormatDateTime('yy/mm/dd hh:nn:ss', Now));
3070               documentElement.setAttribute('generator', VersionString);
3071               documentElement.setAttribute('version', '1.0');
3072             end;
3073             // \82±\82ê\82Í\96¾\8e¦\93I\82ÉFree\82µ\82È\82­\82Ä\82æ\82¢
3074             F := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
3075             try
3076               DOM.writeCodeAsShiftJIS(F);
3077             finally
3078               F.Free;
3079             end;
3080           except
3081             frmLog.AddCurrentSystemLog('SYSTEM', 'XML\83\8d\83O\95Û\91\82É\8e¸\94s\82µ\82Ü\82µ\82½');
3082           end;
3083         finally
3084           Parser.DOMImpl.freeDocument(DOM);
3085           Parser.Free;
3086         end;
3087       finally;
3088         Impl.Free;
3089       end;
3090     end;
3091     F := TFileStream.Create(FileName, fmOpenReadWrite or fmShareExclusive);
3092     try
3093       P := -11;
3094       SetLength(Buf, 12);
3095       while P > -100 do
3096       begin
3097         F.Seek(P, soFromEnd);
3098         F.Read(Buf[1], 12);
3099         if Buf = '</bottlelog>' then
3100           Break;
3101         Dec(P);
3102       end;
3103       if P = -100 then
3104         raise Exception.Create(FileName + ' is not a valid XML bottle log file')
3105       else
3106       begin
3107         F.Seek(P, soFromEnd);
3108         Buf := Format('<message mid="%s">', [Args['MID']]);
3109         Buf := Buf + Format('<date>%s</date>', [FormatDateTime('yy/mm/dd hh:nn:ss', Now)]);
3110         Buf := Buf + Format('<channel>%s</channel>', [XMLEntity(Args['Channel'])]);
3111         Buf := Buf + Format('<script>%s</script>', [XMLEntity(Args['Script'])]);
3112         Buf := Buf + '<votes>0</votes><agrees>0</agrees>';
3113         Buf := Buf + Format('<ghost>%s</ghost>', [XMLEntity(Args['IfGhost'])]);
3114         Buf := Buf + '</message>';
3115         Buf := Buf + '</bottlelog>';
3116         F.Write(Buf[1], Length(Buf));
3117       end;
3118     finally
3119       F.Free;
3120     end;
3121   except
3122     on E: Exception do
3123       frmLog.AddCurrentSystemLog('SYSTEM', 'XML\83\8d\83O\95Û\91\82É\8e¸\94s\82µ\82Ü\82µ\82½:'+E.Message);
3124   end;
3125 end;
3126
3127 procedure TfrmSender.memScriptSelectionChange(Sender: TObject;
3128   Selected: Boolean);
3129 var
3130   SelText: String;
3131 begin
3132   SelText := memScript.SelText;
3133   if SelText <> '' then
3134   begin
3135     StatusBar.Panels[PanelBytes].Text := Format('(%d\83o\83C\83g)', [Length(SelText)]);
3136   end else
3137   begin
3138     memScriptChange(Self);
3139   end; 
3140 end;
3141
3142 function TfrmSender.ReplaceSurface(Script: String;
3143   Params: TCollection): String;
3144 var
3145   Flag, OldLeaveEscape, OldEscapeInvalidMeta: boolean;
3146   OldStr, Tag: String;
3147   i, j, k, Cur: integer;
3148   Item: TSurfaceReplaceItem;
3149   Before: TSurfaceReplaceBeforeItem;
3150 begin
3151   Result := '';
3152   Cur := 0;
3153   with SsParser do
3154   begin
3155     OldStr := InputString;
3156     OldLeaveEscape := LeaveEscape;
3157     OldEscapeInvalidMeta := EscapeInvalidMeta;
3158     LeaveEscape := true;
3159     EscapeInvalidMeta := false;
3160     InputString := Script;
3161   end;
3162   for i := 0 to SsParser.Count-1 do
3163   begin
3164     if SsParser.MarkUpType[i] <> mtTag then
3165     begin
3166       Result := Result + SsParser.Str[i];
3167       Continue;
3168     end;
3169     Tag := SsParser.Str[i];
3170     if SsParser.Match(Tag, '\s%d') = 2 then
3171       Cur := Ord(Tag[3]) - Ord('0')
3172     else if SsParser.Match(Tag, '\s[%D]') > 0 then
3173       Cur := StrToInt(SsParser.GetParam(Tag, 1))
3174     else
3175     begin
3176       Result := Result + Tag;
3177       Continue;
3178     end;
3179     Flag := false;
3180     for j := 0 to Params.Count-1 do
3181     begin
3182       Item := Params.Items[j] as TSurfaceReplaceItem;
3183       for k := 0 to Item.Before.Count-1 do
3184       begin
3185         Before := Item.Before.Items[k] as TSurfaceReplaceBeforeItem;
3186         if (Cur >= Before.FromNo) and (Cur <= Before.ToNo) then
3187         begin
3188           Flag := true;
3189           Result := Result + Format('\s[%d]', [Item.After]);
3190           Break;
3191         end;
3192       end;
3193       if Flag then
3194         Break;
3195     end;
3196     if not Flag then
3197       Result := Result + Tag;
3198   end;
3199   with SsParser do
3200   begin
3201     LeaveEscape := OldLeaveEscape;
3202     EscapeInvalidMeta := OldEscapeInvalidMeta;
3203     InputString := OldStr;
3204   end;
3205 end;
3206
3207 procedure TfrmSender.WMQueryEndSession(var msg: TWMQueryEndSession);
3208 begin
3209   // Windows\82ª\8fI\97¹\82µ\82æ\82¤\82Æ\82µ\82Ä\82¢\82é\82Ì\82ð\8a´\92m\82·\82é
3210   FEndSession := true;
3211   inherited;
3212 end;
3213
3214 procedure TfrmSender.IdSLPP20ConnectFailed(Sender: TObject);
3215 begin
3216   Added := false;
3217   if FBeginConnectFailCount = 0 then
3218   begin
3219     Beep;
3220     if Pref.UseHttpProxy then
3221       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 +
3222                   '\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 +
3223                   '\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')
3224     else
3225       ShowMessage('SSTP Bottle\83T\81[\83o\82É\90Ú\91±\82Å\82«\82Ü\82¹\82ñ\82Å\82µ\82½\81B'#13#10 +
3226                   '\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 +
3227                   '\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');
3228   end;
3229   ShowHintLabel('SSTP Bottle Server\90Ú\91±\82É\8e¸\94s\82µ\82Ü\82µ\82½', WarningColor);
3230   Inc(FBeginConnectFailCount);
3231 end;
3232
3233 procedure TfrmSender.BuildReplaceMenu(Root: TMenuItem);
3234 var
3235   i: integer;
3236   Presets: TReplacePresetCollection;
3237   NewItem: TMenuItem;
3238 begin
3239   Root.Clear;
3240   Presets := Pref.ReplacePresets.Presets;
3241   for i := 0 to Presets.Count-1 do
3242   begin
3243     NewItem := TMenuItem.Create(Root);
3244     with NewItem do
3245     begin
3246       Caption := Presets[i].Title;
3247       ShortCut := Presets[i].ShortCut;
3248       AutoHotkeys := maManual;
3249       Hint := Presets[i].Pairs.StringExpression;
3250       Tag := i;
3251       OnClick := mnPresetReplaceClick;
3252     end;
3253     Root.Add(NewItem);
3254   end;
3255   Root.Enabled := Presets.Count > 0;
3256 end;
3257
3258 procedure TfrmSender.mnPresetReplaceClick(Sender: TObject);
3259 var
3260   Preset: TReplacePreset;
3261   Lines, New: string;
3262 begin
3263   // \83v\83\8a\83Z\83b\83g\92u\8a·\82ð\8eÀ\8ds
3264   Preset := Pref.ReplacePresets.Presets[(Sender as TMenuItem).Tag];
3265   Lines := memScript.Lines.Text;
3266   New := Preset.Pairs.ExecuteReplace(Lines);
3267   if New <> Lines then
3268   begin
3269     memScript.SelectAll;
3270     memScript.SelText := New;
3271   end;
3272   if Preset.ConfirmAfterReplace then
3273   begin
3274     btnConfirm.Click;
3275   end;
3276 end;
3277
3278 end.