ComboEdit.pas 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171
  1. unit ComboEdit;
  2. {$J+}
  3. {$WARN UNIT_PLATFORM OFF}
  4. interface
  5. uses Windows, Classes, StdCtrls, Controls, Messages, Forms, Graphics,
  6. Menus, Buttons, Dialogs, Mask,
  7. { SysUtils must overload deprecated FileCtrl (implements SelectDirectory) }
  8. FileCtrl, SysUtils;
  9. const
  10. scAltDown = scAlt + vk_Down;
  11. scCtrlEnter = scCtrl + vk_Return;
  12. DefEditBtnWidth = 25;
  13. resourcestring
  14. SBrowse = 'Browse';
  15. SDefaultFilter = 'All files (*.*)|*.*';
  16. SInvalidFileName = 'Invalid file name - %s';
  17. type
  18. TFileExt = type string;
  19. { TCustomComboEdit }
  20. TCustomComboEdit = class(TCustomMaskEdit)
  21. private
  22. FButton: TButton;
  23. FBtnControl: TWinControl;
  24. FOnButtonClick: TNotifyEvent;
  25. FClickKey: TShortCut;
  26. FReadOnly: Boolean;
  27. FDirectInput: Boolean;
  28. FAlwaysEnable: Boolean;
  29. FAlignment: TAlignment;
  30. procedure SetEditRect;
  31. procedure UpdateBtnBounds;
  32. procedure EditButtonClick(Sender: TObject);
  33. function GetMinHeight: Integer;
  34. function GetTextHeight: Integer;
  35. procedure SetShowCaret;
  36. function GetButtonWidth: Integer;
  37. procedure SetButtonWidth(Value: Integer);
  38. function GetButtonHint: string;
  39. procedure SetButtonHint(const Value: string);
  40. procedure SetDirectInput(Value: Boolean);
  41. procedure SetReadOnly(Value: Boolean);
  42. procedure SetAlignment(Value: TAlignment);
  43. function BtnWidthStored: Boolean;
  44. procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
  45. procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  46. procedure CMEnter(var Message: TMessage); message CM_ENTER;
  47. procedure CNCtlColor(var Message: TMessage); message CN_CTLCOLOREDIT;
  48. procedure WMSize(var Message: TWMSize); message WM_SIZE;
  49. procedure WMSetFocus(var Message: TMessage); message WM_SETFOCUS;
  50. procedure WMPaste(var Message: TWMPaste); message WM_PASTE;
  51. procedure WMCut(var Message: TWMCut); message WM_CUT;
  52. procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED;
  53. protected
  54. procedure CreateParams(var Params: TCreateParams); override;
  55. procedure CreateWnd; override;
  56. function EditCanModify: Boolean; override;
  57. function GetReadOnly: Boolean; virtual;
  58. procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  59. procedure KeyPress(var Key: Char); override;
  60. procedure ButtonClick; dynamic;
  61. property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify;
  62. property AlwaysEnable: Boolean read FAlwaysEnable write FAlwaysEnable default False;
  63. property Button: TButton read FButton;
  64. property ClickKey: TShortCut read FClickKey write FClickKey
  65. default scAltDown;
  66. property ButtonWidth: Integer read GetButtonWidth write SetButtonWidth
  67. stored BtnWidthStored;
  68. property ButtonHint: string read GetButtonHint write SetButtonHint;
  69. property DirectInput: Boolean read FDirectInput write SetDirectInput default True;
  70. property ReadOnly: Boolean read GetReadOnly write SetReadOnly default False;
  71. property OnButtonClick: TNotifyEvent read FOnButtonClick write FOnButtonClick;
  72. public
  73. constructor Create(AOwner: TComponent); override;
  74. destructor Destroy; override;
  75. procedure DoClick;
  76. procedure SelectAll;
  77. end;
  78. type
  79. TComboEdit = class(TCustomComboEdit)
  80. published
  81. property AutoSelect;
  82. property ButtonHint;
  83. property BorderStyle;
  84. property CharCase;
  85. property ClickKey;
  86. property Color;
  87. property Ctl3D;
  88. property DirectInput;
  89. property DragCursor;
  90. property DragMode;
  91. property EditMask;
  92. property Enabled;
  93. property Font;
  94. property ButtonWidth;
  95. property HideSelection;
  96. property Anchors;
  97. property BiDiMode;
  98. property Constraints;
  99. property DragKind;
  100. property ParentBiDiMode;
  101. property ImeMode;
  102. property ImeName;
  103. property ParentColor;
  104. property ParentCtl3D;
  105. property ParentFont;
  106. property ParentShowHint;
  107. property PopupMenu;
  108. property ReadOnly;
  109. property ShowHint;
  110. property TabOrder;
  111. property TabStop;
  112. property Text;
  113. property Visible;
  114. property OnButtonClick;
  115. property OnChange;
  116. property OnClick;
  117. property OnDblClick;
  118. property OnDragDrop;
  119. property OnDragOver;
  120. property OnEndDrag;
  121. property OnEnter;
  122. property OnExit;
  123. property OnKeyDown;
  124. property OnKeyPress;
  125. property OnKeyUp;
  126. property OnMouseDown;
  127. property OnMouseMove;
  128. property OnMouseUp;
  129. property OnStartDrag;
  130. property OnContextPopup;
  131. property OnEndDock;
  132. property OnStartDock;
  133. end;
  134. { TFileDirEdit }
  135. { The common parent of TFilenameEdit and TDirectoryEdit }
  136. { For internal use only; it's not intended to be used separately }
  137. const
  138. MaxFileLength = SizeOf(TFileName) - 1;
  139. type
  140. TExecOpenDialogEvent = procedure(Sender: TObject; var Name: string;
  141. var Action: Boolean) of object;
  142. TFileDirEdit = class(TCustomComboEdit)
  143. private
  144. FErrMode: Cardinal;
  145. FAcceptFiles: Boolean;
  146. FOnDropFiles: TNotifyEvent;
  147. FOnBeforeDialog: TExecOpenDialogEvent;
  148. FOnAfterDialog: TExecOpenDialogEvent;
  149. procedure SetDragAccept(Value: Boolean);
  150. procedure SetAcceptFiles(Value: Boolean);
  151. procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;
  152. protected
  153. FMultipleDirs: Boolean;
  154. procedure CreateHandle; override;
  155. procedure DestroyWindowHandle; override;
  156. procedure DoAfterDialog(var FileName: string; var Action: Boolean); dynamic;
  157. procedure DoBeforeDialog(var FileName: string; var Action: Boolean); dynamic;
  158. procedure ReceptFileDir(const AFileName: string); virtual; abstract;
  159. procedure ClearFileList; virtual;
  160. procedure DisableSysErrors;
  161. procedure EnableSysErrors;
  162. property MaxLength;
  163. published
  164. property AcceptFiles: Boolean read FAcceptFiles write SetAcceptFiles default False;
  165. property OnBeforeDialog: TExecOpenDialogEvent read FOnBeforeDialog
  166. write FOnBeforeDialog;
  167. property OnAfterDialog: TExecOpenDialogEvent read FOnAfterDialog
  168. write FOnAfterDialog;
  169. property OnDropFiles: TNotifyEvent read FOnDropFiles write FOnDropFiles;
  170. property OnButtonClick;
  171. end;
  172. { TFilenameEdit }
  173. TFileDialogKind = (dkOpen, dkSave , dkOpenPicture,
  174. dkSavePicture);
  175. TFilenameEdit = class(TFileDirEdit)
  176. private
  177. FDialog: TOpenDialog;
  178. FDialogKind: TFileDialogKind;
  179. procedure CreateEditDialog;
  180. function GetFileName: string;
  181. function GetDefaultExt: TFileExt;
  182. function GetFileEditStyle: TFileEditStyle;
  183. function GetFilter: string;
  184. function GetFilterIndex: Integer;
  185. function GetInitialDir: string;
  186. function GetHistoryList: TStrings;
  187. function GetOptions: TOpenOptions;
  188. function GetDialogTitle: string;
  189. function GetDialogFiles: TStrings;
  190. procedure SetDialogKind(Value: TFileDialogKind);
  191. procedure SetFileName(const Value: string);
  192. procedure SetDefaultExt(Value: TFileExt);
  193. procedure SetFileEditStyle(Value: TFileEditStyle);
  194. procedure SetFilter(const Value: string);
  195. procedure SetFilterIndex(Value: Integer);
  196. procedure SetInitialDir(const Value: string);
  197. procedure SetHistoryList(Value: TStrings);
  198. procedure SetOptions(Value: TOpenOptions);
  199. procedure SetDialogTitle(const Value: string);
  200. function IsCustomTitle: Boolean;
  201. function IsCustomFilter: Boolean;
  202. protected
  203. procedure ButtonClick; override;
  204. procedure ReceptFileDir(const AFileName: string); override;
  205. procedure ClearFileList; override;
  206. public
  207. constructor Create(AOwner: TComponent); override;
  208. property Dialog: TOpenDialog read FDialog;
  209. property DialogFiles: TStrings read GetDialogFiles;
  210. published
  211. property DialogKind: TFileDialogKind read FDialogKind write SetDialogKind
  212. default dkOpen;
  213. property DefaultExt: TFileExt read GetDefaultExt write SetDefaultExt;
  214. property FileEditStyle: TFileEditStyle read GetFileEditStyle write SetFileEditStyle
  215. default fsEdit;
  216. property FileName: string read GetFileName write SetFileName stored False;
  217. property Filter: string read GetFilter write SetFilter stored IsCustomFilter;
  218. property FilterIndex: Integer read GetFilterIndex write SetFilterIndex default 1;
  219. property InitialDir: string read GetInitialDir write SetInitialDir;
  220. property HistoryList: TStrings read GetHistoryList write SetHistoryList;
  221. property DialogOptions: TOpenOptions read GetOptions write SetOptions
  222. default [ofHideReadOnly];
  223. property DialogTitle: string read GetDialogTitle write SetDialogTitle
  224. stored IsCustomTitle;
  225. property AutoSelect;
  226. property ButtonHint;
  227. property BorderStyle;
  228. property CharCase;
  229. property ClickKey;
  230. property Color;
  231. property Ctl3D;
  232. property DirectInput;
  233. property DragCursor;
  234. property DragMode;
  235. property EditMask;
  236. property Enabled;
  237. property Font;
  238. property ButtonWidth;
  239. property HideSelection;
  240. property Anchors;
  241. property BiDiMode;
  242. property Constraints;
  243. property DragKind;
  244. property ParentBiDiMode;
  245. property ImeMode;
  246. property ImeName;
  247. property ParentColor;
  248. property ParentCtl3D;
  249. property ParentFont;
  250. property ParentShowHint;
  251. property PopupMenu;
  252. property ReadOnly;
  253. property ShowHint;
  254. property TabOrder;
  255. property TabStop;
  256. property Text;
  257. property Visible;
  258. property OnChange;
  259. property OnClick;
  260. property OnDblClick;
  261. property OnDragDrop;
  262. property OnDragOver;
  263. property OnEndDrag;
  264. property OnEnter;
  265. property OnExit;
  266. property OnKeyDown;
  267. property OnKeyPress;
  268. property OnKeyUp;
  269. property OnMouseDown;
  270. property OnMouseMove;
  271. property OnMouseUp;
  272. property OnStartDrag;
  273. property OnContextPopup;
  274. property OnEndDock;
  275. property OnStartDock;
  276. end;
  277. { TDirectoryEdit }
  278. TDirectoryEdit = class(TFileDirEdit)
  279. private
  280. FInitialDir: string;
  281. FDialogText: string;
  282. protected
  283. procedure ButtonClick; override;
  284. procedure ReceptFileDir(const AFileName: string); override;
  285. public
  286. constructor Create(AOwner: TComponent); override;
  287. published
  288. property DialogText: string read FDialogText write FDialogText;
  289. property InitialDir: string read FInitialDir write FInitialDir;
  290. property MultipleDirs: Boolean read FMultipleDirs write FMultipleDirs default False;
  291. property AutoSelect;
  292. property ButtonHint;
  293. property BorderStyle;
  294. property CharCase;
  295. property ClickKey;
  296. property Color;
  297. property Ctl3D;
  298. property DirectInput;
  299. property DragCursor;
  300. property DragMode;
  301. property EditMask;
  302. property Enabled;
  303. property Font;
  304. property ButtonWidth;
  305. property HideSelection;
  306. property Anchors;
  307. property BiDiMode;
  308. property Constraints;
  309. property DragKind;
  310. property ParentBiDiMode;
  311. property ImeMode;
  312. property ImeName;
  313. property ParentColor;
  314. property ParentCtl3D;
  315. property ParentFont;
  316. property ParentShowHint;
  317. property PopupMenu;
  318. property ReadOnly;
  319. property ShowHint;
  320. property TabOrder;
  321. property TabStop;
  322. property Text;
  323. property Visible;
  324. property OnChange;
  325. property OnClick;
  326. property OnDblClick;
  327. property OnDragDrop;
  328. property OnDragOver;
  329. property OnEndDrag;
  330. property OnEnter;
  331. property OnExit;
  332. property OnKeyDown;
  333. property OnKeyPress;
  334. property OnKeyUp;
  335. property OnMouseDown;
  336. property OnMouseMove;
  337. property OnMouseUp;
  338. property OnStartDrag;
  339. property OnContextPopup;
  340. property OnEndDock;
  341. property OnStartDock;
  342. end;
  343. EComboEditError = class(Exception);
  344. procedure Register;
  345. implementation
  346. uses
  347. ShellAPI, Consts, ExtDlgs, Variants, PasTools, UITypes;
  348. procedure Register;
  349. begin
  350. RegisterComponents('Martin', [TComboEdit, TFilenameEdit, TDirectoryEdit]);
  351. end;
  352. { Utility functions }
  353. type
  354. TCharSet = TSysCharSet;
  355. function ExtractSubstr(const S: string; var Pos: Integer;
  356. const Delims: TCharSet): string;
  357. var
  358. I: Integer;
  359. begin
  360. I := Pos;
  361. while (I <= Length(S)) and not CharInSet(S[I], Delims) do Inc(I);
  362. Result := Copy(S, Pos, I - Pos);
  363. if (I <= Length(S)) and CharInSet(S[I], Delims) then Inc(I);
  364. Pos := I;
  365. end;
  366. function ValidFileName(const FileName: string): Boolean;
  367. function HasAny(const Str, Substr: string): Boolean;
  368. var
  369. I: Integer;
  370. begin
  371. Result := False;
  372. for I := 1 to Length(Substr) do begin
  373. if Pos(Substr[I], Str) > 0 then begin
  374. Result := True;
  375. Break;
  376. end;
  377. end;
  378. end;
  379. begin
  380. Result := (FileName <> '') and (not HasAny(FileName, '<>"[]|'));
  381. if Result then Result := Pos('\', ExtractFileName(FileName)) = 0;
  382. end;
  383. function DirExists(Name: string): Boolean;
  384. var
  385. Code: Integer;
  386. begin
  387. Code := GetFileAttributes(PChar(ApiPath(Name)));
  388. Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
  389. end;
  390. { TCustomComboEdit }
  391. constructor TCustomComboEdit.Create(AOwner: TComponent);
  392. begin
  393. inherited Create(AOwner);
  394. ControlStyle := ControlStyle + [csCaptureMouse];
  395. AutoSize := False;
  396. FDirectInput := True;
  397. FClickKey := scCtrlEnter;
  398. FBtnControl := TWinControl.Create(Self);
  399. with FBtnControl do
  400. begin
  401. ControlStyle := ControlStyle + [csReplicatable];
  402. Width := DefEditBtnWidth;
  403. Height := 17;
  404. Visible := True;
  405. Parent := Self;
  406. end;
  407. FButton := TButton.Create(Self);
  408. with FButton do
  409. begin
  410. SetBounds(0, 0, FBtnControl.Width, FBtnControl.Height);
  411. ControlStyle := ControlStyle + [csReplicatable];
  412. ParentShowHint := True;
  413. Caption := '...';
  414. Visible := True;
  415. Parent := FBtnControl;
  416. OnClick := EditButtonClick;
  417. end;
  418. Height := 21;
  419. end;
  420. destructor TCustomComboEdit.Destroy;
  421. begin
  422. FButton.OnClick := nil;
  423. inherited Destroy;
  424. end;
  425. procedure TCustomComboEdit.CreateParams(var Params: TCreateParams);
  426. const
  427. Alignments: array[TAlignment] of Longword = (ES_LEFT, ES_RIGHT, ES_CENTER);
  428. begin
  429. inherited CreateParams(Params);
  430. Params.Style := Params.Style or ES_MULTILINE or WS_CLIPCHILDREN
  431. or Alignments[FAlignment];
  432. end;
  433. procedure TCustomComboEdit.CreateWnd;
  434. begin
  435. inherited CreateWnd;
  436. SetEditRect;
  437. end;
  438. function TCustomComboEdit.EditCanModify: Boolean;
  439. begin
  440. Result := not FReadOnly;
  441. end;
  442. procedure TCustomComboEdit.KeyDown(var Key: Word; Shift: TShiftState);
  443. begin
  444. inherited KeyDown(Key, Shift);
  445. if (FClickKey = ShortCut(Key, Shift)) and (ButtonWidth > 0) then
  446. begin
  447. EditButtonClick(Self);
  448. Key := 0;
  449. end;
  450. end;
  451. procedure TCustomComboEdit.KeyPress(var Key: Char);
  452. var
  453. OrigKey: Char;
  454. begin
  455. if (Key = Char(VK_RETURN)) or (Key = Char(VK_ESCAPE)) or (Key = #10) then
  456. begin
  457. OrigKey := Key;
  458. { must catch and remove this, since is actually multi-line }
  459. GetParentForm(Self).Perform(CM_DIALOGKEY, Byte(Key), 0);
  460. if Key = OrigKey then
  461. begin
  462. inherited KeyPress(Key);
  463. Key := #0;
  464. Exit;
  465. end;
  466. end;
  467. inherited KeyPress(Key);
  468. end;
  469. function TCustomComboEdit.GetButtonWidth: Integer;
  470. begin
  471. Result := FButton.Width;
  472. end;
  473. procedure TCustomComboEdit.SetButtonWidth(Value: Integer);
  474. begin
  475. if ButtonWidth <> Value then
  476. begin
  477. FBtnControl.Visible := Value > 1;
  478. if (csCreating in ControlState) then
  479. begin
  480. FBtnControl.Width := Value;
  481. FButton.Width := Value;
  482. with FButton do
  483. ControlStyle := ControlStyle - [csFixedWidth];
  484. end
  485. else if (Value <> ButtonWidth) and (Value < ClientWidth) then
  486. begin
  487. FButton.Width := Value;
  488. with FButton do
  489. ControlStyle := ControlStyle - [csFixedWidth];
  490. if HandleAllocated then RecreateWnd;
  491. end;
  492. end;
  493. end;
  494. function TCustomComboEdit.GetButtonHint: string;
  495. begin
  496. Result := FButton.Hint;
  497. end;
  498. procedure TCustomComboEdit.SetButtonHint(const Value: string);
  499. begin
  500. FButton.Hint := Value;
  501. end;
  502. procedure TCustomComboEdit.SetEditRect;
  503. var
  504. Loc: TRect;
  505. begin
  506. SetRect(Loc, 0, 0, ClientWidth - FBtnControl.Width - 2, ClientHeight + 1);
  507. SendMessage(Handle, EM_SETRECTNP, 0, LongInt(@Loc));
  508. end;
  509. procedure TCustomComboEdit.UpdateBtnBounds;
  510. var
  511. BtnRect: TRect;
  512. begin
  513. if NewStyleControls then begin
  514. if Ctl3D and (BorderStyle = bsSingle) then
  515. BtnRect := Bounds(Width - FButton.Width - 4, 0,
  516. FButton.Width, Height - 4)
  517. else begin
  518. if BorderStyle = bsSingle then
  519. BtnRect := Bounds(Width - FButton.Width - 2, 2,
  520. FButton.Width, Height - 4)
  521. else
  522. BtnRect := Bounds(Width - FButton.Width, 0,
  523. FButton.Width, Height);
  524. end;
  525. end
  526. else
  527. BtnRect := Bounds(Width - FButton.Width, 0, FButton.Width, Height);
  528. with BtnRect do
  529. FBtnControl.SetBounds(Left, Top, Right - Left, Bottom - Top);
  530. FButton.Height := FBtnControl.Height;
  531. SetEditRect;
  532. end;
  533. procedure TCustomComboEdit.CMCtl3DChanged(var Message: TMessage);
  534. begin
  535. inherited;
  536. UpdateBtnBounds;
  537. end;
  538. procedure TCustomComboEdit.WMSize(var Message: TWMSize);
  539. var
  540. MinHeight: Integer;
  541. begin
  542. inherited;
  543. if not (csLoading in ComponentState) then
  544. begin
  545. MinHeight := GetMinHeight;
  546. { text edit bug: if size to less than MinHeight, then edit ctrl does
  547. not display the text }
  548. if Height < MinHeight then
  549. begin
  550. Height := MinHeight;
  551. Exit;
  552. end;
  553. end;
  554. UpdateBtnBounds;
  555. end;
  556. function TCustomComboEdit.GetTextHeight: Integer;
  557. var
  558. DC: HDC;
  559. SaveFont: HFont;
  560. SysMetrics, Metrics: TTextMetric;
  561. begin
  562. DC := GetDC(0);
  563. try
  564. GetTextMetrics(DC, SysMetrics);
  565. SaveFont := SelectObject(DC, Font.Handle);
  566. GetTextMetrics(DC, Metrics);
  567. SelectObject(DC, SaveFont);
  568. finally
  569. ReleaseDC(0, DC);
  570. end;
  571. if SysMetrics.tmHeight < Metrics.tmHeight then Result := SysMetrics.tmHeight
  572. else Result := Metrics.tmHeight;
  573. end;
  574. function TCustomComboEdit.GetMinHeight: Integer;
  575. var
  576. I: Integer;
  577. begin
  578. I := GetTextHeight;
  579. Result := I + GetSystemMetricsForControl(Self, SM_CYBORDER) * 4 + 1;
  580. end;
  581. procedure TCustomComboEdit.CMFontChanged(var Message: TMessage);
  582. begin
  583. inherited;
  584. if HandleAllocated then SetEditRect;
  585. end;
  586. procedure TCustomComboEdit.CMEnabledChanged(var Message: TMessage);
  587. begin
  588. inherited;
  589. FButton.Enabled := Enabled;
  590. end;
  591. procedure TCustomComboEdit.CMEnter(var Message: TMessage);
  592. begin
  593. if AutoSelect and not (csLButtonDown in ControlState) then SelectAll;
  594. inherited;
  595. end;
  596. procedure TCustomComboEdit.CNCtlColor(var Message: TMessage);
  597. var
  598. TextColor: Longint;
  599. begin
  600. inherited;
  601. if NewStyleControls then begin
  602. TextColor := ColorToRGB(Font.Color);
  603. if not Enabled and (ColorToRGB(Color) <> ColorToRGB(clGrayText)) then
  604. TextColor := ColorToRGB(clGrayText);
  605. SetTextColor(Message.WParam, TextColor);
  606. end;
  607. end;
  608. procedure TCustomComboEdit.WMSetFocus(var Message: TMessage);
  609. begin
  610. inherited;
  611. SetShowCaret;
  612. end;
  613. procedure TCustomComboEdit.SetShowCaret;
  614. const
  615. CaretWidth: array[Boolean] of Byte = (1, 2);
  616. begin
  617. CreateCaret(Handle, 0, CaretWidth[fsBold in Font.Style], GetTextHeight);
  618. ShowCaret(Handle);
  619. end;
  620. procedure TCustomComboEdit.EditButtonClick(Sender: TObject);
  621. begin
  622. if (not FReadOnly) or AlwaysEnable then ButtonClick;
  623. end;
  624. procedure TCustomComboEdit.DoClick;
  625. begin
  626. EditButtonClick(Self);
  627. end;
  628. procedure TCustomComboEdit.ButtonClick;
  629. begin
  630. if Assigned(FOnButtonClick) then FOnButtonClick(Self);
  631. end;
  632. procedure TCustomComboEdit.SelectAll;
  633. begin
  634. if DirectInput then inherited SelectAll;
  635. end;
  636. procedure TCustomComboEdit.SetDirectInput(Value: Boolean);
  637. begin
  638. inherited ReadOnly := not Value or FReadOnly;
  639. FDirectInput := Value;
  640. end;
  641. procedure TCustomComboEdit.WMPaste(var Message: TWMPaste);
  642. begin
  643. if not FDirectInput or ReadOnly then Exit;
  644. inherited;
  645. end;
  646. procedure TCustomComboEdit.WMCut(var Message: TWMCut);
  647. begin
  648. if not FDirectInput or ReadOnly then Exit;
  649. inherited;
  650. end;
  651. function TCustomComboEdit.GetReadOnly: Boolean;
  652. begin
  653. Result := FReadOnly;
  654. end;
  655. procedure TCustomComboEdit.SetReadOnly(Value: Boolean);
  656. begin
  657. if Value <> FReadOnly then begin
  658. FReadOnly := Value;
  659. inherited ReadOnly := Value or not FDirectInput;
  660. end;
  661. end;
  662. procedure TCustomComboEdit.SetAlignment(Value: TAlignment);
  663. begin
  664. if FAlignment <> Value then begin
  665. FAlignment := Value;
  666. RecreateWnd;
  667. end;
  668. end;
  669. function TCustomComboEdit.BtnWidthStored: Boolean;
  670. begin
  671. Result := ButtonWidth <> DefEditBtnWidth;
  672. end;
  673. { TFileDirEdit }
  674. procedure TFileDirEdit.DoBeforeDialog(var FileName: string;
  675. var Action: Boolean);
  676. begin
  677. if Assigned(FOnBeforeDialog) then FOnBeforeDialog(Self, FileName, Action);
  678. end;
  679. procedure TFileDirEdit.DoAfterDialog(var FileName: string;
  680. var Action: Boolean);
  681. begin
  682. if Assigned(FOnAfterDialog) then FOnAfterDialog(Self, FileName, Action);
  683. end;
  684. procedure TFileDirEdit.CreateHandle;
  685. begin
  686. inherited CreateHandle;
  687. if FAcceptFiles then SetDragAccept(True);
  688. end;
  689. procedure TFileDirEdit.DestroyWindowHandle;
  690. begin
  691. SetDragAccept(False);
  692. inherited DestroyWindowHandle;
  693. end;
  694. procedure TFileDirEdit.SetDragAccept(Value: Boolean);
  695. begin
  696. if not (csDesigning in ComponentState) and (Handle <> 0) then
  697. DragAcceptFiles(Handle, Value);
  698. end;
  699. procedure TFileDirEdit.SetAcceptFiles(Value: Boolean);
  700. begin
  701. if FAcceptFiles <> Value then begin
  702. SetDragAccept(Value);
  703. FAcceptFiles := Value;
  704. end;
  705. end;
  706. procedure TFileDirEdit.DisableSysErrors;
  707. begin
  708. FErrMode := SetErrorMode(SEM_NOOPENFILEERRORBOX or SEM_FAILCRITICALERRORS);
  709. end;
  710. procedure TFileDirEdit.EnableSysErrors;
  711. begin
  712. SetErrorMode(FErrMode);
  713. FErrMode := 0;
  714. end;
  715. procedure TFileDirEdit.WMDropFiles(var Msg: TWMDropFiles);
  716. var
  717. AFileName: array[0..255] of Char;
  718. I, Num: Cardinal;
  719. begin
  720. Msg.Result := 0;
  721. try
  722. Num := DragQueryFile(Msg.Drop, $FFFFFFFF, nil, 0);
  723. if Num > 0 then begin
  724. ClearFileList;
  725. for I := 0 to Num - 1 do begin
  726. DragQueryFile(Msg.Drop, I, PChar(@AFileName), Pred(SizeOf(AFileName)));
  727. ReceptFileDir(StrPas(AFileName));
  728. if not FMultipleDirs then Break;
  729. end;
  730. if Assigned(FOnDropFiles) then FOnDropFiles(Self);
  731. end;
  732. finally
  733. DragFinish(Msg.Drop);
  734. end;
  735. end;
  736. procedure TFileDirEdit.ClearFileList;
  737. begin
  738. end;
  739. { TFilenameEdit }
  740. function StrPAlloc(const S: string): PChar;
  741. begin
  742. Result := StrPCopy(StrAlloc(Length(S) + 1), S);
  743. end;
  744. function GetParamStr(P: PChar; var Param: string): PChar;
  745. var
  746. Len: Integer;
  747. Buffer: array[Byte] of Char;
  748. begin
  749. while True do
  750. begin
  751. while (P[0] <> #0) and (P[0] <= ' ') do Inc(P);
  752. if (P[0] = '"') and (P[1] = '"') then Inc(P, 2) else Break;
  753. end;
  754. Len := 0;
  755. while P[0] > ' ' do
  756. if P[0] = '"' then
  757. begin
  758. Inc(P);
  759. while (P[0] <> #0) and (P[0] <> '"') do
  760. begin
  761. Buffer[Len] := P[0];
  762. Inc(Len);
  763. Inc(P);
  764. end;
  765. if P[0] <> #0 then Inc(P);
  766. end else
  767. begin
  768. Buffer[Len] := P[0];
  769. Inc(Len);
  770. Inc(P);
  771. end;
  772. SetString(Param, Buffer, Len);
  773. Result := P;
  774. end;
  775. function ParamCountFromCommandLine(CmdLine: PChar): Integer;
  776. var
  777. S: string;
  778. P: PChar;
  779. begin
  780. P := CmdLine;
  781. Result := 0;
  782. while True do
  783. begin
  784. P := GetParamStr(P, S);
  785. if S = '' then Break;
  786. Inc(Result);
  787. end;
  788. end;
  789. function ParamStrFromCommandLine(CmdLine: PChar; Index: Integer): string;
  790. var
  791. P: PChar;
  792. begin
  793. P := CmdLine;
  794. while True do
  795. begin
  796. P := GetParamStr(P, Result);
  797. if (Index = 0) or (Result = '') then Break;
  798. Dec(Index);
  799. end;
  800. end;
  801. procedure SplitCommandLine(const CmdLine: string; var ExeName,
  802. Params: string);
  803. var
  804. Buffer: PChar;
  805. Cnt, I: Integer;
  806. S: string;
  807. begin
  808. ExeName := '';
  809. Params := '';
  810. Buffer := StrPAlloc(CmdLine);
  811. try
  812. Cnt := ParamCountFromCommandLine(Buffer);
  813. if Cnt > 0 then begin
  814. ExeName := ParamStrFromCommandLine(Buffer, 0);
  815. for I := 1 to Cnt - 1 do begin
  816. S := ParamStrFromCommandLine(Buffer, I);
  817. if Pos(' ', S) > 0 then S := '"' + S + '"';
  818. Params := Params + S;
  819. if I < Cnt - 1 then Params := Params + ' ';
  820. end;
  821. end;
  822. finally
  823. StrDispose(Buffer);
  824. end;
  825. end;
  826. constructor TFilenameEdit.Create(AOwner: TComponent);
  827. begin
  828. inherited Create(AOwner);
  829. CreateEditDialog;
  830. end;
  831. procedure TFilenameEdit.CreateEditDialog;
  832. var
  833. NewDialog: TOpenDialog;
  834. begin
  835. case FDialogKind of
  836. dkOpen: NewDialog := TOpenDialog.Create(Self);
  837. dkOpenPicture: NewDialog := TOpenPictureDialog.Create(Self);
  838. dkSavePicture: NewDialog := TSavePictureDialog.Create(Self);
  839. else {dkSave} NewDialog := TSaveDialog.Create(Self);
  840. end;
  841. try
  842. if FDialog <> nil then begin
  843. with NewDialog do begin
  844. DefaultExt := FDialog.DefaultExt;
  845. FileEditStyle := FDialog.FileEditStyle;
  846. FileName := FDialog.FileName;
  847. Filter := FDialog.Filter;
  848. FilterIndex := FDialog.FilterIndex;
  849. InitialDir := FDialog.InitialDir;
  850. HistoryList := FDialog.HistoryList;
  851. Files.Assign(FDialog.Files);
  852. Options := FDialog.Options;
  853. Title := FDialog.Title;
  854. end;
  855. FDialog.Free;
  856. end
  857. else begin
  858. NewDialog.Title := SBrowse;
  859. NewDialog.Filter := SDefaultFilter;
  860. NewDialog.Options := [ofHideReadOnly];
  861. end;
  862. finally
  863. FDialog := NewDialog;
  864. end;
  865. end;
  866. function TFilenameEdit.IsCustomTitle: Boolean;
  867. begin
  868. Result := CompareStr(SBrowse, FDialog.Title) <> 0;
  869. end;
  870. function TFilenameEdit.IsCustomFilter: Boolean;
  871. begin
  872. Result := CompareStr(SDefaultFilter, FDialog.Filter) <> 0;
  873. end;
  874. procedure TFilenameEdit.ButtonClick;
  875. var
  876. Temp: string;
  877. Action: Boolean;
  878. begin
  879. inherited ButtonClick;
  880. Temp := inherited Text;
  881. Action := True;
  882. DoBeforeDialog(Temp, Action);
  883. if not Action then Exit;
  884. if ValidFileName(Temp) then
  885. try
  886. if DirExists(ExtractFilePath(Temp)) then
  887. SetInitialDir(ExtractFilePath(Temp));
  888. if (ExtractFileName(Temp) = '') or
  889. not ValidFileName(ExtractFileName(Temp)) then Temp := '';
  890. FDialog.FileName := Temp;
  891. except
  892. { ignore any exceptions }
  893. end;
  894. FDialog.HelpContext := Self.HelpContext;
  895. DisableSysErrors;
  896. try
  897. Action := FDialog.Execute;
  898. finally
  899. EnableSysErrors;
  900. end;
  901. if Action then Temp := FDialog.FileName;
  902. if CanFocus then SetFocus;
  903. DoAfterDialog(Temp, Action);
  904. if Action then begin
  905. inherited Text := Temp;
  906. SetInitialDir(ExtractFilePath(FDialog.FileName));
  907. end;
  908. end;
  909. function TFilenameEdit.GetFileName: string;
  910. begin
  911. Result := inherited Text;
  912. end;
  913. procedure TFilenameEdit.SetFileName(const Value: string);
  914. begin
  915. if (Value = '') or ValidFileName(Value) then begin
  916. inherited Text := Value;
  917. ClearFileList;
  918. end
  919. else raise EComboEditError.CreateFmt(SInvalidFilename, [Value]);
  920. end;
  921. procedure TFilenameEdit.ClearFileList;
  922. begin
  923. FDialog.Files.Clear;
  924. end;
  925. procedure TFilenameEdit.ReceptFileDir(const AFileName: string);
  926. begin
  927. if FMultipleDirs then begin
  928. if FDialog.Files.Count = 0 then SetFileName(AFileName);
  929. FDialog.Files.Add(AFileName);
  930. end
  931. else SetFileName(AFileName);
  932. end;
  933. function TFilenameEdit.GetDialogFiles: TStrings;
  934. begin
  935. Result := FDialog.Files;
  936. end;
  937. function TFilenameEdit.GetDefaultExt: TFileExt;
  938. begin
  939. Result := FDialog.DefaultExt;
  940. end;
  941. function TFilenameEdit.GetFileEditStyle: TFileEditStyle;
  942. begin
  943. Result := FDialog.FileEditStyle;
  944. end;
  945. function TFilenameEdit.GetFilter: string;
  946. begin
  947. Result := FDialog.Filter;
  948. end;
  949. function TFilenameEdit.GetFilterIndex: Integer;
  950. begin
  951. Result := FDialog.FilterIndex;
  952. end;
  953. function TFilenameEdit.GetInitialDir: string;
  954. begin
  955. Result := FDialog.InitialDir;
  956. end;
  957. function TFilenameEdit.GetHistoryList: TStrings;
  958. begin
  959. Result := FDialog.HistoryList;
  960. end;
  961. function TFilenameEdit.GetOptions: TOpenOptions;
  962. begin
  963. Result := FDialog.Options;
  964. end;
  965. function TFilenameEdit.GetDialogTitle: string;
  966. begin
  967. Result := FDialog.Title;
  968. end;
  969. procedure TFilenameEdit.SetDialogKind(Value: TFileDialogKind);
  970. begin
  971. if FDialogKind <> Value then begin
  972. FDialogKind := Value;
  973. CreateEditDialog;
  974. end;
  975. end;
  976. procedure TFilenameEdit.SetDefaultExt(Value: TFileExt);
  977. begin
  978. FDialog.DefaultExt := Value;
  979. end;
  980. procedure TFilenameEdit.SetFileEditStyle(Value: TFileEditStyle);
  981. begin
  982. FDialog.FileEditStyle := Value;
  983. end;
  984. procedure TFilenameEdit.SetFilter(const Value: string);
  985. begin
  986. FDialog.Filter := Value;
  987. end;
  988. procedure TFilenameEdit.SetFilterIndex(Value: Integer);
  989. begin
  990. FDialog.FilterIndex := Value;
  991. end;
  992. procedure TFilenameEdit.SetInitialDir(const Value: string);
  993. begin
  994. FDialog.InitialDir := Value;
  995. end;
  996. procedure TFilenameEdit.SetHistoryList(Value: TStrings);
  997. begin
  998. FDialog.HistoryList := Value;
  999. end;
  1000. procedure TFilenameEdit.SetOptions(Value: TOpenOptions);
  1001. begin
  1002. if Value <> FDialog.Options then begin
  1003. FDialog.Options := Value;
  1004. FMultipleDirs := ofAllowMultiSelect in FDialog.Options;
  1005. if not FMultipleDirs then ClearFileList;
  1006. end;
  1007. end;
  1008. procedure TFilenameEdit.SetDialogTitle(const Value: string);
  1009. begin
  1010. FDialog.Title := Value;
  1011. end;
  1012. { TDirectoryEdit }
  1013. constructor TDirectoryEdit.Create(AOwner: TComponent);
  1014. begin
  1015. inherited Create(AOwner);
  1016. end;
  1017. procedure TDirectoryEdit.ButtonClick;
  1018. var
  1019. Temp: string;
  1020. Action: Boolean;
  1021. begin
  1022. inherited ButtonClick;
  1023. Temp := Text;
  1024. Action := True;
  1025. DoBeforeDialog(Temp, Action);
  1026. if not Action then Exit;
  1027. if (Temp = '') then begin
  1028. if (InitialDir <> '') then Temp := InitialDir
  1029. else Temp := '\';
  1030. end;
  1031. if not DirExists(Temp) then Temp := '\';
  1032. DisableSysErrors;
  1033. try
  1034. Action := SelectDirectory(FDialogText, '', Temp);
  1035. finally
  1036. EnableSysErrors;
  1037. end;
  1038. if CanFocus then SetFocus;
  1039. DoAfterDialog(Temp, Action);
  1040. if Action then begin
  1041. SelText := '';
  1042. if (Text = '') or not MultipleDirs then Text := Temp
  1043. else Text := Text + ';' + Temp;
  1044. if (Temp <> '') and DirExists(Temp) then InitialDir := Temp;
  1045. end;
  1046. end;
  1047. procedure TDirectoryEdit.ReceptFileDir(const AFileName: string);
  1048. var
  1049. Temp: string;
  1050. begin
  1051. if FileExists(ApiPath(AFileName)) then Temp := ExtractFilePath(AFileName)
  1052. else Temp := AFileName;
  1053. if (Text = '') or not MultipleDirs then Text := Temp
  1054. else Text := Text + ';' + Temp;
  1055. end;
  1056. initialization
  1057. end.