OSDN Git Service

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