OSDN Git Service

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