RunView.cpp 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518
  1. /*
  2. * The contents of this file are subject to the Mozilla Public
  3. * License Version 1.1 (the "License"); you may not use this file
  4. * except in compliance with the License. You may obtain a copy of
  5. * the License at http://www.mozilla.org/MPL/
  6. *
  7. * Software distributed under the License is distributed on an "AS
  8. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9. * implied. See the License for the specific language governing
  10. * rights and limitations under the License.
  11. *
  12. * The Original Code is Vision.
  13. *
  14. * The Initial Developer of the Original Code is The Vision Team.
  15. * Portions created by The Vision Team are
  16. * Copyright (C) 1999, 2000, 2001 The Vision Team. All Rights
  17. * Reserved.
  18. *
  19. * Contributor(s): Rene Gollent
  20. * Todd Lair
  21. * Alan Ellis <alan@cgsoftware.org>
  22. */
  23. #define FORE_WHICH 0
  24. #define BACK_WHICH 1
  25. #define FONT_WHICH 2
  26. #define MARGIN_WIDTH 10.0
  27. #define MARGIN_INDENT 10.0
  28. #define OFFVIEW_TIMER (10000LL)
  29. #define ABS(x) (x * ((x<0) ? -1 : 1))
  30. #define SOFTBREAK_STEP 5
  31. #include <string.h>
  32. #include <stdio.h>
  33. #include <time.h>
  34. #include <ctype.h>
  35. #include <assert.h>
  36. #include <Message.h>
  37. #include <Messenger.h>
  38. #include <MessageRunner.h>
  39. #include <PopUpMenu.h>
  40. #include <MenuItem.h>
  41. #include <Clipboard.h>
  42. #include <ScrollView.h>
  43. #include <ScrollBar.h>
  44. #include <Region.h>
  45. #include <Window.h>
  46. #include <Bitmap.h>
  47. #include <Cursor.h>
  48. #include "ObjectList.h"
  49. #include "Theme.h"
  50. #include "RunView.h"
  51. #include "URLCrunch.h"
  52. #include "Vision.h"
  53. #include "Utilities.h"
  54. // cursor data for hovering over URLs
  55. static unsigned char URLCursorData[] = {16,1,2,2,
  56. 0,0,0,0,56,0,36,0,36,0,19,224,18,92,9,42,
  57. 8,1,60,33,76,49,66,121,48,125,12,253,2,0,1,0,
  58. 0,0,0,0,56,0,60,0,60,0,31,224,31,252,15,254,
  59. 15,255,63,255,127,255,127,255,63,255,15,255,3,254,1,248
  60. };
  61. struct SoftBreak
  62. {
  63. int16 fOffset;
  64. float fHeight;
  65. float fAscent;
  66. };
  67. struct URL
  68. {
  69. int32 fOffset;
  70. int32 fLength;
  71. BString fUrl;
  72. URL (const char *address, int32 off, int32 len) :
  73. fOffset (off),
  74. fLength (len),
  75. fUrl (address)
  76. { }
  77. };
  78. typedef BObjectList<URL> urllist;
  79. struct SoftBreakEnd
  80. {
  81. int16 fOffset;
  82. SoftBreakEnd (int16 offset)
  83. : fOffset (offset)
  84. { }
  85. };
  86. struct FontColor
  87. {
  88. int16 fOffset;
  89. // G++ is stupid. We only need 2 bits
  90. // for fWhich, but the compiler has a bug
  91. // and warns us against fWhich == 2
  92. int16 fWhich : 3;
  93. int16 fIndex : 13;
  94. };
  95. struct Line
  96. {
  97. char *fText;
  98. time_t fStamp;
  99. urllist *fUrls;
  100. int16 *fSpaces;
  101. int16 *fEdges;
  102. FontColor *fFcs;
  103. SoftBreak *fSofties;
  104. float fTop;
  105. float fBottom;
  106. int16 fLength;
  107. int16 fSpace_count;
  108. int16 fEdge_count;
  109. int16 fFc_count;
  110. int16 fSoftie_size;
  111. int16 fSoftie_used;
  112. Line (
  113. const char *buffer,
  114. int16 fLength,
  115. float top,
  116. float width,
  117. Theme *fTheme,
  118. const char *fStamp_format,
  119. int16 fore,
  120. int16 back,
  121. int16 font);
  122. ~Line (void);
  123. void Append (
  124. const char *buffer,
  125. int16 len,
  126. float width,
  127. Theme *fTheme,
  128. int16 fore,
  129. int16 back,
  130. int16 font);
  131. void FigureSpaces (void);
  132. void FigureFontColors (
  133. int16 pos,
  134. int16 fore,
  135. int16 back,
  136. int16 font);
  137. void FigureEdges (
  138. Theme *fTheme,
  139. float width);
  140. void SoftBreaks (
  141. Theme * fTheme,
  142. float width);
  143. void AddSoftBreak (SoftBreakEnd , float &,
  144. uint16 &, int16 &, float &, float &, Theme *);
  145. int16 CountChars (int16 pos, int16 len);
  146. size_t SetStamp (const char *, bool);
  147. void SelectWord (int16 *, int16 *);
  148. };
  149. inline int32
  150. UTF8_CHAR_LEN (uchar c)
  151. {
  152. return (((0xE5000000 >> (((c) >> 3) & 0x1E)) & 3) + 1);
  153. }
  154. RunView::RunView (
  155. BRect frame,
  156. const char *name,
  157. Theme *theme,
  158. uint32 resizingMode,
  159. uint32 flags)
  160. : BView (
  161. frame,
  162. name,
  163. resizingMode,
  164. flags | B_WILL_DRAW | B_FRAME_EVENTS),
  165. fScroller (NULL),
  166. fTheme (theme),
  167. fWorking (NULL),
  168. fLine_count (0),
  169. fStamp_format (NULL),
  170. fClipping_name (NULL),
  171. fSp_start (0, 0),
  172. fSp_end (0, 0),
  173. fTracking (0),
  174. fTrack_offset (0, 0),
  175. fOff_view_runner (NULL),
  176. fOff_view_time (0),
  177. fResizedirty (false),
  178. fFontsdirty (false),
  179. fMyPopUp (NULL),
  180. fLastClick (0,0),
  181. fLastClickTime (0)
  182. {
  183. memset (fLines, 0, sizeof (fLines));
  184. fURLCursor = new BCursor (URLCursorData);
  185. fTheme->ReadLock();
  186. BView::SetViewColor (B_TRANSPARENT_COLOR);
  187. BView::SetLowColor (fTheme->BackgroundAt (Theme::NormalBack));
  188. BView::SetHighColor (fTheme->ForegroundAt (Theme::NormalFore));
  189. fTheme->ReadUnlock();
  190. }
  191. RunView::~RunView (void)
  192. {
  193. for (int16 i = 0; i < fLine_count; ++i)
  194. delete fLines[i];
  195. delete fWorking;
  196. delete fURLCursor;
  197. delete [] fStamp_format;
  198. delete [] fClipping_name;
  199. }
  200. void
  201. RunView::AttachedToWindow (void)
  202. {
  203. BView::AttachedToWindow();
  204. #if B_BEOS_VERSION_DANO
  205. SetDoubleBuffering (B_UPDATE_INVALIDATED | B_UPDATE_SCROLLED | B_UPDATE_EXPOSED | B_UPDATE_RESIZED);
  206. #endif
  207. RecalcScrollBar (false);
  208. fTheme->WriteLock();
  209. fTheme->AddView (this);
  210. fTheme->WriteUnlock();
  211. }
  212. void
  213. RunView::DetachedFromWindow (void)
  214. {
  215. fTheme->WriteLock();
  216. fTheme->RemoveView (this);
  217. fTheme->WriteUnlock();
  218. }
  219. void
  220. RunView::FrameResized (float start_width, float height)
  221. {
  222. BView::FrameResized (start_width, height);
  223. if (IsHidden())
  224. {
  225. fResizedirty = true;
  226. return;
  227. }
  228. ResizeRecalc();
  229. }
  230. void
  231. RunView::TargetedByScrollView (BScrollView *s)
  232. {
  233. fScroller = s;
  234. BView::TargetedByScrollView (fScroller);
  235. }
  236. void
  237. RunView::Show (void)
  238. {
  239. if (fFontsdirty)
  240. {
  241. FontChangeRecalc();
  242. // this effectively does the same thing as resize so if both have changed, only
  243. // do the fonts recalculation
  244. fFontsdirty = false;
  245. fResizedirty = false;
  246. }
  247. else if (fResizedirty)
  248. {
  249. ResizeRecalc();
  250. fResizedirty = false;
  251. }
  252. BView::Show();
  253. }
  254. void
  255. RunView::Draw (BRect frame)
  256. {
  257. Window()->DisableUpdates();
  258. Window()->BeginViewTransaction();
  259. rgb_color low_color, hi_color, view_color, sel_color, sel_fText;
  260. float height (frame.bottom);
  261. BRect bounds (Bounds());
  262. BRegion clipper;
  263. bool drawSelection (false);
  264. bool checkSelection (fSp_start != fSp_end);
  265. clipper.Set (frame);
  266. ConstrainClippingRegion (&clipper);
  267. fTheme->ReadLock();
  268. view_color = fTheme->BackgroundAt (Theme::NormalBack);
  269. sel_color = fTheme->BackgroundAt (Theme::SelectionBack);
  270. if (((sel_color.red + sel_color.blue + sel_color.green) / 3) >= 127)
  271. {
  272. sel_fText.red = sel_fText.green = sel_fText.blue = 0;
  273. sel_fText.alpha = 255;
  274. }
  275. else
  276. {
  277. sel_fText.red = sel_fText.green = sel_fText.blue = sel_fText.alpha = 255;
  278. }
  279. BRect remains;
  280. if (fLine_count == 0)
  281. remains = frame;
  282. else if (frame.bottom >= fLines[fLine_count - 1]->fBottom + 1.0)
  283. remains.Set (
  284. frame.left,
  285. fLines[fLine_count - 1]->fBottom + 1.0,
  286. frame.right,
  287. frame.bottom);
  288. if (remains.IsValid())
  289. {
  290. SetLowColor (view_color);
  291. FillRect (remains, B_SOLID_LOW);
  292. }
  293. for (int16 i = fLine_count - 1; i >= 0; --i)
  294. {
  295. Line *line (fLines[i]);
  296. if (line->fBottom < frame.top)
  297. break;
  298. BRect r (bounds.left, line->fTop, bounds.right, line->fBottom);
  299. if (!frame.Intersects (r))
  300. continue;
  301. float indent (ceil (MARGIN_WIDTH / 2.0));
  302. int16 place (0);
  303. int16 fore (0);
  304. int16 back (0);
  305. int16 font (0);
  306. height = line->fTop;
  307. for (int16 sit = 0; sit < line->fSoftie_used; ++sit)
  308. {
  309. int16 last_len (UTF8_CHAR_LEN (line->fText[line->fSofties[sit].fOffset]));
  310. float left (indent);
  311. float start (0.0);
  312. // Fill indentation
  313. SetLowColor (view_color);
  314. SetDrawingMode (B_OP_COPY);
  315. r.Set (0.0, height, indent - 1.0, height + line->fSofties[sit].fHeight - 1.0);
  316. FillRect (r, B_SOLID_LOW);
  317. if (sit)
  318. {
  319. int16 j (place);
  320. while (--j >= 0)
  321. if ((start = line->fEdges[j]) != 0)
  322. break;
  323. }
  324. while (place < line->fSofties[sit].fOffset + last_len)
  325. {
  326. // Get current foreground color and set
  327. while (fore < line->fFc_count)
  328. {
  329. if (line->fFcs[fore].fWhich == FORE_WHICH)
  330. {
  331. if (line->fFcs[fore].fOffset > place)
  332. break;
  333. hi_color = fTheme->ForegroundAt (line->fFcs[fore].fIndex);
  334. }
  335. ++fore;
  336. }
  337. // Get current background color and set
  338. while (back < line->fFc_count)
  339. {
  340. if (line->fFcs[back].fWhich == BACK_WHICH)
  341. {
  342. if (line->fFcs[back].fOffset > place)
  343. break;
  344. low_color = fTheme->BackgroundAt (line->fFcs[back].fIndex);
  345. }
  346. ++back;
  347. }
  348. // Get current font and set
  349. while (font < line->fFc_count)
  350. {
  351. if (line->fFcs[font].fWhich == FONT_WHICH)
  352. {
  353. if (line->fFcs[font].fOffset > place)
  354. break;
  355. const BFont &f (fTheme->FontAt (line->fFcs[font].fIndex));
  356. SetFont (&f);
  357. }
  358. ++font;
  359. }
  360. int16 fLength (line->fSofties[sit].fOffset - place + last_len);
  361. if (fore < line->fFc_count
  362. && line->fFcs[fore].fOffset - place < fLength)
  363. fLength = line->fFcs[fore].fOffset - place;
  364. if (back < line->fFc_count
  365. && line->fFcs[back].fOffset - place < fLength)
  366. fLength = line->fFcs[back].fOffset - place;
  367. if (font < line->fFc_count
  368. && line->fFcs[font].fOffset - place < fLength)
  369. fLength = line->fFcs[font].fOffset - place;
  370. if (checkSelection)
  371. {
  372. // case 1: current line marks beginning of selection
  373. if (i == fSp_start.fLine)
  374. {
  375. // if we're just prior to the selection, clip fLength to only
  376. // draw up to the selection start
  377. if (place + fLength >= fSp_start.fOffset && place < fSp_start.fOffset)
  378. {
  379. fLength = fSp_start.fOffset - place;
  380. drawSelection = false;
  381. }
  382. // we're at the selection, switch drawing color mode
  383. else if (place >= fSp_start.fOffset)
  384. {
  385. if (fSp_end.fLine == fSp_start.fLine)
  386. {
  387. if (place < fSp_end.fOffset)
  388. {
  389. drawSelection = true;
  390. if ((fSp_end.fOffset - place) < fLength)
  391. fLength = fSp_end.fOffset - place;
  392. }
  393. else
  394. drawSelection = false;
  395. }
  396. else
  397. drawSelection = true;
  398. }
  399. else
  400. drawSelection = false;
  401. }
  402. // case 2: line in between beginning and end of selection,
  403. // highlight entire line
  404. else if (i > fSp_start.fLine && i < fSp_end.fLine)
  405. drawSelection = true;
  406. // case 3: last line of selection, with multiple fLines in between
  407. else if (i == fSp_end.fLine && i != fSp_start.fLine)
  408. {
  409. if (place < (fSp_end.fOffset))
  410. {
  411. if (fSp_end.fOffset - place < fLength)
  412. fLength = (fSp_end.fOffset - place);
  413. drawSelection = true;
  414. }
  415. else
  416. drawSelection = false;
  417. }
  418. else
  419. drawSelection = false;
  420. }
  421. if (place + fLength == line->fLength)
  422. --fLength;
  423. int16 k (place + fLength - 1);
  424. while (line->fEdges[k] == 0)
  425. --k;
  426. r.Set (
  427. left,
  428. height,
  429. line->fEdges[k] + indent - start,
  430. height + line->fSofties[sit].fHeight - 1.0);
  431. SetDrawingMode (B_OP_COPY);
  432. if (drawSelection)
  433. SetLowColor (sel_color);
  434. else
  435. SetLowColor (low_color);
  436. SetHighColor (hi_color);
  437. FillRect (r, B_SOLID_LOW);
  438. if (drawSelection)
  439. SetHighColor (sel_fText);
  440. SetDrawingMode (B_OP_OVER);
  441. DrawString (
  442. line->fText + place,
  443. min_c (fLength, line->fLength - place - 1),
  444. BPoint (left, height + line->fSofties[sit].fAscent));
  445. left = line->fEdges[k] + indent - start;
  446. if ((place += fLength) + 1 >= line->fLength)
  447. ++place;
  448. }
  449. // Margin after fText
  450. SetDrawingMode (B_OP_COPY);
  451. SetLowColor (view_color);
  452. FillRect (
  453. BRect (
  454. left + 1.0,
  455. height,
  456. bounds.right,
  457. height + line->fSofties[sit].fHeight - 1.0),
  458. B_SOLID_LOW);
  459. height += line->fSofties[sit].fHeight;
  460. if (sit == 0)
  461. indent += (MARGIN_INDENT / 2.0);
  462. }
  463. }
  464. fTheme->ReadUnlock();
  465. Window()->EndViewTransaction();
  466. Window()->EnableUpdates();
  467. ConstrainClippingRegion (NULL);
  468. }
  469. void
  470. RunView::SetViewColor (rgb_color color)
  471. {
  472. assert (memcmp (&color, &B_TRANSPARENT_COLOR, sizeof (rgb_color)) != 0);
  473. BView::SetViewColor (color);
  474. }
  475. void
  476. RunView::BuildPopUp (void)
  477. {
  478. // This function checks certain criteria (fText is selected,
  479. // TextView is editable, etc) to determine fWhich MenuItems
  480. // to enable and disable
  481. bool enablecopy (true),
  482. enableselectall (true),
  483. enablelookup (false);
  484. BString querystring ("");
  485. if (fSp_start == fSp_end)
  486. enablecopy = false; // no selection
  487. if (!fLine_count)
  488. enableselectall = false;
  489. if (enablecopy)
  490. {
  491. enablelookup = true; // has a selection less than 32 chars long
  492. GetSelectionText(querystring);
  493. }
  494. fMyPopUp = new BPopUpMenu ("IRCView Context Menu", false, false);
  495. BMenuItem *item;
  496. BMessage *lookup;
  497. lookup = new BMessage (M_LOOKUP_WEBSTER);
  498. lookup->AddString ("string", querystring);
  499. item = new BMenuItem("Lookup (dictionary)", lookup);
  500. item->SetEnabled (enablelookup);
  501. item->SetTarget (Parent());
  502. fMyPopUp->AddItem (item);
  503. lookup = new BMessage (M_LOOKUP_GOOGLE);
  504. lookup->AddString ("string", querystring);
  505. item = new BMenuItem("Lookup (Google)", lookup);
  506. item->SetEnabled (enablelookup);
  507. item->SetTarget (Parent());
  508. fMyPopUp->AddItem (item);
  509. lookup = new BMessage (M_LOOKUP_ACRONYM);
  510. lookup->AddString ("string", querystring);
  511. item = new BMenuItem("Lookup (acronym finder)", lookup);
  512. item->SetEnabled (enablelookup);
  513. item->SetTarget (Parent());
  514. fMyPopUp->AddItem (item);
  515. fMyPopUp->AddSeparatorItem();
  516. item = new BMenuItem("Copy", new BMessage (B_COPY), 'C');
  517. item->SetEnabled (enablecopy);
  518. item->SetTarget (this);
  519. fMyPopUp->AddItem (item);
  520. item = new BMenuItem("Select all", new BMessage (B_SELECT_ALL), 'A');
  521. item->SetEnabled (enableselectall);
  522. item->SetTarget (this);
  523. fMyPopUp->AddItem (item);
  524. fMyPopUp->SetFont (be_plain_font);
  525. }
  526. bool
  527. RunView::CheckClickBounds (const SelectPos &s, const BPoint &point) const
  528. {
  529. return ((point.x <= fLines[s.fLine]->fEdges[fLines[s.fLine]->fLength - 1])
  530. && (point.y <= fLines[s.fLine]->fBottom));
  531. }
  532. void
  533. RunView::MouseDown (BPoint point)
  534. {
  535. if (!fLine_count)
  536. return;
  537. BMessage *msg (Window()->CurrentMessage());
  538. uint32 buttons;
  539. uint32 mouseModifiers;
  540. bigtime_t sysTime;
  541. msg->FindInt64 ("when", &sysTime);
  542. uint16 clicks = CheckClickCount (point, fLastClick, sysTime, fLastClickTime, fClickCount) % 3;
  543. msg->FindInt32 ("buttons", reinterpret_cast<int32 *>(&buttons));
  544. msg->FindInt32 ("modifiers", reinterpret_cast<int32 *>(&mouseModifiers));
  545. SelectPos s (PositionAt (point));
  546. bool inBounds (CheckClickBounds (s, point));
  547. if (buttons == B_SECONDARY_MOUSE_BUTTON
  548. && (mouseModifiers & B_SHIFT_KEY) == 0
  549. && (mouseModifiers & B_COMMAND_KEY) == 0
  550. && (mouseModifiers & B_CONTROL_KEY) == 0
  551. && (mouseModifiers & B_OPTION_KEY) == 0
  552. && (mouseModifiers & B_MENU_KEY) == 0)
  553. {
  554. SelectPos start (s),
  555. end (s);
  556. // select word
  557. if (inBounds && !IntersectSelection (s,s))
  558. {
  559. fLines[s.fLine]->SelectWord (&start.fOffset, &end.fOffset);
  560. Select (start, end);
  561. }
  562. BuildPopUp();
  563. fMyPopUp->Go (
  564. ConvertToScreen (point),
  565. true,
  566. false);
  567. delete fMyPopUp;
  568. fMyPopUp = 0;
  569. return;
  570. }
  571. if (buttons == B_PRIMARY_MOUSE_BUTTON
  572. && (mouseModifiers & B_SHIFT_KEY) == 0
  573. && (mouseModifiers & B_COMMAND_KEY) == 0
  574. && (mouseModifiers & B_CONTROL_KEY) == 0
  575. && (mouseModifiers & B_OPTION_KEY) == 0
  576. && (mouseModifiers & B_MENU_KEY) == 0)
  577. {
  578. SelectPos start (s),
  579. end (s);
  580. switch (clicks)
  581. {
  582. case 2:
  583. {
  584. if (inBounds)
  585. {
  586. // select word
  587. fLines[s.fLine]->SelectWord (&start.fOffset, &end.fOffset);
  588. Select (start, end);
  589. return;
  590. }
  591. }
  592. break;
  593. case 0:
  594. {
  595. if (inBounds)
  596. {
  597. start.fOffset = 0;
  598. end.fOffset = fLines[s.fLine]->fLength - 1;
  599. Select (start, end);
  600. return;
  601. }
  602. }
  603. break;
  604. default:
  605. {
  606. if (!inBounds || !IntersectSelection (s, s))
  607. Select (s,s);
  608. SetMouseEventMask (B_POINTER_EVENTS);
  609. fTracking = 1;
  610. fTrack_offset = s;
  611. return;
  612. }
  613. }
  614. }
  615. else if (buttons == B_PRIMARY_MOUSE_BUTTON
  616. && (mouseModifiers & B_SHIFT_KEY) != 0
  617. && (mouseModifiers & B_COMMAND_KEY) == 0
  618. && (mouseModifiers & B_CONTROL_KEY) == 0
  619. && (mouseModifiers & B_OPTION_KEY) == 0
  620. && (mouseModifiers & B_MENU_KEY) == 0)
  621. {
  622. if (s.fLine < fSp_start.fLine || s.fOffset < fSp_start.fOffset)
  623. {
  624. Select (s, fSp_end);
  625. fTrack_offset = SelectPos (fSp_end.fLine, (fSp_end.fOffset > 0) ? fSp_end.fOffset - 1 : fSp_end.fOffset);
  626. }
  627. else
  628. {
  629. Select (fSp_start, s);
  630. fTrack_offset = fSp_start;
  631. }
  632. SetMouseEventMask (B_POINTER_EVENTS);
  633. fTracking = 2;
  634. }
  635. }
  636. void
  637. RunView::CheckURLCursor (BPoint point)
  638. {
  639. if (!fLine_count)
  640. return;
  641. SelectPos s = PositionAt (point);
  642. if (!fLines[s.fLine]->fUrls)
  643. {
  644. // if there aren't any URLs in the current line, go back to default
  645. SetViewCursor (B_CURSOR_SYSTEM_DEFAULT);
  646. return;
  647. }
  648. Line *curline (fLines[s.fLine]);
  649. for (int32 i = 0; i < curline->fUrls->CountItems(); i++)
  650. {
  651. URL *current = curline->fUrls->ItemAt(i);
  652. if ((s.fOffset >= current->fOffset)
  653. && (s.fOffset <= current->fOffset + current->fLength))
  654. {
  655. SetViewCursor (fURLCursor);
  656. return;
  657. }
  658. }
  659. // no URLs found, set back to default
  660. SetViewCursor (B_CURSOR_SYSTEM_DEFAULT);
  661. }
  662. void
  663. RunView::MouseMoved (BPoint point, uint32 transit, const BMessage *msg)
  664. {
  665. if (fTracking == 0
  666. && fLine_count
  667. && (transit == B_ENTERED_VIEW
  668. || transit == B_INSIDE_VIEW))
  669. CheckURLCursor (point);
  670. if (!fLine_count || fTracking == 0)
  671. {
  672. BView::MouseMoved (point, transit, msg);
  673. return;
  674. }
  675. switch (transit)
  676. {
  677. case B_ENTERED_VIEW:
  678. if (fOff_view_runner)
  679. {
  680. delete fOff_view_runner;
  681. fOff_view_runner = 0;
  682. }
  683. if (fTracking == 1 || fTracking == 2)
  684. ExtendTrackingSelect (point);
  685. break;
  686. case B_EXITED_VIEW:
  687. if (fTracking == 1 || fTracking == 2)
  688. ShiftTrackingSelect (point, true, OFFVIEW_TIMER);
  689. break;
  690. case B_OUTSIDE_VIEW:
  691. if (fTracking == 1 || fTracking == 2)
  692. {
  693. bigtime_t now (system_time());
  694. ShiftTrackingSelect (
  695. point,
  696. false,
  697. max_c (0LL, min_c (OFFVIEW_TIMER, OFFVIEW_TIMER - (now - fOff_view_time))));
  698. }
  699. break;
  700. case B_INSIDE_VIEW:
  701. if ((fTracking == 1) && (fSp_start != fSp_end))
  702. {
  703. BMessage msg (B_MIME_DATA);
  704. BString text;
  705. GetSelectionText (text);
  706. msg.AddData (
  707. "text/plain",
  708. B_MIME_TYPE,
  709. text.String(),
  710. text.Length());
  711. BString clip_name (" Clipping");
  712. if (fClipping_name)
  713. clip_name.Prepend (fClipping_name);
  714. else
  715. clip_name.Prepend ("RunView");
  716. msg.AddString ("be:clip_name", clip_name.String());
  717. msg.AddInt32 ("be:actions", B_COPY_TARGET);
  718. BRect frame (
  719. fLines[fSp_start.fLine]->fEdges[fSp_start.fOffset],
  720. fLines[fSp_start.fLine]->fTop,
  721. fLines[fSp_end.fLine]->fEdges[fSp_end.fOffset],
  722. fLines[fSp_end.fLine]->fBottom);
  723. if (fSp_start.fLine != fSp_end.fLine)
  724. {
  725. frame.left = 0.0;
  726. frame.right = Bounds().right;
  727. }
  728. // selection lies within the bounds of a line, check
  729. // if it fLines on one of the wrapped subfLines and calculate rectangle
  730. // appropriately
  731. else
  732. {
  733. Line *line (fLines[fSp_start.fLine]);
  734. float left (line->fEdges[fSp_start.fOffset]),
  735. top (line->fTop),
  736. right (line->fEdges[fSp_end.fOffset]),
  737. bottom (line->fBottom);
  738. int top_softie (0), bottom_softie (0);
  739. bool start_found (false);
  740. bool end_found (false);
  741. if (line->fSoftie_used)
  742. {
  743. if (fSp_start.fOffset < line->fSofties[0].fOffset)
  744. start_found = true;
  745. if (fSp_end.fOffset < line->fSofties[0].fOffset)
  746. end_found = true;
  747. }
  748. if (!end_found)
  749. for (int16 sit = 1; sit < line->fSoftie_used; ++sit)
  750. {
  751. if (!start_found && fSp_start.fOffset < line->fSofties[sit].fOffset)
  752. {
  753. left = line->fEdges[fSp_start.fOffset] -
  754. line->fEdges[line->fSofties[sit-1].fOffset];
  755. top += (sit) * line->fSofties[sit].fHeight;
  756. top_softie = sit;
  757. start_found = true;
  758. }
  759. if (fSp_end.fOffset < line->fSofties[sit].fOffset)
  760. {
  761. right = line->fEdges[fSp_end.fOffset] -
  762. line->fEdges[line->fSofties[sit-1].fOffset];
  763. bottom = top + (sit - top_softie + 1) * line->fSofties[sit].fHeight;
  764. bottom_softie = sit;
  765. end_found = true;
  766. break;
  767. }
  768. }
  769. if (!end_found)
  770. {
  771. int32 soft_count = (line->fSoftie_used >= 2) ?
  772. line->fSoftie_used - 2 : 0;
  773. right = line->fEdges[line->fLength - 1] -
  774. line->fEdges[line->fSofties[soft_count].fOffset];
  775. bottom_softie = soft_count - 2;
  776. }
  777. if (right < left || (bottom_softie - top_softie) > 0)
  778. {
  779. left = 0.0;
  780. right = Bounds().right;
  781. }
  782. frame.Set (left, top, right, bottom);
  783. frame.OffsetBy (MARGIN_WIDTH / 2.0, 0.0);
  784. }
  785. if (frame.Height() > Bounds().Height())
  786. frame = Bounds();
  787. DragMessage (&msg, frame);
  788. fTracking = 3;
  789. }
  790. else if (fTracking == 1 || fTracking == 2)
  791. ExtendTrackingSelect (point);
  792. break;
  793. }
  794. }
  795. void
  796. RunView::MouseUp (BPoint point)
  797. {
  798. SelectPos s (PositionAt (point));
  799. bool url_handle (false);
  800. if (!fLine_count)
  801. {
  802. fTracking = 0;
  803. return;
  804. }
  805. if (fTracking == 1)
  806. {
  807. Line *curline (fLines[s.fLine]);
  808. if (curline->fUrls)
  809. {
  810. for (int32 i = 0; i < curline->fUrls->CountItems(); i++)
  811. {
  812. URL *current = curline->fUrls->ItemAt(i);
  813. if ((s.fOffset >= current->fOffset)
  814. && (s.fOffset <= current->fOffset + current->fLength))
  815. {
  816. vision_app->LoadURL (current->fUrl.String());
  817. url_handle = true;
  818. break;
  819. }
  820. }
  821. }
  822. if (!url_handle && s == fTrack_offset)
  823. Select (s, s);
  824. }
  825. if (fOff_view_runner)
  826. {
  827. delete fOff_view_runner;
  828. fOff_view_runner = 0;
  829. }
  830. fTracking = 0;
  831. }
  832. void
  833. RunView::ExtendTrackingSelect (BPoint point)
  834. {
  835. SelectPos s (PositionAt (point));
  836. if (s.fLine < fTrack_offset.fLine || (s.fLine == fTrack_offset.fLine && s.fOffset < fTrack_offset.fOffset))
  837. {
  838. Select (s, fTrack_offset);
  839. fTracking = 2;
  840. }
  841. else if (s.fLine > fTrack_offset.fLine || (s.fLine == fTrack_offset.fLine && s.fOffset > fTrack_offset.fOffset))
  842. {
  843. Select (fTrack_offset, s);
  844. fTracking = 2;
  845. }
  846. }
  847. void
  848. RunView::ShiftTrackingSelect (BPoint point, bool move, bigtime_t timer)
  849. {
  850. BRect bounds (Bounds());
  851. if (fOff_view_runner)
  852. {
  853. delete fOff_view_runner;
  854. fOff_view_runner = 0;
  855. }
  856. if (point.y < bounds.top)
  857. {
  858. if (bounds.top > 0.0)
  859. {
  860. float delta (bounds.top - point.y);
  861. if (fOff_view_runner == 0)
  862. {
  863. BMessage *msg (new BMessage (M_OFFVIEW_SELECTION));
  864. msg->AddFloat ("delta", delta);
  865. msg->AddBool ("bottom", false);
  866. msg->AddPoint ("point", point);
  867. fOff_view_runner = new BMessageRunner (
  868. BMessenger (this),
  869. msg,
  870. timer == 0LL ? OFFVIEW_TIMER : timer);
  871. }
  872. if (move || timer == 0)
  873. {
  874. delta = max_c (ABS (ceil (delta / 2.0)), 10.0);
  875. delta = min_c (delta, Bounds().Height());
  876. if (bounds.top - delta < 0.0)
  877. delta = bounds.top;
  878. ScrollBy (0.0, -delta);
  879. fOff_view_time = system_time();
  880. }
  881. }
  882. point.x = 0.0;
  883. point.y = Bounds().top;
  884. ExtendTrackingSelect (point);
  885. }
  886. if (point.y > bounds.bottom)
  887. {
  888. Line *line (fLines[fLine_count-1]);
  889. if (line
  890. && line->fBottom > bounds.bottom)
  891. {
  892. float delta (point.y - bounds.bottom);
  893. if (fOff_view_runner == 0)
  894. {
  895. BMessage *msg (new BMessage (M_OFFVIEW_SELECTION));
  896. msg->AddFloat ("delta", delta);
  897. msg->AddBool ("bottom", true);
  898. msg->AddPoint ("point", point);
  899. fOff_view_runner = new BMessageRunner (
  900. BMessenger (this),
  901. msg,
  902. timer == 0LL ? OFFVIEW_TIMER : timer);
  903. }
  904. if (move || timer == 0)
  905. {
  906. delta = max_c (ABS (ceil (delta / 2.0)), 10.0);
  907. delta = min_c (delta, Bounds().Height());
  908. if (bounds.bottom + delta > line->fBottom)
  909. delta = line->fBottom - bounds.bottom;
  910. ScrollBy (0.0, delta);
  911. fOff_view_time = system_time();
  912. }
  913. }
  914. point.x = Bounds().right;
  915. point.y = Bounds().bottom;
  916. ExtendTrackingSelect (point);
  917. }
  918. else
  919. ExtendTrackingSelect (point);
  920. }
  921. void
  922. RunView::MessageReceived (BMessage *msg)
  923. {
  924. switch (msg->what)
  925. {
  926. case M_THEME_FOREGROUND_CHANGE:
  927. case M_THEME_BACKGROUND_CHANGE:
  928. if (!IsHidden())
  929. Invalidate (Bounds());
  930. break;
  931. case M_THEME_FONT_CHANGE:
  932. {
  933. Theme *save (fTheme);
  934. fTheme = NULL;
  935. SetTheme (save);
  936. break;
  937. }
  938. case B_SELECT_ALL:
  939. SelectAll();
  940. break;
  941. case B_COPY:
  942. if (fSp_start != fSp_end
  943. && be_clipboard->Lock())
  944. {
  945. BString fText;
  946. GetSelectionText (fText);
  947. be_clipboard->Clear();
  948. BMessage *msg (be_clipboard->Data());
  949. msg->AddData ("text/plain", B_MIME_TYPE, fText.String(), fText.Length());
  950. be_clipboard->Commit();
  951. be_clipboard->Unlock();
  952. }
  953. break;
  954. case M_OFFVIEW_SELECTION:
  955. {
  956. BPoint point;
  957. float delta;
  958. bool bottom;
  959. msg->FindPoint ("point", &point);
  960. msg->FindBool ("bottom", &bottom);
  961. msg->FindFloat ("delta", &delta);
  962. if (bottom)
  963. point.y = Bounds().bottom + delta;
  964. else
  965. point.y = Bounds().top - delta;
  966. ShiftTrackingSelect (point, true, OFFVIEW_TIMER);
  967. break;
  968. }
  969. default:
  970. BView::MessageReceived (msg);
  971. }
  972. }
  973. void
  974. RunView::ResizeRecalc (void)
  975. {
  976. float width (Bounds().Width() - MARGIN_WIDTH);
  977. int16 fSoftie_size (0), fSoftie_used (0);
  978. SoftBreak *fSofties (NULL);
  979. BRect bounds (Bounds());
  980. BRegion region;
  981. float top (0.0);
  982. fTheme->ReadLock();
  983. for (int16 i = 0; i < fLine_count; ++i)
  984. {
  985. float old_top (fLines[i]->fTop), old_bottom (fLines[i]->fBottom);
  986. if (fSoftie_size < fLines[i]->fSoftie_used)
  987. {
  988. delete [] fSofties;
  989. fSofties = new SoftBreak [fSoftie_size = fLines[i]->fSoftie_size];
  990. }
  991. fSoftie_used = fLines[i]->fSoftie_used;
  992. memcpy (fSofties, fLines[i]->fSofties, (fSoftie_used * sizeof (SoftBreak)));
  993. fLines[i]->fTop = top;
  994. fLines[i]->SoftBreaks (fTheme, width);
  995. top = fLines[i]->fBottom + 1.0;
  996. BRect r (0.0, fLines[i]->fTop, bounds.right, fLines[i]->fBottom);
  997. if (bounds.Intersects (r)
  998. && (old_top != fLines[i]->fTop
  999. || old_bottom != fLines[i]->fBottom
  1000. || fSoftie_used != fLines[i]->fSoftie_used
  1001. || memcmp (fSofties, fLines[i]->fSofties, fSoftie_used * sizeof (SoftBreak))))
  1002. region.Include (r);
  1003. }
  1004. fTheme->ReadUnlock();
  1005. if (Window())
  1006. {
  1007. if (RecalcScrollBar (true))
  1008. Invalidate (Bounds());
  1009. else
  1010. {
  1011. int32 count (region.CountRects()), j;
  1012. for (j = 0; j < count; ++j)
  1013. Invalidate (region.RectAt (j));
  1014. if (top <= bounds.bottom)
  1015. {
  1016. BRect r (bounds);
  1017. r.top = top;
  1018. Invalidate (r);
  1019. }
  1020. }
  1021. Window()->Sync();
  1022. }
  1023. if (fWorking) fWorking->fTop = top;
  1024. delete [] fSofties;
  1025. }
  1026. void
  1027. RunView::FontChangeRecalc (void)
  1028. {
  1029. float width (Bounds().Width() - MARGIN_WIDTH);
  1030. float top (0.0);
  1031. for (int16 i = 0; i < fLine_count; ++i)
  1032. {
  1033. fLines[i]->fTop = top;
  1034. fLines[i]->FigureSpaces();
  1035. fLines[i]->FigureEdges (fTheme, width);
  1036. top = fLines[i]->fBottom + 1.0;
  1037. }
  1038. if (fWorking)
  1039. fWorking->fTop = top;
  1040. RecalcScrollBar (false);
  1041. if (!IsHidden())
  1042. Invalidate (Bounds());
  1043. if (Window()) Window()->UpdateIfNeeded();
  1044. }
  1045. bool
  1046. RunView::RecalcScrollBar (bool constrain)
  1047. {
  1048. BScrollBar *bar;
  1049. if (fScroller == NULL
  1050. || (bar = fScroller->ScrollBar (B_VERTICAL)) == NULL)
  1051. return false;
  1052. float value (bar->Value());
  1053. BRect bounds (Bounds());
  1054. bool changed (false);
  1055. float bottom (0.0);
  1056. float scrollMin, scrollMax;
  1057. bar->GetRange (&scrollMin, &scrollMax);
  1058. if (fLine_count
  1059. && (bounds.Contains (BPoint (0.0, 0.0)) == false
  1060. || bounds.Contains (BPoint (0.0, fLines[fLine_count - 1]->fBottom)) == false))
  1061. {
  1062. bottom = fLines[fLine_count - 1]->fBottom + 5.0;
  1063. bar->SetProportion (bounds.Height() / bottom);
  1064. bar->SetSteps (10.0, bounds.Height());
  1065. bottom -= bounds.Height();
  1066. }
  1067. // We don't want the bar to cause a draw/copybits, so we restrict the
  1068. // clipping region to nothing
  1069. if (constrain)
  1070. {
  1071. BRegion region;
  1072. ConstrainClippingRegion (&region);
  1073. }
  1074. if (scrollMax != bottom)
  1075. {
  1076. bar->SetRange (0.0, bottom);
  1077. if (value == scrollMax || scrollMin == scrollMax)
  1078. {
  1079. bar->SetValue (bottom);
  1080. changed = true;
  1081. }
  1082. }
  1083. if (constrain)
  1084. ConstrainClippingRegion (NULL);
  1085. return changed;
  1086. }
  1087. void
  1088. RunView::Append (
  1089. const char *buffer,
  1090. int16 fore,
  1091. int16 back,
  1092. int16 font)
  1093. {
  1094. Append (buffer, strlen (buffer), fore, back, font);
  1095. }
  1096. void
  1097. RunView::Append (
  1098. const char *buffer,
  1099. int32 len,
  1100. int16 fore,
  1101. int16 back,
  1102. int16 font)
  1103. {
  1104. if (buffer == NULL)
  1105. return;
  1106. float width (Bounds().Width() - 10);
  1107. int32 place (0);
  1108. assert (fore != Theme::TimestampFore);
  1109. assert (back != Theme::TimestampBack);
  1110. assert (font != Theme::TimestampFont);
  1111. assert (fore != Theme::TimespaceFore);
  1112. assert (back != Theme::TimespaceBack);
  1113. assert (font != Theme::TimespaceFont);
  1114. assert (back != Theme::SelectionBack);
  1115. fTheme->ReadLock();
  1116. while (place < len)
  1117. {
  1118. int32 end (place);
  1119. while (end < len && buffer[end] != '\n')
  1120. ++end;
  1121. if (end < len) ++end;
  1122. if (fWorking)
  1123. {
  1124. URLCrunch crunch (buffer + place, end - place);
  1125. BString temp;
  1126. int32 url_offset (0),
  1127. last_offset (0);
  1128. while ((url_offset = crunch.Crunch (&temp)) != B_ERROR)
  1129. {
  1130. fWorking->Append (buffer + place,
  1131. (url_offset - last_offset),
  1132. width,
  1133. fTheme,
  1134. fore,
  1135. back,
  1136. font);
  1137. fWorking->Append (temp.String(),
  1138. temp.Length(),
  1139. width,
  1140. fTheme,
  1141. C_URL,
  1142. back,
  1143. F_URL);
  1144. place += (url_offset - last_offset) + temp.Length();
  1145. last_offset = url_offset + temp.Length();
  1146. }
  1147. if (place < end)
  1148. fWorking->Append (
  1149. buffer + place,
  1150. end - place,
  1151. width,
  1152. fTheme,
  1153. fore,
  1154. back,
  1155. font);
  1156. }
  1157. else
  1158. {
  1159. float top (0.0);
  1160. if (fLine_count > 0)
  1161. top = fLines[fLine_count - 1]->fBottom + 1.0;
  1162. fWorking = new Line (
  1163. buffer + place,
  1164. 0,
  1165. top,
  1166. width,
  1167. fTheme,
  1168. fStamp_format,
  1169. fore,
  1170. back,
  1171. font);
  1172. URLCrunch crunch (buffer + place, end - place);
  1173. BString temp;
  1174. int32 url_offset (0),
  1175. last_offset (0);
  1176. while ((url_offset = crunch.Crunch (&temp)) != B_ERROR)
  1177. {
  1178. fWorking->Append (buffer + place,
  1179. (url_offset - last_offset),
  1180. width,
  1181. fTheme,
  1182. fore,
  1183. back,
  1184. font);
  1185. fWorking->Append (temp.String(),
  1186. temp.Length(),
  1187. width,
  1188. fTheme,
  1189. C_URL,
  1190. back,
  1191. F_URL);
  1192. place += (url_offset - last_offset) + temp.Length();
  1193. last_offset = url_offset + temp.Length();
  1194. }
  1195. if (place < end)
  1196. fWorking->Append (buffer + place,
  1197. end - place,
  1198. width,
  1199. fTheme,
  1200. fore,
  1201. back,
  1202. font);
  1203. }
  1204. if (fWorking->fLength
  1205. && fWorking->fText[fWorking->fLength - 1] == '\n')
  1206. {
  1207. bool chopped;
  1208. if (Window()) Window()->DisableUpdates();
  1209. if ((chopped = (fLine_count == LINE_COUNT)))
  1210. {
  1211. Line *first (fLines[0]);
  1212. float shift (first->fBottom + 1);
  1213. for (int16 i = 1; i < LINE_COUNT; ++i)
  1214. {
  1215. fLines[i]->fTop -= shift;
  1216. fLines[i]->fBottom -= shift;
  1217. fLines[i - 1] = fLines[i];
  1218. }
  1219. fWorking->fTop -= shift;
  1220. fWorking->fBottom -= shift;
  1221. delete first;
  1222. if (fSp_start.fLine > 0)
  1223. fSp_start.fLine--;
  1224. else
  1225. fSp_start.fOffset = 0;
  1226. if (fSp_end.fLine > 0)
  1227. fSp_end.fLine--;
  1228. else
  1229. fSp_end.fOffset = 0;
  1230. // Recalc the scrollbar so that we have clean drawing
  1231. // after the line has been removed
  1232. --fLine_count;
  1233. RecalcScrollBar (true);
  1234. }
  1235. fLines[fLine_count++] = fWorking;
  1236. RecalcScrollBar (true);
  1237. Invalidate (Bounds());
  1238. if (Window())
  1239. {
  1240. Window()->EnableUpdates();
  1241. Window()->UpdateIfNeeded();
  1242. }
  1243. fWorking = NULL;
  1244. }
  1245. place = end;
  1246. }
  1247. fTheme->ReadUnlock();
  1248. }
  1249. void
  1250. RunView::Clear (void)
  1251. {
  1252. for (int16 i = 0; i < fLine_count; ++i)
  1253. delete fLines[i];
  1254. fLine_count = 0;
  1255. RecalcScrollBar (true);
  1256. Invalidate();
  1257. fSp_start.fLine = 0;
  1258. fSp_start.fOffset = 0;
  1259. fSp_end = fSp_start;
  1260. if (fWorking)
  1261. fWorking->fTop = 0.0;
  1262. }
  1263. int16
  1264. RunView::LineCount (void) const
  1265. {
  1266. return fLine_count;
  1267. }
  1268. const char *
  1269. RunView::LineAt (int16 which) const
  1270. {
  1271. if (which < 0 || which >= fLine_count)
  1272. return NULL;
  1273. return fLines[which]->fText;
  1274. }
  1275. void
  1276. RunView::SetTimeStampFormat (const char *format)
  1277. {
  1278. if ((format == NULL
  1279. && fStamp_format == NULL)
  1280. || (format != NULL
  1281. && fStamp_format != NULL
  1282. && strcmp (format, fStamp_format) == 0))
  1283. return;
  1284. bool was_on (false);
  1285. if (fStamp_format)
  1286. {
  1287. delete [] fStamp_format;
  1288. fStamp_format = NULL;
  1289. was_on = true;
  1290. }
  1291. if (format)
  1292. fStamp_format = strcpy (new char [strlen (format) + 1], format);
  1293. float width (Bounds().Width() - MARGIN_WIDTH);
  1294. float top (0.0);
  1295. fTheme->ReadLock();
  1296. for (int16 i = 0; i < fLine_count; ++i)
  1297. {
  1298. fLines[i]->fTop = top;
  1299. fLines[i]->SetStamp (fStamp_format, was_on);
  1300. fLines[i]->FigureSpaces();
  1301. fLines[i]->FigureEdges(fTheme, width);
  1302. top = fLines[i]->fBottom + 1.0;
  1303. }
  1304. fTheme->ReadUnlock();
  1305. if (fWorking)
  1306. {
  1307. fWorking->fTop = top;
  1308. fWorking->SetStamp (fStamp_format, was_on);
  1309. }
  1310. RecalcScrollBar (false);
  1311. Invalidate (Bounds());
  1312. if (Window()) Window()->UpdateIfNeeded();
  1313. }
  1314. void
  1315. RunView::SetTheme (Theme *t)
  1316. {
  1317. if (t == NULL || fTheme == t)
  1318. return;
  1319. fTheme = t;
  1320. if (IsHidden())
  1321. {
  1322. fFontsdirty = true;
  1323. return;
  1324. }
  1325. FontChangeRecalc();
  1326. }
  1327. SelectPos
  1328. RunView::PositionAt (BPoint point) const
  1329. {
  1330. int16 i, lfIndex (0);
  1331. SelectPos pos (-1, 0);
  1332. if (fLine_count == 0)
  1333. return pos;
  1334. // find the line
  1335. for (i = 0; i < fLine_count; ++i)
  1336. {
  1337. if (fLines[i]->fTop > point.y)
  1338. break;
  1339. lfIndex = i;
  1340. }
  1341. // check to make sure we actually did find a line and not just run into fLine_count
  1342. if (fLines[lfIndex]->fBottom < point.y)
  1343. {
  1344. pos.fLine = fLine_count - 1;
  1345. pos.fOffset = fLines[fLine_count - 1]->fLength;
  1346. return pos;
  1347. }
  1348. float height (fLines[lfIndex]->fTop);
  1349. int16 sfIndex (0);
  1350. for (i = 0; i < fLines[lfIndex]->fSoftie_used; ++i)
  1351. {
  1352. if (height > point.y)
  1353. break;
  1354. sfIndex = i;
  1355. height += fLines[lfIndex]->fSofties[i].fHeight;
  1356. }
  1357. float margin (MARGIN_WIDTH / 2.0);
  1358. float width (0);
  1359. int16 start (0);
  1360. if (sfIndex)
  1361. {
  1362. int16 offset (fLines[lfIndex]->fSofties[sfIndex - 1].fOffset);
  1363. width = fLines[lfIndex]->fEdges[offset];
  1364. start = offset + UTF8_CHAR_LEN (fLines[lfIndex]->fText[offset]);
  1365. }
  1366. for (i = start; i <= fLines[lfIndex]->fSofties[sfIndex].fOffset; ++i)
  1367. if (fLines[lfIndex]->fEdges[i] + margin - width >= point.x)
  1368. break;
  1369. pos.fLine = lfIndex;
  1370. pos.fOffset = min_c (i, fLines[lfIndex]->fSofties[sfIndex].fOffset);
  1371. if (pos.fOffset > 0) pos.fOffset += UTF8_CHAR_LEN (fLines[pos.fLine]->fText[pos.fOffset]);
  1372. return pos;
  1373. }
  1374. BPoint
  1375. RunView::PointAt (SelectPos s) const
  1376. {
  1377. return BPoint(fLines[s.fLine]->fTop + fLines[s.fLine]->fBottom / 2.0, fLines[s.fLine]->fEdges[s.fOffset]);
  1378. }
  1379. void
  1380. RunView::GetSelectionText (BString &string) const
  1381. {
  1382. if (fSp_start == fSp_end)
  1383. return;
  1384. if (fSp_start.fLine == fSp_end.fLine)
  1385. {
  1386. const char *line (LineAt (fSp_start.fLine));
  1387. string.Append (line + fSp_start.fOffset, fSp_end.fOffset - fSp_start.fOffset);
  1388. return;
  1389. }
  1390. for (int32 i = fSp_start.fLine; i <= fSp_end.fLine; i++)
  1391. {
  1392. const char *line (LineAt (i));
  1393. if (i == fSp_start.fLine)
  1394. {
  1395. line += fSp_start.fOffset;
  1396. string.Append (line);
  1397. }
  1398. else if (i == fSp_end.fLine)
  1399. {
  1400. string.Append (line, fSp_end.fOffset);
  1401. break;
  1402. }
  1403. else
  1404. string.Append (line);
  1405. }
  1406. }
  1407. bool
  1408. RunView::IntersectSelection (const SelectPos &start, const SelectPos &end) const
  1409. {
  1410. if (fSp_start.fLine == fSp_end.fLine)
  1411. {
  1412. if (start.fLine == fSp_start.fLine && start.fOffset >= fSp_start.fOffset && start.fOffset < fSp_end.fOffset)
  1413. return true;
  1414. if (end.fLine == fSp_start.fLine && end.fOffset >= fSp_start.fOffset && end.fOffset < fSp_end.fOffset)
  1415. return true;
  1416. }
  1417. else
  1418. {
  1419. if (start.fLine > fSp_start.fLine && start.fLine < fSp_end.fLine)
  1420. return true;
  1421. if (end.fLine > fSp_start.fLine && end.fLine < fSp_end.fLine)
  1422. return true;
  1423. if (start.fLine == fSp_start.fLine && start.fOffset >= fSp_start.fOffset)
  1424. return true;
  1425. if (end.fLine == fSp_start.fLine && end.fOffset >= fSp_start.fOffset)
  1426. return true;
  1427. if (start.fLine == fSp_end.fLine && start.fOffset < fSp_end.fOffset)
  1428. return true;
  1429. if (end.fLine == fSp_end.fLine && end.fOffset < fSp_end.fOffset)
  1430. return true;
  1431. }
  1432. return false;
  1433. }
  1434. BRect
  1435. RunView::GetTextFrame(const SelectPos &start, const SelectPos &end) const
  1436. {
  1437. return BRect (0.0, fLines[(start.fLine > 0) ? (start.fLine - 1) : 0]->fTop,
  1438. Bounds().Width(), fLines[end.fLine]->fBottom);
  1439. }
  1440. void
  1441. RunView::Select (const SelectPos &start, const SelectPos &end)
  1442. {
  1443. if (fSp_start != fSp_end)
  1444. {
  1445. if (start == end || !IntersectSelection (start, end))
  1446. {
  1447. BRect frame (GetTextFrame (fSp_start, fSp_end));
  1448. fSp_start = start;
  1449. fSp_end = start;
  1450. Invalidate (frame);
  1451. }
  1452. else
  1453. {
  1454. if (fSp_start.fLine < start.fLine || (fSp_start.fLine == start.fLine && fSp_start.fOffset < start.fOffset))
  1455. {
  1456. BRect frame (GetTextFrame (fSp_start, start));
  1457. fSp_start = start;
  1458. Invalidate (frame);
  1459. }
  1460. if (end.fLine < fSp_end.fLine || (fSp_end.fLine == end.fLine && end.fOffset < fSp_end.fOffset))
  1461. {
  1462. BRect frame (GetTextFrame (end, fSp_end));
  1463. fSp_end = end;
  1464. Invalidate (frame);
  1465. }
  1466. }
  1467. }
  1468. if (fSp_start == fSp_end)
  1469. {
  1470. fSp_start = start;
  1471. fSp_end = end;
  1472. if (fSp_start != fSp_end)
  1473. {
  1474. BRect frame (GetTextFrame (start, end));
  1475. Invalidate (frame);
  1476. }
  1477. }
  1478. else // extension
  1479. {
  1480. if (start.fLine < fSp_start.fLine || (start.fLine == fSp_start.fLine && start.fOffset < fSp_start.fOffset))
  1481. {
  1482. BRect frame (GetTextFrame (start, fSp_start));
  1483. fSp_start = start;
  1484. Invalidate (frame);
  1485. }
  1486. if (end.fLine > fSp_end.fLine || (end.fLine == fSp_end.fLine && end.fOffset > fSp_end.fOffset))
  1487. {
  1488. BRect frame (GetTextFrame (fSp_end, end));
  1489. fSp_end = end;
  1490. Invalidate (frame);
  1491. }
  1492. }
  1493. }
  1494. void
  1495. RunView::SelectAll (void)
  1496. {
  1497. if (fLine_count)
  1498. {
  1499. fSp_start = SelectPos (0, 0);
  1500. fSp_end = SelectPos (fLine_count-1, fLines[fLine_count-1]->fLength);
  1501. Invalidate(Bounds());
  1502. }
  1503. }
  1504. void
  1505. RunView::SetClippingName (const char *name)
  1506. {
  1507. delete [] fClipping_name;
  1508. fClipping_name = new char[strlen(name) + 1];
  1509. memcpy (fClipping_name, name, strlen(name));
  1510. fClipping_name[strlen(name)] = '\0';
  1511. }
  1512. Line::Line (
  1513. const char *buffer,
  1514. int16 len,
  1515. float top,
  1516. float width,
  1517. Theme *theme,
  1518. const char *stamp_format,
  1519. int16 fore,
  1520. int16 back,
  1521. int16 font)
  1522. : fText (NULL),
  1523. fStamp (time(NULL)),
  1524. fUrls (NULL),
  1525. fSpaces (NULL),
  1526. fEdges (NULL),
  1527. fFcs (NULL),
  1528. fSofties (NULL),
  1529. fTop (top),
  1530. fBottom (0.0),
  1531. fLength (len),
  1532. fSpace_count (0),
  1533. fEdge_count (0),
  1534. fFc_count (0),
  1535. fSoftie_size (0),
  1536. fSoftie_used (0)
  1537. {
  1538. // Very important to call SetStamp before Append, It would look real funny otherwise!
  1539. SetStamp( stamp_format, false );
  1540. Append( buffer, len, width, theme, fore, back, font );
  1541. }
  1542. Line::~Line (void)
  1543. {
  1544. delete [] fSpaces;
  1545. delete [] fEdges;
  1546. delete [] fFcs;
  1547. delete [] fText;
  1548. delete [] fSofties;
  1549. if (fUrls)
  1550. {
  1551. while (fUrls->CountItems() > 0)
  1552. delete fUrls->RemoveItemAt(0L);
  1553. delete fUrls;
  1554. }
  1555. }
  1556. void
  1557. Line::Append (
  1558. const char *buffer,
  1559. int16 len,
  1560. float width,
  1561. Theme *theme,
  1562. int16 fore,
  1563. int16 back,
  1564. int16 font)
  1565. {
  1566. int16 save (fLength);
  1567. char *new_fText;
  1568. new_fText = new char [fLength + len + 1];
  1569. if (fText != NULL)
  1570. {
  1571. memcpy (new_fText, fText, fLength);
  1572. delete [] fText;
  1573. }
  1574. memcpy (new_fText + fLength, buffer, len);
  1575. fLength += len;
  1576. new_fText[fLength] = '\0';
  1577. // replace Tab chars with spaces.
  1578. // todo: This should be temp until RunView can properly
  1579. // display tabs.
  1580. for( char* pos = new_fText + save; *pos; ++pos )
  1581. {
  1582. if( '\t' == *pos )
  1583. {
  1584. *pos = ' ';
  1585. }
  1586. }
  1587. fText = new_fText;
  1588. FigureFontColors (save, fore, back, font);
  1589. if (fore == C_URL)
  1590. {
  1591. if (!fUrls)
  1592. fUrls = new urllist;
  1593. fUrls->AddItem (new URL (buffer, save, len));
  1594. }
  1595. if (fText[fLength - 1] == '\n')
  1596. {
  1597. FigureSpaces();
  1598. FigureEdges (theme, width);
  1599. }
  1600. }
  1601. void
  1602. Line::FigureSpaces (void)
  1603. {
  1604. const char spacers[] = " \t\n-\\/";
  1605. const char *buffer (fText);
  1606. size_t offset (0), n;
  1607. int16 count (0);
  1608. delete [] fSpaces;
  1609. fSpace_count = 0;
  1610. while ((n = strcspn (buffer + offset, spacers)) < fLength - offset)
  1611. {
  1612. ++count;
  1613. offset += n + 1;
  1614. }
  1615. fSpaces = new int16 [count];
  1616. offset = 0;
  1617. while ((n = strcspn (buffer + offset, spacers)) < fLength - offset)
  1618. {
  1619. fSpaces[fSpace_count++] = n + offset;
  1620. offset += n + 1;
  1621. }
  1622. }
  1623. void
  1624. Line::FigureFontColors (
  1625. int16 pos,
  1626. int16 fore,
  1627. int16 back,
  1628. int16 font)
  1629. {
  1630. if (fFc_count)
  1631. {
  1632. int16 last_fore = -1;
  1633. int16 last_back = -1;
  1634. int16 last_font = -1;
  1635. int16 i;
  1636. // we have fFcs, so we backtrack for last of each fWhich
  1637. for (i = fFc_count - 1; i >= 0; --i)
  1638. {
  1639. if (last_fore < 0
  1640. && fFcs[i].fWhich == FORE_WHICH)
  1641. last_fore = i;
  1642. else if (last_back < 0
  1643. && fFcs[i].fWhich == BACK_WHICH)
  1644. last_back = i;
  1645. else if (last_font < 0
  1646. && fFcs[i].fWhich == FONT_WHICH)
  1647. last_font = i;
  1648. if (last_fore >= 0
  1649. && last_back >= 0
  1650. && last_font >= 0)
  1651. break;
  1652. }
  1653. // now figure out how many more we need
  1654. int16 count = 0;
  1655. if (fFcs[last_fore].fIndex != fore)
  1656. ++count;
  1657. if (fFcs[last_back].fIndex != back)
  1658. ++count;
  1659. if (fFcs[last_font].fIndex != font)
  1660. ++count;
  1661. if (count)
  1662. {
  1663. FontColor *new_fFcs;
  1664. new_fFcs = new FontColor [fFc_count + count];
  1665. memcpy (new_fFcs, fFcs, fFc_count * sizeof (FontColor));
  1666. delete [] fFcs;
  1667. fFcs = new_fFcs;
  1668. if (fFcs[last_fore].fIndex != fore)
  1669. {
  1670. fFcs[fFc_count].fWhich = FORE_WHICH;
  1671. fFcs[fFc_count].fOffset = pos;
  1672. fFcs[fFc_count].fIndex = fore;
  1673. ++fFc_count;
  1674. }
  1675. if (fFcs[last_back].fIndex != back)
  1676. {
  1677. fFcs[fFc_count].fWhich = BACK_WHICH;
  1678. fFcs[fFc_count].fOffset = pos;
  1679. fFcs[fFc_count].fIndex = back;
  1680. ++fFc_count;
  1681. }
  1682. if (fFcs[last_font].fIndex != font)
  1683. {
  1684. fFcs[fFc_count].fWhich = FONT_WHICH;
  1685. fFcs[fFc_count].fOffset = pos;
  1686. fFcs[fFc_count].fIndex = font;
  1687. ++fFc_count;
  1688. }
  1689. }
  1690. }
  1691. else
  1692. {
  1693. fFcs = new FontColor [fFc_count = 3];
  1694. fFcs[0].fWhich = FORE_WHICH;
  1695. fFcs[0].fOffset = 0;
  1696. fFcs[0].fIndex = fore;
  1697. fFcs[1].fWhich = BACK_WHICH;
  1698. fFcs[1].fOffset = 0;
  1699. fFcs[1].fIndex = back;
  1700. fFcs[2].fWhich = FONT_WHICH;
  1701. fFcs[2].fOffset = 0;
  1702. fFcs[2].fIndex = font;
  1703. }
  1704. }
  1705. void
  1706. Line::FigureEdges (
  1707. Theme *theme,
  1708. float width)
  1709. {
  1710. delete [] fEdges;
  1711. fEdges = new int16 [fLength];
  1712. int16 cur_fFcs (0), next_fFcs (0), cur_font (0);
  1713. fEdge_count = 0;
  1714. while (cur_fFcs < fFc_count)
  1715. {
  1716. if (fFcs[cur_fFcs].fWhich == FONT_WHICH)
  1717. {
  1718. cur_font = cur_fFcs;
  1719. break;
  1720. }
  1721. ++cur_fFcs;
  1722. }
  1723. while (cur_fFcs < fFc_count)
  1724. {
  1725. int16 last_offset (fFcs[cur_fFcs].fOffset);
  1726. next_fFcs = cur_fFcs + 1;
  1727. while (next_fFcs < fFc_count)
  1728. {
  1729. // We want to break at every difference
  1730. // but, we want to break on a font if available
  1731. if (fFcs[next_fFcs].fOffset > last_offset)
  1732. {
  1733. while (next_fFcs < fFc_count
  1734. && fFcs[next_fFcs].fWhich != FONT_WHICH
  1735. && next_fFcs + 1 < fFc_count
  1736. && fFcs[next_fFcs + 1].fOffset == fFcs[next_fFcs].fOffset)
  1737. ++next_fFcs;
  1738. break;
  1739. }
  1740. ++next_fFcs;
  1741. }
  1742. if (fFcs[cur_fFcs].fWhich == FONT_WHICH)
  1743. cur_font = cur_fFcs;
  1744. int16 ccount;
  1745. int16 seglen;
  1746. if (next_fFcs == fFc_count)
  1747. {
  1748. ccount = CountChars (fFcs[cur_fFcs].fOffset, fLength - fFcs[cur_fFcs].fOffset);
  1749. seglen = fLength - fFcs[cur_fFcs].fOffset;
  1750. }
  1751. else
  1752. {
  1753. ccount = CountChars (
  1754. fFcs[cur_fFcs].fOffset,
  1755. fFcs[next_fFcs].fOffset - fFcs[cur_fFcs].fOffset);
  1756. seglen = fFcs[next_fFcs].fOffset - fFcs[cur_fFcs].fOffset;
  1757. }
  1758. const BFont &f (theme->FontAt (fFcs[cur_font].fIndex));
  1759. #ifdef __INTEL__
  1760. float eshift[ccount];
  1761. #else
  1762. float *eshift = new float[ccount];
  1763. #endif
  1764. f.GetEscapements (
  1765. fText + fFcs[cur_fFcs].fOffset,
  1766. ccount,
  1767. eshift);
  1768. // This is not perfect, because we are including the left edge,
  1769. // but BFont::GetEdges doesn't seem to work as we'd like
  1770. int16 i;
  1771. float incrementor = (fEdge_count > 0) ? fEdges[fEdge_count - 1] : 0;
  1772. for (i = 0; i < ccount; ++i)
  1773. {
  1774. incrementor += eshift[i] * f.Size();
  1775. fEdges[fEdge_count+i] = (int16) incrementor;
  1776. // this little backfTracking routine is necessary in the case where an fFcs change
  1777. // comes immediately after a UTF8-char, since all but the first edge will be 0
  1778. // and thus the new edge's starting position will be thrown off if we don't
  1779. // backtrack to the beginning of the char
  1780. if ((fEdge_count + i > 0) && fEdges[fEdge_count + i - 1] == 0)
  1781. {
  1782. int32 temp = fEdge_count + i - 1;
  1783. while (fEdges[--temp] == 0);
  1784. fEdges[fEdge_count + i] += fEdges[temp];
  1785. }
  1786. }
  1787. for (i = fFcs[cur_fFcs].fOffset; i < fFcs[cur_fFcs].fOffset + seglen;)
  1788. {
  1789. int32 len (UTF8_CHAR_LEN (fText[i]) - 1);
  1790. if (len)
  1791. {
  1792. int16 k;
  1793. for (k = fEdge_count + ccount - 1; k > i; --k)
  1794. fEdges[k + len] = fEdges[k];
  1795. for (k = 1; k <= len; ++k)
  1796. fEdges[i + k] = 0;
  1797. ccount += len;
  1798. }
  1799. i += len + 1;
  1800. }
  1801. cur_fFcs = next_fFcs;
  1802. fEdge_count += ccount;
  1803. #ifndef __INTEL__
  1804. delete [] eshift;
  1805. #endif
  1806. }
  1807. SoftBreaks (theme, width);
  1808. }
  1809. void
  1810. Line::AddSoftBreak (SoftBreakEnd sbe, float &start, uint16 &fText_place,
  1811. int16 &font, float &width, float &start_width, Theme *theme)
  1812. {
  1813. fText_place = sbe.fOffset;
  1814. if (fSoftie_size < fSoftie_used + 1)
  1815. {
  1816. SoftBreak *new_softies;
  1817. new_softies = new SoftBreak [fSoftie_size += SOFTBREAK_STEP];
  1818. if (fSofties)
  1819. {
  1820. memcpy (new_softies, fSofties, sizeof (SoftBreak) * fSoftie_used);
  1821. delete [] fSofties;
  1822. }
  1823. fSofties = new_softies;
  1824. }
  1825. // consume whitespace
  1826. while (fText_place + 1 < fLength
  1827. && isspace (fText[fText_place + 1]))
  1828. ++fText_place;
  1829. fSofties[fSoftie_used].fOffset = fText_place;
  1830. fSofties[fSoftie_used].fHeight = 0.0;
  1831. fSofties[fSoftie_used].fAscent = 0.0;
  1832. int16 last (font);
  1833. while (font < fFc_count)
  1834. {
  1835. const BFont &f (theme->FontAt (fFcs[font].fIndex));
  1836. font_height fh;
  1837. float height;
  1838. f.GetHeight (&fh);
  1839. height = ceil (fh.ascent + fh.descent + fh.leading);
  1840. if (fSofties[fSoftie_used].fHeight < height)
  1841. fSofties[fSoftie_used].fHeight = height;
  1842. if (fSofties[fSoftie_used].fAscent < fh.ascent)
  1843. fSofties[fSoftie_used].fAscent = fh.ascent;
  1844. // now try and find next
  1845. while (++font < fFc_count)
  1846. if (fFcs[font].fWhich == FONT_WHICH)
  1847. break;
  1848. if (font == fFc_count
  1849. || fFcs[font].fOffset > fText_place)
  1850. {
  1851. font = last;
  1852. break;
  1853. }
  1854. last = font;
  1855. }
  1856. if (fText_place < fLength)
  1857. start = fEdges[fText_place];
  1858. fBottom += fSofties[fSoftie_used++].fHeight;
  1859. fText_place += UTF8_CHAR_LEN (fText[fText_place]);
  1860. width = start_width - MARGIN_INDENT;
  1861. }
  1862. void
  1863. Line::SoftBreaks (Theme *theme, float start_width)
  1864. {
  1865. float margin (ceil (MARGIN_WIDTH / 2.0));
  1866. float width (start_width);
  1867. float start (0.0);
  1868. uint16 fText_place (0);
  1869. int16 space_place (0);
  1870. int16 font (0);
  1871. fSoftie_used = 0;
  1872. fBottom = fTop;
  1873. // find first font
  1874. while (font < fFc_count && fFcs[font].fWhich != FONT_WHICH)
  1875. ++font;
  1876. while (fText_place < fLength)
  1877. {
  1878. while (space_place < fSpace_count)
  1879. {
  1880. if (fEdges[fSpaces[space_place]] - start > width)
  1881. break;
  1882. ++space_place;
  1883. }
  1884. // we've reached the end of the line (but it might not all fit)
  1885. // or we only have one space, so we check if we need to split the word
  1886. if (space_place == fSpace_count
  1887. || space_place == 0
  1888. || fSpaces[space_place - 1] < fText_place)
  1889. {
  1890. // everything fits.. how wonderful (but we want at least one softbreak)
  1891. if (fEdge_count == 0)
  1892. {
  1893. AddSoftBreak (SoftBreakEnd(fLength - 1), start, fText_place, font, width, start_width, theme);
  1894. break;
  1895. }
  1896. int16 i (fEdge_count - 1);
  1897. while (fEdges[i] == 0)
  1898. --i;
  1899. if (fEdges[i] - start <= width)
  1900. {
  1901. AddSoftBreak (SoftBreakEnd(fLength - 1), start, fText_place, font, width, start_width, theme);
  1902. continue;
  1903. }
  1904. // we force at least one character
  1905. // your font may be a little too large for your window!
  1906. fText_place += UTF8_CHAR_LEN (fText[fText_place]);
  1907. while (fText_place < fLength)
  1908. {
  1909. if (fEdges[fText_place] - start > width - margin)
  1910. break;
  1911. fText_place += UTF8_CHAR_LEN (fText[fText_place]);
  1912. }
  1913. AddSoftBreak (SoftBreakEnd(fText_place), start, fText_place, font, width, start_width, theme);
  1914. continue;
  1915. }
  1916. // we encountered more than one space, so we rule out having to
  1917. // split the word, if the current word will fit within the bounds
  1918. int16 ccount1, ccount2;
  1919. --space_place;
  1920. ccount1 = fSpaces[space_place];
  1921. ccount2 = fSpaces[space_place+1] - ccount1;
  1922. int16 i (ccount1 - 1);
  1923. while (fEdges[i] == 0)
  1924. --i;
  1925. if (fEdges[ccount1 + ccount2] - fEdges[i] < width - margin)
  1926. {
  1927. AddSoftBreak (SoftBreakEnd(fSpaces[space_place]), start, fText_place, font, width, start_width, theme);
  1928. continue;
  1929. }
  1930. // We need to break up the really long word
  1931. fText_place = fSpaces[space_place];
  1932. while (fText_place < fEdge_count)
  1933. {
  1934. if ((fEdges[fText_place] - start) > width)
  1935. break;
  1936. fText_place += UTF8_CHAR_LEN (fText[fText_place]);
  1937. }
  1938. }
  1939. fBottom -= 1.0;
  1940. }
  1941. int16
  1942. Line::CountChars (int16 pos, int16 len)
  1943. {
  1944. int16 ccount (0);
  1945. if (pos >= fLength)
  1946. return ccount;
  1947. if (pos + len > fLength)
  1948. len = fLength - pos;
  1949. register int16 i = pos;
  1950. while (i < pos + len)
  1951. {
  1952. i += UTF8_CHAR_LEN(fText[i]);
  1953. ++ccount;
  1954. }
  1955. return ccount;
  1956. }
  1957. size_t
  1958. Line::SetStamp (const char *format, bool was_on)
  1959. {
  1960. size_t size (0);
  1961. int32 i (0);
  1962. if (was_on)
  1963. {
  1964. int16 offset (fFcs[4].fOffset + 1);
  1965. if (fUrls)
  1966. {
  1967. for (i = 0; i < fUrls->CountItems(); i++)
  1968. fUrls->ItemAt(i)->fOffset -= offset;
  1969. }
  1970. memmove (fText, fText + offset, fLength - offset);
  1971. fText[fLength -= offset] = '\0';
  1972. for (i = 6; i < fFc_count; ++i)
  1973. {
  1974. fFcs[i].fOffset -= offset;
  1975. fFcs[i - 6] = fFcs[i];
  1976. }
  1977. fFc_count -= 6;
  1978. }
  1979. if (format)
  1980. {
  1981. char buffer[1024];
  1982. struct tm curTime;
  1983. localtime_r (&fStamp, &curTime);
  1984. size = strftime (buffer, 1023, format, &curTime);
  1985. if (fUrls)
  1986. {
  1987. for (i = 0; i < fUrls->CountItems(); i++)
  1988. fUrls->ItemAt(i)->fOffset += size;
  1989. }
  1990. char *new_fText;
  1991. new_fText = new char [fLength + size + 2];
  1992. memcpy (new_fText, buffer, size);
  1993. new_fText[size++] = ' ';
  1994. new_fText[size] = '\0';
  1995. if (fText)
  1996. {
  1997. memcpy (new_fText + size, fText, fLength);
  1998. delete [] fText;
  1999. }
  2000. fText = new_fText;
  2001. fText[fLength += size] = '\0';
  2002. FontColor *new_fFcs;
  2003. new_fFcs = new FontColor [fFc_count + 6];
  2004. if (fFcs)
  2005. {
  2006. memcpy (
  2007. new_fFcs + 6,
  2008. fFcs,
  2009. fFc_count * sizeof (FontColor));
  2010. delete [] fFcs;
  2011. }
  2012. fFcs = new_fFcs;
  2013. fFc_count += 6;
  2014. fFcs[0].fWhich = FORE_WHICH;
  2015. fFcs[0].fIndex = Theme::TimestampFore;
  2016. fFcs[0].fOffset = 0;
  2017. fFcs[1].fWhich = BACK_WHICH;
  2018. fFcs[1].fIndex = Theme::TimestampBack;
  2019. fFcs[1].fOffset = 0;
  2020. fFcs[2].fWhich = FONT_WHICH;
  2021. fFcs[2].fIndex = Theme::TimestampFont;
  2022. fFcs[2].fOffset = 0;
  2023. fFcs[3].fWhich = FORE_WHICH;
  2024. fFcs[3].fIndex = Theme::TimespaceFore;
  2025. fFcs[3].fOffset = size - 1;
  2026. fFcs[4].fWhich = BACK_WHICH;
  2027. fFcs[4].fIndex = Theme::TimespaceBack;
  2028. fFcs[4].fOffset = size - 1;
  2029. fFcs[5].fWhich = FONT_WHICH;
  2030. fFcs[5].fIndex = Theme::TimespaceFont;
  2031. fFcs[5].fOffset = size - 1;
  2032. for (i = 6; i < fFc_count; ++i)
  2033. fFcs[i].fOffset += size;
  2034. }
  2035. return size;
  2036. }
  2037. void
  2038. Line::SelectWord (int16 *start, int16 *end)
  2039. {
  2040. int16 start_tmp (*start), end_tmp (*end);
  2041. while(start_tmp > 0 && fText[start_tmp-1] != ' ')
  2042. start_tmp--;
  2043. while ((end_tmp - 1) < fLength && fText[end_tmp] != ' ')
  2044. end_tmp++;
  2045. while (end_tmp >= fLength)
  2046. --end_tmp;
  2047. *start = start_tmp;
  2048. *end = end_tmp;
  2049. }
  2050. bool
  2051. RunView::FindText(const char *text)
  2052. {
  2053. bool result (false);
  2054. if (text != NULL)
  2055. {
  2056. for (int32 i = 0; i < fLine_count; i++)
  2057. {
  2058. char *offset (NULL);
  2059. if ((offset = strstr(fLines[i]->fText, text)) != NULL)
  2060. {
  2061. SelectPos start (i, offset - text),
  2062. end (i, (offset - text) + strlen(text));
  2063. Select(start, end);
  2064. ScrollTo(0.0, fLines[i]->fTop);
  2065. result = true;
  2066. break;
  2067. }
  2068. }
  2069. }
  2070. return result;
  2071. }
  2072. void
  2073. RunView::ScrollToSelection(void)
  2074. {
  2075. if (fLine_count > 0)
  2076. {
  2077. if (fSp_start != fSp_end)
  2078. {
  2079. ScrollTo(0.0, fLines[fSp_start.fLine]->fTop);
  2080. }
  2081. }
  2082. }
  2083. void
  2084. RunView::ScrollToBottom(void)
  2085. {
  2086. if (fLine_count > 0)
  2087. {
  2088. ScrollTo(0.0, fLines[fLine_count - 1]->fTop);
  2089. }
  2090. }