WMSearchComponents.Mod 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. MODULE WMSearchComponents; (** AUTHOR "staubesv"; PURPOSE "Search components"; *)
  2. IMPORT
  3. Inputs, Strings, Texts, TextUtilities, UTF8Strings, WMWindowManager,
  4. WMRectangles, WMGraphics, WMMessages, WMComponents, WMStandardComponents, WMTextView, WMEditors;
  5. CONST
  6. SearchStringMaxLen = 128;
  7. TYPE
  8. SearchString* = ARRAY SearchStringMaxLen OF CHAR;
  9. UcsSearchString* = ARRAY SearchStringMaxLen OF Texts.Char32;
  10. StackData = POINTER TO ARRAY OF LONGINT;
  11. PositionStack = OBJECT
  12. VAR
  13. data: StackData;
  14. size, top: LONGINT;
  15. PROCEDURE & Init*;
  16. BEGIN
  17. size := 32;
  18. NEW(data, 32);
  19. END Init;
  20. PROCEDURE Push(l: LONGINT);
  21. BEGIN
  22. IF top = size THEN Expand END;
  23. data[top] := l;
  24. INC(top);
  25. END Push;
  26. PROCEDURE Pop(): LONGINT;
  27. VAR val: LONGINT;
  28. BEGIN
  29. IF top > 0 THEN
  30. DEC(top);
  31. val := data[top];
  32. ELSE
  33. val := -1;
  34. END;
  35. RETURN val;
  36. END Pop;
  37. PROCEDURE Expand;
  38. VAR
  39. newSize, i: LONGINT;
  40. newData: StackData;
  41. BEGIN
  42. newSize := 2*size;
  43. NEW(newData, newSize);
  44. FOR i := 0 TO size-1 DO
  45. data[i] := newData[i];
  46. END;
  47. size := newSize;
  48. data := newData;
  49. END Expand;
  50. PROCEDURE Invalidate;
  51. BEGIN
  52. IF size > 32 THEN Init END;
  53. top := 0;
  54. END Invalidate;
  55. END PositionStack;
  56. TYPE
  57. Highlight = POINTER TO RECORD
  58. this : WMTextView.Highlight;
  59. next : Highlight;
  60. END;
  61. Highlights = OBJECT
  62. VAR
  63. textView : WMTextView.TextView;
  64. highlights : Highlight;
  65. PROCEDURE Add(from, to : LONGINT);
  66. VAR h : Highlight;
  67. BEGIN {EXCLUSIVE}
  68. NEW(h); h.next := highlights; highlights := h;
  69. h.this := textView.CreateHighlight();
  70. h.this.SetColor(LONGINT(0FFFF0080H));
  71. h.this.SetKind(WMTextView.HLOver);
  72. h.this.SetFromTo(from, to);
  73. END Add;
  74. PROCEDURE RemoveAll;
  75. VAR h : Highlight;
  76. BEGIN {EXCLUSIVE}
  77. h := highlights;
  78. WHILE (h # NIL) DO
  79. textView.RemoveHighlight(h.this);
  80. h := h.next;
  81. END;
  82. highlights := NIL;
  83. END RemoveAll;
  84. PROCEDURE &Init*(textView : WMTextView.TextView);
  85. BEGIN
  86. ASSERT(textView # NIL);
  87. SELF.textView := textView;
  88. END Init;
  89. END Highlights;
  90. TYPE
  91. SearchPanel* = OBJECT(WMComponents.VisualComponent)
  92. VAR
  93. wrap, caseSensitive, backwards*, highlightAll : BOOLEAN;
  94. upperPanel, lowerPanel: WMStandardComponents.Panel;
  95. searchBtn, replBtn, replAllBtn, closeBtn, wrapBtn, caseSensitiveBtn, directionBtn, markAllBtn : WMStandardComponents.Button;
  96. searchEdit-, replEdit-: WMEditors.Editor;
  97. searchLabel, replLabel: WMStandardComponents.Label;
  98. textView: WMTextView.TextView;
  99. text: Texts.Text;
  100. pos, len: LONGINT;
  101. hitCount : LONGINT;
  102. posValid : BOOLEAN;
  103. positionStack: PositionStack;
  104. highlights : Highlights;
  105. lastPos : LONGINT;
  106. lastBackwards : BOOLEAN;
  107. PROCEDURE & Init*;
  108. BEGIN
  109. Init^;
  110. SetNameAsString(StrSearchPanel);
  111. wrap := FALSE;
  112. caseSensitive := TRUE;
  113. highlightAll := FALSE;
  114. hitCount := 0;
  115. backwards := FALSE;
  116. lastPos := -1; lastBackwards := FALSE;
  117. NEW(upperPanel);
  118. upperPanel.alignment.Set(WMComponents.AlignTop);
  119. upperPanel.bounds.SetHeight(20);
  120. AddInternalComponent(upperPanel);
  121. NEW(searchLabel);
  122. searchLabel.alignment.Set(WMComponents.AlignLeft);
  123. searchLabel.bounds.SetWidth(40); searchLabel.fillColor.Set(LONGINT(0FFFFFFFFH));
  124. searchLabel.alignH.Set(WMGraphics.AlignCenter);
  125. searchLabel.SetCaption("Search");
  126. upperPanel.AddInternalComponent(searchLabel);
  127. NEW(searchEdit);
  128. searchEdit.alignment.Set(WMComponents.AlignLeft);
  129. searchEdit.bounds.SetWidth(200); searchEdit.multiLine.Set(FALSE);
  130. searchEdit.tv.borders.Set(WMRectangles.MakeRect(3, 3, 1, 1));
  131. searchEdit.tv.showBorder.Set(TRUE);
  132. searchEdit.fillColor.Set(LONGINT(0FFFFFFFFH));
  133. searchEdit.onEnter.Add(SearchHandler);
  134. searchEdit.text.onTextChanged.Add(TextChanged);
  135. searchEdit.tv.SetExtFocusHandler(FocusHandler);
  136. searchEdit.tv.textAlignV.Set(WMGraphics.AlignCenter);
  137. upperPanel.AddInternalComponent(searchEdit);
  138. NEW(replLabel);
  139. replLabel.alignment.Set(WMComponents.AlignLeft);
  140. replLabel.bounds.SetWidth(50); replLabel.fillColor.Set(LONGINT(0FFFFFFFFH));
  141. replLabel.alignH.Set(WMGraphics.AlignCenter);
  142. replLabel.SetCaption("Replace");
  143. upperPanel.AddInternalComponent(replLabel);
  144. NEW(replEdit);
  145. replEdit.alignment.Set(WMComponents.AlignClient);
  146. replEdit.bounds.SetWidth(150); replEdit.multiLine.Set(FALSE);
  147. replEdit.tv.borders.Set(WMRectangles.MakeRect(3, 3, 1, 1));
  148. replEdit.tv.showBorder.Set(TRUE);
  149. replEdit.tv.textAlignV.Set(WMGraphics.AlignCenter);
  150. replEdit.fillColor.Set(LONGINT(0FFFFFFFFH));
  151. replEdit.onEnter.Add(ReplaceHandler);
  152. upperPanel.AddInternalComponent(replEdit);
  153. NEW(lowerPanel);
  154. lowerPanel.alignment.Set(WMComponents.AlignTop);
  155. lowerPanel.bounds.SetHeight(20);
  156. AddInternalComponent(lowerPanel);
  157. NEW(searchBtn);
  158. searchBtn.alignment.Set(WMComponents.AlignLeft);
  159. searchBtn.caption.SetAOC("Search");
  160. searchBtn.bounds.SetWidth(80);
  161. searchBtn.onClick.Add(SearchHandler);
  162. lowerPanel.AddInternalComponent(searchBtn);
  163. NEW(replBtn);
  164. replBtn.alignment.Set(WMComponents.AlignLeft);
  165. replBtn.caption.SetAOC("Replace");
  166. replBtn.bounds.SetWidth(80);
  167. replBtn.onClick.Add(ReplaceHandler);
  168. lowerPanel.AddInternalComponent(replBtn);
  169. NEW(replAllBtn);
  170. replAllBtn.alignment.Set(WMComponents.AlignLeft);
  171. replAllBtn.caption.SetAOC("Replace All");
  172. replAllBtn.bounds.SetWidth(80);
  173. replAllBtn.onClick.Add(ReplaceAllHandler);
  174. lowerPanel.AddInternalComponent(replAllBtn);
  175. NEW(wrapBtn);
  176. wrapBtn.alignment.Set(WMComponents.AlignLeft);
  177. wrapBtn.caption.SetAOC("Wrap");
  178. wrapBtn.isToggle.Set(TRUE);
  179. wrapBtn.SetPressed(wrap);
  180. wrapBtn.onClick.Add(WrapHandler);
  181. lowerPanel.AddInternalComponent(wrapBtn);
  182. NEW(caseSensitiveBtn);
  183. caseSensitiveBtn.alignment.Set(WMComponents.AlignLeft);
  184. caseSensitiveBtn.caption.SetAOC("CaseSensitive");
  185. caseSensitiveBtn.isToggle.Set(TRUE);
  186. caseSensitiveBtn.SetPressed(caseSensitive);
  187. caseSensitiveBtn.bounds.SetWidth(80);
  188. caseSensitiveBtn.onClick.Add(CaseSensitiveHandler);
  189. lowerPanel.AddInternalComponent(caseSensitiveBtn);
  190. NEW(directionBtn);
  191. directionBtn.alignment.Set(WMComponents.AlignLeft);
  192. directionBtn.caption.SetAOC("Backwards");
  193. directionBtn.isToggle.Set(TRUE);
  194. directionBtn.SetPressed(backwards);
  195. directionBtn.bounds.SetWidth(80);
  196. directionBtn.onClick.Add(DirectionHandler);
  197. lowerPanel.AddInternalComponent(directionBtn);
  198. NEW(markAllBtn);
  199. markAllBtn.alignment.Set(WMComponents.AlignLeft);
  200. markAllBtn.bounds.SetWidth(80);
  201. markAllBtn.caption.SetAOC("Highlight");
  202. markAllBtn.isToggle.Set(TRUE);
  203. markAllBtn.SetPressed(highlightAll);
  204. markAllBtn.onClick.Add(HighlightAllHandler);
  205. lowerPanel.AddInternalComponent(markAllBtn);
  206. NEW(closeBtn);
  207. closeBtn.alignment.Set(WMComponents.AlignLeft);
  208. closeBtn.caption.SetAOC("Close");
  209. closeBtn.bounds.SetWidth(80);
  210. closeBtn.onClick.Add(CloseHandler);
  211. lowerPanel.AddInternalComponent(closeBtn);
  212. NEW(positionStack);
  213. END Init;
  214. PROCEDURE SetToLastSelection*;
  215. VAR
  216. currentSearchString, searchString : SearchString;
  217. selectionText : Texts.Text; from, to : Texts.TextPosition;
  218. a, b : LONGINT;
  219. BEGIN
  220. searchString := "";
  221. IF Texts.GetLastSelection(selectionText, from, to) THEN
  222. selectionText.AcquireRead;
  223. a := MIN(from.GetPosition(), to.GetPosition());
  224. b := MAX(from.GetPosition(), to.GetPosition());
  225. IF ((b - a) <= SearchStringMaxLen) THEN
  226. TextUtilities.SubTextToStr(selectionText, a, b - a, searchString);
  227. END;
  228. Strings.TrimWS(searchString);
  229. selectionText.ReleaseRead;
  230. END;
  231. searchEdit.GetAsString(currentSearchString);
  232. IF (searchString # "") & (searchString # currentSearchString) THEN
  233. searchEdit.SetAsString(searchString);
  234. searchEdit.text.AcquireRead;
  235. searchEdit.tv.selection.SetFromTo(0, searchEdit.text.GetLength());
  236. searchEdit.text.ReleaseRead;
  237. END;
  238. END SetToLastSelection;
  239. PROCEDURE SetText*(t: Texts.Text);
  240. BEGIN
  241. text := t;
  242. posValid := FALSE
  243. END SetText;
  244. PROCEDURE SetTextView*(tv: WMTextView.TextView);
  245. BEGIN
  246. IF (textView # tv) THEN
  247. textView := tv;
  248. IF highlights # NIL THEN
  249. DisableUpdate; highlights.RemoveAll; EnableUpdate; textView.Invalidate;
  250. END;
  251. NEW(highlights, textView);
  252. END;
  253. posValid := FALSE;
  254. END SetTextView;
  255. PROCEDURE ToggleVisibility*;
  256. VAR searchString : SearchString;
  257. BEGIN
  258. IF ~visible.Get() THEN
  259. visible.Set(TRUE);
  260. searchEdit.SetAsString("");
  261. SetToLastSelection;
  262. searchEdit.SetFocus;
  263. ELSE
  264. searchEdit.GetAsString(searchString);
  265. IF searchString = "" THEN
  266. replEdit.SetAsString("");
  267. visible.Set(FALSE);
  268. IF (textView # NIL) THEN textView.SetFocus; END;
  269. ELSE
  270. searchEdit.SetAsString("");
  271. searchEdit.SetFocus;
  272. END;
  273. END;
  274. END ToggleVisibility;
  275. PROCEDURE HandlePreviousNext*(forward : BOOLEAN);
  276. VAR oldBackwards : BOOLEAN;
  277. BEGIN
  278. IF (textView # NIL) THEN textView.SetFocus; END;
  279. IF visible.Get() THEN
  280. oldBackwards := backwards;
  281. backwards := ~forward;
  282. SearchHandler(NIL, NIL);
  283. backwards := oldBackwards;
  284. END;
  285. END HandlePreviousNext;
  286. PROCEDURE HandleTab*() : BOOLEAN;
  287. BEGIN
  288. IF (visible.Get()) THEN
  289. (* hack - should not access hasFocus field *)
  290. IF searchEdit.tv.hasFocus THEN
  291. replEdit.SetFocus;
  292. replEdit.Invalidate;
  293. RETURN TRUE;
  294. ELSIF replEdit.tv.hasFocus THEN
  295. searchEdit.SetFocus;
  296. searchEdit.Invalidate;
  297. RETURN TRUE;
  298. END;
  299. END;
  300. RETURN FALSE;
  301. END HandleTab;
  302. PROCEDURE HandleShortcut*(ucs : LONGINT; flags : SET; keysym : LONGINT) : BOOLEAN;
  303. PROCEDURE ControlKeyDown(flags : SET) : BOOLEAN;
  304. BEGIN
  305. RETURN (flags * Inputs.Ctrl # {}) & (flags - Inputs.Ctrl = {});
  306. END ControlKeyDown;
  307. BEGIN
  308. IF (keysym = 06H) & ControlKeyDown(flags)THEN (* CTRL-F *)
  309. ToggleVisibility;
  310. ELSIF (keysym= 0EH) & ControlKeyDown(flags) THEN (* CTRL-N *)
  311. HandlePreviousNext(TRUE);
  312. ELSIF (keysym = 10H) & ControlKeyDown(flags) THEN (* CTRL-P *)
  313. HandlePreviousNext(FALSE);
  314. ELSIF (keysym = Inputs.KsTab) & (flags = {}) THEN (* TAB *)
  315. RETURN HandleTab();
  316. ELSE
  317. RETURN FALSE; (* Key not handled *)
  318. END;
  319. RETURN TRUE;
  320. END HandleShortcut;
  321. PROCEDURE FocusHandler(hasFocus: BOOLEAN);
  322. BEGIN
  323. IF textView = NIL THEN RETURN END;
  324. IF hasFocus THEN
  325. pos := textView.cursor.GetPosition();
  326. positionStack.Invalidate;
  327. END;
  328. END FocusHandler;
  329. PROCEDURE WrapHandler(sender, data: ANY);
  330. BEGIN
  331. wrap := ~wrap;
  332. END WrapHandler;
  333. PROCEDURE CaseSensitiveHandler(sender, data : ANY);
  334. BEGIN
  335. caseSensitive := ~caseSensitive;
  336. END CaseSensitiveHandler;
  337. PROCEDURE DirectionHandler(sender, data : ANY);
  338. BEGIN
  339. backwards := ~backwards;
  340. END DirectionHandler;
  341. PROCEDURE HighlightAllHandler(sender, data : ANY);
  342. BEGIN
  343. highlightAll := ~highlightAll;
  344. IF highlightAll THEN
  345. DisableUpdate; SearchAndHighlightAll; EnableUpdate; markAllBtn.Invalidate; textView.Invalidate;
  346. ELSE
  347. DisableUpdate; RemoveHighlights; EnableUpdate; textView.Invalidate;
  348. markAllBtn.SetCaption("Highlight");
  349. END;
  350. END HighlightAllHandler;
  351. PROCEDURE TextChanged(sender, data: ANY);
  352. VAR
  353. changeInfo: Texts.TextChangeInfo;
  354. from : LONGINT;
  355. BEGIN
  356. IF ~IsCallFromSequencer() THEN
  357. (* We need to use DisableUpdate/EnableUpdate in order to get reasonable performance... This requires
  358. the call to come from the component's sequencer *)
  359. sequencer.ScheduleEvent(SELF.TextChanged, sender, data)
  360. ELSE
  361. IF data IS Texts.TextChangeInfo THEN
  362. changeInfo := data(Texts.TextChangeInfo);
  363. IF (changeInfo.op = Texts.OpInsert) & (changeInfo.len = 1) THEN
  364. positionStack.Push(pos);
  365. IF highlightAll THEN DisableUpdate; RemoveHighlights; SearchAndHighlightAll; EnableUpdate; markAllBtn.Invalidate; textView.Invalidate; END;
  366. SearchAndHighlight(pos);
  367. ELSIF (changeInfo.op = Texts.OpDelete) & (changeInfo.len = 1) THEN
  368. from := positionStack.Pop();
  369. IF from = 1 THEN from := pos END;
  370. IF highlightAll THEN DisableUpdate; RemoveHighlights; SearchAndHighlightAll; EnableUpdate; markAllBtn.Invalidate; textView.Invalidate;END;
  371. SearchAndHighlight(from);
  372. ELSE
  373. positionStack.Invalidate();
  374. IF (textView # NIL) THEN textView.selection.SetFromTo(0, 0); END;
  375. IF highlightAll THEN DisableUpdate; RemoveHighlights; EnableUpdate; markAllBtn.Invalidate; textView.Invalidate; END;
  376. END;
  377. END;
  378. END;
  379. END TextChanged;
  380. PROCEDURE SearchHandler*(sender, data: ANY);
  381. BEGIN
  382. IF textView = NIL THEN RETURN END;
  383. SearchAndHighlight(textView.cursor.GetPosition());
  384. END SearchHandler;
  385. PROCEDURE ReplaceHandler(sender, data: ANY);
  386. VAR replStr : SearchString; ucsStr: UcsSearchString; idx: LONGINT;
  387. BEGIN
  388. IF text = NIL THEN RETURN END;
  389. IF posValid THEN
  390. replEdit.GetAsString(replStr);
  391. idx := 0;
  392. UTF8Strings.UTF8toUnicode(replStr, ucsStr, idx);
  393. text.AcquireWrite();
  394. Replace(ucsStr);
  395. text.ReleaseWrite();
  396. Highlight;
  397. SearchHandler(sender, data);
  398. END;
  399. END ReplaceHandler;
  400. PROCEDURE ReplaceAllHandler(sender, data: ANY);
  401. VAR
  402. searchStr, replStr : SearchString;
  403. ucsSearchStr, ucsReplStr : UcsSearchString;
  404. oldBackwards : BOOLEAN;
  405. idx: LONGINT;
  406. BEGIN
  407. IF text = NIL THEN RETURN END;
  408. replEdit.GetAsString(replStr);
  409. idx := 0;
  410. UTF8Strings.UTF8toUnicode(replStr, ucsReplStr, idx);
  411. searchEdit.GetAsString(searchStr);
  412. idx := 0;
  413. UTF8Strings.UTF8toUnicode(searchStr, ucsSearchStr, idx);
  414. text.AcquireWrite();
  415. text.AcquireRead();
  416. oldBackwards := backwards;
  417. backwards := FALSE;
  418. Search(ucsSearchStr, 0);
  419. WHILE posValid DO
  420. Replace(ucsReplStr);
  421. Search(ucsSearchStr, pos + len);
  422. END;
  423. backwards := oldBackwards;
  424. text.ReleaseRead();
  425. text.ReleaseWrite();
  426. END ReplaceAllHandler;
  427. PROCEDURE Replace(CONST ucsStr: ARRAY OF Texts.Char32);
  428. BEGIN
  429. text.Delete(pos, len);
  430. text.InsertUCS32(pos, ucsStr);
  431. len := TextUtilities.UCS32StrLength(ucsStr);
  432. posValid := FALSE;
  433. END Replace;
  434. PROCEDURE Search(CONST ucsStr: ARRAY OF Texts.Char32; from: LONGINT);
  435. BEGIN
  436. IF ucsStr[0] = 0 THEN posValid := FALSE; RETURN; END;
  437. IF caseSensitive & ~backwards THEN
  438. pos := TextUtilities.Pos(ucsStr, from, text);
  439. ELSE
  440. (* We want to search the text that's on the left hand side of the cursor. If we start searching at position 'from',
  441. we also consider the character at the current cursor position, which is on the left hand side of the cursro *)
  442. IF (backwards) & (from > 1) THEN DEC(from); END;
  443. pos := TextUtilities.GenericPos(ucsStr, from, text, ~caseSensitive, backwards);
  444. END;
  445. len := TextUtilities.UCS32StrLength(ucsStr);
  446. IF pos > -1 THEN posValid := TRUE
  447. ELSE posValid := FALSE
  448. END;
  449. END Search;
  450. PROCEDURE SearchAndHighlight(from: LONGINT);
  451. VAR searchStr : SearchString; ucsStr: UcsSearchString; length, idx : LONGINT;
  452. BEGIN
  453. IF text = NIL THEN RETURN END;
  454. searchEdit.GetAsString(searchStr);
  455. IF searchStr # "" THEN
  456. idx := 0;
  457. UTF8Strings.UTF8toUnicode(searchStr, ucsStr, idx);
  458. text.AcquireRead();
  459. Search(ucsStr, from);
  460. (* Detect whether we have found the last search result again but in different search direction *)
  461. IF (((pos = lastPos) OR (lastPos = -1)) & (lastBackwards # backwards)) THEN
  462. length := TextUtilities.UCS32StrLength(ucsStr);
  463. IF backwards THEN
  464. IF from >= length - 1 THEN
  465. Search(ucsStr, from - Strings.Length(searchStr));
  466. END;
  467. ELSE
  468. IF from + length < text.GetLength() THEN
  469. Search(ucsStr, from + Strings.Length(searchStr));
  470. END;
  471. END;
  472. END;
  473. IF (pos = -1) & wrap THEN
  474. IF backwards THEN
  475. Search(ucsStr, text.GetLength() - 1);
  476. ELSE
  477. Search(ucsStr, 0);
  478. END;
  479. END;
  480. text.ReleaseRead();
  481. END;
  482. lastPos := pos; lastBackwards := backwards;
  483. Highlight;
  484. END SearchAndHighlight;
  485. PROCEDURE SearchAndHighlightAll;
  486. VAR
  487. searchString : SearchString;
  488. ucsStr : UcsSearchString;
  489. caption : ARRAY 32 OF CHAR; nbr : ARRAY 12 OF CHAR;
  490. length, idx, pos : LONGINT; from : LONGINT;
  491. BEGIN
  492. IF (text = NIL) OR (highlights = NIL) THEN RETURN; END;
  493. hitCount := 0;
  494. searchEdit.GetAsString(searchString);
  495. IF searchString # "" THEN
  496. idx := 0;
  497. UTF8Strings.UTF8toUnicode(searchString, ucsStr, idx);
  498. length := TextUtilities.UCS32StrLength(ucsStr);
  499. from := 0; pos := 0;
  500. WHILE(pos >=0) DO
  501. text.AcquireRead;
  502. IF caseSensitive THEN pos := TextUtilities.Pos(ucsStr, from, text);
  503. ELSE pos := TextUtilities.GenericPos(ucsStr, from, text, ~caseSensitive, FALSE);
  504. END;
  505. text.ReleaseRead;
  506. IF pos >= 0 THEN
  507. INC(hitCount);
  508. highlights.Add(pos, pos + length);
  509. from := pos + 1;
  510. END;
  511. END;
  512. END;
  513. caption := "Highlight:";
  514. Strings.IntToStr(hitCount, nbr); Strings.Append(caption, nbr);
  515. markAllBtn.caption.SetAOC(caption);
  516. END SearchAndHighlightAll;
  517. PROCEDURE RemoveHighlights;
  518. BEGIN
  519. IF (highlights # NIL) THEN highlights.RemoveAll; END;
  520. END RemoveHighlights;
  521. PROCEDURE Highlight;
  522. VAR searchString : SearchString;
  523. BEGIN
  524. IF textView = NIL THEN RETURN END;
  525. IF posValid THEN
  526. textView.selection.SetFrom(pos);
  527. textView.selection.SetTo(pos + len);
  528. IF backwards THEN
  529. textView.cursor.SetPosition(pos);
  530. ELSE
  531. textView.cursor.SetPosition(pos + len);
  532. END;
  533. END;
  534. searchEdit.GetAsString(searchString);
  535. IF (searchString = "") THEN textView.selection.SetFromTo(0, 0); END;
  536. END Highlight;
  537. PROCEDURE CloseHandler(sender, data: ANY);
  538. BEGIN
  539. IF highlights # NIL THEN
  540. DisableUpdate; RemoveHighlights; EnableUpdate; textView.Invalidate;
  541. END;
  542. visible.Set(FALSE);
  543. IF textView # NIL THEN textView.selection.SetFromTo(0, 0); END;
  544. END CloseHandler;
  545. PROCEDURE SetSettings*(wrap, caseSensitive, backwards, highlightAll : BOOLEAN);
  546. BEGIN
  547. SELF.wrap := wrap;
  548. SELF.caseSensitive := caseSensitive;
  549. SELF.backwards := backwards;
  550. SELF.highlightAll := highlightAll;
  551. wrapBtn.SetPressed(wrap);
  552. caseSensitiveBtn.SetPressed(caseSensitive);
  553. directionBtn.SetPressed(backwards);
  554. markAllBtn.SetPressed(highlightAll);
  555. END SetSettings;
  556. PROCEDURE GetSettings*(VAR wrap, caseSensitive, backwards, highlightAll : BOOLEAN);
  557. BEGIN
  558. wrap := SELF.wrap;
  559. caseSensitive := SELF.caseSensitive;
  560. backwards := SELF.backwards;
  561. highlightAll := SELF.highlightAll;
  562. END GetSettings;
  563. PROCEDURE Finalize*;
  564. BEGIN
  565. Finalize^;
  566. IF searchEdit # NIL THEN searchEdit.text.onTextChanged.Remove(TextChanged); END;
  567. END Finalize;
  568. END SearchPanel;
  569. TYPE
  570. SearchWindow* = OBJECT(WMComponents.FormWindow)
  571. VAR
  572. searchPanel : SearchPanel;
  573. hasBeenClosed- : BOOLEAN;
  574. PROCEDURE SetTextView*(textView : WMTextView.TextView; text : Texts.Text);
  575. BEGIN
  576. ASSERT((textView # NIL) & (text # NIL));
  577. searchPanel.SetText(text);
  578. searchPanel.SetTextView(textView);
  579. END SetTextView;
  580. PROCEDURE &New*(textView : WMTextView.TextView; text : Texts.Text);
  581. BEGIN
  582. ASSERT((textView # NIL) & (text # NIL));
  583. hasBeenClosed := FALSE;
  584. NEW(searchPanel);
  585. searchPanel.fillColor.Set(WMGraphics.White);
  586. searchPanel.alignment.Set(WMComponents.AlignClient);
  587. searchPanel.bounds.SetExtents(700, 40);
  588. searchPanel.closeBtn.visible.Set(FALSE);
  589. SetTextView(textView, text);
  590. Init(searchPanel.bounds.GetWidth(), searchPanel.bounds.GetHeight(), FALSE);
  591. SetContent(searchPanel);
  592. SetTitle(Strings.NewString("Search"));
  593. WMWindowManager.DefaultAddWindow(SELF);
  594. END New;
  595. PROCEDURE Close*;
  596. BEGIN
  597. Close^;
  598. IF searchPanel.highlights # NIL THEN
  599. searchPanel.RemoveHighlights; searchPanel.textView.Invalidate;
  600. END;
  601. searchPanel.textView.selection.SetFromTo(0, 0);
  602. hasBeenClosed := TRUE;
  603. END Close;
  604. PROCEDURE HandleShortcut(ucs : LONGINT; flags : SET; keysym : LONGINT) : BOOLEAN;
  605. PROCEDURE ControlKeyDown(flags : SET) : BOOLEAN;
  606. BEGIN
  607. RETURN (flags * Inputs.Ctrl # {}) & (flags - Inputs.Ctrl = {});
  608. END ControlKeyDown;
  609. PROCEDURE HandlePreviousNext(forward : BOOLEAN);
  610. VAR oldBackwards : BOOLEAN;
  611. BEGIN
  612. IF searchPanel.visible.Get() THEN
  613. oldBackwards := searchPanel.backwards;
  614. searchPanel.backwards := ~forward;
  615. searchPanel.SearchHandler(NIL, NIL);
  616. searchPanel.backwards := oldBackwards;
  617. END;
  618. END HandlePreviousNext;
  619. BEGIN
  620. IF (keysym = 06H) & ControlKeyDown(flags)THEN (* CTRL-F *)
  621. searchPanel.searchEdit.SetAsString("");
  622. searchPanel.searchEdit.SetFocus;
  623. ELSIF (keysym= 0EH) & ControlKeyDown(flags) THEN (* CTRL-N *)
  624. HandlePreviousNext(TRUE);
  625. ELSIF (keysym = 10H) & ControlKeyDown(flags) THEN (* CTRL-P *)
  626. HandlePreviousNext(FALSE);
  627. ELSE
  628. RETURN FALSE; (* Key not handled *)
  629. END;
  630. RETURN TRUE;
  631. END HandleShortcut;
  632. PROCEDURE Handle*(VAR m: WMMessages.Message);
  633. BEGIN
  634. IF m.msgType = WMMessages.MsgKey THEN
  635. IF ~HandleShortcut(m.x, m.flags, m.y) THEN
  636. Handle^(m);
  637. END;
  638. ELSE Handle^(m)
  639. END
  640. END Handle;
  641. END SearchWindow;
  642. VAR
  643. StrSearchPanel : Strings.String;
  644. BEGIN
  645. StrSearchPanel := Strings.NewString("SearchPanel");
  646. END WMSearchComponents.