OSDN Git Service

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