OSDN Git Service

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