OSDN Git Service

View log download progress
[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     actTrans: TAction;
39     N6: TMenuItem;
40     mnPopConst: TMenuItem;
41     mnEdit: TMenuItem;
42     mnPaste: TMenuItem;
43     mnCopy: TMenuItem;
44     mnCut: TMenuItem;
45     mnSelectAll: TMenuItem;
46     TaskTray: TTaskTray;
47     ApplicationEvents: TApplicationEvents;
48     mnPopUpTaskTray: TPopupMenu;
49     mnTaskStart: TMenuItem;
50     mnTaskEnd: TMenuItem;
51     mnTaskRestore: TMenuItem;
52     mnTaskNewMessage: TMenuItem;
53     actStart: TAction;
54     actStop: TAction;
55     N8: TMenuItem;
56     mnTaskExit: TMenuItem;
57     actSend: TAction;
58     actConfirm: TAction;
59     actClear: TAction;
60     mnSend: TMenuItem;
61     mnConfirm: TMenuItem;
62     mnClear: TMenuItem;
63     N9: TMenuItem;
64     imgIcon: TImageList;
65     mnPopupConst: TPopupMenu;
66     actEditConst: TAction;
67     mnView: TMenuItem;
68     mnShowToolBar: TMenuItem;
69     mnShowConstBar: TMenuItem;
70     ConstBarMenu: TMainMenu;
71     ToolBar: TToolBar;
72     tbtnClear: TToolButton;
73     tbtnConfirm: TToolButton;
74     tbtnSend: TToolButton;
75     tbtnSeparator: TToolButton;
76     tbtnStart: TToolButton;
77     tbtnSeparator2: TToolButton;
78     tbtnInsertConst: TToolButton;
79     ConstMenuBar: TMenuBar;
80     mnGoToHP: TMenuItem;
81     LabelTimer: TTimer;
82     mnColorScript: TMenuItem;
83     mnCopyAll: TMenuItem;
84     actCopyAll: TAction;
85     actCopyAllNoReturn: TAction;
86     mnCopyAllNoReturn: TMenuItem;
87     mnPopCopyAll: TMenuItem;
88     mnPopCopyAllNoReturn: TMenuItem;
89     actSetting: TAction;
90     tbtnSetting: TToolButton;
91     mnStayOnTop: TMenuItem;
92     mnSetting: TMenuItem;
93     actExitClient: TAction;
94     SsParser: TSsParser;
95     tbtnEditConst: TToolButton;
96     actClearBottles: TAction;
97     mnClearBottles: TMenuItem;
98     MediaPlayer: TMediaPlayer;
99     mnGetNewId: TMenuItem;
100     actNextChannel: TAction;
101     actPrevChannel: TAction;
102     N2: TMenuItem;
103     mnNextChannel: TMenuItem;
104     mnPrevChannel: TMenuItem;
105     actShowLog: TAction;
106     N3: TMenuItem;
107     tbtnShowLog: TToolButton;
108     tbtnSleep: TToolButton;
109     actSleep: TAction;
110     N1: TMenuItem;
111     mnSleep: TMenuItem;
112     mnTaskSleep: TMenuItem;
113     pnlEditor: TPanel;
114     tabChannel: TTabControl;
115     memScript: TRichEdit;
116     pnlPanel: TPanel;
117     lblMessage: TLabel;
118     cbxTargetGhost: TComboBox;
119     actVoteMessage: TAction;
120     mnPopUpChannelTab: TPopupMenu;
121     mnLeaveThisChannel: TMenuItem;
122     N4: TMenuItem;
123     mnGotoVote: TMenuItem;
124     mnGotoGLog: TMenuItem;
125     mnGoToHelp: TMenuItem;
126     btnSend: TButton;
127     btnConfirm: TButton;
128     btnClear: TButton;
129     mnExitAllChannels: TMenuItem;
130     actAgreeMessage: TAction;
131     IdSLPP20: TIdSLPP20;
132     btnIfGhost: TButton;
133     actPrevGhost: TAction;
134     actNextGhost: TAction;
135     mnPrevGhost: TMenuItem;
136     mnNextGhost: TMenuItem;
137     actResetGhost: TAction;
138     mnResetGhost: TMenuItem;
139     timDisconnectCheckTimer: TTimer;
140     DirectSstp: TDirectSstp;
141     XPManifest: TXPManifest;
142     actDownloadLog: TAction;
143     procedure actConfirmExecute(Sender: TObject);
144     procedure FormCreate(Sender: TObject);
145     procedure FormDestroy(Sender: TObject);
146     procedure actSendExecute(Sender: TObject);
147     procedure HTTPSuccess(Sender: TObject);
148     procedure HTTPFailure(Sender: TObject);
149     procedure actStartClick(Sender: TObject);
150     procedure actStopExecute(Sender: TObject);
151     procedure FormShow(Sender: TObject);
152     procedure mnAboutClick(Sender: TObject);
153     procedure actExitClientExecute(Sender: TObject);
154     procedure actClearExecute(Sender: TObject);
155     procedure memScriptChange(Sender: TObject);
156     procedure mnStayOnTopClick(Sender: TObject);
157     procedure mnColorScriptClick(Sender: TObject);
158     procedure actEditConstExecute(Sender: TObject);
159     procedure mnTaskBarClick(Sender: TObject);
160     procedure FormClose(Sender: TObject; var Action: TCloseAction);
161     procedure ApplicationEventsMinimize(Sender: TObject);
162     procedure ApplicationEventsRestore(Sender: TObject);
163     procedure mnTaskRestoreClick(Sender: TObject);
164     procedure TaskTrayDblClick(Seft: TObject; Button: TMouseButton);
165     procedure FormActivate(Sender: TObject);
166     procedure mnTaskNewMessageClick(Sender: TObject);
167     procedure ApplicationEventsHint(Sender: TObject);
168     procedure memScriptKeyDown(Sender: TObject; var Key: Word;
169       Shift: TShiftState);
170     procedure mnShowToolBarClick(Sender: TObject);
171     procedure mnShowConstBarClick(Sender: TObject);
172     procedure ApplicationEventsIdle(Sender: TObject; var Done: Boolean);
173     procedure mnGoToHPClick(Sender: TObject);
174     procedure LabelTimerTimer(Sender: TObject);
175     procedure actCopyAllExecute(Sender: TObject);
176     procedure actCopyAllNoReturnExecute(Sender: TObject);
177     procedure Slpp20SlppEvent(Sender: TObject; EventType: TIdSlppEventType;
178       const Param: String);
179     procedure DirectSstpResendCountChange(Sender: TObject);
180     procedure actSettingExecute(Sender: TObject);
181     procedure memScriptKeyPress(Sender: TObject; var Key: Char);
182     procedure Slpp20Disconnect(Sender: TObject);
183     procedure actClearBottlesExecute(Sender: TObject);
184     procedure SakuraSeekerDetectResultChanged(Sender: TObject);
185     procedure mnGetNewIdClick(Sender: TObject);
186     procedure tabChannelChange(Sender: TObject);
187     procedure actPrevChannelExecute(Sender: TObject);
188     procedure actNextChannelExecute(Sender: TObject);
189     procedure cbxTargetGhostDropDown(Sender: TObject);
190     procedure DirectSstpResendTrying(Sender: TObject; ID: Integer;
191       const Script: String);
192     procedure DirectSstpResendEnd(Sender: TObject; ID: Integer;
193       const Script: String);
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   private
225     FSleeping: boolean;
226     FStatusText: String;
227     FConnecting: boolean;
228     FAdded: boolean;
229     FBooted: boolean; //\8f\89\89ñ\8bN\93®\92Ê\90M\97p
230     FOriginalCaption: String;
231     FAutoAddAfterGetChannel: boolean;
232     FConstDir: String;
233     //\83X\83N\83\8a\83v\83g\90F\95ª\82¯\97p\82Ì\95Ï\90\94
234     FRequireColoring: boolean;
235     FUnyuTalking: boolean;
236     FInSynchronized: boolean;
237     FLastSurfaceH: integer;
238     FLastSurfaceU: integer;
239     FColoringPos: integer;     //\90F\95ª\82¯\8dÏ\82Ý\82Ì\83X\83N\83\8a\83v\83g\82Ì\83o\83C\83g\90\94
240     FColoredElements: integer; //\90F\95ª\82¯\8dÏ\82Ý\82Ì\83G\83\8c\83\81\83\93\83g\82Ì\90\94
241     FcolorTimeLag: Int64; //\95Ï\8a·\8aJ\8en\82Ü\82Å\82Ì\83J\83E\83\93\83^
242     //
243     FMutex: THandle; //Mutex\83I\83u\83W\83F\83N\83g\81c\93ñ\8fd\8bN\93®\96h\8e~\97p
244     //
245     FNowChannel: String; //\8c»\8dÝ\91I\91ð\82³\82ê\82Ä\82¢\82é\83`\83\83\83\93\83l\83\8b
246     JoinChannelsBackup: TStringList; //
247     //
248     FDragTabIndex: integer; //\83^\83u\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
249     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)
250     //
251     FCueGhost: TID2Ghost;
252     //
253     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Ý)
254     FBeginConnectFailCount: integer; //\89½\93x\82à\90Ú\91±\8e¸\94s\82µ\82½\82ç\83\8a\83g\83\89\83C\92\86\8e~
255     procedure SetStatusText(const Value: String);
256     procedure SetSleeping(const Value: boolean);
257     function T2C: TColor;
258     procedure ColorAgain;
259     procedure YenETrans;
260     procedure SetConnecting(const Value: boolean);
261     procedure SetAdded(const Value: boolean);
262     procedure mnConstClick(Sender: TObject);
263     property Added: boolean read FAdded write SetAdded;
264     property Sleeping: boolean read FSleeping write SetSleeping;
265     property StatusText: String read FStatusText write SetStatusText;
266     function GetScriptText: String;
267     procedure ChangeTaskIcon;
268     procedure ShowHintLabel(const Mes: String; Col: TColor = clBlue);
269     procedure UpdateLayout;
270     procedure ScriptColorChange(From, Length: integer; Col: TColor);
271     procedure DispatchBottle(EventType: TIdSlppEventType; Dat: THeadValue);
272     //\83`\83\83\83\93\83l\83\8b\8aÖ\8cW
273     procedure UpdateChannelInfo(Dat: THeadValue);
274     procedure UpdateJoinChannelList(Dat: THeadValue);
275     procedure NoLuidError;
276     procedure UpdateIfGhostBox;
277     function BuildMenuConditionCheck(const IfGhost, Ghost: String): boolean;
278     procedure BuildMenu(Root: TMenuItem; Event: TNotifyEvent; Simple: boolean);
279     procedure PlaySound(const FileName: String);
280   public
281     function DoTrans(var Script: String;
282       Options: TScriptTransOptions): String;
283     procedure BeginConnect;
284     procedure RetryBeginConnect;
285     procedure EndConnect;
286     procedure ConstructMenu(Simple: boolean);
287     property Connecting: boolean read FConnecting write SetConnecting;
288     function SetHWndToFavoriteGhost(const Ghost: String): String;
289     function GhostNameToSetName(const Ghost: String): String;
290     procedure PostCommand(const Command: array of String); overload;
291     procedure PostCommand(Command: TStrings); overload;
292     procedure PostSetChannel(Channels: TStrings);
293   end;
294
295
296 var
297   frmSender: TfrmSender;
298
299 const
300   PanelConnecting = 0;  //\81u\90Ú\91±\92\86\81v\95\\8e¦\97p
301   PanelBytes      = 1;  //\81\9b\81\9b\83o\83C\83g
302   PanelCount      = 2;  //Local Proxy\81A\8c»\8dÝ\81\9b\8c\8f\91Ò\82¿
303   PanelMembers    = 3;
304   PanelStatus     = 4;  //\93o\98^\82³\82ê\82Ä\82¢\82Ü\82·\81c\82È\82Ç
305
306   IconConnected    = 17;
307   IconDisconnected = 18;
308   IconSleep        = 19;
309   IconSleepDisconnected = 20;
310
311   WarningColor = clRed;
312
313   SendButtonLongHint = 'Bottle\82Ì\91\97\90M';
314
315 function Token(const Str: String; const Delimiter: char;
316   const Index: integer): String;
317
318 implementation
319
320 uses SendConfirm, SettingForm, ChannelListForm, LogForm,
321   MessageBox;
322
323 {$R *.DFM}
324
325 // \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
326 function Token(const Str: String; const Delimiter: char;
327   const Index: integer): String;
328 var i, c, len: integer;
329 begin
330   i := 1;
331   c := 0;
332   len := length(Str);
333   Result := '';
334   while i <= len do begin
335     if (Str[i] = Delimiter) and (StrByteType(PChar(Str), i) <> mbTrailByte) then begin
336       Inc(c);
337       if c > Index then Break;
338     end else if c = Index then Result := Result + Str[i];
339     Inc(i);
340   end;
341 end;
342
343 {TfrmSender}
344
345 procedure TfrmSender.actConfirmExecute(Sender: TObject);
346 var Res: TSstpResult;
347     Script, Ghost, Err: String;
348     Opt: TScriptTransOptions;
349 begin
350   if Length(GetScriptText) = 0 then Exit;
351   YenETrans;
352   Script := GetScriptText;
353   if Pref.IgnoreTimeCritical then Opt := [toIgnoreTimeCritical] else Opt := [];
354   if Pref.NoTransUrl then Opt := Opt + [toNoChoice];
355   Err := DoTrans(Script, Opt + [toConvertURL, toWarnMessySurface]);
356   if Err <> '' then begin
357     ShowMessage(Err);
358     Exit;
359   end;
360   if cbxTargetGhost.ItemIndex > 0 then begin
361     Ghost := cbxTargetGhost.Text
362   end else begin
363     if FNowChannel <> '' then
364       Ghost := ChannelList.Channel[FNowChannel].Ghost;
365   end;
366   Ghost := SetHWndToFavoriteGhost(Ghost);
367   DirectSstp.SstpSender := 'SSTP Bottle -\81y\8am\94F\81z';
368   
369   Res := DirectSstp.SstpSEND(Script, [soNoTranslate], GhostNameToSetName(Ghost));
370   if Res <> srOk then begin
371     ShowHintLabel('\91\97\90M\8e¸\94s:' + DirectSstp.RecvLog, WarningColor);
372   end else ShowHintLabel('');
373 end;
374
375 procedure TfrmSender.FormCreate(Sender: TObject);
376 var Str: TStringList;
377 begin
378   SakuraSeeker.OnDetectResultChanged := SakuraSeekerDetectResultChanged;
379   FConstDir := ExtractFileDir(Application.ExeName)+'\consts';
380   ScriptConstList.LoadFromDir(FConstDir);
381   ConstructMenu(false);
382
383   Str := TStringList.Create;
384   try
385     try
386       Str.LoadFromFile(ExtractFilePath(Application.ExeName)+'rule.txt');
387       BottleChainRuleList := StringToComponent(Str.Text) as TBottleChainRuleList;
388     except
389       try
390         Str.LoadFromFile(ExtractFilePath(Application.ExeName)+'defrule.txt');
391         BottleChainRuleList := StringToComponent(Str.Text) as TBottleChainRuleList;
392       except
393         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');
394         Application.Terminate;
395         Application.ProcessMessages;
396         Exit;
397       end;
398     end;
399   finally
400     Str.Free;
401   end;
402
403   FOriginalCaption := Self.Caption;
404
405   {$IFDEF BOTTLEMUTEX}
406   FMutex := OpenMutex(MUTEX_ALL_ACCESS, false, 'SSTPBottleClient2');
407   if FMutex <> 0 then begin
408     Beep;
409     ShowMessage('SSTP Bottle Client\82Í\93ñ\8fd\8bN\93®\82Å\82«\82Ü\82¹\82ñ');
410     CloseHandle(FMutex);
411     Application.Terminate;
412     Application.ProcessMessages; //WM_QUIT\82ð\97¬\82·
413     Exit;
414   end;
415   FMutex := CreateMutex(nil, false, 'SSTPBottleClient2');
416   {$ELSE}
417   ShowMessage('\93ñ\8fd\8bN\93®\8b\96\89Â\83o\81[\83W\83\87\83\93\82Å\82·\81B'#13#10 + VersionString);
418   {$ENDIF}
419
420   UpdateLayout;
421   mnShowToolBar.Checked := Pref.ShowToolBar;
422   mnShowConstBar.Checked := Pref.ShowConstBar;
423   if Pref.StayOnTop then begin
424     FormStyle := fsStayOnTop;
425     mnStayOnTop.Checked := true;
426   end else begin
427     FormStyle := fsNormal;
428     mnStayOnTop.Checked := false;
429   end;
430   mnColorScript.Checked := Pref.ColorScript;
431
432   mnGoToHP.Hint := Pref.HomePage;
433   mnGotoGlog.Hint := Pref.GLogPage;
434   mnGotoVote.Hint := Pref.VotePage;
435   mnGotoHelp.Hint := Pref.HelpPage;
436
437   mnGetNewId.Enabled := (Pref.LUID = '');
438
439   try
440     SsParser.TagPattern.LoadFromFile(ExtractFilePath(Application.Exename) + 'tagpat.txt');
441     SsParser.MetaPattern.LoadFromFile(ExtractFilePath(Application.ExeName) + 'metapat.txt');
442   except
443     ShowMessage('tagpat.txt, metapat.txt\82ª\8c©\82Â\82©\82è\82Ü\82¹\82ñ\81B');
444     Application.Terminate;
445   end;
446
447   with Pref.SenderWindowPosition do begin
448     Self.Left   := Left;
449     Self.Top    := Top;
450     Self.Width  := Right - Left + 1;
451     Self.Height := Bottom - Top + 1;
452   end;
453
454   //\83`\83\83\83\93\83l\83\8b\83\8a\83X\83g
455   FCueGhost := TID2Ghost.Create;
456
457   actClearExecute(Sender);
458   ApplicationEvents.OnIdle := ApplicationEventsIdle;
459   ChangeTaskIcon;
460   UpdateJoinChannelList(nil);
461 end;
462
463 procedure TfrmSender.FormDestroy(Sender: TObject);
464 var Str: TStringList;
465 begin
466   with Pref.SenderWindowPosition do begin
467     Left   := Self.Left;
468     Top    := Self.Top;
469     Right  := Self.Left + Self.Width - 1;
470     Bottom := Self.Top + Self.Height - 1;
471   end;
472
473   if JoinChannelsBackup <> nil then JoinChannelsBackup.Free;
474   if FCueGhost <> nil then FCueGhost.Free;
475
476   ScriptConstList.Save;
477
478   Str := TStringList.Create;
479   try
480     Str.Text := ComponentToString(BottleChainRuleList);
481     Str.SaveToFile(ExtractFileDir(Application.ExeName)+'\rule.txt');
482   finally
483     Str.Free;
484   end;
485   BottleChainRuleList.Free;
486
487   {$IFDEF BOTTLEMUTEX}
488   ReleaseMutex(FMutex);
489   {$ENDIF}
490 end;
491
492
493 procedure TfrmSender.SetConnecting(const Value: boolean);
494 begin
495   FConnecting := Value;
496   if Value then begin
497     StatusBar.Panels[PanelConnecting].Text := '\92Ê\90M\92\86';
498     actStart.Enabled := false;
499     actStop.Enabled := false;
500     actSend.Enabled := false;
501     actVoteMessage.Enabled := false;
502     actAgreeMessage.Enabled := false;
503     mnGetNewId.Enabled := false;
504     Screen.Cursor := crAppStart;
505   end else begin
506     StatusBar.Panels[PanelConnecting].Text := '';
507     actStart.Enabled := true;
508     actStop.Enabled := true;
509     actSend.Enabled := true;
510     //actVoteMessage.Enabled := true;
511     //actAgreeMessage.Enabled := true;
512     frmLog.lvwLogChange(Self, nil, ctState);
513     mnGetNewId.Enabled := Pref.LUID = '';
514     Screen.Cursor := crDefault;
515   end;
516 end;
517
518 procedure TfrmSender.actSendExecute(Sender: TObject);
519 var Talk, Ghost: String;
520     Command: TStringList;
521     Err: String;
522     F: TextFile;
523 begin
524   if Length(GetScriptText) = 0 then begin
525     ShowMessage('\83X\83N\83\8a\83v\83g\82ª\8bó\82Å\82·\81B');
526     Exit;
527   end;
528
529   if Pref.LUID = '' then begin
530     NoLuidError;
531     Exit;
532   end;
533   if tabChannel.TabIndex < 0 then begin
534     ShowMessage('\83`\83\83\83\93\83l\83\8b\82É\8eQ\89Á\82µ\82Ä\82¢\82Ü\82¹\82ñ\81B'#13#10+
535       '\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');
536     Exit;
537   end;
538   if ChannelList.Channel[FNowChannel].NoPost then begin
539     Beep;
540     ShowMessage(FNowChannel + ' \82Í\8eó\90M\90ê\97p\82Å\82·');
541     Exit;
542   end;
543   if not Pref.NoConfirm then begin
544     if not SendConfirmDialog(FNowChannel, cbxTargetGhost.Text) then Exit;
545   end;
546
547   YenETrans;
548   Talk := GetScriptText;
549   Err := DoTrans(Talk, [toWarnMessySurface]);
550   if Err <> '' then begin
551     MessageDlg(Err, mtWarning, [mbOk], 0);
552     Exit;
553   end;
554   Command := nil;
555   Ghost := '';
556   if cbxTargetGhost.ItemIndex > 0 then Ghost := cbxTargetGhost.Text;
557   try
558     Command := TStringList.Create;
559     with Command do begin
560       Add('Command: sendBroadcast');
561       Add('Channel: ' + FNowChannel);
562       Add('LUID: ' + Pref.LUID);
563       Add('Agent: ' + VersionString);
564       if Ghost <> '' then Add('Ghost: ' + Ghost);
565       Add('Talk: ' + Talk);
566     end;
567     PostCommand(Command);
568   finally
569     Command.Free;
570   end;
571
572   //\91\97\90M\83\8d\83O\95Û\91
573   AssignFile(F, ExtractFilePath(Application.ExeName) + SentLogFile);
574   if FileExists(ExtractFilePath(Application.ExeName) + SentLogFile) then
575     Append(F)
576   else
577     Rewrite(F);
578   WriteLn(F, Format('%s,%s,%s,%s', [FNowChannel, Ghost, FormatDateTime('yy/mm/dd hh:nn:ss', Now), Talk]));
579   Flush(F);
580   CloseFile(F);
581 end;
582
583 procedure TfrmSender.BeginConnect;
584 begin
585   if Pref.LUID = '' then begin
586     NoLuidError;
587     Exit;
588   end;
589   IdSlpp20.LUID := Pref.LUID;
590   self.Cursor := crHourGlass;
591   try
592     if IdSlpp20.Connected then IdSlpp20.Disconnect;
593     if Pref.UseHttpProxy then begin
594       IdSlpp20.Host := Pref.ProxyAddress;
595       IdSlpp20.Port := Pref.ProxyPort;
596       IdSlpp20.ProxyMode := true;
597     end else begin
598       IdSlpp20.Host := 'bottle.mikage.to';
599       IdSlpp20.Port := 9871;
600       IdSlpp20.ProxyMode := false;
601     end;
602     IdSlpp20.Connect;
603   except
604     on EIdException do begin
605       Added := false;
606       if FBeginConnectFailCount = 0 then begin
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;
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: 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       if SsParser[i] = '\h' then begin
1159         UnyuTalking := false;
1160         if QuickSection then begin
1161           //Result := '\83N\83C\83b\83N\83Z\83N\83V\83\87\83\93\92\86\82É"\h", "\u"\82Í\8eg\82¦\82Ü\82¹\82ñ\81B';
1162           //Exit;
1163         end;
1164       end else if SsParser[i] = '\u' then begin
1165         UnyuTalking := true;
1166         if QuickSection then begin
1167           //Result := '\83N\83C\83b\83N\83Z\83N\83V\83\87\83\93\92\86\82É"\h", "\u"\82Í\8eg\82¦\82Ü\82¹\82ñ\81B';
1168           //Exit;
1169         end;
1170       end else if SsParser[i] = '\_q' then begin
1171         QuickSection := not QuickSection;
1172       end else if SsParser.Match(SsParser[i], '\s%b') > 0 then begin
1173         if UnyuTalking then begin
1174           LastSurfaceU := StrToIntDef(SsParser.GetParam(SsParser[i], 1),
1175                                       LastSurfaceU);
1176         end else begin
1177           LastSurfaceH := StrToIntDef(SsParser.GetParam(SsParser[i], 1),
1178                                       LastSurfaceH);
1179         end;
1180       end else if SsParser.Match(SsParser[i], '\s%d') > 0 then begin
1181         if UnyuTalking then begin
1182           LastSurfaceU := StrToIntDef(SsParser[i][3], LastSurfaceU);
1183         end else begin
1184           LastSurfaceH := StrToIntDef(SsParser[i][3], LastSurfaceH);
1185         end;
1186       end;
1187       Script := Script + SsParser[i];
1188     end;
1189   end;
1190   if UrlCount > 0 then begin
1191     Script := Script + '\h\n';
1192     if not (toNoChoice in Options) then begin
1193       for i := 0 to UrlCount-1 do begin
1194         Script := Script + Format('\q%d[%s][%s]',
1195                     [i, SsParser.EscapeParam(Url[i]), UrlName[i]]);
1196       end;
1197       Script := Script + Format('\q%d[#cancel][%s]', [UrlCount, UrlCancel]);
1198       //Script := Script + '\z'; //\8dÅ\90Vphase\82Å\82Í\8dí\8f\9c
1199     end else begin
1200       Script := Script + '\h';
1201       for i := 0 to UrlCount-1 do begin
1202         Script := Script + Format('\n{%s}(%s)', [UrlName[i], Url[i]]);
1203         Script := Script + Format('\n{%s}', [UrlCancel]);
1204       end;
1205     end;
1206   end;
1207   //\83X\83N\83\8a\83v\83g\82Ì\8dÅ\8cã\82É\83E\83F\83C\83g\91}\93ü
1208   if toWaitScriptEnd in Options then begin
1209     i := Pref.WaitScriptEnd;
1210     while i > 0 do begin
1211       if i > 9 then begin
1212         Script := Script + '\w9';
1213         Dec(i, 9);
1214       end else begin
1215         Script := Script + '\w' + IntToStr(i);
1216         i := 0;
1217       end;
1218     end;
1219   end;
1220
1221   Script := Script + '\e';
1222   RegExp.Subst('s/\r\n//gk', Script);
1223
1224   //\83^\83O\83`\83F\83b\83N\8aÖ\98A
1225   for i := 0 to SsParser.Count-1 do begin
1226     if SsParser.MarkUpType[i] = mtTagErr then begin
1227       Result := '"' + SsParser[i] + '"'#13#10 +
1228                 '\82Í\81ASSTP Bottle\82Å\94F\82ß\82ç\82ê\82È\82¢\82©\81A\94F\8e¯\82Å\82«\82È\82¢\83^\83O\82Å\82·\81B';
1229     end;
1230   end;
1231
1232   SsParser.InputString := Orig;
1233 end;
1234
1235 procedure TfrmSender.mnGoToHPClick(Sender: TObject);
1236 begin
1237   ShellExecute(Handle, 'open', PChar(Pref.HomePage), nil, nil, SW_SHOW);
1238 end;
1239
1240 procedure TfrmSender.ShowHintLabel(const Mes: String; Col: TColor);
1241 begin
1242   lblMessage.Caption := Mes;
1243   lblMessage.Font.Color := Col;
1244   lblMessage.Visible := true;
1245   LabelTimer.Enabled := false;
1246   LabelTimer.Enabled := true;
1247 end;
1248
1249 procedure TfrmSender.LabelTimerTimer(Sender: TObject);
1250 begin
1251   LabelTimer.Enabled := false;
1252   lblmessage.Visible := false;
1253 end;
1254
1255 procedure TfrmSender.ScriptColorChange(From, Length: integer; Col: TColor);
1256 //var Fmt: TCharFormat;
1257 begin
1258   memScript.SelStart := From;
1259   memScript.SelLength := Length;
1260   memScript.SelAttributes.Color := Col;
1261 end;
1262
1263 procedure TfrmSender.actCopyAllExecute(Sender: TObject);
1264 var Str: String;
1265     Clip: TClipBoard;
1266 begin
1267   Str := memScript.Text;
1268   Clip := ClipBoard();
1269   Clip.SetTextBuf(PChar(Str));
1270 end;
1271
1272 procedure TfrmSender.actCopyAllNoReturnExecute(Sender: TObject);
1273 var Str: String;
1274     Clip: TClipBoard;
1275 begin
1276   Str := memScript.Text;
1277   RegExp.Subst('s/\r\n//gk', Str);
1278   Clip := ClipBoard();
1279   Clip.SetTextBuf(PChar(Str));
1280 end;
1281
1282 procedure TfrmSender.Slpp20SlppEvent(Sender: TObject; EventType: TIdSlppEventType;
1283   const Param: String);
1284 var HeadValue: THeadValue;
1285 begin
1286   HeadValue := nil;
1287   try
1288     HeadValue := THeadValue.Create(Param);
1289     case EventType of
1290       etScript, etForceBroadcast, etUnicast: begin
1291         //\83\81\83b\83Z\81[\83W\8eó\90M
1292         HeadValue := THeadValue.Create(Param);
1293         DispatchBottle(EventType, HeadValue);
1294       end;
1295       etMemberCount: begin
1296         StatusBar.Panels[PanelMembers].Text := HeadValue['Num'] + '\90l'
1297       end;
1298       etChannelCount: begin
1299         try
1300           ChannelList.Channel[HeadValue['Channel']].Members := StrToInt(HeadValue['Num']);
1301         except
1302         end;
1303       end;
1304       etConnectOk: begin
1305         ShowHintLabel('SSTP Bottle\83T\81[\83o\82Æ\92Ê\90M\8am\97§\81B');
1306         FBeginConnectFailCount := 0;
1307         //\83`\83\83\83\93\83l\83\8b\8e©\93®\93o\98^
1308         if not Connecting then
1309           PostCommand(['Command: getChannels']);
1310       end;
1311       etChannelList: begin
1312         UpdateJoinChannelList(HeadValue);
1313         // \8dÅ\8cã\82É\8eQ\89Á\82µ\82Ä\82¢\82½\83`\83\83\83\93\83l\83\8b\82ð\8bL\98^\82·\82é
1314         if JoinChannelsBackup = nil then JoinChannelsBackup := TStringList.Create;
1315         JoinChannelsBackup.Assign(JoinChannels);
1316       end;
1317       etCloseChannel: begin
1318         with JoinChannels do
1319           if IndexOf(HeadValue['Channel']) >= 0 then
1320             Delete(IndexOf(HeadValue['Channel']));
1321         with tabChannel do begin
1322           if Tabs.IndexOf(HeadValue['Channel']) >= 0 then
1323             Tabs.Delete(Tabs.IndexOf(HeadValue['Channel']));
1324           if Tabs.Count > 0 then TabIndex := 0 else TabIndex := -1;
1325           tabChannelChange(self);
1326         end;
1327         ShowHintLabel(HeadValue['Channel'] + '\83`\83\83\83\93\83l\83\8b\82Í\94p\8e~\82³\82ê\82Ü\82µ\82½',
1328                       WarningColor);
1329         frmLog.AddCurrentSystemLog('SYSTEM', HeadValue['Channel'] + '\83`\83\83\83\93\83l\83\8b\82Í\94p\8e~\82³\82ê\82Ü\82µ\82½');
1330         frmMessageBox.ShowMessage(HeadValue['Channel'] + '\83`\83\83\83\93\83l\83\8b\82Í\94p\8e~\82³\82ê\82Ü\82µ\82½');
1331       end;
1332       etForceBroadcastInformation: begin
1333         if HeadValue['Type'] = 'Vote' then begin
1334           frmLog.VoteLog(HeadValue['MID'], StrToIntDef(HeadValue['Num'], 0));
1335         end else if HeadValue['Type'] = 'Agree' then begin
1336           frmLog.AgreeLog(HeadValue['MID'], StrToIntDef(HeadValue['Num'], 0));
1337         end;
1338       end;
1339     end;
1340   finally
1341     HeadValue.Free;
1342   end;
1343 end;
1344
1345 procedure TfrmSender.DirectSstpResendCountChange(Sender: TObject);
1346 begin
1347   StatusBar.Panels[PanelCount].Text := IntToStr(DirectSstp.CueCount) + '\8c\8f';
1348   TaskTray.TipString := 'SSTP Bottle Client (' +
1349                         IntToStr(DirectSstp.CueCount) + '\8c\8f)';
1350   actClearBottles.Enabled := (DirectSstp.CueCount > 0);
1351 end;
1352
1353 procedure TfrmSender.actSettingExecute(Sender: TObject);
1354 begin
1355   Application.CreateForm(TfrmSetting, frmSetting);
1356   frmSetting.Execute;
1357   frmSetting.Release;
1358   frmSetting := nil;
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   end;
1395   frmLog.AllBottleOpened;
1396   frmLog.UpdateWindow;
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   Err := DoTrans(Script, TransOpt);
1644   if Err <> '' then begin
1645     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'+
1646                   '\94z\91\97\82³\82ê\82Ü\82¹\82ñ\82Å\82µ\82½\81@\81c '+Dat['Script']);
1647     Exit;
1648   end;
1649
1650   BreakFlag := false;
1651   NoDispatch := false;
1652   Sound := '';
1653   LogNameList := TStringList.Create;
1654   SkipCount := 0;
1655   try
1656     for i := 0 to BottleChainRuleList.Count-1 do begin
1657       if SkipCount > 0 then begin
1658         Dec(SkipCount);
1659         Continue;
1660       end;
1661       Rule := BottleChainRuleList[i];
1662       if not Rule.Enabled then Continue;
1663       if not Rule.Check(Event) then Continue;
1664       for j := 0 to Rule.Actions.Count-1 do begin
1665         Action := (Rule.Actions[j] as TBottleChainAction);
1666         if Action is TBottleChainAbortRuleAction then BreakFlag := true;
1667         if Action is TBottleChainSkipRuleAction then
1668           SkipCount := (Action as TBottleChainSkipRuleAction).SkipCount;
1669         if (Action is TBottleChainSoundAction) and (Sound = '') then
1670           Sound := (Action as TBottleChainSoundAction).SoundFile;
1671         if Action is TBottleChainNoDispatchAction then NoDispatch := true;
1672         if Action is TBottleChainLogAction then begin
1673           for k := 0 to (Action as TBottleChainLogAction).LogNames.Count-1 do begin
1674             LogName := (Action as TBottleChainLogAction).LogNames[k];
1675             LogName := StringReplace(LogName, '%channel%', Dat['Channel'], [rfReplaceAll]);
1676             LogName := StringReplace(LogName, '%ghost%', Dat['TargetGhost'], [rfReplaceAll]);
1677             LogName := StringReplace(LogName, '%date%', FormatDateTime('yy/mm/dd', Now()), [rfReplaceAll]);
1678             LogNameList.Add(LogName);
1679           end;
1680         end;
1681         if Action is TBottleChainOverrideGhostAction then begin
1682           Dat['TargetGhost'] := (Action as TBottleChainOverrideGhostAction).TargetGhost;
1683         end;
1684         if Action is TBottleChainQuitAction then Application.Terminate;
1685       end;
1686       if BreakFlag then Break;
1687     end;
1688
1689     if Dat['Script'] <> '' then begin
1690       for i := 0 to LogNameList.Count-1 do
1691         frmLog.AddCurrentScriptLog(LogNameList[i], Dat['Script'], Sender, Dat['MID'], Dat['IfGhost']);
1692       if NoDispatch then begin
1693         frmLog.SetBottleStatusToOpened(Dat['MID']);
1694       end else begin
1695         Ghost := Dat['TargetGhost']; // \83I\81[\83o\81[\83\89\83C\83h\82³\82ê\82Ä\82¢\82é\89Â\94\\90«\82ª\82 \82é
1696         CueID := DirectSstp.SstpSENDCue(Script, false, Opt, GhostNameToSetName(Ghost));
1697         if Dat['IfGhost'] <> '' then Sender := Sender + '/' + Ghost; // Dat['IfGhost'];
1698         FCueGhost.AddIdGhost(CueID, Ghost, Sender, Dat['MID']);
1699       end;
1700     end;
1701
1702     if Dat['DialogMessage'] <> '' then begin
1703       Beep;
1704       frmMessageBox.ShowMessage(
1705         DateTimeToStr(Now) + #13#10 +
1706         'SSTP Bottle\83T\81[\83o\82©\82ç\82¨\92m\82ç\82¹'#13#10+Dat['DialogMessage']);
1707       for i := 0 to LogNameList.Count-1 do
1708         frmLog.AddCurrentSystemLog(LogNameList[i], Dat['DialogMessage']);
1709     end;
1710
1711     //\89¹\82Ì\8dÄ\90
1712     if (Sound <> '') then PlaySound(Sound);
1713   finally
1714     LogNameList.Free;
1715   end;
1716 end;
1717
1718 function TfrmSender.SetHWndToFavoriteGhost(const Ghost: String): String;
1719 begin
1720   //DirectSstp.TargetHWnd\82ð\81A\90\84\8f§\82·\82é\83S\81[\83X\83g\82É\90Ý\92è\82·\82é\81B
1721   //\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
1722   SakuraSeeker.BeginDetect; //\8dÅ\90V\82ÌFMO\8eæ\93¾
1723   if SakuraSeeker.ProcessByName[Ghost] <> nil then begin
1724     DirectSstp.TargetHWnd := SakuraSeeker.ProcessByName[Ghost].HWnd;
1725     Result := Ghost;
1726   end else if SakuraSeeker.Count > 0 then begin
1727     DirectSstp.TargetHWnd := SakuraSeeker[0].HWnd;
1728     Result := SakuraSeeker[0].Name;
1729   end else begin
1730     DirectSstp.TargetHwnd := 0;
1731     Result := '';
1732   end;
1733 end;
1734
1735 procedure TfrmSender.YenETrans;
1736 var St, Le, i: integer;
1737     Orig, Text: String;
1738 begin
1739   St := memScript.SelStart;
1740   Le := memScript.SelLength;
1741   Orig := GetScriptText;
1742   RegExp.Subst('s/(\r\n)+$//kg', Orig);
1743
1744   if SsParser.InputString <> Orig then begin
1745     //\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é
1746     SsParser.InputString := Orig
1747   end;
1748   for i := 0 to SsParser.Count-1 do begin
1749     if SsParser[i] <> '\e' then Text := Text + SsParser[i];
1750   end;
1751
1752   Text := Text + '\e';
1753
1754   if Orig <> Text then memScript.Lines.Text := Text;
1755   SsParser.InputString := Text;
1756
1757   RegExp.Subst('s/\r\n//kg', Text);
1758   memScript.SetFocus;
1759   memScript.SelStart := St;
1760   memScript.SelLength := Le;
1761 end;
1762
1763 procedure TfrmSender.PostCommand(const Command: array of String);
1764 var PostStr: TStringList;
1765     i: integer;
1766 begin
1767   PostStr := nil;
1768   try
1769     PostStr := TStringList.Create;
1770     for i := Low(Command) to High(Command) do begin
1771       PostStr.Add(Command[i]);
1772     end;
1773     PostCommand(PostStr);
1774   finally
1775     PostStr.Free;
1776   end;
1777 end;
1778
1779 procedure TfrmSender.PostCommand(Command: TStrings);
1780 var PostStr: String;
1781 begin
1782   Connecting := true;
1783   PostStr := Command.Text;
1784   PostStr := TIdURI.ParamsEncode(PostStr);
1785   try
1786     FHttp := THTTPDownloadThread.Create(BottleServer, Pref.CgiName, PostStr);
1787     if Pref.UseHttpProxy then begin
1788       FHttp.ProxyServer := Pref.ProxyAddress;
1789       FHttp.ProxyPort   := Pref.ProxyPort;
1790     end;
1791     FHttp.OnSuccess := HttpSuccess;
1792     FHttp.OnConnectionFailed := HttpFailure;
1793     FHttp.FreeOnTerminate := true; // \8f\9f\8eè\82É\8e©\95ª\82ÅFree\82µ\82Ä\82­\82¾\82³\82¢
1794     FHTTP.Resume;
1795   except
1796     on EHeapException do begin
1797       Connecting := false;
1798       FHttp.Free;
1799     end;
1800   end;
1801 end;
1802
1803 procedure TfrmSender.tabChannelDrawTab(Control: TCustomTabControl;
1804   TabIndex: Integer; const Rect: TRect; Active: Boolean);
1805 var X, Y: integer;
1806 begin
1807   with tabChannel.Canvas do begin
1808     FillRect(Rect);
1809     if Active then begin
1810       Font.Color := clBlue;
1811     end else begin
1812       Font.Style := Font.Style - [fsBold];
1813       Font.Color := clWindowText;
1814     end;
1815     X := (Rect.Left + Rect.Right) div 2;
1816     X := X - TextWidth(tabChannel.Tabs[TabIndex]) div 2;
1817     if tabChannel.TabPosition = tpTop then
1818       Y := Rect.Top + 4
1819     else
1820       Y := Rect.Bottom - 15;
1821     TextOut(X, Y, tabChannel.Tabs[TabIndex]);
1822   end;
1823 end;
1824
1825 procedure TfrmSender.actVoteMessageExecute(Sender: TObject);
1826 var Log: TLogItem;
1827 begin
1828   if frmLog.lvwLog.Selected = nil then Exit;
1829   Log := frmLog.SelectedBottleLog[frmLog.lvwLog.Selected.Index] as TLogItem;
1830   if Log = nil then Exit;
1831   if Log.LogType <> ltBottle then Exit;
1832   PostCommand([
1833     'Command: voteMessage',
1834     'VoteType: Vote',
1835     'LUID: ' + Pref.LUID,
1836     'MID: ' + Log.MID
1837   ]);
1838 end;
1839
1840
1841 procedure TfrmSender.actAgreeMessageExecute(Sender: TObject);
1842 var Log: TLogItem;
1843 begin
1844   if frmLog.lvwLog.Selected = nil then Exit;
1845   Log := frmLog.SelectedBottleLog[frmLog.lvwLog.Selected.Index] as TLogItem;
1846   if Log = nil then Exit;
1847   if Log.LogType <> ltBottle then Exit;
1848   PostCommand([
1849     'Command: voteMessage',
1850     'VoteType: Agree',
1851     'LUID: ' + Pref.LUID,
1852     'MID: ' + Log.MID
1853   ]);
1854 end;
1855
1856
1857 function TfrmSender.GhostNameToSetName(const Ghost: String): String;
1858 begin
1859   if SakuraSeeker.ProcessByName[Ghost] <> nil then
1860     Result := SakuraSeeker.ProcessByName[Ghost].SetName
1861   else
1862     Result := '';
1863 end;
1864
1865 procedure TfrmSender.tabChannelContextPopup(Sender: TObject;
1866   MousePos: TPoint; var Handled: Boolean);
1867 var Ch: String;
1868 begin
1869   with tabChannel do begin
1870     Tag := IndexOfTabAt(MousePos.X, MousePos.Y);
1871     if Tag < 0 then Handled := true;
1872     Ch := Tabs[Tag];
1873   end;
1874 end;
1875
1876 procedure TfrmSender.PostSetChannel(Channels: TStrings);
1877 var PostStr: TStringList;
1878     i: integer;
1879 begin
1880   PostStr := nil;
1881   try
1882     PostStr := TStringList.Create;
1883     with PostStr do begin
1884       Add('Command: setChannels');
1885       Add('Agent: ' + VersionString);
1886       Add('LUID: ' + Pref.LUID);
1887       if Channels <> nil then
1888         for i := 0 to Channels.Count-1 do begin
1889           Add(Format('Ch%d: %s'#13#10, [i+1, Channels[i]]));
1890         end;
1891     end;
1892     PostCommand(PostStr);
1893   finally
1894     PostStr.Free;
1895   end;
1896 end;
1897
1898 procedure TfrmSender.mnLeaveThisChannelClick(Sender: TObject);
1899 var Ch: String;
1900     Chs: TStringList;
1901 begin
1902   with tabChannel do Ch := Tabs[Tag];
1903   Chs := nil;
1904   try
1905     Chs := TStringList.Create;
1906     Chs.Assign(JoinChannels);
1907     while Chs.IndexOf(Ch) >= 0 do Chs.Delete(Chs.IndexOf(Ch));
1908     PostSetChannel(Chs);
1909   finally
1910     Chs.Free;
1911   end;
1912 end;
1913
1914 procedure TfrmSender.mnGotoVoteClick(Sender: TObject);
1915 begin
1916   ShellExecute(Handle, 'open', PChar(Pref.VotePage), nil, nil, SW_SHOW);
1917 end;
1918
1919 procedure TfrmSender.mnGotoGLogClick(Sender: TObject);
1920 begin
1921   ShellExecute(Handle, 'open', PChar(Pref.GLogPage), nil, nil, SW_SHOW);
1922 end;
1923
1924 procedure TfrmSender.tabChannelMouseMove(Sender: TObject;
1925   Shift: TShiftState; X, Y: Integer);
1926 var Index: integer;
1927     Ch: String;
1928 begin
1929   with tabChannel do begin
1930     Index := IndexOfTabAt(X, Y);
1931     Ch := Tabs[Index];
1932     Hint := Ch + ': ' + IntToStr(ChannelList.Channel[Ch].Members) + '\90l';
1933   end;
1934 end;
1935
1936 procedure TfrmSender.mnGoToHelpClick(Sender: TObject);
1937 begin
1938   ShellExecute(Handle, 'open', PChar(Pref.HelpPage), nil, nil, SW_SHOW);
1939 end;
1940
1941 procedure TfrmSender.tabChannelMouseDown(Sender: TObject;
1942   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
1943 var Index: integer;
1944 begin
1945   with tabChannel do begin
1946     Index := IndexOfTabAt(X, Y);
1947     if Index = -1 then Exit; //\83^\83u\82ª\82È\82¢\82Ì\82Å\83h\83\89\83b\83O\82Å\82«\82È\82¢
1948     if Button = mbLeft then begin
1949       FDragTabIndex := Index; //\83h\83\89\83b\83O\82·\82é\83^\83u\82Ì\83C\83\93\83f\83b\83N\83X\82ð\95Û\91
1950       BeginDrag(False);
1951       FDragTabDest := -1;     //\83h\83\89\83b\83O\98g\90ü\95`\89æ\83t\83\89\83O\83N\83\8a\83A\82Ì\82½\82ß
1952     end;
1953   end;
1954 end;
1955
1956 procedure TfrmSender.tabChannelDragOver(Sender, Source: TObject; X,
1957   Y: Integer; State: TDragState; var Accept: Boolean);
1958 var TargetRect: TRect;
1959     OldDest: integer;
1960 begin
1961   Accept := Source = tabChannel;
1962   if not Accept then Exit;
1963   with tabChannel do begin
1964     OldDest := FDragTabDest;
1965     FDragTabDest := IndexOfTabAt(X, Y);
1966     if FDragTabDest = -1 then begin
1967       Accept := false; //\82±\82Ì\8fê\8d\87\82Í\83h\83\8d\83b\83v\82ð\94F\82ß\82È\82¢
1968       Exit;
1969     end;
1970     with Canvas do begin
1971       Pen.Mode := pmNot;
1972       Pen.Width := 3;
1973     end;
1974     if (OldDest <> FDragTabDest) and (OldDest >= 0) then begin
1975       //\88È\91O\82Ì\98g\90ü\8fÁ\8b\8e
1976       TargetRect := TabRect(OldDest);
1977       with Canvas do begin
1978         Brush.Style := bsClear;
1979         Rectangle(TargetRect.Left, TargetRect.Top,
1980                   TargetRect.Right, TargetRect.Bottom);
1981       end;
1982     end;
1983     if (OldDest <> FDragTabDest) then begin
1984       //\90V\82µ\82¢\98g\90ü\95`\89æ
1985       TargetRect := TabRect(FDragTabDest);
1986       with Canvas do begin
1987         Brush.Style := bsClear;
1988         Rectangle(TargetRect.Left, TargetRect.Top,
1989                   TargetRect.Right, TargetRect.Bottom);
1990       end;
1991     end;
1992   end;
1993 end;
1994
1995 procedure TfrmSender.tabChannelDragDrop(Sender, Source: TObject; X,
1996   Y: Integer);
1997 var DestIndex: integer;
1998 begin
1999   with tabChannel do begin
2000     DestIndex := IndexOfTabAt(X, Y);
2001     Tabs.Move(FDragTabIndex, DestIndex);
2002   end;
2003 end;
2004
2005 procedure TfrmSender.tabChannelEndDrag(Sender, Target: TObject; X,
2006   Y: Integer);
2007 begin
2008   //\8b­\90§\93I\82É\83^\83u\82ð\8dÄ\95`\89æ\82³\82¹\82é\81B\98g\90ü\8fÁ\82µ\91Î\8dô
2009   tabChannel.Tabs.BeginUpdate;
2010   tabChannel.Tabs.EndUpdate;
2011 end;
2012
2013 procedure TfrmSender.cbxTargetGhostDrawItem(Control: TWinControl;
2014   Index: Integer; Rect: TRect; State: TOwnerDrawState);
2015 begin
2016   with cbxTargetGhost do begin
2017     if Index > 0 then begin
2018       if SakuraSeeker.ProcessByName[Items[Index]] = nil then
2019         Canvas.Font.Color := clRed
2020       else
2021         Canvas.Font.Color := clBlue;
2022       Canvas.Font.Style := [fsBold];
2023     end else begin
2024       Canvas.Font.Color := clWindowText;
2025       Canvas.Font.Style := [];
2026     end;
2027     if odSelected in State then
2028       Canvas.Font.Color := clHighlightText;
2029     cbxTargetGhost.Canvas.TextRect(Rect, Rect.Left, Rect.Top,
2030       cbxTargetGhost.Items[Index]);
2031   end;
2032 end;
2033
2034 procedure TfrmSender.FormCloseQuery(Sender: TObject;
2035   var CanClose: Boolean);
2036 begin
2037   if not Pref.ConfirmOnExit then Exit;
2038   if MessageDlg('SSTP Bottle Client\82ð\8fI\97¹\82µ\82Ü\82·', mtConfirmation,
2039                 mbOkCancel, 0) = mrCancel then CanClose := false;
2040 end;
2041
2042 procedure TfrmSender.UpdateIfGhostBox;
2043 var
2044   Selected: String;
2045   i: integer;
2046 begin
2047   cbxTargetGhost.DropDownCount := Pref.GhostDropDownCount;
2048   Selected := cbxTargetGhost.Text;
2049   with cbxTargetGhost do begin
2050     Items.BeginUpdate;
2051     Items.Clear;
2052     Items.Add('(CH\90\84\8f§)');
2053     for i := 0 to SakuraSeeker.Count-1 do begin
2054       // \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é
2055       if Length(SakuraSeeker[i].Name) = 0 then Continue;
2056       if cbxTargetGhost.Items.IndexOf(SakuraSeeker[i].Name) < 0 then
2057         cbxTargetGhost.Items.Add(SakuraSeeker[i].Name);
2058     end;
2059     Items.EndUpdate;
2060     cbxTargetGhost.ItemIndex := 0;
2061     if (Length(Selected) > 0) and (Selected <> '(CH\90\84\8f§)') then begin
2062       with cbxTargetGhost do begin
2063         for i := 1 to Items.Count-1 do begin
2064           if Items[i] = Selected then
2065             ItemIndex := i;
2066         end;
2067         //\83S\81[\83X\83g\82ª\93Ë\91R\91\8dÝ\82µ\82È\82­\82È\82Á\82½\8fê\8d\87\91Î\8dô
2068         if ItemIndex = 0 then begin
2069           Items.Add(Selected);
2070           ItemIndex := Items.Count - 1;
2071         end;
2072       end;
2073     end;
2074   end;
2075
2076 end;
2077
2078 procedure TfrmSender.HTTPFailure(Sender: TObject);
2079 begin
2080   SysUtils.Beep;
2081   Beep;
2082   ShowHintLabel('SSTP Bottle\83T\81[\83o\82Æ\82Ì\90Ú\91±\82É\8e¸\94s\82µ\82Ü\82µ\82½', WarningColor);
2083   ShowMessage((Sender as THTTPDownloadThread).LastErrorMessage);
2084   Connecting := false;
2085 end;
2086
2087 procedure TfrmSender.actPrevGhostExecute(Sender: TObject);
2088 var i: integer;
2089 begin
2090   SakuraSeeker.BeginDetect;
2091   UpdateIfGhostBox;
2092   i := cbxTargetGhost.ItemIndex;
2093   Dec(i);
2094   if i <= -1 then i := cbxTargetGhost.Items.Count-1;
2095   cbxTargetGhost.ItemIndex := i;
2096   cbxTargetGhostChange(self);
2097 end;
2098
2099 procedure TfrmSender.actNextGhostExecute(Sender: TObject);
2100 var i: integer;
2101 begin
2102   SakuraSeeker.BeginDetect;
2103   UpdateIfGhostBox;
2104   i := cbxTargetGhost.ItemIndex;
2105   Inc(i);
2106   if i > cbxTargetGhost.Items.Count-1 then i := 0;
2107   cbxTargetGhost.ItemIndex := i;
2108   cbxTargetGhostChange(self);
2109 end;
2110
2111 procedure TfrmSender.actResetGhostExecute(Sender: TObject);
2112 begin
2113   cbxTargetGhost.ItemIndex := 0; // (CH\90\84\8f§)\82É\96ß\82·
2114   if Visible then memScript.SetFocus;
2115   cbxTargetGhostChange(self);
2116 end;
2117
2118 procedure TfrmSender.timDisconnectCheckTimerTimer(Sender: TObject);
2119 begin
2120   if (IdSlpp20.LastReadTimeInterval > BottleServerTimeOut) then begin
2121     SysUtils.Beep;
2122     frmLog.AddCurrentSystemLog('SYSTEM', 'SSTP Bottle\83T\81[\83o\82Æ\82Ì\90Ú\91±\82ª\83^\83C\83\80\83A\83E\83g\82µ\82Ü\82µ\82½');
2123     if IdSlpp20.Connected then IdSlpp20.Disconnect;
2124   end;
2125   if not IdSlpp20.Connected then begin
2126     if Added then begin
2127       Slpp20Disconnect(self); //\82È\82º\82©Disconnect\83C\83x\83\93\83g\82ª\8bN\82±\82ç\82¸\82É\90Ø\92f\82µ\82½\8fê\8d\87
2128     end else begin
2129       //\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
2130       //\82½\82¾\82µ\89ñ\90\94\90§\8cÀ\82 \82è
2131       RetryBeginConnect;
2132     end;
2133   end;
2134 end;
2135
2136 procedure TfrmSender.RetryBeginConnect;
2137 begin
2138   if FBeginConnectFailCount < 3 then begin
2139     // \90Ø\92f\82³\82ê\82Ä\82¢\82ê\82Î\8dÄ\90Ú\91±
2140     FAutoAddAfterGetChannel := true;
2141     BeginConnect;
2142   end else if FBeginConnectFailCount = 3 then begin
2143     frmLog.AddCurrentSystemLog('SYSTEM', '\8dÄ\90Ú\91±\8e©\93®\83\8a\83g\83\89\83C\82ð\92\86\8e~\82µ\82Ü\82·');
2144     frmMessageBox.ShowMessage(
2145       'SSTP Bottle\83T\81[\83o\82É\90Ú\91±\82Å\82«\82Ü\82¹\82ñ\81B'#13#10+
2146       '\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+
2147       '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'
2148     );
2149     Inc(FBeginConnectFailCount);
2150   end;
2151 end;
2152
2153 procedure TfrmSender.actDownloadLogExecute(Sender: TObject);
2154 var BottleLog: TBottleLogList;
2155     Title: String;
2156     Cond: TBottleLogDownloadCondition;
2157     NewIndex: integer;
2158   function TimeStr(Mins: integer): String;
2159   var day, hour, min: integer;
2160   begin
2161     day := Mins div (60 * 24);
2162     hour := (Mins div 60) mod 24;
2163     min := Mins mod 60;
2164     Result := '';
2165     if day  > 0 then Result := Result + Format('%d\93ú', [day]);
2166     if hour > 0 then Result := Result + Format('%d\8e\9e\8aÔ', [hour]);
2167     if (min  > 0) or (Result = '') then Result := Result + Format('%d\95ª', [min]);
2168   end;
2169 begin
2170   Application.CreateForm(TfrmLogDownload, frmLogDownload);
2171   try
2172     if frmLogDownload.Execute then begin
2173       with frmLogDownload do begin
2174         if IsRange then begin
2175           if CompareDate(DateLo, DateHi) = 0 then
2176             Title := FormatDateTime('yy/mm/dd', DateLo)
2177           else
2178             Title := FormatDateTime('yy/mm/dd', DateLo) + ' - ' + FormatdateTime('yy/mm/dd', DateHi);
2179         end else begin
2180           Title := Format('\89ß\8b\8e%s', [TimeStr(RecentCount)]);
2181         end;
2182         if Channel <> '' then Title := Title + '(' + Channel + ')';
2183       end;
2184       try
2185         BottleLog := TBottleLogList.Create(Title);
2186         BottleLog.OnLoaded := frmLog.LogLoaded;
2187         BottleLog.OnLoadFailure := frmLog.LogLoadFailure;
2188         BottleLog.OnLoadWork := frmLog.LogLoadWork;
2189         with frmLogDownload do begin
2190           Cond.IsRange := IsRange;
2191           Cond.RecentCount := RecentCount;
2192           Cond.DateLo := DateLo;
2193           Cond.DateHi := DateHi;
2194           Cond.MinVote := MinVote;
2195           Cond.MinAgree := MinAgree;
2196           Cond.Channel := Channel;
2197         end;
2198         BottleLog.LoadFromWeb(Cond);
2199       except
2200         FreeAndNil(BottleLog);
2201       end;
2202       if BottleLog <> nil then begin
2203         NewIndex := frmLog.BottleLogList.Add(BottleLog);
2204         frmLog.UpdateTab;
2205         frmLog.tabBottleLog.TabIndex := NewIndex;
2206         frmLog.UpdateWindow;
2207       end;
2208     end;
2209   finally
2210     frmLogDownload.Release;
2211   end;
2212 end;
2213
2214 function TfrmSender.BuildMenuConditionCheck(const IfGhost,
2215   Ghost: String): boolean;
2216 var i: integer;
2217     Cond: String;
2218 begin
2219   i := 0;
2220   Result := true;
2221   repeat
2222     Cond := Token(IfGhost, ',', i);
2223     if Cond <> '' then begin
2224       if Cond[1] = '!' then begin
2225         Cond := Copy(Cond, 2, High(integer));
2226         if Cond = Ghost then Result := false;
2227       end else begin
2228         if Cond <> Ghost then Result := false;
2229       end;
2230     end;
2231     Inc(i);
2232   until Cond = '';
2233 end;
2234
2235 procedure TfrmSender.BuildMenu(Root: TMenuItem; Event: TNotifyEvent; Simple: boolean);
2236 var i, j, k, count: integer;
2237     ConstData: TScriptConst;
2238     Menu1, Menu2: TMenuItem;
2239     Ghost: String;
2240 begin
2241   // Simple = false \82Ì\8fê\8d\87\82Í\83\81\83j\83\85\81[\82ð\8a®\91S\82É\8dÄ\8d\\92z\82·\82é\81B
2242   // 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
2243   if cbxTargetGhost.ItemIndex > 0 then Ghost := cbxTargetGhost.Text
2244   else if FNowChannel <> '' then Ghost := ChannelList.Channel[FNowChannel].Ghost;
2245
2246   // \8aù\91\82Ì\83\81\83j\83\85\81[\8dí\8f\9c
2247   if Simple then begin
2248     // IfGhost\8fð\8c\8f\95t\82«\83\81\83j\83\85\81[\82Ì\82Ý\8dí\8f\9c
2249     for i := Root.Count-1 downto 0 do begin
2250       if ScriptConstList.GetMenuByID(Root.Items[i].Tag).IfGhost <> '' then
2251         Root.Items[i].Free;
2252     end;
2253   end else begin
2254     // \91S\95\94\8dí\8f\9c
2255     for i := Root.Count-1 downto 0 do begin
2256       Root.Items[i].Free;
2257     end;
2258   end;
2259
2260   count := -1;
2261   for i := 0 to ScriptConstList.Count-1 do begin
2262     for j := 0 to ScriptConstList[i].Count-1 do begin
2263       // \83S\81[\83X\83g\88á\82¢\82Ì\8fê\8d\87\82Í\83X\83L\83b\83v
2264       if not BuildMenuConditionCheck(ScriptConstList[i][j].IfGhost, Ghost) then Continue;
2265       Inc(count);
2266       // 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
2267       if Simple and (count < Root.Count) then
2268         if (Root.Items[count].Tag = ScriptConstList[i][j].ID) then begin
2269           Continue;
2270         end;
2271
2272       Menu1 := TMenuItem.Create(Root);
2273       Menu1.Caption := ScriptConstList[i][j].Caption;
2274       Menu1.Hint    := ScriptConstList[i][j].Caption;
2275       Menu1.AutoHotkeys := maManual;
2276       Menu1.Tag := ScriptConstList[i][j].ID;
2277
2278       if not Simple then begin
2279         Root.Add(Menu1);
2280       end else begin
2281         if count < Root.Count-1 then
2282           Root.Insert(count, Menu1)
2283         else
2284           Root.Add(Menu1);
2285       end;
2286
2287       Menu1.Enabled := ScriptConstList[i][j].Count > 0;
2288       for k := 0 to ScriptConstList[i][j].Count-1 do begin
2289         ConstData := ScriptConstList[i][j][k];
2290         Menu2 := TMenuItem.Create(Root);
2291         Menu2.Caption := ConstData.Caption;
2292         Menu2.Hint := ConstData.ConstText;
2293         if ConstData.ShortCut <> 0 then Menu2.Hint := Menu2.Hint
2294           + ' (' + ShortCutToText(ConstData.ShortCut) + ')';
2295         Menu2.ShortCut := ConstData.ShortCut;
2296         Menu2.OnClick := Event;
2297         Menu2.AutoHotkeys := maManual;
2298         Menu2.Tag := ConstData.ID;
2299         if (k mod 15 = 0) and (k > 0) then Menu2.Break := mbBarBreak;
2300         Menu1.Add(Menu2);
2301       end;
2302     end;
2303   end;
2304 end;
2305
2306 procedure TfrmSender.cbxTargetGhostChange(Sender: TObject);
2307 begin
2308   ConstructMenu(true);
2309 end;
2310
2311 procedure TfrmSender.PlaySound(const FileName: String);
2312 begin
2313   if Pref.SilentWhenHidden and not Application.ShowMainForm then Exit;
2314   try
2315     MediaPlayer.FileName := FileName;
2316     MediaPlayer.Open;
2317     MediaPlayer.Play;
2318   except
2319     on E: EMCIDeviceError do begin
2320       ShowMessage('\83T\83E\83\93\83h\8dÄ\90\83G\83\89\81[:'#13#10 + E.Message);
2321     end;
2322   end;
2323 end;
2324
2325 end.