OSDN Git Service

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