OSDN Git Service

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