OSDN Git Service

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