OSDN Git Service

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