OSDN Git Service

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