tb_style_edit.cpp 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268
  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 & P_SHIFT) // Select to new caretpos
  1702. //{
  1703. //}
  1704. //else // Start selection
  1705. {
  1706. mousedown_point = TBPoint(point.x + scroll_x, point.y + scroll_y);
  1707. selection.SelectNothing();
  1708. // clicks is 1 to infinite, and here we support only doubleclick, so make it either single or double.
  1709. select_state = ((clicks - 1) % 2) + 1;
  1710. MouseMove(point);
  1711. if (caret.pos.block)
  1712. mousedown_fragment = caret.pos.block->FindFragment(mousedown_point.x, mousedown_point.y - caret.pos.block->ypos);
  1713. }
  1714. caret.ResetBlink();
  1715. }
  1716. return true;
  1717. }
  1718. bool TBStyleEdit::MouseUp(const TBPoint &point, int button, MODIFIER_KEYS modifierkeys, bool touch)
  1719. {
  1720. if (button != 1)
  1721. return false;
  1722. if (touch && !TBWidget::cancel_click)
  1723. {
  1724. selection.SelectNothing();
  1725. caret.Place(mousedown_point);
  1726. caret.UpdateWantedX();
  1727. caret.ResetBlink();
  1728. }
  1729. select_state = 0;
  1730. if (caret.pos.block && !TBWidget::cancel_click)
  1731. {
  1732. TBTextFragment *fragment = caret.pos.block->FindFragment(point.x + scroll_x, point.y + scroll_y - caret.pos.block->ypos);
  1733. if (fragment && fragment == mousedown_fragment)
  1734. fragment->Click(button, modifierkeys);
  1735. }
  1736. return true;
  1737. }
  1738. bool TBStyleEdit::MouseMove(const TBPoint &point)
  1739. {
  1740. if (select_state)
  1741. {
  1742. TBPoint p(point.x + scroll_x, point.y + scroll_y);
  1743. selection.Select(mousedown_point, p);
  1744. if (select_state == 2)
  1745. {
  1746. bool has_initial_selection = selection.IsSelected();
  1747. if (has_initial_selection)
  1748. caret.Place(selection.start.block, selection.start.ofs);
  1749. caret.Move(false, true);
  1750. selection.start.Set(caret.pos);
  1751. if (has_initial_selection)
  1752. caret.Place(selection.stop.block, selection.stop.ofs);
  1753. caret.Move(true, true);
  1754. selection.stop.Set(caret.pos);
  1755. selection.CorrectOrder();
  1756. caret.UpdateWantedX();
  1757. }
  1758. return true;
  1759. }
  1760. return false;
  1761. }
  1762. void TBStyleEdit::Focus(bool focus)
  1763. {
  1764. if (focus)
  1765. listener->CaretBlinkStart();
  1766. else
  1767. listener->CaretBlinkStop();
  1768. caret.on = focus;
  1769. caret.Invalidate();
  1770. selection.Invalidate();
  1771. }
  1772. bool TBStyleEdit::SetText(const char *text, TB_CARET_POS pos)
  1773. {
  1774. return SetText(text, strlen(text), pos);
  1775. }
  1776. bool TBStyleEdit::SetText(const char *text, int text_len, TB_CARET_POS pos)
  1777. {
  1778. if (!text || !*text)
  1779. {
  1780. Clear(true);
  1781. caret.UpdateWantedX();
  1782. ScrollIfNeeded(true, true);
  1783. return true;
  1784. }
  1785. Clear(true);
  1786. blocks.GetFirst()->InsertText(0, text, text_len, true);
  1787. caret.Place(blocks.GetFirst(), 0);
  1788. caret.UpdateWantedX();
  1789. ScrollIfNeeded(true, false);
  1790. if (pos == TB_CARET_POS_END)
  1791. caret.Place(blocks.GetLast(), blocks.GetLast()->str_len);
  1792. listener->OnChange();
  1793. return true;
  1794. }
  1795. bool TBStyleEdit::Load(const char *filename)
  1796. {
  1797. TBFile* f = TBFile::Open(filename, TBFile::MODE_READ);
  1798. if (!f)
  1799. return false;
  1800. uint32 num_bytes = f->Size();
  1801. char *str = new char[num_bytes + 1];
  1802. if (!str)
  1803. {
  1804. delete f;
  1805. return false;
  1806. }
  1807. num_bytes = f->Read(str, 1, num_bytes);
  1808. str[num_bytes] = 0;
  1809. delete f;
  1810. SetText(str);
  1811. delete [] str;
  1812. return true;
  1813. }
  1814. bool TBStyleEdit::GetText(TBStr &text)
  1815. {
  1816. TBSelection tmp_selection(this);
  1817. tmp_selection.SelectAll();
  1818. return tmp_selection.GetText(text);
  1819. }
  1820. bool TBStyleEdit::IsEmpty() const
  1821. {
  1822. return blocks.GetFirst() == blocks.GetLast() && blocks.GetFirst()->str.IsEmpty();
  1823. }
  1824. void TBStyleEdit::SetAlign(TB_TEXT_ALIGN align)
  1825. {
  1826. this->align = align;
  1827. // Call SetAlign on all blocks currently selected, or the block of the current caret position.
  1828. TBBlock *start = selection.IsSelected() ? selection.start.block : caret.pos.block;
  1829. TBBlock *stop = selection.IsSelected() ? selection.stop.block : caret.pos.block;
  1830. while (start && start != stop->GetNext())
  1831. {
  1832. start->SetAlign(align);
  1833. start = start->GetNext();
  1834. }
  1835. }
  1836. void TBStyleEdit::SetMultiline(bool multiline)
  1837. {
  1838. packed.multiline_on = multiline;
  1839. }
  1840. void TBStyleEdit::SetStyling(bool styling)
  1841. {
  1842. packed.styling_on = styling;
  1843. }
  1844. void TBStyleEdit::SetReadOnly(bool readonly)
  1845. {
  1846. packed.read_only = readonly;
  1847. }
  1848. void TBStyleEdit::SetSelection(bool selection)
  1849. {
  1850. packed.selection_on = selection;
  1851. }
  1852. void TBStyleEdit::SetPassword(bool password)
  1853. {
  1854. if (packed.password_on == password)
  1855. return;
  1856. packed.password_on = password;
  1857. Reformat(true);
  1858. }
  1859. void TBStyleEdit::SetWrapping(bool wrapping)
  1860. {
  1861. if (packed.wrapping == wrapping)
  1862. return;
  1863. packed.wrapping = wrapping;
  1864. Reformat(false);
  1865. }
  1866. int32 TBStyleEdit::GetCaretLine()
  1867. {
  1868. int line = 0;
  1869. TBBlock *block = NULL;
  1870. TBTextFragment* frag = caret.GetFragment();
  1871. if (!frag)
  1872. return 0;
  1873. for (block = blocks.GetFirst(); block; block = block->GetNext())
  1874. {
  1875. if (frag->block == block)
  1876. return line;
  1877. line++;
  1878. }
  1879. return 0;
  1880. }
  1881. // == TBUndoRedoStack ==================================================
  1882. TBUndoRedoStack::~TBUndoRedoStack()
  1883. {
  1884. Clear(true, true);
  1885. }
  1886. void TBUndoRedoStack::Undo(TBStyleEdit *styledit)
  1887. {
  1888. if (!undos.GetNumItems())
  1889. return;
  1890. TBUndoEvent *e = undos.Remove(undos.GetNumItems() - 1);
  1891. redos.Add(e);
  1892. Apply(styledit, e, true);
  1893. }
  1894. void TBUndoRedoStack::Redo(TBStyleEdit *styledit)
  1895. {
  1896. if (!redos.GetNumItems())
  1897. return;
  1898. TBUndoEvent *e = redos.Remove(redos.GetNumItems() - 1);
  1899. undos.Add(e);
  1900. Apply(styledit, e, false);
  1901. }
  1902. void TBUndoRedoStack::Apply(TBStyleEdit *styledit, TBUndoEvent *e, bool reverse)
  1903. {
  1904. applying = true;
  1905. if (e->insert == reverse)
  1906. {
  1907. styledit->selection.SelectNothing();
  1908. styledit->caret.SetGlobalOfs(e->gofs, false);
  1909. assert(TBTextOfs(styledit->caret.pos).GetGlobalOfs(styledit) == e->gofs);
  1910. TBTextOfs start = styledit->caret.pos;
  1911. styledit->caret.SetGlobalOfs(e->gofs + e->text.Length(), false);
  1912. assert(TBTextOfs(styledit->caret.pos).GetGlobalOfs(styledit) == e->gofs + e->text.Length());
  1913. styledit->selection.Select(start, styledit->caret.pos);
  1914. styledit->selection.RemoveContent();
  1915. }
  1916. else
  1917. {
  1918. styledit->selection.SelectNothing();
  1919. styledit->caret.SetGlobalOfs(e->gofs, true, true);
  1920. styledit->InsertText(e->text);
  1921. int text_len = e->text.Length();
  1922. if (text_len > 1)
  1923. styledit->selection.Select(e->gofs, e->gofs + text_len);
  1924. }
  1925. styledit->ScrollIfNeeded(true, true);
  1926. applying = false;
  1927. }
  1928. void TBUndoRedoStack::Clear(bool clear_undo, bool clear_redo)
  1929. {
  1930. assert(!applying);
  1931. if (clear_undo)
  1932. undos.DeleteAll();
  1933. if (clear_redo)
  1934. redos.DeleteAll();
  1935. }
  1936. TBUndoEvent *TBUndoRedoStack::Commit(TBStyleEdit *styledit, int32 gofs, int32 len, const char *text, bool insert)
  1937. {
  1938. if (applying || styledit->packed.read_only)
  1939. return nullptr;
  1940. Clear(false, true);
  1941. // If we're inserting a single character, check if we want to append it to the previous event.
  1942. if (insert && undos.GetNumItems())
  1943. {
  1944. int num_char = utf8::count_characters(text, len);
  1945. TBUndoEvent *e = undos[undos.GetNumItems() - 1];
  1946. if (num_char == 1 && e->insert && e->gofs + e->text.Length() == gofs)
  1947. {
  1948. // Appending a space to other space(s) should append
  1949. if ((text[0] == ' ' && !strpbrk(e->text.CStr(), "\r\n")) ||
  1950. // But non spaces should not
  1951. !strpbrk(e->text.CStr(), " \r\n"))
  1952. {
  1953. e->text.Append(text, len);
  1954. return e;
  1955. }
  1956. }
  1957. }
  1958. // Create a new event
  1959. if (TBUndoEvent *e = new TBUndoEvent())
  1960. {
  1961. e->gofs = gofs;
  1962. e->text.Set(text, len);
  1963. e->insert = insert;
  1964. undos.Add(e);
  1965. return e;
  1966. }
  1967. // OOM
  1968. Clear(true, true);
  1969. return nullptr;
  1970. }
  1971. }; // namespace tb