OSDN Git Service

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