OSDN Git Service

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