fixed lots of compile bugs in wtext.c and made a few api changes to it
[wmaker-crm.git] / WINGs / wtext.c
blob421779888211b5663a3883bc0a6d222cce87ae71
1 /* WMText: multi-line/font/color text widget for WINGs */
2 /* Copyleft (>) 1999, 2000 Nwanua Elumeze <nwanua@colorado.edu> */
5 /* .( * .
6 .* . ) .
8 . . POOF .* .
9 '* . ( .) '
10 jgs ` ( . * */
13 /* if monoFont, ignore pixmaps, colors, fonts, script, underline */
16 #include <WMaker.h>
17 #include <WINGsP.h>
18 #include <X11/keysym.h>
19 #include <X11/Xatom.h>
20 #include <ctype.h>
23 typedef enum {
24 ctText=0,
25 ctImage=1
26 } ChunkType;
28 typedef enum {
29 dtDelete=0,
30 dtBackSpace
31 } DeleteType;
33 typedef enum {
34 wrWord=0,
35 wrChar=1,
36 wrNone=2
37 } Wrapping;
40 void wgdbFree(void *ptr)
42 if(!ptr) printf("err... cannot ");
43 printf("gdbFree [%p]\n", ptr);
44 wfree(ptr);
48 /* Why single-linked and not say double-linked?
49 99% of the time (draw, append), the "prior"
50 member would have been a useless memory and CPU overhead,
51 and deletes _are_ relatively infrequent.
52 When the "prior" member needs to be used, the overhead of
53 doing things the hard way will be incurred... but seldomly. */
56 /* a Chunk is a single-linked list of chunks containing:
57 * o text with a given format
58 * o or an image
59 * o but NOT both
61 typedef struct _Chunk {
62 char *text; /* the text in the chunk */
63 WMPixmap *pixmap; /* OR the pixmap it holds */
64 short chars; /* the number of characters in this chunk */
65 short mallocedSize; /* the number of characters that can be held */
67 WMFont *font; /* the chunk's font */
68 WMColor *color; /* the chunk's color */
69 short ul:1; /* underlined or not */
70 ChunkType type:1; /* a "Text" or "Image" chunk */
71 short script:4; /* script in points: negative for subscript */
72 /* hrmm selec... */
73 ushort selected;
74 ushort sStart;
75 ushort sEnd;
76 ushort RESERVED:10;
77 struct _Chunk *next; /*the next member in this list */
79 } Chunk;
83 /* a Paragraph is a singly-linked list of paragraphs containing:
84 * o a list of chunks in that paragraph
85 * o the formats for that paragraph
86 * o its (draw) position relative to the entire document
88 typedef struct _Paragraph {
89 Chunk *chunks; /* the list of text and/or image chunks */
90 short fmargin; /* the start position of the first line */
91 short bmargin; /* the start positions of the rest of the lines */
92 short rmargin; /* the end position of the entire paragraph */
93 short numTabs; /* the number of tabstops */
94 short *tabstops; /* an array of tabstops */
96 Pixmap drawbuffer; /* the pixmap onto which the (entire)
97 paragraph will be drawn */
98 WMPixmap *bulletPix; /* the pixmap to use for bulleting */
99 int top; /* the top of the paragraph relative to document */
100 int bottom; /* the bottom of the paragraph relative to document */
101 int width; /* the width of the paragraph */
102 int height; /* the height of the paragraph */
103 WMAlignment align:2; /* justification of this paragraph */
104 ushort RESERVED:14;
106 struct _Paragraph *next; /* the next member in this list */
107 } Paragraph;
110 static char *default_bullet[] = {
111 "6 6 4 1",
112 " c None s None", ". c black",
113 "X c white", "o c #808080",
114 " ... ",
115 ".XX.. ",
116 ".XX..o",
117 ".....o",
118 " ...oo",
119 " ooo "
122 /* this is really a shrunk down version of the original
123 "broken" icon... I did not draw it, I simply shrunk it */
124 static char * unk_xpm[] = {
125 "24 24 17 1",
126 " c None", ". c #0B080C", "+ c #13A015", "@ c #5151B8",
127 "# c #992719", "$ c #5B1C20", "% c #1DF51D", "& c #D1500D", "* c #2F304A",
128 "= c #0C6A0C", "- c #F2F1DE", "; c #D59131", "> c #B2B083", ", c #DD731A",
129 "' c #CC3113", ") c #828238", "! c #6A6A94",
130 "......!@@@@@@@....$$....",
131 "...@!@@@@@@@**...$#'....",
132 "..!!@@@@@@@@.......#....",
133 "..!@@@@@@@@@*.......$...",
134 ".!@@@#,,#*@@*..*>.*.#...",
135 "*@@@@#'',,@@@...---!....",
136 "!@@@@@*.#;*@@..!--->....",
137 "@@@@@@@@#,.@@..!----@...",
138 "!@@@@@@*#;'$...!----@...",
139 "*@@@@@@..'&;;#.)----)...",
140 ".@@@@@@..$..&'.>----)...",
141 ".@@@@@@**---,'>-----!...",
142 ".@@@@@@**---,'>-----@...",
143 "..@@@@@@@---;;;,;---....",
144 "..*@@@@*@--->#',;,-*.)..",
145 "........)---->)@;#!..>..",
146 ".....)----------;$..>)..",
147 "=%%%*.*!-------);..)-*..",
148 "=%%%%+...*)>!@*$,.>--...",
149 "*+++++++.......*$@-->...",
150 "............**@)!)>->...",
151 "........................",
152 "........................",
153 "........................"
156 typedef struct W_Text {
157 W_Class widgetClass; /* the class number of this widget */
158 W_View *view; /* the view referring to this instance */
159 WMColor *bg; /* the background color to use when drawing */
161 WMRuler *ruler; /* the ruler subwiget to maipulate paragraphs */
163 WMScroller *hscroller; /* the horizontal scroller */
164 short hpos; /* the current horizontal position */
165 short prevHpos; /* the previous horizontal position */
167 WMScroller *vscroller; /* the vertical scroller */
168 int vpos; /* the current vertical position */
169 int prevVpos; /* the previous vertical position */
171 int visibleW; /* the actual horizontal space available */
172 int visibleH; /* the actual vertical space available */
174 Paragraph *paragraphs; /* the linked list of the paragraphs in the doc. */
175 int docWidth; /* the width of the entire document */
176 int docHeight; /* the height of the entire document */
178 WMFont *dFont; /* the default font */
179 WMColor *dColor; /* the default color */
180 WMPixmap *dBulletPix; /* the default pixmap for bullets */
181 WMPixmap *dUnknownImg; /* the pixmap for (missing/broken) images */
183 WMRect sRect; /* the selected area */
184 Paragraph *currentPara; /* the current paragraph, in which actions occur */
185 Chunk *currentChunk; /* the current chunk, about which actions occur */
186 short tpos; /* the cursor position (text position) */
187 WMParseAction *parser; /* what action to use to parse input text */
188 WMParseAction *writer; /* what action to use to write text */
189 WMParserActions funcs; /* the "things" that parsers/writers might do */
190 XPoint clicked; /* the position of the last mouse click */
191 XPoint cursor; /* where the cursor is "placed" */
192 short clheight; /* the height of the "line" clicked on */
193 short clwidth; /* the width of the "line" clicked on */
195 WMReliefType relief:3; /* the relief to display with */
196 Wrapping wrapping:2; /* the type of wrapping to use in drawing */
197 WMAlignment dAlignment:2; /* default justification */
198 ushort monoFont:1; /* whether to ignore "rich" commands */
199 ushort fixedPitch:1; /* assume each char in dFont is the same size */
200 ushort editable:1; /* whether to accept user changes or not*/
201 ushort rulerShown:1; /* whether the ruler is shown or not */
202 ushort cursorShown:1; /* whether the cursor is currently being shown */
203 ushort frozen:1; /* whether screen updates are to be made */
204 ushort focused:1; /* whether this instance has input focus */
205 ushort pointerGrabbed:1; /* whether this instance has the pointer */
206 ushort buttonHeld:1; /* the user is still holding down the button */
207 ushort ignoreNewLine:1; /* whether to ignore the newline character */
208 ushort waitingForSelection:1; /* whether there is a pending paste event */
209 ushort ownsSelection:1; /* whether it ownz the current selection */
210 ushort findingClickPoint:1; /* whether the search for a clickpoint is on */
211 ushort foundClickPoint:1; /* whether the clickpoint has been found */
212 ushort hasVscroller:1; /* whether to enable the vertical scroller */
213 ushort hasHscroller:1; /* whether to enable the horizontal scroller */
214 ushort RESERVED:10;
215 } Text;
219 /* --------- static functions that are "private". don't touch :-) --------- */
222 /* max "characters per chunk that will be drawn at a time" */
223 #define MAX_WORD_LENGTH 100
224 /* max on a line */
225 #define MAX_CHUNX 64
226 #define MIN_DOC_WIDTH 200
228 typedef struct _LocalMargins {
229 short left, right, first, body;
230 } LocalMargins;
232 typedef struct _MyTextItems {
233 char text[MAX_WORD_LENGTH+1];
234 WMPixmap *pix;
235 short chars;
236 short x;
237 Chunk *chunk; /* used for "click" events */
238 short start; /* ditto... where in the chunk we start (ie. wrapped chunk) */
239 ushort type:1;
240 ushort RESERVED:15;
241 } MyTextItems;
244 static WMRect
245 chunkSelectionRect(Text *tPtr, Paragraph *para, MyTextItems item,
246 short y, short j, short lh)
248 WMRect rect;
249 short type=0; /* 0:none 1:partial: 2:all */
250 short rh=(tPtr->rulerShown)?45:5;
251 short w, lm;
252 WMFont *font = (tPtr->monoFont || item.chunk->type != ctText)?
253 tPtr->dFont:item.chunk->font;
255 rect.pos.x = -23;
256 if(y+para->top+rh > tPtr->sRect.pos.y+tPtr->sRect.size.height
257 || y+para->top+rh+lh < tPtr->sRect.pos.y)
258 return rect;
260 if(item.chunk->type == ctText)
261 w = WMWidthOfString(font, item.text, item.chars);
262 else w = item.chunk->pixmap->width;
264 if(y+para->top+rh >= tPtr->sRect.pos.y && (y+para->top+rh+lh
265 <= tPtr->sRect.pos.y+tPtr->sRect.size.height))
266 //&& item.x+j >= tPtr->sRect.pos.x+tPtr->sRect.size.width))
267 type = 2;
268 else type = 1;
271 #if 0
272 if(item.x+j >= tPtr->sRect.pos.x &&
273 item.x+j+w < tPtr->sRect.pos.x+tPtr->sRect.size.width)
274 type = 2;
276 if(type == 1 && y+para->top+rh+lh <=
277 tPtr->sRect.pos.y+tPtr->sRect.size.height)
278 type = 2;
279 #endif
282 if(type == 1 && item.chunk->type == ctText) { /* partial coverage */
283 lm = 2+WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
284 /* even I am still confused, so don't ask please */
285 if( (item.x+j+lm >= tPtr->sRect.pos.x &&
286 item.x+j+lm <= tPtr->sRect.pos.x+tPtr->sRect.size.width)
287 || (item.x+j+lm >= tPtr->sRect.pos.x+tPtr->sRect.size.width
288 && y+para->top+rh+lh <=
289 tPtr->sRect.pos.y+tPtr->sRect.size.height)
290 || (tPtr->sRect.pos.y < y+para->top+rh
291 && tPtr->sRect.pos.x+tPtr->sRect.size.width >
292 item.x+j+lm) ){
293 rect.size.width = w;
294 rect.pos.x = item.x+j;
295 item.chunk->selected = True;
296 if(item.chunk->chars > 6) {
297 item.chunk->sStart = 3;
298 item.chunk->sEnd = item.chunk->chars;
299 } else {
300 item.chunk->sStart = 0;
301 item.chunk->sEnd = item.chunk->chars;
304 } else if(type == 2) {
305 rect.pos.x = item.x+j;
306 item.chunk->selected = True;
307 if(item.chunk->type == ctText) {
308 item.chunk->sStart = 0;
309 item.chunk->sStart = item.chunk->chars;
310 rect.size.width = WMWidthOfString(font,
311 item.text, item.chars);
312 } else {
313 rect.size.width = item.chunk->pixmap->width;
317 rect.pos.y = y;
318 rect.size.height = lh;
319 return rect;
322 static int
323 myDrawText(Text *tPtr, Paragraph *para, MyTextItems *items,
324 short nitems, short pwidth, int y, short draw, short spacepos)
326 short i, ul_thick, u, j=0; /* j = justification */
327 short line_width = 0, line_height=0, mx_descent=0;
328 WMScreen *screen = tPtr->view->screen;
329 GC gc;
330 WMFont *font;
332 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0;
333 for(i=0; i<nitems; i++) {
334 if(items[i].type == ctText) {
335 font = (tPtr->monoFont)?tPtr->dFont:items[i].chunk->font;
336 mx_descent = WMIN(mx_descent, -font->y);
337 line_height = WMAX(line_height, font->height);
338 //printf("chunk.x %d xpoint.x %d\n",
339 // items[i].x, tPtr->clicked.x);
341 line_width += WMWidthOfString(font,
342 items[i].text, items[i].chars);
343 } else {
344 mx_descent = WMIN(mx_descent, -(items[i].pix->height-3));
345 /* replace -3 wif descent... */
346 line_height = WMAX(line_height, items[i].pix->height);
347 if(para->align == WARight || para->align == WACenter) {
348 line_width += items[i].pix->width;
349 } } }
351 if(para->align == WARight) {
352 j = pwidth - line_width;
353 } else if (para->align == WACenter) {
354 j = (short) ((float)(pwidth - line_width))/2.0;
357 if(tPtr->findingClickPoint && (y+line_height >= tPtr->clicked.y)) {
358 tPtr->foundClickPoint = True;
359 tPtr->currentChunk = items[0].chunk; /* just first on this "line" */
360 tPtr->tpos = items[0].start; /* where to "start" counting from */
361 tPtr->clicked.x = j+items[0].x;
362 tPtr->clicked.y = y+line_height+mx_descent;
363 tPtr->clheight = line_height; /* to draw the cursor */
364 tPtr->clwidth = line_width; /* where to stop searching */
365 return 0;
366 } if(!draw) return line_height;
368 for(i=0; i<nitems; i++) {
370 //account for vpos
371 if(tPtr->ownsSelection) {
372 WMRect rect = chunkSelectionRect(tPtr, para,
373 items[i], y, j, line_height);
374 if(rect.pos.x != -23) { /* has been selected */
375 XFillRectangle(tPtr->view->screen->display, para->drawbuffer,
376 WMColorGC(WMGrayColor(tPtr->view->screen)),
377 rect.pos.x, rect.pos.y, rect.size.width, rect.size.height);
381 if(items[i].type == ctText) {
382 gc = WMColorGC(items[i].chunk->color);
383 font = (tPtr->monoFont)?tPtr->dFont:items[i].chunk->font;
384 WMDrawString(screen, para->drawbuffer, gc, font,
385 items[i].x+j, y - font->y - mx_descent,
386 items[i].text, items[i].chars);
387 if(items[i].chunk->ul && !tPtr->monoFont) {
388 ul_thick = (short) ((float)font->height)/12.0;
389 if (ul_thick < 1) ul_thick = 1;
390 for(u=0; u<ul_thick; u++) {
391 XDrawLine(screen->display, para->drawbuffer, gc, items[i].x+j,
392 y + 1 + u - mx_descent,
393 items[i].x + j + WMWidthOfString(font,
394 items[i].text, items[i].chars), y + 1 + u - mx_descent);
397 } } else {
398 WMDrawPixmap(items[i].pix, para->drawbuffer, items[i].x+j,
399 y + 3 - mx_descent - items[i].pix->height);
401 return line_height;
404 static void
405 drawPChunkPart(Text *tPtr, Chunk *chunk, LocalMargins m, Paragraph *para,
406 MyTextItems *items, short *nitems, short *Lmargin, XPoint *where, short draw)
408 short p_width;
410 if(!chunk) return;
411 if(!chunk->pixmap)
412 chunk->pixmap = WMRetainPixmap(tPtr->dUnknownImg);
414 p_width = m.right - WMIN(m.first, m.body) - WMGetRulerOffset(tPtr->ruler);
415 if(p_width < MIN_DOC_WIDTH) // need WMRuler to take care of this...
416 return;
417 if(where->x + chunk->pixmap->width <= p_width - *Lmargin) {
418 /* it can fit on rest of line */
419 items[*nitems].pix = chunk->pixmap;
420 items[*nitems].type = ctImage;
421 items[*nitems].chars = 0;
422 items[*nitems].x = *Lmargin+where->x;
423 items[*nitems].chunk = chunk;
424 items[*nitems].start = 0;
426 if(*nitems >= MAX_CHUNX) {
427 items[*nitems].chars = 0;
428 items[*nitems].x = *Lmargin+where->x;
429 items[*nitems].chunk = chunk;
430 items[*nitems].start = 0;
431 where->y += myDrawText(tPtr, para, items, *nitems+1,
432 p_width-*Lmargin, where->y, draw, 0);
433 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
434 *nitems = 0;
435 where->x = 0;
436 } else {
437 (*nitems)++;
438 where->x += chunk->pixmap->width;
440 } else if(chunk->pixmap->width <= p_width - *Lmargin) {
441 /* it can fit on an entire line, flush the myDrawText then wrap it */
442 where->y += myDrawText(tPtr, para, items, *nitems+1,
443 p_width-*Lmargin, where->y, draw, 0);
444 *nitems = 0;
445 *Lmargin = WMAX(0, m.body - m.first);
446 where->x = 0;
447 drawPChunkPart(tPtr, chunk, m, para, items, nitems,
448 Lmargin, where, draw);
449 } else {
450 #if 1
451 *nitems = 0;
452 where->x = 0;
453 *Lmargin = WMAX(0, m.body - m.first);
454 items[*nitems].pix = chunk->pixmap;
455 items[*nitems].type = ctImage;
456 items[*nitems].chars = 0;
457 items[*nitems].x = *Lmargin+where->x;
458 items[*nitems].chunk = chunk;
459 items[*nitems].start = 0;
460 where->y += myDrawText(tPtr, para, items, *nitems+1,
461 p_width-*Lmargin, where->y, draw, 0);
462 #endif
464 /* scale image to fit, call self again */
465 /* deprecated - the management */
470 static void
471 drawTChunkPart(Text *tPtr, Chunk *chunk, char *bufr, LocalMargins m,
472 Paragraph *para, MyTextItems *items, short *nitems, short len, short start,
473 short *Lmargin, XPoint *where, short draw, short spacepos)
475 short t_chunk_width, p_width, chars;
476 WMFont *font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
477 /* if(doc->clickstart.yes && doc->clickstart.done) return; */
479 if(len==0) return;
480 p_width = m.right - WMIN(m.first, m.body);
481 if(p_width < MIN_DOC_WIDTH) // need WMRuler to take care of this...
482 return;
485 t_chunk_width = WMWidthOfString(font, bufr, len);
486 if((where->x + t_chunk_width <= p_width - *Lmargin)
487 || (tPtr->wrapping == wrNone)) {
488 /* if it can fit on rest of line, append to line */
489 chars = WMIN(len, MAX_WORD_LENGTH);
490 snprintf(items[*nitems].text, chars+1, "%s", bufr);
491 items[*nitems].chars = chars;
492 items[*nitems].x = *Lmargin+where->x;
493 items[*nitems].type = ctText;
494 items[*nitems].chunk = chunk;
495 items[*nitems].start = start;
497 if(*nitems >= MAX_CHUNX) {
498 chars = WMIN(len, MAX_WORD_LENGTH);
499 snprintf(items[*nitems].text, chars+1, "%s", bufr);
500 items[*nitems].chars = chars;
501 items[*nitems].x = *Lmargin+where->x;
502 items[*nitems].type = ctText;
503 items[*nitems].chunk = chunk;
504 items[*nitems].start = start;
505 where->y += myDrawText(tPtr, para, items, *nitems+1,
506 p_width-*Lmargin, where->y, draw, spacepos);
507 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
508 *nitems = 0;
509 where->x = 0;
510 } else {
511 (*nitems)++;
512 where->x += t_chunk_width;
514 } else if(t_chunk_width <= p_width - *Lmargin) {
515 /* it can fit on an entire line, flush and wrap it to a new line */
516 where->y += myDrawText(tPtr, para, items, *nitems,
517 p_width-*Lmargin, where->y, draw, spacepos);
518 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
519 *nitems = 0;
520 *Lmargin = WMAX(0, m.body - m.first);
521 where->x = 0;
522 drawTChunkPart(tPtr, chunk, bufr, m, para, items, nitems,
523 len, start, Lmargin, where, draw, spacepos);
524 } else {
525 /* otherwise, chop line, call ourself recursively until it's all gone */
526 short J=0; /* bufr */
527 short j=0; /* local tmp buffer */
528 char tmp[len];
529 short diff = p_width - *Lmargin - where->x;
530 short _start=0;
532 if(diff < 20) {
533 where->y += myDrawText(tPtr, para, items, *nitems,
534 p_width-*Lmargin, where->y, draw, spacepos);
535 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
536 *nitems = 0;
537 *Lmargin = WMAX(0, m.body - m.first);
538 where->x = 0;
539 diff = p_width - *Lmargin - where->x;
542 for(J=0; J<len; J++) {
543 tmp[j] = bufr[J];
544 if(WMWidthOfString(font, tmp, j+1) > diff) {
545 drawTChunkPart(tPtr, chunk, tmp, m, para, items, nitems,
546 j, start+_start, Lmargin, where, draw, spacepos);
547 _start = J;
548 J--; j=0;
549 } else j++;
551 /* and there's always that last chunk, get it too */
552 drawTChunkPart(tPtr, chunk, tmp, m, para, items, nitems,
553 j, start+_start, Lmargin, where, draw, spacepos);
557 /* this function does what it's called :-)
558 o It is also used for calculating extents of para,
559 (returns height) so watch out for (Bool) draw
560 o Also used to determine where mouse was clicked */
561 static int
562 putParagraphOnPixmap(Text *tPtr, Paragraph *para, Bool draw)
564 char bufr[MAX_WORD_LENGTH+1]; /* a single word + '\0' */
565 MyTextItems items[MAX_CHUNX+1];
566 short lmargin, spacepos, i, s, nitems, start;
567 LocalMargins m;
568 XPoint where;
569 Chunk *chunk;
571 if(!tPtr->view->flags.realized || !para) return 0;
573 where.x = 0, where.y =0, nitems = 0;
574 m.left = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
575 m.right = WMGetRulerMargin(tPtr->ruler, WRulerRight) - m.left;
576 m.first = para->fmargin, m.body = para->bmargin;
578 if(draw) {
579 W_Screen *screen = tPtr->view->screen;
580 if(para->drawbuffer)
581 XFreePixmap(screen->display, para->drawbuffer);
582 if(para->width<2*tPtr->dFont->height) para->width = 2*tPtr->dFont->height;
583 if(para->height<tPtr->dFont->height) para->height = tPtr->dFont->height;
584 para->drawbuffer = XCreatePixmap(screen->display,
585 tPtr->view->window, para->width, para->height, screen->depth);
586 XFillRectangle(screen->display, para->drawbuffer,
587 WMColorGC(tPtr->bg), 0, 0, para->width, para->height);
591 //if(para->align != tPtr->dAlignment)
592 // para->align = tPtr->dAlignment;
594 /* draw the bullet if appropriate */
595 if(m.body>m.first && !tPtr->monoFont) {
596 lmargin = m.body - m.first;
597 if(draw) {
598 if(para->bulletPix)
599 WMDrawPixmap(para->bulletPix, para->drawbuffer, lmargin-10, 5);
600 else
601 WMDrawPixmap(tPtr->dBulletPix, para->drawbuffer, lmargin-10, 5);
603 /* NeXT sez next tab, I say the m.body - m.first margin */
604 } else {
605 lmargin = WMAX(0, m.first - m.body);
608 if(tPtr->findingClickPoint && !para->chunks) {
609 tPtr->currentChunk = NULL;
610 tPtr->foundClickPoint = True;
611 tPtr->tpos = 0;
612 tPtr->clicked.x = lmargin;
613 tPtr->clicked.y = 5;
614 tPtr->clheight = para->height;
615 tPtr->clwidth = 0;
616 return 0;
619 chunk = para->chunks;
620 while(chunk) {
622 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0;
624 if(chunk->type == ctImage && !tPtr->monoFont ) {
625 drawPChunkPart(tPtr, chunk, m, para, items, &nitems,
626 &lmargin, &where, draw);
627 } else if(chunk->text && chunk->type == ctText) {
628 if(tPtr->wrapping == wrNone) {
629 drawTChunkPart(tPtr, chunk, chunk->text, m, para, items, &nitems,
630 chunk->chars, 0, &lmargin, &where, draw, spacepos);
631 } else if(tPtr->wrapping == wrWord) {
632 spacepos=0, i=0, start=0;
633 while(spacepos < chunk->chars) {
634 bufr[i] = chunk->text[spacepos];
635 if( bufr[i] == ' ' || i >= MAX_WORD_LENGTH ) {
636 if(bufr[i] == ' ') s=1; else s=0;
637 drawTChunkPart(tPtr, chunk, bufr, m, para,
638 items, &nitems, i+s, start, &lmargin, &where,
639 draw, spacepos);
640 start = spacepos+s;
641 if(i > MAX_WORD_LENGTH-1)
642 spacepos--;
643 i=0;
644 } else i++;
645 spacepos++;
647 /* catch that last onery one. */
648 drawTChunkPart(tPtr, chunk, bufr, m, para,
649 items, &nitems, i, start, &lmargin, &where, draw, spacepos);
650 } } chunk = chunk->next;
652 /* we might have a few leftover items that need drawing */
653 if(nitems >0) {
654 where.y += myDrawText(tPtr, para, items,
655 nitems, m.right-m.left-lmargin, where.y, draw, spacepos);
656 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0;
658 return where.y;
661 static int
662 calcParaExtents(Text *tPtr, Paragraph *para)
664 if(!para) return 0;
666 if(tPtr->monoFont) {
667 para->width = tPtr->visibleW;
668 para->fmargin = 0;
669 para->bmargin = 0;
670 para->rmargin = tPtr->visibleW;
671 } else {
672 para->width = WMGetRulerMargin(tPtr->ruler, WRulerRight) -
673 WMIN(para->fmargin, para->bmargin)
674 - WMGetRulerOffset(tPtr->ruler);
677 if(!para->chunks)
678 para->height = tPtr->dFont->height;
679 else
680 para->height = putParagraphOnPixmap(tPtr, para, False);
682 if(para->height<tPtr->dFont->height)
683 para->height = tPtr->dFont->height;
684 para->bottom = para->top + para->height;
685 return para->height;
689 /* rather than bother with redrawing _all_ the pixmaps, simply
690 rearrange (i.e., push down or pull up) paragraphs after this one */
691 static void
692 affectNextParas(Text *tPtr, Paragraph *para, int move_y)
694 Paragraph *next;
695 int old_y = 0;
697 if(!para || move_y==0) return;
698 if(move_y == -23) {
699 old_y = para->bottom;
700 calcParaExtents(tPtr, para);
701 old_y -= para->bottom;
702 if(old_y == 0) return;
703 move_y = -old_y;
704 }if(move_y == 0) return;
706 next = para->next;
707 while(next) {
708 next->top += move_y;
709 next->bottom = next->top + next->height;
710 next = next->next; // I know, I know
711 }tPtr->docHeight += move_y;
713 #if 0
714 tPtr->vpos += move_y;
715 if(tPtr->vpos < 0) tPtr->vpos = 0;
716 if(tPtr->vpos > tPtr->docHeight - tPtr->visibleH)
717 tPtr->vpos = tPtr->docHeight - tPtr->visibleH;
718 #endif
723 static void
724 calcDocExtents(Text *tPtr)
726 Paragraph *para;
728 if(tPtr->monoFont) {
729 tPtr->docWidth = tPtr->visibleW;
730 } else {
731 tPtr->docWidth = WMGetRulerMargin(tPtr->ruler, WRulerRight) -
732 WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
734 tPtr->docHeight = 0;
735 para = tPtr->paragraphs;
736 if(para) {
737 while(para) {
738 para->top = tPtr->docHeight;
739 tPtr->docHeight += calcParaExtents(tPtr, para);
740 para->bottom = tPtr->docHeight;
741 para = para->next;
743 } else { /* default to this if no paragraphs */
744 tPtr->docHeight = tPtr->dFont->height;
746 #if 0
747 if(tPtr->editable) /* add space at bottom to enter new stuff */
748 tPtr->docHeight += tPtr->dFont->height;
749 #endif
753 /* If any part of a paragraph is viewable, the entire
754 paragraph is drawn on an otherwise empty (XFreePixmap) pixmap.
755 The actual viewable parts of the paragraph(s) are then pieced
756 together via paintText:
758 -------------------------------------------
759 || this is a paragraph in this document||
760 ||========================================||
761 || | only part of it is visible though. ||
762 || |-------------------------------------||
763 ||[.| This is another paragraph ||
764 || | which I'll make relatively long ||
765 || | just for the sake of writing a long ||
766 || | paragraph with a picture: ^_^ ||
767 || |-------------------------------------||
768 ||==| Of the three paragraphs, only ||
769 ||/\| the preceding was totally copied to ||
770 ||\/| totally copied to the window, even ||
771 ==========================================||
772 though they are all on pixmaps.
773 -------------------------------------------
774 This paragraph exists only in
775 memory and so has a NULL pixmap.
776 -------------------------------------------
779 simple, right? Performance: the best of both worlds...
780 o fast scrolling: no need to rewrite what's already
781 on the screen, simply XCopy it.
782 o fast typing: only change current para, then simply
783 affect other (i.e., subsequent) paragraphs.
784 o If no part of para is on screen, gdbFree pixmap; else draw on
785 individual pixmap per para then piece several paras together
786 o Keep track of who to XCopy to window (see paintText) */
787 static void
788 drawDocumentPartsOnPixmap(Text *tPtr, Bool all)
790 Paragraph *para;
792 para = tPtr->paragraphs;
793 while(para) {
794 /* the 32 reduces jitter on the human eye by preparing paragraphs
795 in anticipation of when the _moving_ scrollbar reaches them */
796 if(para->bottom + 32 < tPtr->vpos ||
797 para->top > tPtr->visibleH + tPtr->vpos + 32 ) {
798 if(para->drawbuffer) {
799 XFreePixmap(tPtr->view->screen->display, para->drawbuffer);
800 para->drawbuffer = (Pixmap) NULL;
802 } else {
803 if(!para->drawbuffer || all)
804 putParagraphOnPixmap(tPtr, para, True);
806 para = para->next;
812 /* this function blindly copies the "visible" parts of a pragraph
813 unto the view, (top-down approach). It starts drawing from
814 the top of the view (which paragraph to draw is determined by
815 drawDocumentPartsOnPixmap); it stops at the bottom of the view. */
816 static void
817 paintText(Text *tPtr)
819 short lmargin, para_lmargin;
820 int from=5, to=5, height;
821 Paragraph *para;
822 short vS=0, hS=0, rh=0;
824 if(!tPtr->view->flags.realized) return;
826 if(tPtr->rulerShown) rh = 40;
827 to += rh;
829 if(tPtr->hasVscroller) vS = 21;
830 if(tPtr->hasHscroller) hS = 21;
832 //XClearWindow(tPtr->view->screen->display, tPtr->view->window);
834 lmargin = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
835 if(tPtr->paragraphs) {
836 para = tPtr->paragraphs;
837 while(para) {
838 if(para->drawbuffer) {
839 from = (para->top<=tPtr->vpos)?tPtr->vpos-para->top:0;
840 height = para->height - from;
841 if(from>=0 && height>0 ) {
842 para_lmargin = WMIN(para->fmargin, para->bmargin);
843 if(lmargin-vS<WMIN(para->fmargin, para->bmargin)) {
844 #if 0
845 XClearArea(tPtr->view->screen->display, tPtr->view->window,
846 lmargin, to, 2+para_lmargin, height, False);
847 #else
848 XFillRectangle(tPtr->view->screen->display, tPtr->view->window,
849 WMColorGC(tPtr->dColor), lmargin, to, 2+para_lmargin, height);
850 #endif
852 XCopyArea(tPtr->view->screen->display, para->drawbuffer,
853 tPtr->view->window, WMColorGC(tPtr->bg), 0, from,
854 para->width-4, height, lmargin+para_lmargin+2, to);
855 if( (to+=height) > tPtr->visibleH+rh)
856 break;
858 para = para->next;
862 #if 0
863 /* clear any left over space (esp. during para deletes/ ruler changes) */
864 if(tPtr->docHeight < tPtr->visibleH && tPtr->visibleH+rh+5-to>0) {
865 XClearArea(tPtr->view->screen->display, tPtr->view->window, vS, to,
866 tPtr->view->size.width-vS, tPtr->visibleH+rh+hS+5-to, False);
869 if(lmargin>vS)
870 XClearArea(tPtr->view->screen->display, tPtr->view->window,
871 vS+1, rh+5, lmargin-vS, tPtr->visibleH+rh+5-vS, False);
874 // from the "selection" days...
875 W_DrawRelief(tPtr->view->screen, WMWidgetXID(tPtr),
876 tPtr->sRect.pos.x, tPtr->sRect.pos.y,
877 tPtr->sRect.size.width, tPtr->sRect.size.height, tPtr->relief);
878 #endif
880 W_DrawRelief(tPtr->view->screen, WMWidgetXID(tPtr), 0, rh,
881 tPtr->visibleW+vS, tPtr->visibleH+hS, tPtr->relief);
883 if(tPtr->editable && tPtr->clheight > 0) {
884 int top = tPtr->cursor.y-tPtr->vpos;
885 int bot = top+tPtr->clheight;
886 if(bot>5) {
887 if(top<5) top=5;
888 if(bot>tPtr->visibleH+hS-2) bot = tPtr->visibleH+hS-2;
889 if(bot-top>1) {
890 //do something about italic text...
891 XDrawLine(tPtr->view->screen->display, tPtr->view->window,
892 WMColorGC(tPtr->dColor), lmargin+tPtr->cursor.x, top,
893 lmargin+tPtr->cursor.x, bot);
894 } } }
900 /* called anytime either the ruler, vscroller or hscroller is hidden/shown,
901 or when the widget is resized by some user action */
902 static void
903 resizeText(W_ViewDelegate *self, WMView *view)
905 Text *tPtr = (Text *)view->self;
906 short rh=0;
908 if(!tPtr->monoFont && tPtr->rulerShown)
909 rh = 40;
911 W_ResizeView(view, view->size.width, view->size.height);
912 WMResizeWidget(tPtr->ruler, view->size.width, 40);
915 if(tPtr->hasVscroller) {
916 WMMoveWidget(tPtr->vscroller, 1, 1+rh);
917 WMResizeWidget(tPtr->vscroller, 20, view->size.height-rh-2);
918 tPtr->visibleW = view->size.width-21;
920 if(tPtr->hasHscroller) {
921 WMMoveWidget(tPtr->hscroller, 20, view->size.height-21);
922 WMResizeWidget(tPtr->hscroller, view->size.width-21, 20);
923 tPtr->visibleH = view->size.height-21-rh;
924 } else tPtr->visibleH = view->size.height-rh;
925 } else {
926 tPtr->visibleW = view->size.width;
927 if(tPtr->hasHscroller) {
928 WMMoveWidget(tPtr->hscroller, 1, view->size.height-21);
929 WMResizeWidget(tPtr->hscroller, view->size.width-2, 20);
930 tPtr->visibleH = view->size.height-21-rh;
931 } else tPtr->visibleH = view->size.width-2-rh;
933 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
936 W_ViewDelegate _TextViewDelegate =
938 NULL,
939 NULL,
940 resizeText,
941 NULL,
946 /* a plain text parser */
947 /* this gives useful hints on how to make a more
948 interesting parser for say HTML, RTF */
949 static void
950 defaultParser(Text *tPtr, void *data, short type)
952 char *start, *mark, *text = (char *) data;
953 Chunk *chunk = NULL;
954 Paragraph *para = NULL;
956 start = text;
957 while(start) {
958 mark = strchr(start, '\n');
959 if(mark) {
960 /* there is a newline, indicating the need for a new paragraph */
961 /* attach the chunk to the current paragraph */
962 if((short)(mark-start) > 1) {
963 /* ignore chunks with just a single newline but still make a
964 blank paragraph */
965 chunk = (tPtr->funcs.createTChunk) (start, (short)(mark-start),
966 tPtr->dFont, tPtr->dColor, 0, False);
967 (tPtr->funcs.insertChunk) (tPtr, chunk, type);
969 /* _then_ create a new paragraph for the _next_ chunk */
970 para = (tPtr->funcs.createParagraph) (0, 0, tPtr->visibleW,
971 NULL, 0, WALeft);
972 (tPtr->funcs.insertParagraph) (tPtr, para, type);
973 start = mark+1;
974 } else {
975 /* just attach the chunk to the current paragraph */
976 if(strlen(start) > 0) {
977 chunk = (tPtr->funcs.createTChunk) (start, strlen(start),
978 tPtr->dFont, tPtr->dColor, 0, False);
979 (tPtr->funcs.insertChunk) (tPtr, chunk, type);
980 } start = mark;
985 static void
986 updateScrollers(Text *tPtr)
988 if(tPtr->hasVscroller) {
989 if(tPtr->docHeight < tPtr->visibleH) {
990 WMSetScrollerParameters(tPtr->vscroller, 0, 1);
991 tPtr->vpos = 0;
992 } else {
993 float vmax = (float)(tPtr->docHeight);
994 WMSetScrollerParameters(tPtr->vscroller,
995 ((float)tPtr->vpos)/(vmax - (float)tPtr->visibleH),
996 (float)tPtr->visibleH/vmax);
1000 if(tPtr->hasHscroller)
1004 static void
1005 scrollersCallBack(WMWidget *w, void *self)
1007 Text *tPtr = (Text *)self;
1008 Bool scroll = False;
1009 Bool dimple = False;
1011 if(!tPtr->view->flags.realized) return;
1013 if(w == tPtr->vscroller) {
1014 float vmax;
1015 int height;
1016 vmax = (float)(tPtr->docHeight);
1017 height = tPtr->visibleH;
1018 if(height>7)
1019 height -= 7; /* the top border (5) + bottom (2) */
1021 switch(WMGetScrollerHitPart(tPtr->vscroller)) {
1022 case WSDecrementLine:
1023 if(tPtr->vpos > 0) {
1024 if(tPtr->vpos>16) tPtr->vpos-=16;
1025 else tPtr->vpos=0;
1026 scroll=True;
1027 }break;
1028 case WSIncrementLine: {
1029 int limit = tPtr->docHeight - height;
1030 if(tPtr->vpos < limit) {
1031 if(tPtr->vpos<limit-16) tPtr->vpos+=16;
1032 else tPtr->vpos=limit;
1033 scroll = True;
1034 }}break;
1035 case WSDecrementPage:
1036 tPtr->vpos -= height;
1038 if(tPtr->vpos < 0)
1039 tPtr->vpos = 0;
1040 dimple = True;
1041 scroll = True;
1042 printf("dimple needs to jump to mouse location ;-/\n");
1043 break;
1044 case WSIncrementPage:
1045 tPtr->vpos += height;
1046 if(tPtr->vpos > (tPtr->docHeight - height))
1047 tPtr->vpos = tPtr->docHeight - height;
1048 dimple = True;
1049 scroll = True;
1050 printf("dimple needs to jump to mouse location ;-/\n");
1051 break;
1054 case WSKnob:
1055 tPtr->vpos = WMGetScrollerValue(tPtr->vscroller)
1056 * (float)(tPtr->docHeight - height);
1057 scroll = True;
1058 break;
1060 #if 0
1061 case WSKnobSlot:
1062 case WSNoPart:
1063 float vmax = (float)(tPtr->docHeight);
1064 ((float)tPtr->vpos)/(vmax - (float)tPtr->visibleH),
1065 (float)tPtr->visibleH/vmax);
1066 dimple =where mouse is.
1067 #endif
1068 break;
1070 scroll = (tPtr->vpos != tPtr->prevVpos);
1071 tPtr->prevVpos = tPtr->vpos;
1074 if(w == tPtr->hscroller)
1077 //need scrollv || scrollh
1078 if(scroll) {
1080 if(0&&dimple) {
1081 if(tPtr->rulerShown)
1082 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
1083 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
1084 else
1085 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
1086 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
1089 updateScrollers(tPtr);
1090 drawDocumentPartsOnPixmap(tPtr, False);
1091 paintText(tPtr);
1096 void
1097 W_InsertText(WMText *tPtr, void *data, Bool prepend)
1099 if(!tPtr) return;
1100 if(!data) {
1101 Paragraph *para = tPtr->paragraphs, *ptmp;
1102 Chunk *chunk, *ctmp;
1103 WMFreezeText(tPtr);
1104 while(para) {
1105 chunk = para->chunks;
1106 while(chunk) {
1107 if(chunk->type == ctText && chunk->text)
1108 wgdbFree(chunk->text);
1109 else if(chunk->pixmap)
1110 WMReleasePixmap(chunk->pixmap);
1111 ctmp = chunk;
1112 chunk = chunk->next;
1113 wgdbFree(ctmp);
1115 ptmp = para;
1116 para = para->next;
1117 if(ptmp->drawbuffer)
1118 XFreePixmap(tPtr->view->screen->display, ptmp->drawbuffer);
1119 wgdbFree(ptmp);
1121 tPtr->paragraphs = NULL;
1122 tPtr->currentPara = NULL;
1123 tPtr->currentChunk = NULL;
1124 WMThawText(tPtr);
1125 WMRefreshText(tPtr, 0, 0);
1126 return;
1129 if(tPtr->parser)
1130 (tPtr->parser)(tPtr, data, prepend);
1131 else
1132 defaultParser(tPtr, data, prepend);
1135 static void
1136 cursorToTextPosition(Text *tPtr, int x, int y)
1138 Paragraph *para = NULL;
1139 Chunk *chunk = NULL;
1140 WMFont *font;
1141 short line_width=0;
1142 short orig_x, orig_y;
1144 if(x<(tPtr->hasVscroller?21:1)) {
1145 y -= tPtr->clheight;
1146 x = tPtr->view->size.width; //tPtr->visibleW;
1147 } else if(x>tPtr->clwidth && x<tPtr->clicked.x) {
1148 //x = (tPtr->hasVscroller)?21:1;
1149 //y += tPtr->clheight;
1152 if(x<0) x=0;
1153 orig_x = x;
1155 if(y<0 || y>tPtr->view->size.height-3) return;
1156 orig_y = y;
1157 tPtr->clicked.x = orig_x;
1158 tPtr->clicked.y = y;
1159 tPtr->clicked.y += tPtr->vpos;
1160 tPtr->clicked.y -= tPtr->rulerShown?40:0;
1161 para = tPtr->paragraphs;
1162 if(!para) return;
1163 while(para->next) {
1164 if( tPtr->clicked.y>= para->top-4 &&
1165 tPtr->clicked.y < para->bottom+4) break;
1166 para = para->next;
1167 } if(!(tPtr->currentPara = para)) return;
1169 tPtr->clicked.y -= para->top;
1170 if(tPtr->clicked.y<0) tPtr->clicked.y=0;
1171 if(tPtr->hasVscroller) x -= 21;
1172 if(x<0) x=0;
1174 tPtr->findingClickPoint = True;
1175 tPtr->foundClickPoint = False;
1176 /* also affects tPtr->currentChunk, tPtr->clicked.x and y,
1177 tPtr->clheight and ->width */
1178 putParagraphOnPixmap(tPtr, para, False);
1179 tPtr->findingClickPoint = False;
1180 tPtr->clicked.y += para->top;
1182 if(tPtr->currentChunk) {
1183 short _width=0, start=tPtr->tpos, done=False, w=0;
1184 chunk = tPtr->currentChunk;
1185 while(!done && chunk && line_width<tPtr->clwidth) {
1186 if(chunk->type == ctText) {
1187 font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
1188 for (w=start; w<chunk->chars; w++) {
1189 _width = WMWidthOfString(font, &chunk->text[w], 1);
1190 line_width += _width;
1191 if(line_width+tPtr->clicked.x >= x) {
1192 line_width -= _width;
1193 done = True;
1194 printf("break\n");
1195 break;
1198 if(0&&chunk->next) {
1199 if(chunk->next->type == ctImage) {
1200 if(x+10 < line_width+chunk->next->pixmap->width) {
1201 printf("true\n");
1202 done = True;
1203 } } }
1204 } else {
1205 _width = chunk->pixmap->width;
1206 line_width += _width;
1207 if(line_width+tPtr->clicked.x >= x) {
1208 line_width -= _width;
1209 tPtr->tpos = 0;
1210 done = True;
1213 if(!done) {
1214 chunk = chunk->next;
1215 start = w = 0;
1216 } else {
1217 tPtr->tpos = w;
1218 tPtr->currentChunk = chunk;
1219 break;
1220 } } } else {
1221 short vS = (tPtr->hasVscroller)?32:12;
1222 if(para->align == WARight) {
1223 tPtr->clicked.x = tPtr->view->size.width-vS;
1224 } else if (para->align == WACenter) {
1225 tPtr->clicked.x = -(vS/2)+(tPtr->view->size.width-vS)/2;
1226 } else {
1227 tPtr->clicked.x = 2;
1230 tPtr->cursor.x = tPtr->clicked.x+2+line_width;
1231 tPtr->cursor.y = tPtr->clicked.y;
1232 tPtr->clicked.y = orig_y;
1233 tPtr->clicked.x = orig_x;
1234 putParagraphOnPixmap(tPtr, para, True);
1235 paintText(tPtr);
1238 static void
1239 deleteTextInteractively(Text *tPtr, DeleteType type)
1241 Paragraph *para;
1242 Chunk *chunk;
1243 short pos,w=0,h=0, doprev=False, doprevpara=False;
1244 WMFont *font;
1245 int current = WMGetTextCurrentChunk(tPtr);
1247 if(!(para = tPtr->currentPara)) return;
1248 if(!(chunk = tPtr->currentChunk)) return;
1249 font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
1250 doprev = (tPtr->tpos < 2);
1252 switch(type) {
1253 case dtDelete: /* delete _after_ cursor ... implement later */
1254 case dtBackSpace: /* delete _before_ cursor */
1255 if(chunk->chars > 1) {
1256 pos = tPtr->tpos-1;
1257 printf("here %d\n", pos);
1258 if(pos>0) {
1259 w = WMWidthOfString(font, &chunk->text[pos], 1);
1260 memmove(&(chunk->text[pos]),
1261 &(chunk->text[pos+1]), chunk->chars-pos+1);
1262 tPtr->tpos--;
1263 chunk->chars--;
1264 } } else {
1265 WMRemoveTextChunk(tPtr, current);
1266 doprev = True;
1269 if(doprev) {
1270 if(current > 0) {
1271 WMSetTextCurrentChunk(tPtr, current-1);
1272 if(!tPtr->currentChunk) {
1273 printf("PREV PARA\n");
1274 } else {
1275 tPtr->tpos = tPtr->currentChunk->chars;
1277 } else if(0){
1278 int currentp = WMGetTextCurrentParagraph(tPtr);
1279 doprevpara = True;
1280 if(currentp > 1) {
1281 para->chunks = NULL;
1282 WMRemoveTextParagraph(tPtr, currentp);
1283 WMSetTextCurrentParagraph(tPtr, currentp-1);
1284 WMSetTextCurrentChunk(tPtr, -1);
1285 para = tPtr->currentPara;
1286 if(para) {
1287 if(!tPtr->currentChunk || !para->chunks) {
1288 para->chunks = chunk;
1289 tPtr->currentChunk = chunk;
1290 } else
1291 tPtr->currentChunk->next = chunk;
1292 } } } } }
1294 if(1) { //if(1||(para && !doprevpara)) {
1295 affectNextParas(tPtr, para, -23);
1296 putParagraphOnPixmap(tPtr, para, True);
1297 drawDocumentPartsOnPixmap(tPtr, False);
1298 updateScrollers(tPtr);
1299 paintText(tPtr);
1300 //cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1301 } else WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1305 /* give us nice chunk sizes (multiples of 16) */
1306 static short
1307 reqBlockSize(short requested)
1309 return requested+16-(requested%16);
1312 static void
1313 insertTextInteractively(Text *tPtr, char *text)
1315 Paragraph *para=NULL;
1316 Chunk *chunk=NULL, *newchunk=NULL;
1317 int height = -23; /* should only be changed upon newline */
1318 short w=0,h=0;
1319 WMFont *font;
1321 if(!tPtr->editable) return;
1322 if(*text == '\n' && tPtr->ignoreNewLine)
1323 return;
1325 para = tPtr->currentPara;
1326 chunk = tPtr->currentChunk;
1327 font = (tPtr->monoFont || !chunk)?tPtr->dFont:chunk->font;
1329 if(*text == '\n') {
1330 int new_top=0;
1331 if(chunk) { /* there's a chunk (or part of it) to detach from old */
1332 int current = WMGetTextCurrentChunk(tPtr);
1333 if(tPtr->tpos <=0) { /* at start of chunk */
1334 if(current<1) { /* the first chunk... make old para blank */
1335 newchunk = para->chunks;
1336 para->chunks = NULL;
1337 putParagraphOnPixmap(tPtr, para, True);
1338 } else { /* not first chunk... */
1339 printf("cut me out \n");
1341 } else if(tPtr->tpos < chunk->chars && chunk->type == ctText) {
1342 /* not at start of chunk */
1343 char text[chunk->chars-tPtr->tpos+1];
1344 int i=0;
1345 do {
1346 text[i] = chunk->text[tPtr->tpos+i];
1347 } while(++i < chunk->chars-tPtr->tpos);
1348 chunk->chars -= i;
1349 newchunk = (tPtr->funcs.createTChunk) (text, i, chunk->font,
1350 chunk->color, chunk->script, chunk->ul);
1351 newchunk->next = chunk->next;
1352 chunk->next = NULL;
1353 /* might want to demalloc for LARGE cuts */
1354 //calcParaExtents(tPtr, para);
1355 para->height = putParagraphOnPixmap(tPtr, para, True);
1356 //putParagraphOnPixmap(tPtr, para, True);
1357 } else if(tPtr->tpos >= chunk->chars) {
1358 Chunk *prev;
1359 WMSetTextCurrentChunk(tPtr, current-1);
1360 prev = tPtr->currentChunk;
1361 if(!prev) return;
1362 newchunk = prev->next;
1363 prev->next = NULL;
1364 putParagraphOnPixmap(tPtr, para, True);
1366 } else newchunk = NULL;
1368 if(para) /* the preceeding one */
1369 new_top = para->bottom;
1371 WMAppendTextStream(tPtr, "\n");
1372 para = tPtr->currentPara;
1373 if(!para) return;
1374 para->chunks = newchunk;
1375 tPtr->currentChunk = newchunk;
1376 tPtr->tpos = 0;
1377 para->top = new_top;
1378 calcParaExtents(tPtr, para);
1379 height = para->height;
1380 } else {
1381 if(!para) {
1382 WMAppendTextStream(tPtr, text);
1383 para = tPtr->currentPara;
1384 } else if(!para->chunks || !chunk) {
1385 //WMPrependTextStream(tPtr, text);
1386 WMAppendTextStream(tPtr, text);
1387 } else if(chunk->type == ctImage) {
1388 WMPrependTextStream(tPtr, text);
1390 printf("\n\nprepe\n\n");
1391 } else {
1392 if(tPtr->tpos > chunk->chars) {
1393 printf("\n\nmore\n\n");
1394 tPtr->tpos = chunk->chars;
1397 if(chunk->chars+1 >= chunk->mallocedSize) {
1398 chunk->mallocedSize = reqBlockSize(chunk->chars+1);
1399 chunk->text = wrealloc(chunk->text, chunk->mallocedSize);
1402 memmove(&(chunk->text[tPtr->tpos+1]), &chunk->text[tPtr->tpos],
1403 chunk->chars-tPtr->tpos+1);
1404 w = WMWidthOfString(font, text, 1);
1405 memmove(&chunk->text[tPtr->tpos], text, 1);
1406 chunk->chars++;
1407 tPtr->tpos++;
1408 //doc->clickstart.cursor.x +=
1409 //WMWidthOfString(chunk->fmt->font, text,len);
1414 if(para) {
1415 affectNextParas(tPtr, para, height);
1416 putParagraphOnPixmap(tPtr, para, True);
1417 drawDocumentPartsOnPixmap(tPtr, False);
1418 updateScrollers(tPtr);
1419 paintText(tPtr);
1420 //cursorToTextPosition(tPtr, tPtr->clicked.x+w, tPtr->clicked.y);
1421 //check for "sneppah tahw" with blank paras...
1422 //paintText(tPtr);
1427 static void
1428 selectRegion(Text *tPtr, int x, int y)
1430 tPtr->sRect.pos.x = WMIN(tPtr->clicked.x, x);
1431 tPtr->sRect.size.width = abs(tPtr->clicked.x-x);
1432 tPtr->sRect.pos.y = WMIN(tPtr->clicked.y, y);
1433 if(tPtr->sRect.pos.y<0) tPtr->sRect.pos.y=0;
1434 tPtr->sRect.size.height = abs(tPtr->clicked.y-y);
1437 while(y>tPtr->visibleH && tPtr->vpos < tPtr->docHeight-tPtr->visibleH) {
1438 WMRefreshText(tPtr, tPtr->vpos+16, tPtr->hpos);
1441 //printf("%d %d \n", y, tPtr->vpos);
1443 //foreach para in selection...
1444 drawDocumentPartsOnPixmap(tPtr, True);
1445 paintText(tPtr);
1452 #define WM_EMACSKEYMASK ControlMask
1453 #define WM_EMACSKEY_LEFT XK_b
1454 #define WM_EMACSKEY_RIGHT XK_f
1455 #define WM_EMACSKEY_HOME XK_a
1456 #define WM_EMACSKEY_END XK_e
1457 #define WM_EMACSKEY_BS XK_h
1458 #define WM_EMACSKEY_DEL XK_d
1460 static void
1461 handleTextKeyPress(Text *tPtr, XEvent *event)
1463 char buffer[2];
1464 KeySym ksym;
1465 int control_pressed = False;
1467 if(!tPtr->editable) return;
1469 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
1470 control_pressed = True;
1471 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = '\0';
1473 switch(ksym) {
1475 case XK_Right:
1476 case XK_Left:
1477 if(tPtr->currentChunk) {
1478 short w;
1479 Chunk *chunk = tPtr->currentChunk;
1480 if(chunk->type == ctText) {
1481 WMFont *font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
1482 if(ksym==XK_Right) {
1483 short pos = (tPtr->tpos<chunk->chars)?tPtr->tpos+1:
1484 chunk->chars;
1485 w = WMWidthOfString(font,&chunk->text[pos],1);
1486 } else {
1487 short pos = (tPtr->tpos>0)?tPtr->tpos-1:0;
1488 w = WMWidthOfString(font,&chunk->text[pos],1);
1490 } else { w = chunk->pixmap->width; }
1491 if(ksym==XK_Right) w = -w;
1492 cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1493 } else {
1494 if(ksym==XK_Right) ksym = XK_Down;
1495 else ksym = XK_Up;
1496 goto noCChunk;
1498 break;
1500 case XK_Down:
1501 case XK_Up:
1502 noCChunk: { short h = tPtr->clheight-2;
1503 if(ksym==XK_Down) h = -h;
1504 cursorToTextPosition(tPtr, tPtr->clicked.x, tPtr->clicked.y-h);
1505 } break;
1507 case XK_BackSpace:
1508 deleteTextInteractively(tPtr, dtBackSpace);
1509 break;
1511 case XK_Delete:
1512 case XK_KP_Delete:
1513 deleteTextInteractively(tPtr, dtDelete);
1514 break;
1516 case XK_Return:
1517 buffer[0] = '\n';
1518 default:
1519 if(buffer[0] != '\0' && (buffer[0] == '\n' || !iscntrl(buffer[0])))
1520 insertTextInteractively(tPtr, buffer);
1521 else if(control_pressed && ksym==XK_r)
1522 {Bool i = !tPtr->rulerShown; WMShowTextRuler(tPtr, i);
1523 tPtr->rulerShown = i; }
1530 static void
1531 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1532 void *cdata, WMData *data)
1534 Text *tPtr = (Text *)view->self;
1535 char *str;
1538 tPtr->waitingForSelection = False;
1539 if(data) {
1540 str = (char*)WMDataBytes(data);
1541 if(tPtr->tpos<1) WMPrependTextStream(tPtr, str);
1542 else WMAppendTextStream(tPtr, str);
1543 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1544 } else {
1545 int n;
1546 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1547 if(str) {
1548 str[n] = 0;
1549 if(tPtr->tpos<1) WMPrependTextStream(tPtr, str);
1550 else WMAppendTextStream(tPtr, str);
1551 XFree(str);
1552 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1558 static void
1559 releaseSelection(Text *tPtr)
1561 Paragraph *para = tPtr->paragraphs;
1562 Chunk *chunk;
1563 while(para) {
1564 chunk = para->chunks;
1565 while(chunk) {
1566 chunk->selected = False;
1567 chunk = chunk->next;
1569 para = para->next;
1571 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1572 tPtr->ownsSelection = False;
1573 drawDocumentPartsOnPixmap(tPtr, True);
1574 paintText(tPtr);
1578 static WMData*
1579 requestHandler(WMView *view, Atom selection, Atom target,
1580 void *cdata, Atom *type)
1582 Text *tPtr = view->self;
1583 int count;
1584 Display *dpy = tPtr->view->screen->display;
1585 Atom _TARGETS;
1586 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1587 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1588 WMData *data = NULL;
1591 if(!tPtr->ownsSelection || !tPtr->paragraphs) return NULL;
1592 //printf("got here\n");
1594 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
1595 //for bleh in selection...
1596 char *s = NULL;
1597 Paragraph *para = tPtr->paragraphs;
1598 Chunk *chunk = NULL;
1599 char pixmap[] = "[pixmap]";
1600 Bool first=True;
1601 short len;
1603 while(para) {
1604 chunk = para->chunks;
1605 while(chunk) {
1607 if(chunk->selected && chunk->type == ctText) {
1608 len = chunk->chars; //chunk->sEnd - chunk->sStart;
1609 if(len>0) {
1610 s = wmalloc(len+1);
1611 if(s) {
1612 memcpy(s, &chunk->text[0*chunk->sStart], len);
1613 s[len] = 0;
1614 if(first) {
1615 data = WMCreateDataWithBytes(s, strlen(s));
1616 first = False;
1617 } else {
1618 printf("append: %c %d\n", *s, strlen(s));
1619 WMAppendDataBytes(data, s, strlen(s));
1621 //gdbFree(s);
1622 } } }
1623 #if 0
1624 printf("len is %d [%d %d] %d \n", len, chunk->sStart, chunk->sEnd,
1625 chunk->chars);
1626 #endif
1627 chunk = chunk->next;
1629 para = para->next;
1632 if(data) {
1633 WMSetDataFormat(data, 8);
1634 *type = target;
1636 return data;
1639 #if 0
1640 _TARGETS = XInternAtom(dpy, "TARGETS", False);
1641 if (target == _TARGETS) {
1642 Atom *ptr = wmalloc(4 * sizeof(Atom));
1643 ptr[0] = _TARGETS;
1644 ptr[1] = XA_STRING;
1645 ptr[2] = TEXT;
1646 ptr[3] = COMPOUND_TEXT;
1648 data = WMCreateDataWithBytes(ptr, 4*4);
1649 WMSetDataFormat(data, 32);
1651 *type = target;
1652 return data;
1654 #endif
1656 return NULL;
1661 static void
1662 lostHandler(WMView *view, Atom selection, void *cdata)
1664 WMText *tPtr = (WMText *)view->self;
1665 releaseSelection(tPtr);
1668 static WMSelectionProcs selectionHandler = {
1669 requestHandler, lostHandler, NULL };
1671 static void
1672 _notification(void *observerData, WMNotification *notification)
1674 WMText *to = (WMText *)observerData;
1675 WMText *tw = (WMText *)WMGetNotificationClientData(notification);
1676 if (to != tw) lostHandler(to->view, XA_PRIMARY, NULL);
1679 static void
1680 handleTextEvents(XEvent *event, void *data)
1682 Text *tPtr = (Text *)data;
1683 Display *dpy = event->xany.display;
1685 if(tPtr->waitingForSelection) return;
1687 switch (event->type) {
1688 case KeyPress:
1689 if(!tPtr->editable || tPtr->buttonHeld) {
1690 XBell(dpy, 0);
1691 return;
1693 if(tPtr->ownsSelection) releaseSelection(tPtr);
1694 //if (tPtr->waitingForSelection) return;
1695 if(tPtr->focused) {
1696 #if 0
1697 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1698 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1699 GrabModeAsync, GrabModeAsync, None,
1700 W_VIEW(tPtr)->screen->invisibleCursor, CurrentTime);
1701 tPtr->pointerGrabbed = True;
1702 #endif
1703 handleTextKeyPress(tPtr, event);
1704 } break;
1706 case MotionNotify:
1707 if(tPtr->pointerGrabbed) {
1708 tPtr->pointerGrabbed = False;
1709 XUngrabPointer(dpy, CurrentTime);
1711 if((event->xmotion.state & Button1Mask)) {
1712 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1713 if(!tPtr->ownsSelection) {
1714 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1715 event->xbutton.time, &selectionHandler, NULL);
1716 tPtr->ownsSelection = True;
1718 break;
1720 case ButtonPress:
1721 if(event->xbutton.button == Button1) {
1722 if(tPtr->ownsSelection) releaseSelection(tPtr);
1723 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1724 if (tPtr->pointerGrabbed) {
1725 tPtr->pointerGrabbed = False;
1726 XUngrabPointer(dpy, CurrentTime);
1727 break;
1730 if(!tPtr->focused) {
1731 WMSetFocusToWidget(tPtr);
1732 tPtr->focused = True;
1733 break;
1735 if(event->xbutton.button == 4)
1736 WMScrollText(tPtr, -16);
1737 else if(event->xbutton.button == 5)
1738 WMScrollText(tPtr, 16);
1740 break;
1742 case ButtonRelease:
1743 tPtr->buttonHeld = False;
1744 if (tPtr->pointerGrabbed) {
1745 tPtr->pointerGrabbed = False;
1746 XUngrabPointer(dpy, CurrentTime);
1747 break;
1749 if(event->xbutton.button == 4 || event->xbutton.button == 5)
1750 break;
1751 if(event->xbutton.button == Button2 && tPtr->editable) {
1752 char *text = NULL;
1753 int n;
1754 if(!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1755 event->xbutton.time, pasteText, NULL)) {
1756 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1757 if(text) {
1758 text[n] = 0;
1759 WMAppendTextStream(tPtr, text);
1760 XFree(text);
1761 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1762 } else tPtr->waitingForSelection = True;
1763 } } break;
1770 static void
1771 handleNonTextEvents(XEvent *event, void *data)
1773 Text *tPtr = (Text *)data;
1775 switch(event->type) {
1776 case Expose:
1777 if(!event->xexpose.count && tPtr->view->flags.realized)
1778 paintText(tPtr);
1779 break;
1781 case FocusIn:
1782 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
1783 return;
1784 tPtr->focused = True;
1785 //cursor...paintText(tPtr);
1786 break;
1788 case FocusOut:
1789 tPtr->focused = False;
1790 //cursor...paintText(tPtr);
1791 break;
1793 case DestroyNotify:
1794 printf("destroy");
1795 //for(...)WMRemoveTextParagraph(tPtr, para);
1796 break;
1800 //printf("handleNonTextEvents\n");
1805 static void
1806 rulerCallBack(WMWidget *w, void *self)
1808 Text *tPtr = (Text *)self;
1809 short which;
1811 if(tPtr->currentPara) {
1812 Paragraph *para = tPtr->currentPara;
1813 para->fmargin = WMGetRulerMargin(tPtr->ruler, WRulerFirst);
1814 para->bmargin = WMGetRulerMargin(tPtr->ruler, WRulerBody);
1815 para->rmargin = WMGetRulerMargin(tPtr->ruler, WRulerRight);
1816 affectNextParas(tPtr, para, -23);
1817 putParagraphOnPixmap(tPtr, para, True);
1819 #if 0
1820 which = WMGetReleasedRulerMargin(tPtr->ruler);
1821 if(which != WRulerDocLeft && which != WRulerRight
1822 /* && Selection.para.count > 0 */ ) {
1823 printf(""
1824 "//for(i=0; i<Selection.para.count; i++) {"
1825 "affect"
1826 "//calcParaExtents(tPtr, para);}\n");
1827 } else {
1828 WMRefreshText(tPtr, 0, 0);
1830 #endif
1834 static void
1835 rulerMoveCallBack(WMWidget *w, void *self)
1837 Text *tPtr = (Text *)self;
1838 short rmargin = WMGetRulerMargin(tPtr->ruler, WRulerRight);
1841 if(WMGetGrabbedRulerMargin(tPtr->ruler) == WRulerLeft) {
1842 short lmargin = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
1843 XClearArea(tPtr->view->screen->display, tPtr->view->window,
1844 22, 42, lmargin-21, tPtr->visibleH, True);
1845 } else if(WMGetGrabbedRulerMargin(tPtr->ruler) == WRulerRight &&
1846 tPtr->docWidth+11 < rmargin) {
1847 XClearArea(tPtr->view->screen->display, tPtr->view->window,
1848 rmargin-3, 42, 10, tPtr->visibleH, True);
1850 paintText(tPtr);
1855 /* ------------- non-static functions that are "friends" ------------- */
1856 /* ------------- called as (tPtr->funcs.foo)(bars...) ------------- */
1858 /* create a new paragraph. Don't do anything with it just yet */
1859 //Paragraph *
1860 void *
1861 createParagraph(short fmargin, short bmargin, short rmargin,
1862 short *tabstops, short numTabs, WMAlignment alignment)
1864 Paragraph *para = wmalloc(sizeof(Paragraph));
1865 if(!para) return NULL;
1867 para->chunks = NULL;
1868 para->next = NULL;
1871 para->fmargin = (fmargin>=0)?fmargin:0;
1872 para->bmargin = (bmargin>=0)?bmargin:0;
1873 if(rmargin-bmargin >= 100 && rmargin-fmargin >= 100)
1874 para->rmargin = rmargin;
1875 else
1876 para->rmargin = 100;
1877 para->tabstops = tabstops;
1878 para->numTabs = (tabstops)?numTabs:0;
1880 para->drawbuffer = (Pixmap)NULL;
1881 para->bulletPix = NULL;
1882 para->top = para->bottom = 0;
1883 para->width = para->height = 0;
1885 para->align = alignment;
1887 return para;
1890 /* insert the new paragraph in the tPtr, either right before
1891 or after the currentPara. It's the responsibility of the
1892 calling code to set what currentPara is. via WMSetTextCurrentParagraph.
1893 If currentPara is not set, set it as the first in the document.
1894 This function then sets currentPara as _this_ paragraph.
1895 NOTE: this means careless parser implementors might lose previous
1896 paragraphs... but this keeps stuff small and non-buggy :-) */
1898 void
1899 insertParagraph(WMText *tPtr, void *v, Bool prepend)
1900 //insertParagraph(WMText *tPtr, Paragraph *para, InsertType type)
1902 Paragraph *tmp;
1903 Paragraph *para = (Paragraph *)v;
1904 if(!para || !tPtr) return;
1906 if(!tPtr->currentPara) {
1907 tPtr->paragraphs = para;
1908 } else {
1909 tmp = tPtr->paragraphs;
1910 if(!prepend) {
1911 while(tmp->next && tmp != tPtr->currentPara)
1912 tmp = tmp->next;
1914 para->next = tmp->next;
1915 tmp->next = para;
1916 } else { /* must be prepend */
1917 /* this "prior" member is that "doing things the hard way"
1918 I spoke of. See? it's not too bad afterall... */
1919 Paragraph *prior = NULL;
1920 while(tmp->next && tmp != tPtr->currentPara) {
1921 prior = tmp;
1922 tmp = tmp->next;
1924 /* if this is the first */
1925 if(tmp == tPtr->paragraphs) {
1926 para->next = tmp;
1927 tPtr->paragraphs = para;
1928 } else {
1929 prior->next = para;
1930 para->next = tmp;
1931 } } }
1932 tPtr->currentPara = para;
1936 /* create a new chunk to contain exactly ONE pixmap */
1937 void *
1938 //Chunk *
1939 createPChunk(WMPixmap *pixmap, short script, ushort ul)
1941 Chunk *chunk;
1943 chunk = wmalloc(sizeof(Chunk));
1944 if(!chunk)
1945 return NULL;
1947 chunk->text = NULL;
1948 if(!pixmap)
1949 chunk->pixmap = NULL; /* if it's NULL, we'll draw the "broken" pixmap... */
1950 else chunk->pixmap = WMRetainPixmap(pixmap);
1951 chunk->chars = 0;
1952 chunk->mallocedSize = 0;
1953 chunk->type = ctImage;
1954 chunk->font = NULL;
1955 chunk->color = NULL;
1956 chunk->script = script;
1957 chunk->ul = ul;
1958 chunk->selected = False;
1959 chunk->next = NULL;
1960 return chunk;
1964 /* create a new chunk to contain some text with the given format */
1965 void *
1966 //Chunk *
1967 createTChunk(char *text, short chars, WMFont *font,
1968 WMColor *color, short script, ushort ul)
1970 Chunk *chunk;
1972 if(!text || chars<0 || !font || !color) return NULL;
1973 chunk = wmalloc(sizeof(Chunk));
1974 if(!chunk) return NULL;
1976 chunk->mallocedSize = reqBlockSize(chars);
1977 chunk->text = (char *)wmalloc(chunk->mallocedSize);
1978 memcpy(chunk->text, text, chars);
1979 chunk->pixmap = NULL;
1980 chunk->chars = chars;
1981 chunk->type = ctText;
1982 chunk->font = WMRetainFont(font);
1983 chunk->color = WMRetainColor(color);
1984 chunk->script = script;
1985 chunk->ul = ul;
1986 chunk->selected = False;
1987 chunk->next = NULL;
1989 return chunk;
1992 /* insert the new chunk in the paragraph, either right before
1993 or after the currentChunk. It's the responsibility of the
1994 calling code to set what currentChunk is via WMSetTextCurrentChunk.
1995 If currentChunk is not set, set it as the first in the existing
1996 paragraph... if not even that, you lose... try again.
1997 This function then sets currentChunk as _this_ chunk.
1998 NOTE: this means careless parser implementors might lose previous
1999 paragraphs/chunks... but this keeps stuff small and non-buggy :-) */
2000 void
2001 insertChunk(WMText *tPtr, void *v, Bool prepend)
2003 Chunk *tmp;
2004 Chunk *chunk = (Chunk *)v;
2006 if(!tPtr || !chunk) return;
2008 if(!tPtr->paragraphs) { /* i.e., first chunk via insertTextInteractively */
2009 Paragraph *para = (tPtr->funcs.createParagraph) (0, 0, tPtr->visibleW,
2010 NULL, 0, WALeft);
2011 (tPtr->funcs.insertParagraph) (tPtr, para, False);
2014 if(!tPtr->currentPara)
2015 return;
2016 if(!tPtr->currentChunk) { /* there is a current chunk */
2017 tPtr->currentPara->chunks = chunk;
2018 } else if(!tPtr->currentPara->chunks) {
2019 /* but it's not of this paragraph */
2020 tPtr->currentPara->chunks = chunk;
2021 } else {
2022 tmp = tPtr->currentPara->chunks;
2024 if(!prepend) {
2025 while(tmp->next && tmp != tPtr->currentChunk)
2026 tmp = tmp->next;
2028 chunk->next = tmp->next;
2029 tmp->next = chunk;
2031 } else { /* must be prepend */
2032 /* this "prior" member is that "doing things the hard way"
2033 I spoke of. See? it's not too bad afterall... */
2034 Chunk *prior = NULL;
2035 while(tmp->next && tmp != tPtr->currentChunk) {
2036 prior = tmp;
2037 tmp = tmp->next;
2039 /* if this is the first */
2040 if(tmp == tPtr->currentPara->chunks) {
2041 chunk->next = tmp;
2042 tPtr->currentPara->chunks = chunk;
2043 } else {
2044 prior->next = chunk;
2045 chunk->next = tmp;
2046 } } }
2047 tPtr->currentChunk = chunk;
2048 tPtr->tpos = chunk->chars;
2052 /* ------------- non-static functions (i.e., APIs) ------------- */
2053 /* ------------- called as WMVerbText[Subject] ------------- */
2055 #define DEFAULT_TEXT_WIDTH 250
2056 #define DEFAULT_TEXT_HEIGHT 200
2060 WMText*
2061 WMCreateText(WMWidget *parent)
2063 Text *tPtr = wmalloc(sizeof(Text));
2064 if(!tPtr) {
2065 perror("could not create text widget\n");
2066 return NULL;
2068 memset(tPtr, 0, sizeof(Text));
2069 tPtr->widgetClass = WC_Text;
2070 tPtr->view = W_CreateView(W_VIEW(parent));
2071 if (!tPtr->view) {
2072 perror("could not create text's view\n");
2073 wgdbFree(tPtr);
2074 return NULL;
2076 tPtr->view->self = tPtr;
2077 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
2078 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
2079 W_ResizeView(tPtr->view, DEFAULT_TEXT_WIDTH, DEFAULT_TEXT_HEIGHT);
2080 tPtr->bg = tPtr->view->screen->white;
2081 W_SetViewBackgroundColor(tPtr->view, tPtr->bg);
2084 tPtr->ruler = WMCreateRuler(tPtr);
2085 (W_VIEW(tPtr->ruler))->attribs.cursor = tPtr->view->screen->defaultCursor;
2086 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
2087 WMMoveWidget(tPtr->ruler, 0, 0);
2088 WMResizeWidget(tPtr->ruler, W_VIEW(parent)->size.width, 40);
2089 WMShowRulerTabs(tPtr->ruler, True);
2090 WMSetRulerAction(tPtr->ruler, rulerCallBack, tPtr);
2091 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
2093 tPtr->vpos = 0;
2094 tPtr->prevVpos = 0;
2095 tPtr->vscroller = WMCreateScroller(tPtr);
2096 (W_VIEW(tPtr->vscroller))->attribs.cursor =
2097 tPtr->view->screen->defaultCursor;
2098 (W_VIEW(tPtr->vscroller))->attribFlags |= CWOverrideRedirect | CWCursor;
2099 WMMoveWidget(tPtr->vscroller, 1, 1);
2100 WMResizeWidget(tPtr->vscroller, 20, tPtr->view->size.height - 2);
2101 WMSetScrollerArrowsPosition(tPtr->vscroller, WSAMaxEnd);
2102 WMSetScrollerAction(tPtr->vscroller, scrollersCallBack, tPtr);
2104 tPtr->hpos = 0;
2105 tPtr->prevHpos = 0;
2106 tPtr->hscroller = WMCreateScroller(tPtr);
2107 (W_VIEW(tPtr->hscroller))->attribs.cursor =
2108 tPtr->view->screen->defaultCursor;
2109 (W_VIEW(tPtr->hscroller))->attribFlags |= CWOverrideRedirect | CWCursor;
2110 WMMoveWidget(tPtr->hscroller, 1, tPtr->view->size.height-21);
2111 WMResizeWidget(tPtr->hscroller, tPtr->view->size.width - 2, 20);
2112 WMSetScrollerArrowsPosition(tPtr->hscroller, WSAMaxEnd);
2113 WMSetScrollerAction(tPtr->hscroller, scrollersCallBack, tPtr);
2115 tPtr->visibleW = tPtr->view->size.width;
2116 tPtr->visibleH = tPtr->view->size.height;
2118 tPtr->paragraphs = NULL;
2119 tPtr->docWidth = 0;
2120 tPtr->docHeight = 0;
2121 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
2122 default_bullet);
2123 tPtr->dUnknownImg = WMCreatePixmapFromXPMData(tPtr->view->screen,
2124 unk_xpm);
2126 tPtr->sRect.pos.x = tPtr->sRect.pos.y = 0;
2127 tPtr->sRect.size.width = tPtr->sRect.size.height = 0;
2128 tPtr->currentPara = NULL;
2129 tPtr->currentChunk = NULL;
2130 tPtr->tpos = 0;
2132 tPtr->parser = NULL;
2133 tPtr->writer = NULL;
2134 tPtr->funcs.createParagraph = createParagraph;
2135 tPtr->funcs.insertParagraph = insertParagraph;
2136 tPtr->funcs.createPChunk = createPChunk;
2137 tPtr->funcs.createTChunk = createTChunk;
2138 tPtr->funcs.insertChunk = insertChunk;
2140 tPtr->clicked.x = tPtr->clicked.y = -23;
2141 tPtr->cursor.x = tPtr->cursor.y = -23;
2143 tPtr->relief = WRSunken;
2144 tPtr->wrapping = wrWord;
2145 tPtr->editable = False;
2146 tPtr->cursorShown = False;
2147 tPtr->frozen = False;
2148 tPtr->focused = False;
2149 tPtr->pointerGrabbed = False;
2150 tPtr->buttonHeld = False;
2151 tPtr->ignoreNewLine = False;
2152 tPtr->waitingForSelection = False;
2153 tPtr->findingClickPoint = False;
2154 tPtr->foundClickPoint = False;
2155 tPtr->ownsSelection = False;
2156 tPtr->clheight = 0;
2157 tPtr->clwidth = 0;
2159 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2160 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2162 tPtr->view->delegate = &_TextViewDelegate;
2163 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
2164 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
2165 handleNonTextEvents, tPtr);
2166 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
2167 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
2168 handleTextEvents, tPtr);
2170 WMAddNotificationObserver(_notification, tPtr, "_lostOwnership", tPtr);
2172 WMSetTextMonoFont(tPtr, True);
2173 WMShowTextRuler(tPtr, False);
2174 WMSetTextHasHorizontalScroller(tPtr, False);
2175 WMSetTextHasVerticalScroller(tPtr, True);
2176 //printf("the sizeof chunk is %d\n", sizeof(Chunk));
2177 //printf("the sizeof para is %d\n", sizeof(Paragraph));
2178 //printf("the sizeof text is %d\n", sizeof(Text));
2179 return tPtr;
2182 //WMSetTextBullet()
2183 //WRetainPixmap(tPtr->dBulletPix);
2185 void
2186 WMRemoveTextParagraph(WMText *tPtr, int which)
2188 Paragraph *prior, *removed;
2189 if(!tPtr || which<0) return;
2191 WMSetTextCurrentParagraph(tPtr, which);
2192 removed = tPtr->currentPara;
2193 if(!removed) return;
2194 if(removed->chunks)printf("WMRemoveTextChunks\n");
2195 if(removed == tPtr->paragraphs || which==0) {
2196 tPtr->paragraphs = removed->next;
2197 } else {
2198 WMSetTextCurrentParagraph(tPtr, which-1);
2199 prior = tPtr->currentPara;
2200 if(!prior) return;
2201 prior->next = removed->next;
2203 wgdbFree(removed);
2204 // removeChunks
2205 removed = NULL;
2210 /* set what is known as the currentPara in the tPtr. */
2211 /* negative number means: "gib me last chunk" */
2212 void
2213 WMSetTextCurrentParagraph(WMText *tPtr, int current)
2215 Paragraph *tmp;
2216 int i=0;
2218 if(!tPtr || current<0) return;
2219 if(current == 0) {
2220 tPtr->currentPara = tPtr->paragraphs;
2221 return;
2223 tmp = tPtr->paragraphs;
2224 while(tmp->next && ((current==-23)?1:i++<current)) {
2225 //while(tmp && i++<current) {
2226 tmp = tmp->next;
2227 } tPtr->currentPara = tmp;
2228 //? want to do this?if(tmp) tPtr->currentChunk = tmp
2233 WMGetTextParagraphs(WMText *tPtr)
2235 int current=0;
2236 Paragraph *tmp;
2237 if(!tPtr) return 0;
2238 tmp = tPtr->paragraphs;
2239 while(tmp) {
2240 tmp = tmp->next;
2241 current++;
2242 } return current;
2248 WMGetTextCurrentParagraph(WMText *tPtr)
2250 int current=-1;
2251 Paragraph *tmp;
2253 if(!tPtr) return current;
2254 if(!tPtr->currentPara) return current;
2255 if(!tPtr->paragraphs) return current;
2256 tmp = tPtr->paragraphs;
2257 while(tmp) {
2258 current++;
2259 if(tmp == tPtr->currentPara)
2260 break;
2261 tmp = tmp->next;
2262 } return current;
2265 /* set what is known as the currentChunk within the currently
2266 selected currentPara (or the first paragraph in the document). */
2267 void
2268 WMSetTextCurrentChunk(WMText *tPtr, int current)
2270 Chunk *tmp;
2271 int i=0;
2273 if(!tPtr) return;
2274 tPtr->currentChunk = NULL;
2275 if(!tPtr->currentPara) {
2276 tPtr->currentPara = tPtr->paragraphs;
2277 if(!tPtr->currentPara)
2278 return;
2281 if(current == 0) {
2282 tPtr->currentChunk = tPtr->currentPara->chunks;
2283 return;
2285 tmp = tPtr->currentPara->chunks;
2286 if(tmp) {
2287 while(tmp->next && ((current<0)?1:i++<current))
2288 tmp = tmp->next;
2289 } tPtr->currentChunk = tmp;
2293 void
2294 WMRemoveTextChunk(WMText *tPtr, int which)
2296 Chunk *prior, *removed;
2297 Paragraph *para;
2298 if(!tPtr || which<0) return;
2299 para = tPtr->currentPara;
2300 if(!para) return;
2302 WMSetTextCurrentChunk(tPtr, which);
2303 removed = tPtr->currentChunk;
2304 if(!removed) return;
2305 if(removed == tPtr->currentPara->chunks || which==0) {
2306 para->chunks = removed->next;
2307 } else {
2308 WMSetTextCurrentChunk(tPtr, which-1);
2309 prior = tPtr->currentChunk;
2310 if(!prior) return;
2311 prior->next = removed->next;
2313 if(removed->type == ctText) {
2314 wgdbFree(removed->text);
2315 WMReleaseFont(removed->font);
2316 WMReleaseColor(removed->color);
2317 } else {
2318 WMReleasePixmap(removed->pixmap);
2320 wgdbFree(removed);
2321 removed = NULL;
2325 WMGetTextCurrentChunk(WMText *tPtr)
2327 int current=0;
2328 Chunk *tmp;
2330 if(!tPtr) return 0;
2331 if(!tPtr->currentChunk) return 0;
2332 if(!tPtr->currentPara) {
2333 tPtr->currentPara = tPtr->paragraphs;
2334 if(!tPtr->currentPara)
2335 return 0;
2338 tmp = tPtr->currentPara->chunks;
2339 while(tmp) {
2340 if(tmp == tPtr->currentChunk)
2341 break;
2342 tmp = tmp->next;
2343 current++;
2345 return current;
2349 WMGetTextChunks(WMText *tPtr)
2351 short current=0;
2352 Chunk *tmp;
2353 if(!tPtr || !tPtr->currentPara) return 0;
2354 tmp = tPtr->currentPara->chunks;
2355 while(tmp) {
2356 tmp = tmp->next;
2357 current++;
2358 } return current;
2361 void
2362 WMShowTextRuler(WMText *tPtr, Bool show)
2364 if(!tPtr) return;
2365 if(tPtr->monoFont) show = False;
2367 tPtr->rulerShown = show;
2368 if(show) WMMapWidget(tPtr->ruler);
2369 else WMUnmapWidget(tPtr->ruler);
2370 resizeText(tPtr->view->delegate, tPtr->view);
2373 Bool
2374 WMGetTextRulerShown(WMText *tPtr)
2376 if(!tPtr) return False;
2377 return tPtr->rulerShown;
2380 void
2381 WMSetTextRulerMargin(WMText *tPtr, char which, short pixels)
2383 if(!tPtr) return;
2384 if(tPtr->monoFont) return;
2385 WMSetRulerMargin(tPtr->ruler, which, pixels);
2386 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2389 short
2390 WMGetTextRulerMargin(WMText *tPtr, char which)
2392 if(!tPtr) return 0;
2393 if(tPtr->monoFont)
2394 return 0;
2395 return WMGetRulerMargin(tPtr->ruler, which);
2399 void
2400 WMShowTextRulerTabs(WMText *tPtr, Bool show)
2402 if(!tPtr) return;
2403 if(tPtr->monoFont) return;
2404 WMShowRulerTabs(tPtr->ruler, show);
2407 void
2408 WMSetTextMonoFont(WMText *tPtr, Bool mono)
2410 if(!tPtr) return;
2411 if(mono && tPtr->rulerShown)
2412 WMShowTextRuler(tPtr, False);
2414 tPtr->monoFont = mono;
2417 Bool
2418 WMGetTextMonoFont(WMText *tPtr)
2420 if(!tPtr) return True;
2421 return tPtr->monoFont;
2424 void
2425 WMForceTextFocus(WMText *tPtr)
2427 if(!tPtr) return;
2429 if(tPtr->clicked.x == -23 || tPtr->clicked.y == 23)
2430 cursorToTextPosition(tPtr, 100, 100); /* anyplace */
2431 else
2432 cursorToTextPosition(tPtr, tPtr->clicked.x, tPtr->clicked.y);
2436 void
2437 WMSetTextEditable(WMText *tPtr, Bool editable)
2439 if(!tPtr) return;
2440 tPtr->editable = editable;
2444 Bool
2445 WMGetTextEditable(WMText *tPtr)
2447 if(!tPtr) return 0;
2448 return tPtr->editable;
2452 Bool
2453 WMScrollText(WMText *tPtr, int amount)
2455 Bool scroll=False;
2456 if(amount == 0 || !tPtr) return;
2457 if(!tPtr->view->flags.realized) return;
2459 if(amount < 0) {
2460 if(tPtr->vpos > 0) {
2461 if(tPtr->vpos > amount) tPtr->vpos += amount;
2462 else tPtr->vpos=0;
2463 scroll=True;
2464 } } else {
2465 int limit = tPtr->docHeight - tPtr->visibleH;
2466 if(tPtr->vpos < limit) {
2467 if(tPtr->vpos < limit-amount) tPtr->vpos += amount;
2468 else tPtr->vpos = limit;
2469 scroll = True;
2472 if(scroll && tPtr->vpos != tPtr->prevVpos) {
2473 updateScrollers(tPtr);
2474 drawDocumentPartsOnPixmap(tPtr, False);
2475 paintText(tPtr);
2477 tPtr->prevVpos = tPtr->vpos;
2478 return scroll;
2481 Bool
2482 WMPageText(WMText *tPtr, Bool scrollUp)
2484 if(!tPtr) return;
2485 if(!tPtr->view->flags.realized) return;
2487 return WMScrollText(tPtr, scrollUp
2488 ? tPtr->visibleH:-tPtr->visibleH);
2491 void
2492 WMIgnoreTextNewline(WMText *tPtr, Bool ignore)
2494 if(!tPtr) return;
2495 tPtr->ignoreNewLine = ignore;
2499 void
2500 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool flag)
2502 if(tPtr) {
2503 short rh;
2504 if(tPtr->monoFont)
2505 return;
2506 rh = tPtr->rulerShown?40:0;
2507 tPtr->hasHscroller = flag;
2508 if(flag) {
2509 WMMapWidget(tPtr->hscroller);
2510 tPtr->visibleH = tPtr->view->size.height-rh-22;
2511 } else {
2512 WMUnmapWidget(tPtr->hscroller);
2513 tPtr->visibleH = tPtr->view->size.height-rh;
2515 resizeText(tPtr->view->delegate, tPtr->view);
2520 void
2521 WMSetTextHasVerticalScroller(WMText *tPtr, Bool flag)
2523 if(tPtr) {
2524 tPtr->hasVscroller = flag;
2525 if(flag) {
2526 WMMapWidget(tPtr->vscroller);
2527 tPtr->visibleW = tPtr->view->size.width-22;
2528 WMSetRulerOffset(tPtr->ruler, 22); /* scrollbar width + 2 */
2529 } else {
2530 WMUnmapWidget(tPtr->vscroller);
2531 tPtr->visibleW = tPtr->view->size.width;
2532 WMSetRulerOffset(tPtr->ruler, 2);
2534 resizeText(tPtr->view->delegate, tPtr->view);
2540 void
2541 WMRefreshText(WMText *tPtr, int vpos, int hpos)
2544 if(!tPtr)
2545 return;
2547 if(tPtr->frozen || !tPtr->view->flags.realized)
2548 return;
2551 XClearArea(tPtr->view->screen->display, tPtr->view->window,
2552 22, (tPtr->rulerShown)?45:5,
2553 tPtr->visibleW, tPtr->visibleH, True);
2555 calcDocExtents(tPtr);
2557 printf("vpos:%d tPtr->docHeight%d tPtr->visibleH%d \n",
2558 vpos, tPtr->docHeight, tPtr->visibleH);
2561 // tPtr->vpos = vpos;
2563 if(vpos < 0 || tPtr->docHeight < tPtr->visibleH)
2564 tPtr->vpos = 0;
2565 else if(vpos-tPtr->visibleH>tPtr->docHeight)
2566 tPtr->vpos = vpos-tPtr->docHeight-tPtr->visibleH-tPtr->docHeight;
2567 else
2568 tPtr->vpos = tPtr->docHeight-tPtr->visibleH;
2572 if(hpos < 0 || hpos > tPtr->docWidth)
2573 tPtr->hpos = 0;
2574 else
2575 tPtr->hpos = hpos;
2577 drawDocumentPartsOnPixmap(tPtr, True);
2578 updateScrollers(tPtr);
2579 paintText(tPtr);
2582 /* would be nice to have in WINGs proper... */
2583 static void
2584 changeFontProp(char *fname, char *newprop, short which)
2586 char before[128], prop[128], after[128];
2587 char *ptr, *bptr;
2588 int part=0;
2590 if(!fname || !prop)
2591 return;
2593 ptr = fname;
2594 bptr = before;
2595 while (*ptr) {
2596 if(*ptr == '-') {
2597 *bptr = 0;
2598 if(part==which) bptr = prop;
2599 else if(part==which+1) bptr = after;
2600 *bptr++ = *ptr;
2601 part++;
2602 } else {
2603 *bptr++ = *ptr;
2604 } ptr++;
2605 }*bptr = 0;
2606 snprintf(fname, 255, "%s-%s%s", before, newprop, after);
2609 /* TODO: put in wfont? */
2610 WMFont *
2611 WMGetFontPlain(WMScreen *scrPtr, WMFont *font)
2613 WMFont *nfont=NULL;
2614 if(!scrPtr|| !font)
2615 return NULL;
2616 return font;
2620 WMFont *
2621 WMGetFontBold(WMScreen *scrPtr, WMFont *font)
2623 WMFont *newfont=NULL;
2624 char fname[256];
2625 if(!scrPtr || !font)
2626 return NULL;
2627 snprintf(fname, 255, font->name);
2628 changeFontProp(fname, "bold", 2);
2629 newfont = WMCreateNormalFont(scrPtr, fname);
2630 if(!newfont)
2631 newfont = font;
2632 return newfont;
2635 WMFont *
2636 WMGetFontItalic(WMScreen *scrPtr, WMFont *font)
2638 WMFont *newfont=NULL;
2639 char fname[256];
2640 if(!scrPtr || !font)
2641 return NULL;
2642 snprintf(fname, 255, font->name);
2643 changeFontProp(fname, "o", 3);
2644 newfont = WMCreateNormalFont(scrPtr, fname);
2645 if(!newfont)
2646 newfont = font;
2647 return newfont;
2650 WMFont *
2651 WMGetFontOfSize(WMScreen *scrPtr, WMFont *font, short size)
2653 WMFont *nfont=NULL;
2654 if(!scrPtr || !font || size<1)
2655 return NULL;
2656 return font;
2658 /* */
2660 void
2661 WMFreezeText(WMText *tPtr)
2663 if(!tPtr)
2664 return;
2665 tPtr->frozen = True;
2668 void
2669 WMThawText(WMText *tPtr)
2671 if(!tPtr)
2672 return;
2673 tPtr->frozen = False;
2677 void
2678 WMSetTextDefaultAlignment(WMText *tPtr, WMAlignment alignment)
2680 if(!tPtr) return;
2681 if(tPtr->monoFont) return;
2683 tPtr->dAlignment = alignment;
2684 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2689 void
2690 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
2692 if(!tPtr)
2693 return;
2695 if(color)
2696 tPtr->bg = color;
2697 else
2698 tPtr->bg = WMWhiteColor(tPtr->view->screen);
2700 W_SetViewBackgroundColor(tPtr->view, tPtr->bg);
2701 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2704 void
2705 WMSetTextDefaultColor(WMText *tPtr, WMColor *color)
2707 if(!tPtr)
2708 return;
2710 if(color)
2711 tPtr->dColor = color;
2712 else
2713 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2716 void
2717 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
2719 if(!tPtr)
2720 return;
2722 if(font)
2723 tPtr->dFont = font;
2724 else
2725 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2728 void
2729 WMSetTextUseFixedPitchFont(Text *tPtr, Bool fixed)
2731 if(!tPtr)
2732 return;
2733 if(fixed)
2734 tPtr->dFont = WMCreateFontSet(tPtr->view->screen,
2735 "lucidasanstypewriter-12");
2736 else
2737 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2738 tPtr->fixedPitch = fixed;
2741 void
2742 WMSetTextParser(WMText *tPtr, WMParseAction *parser)
2744 if(!tPtr) return;
2745 if(tPtr->monoFont) return;
2746 tPtr->parser = parser;
2750 WMParserActions
2751 WMGetTextParserActions(WMText *tPtr)
2753 WMParserActions null;
2754 if(!tPtr) return null;
2755 return tPtr->funcs;
2759 char *
2760 WMGetTextAll(WMText *tPtr)
2762 char *text;
2763 int length=0;
2764 int where=0;
2765 Paragraph *para;
2766 Chunk *chunk;
2768 if(!tPtr) return NULL;
2770 para = tPtr->paragraphs;
2771 while(para) {
2772 chunk = para->chunks;
2773 while(chunk) {
2774 if(chunk->type == ctText) {
2775 if(chunk->text) length += chunk->chars;
2776 } else {
2777 printf("getting image \n");
2779 chunk = chunk->next;
2782 if(tPtr->ignoreNewLine) break;
2783 length += 4; // newlines
2784 para = para->next;
2787 text = wmalloc(length+1);
2789 para = tPtr->paragraphs;
2790 while(para) {
2791 chunk = para->chunks;
2792 while(chunk) {
2793 if(chunk->type == ctText) {
2794 if(chunk->text) {
2795 snprintf(&text[where], chunk->chars+1, "%s", chunk->text);
2796 where += chunk->chars;
2798 } else {
2799 printf("writing image \n");
2801 chunk = chunk->next;
2803 if(tPtr->ignoreNewLine) break;
2804 snprintf(&text[where++], 2, "\n");
2805 para = para->next;
2806 } text[where] = '\0';
2808 return text;
2811 void
2812 WMSetTextWriter(WMText *tPtr, WMParseAction *writer)
2814 if(!tPtr)
2815 return;
2816 if(tPtr->monoFont)
2817 return;
2818 tPtr->writer = writer;