OSDN Git Service

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