Editor.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include <Common.h>
  5. #include "Editor.h"
  6. #include "WinInterface.h"
  7. #include "TextsWin.h"
  8. #include "Tools.h"
  9. #include <ScpMain.h>
  10. #include "VCLCommon.h"
  11. #include "WinConfiguration.h"
  12. #include "HelpWin.h"
  13. #include <CommDlg.hpp>
  14. //---------------------------------------------------------------------------
  15. #pragma package(smart_init)
  16. #pragma link "TB2Dock"
  17. #pragma link "TBX"
  18. #pragma link "TB2Item"
  19. #pragma link "TB2Toolbar"
  20. #pragma link "TBXStatusBars"
  21. #pragma resource "*.dfm"
  22. //---------------------------------------------------------------------------
  23. TForm * __fastcall ShowEditorForm(const AnsiString FileName, TCustomForm * ParentForm,
  24. TNotifyEvent OnFileChanged, TNotifyEvent OnClose, const AnsiString Caption,
  25. bool ShowWindowButton)
  26. {
  27. TEditorForm * Dialog = new TEditorForm(Application);
  28. try
  29. {
  30. Dialog->ShowWindowButton = ShowWindowButton;
  31. Dialog->FileName = FileName;
  32. Dialog->ParentForm = ParentForm;
  33. Dialog->Caption = Caption.IsEmpty() ? FileName : Caption;
  34. Dialog->OnFileChanged = OnFileChanged;
  35. Dialog->OnWindowClose = OnClose;
  36. Dialog->Show();
  37. }
  38. catch(...)
  39. {
  40. delete Dialog;
  41. throw;
  42. }
  43. return Dialog;
  44. }
  45. //---------------------------------------------------------------------------
  46. void __fastcall ReconfigureEditorForm(TForm * Form)
  47. {
  48. TEditorForm * Editor = dynamic_cast<TEditorForm *>(Form);
  49. assert(Editor != NULL);
  50. Editor->ApplyConfiguration();
  51. }
  52. //---------------------------------------------------------------------------
  53. class TFindDialogEx : public TFindDialog
  54. {
  55. public:
  56. __fastcall virtual TFindDialogEx(TComponent * AOwner) : TFindDialog(AOwner)
  57. {
  58. FHelpMsg = RegisterWindowMessage(HELPMSGSTRING);
  59. }
  60. protected:
  61. unsigned int FHelpMsg;
  62. virtual bool __fastcall MessageHook(TMessage & Msg)
  63. {
  64. bool Result = false;
  65. if (Msg.Msg == FHelpMsg)
  66. {
  67. Application->HelpKeyword(HELP_EDITOR_FIND);
  68. Result = true;
  69. }
  70. if (!Result)
  71. {
  72. Result = TFindDialog::MessageHook(Msg);
  73. }
  74. return Result;
  75. }
  76. };
  77. //---------------------------------------------------------------------------
  78. class TReplaceDialogEx : public TReplaceDialog
  79. {
  80. public:
  81. __fastcall virtual TReplaceDialogEx(TComponent * AOwner) : TReplaceDialog(AOwner)
  82. {
  83. FHelpMsg = RegisterWindowMessage(HELPMSGSTRING);
  84. }
  85. protected:
  86. unsigned int FHelpMsg;
  87. virtual bool __fastcall MessageHook(TMessage & Msg)
  88. {
  89. bool Result = false;
  90. if (Msg.Msg == FHelpMsg)
  91. {
  92. Application->HelpKeyword(HELP_EDITOR_REPLACE);
  93. Result = true;
  94. }
  95. if (!Result)
  96. {
  97. Result = TReplaceDialog::MessageHook(Msg);
  98. }
  99. return Result;
  100. }
  101. };
  102. //---------------------------------------------------------------------------
  103. __fastcall TEditorForm::TEditorForm(TComponent* Owner)
  104. : TForm(Owner)
  105. {
  106. FParentForm = NULL;
  107. FCaretPos.x = -1;
  108. FCaretPos.y = -1;
  109. FLastFindDialog = NULL;
  110. FShowWindowButton = false;
  111. FCloseAnnounced = false;
  112. ApplyConfiguration();
  113. FFindDialog = new TFindDialogEx(this);
  114. FFindDialog->OnFind = FindDialogFind;
  115. FReplaceDialog = new TReplaceDialogEx(this);
  116. FReplaceDialog->OnFind = FindDialogFind;
  117. FReplaceDialog->OnReplace = FindDialogFind;
  118. UseSystemSettings(this);
  119. }
  120. //---------------------------------------------------------------------------
  121. __fastcall TEditorForm::~TEditorForm()
  122. {
  123. // see FormClose for explanation
  124. if (!FCloseAnnounced)
  125. {
  126. DoWindowClose();
  127. }
  128. }
  129. //---------------------------------------------------------------------------
  130. void __fastcall TEditorForm::SetFileName(const AnsiString value)
  131. {
  132. if (value != FFileName)
  133. {
  134. FFileName = value;
  135. if (Visible)
  136. {
  137. LoadFile();
  138. }
  139. }
  140. }
  141. //---------------------------------------------------------------------------
  142. void __fastcall TEditorForm::SetParentForm(TCustomForm * value)
  143. {
  144. if (FParentForm != value)
  145. {
  146. FParentForm = value;
  147. if (value)
  148. {
  149. BoundsRect = value->BoundsRect;
  150. }
  151. }
  152. }
  153. //---------------------------------------------------------------------------
  154. void __fastcall TEditorForm::EditorActionsUpdate(TBasicAction *Action,
  155. bool &Handled)
  156. {
  157. Handled = true;
  158. if (Action == SaveAction)
  159. {
  160. SaveAction->Enabled = EditorMemo->Modified;
  161. }
  162. else if (Action == FindNextAction)
  163. {
  164. FindNextAction->Enabled =
  165. FLastFindDialog != NULL || !FFindDialog->FindText.IsEmpty();
  166. }
  167. else if (Action == PreferencesAction || Action == CloseAction ||
  168. Action == FindAction || Action == ReplaceAction || Action == GoToLineAction ||
  169. Action == HelpAction)
  170. {
  171. ((TAction *)Action)->Enabled = true;
  172. }
  173. else
  174. {
  175. Handled = false;
  176. }
  177. }
  178. //---------------------------------------------------------------------------
  179. void __fastcall TEditorForm::EditorActionsExecute(TBasicAction *Action,
  180. bool &Handled)
  181. {
  182. Handled = true;
  183. if (Action == SaveAction)
  184. {
  185. assert(!FFileName.IsEmpty());
  186. EditorMemo->Lines->SaveToFile(FFileName);
  187. if (FOnFileChanged)
  188. {
  189. FOnFileChanged(this);
  190. }
  191. EditorMemo->Modified = false;
  192. UpdateControls();
  193. }
  194. else if (Action == PreferencesAction)
  195. {
  196. DoPreferencesDialog(pmEditor);
  197. }
  198. else if (Action == CloseAction)
  199. {
  200. Close();
  201. }
  202. else if (Action == FindAction || Action == ReplaceAction)
  203. {
  204. StartFind(Action == FindAction);
  205. }
  206. else if (Action == FindNextAction)
  207. {
  208. if (!FLastFindDialog)
  209. {
  210. FLastFindDialog = FFindDialog;
  211. }
  212. Find();
  213. }
  214. else if (Action == GoToLineAction)
  215. {
  216. GoToLine();
  217. }
  218. else if (Action == EditPaste)
  219. {
  220. // original source: http://home.att.net/~robertdunn/FAQs/Faqs.html
  221. // tell the Rich Edit control to insert unformatted text (CF_TEXT)
  222. REPASTESPECIAL RepasteSpecial = { 0, 0 };
  223. SendMessage(EditorMemo->Handle, EM_PASTESPECIAL, CF_TEXT,
  224. (LPARAM)&RepasteSpecial);
  225. }
  226. else if (Action == HelpAction)
  227. {
  228. FormHelp(this);
  229. }
  230. else
  231. {
  232. Handled = false;
  233. }
  234. }
  235. //---------------------------------------------------------------------------
  236. void __fastcall TEditorForm::FormCloseQuery(TObject * /*Sender*/,
  237. bool &CanClose)
  238. {
  239. if (EditorMemo->Modified)
  240. {
  241. SetFocus();
  242. int Answer = MessageDialog(LoadStr(SAVE_CHANGES), qtConfirmation,
  243. qaYes | qaNo | qaCancel);
  244. CanClose = (Answer != qaCancel);
  245. if (Answer == qaYes)
  246. {
  247. SaveAction->Execute();
  248. }
  249. }
  250. }
  251. //---------------------------------------------------------------------------
  252. void __fastcall TEditorForm::ApplyConfiguration()
  253. {
  254. bool PrevModified = EditorMemo->Modified;
  255. assert(Configuration);
  256. EditorMemo->Font->Name = WinConfiguration->Editor.FontName;
  257. EditorMemo->Font->Height = WinConfiguration->Editor.FontHeight;
  258. EditorMemo->Font->Charset = (TFontCharset)WinConfiguration->Editor.FontCharset;
  259. EditorMemo->Font->Style = IntToFontStyles(WinConfiguration->Editor.FontStyle);
  260. EditorMemo->DefAttributes->Assign(EditorMemo->Font);
  261. if (EditorMemo->WordWrap != WinConfiguration->Editor.WordWrap)
  262. {
  263. if (Visible)
  264. {
  265. TStrings * Content = new TStringList();
  266. try
  267. {
  268. EditorMemo->WordWrap = False;
  269. Content->Assign(EditorMemo->Lines);
  270. EditorMemo->WordWrap = WinConfiguration->Editor.WordWrap;
  271. EditorMemo->Lines = Content;
  272. EditorMemo->CaretPos = TPoint(0, 0);
  273. }
  274. __finally
  275. {
  276. delete Content;
  277. }
  278. }
  279. else
  280. {
  281. EditorMemo->WordWrap = WinConfiguration->Editor.WordWrap;
  282. }
  283. }
  284. EditorMemo->Modified = PrevModified;
  285. EditorMemo->ClearUndo();
  286. UpdateControls();
  287. }
  288. //---------------------------------------------------------------------------
  289. void __fastcall TEditorForm::UpdateControls()
  290. {
  291. TPoint ACaretPos = EditorMemo->CaretPos;
  292. if (ACaretPos.x != FCaretPos.x || ACaretPos.y != FCaretPos.y)
  293. {
  294. FCaretPos = ACaretPos;
  295. int Count = EditorMemo->Lines->Count;
  296. StatusBar->Panels->Items[0]->Caption = FMTLOAD(EDITOR_LINE_STATUS,
  297. ((int)FCaretPos.y+1, Count));
  298. StatusBar->Panels->Items[1]->Caption = FMTLOAD(EDITOR_COLUMN_STATUS,
  299. ((int)FCaretPos.x+1));
  300. AnsiString Character;
  301. if (FCaretPos.y >= 0 && FCaretPos.y < EditorMemo->Lines->Count)
  302. {
  303. AnsiString Line = EditorMemo->Lines->Strings[FCaretPos.y];
  304. if (FCaretPos.x+1 <= Line.Length())
  305. {
  306. Character = FMTLOAD(EDITOR_CHARACTER_STATUS,
  307. (int((unsigned char)Line[FCaretPos.x+1]), int((unsigned char)Line[FCaretPos.x+1])));
  308. }
  309. }
  310. StatusBar->Panels->Items[2]->Caption = Character;
  311. }
  312. StatusBar->Panels->Items[3]->Caption =
  313. (EditorMemo->Modified ? LoadStr(EDITOR_MODIFIED) : AnsiString(""));
  314. EditorActions->UpdateAction(SaveAction);
  315. }
  316. //---------------------------------------------------------------------------
  317. void __fastcall TEditorForm::EditorMemoMouseUp(TObject * /*Sender*/,
  318. TMouseButton /*Button*/, TShiftState /*Shift*/, int /*X*/, int /*Y*/)
  319. {
  320. UpdateControls();
  321. }
  322. //---------------------------------------------------------------------------
  323. void __fastcall TEditorForm::EditorMemoKeyUp(TObject * /*Sender*/,
  324. WORD & /*Key*/, TShiftState /*Shift*/)
  325. {
  326. UpdateControls();
  327. }
  328. //---------------------------------------------------------------------------
  329. void __fastcall TEditorForm::EditorMemoChange(TObject * /*Sender*/)
  330. {
  331. UpdateControls();
  332. }
  333. //---------------------------------------------------------------------------
  334. void __fastcall TEditorForm::FindDialogFind(TObject * /*Sender*/)
  335. {
  336. Find();
  337. }
  338. //---------------------------------------------------------------------------
  339. void __fastcall TEditorForm::Find()
  340. {
  341. int NewPos;
  342. int Replacements = 0;
  343. do
  344. {
  345. assert(FLastFindDialog);
  346. TSearchTypes SearchTypes;
  347. // length condition is there to improve performance when large
  348. // block is selected in editor
  349. if (FLastFindDialog == FReplaceDialog &&
  350. (FReplaceDialog->Options.Contains(frReplace) ||
  351. FReplaceDialog->Options.Contains(frReplaceAll)) &&
  352. FReplaceDialog->FindText.Length() == EditorMemo->SelLength &&
  353. AnsiSameText(FReplaceDialog->FindText, EditorMemo->SelText))
  354. {
  355. EditorMemo->SelText = FReplaceDialog->ReplaceText;
  356. Replacements++;
  357. }
  358. TEditorConfiguration EditorConfiguration = WinConfiguration->Editor;
  359. EditorConfiguration.FindText = FLastFindDialog->FindText;
  360. EditorConfiguration.ReplaceText = FReplaceDialog->ReplaceText;
  361. EditorConfiguration.FindMatchCase = FLastFindDialog->Options.Contains(frMatchCase);
  362. EditorConfiguration.FindWholeWord = FLastFindDialog->Options.Contains(frWholeWord);
  363. WinConfiguration->Editor = EditorConfiguration;
  364. if (EditorConfiguration.FindMatchCase)
  365. {
  366. SearchTypes << stMatchCase;
  367. }
  368. if (EditorConfiguration.FindWholeWord)
  369. {
  370. SearchTypes << stWholeWord;
  371. }
  372. NewPos = EditorMemo->FindText(EditorConfiguration.FindText,
  373. EditorMemo->SelLength ? EditorMemo->SelStart+1 : EditorMemo->SelStart,
  374. EditorMemo->Text.Length(), SearchTypes);
  375. if (NewPos >= 0)
  376. {
  377. EditorMemo->SelStart = NewPos;
  378. EditorMemo->SelLength = EditorConfiguration.FindText.Length();
  379. }
  380. if (FLastFindDialog->Handle)
  381. {
  382. PositionFindDialog(true);
  383. }
  384. if (NewPos < 0)
  385. {
  386. if ((Replacements == 0) || FReplaceDialog->Options.Contains(frReplaceAll))
  387. {
  388. // now Screen->ActiveForm can be NULL when other form was meanwhile
  389. // activated and then focus was returned back to "find" dialog
  390. // (non VCL form)
  391. if (Screen->ActiveForm != this)
  392. {
  393. SetFocus();
  394. FLastFindDialog->Execute();
  395. }
  396. if (Replacements == 0)
  397. {
  398. MessageDialog(FMTLOAD(EDITOR_FIND_END, (EditorConfiguration.FindText)), qtInformation, qaOK, HELP_NONE);
  399. }
  400. else if (FReplaceDialog->Options.Contains(frReplaceAll))
  401. {
  402. MessageDialog(FMTLOAD(EDITOR_REPLACE_END, (Replacements)), qtInformation, qaOK, HELP_NONE);
  403. }
  404. }
  405. }
  406. }
  407. while (NewPos >= 0 && FLastFindDialog == FReplaceDialog &&
  408. FReplaceDialog->Options.Contains(frReplaceAll));
  409. }
  410. //---------------------------------------------------------------------------
  411. void __fastcall TEditorForm::FormShow(TObject * /*Sender*/)
  412. {
  413. LoadFile();
  414. CutFormToDesktop(this);
  415. }
  416. //---------------------------------------------------------------------------
  417. void __fastcall TEditorForm::LoadFile()
  418. {
  419. EditorMemo->Lines->LoadFromFile(FFileName);
  420. EditorMemo->Modified = false;
  421. FCaretPos.x = -1;
  422. ApplyConfiguration();
  423. }
  424. //---------------------------------------------------------------------------
  425. bool __fastcall TEditorForm::CursorInUpperPart()
  426. {
  427. HFONT OldFont;
  428. void *DC;
  429. TTextMetric TM;
  430. TRect Rect;
  431. DC = GetDC(EditorMemo->Handle);
  432. OldFont = SelectObject(DC, EditorMemo->Font->Handle);
  433. try
  434. {
  435. GetTextMetrics(DC, &TM);
  436. EditorMemo->Perform(EM_GETRECT, 0, ((int)&Rect));
  437. }
  438. __finally
  439. {
  440. SelectObject(DC, OldFont);
  441. ReleaseDC(EditorMemo->Handle, DC);
  442. }
  443. int VisibleLines = (Rect.Bottom - Rect.Top) / (TM.tmHeight + TM.tmExternalLeading);
  444. int FirstLine = SendMessage(EditorMemo->Handle, EM_GETFIRSTVISIBLELINE, 0, 0);
  445. TPoint CaretPos = EditorMemo->CaretPos;
  446. return (CaretPos.y - FirstLine) < VisibleLines / 2;
  447. }
  448. //---------------------------------------------------------------------------
  449. void __fastcall TEditorForm::PositionFindDialog(bool VerticalOnly)
  450. {
  451. assert(FLastFindDialog);
  452. if (!VerticalOnly)
  453. {
  454. FLastFindDialog->Left = Left + EditorMemo->Left + EditorMemo->Width / 2 - 100;
  455. }
  456. FLastFindDialog->Top = Top + EditorMemo->Top + (EditorMemo->Height / 4) +
  457. (CursorInUpperPart() ? (EditorMemo->Height / 2) : 0) - 40;
  458. }
  459. //---------------------------------------------------------------------------
  460. void __fastcall TEditorForm::StartFind(bool Find)
  461. {
  462. AnsiString Text = EditorMemo->SelText;
  463. TFindOptions Options;
  464. Options << frHideUpDown; // not implemented
  465. Options << frShowHelp;
  466. if (Text.IsEmpty())
  467. {
  468. Text = WinConfiguration->Editor.FindText;
  469. }
  470. TFindDialog * Dialog = Find ? FFindDialog : FReplaceDialog;
  471. if (FLastFindDialog && Dialog != FLastFindDialog && FLastFindDialog->Handle)
  472. {
  473. FLastFindDialog->CloseDialog();
  474. }
  475. FLastFindDialog = Dialog;
  476. if (!Text.IsEmpty())
  477. {
  478. FLastFindDialog->FindText = Text;
  479. }
  480. FReplaceDialog->ReplaceText = WinConfiguration->Editor.ReplaceText;
  481. if (WinConfiguration->Editor.FindMatchCase)
  482. {
  483. Options << frMatchCase;
  484. }
  485. if (WinConfiguration->Editor.FindWholeWord)
  486. {
  487. Options << frWholeWord;
  488. }
  489. FLastFindDialog->Options = Options;
  490. if (!FLastFindDialog->Handle)
  491. {
  492. PositionFindDialog(false);
  493. }
  494. FLastFindDialog->Execute();
  495. }
  496. //---------------------------------------------------------------------------
  497. void __fastcall TEditorForm::GoToLine()
  498. {
  499. AnsiString Str;
  500. if (InputDialog(LoadStr(EDITOR_GO_TO_LINE), LoadStr(EDITOR_LINE_NUMBER), Str))
  501. {
  502. int Line = StrToIntDef(Str, -1);
  503. if (Line <= 0 || Line > EditorMemo->Lines->Count)
  504. {
  505. throw Exception(LoadStr(EDITOR_INVALID_LINE));
  506. }
  507. else
  508. {
  509. EditorMemo->CaretPos = TPoint(0, Line-1);
  510. }
  511. }
  512. }
  513. //---------------------------------------------------------------------------
  514. void __fastcall TEditorForm::FormClose(TObject * /*Sender*/,
  515. TCloseAction & Action)
  516. {
  517. // Preferably announce closure here as this is called from within TForm::Close(),
  518. // so the annoucement will be synchronous (and editor manager thus
  519. // will consider the form to be really closed and will not block
  520. // application closure).
  521. // However FormClose is not called when form is closed due to
  522. // application exit, so there is last resort call from destructor.
  523. DoWindowClose();
  524. FCloseAnnounced = true;
  525. Action = caFree;
  526. }
  527. //---------------------------------------------------------------------------
  528. void __fastcall TEditorForm::DoWindowClose()
  529. {
  530. if (FOnWindowClose != NULL)
  531. {
  532. try
  533. {
  534. FOnWindowClose(this);
  535. }
  536. catch(Exception & E)
  537. {
  538. ShowExtendedException(&E);
  539. }
  540. }
  541. }
  542. //---------------------------------------------------------------------------
  543. void __fastcall TEditorForm::SetShowWindowButton(bool value)
  544. {
  545. if (value != ShowWindowButton)
  546. {
  547. FShowWindowButton = value;
  548. RecreateWnd();
  549. }
  550. }
  551. //---------------------------------------------------------------------------
  552. void __fastcall TEditorForm::CreateParams(TCreateParams & Params)
  553. {
  554. TForm::CreateParams(Params);
  555. if (ShowWindowButton)
  556. {
  557. Params.WndParent = GetDesktopWindow();
  558. }
  559. }
  560. //---------------------------------------------------------------------------