OSDN Git Service

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