tb_style_edit.cpp 57 KB

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