OSDN Git Service

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