OSDN Git Service

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