tb_style_edit.cpp 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273
  1. // ================================================================================
  2. // == This file is a part of Turbo Badger. (C) 2011-2014, Emil Segerås ==
  3. // == See tb_core.h for more information. ==
  4. // ================================================================================
  5. #include "tb_style_edit.h"
  6. #include "tb_widgets_common.h"
  7. #include "tb_style_edit_content.h"
  8. #include "tb_system.h"
  9. #include "tb_tempbuffer.h"
  10. #include "tb_font_renderer.h"
  11. #include "utf8/utf8.h"
  12. #include <assert.h>
  13. namespace tb {
  14. #if 0 // Enable for some graphical debugging
  15. #define TMPDEBUG(expr) expr
  16. #define nTMPDEBUG(expr)
  17. #else
  18. #define TMPDEBUG(expr)
  19. #define nTMPDEBUG(expr) expr
  20. #endif
  21. const int TAB_SPACE = 4;
  22. const char *special_char_newln = "¶"; // 00B6 PILCROW SIGN
  23. const char *special_char_space = "·"; // 00B7 MIDDLE DOT
  24. const char *special_char_tab = "»"; // 00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
  25. const char *special_char_password = "•"; // 2022 BULLET
  26. static bool is_space(int8 c)
  27. {
  28. switch(c)
  29. {
  30. case ' ':
  31. return true;
  32. }
  33. return false;
  34. }
  35. static bool is_linebreak(int8 c)
  36. {
  37. switch(c)
  38. {
  39. case '\n':
  40. case '\r':
  41. case 0:
  42. return true;
  43. }
  44. return false;
  45. }
  46. static bool is_wordbreak(int8 c)
  47. {
  48. switch(c)
  49. {
  50. case 0:
  51. case '\n':
  52. case '\r':
  53. case '-':
  54. case '\t':
  55. case '\"':
  56. case '(':
  57. case ')':
  58. case '/':
  59. case '\\':
  60. case '*':
  61. case '+':
  62. case ',':
  63. case '.':
  64. case ';':
  65. case ':':
  66. case '>':
  67. case '<':
  68. case '&':
  69. case '#':
  70. case '!':
  71. case '=':
  72. case '[':
  73. case ']':
  74. case '{':
  75. case '}':
  76. case '^':
  77. return true;
  78. }
  79. return is_space(c);
  80. }
  81. /** Check if no line wrapping is allowed before the character at the given offset.
  82. The string must be null terminated. */
  83. static bool is_never_break_before(const char *str, int ofs)
  84. {
  85. switch (str[ofs])
  86. {
  87. case '\n':
  88. case '\r':
  89. case ' ':
  90. case '-':
  91. case '.':
  92. case ',':
  93. case ':':
  94. case ';':
  95. case '!':
  96. case '?':
  97. case ')':
  98. case ']':
  99. case '}':
  100. case '>':
  101. return true;
  102. case '\'':
  103. case '"':
  104. // Simple test if it's the first quote in a word surrounded by space.
  105. if (ofs > 0 && !is_space(str[ofs - 1]))
  106. return true;
  107. default:
  108. return false;
  109. }
  110. }
  111. /** Check if no line wrapping is allowed after the character at the given offset.
  112. The string must be null terminated. */
  113. static bool is_never_break_after(const char *str, int ofs)
  114. {
  115. switch (str[ofs])
  116. {
  117. case '(':
  118. case '[':
  119. case '{':
  120. case '<':
  121. return true;
  122. case '\'':
  123. case '"':
  124. // Simple test if it's the last quote in a word surrounded by space.
  125. if (!is_space(str[ofs+ 1]))
  126. return true;
  127. default:
  128. return false;
  129. }
  130. }
  131. static bool GetNextFragment(const char *text, TBTextFragmentContentFactory *content_factory, int *frag_len, bool *is_embed)
  132. {
  133. if (text[0] == '\t')
  134. {
  135. *frag_len = 1;
  136. return text[1] != 0;
  137. }
  138. else if (text[0] == 0) // happens when not setting text and maby when setting ""
  139. {
  140. *frag_len = 0;
  141. return false;
  142. }
  143. else if (text[0] == '\r' || text[0] == '\n')
  144. {
  145. int len = (text[0] == '\r' && text[1] == '\n') ? 2 : 1;
  146. *frag_len = len;
  147. return false;
  148. }
  149. else if (content_factory)
  150. {
  151. if (int content_len = content_factory->GetContent(text))
  152. {
  153. *frag_len = content_len;
  154. *is_embed = true;
  155. return text[content_len] != 0;
  156. }
  157. }
  158. int i = 0;
  159. while (!is_wordbreak(text[i]))
  160. i++;
  161. if (i == 0)
  162. if (is_wordbreak(text[i]))
  163. i++;
  164. *frag_len = i;
  165. if (text[i] == 0)
  166. return false;
  167. return true;
  168. }
  169. // == TBSelection ==================================================
  170. TBSelection::TBSelection(TBStyleEdit *styledit)
  171. : styledit(styledit)
  172. {
  173. }
  174. void TBSelection::CorrectOrder()
  175. {
  176. if (start.block == stop.block && start.ofs == stop.ofs)
  177. SelectNothing();
  178. else
  179. {
  180. if ((start.block == stop.block && start.ofs > stop.ofs) ||
  181. (start.block != stop.block && start.block->ypos > stop.block->ypos))
  182. {
  183. TBTextOfs tmp = start;
  184. start = stop;
  185. stop = tmp;
  186. }
  187. }
  188. }
  189. void TBSelection::CopyToClipboard()
  190. {
  191. if (IsSelected())
  192. {
  193. TBStr text;
  194. if (GetText(text))
  195. TBClipboard::SetText(text);
  196. }
  197. }
  198. void TBSelection::Invalidate() const
  199. {
  200. TBBlock *block = start.block;
  201. while (block)
  202. {
  203. block->Invalidate();
  204. if (block == stop.block)
  205. break;
  206. block = block->GetNext();
  207. }
  208. }
  209. void TBSelection::Select(const TBTextOfs &new_start, const TBTextOfs &new_stop)
  210. {
  211. Invalidate();
  212. start.Set(new_start);
  213. stop.Set(new_stop);
  214. CorrectOrder();
  215. Invalidate();
  216. }
  217. void TBSelection::Select(const TBPoint &from, const TBPoint &to)
  218. {
  219. Invalidate();
  220. styledit->caret.Place(from);
  221. start.Set(styledit->caret.pos);
  222. styledit->caret.Place(to);
  223. stop.Set(styledit->caret.pos);
  224. CorrectOrder();
  225. Invalidate();
  226. styledit->caret.UpdateWantedX();
  227. }
  228. void TBSelection::Select(int glob_ofs_from, int glob_ofs_to)
  229. {
  230. TBTextOfs ofs1, ofs2;
  231. ofs1.SetGlobalOfs(styledit, glob_ofs_from);
  232. ofs2.SetGlobalOfs(styledit, glob_ofs_to);
  233. Select(ofs1, ofs2);
  234. }
  235. void TBSelection::SelectToCaret(TBBlock *old_caret_block, int32 old_caret_ofs)
  236. {
  237. Invalidate();
  238. if (!start.block)
  239. {
  240. start.Set(old_caret_block, old_caret_ofs);
  241. stop.Set(styledit->caret.pos);
  242. }
  243. else
  244. {
  245. if (start.block == old_caret_block && start.ofs == old_caret_ofs)
  246. start.Set(styledit->caret.pos);
  247. else
  248. stop.Set(styledit->caret.pos);
  249. }
  250. CorrectOrder();
  251. Invalidate();
  252. }
  253. void TBSelection::SelectAll()
  254. {
  255. start.Set(styledit->blocks.GetFirst(), 0);
  256. stop.Set(styledit->blocks.GetLast(), styledit->blocks.GetLast()->str_len);
  257. Invalidate();
  258. }
  259. void TBSelection::SelectNothing()
  260. {
  261. Invalidate();
  262. start.Set(nullptr, 0);
  263. stop.Set(nullptr, 0);
  264. }
  265. bool TBSelection::IsBlockSelected(TBBlock *block) const
  266. {
  267. if (!IsSelected())
  268. return false;
  269. return block->ypos >= start.block->ypos && block->ypos <= stop.block->ypos;
  270. }
  271. bool TBSelection::IsFragmentSelected(TBTextFragment *elm) const
  272. {
  273. if (!IsSelected())
  274. return false;
  275. if (start.block == stop.block)
  276. {
  277. if (elm->block != start.block)
  278. return false;
  279. if (start.ofs < elm->ofs + elm->len && stop.ofs >= elm->ofs)
  280. return true;
  281. return false;
  282. }
  283. if (elm->block->ypos > start.block->ypos && elm->block->ypos < stop.block->ypos)
  284. return true;
  285. if (elm->block->ypos == start.block->ypos && elm->ofs + elm->len > start.ofs)
  286. return true;
  287. if (elm->block->ypos == stop.block->ypos && elm->ofs < stop.ofs)
  288. return true;
  289. return false;
  290. }
  291. bool TBSelection::IsSelected() const
  292. {
  293. return start.block ? true : false;
  294. }
  295. void TBSelection::RemoveContent()
  296. {
  297. if (!IsSelected())
  298. return;
  299. styledit->BeginLockScrollbars();
  300. if (start.block == stop.block)
  301. {
  302. if (!styledit->undoredo.applying)
  303. styledit->undoredo.Commit(styledit, start.GetGlobalOfs(styledit), stop.ofs - start.ofs, start.block->str.CStr() + start.ofs, false);
  304. start.block->RemoveContent(start.ofs, stop.ofs - start.ofs);
  305. }
  306. else
  307. {
  308. // Remove text in first block
  309. TBTempBuffer commit_string;
  310. int32 start_gofs = 0;
  311. if (!styledit->undoredo.applying)
  312. {
  313. start_gofs = start.GetGlobalOfs(styledit);
  314. commit_string.Append(start.block->str.CStr() + start.ofs, start.block->str_len - start.ofs);
  315. }
  316. start.block->RemoveContent(start.ofs, start.block->str_len - start.ofs);
  317. // Remove text in all block in between start and stop
  318. TBBlock *block = start.block->GetNext();
  319. while (block != stop.block)
  320. {
  321. if (!styledit->undoredo.applying)
  322. commit_string.Append(block->str, block->str_len);
  323. TBBlock *next = block->GetNext();
  324. styledit->blocks.Delete(block);
  325. block = next;
  326. }
  327. // Remove text in last block
  328. if (!styledit->undoredo.applying)
  329. {
  330. commit_string.Append(stop.block->str, stop.ofs);
  331. styledit->undoredo.Commit(styledit, start_gofs, commit_string.GetAppendPos(), commit_string.GetData(), false);
  332. }
  333. stop.block->RemoveContent(0, stop.ofs);
  334. }
  335. stop.block->Merge();
  336. start.block->Merge();
  337. styledit->caret.Place(start.block, start.ofs);
  338. styledit->caret.UpdateWantedX();
  339. SelectNothing();
  340. styledit->EndLockScrollbars();
  341. if (styledit->text_change_listener)
  342. styledit->text_change_listener->OnChange(styledit);
  343. }
  344. bool TBSelection::GetText(TBStr &text) const
  345. {
  346. if (!IsSelected())
  347. {
  348. text.Clear();
  349. return true;
  350. }
  351. if (start.block == stop.block)
  352. text.Append(start.block->str.CStr() + start.ofs, stop.ofs - start.ofs);
  353. else
  354. {
  355. TBTempBuffer buf;
  356. buf.Append(start.block->str.CStr() + start.ofs, start.block->str_len - start.ofs);
  357. TBBlock *block = start.block->GetNext();
  358. while (block != stop.block)
  359. {
  360. buf.Append(block->str, block->str_len);
  361. block = block->GetNext();
  362. }
  363. // FIX: Add methods to change data owner from temp buffer to string!
  364. buf.Append(stop.block->str, stop.ofs);
  365. text.Set((char*)buf.GetData(), buf.GetAppendPos());
  366. }
  367. return true;
  368. }
  369. // == TBTextOfs =========================================================================
  370. int32 TBTextOfs::GetGlobalOfs(TBStyleEdit *se) const
  371. {
  372. int32 gofs = 0;
  373. TBBlock *b = se->blocks.GetFirst();
  374. while (b && b != block)
  375. {
  376. gofs += b->str_len;
  377. b = b->GetNext();
  378. }
  379. gofs += ofs;
  380. return gofs;
  381. }
  382. bool TBTextOfs::SetGlobalOfs(TBStyleEdit *se, int32 gofs)
  383. {
  384. TBBlock *b = se->blocks.GetFirst();
  385. while (b)
  386. {
  387. int b_len = b->str_len;
  388. if (gofs < b_len)
  389. {
  390. block = b;
  391. ofs = gofs;
  392. return true;
  393. }
  394. gofs -= b_len;
  395. if (!gofs && !b->GetNext())
  396. {
  397. block = b;
  398. ofs = b->str.Length() + 1;
  399. return true;
  400. }
  401. b = b->GetNext();
  402. }
  403. assert(!"out of range! not a valid global offset!");
  404. return false;
  405. }
  406. // == TBCaret ============================================================================
  407. TBCaret::TBCaret(TBStyleEdit *styledit)
  408. : styledit(styledit)
  409. , x(0)
  410. , y(0)
  411. , width(2)
  412. , height(0)
  413. , wanted_x(0)
  414. , on(false)
  415. , prefer_first(true)
  416. {
  417. }
  418. void TBCaret::Invalidate()
  419. {
  420. if (styledit->listener)
  421. styledit->listener->Invalidate(TBRect(x - styledit->scroll_x, y - styledit->scroll_y, width, height));
  422. }
  423. void TBCaret::UpdatePos()
  424. {
  425. Invalidate();
  426. TBTextFragment *fragment = GetFragment();
  427. x = fragment->xpos + fragment->GetCharX(styledit->font, pos.ofs - fragment->ofs);
  428. y = fragment->ypos + pos.block->ypos;
  429. height = fragment->GetHeight(styledit->font);
  430. if (!height)
  431. {
  432. // If we don't have height, we're probably inside a style switch embed.
  433. y = fragment->line_ypos + pos.block->ypos;
  434. height = fragment->line_height;
  435. }
  436. Invalidate();
  437. }
  438. bool TBCaret::Move(bool forward, bool word)
  439. {
  440. // Make it stay on the same line if it reach the wrap point.
  441. prefer_first = forward;
  442. if (this->styledit->packed.password_on)
  443. word = false;
  444. int len = pos.block->str_len;
  445. if (word && !(forward && pos.ofs == len) && !(!forward && pos.ofs == 0))
  446. {
  447. const char *str = pos.block->str;
  448. if (forward)
  449. {
  450. if (is_linebreak(str[pos.ofs]))
  451. {
  452. pos.ofs++;
  453. }
  454. else if (is_wordbreak(str[pos.ofs]))
  455. {
  456. while (pos.ofs < len && is_wordbreak(str[pos.ofs]) && !is_linebreak(str[pos.ofs]))
  457. pos.ofs++;
  458. }
  459. else
  460. {
  461. while (pos.ofs < len && !is_wordbreak(str[pos.ofs]))
  462. pos.ofs++;
  463. while (pos.ofs < len && is_space(str[pos.ofs]))
  464. pos.ofs++;
  465. }
  466. }
  467. else if (pos.ofs > 0)
  468. {
  469. while (pos.ofs > 0 && is_space(str[pos.ofs - 1]))
  470. pos.ofs--;
  471. if (pos.ofs > 0 && is_wordbreak(str[pos.ofs - 1]))
  472. {
  473. while (pos.ofs > 0 && is_wordbreak(str[pos.ofs - 1]))
  474. pos.ofs--;
  475. }
  476. else
  477. {
  478. while (pos.ofs > 0 && !is_wordbreak(str[pos.ofs - 1]))
  479. pos.ofs--;
  480. }
  481. }
  482. }
  483. else
  484. {
  485. if (forward && pos.ofs >= pos.block->str_len && pos.block->GetNext())
  486. {
  487. pos.block = pos.block->GetNext();
  488. pos.ofs = 0;
  489. }
  490. else if (!forward && pos.ofs <= 0 && pos.block->prev)
  491. {
  492. pos.block = pos.block->GetPrev();
  493. pos.ofs = pos.block->str_len;
  494. }
  495. else
  496. {
  497. int i = pos.ofs;
  498. if (forward)
  499. utf8::move_inc(pos.block->str, &i, pos.block->str_len);
  500. else
  501. utf8::move_dec(pos.block->str, &i);
  502. pos.ofs = i;
  503. }
  504. }
  505. return Place(pos.block, pos.ofs, true, forward);
  506. }
  507. bool TBCaret::Place(const TBPoint &point)
  508. {
  509. TBBlock *block = styledit->FindBlock(point.y);
  510. TBTextFragment *fragment = block->FindFragment(point.x, point.y - block->ypos);
  511. int ofs = fragment->ofs + fragment->GetCharOfs(styledit->font, point.x - fragment->xpos);
  512. if (Place(block, ofs))
  513. {
  514. if (GetFragment() != fragment)
  515. {
  516. prefer_first = !prefer_first;
  517. Place(block, ofs);
  518. }
  519. return true;
  520. }
  521. return false;
  522. }
  523. void TBCaret::Place(TB_CARET_POS place)
  524. {
  525. if (place == TB_CARET_POS_BEGINNING)
  526. Place(styledit->blocks.GetFirst(), 0);
  527. else if (place == TB_CARET_POS_END)
  528. Place(styledit->blocks.GetLast(), styledit->blocks.GetLast()->str_len);
  529. }
  530. bool TBCaret::Place(TBBlock *block, int ofs, bool allow_snap, bool snap_forward)
  531. {
  532. if (block)
  533. {
  534. while (block->GetNext() && ofs > block->str_len)
  535. {
  536. ofs -= block->str_len;
  537. block = block->GetNext();
  538. }
  539. while (block->prev && ofs < 0)
  540. {
  541. block = block->GetPrev();
  542. ofs += block->str_len;
  543. }
  544. if (ofs < 0)
  545. ofs = 0;
  546. if (ofs > block->str_len)
  547. ofs = block->str_len;
  548. // Avoid being inside linebreak
  549. if (allow_snap)
  550. {
  551. TBTextFragment *fragment = block->FindFragment(ofs);
  552. if (ofs > fragment->ofs && fragment->IsBreak())
  553. {
  554. if (snap_forward && block->GetNext())
  555. {
  556. block = block->GetNext();
  557. ofs = 0;
  558. }
  559. else
  560. ofs = fragment->ofs;
  561. }
  562. }
  563. }
  564. bool changed = (pos.block != block || pos.ofs != ofs);
  565. pos.Set(block, ofs);
  566. if (block)
  567. UpdatePos();
  568. return changed;
  569. }
  570. void TBCaret::AvoidLineBreak()
  571. {
  572. TBTextFragment *fragment = GetFragment();
  573. if (pos.ofs > fragment->ofs && fragment->IsBreak())
  574. pos.ofs = fragment->ofs;
  575. UpdatePos();
  576. }
  577. void TBCaret::Paint(int32 translate_x, int32 translate_y)
  578. {
  579. // if (on && !(styledit->select_state && styledit->selection.IsSelected()))
  580. if (on || styledit->select_state)
  581. {
  582. styledit->listener->DrawCaret(TBRect(translate_x + x, translate_y + y, width, height));
  583. }
  584. }
  585. void TBCaret::ResetBlink()
  586. {
  587. styledit->listener->CaretBlinkStop();
  588. on = true;
  589. styledit->listener->CaretBlinkStart();
  590. }
  591. void TBCaret::UpdateWantedX()
  592. {
  593. wanted_x = x;
  594. }
  595. TBTextFragment *TBCaret::GetFragment()
  596. {
  597. return pos.block->FindFragment(pos.ofs, prefer_first);
  598. }
  599. void TBCaret::SwitchBlock(bool second)
  600. {
  601. }
  602. void TBCaret::SetGlobalOfs(int32 gofs, bool allow_snap, bool snap_forward)
  603. {
  604. TBTextOfs ofs;
  605. if (ofs.SetGlobalOfs(styledit, gofs))
  606. Place(ofs.block, ofs.ofs, allow_snap, snap_forward);
  607. }
  608. // == TBTextProps =======================================================================
  609. TBTextProps::TBTextProps(const TBFontDescription &font_desc, const TBColor &text_color)
  610. {
  611. base_data.font_desc = font_desc;
  612. base_data.text_color = text_color;
  613. base_data.underline = false;
  614. data = &base_data;
  615. }
  616. TBTextProps::Data *TBTextProps::Push()
  617. {
  618. if (Data *new_data = new Data)
  619. {
  620. data_list.AddLast(new_data);
  621. new_data->font_desc = data->font_desc;
  622. new_data->text_color = data->text_color;
  623. new_data->underline = data->underline;
  624. data = new_data;
  625. return data;
  626. }
  627. return nullptr;
  628. }
  629. void TBTextProps::Pop()
  630. {
  631. if (!data_list.GetLast())
  632. return; // Unballanced or we previosly got OOM.
  633. data_list.Delete(data_list.GetLast());
  634. data = data_list.GetLast() ? data_list.GetLast() : &base_data;
  635. }
  636. TBFontFace *TBTextProps::GetFont()
  637. {
  638. return g_font_manager->GetFontFace(data->font_desc);
  639. }
  640. // ============================================================================
  641. TBBlock::TBBlock(TBStyleEdit *styledit)
  642. : styledit(styledit)
  643. , ypos(0)
  644. , height(0)
  645. , align(styledit->align)
  646. , line_width_max(0)
  647. , str_len(0)
  648. {
  649. }
  650. TBBlock::~TBBlock()
  651. {
  652. Clear();
  653. }
  654. void TBBlock::Clear()
  655. {
  656. fragments.DeleteAll();
  657. }
  658. void TBBlock::Set(const char *newstr, int32 len)
  659. {
  660. str.Set(newstr, len);
  661. str_len = len;
  662. Split();
  663. Layout(true, true);
  664. }
  665. void TBBlock::SetAlign(TB_TEXT_ALIGN align)
  666. {
  667. if (this->align == align)
  668. return;
  669. this->align = align;
  670. Layout(false, false);
  671. }
  672. int32 TBBlock::InsertText(int32 ofs, const char *text, int32 len, bool allow_line_recurse)
  673. {
  674. styledit->BeginLockScrollbars();
  675. int first_line_len = len;
  676. for(int i = 0; i < len; i++)
  677. if (text[i] == '\r' || text[i] == '\n')
  678. {
  679. first_line_len = i;
  680. // Include the line break too but not for single lines
  681. if (!styledit->packed.multiline_on)
  682. break;
  683. if (text[i] == '\r' && text[i + 1] == '\n')
  684. first_line_len++;
  685. first_line_len++;
  686. break;
  687. }
  688. int32 inserted_len = first_line_len;
  689. str.Insert(ofs, text, first_line_len);
  690. str_len += first_line_len;
  691. Split();
  692. Layout(true, true);
  693. // Add the rest which was after the linebreak.
  694. if (allow_line_recurse && styledit->packed.multiline_on)
  695. {
  696. // Instead of recursively calling InsertText, we will loop through them all here
  697. TBBlock *next_block = GetNext();
  698. const char *next_line_ptr = &text[first_line_len];
  699. int remaining = len - first_line_len;
  700. while (remaining > 0)
  701. {
  702. if (!next_block)
  703. {
  704. next_block = new TBBlock(styledit);
  705. styledit->blocks.AddLast(next_block);
  706. }
  707. int consumed = next_block->InsertText(0, next_line_ptr, remaining, false);
  708. next_line_ptr += consumed;
  709. inserted_len += consumed;
  710. remaining -= consumed;
  711. next_block = next_block->GetNext();
  712. }
  713. }
  714. styledit->EndLockScrollbars();
  715. return inserted_len;
  716. }
  717. void TBBlock::RemoveContent(int32 ofs, int32 len)
  718. {
  719. if (!len)
  720. return;
  721. str.Remove(ofs, len);
  722. str_len -= len;
  723. Layout(true, true);
  724. }
  725. void TBBlock::Split()
  726. {
  727. int32 len = str_len;
  728. int brlen = 1; // FIX: skip ending newline fragment but not if there is several newlines and check for singleline newline.
  729. if (len > 1 && str.CStr()[len - 2] == '\r' && str.CStr()[len - 1] == '\n')
  730. brlen++;
  731. len -= brlen;
  732. for(int i = 0; i < len; i++)
  733. {
  734. if (is_linebreak(str.CStr()[i]))
  735. {
  736. TBBlock *block = new TBBlock(styledit);
  737. if (!block)
  738. return;
  739. styledit->blocks.AddAfter(block, this);
  740. if (i < len - 1 && str.CStr()[i] == '\r' && str.CStr()[i + 1] == '\n')
  741. i++;
  742. i++;
  743. len = len + brlen - i;
  744. block->Set(str.CStr() + i, len);
  745. str.Remove(i, len);
  746. str_len -= len;
  747. break;
  748. }
  749. }
  750. }
  751. void TBBlock::Merge()
  752. {
  753. TBBlock *next_block = GetNext();
  754. if (next_block && !fragments.GetLast()->IsBreak())
  755. {
  756. str.Append(GetNext()->str);
  757. str_len = str.Length();
  758. styledit->blocks.Delete(next_block);
  759. height = 0; // Ensure that Layout propagate height to remaining blocks.
  760. Layout(true, true);
  761. }
  762. }
  763. int32 TBBlock::CalculateTabWidth(TBFontFace *font, int32 xpos) const
  764. {
  765. int tabsize = font->GetStringWidth("x", 1) * TAB_SPACE;
  766. int p2 = int(xpos / tabsize) * tabsize + tabsize;
  767. return p2 - xpos;
  768. }
  769. int32 TBBlock::FirstNonTabPos() const
  770. {
  771. for (int i = 0; i < str.Length(); i++)
  772. if (str[i] != ' ' && str[i] != '\t')
  773. return i;
  774. return 0;
  775. }
  776. int32 TBBlock::CalculateStringWidth(TBFontFace *font, const char *str, int len) const
  777. {
  778. if (styledit->packed.password_on)
  779. {
  780. // Convert the length in number or characters, since that's what matters for password width.
  781. len = utf8::count_characters(str, len);
  782. return font->GetStringWidth(special_char_password) * len;
  783. }
  784. return font->GetStringWidth(str, len);
  785. }
  786. int32 TBBlock::CalculateLineHeight(TBFontFace *font) const
  787. {
  788. return font->GetHeight();
  789. }
  790. int32 TBBlock::CalculateBaseline(TBFontFace *font) const
  791. {
  792. return font->GetAscent();
  793. }
  794. int TBBlock::GetStartIndentation(TBFontFace *font, int first_line_len) const
  795. {
  796. // Lines beginning with whitespace or list points, should
  797. // indent to the same as the beginning when wrapped.
  798. int indentation = 0;
  799. int i = 0;
  800. while (i < first_line_len)
  801. {
  802. const char *current_str = str.CStr() + i;
  803. UCS4 uc = utf8::decode_next(str, &i, first_line_len);
  804. switch (uc)
  805. {
  806. case '\t':
  807. indentation += CalculateTabWidth(font, indentation);
  808. continue;
  809. case ' ':
  810. case '-':
  811. case '*':
  812. indentation += CalculateStringWidth(font, current_str, 1);
  813. continue;
  814. case 0x2022: // BULLET
  815. indentation += CalculateStringWidth(font, current_str, 3);
  816. continue;
  817. };
  818. break;
  819. }
  820. return indentation;
  821. }
  822. void TBBlock::Layout(bool update_fragments, bool propagate_height)
  823. {
  824. // Create fragments from the word fragments
  825. if (update_fragments || !fragments.GetFirst())
  826. {
  827. Clear();
  828. int ofs = 0;
  829. const char *text = str;
  830. while (true)
  831. {
  832. int frag_len;
  833. bool is_embed = false;
  834. bool more = GetNextFragment(&text[ofs], styledit->packed.styling_on ? styledit->content_factory : nullptr, &frag_len, &is_embed);
  835. TBTextFragment *fragment = new TBTextFragment();
  836. if (!fragment)
  837. break;
  838. fragment->Init(this, ofs, frag_len);
  839. if (is_embed)
  840. fragment->content = styledit->content_factory->CreateFragmentContent(&text[ofs], frag_len);
  841. fragments.AddLast(fragment);
  842. ofs += frag_len;
  843. if (!more)
  844. break;
  845. }
  846. }
  847. // Layout
  848. if (styledit->layout_width <= 0 && styledit->GetSizeAffectsLayout())
  849. // Don't layout if we have no space. This will happen when setting text
  850. // before the widget has been layouted. We will relayout when we are resized.
  851. return;
  852. int old_line_width_max = line_width_max;
  853. line_width_max = 0;
  854. int line_ypos = 0;
  855. int first_line_indentation = 0;
  856. TBTextFragment *first_fragment_on_line = fragments.GetFirst();
  857. while (first_fragment_on_line)
  858. {
  859. int line_width = 0;
  860. // Get the last fragment that should be laid out on the line while
  861. // calculating line width and preliminary x positions for the fragments.
  862. TBTextFragment *last_fragment_on_line = fragments.GetLast();
  863. if (styledit->packed.wrapping)
  864. {
  865. // If we should wrap, search for the last allowed break point before the overflow.
  866. TBTextFragment *allowed_last_fragment = nullptr;
  867. int line_xpos = first_line_indentation;
  868. for (TBTextFragment *fragment = first_fragment_on_line; fragment; fragment = fragment->GetNext())
  869. {
  870. // Give the fragment the current x. Then tab widths are calculated properly in GetWidth.
  871. fragment->xpos = line_xpos;
  872. int fragment_w = fragment->GetWidth(styledit->font);
  873. // Check if we overflow
  874. bool overflow = line_xpos + fragment_w > styledit->layout_width;
  875. if (overflow && allowed_last_fragment)
  876. {
  877. last_fragment_on_line = allowed_last_fragment;
  878. break;
  879. }
  880. // Check if this is a allowed break position
  881. if (fragment->GetAllowBreakAfter())
  882. {
  883. if (!fragment->GetNext() || fragment->GetNext()->GetAllowBreakBefore())
  884. {
  885. allowed_last_fragment = fragment;
  886. line_width = line_xpos + fragment_w;
  887. }
  888. }
  889. line_xpos += fragment_w;
  890. }
  891. if (!allowed_last_fragment)
  892. line_width = line_xpos;
  893. }
  894. else
  895. {
  896. // When wrapping is off, just measure and set pos.
  897. line_width = first_line_indentation;
  898. for (TBTextFragment *fragment = first_fragment_on_line; fragment; fragment = fragment->GetNext())
  899. {
  900. fragment->xpos = line_width;
  901. line_width += fragment->GetWidth(styledit->font);
  902. }
  903. }
  904. // Commit line - Layout each fragment on the line.
  905. int line_height = 0;
  906. int line_baseline = 0;
  907. TBTextFragment *fragment = first_fragment_on_line;
  908. while (fragment)
  909. {
  910. line_height = MAX(fragment->GetHeight(styledit->font), line_height);
  911. line_baseline = MAX(fragment->GetBaseline(styledit->font), line_baseline);
  912. // These positions are not final. Will be adjusted below.
  913. fragment->ypos = line_ypos;
  914. if (fragment == last_fragment_on_line)
  915. break;
  916. fragment = fragment->GetNext();
  917. }
  918. // Adjust the position of fragments on the line - now when we know the line totals.
  919. // x change because of alignment, y change because of fragment baseline vs line baseline.
  920. int32 xofs = 0;
  921. if (align == TB_TEXT_ALIGN_RIGHT)
  922. xofs = styledit->layout_width - line_width;
  923. else if (align == TB_TEXT_ALIGN_CENTER)
  924. xofs = (styledit->layout_width - line_width) / 2;
  925. int adjusted_line_height = line_height;
  926. fragment = first_fragment_on_line;
  927. while (fragment)
  928. {
  929. // The fragment need to know these later.
  930. fragment->line_ypos = line_ypos;
  931. fragment->line_height = line_height;
  932. // Adjust the position
  933. fragment->ypos += line_baseline - fragment->GetBaseline(styledit->font);
  934. fragment->xpos += xofs;
  935. // We now know the final position so update content.
  936. fragment->UpdateContentPos();
  937. // Total line height may now have changed a bit.
  938. adjusted_line_height = MAX(line_baseline - fragment->GetBaseline(styledit->font) + fragment->GetHeight(styledit->font), adjusted_line_height);
  939. if (fragment == last_fragment_on_line)
  940. break;
  941. fragment = fragment->GetNext();
  942. }
  943. // Update line_height set on fragments if needed
  944. if (line_height != adjusted_line_height)
  945. {
  946. for (fragment = first_fragment_on_line; fragment != last_fragment_on_line->GetNext(); fragment = fragment->GetNext())
  947. fragment->line_height = adjusted_line_height;
  948. }
  949. line_width_max = MAX(line_width_max, line_width);
  950. // This was the first line so calculate the indentation to use for the other lines.
  951. if (styledit->packed.wrapping && first_fragment_on_line == fragments.GetFirst())
  952. first_line_indentation = GetStartIndentation(styledit->font, last_fragment_on_line->ofs + last_fragment_on_line->len);
  953. // Consume line
  954. line_ypos += adjusted_line_height;
  955. first_fragment_on_line = last_fragment_on_line->GetNext();
  956. }
  957. ypos = GetPrev() ? GetPrev()->ypos + GetPrev()->height : 0;
  958. SetSize(old_line_width_max, line_width_max, line_ypos, propagate_height);
  959. Invalidate();
  960. }
  961. void TBBlock::SetSize(int32 old_w, int32 new_w, int32 new_h, bool propagate_height)
  962. {
  963. // Later: could optimize with Scroll here.
  964. int32 dh = new_h - height;
  965. height = new_h;
  966. if (dh != 0 && propagate_height)
  967. {
  968. TBBlock *block = GetNext();
  969. while (block)
  970. {
  971. block->ypos = block->GetPrev()->ypos + block->GetPrev()->height;
  972. block->Invalidate();
  973. block = block->GetNext();
  974. }
  975. }
  976. // Update content_width and content_height
  977. // content_width can only be calculated in constant time if we grow larger.
  978. // If we shrink our width and where equal to content_width, we don't know
  979. // how wide the widest block is and we set a flag to update it when needed.
  980. if (!styledit->packed.wrapping && !styledit->packed.multiline_on)
  981. styledit->content_width = new_w;
  982. else if (new_w > styledit->content_width)
  983. styledit->content_width = new_w;
  984. else if (new_w < old_w && old_w == styledit->content_width)
  985. styledit->packed.calculate_content_width_needed = 1;
  986. styledit->content_height = styledit->blocks.GetLast()->ypos + styledit->blocks.GetLast()->height;
  987. if (styledit->listener && styledit->packed.lock_scrollbars_counter == 0 && propagate_height)
  988. styledit->listener->UpdateScrollbars();
  989. }
  990. TBTextFragment *TBBlock::FindFragment(int32 ofs, bool prefer_first) const
  991. {
  992. TBTextFragment *fragment = fragments.GetFirst();
  993. while (fragment)
  994. {
  995. if (prefer_first && ofs <= fragment->ofs + fragment->len)
  996. return fragment;
  997. if (!prefer_first && ofs < fragment->ofs + fragment->len)
  998. return fragment;
  999. fragment = fragment->GetNext();
  1000. }
  1001. return fragments.GetLast();
  1002. }
  1003. TBTextFragment *TBBlock::FindFragment(int32 x, int32 y) const
  1004. {
  1005. TBTextFragment *fragment = fragments.GetFirst();
  1006. while (fragment)
  1007. {
  1008. if (y < fragment->line_ypos + fragment->line_height)
  1009. {
  1010. if (x < fragment->xpos + fragment->GetWidth(styledit->font))
  1011. return fragment;
  1012. if (fragment->GetNext() && fragment->GetNext()->line_ypos > fragment->line_ypos)
  1013. return fragment;
  1014. }
  1015. fragment = fragment->GetNext();
  1016. }
  1017. return fragments.GetLast();
  1018. }
  1019. void TBBlock::Invalidate()
  1020. {
  1021. if (styledit->listener)
  1022. styledit->listener->Invalidate(TBRect(0, - styledit->scroll_y + ypos, styledit->layout_width, height));
  1023. }
  1024. void TBBlock::BuildSelectionRegion(int32 translate_x, int32 translate_y, TBTextProps *props,
  1025. TBRegion &bg_region, TBRegion &fg_region)
  1026. {
  1027. if (!styledit->selection.IsBlockSelected(this))
  1028. return;
  1029. TBTextFragment *fragment = fragments.GetFirst();
  1030. while (fragment)
  1031. {
  1032. fragment->BuildSelectionRegion(translate_x, translate_y + ypos, props, bg_region, fg_region);
  1033. fragment = fragment->GetNext();
  1034. }
  1035. }
  1036. void TBBlock::Paint(int32 translate_x, int32 translate_y, TBTextProps *props)
  1037. {
  1038. TMPDEBUG(styledit->listener->DrawRect(TBRect(translate_x, translate_y + ypos, styledit->layout_width, height), TBColor(255, 200, 0, 128)));
  1039. TBTextFragment *fragment = fragments.GetFirst();
  1040. while (fragment)
  1041. {
  1042. fragment->Paint(translate_x, translate_y + ypos, props);
  1043. fragment = fragment->GetNext();
  1044. }
  1045. }
  1046. // == TBTextFragment =========================================================================
  1047. TBTextFragment::~TBTextFragment()
  1048. {
  1049. delete content;
  1050. }
  1051. void TBTextFragment::Init(TBBlock *block, uint16 ofs, uint16 len)
  1052. {
  1053. this->block = block; this->ofs = ofs; this->len = len;
  1054. }
  1055. void TBTextFragment::UpdateContentPos()
  1056. {
  1057. if (content)
  1058. content->UpdatePos(xpos, ypos + block->ypos);
  1059. }
  1060. void TBTextFragment::BuildSelectionRegion(int32 translate_x, int32 translate_y, TBTextProps *props,
  1061. TBRegion &bg_region, TBRegion &fg_region)
  1062. {
  1063. if (!block->styledit->selection.IsFragmentSelected(this))
  1064. return;
  1065. int x = translate_x + xpos;
  1066. int y = translate_y + ypos;
  1067. TBFontFace *font = props->GetFont();
  1068. if (content)
  1069. {
  1070. // Selected embedded content should add to the foreground region.
  1071. fg_region.IncludeRect(TBRect(x, y, GetWidth(font), GetHeight(font)));
  1072. return;
  1073. }
  1074. // Selected text should add to the backgroud region.
  1075. TBSelection *sel = &block->styledit->selection;
  1076. int sofs1 = sel->start.block == block ? sel->start.ofs : 0;
  1077. int sofs2 = sel->stop.block == block ? sel->stop.ofs : block->str_len;
  1078. sofs1 = MAX(sofs1, (int)ofs);
  1079. sofs2 = MIN(sofs2, (int)(ofs + len));
  1080. int s1x = GetStringWidth(font, block->str.CStr() + ofs, sofs1 - ofs);
  1081. int s2x = GetStringWidth(font, block->str.CStr() + sofs1, sofs2 - sofs1);
  1082. bg_region.IncludeRect(TBRect(x + s1x, y, s2x, GetHeight(font)));
  1083. }
  1084. void TBTextFragment::Paint(int32 translate_x, int32 translate_y, TBTextProps *props)
  1085. {
  1086. TBStyleEditListener *listener = block->styledit->listener;
  1087. int x = translate_x + xpos;
  1088. int y = translate_y + ypos;
  1089. TBColor color = props->data->text_color;
  1090. TBFontFace *font = props->GetFont();
  1091. if (block->styledit->text_theme)
  1092. color = block->styledit->text_theme->themeColors[themeColor];
  1093. if (content)
  1094. {
  1095. content->Paint(this, translate_x, translate_y, props);
  1096. return;
  1097. }
  1098. TMPDEBUG(listener->DrawRect(TBRect(x, y, GetWidth(font), GetHeight(font)), TBColor(255, 255, 255, 128)));
  1099. if (block->styledit->packed.password_on)
  1100. {
  1101. int cw = block->CalculateStringWidth(font, special_char_password);
  1102. int num_char = utf8::count_characters(Str(), len);
  1103. for(int i = 0; i < num_char; i++)
  1104. listener->DrawString(x + i * cw, y, font, color, special_char_password);
  1105. }
  1106. else if (block->styledit->packed.show_whitespace)
  1107. {
  1108. if (IsTab())
  1109. listener->DrawString(x, y, font, color, special_char_tab);
  1110. else if (IsBreak())
  1111. listener->DrawString(x, y, font, color, special_char_newln);
  1112. else if (IsSpace())
  1113. listener->DrawString(x, y, font, color, special_char_space);
  1114. else
  1115. listener->DrawString(x, y, font, color, Str(), len);
  1116. }
  1117. else if (!IsTab() && !IsBreak() && !IsSpace())
  1118. listener->DrawString(x, y, font, color, Str(), len);
  1119. if (props->data->underline)
  1120. {
  1121. int line_h = font->GetHeight() / 16;
  1122. line_h = MAX(line_h, 1);
  1123. listener->DrawRectFill(TBRect(x, y + GetBaseline(font) + 1, GetWidth(font), line_h), color);
  1124. }
  1125. }
  1126. void TBTextFragment::Click(int button, uint32 modifierkeys)
  1127. {
  1128. if (content)
  1129. content->Click(this, button, modifierkeys);
  1130. }
  1131. int32 TBTextFragment::GetWidth(TBFontFace *font)
  1132. {
  1133. if (content)
  1134. return content->GetWidth(font, this);
  1135. if (IsBreak())
  1136. return 0;
  1137. if (IsTab())
  1138. return block->CalculateTabWidth(font, xpos);
  1139. return block->CalculateStringWidth(font, block->str.CStr() + ofs, len);
  1140. }
  1141. int32 TBTextFragment::GetHeight(TBFontFace *font)
  1142. {
  1143. if (content)
  1144. return content->GetHeight(font, this);
  1145. return block->CalculateLineHeight(font);
  1146. }
  1147. int32 TBTextFragment::GetBaseline(TBFontFace *font)
  1148. {
  1149. if (content)
  1150. return content->GetBaseline(font, this);
  1151. return block->CalculateBaseline(font);
  1152. }
  1153. int32 TBTextFragment::GetCharX(TBFontFace *font, int32 ofs)
  1154. {
  1155. assert(ofs >= 0 && ofs <= len);
  1156. if (IsEmbedded() || IsTab())
  1157. return ofs == 0 ? 0 : GetWidth(font);
  1158. if (IsBreak())
  1159. return 0;
  1160. return block->CalculateStringWidth(font, block->str.CStr() + this->ofs, ofs);
  1161. }
  1162. int32 TBTextFragment::GetCharOfs(TBFontFace *font, int32 x)
  1163. {
  1164. if (IsEmbedded() || IsTab())
  1165. return x > GetWidth(font) / 2 ? 1 : 0;
  1166. if (IsBreak())
  1167. return 0;
  1168. const char *str = block->str.CStr() + ofs;
  1169. int i = 0;
  1170. while (i < len)
  1171. {
  1172. int pos = i;
  1173. utf8::move_inc(str, &i, len);
  1174. int last_char_len = i - pos;
  1175. // Always measure from the beginning of the fragment because of eventual kerning & text shaping etc.
  1176. int width_except_last_char = block->CalculateStringWidth(font, str, i - last_char_len);
  1177. int width = block->CalculateStringWidth(font, str, i);
  1178. if (x < width - (width - width_except_last_char) / 2)
  1179. return pos;
  1180. }
  1181. return len;
  1182. }
  1183. int32 TBTextFragment::GetGlobalOfs() const
  1184. {
  1185. int32 gofs = 0;
  1186. TBBlock *b = block->styledit->blocks.GetFirst();
  1187. while (b && b != block)
  1188. {
  1189. gofs += b->str_len;
  1190. b = b->GetNext();
  1191. }
  1192. gofs += ofs;
  1193. return gofs;
  1194. }
  1195. int32 TBTextFragment::GetStringWidth(TBFontFace *font, const char *str, int len)
  1196. {
  1197. if (IsTab())
  1198. return len == 0 ? 0 : block->CalculateTabWidth(font, xpos);
  1199. if (IsBreak())
  1200. return len == 0 ? 0 : 8;
  1201. return block->CalculateStringWidth(font, str, len);
  1202. }
  1203. bool TBTextFragment::IsBreak() const
  1204. {
  1205. return Str()[0] == '\r' || Str()[0] == '\n';
  1206. }
  1207. bool TBTextFragment::IsSpace() const
  1208. {
  1209. return is_space(Str()[0]);
  1210. }
  1211. bool TBTextFragment::IsTab() const
  1212. {
  1213. return Str()[0] == '\t';
  1214. }
  1215. bool TBTextFragment::GetAllowBreakBefore() const
  1216. {
  1217. if (content)
  1218. return content->GetAllowBreakBefore();
  1219. if (len && !is_never_break_before(block->str.CStr(), ofs))
  1220. return true;
  1221. return false;
  1222. }
  1223. bool TBTextFragment::GetAllowBreakAfter() const
  1224. {
  1225. if (content)
  1226. return content->GetAllowBreakAfter();
  1227. if (len && !is_never_break_after(block->str.CStr(), ofs + len - 1))
  1228. return true;
  1229. return false;
  1230. }
  1231. // ============================================================================
  1232. TBStyleEdit::TBStyleEdit()
  1233. : listener(nullptr)
  1234. , content_factory(&default_content_factory)
  1235. , text_change_listener(nullptr)
  1236. , text_theme(nullptr)
  1237. , layout_width(0)
  1238. , layout_height(0)
  1239. , content_width(0)
  1240. , content_height(0)
  1241. , caret(nullptr)
  1242. , selection(nullptr)
  1243. , scroll_x(0)
  1244. , scroll_y(0)
  1245. , autocomplete_visible(false)
  1246. , select_state(0)
  1247. , mousedown_fragment(nullptr)
  1248. , font(nullptr)
  1249. , align(TB_TEXT_ALIGN_LEFT)
  1250. , packed_init(0)
  1251. {
  1252. caret.styledit = this;
  1253. selection.styledit = this;
  1254. TMPDEBUG(packed.show_whitespace = true);
  1255. font_desc = g_font_manager->GetDefaultFontDescription();
  1256. font = g_font_manager->GetFontFace(font_desc);
  1257. #ifdef TB_TARGET_WINDOWS
  1258. packed.win_style_br = 1;
  1259. #endif
  1260. packed.selection_on = 1;
  1261. Clear();
  1262. }
  1263. TBStyleEdit::~TBStyleEdit()
  1264. {
  1265. listener->CaretBlinkStop();
  1266. Clear(false);
  1267. }
  1268. void TBStyleEdit::SetListener(TBStyleEditListener *listener)
  1269. {
  1270. this->listener = listener;
  1271. }
  1272. void TBStyleEdit::SetContentFactory(TBTextFragmentContentFactory *content_factory)
  1273. {
  1274. if (content_factory)
  1275. this->content_factory = content_factory;
  1276. else
  1277. this->content_factory = &default_content_factory;
  1278. }
  1279. void TBStyleEdit::SetFont(const TBFontDescription &font_desc)
  1280. {
  1281. if (this->font_desc == font_desc)
  1282. return;
  1283. this->font_desc = font_desc;
  1284. font = g_font_manager->GetFontFace(font_desc);
  1285. Reformat(true);
  1286. }
  1287. void TBStyleEdit::Clear(bool init_new)
  1288. {
  1289. undoredo.Clear(true, true);
  1290. selection.SelectNothing();
  1291. if (init_new && blocks.GetFirst() && IsEmpty())
  1292. return;
  1293. for (TBBlock *block = blocks.GetFirst(); block; block = block->GetNext())
  1294. block->Invalidate();
  1295. blocks.DeleteAll();
  1296. if (init_new)
  1297. {
  1298. blocks.AddLast(new TBBlock(this));
  1299. blocks.GetFirst()->Set("", 0);
  1300. }
  1301. caret.Place(blocks.GetFirst(), 0);
  1302. caret.UpdateWantedX();
  1303. }
  1304. void TBStyleEdit::ScrollIfNeeded(bool x, bool y)
  1305. {
  1306. if (layout_width <= 0 || layout_height <= 0)
  1307. return; // This is likely during construction before layout.
  1308. int32 newx = scroll_x, newy = scroll_y;
  1309. if (x)
  1310. {
  1311. if (caret.x - scroll_x < 0)
  1312. newx = caret.x;
  1313. if (caret.x + caret.width - scroll_x > layout_width)
  1314. newx = caret.x + caret.width - layout_width;
  1315. }
  1316. if (y)
  1317. {
  1318. if (caret.y - scroll_y < 0)
  1319. newy = caret.y;
  1320. if (caret.y + caret.height - scroll_y > layout_height)
  1321. newy = caret.y + caret.height - layout_height;
  1322. }
  1323. SetScrollPos(newx, newy);
  1324. }
  1325. void TBStyleEdit::SetScrollPos(int32 x, int32 y)
  1326. {
  1327. x = MIN(x, GetContentWidth() - layout_width);
  1328. y = MIN(y, GetContentHeight() - layout_height);
  1329. x = MAX(x, 0);
  1330. y = MAX(y, 0);
  1331. if (!packed.multiline_on)
  1332. y = 0;
  1333. int dx = scroll_x - x;
  1334. int dy = scroll_y - y;
  1335. if (dx || dy)
  1336. {
  1337. scroll_x = x;
  1338. scroll_y = y;
  1339. listener->Scroll(dx, dy);
  1340. }
  1341. }
  1342. void TBStyleEdit::BeginLockScrollbars()
  1343. {
  1344. packed.lock_scrollbars_counter++;
  1345. }
  1346. void TBStyleEdit::EndLockScrollbars()
  1347. {
  1348. packed.lock_scrollbars_counter--;
  1349. if (listener && packed.lock_scrollbars_counter == 0)
  1350. listener->UpdateScrollbars();
  1351. }
  1352. void TBStyleEdit::SetLayoutSize(int32 width, int32 height, bool is_virtual_reformat)
  1353. {
  1354. if (width == layout_width && height == layout_height)
  1355. return;
  1356. bool reformat = layout_width != width;
  1357. layout_width = width;
  1358. layout_height = height;
  1359. if (reformat && GetSizeAffectsLayout())
  1360. Reformat(false);
  1361. caret.UpdatePos();
  1362. caret.UpdateWantedX();
  1363. if (!is_virtual_reformat)
  1364. SetScrollPos(scroll_x, scroll_y); ///< Trig a bounds check (scroll if outside)
  1365. }
  1366. bool TBStyleEdit::GetSizeAffectsLayout() const
  1367. {
  1368. if (packed.wrapping || align != TB_TEXT_ALIGN_LEFT)
  1369. return true;
  1370. return false;
  1371. }
  1372. void TBStyleEdit::Reformat(bool update_fragments)
  1373. {
  1374. int ypos = 0;
  1375. BeginLockScrollbars();
  1376. TBBlock *block = blocks.GetFirst();
  1377. while (block)
  1378. {
  1379. // Update ypos directly instead of using "propagate_height" since propagating
  1380. // would iterate forward through all remaining blocks and we're going to visit
  1381. // them all anyway.
  1382. block->ypos = ypos;
  1383. block->Layout(update_fragments, false);
  1384. ypos += block->height;
  1385. block = block->GetNext();
  1386. }
  1387. EndLockScrollbars();
  1388. listener->Invalidate(TBRect(0, 0, layout_width, layout_height));
  1389. }
  1390. int32 TBStyleEdit::GetContentWidth()
  1391. {
  1392. if (packed.calculate_content_width_needed)
  1393. {
  1394. packed.calculate_content_width_needed = 0;
  1395. content_width = 0;
  1396. TBBlock *block = blocks.GetFirst();
  1397. while (block)
  1398. {
  1399. content_width = MAX(content_width, block->line_width_max);
  1400. block = block->GetNext();
  1401. }
  1402. }
  1403. return content_width;
  1404. }
  1405. int32 TBStyleEdit::GetContentHeight() const
  1406. {
  1407. return content_height;
  1408. }
  1409. void TBStyleEdit::Paint(const TBRect &rect, const TBFontDescription &font_desc, const TBColor &text_color)
  1410. {
  1411. TBTextProps props(font_desc, text_color);
  1412. // Find the first visible block
  1413. TBBlock *first_visible_block = blocks.GetFirst();
  1414. while (first_visible_block)
  1415. {
  1416. if (first_visible_block->ypos + first_visible_block->height - scroll_y >= 0)
  1417. break;
  1418. first_visible_block = first_visible_block->GetNext();
  1419. }
  1420. // Get the selection region for all visible blocks
  1421. TBRegion bg_region, fg_region;
  1422. if (selection.IsSelected())
  1423. {
  1424. TBBlock *block = first_visible_block;
  1425. while (block)
  1426. {
  1427. if (block->ypos - scroll_y > rect.y + rect.h)
  1428. break;
  1429. block->BuildSelectionRegion(-scroll_x, -scroll_y, &props, bg_region, fg_region);
  1430. block = block->GetNext();
  1431. }
  1432. // Paint bg selection
  1433. for (int i = 0; i < bg_region.GetNumRects(); i++)
  1434. listener->DrawTextSelectionBg(bg_region.GetRect(i));
  1435. }
  1436. // Paint the content
  1437. TBBlock *block = first_visible_block;
  1438. while (block)
  1439. {
  1440. if (block->ypos - scroll_y > rect.y + rect.h)
  1441. break;
  1442. block->Paint(-scroll_x, -scroll_y, &props);
  1443. block = block->GetNext();
  1444. }
  1445. // Paint fg selection
  1446. for (int i = 0; i < fg_region.GetNumRects(); i++)
  1447. listener->DrawTextSelectionBg(fg_region.GetRect(i));
  1448. // Paint caret
  1449. caret.Paint(- scroll_x, - scroll_y);
  1450. }
  1451. void TBStyleEdit::InsertBreak()
  1452. {
  1453. if (!packed.multiline_on)
  1454. return;
  1455. const char *new_line_str = packed.win_style_br ? "\r\n" : "\n";
  1456. // If we stand at the end and don't have any ending break, we're standing at the last line and
  1457. // should insert breaks twice. One to end the current line, and one for the new empty line.
  1458. if (caret.pos.ofs == caret.pos.block->str_len && !caret.pos.block->fragments.GetLast()->IsBreak())
  1459. new_line_str = packed.win_style_br ? "\r\n\r\n" : "\n\n";
  1460. TBStr indent_str;
  1461. for (int i = 0; i < caret.pos.block->str_len; i++)
  1462. {
  1463. if (caret.pos.block->str[i] == '\t')
  1464. indent_str.Append("\t", 1);
  1465. else if (caret.pos.block->str[i] == ' ')
  1466. indent_str.Append(" ", 1);
  1467. else
  1468. break;
  1469. }
  1470. InsertText(new_line_str);
  1471. caret.AvoidLineBreak();
  1472. if (caret.pos.block->GetNext())
  1473. {
  1474. caret.Place(caret.pos.block->GetNext(), 0);
  1475. if (indent_str.Length())
  1476. {
  1477. InsertText(indent_str);
  1478. caret.Place(TBPoint(32000, caret.y));
  1479. }
  1480. }
  1481. if (text_change_listener)
  1482. text_change_listener->OnChange(this);
  1483. }
  1484. void TBStyleEdit::InsertText(const char *text, int32 len, bool after_last, bool clear_undo_redo)
  1485. {
  1486. if (len == TB_ALL_TO_TERMINATION)
  1487. len = strlen(text);
  1488. selection.RemoveContent();
  1489. if (after_last)
  1490. caret.Place(blocks.GetLast(), blocks.GetLast()->str_len, false);
  1491. int32 len_inserted = caret.pos.block->InsertText(caret.pos.ofs, text, len, true);
  1492. if (clear_undo_redo)
  1493. undoredo.Clear(true, true);
  1494. else
  1495. undoredo.Commit(this, caret.GetGlobalOfs(), len_inserted, text, true);
  1496. caret.Place(caret.pos.block, caret.pos.ofs + len, false);
  1497. caret.UpdatePos();
  1498. caret.UpdateWantedX();
  1499. if (text_change_listener)
  1500. text_change_listener->OnChange(this);
  1501. }
  1502. TBBlock *TBStyleEdit::FindBlock(int32 y) const
  1503. {
  1504. TBBlock *block = blocks.GetFirst();
  1505. while (block)
  1506. {
  1507. if (y < block->ypos + block->height)
  1508. return block;
  1509. block = block->GetNext();
  1510. }
  1511. return blocks.GetLast();
  1512. }
  1513. bool TBStyleEdit::KeyDown(int key, SPECIAL_KEY special_key, MODIFIER_KEYS modifierkeys)
  1514. {
  1515. if (select_state)
  1516. return false;
  1517. if (autocomplete_visible
  1518. && (special_key == TB_KEY_UP || special_key == TB_KEY_DOWN
  1519. || special_key == TB_KEY_ENTER || special_key == TB_KEY_ESC
  1520. || special_key == TB_KEY_TAB)
  1521. && !modifierkeys)
  1522. {
  1523. return false;
  1524. }
  1525. bool handled = true;
  1526. bool move_caret = special_key == TB_KEY_LEFT || special_key == TB_KEY_RIGHT ||
  1527. special_key == TB_KEY_UP || special_key == TB_KEY_DOWN ||
  1528. special_key == TB_KEY_HOME || special_key == TB_KEY_END ||
  1529. special_key == TB_KEY_PAGE_UP || special_key == TB_KEY_PAGE_DOWN;
  1530. if (!(modifierkeys & TB_SHIFT) && move_caret)
  1531. selection.SelectNothing();
  1532. bool superDown = (modifierkeys & TB_SUPER);
  1533. bool ctrlOrSuper = ((modifierkeys & TB_CTRL) || superDown);
  1534. TBTextOfs old_caret_pos = caret.pos;
  1535. TBTextFragment *old_caret_elm = caret.GetFragment();
  1536. if ((special_key == TB_KEY_UP || special_key == TB_KEY_DOWN) && (modifierkeys & TB_CTRL))
  1537. {
  1538. int32 line_height = old_caret_pos.block->CalculateLineHeight(font);
  1539. int32 new_y = scroll_y + (special_key == TB_KEY_UP ? -line_height : line_height);
  1540. SetScrollPos(scroll_x, new_y);
  1541. }
  1542. else if (special_key == TB_KEY_LEFT && !superDown)
  1543. caret.Move(false, (modifierkeys & TB_CTRL) ? true : false);
  1544. else if (special_key == TB_KEY_RIGHT && !superDown)
  1545. caret.Move(true, (modifierkeys & TB_CTRL) ? true : false);
  1546. else if (special_key == TB_KEY_UP)
  1547. handled = caret.Place(TBPoint(caret.wanted_x, old_caret_pos.block->ypos + old_caret_elm->line_ypos - 1));
  1548. else if (special_key == TB_KEY_DOWN)
  1549. handled = caret.Place(TBPoint(caret.wanted_x, old_caret_pos.block->ypos + old_caret_elm->line_ypos + old_caret_elm->line_height + 1));
  1550. else if (special_key == TB_KEY_PAGE_UP)
  1551. caret.Place(TBPoint(caret.wanted_x, caret.y - layout_height));
  1552. else if (special_key == TB_KEY_PAGE_DOWN)
  1553. caret.Place(TBPoint(caret.wanted_x, caret.y + layout_height + old_caret_elm->line_height));
  1554. else if (special_key == TB_KEY_HOME && modifierkeys & TB_CTRL)
  1555. caret.Place(TBPoint(0, 0));
  1556. else if (special_key == TB_KEY_END && modifierkeys & TB_CTRL)
  1557. caret.Place(TBPoint(32000, blocks.GetLast()->ypos + blocks.GetLast()->height));
  1558. else if (special_key == TB_KEY_HOME || ( special_key == TB_KEY_LEFT && superDown))
  1559. caret.Place(TBPoint(0, caret.y));
  1560. else if (special_key == TB_KEY_END || ( special_key == TB_KEY_RIGHT && superDown))
  1561. caret.Place(TBPoint(32000, caret.y));
  1562. else if (key == '8' && (modifierkeys & TB_CTRL))
  1563. {
  1564. packed.show_whitespace = !packed.show_whitespace;
  1565. listener->Invalidate(TBRect(0, 0, layout_width, layout_height));
  1566. }
  1567. else if (!packed.read_only && (special_key == TB_KEY_DELETE || special_key == TB_KEY_BACKSPACE))
  1568. {
  1569. if (!selection.IsSelected())
  1570. {
  1571. caret.Move(special_key == TB_KEY_DELETE, (modifierkeys & TB_CTRL) ? true : false);
  1572. selection.SelectToCaret(old_caret_pos.block, old_caret_pos.ofs);
  1573. }
  1574. selection.RemoveContent();
  1575. }
  1576. else if (!packed.read_only && !(modifierkeys & TB_SHIFT) && (special_key == TB_KEY_TAB && packed.multiline_on))
  1577. {
  1578. if (!selection.IsSelected() || selection.start.block == selection.stop.block)
  1579. {
  1580. InsertText(" ", 4);
  1581. }
  1582. else
  1583. {
  1584. for (TBBlock* block = selection.start.block; block; block = block->GetNext())
  1585. {
  1586. if (block != selection.stop.block || selection.stop.ofs != 0)
  1587. {
  1588. block->InsertText(0, " ", 4, false);
  1589. // these shouldn't be multiple undo events
  1590. undoredo.Commit(this, block->fragments.GetFirst()->GetGlobalOfs(), 1, "\t", true);
  1591. }
  1592. if (block == selection.stop.block)
  1593. break;
  1594. }
  1595. }
  1596. }
  1597. else if (!packed.read_only && (modifierkeys & TB_SHIFT) && (special_key == TB_KEY_TAB && packed.multiline_on))
  1598. {
  1599. if (!selection.IsSelected() || selection.start.block == selection.stop.block)
  1600. {
  1601. if (!selection.IsSelected() && caret.pos.block)
  1602. {
  1603. int32 start = caret.pos.block->FirstNonTabPos();
  1604. if (start)
  1605. {
  1606. caret.pos.block->RemoveContent(0, 1);
  1607. }
  1608. }
  1609. }
  1610. else
  1611. {
  1612. }
  1613. }
  1614. else if (!packed.read_only && (special_key == TB_KEY_ENTER && packed.multiline_on) && !(ctrlOrSuper))
  1615. InsertBreak();
  1616. else if (!packed.read_only && (key && !(ctrlOrSuper)) && special_key != TB_KEY_ENTER)
  1617. {
  1618. char utf8[8];
  1619. int len = utf8::encode(key, utf8);
  1620. InsertText(utf8, len);
  1621. }
  1622. else
  1623. handled = false;
  1624. if ((modifierkeys & TB_SHIFT) && move_caret)
  1625. selection.SelectToCaret(old_caret_pos.block, old_caret_pos.ofs);
  1626. if (!(special_key == TB_KEY_UP || special_key == TB_KEY_DOWN ||
  1627. special_key == TB_KEY_PAGE_UP || special_key == TB_KEY_PAGE_DOWN))
  1628. caret.UpdateWantedX();
  1629. caret.ResetBlink();
  1630. // Hooks
  1631. if (!move_caret && handled)
  1632. listener->OnChange();
  1633. if (special_key == TB_KEY_ENTER && !(modifierkeys & TB_CTRL))
  1634. {
  1635. if (listener->OnEnter())
  1636. handled = true;
  1637. }
  1638. if (handled)
  1639. ScrollIfNeeded();
  1640. return handled;
  1641. }
  1642. void TBStyleEdit::Cut()
  1643. {
  1644. if (packed.password_on)
  1645. return;
  1646. Copy();
  1647. KeyDown(0, TB_KEY_DELETE, TB_MODIFIER_NONE);
  1648. }
  1649. void TBStyleEdit::Copy()
  1650. {
  1651. if (packed.password_on)
  1652. return;
  1653. selection.CopyToClipboard();
  1654. }
  1655. void TBStyleEdit::Paste()
  1656. {
  1657. TBStr text;
  1658. if (TBClipboard::HasText() && TBClipboard::GetText(text))
  1659. {
  1660. InsertText(text, text.Length());
  1661. ScrollIfNeeded(true, true);
  1662. listener->OnChange();
  1663. if (text_change_listener)
  1664. text_change_listener->OnChange(this);
  1665. }
  1666. }
  1667. void TBStyleEdit::Delete()
  1668. {
  1669. if (selection.IsSelected())
  1670. {
  1671. selection.RemoveContent();
  1672. listener->OnChange();
  1673. }
  1674. }
  1675. void TBStyleEdit::Undo()
  1676. {
  1677. if (CanUndo())
  1678. {
  1679. undoredo.Undo(this);
  1680. listener->OnChange();
  1681. }
  1682. }
  1683. void TBStyleEdit::Redo()
  1684. {
  1685. if (CanRedo())
  1686. {
  1687. undoredo.Redo(this);
  1688. listener->OnChange();
  1689. }
  1690. }
  1691. bool TBStyleEdit::MouseDown(const TBPoint &point, int button, int clicks, MODIFIER_KEYS modifierkeys, bool touch)
  1692. {
  1693. if (button != 1)
  1694. return false;
  1695. if (touch)
  1696. {
  1697. mousedown_point = TBPoint(point.x + scroll_x, point.y + scroll_y);
  1698. }
  1699. else if (packed.selection_on)
  1700. {
  1701. if (modifierkeys & TB_SHIFT) // Select to new caretpos
  1702. {
  1703. mousedown_fragment = nullptr;
  1704. mousedown_point = TBPoint(point.x + scroll_x, point.y + scroll_y);
  1705. TBTextOfs old_caret_pos = caret.pos;
  1706. caret.Place(mousedown_point);
  1707. selection.SelectToCaret(old_caret_pos.block, old_caret_pos.ofs);
  1708. }
  1709. else // Start selection
  1710. {
  1711. mousedown_point = TBPoint(point.x + scroll_x, point.y + scroll_y);
  1712. selection.SelectNothing();
  1713. // clicks is 1 to infinite, and here we support only doubleclick, so make it either single or double.
  1714. select_state = ((clicks - 1) % 2) + 1;
  1715. MouseMove(point);
  1716. if (caret.pos.block)
  1717. mousedown_fragment = caret.pos.block->FindFragment(mousedown_point.x, mousedown_point.y - caret.pos.block->ypos);
  1718. }
  1719. caret.ResetBlink();
  1720. }
  1721. return true;
  1722. }
  1723. bool TBStyleEdit::MouseUp(const TBPoint &point, int button, MODIFIER_KEYS modifierkeys, bool touch)
  1724. {
  1725. if (button != 1)
  1726. return false;
  1727. if (touch && !TBWidget::cancel_click)
  1728. {
  1729. selection.SelectNothing();
  1730. caret.Place(mousedown_point);
  1731. caret.UpdateWantedX();
  1732. caret.ResetBlink();
  1733. }
  1734. select_state = 0;
  1735. if (caret.pos.block && !TBWidget::cancel_click)
  1736. {
  1737. TBTextFragment *fragment = caret.pos.block->FindFragment(point.x + scroll_x, point.y + scroll_y - caret.pos.block->ypos);
  1738. if (fragment && fragment == mousedown_fragment)
  1739. fragment->Click(button, modifierkeys);
  1740. }
  1741. return true;
  1742. }
  1743. bool TBStyleEdit::MouseMove(const TBPoint &point)
  1744. {
  1745. if (select_state)
  1746. {
  1747. TBPoint p(point.x + scroll_x, point.y + scroll_y);
  1748. selection.Select(mousedown_point, p);
  1749. if (select_state == 2)
  1750. {
  1751. bool has_initial_selection = selection.IsSelected();
  1752. if (has_initial_selection)
  1753. caret.Place(selection.start.block, selection.start.ofs);
  1754. caret.Move(false, true);
  1755. selection.start.Set(caret.pos);
  1756. if (has_initial_selection)
  1757. caret.Place(selection.stop.block, selection.stop.ofs);
  1758. caret.Move(true, true);
  1759. selection.stop.Set(caret.pos);
  1760. selection.CorrectOrder();
  1761. caret.UpdateWantedX();
  1762. }
  1763. return true;
  1764. }
  1765. return false;
  1766. }
  1767. void TBStyleEdit::Focus(bool focus)
  1768. {
  1769. if (focus)
  1770. listener->CaretBlinkStart();
  1771. else
  1772. listener->CaretBlinkStop();
  1773. caret.on = focus;
  1774. caret.Invalidate();
  1775. selection.Invalidate();
  1776. }
  1777. bool TBStyleEdit::SetText(const char *text, TB_CARET_POS pos)
  1778. {
  1779. return SetText(text, strlen(text), pos);
  1780. }
  1781. bool TBStyleEdit::SetText(const char *text, int text_len, TB_CARET_POS pos)
  1782. {
  1783. if (!text || !*text)
  1784. {
  1785. Clear(true);
  1786. caret.UpdateWantedX();
  1787. ScrollIfNeeded(true, true);
  1788. return true;
  1789. }
  1790. Clear(true);
  1791. blocks.GetFirst()->InsertText(0, text, text_len, true);
  1792. caret.Place(blocks.GetFirst(), 0);
  1793. caret.UpdateWantedX();
  1794. ScrollIfNeeded(true, false);
  1795. if (pos == TB_CARET_POS_END)
  1796. caret.Place(blocks.GetLast(), blocks.GetLast()->str_len);
  1797. listener->OnChange();
  1798. return true;
  1799. }
  1800. bool TBStyleEdit::Load(const char *filename)
  1801. {
  1802. TBFile* f = TBFile::Open(filename, TBFile::MODE_READ);
  1803. if (!f)
  1804. return false;
  1805. uint32 num_bytes = f->Size();
  1806. char *str = new char[num_bytes + 1];
  1807. if (!str)
  1808. {
  1809. delete f;
  1810. return false;
  1811. }
  1812. num_bytes = f->Read(str, 1, num_bytes);
  1813. str[num_bytes] = 0;
  1814. delete f;
  1815. SetText(str);
  1816. delete [] str;
  1817. return true;
  1818. }
  1819. bool TBStyleEdit::GetText(TBStr &text)
  1820. {
  1821. TBSelection tmp_selection(this);
  1822. tmp_selection.SelectAll();
  1823. return tmp_selection.GetText(text);
  1824. }
  1825. bool TBStyleEdit::IsEmpty() const
  1826. {
  1827. return blocks.GetFirst() == blocks.GetLast() && blocks.GetFirst()->str.IsEmpty();
  1828. }
  1829. void TBStyleEdit::SetAlign(TB_TEXT_ALIGN align)
  1830. {
  1831. this->align = align;
  1832. // Call SetAlign on all blocks currently selected, or the block of the current caret position.
  1833. TBBlock *start = selection.IsSelected() ? selection.start.block : caret.pos.block;
  1834. TBBlock *stop = selection.IsSelected() ? selection.stop.block : caret.pos.block;
  1835. while (start && start != stop->GetNext())
  1836. {
  1837. start->SetAlign(align);
  1838. start = start->GetNext();
  1839. }
  1840. }
  1841. void TBStyleEdit::SetMultiline(bool multiline)
  1842. {
  1843. packed.multiline_on = multiline;
  1844. }
  1845. void TBStyleEdit::SetStyling(bool styling)
  1846. {
  1847. packed.styling_on = styling;
  1848. }
  1849. void TBStyleEdit::SetReadOnly(bool readonly)
  1850. {
  1851. packed.read_only = readonly;
  1852. }
  1853. void TBStyleEdit::SetSelection(bool selection)
  1854. {
  1855. packed.selection_on = selection;
  1856. }
  1857. void TBStyleEdit::SetPassword(bool password)
  1858. {
  1859. if (packed.password_on == password)
  1860. return;
  1861. packed.password_on = password;
  1862. Reformat(true);
  1863. }
  1864. void TBStyleEdit::SetWrapping(bool wrapping)
  1865. {
  1866. if (packed.wrapping == wrapping)
  1867. return;
  1868. packed.wrapping = wrapping;
  1869. Reformat(false);
  1870. }
  1871. int32 TBStyleEdit::GetCaretLine()
  1872. {
  1873. int line = 0;
  1874. TBBlock *block = NULL;
  1875. TBTextFragment* frag = caret.GetFragment();
  1876. if (!frag)
  1877. return 0;
  1878. for (block = blocks.GetFirst(); block; block = block->GetNext())
  1879. {
  1880. if (frag->block == block)
  1881. return line;
  1882. line++;
  1883. }
  1884. return 0;
  1885. }
  1886. // == TBUndoRedoStack ==================================================
  1887. TBUndoRedoStack::~TBUndoRedoStack()
  1888. {
  1889. Clear(true, true);
  1890. }
  1891. void TBUndoRedoStack::Undo(TBStyleEdit *styledit)
  1892. {
  1893. if (!undos.GetNumItems())
  1894. return;
  1895. TBUndoEvent *e = undos.Remove(undos.GetNumItems() - 1);
  1896. redos.Add(e);
  1897. Apply(styledit, e, true);
  1898. }
  1899. void TBUndoRedoStack::Redo(TBStyleEdit *styledit)
  1900. {
  1901. if (!redos.GetNumItems())
  1902. return;
  1903. TBUndoEvent *e = redos.Remove(redos.GetNumItems() - 1);
  1904. undos.Add(e);
  1905. Apply(styledit, e, false);
  1906. }
  1907. void TBUndoRedoStack::Apply(TBStyleEdit *styledit, TBUndoEvent *e, bool reverse)
  1908. {
  1909. applying = true;
  1910. if (e->insert == reverse)
  1911. {
  1912. styledit->selection.SelectNothing();
  1913. styledit->caret.SetGlobalOfs(e->gofs, false);
  1914. assert(TBTextOfs(styledit->caret.pos).GetGlobalOfs(styledit) == e->gofs);
  1915. TBTextOfs start = styledit->caret.pos;
  1916. styledit->caret.SetGlobalOfs(e->gofs + e->text.Length(), false);
  1917. assert(TBTextOfs(styledit->caret.pos).GetGlobalOfs(styledit) == e->gofs + e->text.Length());
  1918. styledit->selection.Select(start, styledit->caret.pos);
  1919. styledit->selection.RemoveContent();
  1920. }
  1921. else
  1922. {
  1923. styledit->selection.SelectNothing();
  1924. styledit->caret.SetGlobalOfs(e->gofs, true, true);
  1925. styledit->InsertText(e->text);
  1926. int text_len = e->text.Length();
  1927. if (text_len > 1)
  1928. styledit->selection.Select(e->gofs, e->gofs + text_len);
  1929. }
  1930. styledit->ScrollIfNeeded(true, true);
  1931. applying = false;
  1932. }
  1933. void TBUndoRedoStack::Clear(bool clear_undo, bool clear_redo)
  1934. {
  1935. assert(!applying);
  1936. if (clear_undo)
  1937. undos.DeleteAll();
  1938. if (clear_redo)
  1939. redos.DeleteAll();
  1940. }
  1941. TBUndoEvent *TBUndoRedoStack::Commit(TBStyleEdit *styledit, int32 gofs, int32 len, const char *text, bool insert)
  1942. {
  1943. if (applying || styledit->packed.read_only)
  1944. return nullptr;
  1945. Clear(false, true);
  1946. // If we're inserting a single character, check if we want to append it to the previous event.
  1947. if (insert && undos.GetNumItems())
  1948. {
  1949. int num_char = utf8::count_characters(text, len);
  1950. TBUndoEvent *e = undos[undos.GetNumItems() - 1];
  1951. if (num_char == 1 && e->insert && e->gofs + e->text.Length() == gofs)
  1952. {
  1953. // Appending a space to other space(s) should append
  1954. if ((text[0] == ' ' && !strpbrk(e->text.CStr(), "\r\n")) ||
  1955. // But non spaces should not
  1956. !strpbrk(e->text.CStr(), " \r\n"))
  1957. {
  1958. e->text.Append(text, len);
  1959. return e;
  1960. }
  1961. }
  1962. }
  1963. // Create a new event
  1964. if (TBUndoEvent *e = new TBUndoEvent())
  1965. {
  1966. e->gofs = gofs;
  1967. e->text.Set(text, len);
  1968. e->insert = insert;
  1969. undos.Add(e);
  1970. return e;
  1971. }
  1972. // OOM
  1973. Clear(true, true);
  1974. return nullptr;
  1975. }
  1976. }; // namespace tb