tb_style_edit.cpp 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362
  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. , autocomplete_visible(false)
  1250. , select_state(0)
  1251. , mousedown_fragment(nullptr)
  1252. , font(nullptr)
  1253. , align(TB_TEXT_ALIGN_LEFT)
  1254. , packed_init(0)
  1255. {
  1256. caret.styledit = this;
  1257. selection.styledit = this;
  1258. TMPDEBUG(packed.show_whitespace = true);
  1259. font_desc = g_font_manager->GetDefaultFontDescription();
  1260. font = g_font_manager->GetFontFace(font_desc);
  1261. #ifdef TB_TARGET_WINDOWS
  1262. packed.win_style_br = 1;
  1263. #endif
  1264. packed.selection_on = 1;
  1265. Clear();
  1266. }
  1267. TBStyleEdit::~TBStyleEdit()
  1268. {
  1269. listener->CaretBlinkStop();
  1270. Clear(false);
  1271. }
  1272. void TBStyleEdit::SetListener(TBStyleEditListener *listener)
  1273. {
  1274. this->listener = listener;
  1275. }
  1276. void TBStyleEdit::SetContentFactory(TBTextFragmentContentFactory *content_factory)
  1277. {
  1278. if (content_factory)
  1279. this->content_factory = content_factory;
  1280. else
  1281. this->content_factory = &default_content_factory;
  1282. }
  1283. void TBStyleEdit::SetFont(const TBFontDescription &font_desc)
  1284. {
  1285. if (this->font_desc == font_desc)
  1286. return;
  1287. this->font_desc = font_desc;
  1288. font = g_font_manager->GetFontFace(font_desc);
  1289. Reformat(true);
  1290. }
  1291. void TBStyleEdit::Clear(bool init_new)
  1292. {
  1293. undoredo.Clear(true, true);
  1294. selection.SelectNothing();
  1295. if (init_new && blocks.GetFirst() && IsEmpty())
  1296. return;
  1297. for (TBBlock *block = blocks.GetFirst(); block; block = block->GetNext())
  1298. block->Invalidate();
  1299. blocks.DeleteAll();
  1300. if (init_new)
  1301. {
  1302. blocks.AddLast(new TBBlock(this));
  1303. blocks.GetFirst()->Set("", 0);
  1304. }
  1305. caret.Place(blocks.GetFirst(), 0);
  1306. caret.UpdateWantedX();
  1307. }
  1308. void TBStyleEdit::ScrollIfNeeded(bool x, bool y)
  1309. {
  1310. if (layout_width <= 0 || layout_height <= 0)
  1311. return; // This is likely during construction before layout.
  1312. int32 newx = scroll_x, newy = scroll_y;
  1313. if (x)
  1314. {
  1315. if (caret.x - scroll_x < 0)
  1316. newx = caret.x;
  1317. if (caret.x + caret.width - scroll_x > layout_width)
  1318. newx = caret.x + caret.width - layout_width;
  1319. }
  1320. if (y)
  1321. {
  1322. if (caret.y - scroll_y < 0)
  1323. newy = caret.y;
  1324. if (caret.y + caret.height - scroll_y > layout_height)
  1325. newy = caret.y + caret.height - layout_height;
  1326. }
  1327. SetScrollPos(newx, newy);
  1328. }
  1329. void TBStyleEdit::SetScrollPos(int32 x, int32 y)
  1330. {
  1331. x = MIN(x, GetContentWidth() - layout_width);
  1332. y = MIN(y, GetContentHeight() - layout_height);
  1333. x = MAX(x, 0);
  1334. y = MAX(y, 0);
  1335. if (!packed.multiline_on)
  1336. y = 0;
  1337. int dx = scroll_x - x;
  1338. int dy = scroll_y - y;
  1339. if (dx || dy)
  1340. {
  1341. scroll_x = x;
  1342. scroll_y = y;
  1343. listener->Scroll(dx, dy);
  1344. }
  1345. }
  1346. void TBStyleEdit::BeginLockScrollbars()
  1347. {
  1348. packed.lock_scrollbars_counter++;
  1349. }
  1350. void TBStyleEdit::EndLockScrollbars()
  1351. {
  1352. packed.lock_scrollbars_counter--;
  1353. if (listener && packed.lock_scrollbars_counter == 0)
  1354. listener->UpdateScrollbars();
  1355. }
  1356. void TBStyleEdit::SetLayoutSize(int32 width, int32 height, bool is_virtual_reformat)
  1357. {
  1358. if (width == layout_width && height == layout_height)
  1359. return;
  1360. bool reformat = layout_width != width;
  1361. layout_width = width;
  1362. layout_height = height;
  1363. if (reformat && GetSizeAffectsLayout())
  1364. Reformat(false);
  1365. caret.UpdatePos();
  1366. caret.UpdateWantedX();
  1367. if (!is_virtual_reformat)
  1368. SetScrollPos(scroll_x, scroll_y); ///< Trig a bounds check (scroll if outside)
  1369. }
  1370. bool TBStyleEdit::GetSizeAffectsLayout() const
  1371. {
  1372. if (packed.wrapping || align != TB_TEXT_ALIGN_LEFT)
  1373. return true;
  1374. return false;
  1375. }
  1376. void TBStyleEdit::Reformat(bool update_fragments)
  1377. {
  1378. int ypos = 0;
  1379. BeginLockScrollbars();
  1380. TBBlock *block = blocks.GetFirst();
  1381. while (block)
  1382. {
  1383. // Update ypos directly instead of using "propagate_height" since propagating
  1384. // would iterate forward through all remaining blocks and we're going to visit
  1385. // them all anyway.
  1386. block->ypos = ypos;
  1387. block->Layout(update_fragments, false);
  1388. ypos += block->height;
  1389. block = block->GetNext();
  1390. }
  1391. EndLockScrollbars();
  1392. listener->Invalidate(TBRect(0, 0, layout_width, layout_height));
  1393. }
  1394. int32 TBStyleEdit::GetContentWidth()
  1395. {
  1396. if (packed.calculate_content_width_needed)
  1397. {
  1398. packed.calculate_content_width_needed = 0;
  1399. content_width = 0;
  1400. TBBlock *block = blocks.GetFirst();
  1401. while (block)
  1402. {
  1403. content_width = MAX(content_width, block->line_width_max);
  1404. block = block->GetNext();
  1405. }
  1406. }
  1407. return content_width;
  1408. }
  1409. int32 TBStyleEdit::GetContentHeight() const
  1410. {
  1411. return content_height;
  1412. }
  1413. void TBStyleEdit::Paint(const TBRect &rect, const TBFontDescription &font_desc, const TBColor &text_color)
  1414. {
  1415. TBTextProps props(font_desc, text_color);
  1416. // Find the first visible block
  1417. TBBlock *first_visible_block = blocks.GetFirst();
  1418. while (first_visible_block)
  1419. {
  1420. if (first_visible_block->ypos + first_visible_block->height - scroll_y >= 0)
  1421. break;
  1422. first_visible_block = first_visible_block->GetNext();
  1423. }
  1424. // Get the selection region for all visible blocks
  1425. TBRegion bg_region, fg_region;
  1426. if (selection.IsSelected())
  1427. {
  1428. TBBlock *block = first_visible_block;
  1429. while (block)
  1430. {
  1431. if (block->ypos - scroll_y > rect.y + rect.h)
  1432. break;
  1433. block->BuildSelectionRegion(-scroll_x, -scroll_y, &props, bg_region, fg_region);
  1434. block = block->GetNext();
  1435. }
  1436. // Paint bg selection
  1437. for (int i = 0; i < bg_region.GetNumRects(); i++)
  1438. listener->DrawTextSelectionBg(bg_region.GetRect(i));
  1439. }
  1440. // Paint the content
  1441. TBBlock *block = first_visible_block;
  1442. while (block)
  1443. {
  1444. if (block->ypos - scroll_y > rect.y + rect.h)
  1445. break;
  1446. block->Paint(-scroll_x, -scroll_y, &props);
  1447. block = block->GetNext();
  1448. }
  1449. // Paint fg selection
  1450. for (int i = 0; i < fg_region.GetNumRects(); i++)
  1451. listener->DrawTextSelectionBg(fg_region.GetRect(i));
  1452. // Paint caret
  1453. caret.Paint(- scroll_x, - scroll_y);
  1454. }
  1455. void TBStyleEdit::InsertBreak()
  1456. {
  1457. if (!packed.multiline_on)
  1458. return;
  1459. const char *new_line_str = packed.win_style_br ? "\r\n" : "\n";
  1460. // If we stand at the end and don't have any ending break, we're standing at the last line and
  1461. // should insert breaks twice. One to end the current line, and one for the new empty line.
  1462. if (caret.pos.ofs == caret.pos.block->str_len && !caret.pos.block->fragments.GetLast()->IsBreak())
  1463. new_line_str = packed.win_style_br ? "\r\n\r\n" : "\n\n";
  1464. TBStr indent_str;
  1465. for (int i = 0; i < caret.pos.block->str_len; i++)
  1466. {
  1467. if (caret.pos.block->str[i] == '\t')
  1468. indent_str.Append("\t", 1);
  1469. else if (caret.pos.block->str[i] == ' ')
  1470. indent_str.Append(" ", 1);
  1471. else
  1472. break;
  1473. }
  1474. InsertText(new_line_str);
  1475. caret.AvoidLineBreak();
  1476. if (caret.pos.block->GetNext())
  1477. {
  1478. caret.Place(caret.pos.block->GetNext(), 0);
  1479. if (indent_str.Length())
  1480. {
  1481. InsertText(indent_str);
  1482. caret.Place(TBPoint(32000, caret.y));
  1483. }
  1484. }
  1485. if (text_change_listener)
  1486. text_change_listener->OnChange(this);
  1487. }
  1488. void TBStyleEdit::InsertText(const char *text, int32 len, bool after_last, bool clear_undo_redo)
  1489. {
  1490. if (len == TB_ALL_TO_TERMINATION)
  1491. len = strlen(text);
  1492. bool selected = selection.IsSelected();
  1493. selection.RemoveContent();
  1494. if (after_last)
  1495. caret.Place(blocks.GetLast(), blocks.GetLast()->str_len, false);
  1496. int32 len_inserted = caret.pos.block->InsertText(caret.pos.ofs, text, len, true);
  1497. if (clear_undo_redo)
  1498. undoredo.Clear(true, true);
  1499. else
  1500. {
  1501. TBUndoEvent* uevent = undoredo.Commit(this, caret.GetGlobalOfs(), len_inserted, text, true);
  1502. if (selected)
  1503. uevent->chain = true;
  1504. }
  1505. caret.Place(caret.pos.block, caret.pos.ofs + len, false);
  1506. caret.UpdatePos();
  1507. caret.UpdateWantedX();
  1508. if (selected)
  1509. selection.SelectNothing();
  1510. if (text_change_listener)
  1511. text_change_listener->OnChange(this);
  1512. }
  1513. TBBlock *TBStyleEdit::FindBlock(int32 y) const
  1514. {
  1515. TBBlock *block = blocks.GetFirst();
  1516. while (block)
  1517. {
  1518. if (y < block->ypos + block->height)
  1519. return block;
  1520. block = block->GetNext();
  1521. }
  1522. return blocks.GetLast();
  1523. }
  1524. bool TBStyleEdit::KeyDown(int key, SPECIAL_KEY special_key, MODIFIER_KEYS modifierkeys)
  1525. {
  1526. if (select_state)
  1527. return false;
  1528. if (autocomplete_visible
  1529. && (special_key == TB_KEY_UP || special_key == TB_KEY_DOWN
  1530. || special_key == TB_KEY_ENTER || special_key == TB_KEY_ESC
  1531. || special_key == TB_KEY_TAB)
  1532. && !modifierkeys)
  1533. {
  1534. return false;
  1535. }
  1536. bool handled = true;
  1537. bool move_caret = special_key == TB_KEY_LEFT || special_key == TB_KEY_RIGHT ||
  1538. special_key == TB_KEY_UP || special_key == TB_KEY_DOWN ||
  1539. special_key == TB_KEY_HOME || special_key == TB_KEY_END ||
  1540. special_key == TB_KEY_PAGE_UP || special_key == TB_KEY_PAGE_DOWN;
  1541. if (!(modifierkeys & TB_SHIFT) && move_caret)
  1542. selection.SelectNothing();
  1543. bool superDown = (modifierkeys & TB_SUPER);
  1544. bool ctrlOrSuper = ((modifierkeys & TB_CTRL) || superDown);
  1545. TBTextOfs old_caret_pos = caret.pos;
  1546. TBTextFragment *old_caret_elm = caret.GetFragment();
  1547. if ((special_key == TB_KEY_UP || special_key == TB_KEY_DOWN) && (modifierkeys & TB_CTRL))
  1548. {
  1549. int32 line_height = old_caret_pos.block->CalculateLineHeight(font);
  1550. int32 new_y = scroll_y + (special_key == TB_KEY_UP ? -line_height : line_height);
  1551. SetScrollPos(scroll_x, new_y);
  1552. }
  1553. else if (special_key == TB_KEY_LEFT && !superDown)
  1554. caret.Move(false, (modifierkeys & TB_CTRL) ? true : false);
  1555. else if (special_key == TB_KEY_RIGHT && !superDown)
  1556. caret.Move(true, (modifierkeys & TB_CTRL) ? true : false);
  1557. else if (special_key == TB_KEY_UP)
  1558. handled = caret.Place(TBPoint(caret.wanted_x, old_caret_pos.block->ypos + old_caret_elm->line_ypos - 1));
  1559. else if (special_key == TB_KEY_DOWN)
  1560. handled = caret.Place(TBPoint(caret.wanted_x, old_caret_pos.block->ypos + old_caret_elm->line_ypos + old_caret_elm->line_height + 1));
  1561. else if (special_key == TB_KEY_PAGE_UP)
  1562. caret.Place(TBPoint(caret.wanted_x, caret.y - layout_height));
  1563. else if (special_key == TB_KEY_PAGE_DOWN)
  1564. caret.Place(TBPoint(caret.wanted_x, caret.y + layout_height + old_caret_elm->line_height));
  1565. else if (special_key == TB_KEY_HOME && modifierkeys & TB_CTRL)
  1566. caret.Place(TBPoint(0, 0));
  1567. else if (special_key == TB_KEY_END && modifierkeys & TB_CTRL)
  1568. caret.Place(TBPoint(32000, blocks.GetLast()->ypos + blocks.GetLast()->height));
  1569. else if (special_key == TB_KEY_HOME || ( special_key == TB_KEY_LEFT && superDown))
  1570. {
  1571. if (old_caret_pos.block)
  1572. {
  1573. int32 pos = old_caret_pos.block->FirstNonTabPos();
  1574. if (old_caret_pos.block->str[pos] == '\n' || old_caret_pos.block->str[pos] == '\r')
  1575. pos = 0;
  1576. if (old_caret_pos.ofs <= pos)
  1577. pos = 0;
  1578. caret.Place(old_caret_pos.block, pos);
  1579. }
  1580. else
  1581. caret.Place(TBPoint(0, caret.y));
  1582. }
  1583. else if (special_key == TB_KEY_END || ( special_key == TB_KEY_RIGHT && superDown))
  1584. caret.Place(TBPoint(32000, caret.y));
  1585. else if (key == '8' && (modifierkeys & TB_CTRL))
  1586. {
  1587. packed.show_whitespace = !packed.show_whitespace;
  1588. listener->Invalidate(TBRect(0, 0, layout_width, layout_height));
  1589. }
  1590. else if (!packed.read_only && (special_key == TB_KEY_DELETE || special_key == TB_KEY_BACKSPACE))
  1591. {
  1592. if (!selection.IsSelected())
  1593. {
  1594. caret.Move(special_key == TB_KEY_DELETE, (modifierkeys & TB_CTRL) ? true : false);
  1595. selection.SelectToCaret(old_caret_pos.block, old_caret_pos.ofs);
  1596. }
  1597. selection.RemoveContent();
  1598. }
  1599. else if (!packed.read_only && !(modifierkeys & TB_SHIFT) && (special_key == TB_KEY_TAB && packed.multiline_on))
  1600. {
  1601. if (!selection.IsSelected() || selection.start.block == selection.stop.block)
  1602. {
  1603. InsertText(" ", 4);
  1604. }
  1605. else
  1606. {
  1607. bool chain = false;
  1608. for (TBBlock* block = selection.start.block; block; block = block->GetNext())
  1609. {
  1610. if (block != selection.stop.block || selection.stop.ofs != 0)
  1611. {
  1612. block->InsertText(0, " ", 4, false);
  1613. TBUndoEvent* uevent = undoredo.Commit(this, block->fragments.GetFirst()->GetGlobalOfs(), 4, " ", true);
  1614. uevent->chain = chain;
  1615. chain = true;
  1616. }
  1617. if (block == selection.stop.block)
  1618. break;
  1619. }
  1620. if (text_change_listener)
  1621. text_change_listener->OnChange(this);
  1622. }
  1623. }
  1624. else if (!packed.read_only && (modifierkeys & TB_SHIFT) && (special_key == TB_KEY_TAB && packed.multiline_on))
  1625. {
  1626. if (!selection.IsSelected() || selection.start.block == selection.stop.block)
  1627. {
  1628. TBBlock* block = caret.pos.block;
  1629. if (block)
  1630. {
  1631. int32 start = block->FirstNonTabPos();
  1632. if (start > 4)
  1633. start = 4;
  1634. if (start)
  1635. {
  1636. TBStr str;
  1637. for (int32 i = 0; i < start; i++)
  1638. {
  1639. str.Append(" ");
  1640. }
  1641. undoredo.Commit(this, block->fragments.GetFirst()->GetGlobalOfs(), start, str.CStr(), false);
  1642. block->RemoveContent(0, start);
  1643. }
  1644. selection.SelectNothing();
  1645. start = block->FirstNonTabPos();
  1646. caret.Place(block, start);
  1647. }
  1648. }
  1649. else
  1650. {
  1651. bool chain = false;
  1652. for (TBBlock* block = selection.start.block; block; block = block->GetNext())
  1653. {
  1654. if (block != selection.stop.block || selection.stop.ofs != 0)
  1655. {
  1656. int32 start = block->FirstNonTabPos();
  1657. if (start > 4)
  1658. start = 4;
  1659. if (start)
  1660. {
  1661. TBStr str;
  1662. for (int32 i = 0; i < start; i++)
  1663. {
  1664. str.Append(" ");
  1665. }
  1666. TBUndoEvent* uevent = undoredo.Commit(this, block->fragments.GetFirst()->GetGlobalOfs(), start, str.CStr(), false);
  1667. uevent->chain = chain;
  1668. chain = true;
  1669. block->RemoveContent(0, start);
  1670. }
  1671. }
  1672. if (block == selection.stop.block)
  1673. break;
  1674. }
  1675. if (text_change_listener)
  1676. text_change_listener->OnChange(this);
  1677. }
  1678. }
  1679. else if (!packed.read_only && (special_key == TB_KEY_ENTER && packed.multiline_on) && !(ctrlOrSuper))
  1680. InsertBreak();
  1681. else if (!packed.read_only && (key && !(ctrlOrSuper)) && special_key != TB_KEY_ENTER)
  1682. {
  1683. char utf8[8];
  1684. int len = utf8::encode(key, utf8);
  1685. InsertText(utf8, len);
  1686. }
  1687. else
  1688. handled = false;
  1689. if ((modifierkeys & TB_SHIFT) && move_caret)
  1690. selection.SelectToCaret(old_caret_pos.block, old_caret_pos.ofs);
  1691. if (!(special_key == TB_KEY_UP || special_key == TB_KEY_DOWN ||
  1692. special_key == TB_KEY_PAGE_UP || special_key == TB_KEY_PAGE_DOWN))
  1693. caret.UpdateWantedX();
  1694. caret.ResetBlink();
  1695. // Hooks
  1696. if (!move_caret && handled)
  1697. listener->OnChange();
  1698. if (special_key == TB_KEY_ENTER && !(modifierkeys & TB_CTRL))
  1699. {
  1700. if (listener->OnEnter())
  1701. handled = true;
  1702. }
  1703. if (handled)
  1704. ScrollIfNeeded();
  1705. return handled;
  1706. }
  1707. void TBStyleEdit::Cut()
  1708. {
  1709. if (packed.password_on)
  1710. return;
  1711. Copy();
  1712. KeyDown(0, TB_KEY_DELETE, TB_MODIFIER_NONE);
  1713. }
  1714. void TBStyleEdit::Copy()
  1715. {
  1716. if (packed.password_on)
  1717. return;
  1718. selection.CopyToClipboard();
  1719. }
  1720. void TBStyleEdit::Paste()
  1721. {
  1722. TBStr text;
  1723. if (TBClipboard::HasText() && TBClipboard::GetText(text))
  1724. {
  1725. InsertText(text, text.Length());
  1726. ScrollIfNeeded(true, true);
  1727. listener->OnChange();
  1728. if (text_change_listener)
  1729. text_change_listener->OnChange(this);
  1730. }
  1731. }
  1732. void TBStyleEdit::Delete()
  1733. {
  1734. if (selection.IsSelected())
  1735. {
  1736. selection.RemoveContent();
  1737. listener->OnChange();
  1738. }
  1739. }
  1740. void TBStyleEdit::Undo()
  1741. {
  1742. if (CanUndo())
  1743. {
  1744. undoredo.Undo(this);
  1745. listener->OnChange();
  1746. }
  1747. }
  1748. void TBStyleEdit::Redo()
  1749. {
  1750. if (CanRedo())
  1751. {
  1752. undoredo.Redo(this);
  1753. listener->OnChange();
  1754. }
  1755. }
  1756. bool TBStyleEdit::MouseDown(const TBPoint &point, int button, int clicks, MODIFIER_KEYS modifierkeys, bool touch)
  1757. {
  1758. if (button != 1)
  1759. return false;
  1760. if (touch)
  1761. {
  1762. mousedown_point = TBPoint(point.x + scroll_x, point.y + scroll_y);
  1763. }
  1764. else if (packed.selection_on)
  1765. {
  1766. if (modifierkeys & TB_SHIFT) // Select to new caretpos
  1767. {
  1768. mousedown_fragment = nullptr;
  1769. mousedown_point = TBPoint(point.x + scroll_x, point.y + scroll_y);
  1770. TBTextOfs old_caret_pos = caret.pos;
  1771. caret.Place(mousedown_point);
  1772. selection.SelectToCaret(old_caret_pos.block, old_caret_pos.ofs);
  1773. }
  1774. else // Start selection
  1775. {
  1776. mousedown_point = TBPoint(point.x + scroll_x, point.y + scroll_y);
  1777. selection.SelectNothing();
  1778. // clicks is 1 to infinite, and here we support only doubleclick, so make it either single or double.
  1779. select_state = ((clicks - 1) % 2) + 1;
  1780. MouseMove(point);
  1781. if (caret.pos.block)
  1782. mousedown_fragment = caret.pos.block->FindFragment(mousedown_point.x, mousedown_point.y - caret.pos.block->ypos);
  1783. }
  1784. caret.ResetBlink();
  1785. }
  1786. return true;
  1787. }
  1788. bool TBStyleEdit::MouseUp(const TBPoint &point, int button, MODIFIER_KEYS modifierkeys, bool touch)
  1789. {
  1790. if (button != 1)
  1791. return false;
  1792. if (touch && !TBWidget::cancel_click)
  1793. {
  1794. selection.SelectNothing();
  1795. caret.Place(mousedown_point);
  1796. caret.UpdateWantedX();
  1797. caret.ResetBlink();
  1798. }
  1799. select_state = 0;
  1800. if (caret.pos.block && !TBWidget::cancel_click)
  1801. {
  1802. TBTextFragment *fragment = caret.pos.block->FindFragment(point.x + scroll_x, point.y + scroll_y - caret.pos.block->ypos);
  1803. if (fragment && fragment == mousedown_fragment)
  1804. fragment->Click(button, modifierkeys);
  1805. }
  1806. return true;
  1807. }
  1808. bool TBStyleEdit::MouseMove(const TBPoint &point)
  1809. {
  1810. if (select_state)
  1811. {
  1812. TBPoint p(point.x + scroll_x, point.y + scroll_y);
  1813. selection.Select(mousedown_point, p);
  1814. if (select_state == 2)
  1815. {
  1816. bool has_initial_selection = selection.IsSelected();
  1817. if (has_initial_selection)
  1818. caret.Place(selection.start.block, selection.start.ofs);
  1819. caret.Move(false, true);
  1820. selection.start.Set(caret.pos);
  1821. if (has_initial_selection)
  1822. caret.Place(selection.stop.block, selection.stop.ofs);
  1823. caret.Move(true, true);
  1824. selection.stop.Set(caret.pos);
  1825. selection.CorrectOrder();
  1826. caret.UpdateWantedX();
  1827. }
  1828. return true;
  1829. }
  1830. return false;
  1831. }
  1832. void TBStyleEdit::Focus(bool focus)
  1833. {
  1834. if (focus)
  1835. listener->CaretBlinkStart();
  1836. else
  1837. listener->CaretBlinkStop();
  1838. caret.on = focus;
  1839. caret.Invalidate();
  1840. selection.Invalidate();
  1841. }
  1842. bool TBStyleEdit::SetText(const char *text, TB_CARET_POS pos)
  1843. {
  1844. return SetText(text, strlen(text), pos);
  1845. }
  1846. bool TBStyleEdit::SetText(const char *text, int text_len, TB_CARET_POS pos)
  1847. {
  1848. if (!text || !*text)
  1849. {
  1850. Clear(true);
  1851. caret.UpdateWantedX();
  1852. ScrollIfNeeded(true, true);
  1853. return true;
  1854. }
  1855. Clear(true);
  1856. blocks.GetFirst()->InsertText(0, text, text_len, true);
  1857. caret.Place(blocks.GetFirst(), 0);
  1858. caret.UpdateWantedX();
  1859. ScrollIfNeeded(true, false);
  1860. if (pos == TB_CARET_POS_END)
  1861. caret.Place(blocks.GetLast(), blocks.GetLast()->str_len);
  1862. listener->OnChange();
  1863. return true;
  1864. }
  1865. bool TBStyleEdit::Load(const char *filename)
  1866. {
  1867. TBFile* f = TBFile::Open(filename, TBFile::MODE_READ);
  1868. if (!f)
  1869. return false;
  1870. uint32 num_bytes = f->Size();
  1871. char *str = new char[num_bytes + 1];
  1872. if (!str)
  1873. {
  1874. delete f;
  1875. return false;
  1876. }
  1877. num_bytes = f->Read(str, 1, num_bytes);
  1878. str[num_bytes] = 0;
  1879. delete f;
  1880. SetText(str);
  1881. delete [] str;
  1882. return true;
  1883. }
  1884. bool TBStyleEdit::GetText(TBStr &text)
  1885. {
  1886. TBSelection tmp_selection(this);
  1887. tmp_selection.SelectAll();
  1888. return tmp_selection.GetText(text);
  1889. }
  1890. bool TBStyleEdit::IsEmpty() const
  1891. {
  1892. return blocks.GetFirst() == blocks.GetLast() && blocks.GetFirst()->str.IsEmpty();
  1893. }
  1894. void TBStyleEdit::SetAlign(TB_TEXT_ALIGN align)
  1895. {
  1896. this->align = align;
  1897. // Call SetAlign on all blocks currently selected, or the block of the current caret position.
  1898. TBBlock *start = selection.IsSelected() ? selection.start.block : caret.pos.block;
  1899. TBBlock *stop = selection.IsSelected() ? selection.stop.block : caret.pos.block;
  1900. while (start && start != stop->GetNext())
  1901. {
  1902. start->SetAlign(align);
  1903. start = start->GetNext();
  1904. }
  1905. }
  1906. void TBStyleEdit::SetMultiline(bool multiline)
  1907. {
  1908. packed.multiline_on = multiline;
  1909. }
  1910. void TBStyleEdit::SetStyling(bool styling)
  1911. {
  1912. packed.styling_on = styling;
  1913. }
  1914. void TBStyleEdit::SetReadOnly(bool readonly)
  1915. {
  1916. packed.read_only = readonly;
  1917. }
  1918. void TBStyleEdit::SetSelection(bool selection)
  1919. {
  1920. packed.selection_on = selection;
  1921. }
  1922. void TBStyleEdit::SetPassword(bool password)
  1923. {
  1924. if (packed.password_on == password)
  1925. return;
  1926. packed.password_on = password;
  1927. Reformat(true);
  1928. }
  1929. void TBStyleEdit::SetWrapping(bool wrapping)
  1930. {
  1931. if (packed.wrapping == wrapping)
  1932. return;
  1933. packed.wrapping = wrapping;
  1934. Reformat(false);
  1935. }
  1936. int32 TBStyleEdit::GetCaretLine()
  1937. {
  1938. int line = 0;
  1939. TBBlock *block = NULL;
  1940. TBTextFragment* frag = caret.GetFragment();
  1941. if (!frag)
  1942. return 0;
  1943. for (block = blocks.GetFirst(); block; block = block->GetNext())
  1944. {
  1945. if (frag->block == block)
  1946. return line;
  1947. line++;
  1948. }
  1949. return 0;
  1950. }
  1951. // == TBUndoRedoStack ==================================================
  1952. TBUndoRedoStack::~TBUndoRedoStack()
  1953. {
  1954. Clear(true, true);
  1955. }
  1956. void TBUndoRedoStack::Undo(TBStyleEdit *styledit)
  1957. {
  1958. if (!undos.GetNumItems())
  1959. return;
  1960. TBUndoEvent *e = undos.Remove(undos.GetNumItems() - 1);
  1961. redos.Add(e);
  1962. Apply(styledit, e, true);
  1963. if (e->chain)
  1964. Undo(styledit);
  1965. }
  1966. void TBUndoRedoStack::Redo(TBStyleEdit *styledit)
  1967. {
  1968. if (!redos.GetNumItems())
  1969. return;
  1970. TBUndoEvent *e = redos.Remove(redos.GetNumItems() - 1);
  1971. undos.Add(e);
  1972. Apply(styledit, e, false);
  1973. if (redos.GetNumItems())
  1974. if (redos[0]->chain)
  1975. Redo(styledit);
  1976. }
  1977. void TBUndoRedoStack::Apply(TBStyleEdit *styledit, TBUndoEvent *e, bool reverse)
  1978. {
  1979. applying = true;
  1980. if (e->insert == reverse)
  1981. {
  1982. styledit->selection.SelectNothing();
  1983. styledit->caret.SetGlobalOfs(e->gofs, false);
  1984. assert(TBTextOfs(styledit->caret.pos).GetGlobalOfs(styledit) == e->gofs);
  1985. TBTextOfs start = styledit->caret.pos;
  1986. styledit->caret.SetGlobalOfs(e->gofs + e->text.Length(), false);
  1987. assert(TBTextOfs(styledit->caret.pos).GetGlobalOfs(styledit) == e->gofs + e->text.Length());
  1988. styledit->selection.Select(start, styledit->caret.pos);
  1989. styledit->selection.RemoveContent();
  1990. }
  1991. else
  1992. {
  1993. styledit->selection.SelectNothing();
  1994. styledit->caret.SetGlobalOfs(e->gofs, true, true);
  1995. styledit->InsertText(e->text);
  1996. int text_len = e->text.Length();
  1997. if (text_len > 1)
  1998. styledit->selection.Select(e->gofs, e->gofs + text_len);
  1999. }
  2000. styledit->ScrollIfNeeded(true, true);
  2001. applying = false;
  2002. }
  2003. void TBUndoRedoStack::Clear(bool clear_undo, bool clear_redo)
  2004. {
  2005. assert(!applying);
  2006. if (clear_undo)
  2007. undos.DeleteAll();
  2008. if (clear_redo)
  2009. redos.DeleteAll();
  2010. }
  2011. TBUndoEvent *TBUndoRedoStack::Commit(TBStyleEdit *styledit, int32 gofs, int32 len, const char *text, bool insert)
  2012. {
  2013. if (applying || styledit->packed.read_only)
  2014. return nullptr;
  2015. Clear(false, true);
  2016. // If we're inserting a single character, check if we want to append it to the previous event.
  2017. if (insert && undos.GetNumItems())
  2018. {
  2019. int num_char = utf8::count_characters(text, len);
  2020. TBUndoEvent *e = undos[undos.GetNumItems() - 1];
  2021. if (num_char == 1 && e->insert && e->gofs + e->text.Length() == gofs)
  2022. {
  2023. // Appending a space to other space(s) should append
  2024. if ((text[0] == ' ' && !strpbrk(e->text.CStr(), "\r\n")) ||
  2025. // But non spaces should not
  2026. !strpbrk(e->text.CStr(), " \r\n"))
  2027. {
  2028. e->text.Append(text, len);
  2029. return e;
  2030. }
  2031. }
  2032. }
  2033. // Create a new event
  2034. if (TBUndoEvent *e = new TBUndoEvent())
  2035. {
  2036. e->gofs = gofs;
  2037. e->text.Set(text, len);
  2038. e->insert = insert;
  2039. undos.Add(e);
  2040. return e;
  2041. }
  2042. // OOM
  2043. Clear(true, true);
  2044. return nullptr;
  2045. }
  2046. }; // namespace tb