textw.c 78 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701
  1. #include "lib9.h"
  2. #include "draw.h"
  3. #include "keyboard.h"
  4. #include "tk.h"
  5. #include "textw.h"
  6. /*
  7. * useful text widget info to be found at:
  8. * :/coordinate.systems - what coord. systems are in use
  9. * textu.c:/assumed.invariants - some invariants that must be preserved
  10. */
  11. #define istring u.string
  12. #define iwin u.win
  13. #define imark u.mark
  14. #define iline u.line
  15. #define FLUSH() flushimage(tk->env->top->display, 1)
  16. #define O(t, e) ((long)(&((t*)0)->e))
  17. /* Layout constants */
  18. enum {
  19. Textpadx = 2,
  20. Textpady = 0,
  21. };
  22. typedef struct Interval {
  23. int lo;
  24. int hi;
  25. } Interval;
  26. typedef struct Mprint Mprint;
  27. struct Mprint
  28. {
  29. char* buf;
  30. int ptr;
  31. int len;
  32. };
  33. typedef struct TkDump TkDump;
  34. struct TkDump
  35. {
  36. int sgml;
  37. int metrics;
  38. };
  39. static
  40. TkOption dumpopts[] =
  41. {
  42. "sgml", OPTbool, O(TkDump, sgml), nil,
  43. "metrics", OPTbool, O(TkDump, metrics), nil,
  44. nil
  45. };
  46. static
  47. TkStab tkcompare[] =
  48. {
  49. "<", TkLt,
  50. "<=", TkLte,
  51. "==", TkEq,
  52. ">=", TkGte,
  53. ">", TkGt,
  54. "!=", TkNeq,
  55. nil
  56. };
  57. static
  58. TkOption textopts[] =
  59. {
  60. "wrap", OPTstab, O(TkText, opts[TkTwrap]), tkwrap,
  61. "spacing1", OPTnndist, O(TkText, opts[TkTspacing1]), (void *)O(Tk, env),
  62. "spacing2", OPTnndist, O(TkText, opts[TkTspacing2]), (void *)O(Tk, env),
  63. "spacing3", OPTnndist, O(TkText, opts[TkTspacing3]), (void *)O(Tk, env),
  64. "tabs", OPTtabs, O(TkText, tabs), (void *)O(Tk, env),
  65. "xscrollcommand", OPTtext, O(TkText, xscroll), nil,
  66. "yscrollcommand", OPTtext, O(TkText, yscroll), nil,
  67. "insertwidth", OPTnndist, O(TkText, inswidth), nil,
  68. "tagshare", OPTwinp, O(TkText, tagshare), nil,
  69. "propagate", OPTstab, O(TkText, propagate), tkbool,
  70. "selectborderwidth", OPTnndist, O(TkText, sborderwidth), nil,
  71. nil
  72. };
  73. #define CNTL(c) ((c)&0x1f)
  74. #define DEL 0x7f
  75. static TkEbind tktbinds[] = {
  76. {TkButton1P, "%W tkTextButton1 %X %Y"},
  77. {TkButton1P|TkMotion, "%W tkTextSelectTo %X %Y"},
  78. {TkButton1P|TkDouble, "%W tkTextSelectTo %X %Y double"},
  79. {TkButton1R, "%W tkTextButton1R"},
  80. {TkButton2P, "%W scan mark %x %y"},
  81. {TkButton2P|TkMotion, "%W scan dragto %x %y"},
  82. {TkKey, "%W tkTextInsert {%A}"},
  83. {TkKey|CNTL('a'), "%W tkTextSetCursor {insert linestart}"},
  84. {TkKey|Home, "%W tkTextSetCursor {insert linestart}"},
  85. {TkKey|CNTL('<'), "%W tkTextSetCursor {insert linestart}"},
  86. {TkKey|CNTL('b'), "%W tkTextSetCursor insert-1c"},
  87. {TkKey|Left, "%W tkTextSetCursor insert-1c"},
  88. {TkKey|CNTL('d'), "%W tkTextDelIns"},
  89. {TkKey|CNTL('e'), "%W tkTextSetCursor {insert lineend}"},
  90. {TkKey|End, "%W tkTextSetCursor {insert lineend}"},
  91. {TkKey|CNTL('>'), "%W tkTextSetCursor {insert lineend}"},
  92. {TkKey|CNTL('f'), "%W tkTextSetCursor insert+1c"},
  93. {TkKey|Right, "%W tkTextSetCursor insert+1c"},
  94. {TkKey|CNTL('h'), "%W tkTextDelIns -c"},
  95. {TkKey|DEL, "%W tkTextDelIns"},
  96. {TkKey|CNTL('k'), "%W tkTextDelIns +l"},
  97. {TkKey|CNTL('n'), "%W tkTextSetCursor {insert+1l}"},
  98. {TkKey|Down, "%W tkTextSetCursor {insert+1l}"},
  99. {TkKey|CNTL('o'), "%W tkTextInsert {\n}; %W mark set insert insert-1c"},
  100. {TkKey|CNTL('p'), "%W tkTextSetCursor {insert-1l}"},
  101. {TkKey|Up, "%W tkTextSetCursor {insert-1l}"},
  102. {TkKey|CNTL('u'), "%W tkTextDelIns -l"},
  103. {TkKey|CNTL('v'), "%W yview scroll 0.75 page"},
  104. {TkKey|Pgdown, "%W yview scroll 0.75 page"},
  105. {TkKey|CNTL('w'), "%W tkTextDelIns -w"},
  106. {TkKey|Pgup, "%W yview scroll -0.75 page"},
  107. {TkButton4P, "%W yview scroll -0.2 page"},
  108. {TkButton5P, "%W yview scroll 0.2 page"},
  109. {TkFocusout, "%W tkTextCursor delete"},
  110. {TkKey|APP|'\t', ""},
  111. {TkKey|BackTab, ""},
  112. };
  113. static int tktclickmatch(TkText *, int, int, int, TkTindex *);
  114. static void tktdoubleclick(TkText *, TkTindex *, TkTindex *);
  115. static char* tktdrawline(Image*, Tk*, TkTline*, Point);
  116. static void tktextcursordraw(Tk *, int);
  117. static char* tktsetscroll(Tk*, int);
  118. static void tktsetclip(Tk *);
  119. static char* tktview(Tk*, char*, char**, int, int*, int, int);
  120. static Interval tkttranslate(Tk*, Interval, int);
  121. static void tktfixscroll(Tk*, Point);
  122. static void tktnotdrawn(Tk*, int, int, int);
  123. static void tktdrawbg(Tk*, int, int, int);
  124. static int tktwidbetween(Tk*, int, TkTindex*, TkTindex*);
  125. static int tktpostspace(Tk*, TkTline*);
  126. static int tktprespace(Tk*, TkTline*);
  127. static void tktsee(Tk*, TkTindex*, int);
  128. static Point tktrelpos(Tk*);
  129. static void autoselect(Tk*, void*, int);
  130. static void blinkreset(Tk*);
  131. /* debugging */
  132. extern int tktdbg;
  133. extern void tktprinttext(TkText*);
  134. extern void tktprintindex(TkTindex*);
  135. extern void tktprintitem(TkTitem*);
  136. extern void tktprintline(TkTline*);
  137. extern void tktcheck(TkText*, char*);
  138. extern int tktutfpos(char *, int);
  139. char*
  140. tktext(TkTop *t, char* arg, char **ret)
  141. {
  142. Tk *tk;
  143. char *e;
  144. TkEnv *ev;
  145. TkTline *l;
  146. TkTitem *it = nil;
  147. TkName *names = nil;
  148. TkTtaginfo *ti = nil;
  149. TkOptab tko[3];
  150. TkTmarkinfo *mi = nil;
  151. TkText *tkt, *tktshare;
  152. tk = tknewobj(t, TKtext, sizeof(Tk)+sizeof(TkText));
  153. if(tk == nil)
  154. return TkNomem;
  155. tkt = TKobj(TkText, tk);
  156. tk->relief = TKsunken;
  157. tk->borderwidth = 1;
  158. tk->ipad.x = Textpadx * 2;
  159. tk->ipad.y = Textpady * 2;
  160. tk->flag |= Tktakefocus;
  161. tkt->sborderwidth = 0;
  162. tkt->inswidth = 2;
  163. tkt->cur_flag = 0; /* text cursor doesn't show up initially */
  164. tkt->opts[TkTwrap] = Tkwrapchar;
  165. tkt->opts[TkTrelief] = TKflat;
  166. tkt->opts[TkTjustify] = Tkleft;
  167. tkt->propagate = BoolX;
  168. tko[0].ptr = tk;
  169. tko[0].optab = tkgeneric;
  170. tko[1].ptr = tkt;
  171. tko[1].optab = textopts;
  172. tko[2].ptr = nil;
  173. tk->req.width = tk->env->wzero*Textwidth;
  174. tk->req.height = tk->env->font->height*Textheight;
  175. names = nil;
  176. e = tkparse(t, arg, tko, &names);
  177. if(e != nil)
  178. goto err;
  179. tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
  180. if(names == nil) {
  181. /* tkerr(t, arg); XXX */
  182. e = TkBadwp;
  183. goto err;
  184. }
  185. if(tkt->tagshare != nil) {
  186. tkputenv(tk->env);
  187. tk->env = tkt->tagshare->env;
  188. tk->env->ref++;
  189. }
  190. if(tk->flag&Tkdisabled)
  191. tkt->inswidth = 0;
  192. if(tkt->tabs == nil) {
  193. tkt->tabs = malloc(sizeof(TkTtabstop));
  194. if(tkt->tabs == nil)
  195. goto err;
  196. tkt->tabs->pos = 8*tk->env->wzero;
  197. tkt->tabs->justify = Tkleft;
  198. tkt->tabs->next = nil;
  199. }
  200. if(tkt->tagshare != nil) {
  201. tktshare = TKobj(TkText, tkt->tagshare);
  202. tkt->tags = tktshare->tags;
  203. tkt->nexttag = tktshare->nexttag;
  204. }
  205. else {
  206. /* Note: sel should have id == TkTselid == 0 */
  207. e = tktaddtaginfo(tk, "sel", &ti);
  208. if(e != nil)
  209. goto err;
  210. tkputenv(ti->env);
  211. ti->env = tknewenv(t);
  212. if(ti->env == nil)
  213. goto err;
  214. ev = ti->env;
  215. ev->colors[TkCbackgnd] = tk->env->colors[TkCselectbgnd];
  216. ev->colors[TkCbackgndlght] = tk->env->colors[TkCselectbgndlght];
  217. ev->colors[TkCbackgnddark] = tk->env->colors[TkCselectbgnddark];
  218. ev->colors[TkCforegnd] = tk->env->colors[TkCselectfgnd];
  219. ev->set = (1<<TkCbackgnd)|(1<<TkCbackgndlght)|
  220. (1<<TkCbackgnddark)|(1<<TkCforegnd);
  221. ti->opts[TkTborderwidth] = tkt->sborderwidth;
  222. if(tkt->sborderwidth > 0)
  223. ti->opts[TkTrelief] = TKraised;
  224. }
  225. e = tktaddmarkinfo(tkt, "current", &mi);
  226. if(e != nil)
  227. goto err;
  228. e = tktaddmarkinfo(tkt, "insert", &mi);
  229. if(e != nil)
  230. goto err;
  231. tkt->start.flags = TkTfirst|TkTlast;
  232. tkt->end.flags = TkTlast;
  233. e = tktnewitem(TkTnewline, 0, &it);
  234. if(e != nil)
  235. goto err;
  236. e = tktnewline(TkTfirst|TkTlast, it, &tkt->start, &tkt->end, &l);
  237. if(e != nil)
  238. goto err;
  239. e = tktnewitem(TkTmark, 0, &it);
  240. if(e != nil)
  241. goto err;
  242. it->next = l->items;
  243. l->items = it;
  244. it->imark = mi;
  245. mi->cur = it;
  246. tkt->nlines = 1;
  247. tkt->scrolltop[Tkvertical] = -1;
  248. tkt->scrolltop[Tkhorizontal] = -1;
  249. tkt->scrollbot[Tkvertical] = -1;
  250. tkt->scrollbot[Tkhorizontal] = -1;
  251. if(tkt->tagshare != nil)
  252. tk->binds = tkt->tagshare->binds;
  253. else {
  254. e = tkbindings(t, tk, tktbinds, nelem(tktbinds));
  255. if(e != nil)
  256. goto err;
  257. }
  258. if (tkt->propagate == BoolT) {
  259. if ((tk->flag & Tksetwidth) == 0)
  260. tk->req.width = tktmaxwid(tkt->start.next);
  261. if ((tk->flag & Tksetheight) == 0)
  262. tk->req.height = tkt->end.orig.y;
  263. }
  264. e = tkaddchild(t, tk, &names);
  265. tkfreename(names);
  266. if(e != nil)
  267. goto err;
  268. tk->name->link = nil;
  269. return tkvalue(ret, "%s", tk->name->name);
  270. err:
  271. /* XXX it's possible there's a memory leak here */
  272. tkfreeobj(tk);
  273. return e;
  274. }
  275. /*
  276. * There are four coordinate systems of interest:
  277. * S - screen coordinate system (i.e. top left corner of
  278. * inferno screen is (0,0) in S space.)
  279. * I - image coordinate system (i.e. top left corner of
  280. * tkimageof(this widget) is (0,0) in I space.)
  281. * T - text coordinate system (i.e., top left of first line
  282. * is at (0,0) in T space.)
  283. * V - view coordinate system (i.e., top left of visible
  284. * portion of widget is at (0,0) in V space.)
  285. *
  286. * A point P in the four systems (Ps, Pi, Pt, Pv) satisfies:
  287. * Pt = Ps - deltast
  288. * Pv = Ps - deltasv
  289. * Pv = Pi - deltaiv
  290. * (where deltast is vector from S origin to T origin;
  291. * deltasv is vector from S origin to V origin;
  292. * deltaiv is vector from I origin to V origin)
  293. *
  294. * We keep deltatv, deltasv, and deltaiv in tkt.
  295. * Deltatv is updated by scrolling.
  296. * Deltasv is updated by geom changes:
  297. * tkposn(tk)+ipad/2
  298. * Deltaiv is affected by geom changes and the call to the draw function:
  299. * tk->act+orig+ipad/2+(bw,bw) (orig is the parameter to tkdrawtext),
  300. *
  301. * We can derive
  302. * Ps = Pt + deltast
  303. * = Pt + deltasv - deltatv
  304. *
  305. * Pv = Pt - deltatv
  306. *
  307. * Here are various coordinates in the text widget according
  308. * to which coordinate system they use:
  309. *
  310. * S - Mouse coordinates (coming in to tktextevent);
  311. * the deltasv parameter to tkdrawtext;
  312. * coords in tkt->image, where drawing is done to
  313. * (to get same bit-alignment as screen, for fast transfer)
  314. * T - orig in TkTlines
  315. * V - %x,%y delivered via binds to TkText or its tags
  316. * Note deltasv changes underneath us, so is calculated on the fly
  317. * when it needs to be (in tktextevent).
  318. *
  319. */
  320. static void
  321. tktsetdeltas(Tk *tk, Point orig)
  322. {
  323. TkText *tkt = TKobj(TkText, tk);
  324. tkt->deltaiv.x = orig.x + tk->act.x + tk->ipad.x/2 + tk->borderwidth;
  325. tkt->deltaiv.y = orig.y + tk->act.y + tk->ipad.y/2 + tk->borderwidth;
  326. }
  327. static Point
  328. tktrelpos(Tk *sub)
  329. {
  330. Tk *tk;
  331. TkTindex ix;
  332. Rectangle r;
  333. Point ans;
  334. tk = sub->parent;
  335. if(tk == nil)
  336. return ZP;
  337. if(tktfindsubitem(sub, &ix)) {
  338. r = tktbbox(tk, &ix);
  339. ans.x = r.min.x;
  340. ans.y = r.min.y;
  341. return r.min;
  342. }
  343. return ZP;
  344. }
  345. static void
  346. tktreplclipr(Image *dst, Rectangle r)
  347. {
  348. int locked;
  349. locked = lockdisplay(dst->display);
  350. replclipr(dst, 0, r);
  351. if(locked)
  352. unlockdisplay(dst->display);
  353. }
  354. char*
  355. tkdrawtext(Tk *tk, Point orig)
  356. {
  357. int vh;
  358. Image *dst;
  359. TkText *tkt;
  360. TkTline *l, *lend;
  361. Point p, deltait;
  362. Rectangle oclipr;
  363. int reldone = 1;
  364. char *e;
  365. tkt = TKobj(TkText, tk);
  366. dst = tkimageof(tk);
  367. if (dst == nil)
  368. return nil;
  369. tkt->image = dst;
  370. tktsetdeltas(tk, orig);
  371. tkt->tflag |= TkTdrawn|TkTdlocked;
  372. oclipr = dst->clipr;
  373. tktsetclip(tk);
  374. if(tk->flag&Tkrefresh) {
  375. reldone = 0;
  376. tktnotdrawn(tk, 0, tkt->end.orig.y, 1);
  377. }
  378. tk->flag &= ~Tkrefresh;
  379. deltait = subpt(tkt->deltaiv, tkt->deltatv);
  380. vh = tk->act.height - tk->ipad.y/2;
  381. lend = &tkt->end;
  382. for(l = tkt->start.next; l != lend; l = l->next) {
  383. if(l->orig.y+l->height < tkt->deltatv.y)
  384. continue;
  385. if(l->orig.y > tkt->deltatv.y + vh)
  386. break;
  387. if(!(l->flags&TkTdrawn)) {
  388. e = tktdrawline(dst, tk, l, deltait);
  389. if(e != nil)
  390. return e;
  391. }
  392. }
  393. tktreplclipr(dst, oclipr);
  394. if(!reldone) {
  395. p.x = orig.x + tk->act.x;
  396. p.y = orig.y + tk->act.y;
  397. tkdrawrelief(dst, tk, p, TkCbackgnd, tk->relief);
  398. }
  399. tkt->tflag &= ~TkTdlocked;
  400. return nil;
  401. }
  402. /*
  403. * Set the clipping rectangle of the destination image to the
  404. * intersection of the current clipping rectangle and the area inside
  405. * the text widget that needs to be redrawn.
  406. * The caller should save the old one and restore it later.
  407. */
  408. static void
  409. tktsetclip(Tk *tk)
  410. {
  411. Rectangle r;
  412. Image *dst;
  413. TkText *tkt = TKobj(TkText, tk);
  414. dst = tkt->image;
  415. r.min = tkt->deltaiv;
  416. r.max.x = r.min.x + tk->act.width - tk->ipad.x / 2;
  417. r.max.y = r.min.y + tk->act.height - tk->ipad.y / 2;
  418. if(!rectclip(&r, dst->clipr))
  419. r.max = r.min;
  420. tktreplclipr(dst, r);
  421. }
  422. static char*
  423. tktdrawline(Image *i, Tk *tk, TkTline *l, Point deltait)
  424. {
  425. Tk *sub;
  426. Font *f;
  427. Image *bg;
  428. Point p, q;
  429. Rectangle r;
  430. TkText *tkt;
  431. TkTitem *it, *z;
  432. int bevtop, bevbot;
  433. TkEnv *e, *et, *env;
  434. int *opts;
  435. int o, bd, ul, ov, h, w, la, lh, cursorx, join;
  436. char *err;
  437. env = mallocz(sizeof(TkEnv), 0);
  438. if(env == nil)
  439. return TkNomem;
  440. opts = mallocz(TkTnumopts*sizeof(int), 0);
  441. if(opts == nil) {
  442. free(env);
  443. return TkNomem;
  444. }
  445. tkt = TKobj(TkText, tk);
  446. e = tk->env;
  447. et = env;
  448. et->top = e->top;
  449. f = e->font;
  450. /* l->orig is in T space, p is in I space */
  451. la = l->ascent;
  452. lh = l->height;
  453. p = addpt(l->orig, deltait);
  454. p.y += la;
  455. /* if(tktdbg){print("drawline, p=(%d,%d), f->a=%d, f->h=%d\n", p.x, p.y, f->ascent, f->height); tktprintline(l);} */
  456. cursorx = -1000;
  457. join = 0;
  458. for(it = l->items; it != nil; it = it->next) {
  459. bg = tkgc(e, TkCbackgnd);
  460. if(tktanytags(it)) {
  461. tkttagopts(tk, it, opts, env, nil, 1);
  462. if(e->colors[TkCbackgnd] != et->colors[TkCbackgnd]) {
  463. bg = tkgc(et, TkCbackgnd);
  464. r.min = p;
  465. r.min.y -= la;
  466. r.max.x = r.min.x + it->width;
  467. r.max.y = r.min.y + lh;
  468. draw(i, r, bg, nil, ZP);
  469. }
  470. o = opts[TkTrelief];
  471. bd = opts[TkTborderwidth];
  472. if((o == TKsunken || o == TKraised) && bd > 0) {
  473. /* fit relief inside item bounding box */
  474. q.x = p.x;
  475. q.y = p.y - la;
  476. if(it->width < 2*bd)
  477. bd = it->width / 2;
  478. if(lh < 2*bd)
  479. bd = lh / 2;
  480. w = it->width - 2*bd;
  481. h = lh - 2*bd;
  482. if(o == TKraised) {
  483. bevtop = TkLightshade;
  484. bevbot = TkDarkshade;
  485. }
  486. else {
  487. bevtop = TkDarkshade;
  488. bevbot = TkLightshade;
  489. }
  490. tkbevel(i, q, w, h, bd,
  491. tkgc(et, TkCbackgnd+bevtop), tkgc(et, TkCbackgnd+bevbot));
  492. /* join relief between adjacent items if tags match */
  493. if(join) {
  494. r.min.x = q.x;
  495. r.max.x = q.x + bd;
  496. r.min.y = q.y + bd;
  497. r.max.y = r.min.y + h;
  498. draw(i, r, bg, nil, ZP);
  499. r.min.y = r.max.y;
  500. r.max.y = r.min.y + bd;
  501. draw(i, r, tkgc(et, TkCbackgnd+bevbot), nil, ZP);
  502. }
  503. for(z = it->next; z != nil && z->kind == TkTmark; )
  504. z = z->next;
  505. if(z != nil && tktsametags(z, it)) {
  506. r.min.x = q.x + bd + w;
  507. r.max.x = r.min.x + bd;
  508. r.min.y = q.y;
  509. r.max.y = q.y + bd;
  510. draw(i, r, tkgc(et, TkCbackgnd+bevtop), nil, ZP);
  511. r.min.y = r.max.y;
  512. r.max.y = r.min.y + h;
  513. draw(i, r, bg, nil, ZP);
  514. join = 1;
  515. }
  516. else
  517. join = 0;
  518. }
  519. o = opts[TkToffset];
  520. ul = opts[TkTunderline];
  521. ov = opts[TkToverstrike];
  522. }
  523. else {
  524. et->font = f;
  525. et->colors[TkCforegnd] = e->colors[TkCforegnd];
  526. o = 0;
  527. ul = 0;
  528. ov = 0;
  529. }
  530. switch(it->kind) {
  531. case TkTascii:
  532. case TkTrune:
  533. q.x = p.x;
  534. q.y = p.y - env->font->ascent - o;
  535. /*if(tktdbg)print("q=(%d,%d)\n", q.x, q.y);*/
  536. string(i, q, tkgc(et, TkCforegnd), q, env->font, it->istring);
  537. if(ov == BoolT) {
  538. r.min.x = q.x;
  539. r.max.x = r.min.x + it->width;
  540. r.min.y = q.y + 2*env->font->ascent/3;
  541. r.max.y = r.min.y + 2;
  542. draw(i, r, tkgc(et, TkCforegnd), nil, ZP);
  543. }
  544. if(ul == BoolT) {
  545. r.min.x = q.x;
  546. r.max.x = r.min.x + it->width;
  547. r.max.y = p.y - la + lh;
  548. r.min.y = r.max.y - 2;
  549. draw(i, r, tkgc(et, TkCforegnd), nil, ZP);
  550. }
  551. break;
  552. case TkTmark:
  553. if((it->imark != nil)
  554. && strcmp(it->imark->name, "insert") == 0) {
  555. cursorx = p.x - 1;
  556. }
  557. break;
  558. case TkTwin:
  559. sub = it->iwin->sub;
  560. if(sub != nil) {
  561. int dirty;
  562. sub->flag |= Tkrefresh;
  563. sub->dirty = tkrect(sub, 1);
  564. err = tkdrawslaves(sub, p, &dirty);
  565. if(err != nil) {
  566. free(opts);
  567. free(env);
  568. return err;
  569. }
  570. }
  571. break;
  572. }
  573. p.x += it->width;
  574. }
  575. l->flags |= TkTdrawn;
  576. /* do cursor last, so not overwritten by later items */
  577. if(cursorx != -1000 && tkt->inswidth > 0) {
  578. r.min.x = cursorx;
  579. r.min.y = p.y - la;
  580. r.max.x = r.min.x + tkt->inswidth;
  581. r.max.y = r.min.y + lh;
  582. r = rectsubpt(r, deltait);
  583. if (!eqrect(tkt->cur_rec, r))
  584. blinkreset(tk);
  585. tkt->cur_rec = r;
  586. if(tkt->cur_flag)
  587. tktextcursordraw(tk, TkCforegnd);
  588. }
  589. free(opts);
  590. free(env);
  591. return nil;
  592. }
  593. static void
  594. tktextcursordraw(Tk *tk, int color)
  595. {
  596. Rectangle r;
  597. TkText *tkt;
  598. Image *i;
  599. tkt = TKobj(TkText, tk);
  600. r = rectaddpt(tkt->cur_rec, subpt(tkt->deltaiv, tkt->deltatv));
  601. /* check the cursor with widget boundary */
  602. /* do nothing if entire cursor outside widget boundary */
  603. if( ! ( r.max.x < tkt->deltaiv.x ||
  604. r.min.x > tkt->deltaiv.x + tk->act.width ||
  605. r.max.y < tkt->deltaiv.y ||
  606. r.min.y > tkt->deltaiv.y + tk->act.height)) {
  607. /* clip rectangle if extends beyond widget boundary */
  608. if (r.min.x < tkt->deltaiv.x)
  609. r.min.x = tkt->deltaiv.x;
  610. if (r.max.x > tkt->deltaiv.x + tk->act.width)
  611. r.max.x = tkt->deltaiv.x + tk->act.width;
  612. if (r.min.y < tkt->deltaiv.y)
  613. r.min.y = tkt->deltaiv.y;
  614. if (r.max.y > tkt->deltaiv.y + tk->act.height)
  615. r.max.y = tkt->deltaiv.y + tk->act.height;
  616. i = tkimageof(tk);
  617. if (i != nil)
  618. draw(i, r, tkgc(tk->env, color), nil, ZP);
  619. }
  620. }
  621. static void
  622. blinkreset(Tk *tk)
  623. {
  624. TkText *tkt = TKobj(TkText, tk);
  625. if (!tkhaskeyfocus(tk) || tk->flag&Tkdisabled)
  626. return;
  627. tkt->cur_flag = 1;
  628. tkblinkreset(tk);
  629. }
  630. static void
  631. showcaret(Tk *tk, int on)
  632. {
  633. TkText *tkt = TKobj(TkText, tk);
  634. TkTline *l, *lend;
  635. TkTitem *it;
  636. tkt->cur_flag = on;
  637. lend = &tkt->end;
  638. for(l = tkt->start.next; l != lend; l = l->next) {
  639. for (it = l->items; it != nil; it = it->next) {
  640. if (it->kind == TkTmark && it->imark != nil &&
  641. strcmp(it->imark->name, "insert") == 0) {
  642. if (on) {
  643. tktextcursordraw(tk, TkCforegnd);
  644. tk->dirty = tkrect(tk, 1);
  645. } else
  646. tktnotdrawn(tk, l->orig.y, l->orig.y+l->height, 0);
  647. tkdirty(tk);
  648. return;
  649. }
  650. }
  651. }
  652. }
  653. char*
  654. tktextcursor(Tk *tk, char* arg, char **ret)
  655. {
  656. int on = 0;
  657. USED(ret);
  658. if (tk->flag&Tkdisabled)
  659. return nil;
  660. if(strcmp(arg, " insert") == 0) {
  661. tkblink(tk, showcaret);
  662. on = 1;
  663. }
  664. else
  665. tkblink(nil, nil);
  666. showcaret(tk, on);
  667. return nil;
  668. }
  669. /*
  670. * Insert string s just before ins, but don't worry about geometry values.
  671. * Don't worry about doing wrapping correctly, but break long strings
  672. * into pieces to avoid bad behavior in the wrapping code of tktfixgeom.
  673. * If tagit != 0, use its tags, else use the intersection of tags of
  674. * non cont or mark elements just before and just after insertion point.
  675. * (At beginning and end of widget, just use the tags of one adjacent item).
  676. * Keep *ins up-to-date.
  677. */
  678. char*
  679. tktinsert(Tk *tk, TkTindex *ins, char *s, TkTitem *tagit)
  680. {
  681. int c, n, nextra, nmax, atend, atbeg;
  682. char *e, *p;
  683. Rune r;
  684. TkTindex iprev, inext;
  685. TkTitem *i, *utagit;
  686. TkText *tkt = TKobj(TkText, tk);
  687. e = tktsplititem(ins);
  688. if(e != nil)
  689. return e;
  690. /* if no tags give, use intersection of previous and next char tags */
  691. nextra = 0;
  692. n = tk->env->wzero;
  693. if(n <= 0)
  694. n = 8;
  695. nmax = tk->act.width - tk->ipad.x;
  696. if(nmax <= 0) {
  697. if (tkt->propagate != BoolT || (tk->flag & Tksetwidth))
  698. nmax = tk->req.width;
  699. if(nmax <= 0)
  700. nmax = 60*n;
  701. }
  702. nmax = (nmax + n - 1) / n;
  703. utagit = nil;
  704. if(tagit == nil) {
  705. inext = *ins;
  706. tktadjustind(tkt, TkTbycharstart, &inext);
  707. atend = (inext.item->next == nil && inext.line->next == &tkt->end);
  708. if(atend || tktanytags(inext.item)) {
  709. iprev = *ins;
  710. tktadjustind(tkt, TkTbycharback, &iprev);
  711. atbeg = (iprev.line->prev == &tkt->start && iprev.line->items == iprev.item);
  712. if(atbeg || tktanytags(iprev.item)) {
  713. nextra = 0;
  714. if(!atend)
  715. nextra = inext.item->tagextra;
  716. if(!atbeg && iprev.item->tagextra > nextra)
  717. nextra = iprev.item->tagextra;
  718. e = tktnewitem(TkTascii, nextra, &utagit);
  719. if(e != nil)
  720. return e;
  721. if(!atend) {
  722. tkttagcomb(utagit, inext.item, 1);
  723. if(!atbeg)
  724. tkttagcomb(utagit, iprev.item, 0);
  725. }
  726. else if(!atbeg)
  727. tkttagcomb(utagit, iprev.item, 1);
  728. tagit = utagit;
  729. }
  730. }
  731. }
  732. else
  733. nextra = tagit->tagextra;
  734. while((c = *s) != '\0') {
  735. e = tktnewitem(TkTascii, nextra, &i);
  736. if(e != nil) {
  737. if(utagit != nil)
  738. free(utagit);
  739. return e;
  740. }
  741. if(tagit != nil)
  742. tkttagcomb(i, tagit, 1);
  743. if(c == '\n') {
  744. i->kind = TkTnewline;
  745. tkt->nlines++;
  746. s++;
  747. }
  748. else
  749. if(c == '\t') {
  750. i->kind = TkTtab;
  751. s++;
  752. }
  753. else {
  754. p = s;
  755. n = 0;
  756. i->kind = TkTascii;
  757. while(c != '\0' && c != '\n' && c != '\t' && n < nmax){
  758. s += chartorune(&r, s);
  759. c = *s;
  760. n++;
  761. }
  762. /*
  763. * if more bytes than runes, then it's not all ascii, so create a TkTrune item
  764. */
  765. if(s - p > n)
  766. i->kind = TkTrune;
  767. n = s - p;
  768. i->istring = malloc(n+1);
  769. if(i->istring == nil) {
  770. tktfreeitems(tkt, i, 1);
  771. if(utagit != nil)
  772. free(utagit);
  773. return TkNomem;
  774. }
  775. memmove(i->istring, p, n);
  776. i->istring[n] = '\0';
  777. }
  778. e = tktiteminsert(tkt, ins, i);
  779. if(e != nil) {
  780. if(utagit != nil)
  781. free(utagit);
  782. tktfreeitems(tkt, i, 1);
  783. return e;
  784. }
  785. }
  786. if(utagit != nil)
  787. free(utagit);
  788. return nil;
  789. }
  790. void
  791. tktextsize(Tk *tk, int dogeom)
  792. {
  793. TkText *tkt;
  794. TkGeom g;
  795. tkt = TKobj(TkText, tk);
  796. if (tkt->propagate == BoolT) {
  797. g = tk->req;
  798. if ((tk->flag & Tksetwidth) == 0)
  799. tk->req.width = tktmaxwid(tkt->start.next);
  800. if ((tk->flag & Tksetheight) == 0)
  801. tk->req.height = tkt->end.orig.y;
  802. if (dogeom)
  803. tkgeomchg(tk, &g, tk->borderwidth);
  804. }
  805. }
  806. static int
  807. maximum(int a, int b)
  808. {
  809. if (a > b)
  810. return a;
  811. return b;
  812. }
  813. /*
  814. * For lines l1->next, ..., l2, fix up the geometry
  815. * elements of constituent TkTlines and TkTitems.
  816. * This involves doing proper line wrapping, and calculating item
  817. * widths and positions.
  818. * Also, merge any adjacent TkTascii/TkTrune items with the same tags.
  819. * Finally, bump the y component of lines l2->next, ... end.
  820. * l2 should not be tkt->end.
  821. *
  822. * if finalwidth is 0, we're trying to work out what the
  823. * width and height should be. if propagation is off,
  824. * it's irrelevant; otherwise it must assume that
  825. * its desired width will be fulfilled, as the packer
  826. * doesn't iterate...
  827. *
  828. * N.B. this function rearranges lines, merges and splits items.
  829. * this means that in general the item and line pointed to
  830. * by any index might have been freed after tktfixgeom
  831. * has been called.
  832. */
  833. char*
  834. tktfixgeom(Tk *tk, TkTline *l1, TkTline *l2, int finalwidth)
  835. {
  836. int x, y, a, wa, h, w, o, n, j, sp3, xleft, xright, winw, oa, oh, lh;
  837. int wrapmode, just, needsplit;
  838. char *e, *s;
  839. TkText *tkt;
  840. Tk *sub;
  841. TkTitem *i, *it, *ilast, *iprev;
  842. TkTindex ix, ixprev, ixw;
  843. TkTline *l, *lafter;
  844. Interval oldi, hole, rest, newrest;
  845. TkEnv *env;
  846. Font *f;
  847. int *opts;
  848. TkTtabstop *tb;
  849. tkt = TKobj(TkText, tk);
  850. if(tktdbg)
  851. tktcheck(tkt, "tktfixgeom");
  852. if (!finalwidth && tkt->propagate == BoolT) {
  853. if ((tk->flag & Tksetwidth) == 0)
  854. winw = 1000000;
  855. else
  856. winw = tk->req.width;
  857. } else {
  858. winw = tk->act.width - tk->ipad.x;
  859. if(winw <= 0)
  860. winw = tk->req.width;
  861. }
  862. if(winw < 0)
  863. return nil;
  864. /*
  865. * Make lafter be the first line after l2 that comes after a newline
  866. * (so that wrap correction cannot affect it)
  867. */
  868. lafter = l2->next;
  869. if(tktdbg && lafter == nil) {
  870. print("tktfixgeom: botch 1\n");
  871. return nil;
  872. }
  873. while((lafter->flags & TkTfirst) == 0 && lafter != &tkt->end)
  874. lafter = lafter->next;
  875. y = l1->orig.y + l1->height + tktpostspace(tk, l1);
  876. oldi.lo = y;
  877. oldi.hi = lafter->orig.y;
  878. rest.lo = oldi.hi;
  879. rest.hi = rest.lo + 1000; /* get background after end, too */
  880. opts = mallocz(TkTnumopts*sizeof(int), 0);
  881. if(opts == nil)
  882. return TkNomem;
  883. env = mallocz(sizeof(TkEnv), 0);
  884. if(env == nil) {
  885. free(opts);
  886. return TkNomem;
  887. }
  888. for(l = l1->next; l != lafter; l = l->next) {
  889. if(tktdbg && l == nil) {
  890. print("tktfixgeom: botch 2\n");
  891. free(opts);
  892. free(env);
  893. return nil;
  894. }
  895. l->flags &= ~TkTdrawn;
  896. /* some spacing depends on tags of first non-mark on display line */
  897. iprev = nil;
  898. for(i = l->items; i->kind == TkTmark; ) {
  899. iprev = i;
  900. i = i->next;
  901. }
  902. tkttagopts(tk, i, opts, env, &tb, 1);
  903. if(l->flags&TkTfirst) {
  904. xleft = opts[TkTlmargin1];
  905. y += opts[TkTspacing1];
  906. }
  907. else {
  908. xleft = opts[TkTlmargin2];
  909. y += opts[TkTspacing2];
  910. }
  911. sp3 = opts[TkTspacing3];
  912. just = opts[TkTjustify];
  913. wrapmode = opts[TkTwrap];
  914. f = env->font;
  915. h = f->height;
  916. lh = opts[TkTlineheight];
  917. a = f->ascent;
  918. x = xleft;
  919. xright = winw - opts[TkTrmargin];
  920. if(xright < xleft)
  921. xright = xleft;
  922. /*
  923. * perform line wrapping and calculate h (height) and a (ascent)
  924. * for the current line
  925. */
  926. for(; i != nil; iprev = i, i = i->next) {
  927. again:
  928. if(i->kind == TkTmark)
  929. continue;
  930. if(i->kind == TkTnewline)
  931. break;
  932. if(i->kind == TkTcontline) {
  933. /*
  934. * See if some of following line fits on this one.
  935. * First, ensure that following line isn't empty.
  936. */
  937. it = l->next->items;
  938. while(it->kind == TkTmark)
  939. it = it->next;
  940. if(it->kind == TkTnewline || it->kind == TkTcontline) {
  941. /* next line is empty; join it to this one by removing i */
  942. ix.item = i;
  943. ix.line = l;
  944. ix.pos = 0;
  945. tktremitem(tkt, &ix);
  946. it = l->next->items;
  947. if(iprev == nil)
  948. i = l->items;
  949. else
  950. i = iprev->next;
  951. goto again;
  952. }
  953. n = xright - x;
  954. if(n <= 0)
  955. break;
  956. ixprev.line = l;
  957. ixprev.item = i;
  958. ixprev.pos = 0;
  959. ix = ixprev;
  960. tktadjustind(tkt, TkTbychar, &ix);
  961. if(wrapmode == Tkwrapword)
  962. tktadjustind(tkt, TkTbywrapend, &ix);
  963. if(wrapmode != Tkwrapnone && tktwidbetween(tk, x, &ixprev, &ix) > n)
  964. break;
  965. /* move one item up from next line and try again */
  966. it = l->next->items;
  967. if(tktdbg && (it == nil || it->kind == TkTnewline || it->kind == TkTcontline)) {
  968. print("tktfixgeom: botch 3\n");
  969. free(opts);
  970. free(env);
  971. return nil;
  972. }
  973. if(iprev == nil)
  974. l->items = it;
  975. else
  976. iprev->next = it;
  977. l->next->items = it->next;
  978. it->next = i;
  979. i = it;
  980. goto again;
  981. }
  982. oa = a;
  983. oh = h;
  984. if(!tktanytags(i)) {
  985. env->font = tk->env->font;
  986. o = 0;
  987. }
  988. else {
  989. tkttagopts(tk, i, opts, env, nil, 1);
  990. o = opts[TkToffset];
  991. }
  992. if((o != 0 || env->font != f) && i->kind != TkTwin) {
  993. /* check ascent of current item */
  994. n = o+env->font->ascent;
  995. if(n > a) {
  996. a = n;
  997. h += (a - oa);
  998. }
  999. /* check descent of current item */
  1000. n = (env->font->height - env->font->ascent) - o;
  1001. if(n > h-a)
  1002. h = a + n;
  1003. }
  1004. if(i->kind == TkTwin && i->iwin->sub != nil) {
  1005. sub = i->iwin->sub;
  1006. n = 2 * i->iwin->pady + sub->act.height +
  1007. 2 * sub->borderwidth;
  1008. switch(i->iwin->align) {
  1009. case Tktop:
  1010. case Tkbottom:
  1011. if(n > h)
  1012. h = n;
  1013. break;
  1014. case Tkcenter:
  1015. if(n/2 > a)
  1016. a = n/2;
  1017. if(n/2 > h-a)
  1018. h = a + n/2;
  1019. break;
  1020. case Tkbaseline:
  1021. wa = i->iwin->ascent;
  1022. if (wa == -1)
  1023. wa = n;
  1024. h = maximum(a, wa) + maximum(h - a, n - wa);
  1025. a = maximum(a, wa);
  1026. break;
  1027. }
  1028. }
  1029. w = tktdispwidth(tk, tb, i, env->font, x, 0, -1);
  1030. n = x + w - xright;
  1031. if(n > 0 && wrapmode != Tkwrapnone) {
  1032. /* find shortest suffix that can be removed to fit item */
  1033. j = tktposcount(i) - 1;
  1034. while(j > 0 && tktdispwidth(tk, tb, i, env->font, x, j, -1) < n)
  1035. j--;
  1036. /* put at least one item on a line before splitting */
  1037. if(j == 0 && x == xleft) {
  1038. if(tktposcount(i) == 1)
  1039. goto Nosplit;
  1040. j = 1;
  1041. }
  1042. ix.line = l;
  1043. ix.item = i;
  1044. ix.pos = j;
  1045. if(wrapmode == Tkwrapword) {
  1046. /* trim the item at the first word at or before the shortest suffix */
  1047. /* TO DO: convert any resulting trailing white space to zero width */
  1048. ixw = ix;
  1049. if(tktisbreak(tktindrune(&ixw))) {
  1050. /* at break character, find end of word preceding it */
  1051. while(tktisbreak(tktindrune(&ixw))){
  1052. if(!tktadjustind(tkt, TkTbycharback, &ixw) ||
  1053. ixw.line != l || ixw.item == l->items && ixw.pos == 0)
  1054. goto Wrapchar; /* no suitable point, degrade to char wrap */
  1055. }
  1056. ix = ixw;
  1057. }
  1058. /* now find start of word */
  1059. tktadjustind(tkt, TkTbywrapstart, &ixw);
  1060. if(ixw.line == l && (ixw.item != l->items || ixw.pos > 0)){
  1061. /* it will leave something on the line, so reasonable to split here */
  1062. ix = ixw;
  1063. }
  1064. /* otherwise degrade to char wrap */
  1065. }
  1066. Wrapchar:
  1067. if(ix.pos > 0) {
  1068. needsplit = 1;
  1069. e = tktsplititem(&ix);
  1070. if(e != nil) {
  1071. free(opts);
  1072. free(env);
  1073. return e;
  1074. }
  1075. }
  1076. else
  1077. needsplit = 0;
  1078. e = tktnewitem(TkTcontline, 0, &it);
  1079. if(e != nil) {
  1080. free(opts);
  1081. free(env);
  1082. return e;
  1083. }
  1084. e = tktiteminsert(tkt, &ix, it);
  1085. if(e != nil) {
  1086. tktfreeitems(tkt, it, 1);
  1087. free(opts);
  1088. free(env);
  1089. return e;
  1090. }
  1091. l = l->prev; /* work on part of line up to split */
  1092. if(needsplit) {
  1093. /* have to calculate width of pre-split part */
  1094. ixprev = ix;
  1095. if(tktadjustind(tkt, TkTbyitemback, &ixprev) &&
  1096. tktadjustind(tkt, TkTbyitemback, &ixprev)) {
  1097. w = tktdispwidth(tk, tb, ixprev.item, nil, x, 0, -1);
  1098. ixprev.item->width = w;
  1099. x += w;
  1100. }
  1101. }
  1102. else {
  1103. h = oh;
  1104. a = oa;
  1105. }
  1106. break;
  1107. }
  1108. else {
  1109. Nosplit:
  1110. i->width =w;
  1111. x += w;
  1112. }
  1113. }
  1114. if (a > h)
  1115. h = a;
  1116. if (lh == 0)
  1117. lh = f->height;
  1118. if (lh > h) {
  1119. a += (lh - h) / 2;
  1120. h = lh;
  1121. }
  1122. /*
  1123. * Now line l is broken correctly and has correct item widths/line height/ascent.
  1124. * Merge adjacent TkTascii/TkTrune items with same tags.
  1125. * Also, set act{x,y} of embedded widgets to offset from
  1126. * left of item box at baseline.
  1127. */
  1128. for(i = l->items; i->next != nil; i = i->next) {
  1129. it = i->next;
  1130. if( (i->kind == TkTascii || i->kind == TkTrune)
  1131. &&
  1132. i->kind == it->kind
  1133. &&
  1134. tktsametags(i, it)) {
  1135. n = strlen(i->istring);
  1136. j = strlen(it->istring);
  1137. s = realloc(i->istring, n + j + 1);
  1138. if(s == nil) {
  1139. free(opts);
  1140. free(env);
  1141. return TkNomem;
  1142. }
  1143. i->istring = s;
  1144. memmove(i->istring+n, it->istring, j+1);
  1145. i->width += it->width;
  1146. i->next = it->next;
  1147. it->next = nil;
  1148. tktfreeitems(tkt, it, 1);
  1149. }
  1150. else if(i->kind == TkTwin && i->iwin->sub != nil) {
  1151. sub = i->iwin->sub;
  1152. n = sub->act.height + 2 * sub->borderwidth;
  1153. o = i->iwin->pady;
  1154. sub->act.x = i->iwin->padx;
  1155. /*
  1156. * sub->act.y is y-origin of widget relative to baseline.
  1157. */
  1158. switch(i->iwin->align) {
  1159. case Tktop:
  1160. sub->act.y = o - a;
  1161. break;
  1162. case Tkbottom:
  1163. sub->act.y = h - (o + n) - a;
  1164. break;
  1165. case Tkcenter:
  1166. sub->act.y = (h - n) / 2 - a;
  1167. break;
  1168. case Tkbaseline:
  1169. wa = i->iwin->ascent;
  1170. if (wa == -1)
  1171. wa = n;
  1172. sub->act.y = -wa;
  1173. break;
  1174. }
  1175. }
  1176. }
  1177. l->width = x - xleft;
  1178. /* justification bug: wrong if line has tabs */
  1179. l->orig.x = xleft;
  1180. n = xright - x;
  1181. if(n > 0) {
  1182. if(just == Tkright)
  1183. l->orig.x += n;
  1184. else
  1185. if(just == Tkcenter)
  1186. l->orig.x += n/2;
  1187. }
  1188. /* give newline or contline width up to right margin */
  1189. ilast = tktlastitem(l->items);
  1190. ilast->width = xright - l->width;
  1191. if(ilast->width < 0)
  1192. ilast->width = 0;
  1193. l->orig.y = y;
  1194. l->height = h;
  1195. l->ascent = a;
  1196. y += h;
  1197. if(l->flags&TkTlast)
  1198. y += sp3;
  1199. }
  1200. free(opts);
  1201. free(env);
  1202. tktdrawbg(tk, oldi.lo, oldi.hi, 0);
  1203. y += tktprespace(tk, l);
  1204. newrest.lo = y;
  1205. newrest.hi = y + rest.hi - rest.lo;
  1206. hole = tkttranslate(tk, newrest, rest.lo);
  1207. tktdrawbg(tk, hole.lo, hole.hi, 0);
  1208. if(l != &tkt->end) {
  1209. while(l != &tkt->end) {
  1210. oh = l->next->orig.y - l->orig.y;
  1211. l->orig.y = y;
  1212. if(y + oh > hole.lo && y < hole.hi) {
  1213. l->flags &= ~TkTdrawn;
  1214. }
  1215. y += oh;
  1216. l = l->next;
  1217. }
  1218. }
  1219. tkt->end.orig.y = tkt->end.prev->orig.y + tkt->end.prev->height;
  1220. if(tkt->deltatv.y > tkt->end.orig.y)
  1221. tkt->deltatv.y = tkt->end.prev->orig.y;
  1222. e = tktsetscroll(tk, Tkvertical);
  1223. if(e != nil)
  1224. return e;
  1225. e = tktsetscroll(tk, Tkhorizontal);
  1226. if(e != nil)
  1227. return e;
  1228. tk->dirty = tkrect(tk, 1);
  1229. if(tktdbg)
  1230. tktcheck(tkt, "tktfixgeom end");
  1231. return nil;
  1232. }
  1233. static int
  1234. tktpostspace(Tk *tk, TkTline *l)
  1235. {
  1236. int ans;
  1237. TkTitem *i;
  1238. TkEnv env;
  1239. int *opts;
  1240. opts = mallocz(TkTnumopts*sizeof(int), 0);
  1241. if(opts == nil)
  1242. return 0;
  1243. ans = 0;
  1244. if(l->items != nil && (l->flags&TkTlast)) {
  1245. for(i = l->items; i->kind == TkTmark; )
  1246. i = i->next;
  1247. tkttagopts(tk, i, opts, &env, nil, 1);
  1248. ans = opts[TkTspacing3];
  1249. }
  1250. free(opts);
  1251. return ans;
  1252. }
  1253. static int
  1254. tktprespace(Tk *tk, TkTline *l)
  1255. {
  1256. int ans;
  1257. TkTitem *i;
  1258. TkEnv env;
  1259. int *opts;
  1260. opts = mallocz(TkTnumopts*sizeof(int), 0);
  1261. if(opts == nil)
  1262. return 0;
  1263. ans = 0;
  1264. if(l->items != nil) {
  1265. for(i = l->items; i->kind == TkTmark; )
  1266. i = i->next;
  1267. tkttagopts(tk, i, opts, &env, nil, 1);
  1268. if(l->flags&TkTfirst)
  1269. ans = opts[TkTspacing1];
  1270. else
  1271. ans = opts[TkTspacing2];
  1272. }
  1273. free(opts);
  1274. return ans;
  1275. }
  1276. static int
  1277. tktwidbetween(Tk *tk, int x, TkTindex *i1, TkTindex *i2)
  1278. {
  1279. int d, w, n;
  1280. TkTindex ix;
  1281. TkText *tkt = TKobj(TkText, tk);
  1282. w = 0;
  1283. ix = *i1;
  1284. while(ix.item != i2->item) {
  1285. /* probably wrong w.r.t tag tabs */
  1286. d = tktdispwidth(tk, nil, ix.item, nil, x, ix.pos, -1);
  1287. w += d;
  1288. x += d;
  1289. if(!tktadjustind(tkt, TkTbyitem, &ix)) {
  1290. if(tktdbg)
  1291. print("tktwidbetween botch\n");
  1292. break;
  1293. }
  1294. }
  1295. n = i2->pos - ix.pos;
  1296. if(n > 0)
  1297. /* probably wrong w.r.t tag tabs */
  1298. w += tktdispwidth(tk, nil, ix.item, nil, x, ix.pos, i2->pos-ix.pos);
  1299. return w;
  1300. }
  1301. static Interval
  1302. tktvclip(Interval i, int vh)
  1303. {
  1304. if(i.lo < 0)
  1305. i.lo = 0;
  1306. if(i.hi > vh)
  1307. i.hi = vh;
  1308. return i;
  1309. }
  1310. /*
  1311. * Do translation of any part of interval that appears on screen
  1312. * starting at srcy to its new position, dsti.
  1313. * Return y-range of the hole left in the image (either because
  1314. * the src bits were out of the V window, or because the src bits
  1315. * vacated an area of the V window).
  1316. * The coordinates passed in and out are in T space.
  1317. */
  1318. static Interval
  1319. tkttranslate(Tk *tk, Interval dsti, int srcy)
  1320. {
  1321. int vh, vw, dvty, locked;
  1322. TkText *tkt;
  1323. Image *i;
  1324. Interval hole, vdst, vsrc;
  1325. Point src;
  1326. Rectangle dst;
  1327. Display *d;
  1328. hole.hi = 0;
  1329. hole.lo = 0;
  1330. /*
  1331. * If we are embedded in a text widget, we need to come in through
  1332. * the tkdrawtext routine, to ensure our clipr is set properly, so we
  1333. * just punt in that case.
  1334. * XXX is just checking parent good enough. what if we're in
  1335. * a frame in a text widget?
  1336. * BUG!
  1337. * if(tk->parent != nil && tk->parent->type == TKtext) {
  1338. * tk->flag |= Tkrefresh;
  1339. * return hole;
  1340. * }
  1341. */
  1342. tkt = TKobj(TkText, tk);
  1343. dvty = tkt->deltatv.y;
  1344. i = tkt->image;
  1345. vw = tk->act.width - tk->ipad.x;
  1346. vh = tk->act.height - tk->ipad.y;
  1347. /* convert to V space */
  1348. vdst.lo = dsti.lo - dvty;
  1349. vdst.hi = dsti.hi - dvty;
  1350. vsrc.lo = srcy - dvty;
  1351. vsrc.hi = vsrc.lo + dsti.hi - dsti.lo;
  1352. if(vsrc.lo == vsrc.hi || vsrc.lo == vdst.lo)
  1353. return hole;
  1354. else if(vsrc.hi <= 0 || vsrc.lo >= vh)
  1355. hole = tktvclip(vdst, vh);
  1356. else if(vdst.hi <= 0 || vdst.lo >= vh)
  1357. hole = tktvclip(vsrc, vh);
  1358. else if(i != nil) {
  1359. src.x = 0;
  1360. src.y = vsrc.lo;
  1361. if(vdst.lo > vsrc.lo) { /* see earlier text lines */
  1362. if(vsrc.lo < 0) {
  1363. src.y = 0;
  1364. vdst.lo -= vsrc.lo;
  1365. }
  1366. if(vdst.hi > vh)
  1367. vdst.hi = vh;
  1368. hole.lo = src.y;
  1369. hole.hi = vdst.lo;
  1370. }
  1371. else { /* see later text lines */
  1372. if(vsrc.hi > vh)
  1373. vdst.hi -= (vsrc.hi - vh);
  1374. if(vdst.lo < 0){
  1375. src.y -= vdst.lo;
  1376. vdst.lo = 0;
  1377. }
  1378. hole.lo = vdst.hi;
  1379. hole.hi = src.y + (vdst.hi - vdst.lo);
  1380. }
  1381. if(vdst.hi > vdst.lo && (tkt->tflag&TkTdrawn)) {
  1382. src = addpt(src, tkt->deltaiv);
  1383. dst = rectaddpt(Rect(0, vdst.lo, vw, vdst.hi), tkt->deltaiv);
  1384. d = tk->env->top->display;
  1385. locked = 0;
  1386. if(!(tkt->tflag&TkTdlocked))
  1387. locked = lockdisplay(d);
  1388. i = tkimageof(tk);
  1389. tkt->image = i;
  1390. if(i != nil)
  1391. draw(i, dst, i, nil, src);
  1392. if(locked)
  1393. unlockdisplay(d);
  1394. }
  1395. }
  1396. hole.lo += dvty;
  1397. hole.hi += dvty;
  1398. return hole;
  1399. }
  1400. /*
  1401. * mark lines from firsty to lasty as not drawn.
  1402. * firsty and lasty are in T space
  1403. */
  1404. static void
  1405. tktnotdrawn(Tk *tk, int firsty, int lasty, int all)
  1406. {
  1407. TkTline *lend, *l;
  1408. TkText *tkt = TKobj(TkText, tk);
  1409. if(firsty >= lasty && !all)
  1410. return;
  1411. lend = &tkt->end;
  1412. for(l = tkt->start.next; l != lend; l = l->next) {
  1413. if(l->orig.y+l->height <= firsty)
  1414. continue;
  1415. if(l->orig.y >= lasty)
  1416. break;
  1417. l->flags &= ~TkTdrawn;
  1418. if (firsty > l->orig.y)
  1419. firsty = l->orig.y;
  1420. if (lasty < l->orig.y+l->height)
  1421. lasty = l->orig.y+l->height;
  1422. }
  1423. tktdrawbg(tk, firsty, lasty, all);
  1424. tk->dirty = tkrect(tk, 1);
  1425. }
  1426. /*
  1427. * firsty and lasty are in T space
  1428. */
  1429. static void
  1430. tktdrawbg(Tk *tk, int firsty, int lasty, int all)
  1431. {
  1432. int vw, vh, locked;
  1433. Rectangle r;
  1434. Image *i;
  1435. Display *d;
  1436. TkText *tkt = TKobj(TkText, tk);
  1437. if(tk->env->top->root->flag & Tksuspended){
  1438. tk->flag |= Tkrefresh;
  1439. return;
  1440. }
  1441. /*
  1442. * If we are embedded in a text widget, we need to come in through
  1443. * the tkdrawtext routine, to ensure our clipr is set properly, so we
  1444. * just punt in that case.
  1445. * BUG!
  1446. * if(tk->parent != nil && tk->parent->type == TKtext) {
  1447. * tk->flag |= Tkrefresh;
  1448. * return;
  1449. * }
  1450. */
  1451. vw = tk->act.width - tk->ipad.x;
  1452. vh = tk->act.height - tk->ipad.y;
  1453. if(all) {
  1454. /* whole background is to be drawn, not just until last line */
  1455. firsty = 0;
  1456. lasty = 100000;
  1457. }
  1458. if(firsty >= lasty)
  1459. return;
  1460. firsty -= tkt->deltatv.y;
  1461. lasty -= tkt->deltatv.y;
  1462. if(firsty < 0)
  1463. firsty = 0;
  1464. if(lasty > vh)
  1465. lasty = vh;
  1466. r = rectaddpt(Rect(0, firsty, vw, lasty), tkt->deltaiv);
  1467. if(r.min.y < r.max.y && (tkt->tflag&TkTdrawn)) {
  1468. d = tk->env->top->display;
  1469. locked = 0;
  1470. if(!(tkt->tflag&TkTdlocked))
  1471. locked = lockdisplay(d);
  1472. i = tkimageof(tk);
  1473. tkt->image = i;
  1474. if(i != nil)
  1475. draw(i, r, tkgc(tk->env, TkCbackgnd), nil, ZP);
  1476. if(locked)
  1477. unlockdisplay(d);
  1478. }
  1479. }
  1480. static void
  1481. tktfixscroll(Tk *tk, Point odeltatv)
  1482. {
  1483. int lasty;
  1484. Interval oi, hole;
  1485. Rectangle oclipr;
  1486. Image *dst;
  1487. Point ndeltatv;
  1488. TkText *tkt = TKobj(TkText, tk);
  1489. ndeltatv = tkt->deltatv;
  1490. if(eqpt(odeltatv, ndeltatv))
  1491. return;
  1492. /* set clipr to avoid spilling outside (in case didn't come in through draw) */
  1493. dst = tkimageof(tk);
  1494. if(dst != nil) {
  1495. tkt->image = dst;
  1496. oclipr = dst->clipr;
  1497. tktsetclip(tk);
  1498. }
  1499. lasty = tkt->end.orig.y;
  1500. if(odeltatv.x != ndeltatv.x)
  1501. tktnotdrawn(tk, ndeltatv.y, lasty, 0);
  1502. else {
  1503. oi.lo = odeltatv.y;
  1504. oi.hi = lasty;
  1505. hole = tkttranslate(tk, oi, ndeltatv.y);
  1506. tktnotdrawn(tk, hole.lo, hole.hi, 0);
  1507. }
  1508. if(dst != nil)
  1509. tktreplclipr(dst, oclipr);
  1510. }
  1511. void
  1512. tktextgeom(Tk *tk)
  1513. {
  1514. TkTindex ix;
  1515. Rectangle oclipr;
  1516. Image *dst;
  1517. TkText *tkt = TKobj(TkText, tk);
  1518. char buf[20], *p;
  1519. tkt->tflag &= ~TkTdrawn;
  1520. tktsetdeltas(tk, ZP);
  1521. /* find index of current top-left, so can see it again */
  1522. tktxyind(tk, 0, 0, &ix);
  1523. /* make sure scroll bar is redrawn */
  1524. tkt->scrolltop[Tkvertical] = -1;
  1525. tkt->scrolltop[Tkhorizontal] = -1;
  1526. tkt->scrollbot[Tkvertical] = -1;
  1527. tkt->scrollbot[Tkhorizontal] = -1;
  1528. /* set clipr to avoid spilling outside (didn't come in through draw) */
  1529. dst = tkimageof(tk);
  1530. if(dst != nil) {
  1531. tkt->image = dst;
  1532. oclipr = dst->clipr;
  1533. tktsetclip(tk);
  1534. }
  1535. /*
  1536. * have to save index in a reusable format, as
  1537. * tktfixgeom can free everything that ix points to.
  1538. */
  1539. snprint(buf, sizeof(buf), "%d.%d", tktlinenum(tkt, &ix), tktlinepos(tkt, &ix));
  1540. tktfixgeom(tk, &tkt->start, tkt->end.prev, 1);
  1541. p = buf;
  1542. tktindparse(tk, &p, &ix); /* restore index to something close to original value */
  1543. tktsee(tk, &ix, 1);
  1544. if(dst != nil)
  1545. tktreplclipr(dst, oclipr);
  1546. }
  1547. static char*
  1548. tktsetscroll(Tk *tk, int orient)
  1549. {
  1550. TkText *tkt;
  1551. TkTline *l;
  1552. int ntot, nmin, nmax, top, bot, vw, vh;
  1553. char *val, *cmd, *v, *e, *s;
  1554. tkt = TKobj(TkText, tk);
  1555. s = (orient == Tkvertical)? tkt->yscroll : tkt->xscroll;
  1556. if(s == nil)
  1557. return nil;
  1558. vw = tk->act.width - tk->ipad.x;
  1559. vh = tk->act.height - tk->ipad.y;
  1560. if(orient == Tkvertical) {
  1561. l = tkt->end.prev;
  1562. ntot = l->orig.y + l->height;
  1563. nmin = tkt->deltatv.y;
  1564. if(vh <= 0)
  1565. nmax = nmin;
  1566. else
  1567. nmax = nmin + vh;
  1568. }
  1569. else {
  1570. ntot = tktmaxwid(tkt->start.next);
  1571. nmin = tkt->deltatv.x;
  1572. if(vw <= 0)
  1573. nmax = nmin;
  1574. else
  1575. nmax = nmin + vw;
  1576. }
  1577. if(ntot == 0) {
  1578. top = 0;
  1579. bot = TKI2F(1);
  1580. }
  1581. else {
  1582. if(ntot < nmax)
  1583. ntot = nmax;
  1584. top = TKI2F(nmin)/ntot;
  1585. bot = TKI2F(nmax)/ntot;
  1586. }
  1587. if(tkt->scrolltop[orient] == top && tkt->scrollbot[orient] == bot)
  1588. return nil;
  1589. tkt->scrolltop[orient] = top;
  1590. tkt->scrollbot[orient] = bot;
  1591. val = mallocz(Tkminitem, 0);
  1592. if(val == nil)
  1593. return TkNomem;
  1594. cmd = mallocz(Tkmaxitem, 0);
  1595. if(cmd == nil) {
  1596. free(val);
  1597. return TkNomem;
  1598. }
  1599. v = tkfprint(val, top);
  1600. *v++ = ' ';
  1601. tkfprint(v, bot);
  1602. snprint(cmd, Tkmaxitem, "%s %s", s, val);
  1603. e = tkexec(tk->env->top, cmd, nil);
  1604. free(cmd);
  1605. free(val);
  1606. return e;
  1607. }
  1608. static char*
  1609. tktview(Tk *tk, char *arg, char **val, int nl, int *posn, int max, int orient)
  1610. {
  1611. int top, bot, amount, n;
  1612. char buf[Tkminitem], *v, *e;
  1613. if(*arg == '\0') {
  1614. if ( max == 0 ) {
  1615. top = 0;
  1616. bot = TKI2F(1);
  1617. }
  1618. else {
  1619. top = TKI2F(*posn)/max;
  1620. bot = TKI2F(*posn+nl)/max;
  1621. if (bot > TKI2F(1))
  1622. bot = TKI2F(1);
  1623. }
  1624. v = tkfprint(buf, top);
  1625. *v++ = ' ';
  1626. tkfprint(v, bot);
  1627. return tkvalue(val, "%s", buf);
  1628. }
  1629. arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
  1630. if(strcmp(buf, "moveto") == 0) {
  1631. e = tkfracword(tk->env->top, &arg, &top, nil);
  1632. if (e != nil)
  1633. return e;
  1634. *posn = TKF2I(top*max);
  1635. }
  1636. else
  1637. if(strcmp(buf, "scroll") == 0) {
  1638. e = tkfracword(tk->env->top, &arg, &amount, nil);
  1639. if(e != nil)
  1640. return e;
  1641. arg = tkskip(arg, " \t");
  1642. if(*arg == 'p') /* Pages */
  1643. amount *= nl;
  1644. else /* Lines or Characters */
  1645. if(orient == Tkvertical) {
  1646. /* XXX needs improvement */
  1647. amount *= tk->env->font->height;
  1648. }
  1649. else
  1650. amount *= tk->env->wzero;
  1651. amount = TKF2I(amount);
  1652. n = *posn + amount;
  1653. if(n < 0)
  1654. n = 0;
  1655. if(n > max)
  1656. n = max;
  1657. *posn = n;
  1658. }
  1659. else
  1660. return TkBadcm;
  1661. bot = max - (nl * 3 / 4);
  1662. if(*posn > bot)
  1663. *posn = bot;
  1664. if(*posn < 0)
  1665. *posn = 0;
  1666. return nil;
  1667. }
  1668. static void
  1669. tktclearsel(Tk *tk)
  1670. {
  1671. TkTindex ibeg, iend;
  1672. TkText *tkt = TKobj(TkText, tk);
  1673. if(tkt->selfirst == nil)
  1674. return;
  1675. tktitemind(tkt->selfirst, &ibeg);
  1676. tktitemind(tkt->sellast, &iend);
  1677. tkttagchange(tk, TkTselid, &ibeg, &iend, 0);
  1678. }
  1679. static int
  1680. tktgetsel(Tk *tk, TkTindex *i1, TkTindex *i2)
  1681. {
  1682. TkText *tkt =TKobj(TkText, tk);
  1683. if(tkt->selfirst == nil)
  1684. return 0;
  1685. tktitemind(tkt->selfirst, i1);
  1686. tktitemind(tkt->sellast, i2);
  1687. return 1;
  1688. }
  1689. /*
  1690. * Adjust tkt->deltatv so that indexed character is visible.
  1691. * - if seetop is true, make indexed char be at top of window
  1692. * - if it is already visible, do nothing.
  1693. * - if it is > 1/2 screenful off edge of screen, center it
  1694. * else put it at bottom or top (whichever is nearer)
  1695. * - if first line is visible, put it at top
  1696. * - if last line is visible, allow one blank line at bottom
  1697. *
  1698. * BUG: should handle x visibility too
  1699. */
  1700. static void
  1701. tktsee(Tk *tk, TkTindex *ixp, int seetop)
  1702. {
  1703. int ycur, ynext, deltatvy, adjy, h;
  1704. Point p, odeltatv;
  1705. Rectangle bbox;
  1706. TkTline *l, *el;
  1707. TkText *tkt = TKobj(TkText, tk);
  1708. TkTindex ix;
  1709. ix = *ixp;
  1710. deltatvy = tkt->deltatv.y;
  1711. odeltatv = tkt->deltatv;
  1712. h = tk->act.height;
  1713. /* find p (in T space): top left of indexed line */
  1714. l = ix.line;
  1715. p = l->orig;
  1716. /* ycur, ynext in V space */
  1717. ycur = p.y - deltatvy;
  1718. ynext = ycur + l->height;
  1719. adjy = 0;
  1720. /* quantize h to line boundaries (works if single font) */
  1721. if ( l->height )
  1722. h -= h%l->height;
  1723. if(seetop) {
  1724. deltatvy = p.y;
  1725. adjy = 1;
  1726. }
  1727. else
  1728. if(ycur < 0 || ynext >= h) {
  1729. adjy = 1;
  1730. if(ycur < -h/2 || ycur > 3*h/2)
  1731. deltatvy = p.y - h/2;
  1732. else if(ycur < 0)
  1733. deltatvy = p.y;
  1734. else
  1735. deltatvy = p.y - h + l->height;
  1736. el = tkt->end.prev;
  1737. if(el != nil && el->orig.y - deltatvy < h)
  1738. deltatvy = tkt->end.orig.y - (h * 3 / 4);
  1739. if(p.y - deltatvy < 0)
  1740. deltatvy = p.y;
  1741. if(deltatvy < 0)
  1742. deltatvy = 0;
  1743. }
  1744. if(adjy) {
  1745. tkt->deltatv.y = deltatvy;
  1746. tktsetscroll(tk, Tkvertical); /* XXX - Tad: err ignored */
  1747. tktfixscroll(tk, odeltatv);
  1748. }
  1749. while (ix.item->kind == TkTmark)
  1750. ix.item = ix.item->next;
  1751. bbox = tktbbox(tk, &ix);
  1752. /* make sure that cursor at the end gets shown */
  1753. tksee(tk, bbox, Pt(bbox.min.x, (bbox.min.y + bbox.max.y) / 2));
  1754. }
  1755. static int
  1756. tktcmatch(int c1, int c2, int nocase)
  1757. {
  1758. if(nocase) {
  1759. if(c1 >= 'a' && c1 <= 'z')
  1760. c1 -= 'a' - 'A';
  1761. if(c2 >= 'a' && c2 <= 'z')
  1762. c2 -= 'a' - 'A';
  1763. }
  1764. return (c1 == c2);
  1765. }
  1766. /*
  1767. * Return 1 if tag with id m1 ends before tag with id m2,
  1768. * starting at the item after that indexed in ix (but don't
  1769. * modify ix).
  1770. */
  1771. static int
  1772. tagendsbefore(TkText *tkt, TkTindex *ix, int m1, int m2)
  1773. {
  1774. int s1, s2;
  1775. TkTindex ix1;
  1776. TkTitem *i;
  1777. ix1 = *ix;
  1778. while(tktadjustind(tkt, TkTbyitem, &ix1)) {
  1779. i = ix1.item;
  1780. if(i->kind == TkTwin || i->kind == TkTcontline || i->kind == TkTmark)
  1781. continue;
  1782. s1 = tkttagset(i, m1);
  1783. s2 = tkttagset(i, m2);
  1784. if(!s1)
  1785. return s2;
  1786. else if(!s2)
  1787. return 0;
  1788. }
  1789. return 0;
  1790. }
  1791. static int
  1792. tktsgmltags(TkText *tkt, Fmt *fmt, TkTitem *iprev, TkTitem *i, TkTindex *ix, int *stack, int *pnstack, int *tmpstack)
  1793. {
  1794. int nprev, n, m, r, k, j, ii, onstack, nt;
  1795. nprev = 0;
  1796. if(iprev != nil && (iprev->tags[0] != 0 || iprev->tagextra > 0))
  1797. nprev = 32*(iprev->tagextra + 1);
  1798. n = 0;
  1799. if(i != nil && (i->tags[0] != 0 || i->tagextra > 0))
  1800. n = 32*(i->tagextra + 1);
  1801. nt = 0;
  1802. if(n > 0) {
  1803. /* find tags which open here */
  1804. for(m = 0; m < n; m++)
  1805. if(tkttagset(i, m) && (iprev == nil || !tkttagset(iprev, m)))
  1806. tmpstack[nt++] = m;
  1807. }
  1808. if(nprev > 0) {
  1809. /*
  1810. * Find lowest tag in stack that ends before any tag beginning here.
  1811. * We have to emit end tags all the way down to there, then add
  1812. * back the ones that haven't actually ended here, together with ones
  1813. * that start here, and sort all of the added ones so that tags that
  1814. * end later are lower in the stack.
  1815. */
  1816. ii = *pnstack;
  1817. for(k = *pnstack - 1; k >=0; k--) {
  1818. m = stack[k];
  1819. if(i == nil || !tkttagset(i, m))
  1820. ii = k;
  1821. else
  1822. for(j = 0; j < nt; j++)
  1823. if(tagendsbefore(tkt, ix, m, tmpstack[j]))
  1824. ii = k;
  1825. }
  1826. for(k = *pnstack - 1; k >= ii; k--) {
  1827. m = stack[k];
  1828. r = fmtprint(fmt, "</%s>", tkttagname(tkt, m));
  1829. if(r < 0)
  1830. return r;
  1831. /* add m back to starting tags if m didn't actually end here */
  1832. if(i != nil && tkttagset(i, m))
  1833. tmpstack[nt++] = m;
  1834. }
  1835. *pnstack = ii;
  1836. }
  1837. if(nt > 0) {
  1838. /* add tags which open or reopen here */
  1839. onstack = *pnstack;
  1840. k = onstack;
  1841. for(j = 0; j < nt; j++)
  1842. stack[k++] = tmpstack[j];
  1843. *pnstack = k;
  1844. if(k - onstack > 1) {
  1845. /* sort new stack entries so tags that end later are lower in stack */
  1846. for(ii = k-2; ii>= onstack; ii--) {
  1847. m = stack[ii];
  1848. for(j = ii+1; j < k && tagendsbefore(tkt, ix, m, stack[j]); j++) {
  1849. stack[j-1] = stack[j];
  1850. }
  1851. stack[j-1] = m;
  1852. }
  1853. }
  1854. for(j = onstack; j < k; j++) {
  1855. r = fmtprint(fmt, "<%s>", tkttagname(tkt, stack[j]));
  1856. if(r < 0)
  1857. return r;
  1858. }
  1859. }
  1860. return 0;
  1861. }
  1862. /*
  1863. * In 'sgml' format, just print text (no special treatment of
  1864. * special characters, except that < turns into &lt;)
  1865. * interspersed with things like <Bold> and </Bold>
  1866. * (where Bold is a tag name).
  1867. * Make sure that the tag pairs nest properly.
  1868. */
  1869. static char*
  1870. tktget(TkText *tkt, TkTindex *ix1, TkTindex *ix2, int sgml, char **val)
  1871. {
  1872. int n, m, i, bychar, nstack;
  1873. int *stack, *tmpstack;
  1874. char *s;
  1875. TkTitem *iprev;
  1876. Tk *sub;
  1877. Fmt fmt;
  1878. char *buf;
  1879. if(!tktindbefore(ix1, ix2))
  1880. return nil;
  1881. stack = nil;
  1882. tmpstack = nil;
  1883. iprev = nil;
  1884. fmtstrinit(&fmt);
  1885. buf = mallocz(100, 0);
  1886. if(buf == nil)
  1887. return TkNomem;
  1888. if(sgml) {
  1889. stack = malloc((tkt->nexttag+1)*sizeof(int));
  1890. tmpstack = malloc((tkt->nexttag+1)*sizeof(int));
  1891. if(stack == nil || tmpstack == nil)
  1892. goto nomemret;
  1893. nstack = 0;
  1894. }
  1895. for(;;) {
  1896. if(ix1->item == ix2->item && ix1->pos == ix2->pos)
  1897. break;
  1898. s = nil;
  1899. bychar = 0;
  1900. m = 1;
  1901. switch(ix1->item->kind) {
  1902. case TkTrune:
  1903. s = ix1->item->istring;
  1904. s += tktutfpos(s, ix1->pos);
  1905. if(ix1->item == ix2->item) {
  1906. m = ix2->pos - ix1->pos;
  1907. bychar = 1;
  1908. }
  1909. break;
  1910. case TkTascii:
  1911. s = ix1->item->istring + ix1->pos;
  1912. if(ix1->item == ix2->item) {
  1913. m = ix2->pos - ix1->pos;
  1914. bychar = 1;
  1915. }
  1916. else {
  1917. m = strlen(s);
  1918. if(sgml && memchr(s, '<', m) != nil)
  1919. bychar = 1;
  1920. }
  1921. break;
  1922. case TkTtab:
  1923. s = "\t";
  1924. break;
  1925. case TkTnewline:
  1926. s = "\n";
  1927. break;
  1928. case TkTwin:
  1929. sub = ix1->item->iwin->sub;
  1930. if(sgml && sub != nil && sub->name != nil) {
  1931. snprint(buf, 100, "<Window %s>", sub->name->name);
  1932. s = buf;
  1933. }
  1934. }
  1935. if(s != nil) {
  1936. if(sgml) {
  1937. n = tktsgmltags(tkt, &fmt, iprev, ix1->item, ix1, stack, &nstack, tmpstack);
  1938. if(n < 0)
  1939. goto nomemret;
  1940. }
  1941. if(bychar) {
  1942. if (ix1->item->kind == TkTrune)
  1943. n = fmtprint(&fmt, "%.*s", m, s);
  1944. else {
  1945. n = 0;
  1946. for(i = 0; i < m && n >= 0; i++) {
  1947. if(s[i] == '<')
  1948. n = fmtprint(&fmt, "&lt;");
  1949. else
  1950. n = fmtprint(&fmt, "%c", s[i]);
  1951. }
  1952. }
  1953. }
  1954. else
  1955. n = fmtprint(&fmt, "%s", s);
  1956. if(n < 0)
  1957. goto nomemret;
  1958. iprev = ix1->item;
  1959. }
  1960. if(ix1->item == ix2->item)
  1961. break;
  1962. if(!tktadjustind(tkt, TkTbyitem, ix1)) {
  1963. if(tktdbg)
  1964. print("tktextget botch\n");
  1965. break;
  1966. }
  1967. }
  1968. if(sgml) {
  1969. n = tktsgmltags(tkt, &fmt, iprev, nil, nil, stack, &nstack, tmpstack);
  1970. if(n < 0)
  1971. goto nomemret;
  1972. }
  1973. *val = fmtstrflush(&fmt);
  1974. free(buf);
  1975. return nil;
  1976. nomemret:
  1977. free(buf);
  1978. if(stack != nil)
  1979. free(stack);
  1980. if(tmpstack != nil)
  1981. free(tmpstack);
  1982. return TkNomem;
  1983. }
  1984. /* Widget Commands (+ means implemented)
  1985. +bbox
  1986. +cget
  1987. +compare
  1988. +configure
  1989. +debug
  1990. +delete
  1991. +dlineinfo
  1992. +dump
  1993. +get
  1994. +index
  1995. +insert
  1996. +mark
  1997. +scan
  1998. +search
  1999. +see
  2000. +tag
  2001. +window
  2002. +xview
  2003. +yview
  2004. */
  2005. static int
  2006. tktviewrectclip(Rectangle *r, Rectangle b);
  2007. static char*
  2008. tktextbbox(Tk *tk, char *arg, char **val)
  2009. {
  2010. char *e;
  2011. int noclip, w, h;
  2012. Rectangle r, rview;
  2013. TkTindex ix;
  2014. TkText *tkt;
  2015. char buf[Tkmaxitem];
  2016. e = tktindparse(tk, &arg, &ix);
  2017. if(e != nil)
  2018. return e;
  2019. noclip = 0;
  2020. if(*arg != '\0') {
  2021. /* extension to tk4.0:
  2022. * "noclip" means don't clip to viewable area
  2023. * "all" means give unclipped bbox of entire contents
  2024. */
  2025. arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
  2026. if(strcmp(buf, "noclip") == 0)
  2027. noclip = 1;
  2028. else
  2029. if(strcmp(buf, "all") == 0) {
  2030. tkt = TKobj(TkText, tk);
  2031. w = tktmaxwid(tkt->start.next);
  2032. h = tkt->end.orig.y;
  2033. return tkvalue(val, "0 0 %d %d", w, h);
  2034. }
  2035. }
  2036. /*
  2037. * skip marks; bbox applies to characters only.
  2038. * it's not defined what happens when bbox is applied to a newline char,
  2039. * so we'll just let the default case sort that out.
  2040. */
  2041. while (ix.item->kind == TkTmark)
  2042. ix.item = ix.item->next;
  2043. r = tktbbox(tk, &ix);
  2044. rview.min.x = 0;
  2045. rview.min.y = 0;
  2046. rview.max.x = tk->act.width - tk->ipad.x;
  2047. rview.max.y = tk->act.height - tk->ipad.y;
  2048. if(noclip || tktviewrectclip(&r, rview))
  2049. return tkvalue(val, "%d %d %d %d", r.min.x, r.min.y,
  2050. r.max.x-r.min.x, r.max.y-r.min.y);
  2051. return nil;
  2052. }
  2053. /*
  2054. * a supplemented rectclip, as ((0, 1), (0,1)) does not intersect ((0, 0), (5, 5))
  2055. * but for our purposes, we want it to. it's a hack.
  2056. */
  2057. static int
  2058. tktviewrectclip(Rectangle *rp, Rectangle b)
  2059. {
  2060. Rectangle *bp = &b;
  2061. if((rp->min.x<bp->max.x &&
  2062. (bp->min.x<rp->max.x || (rp->max.x == b.min.x
  2063. && rp->min.x == b.min.x)) &&
  2064. rp->min.y<bp->max.y && bp->min.y<rp->max.y)==0)
  2065. return 0;
  2066. /* They must overlap */
  2067. if(rp->min.x < bp->min.x)
  2068. rp->min.x = bp->min.x;
  2069. if(rp->min.y < bp->min.y)
  2070. rp->min.y = bp->min.y;
  2071. if(rp->max.x > bp->max.x)
  2072. rp->max.x = bp->max.x;
  2073. if(rp->max.y > bp->max.y)
  2074. rp->max.y = bp->max.y;
  2075. return 1;
  2076. }
  2077. static Point
  2078. scr2local(Tk *tk, Point p)
  2079. {
  2080. p = subpt(p, tkposn(tk));
  2081. p.x -= tk->borderwidth;
  2082. p.y -= tk->borderwidth;
  2083. return p;
  2084. }
  2085. static char*
  2086. tktextbutton1(Tk *tk, char *arg, char **val)
  2087. {
  2088. char *e;
  2089. Point p;
  2090. TkCtxt *c;
  2091. TkTindex ix;
  2092. TkTmarkinfo *mi;
  2093. TkText *tkt = TKobj(TkText, tk);
  2094. USED(val);
  2095. e = tkxyparse(tk, &arg, &p);
  2096. if(e != nil)
  2097. return e;
  2098. tkt->track = p;
  2099. p = scr2local(tk, p);
  2100. tktxyind(tk, p.x, p.y, &ix);
  2101. tkt->tflag &= ~TkTjustfoc;
  2102. c = tk->env->top->ctxt;
  2103. if(!(tk->flag&Tkdisabled) && c->tkkeygrab != tk
  2104. && (tk->name != nil) && ix.item->kind != TkTwin) {
  2105. tkfocus(tk->env->top, tk->name->name, nil);
  2106. tkt->tflag |= TkTjustfoc;
  2107. return nil;
  2108. }
  2109. mi = tktfindmark(tkt->marks, "insert");
  2110. if(tktdbg && !mi) {
  2111. print("tktextbutton1: botch\n");
  2112. return nil;
  2113. }
  2114. tktmarkmove(tk, mi, &ix);
  2115. tktclearsel(tk);
  2116. tkrepeat(tk, autoselect, nil, TkRptpause, TkRptinterval);
  2117. return nil;
  2118. }
  2119. static char*
  2120. tktextbutton1r(Tk *tk, char *arg, char **val)
  2121. {
  2122. TkText *tkt;
  2123. USED(arg);
  2124. USED(val);
  2125. tkt = TKobj(TkText, tk);
  2126. tkt->tflag &= ~TkTnodrag;
  2127. tkcancelrepeat(tk);
  2128. return nil;
  2129. }
  2130. static char*
  2131. tktextcget(Tk *tk, char *arg, char **val)
  2132. {
  2133. TkText *tkt;
  2134. TkOptab tko[3];
  2135. tkt = TKobj(TkText, tk);
  2136. tko[0].ptr = tk;
  2137. tko[0].optab = tkgeneric;
  2138. tko[1].ptr = tkt;
  2139. tko[1].optab = textopts;
  2140. tko[2].ptr = nil;
  2141. return tkgencget(tko, arg, val, tk->env->top);
  2142. }
  2143. static char*
  2144. tktextcompare(Tk *tk, char *arg, char **val)
  2145. {
  2146. int op;
  2147. char *e;
  2148. TkTindex i1, i2;
  2149. TkText *tkt;
  2150. TkStab *s;
  2151. char *buf;
  2152. tkt = TKobj(TkText, tk);
  2153. e = tktindparse(tk, &arg, &i1);
  2154. if(e != nil)
  2155. return e;
  2156. if(*arg == '\0')
  2157. return TkBadcm;
  2158. buf = mallocz(Tkmaxitem, 0);
  2159. if(buf == nil)
  2160. return TkNomem;
  2161. arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
  2162. op = -1;
  2163. for(s = tkcompare; s->val; s++)
  2164. if(strcmp(s->val, buf) == 0) {
  2165. op = s->con;
  2166. break;
  2167. }
  2168. if(op == -1) {
  2169. free(buf);
  2170. return TkBadcm;
  2171. }
  2172. e = tktindparse(tk, &arg, &i2);
  2173. if(e != nil) {
  2174. free(buf);
  2175. return e;
  2176. }
  2177. e = tkvalue(val, tktindcompare(tkt, &i1, op, &i2)? "1" : "0");
  2178. free(buf);
  2179. return e;
  2180. }
  2181. static char*
  2182. tktextconfigure(Tk *tk, char *arg, char **val)
  2183. {
  2184. char *e;
  2185. TkGeom g;
  2186. int bd;
  2187. TkText *tkt;
  2188. TkOptab tko[3];
  2189. tkt = TKobj(TkText, tk);
  2190. tko[0].ptr = tk;
  2191. tko[0].optab = tkgeneric;
  2192. tko[1].ptr = tkt;
  2193. tko[1].optab = textopts;
  2194. tko[2].ptr = nil;
  2195. if(*arg == '\0')
  2196. return tkconflist(tko, val);
  2197. g = tk->req;
  2198. bd = tk->borderwidth;
  2199. e = tkparse(tk->env->top, arg, tko, nil);
  2200. tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
  2201. if (tkt->propagate != BoolT) {
  2202. if ((tk->flag & Tksetwidth) == 0)
  2203. tk->req.width = tk->env->wzero*Textwidth;
  2204. if ((tk->flag & Tksetheight) == 0)
  2205. tk->req.height = tk->env->font->height*Textheight;
  2206. }
  2207. /* note: tkgeomchg() may also call tktfixgeom() via tktextgeom() */
  2208. tktfixgeom(tk, &tkt->start, tkt->end.prev, 0);
  2209. tktextsize(tk, 0);
  2210. tkgeomchg(tk, &g, bd);
  2211. tktnotdrawn(tk, 0, tkt->end.orig.y, 1);
  2212. return e;
  2213. }
  2214. static char*
  2215. tktextdebug(Tk *tk, char *arg, char **val)
  2216. {
  2217. char buf[Tkmaxitem];
  2218. tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
  2219. if(*buf == '\0')
  2220. return tkvalue(val, "%s", tktdbg? "on" : "off");
  2221. else {
  2222. tktdbg = (strcmp(buf, "1") == 0 || strcmp(buf, "yes") == 0);
  2223. if(tktdbg) {
  2224. tktprinttext(TKobj(TkText, tk));
  2225. }
  2226. return nil;
  2227. }
  2228. }
  2229. static char*
  2230. tktextdelete(Tk *tk, char *arg, char **val)
  2231. {
  2232. int sameit;
  2233. char *e;
  2234. TkTindex i1, i2, ip, isee;
  2235. TkTline *lmin;
  2236. TkText *tkt = TKobj(TkText, tk);
  2237. char buf[20], *p;
  2238. USED(val);
  2239. e = tktindparse(tk, &arg, &i1);
  2240. if(e != nil)
  2241. return e;
  2242. tktadjustind(tkt, TkTbycharstart, &i1);
  2243. e = tktsplititem(&i1);
  2244. if(e != nil)
  2245. return e;
  2246. if(*arg != '\0') {
  2247. e = tktindparse(tk, &arg, &i2);
  2248. if(e != nil)
  2249. return e;
  2250. }
  2251. else {
  2252. i2 = i1;
  2253. tktadjustind(tkt, TkTbychar, &i2);
  2254. }
  2255. if(tktindcompare(tkt, &i1, TkGte, &i2))
  2256. return nil;
  2257. sameit = (i1.item == i2.item);
  2258. /* save possible fixup see place */
  2259. isee.line = nil;
  2260. if(i2.line->orig.y + i2.line->height < tkt->deltatv.y) {
  2261. /* delete completely precedes view */
  2262. tktxyind(tk, 0, 0, &isee);
  2263. }
  2264. e = tktsplititem(&i2);
  2265. if(e != nil)
  2266. return e;
  2267. if(sameit) {
  2268. /* after split, i1 should be in previous item to i2 */
  2269. ip = i2;
  2270. tktadjustind(tkt, TkTbyitemback, &ip);
  2271. i1.item = ip.item;
  2272. }
  2273. lmin = tktprevwrapline(tk, i1.line);
  2274. while(i1.item != i2.item) {
  2275. if(i1.item->kind != TkTmark)
  2276. tktremitem(tkt, &i1);
  2277. /* tktremitem moves i1 to next item */
  2278. else {
  2279. if(!tktadjustind(tkt, TkTbyitem, &i1)) {
  2280. if(tktdbg)
  2281. print("tktextdelete botch\n");
  2282. break;
  2283. }
  2284. }
  2285. }
  2286. /*
  2287. * guard against invalidation of index by tktfixgeom
  2288. */
  2289. if (isee.line != nil)
  2290. snprint(buf, sizeof(buf), "%d.%d", tktlinenum(tkt, &isee), tktlinepos(tkt, &isee));
  2291. tktfixgeom(tk, lmin, i1.line, 0);
  2292. tktextsize(tk, 1);
  2293. if(isee.line != nil) {
  2294. p = buf;
  2295. tktindparse(tk, &p, &isee);
  2296. tktsee(tk, &isee, 1);
  2297. }
  2298. return nil;
  2299. }
  2300. static char*
  2301. tktextsee(Tk *tk, char *arg, char **val)
  2302. {
  2303. char *e;
  2304. TkTindex ix;
  2305. USED(val);
  2306. e = tktindparse(tk, &arg, &ix);
  2307. if(e != nil)
  2308. return e;
  2309. tktsee(tk, &ix, 0);
  2310. return nil;
  2311. }
  2312. static char*
  2313. tktextdelins(Tk *tk, char *arg, char **val)
  2314. {
  2315. int m, c, skipping, wordc, n;
  2316. TkTindex ix, ix2;
  2317. TkText *tkt = TKobj(TkText, tk);
  2318. char buf[30];
  2319. USED(val);
  2320. if(tk->flag&Tkdisabled)
  2321. return nil;
  2322. if(tktgetsel(tk, &ix, &ix2))
  2323. tktextdelete(tk, "sel.first sel.last", nil);
  2324. else {
  2325. while(*arg == ' ')
  2326. arg++;
  2327. if(*arg == '-') {
  2328. m = arg[1];
  2329. if(m == 'c')
  2330. n = 1;
  2331. else {
  2332. /* delete prev word (m=='w') or prev line (m=='l') */
  2333. if(!tktmarkind(tk, "insert", &ix))
  2334. return nil;
  2335. if(!tktadjustind(tkt, TkTbycharback, &ix))
  2336. return nil;
  2337. n = 1;
  2338. /* ^W skips back over nonwordchars, then takes maximal seq of wordchars */
  2339. skipping = 1;
  2340. for(;;) {
  2341. c = tktindrune(&ix);
  2342. if(c == '\n') {
  2343. /* special case: always delete at least one char */
  2344. if(n > 1)
  2345. n--;
  2346. break;
  2347. }
  2348. if(m == 'w') {
  2349. wordc = tkiswordchar(c);
  2350. if(wordc && skipping)
  2351. skipping = 0;
  2352. else if(!wordc && !skipping) {
  2353. n--;
  2354. break;
  2355. }
  2356. }
  2357. if(tktadjustind(tkt, TkTbycharback, &ix))
  2358. n++;
  2359. else
  2360. break;
  2361. }
  2362. }
  2363. sprint(buf, "insert-%dc insert", n);
  2364. tktextdelete(tk, buf, nil);
  2365. }
  2366. else if(arg[0] == '+' && arg[1] == 'l')
  2367. tktextdelete(tk, "insert {insert lineend}", nil);
  2368. else
  2369. tktextdelete(tk, "insert", nil);
  2370. tktextsee(tk, "insert", nil);
  2371. }
  2372. return nil;
  2373. }
  2374. static char*
  2375. tktextdlineinfo(Tk *tk, char *arg, char **val)
  2376. {
  2377. char *e;
  2378. TkTindex ix;
  2379. TkTline *l;
  2380. Point p;
  2381. int vh;
  2382. TkText *tkt = TKobj(TkText, tk);
  2383. e = tktindparse(tk, &arg, &ix);
  2384. if(e != nil)
  2385. return e;
  2386. l = ix.line;
  2387. vh = tk->act.height;
  2388. /* get p in V space */
  2389. p = subpt(l->orig, tkt->deltatv);
  2390. if(p.y+l->height < 0 || p.y >= vh)
  2391. return nil;
  2392. return tkvalue(val, "%d %d %d %d %d",
  2393. p.x, p.y, l->width, l->height, l->ascent);
  2394. }
  2395. static char*
  2396. tktextdump(Tk *tk, char *arg, char **val)
  2397. {
  2398. TkTline *l;
  2399. TkTitem *i;
  2400. Fmt fmt;
  2401. TkText *tkt;
  2402. TkDump tkdump;
  2403. TkOptab tko[2];
  2404. TkTtaginfo *ti;
  2405. TkName *names, *n;
  2406. char *e, *win, *p;
  2407. TkTindex ix1, ix2;
  2408. int r, j, numitems;
  2409. ulong fg, bg;
  2410. tkt = TKobj(TkText, tk);
  2411. tkdump.sgml = 0;
  2412. tkdump.metrics = 0;
  2413. tko[0].ptr = &tkdump;
  2414. tko[0].optab = dumpopts;
  2415. tko[1].ptr = nil;
  2416. names = nil;
  2417. e = tkparse(tk->env->top, arg, tko, &names);
  2418. if(e != nil)
  2419. return e;
  2420. if(names != nil) { /* supplied indices */
  2421. p = names->name;
  2422. e = tktindparse(tk, &p, &ix1);
  2423. if(e != nil) {
  2424. tkfreename(names);
  2425. return e;
  2426. }
  2427. n = names->link;
  2428. if(n != nil) {
  2429. p = n->name;
  2430. e = tktindparse(tk, &p, &ix2);
  2431. if(e != nil) {
  2432. tkfreename(names);
  2433. return e;
  2434. }
  2435. }
  2436. else {
  2437. ix2 = ix1;
  2438. tktadjustind(tkt, TkTbychar, &ix2);
  2439. }
  2440. tkfreename(names);
  2441. if(!tktindbefore(&ix1, &ix2))
  2442. return nil;
  2443. }
  2444. else
  2445. return TkBadix;
  2446. if(tkdump.metrics != 0) {
  2447. fmtstrinit(&fmt);
  2448. if(fmtprint(&fmt, "%%Fonts\n") < 0)
  2449. return TkNomem;
  2450. for(ti=tkt->tags; ti != nil; ti=ti->next) {
  2451. if(ti->env == nil || ti->env->font == nil)
  2452. continue;
  2453. if(fmtprint(&fmt, "%d::%s\n", ti->id,ti->env->font->name) < 0)
  2454. return TkNomem;
  2455. }
  2456. if(fmtprint(&fmt, "-1::%s\n%%Colors\n", tk->env->font->name) < 0)
  2457. return TkNomem;
  2458. for(ti=tkt->tags; ti != nil; ti=ti->next) {
  2459. if(ti->env == nil)
  2460. continue;
  2461. bg = ti->env->colors[TkCbackgnd];
  2462. fg = ti->env->colors[TkCforegnd];
  2463. if(bg == tk->env->colors[TkCbackgnd] &&
  2464. fg == ti->env->colors[TkCforegnd])
  2465. continue;
  2466. r = fmtprint(&fmt,"%d::#%.8lux\n", ti->id, bg);
  2467. if(r < 0)
  2468. return TkNomem;
  2469. r = fmtprint(&fmt,"%d::#%.8lux\n", ti->id, fg);
  2470. if(r < 0)
  2471. return TkNomem;
  2472. }
  2473. if(fmtprint(&fmt, "%%Lines\n") < 0)
  2474. return TkNomem;
  2475. /*
  2476. * In 'metrics' format lines are recorded in the following way:
  2477. * xorig yorig wd ht as [data]
  2478. * where data is of the form:
  2479. * CodeWidth{tags} data
  2480. * For Example;
  2481. * A200{200000} Hello World!
  2482. * denotes an A(scii) contiguous string of 200 pixels with
  2483. * bit 20 set in its tags which corresponds to some font.
  2484. *
  2485. */
  2486. if(ix2.line->items != ix2.item)
  2487. ix2.line = ix2.line->next;
  2488. for(l = ix1.line; l != ix2.line; l = l->next) {
  2489. numitems = 0;
  2490. for(i = l->items; i != nil; i = i->next) {
  2491. if(i->kind != TkTmark)
  2492. numitems++;
  2493. }
  2494. r = fmtprint(&fmt, "%d %d %d %d %d %d ",
  2495. l->orig.x, l->orig.y, l->width, l->height, l->ascent,numitems);
  2496. if(r < 0)
  2497. return TkNomem;
  2498. for(i = l->items; i != nil; i = i->next) {
  2499. switch(i->kind) {
  2500. case TkTascii:
  2501. case TkTrune:
  2502. r = i->kind == TkTascii ? 'A' : 'R';
  2503. if(fmtprint(&fmt,"[%c%d{", r, i->width) < 0)
  2504. return TkNomem;
  2505. if(i->tags !=0 || i->tagextra !=0) {
  2506. if(fmtprint(&fmt,"%lux", i->tags[0]) < 0)
  2507. return TkNomem;
  2508. for(j=0; j < i->tagextra; j++)
  2509. if(fmtprint(&fmt,"::%lux", i->tags[j+1]) < 0)
  2510. return TkNomem;
  2511. }
  2512. /* XXX string should be quoted to avoid embedded ']'s */
  2513. if(fmtprint(&fmt,"}%s]", i->istring) < 0)
  2514. return TkNomem;
  2515. break;
  2516. case TkTnewline:
  2517. case TkTcontline:
  2518. r = i->kind == TkTnewline ? 'N' : 'C';
  2519. if(fmtprint(&fmt, "[%c]", r) < 0)
  2520. return TkNomem;
  2521. break;
  2522. case TkTtab:
  2523. if(fmtprint(&fmt,"[T%d]",i->width) < 0)
  2524. return TkNomem;
  2525. break;
  2526. case TkTwin:
  2527. win = "<null>";
  2528. if(i->iwin->sub != nil)
  2529. win = i->iwin->sub->name->name;
  2530. if(fmtprint(&fmt,"[W%d %s]",i->width, win) < 0)
  2531. return TkNomem;
  2532. break;
  2533. }
  2534. if(fmtprint(&fmt, " ") < 0)
  2535. return TkNomem;
  2536. }
  2537. if(fmtprint(&fmt, "\n") < 0)
  2538. return TkNomem;
  2539. *val = fmtstrflush(&fmt);
  2540. if(*val == nil)
  2541. return TkNomem;
  2542. }
  2543. }
  2544. else
  2545. return tktget(tkt, &ix1, &ix2, tkdump.sgml, val);
  2546. return nil;
  2547. }
  2548. static char*
  2549. tktextget(Tk *tk, char *arg, char **val)
  2550. {
  2551. char *e;
  2552. TkTindex ix1, ix2;
  2553. TkText *tkt = TKobj(TkText, tk);
  2554. e = tktindparse(tk, &arg, &ix1);
  2555. if(e != nil)
  2556. return e;
  2557. if(*arg != '\0') {
  2558. e = tktindparse(tk, &arg, &ix2);
  2559. if(e != nil)
  2560. return e;
  2561. }
  2562. else {
  2563. ix2 = ix1;
  2564. tktadjustind(tkt, TkTbychar, &ix2);
  2565. }
  2566. return tktget(tkt, &ix1, &ix2, 0, val);
  2567. }
  2568. static char*
  2569. tktextindex(Tk *tk, char *arg, char **val)
  2570. {
  2571. char *e;
  2572. TkTindex ix;
  2573. TkText *tkt = TKobj(TkText, tk);
  2574. e = tktindparse(tk, &arg, &ix);
  2575. if(e != nil)
  2576. return e;
  2577. return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix), tktlinepos(tkt, &ix));
  2578. }
  2579. static char*
  2580. tktextinsert(Tk *tk, char *arg, char **val)
  2581. {
  2582. int n;
  2583. char *e, *p, *pe;
  2584. TkTindex ins, pins;
  2585. TkTtaginfo *ti;
  2586. TkText *tkt;
  2587. TkTline *lmin;
  2588. TkTop *top;
  2589. TkTitem *tagit;
  2590. char *tbuf, *buf;
  2591. USED(val);
  2592. tkt = TKobj(TkText, tk);
  2593. top = tk->env->top;
  2594. e = tktindparse(tk, &arg, &ins);
  2595. if(e != nil)
  2596. return e;
  2597. if(ins.item->kind == TkTmark) {
  2598. if(ins.item->imark->gravity == Tkleft) {
  2599. while(ins.item->kind == TkTmark && ins.item->imark->gravity == Tkleft)
  2600. if(!tktadjustind(tkt, TkTbyitem, &ins)) {
  2601. if(tktdbg)
  2602. print("tktextinsert botch\n");
  2603. break;
  2604. }
  2605. }
  2606. else {
  2607. for(;;) {
  2608. pins = ins;
  2609. if(!tktadjustind(tkt, TkTbyitemback, &pins))
  2610. break;
  2611. if(pins.item->kind == TkTmark && pins.item->imark->gravity == Tkright)
  2612. ins = pins;
  2613. else
  2614. break;
  2615. }
  2616. }
  2617. }
  2618. lmin = tktprevwrapline(tk, ins.line);
  2619. n = strlen(arg) + 1;
  2620. if(n < Tkmaxitem)
  2621. n = Tkmaxitem;
  2622. tbuf = malloc(n);
  2623. if(tbuf == nil)
  2624. return TkNomem;
  2625. buf = mallocz(Tkmaxitem, 0);
  2626. if(buf == nil) {
  2627. free(tbuf);
  2628. return TkNomem;
  2629. }
  2630. tagit = nil;
  2631. while(*arg != '\0') {
  2632. arg = tkword(top, arg, tbuf, tbuf+n, nil);
  2633. if(*arg != '\0') {
  2634. /* tag list spec -- add some slop to tagextra for added tags */
  2635. e = tktnewitem(TkTascii, (tkt->nexttag-1)/32 + 1, &tagit);
  2636. if(e != nil) {
  2637. free(tbuf);
  2638. free(buf);
  2639. return e;
  2640. }
  2641. arg = tkword(top, arg, buf, buf+Tkmaxitem, nil);
  2642. p = buf;
  2643. while(*p) {
  2644. while(*p == ' ') {
  2645. p++;
  2646. }
  2647. if(*p == '\0')
  2648. break;
  2649. pe = strchr(p, ' ');
  2650. if(pe != nil)
  2651. *pe = '\0';
  2652. ti = tktfindtag(tkt->tags, p);
  2653. if(ti == nil) {
  2654. e = tktaddtaginfo(tk, p, &ti);
  2655. if(e != nil) {
  2656. if(tagit != nil)
  2657. free(tagit);
  2658. free(tbuf);
  2659. free(buf);
  2660. return e;
  2661. }
  2662. }
  2663. tkttagbit(tagit, ti->id, 1);
  2664. if(pe == nil)
  2665. break;
  2666. else
  2667. p = pe+1;
  2668. }
  2669. }
  2670. e = tktinsert(tk, &ins, tbuf, tagit);
  2671. if(tagit != nil) {
  2672. free(tagit);
  2673. tagit = nil;
  2674. }
  2675. if(e != nil) {
  2676. free(tbuf);
  2677. free(buf);
  2678. return e;
  2679. }
  2680. }
  2681. tktfixgeom(tk, lmin, ins.line, 0);
  2682. tktextsize(tk, 1);
  2683. free(tbuf);
  2684. free(buf);
  2685. return nil;
  2686. }
  2687. static char*
  2688. tktextinserti(Tk *tk, char *arg, char **val)
  2689. {
  2690. int n;
  2691. TkTline *lmin;
  2692. TkTindex ix, is1, is2;
  2693. TkText *tkt = TKobj(TkText, tk);
  2694. char *tbuf, *buf;
  2695. USED(val);
  2696. if(tk->flag&Tkdisabled)
  2697. return nil;
  2698. buf = mallocz(Tkmaxitem, 0);
  2699. if(buf == nil)
  2700. return TkNomem;
  2701. tbuf = nil;
  2702. n = strlen(arg) + 1;
  2703. if(n < Tkmaxitem)
  2704. tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
  2705. else {
  2706. tbuf = malloc(n);
  2707. if(tbuf == nil) {
  2708. free(buf);
  2709. return TkNomem;
  2710. }
  2711. tkword(tk->env->top, arg, tbuf, buf+n, nil);
  2712. }
  2713. if(*buf == '\0')
  2714. goto Ret;
  2715. if(!tktmarkind(tk, "insert", &ix)) {
  2716. print("tktextinserti: botch\n");
  2717. goto Ret;
  2718. }
  2719. if(tktgetsel(tk, &is1, &is2)) {
  2720. if(tktindcompare(tkt, &is1, TkLte, &ix) &&
  2721. tktindcompare(tkt, &is2, TkGte, &ix)) {
  2722. tktextdelete(tk, "sel.first sel.last", nil);
  2723. /* delete might have changed ix item */
  2724. tktmarkind(tk, "insert", &ix);
  2725. }
  2726. }
  2727. lmin = tktprevwrapline(tk, ix.line);
  2728. tktinsert(tk, &ix, tbuf==nil ? buf : tbuf, 0);
  2729. tktfixgeom(tk, lmin, ix.line, 0);
  2730. if(tktmarkind(tk, "insert", &ix)) /* index doesn't remain valid after fixgeom */
  2731. tktsee(tk, &ix, 0);
  2732. tktextsize(tk, 1);
  2733. Ret:
  2734. if(tbuf != nil)
  2735. free(tbuf);
  2736. free(buf);
  2737. return nil;
  2738. }
  2739. static char*
  2740. tktextmark(Tk *tk, char *arg, char **val)
  2741. {
  2742. char *buf;
  2743. TkCmdtab *cmd;
  2744. buf = mallocz(Tkmaxitem, 0);
  2745. if(buf == nil)
  2746. return TkNomem;
  2747. arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
  2748. for(cmd = tktmarkcmd; cmd->name != nil; cmd++) {
  2749. if(strcmp(cmd->name, buf) == 0) {
  2750. free(buf);
  2751. return cmd->fn(tk, arg, val);
  2752. }
  2753. }
  2754. free(buf);
  2755. return TkBadcm;
  2756. }
  2757. static char*
  2758. tktextscan(Tk *tk, char *arg, char **val)
  2759. {
  2760. char *e;
  2761. int mark, x, y, xmax, ymax, vh, vw;
  2762. Point p, odeltatv;
  2763. char buf[Tkmaxitem];
  2764. TkText *tkt = TKobj(TkText, tk);
  2765. USED(val);
  2766. arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
  2767. if(strcmp(buf, "mark") == 0)
  2768. mark = 1;
  2769. else
  2770. if(strcmp(buf, "dragto") == 0)
  2771. mark = 0;
  2772. else
  2773. return TkBadcm;
  2774. e = tkxyparse(tk, &arg, &p);
  2775. if(e != nil)
  2776. return e;
  2777. if(mark)
  2778. tkt->track = p;
  2779. else {
  2780. odeltatv = tkt->deltatv;
  2781. vw = tk->act.width - tk->ipad.x;
  2782. vh = tk->act.height - tk->ipad.y;
  2783. ymax = tkt->end.prev->orig.y + tkt->end.prev->height - vh;
  2784. y = tkt->deltatv.y -10*(p.y - tkt->track.y);
  2785. if(y > ymax)
  2786. y = ymax;
  2787. if(y < 0)
  2788. y = 0;
  2789. tkt->deltatv.y = y;
  2790. e = tktsetscroll(tk, Tkvertical);
  2791. if(e != nil)
  2792. return e;
  2793. if(tkt->opts[TkTwrap] == Tkwrapnone) {
  2794. xmax = tktmaxwid(tkt->start.next) - vw;
  2795. x = tkt->deltatv.x - 10*(p.x - tkt->track.x);
  2796. if(x > xmax)
  2797. x = xmax;
  2798. if(x < 0)
  2799. x = 0;
  2800. tkt->deltatv.x = x;
  2801. e = tktsetscroll(tk, Tkhorizontal);
  2802. if(e != nil)
  2803. return e;
  2804. }
  2805. tktfixscroll(tk, odeltatv);
  2806. tkt->track = p;
  2807. }
  2808. return nil;
  2809. }
  2810. static char*
  2811. tktextscrollpages(Tk *tk, char *arg, char **val)
  2812. {
  2813. TkText *tkt = TKobj(TkText, tk);
  2814. USED(tkt);
  2815. USED(arg);
  2816. USED(val);
  2817. return nil;
  2818. }
  2819. static char*
  2820. tktextsearch(Tk *tk, char *arg, char **val)
  2821. {
  2822. int i, n;
  2823. Rune r;
  2824. char *e, *s;
  2825. int wrap, fwd, nocase;
  2826. TkText *tkt;
  2827. TkTindex ix1, ix2, ixstart, ixend, tx;
  2828. char buf[Tkmaxitem];
  2829. tkt = TKobj(TkText, tk);
  2830. fwd = 1;
  2831. nocase = 0;
  2832. while(*arg != '\0') {
  2833. arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
  2834. if(*buf != '-')
  2835. break;
  2836. if(strcmp(buf, "-backwards") == 0)
  2837. fwd = 0;
  2838. else if(strcmp(buf, "-nocase") == 0)
  2839. nocase = 1;
  2840. else if(strcmp(buf, "--") == 0) {
  2841. arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
  2842. break;
  2843. }
  2844. }
  2845. tktstartind(tkt, &ixstart);
  2846. tktadjustind(tkt, TkTbycharstart, &ixstart);
  2847. tktendind(tkt, &ixend);
  2848. if(*arg == '\0')
  2849. return TkOparg;
  2850. e = tktindparse(tk, &arg, &ix1);
  2851. if(e != nil)
  2852. return e;
  2853. tktadjustind(tkt, fwd? TkTbycharstart : TkTbycharback, &ix1);
  2854. if(*arg != '\0') {
  2855. wrap = 0;
  2856. e = tktindparse(tk, &arg, &ix2);
  2857. if(e != nil)
  2858. return e;
  2859. if(!fwd)
  2860. tktadjustind(tkt, TkTbycharback, &ix2);
  2861. }
  2862. else {
  2863. wrap = 1;
  2864. if(fwd) {
  2865. if(tktindcompare(tkt, &ix1, TkEq, &ixstart))
  2866. ix2 = ixend;
  2867. else {
  2868. ix2 = ix1;
  2869. tktadjustind(tkt, TkTbycharback, &ix2);
  2870. }
  2871. }
  2872. else {
  2873. if(tktindcompare(tkt, &ix1, TkEq, &ixend))
  2874. ix2 = ixstart;
  2875. else {
  2876. ix2 = ix1;
  2877. tktadjustind(tkt, TkTbychar, &ix2);
  2878. }
  2879. }
  2880. }
  2881. tktadjustind(tkt, TkTbycharstart, &ix2);
  2882. if(tktindcompare(tkt, &ix1, TkEq, &ix2))
  2883. return nil;
  2884. if(*buf == '\0')
  2885. return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix1), tktlinepos(tkt, &ix1));
  2886. while(!(ix1.item == ix2.item && ix1.pos == ix2.pos)) {
  2887. tx = ix1;
  2888. for(i = 0; buf[i] != '\0'; i++) {
  2889. switch(tx.item->kind) {
  2890. case TkTascii:
  2891. if(!tktcmatch(tx.item->istring[tx.pos], buf[i], nocase))
  2892. goto nomatch;
  2893. break;
  2894. case TkTrune:
  2895. s = tx.item->istring;
  2896. s += tktutfpos(s, tx.pos);
  2897. n = chartorune(&r, s);
  2898. if(strncmp(s, buf+i, n) != 0)
  2899. goto nomatch;
  2900. i += n-1;
  2901. break;
  2902. case TkTtab:
  2903. if(buf[i] != '\t')
  2904. goto nomatch;
  2905. break;
  2906. case TkTnewline:
  2907. if(buf[i] != '\n')
  2908. goto nomatch;
  2909. break;
  2910. default:
  2911. goto nomatch;
  2912. }
  2913. tktadjustind(tkt, TkTbychar, &tx);
  2914. }
  2915. return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix1), tktlinepos(tkt, &ix1));
  2916. nomatch:
  2917. if(fwd) {
  2918. if(!tktadjustind(tkt, TkTbychar, &ix1)) {
  2919. if(!wrap)
  2920. break;
  2921. ix1 = ixstart;
  2922. }
  2923. }
  2924. else {
  2925. if(!tktadjustind(tkt, TkTbycharback, &ix1)) {
  2926. if(!wrap)
  2927. break;
  2928. ix1 = ixend;
  2929. }
  2930. }
  2931. }
  2932. return nil;
  2933. }
  2934. char*
  2935. tktextselection(Tk *tk, char *arg, char **val)
  2936. {
  2937. USED(val);
  2938. if (strcmp(arg, " clear") == 0) {
  2939. tktclearsel(tk);
  2940. return nil;
  2941. }
  2942. else
  2943. return TkBadcm;
  2944. }
  2945. static void
  2946. doselectto(Tk *tk, Point p, int dbl)
  2947. {
  2948. int halfway;
  2949. TkTindex cur, insert, first, last;
  2950. TkText *tkt = TKobj(TkText, tk);
  2951. tktclearsel(tk);
  2952. halfway = tktxyind(tk, p.x, p.y, &cur);
  2953. if(!dbl) {
  2954. if(!tktmarkind(tk, "insert", &insert))
  2955. insert = cur;
  2956. if(tktindcompare(tkt, &cur, TkLt, &insert)) {
  2957. first = cur;
  2958. last = insert;
  2959. }
  2960. else {
  2961. first = insert;
  2962. last = cur;
  2963. if(halfway)
  2964. tktadjustind(tkt, TkTbychar, &last);
  2965. if(last.line == &tkt->end)
  2966. tktadjustind(tkt, TkTbycharback, &last);
  2967. if(tktindcompare(tkt, &first, TkGte, &last))
  2968. return;
  2969. cur = last;
  2970. }
  2971. tktsee(tk, &cur, 0);
  2972. }
  2973. else {
  2974. first = cur;
  2975. last = cur;
  2976. tktdoubleclick(tkt, &first, &last);
  2977. }
  2978. tkttagchange(tk, TkTselid, &first, &last, 1);
  2979. }
  2980. static void
  2981. autoselect(Tk *tk, void *v, int cancelled)
  2982. {
  2983. TkText *tkt = TKobj(TkText, tk);
  2984. Rectangle hitr;
  2985. Point p;
  2986. USED(v);
  2987. if (cancelled)
  2988. return;
  2989. p = scr2local(tk, tkt->track);
  2990. if (tkvisiblerect(tk, &hitr) && ptinrect(p, hitr))
  2991. return;
  2992. doselectto(tk, p, 0);
  2993. tkdirty(tk);
  2994. tkupdate(tk->env->top);
  2995. }
  2996. static char*
  2997. tktextselectto(Tk *tk, char *arg, char **val)
  2998. {
  2999. int dbl;
  3000. char *e;
  3001. Point p;
  3002. Rectangle hitr;
  3003. TkText *tkt = TKobj(TkText, tk);
  3004. USED(val);
  3005. if(tkt->tflag & (TkTjustfoc|TkTnodrag))
  3006. return nil;
  3007. e = tkxyparse(tk, &arg, &p);
  3008. if(e != nil)
  3009. return e;
  3010. tkt->track = p;
  3011. p = scr2local(tk, p);
  3012. arg = tkskip(arg, " ");
  3013. if(*arg == 'd') {
  3014. tkcancelrepeat(tk);
  3015. dbl = 1;
  3016. tkt->tflag |= TkTnodrag;
  3017. } else {
  3018. dbl = 0;
  3019. if (!tkvisiblerect(tk, &hitr) || !ptinrect(p, hitr))
  3020. return nil;
  3021. }
  3022. doselectto(tk, p, dbl);
  3023. return nil;
  3024. }
  3025. static char tktleft1[] = "{[(<";
  3026. static char tktright1[] = "}])>";
  3027. static char tktleft2[] = "\n";
  3028. static char tktleft3[] = "\'\"`";
  3029. static char *tktleft[] = {tktleft1, tktleft2, tktleft3, nil};
  3030. static char *tktright[] = {tktright1, tktleft2, tktleft3, nil};
  3031. static void
  3032. tktdoubleclick(TkText *tkt, TkTindex *first, TkTindex *last)
  3033. {
  3034. int c, i;
  3035. TkTindex ix, ix2;
  3036. char *r, *l, *p;
  3037. for(i = 0; tktleft[i] != nil; i++) {
  3038. ix = *first;
  3039. l = tktleft[i];
  3040. r = tktright[i];
  3041. /* try matching character to left, looking right */
  3042. ix2 = ix;
  3043. if(!tktadjustind(tkt, TkTbycharback, &ix2))
  3044. c = '\n';
  3045. else
  3046. c = tktindrune(&ix2);
  3047. p = strchr(l, c);
  3048. if(p != nil) {
  3049. if(tktclickmatch(tkt, c, r[p-l], 1, &ix)) {
  3050. *last = ix;
  3051. if(c != '\n')
  3052. tktadjustind(tkt, TkTbycharback, last);
  3053. }
  3054. return;
  3055. }
  3056. /* try matching character to right, looking left */
  3057. c = tktindrune(&ix);
  3058. p = strchr(r, c);
  3059. if(p != nil) {
  3060. if(tktclickmatch(tkt, c, l[p-r], -1, &ix)) {
  3061. *last = *first;
  3062. if(c == '\n')
  3063. tktadjustind(tkt, TkTbychar, last);
  3064. *first = ix;
  3065. if(!(c=='\n' && ix.line == tkt->start.next && ix.item == ix.line->items))
  3066. tktadjustind(tkt, TkTbychar, first);
  3067. }
  3068. return;
  3069. }
  3070. }
  3071. /* try filling out word to right */
  3072. while(tkiswordchar(tktindrune(last))) {
  3073. if(!tktadjustind(tkt, TkTbychar, last))
  3074. break;
  3075. }
  3076. /* try filling out word to left */
  3077. for(;;) {
  3078. ix = *first;
  3079. if(!tktadjustind(tkt, TkTbycharback, &ix))
  3080. break;
  3081. if(!tkiswordchar(tktindrune(&ix)))
  3082. break;
  3083. *first = ix;
  3084. }
  3085. }
  3086. static int
  3087. tktclickmatch(TkText *tkt, int cl, int cr, int dir, TkTindex *ix)
  3088. {
  3089. int c, nest, atend;
  3090. nest = 1;
  3091. atend = 0;
  3092. for(;;) {
  3093. if(dir > 0) {
  3094. if(atend)
  3095. break;
  3096. c = tktindrune(ix);
  3097. atend = !tktadjustind(tkt, TkTbychar, ix);
  3098. } else {
  3099. if(!tktadjustind(tkt, TkTbycharback, ix))
  3100. break;
  3101. c = tktindrune(ix);
  3102. }
  3103. if(c == cr){
  3104. if(--nest==0)
  3105. return 1;
  3106. }else if(c == cl)
  3107. nest++;
  3108. }
  3109. return cl=='\n' && nest==1;
  3110. }
  3111. /*
  3112. * return the line before line l, unless word wrap is on,
  3113. * (for the first word of line l), in which case return the last non-empty line before that.
  3114. * tktgeom might then combine the end of that line with the start of the insertion
  3115. * (unless there is a newline in the way).
  3116. */
  3117. TkTline*
  3118. tktprevwrapline(Tk *tk, TkTline *l)
  3119. {
  3120. TkTitem *i;
  3121. int *opts, wrapmode;
  3122. TkText *tkt = TKobj(TkText, tk);
  3123. TkEnv env;
  3124. if(l == nil)
  3125. return nil;
  3126. /* some spacing depends on tags of first non-mark on display line */
  3127. for(i = l->items; i != nil; i = i->next)
  3128. if(i->kind != TkTmark && i->kind != TkTcontline)
  3129. break;
  3130. if(i == nil || i->kind == TkTnewline) /* can't use !tkanytags(i) because it doesn't check env */
  3131. return l->prev;
  3132. opts = mallocz(TkTnumopts*sizeof(int), 0);
  3133. if(opts == nil)
  3134. return l->prev; /* in worst case gets word wrap wrong */
  3135. tkttagopts(tk, i, opts, &env, nil, 1);
  3136. wrapmode = opts[TkTwrap];
  3137. free(opts);
  3138. if(wrapmode != Tkwrapword)
  3139. return l->prev;
  3140. if(l->prev != &tkt->start)
  3141. l = l->prev; /* having been processed by tktgeom, shouldn't have extraneous marks etc */
  3142. return l->prev;
  3143. }
  3144. static char*
  3145. tktextsetcursor(Tk *tk, char *arg, char **val)
  3146. {
  3147. char *e;
  3148. TkTindex ix;
  3149. TkTmarkinfo *mi;
  3150. TkText *tkt = TKobj(TkText, tk);
  3151. USED(val);
  3152. /* do clearsel here, because it can change indices */
  3153. tktclearsel(tk);
  3154. e = tktindparse(tk, &arg, &ix);
  3155. if(e != nil)
  3156. return e;
  3157. mi = tktfindmark(tkt->marks, "insert");
  3158. if(tktdbg && mi == nil) {
  3159. print("tktextsetcursor: botch\n");
  3160. return nil;
  3161. }
  3162. tktmarkmove(tk, mi, &ix);
  3163. tktsee(tk, &ix, 0);
  3164. return nil;
  3165. }
  3166. static char*
  3167. tktexttag(Tk *tk, char *arg, char **val)
  3168. {
  3169. char *buf;
  3170. TkCmdtab *cmd;
  3171. buf = mallocz(Tkmaxitem, 0);
  3172. if(buf == nil)
  3173. return TkNomem;
  3174. arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
  3175. for(cmd = tkttagcmd; cmd->name != nil; cmd++) {
  3176. if(strcmp(cmd->name, buf) == 0) {
  3177. free(buf);
  3178. return cmd->fn(tk, arg, val);
  3179. }
  3180. }
  3181. free(buf);
  3182. return TkBadcm;
  3183. }
  3184. static char*
  3185. tktextwindow(Tk *tk, char *arg, char **val)
  3186. {
  3187. char buf[Tkmaxitem];
  3188. TkCmdtab *cmd;
  3189. arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
  3190. for(cmd = tktwincmd; cmd->name != nil; cmd++) {
  3191. if(strcmp(cmd->name, buf) == 0)
  3192. return cmd->fn(tk, arg, val);
  3193. }
  3194. return TkBadcm;
  3195. }
  3196. static char*
  3197. tktextxview(Tk *tk, char *arg, char **val)
  3198. {
  3199. int ntot, vw;
  3200. char *e;
  3201. Point odeltatv;
  3202. TkText *tkt = TKobj(TkText, tk);
  3203. odeltatv = tkt->deltatv;
  3204. vw = tk->act.width - tk->ipad.x;
  3205. ntot = tktmaxwid(tkt->start.next);
  3206. if(ntot < tkt->deltatv.x +vw)
  3207. ntot = tkt->deltatv.x + vw;
  3208. e = tktview(tk, arg, val, vw, &tkt->deltatv.x, ntot, Tkhorizontal);
  3209. if(e == nil) {
  3210. e = tktsetscroll(tk, Tkhorizontal);
  3211. if(e == nil)
  3212. tktfixscroll(tk, odeltatv);
  3213. }
  3214. return e;
  3215. }
  3216. static int
  3217. istext(TkTline *l)
  3218. {
  3219. TkTitem *i;
  3220. for(i = l->items; i != nil; i = i->next)
  3221. if(i->kind == TkTwin || i->kind == TkTmark)
  3222. return 0;
  3223. return 1;
  3224. }
  3225. static void
  3226. tkadjpage(Tk *tk, int ody, int *dy)
  3227. {
  3228. int y, a, b, d;
  3229. TkTindex ix;
  3230. TkTline *l;
  3231. d = *dy-ody;
  3232. y = d > 0 ? tk->act.height : 0;
  3233. tktxyind(tk, 0, y-d, &ix);
  3234. if((l = ix.line) != nil && istext(l)){
  3235. a = l->orig.y;
  3236. b = a+l->height;
  3237. /* print("AP: %d %d %d (%d+%d)\n", a, ody+y, b, ody, y); */
  3238. if(a+2 < ody+y && ody+y < b-2){ /* partially obscured line */
  3239. if(d > 0)
  3240. *dy -= ody+y-a;
  3241. else
  3242. *dy += b-ody;
  3243. }
  3244. }
  3245. }
  3246. static char*
  3247. tktextyview(Tk *tk, char *arg, char **val)
  3248. {
  3249. int ntot, vh, d;
  3250. char *e;
  3251. TkTline *l;
  3252. Point odeltatv;
  3253. TkTindex ix;
  3254. TkText *tkt = TKobj(TkText, tk);
  3255. char buf[Tkmaxitem], *v;
  3256. if(*arg != '\0') {
  3257. v = tkitem(buf, arg);
  3258. if(strcmp(buf, "-pickplace") == 0)
  3259. return tktextsee(tk,v, val);
  3260. if(strcmp(buf, "moveto") != 0 && strcmp(buf, "scroll") != 0) {
  3261. e = tktindparse(tk, &arg, &ix);
  3262. if(e != nil)
  3263. return e;
  3264. tktsee(tk, &ix, 1);
  3265. return nil;
  3266. }
  3267. }
  3268. odeltatv = tkt->deltatv;
  3269. vh = tk->act.height;
  3270. l = tkt->end.prev;
  3271. ntot = l->orig.y + l->height;
  3272. // if(ntot < tkt->deltatv.y + vh)
  3273. // ntot = tkt->deltatv.y + vh;
  3274. e = tktview(tk, arg, val, vh, &tkt->deltatv.y, ntot, Tkvertical);
  3275. d = tkt->deltatv.y-odeltatv.y;
  3276. if(d == vh || d == -vh)
  3277. tkadjpage(tk, odeltatv.y, &tkt->deltatv.y);
  3278. if(e == nil) {
  3279. e = tktsetscroll(tk, Tkvertical);
  3280. if(e == nil)
  3281. tktfixscroll(tk, odeltatv);
  3282. }
  3283. return e;
  3284. }
  3285. static void
  3286. tktextfocusorder(Tk *tk)
  3287. {
  3288. TkTindex ix;
  3289. TkText *t;
  3290. Tk *isub;
  3291. t = TKobj(TkText, tk);
  3292. tktstartind(t, &ix);
  3293. do {
  3294. if(ix.item->kind == TkTwin) {
  3295. isub = ix.item->iwin->sub;
  3296. if(isub != nil)
  3297. tkappendfocusorder(isub);
  3298. }
  3299. } while(tktadjustind(t, TkTbyitem, &ix));
  3300. }
  3301. TkCmdtab tktextcmd[] =
  3302. {
  3303. "bbox", tktextbbox,
  3304. "cget", tktextcget,
  3305. "compare", tktextcompare,
  3306. "configure", tktextconfigure,
  3307. "debug", tktextdebug,
  3308. "delete", tktextdelete,
  3309. "dlineinfo", tktextdlineinfo,
  3310. "dump", tktextdump,
  3311. "get", tktextget,
  3312. "index", tktextindex,
  3313. "insert", tktextinsert,
  3314. "mark", tktextmark,
  3315. "scan", tktextscan,
  3316. "search", tktextsearch,
  3317. "see", tktextsee,
  3318. "selection", tktextselection,
  3319. "tag", tktexttag,
  3320. "window", tktextwindow,
  3321. "xview", tktextxview,
  3322. "yview", tktextyview,
  3323. "tkTextButton1", tktextbutton1,
  3324. "tkTextButton1R", tktextbutton1r,
  3325. "tkTextDelIns", tktextdelins,
  3326. "tkTextInsert", tktextinserti,
  3327. "tkTextSelectTo", tktextselectto,
  3328. "tkTextSetCursor", tktextsetcursor,
  3329. "tkTextScrollPages", tktextscrollpages,
  3330. "tkTextCursor", tktextcursor,
  3331. nil
  3332. };
  3333. TkMethod textmethod = {
  3334. "text",
  3335. tktextcmd,
  3336. tkfreetext,
  3337. tkdrawtext,
  3338. tktextgeom,
  3339. nil,
  3340. tktextfocusorder,
  3341. tktdirty,
  3342. tktrelpos,
  3343. tktextevent,
  3344. nil, /* XXX need to implement textsee */
  3345. tktinwindow,
  3346. nil,
  3347. tktxtforgetsub,
  3348. };