tb_style_edit.cpp 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355
  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. // This is causing trailing space to be selected
  464. // on double click of text fragment
  465. // I seem to remember disabling this once before
  466. // and having an issue, so leaving it here for reference
  467. //while (pos.ofs < len && is_space(str[pos.ofs]))
  468. //pos.ofs++;
  469. }
  470. }
  471. else if (pos.ofs > 0)
  472. {
  473. while (pos.ofs > 0 && is_space(str[pos.ofs - 1]))
  474. pos.ofs--;
  475. if (pos.ofs > 0 && is_wordbreak(str[pos.ofs - 1]))
  476. {
  477. while (pos.ofs > 0 && is_wordbreak(str[pos.ofs - 1]))
  478. pos.ofs--;
  479. }
  480. else
  481. {
  482. while (pos.ofs > 0 && !is_wordbreak(str[pos.ofs - 1]))
  483. pos.ofs--;
  484. }
  485. }
  486. }
  487. else
  488. {
  489. if (forward && pos.ofs >= pos.block->str_len && pos.block->GetNext())
  490. {
  491. pos.block = pos.block->GetNext();
  492. pos.ofs = 0;
  493. }
  494. else if (!forward && pos.ofs <= 0 && pos.block->prev)
  495. {
  496. pos.block = pos.block->GetPrev();
  497. pos.ofs = pos.block->str_len;
  498. }
  499. else
  500. {
  501. int i = pos.ofs;
  502. if (forward)
  503. utf8::move_inc(pos.block->str, &i, pos.block->str_len);
  504. else
  505. utf8::move_dec(pos.block->str, &i);
  506. pos.ofs = i;
  507. }
  508. }
  509. return Place(pos.block, pos.ofs, true, forward);
  510. }
  511. bool TBCaret::Place(const TBPoint &point)
  512. {
  513. TBBlock *block = styledit->FindBlock(point.y);
  514. TBTextFragment *fragment = block->FindFragment(point.x, point.y - block->ypos);
  515. int ofs = fragment->ofs + fragment->GetCharOfs(styledit->font, point.x - fragment->xpos);
  516. if (Place(block, ofs))
  517. {
  518. if (GetFragment() != fragment)
  519. {
  520. prefer_first = !prefer_first;
  521. Place(block, ofs);
  522. }
  523. return true;
  524. }
  525. return false;
  526. }
  527. void TBCaret::Place(TB_CARET_POS place)
  528. {
  529. if (place == TB_CARET_POS_BEGINNING)
  530. Place(styledit->blocks.GetFirst(), 0);
  531. else if (place == TB_CARET_POS_END)
  532. Place(styledit->blocks.GetLast(), styledit->blocks.GetLast()->str_len);
  533. }
  534. bool TBCaret::Place(TBBlock *block, int ofs, bool allow_snap, bool snap_forward)
  535. {
  536. if (block)
  537. {
  538. while (block->GetNext() && ofs > block->str_len)
  539. {
  540. ofs -= block->str_len;
  541. block = block->GetNext();
  542. }
  543. while (block->prev && ofs < 0)
  544. {
  545. block = block->GetPrev();
  546. ofs += block->str_len;
  547. }
  548. if (ofs < 0)
  549. ofs = 0;
  550. if (ofs > block->str_len)
  551. ofs = block->str_len;
  552. // Avoid being inside linebreak
  553. if (allow_snap)
  554. {
  555. TBTextFragment *fragment = block->FindFragment(ofs);
  556. if (ofs > fragment->ofs && fragment->IsBreak())
  557. {
  558. if (snap_forward && block->GetNext())
  559. {
  560. block = block->GetNext();
  561. ofs = 0;
  562. }
  563. else
  564. ofs = fragment->ofs;
  565. }
  566. }
  567. }
  568. bool changed = (pos.block != block || pos.ofs != ofs);
  569. pos.Set(block, ofs);
  570. if (block)
  571. UpdatePos();
  572. return changed;
  573. }
  574. void TBCaret::AvoidLineBreak()
  575. {
  576. TBTextFragment *fragment = GetFragment();
  577. if (pos.ofs > fragment->ofs && fragment->IsBreak())
  578. pos.ofs = fragment->ofs;
  579. UpdatePos();
  580. }
  581. void TBCaret::Paint(int32 translate_x, int32 translate_y)
  582. {
  583. // if (on && !(styledit->select_state && styledit->selection.IsSelected()))
  584. if (on || styledit->select_state)
  585. {
  586. styledit->listener->DrawCaret(TBRect(translate_x + x, translate_y + y, width, height));
  587. }
  588. }
  589. void TBCaret::ResetBlink()
  590. {
  591. styledit->listener->CaretBlinkStop();
  592. on = true;
  593. styledit->listener->CaretBlinkStart();
  594. }
  595. void TBCaret::UpdateWantedX()
  596. {
  597. wanted_x = x;
  598. }
  599. TBTextFragment *TBCaret::GetFragment()
  600. {
  601. return pos.block->FindFragment(pos.ofs, prefer_first);
  602. }
  603. void TBCaret::SwitchBlock(bool second)
  604. {
  605. }
  606. void TBCaret::SetGlobalOfs(int32 gofs, bool allow_snap, bool snap_forward)
  607. {
  608. TBTextOfs ofs;
  609. if (ofs.SetGlobalOfs(styledit, gofs))
  610. Place(ofs.block, ofs.ofs, allow_snap, snap_forward);
  611. }
  612. // == TBTextProps =======================================================================
  613. TBTextProps::TBTextProps(const TBFontDescription &font_desc, const TBColor &text_color)
  614. {
  615. base_data.font_desc = font_desc;
  616. base_data.text_color = text_color;
  617. base_data.underline = false;
  618. data = &base_data;
  619. }
  620. TBTextProps::Data *TBTextProps::Push()
  621. {
  622. if (Data *new_data = new Data)
  623. {
  624. data_list.AddLast(new_data);
  625. new_data->font_desc = data->font_desc;
  626. new_data->text_color = data->text_color;
  627. new_data->underline = data->underline;
  628. data = new_data;
  629. return data;
  630. }
  631. return nullptr;
  632. }
  633. void TBTextProps::Pop()
  634. {
  635. if (!data_list.GetLast())
  636. return; // Unballanced or we previosly got OOM.
  637. data_list.Delete(data_list.GetLast());
  638. data = data_list.GetLast() ? data_list.GetLast() : &base_data;
  639. }
  640. TBFontFace *TBTextProps::GetFont()
  641. {
  642. return g_font_manager->GetFontFace(data->font_desc);
  643. }
  644. // ============================================================================
  645. TBBlock::TBBlock(TBStyleEdit *styledit)
  646. : styledit(styledit)
  647. , ypos(0)
  648. , height(0)
  649. , align(styledit->align)
  650. , line_width_max(0)
  651. , str_len(0)
  652. {
  653. }
  654. TBBlock::~TBBlock()
  655. {
  656. Clear();
  657. }
  658. void TBBlock::Clear()
  659. {
  660. fragments.DeleteAll();
  661. }
  662. void TBBlock::Set(const char *newstr, int32 len)
  663. {
  664. str.Set(newstr, len);
  665. str_len = len;
  666. Split();
  667. Layout(true, true);
  668. }
  669. void TBBlock::SetAlign(TB_TEXT_ALIGN align)
  670. {
  671. if (this->align == align)
  672. return;
  673. this->align = align;
  674. Layout(false, false);
  675. }
  676. int32 TBBlock::InsertText(int32 ofs, const char *text, int32 len, bool allow_line_recurse)
  677. {
  678. styledit->BeginLockScrollbars();
  679. int first_line_len = len;
  680. for(int i = 0; i < len; i++)
  681. if (text[i] == '\r' || text[i] == '\n')
  682. {
  683. first_line_len = i;
  684. // Include the line break too but not for single lines
  685. if (!styledit->packed.multiline_on)
  686. break;
  687. if (text[i] == '\r' && text[i + 1] == '\n')
  688. first_line_len++;
  689. first_line_len++;
  690. break;
  691. }
  692. int32 inserted_len = first_line_len;
  693. str.Insert(ofs, text, first_line_len);
  694. str_len += first_line_len;
  695. Split();
  696. Layout(true, true);
  697. // Add the rest which was after the linebreak.
  698. if (allow_line_recurse && styledit->packed.multiline_on)
  699. {
  700. // Instead of recursively calling InsertText, we will loop through them all here
  701. TBBlock *next_block = GetNext();
  702. const char *next_line_ptr = &text[first_line_len];
  703. int remaining = len - first_line_len;
  704. while (remaining > 0)
  705. {
  706. if (!next_block)
  707. {
  708. next_block = new TBBlock(styledit);
  709. styledit->blocks.AddLast(next_block);
  710. }
  711. int consumed = next_block->InsertText(0, next_line_ptr, remaining, false);
  712. next_line_ptr += consumed;
  713. inserted_len += consumed;
  714. remaining -= consumed;
  715. next_block = next_block->GetNext();
  716. }
  717. }
  718. styledit->EndLockScrollbars();
  719. return inserted_len;
  720. }
  721. void TBBlock::RemoveContent(int32 ofs, int32 len)
  722. {
  723. if (!len)
  724. return;
  725. str.Remove(ofs, len);
  726. str_len -= len;
  727. Layout(true, true);
  728. }
  729. void TBBlock::Split()
  730. {
  731. int32 len = str_len;
  732. int brlen = 1; // FIX: skip ending newline fragment but not if there is several newlines and check for singleline newline.
  733. if (len > 1 && str.CStr()[len - 2] == '\r' && str.CStr()[len - 1] == '\n')
  734. brlen++;
  735. len -= brlen;
  736. for(int i = 0; i < len; i++)
  737. {
  738. if (is_linebreak(str.CStr()[i]))
  739. {
  740. TBBlock *block = new TBBlock(styledit);
  741. if (!block)
  742. return;
  743. styledit->blocks.AddAfter(block, this);
  744. if (i < len - 1 && str.CStr()[i] == '\r' && str.CStr()[i + 1] == '\n')
  745. i++;
  746. i++;
  747. len = len + brlen - i;
  748. block->Set(str.CStr() + i, len);
  749. str.Remove(i, len);
  750. str_len -= len;
  751. break;
  752. }
  753. }
  754. }
  755. void TBBlock::Merge()
  756. {
  757. TBBlock *next_block = GetNext();
  758. if (next_block && !fragments.GetLast()->IsBreak())
  759. {
  760. str.Append(GetNext()->str);
  761. str_len = str.Length();
  762. styledit->blocks.Delete(next_block);
  763. height = 0; // Ensure that Layout propagate height to remaining blocks.
  764. Layout(true, true);
  765. }
  766. }
  767. int32 TBBlock::CalculateTabWidth(TBFontFace *font, int32 xpos) const
  768. {
  769. int tabsize = font->GetStringWidth("x", 1) * TAB_SPACE;
  770. int p2 = int(xpos / tabsize) * tabsize + tabsize;
  771. return p2 - xpos;
  772. }
  773. int32 TBBlock::FirstNonTabPos() const
  774. {
  775. for (int i = 0; i < str.Length(); i++)
  776. if (str[i] != ' ' && str[i] != '\t')
  777. return i;
  778. return 0;
  779. }
  780. int32 TBBlock::CalculateStringWidth(TBFontFace *font, const char *str, int len) const
  781. {
  782. if (styledit->packed.password_on)
  783. {
  784. // Convert the length in number or characters, since that's what matters for password width.
  785. len = utf8::count_characters(str, len);
  786. return font->GetStringWidth(special_char_password) * len;
  787. }
  788. return font->GetStringWidth(str, len);
  789. }
  790. int32 TBBlock::CalculateLineHeight(TBFontFace *font) const
  791. {
  792. // ATOMIC BEGIN
  793. // Atomic adds a bit of buffer here for better line separation, this could be exposed as a setting
  794. return font->GetHeight() + 4;
  795. // ATOMIC END
  796. }
  797. int32 TBBlock::CalculateBaseline(TBFontFace *font) const
  798. {
  799. return font->GetAscent();
  800. }
  801. int TBBlock::GetStartIndentation(TBFontFace *font, int first_line_len) const
  802. {
  803. // Lines beginning with whitespace or list points, should
  804. // indent to the same as the beginning when wrapped.
  805. int indentation = 0;
  806. int i = 0;
  807. while (i < first_line_len)
  808. {
  809. const char *current_str = str.CStr() + i;
  810. UCS4 uc = utf8::decode_next(str, &i, first_line_len);
  811. switch (uc)
  812. {
  813. case '\t':
  814. indentation += CalculateTabWidth(font, indentation);
  815. continue;
  816. case ' ':
  817. case '-':
  818. case '*':
  819. indentation += CalculateStringWidth(font, current_str, 1);
  820. continue;
  821. case 0x2022: // BULLET
  822. indentation += CalculateStringWidth(font, current_str, 3);
  823. continue;
  824. };
  825. break;
  826. }
  827. return indentation;
  828. }
  829. void TBBlock::Layout(bool update_fragments, bool propagate_height)
  830. {
  831. // Create fragments from the word fragments
  832. if (update_fragments || !fragments.GetFirst())
  833. {
  834. Clear();
  835. int ofs = 0;
  836. const char *text = str;
  837. while (true)
  838. {
  839. int frag_len;
  840. bool is_embed = false;
  841. bool more = GetNextFragment(&text[ofs], styledit->packed.styling_on ? styledit->content_factory : nullptr, &frag_len, &is_embed);
  842. TBTextFragment *fragment = new TBTextFragment();
  843. if (!fragment)
  844. break;
  845. fragment->Init(this, ofs, frag_len);
  846. if (is_embed)
  847. fragment->content = styledit->content_factory->CreateFragmentContent(&text[ofs], frag_len);
  848. fragments.AddLast(fragment);
  849. ofs += frag_len;
  850. if (!more)
  851. break;
  852. }
  853. }
  854. // Layout
  855. if (styledit->layout_width <= 0 && styledit->GetSizeAffectsLayout())
  856. // Don't layout if we have no space. This will happen when setting text
  857. // before the widget has been layouted. We will relayout when we are resized.
  858. return;
  859. int old_line_width_max = line_width_max;
  860. line_width_max = 0;
  861. int line_ypos = 0;
  862. int first_line_indentation = 0;
  863. TBTextFragment *first_fragment_on_line = fragments.GetFirst();
  864. while (first_fragment_on_line)
  865. {
  866. int line_width = 0;
  867. // Get the last fragment that should be laid out on the line while
  868. // calculating line width and preliminary x positions for the fragments.
  869. TBTextFragment *last_fragment_on_line = fragments.GetLast();
  870. if (styledit->packed.wrapping)
  871. {
  872. // If we should wrap, search for the last allowed break point before the overflow.
  873. TBTextFragment *allowed_last_fragment = nullptr;
  874. int line_xpos = first_line_indentation;
  875. for (TBTextFragment *fragment = first_fragment_on_line; fragment; fragment = fragment->GetNext())
  876. {
  877. // Give the fragment the current x. Then tab widths are calculated properly in GetWidth.
  878. fragment->xpos = line_xpos;
  879. int fragment_w = fragment->GetWidth(styledit->font);
  880. // Check if we overflow
  881. bool overflow = line_xpos + fragment_w > styledit->layout_width;
  882. if (overflow && allowed_last_fragment)
  883. {
  884. last_fragment_on_line = allowed_last_fragment;
  885. break;
  886. }
  887. // Check if this is a allowed break position
  888. if (fragment->GetAllowBreakAfter())
  889. {
  890. if (!fragment->GetNext() || fragment->GetNext()->GetAllowBreakBefore())
  891. {
  892. allowed_last_fragment = fragment;
  893. line_width = line_xpos + fragment_w;
  894. }
  895. }
  896. line_xpos += fragment_w;
  897. }
  898. if (!allowed_last_fragment)
  899. line_width = line_xpos;
  900. }
  901. else
  902. {
  903. // When wrapping is off, just measure and set pos.
  904. line_width = first_line_indentation;
  905. for (TBTextFragment *fragment = first_fragment_on_line; fragment; fragment = fragment->GetNext())
  906. {
  907. fragment->xpos = line_width;
  908. line_width += fragment->GetWidth(styledit->font);
  909. }
  910. }
  911. // Commit line - Layout each fragment on the line.
  912. int line_height = 0;
  913. int line_baseline = 0;
  914. TBTextFragment *fragment = first_fragment_on_line;
  915. while (fragment)
  916. {
  917. line_height = MAX(fragment->GetHeight(styledit->font), line_height);
  918. line_baseline = MAX(fragment->GetBaseline(styledit->font), line_baseline);
  919. // These positions are not final. Will be adjusted below.
  920. fragment->ypos = line_ypos;
  921. if (fragment == last_fragment_on_line)
  922. break;
  923. fragment = fragment->GetNext();
  924. }
  925. // Adjust the position of fragments on the line - now when we know the line totals.
  926. // x change because of alignment, y change because of fragment baseline vs line baseline.
  927. int32 xofs = 0;
  928. if (align == TB_TEXT_ALIGN_RIGHT)
  929. xofs = styledit->layout_width - line_width;
  930. else if (align == TB_TEXT_ALIGN_CENTER)
  931. xofs = (styledit->layout_width - line_width) / 2;
  932. int adjusted_line_height = line_height;
  933. fragment = first_fragment_on_line;
  934. while (fragment)
  935. {
  936. // The fragment need to know these later.
  937. fragment->line_ypos = line_ypos;
  938. fragment->line_height = line_height;
  939. // Adjust the position
  940. fragment->ypos += line_baseline - fragment->GetBaseline(styledit->font);
  941. fragment->xpos += xofs;
  942. // We now know the final position so update content.
  943. fragment->UpdateContentPos();
  944. // Total line height may now have changed a bit.
  945. adjusted_line_height = MAX(line_baseline - fragment->GetBaseline(styledit->font) + fragment->GetHeight(styledit->font), adjusted_line_height);
  946. if (fragment == last_fragment_on_line)
  947. break;
  948. fragment = fragment->GetNext();
  949. }
  950. // Update line_height set on fragments if needed
  951. if (line_height != adjusted_line_height)
  952. {
  953. for (fragment = first_fragment_on_line; fragment != last_fragment_on_line->GetNext(); fragment = fragment->GetNext())
  954. fragment->line_height = adjusted_line_height;
  955. }
  956. line_width_max = MAX(line_width_max, line_width);
  957. // This was the first line so calculate the indentation to use for the other lines.
  958. if (styledit->packed.wrapping && first_fragment_on_line == fragments.GetFirst())
  959. first_line_indentation = GetStartIndentation(styledit->font, last_fragment_on_line->ofs + last_fragment_on_line->len);
  960. // Consume line
  961. line_ypos += adjusted_line_height;
  962. first_fragment_on_line = last_fragment_on_line->GetNext();
  963. }
  964. ypos = GetPrev() ? GetPrev()->ypos + GetPrev()->height : 0;
  965. SetSize(old_line_width_max, line_width_max, line_ypos, propagate_height);
  966. Invalidate();
  967. }
  968. void TBBlock::SetSize(int32 old_w, int32 new_w, int32 new_h, bool propagate_height)
  969. {
  970. // Later: could optimize with Scroll here.
  971. int32 dh = new_h - height;
  972. height = new_h;
  973. if (dh != 0 && propagate_height)
  974. {
  975. TBBlock *block = GetNext();
  976. while (block)
  977. {
  978. block->ypos = block->GetPrev()->ypos + block->GetPrev()->height;
  979. block->Invalidate();
  980. block = block->GetNext();
  981. }
  982. }
  983. // Update content_width and content_height
  984. // content_width can only be calculated in constant time if we grow larger.
  985. // If we shrink our width and where equal to content_width, we don't know
  986. // how wide the widest block is and we set a flag to update it when needed.
  987. if (!styledit->packed.wrapping && !styledit->packed.multiline_on)
  988. styledit->content_width = new_w;
  989. else if (new_w > styledit->content_width)
  990. styledit->content_width = new_w;
  991. else if (new_w < old_w && old_w == styledit->content_width)
  992. styledit->packed.calculate_content_width_needed = 1;
  993. styledit->content_height = styledit->blocks.GetLast()->ypos + styledit->blocks.GetLast()->height;
  994. if (styledit->listener && styledit->packed.lock_scrollbars_counter == 0 && propagate_height)
  995. styledit->listener->UpdateScrollbars();
  996. }
  997. TBTextFragment *TBBlock::FindFragment(int32 ofs, bool prefer_first) const
  998. {
  999. TBTextFragment *fragment = fragments.GetFirst();
  1000. while (fragment)
  1001. {
  1002. if (prefer_first && ofs <= fragment->ofs + fragment->len)
  1003. return fragment;
  1004. if (!prefer_first && ofs < fragment->ofs + fragment->len)
  1005. return fragment;
  1006. fragment = fragment->GetNext();
  1007. }
  1008. return fragments.GetLast();
  1009. }
  1010. TBTextFragment *TBBlock::FindFragment(int32 x, int32 y) const
  1011. {
  1012. TBTextFragment *fragment = fragments.GetFirst();
  1013. while (fragment)
  1014. {
  1015. if (y < fragment->line_ypos + fragment->line_height)
  1016. {
  1017. if (x < fragment->xpos + fragment->GetWidth(styledit->font))
  1018. return fragment;
  1019. if (fragment->GetNext() && fragment->GetNext()->line_ypos > fragment->line_ypos)
  1020. return fragment;
  1021. }
  1022. fragment = fragment->GetNext();
  1023. }
  1024. return fragments.GetLast();
  1025. }
  1026. void TBBlock::Invalidate()
  1027. {
  1028. if (styledit->listener)
  1029. styledit->listener->Invalidate(TBRect(0, - styledit->scroll_y + ypos, styledit->layout_width, height));
  1030. }
  1031. void TBBlock::BuildSelectionRegion(int32 translate_x, int32 translate_y, TBTextProps *props,
  1032. TBRegion &bg_region, TBRegion &fg_region)
  1033. {
  1034. if (!styledit->selection.IsBlockSelected(this))
  1035. return;
  1036. TBTextFragment *fragment = fragments.GetFirst();
  1037. while (fragment)
  1038. {
  1039. fragment->BuildSelectionRegion(translate_x, translate_y + ypos, props, bg_region, fg_region);
  1040. fragment = fragment->GetNext();
  1041. }
  1042. }
  1043. void TBBlock::Paint(int32 translate_x, int32 translate_y, TBTextProps *props)
  1044. {
  1045. TMPDEBUG(styledit->listener->DrawRect(TBRect(translate_x, translate_y + ypos, styledit->layout_width, height), TBColor(255, 200, 0, 128)));
  1046. TBTextFragment *fragment = fragments.GetFirst();
  1047. while (fragment)
  1048. {
  1049. fragment->Paint(translate_x, translate_y + ypos, props);
  1050. fragment = fragment->GetNext();
  1051. }
  1052. }
  1053. // == TBTextFragment =========================================================================
  1054. TBTextFragment::~TBTextFragment()
  1055. {
  1056. delete content;
  1057. }
  1058. void TBTextFragment::Init(TBBlock *block, uint16 ofs, uint16 len)
  1059. {
  1060. this->block = block; this->ofs = ofs; this->len = len;
  1061. }
  1062. void TBTextFragment::UpdateContentPos()
  1063. {
  1064. if (content)
  1065. content->UpdatePos(xpos, ypos + block->ypos);
  1066. }
  1067. void TBTextFragment::BuildSelectionRegion(int32 translate_x, int32 translate_y, TBTextProps *props,
  1068. TBRegion &bg_region, TBRegion &fg_region)
  1069. {
  1070. if (!block->styledit->selection.IsFragmentSelected(this))
  1071. return;
  1072. int x = translate_x + xpos;
  1073. int y = translate_y + ypos;
  1074. TBFontFace *font = props->GetFont();
  1075. if (content)
  1076. {
  1077. // Selected embedded content should add to the foreground region.
  1078. fg_region.IncludeRect(TBRect(x, y, GetWidth(font), GetHeight(font)));
  1079. return;
  1080. }
  1081. // Selected text should add to the backgroud region.
  1082. TBSelection *sel = &block->styledit->selection;
  1083. int sofs1 = sel->start.block == block ? sel->start.ofs : 0;
  1084. int sofs2 = sel->stop.block == block ? sel->stop.ofs : block->str_len;
  1085. sofs1 = MAX(sofs1, (int)ofs);
  1086. sofs2 = MIN(sofs2, (int)(ofs + len));
  1087. int s1x = GetStringWidth(font, block->str.CStr() + ofs, sofs1 - ofs);
  1088. int s2x = GetStringWidth(font, block->str.CStr() + sofs1, sofs2 - sofs1);
  1089. bg_region.IncludeRect(TBRect(x + s1x, y, s2x, GetHeight(font)));
  1090. }
  1091. void TBTextFragment::Paint(int32 translate_x, int32 translate_y, TBTextProps *props)
  1092. {
  1093. TBStyleEditListener *listener = block->styledit->listener;
  1094. int x = translate_x + xpos;
  1095. int y = translate_y + ypos;
  1096. TBColor color = props->data->text_color;
  1097. TBFontFace *font = props->GetFont();
  1098. if (block->styledit->text_theme)
  1099. color = block->styledit->text_theme->themeColors[themeColor];
  1100. if (content)
  1101. {
  1102. content->Paint(this, translate_x, translate_y, props);
  1103. return;
  1104. }
  1105. TMPDEBUG(listener->DrawRect(TBRect(x, y, GetWidth(font), GetHeight(font)), TBColor(255, 255, 255, 128)));
  1106. if (block->styledit->packed.password_on)
  1107. {
  1108. int cw = block->CalculateStringWidth(font, special_char_password);
  1109. int num_char = utf8::count_characters(Str(), len);
  1110. for(int i = 0; i < num_char; i++)
  1111. listener->DrawString(x + i * cw, y, font, color, special_char_password);
  1112. }
  1113. else if (block->styledit->packed.show_whitespace)
  1114. {
  1115. if (IsTab())
  1116. listener->DrawString(x, y, font, color, special_char_tab);
  1117. else if (IsBreak())
  1118. listener->DrawString(x, y, font, color, special_char_newln);
  1119. else if (IsSpace())
  1120. listener->DrawString(x, y, font, color, special_char_space);
  1121. else
  1122. listener->DrawString(x, y, font, color, Str(), len);
  1123. }
  1124. else if (!IsTab() && !IsBreak() && !IsSpace())
  1125. listener->DrawString(x, y, font, color, Str(), len);
  1126. if (props->data->underline)
  1127. {
  1128. int line_h = font->GetHeight() / 16;
  1129. line_h = MAX(line_h, 1);
  1130. listener->DrawRectFill(TBRect(x, y + GetBaseline(font) + 1, GetWidth(font), line_h), color);
  1131. }
  1132. }
  1133. void TBTextFragment::Click(int button, uint32 modifierkeys)
  1134. {
  1135. if (content)
  1136. content->Click(this, button, modifierkeys);
  1137. }
  1138. int32 TBTextFragment::GetWidth(TBFontFace *font)
  1139. {
  1140. if (content)
  1141. return content->GetWidth(font, this);
  1142. if (IsBreak())
  1143. return 0;
  1144. if (IsTab())
  1145. return block->CalculateTabWidth(font, xpos);
  1146. return block->CalculateStringWidth(font, block->str.CStr() + ofs, len);
  1147. }
  1148. int32 TBTextFragment::GetHeight(TBFontFace *font)
  1149. {
  1150. if (content)
  1151. return content->GetHeight(font, this);
  1152. return block->CalculateLineHeight(font);
  1153. }
  1154. int32 TBTextFragment::GetBaseline(TBFontFace *font)
  1155. {
  1156. if (content)
  1157. return content->GetBaseline(font, this);
  1158. return block->CalculateBaseline(font);
  1159. }
  1160. int32 TBTextFragment::GetCharX(TBFontFace *font, int32 ofs)
  1161. {
  1162. assert(ofs >= 0 && ofs <= len);
  1163. if (IsEmbedded() || IsTab())
  1164. return ofs == 0 ? 0 : GetWidth(font);
  1165. if (IsBreak())
  1166. return 0;
  1167. return block->CalculateStringWidth(font, block->str.CStr() + this->ofs, ofs);
  1168. }
  1169. int32 TBTextFragment::GetCharOfs(TBFontFace *font, int32 x)
  1170. {
  1171. if (IsEmbedded() || IsTab())
  1172. return x > GetWidth(font) / 2 ? 1 : 0;
  1173. if (IsBreak())
  1174. return 0;
  1175. const char *str = block->str.CStr() + ofs;
  1176. int i = 0;
  1177. while (i < len)
  1178. {
  1179. int pos = i;
  1180. utf8::move_inc(str, &i, len);
  1181. int last_char_len = i - pos;
  1182. // Always measure from the beginning of the fragment because of eventual kerning & text shaping etc.
  1183. int width_except_last_char = block->CalculateStringWidth(font, str, i - last_char_len);
  1184. int width = block->CalculateStringWidth(font, str, i);
  1185. if (x < width - (width - width_except_last_char) / 2)
  1186. return pos;
  1187. }
  1188. return len;
  1189. }
  1190. int32 TBTextFragment::GetGlobalOfs() const
  1191. {
  1192. int32 gofs = 0;
  1193. TBBlock *b = block->styledit->blocks.GetFirst();
  1194. while (b && b != block)
  1195. {
  1196. gofs += b->str_len;
  1197. b = b->GetNext();
  1198. }
  1199. gofs += ofs;
  1200. return gofs;
  1201. }
  1202. int32 TBTextFragment::GetStringWidth(TBFontFace *font, const char *str, int len)
  1203. {
  1204. if (IsTab())
  1205. return len == 0 ? 0 : block->CalculateTabWidth(font, xpos);
  1206. if (IsBreak())
  1207. return len == 0 ? 0 : 8;
  1208. return block->CalculateStringWidth(font, str, len);
  1209. }
  1210. bool TBTextFragment::IsBreak() const
  1211. {
  1212. return Str()[0] == '\r' || Str()[0] == '\n';
  1213. }
  1214. bool TBTextFragment::IsSpace() const
  1215. {
  1216. return is_space(Str()[0]);
  1217. }
  1218. bool TBTextFragment::IsTab() const
  1219. {
  1220. return Str()[0] == '\t';
  1221. }
  1222. bool TBTextFragment::GetAllowBreakBefore() const
  1223. {
  1224. if (content)
  1225. return content->GetAllowBreakBefore();
  1226. if (len && !is_never_break_before(block->str.CStr(), ofs))
  1227. return true;
  1228. return false;
  1229. }
  1230. bool TBTextFragment::GetAllowBreakAfter() const
  1231. {
  1232. if (content)
  1233. return content->GetAllowBreakAfter();
  1234. if (len && !is_never_break_after(block->str.CStr(), ofs + len - 1))
  1235. return true;
  1236. return false;
  1237. }
  1238. // ============================================================================
  1239. TBStyleEdit::TBStyleEdit()
  1240. : listener(nullptr)
  1241. , content_factory(&default_content_factory)
  1242. , text_change_listener(nullptr)
  1243. , text_theme(nullptr)
  1244. , layout_width(0)
  1245. , layout_height(0)
  1246. , content_width(0)
  1247. , content_height(0)
  1248. , caret(nullptr)
  1249. , selection(nullptr)
  1250. , scroll_x(0)
  1251. , scroll_y(0)
  1252. , select_state(0)
  1253. , mousedown_fragment(nullptr)
  1254. , font(nullptr)
  1255. , align(TB_TEXT_ALIGN_LEFT)
  1256. , packed_init(0)
  1257. {
  1258. caret.styledit = this;
  1259. selection.styledit = this;
  1260. TMPDEBUG(packed.show_whitespace = true);
  1261. font_desc = g_font_manager->GetDefaultFontDescription();
  1262. font = g_font_manager->GetFontFace(font_desc);
  1263. #ifdef TB_TARGET_WINDOWS
  1264. packed.win_style_br = 1;
  1265. #endif
  1266. packed.selection_on = 1;
  1267. Clear();
  1268. }
  1269. TBStyleEdit::~TBStyleEdit()
  1270. {
  1271. listener->CaretBlinkStop();
  1272. Clear(false);
  1273. }
  1274. void TBStyleEdit::SetListener(TBStyleEditListener *listener)
  1275. {
  1276. this->listener = listener;
  1277. }
  1278. void TBStyleEdit::SetContentFactory(TBTextFragmentContentFactory *content_factory)
  1279. {
  1280. if (content_factory)
  1281. this->content_factory = content_factory;
  1282. else
  1283. this->content_factory = &default_content_factory;
  1284. }
  1285. void TBStyleEdit::SetFont(const TBFontDescription &font_desc)
  1286. {
  1287. if (this->font_desc == font_desc)
  1288. return;
  1289. this->font_desc = font_desc;
  1290. font = g_font_manager->GetFontFace(font_desc);
  1291. Reformat(true);
  1292. }
  1293. void TBStyleEdit::Clear(bool init_new)
  1294. {
  1295. undoredo.Clear(true, true);
  1296. selection.SelectNothing();
  1297. if (init_new && blocks.GetFirst() && IsEmpty())
  1298. return;
  1299. for (TBBlock *block = blocks.GetFirst(); block; block = block->GetNext())
  1300. block->Invalidate();
  1301. blocks.DeleteAll();
  1302. if (init_new)
  1303. {
  1304. blocks.AddLast(new TBBlock(this));
  1305. blocks.GetFirst()->Set("", 0);
  1306. }
  1307. caret.Place(blocks.GetFirst(), 0);
  1308. caret.UpdateWantedX();
  1309. }
  1310. void TBStyleEdit::ScrollIfNeeded(bool x, bool y)
  1311. {
  1312. if (layout_width <= 0 || layout_height <= 0)
  1313. return; // This is likely during construction before layout.
  1314. int32 newx = scroll_x, newy = scroll_y;
  1315. if (x)
  1316. {
  1317. if (caret.x - scroll_x < 0)
  1318. newx = caret.x;
  1319. if (caret.x + caret.width - scroll_x > layout_width)
  1320. newx = caret.x + caret.width - layout_width;
  1321. }
  1322. if (y)
  1323. {
  1324. if (caret.y - scroll_y < 0)
  1325. newy = caret.y;
  1326. if (caret.y + caret.height - scroll_y > layout_height)
  1327. newy = caret.y + caret.height - layout_height;
  1328. }
  1329. SetScrollPos(newx, newy);
  1330. }
  1331. void TBStyleEdit::SetScrollPos(int32 x, int32 y)
  1332. {
  1333. x = MIN(x, GetContentWidth() - layout_width);
  1334. y = MIN(y, GetContentHeight() - layout_height);
  1335. x = MAX(x, 0);
  1336. y = MAX(y, 0);
  1337. if (!packed.multiline_on)
  1338. y = 0;
  1339. int dx = scroll_x - x;
  1340. int dy = scroll_y - y;
  1341. if (dx || dy)
  1342. {
  1343. scroll_x = x;
  1344. scroll_y = y;
  1345. listener->Scroll(dx, dy);
  1346. }
  1347. }
  1348. void TBStyleEdit::BeginLockScrollbars()
  1349. {
  1350. packed.lock_scrollbars_counter++;
  1351. }
  1352. void TBStyleEdit::EndLockScrollbars()
  1353. {
  1354. packed.lock_scrollbars_counter--;
  1355. if (listener && packed.lock_scrollbars_counter == 0)
  1356. listener->UpdateScrollbars();
  1357. }
  1358. void TBStyleEdit::SetLayoutSize(int32 width, int32 height, bool is_virtual_reformat)
  1359. {
  1360. if (width == layout_width && height == layout_height)
  1361. return;
  1362. bool reformat = layout_width != width;
  1363. layout_width = width;
  1364. layout_height = height;
  1365. if (reformat && GetSizeAffectsLayout())
  1366. Reformat(false);
  1367. caret.UpdatePos();
  1368. caret.UpdateWantedX();
  1369. if (!is_virtual_reformat)
  1370. SetScrollPos(scroll_x, scroll_y); ///< Trig a bounds check (scroll if outside)
  1371. }
  1372. bool TBStyleEdit::GetSizeAffectsLayout() const
  1373. {
  1374. if (packed.wrapping || align != TB_TEXT_ALIGN_LEFT)
  1375. return true;
  1376. return false;
  1377. }
  1378. void TBStyleEdit::Reformat(bool update_fragments)
  1379. {
  1380. int ypos = 0;
  1381. BeginLockScrollbars();
  1382. TBBlock *block = blocks.GetFirst();
  1383. while (block)
  1384. {
  1385. // Update ypos directly instead of using "propagate_height" since propagating
  1386. // would iterate forward through all remaining blocks and we're going to visit
  1387. // them all anyway.
  1388. block->ypos = ypos;
  1389. block->Layout(update_fragments, false);
  1390. ypos += block->height;
  1391. block = block->GetNext();
  1392. }
  1393. EndLockScrollbars();
  1394. listener->Invalidate(TBRect(0, 0, layout_width, layout_height));
  1395. }
  1396. int32 TBStyleEdit::GetContentWidth()
  1397. {
  1398. if (packed.calculate_content_width_needed)
  1399. {
  1400. packed.calculate_content_width_needed = 0;
  1401. content_width = 0;
  1402. TBBlock *block = blocks.GetFirst();
  1403. while (block)
  1404. {
  1405. content_width = MAX(content_width, block->line_width_max);
  1406. block = block->GetNext();
  1407. }
  1408. }
  1409. return content_width;
  1410. }
  1411. int32 TBStyleEdit::GetContentHeight() const
  1412. {
  1413. return content_height;
  1414. }
  1415. void TBStyleEdit::Paint(const TBRect &rect, const TBFontDescription &font_desc, const TBColor &text_color)
  1416. {
  1417. TBTextProps props(font_desc, text_color);
  1418. // Find the first visible block
  1419. TBBlock *first_visible_block = blocks.GetFirst();
  1420. while (first_visible_block)
  1421. {
  1422. if (first_visible_block->ypos + first_visible_block->height - scroll_y >= 0)
  1423. break;
  1424. first_visible_block = first_visible_block->GetNext();
  1425. }
  1426. // Get the selection region for all visible blocks
  1427. TBRegion bg_region, fg_region;
  1428. if (selection.IsSelected())
  1429. {
  1430. TBBlock *block = first_visible_block;
  1431. while (block)
  1432. {
  1433. if (block->ypos - scroll_y > rect.y + rect.h)
  1434. break;
  1435. block->BuildSelectionRegion(-scroll_x, -scroll_y, &props, bg_region, fg_region);
  1436. block = block->GetNext();
  1437. }
  1438. // Paint bg selection
  1439. for (int i = 0; i < bg_region.GetNumRects(); i++)
  1440. listener->DrawTextSelectionBg(bg_region.GetRect(i));
  1441. }
  1442. // Paint the content
  1443. TBBlock *block = first_visible_block;
  1444. while (block)
  1445. {
  1446. if (block->ypos - scroll_y > rect.y + rect.h)
  1447. break;
  1448. block->Paint(-scroll_x, -scroll_y, &props);
  1449. block = block->GetNext();
  1450. }
  1451. // Paint fg selection
  1452. for (int i = 0; i < fg_region.GetNumRects(); i++)
  1453. listener->DrawTextSelectionBg(fg_region.GetRect(i));
  1454. // Paint caret
  1455. caret.Paint(- scroll_x, - scroll_y);
  1456. }
  1457. void TBStyleEdit::InsertBreak()
  1458. {
  1459. if (!packed.multiline_on)
  1460. return;
  1461. const char *new_line_str = packed.win_style_br ? "\r\n" : "\n";
  1462. // If we stand at the end and don't have any ending break, we're standing at the last line and
  1463. // should insert breaks twice. One to end the current line, and one for the new empty line.
  1464. if (caret.pos.ofs == caret.pos.block->str_len && !caret.pos.block->fragments.GetLast()->IsBreak())
  1465. new_line_str = packed.win_style_br ? "\r\n\r\n" : "\n\n";
  1466. TBStr indent_str;
  1467. for (int i = 0; i < caret.pos.block->str_len; i++)
  1468. {
  1469. if (caret.pos.block->str[i] == '\t')
  1470. indent_str.Append("\t", 1);
  1471. else if (caret.pos.block->str[i] == ' ')
  1472. indent_str.Append(" ", 1);
  1473. else
  1474. break;
  1475. }
  1476. InsertText(new_line_str);
  1477. caret.AvoidLineBreak();
  1478. if (caret.pos.block->GetNext())
  1479. {
  1480. caret.Place(caret.pos.block->GetNext(), 0);
  1481. if (indent_str.Length())
  1482. {
  1483. InsertText(indent_str);
  1484. caret.Place(TBPoint(32000, caret.y));
  1485. }
  1486. }
  1487. if (text_change_listener)
  1488. text_change_listener->OnChange(this);
  1489. }
  1490. void TBStyleEdit::InsertText(const char *text, int32 len, bool after_last, bool clear_undo_redo)
  1491. {
  1492. if (len == TB_ALL_TO_TERMINATION)
  1493. len = strlen(text);
  1494. bool selected = selection.IsSelected();
  1495. selection.RemoveContent();
  1496. if (after_last)
  1497. caret.Place(blocks.GetLast(), blocks.GetLast()->str_len, false);
  1498. int32 len_inserted = caret.pos.block->InsertText(caret.pos.ofs, text, len, true);
  1499. if (clear_undo_redo)
  1500. undoredo.Clear(true, true);
  1501. else
  1502. {
  1503. TBUndoEvent* uevent = undoredo.Commit(this, caret.GetGlobalOfs(), len_inserted, text, true);
  1504. if (selected)
  1505. uevent->chain = true;
  1506. }
  1507. caret.Place(caret.pos.block, caret.pos.ofs + len, false);
  1508. caret.UpdatePos();
  1509. caret.UpdateWantedX();
  1510. if (selected)
  1511. selection.SelectNothing();
  1512. if (text_change_listener)
  1513. text_change_listener->OnChange(this);
  1514. }
  1515. TBBlock *TBStyleEdit::FindBlock(int32 y) const
  1516. {
  1517. TBBlock *block = blocks.GetFirst();
  1518. while (block)
  1519. {
  1520. if (y < block->ypos + block->height)
  1521. return block;
  1522. block = block->GetNext();
  1523. }
  1524. return blocks.GetLast();
  1525. }
  1526. bool TBStyleEdit::KeyDown(int key, SPECIAL_KEY special_key, MODIFIER_KEYS modifierkeys)
  1527. {
  1528. if (select_state)
  1529. return false;
  1530. bool handled = true;
  1531. bool move_caret = special_key == TB_KEY_LEFT || special_key == TB_KEY_RIGHT ||
  1532. special_key == TB_KEY_UP || special_key == TB_KEY_DOWN ||
  1533. special_key == TB_KEY_HOME || special_key == TB_KEY_END ||
  1534. special_key == TB_KEY_PAGE_UP || special_key == TB_KEY_PAGE_DOWN;
  1535. if (!(modifierkeys & TB_SHIFT) && move_caret)
  1536. selection.SelectNothing();
  1537. bool superDown = (modifierkeys & TB_SUPER);
  1538. bool ctrlOrSuper = ((modifierkeys & TB_CTRL) || superDown);
  1539. TBTextOfs old_caret_pos = caret.pos;
  1540. TBTextFragment *old_caret_elm = caret.GetFragment();
  1541. if ((special_key == TB_KEY_UP || special_key == TB_KEY_DOWN) && (modifierkeys & TB_CTRL))
  1542. {
  1543. int32 line_height = old_caret_pos.block->CalculateLineHeight(font);
  1544. int32 new_y = scroll_y + (special_key == TB_KEY_UP ? -line_height : line_height);
  1545. SetScrollPos(scroll_x, new_y);
  1546. }
  1547. else if (special_key == TB_KEY_LEFT && !superDown)
  1548. caret.Move(false, (modifierkeys & TB_CTRL) ? true : false);
  1549. else if (special_key == TB_KEY_RIGHT && !superDown)
  1550. caret.Move(true, (modifierkeys & TB_CTRL) ? true : false);
  1551. else if (special_key == TB_KEY_UP)
  1552. handled = caret.Place(TBPoint(caret.wanted_x, old_caret_pos.block->ypos + old_caret_elm->line_ypos - 1));
  1553. else if (special_key == TB_KEY_DOWN)
  1554. handled = caret.Place(TBPoint(caret.wanted_x, old_caret_pos.block->ypos + old_caret_elm->line_ypos + old_caret_elm->line_height + 1));
  1555. else if (special_key == TB_KEY_PAGE_UP)
  1556. caret.Place(TBPoint(caret.wanted_x, caret.y - layout_height));
  1557. else if (special_key == TB_KEY_PAGE_DOWN)
  1558. caret.Place(TBPoint(caret.wanted_x, caret.y + layout_height + old_caret_elm->line_height));
  1559. else if (special_key == TB_KEY_HOME && modifierkeys & TB_CTRL)
  1560. caret.Place(TBPoint(0, 0));
  1561. else if (special_key == TB_KEY_END && modifierkeys & TB_CTRL)
  1562. caret.Place(TBPoint(32000, blocks.GetLast()->ypos + blocks.GetLast()->height));
  1563. else if (special_key == TB_KEY_HOME || ( special_key == TB_KEY_LEFT && superDown))
  1564. {
  1565. if (old_caret_pos.block)
  1566. {
  1567. int32 pos = old_caret_pos.block->FirstNonTabPos();
  1568. if (old_caret_pos.block->str[pos] == '\n' || old_caret_pos.block->str[pos] == '\r')
  1569. pos = 0;
  1570. if (old_caret_pos.ofs <= pos)
  1571. pos = 0;
  1572. caret.Place(old_caret_pos.block, pos);
  1573. }
  1574. else
  1575. caret.Place(TBPoint(0, caret.y));
  1576. }
  1577. else if (special_key == TB_KEY_END || ( special_key == TB_KEY_RIGHT && superDown))
  1578. caret.Place(TBPoint(32000, caret.y));
  1579. else if (key == '8' && (modifierkeys & TB_CTRL))
  1580. {
  1581. packed.show_whitespace = !packed.show_whitespace;
  1582. listener->Invalidate(TBRect(0, 0, layout_width, layout_height));
  1583. }
  1584. else if (!packed.read_only && (special_key == TB_KEY_DELETE || special_key == TB_KEY_BACKSPACE))
  1585. {
  1586. if (!selection.IsSelected())
  1587. {
  1588. caret.Move(special_key == TB_KEY_DELETE, (modifierkeys & TB_CTRL) ? true : false);
  1589. selection.SelectToCaret(old_caret_pos.block, old_caret_pos.ofs);
  1590. }
  1591. selection.RemoveContent();
  1592. }
  1593. else if (!packed.read_only && !(modifierkeys & TB_SHIFT) && (special_key == TB_KEY_TAB && packed.multiline_on))
  1594. {
  1595. if (!selection.IsSelected() || selection.start.block == selection.stop.block)
  1596. {
  1597. InsertText(" ", 4);
  1598. }
  1599. else
  1600. {
  1601. bool chain = false;
  1602. for (TBBlock* block = selection.start.block; block; block = block->GetNext())
  1603. {
  1604. if (block != selection.stop.block || selection.stop.ofs != 0)
  1605. {
  1606. block->InsertText(0, " ", 4, false);
  1607. TBUndoEvent* uevent = undoredo.Commit(this, block->fragments.GetFirst()->GetGlobalOfs(), 4, " ", true);
  1608. uevent->chain = chain;
  1609. chain = true;
  1610. }
  1611. if (block == selection.stop.block)
  1612. break;
  1613. }
  1614. if (text_change_listener)
  1615. text_change_listener->OnChange(this);
  1616. }
  1617. }
  1618. else if (!packed.read_only && (modifierkeys & TB_SHIFT) && (special_key == TB_KEY_TAB && packed.multiline_on))
  1619. {
  1620. if (!selection.IsSelected() || selection.start.block == selection.stop.block)
  1621. {
  1622. TBBlock* block = caret.pos.block;
  1623. if (block)
  1624. {
  1625. int32 start = block->FirstNonTabPos();
  1626. if (start > 4)
  1627. start = 4;
  1628. if (start)
  1629. {
  1630. TBStr str;
  1631. for (int32 i = 0; i < start; i++)
  1632. {
  1633. str.Append(" ");
  1634. }
  1635. undoredo.Commit(this, block->fragments.GetFirst()->GetGlobalOfs(), start, str.CStr(), false);
  1636. block->RemoveContent(0, start);
  1637. }
  1638. selection.SelectNothing();
  1639. start = block->FirstNonTabPos();
  1640. caret.Place(block, start);
  1641. }
  1642. }
  1643. else
  1644. {
  1645. bool chain = false;
  1646. for (TBBlock* block = selection.start.block; block; block = block->GetNext())
  1647. {
  1648. if (block != selection.stop.block || selection.stop.ofs != 0)
  1649. {
  1650. int32 start = block->FirstNonTabPos();
  1651. if (start > 4)
  1652. start = 4;
  1653. if (start)
  1654. {
  1655. TBStr str;
  1656. for (int32 i = 0; i < start; i++)
  1657. {
  1658. str.Append(" ");
  1659. }
  1660. TBUndoEvent* uevent = undoredo.Commit(this, block->fragments.GetFirst()->GetGlobalOfs(), start, str.CStr(), false);
  1661. uevent->chain = chain;
  1662. chain = true;
  1663. block->RemoveContent(0, start);
  1664. }
  1665. }
  1666. if (block == selection.stop.block)
  1667. break;
  1668. }
  1669. if (text_change_listener)
  1670. text_change_listener->OnChange(this);
  1671. }
  1672. }
  1673. else if (!packed.read_only && (special_key == TB_KEY_ENTER && packed.multiline_on) && !(ctrlOrSuper))
  1674. InsertBreak();
  1675. else if (!packed.read_only && (key && !(ctrlOrSuper)) && special_key != TB_KEY_ENTER)
  1676. {
  1677. char utf8[8];
  1678. int len = utf8::encode(key, utf8);
  1679. InsertText(utf8, len);
  1680. }
  1681. else
  1682. handled = false;
  1683. if ((modifierkeys & TB_SHIFT) && move_caret)
  1684. selection.SelectToCaret(old_caret_pos.block, old_caret_pos.ofs);
  1685. if (!(special_key == TB_KEY_UP || special_key == TB_KEY_DOWN ||
  1686. special_key == TB_KEY_PAGE_UP || special_key == TB_KEY_PAGE_DOWN))
  1687. caret.UpdateWantedX();
  1688. caret.ResetBlink();
  1689. // Hooks
  1690. if (!move_caret && handled)
  1691. listener->OnChange();
  1692. if (special_key == TB_KEY_ENTER && !(modifierkeys & TB_CTRL))
  1693. {
  1694. if (listener->OnEnter())
  1695. handled = true;
  1696. }
  1697. if (handled)
  1698. ScrollIfNeeded();
  1699. return handled;
  1700. }
  1701. void TBStyleEdit::Cut()
  1702. {
  1703. if (packed.password_on)
  1704. return;
  1705. Copy();
  1706. KeyDown(0, TB_KEY_DELETE, TB_MODIFIER_NONE);
  1707. }
  1708. void TBStyleEdit::Copy()
  1709. {
  1710. if (packed.password_on)
  1711. return;
  1712. selection.CopyToClipboard();
  1713. }
  1714. void TBStyleEdit::Paste()
  1715. {
  1716. TBStr text;
  1717. if (TBClipboard::HasText() && TBClipboard::GetText(text))
  1718. {
  1719. InsertText(text, text.Length());
  1720. ScrollIfNeeded(true, true);
  1721. listener->OnChange();
  1722. if (text_change_listener)
  1723. text_change_listener->OnChange(this);
  1724. }
  1725. }
  1726. void TBStyleEdit::Delete()
  1727. {
  1728. if (selection.IsSelected())
  1729. {
  1730. selection.RemoveContent();
  1731. listener->OnChange();
  1732. }
  1733. }
  1734. void TBStyleEdit::Undo()
  1735. {
  1736. if (CanUndo())
  1737. {
  1738. undoredo.Undo(this);
  1739. listener->OnChange();
  1740. }
  1741. }
  1742. void TBStyleEdit::Redo()
  1743. {
  1744. if (CanRedo())
  1745. {
  1746. undoredo.Redo(this);
  1747. listener->OnChange();
  1748. }
  1749. }
  1750. bool TBStyleEdit::MouseDown(const TBPoint &point, int button, int clicks, MODIFIER_KEYS modifierkeys, bool touch)
  1751. {
  1752. if (button != 1)
  1753. return false;
  1754. if (touch)
  1755. {
  1756. mousedown_point = TBPoint(point.x + scroll_x, point.y + scroll_y);
  1757. }
  1758. else if (packed.selection_on)
  1759. {
  1760. if (modifierkeys & TB_SHIFT) // Select to new caretpos
  1761. {
  1762. mousedown_fragment = nullptr;
  1763. mousedown_point = TBPoint(point.x + scroll_x, point.y + scroll_y);
  1764. TBTextOfs old_caret_pos = caret.pos;
  1765. caret.Place(mousedown_point);
  1766. selection.SelectToCaret(old_caret_pos.block, old_caret_pos.ofs);
  1767. }
  1768. else // Start selection
  1769. {
  1770. mousedown_point = TBPoint(point.x + scroll_x, point.y + scroll_y);
  1771. selection.SelectNothing();
  1772. // clicks is 1 to infinite, and here we support only doubleclick, so make it either single or double.
  1773. select_state = ((clicks - 1) % 2) + 1;
  1774. MouseMove(point);
  1775. if (caret.pos.block)
  1776. mousedown_fragment = caret.pos.block->FindFragment(mousedown_point.x, mousedown_point.y - caret.pos.block->ypos);
  1777. }
  1778. caret.ResetBlink();
  1779. }
  1780. return true;
  1781. }
  1782. bool TBStyleEdit::MouseUp(const TBPoint &point, int button, MODIFIER_KEYS modifierkeys, bool touch)
  1783. {
  1784. if (button != 1)
  1785. return false;
  1786. if (touch && !TBWidget::cancel_click)
  1787. {
  1788. selection.SelectNothing();
  1789. caret.Place(mousedown_point);
  1790. caret.UpdateWantedX();
  1791. caret.ResetBlink();
  1792. }
  1793. select_state = 0;
  1794. if (caret.pos.block && !TBWidget::cancel_click)
  1795. {
  1796. TBTextFragment *fragment = caret.pos.block->FindFragment(point.x + scroll_x, point.y + scroll_y - caret.pos.block->ypos);
  1797. if (fragment && fragment == mousedown_fragment)
  1798. fragment->Click(button, modifierkeys);
  1799. }
  1800. return true;
  1801. }
  1802. bool TBStyleEdit::MouseMove(const TBPoint &point)
  1803. {
  1804. if (select_state)
  1805. {
  1806. TBPoint p(point.x + scroll_x, point.y + scroll_y);
  1807. selection.Select(mousedown_point, p);
  1808. if (select_state == 2)
  1809. {
  1810. bool has_initial_selection = selection.IsSelected();
  1811. if (has_initial_selection)
  1812. caret.Place(selection.start.block, selection.start.ofs);
  1813. caret.Move(false, true);
  1814. selection.start.Set(caret.pos);
  1815. if (has_initial_selection)
  1816. caret.Place(selection.stop.block, selection.stop.ofs);
  1817. caret.Move(true, true);
  1818. selection.stop.Set(caret.pos);
  1819. selection.CorrectOrder();
  1820. caret.UpdateWantedX();
  1821. }
  1822. return true;
  1823. }
  1824. return false;
  1825. }
  1826. void TBStyleEdit::Focus(bool focus)
  1827. {
  1828. if (focus)
  1829. listener->CaretBlinkStart();
  1830. else
  1831. listener->CaretBlinkStop();
  1832. caret.on = focus;
  1833. caret.Invalidate();
  1834. selection.Invalidate();
  1835. }
  1836. bool TBStyleEdit::SetText(const char *text, TB_CARET_POS pos)
  1837. {
  1838. return SetText(text, strlen(text), pos);
  1839. }
  1840. bool TBStyleEdit::SetText(const char *text, int text_len, TB_CARET_POS pos)
  1841. {
  1842. if (!text || !*text)
  1843. {
  1844. Clear(true);
  1845. caret.UpdateWantedX();
  1846. ScrollIfNeeded(true, true);
  1847. return true;
  1848. }
  1849. Clear(true);
  1850. blocks.GetFirst()->InsertText(0, text, text_len, true);
  1851. caret.Place(blocks.GetFirst(), 0);
  1852. caret.UpdateWantedX();
  1853. ScrollIfNeeded(true, false);
  1854. if (pos == TB_CARET_POS_END)
  1855. caret.Place(blocks.GetLast(), blocks.GetLast()->str_len);
  1856. listener->OnChange();
  1857. return true;
  1858. }
  1859. bool TBStyleEdit::Load(const char *filename)
  1860. {
  1861. TBFile* f = TBFile::Open(filename, TBFile::MODE_READ);
  1862. if (!f)
  1863. return false;
  1864. uint32 num_bytes = f->Size();
  1865. char *str = new char[num_bytes + 1];
  1866. if (!str)
  1867. {
  1868. delete f;
  1869. return false;
  1870. }
  1871. num_bytes = f->Read(str, 1, num_bytes);
  1872. str[num_bytes] = 0;
  1873. delete f;
  1874. SetText(str);
  1875. delete [] str;
  1876. return true;
  1877. }
  1878. bool TBStyleEdit::GetText(TBStr &text)
  1879. {
  1880. TBSelection tmp_selection(this);
  1881. tmp_selection.SelectAll();
  1882. return tmp_selection.GetText(text);
  1883. }
  1884. bool TBStyleEdit::IsEmpty() const
  1885. {
  1886. return blocks.GetFirst() == blocks.GetLast() && blocks.GetFirst()->str.IsEmpty();
  1887. }
  1888. void TBStyleEdit::SetAlign(TB_TEXT_ALIGN align)
  1889. {
  1890. this->align = align;
  1891. // Call SetAlign on all blocks currently selected, or the block of the current caret position.
  1892. TBBlock *start = selection.IsSelected() ? selection.start.block : caret.pos.block;
  1893. TBBlock *stop = selection.IsSelected() ? selection.stop.block : caret.pos.block;
  1894. while (start && start != stop->GetNext())
  1895. {
  1896. start->SetAlign(align);
  1897. start = start->GetNext();
  1898. }
  1899. }
  1900. void TBStyleEdit::SetMultiline(bool multiline)
  1901. {
  1902. packed.multiline_on = multiline;
  1903. }
  1904. void TBStyleEdit::SetStyling(bool styling)
  1905. {
  1906. packed.styling_on = styling;
  1907. }
  1908. void TBStyleEdit::SetReadOnly(bool readonly)
  1909. {
  1910. packed.read_only = readonly;
  1911. }
  1912. void TBStyleEdit::SetSelection(bool selection)
  1913. {
  1914. packed.selection_on = selection;
  1915. }
  1916. void TBStyleEdit::SetPassword(bool password)
  1917. {
  1918. if (packed.password_on == password)
  1919. return;
  1920. packed.password_on = password;
  1921. Reformat(true);
  1922. }
  1923. void TBStyleEdit::SetWrapping(bool wrapping)
  1924. {
  1925. if (packed.wrapping == wrapping)
  1926. return;
  1927. packed.wrapping = wrapping;
  1928. Reformat(false);
  1929. }
  1930. int32 TBStyleEdit::GetCaretLine()
  1931. {
  1932. int line = 0;
  1933. TBBlock *block = NULL;
  1934. TBTextFragment* frag = caret.GetFragment();
  1935. if (!frag)
  1936. return 0;
  1937. for (block = blocks.GetFirst(); block; block = block->GetNext())
  1938. {
  1939. if (frag->block == block)
  1940. return line;
  1941. line++;
  1942. }
  1943. return 0;
  1944. }
  1945. // == TBUndoRedoStack ==================================================
  1946. TBUndoRedoStack::~TBUndoRedoStack()
  1947. {
  1948. Clear(true, true);
  1949. }
  1950. void TBUndoRedoStack::Undo(TBStyleEdit *styledit)
  1951. {
  1952. if (!undos.GetNumItems())
  1953. return;
  1954. TBUndoEvent *e = undos.Remove(undos.GetNumItems() - 1);
  1955. redos.Add(e);
  1956. Apply(styledit, e, true);
  1957. if (e->chain)
  1958. Undo(styledit);
  1959. }
  1960. void TBUndoRedoStack::Redo(TBStyleEdit *styledit)
  1961. {
  1962. if (!redos.GetNumItems())
  1963. return;
  1964. TBUndoEvent *e = redos.Remove(redos.GetNumItems() - 1);
  1965. undos.Add(e);
  1966. Apply(styledit, e, false);
  1967. if (redos.GetNumItems())
  1968. if (redos[0]->chain)
  1969. Redo(styledit);
  1970. }
  1971. void TBUndoRedoStack::Apply(TBStyleEdit *styledit, TBUndoEvent *e, bool reverse)
  1972. {
  1973. applying = true;
  1974. if (e->insert == reverse)
  1975. {
  1976. styledit->selection.SelectNothing();
  1977. styledit->caret.SetGlobalOfs(e->gofs, false);
  1978. assert(TBTextOfs(styledit->caret.pos).GetGlobalOfs(styledit) == e->gofs);
  1979. TBTextOfs start = styledit->caret.pos;
  1980. styledit->caret.SetGlobalOfs(e->gofs + e->text.Length(), false);
  1981. assert(TBTextOfs(styledit->caret.pos).GetGlobalOfs(styledit) == e->gofs + e->text.Length());
  1982. styledit->selection.Select(start, styledit->caret.pos);
  1983. styledit->selection.RemoveContent();
  1984. }
  1985. else
  1986. {
  1987. styledit->selection.SelectNothing();
  1988. styledit->caret.SetGlobalOfs(e->gofs, true, true);
  1989. styledit->InsertText(e->text);
  1990. int text_len = e->text.Length();
  1991. if (text_len > 1)
  1992. styledit->selection.Select(e->gofs, e->gofs + text_len);
  1993. }
  1994. styledit->ScrollIfNeeded(true, true);
  1995. applying = false;
  1996. }
  1997. void TBUndoRedoStack::Clear(bool clear_undo, bool clear_redo)
  1998. {
  1999. assert(!applying);
  2000. if (clear_undo)
  2001. undos.DeleteAll();
  2002. if (clear_redo)
  2003. redos.DeleteAll();
  2004. }
  2005. TBUndoEvent *TBUndoRedoStack::Commit(TBStyleEdit *styledit, int32 gofs, int32 len, const char *text, bool insert)
  2006. {
  2007. if (applying || styledit->packed.read_only)
  2008. return nullptr;
  2009. Clear(false, true);
  2010. // If we're inserting a single character, check if we want to append it to the previous event.
  2011. if (insert && undos.GetNumItems())
  2012. {
  2013. int num_char = utf8::count_characters(text, len);
  2014. TBUndoEvent *e = undos[undos.GetNumItems() - 1];
  2015. if (num_char == 1 && e->insert && e->gofs + e->text.Length() == gofs)
  2016. {
  2017. // Appending a space to other space(s) should append
  2018. if ((text[0] == ' ' && !strpbrk(e->text.CStr(), "\r\n")) ||
  2019. // But non spaces should not
  2020. !strpbrk(e->text.CStr(), " \r\n"))
  2021. {
  2022. e->text.Append(text, len);
  2023. return e;
  2024. }
  2025. }
  2026. }
  2027. // Create a new event
  2028. if (TBUndoEvent *e = new TBUndoEvent())
  2029. {
  2030. e->gofs = gofs;
  2031. e->text.Set(text, len);
  2032. e->insert = insert;
  2033. undos.Add(e);
  2034. return e;
  2035. }
  2036. // OOM
  2037. Clear(true, true);
  2038. return nullptr;
  2039. }
  2040. }; // namespace tb