OSDN Git Service

正規表現まわりの再修正
[winbottle/winbottle.git] / bottleclient / MainForm.pas
1 unit MainForm;
2
3 {
4 \83A\83v\83\8a\83P\81[\83V\83\87\83\93\82Ì\83\81\83C\83\93\83t\83H\81[\83\80\81B
5 \91\97\90M\81E\8eó\90M\81E\83{\83g\83\8b\94z\91\97\8aÖ\8cW\82Ì\82¢\82ë\82¢\82ë\82È\8f\88\97\9d\82ð\8ds\82¤\81B
6 }
7
8 interface
9
10 uses
11   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
12   Menus, StdCtrls, ComCtrls, BRegExp, BottleDef, BottleSstp,
13   DirectSstp, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
14   IdSLPP20, SsParser, ImgList, AppEvnts, TaskTray, StdActns,
15   ActnList, MPlayer, MenuBar, ToolWin,
16   IniFiles, ExtCtrls, ShellAPI, Contnrs,
17   ConstEditor, Buttons, Clipbrd, HeadValue, Logs, MultipleChoiceEditor,
18   IdException, HttpThread, IdHTTP, LogDownload,
19   ScriptConsts, DateUtils, BottleChainRule, BottleChainEvent,
20   SakuraSeekerInstance, HEditor, HTSearch, heClasses, heFountain,
21   SakuraScriptFountain, SppList, SurfacePreview, XDOM_2_3_J3, SsPlayTime;
22
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     mnGoToHelp: TMenuItem;
126     btnSend: TButton;
127     btnConfirm: TButton;
128     btnClear: TButton;
129     mnExitAllChannels: TMenuItem;
130     actAgreeMessage: TAction;
131     IdSLPP20: TIdSLPP20;
132     btnIfGhost: TButton;
133     actPrevGhost: TAction;
134     actNextGhost: TAction;
135     mnPrevGhost: TMenuItem;
136     mnNextGhost: TMenuItem;
137     actResetGhost: TAction;
138     mnResetGhost: TMenuItem;
139     timDisconnectCheckTimer: TTimer;
140     DirectSstp: TDirectSstp;
141     actDownloadLog: TAction;
142     actFMOExplorer: TAction;
143     tbtnFMOExplorer: TToolButton;
144     mnFMOExplorer: TMenuItem;
145     mnLine: TMenuItem;
146     actInsertCue: TAction;
147     SakuraScriptFountain: TSakuraScriptFountain;
148     memScript: TEditor;
149     actCopy: TAction;
150     actPaste: TAction;
151     actCut: TAction;
152     actSelectAll: TAction;
153     actRecallScriptBuffer: TAction;
154     N5: TMenuItem;
155     mnRecallScriptBuffer: TMenuItem;
156     tbtnEditorPreview: TToolButton;
157     actEditorPreview: TAction;
158     mnEditorPreview: TMenuItem;
159     actResetPlugins: TAction;
160     N7: TMenuItem;
161     mnResetPlugins: TMenuItem;
162     ReplaceDialog: TReplaceDialog;
163     actReplace: TAction;
164     N10: TMenuItem;
165     mnReplace: TMenuItem;
166     actSendToEditor: TAction;
167     actSendToLogWindow: TAction;
168     mnSendLogWindow: TMenuItem;
169     actDeleteLogItem: TAction;
170     actAbout: TAction;
171     actEditCopy: TEditCopy;
172     tbtnSendToLogWindow: TToolButton;
173     SsPlayTime: TSsPlayTime;
174     actUndo: TAction;
175     actRedo: TAction;
176     mnUndo: TMenuItem;
177     mnRedo: TMenuItem;
178     N9: TMenuItem;
179     procedure actConfirmExecute(Sender: TObject);
180     procedure FormCreate(Sender: TObject);
181     procedure FormDestroy(Sender: TObject);
182     procedure actSendExecute(Sender: TObject);
183     procedure HTTPSuccess(Sender: TObject);
184     procedure HTTPFailure(Sender: TObject);
185     procedure actStartClick(Sender: TObject);
186     procedure actStopExecute(Sender: TObject);
187     procedure FormShow(Sender: TObject);
188     procedure actAboutClick(Sender: TObject);
189     procedure actExitClientExecute(Sender: TObject);
190     procedure actClearExecute(Sender: TObject);
191     procedure memScriptChange(Sender: TObject);
192     procedure mnStayOnTopClick(Sender: TObject);
193     procedure actEditConstExecute(Sender: TObject);
194     procedure mnTaskBarClick(Sender: TObject);
195     procedure FormClose(Sender: TObject; var Action: TCloseAction);
196     procedure ApplicationEventsMinimize(Sender: TObject);
197     procedure ApplicationEventsRestore(Sender: TObject);
198     procedure mnTaskRestoreClick(Sender: TObject);
199     procedure TaskTrayDblClick(Seft: TObject; Button: TMouseButton);
200     procedure FormActivate(Sender: TObject);
201     procedure mnTaskNewMessageClick(Sender: TObject);
202     procedure ApplicationEventsHint(Sender: TObject);
203     procedure memScriptKeyDown(Sender: TObject; var Key: Word;
204       Shift: TShiftState);
205     procedure mnShowToolBarClick(Sender: TObject);
206     procedure mnShowConstBarClick(Sender: TObject);
207     procedure mnGoToHPClick(Sender: TObject);
208     procedure LabelTimerTimer(Sender: TObject);
209     procedure actCopyAllExecute(Sender: TObject);
210     procedure actCopyAllNoReturnExecute(Sender: TObject);
211     procedure Slpp20SlppEvent(Sender: TObject; EventType: TIdSlppEventType;
212       const Param: String);
213     procedure actSettingExecute(Sender: TObject);
214     procedure memScriptKeyPress(Sender: TObject; var Key: Char);
215     procedure Slpp20Disconnect(Sender: TObject);
216     procedure actClearBottlesExecute(Sender: TObject);
217     procedure SakuraSeekerDetectResultChanged(Sender: TObject);
218     procedure mnGetNewIdClick(Sender: TObject);
219     procedure tabChannelChange(Sender: TObject);
220     procedure actPrevChannelExecute(Sender: TObject);
221     procedure actNextChannelExecute(Sender: TObject);
222     procedure cbxTargetGhostDropDown(Sender: TObject);
223     procedure actShowLogExecute(Sender: TObject);
224     procedure actSleepExecute(Sender: TObject);
225     procedure actVoteMessageExecute(Sender: TObject);
226     procedure tabChannelContextPopup(Sender: TObject; MousePos: TPoint;
227       var Handled: Boolean);
228     procedure mnLeaveThisChannelClick(Sender: TObject);
229     procedure mnGotoVoteClick(Sender: TObject);
230     procedure tabChannelMouseMove(Sender: TObject; Shift: TShiftState; X,
231       Y: Integer);
232     procedure mnGoToHelpClick(Sender: TObject);
233     procedure tabChannelMouseDown(Sender: TObject; Button: TMouseButton;
234       Shift: TShiftState; X, Y: Integer);
235     procedure tabChannelDragOver(Sender, Source: TObject; X, Y: Integer;
236       State: TDragState; var Accept: Boolean);
237     procedure tabChannelDragDrop(Sender, Source: TObject; X, Y: Integer);
238     procedure tabChannelEndDrag(Sender, Target: TObject; X, Y: Integer);
239     procedure cbxTargetGhostDrawItem(Control: TWinControl; Index: Integer;
240       Rect: TRect; State: TOwnerDrawState);
241     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
242     procedure actAgreeMessageExecute(Sender: TObject);
243     procedure actPrevGhostExecute(Sender: TObject);
244     procedure actNextGhostExecute(Sender: TObject);
245     procedure actResetGhostExecute(Sender: TObject);
246     procedure timDisconnectCheckTimerTimer(Sender: TObject);
247     procedure actDownloadLogExecute(Sender: TObject);
248     procedure cbxTargetGhostChange(Sender: TObject);
249     procedure actFMOExplorerExecute(Sender: TObject);
250     procedure actInsertCueExecute(Sender: TObject);
251     procedure FormResize(Sender: TObject);
252     procedure actCopyExecute(Sender: TObject);
253     procedure actPasteExecute(Sender: TObject);
254     procedure actCutExecute(Sender: TObject);
255     procedure actSelectAllExecute(Sender: TObject);
256     procedure memScriptMouseMove(Sender: TObject; Shift: TShiftState; X,
257       Y: Integer);
258     procedure actRecallScriptBufferExecute(Sender: TObject);
259     procedure actEditorPreviewExecute(Sender: TObject);
260     procedure actResetPluginsExecute(Sender: TObject);
261     procedure IdSLPP20Connect(Sender: TObject);
262     procedure actReplaceExecute(Sender: TObject);
263     procedure ReplaceDialogFind(Sender: TObject);
264     procedure ReplaceDialogReplace(Sender: TObject);
265     procedure actSendToEditorExecute(Sender: TObject);
266     procedure actSendToLogWindowExecute(Sender: TObject);
267     procedure memScriptDragOver(Sender, Source: TObject; X, Y: Integer;
268       State: TDragState; var Accept: Boolean);
269     procedure memScriptDragDrop(Sender, Source: TObject; X, Y: Integer);
270     procedure actDeleteLogItemExecute(Sender: TObject);
271     procedure memScriptSelectionChange(Sender: TObject; Selected: Boolean);
272     procedure actUndoExecute(Sender: TObject);
273     procedure actRedoExecute(Sender: TObject);
274   private
275     FSleeping: boolean;  // \94z\91\97\83X\83\8a\81[\83v\92\86\82©\82Ç\82¤\82©
276     FStatusText: String;
277     FConnecting: boolean;
278     FAdded: boolean;
279     FBooted: boolean; //\8f\89\89ñ\8bN\93®\92Ê\90M\97p
280     FOriginalCaption: String;
281     FAutoAddAfterGetChannel: boolean; //\83`\83\83\83\93\83l\83\8b\8eæ\93¾\8cã\82É\83_\83C\83A\83\8d\83O\82È\82µ\82É
282                                       //\83`\83\83\83\93\83l\83\8b\82É\8eQ\89Á\82·\82é\82©\82Ç\82¤\82©
283     FConstDir: String;
284     FSppDir: String;
285     //
286     FMutex: THandle; //Mutex\83I\83u\83W\83F\83N\83g\81c\93ñ\8fd\8bN\93®\96h\8e~\97p
287     //
288     FNowChannel: String; //\8c»\8dÝ\91I\91ð\82³\82ê\82Ä\82¢\82é\83`\83\83\83\93\83l\83\8b
289     JoinChannelsBackup: TStringList; //\88ê\8e\9e\8eg\97p
290     //
291     FScriptModified: boolean; // \83X\83N\83\8a\83v\83g\82ª\95Ï\8dX\82³\82ê\82Ä\82¢\82é\82©\82Ç\82¤\82©\81B
292                               // \83\8d\81[\83J\83\8b\8am\94F\8b­\90§\97p\83t\83\89\83O\81BTRichEdit.Modified\82Í
293                               //\95Ê\82Ì\97p\93r\82Å\8eg\82Á\82Ä\82¢\82é\82Ì\82Å\8eg\82¦\82È\82¢
294     //
295     FDragTabIndex: integer; //\83^\83u\83h\83\89\83b\83O\83h\83\8d\83b\83v\8aÖ\98A
296     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)
297     //
298     FBottleSstp: TBottleSstp; // \8dÄ\91\97\83v\83\8d\83O\83\89\83\80
299     //
300     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Ý)
301     FBeginConnectFailCount: integer; //\89½\93x\82à\90Ú\91±\8e¸\94s\82µ\82½\82ç\83\8a\83g\83\89\83C\92\86\8e~
302     //
303     FVisiblePreviewGhost: String;
304     FVisiblePreviewSurface: integer; //\83T\81[\83t\83B\83X\83v\83\8c\83r\83\85\81[\82Å\8c©\82¦\82Ä\82¢\82é\82à\82Ì
305     FVisibleMenuItem: TMenuItem; //\83N\83\8a\83b\83N\82³\82ê\82½\83\81\83j\83\85\81[\83A\83C\83e\83\80\81B
306                                  //\83T\81[\83t\83B\83X\83v\83\8c\83r\83\85\81[\82ª\83\81\83j\83\85\81[\82Ì\89º\82É
307                                  //\89B\82ê\82È\82¢\82æ\82¤\82É\95\\8e¦\82³\82ê\82Ä\82¢\82é\82à\82Ì\82ð\8bL\89¯\82µ\82Ä\82¨\82­
308     //
309     FTargetGhostExpand: boolean; //\83S\81[\83X\83g\91I\91ð\83{\83b\83N\83X\82ª\81A
310                                  //\88ê\8e\9e\93I\82É\91S\95\94\95\\8e¦\82³\82ê\82Ä\82¢\82é\82©\82Ç\82¤\82©\82Ì\83t\83\89\83O
311     //
312     FScriptBuffer: TObjectList;  //\83X\83N\83\8a\83v\83g\83N\83\8a\83A\83o\83b\83t\83@
313     //
314     FWM_TaskBarCreated: WORD; // \83^\83X\83N\83o\81[\93o\98^\97p\83E\83B\83\93\83h\83E\83\81\83b\83Z\81[\83WID
315     //
316     procedure SetStatusText(const Value: String);
317     procedure SetSleeping(const Value: boolean);
318     procedure YenETrans;
319     procedure SetConnecting(const Value: boolean);
320     procedure SetAdded(const Value: boolean);
321     procedure mnConstClick(Sender: TObject);
322     procedure mnConstGroupClick(Sender: TObject);
323     property Added: boolean read FAdded write SetAdded;
324     property Sleeping: boolean read FSleeping write SetSleeping;
325     property StatusText: String read FStatusText write SetStatusText;
326     function GetScriptText: String;
327     procedure ChangeTaskIcon;
328     procedure ShowHintLabel(const Mes: String; Col: TColor = clBlue);
329     procedure UpdateLayout;
330     procedure DispatchBottle(EventType: TIdSlppEventType; Dat: THeadValue);
331     //\83`\83\83\83\93\83l\83\8b\8aÖ\8cW
332     procedure UpdateChannelInfo(Dat: THeadValue);
333     procedure UpdateJoinChannelList(Dat: THeadValue);
334     procedure NoLuidError;
335     procedure UpdateIfGhostBox;
336     function BuildMenuConditionCheck(const IfGhost, Ghost: String): boolean;
337     procedure BuildMenu(Root: TMenuItem; Simple: boolean);
338     procedure PlaySound(const FileName: String);
339     //TBottleSstp\8aÖ\8cW\83C\83x\83\93\83g\83n\83\93\83h\83\89
340     procedure BottleSstpResendCountChange(Sender: TObject);
341     procedure BottleSstpResendTrying(Sender: TObject; MID: String);
342     procedure BottleSstpResendEnd(Sender: TObject; MID: String);
343     function IsSurfaceTag(const Script: String; var ID: integer): boolean;
344     procedure DoSurfacePreview(Surface: integer; const Kind:
345       TSurfacePreviewType);
346     function GetSurfacePreviewPositionHint(w, h: integer): TPoint;
347     function GetSurfacePreviewPositionScriptPoint(w, h: integer): TPoint;
348     procedure EditorPreview;
349     // \83^\83O\82Ì\95\8e\9a\97ñ\82ð\95Ï\8a·\82·\82é
350     function TagReplace(Script: String;
351       Before, After: array of String): String; overload;
352     function TagReplace(Script: String;
353       Before, After: TStrings): String; overload;
354     // \83T\81[\83t\83B\83X\82ð\95Ï\8a·\82·\82é
355     function ReplaceSurface(Script: String; Params: TCollection): String;
356     procedure ClearEditor;
357     procedure CopyFromLogToEditor(Log: TLogItem);
358     //
359     procedure AppendTextLog(const FileName, Line: String);
360     procedure AppendXMLLog(const FileName: String; Args: THeadValue);
361   protected
362     procedure WndProc(var Message: TMessage); override;
363   public
364     function DoTrans(var Script: String;
365       Options: TScriptTransOptions): String; overload;
366     function DoTrans(var Script: String;
367       Options: TScriptTransOptions; out FoundURL: boolean): String; overload;
368     function ScriptTransForSSTP(const Script: String;
369       out Error: String): String; overload;
370     procedure BeginConnect;
371     procedure RetryBeginConnect;
372     procedure EndConnect;
373     procedure ConstructMenu(Simple: boolean);
374     property Connecting: boolean read FConnecting write SetConnecting;
375     property BottleSstp: TBottleSstp read FBottleSstp;
376     function GhostNameToSetName(const Ghost: String): String;
377     procedure PostCommand(const Command: array of String); overload;
378     procedure PostCommand(Command: TStrings); overload;
379     procedure PostSetChannel(Channels: TStrings);
380     procedure SaveChainRuleList;
381   end;
382
383
384 var
385   frmSender: TfrmSender;
386
387 const
388   PanelConnecting = 0;  //\81u\90Ú\91±\92\86\81v\95\\8e¦\97p
389   PanelBytes      = 1;  //\81\9b\81\9b\83o\83C\83g
390   PanelCount      = 2;  //Local Proxy\81A\8c»\8dÝ\81\9b\8c\8f\91Ò\82¿
391   PanelMembers    = 3;  //\81\9b\81\9b\90l
392   PanelStatus     = 4;  //SSTP Bottle\83T\81[\83o\82É\93o\98^\82³\82ê\82Ä\82¢\82Ü\82·\81c\82È\82Ç
393
394   IconConnected    = 17;
395   IconDisconnected = 18;
396   IconSleep        = 19;
397   IconSleepDisconnected = 20;
398
399   WarningColor = clRed;
400
401   SendButtonLongHint = 'Bottle\82Ì\91\97\90M';
402
403 function Token(const Str: String; const Delimiter: char;
404   const Index: integer): String;
405
406 function StringReplaceEx(const Before: String; List: THeadValue): String;
407
408 implementation
409
410 uses SendConfirm, SettingForm, ChannelListForm, LogForm,
411   MessageBox, FMOExplorer, EditorTalkShow;
412
413 {$R *.DFM}
414
415 // \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
416 function Token(const Str: String; const Delimiter: char;
417   const Index: integer): String;
418 var i, c, len: integer;
419 begin
420   i := 1;
421   c := 0;
422   len := length(Str);
423   Result := '';
424   while i <= len do begin
425     if (Str[i] = Delimiter) and (StrByteType(PChar(Str), i) <> mbTrailByte) then begin
426       Inc(c);
427       if c > Index then Break;
428     end else if c = Index then Result := Result + Str[i];
429     Inc(i);
430   end;
431 end;
432
433 // \95\8e\9a\97ñ\82ð\92u\82«\8a·\82¦\82é\83\86\81[\83e\83B\83\8a\83e\83B\8aÖ\90\94
434 function StringReplaceEx(const Before: String; List: THeadValue): String;
435 var
436   i, MinPos, MinKey, p: integer;
437   Work: String;
438 begin
439   Work := Before;
440   Result := '';
441   MinKey := -1;
442   while Work <> '' do
443   begin
444     MinPos := -1;
445     for i := 0 to List.Count-1 do
446     begin
447       p := Pos(List.KeyAt[i], Work);
448       if (p > 0) and ((p < MinPos) or (MinPos < 0)) then
449       begin
450         MinPos := p;
451         MinKey := i;
452       end;
453     end;
454     if MinPos < 0 then
455     begin
456       Result := Result + Work;
457       Break;
458     end else
459     begin
460       Result := Result + Copy(Work, 1, MinPos-1) + List.ValueAt[MinKey];
461       Delete(Work, 1, (MinPos - 1) + Length(List.KeyAt[MinKey]));
462     end;
463   end;
464 end;
465
466 {TfrmSender}
467
468 procedure TfrmSender.actConfirmExecute(Sender: TObject);
469 var
470   AScript, Err, AGhost: String;
471   Item: TLogItem;
472   Choice: integer;
473 begin
474   // Partial Confirmation
475   if memScript.SelText <> '' then
476   begin
477     Choice := 0;
478     if not Pref.AutoPartialConfirm then
479       if not MultipleChoiceEdit('\8am\94F', ['\91I\91ð\95\94\95ª\82Ì\82Ý', '\83X\83N\83\8a\83v\83g\91S\91Ì'], Choice) then
480         Exit;
481     if Choice = 0 then
482     begin
483       AScript := memScript.SelText;
484       AScript := StringReplace(Pref.PartialConfirmFormat, '|', AScript, []);
485     end else
486       AScript := GetScriptText;
487   end else
488     AScript := GetScriptText;
489   AScript := StringReplace(AScript, #13#10, '', [rfReplaceAll]);
490
491   if Length(AScript) = 0 then Exit;
492   YenETrans;
493   AScript := ScriptTransForSSTP(AScript, Err);
494   if Err <> '' then
495   begin
496     ShowMessage(Err);
497     Exit;
498   end;
499
500   if cbxTargetGhost.ItemIndex > 0 then
501     AGhost := cbxTargetGhost.Text
502   else if FNowChannel <> '' then
503     AGhost := ChannelList.Channel[FNowChannel].Ghost
504   else
505     AGhost := '';
506
507   if Pref.IgnoreTimeCritical then
508     AScript := TagReplace(AScript, ['\t'], ['']);
509
510   Item := TLogItem.Create;
511   try
512     with Item do
513     begin
514       LogType := ltBottle;
515       Script := AScript;
516       Channel := '\81y\8am\94F\81z';
517       Ghost := AGhost;
518     end;
519     BottleSstp.Unshift(Item);
520   except
521     Item.Free;
522   end;
523
524   FScriptModified := false;
525 end;
526
527 procedure TfrmSender.FormCreate(Sender: TObject);
528 var Str: TStringList;
529 begin
530   FScriptBuffer := TObjectList.Create(true);
531
532   SakuraSeeker.OnDetectResultChanged := SakuraSeekerDetectResultChanged;
533   FConstDir := ExtractFileDir(Application.ExeName)+'\consts';
534   ScriptConstList.LoadFromDir(FConstDir);
535   FSppDir := ExtractFileDir(Application.ExeName)+'\spp';
536   Spps.LoadFromDirectory(FSppDir);
537   ConstructMenu(false);
538
539   Str := TStringList.Create;
540   try
541     try
542       Str.LoadFromFile(ExtractFilePath(Application.ExeName)+'rule.txt');
543       BottleChainRuleList := StringToComponent(Str.Text) as TBottleChainRuleList;
544     except
545       try
546         Str.LoadFromFile(ExtractFilePath(Application.ExeName)+'defrule.txt');
547         BottleChainRuleList := StringToComponent(Str.Text) as TBottleChainRuleList;
548       except
549         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');
550         Application.Terminate;
551         Application.ProcessMessages;
552         Exit;
553       end;
554     end;
555   finally
556     Str.Free;
557   end;
558
559   FOriginalCaption := Self.Caption;
560
561   {$IFDEF NOMUTEX}
562   ShowMessage('\93ñ\8fd\8bN\93®\8b\96\89Â\83o\81[\83W\83\87\83\93\82Å\82·\81B'#13#10 + VersionString);
563   {$ELSE}
564   FMutex := CreateMutex(nil, true, 'SSTPBottleClient2');
565   if GetLastError = ERROR_ALREADY_EXISTS then begin
566     Beep;
567     ShowMessage('SSTP Bottle Client\82Í\93ñ\8fd\8bN\93®\82Å\82«\82Ü\82¹\82ñ');
568     CloseHandle(FMutex);
569     Application.Terminate;
570     Application.ProcessMessages; //WM_QUIT\82ð\97¬\82·
571     Exit;
572   end;
573   {$ENDIF}
574
575   UpdateLayout;
576   mnShowToolBar.Checked := Pref.ShowToolBar;
577   mnShowConstBar.Checked := Pref.ShowConstBar;
578   if Pref.StayOnTop then begin
579     FormStyle := fsStayOnTop;
580     mnStayOnTop.Checked := true;
581   end else begin
582     FormStyle := fsNormal;
583     mnStayOnTop.Checked := false;
584   end;
585
586   // URL\83W\83\83\83\93\83v\90æ\82ð\83q\83\93\83g\95\8e\9a\97ñ\82Æ\82µ\82Ä\90Ý\92è
587   mnGoToHP.Hint := Pref.HomePage;
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 := StringReplace(GetScriptText, #13#10, '', [rfReplaceAll]);
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
721   begin
722     MessageDlg('\91\97\90M\91O\82É\83\8d\81[\83J\83\8b\8am\94F\82µ\82Ä\82­\82¾\82³\82¢\81B', mtError, [mbOk], 0);
723     Exit;
724   end;
725
726   if not Pref.NoConfirm then begin
727     if not SendConfirmDialog(FNowChannel, cbxTargetGhost.Text) then Exit;
728   end;
729
730   Ghost := '';
731   if cbxTargetGhost.ItemIndex > 0 then Ghost := cbxTargetGhost.Text;
732   Command := TStringList.Create;
733   try
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 := Pref.BottleServer;
774       IdSlpp20.Port := Pref.BottleServerPort;
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.TextColor;
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     TabWidth := Pref.TabWidth;
1234   end;
1235 end;
1236
1237 function TfrmSender.DoTrans(var Script: String;
1238   Options: TScriptTransOptions; out FoundURL: boolean): String;
1239 var UrlCancel, Mark: String;
1240     Url, UrlName: array[0..6] of String;
1241     i, j, u, UrlCount: integer;
1242     LastSurfaceH, LastSurfaceU: integer;
1243     UnyuTalking: boolean;
1244     QuickSection, Synchronize: boolean;
1245     Warnings: TStringList;
1246 begin
1247   UrlCount := 0;
1248   LastSurfaceH := 0;
1249   LastSurfaceU := 10;
1250   UnyuTalking := false;
1251   QuickSection := false;
1252   Synchronize := false;
1253   SsParser.LeaveEscape := true;
1254   SsParser.EscapeInvalidMeta := false;
1255   SsParser.InputString := Script;
1256   Script := '';
1257   Warnings := TStringList.Create;
1258   try
1259     for i := 0 to SsParser.Count-1 do begin
1260       if SsParser[i] = '\t' then begin
1261         if not(toIgnoreTimeCritical in Options) then
1262           Script := Script + '\t';
1263       end else if SsParser[i] = '\e' then begin
1264         Continue;
1265       end else if (SsParser.Match(SsParser[i], '\URL%b') > 0) then begin
1266         if toConvertURL in Options then begin
1267           UrlCount := 0; //\91O\82ÌURL\83^\83O\82Ì\89e\8b¿\82ð\96³\8e\8b\81B
1268           for u := 7 downto 1 do begin
1269             if (SsParser.Match(SsParser[i],
1270                 '\URL%b'+StringReplace(StringOfChar('-', u*2),
1271                 '-', '%b', [rfReplaceAll]))) > 0 then begin
1272               for j := 1 to u do begin
1273                 Url[UrlCount] := SsParser.GetParam(SsParser[i], UrlCount*2+2);
1274                 UrlName[UrlCount] := SsParser.GetParam(SsParser[i], UrlCount*2+3);
1275                 if UrlName[UrlCount] = '' then UrlName[UrlCount] := Url[UrlCount];
1276                 if Pos('http://', Url[UrlCount]) > 0 then Inc(UrlCount);
1277               end;
1278             end;
1279             if UrlCount > 0 then UrlCancel := SsParser.GetParam(SsParser[i], 1);
1280             if UrlCancel = '' then UrlCancel := '\8ds\82©\82È\82¢\81@\81@\81@\81@';
1281           end;
1282           if SsParser.Match(SsParser[i], '\URL%b%b') = 0 then begin //\8aÈ\88Õ\94ÅURL\95Ï\8a·
1283             //\8aÈ\88Õ\8c`\8e®\URL\83^\83O\95Ï\8a·
1284             Url[0] := SsParser.GetParam(SsParser[i], 1);
1285             UrlName[0] := '\8ds\82­\81@\81@\81@\81@\81@\81@';
1286             UrlCancel  := '\8ds\82©\82È\82¢\81@\81@\81@\81@';
1287             if Pos('http://', Url[0]) > 0 then begin
1288               UrlCount := 1;
1289               if not QuickSection then
1290                 Script := Script + '\_q' + Url[0] + '\_q'
1291               else
1292                 Script := Script + Url[0];
1293             end;
1294           end;
1295         end else Script := Script + SsParser[i];
1296       end else begin
1297         Mark := SsParser[i];
1298         if Mark = '\h' then begin
1299           UnyuTalking := false;
1300           if toHUTagTo01Tag in Options then Mark := '\0';
1301           if Synchronize and Pref.WarnScopeChangeInSynchronize then
1302             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');
1303         end else if Mark = '\u' then begin
1304           UnyuTalking := true;
1305           if toHUTagTo01Tag in Options then Mark := '\1';
1306           if Synchronize and Pref.WarnScopeChangeInSynchronize then
1307             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');
1308         end else if Mark = '\_q' then begin
1309           QuickSection := not QuickSection;
1310         end else if Mark = '\_s' then begin
1311           Synchronize := not Synchronize;
1312         end else if SsParser.Match(Mark, '\s%b') > 0 then begin
1313           if UnyuTalking then begin
1314             LastSurfaceU := StrToIntDef(SsParser.GetParam(Mark, 1),
1315                                         LastSurfaceU);
1316           end else begin
1317             LastSurfaceH := StrToIntDef(SsParser.GetParam(Mark, 1),
1318                                         LastSurfaceH);
1319           end;
1320         end else if SsParser.Match(Mark, '\s%d') > 0 then begin
1321           if UnyuTalking then begin
1322             LastSurfaceU := StrToIntDef(Mark[3], LastSurfaceU);
1323           end else begin
1324             LastSurfaceH := StrToIntDef(Mark[3], LastSurfaceH);
1325           end;
1326         end;
1327         Script := Script + Mark;
1328       end;
1329     end;
1330     if UrlCount > 0 then begin
1331       FoundUrl := true;
1332       Script := Script + '\h\n';
1333       if not (toNoChoice in Options) then begin
1334         for i := 0 to UrlCount-1 do begin
1335           Script := Script + Format('\q%d[%s][%s]',
1336                       [i, SsParser.EscapeParam(Url[i]), UrlName[i]]);
1337         end;
1338         Script := Script + Format('\q%d[#cancel][%s]', [UrlCount, UrlCancel]);
1339         //Script := Script + '\z'; //\8dÅ\90Vphase\82Å\82Í\8dí\8f\9c
1340       end else begin
1341         Script := Script + '\h';
1342         for i := 0 to UrlCount-1 do begin
1343           Script := Script + Format('\n{%s}(%s)', [UrlName[i], Url[i]]);
1344         end;
1345         Script := Script + Format('\n{%s}', [UrlCancel]);
1346       end;
1347     end else
1348       FoundUrl := false;
1349     //\83X\83N\83\8a\83v\83g\82Ì\8dÅ\8cã\82É\83E\83F\83C\83g\91}\93ü
1350     if toWaitScriptEnd in Options then begin
1351       i := Pref.WaitScriptEnd;
1352       while i > 0 do begin
1353         if i > 9 then begin
1354           Script := Script + '\w9';
1355           Dec(i, 9);
1356         end else begin
1357           Script := Script + '\w' + IntToStr(i);
1358           i := 0;
1359         end;
1360       end;
1361     end;
1362
1363     Script := Script + '\e';
1364     RegExp.Subst('s/\r\n//gk', Script);
1365
1366     //\83^\83O\83`\83F\83b\83N\8aÖ\98A
1367     for i := 0 to SsParser.Count-1 do begin
1368       if SsParser.MarkUpType[i] = mtTagErr then begin
1369         Result := '"' + SsParser[i] + '"'#13#10 +
1370                   '\82Í\81ASSTP Bottle\82Å\94F\82ß\82ç\82ê\82È\82¢\82©\81A\94F\8e¯\82Å\82«\82È\82¢\83^\83O\82Å\82·\81B';
1371         Exit;
1372       end;
1373     end;
1374     if (SsParser[0] <> '\t') and Pref.WarnYenTNotExist then begin
1375       Warnings.Add('\83X\83N\83\8a\83v\83g\82ª\t\82©\82ç\8en\82Ü\82Á\82Ä\82¢\82Ü\82¹\82ñ\81B');
1376     end;
1377
1378     //\83`\83F\83b\83N
1379     if (Warnings.Count > 0) and (toWarnCheck in Options) then begin
1380       if MessageDlg(Warnings.Text + #13#10 + '\91\97\90M\82µ\82Ü\82·\82©?', mtWarning,
1381                     mbOkCancel, 0) = mrCancel then
1382         Result := Warnings.Text;
1383     end;
1384   finally
1385     Warnings.Free;
1386   end;
1387 end;
1388
1389 function TfrmSender.DoTrans(var Script: String;
1390   Options: TScriptTransOptions): String;
1391 var dum: boolean;
1392 begin
1393   Result := DoTrans(Script, Options, dum);
1394 end;
1395
1396 procedure TfrmSender.mnGoToHPClick(Sender: TObject);
1397 begin
1398   ShellExecute(Handle, 'open', PChar(Pref.HomePage), nil, nil, SW_SHOW);
1399 end;
1400
1401 procedure TfrmSender.ShowHintLabel(const Mes: String; Col: TColor);
1402 begin
1403   lblMessage.Caption := Mes;
1404   lblMessage.Font.Color := Col;
1405   lblMessage.Visible := true;
1406   LabelTimer.Enabled := false;
1407   LabelTimer.Enabled := true;
1408 end;
1409
1410 procedure TfrmSender.LabelTimerTimer(Sender: TObject);
1411 begin
1412   LabelTimer.Enabled := false;
1413   lblmessage.Visible := false;
1414 end;
1415
1416 procedure TfrmSender.actCopyAllExecute(Sender: TObject);
1417 var Str: String;
1418     Clip: TClipBoard;
1419 begin
1420   Str := memScript.Lines.Text;
1421   Clip := ClipBoard();
1422   Clip.SetTextBuf(PChar(Str));
1423 end;
1424
1425 procedure TfrmSender.actCopyAllNoReturnExecute(Sender: TObject);
1426 var Str: String;
1427     Clip: TClipBoard;
1428 begin
1429   Str := memScript.Lines.Text;
1430   RegExp.Subst('s/\r\n//gk', Str);
1431   Clip := ClipBoard();
1432   Clip.SetTextBuf(PChar(Str));
1433 end;
1434
1435 procedure TfrmSender.Slpp20SlppEvent(Sender: TObject; EventType: TIdSlppEventType;
1436   const Param: String);
1437 var HeadValue: THeadValue;
1438 begin
1439   HeadValue := THeadValue.Create(Param);
1440   try
1441     case EventType of
1442       etScript, etForceBroadcast, etUnicast: begin
1443         //\83\81\83b\83Z\81[\83W\8eó\90M
1444         DispatchBottle(EventType, HeadValue);
1445       end;
1446       etMemberCount: begin
1447         StatusBar.Panels[PanelMembers].Text := HeadValue['Num'] + '\90l'
1448       end;
1449       etChannelCount: begin
1450         try
1451           ChannelList.Channel[HeadValue['Channel']].Members := StrToInt(HeadValue['Num']);
1452         except
1453         end;
1454       end;
1455       etConnectOk: begin
1456         ShowHintLabel('SSTP Bottle\83T\81[\83o\82Æ\92Ê\90M\8am\97§\81B');
1457         Added := true;
1458         FBeginConnectFailCount := 0;
1459         //\83`\83\83\83\93\83l\83\8b\8e©\93®\93o\98^
1460         if not Connecting then
1461           PostCommand(['Command: getChannels']);
1462         SakuraSeeker.BeginDetect;
1463       end;
1464       etChannelList: begin
1465         UpdateJoinChannelList(HeadValue);
1466         // \8dÅ\8cã\82É\8eQ\89Á\82µ\82Ä\82¢\82½\83`\83\83\83\93\83l\83\8b\82ð\8bL\98^\82·\82é
1467         if JoinChannelsBackup = nil then JoinChannelsBackup := TStringList.Create;
1468         JoinChannelsBackup.Assign(JoinChannels);
1469       end;
1470       etCloseChannel: begin
1471         with JoinChannels do
1472           if IndexOf(HeadValue['Channel']) >= 0 then
1473             Delete(IndexOf(HeadValue['Channel']));
1474         with tabChannel do begin
1475           if Tabs.IndexOf(HeadValue['Channel']) >= 0 then
1476             Tabs.Delete(Tabs.IndexOf(HeadValue['Channel']));
1477           if Tabs.Count > 0 then TabIndex := 0 else TabIndex := -1;
1478           tabChannelChange(self);
1479         end;
1480         ShowHintLabel(HeadValue['Channel'] + '\83`\83\83\83\93\83l\83\8b\82Í\94p\8e~\82³\82ê\82Ü\82µ\82½',
1481                       WarningColor);
1482         frmLog.AddCurrentSystemLog('SYSTEM', HeadValue['Channel'] + '\83`\83\83\83\93\83l\83\8b\82Í\94p\8e~\82³\82ê\82Ü\82µ\82½');
1483         frmMessageBox.ShowMessage(HeadValue['Channel'] + '\83`\83\83\83\93\83l\83\8b\82Í\94p\8e~\82³\82ê\82Ü\82µ\82½');
1484       end;
1485       etForceBroadcastInformation: begin
1486         if HeadValue['Type'] = 'Vote' then begin
1487           frmLog.VoteLog(HeadValue['MID'], StrToIntDef(HeadValue['Num'], 0));
1488         end else if HeadValue['Type'] = 'Agree' then begin
1489           frmLog.AgreeLog(HeadValue['MID'], StrToIntDef(HeadValue['Num'], 0));
1490         end;
1491       end;
1492     end;
1493   finally
1494     HeadValue.Free;
1495   end;
1496 end;
1497
1498 procedure TfrmSender.BottleSstpResendCountChange(Sender: TObject);
1499 begin
1500   StatusBar.Panels[PanelCount].Text := IntToStr(FBottleSstp.CueCount) + '\8c\8f';
1501   try
1502     TaskTray.TipString := 'SSTP Bottle Client (' +
1503                           IntToStr(FBottleSstp.CueCount) + '\8c\8f)';
1504   except
1505     ; // \83^\83X\83N\83g\83\8c\83C\93o\98^\8e¸\94s\82ð\96³\8e\8b\82·\82é
1506   end;
1507   actClearBottles.Enabled := (FBottleSstp.CueCount > 0);
1508 end;
1509
1510 procedure TfrmSender.actSettingExecute(Sender: TObject);
1511 begin
1512   Application.CreateForm(TfrmSetting, frmSetting);
1513   try
1514     frmSetting.Execute;
1515     Pref.SaveSettings;
1516     SaveChainRuleList;
1517   finally
1518     frmSetting.Release;
1519     frmSetting := nil;
1520   end;
1521   //
1522   UpdateLayout;
1523   tabChannel.Repaint;
1524   frmLog.UpdateWindow;
1525 end;
1526
1527 procedure TfrmSender.memScriptKeyPress(Sender: TObject; var Key: Char);
1528 begin
1529   if (Key = #13) or (Key = #10) then Key := Char(0);
1530 end;
1531
1532 procedure TfrmSender.Slpp20Disconnect(Sender: TObject);
1533 begin
1534   Added := false;
1535   UpdateJoinChannelList(nil);
1536   frmLog.AddCurrentSystemLog('SYSTEM', '\83T\81[\83o\82©\82ç\90Ø\92f\82³\82ê\82Ü\82µ\82½');
1537   if not Application.Terminated then RetryBeginConnect;
1538 end;
1539
1540 procedure TfrmSender.SetSleeping(const Value: boolean);
1541 begin
1542   FSleeping := Value;
1543   FBottleSstp.ResendSleep := Value;
1544   ChangeTaskIcon;
1545 end;
1546
1547 procedure TfrmSender.actClearBottlesExecute(Sender: TObject);
1548 var Re: integer;
1549 begin
1550   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]),
1551                    mtWarning, mbOkCancel, 0);
1552   if Re = mrOk then begin
1553     FBottleSstp.Clear;
1554     frmLog.AllBottleOpened;
1555     frmLog.UpdateWindow;
1556   end;
1557 end;
1558
1559 procedure TfrmSender.SakuraSeekerDetectResultChanged(Sender: TObject);
1560 begin
1561   UpdateIfGhostBox; // \83h\83\8d\83b\83v\83_\83E\83\93\82Ì\92\86\90g\82ð\8f\91\82«\8a·\82¦\82é
1562 end;
1563
1564 procedure TfrmSender.UpdateChannelInfo(Dat: THeadValue);
1565 var i: integer;
1566     Ch: TChannelListItem;
1567 begin
1568   ChannelList.Clear;
1569   for i := 1 to Dat.IntData['Count'] do begin
1570     Ch := TChannelListItem.Create;
1571     Ch.Name    := Dat[Format('CH%d_name', [i])];
1572     Ch.Ghost   := Dat[Format('CH%d_ghost', [i])];
1573     Ch.Info    := Dat[Format('CH%d_info', [i])];
1574     Ch.NoPost  := Dat[Format('CH%d_nopost', [i])] = '1';
1575     Ch.Members := Dat.IntData[Format('CH%d_count', [i])];
1576     Ch.WarnPost:= Dat[Format('CH%d_warnpost', [i])] = '1';
1577     ChannelList.Add(Ch);
1578   end;
1579   UpdateLayout;
1580 end;
1581
1582 procedure TfrmSender.mnGetNewIdClick(Sender: TObject);
1583 begin
1584   PostCommand(['Command: getNewId', 'Agent: ' + VersionString]);
1585 end;
1586
1587 procedure TfrmSender.NoLuidError;
1588 begin
1589   Beep;
1590   ShowMessage('SSTP Bottle ID\82Ì\8eæ\93¾\82ª\82Ü\82¾\8a®\97¹\82µ\82Ä\82¢\82Ü\82¹\82ñ\81B'#13#10+
1591               '\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+
1592               '\82±\82Ì\91\80\8dì\82ÍClient\8f\89\89ñ\8bN\93®\8e\9e\82É1\89ñ\82¾\82¯\95K\97v\82Å\82·\81B');
1593 end;
1594
1595 procedure TfrmSender.tabChannelChange(Sender: TObject);
1596 begin
1597   if tabChannel.TabIndex >= 0 then begin
1598     FNowChannel := tabChannel.Tabs[tabChannel.TabIndex];
1599     actSend.Hint := Format('\81u%s\81v\82É\91\97\90M|%s', [FNowChannel, SendButtonLongHint]);
1600   end else begin
1601     FNowChannel := '';
1602     actSend.Hint := '';
1603   end;
1604   tabChannel.Repaint; //\82±\82ê\82ª\82È\82¢\82Æ\90F\82ª\95Ï\82í\82ç\82È\82¢\82±\82Æ\82ª\82 \82é
1605   ConstructMenu(true);
1606 end;
1607
1608 procedure TfrmSender.actPrevChannelExecute(Sender: TObject);
1609 begin
1610   with tabChannel do begin
1611     if Tabs.Count = 0 then Exit;
1612     if TabIndex=0 then TabIndex := Tabs.Count-1
1613     else TabIndex := TabIndex-1;
1614   end;
1615   tabChannelChange(Self);
1616 end;
1617
1618 procedure TfrmSender.actNextChannelExecute(Sender: TObject);
1619 begin
1620   with tabChannel do begin
1621     if Tabs.Count = 0 then Exit;
1622     if TabIndex=Tabs.Count-1 then TabIndex := 0
1623     else TabIndex := TabIndex+1;
1624   end;
1625   tabChannelChange(Self);
1626 end;
1627
1628 procedure TfrmSender.UpdateJoinChannelList(Dat: THeadValue);
1629 var i: integer;
1630     nodat: boolean;
1631 begin
1632   nodat := Dat = nil; //nil\82È\82ç\83`\83\83\83\93\83l\83\8b\91S\89ð\8f\9c
1633   if nodat then Dat := THeadValue.Create('');
1634   JoinChannels.Clear;
1635   for i := 0 to Dat.Count-1 do
1636     if Dat.KeyAt[i] = 'Entry' then begin
1637       if RegExp.Match('m/^(.+?) \((\d+?)\)$/', Dat.ValueAt[i]) then
1638         JoinChannels.Add(RegExp[1]);
1639     end;
1640   with tabChannel do begin
1641     OnChange := nil;
1642     JoinChannels.Sort;
1643     Tabs.BeginUpdate;
1644     Tabs.Clear;
1645     for i := 0 to JoinChannels.Count-1 do begin
1646       //\8eó\90M\90ê\97p\83`\83\83\83\93\83l\83\8b\82Í\95\\8e¦\82µ\82È\82¢
1647       if not ChannelList.Channel[JoinChannels[i]].NoPost then
1648         Tabs.Add(JoinChannels[i]);
1649     end;
1650     Tabs.EndUpdate;
1651     // \8c³\82©\82ç\83`\83\83\83\93\83l\83\8b\82É\8eQ\89Á\82µ\82Ä\82¢\82½\8fê\8d\87\82Í
1652     // \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)
1653     TabIndex := 0;
1654     for i := 0 to Tabs.Count-1 do
1655       if Tabs[i] = FNowChannel then TabIndex := i;
1656     if Tabs.Count > 0 then begin
1657       FNowChannel := Tabs[TabIndex];
1658       actSend.Hint := Format('\81u%s\81v\82É\91\97\90M|%s', [FNowChannel, SendButtonLongHint]);
1659     end else begin
1660       FNowChannel := '';
1661       actSend.Hint := Format('\91\97\90M|%s', [SendButtonLongHint]);
1662     end;
1663     Visible := Tabs.Count > 0;
1664     if Tabs.Count > 1 then begin
1665       actNextChannel.Enabled := true;
1666       actPrevChannel.Enabled := true;
1667     end else begin
1668       actNextChannel.Enabled := false;
1669       actPrevChannel.Enabled := false;
1670     end;
1671     OnChange := tabChannelChange;
1672   end;
1673   if nodat then Dat.Free;
1674   if JoinChannels.Count = 0 then begin
1675     Self.Caption := FOriginalCaption + ' - \83`\83\83\83\93\83l\83\8b\82É\8eQ\89Á\82µ\82Ä\82¢\82Ü\82¹\82ñ';
1676     actSend.Enabled := false;
1677   end else begin
1678     Self.Caption := FOriginalCaption;
1679     actSend.Enabled := true;
1680   end;
1681 end;
1682
1683 procedure TfrmSender.cbxTargetGhostDropDown(Sender: TObject);
1684 begin
1685   SakuraSeeker.BeginDetect;
1686   UpdateIfGhostBox;
1687 end;
1688
1689 procedure TfrmSender.actShowLogExecute(Sender: TObject);
1690 begin
1691   frmLog.Show;
1692   if frmLog.WindowState = wsMinimized then frmLog.WindowState := wsNormal;
1693 end;
1694
1695 procedure TfrmSender.actSleepExecute(Sender: TObject);
1696 begin
1697   if actSleep.Checked then begin
1698     actSleep.Checked := false;
1699     ShowHintLabel('\83X\83\8a\81[\83v\82ð\89ð\8f\9c\82µ\82Ü\82µ\82½');
1700   end else begin
1701     actSleep.Checked := true;
1702     ShowHintLabel('\83X\83\8a\81[\83v\82ð\90Ý\92è\82µ\82Ü\82µ\82½');
1703   end;
1704   Sleeping := actSleep.Checked;
1705   ChangeTaskIcon;
1706 end;
1707
1708
1709 procedure TfrmSender.DispatchBottle(EventType: TIdSlppEventType;
1710   Dat: THeadValue);
1711 var Opt: TSstpSendOptions;
1712     Event: TBottleChainBottleEvent;
1713     Script, Sender, Ghost, Channel, ErrorMes: String;
1714     BreakFlag, NoDispatch: boolean;
1715     Sound, LogName: String;
1716     i, j, k, SkipCount: integer;
1717     Rule: TBottleChainRule;
1718     Action: TBottleChainAction;
1719     LogNameList: TStringList;
1720     CueItem: TLogItem;
1721     ReplaceHash: THeadValue; 
1722 begin
1723   Opt := [];
1724   if Pref.NoTranslate then Opt := Opt + [soNoTranslate];
1725   if Pref.NoDescript  then Opt := Opt + [soNoDescript];
1726   Channel := Dat['Channel'];
1727   case EventType of
1728     etScript: Sender := Channel;
1729     etForceBroadcast: Sender := '\81y\82¨\92m\82ç\82¹\81z';
1730     etUnicast: Sender := Dat['SenderUID'];
1731   end;
1732
1733   //\96Ú\95W\83S\81[\83X\83g(\83I\81[\83o\81[\83\89\83C\83h\88È\91O)\8c\88\92è
1734   if Dat['IfGhost'] <> '' then begin
1735     Ghost := Dat['IfGhost'];
1736   end else begin
1737     if ChannelList.Channel[Channel] <> nil then
1738       Ghost := ChannelList.Channel[Channel].Ghost;
1739   end;
1740   Dat['TargetGhost'] := Ghost;
1741
1742   // \83\81\83^\95\8e\9a\8f\80\94õ
1743   ReplaceHash := THeadValue.Create;
1744   ReplaceHash['%channel%'] := SafeFileName(Dat['Channel']);
1745   ReplaceHash['%ghost%'] := SafeFileName(Dat['IfGhost']);
1746   ReplaceHash['%date%'] := FormatDateTime('yy-mm-dd', Now());
1747   ReplaceHash['%year%'] := FormatDateTime('yyyy', Now());
1748   ReplaceHash['%yy%'] := FormatDateTime('yy', Now());
1749   ReplaceHash['%month%'] := FormatDateTime('mm', Now());
1750   ReplaceHash['%day%'] := FormatDateTime('dd', Now());
1751   ReplaceHash['%hour%'] := FormatDateTime('hh', Now());
1752   ReplaceHash['%minute%'] := FormatDateTime('nn', Now());
1753
1754   Event := TBottleChainBottleEvent.Create;
1755   try
1756     Event.Data := Dat;
1757     if EventType = etScript then Event.LogType := ltBottle
1758     else Event.LogType := ltSystemLog;
1759
1760     //\83X\83N\83\8a\83v\83g\95Ï\8a·
1761     Script := ScriptTransForSSTP(Dat['Script'], ErrorMes);
1762     if ErrorMes <> '' then begin
1763       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'+
1764                     '\94z\91\97\82³\82ê\82Ü\82¹\82ñ\82Å\82µ\82½\81@\81c '+Dat['Script']);
1765       Exit;
1766     end;
1767
1768     BreakFlag := false;
1769     NoDispatch := false;
1770     Sound := '';
1771     LogNameList := TStringList.Create;
1772     SkipCount := 0;
1773     try
1774       for i := 0 to BottleChainRuleList.Count-1 do begin
1775         if SkipCount > 0 then begin
1776           Dec(SkipCount);
1777           Continue;
1778         end;
1779         Rule := BottleChainRuleList[i];
1780         if not Rule.Enabled then Continue;
1781         if not Rule.Check(Event) then Continue;
1782         for j := 0 to Rule.Actions.Count-1 do begin
1783           Action := (Rule.Actions[j] as TBottleChainAction);
1784           if Action is TBottleChainAbortRuleAction then
1785             BreakFlag := true;
1786           if Action is TBottleChainSkipRuleAction then
1787             SkipCount := (Action as TBottleChainSkipRuleAction).SkipCount;
1788           if (Action is TBottleChainSoundAction) and (Sound = '') then
1789           begin
1790             Sound := (Action as TBottleChainSoundAction).SoundFile;
1791             Sound := StringReplaceEx(Sound, ReplaceHash);
1792           end;
1793           if Action is TBottleChainNoDispatchAction then
1794             NoDispatch := true;
1795           if Action is TBottleChainLogAction then
1796           begin
1797             for k := 0 to (Action as TBottleChainLogAction).LogNames.Count-1 do begin
1798               LogName := (Action as TBottleChainLogAction).LogNames[k];
1799               LogName := StringReplaceEx(LogName, ReplaceHash);
1800               LogNameList.Add(LogName);
1801             end;
1802           end;
1803           if Action is TBottleChainOverrideGhostAction then
1804           begin
1805             Dat['TargetGhost'] := (Action as TBottleChainOverrideGhostAction).TargetGhost;
1806           end;
1807           if Action is TBottleChainQuitAction then
1808             Application.Terminate;
1809           if Action is TBottleChainSaveTextLogAction then
1810             AppendTextLog(StringReplaceEx((Action as TBottleChainSaveTextLogAction).FileName, ReplaceHash),
1811               Format('%s,%s,%s,%s', [Dat['Channel'], Dat['IfGhost'],
1812                 FormatDateTime('yy/mm/dd hh:nn:ss', Now), Dat['Script']]));
1813           if Action is TBottleChainSaveXMLLogAction then
1814             AppendXMLLog(StringReplaceEx((Action as TBottleChainSaveXMLLogAction).FileName, ReplaceHash),
1815               Dat);
1816           if Action is TBottleChainSurfaceReplaceAction then
1817             Script := ReplaceSurface(Script, (Action as TBottleChainSurfaceReplaceAction).Params);
1818         end;
1819         if BreakFlag then Break;
1820       end;
1821
1822       if Dat['Script'] <> '' then begin
1823         for i := 0 to LogNameList.Count-1 do
1824           frmLog.AddCurrentScriptLog(LogNameList[i], Dat['Script'], Sender, Dat['MID'], Dat['IfGhost']);
1825         if NoDispatch then begin
1826           frmLog.SetBottleState(Dat['MID'], lsOpened);
1827         end else begin
1828           Ghost := Dat['TargetGhost']; // \83I\81[\83o\81[\83\89\83C\83h\82³\82ê\82Ä\82¢\82é\89Â\94\\90«\82ª\82 \82é
1829           CueItem := TLogItem.Create(ltBottle, Dat['MID'], Dat['Channel'],
1830             Script, Ghost, Now());
1831           try
1832             FBottleSstp.Push(CueItem);
1833           except
1834             CueItem.Free;
1835           end;
1836         end;
1837       end;
1838
1839       if Dat['DialogMessage'] <> '' then begin
1840         Beep;
1841         frmMessageBox.ShowMessage(
1842           DateTimeToStr(Now) + #13#10 +
1843           'SSTP Bottle\83T\81[\83o\82©\82ç\82¨\92m\82ç\82¹'#13#10+Dat['DialogMessage']);
1844         for i := 0 to LogNameList.Count-1 do
1845           frmLog.AddCurrentSystemLog(LogNameList[i], Dat['DialogMessage']);
1846       end;
1847
1848       //\89¹\82Ì\8dÄ\90
1849       if (Sound <> '') then PlaySound(Sound);
1850     finally
1851       LogNameList.Free;
1852     end;
1853   finally
1854     Event.Free;
1855     ReplaceHash.Free;
1856   end;
1857 end;
1858
1859 procedure TfrmSender.YenETrans;
1860 var St, Le, i: integer;
1861     Orig, Text: String;
1862 begin
1863   St := memScript.SelStart;
1864   Le := memScript.SelLength;
1865   Orig := GetScriptText;
1866   RegExp.Subst('s/(\r\n)+$//kg', Orig);
1867
1868   with SsParser do begin
1869     LeaveEscape := true;
1870     EscapeInvalidMeta := false;
1871     InputString := Orig;
1872   end;
1873   for i := 0 to SsParser.Count-1 do begin
1874     if SsParser[i] <> '\e' then Text := Text + SsParser[i];
1875   end;
1876
1877   Text := Text + '\e';
1878
1879   if Orig <> Text then memScript.Lines.Text := Text;
1880   SsParser.InputString := Text;
1881
1882   RegExp.Subst('s/\r\n//kg', Text);
1883   memScript.SetFocus;
1884   memScript.SelStart := St;
1885   memScript.SelLength := Le;
1886 end;
1887
1888 procedure TfrmSender.PostCommand(const Command: array of String);
1889 var PostStr: TStringList;
1890     i: integer;
1891 begin
1892   PostStr := nil;
1893   try
1894     PostStr := TStringList.Create;
1895     for i := Low(Command) to High(Command) do begin
1896       PostStr.Add(Command[i]);
1897     end;
1898     PostCommand(PostStr);
1899   finally
1900     PostStr.Free;
1901   end;
1902 end;
1903
1904 procedure TfrmSender.PostCommand(Command: TStrings);
1905 var PostStr: String;
1906 begin
1907   Connecting := true;
1908   PostStr := Command.Text;
1909   PostStr := ParamsEncode(PostStr);
1910   try
1911     FHttp := THTTPDownloadThread.Create(Pref.BottleServer, Pref.CgiName, PostStr);
1912     if Pref.UseHttpProxy then begin
1913       FHttp.ProxyServer := Pref.ProxyAddress;
1914       FHttp.ProxyPort   := Pref.ProxyPort;
1915     end;
1916     FHttp.OnSuccess := HttpSuccess;
1917     FHttp.OnConnectionFailed := HttpFailure;
1918     FHttp.FreeOnTerminate := true; // \8f\9f\8eè\82É\8e©\95ª\82ÅFree\82µ\82Ä\82­\82¾\82³\82¢
1919     FHTTP.Resume;
1920   except
1921     on EHeapException do begin
1922       Connecting := false;
1923       FHttp.Free;
1924     end;
1925   end;
1926 end;
1927
1928 procedure TfrmSender.actVoteMessageExecute(Sender: TObject);
1929 var Log: TLogItem;
1930 begin
1931   if frmLog.lvwLog.Selected = nil then Exit;
1932   Log := frmLog.SelectedBottleLog[frmLog.lvwLog.Selected.Index] as TLogItem;
1933   if Log = nil then Exit;
1934   if Log.LogType <> ltBottle then Exit;
1935   PostCommand([
1936     'Command: voteMessage',
1937     'VoteType: Vote',
1938     'LUID: ' + Pref.LUID,
1939     'MID: ' + Log.MID
1940   ]);
1941 end;
1942
1943
1944 procedure TfrmSender.actAgreeMessageExecute(Sender: TObject);
1945 var Log: TLogItem;
1946 begin
1947   if frmLog.lvwLog.Selected = nil then Exit;
1948   Log := frmLog.SelectedBottleLog[frmLog.lvwLog.Selected.Index] as TLogItem;
1949   if Log = nil then Exit;
1950   if Log.LogType <> ltBottle then Exit;
1951   PostCommand([
1952     'Command: voteMessage',
1953     'VoteType: Agree',
1954     'LUID: ' + Pref.LUID,
1955     'MID: ' + Log.MID
1956   ]);
1957 end;
1958
1959
1960 function TfrmSender.GhostNameToSetName(const Ghost: String): String;
1961 begin
1962   if SakuraSeeker.ProcessByName[Ghost] <> nil then
1963     Result := SakuraSeeker.ProcessByName[Ghost].SetName
1964   else
1965     Result := '';
1966 end;
1967
1968 procedure TfrmSender.tabChannelContextPopup(Sender: TObject;
1969   MousePos: TPoint; var Handled: Boolean);
1970 var Ch: String;
1971 begin
1972   with tabChannel do begin
1973     Tag := IndexOfTabAt(MousePos.X, MousePos.Y);
1974     if Tag < 0 then Handled := true;
1975     Ch := Tabs[Tag];
1976   end;
1977 end;
1978
1979 procedure TfrmSender.PostSetChannel(Channels: TStrings);
1980 var PostStr: TStringList;
1981     i: integer;
1982 begin
1983   PostStr := TStringList.Create;
1984   try
1985     with PostStr do begin
1986       Add('Command: setChannels');
1987       Add('Agent: ' + VersionString);
1988       Add('LUID: ' + Pref.LUID);
1989       if Channels <> nil then
1990         for i := 0 to Channels.Count-1 do begin
1991           Add(Format('Ch%d: %s'#13#10, [i+1, Channels[i]]));
1992         end;
1993     end;
1994     PostCommand(PostStr);
1995   finally
1996     PostStr.Free;
1997   end;
1998 end;
1999
2000 procedure TfrmSender.mnLeaveThisChannelClick(Sender: TObject);
2001 var Ch: String;
2002     Chs: TStringList;
2003 begin
2004   // \8ew\92è\82µ\82½\83`\83\83\83\93\83l\83\8b\82©\82ç\94²\82¯\82é
2005   with tabChannel do
2006     Ch := Tabs[Tag]; // \94²\82¯\82½\82¢\83`\83\83\83\93\83l\83\8b\96¼
2007   Chs := TStringList.Create;
2008
2009   // \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ð
2010   // \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é
2011   try
2012     Chs.Assign(JoinChannels);
2013     while Chs.IndexOf(Ch) >= 0 do
2014       Chs.Delete(Chs.IndexOf(Ch));
2015     PostSetChannel(Chs);
2016   finally
2017     Chs.Free;
2018   end;
2019 end;
2020
2021 procedure TfrmSender.mnGotoVoteClick(Sender: TObject);
2022 begin
2023   ShellExecute(Handle, 'open', PChar(Pref.VotePage), nil, nil, SW_SHOW);
2024 end;
2025
2026 procedure TfrmSender.tabChannelMouseMove(Sender: TObject;
2027   Shift: TShiftState; X, Y: Integer);
2028 var Index: integer;
2029     Ch: String;
2030 begin
2031   with tabChannel do begin
2032     Index := IndexOfTabAt(X, Y);
2033     Ch := Tabs[Index];
2034     Hint := Ch + ': ' + IntToStr(ChannelList.Channel[Ch].Members) + '\90l';
2035   end;
2036 end;
2037
2038 procedure TfrmSender.mnGoToHelpClick(Sender: TObject);
2039 begin
2040   ShellExecute(Handle, 'open', PChar(Pref.HelpPage), nil, nil, SW_SHOW);
2041 end;
2042
2043 procedure TfrmSender.tabChannelMouseDown(Sender: TObject;
2044   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
2045 var Index: integer;
2046 begin
2047   with tabChannel do begin
2048     Index := IndexOfTabAt(X, Y);
2049     if Index = -1 then Exit; //\83^\83u\82ª\82È\82¢\82Ì\82Å\83h\83\89\83b\83O\82Å\82«\82È\82¢
2050     if Button = mbLeft then begin
2051       FDragTabIndex := Index; //\83h\83\89\83b\83O\82·\82é\83^\83u\82Ì\83C\83\93\83f\83b\83N\83X\82ð\95Û\91
2052       BeginDrag(False);
2053       FDragTabDest := -1;     //\83h\83\89\83b\83O\98g\90ü\95`\89æ\83t\83\89\83O\83N\83\8a\83A\82Ì\82½\82ß
2054     end;
2055   end;
2056 end;
2057
2058 procedure TfrmSender.tabChannelDragOver(Sender, Source: TObject; X,
2059   Y: Integer; State: TDragState; var Accept: Boolean);
2060 var TargetRect: TRect;
2061     OldDest: integer;
2062 begin
2063   Accept := Source = tabChannel;
2064   if not Accept then Exit;
2065   with tabChannel do begin
2066     OldDest := FDragTabDest;
2067     FDragTabDest := IndexOfTabAt(X, Y);
2068     if FDragTabDest = -1 then begin
2069       Accept := false; //\82±\82Ì\8fê\8d\87\82Í\83h\83\8d\83b\83v\82ð\94F\82ß\82È\82¢
2070       Exit;
2071     end;
2072     with Canvas do begin
2073       Pen.Mode := pmNot;
2074       Pen.Width := 3;
2075     end;
2076     if (OldDest <> FDragTabDest) and (OldDest >= 0) then begin
2077       //\88È\91O\82Ì\98g\90ü\8fÁ\8b\8e
2078       TargetRect := TabRect(OldDest);
2079       with Canvas do begin
2080         Brush.Style := bsClear;
2081         Rectangle(TargetRect.Left, TargetRect.Top,
2082                   TargetRect.Right, TargetRect.Bottom);
2083       end;
2084     end;
2085     if (OldDest <> FDragTabDest) then begin
2086       //\90V\82µ\82¢\98g\90ü\95`\89æ
2087       TargetRect := TabRect(FDragTabDest);
2088       with Canvas do begin
2089         Brush.Style := bsClear;
2090         Rectangle(TargetRect.Left, TargetRect.Top,
2091                   TargetRect.Right, TargetRect.Bottom);
2092       end;
2093     end;
2094   end;
2095 end;
2096
2097 procedure TfrmSender.tabChannelDragDrop(Sender, Source: TObject; X,
2098   Y: Integer);
2099 var DestIndex: integer;
2100 begin
2101   with tabChannel do begin
2102     DestIndex := IndexOfTabAt(X, Y);
2103     Tabs.Move(FDragTabIndex, DestIndex);
2104   end;
2105 end;
2106
2107 procedure TfrmSender.tabChannelEndDrag(Sender, Target: TObject; X,
2108   Y: Integer);
2109 begin
2110   //\8b­\90§\93I\82É\83^\83u\82ð\8dÄ\95`\89æ\82³\82¹\82é\81B\98g\90ü\8fÁ\82µ\91Î\8dô
2111   tabChannel.Tabs.BeginUpdate;
2112   tabChannel.Tabs.EndUpdate;
2113 end;
2114
2115 procedure TfrmSender.cbxTargetGhostDrawItem(Control: TWinControl;
2116   Index: Integer; Rect: TRect; State: TOwnerDrawState);
2117 var AlignRight: boolean;
2118     w: integer;
2119 begin
2120   //\83S\81[\83X\83g\91I\91ð\83{\83b\83N\83X\82Ì\83I\81[\83i\81[\83h\83\8d\81[
2121   with cbxTargetGhost do begin
2122     AlignRight := false;
2123     if Pref.HideGhosts and not FTargetGhostExpand and
2124        (Index = Items.Count-1) and (Index > 0) then
2125     begin
2126       // \81u\82·\82×\82Ä\95\\8e¦\81v
2127       Canvas.Font.Color := clWindowText;
2128       Canvas.Font.Style := [];
2129       AlignRight := true;
2130     end else if (Index > 0) then
2131     begin
2132       // \83S\81[\83X\83g\96¼\82ð\91I\91ð
2133       if SakuraSeeker.ProcessByName[Items[Index]] = nil then
2134         Canvas.Font.Color := clRed // \91\8dÝ\82µ\82È\82¢\83S\81[\83X\83g
2135       else
2136         Canvas.Font.Color := clBlue; // \91\8dÝ\82·\82é\83S\81[\83X\83g
2137       Canvas.Font.Style := [fsBold];
2138     end else
2139     begin
2140       Canvas.Font.Color := clWindowText;
2141       Canvas.Font.Style := [];
2142     end;
2143     if odSelected in State then
2144       Canvas.Font.Color := clHighlightText;
2145     // \95`\89æ
2146     if AlignRight then
2147     begin
2148       w := Canvas.TextWidth(cbxTargetGhost.Items[Index]);
2149       Canvas.TextRect(Rect, Rect.Right - w, Rect.Top,
2150         cbxTargetGhost.Items[Index]);
2151     end else
2152       Canvas.TextRect(Rect, Rect.Left, Rect.Top,
2153         cbxTargetGhost.Items[Index]);
2154   end;
2155 end;
2156
2157 procedure TfrmSender.FormCloseQuery(Sender: TObject;
2158   var CanClose: Boolean);
2159 begin
2160   if not Pref.ConfirmOnExit then Exit;
2161   if MessageDlg('SSTP Bottle Client\82ð\8fI\97¹\82µ\82Ü\82·', mtConfirmation,
2162                 mbOkCancel, 0) = mrCancel then CanClose := false;
2163 end;
2164
2165 procedure TfrmSender.UpdateIfGhostBox;
2166 var
2167   Selected: String;
2168   i: integer;
2169   HiddenCount: integer;
2170 begin
2171   cbxTargetGhost.DropDownCount := Pref.GhostDropDownCount;
2172   Selected := cbxTargetGhost.Text;
2173   HiddenCount := 0;
2174   with cbxTargetGhost do begin
2175     Items.BeginUpdate;
2176     Items.Clear;
2177     Items.Add(ChannelDefault);
2178     for i := 0 to SakuraSeeker.Count-1 do begin
2179       // \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é
2180       if Length(SakuraSeeker[i].Name) = 0 then Continue;
2181       if Pref.HideGhosts and not FTargetGhostExpand then
2182         if Pref.VisibleGhostsList.IndexOf(SakuraSeeker[i].Name) < 0 then
2183         begin
2184           Inc(HiddenCount);
2185           Continue;
2186         end;
2187       if cbxTargetGhost.Items.IndexOf(SakuraSeeker[i].Name) < 0 then
2188         cbxTargetGhost.Items.Add(SakuraSeeker[i].Name);
2189     end;
2190     cbxTargetGhost.ItemIndex := 0;
2191     if (Length(Selected) > 0) and (Selected <> ChannelDefault) then begin
2192       with cbxTargetGhost do begin
2193         for i := 1 to Items.Count-1 do begin
2194           if Items[i] = Selected then
2195             ItemIndex := i;
2196         end;
2197         //\83S\81[\83X\83g\82ª\93Ë\91R\91\8dÝ\82µ\82È\82­\82È\82Á\82½\8fê\8d\87\91Î\8dô
2198         if ItemIndex = 0 then begin
2199           Items.Add(Selected);
2200           ItemIndex := Items.Count - 1;
2201         end;
2202       end;
2203     end;
2204     if Pref.HideGhosts and not FTargetGhostExpand then
2205       Items.Add(Format('\82·\82×\82Ä(%d)...', [HiddenCount]));
2206     Items.EndUpdate;
2207   end;
2208 end;
2209
2210 procedure TfrmSender.HTTPFailure(Sender: TObject);
2211 begin
2212   SysUtils.Beep;
2213   Beep;
2214   ShowHintLabel('SSTP Bottle\83T\81[\83o\82Æ\82Ì\90Ú\91±\82É\8e¸\94s\82µ\82Ü\82µ\82½', WarningColor);
2215   ShowMessage('SSTP Bottle\83T\81[\83o\82Æ\82Ì\90Ú\91±\82É\8e¸\94s\82µ\82Ü\82µ\82½'#13#10 +
2216     (Sender as THTTPDownloadThread).LastErrorMessage);
2217   Connecting := false;
2218 end;
2219
2220 procedure TfrmSender.actPrevGhostExecute(Sender: TObject);
2221 var i: integer;
2222 begin
2223   SakuraSeeker.BeginDetect;
2224   UpdateIfGhostBox;
2225   i := cbxTargetGhost.ItemIndex;
2226   Dec(i);
2227   if i <= -1 then
2228   begin
2229     i := cbxTargetGhost.Items.Count-1;
2230     if Pref.HideGhosts and not FTargetGhostExpand then
2231       i := i - 1;
2232   end;
2233   cbxTargetGhost.ItemIndex := i;
2234   cbxTargetGhostChange(self);
2235 end;
2236
2237 procedure TfrmSender.actNextGhostExecute(Sender: TObject);
2238 var i: integer;
2239 begin
2240   SakuraSeeker.BeginDetect;
2241   UpdateIfGhostBox;
2242   i := cbxTargetGhost.ItemIndex;
2243   Inc(i);
2244   if Pref.HideGhosts and not FTargetGhostExpand then
2245   begin
2246     if  i > cbxTargetGhost.Items.Count-2 then i := 0;
2247   end else
2248   begin
2249     if  i > cbxTargetGhost.Items.Count-1 then i := 0;
2250   end;
2251   cbxTargetGhost.ItemIndex := i;
2252   cbxTargetGhostChange(self);
2253 end;
2254
2255 procedure TfrmSender.actResetGhostExecute(Sender: TObject);
2256 begin
2257   cbxTargetGhost.ItemIndex := 0; // (CH\90\84\8f§)\82É\96ß\82·
2258   FTargetGhostExpand := false;
2259   if Visible then memScript.SetFocus;
2260   cbxTargetGhostChange(self);
2261 end;
2262
2263 procedure TfrmSender.timDisconnectCheckTimerTimer(Sender: TObject);
2264 begin
2265   if (IdSlpp20.LastReadTimeInterval > Pref.ReconnectWait * 60000) then begin
2266     SysUtils.Beep;
2267     frmLog.AddCurrentSystemLog('SYSTEM', 'SSTP Bottle\83T\81[\83o\82Æ\82Ì\90Ú\91±\82ª\83^\83C\83\80\83A\83E\83g\82µ\82Ü\82µ\82½');
2268     if IdSlpp20.Connected then IdSlpp20.Disconnect;
2269   end;
2270   if not IdSlpp20.Connected then begin
2271     if Added then begin
2272       Slpp20Disconnect(self); //\82È\82º\82©Disconnect\83C\83x\83\93\83g\82ª\8bN\82±\82ç\82¸\82É\90Ø\92f\82µ\82½\8fê\8d\87
2273     end else begin
2274       //\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
2275       //\82½\82¾\82µ\89ñ\90\94\90§\8cÀ\82 \82è
2276       RetryBeginConnect;
2277     end;
2278   end;
2279 end;
2280
2281 procedure TfrmSender.RetryBeginConnect;
2282 begin
2283   if FBeginConnectFailCount < 3 then begin
2284     // \90Ø\92f\82³\82ê\82Ä\82¢\82ê\82Î\8dÄ\90Ú\91±
2285     FAutoAddAfterGetChannel := true;
2286     BeginConnect;
2287   end else if FBeginConnectFailCount = 3 then begin
2288     frmLog.AddCurrentSystemLog('SYSTEM', '\8dÄ\90Ú\91±\8e©\93®\83\8a\83g\83\89\83C\82ð\92\86\8e~\82µ\82Ü\82·');
2289     frmMessageBox.ShowMessage(
2290       'SSTP Bottle\83T\81[\83o\82É\90Ú\91±\82Å\82«\82Ü\82¹\82ñ\81B'#13#10+
2291       '\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+
2292       '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'
2293     );
2294     Inc(FBeginConnectFailCount);
2295   end;
2296 end;
2297
2298 procedure TfrmSender.actDownloadLogExecute(Sender: TObject);
2299 var BottleLog: TBottleLogList;
2300     Title: String;
2301     Cond: TBottleLogDownloadCondition;
2302     NewIndex: integer;
2303   function TimeStr(Mins: integer): String;
2304   var day, hour, min: integer;
2305   begin
2306     day := Mins div (60 * 24);
2307     hour := (Mins div 60) mod 24;
2308     min := Mins mod 60;
2309     Result := '';
2310     if day  > 0 then Result := Result + Format('%d\93ú', [day]);
2311     if hour > 0 then Result := Result + Format('%d\8e\9e\8aÔ', [hour]);
2312     if (min  > 0) or (Result = '') then Result := Result + Format('%d\95ª', [min]);
2313   end;
2314 begin
2315   Application.CreateForm(TfrmLogDownload, frmLogDownload);
2316   try
2317     if frmLogDownload.Execute then begin
2318       with frmLogDownload do begin
2319         if IsRange then begin
2320           if CompareDate(DateLo, DateHi) = 0 then
2321             Title := FormatDateTime('yy-mm-dd', DateLo)
2322           else
2323             Title := FormatDateTime('yy-mm-dd', DateLo) + ' - ' + FormatdateTime('yy-mm-dd', DateHi);
2324         end else begin
2325           Title := Format('\89ß\8b\8e%s', [TimeStr(RecentCount)]);
2326         end;
2327         if Channel <> '' then Title := Title + '(' + Channel + ')';
2328       end;
2329       BottleLog := TBottleLogList.Create(Title);
2330       try
2331         BottleLog.OnLoaded := frmLog.LogLoaded;
2332         BottleLog.OnLoadFailure := frmLog.LogLoadFailure;
2333         BottleLog.OnLoadWork := frmLog.LogLoadWork;
2334         with frmLogDownload do begin
2335           Cond.IsRange := IsRange;
2336           Cond.RecentCount := RecentCount;
2337           Cond.DateLo := DateLo;
2338           Cond.DateHi := DateHi;
2339           Cond.MinVote := MinVote;
2340           Cond.MinAgree := MinAgree;
2341           Cond.Channel := Channel;
2342         end;
2343         BottleLog.LoadFromWeb(Cond);
2344       except
2345         FreeAndNil(BottleLog);
2346       end;
2347       if BottleLog <> nil then begin
2348         NewIndex := frmLog.BottleLogList.Add(BottleLog);
2349         frmLog.UpdateTab;
2350         frmLog.tabBottleLog.TabIndex := NewIndex;
2351         frmLog.UpdateWindow;
2352       end;
2353     end;
2354   finally
2355     frmLogDownload.Release;
2356   end;
2357 end;
2358
2359 function TfrmSender.BuildMenuConditionCheck(const IfGhost,
2360   Ghost: String): boolean;
2361 var i: integer;
2362     Cond: String;
2363 begin
2364   i := 0;
2365   Result := true;
2366   repeat
2367     Cond := Token(IfGhost, ',', i);
2368     if Cond <> '' then begin
2369       if Cond[1] = '!' then begin
2370         Cond := Copy(Cond, 2, High(integer));
2371         if Cond = Ghost then Result := false;
2372       end else begin
2373         if Cond <> Ghost then Result := false;
2374       end;
2375     end;
2376     Inc(i);
2377   until Cond = '';
2378 end;
2379
2380 procedure TfrmSender.BuildMenu(Root: TMenuItem; Simple: boolean);
2381 var i, j, k, count: integer;
2382     ConstData: TScriptConst;
2383     Menu1, Menu2: TMenuItem;
2384     Ghost: String;
2385 begin
2386   // Simple = false \82Ì\8fê\8d\87\82Í\83\81\83j\83\85\81[\82ð\8a®\91S\82É\8dÄ\8d\\92z\82·\82é\81B
2387   // 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
2388   if cbxTargetGhost.ItemIndex > 0 then Ghost := cbxTargetGhost.Text
2389   else if FNowChannel <> '' then Ghost := ChannelList.Channel[FNowChannel].Ghost;
2390
2391   // \8aù\91\82Ì\83\81\83j\83\85\81[\8dí\8f\9c
2392   if Simple then begin
2393     // IfGhost\8fð\8c\8f\95t\82«\83\81\83j\83\85\81[\82Ì\82Ý\8dí\8f\9c
2394     for i := Root.Count-1 downto 0 do begin
2395       if ScriptConstList.GetMenuByID(Root.Items[i].Tag).IfGhost <> '' then
2396         Root.Items[i].Free;
2397     end;
2398   end else begin
2399     // \91S\95\94\8dí\8f\9c
2400     for i := Root.Count-1 downto 0 do begin
2401       Root.Items[i].Free;
2402     end;
2403   end;
2404
2405   count := -1;
2406   for i := 0 to ScriptConstList.Count-1 do begin
2407     for j := 0 to ScriptConstList[i].Count-1 do begin
2408       // \83S\81[\83X\83g\88á\82¢\82Ì\8fê\8d\87\82Í\83X\83L\83b\83v
2409       if not BuildMenuConditionCheck(ScriptConstList[i][j].IfGhost, Ghost) then Continue;
2410       Inc(count);
2411       // 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
2412       if Simple and (count < Root.Count) then
2413         if (Root.Items[count].Tag = ScriptConstList[i][j].ID) then begin
2414           Continue;
2415         end;
2416
2417       Menu1 := TMenuItem.Create(Root);
2418       Menu1.Caption := ScriptConstList[i][j].Caption;
2419       Menu1.Hint    := ScriptConstList[i][j].Caption;
2420       Menu1.AutoHotkeys := maManual;
2421       Menu1.Tag := ScriptConstList[i][j].ID;
2422       Menu1.OnClick := mnConstGroupClick;
2423
2424       if not Simple then begin
2425         Root.Add(Menu1);
2426       end else begin
2427         if count < Root.Count-1 then
2428           Root.Insert(count, Menu1)
2429         else
2430           Root.Add(Menu1);
2431       end;
2432
2433       Menu1.Enabled := ScriptConstList[i][j].Count > 0;
2434       for k := 0 to ScriptConstList[i][j].Count-1 do begin
2435         ConstData := ScriptConstList[i][j][k];
2436         Menu2 := TMenuItem.Create(Root);
2437         Menu2.Caption := ConstData.Caption;
2438         Menu2.Hint := ConstData.ConstText;
2439         // if ConstData.ShortCut <> 0 then Menu2.Hint := Menu2.Hint
2440         //   + ' (' + ShortCutToText(ConstData.ShortCut) + ')';
2441         // \83T\81[\83t\83B\83X\83v\83\8c\83r\83\85\81[\8eÀ\8c»\82Ì\82½\82ß\8fã\8dí\8f\9c
2442         Menu2.ShortCut := ConstData.ShortCut;
2443         Menu2.OnClick := mnConstClick;
2444         Menu2.AutoHotkeys := maManual;
2445         Menu2.Tag := ConstData.ID;
2446         if (k mod 15 = 0) and (k > 0) then Menu2.Break := mbBarBreak;
2447         Menu1.Add(Menu2);
2448       end;
2449     end;
2450   end;
2451 end;
2452
2453 procedure TfrmSender.cbxTargetGhostChange(Sender: TObject);
2454 begin
2455   // \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
2456   if Pref.HideGhosts and not FTargetGhostExpand then
2457   begin
2458     with cbxTargetGhost do
2459     begin
2460       // \88ê\94Ô\89º\82Ì\83A\83C\83e\83\80\82ª\91I\91ð\82³\82ê\82½\82Æ\82«\81B
2461       // \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
2462       if (ItemIndex = Items.Count-1) and (ItemIndex > 0) then
2463       begin
2464         FTargetGhostExpand := true;
2465         UpdateIfGhostBox;
2466         ItemIndex := 0;
2467         DroppedDown := true;
2468       end;
2469     end;
2470   end;
2471   // \92è\8c^\8bå\83\81\83j\83\85\81[\82ð\8dÄ\8d\\92z
2472   ConstructMenu(true);
2473   // \83v\83\8c\83r\83\85\81[\82ª\82 \82é\8fê\8d\87\82Í\83v\83\8c\83r\83\85\81[
2474   EditorPreview;
2475 end;
2476
2477 procedure TfrmSender.PlaySound(const FileName: String);
2478 begin
2479   if Pref.SilentWhenHidden and not Application.ShowMainForm then Exit;
2480   try
2481     MediaPlayer.FileName := FileName;
2482     MediaPlayer.Open;
2483     MediaPlayer.Play;
2484   except
2485     on E: EMCIDeviceError do begin
2486       ShowMessage('\83T\83E\83\93\83h\8dÄ\90\83G\83\89\81[:'#13#10 + FileName + #13#10#13#10 + E.Message);
2487     end;
2488   end;
2489 end;
2490
2491 procedure TfrmSender.actFMOExplorerExecute(Sender: TObject);
2492 begin
2493   try
2494     if not Assigned(frmFMOExplorer) then
2495       Application.CreateForm(TfrmFMOExplorer, frmFMOExplorer);
2496   except
2497     on E: Exception do
2498       ShowMessage('FMO\83G\83N\83X\83v\83\8d\81[\83\89\82ð\95\\8e¦\82Å\82«\82Ü\82¹\82ñ\81B'#13#10#13#10 +
2499         E.Message);
2500   end;
2501   frmFMOExplorer.Show;
2502 end;
2503
2504 procedure TfrmSender.SaveChainRuleList;
2505 var Str: TStringList;
2506 begin
2507   Str := TStringList.Create;
2508   try
2509     Str.Text := ComponentToString(BottleChainRuleList);
2510     Str.SaveToFile(ExtractFileDir(Application.ExeName)+'\rule.txt');
2511   finally
2512     Str.Free;
2513   end;
2514 end;
2515
2516 procedure TfrmSender.BottleSstpResendEnd(Sender: TObject; MID: String);
2517 begin
2518   frmLog.SetBottleState(MID, lsOpened);
2519 end;
2520
2521 procedure TfrmSender.BottleSstpResendTrying(Sender: TObject; MID: String);
2522 begin
2523   frmLog.SetBottleState(MID, lsPlaying);
2524 end;
2525
2526 procedure TfrmSender.actInsertCueExecute(Sender: TObject);
2527 var InsertItem: TLogItem;
2528     i, errCount, Res: integer;
2529     Log: TBottleLogList;
2530     ErrorMes: String; // \83X\83N\83\8a\83v\83g\82Ì\83G\83\89\81[\82Ì\93à\97e
2531 begin
2532   if FBottleSstp.CueCount > 0 then begin
2533     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'+
2534       '\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+
2535       '\90V\92\85\83\81\83b\83Z\81[\83W\82Í\82»\82Ì\8cã\82É\8dÄ\90\82³\82ê\82Ü\82·\81B', [FBottleSstp.CueCount]),
2536       mtWarning, mbOkCancel, 0) = mrCancel then Exit;
2537   end;
2538   FBottleSstp.Clear;
2539   frmLog.AllBottleOpened;
2540   if frmLog.lvwLog.Selected = nil then Exit;
2541   Log := frmLog.SelectedBottleLog;
2542   if Log = nil then Exit;
2543   FBottleSSTP.OnResendCountChange := nil;
2544   errCount := 0;
2545   for i := frmLog.lvwLog.Selected.Index downto 0 do begin
2546     if (Log[i] as TLogItem).LogType <> ltBottle then Continue;
2547     InsertItem := TLogItem.Create(Log[i] as TLogItem);
2548     try
2549       InsertItem.Script := ScriptTransForSSTP(InsertItem.Script, ErrorMes);
2550       if ErrorMes <> '' then
2551       begin
2552         Res := MessageDlg('\83X\83N\83\8a\83v\83g\82É\96â\91è\82ª\82 \82é\89Â\94\\90«\82ª\82 \82è\82Ü\82·\81B' +
2553           '\8dÄ\90\82µ\82Ü\82·\82©?'#13#10 + ErrorMes, mtWarning,
2554           mbYesNoCancel, 0);
2555         if Res = mrNo then
2556           raise Exception.Create('Script Syntax Error')
2557         else if Res = mrCancel then
2558         begin
2559           InsertItem.Free;
2560           FBottleSstp.Clear;
2561           frmLog.AllBottleOpened;
2562           Break;
2563         end;
2564       end;
2565       if InsertItem.Ghost = '' then begin
2566         if ChannelList.Channel[InsertItem.Channel] <> nil then
2567         InsertItem.Ghost := ChannelList.Channel[InsertItem.Channel].Ghost;
2568       end;
2569       FBottleSSTP.Push(InsertItem);
2570       frmLog.SetBottleState(InsertItem.MID, lsUnopened);
2571     except
2572       InsertItem.Free;
2573       Inc(errCount);
2574     end;
2575   end;
2576   if errCount > 0 then
2577     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]));
2578   FBottleSSTP.OnResendCountChange := BottleSstpResendCountChange;
2579   BottleSstpResendCountChange(self);
2580   frmLog.lvwLog.Invalidate;
2581 end;
2582
2583 function TfrmSender.ScriptTransForSSTP(const Script: String;
2584   out Error: String): String;
2585 var TransOpt: TScriptTransOptions;
2586 begin
2587   if Pref.NoTransURL then
2588     TransOpt := [toConvertURL, toNoChoice, toWaitScriptEnd]
2589   else
2590     TransOpt := [toConvertURL, toWaitScriptEnd];
2591   if Pref.IgnoreFrequentYenS then TransOpt := TransOpt + [toIgnoreFrequentYenS];
2592   if Pref.FixMessySurface then TransOpt := TransOpt + [toFixMessySurface];
2593   if Pref.HUTagTo01Tag then TransOpt := TransOpt + [toHUTagTo01Tag];
2594   Result := Script;
2595   Error := DoTrans(Result, TransOpt);
2596 end;
2597
2598 procedure TfrmSender.FormResize(Sender: TObject);
2599 var w: integer;
2600 begin
2601   // \83G\83f\83B\83^\81[\95\94\95ª\82Ì\83\8f\81[\83h\83\89\83b\83v\95\9d\82ð\92²\90®\82·\82é
2602   if memScript.ColWidth <> 0 then
2603   begin
2604     with memScript do
2605     begin
2606       w := Width - LeftMargin - ColWidth div 2;
2607       if ScrollBars in [ssVertical, ssBoth] then
2608         w := w - GetSystemMetrics(SM_CYVSCROLL);
2609       WrapOption.WrapByte := w div ColWidth;
2610     end;
2611   end;
2612 end;
2613
2614 procedure TfrmSender.actCopyExecute(Sender: TObject);
2615 begin
2616   memScript.CopyToClipboard;
2617 end;
2618
2619 procedure TfrmSender.actPasteExecute(Sender: TObject);
2620 begin
2621   memScript.PasteFromClipboard;
2622 end;
2623
2624 procedure TfrmSender.actCutExecute(Sender: TObject);
2625 begin
2626   memScript.CutToClipboard;
2627 end;
2628
2629 procedure TfrmSender.actSelectAllExecute(Sender: TObject);
2630 begin
2631   memScript.SelectAll;
2632 end;
2633
2634 procedure TfrmSender.actUndoExecute(Sender: TObject);
2635 begin
2636   memScript.Undo;
2637 end;
2638
2639 procedure TfrmSender.actRedoExecute(Sender: TObject);
2640 begin
2641   memScript.Redo;
2642 end;
2643
2644 function TfrmSender.IsSurfaceTag(const Script: String;
2645   var ID: integer): boolean;
2646 begin
2647   Result := false;
2648   if SsParser.Match(Script, '\s%d') = 3 then
2649   begin
2650     Result := true;
2651     ID := Ord(Script[3]) - Ord('0')
2652   end
2653   else if (Length(Script) > 0) and (SsParser.Match(Script, '\s[%D]') = Length(Script)) then
2654   begin
2655     Result := true;
2656     ID := StrToIntDef(SsParser.GetParam(Script, 1), 0);
2657   end;
2658 end;
2659
2660 procedure TfrmSender.memScriptMouseMove(Sender: TObject;
2661   Shift: TShiftState; X, Y: Integer);
2662 var id: integer;
2663     token: String;
2664 begin
2665   // \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[
2666   if not Pref.SurfacePreviewOnScriptPoint then
2667     Exit;
2668   token := memScript.TokenStringFromPos(Point(X, Y));
2669   if IsSurfaceTag(token, id) then
2670   begin
2671     DoSurfacePreview(id, spEditor);
2672   end else
2673   begin
2674     frmSurfacePreview.HideAway;
2675   end;
2676 end;
2677
2678 procedure TfrmSender.DoSurfacePreview(Surface: integer;
2679   const Kind: TSurfacePreviewType);
2680 var Ghost: String;
2681     Bmp: TBitmap;
2682     CPos: TPoint;
2683 begin
2684   if cbxTargetGhost.ItemIndex > 0 then
2685     ghost := cbxTargetGhost.Text
2686   else if FNowChannel <> '' then
2687     ghost := ChannelList.Channel[FNowChannel].Ghost;
2688
2689   // \93ñ\8fd\95\\8e¦\82Ì\97}\90§
2690   if (FVisiblePreviewGhost = Ghost) and (FVisiblePreviewSurface = Surface) and
2691     not (frmSurfacePreview.IsHidden) then
2692   begin
2693     Exit;
2694   end;
2695
2696   if ghost <> '' then
2697   begin
2698     Bmp := TBitmap.Create;
2699     try
2700       if Spps.TryGetImage(ghost, Surface, Bmp) then
2701       begin
2702         case Kind of
2703           spHint:
2704             CPos := GetSurfacePreviewPositionHint(Bmp.Width, Bmp.Height);
2705           spEditor:
2706             CPos := GetSurfacePreviewPositionScriptPoint(Bmp.Width, Bmp.Height);
2707           else
2708             CPos := Point(0, 0);
2709         end;
2710         frmSurfacePreview.ShowPreview(Bmp, CPos.X, CPos.Y);
2711         FVisiblePreviewGhost := Ghost;
2712         FVisiblePreviewSurface := Surface;
2713       end else
2714         frmSurfacePreview.HideAway;
2715     finally
2716       Bmp.Free;
2717     end;
2718   end;
2719 end;
2720
2721 function TfrmSender.GetSurfacePreviewPositionHint(w, h: integer): TPoint;
2722 {var MousePoint: TPoint;
2723     MenuRect: TRect;}
2724 begin
2725   // \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é
2726   // \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Å
2727   // \88Ä\8aO\82â\82â\82±\82µ\82¢
2728   {GetCursorPos(MousePoint);
2729   OutputDebugString(PChar(IntToStr(FVisibleMenuItem.Count)));
2730   //if GetMenuItemRect(Self.Handle, FVisibleMenuItem.Items[0].Handle, 1, MenuRect) then ;
2731   Result := Point(MenuRect.Left-10, MenuRect.Top-10);}
2732   Result := GetSurfacePreviewPositionScriptPoint(w, h);
2733 end;
2734
2735 function TfrmSender.GetSurfacePreviewPositionScriptPoint(w, h: integer): TPoint;
2736 var MousePoint: TPoint;
2737 begin
2738   GetCursorPos(MousePoint);
2739   case Pref.SurfacePreviewOnScriptPointPosition of
2740     spspMainWindowRight:
2741       Result := Point(Self.Left + Self.Width, MousePoint.Y);
2742   else
2743       Result := Point(Self.Left - w, MousePoint.Y);
2744   end;
2745 end;
2746
2747 procedure TfrmSender.mnConstGroupClick(Sender: TObject);
2748 begin
2749   if (Sender is TMenuItem) then
2750     FVisibleMenuItem := Sender as TMenuItem;
2751 end;
2752
2753 procedure TfrmSender.actRecallScriptBufferExecute(Sender: TObject);
2754 begin
2755   if FScriptBuffer.Count = 0 then
2756     Exit;
2757   memScript.Lines.Assign(FScriptBuffer[0] as TStringList);
2758   memScriptChange(Self);
2759   ShowHintLabel('\83X\83N\83\8a\83v\83g\82ð\8cÄ\82Ñ\8fo\82µ\82Ü\82µ\82½');
2760 end;
2761
2762 procedure TfrmSender.EditorPreview;
2763 var Ghost, Script: String;
2764 begin
2765   if frmEditorTalkShow <> nil then
2766     if frmEditorTalkShow.Visible then
2767     begin
2768       Script := StringReplace(GetScriptText, #13#10, '', [rfReplaceAll]);
2769       Ghost := '';
2770       if cbxTargetGhost.ItemIndex > 0 then
2771         Ghost := cbxTargetGhost.Text
2772       else if FNowChannel <> '' then
2773         Ghost := ChannelList.Channel[FNowChannel].Ghost;
2774       frmEditorTalkShow.TalkShowFrame.View(Script, Ghost);
2775     end;
2776 end;
2777
2778 procedure TfrmSender.actEditorPreviewExecute(Sender: TObject);
2779 begin
2780   if frmEditorTalkShow <> nil then
2781     frmEditorTalkShow.Show
2782   else
2783   begin
2784     Application.CreateForm(TfrmEditorTalkShow, frmEditorTalkShow);
2785     frmEditorTalkShow.Show;
2786   end;
2787   EditorPreview;
2788 end;
2789
2790 // \83v\83\89\83O\83C\83\93\83\8a\83Z\83b\83g
2791 procedure TfrmSender.actResetPluginsExecute(Sender: TObject);
2792 begin
2793   Spps.ClearImagePool;
2794   Spps.LoadFromDirectory(FSppDir);
2795 end;
2796
2797 procedure TfrmSender.IdSLPP20Connect(Sender: TObject);
2798 begin
2799   ShowHintLabel('SSTP Bottle\83T\81[\83o\82ª\8c©\82Â\82©\82è\82Ü\82µ\82½');
2800 end;
2801
2802 // \83X\83N\83\8a\83v\83g\92\86\82Ì\83^\83O\82ð\92u\8a·\82·\82é
2803 // \83T\83C\83Y\89Â\95Ï\82Ì\94z\97ñ\83p\83\89\83\81\81[\83^\94Å
2804 function TfrmSender.TagReplace(Script: String; Before,
2805   After: array of String): String;
2806 var BeforeList, AfterList: TStringList;
2807     i: integer;
2808 begin
2809   BeforeList := TStringList.Create;
2810   AfterList  := TStringList.Create;
2811   try
2812     for i := Low(Before) to High(Before) do
2813       BeforeList.Add(Before[i]);
2814     for i := Low(After) to High(After) do
2815       AfterList.Add(After[i]);
2816     Result := TagReplace(Script, BeforeList, AfterList);
2817   finally
2818     BeforeList.Free;
2819     AfterList.Free;
2820   end;
2821 end;
2822
2823 // \83X\83N\83\8a\83v\83g\92\86\82Ì\83^\83O\82ð\92u\8a·\82·\82é
2824 // StringReplace\82Æ\88á\82Á\82Ä\90³\8am\82É\83^\83O\82É\83}\83b\83`\82µ\81A
2825 // \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¢)
2826 function TfrmSender.TagReplace(Script: String; Before,
2827   After: TStrings): String;
2828 var i, j: integer;
2829     Flag, OldLeaveEscape, OldEscapeInvalidMeta: boolean;
2830     OldStr: String;
2831 begin
2832   Result := '';
2833   with SsParser do
2834   begin
2835     OldStr := InputString;
2836     OldLeaveEscape := LeaveEscape;
2837     OldEscapeInvalidMeta := EscapeInvalidMeta;
2838     LeaveEscape := true;
2839     EscapeInvalidMeta := false;
2840     InputString := Script;
2841   end;
2842   for i := 0 to SsParser.Count-1 do
2843   begin
2844     Flag := false;
2845     for j := 0 to Before.Count-1 do
2846     begin
2847       if (SsParser.MarkUpType[i] = mtTag) and (SsParser[i] = Before[j]) then
2848       begin
2849         Flag := true;
2850         Result := Result + After[j];
2851       end;
2852     end;
2853     if not Flag then
2854       Result := Result + SsParser[i];
2855   end;
2856   with SsParser do
2857   begin
2858     LeaveEscape := OldLeaveEscape;
2859     EscapeInvalidMeta := OldEscapeInvalidMeta;
2860     InputString := OldStr;
2861   end;
2862 end;
2863
2864 // WndProc\82ð\83I\81[\83o\81[\83\89\83C\83h\82µ\82Ä\81AFWM_TaskBarCraeted\82É
2865 // \91Î\89\9e\82·\82é
2866 procedure TfrmSender.WndProc(var Message: TMessage);
2867 begin
2868   if (Message.Msg = FWM_TaskBarCreated) and (FWM_TaskBarCreated <> 0) then
2869   begin
2870     TaskTray.Registered := false; // TTaskTray\82É\83^\83X\83N\83g\83\8c\83C\82ª\8fÁ\82¦\82½\82±\82Æ\82ð
2871                                   // \8bC\82Ã\82©\82¹\82é
2872     ChangeTaskIcon;
2873   end
2874   else
2875     inherited;
2876 end;
2877
2878 // \8c\9f\8dõ\81E\92u\8a·\83_\83C\83A\83\8d\83O\82ð\8f\89\8aú\89»\82µ\82Ä\95\\8e¦
2879 procedure TfrmSender.actReplaceExecute(Sender: TObject);
2880 begin
2881   with ReplaceDialog do
2882   begin
2883     FindText := '';
2884     ReplaceText := '';
2885     Execute;
2886   end;
2887 end;
2888
2889 // \8c\9f\8dõ\81E\92u\8a·
2890 procedure TfrmSender.ReplaceDialogFind(Sender: TObject);
2891 var Opt: TSearchOptions;
2892 begin
2893   Opt := [sfrDown];
2894   if frMatchCase in ReplaceDialog.Options then
2895     Opt := Opt + [sfrMatchCase];
2896   if frWholeWord in ReplaceDialog.Options then
2897     Opt := Opt + [sfrWholeWord];
2898   memScript.Search(ReplaceDialog.FindText, Opt);
2899 end;
2900
2901 // \8c\9f\8dõ\81E\92u\8a·
2902 procedure TfrmSender.ReplaceDialogReplace(Sender: TObject);
2903 var Opt: TSearchOptions;
2904 begin
2905   Opt := [sfrDown];
2906   if frMatchCase in ReplaceDialog.Options then
2907     Opt := Opt + [sfrMatchCase];
2908   if frWholeWord in ReplaceDialog.Options then
2909     Opt := Opt + [sfrWholeWord];
2910   if frReplaceAll in ReplaceDialog.Options then
2911     while memScript.Search(ReplaceDialog.FindText, Opt) do
2912       memScript.SelText := ReplaceDialog.ReplaceText
2913   else
2914   begin
2915     if memScript.Search(ReplaceDialog.FindText, Opt) then
2916       memScript.SelText := ReplaceDialog.ReplaceText
2917   end;
2918 end;
2919
2920 procedure TfrmSender.actSendToEditorExecute(Sender: TObject);
2921 var Log: TLogItem;
2922 begin
2923   if frmLog.lvwLog.Selected = nil then Exit;
2924   Log := frmLog.SelectedBottleLog[frmLog.lvwLog.Selected.Index] as TLogItem;
2925   if Log = nil then Exit;
2926   CopyFromLogToEditor(Log);
2927 end;
2928
2929 procedure TfrmSender.actSendToLogWindowExecute(Sender: TObject);
2930 var Ghost, Script: String;
2931 begin
2932   YenETrans;
2933   Script := StringReplace(GetScriptText, #13#10, '', [rfReplaceAll]);
2934   if cbxTargetGhost.ItemIndex > 0 then
2935     Ghost := cbxTargetGhost.Text
2936   else
2937     Ghost := '';
2938   frmLog.AddCurrentScriptLog('\83N\83\8a\83b\83v', Script, ClipChannel, '', Ghost);
2939   ClearEditor;
2940 end;
2941
2942 procedure TfrmSender.memScriptDragOver(Sender, Source: TObject; X,
2943   Y: Integer; State: TDragState; var Accept: Boolean);
2944 begin
2945   // \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é
2946   if Source is TBottleLogDragObject then
2947     Accept := (Source as TBottleLogDragObject).LogItem.LogType = ltBottle
2948 end;
2949
2950 procedure TfrmSender.memScriptDragDrop(Sender, Source: TObject; X,
2951   Y: Integer);
2952 var Src: TBottleLogDragObject;
2953     Log: TLogItem;
2954 begin
2955   // \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é
2956   if not (Source is TBottleLogDragObject) then
2957     Exit;
2958   if (Source as TBottleLogDragObject).LogItem.LogType <> ltBottle then
2959     Exit;
2960   Src := Source as TBottleLogDragObject;
2961   Log := Src.LogItem;
2962   CopyFromLogToEditor(Log);
2963 end;
2964
2965 procedure TfrmSender.CopyFromLogToEditor(Log: TLogItem);
2966 begin
2967   if Log.LogType <> ltBottle then Exit;
2968   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Ä)
2969   memScript.Lines.Clear;
2970   memScript.Lines.Add(Log.Script);
2971   if Log.Ghost <> '' then
2972   begin
2973     // \83S\81[\83X\83g\96¼\82ð\83{\83b\83N\83X\82É\93ü\82ê\82é
2974     // \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Å
2975     // \96³\97\9d\96î\97\9d\83S\81[\83X\83g\96¼\82ª\83{\83b\83N\83X\82É\93ü\82é
2976     cbxTargetGhost.Items.Add(Log.Ghost);
2977     cbxTargetGhost.ItemIndex := cbxTargetGhost.Items.Count-1;
2978     UpdateIfGhostBox;
2979     cbxTargetGhost.Invalidate;
2980   end else
2981     cbxTargetGhost.ItemIndex := 0; // 'CH\90\84\8f§'\82É\82·\82é
2982   memScript.SetFocus;
2983 end;
2984
2985 procedure TfrmSender.actDeleteLogItemExecute(Sender: TObject);
2986 begin
2987   // \83\8d\83O\83E\83B\83\93\83h\83E\82Ì\8cÂ\95Ê\83\8d\83O\82ð\8dí\8f\9c\82·\82é
2988   if frmLog.SelectedBottleLog = nil then
2989     Exit;
2990   if frmLog.lvwLog.Selected = nil then
2991     Exit;
2992   frmLog.SelectedBottleLog.Delete(frmLog.lvwLog.Selected.Index);
2993   frmLog.UpdateWindow;
2994   frmLog.lvwLogChange(Self, nil, ctState);
2995 end;
2996
2997 procedure TfrmSender.ClearEditor;
2998 var TmpScript: String;
2999     Position: Integer;
3000     DoSaveBuffer: boolean;
3001     SavedScript: TStringList;
3002 begin
3003   // \83X\83N\83\8a\83v\83g\82Ì\83N\83\8a\83A
3004   // \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é
3005   DoSaveBuffer := false;
3006   if FScriptBuffer.Count = 0 then
3007     DoSaveBuffer := true
3008   else if (FScriptBuffer[0] as TStringList).Text <> GetScriptText then
3009     DoSaveBuffer := true;
3010   if (GetScriptText = Pref.DefaultScript) or (GetScriptText = '') then
3011     DoSaveBuffer := false;
3012   if DoSaveBuffer then
3013   begin
3014     SavedScript := TStringList.Create;
3015     SavedScript.Text := GetScriptText;
3016     FScriptBuffer.Insert(0, SavedScript);
3017   end;
3018   if FScriptBuffer.Count >= 4 then
3019     FScriptBuffer.Delete(FScriptBuffer.Count-1);
3020   actRecallScriptBuffer.Enabled := FScriptBuffer.Count > 0;
3021
3022   TmpScript := Pref.DefaultScript;
3023   Position := Pos('|', TmpScript);
3024   if Position < 1 then Position := 1;
3025   TmpScript := StringReplace(TmpScript, '|', '', []);
3026   memScript.Lines.Text := TmpScript;
3027   Sendmessage(memScript.Handle, WM_VSCROLL, SB_LINEUP, 0);
3028   memScript.SelStart := Position-1;
3029
3030   if Visible then memScript.SetFocus;
3031   FScriptModified := false;
3032   memScriptChange(self);
3033 end;
3034
3035 procedure TfrmSender.AppendTextLog(const FileName, Line: String);
3036 var
3037   F: TextFile;
3038 begin
3039   //\91\97\90M\83\8d\83O\95Û\91
3040   try
3041     ForceDirectories(ExtractFileDir(FileName));
3042     AssignFile(F, FileName);
3043     if FileExists(FileName) then
3044       Append(F)
3045     else
3046       Rewrite(F);
3047     WriteLn(F, Line);
3048     Flush(F);
3049     CloseFile(F);
3050   except
3051     on E: Exception do
3052       frmLog.AddCurrentSystemLog('SYSTEM', '\83e\83L\83X\83g\83\8d\83O\95Û\91\82É\8e¸\94s\81F'+E.Message);
3053   end;
3054 end;
3055
3056 procedure TfrmSender.AppendXMLLog(const FileName: String; Args: THeadValue);
3057 var
3058   F: TFileStream;
3059   Buf: String;
3060   P: integer;
3061   Impl: TDomImplementation;
3062   Parser: TXmlToDomParser;
3063   DOM: TdomDocument;
3064 begin
3065   try // Long try block to handle all kinds of exception in this method
3066     if not FileExists(FileName) then
3067     begin
3068       // Create empty XML log file
3069       Impl := TDomImplementation.create(nil);
3070       try
3071         ForceDirectories(ExtractFileDir(FileName));
3072         Parser := TXmlToDomParser.create(nil);
3073         Parser.DOMImpl := Impl;
3074         try
3075           try
3076             DOM := Parser.fileToDom(ExtractFilePath(Application.ExeName)+'xbtl.dat');
3077             with DOM do
3078             begin
3079               documentElement.setAttribute('saved',
3080                 FormatDateTime('yy/mm/dd hh:nn:ss', Now));
3081               documentElement.setAttribute('generator', VersionString);
3082               documentElement.setAttribute('version', '1.0');
3083             end;
3084             // \82±\82ê\82Í\96¾\8e¦\93I\82ÉFree\82µ\82È\82­\82Ä\82æ\82¢
3085             F := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
3086             try
3087               DOM.writeCodeAsShiftJIS(F);
3088             finally
3089               F.Free;
3090             end;
3091           except
3092             frmLog.AddCurrentSystemLog('SYSTEM', 'XML\83\8d\83O\95Û\91\82É\8e¸\94s\82µ\82Ü\82µ\82½');
3093           end;
3094         finally
3095           Parser.DOMImpl.freeDocument(DOM);
3096           Parser.Free;
3097         end;
3098       finally;
3099         Impl.Free;
3100       end;
3101     end;
3102     F := TFileStream.Create(FileName, fmOpenReadWrite or fmShareExclusive);
3103     try
3104       P := -11;
3105       SetLength(Buf, 12);
3106       while P > -100 do
3107       begin
3108         F.Seek(P, soFromEnd);
3109         F.Read(Buf[1], 12);
3110         if Buf = '</bottlelog>' then
3111           Break;
3112         Dec(P);
3113       end;
3114       if P = -100 then
3115         raise Exception.Create(FileName + ' is not a valid XML bottle log file')
3116       else
3117       begin
3118         F.Seek(P, soFromEnd);
3119         Buf := Format('<message mid="%s">', [Args['MID']]);
3120         Buf := Buf + Format('<date>%s</date>', [FormatDateTime('yy/mm/dd hh:nn:ss', Now)]);
3121         Buf := Buf + Format('<channel>%s</channel>', [XMLEntity(Args['Channel'])]);
3122         Buf := Buf + Format('<script>%s</script>', [XMLEntity(Args['Script'])]);
3123         Buf := Buf + '<votes>0</votes><agrees>0</agrees>';
3124         Buf := Buf + Format('<ghost>%s</ghost>', [XMLEntity(Args['IfGhost'])]);
3125         Buf := Buf + '</message>';
3126         Buf := Buf + '</bottlelog>';
3127         F.Write(Buf[1], Length(Buf));
3128       end;
3129     finally
3130       F.Free;
3131     end;
3132   except
3133     on E: Exception do
3134       frmLog.AddCurrentSystemLog('SYSTEM', 'XML\83\8d\83O\95Û\91\82É\8e¸\94s\82µ\82Ü\82µ\82½:'+E.Message);
3135   end;
3136 end;
3137
3138 procedure TfrmSender.memScriptSelectionChange(Sender: TObject;
3139   Selected: Boolean);
3140 var
3141   SelText: String;
3142 begin
3143   SelText := memScript.SelText;
3144   if SelText <> '' then
3145   begin
3146     StatusBar.Panels[PanelBytes].Text := Format('(%d\83o\83C\83g)', [Length(SelText)]);
3147   end else
3148   begin
3149     memScriptChange(Self);
3150   end; 
3151 end;
3152
3153 function TfrmSender.ReplaceSurface(Script: String;
3154   Params: TCollection): String;
3155 var
3156   Flag, OldLeaveEscape, OldEscapeInvalidMeta: boolean;
3157   OldStr, Tag: String;
3158   i, j, k, Cur: integer;
3159   Item: TSurfaceReplaceItem;
3160   Before: TSurfaceReplaceBeforeItem;
3161 begin
3162   Result := '';
3163   Cur := 0;
3164   with SsParser do
3165   begin
3166     OldStr := InputString;
3167     OldLeaveEscape := LeaveEscape;
3168     OldEscapeInvalidMeta := EscapeInvalidMeta;
3169     LeaveEscape := true;
3170     EscapeInvalidMeta := false;
3171     InputString := Script;
3172   end;
3173   for i := 0 to SsParser.Count-1 do
3174   begin
3175     if SsParser.MarkUpType[i] <> mtTag then
3176     begin
3177       Result := Result + SsParser.Str[i];
3178       Continue;
3179     end;
3180     Tag := SsParser.Str[i];
3181     if SsParser.Match(Tag, '\s%d') = 2 then
3182       Cur := Ord(Tag[3]) - Ord('0')
3183     else if SsParser.Match(Tag, '\s[%D]') > 0 then
3184       Cur := StrToInt(SsParser.GetParam(Tag, 1))
3185     else
3186     begin
3187       Result := Result + Tag;
3188       Continue;
3189     end;
3190     Flag := false;
3191     for j := 0 to Params.Count-1 do
3192     begin
3193       Item := Params.Items[j] as TSurfaceReplaceItem;
3194       for k := 0 to Item.Before.Count-1 do
3195       begin
3196         Before := Item.Before.Items[k] as TSurfaceReplaceBeforeItem;
3197         if (Cur >= Before.FromNo) and (Cur <= Before.ToNo) then
3198         begin
3199           Flag := true;
3200           Result := Result + Format('\s[%d]', [Item.After]);
3201           Break;
3202         end;
3203       end;
3204       if Flag then
3205         Break;
3206     end;
3207     if not Flag then
3208       Result := Result + Tag;
3209   end;
3210   with SsParser do
3211   begin
3212     LeaveEscape := OldLeaveEscape;
3213     EscapeInvalidMeta := OldEscapeInvalidMeta;
3214     InputString := OldStr;
3215   end;
3216 end;
3217
3218 end.