OSDN Git Service

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