added text widget from nwanua
[wmaker-crm.git] / WINGs / wtext.c
blob68ffea7476a5b2796d5eaed2fa93d362bc0b4c7e
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 <WINGs.h>
17 #include <WMaker.h>
18 #include <WINGsP.h>
19 #include <X11/keysym.h>
20 #include <X11/Xatom.h>
21 #include <ctype.h>
23 #include "wruler.h"
24 #include "wtext.h"
26 void wgdbFree(void *ptr)
27 { if(!ptr) printf("err... cannot ");
28 printf("gdbFree [%p]\n", ptr);
29 wfree(ptr);
33 typedef enum {ctText=0, ctImage=1} ChunkType;
34 typedef enum { dtDelete=0, dtBackSpace } DeleteType;
35 typedef enum {wrWord=0, wrChar=1, wrNone=2} Wrapping;
37 /* Why singly-linked and not say doubly-linked?
38 99% of the time (draw, append), the "prior"
39 member would have been a useless memory and CPU overhead,
40 and deletes _are_ relatively infrequent.
41 When the "prior" member needs to be used, the overhead of
42 doing things the hard way will be incurred... but seldomly. */
45 /* a Chunk is a singly-linked list of chunks containing:
46 o text with a given format
47 o or an image
48 o but NOT both */
49 typedef struct _Chunk {
50 char *text; /* the text in the chunk */
51 WMPixmap *pixmap; /* OR the pixmap it holds */
52 short chars; /* the number of characters in this chunk */
53 short mallocedSize; /* the number of characters that can be held */
55 WMFont *font; /* the chunk's font */
56 WMColor *color; /* the chunk's color */
57 short ul:1; /* underlined or not */
58 ChunkType type:1; /* a "Text" or "Image" chunk */
59 short script:4; /* script in points: negative for subscript */
60 //hrmm selec...
61 ushort selected;
62 ushort sStart;
63 ushort sEnd;
64 ushort RESERVED:10;
65 struct _Chunk *next;/*the next member in this list */
67 }Chunk;
71 /* a Paragraph is a singly-linked list of paragraphs containing:
72 o a list of chunks in that paragraph
73 o the formats for that paragraph
74 o its (draw) position relative to the entire document */
75 typedef struct _Paragraph {
76 Chunk *chunks; /* the list of text and/or image chunks */
77 short fmargin; /* the start position of the first line */
78 short bmargin; /* the start positions of the rest of the lines */
79 short rmargin; /* the end position of the entire paragraph */
80 short numTabs; /* the number of tabstops */
81 short *tabstops; /* an array of tabstops */
83 Pixmap drawbuffer; /* the pixmap onto which the (entire)
84 paragraph will be drawn */
85 WMPixmap *bulletPix;/* the pixmap to use for bulleting */
86 int top; /* the top of the paragraph relative to document */
87 int bottom; /* the bottom of the paragraph relative to document */
88 int width; /* the width of the paragraph */
89 int height; /* the height of the paragraph */
90 WMAlignment align:2;/* justification of this paragraph */
91 ushort RESERVED:14;
93 struct _Paragraph *next; /* the next member in this list */
94 } Paragraph;
97 static char *default_bullet[] = {
98 "6 6 4 1",
99 " c None s None", ". c black",
100 "X c white", "o c #808080",
101 " ... ",
102 ".XX.. ",
103 ".XX..o",
104 ".....o",
105 " ...oo",
106 " ooo "};
108 /* this is really a shrunk down version of the original
109 "broken" icon... I did not draw it, I simply shrunk it */
110 static char * unk_xpm[] = {
111 "24 24 17 1",
112 " c None", ". c #0B080C", "+ c #13A015", "@ c #5151B8",
113 "# c #992719", "$ c #5B1C20", "% c #1DF51D", "& c #D1500D", "* c #2F304A",
114 "= c #0C6A0C", "- c #F2F1DE", "; c #D59131", "> c #B2B083", ", c #DD731A",
115 "' c #CC3113", ") c #828238", "! c #6A6A94",
116 "......!@@@@@@@....$$....",
117 "...@!@@@@@@@**...$#'....",
118 "..!!@@@@@@@@.......#....",
119 "..!@@@@@@@@@*.......$...",
120 ".!@@@#,,#*@@*..*>.*.#...",
121 "*@@@@#'',,@@@...---!....",
122 "!@@@@@*.#;*@@..!--->....",
123 "@@@@@@@@#,.@@..!----@...",
124 "!@@@@@@*#;'$...!----@...",
125 "*@@@@@@..'&;;#.)----)...",
126 ".@@@@@@..$..&'.>----)...",
127 ".@@@@@@**---,'>-----!...",
128 ".@@@@@@**---,'>-----@...",
129 "..@@@@@@@---;;;,;---....",
130 "..*@@@@*@--->#',;,-*.)..",
131 "........)---->)@;#!..>..",
132 ".....)----------;$..>)..",
133 "=%%%*.*!-------);..)-*..",
134 "=%%%%+...*)>!@*$,.>--...",
135 "*+++++++.......*$@-->...",
136 "............**@)!)>->...",
137 "........................",
138 "........................",
139 "........................"};
141 typedef struct W_Text {
142 W_Class widgetClass; /* the class number of this widget */
143 W_View *view; /* the view referring to this instance */
144 WMColor *bg; /* the background color to use when drawing */
146 WMRuler *ruler; /* the ruler subwiget to maipulate paragraphs */
148 WMScroller *hscroller; /* the horizontal scroller */
149 short hpos; /* the current horizontal position */
150 short prevHpos; /* the previous horizontal position */
152 WMScroller *vscroller; /* the vertical scroller */
153 int vpos; /* the current vertical position */
154 int prevVpos; /* the previous vertical position */
156 int visibleW; /* the actual horizontal space available */
157 int visibleH; /* the actual vertical space available */
159 Paragraph *paragraphs; /* the linked list of the paragraphs in the doc. */
160 int docWidth; /* the width of the entire document */
161 int docHeight; /* the height of the entire document */
163 WMFont *dFont; /* the default font */
164 WMColor *dColor; /* the default color */
165 WMPixmap *dBulletPix; /* the default pixmap for bullets */
166 WMPixmap *dUnknownImg; /* the pixmap for (missing/broken) images */
168 WMRect sRect; /* the selected area */
169 Paragraph *currentPara; /* the current paragraph, in which actions occur */
170 Chunk *currentChunk; /* the current chunk, about which actions occur */
171 short tpos; /* the cursor position (text position) */
172 WMParseAction *parser; /* what action to use to parse input text */
173 WMParseAction *writer; /* what action to use to write text */
174 WMParserActions funcs; /* the "things" that parsers/writers might do */
175 XPoint clicked; /* the position of the last mouse click */
176 XPoint cursor; /* where the cursor is "placed" */
177 short clheight; /* the height of the "line" clicked on */
178 short clwidth; /* the width of the "line" clicked on */
180 WMReliefType relief:2; /* the relief to display with */
181 Wrapping wrapping:2; /* the type of wrapping to use in drawing */
182 WMAlignment dAlignment:2;/* default justification */
183 ushort monoFont:1; /* whether to ignore "rich" commands */
184 ushort fixedPitch:1; /* assume each char in dFont is the same size */
185 ushort editable:1; /* whether to accept user changes or not*/
186 ushort rulerShown:1; /* whether the ruler is shown or not */
187 ushort cursorShown:1; /* whether the cursor is currently being shown */
188 ushort frozen:1; /* whether screen updates are to be made */
189 ushort focused:1; /* whether this instance has input focus */
190 ushort pointerGrabbed:1;/* whether this instance has the pointer */
191 ushort buttonHeld:1; /* the user is still holding down the button */
192 ushort ignoreNewLine:1; /* whether to ignore the newline character */
193 ushort waitingForSelection:1; /* whether there is a pending paste event */
194 ushort ownsSelection:1; /* whether it ownz the current selection */
195 ushort findingClickPoint:1;/* whether the search for a clickpoint is on */
196 ushort foundClickPoint:1;/* whether the clickpoint has been found */
197 ushort hasVscroller:1; /* whether to enable the vertical scroller */
198 ushort hasHscroller:1; /* whether to enable the horizontal scroller */
199 ushort RESERVED:10;
200 } Text;
204 /* --------- static functions that are "private". don't touch :-) --------- */
207 /* max "characters per chunk that will be drawn at a time" */
208 #define MAX_WORD_LENGTH 100
209 /* max on a line */
210 #define MAX_CHUNX 64
211 #define MIN_DOC_WIDTH 200
212 typedef struct _LocalMargins {
213 short left, right, first, body;
214 } LocalMargins;
216 typedef struct _MyTextItems {
217 char text[MAX_WORD_LENGTH+1];
218 WMPixmap *pix;
219 short chars;
220 short x;
221 Chunk *chunk;/* used for "click" events */
222 short start; /* ditto... where in the chunk we start (ie. wrapped chunk) */
223 ushort type:1;
224 ushort RESERVED:15;
225 } MyTextItems;
228 static WMRect
229 chunkSelectionRect(Text *tPtr, Paragraph *para, MyTextItems item,
230 short y, short j, short lh)
232 WMRect rect;
233 short type=0; /* 0:none 1:partial: 2:all */
234 short rh=(tPtr->rulerShown)?45:5;
235 short w, lm;
236 WMFont *font = (tPtr->monoFont || item.chunk->type != ctText)?
237 tPtr->dFont:item.chunk->font;
239 rect.pos.x = -23;
240 if(y+para->top+rh > tPtr->sRect.pos.y+tPtr->sRect.size.height
241 || y+para->top+rh+lh < tPtr->sRect.pos.y)
242 return rect;
244 if(item.chunk->type == ctText)
245 w = WMWidthOfString(font, item.text, item.chars);
246 else w = item.chunk->pixmap->width;
248 if(y+para->top+rh >= tPtr->sRect.pos.y && (y+para->top+rh+lh
249 <= tPtr->sRect.pos.y+tPtr->sRect.size.height))
250 //&& item.x+j >= tPtr->sRect.pos.x+tPtr->sRect.size.width))
251 type = 2;
252 else type = 1;
255 #if 0
256 if(item.x+j >= tPtr->sRect.pos.x &&
257 item.x+j+w < tPtr->sRect.pos.x+tPtr->sRect.size.width)
258 type = 2;
260 if(type == 1 && y+para->top+rh+lh <=
261 tPtr->sRect.pos.y+tPtr->sRect.size.height)
262 type = 2;
263 #endif
266 if(type == 1 && item.chunk->type == ctText) { /* partial coverage */
267 lm = 2+WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
268 /* even I am still confused, so don't ask please */
269 if( (item.x+j+lm >= tPtr->sRect.pos.x &&
270 item.x+j+lm <= tPtr->sRect.pos.x+tPtr->sRect.size.width)
271 || (item.x+j+lm >= tPtr->sRect.pos.x+tPtr->sRect.size.width
272 && y+para->top+rh+lh <=
273 tPtr->sRect.pos.y+tPtr->sRect.size.height)
274 || (tPtr->sRect.pos.y < y+para->top+rh
275 && tPtr->sRect.pos.x+tPtr->sRect.size.width >
276 item.x+j+lm) ){
277 rect.size.width = w;
278 rect.pos.x = item.x+j;
279 item.chunk->selected = True;
280 if(item.chunk->chars > 6) {
281 item.chunk->sStart = 3;
282 item.chunk->sEnd = item.chunk->chars;
283 } else {
284 item.chunk->sStart = 0;
285 item.chunk->sEnd = item.chunk->chars;
288 } else if(type == 2) {
289 rect.pos.x = item.x+j;
290 item.chunk->selected = True;
291 if(item.chunk->type == ctText) {
292 item.chunk->sStart = 0;
293 item.chunk->sStart = item.chunk->chars;
294 rect.size.width = WMWidthOfString(font,
295 item.text, item.chars);
296 } else {
297 rect.size.width = item.chunk->pixmap->width;
301 rect.pos.y = y;
302 rect.size.height = lh;
303 return rect;
306 static int
307 myDrawText(Text *tPtr, Paragraph *para, MyTextItems *items,
308 short nitems, short pwidth, int y, short draw, short spacepos)
310 short i, ul_thick, u, j=0; /* j = justification */
311 short line_width = 0, line_height=0, mx_descent=0;
312 WMScreen *screen = tPtr->view->screen;
313 GC gc;
314 WMFont *font;
316 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0;
317 for(i=0; i<nitems; i++) {
318 if(items[i].type == ctText) {
319 font = (tPtr->monoFont)?tPtr->dFont:items[i].chunk->font;
320 mx_descent = WMIN(mx_descent, -font->y);
321 line_height = WMAX(line_height, font->height);
322 //printf("chunk.x %d xpoint.x %d\n",
323 // items[i].x, tPtr->clicked.x);
325 line_width += WMWidthOfString(font,
326 items[i].text, items[i].chars);
327 } else {
328 mx_descent = WMIN(mx_descent, -(items[i].pix->height-3));
329 /* replace -3 wif descent... */
330 line_height = WMAX(line_height, items[i].pix->height);
331 if(para->align == WARight || para->align == WACenter) {
332 line_width += items[i].pix->width;
333 } } }
335 if(para->align == WARight) {
336 j = pwidth - line_width;
337 } else if (para->align == WACenter) {
338 j = (short) ((float)(pwidth - line_width))/2.0;
341 if(tPtr->findingClickPoint && (y+line_height >= tPtr->clicked.y)) {
342 tPtr->foundClickPoint = True;
343 tPtr->currentChunk = items[0].chunk; /* just first on this "line" */
344 tPtr->tpos = items[0].start; /* where to "start" counting from */
345 tPtr->clicked.x = j+items[0].x;
346 tPtr->clicked.y = y+line_height+mx_descent;
347 tPtr->clheight = line_height; /* to draw the cursor */
348 tPtr->clwidth = line_width; /* where to stop searching */
349 return 0;
350 } if(!draw) return line_height;
352 for(i=0; i<nitems; i++) {
354 //account for vpos
355 if(tPtr->ownsSelection) {
356 WMRect rect = chunkSelectionRect(tPtr, para,
357 items[i], y, j, line_height);
358 if(rect.pos.x != -23) { /* has been selected */
359 XFillRectangle(tPtr->view->screen->display, para->drawbuffer,
360 WMColorGC(WMGrayColor(tPtr->view->screen)),
361 rect.pos.x, rect.pos.y, rect.size.width, rect.size.height);
365 if(items[i].type == ctText) {
366 gc = WMColorGC(items[i].chunk->color);
367 font = (tPtr->monoFont)?tPtr->dFont:items[i].chunk->font;
368 WMDrawString(screen, para->drawbuffer, gc, font,
369 items[i].x+j, y - font->y - mx_descent,
370 items[i].text, items[i].chars);
371 if(items[i].chunk->ul && !tPtr->monoFont) {
372 ul_thick = (short) ((float)font->height)/12.0;
373 if (ul_thick < 1) ul_thick = 1;
374 for(u=0; u<ul_thick; u++) {
375 XDrawLine(screen->display, para->drawbuffer, gc, items[i].x+j,
376 y + 1 + u - mx_descent,
377 items[i].x + j + WMWidthOfString(font,
378 items[i].text, items[i].chars), y + 1 + u - mx_descent);
381 } } else {
382 WMDrawPixmap(items[i].pix, para->drawbuffer, items[i].x+j,
383 y + 3 - mx_descent - items[i].pix->height);
384 } }
385 return line_height;
388 static void
389 drawPChunkPart(Text *tPtr, Chunk *chunk, LocalMargins m, Paragraph *para,
390 MyTextItems *items, short *nitems, short *Lmargin, XPoint *where, short draw)
392 short p_width;
394 if(!chunk) return;
395 if(!chunk->pixmap)
396 chunk->pixmap = WMRetainPixmap(tPtr->dUnknownImg);
398 p_width = m.right - WMIN(m.first, m.body) - WMGetRulerOffset(tPtr->ruler);
399 if(p_width < MIN_DOC_WIDTH) // need WMRuler to take care of this...
400 return;
401 if(where->x + chunk->pixmap->width <= p_width - *Lmargin) {
402 /* it can fit on rest of line */
403 items[*nitems].pix = chunk->pixmap;
404 items[*nitems].type = ctImage;
405 items[*nitems].chars = 0;
406 items[*nitems].x = *Lmargin+where->x;
407 items[*nitems].chunk = chunk;
408 items[*nitems].start = 0;
410 if(*nitems >= MAX_CHUNX) {
411 items[*nitems].chars = 0;
412 items[*nitems].x = *Lmargin+where->x;
413 items[*nitems].chunk = chunk;
414 items[*nitems].start = 0;
415 where->y += myDrawText(tPtr, para, items, *nitems+1,
416 p_width-*Lmargin, where->y, draw, 0);
417 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
418 *nitems = 0;
419 where->x = 0;
420 } else {
421 (*nitems)++;
422 where->x += chunk->pixmap->width;
424 } else if(chunk->pixmap->width <= p_width - *Lmargin) {
425 /* it can fit on an entire line, flush the myDrawText then wrap it */
426 where->y += myDrawText(tPtr, para, items, *nitems+1,
427 p_width-*Lmargin, where->y, draw, 0);
428 *nitems = 0;
429 *Lmargin = WMAX(0, m.body - m.first);
430 where->x = 0;
431 drawPChunkPart(tPtr, chunk, m, para, items, nitems,
432 Lmargin, where, draw);
433 } else {
434 #if 1
435 *nitems = 0;
436 where->x = 0;
437 *Lmargin = WMAX(0, m.body - m.first);
438 items[*nitems].pix = chunk->pixmap;
439 items[*nitems].type = ctImage;
440 items[*nitems].chars = 0;
441 items[*nitems].x = *Lmargin+where->x;
442 items[*nitems].chunk = chunk;
443 items[*nitems].start = 0;
444 where->y += myDrawText(tPtr, para, items, *nitems+1,
445 p_width-*Lmargin, where->y, draw, 0);
446 #endif
448 /* scale image to fit, call self again */
449 /* deprecated - the management */
454 static void
455 drawTChunkPart(Text *tPtr, Chunk *chunk, char *bufr, LocalMargins m,
456 Paragraph *para, MyTextItems *items, short *nitems, short len, short start,
457 short *Lmargin, XPoint *where, short draw, short spacepos)
459 short t_chunk_width, p_width, chars;
460 WMFont *font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
461 /* if(doc->clickstart.yes && doc->clickstart.done) return; */
463 if(len==0) return;
464 p_width = m.right - WMIN(m.first, m.body);
465 if(p_width < MIN_DOC_WIDTH) // need WMRuler to take care of this...
466 return;
469 t_chunk_width = WMWidthOfString(font, bufr, len);
470 if((where->x + t_chunk_width <= p_width - *Lmargin)
471 || (tPtr->wrapping == wrNone)) {
472 /* if it can fit on rest of line, append to line */
473 chars = WMIN(len, MAX_WORD_LENGTH);
474 snprintf(items[*nitems].text, chars+1, "%s", bufr);
475 items[*nitems].chars = chars;
476 items[*nitems].x = *Lmargin+where->x;
477 items[*nitems].type = ctText;
478 items[*nitems].chunk = chunk;
479 items[*nitems].start = start;
481 if(*nitems >= MAX_CHUNX) {
482 chars = WMIN(len, MAX_WORD_LENGTH);
483 snprintf(items[*nitems].text, chars+1, "%s", bufr);
484 items[*nitems].chars = chars;
485 items[*nitems].x = *Lmargin+where->x;
486 items[*nitems].type = ctText;
487 items[*nitems].chunk = chunk;
488 items[*nitems].start = start;
489 where->y += myDrawText(tPtr, para, items, *nitems+1,
490 p_width-*Lmargin, where->y, draw, spacepos);
491 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
492 *nitems = 0;
493 where->x = 0;
494 } else {
495 (*nitems)++;
496 where->x += t_chunk_width;
498 } else if(t_chunk_width <= p_width - *Lmargin) {
499 /* it can fit on an entire line, flush and wrap it to a new line */
500 where->y += myDrawText(tPtr, para, items, *nitems,
501 p_width-*Lmargin, where->y, draw, spacepos);
502 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
503 *nitems = 0;
504 *Lmargin = WMAX(0, m.body - m.first);
505 where->x = 0;
506 drawTChunkPart(tPtr, chunk, bufr, m, para, items, nitems,
507 len, start, Lmargin, where, draw, spacepos);
508 } else {
509 /* otherwise, chop line, call ourself recursively until it's all gone */
510 short J=0; /* bufr */
511 short j=0; /* local tmp buffer */
512 char tmp[len];
513 short diff = p_width - *Lmargin - where->x;
514 short _start=0;
516 if(diff < 20) {
517 where->y += myDrawText(tPtr, para, items, *nitems,
518 p_width-*Lmargin, where->y, draw, spacepos);
519 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
520 *nitems = 0;
521 *Lmargin = WMAX(0, m.body - m.first);
522 where->x = 0;
523 diff = p_width - *Lmargin - where->x;
526 for(J=0; J<len; J++) {
527 tmp[j] = bufr[J];
528 if(WMWidthOfString(font, tmp, j+1) > diff) {
529 drawTChunkPart(tPtr, chunk, tmp, m, para, items, nitems,
530 j, start+_start, Lmargin, where, draw, spacepos);
531 _start = J;
532 J--; j=0;
533 } else j++;
535 /* and there's always that last chunk, get it too */
536 drawTChunkPart(tPtr, chunk, tmp, m, para, items, nitems,
537 j, start+_start, Lmargin, where, draw, spacepos);
541 /* this function does what it's called :-)
542 o It is also used for calculating extents of para,
543 (returns height) so watch out for (Bool) draw
544 o Also used to determine where mouse was clicked */
545 static int
546 putParagraphOnPixmap(Text *tPtr, Paragraph *para, Bool draw)
548 char bufr[MAX_WORD_LENGTH+1]; /* a single word + '\0' */
549 MyTextItems items[MAX_CHUNX+1];
550 short lmargin, spacepos, i, s, nitems, start;
551 LocalMargins m;
552 XPoint where;
553 Chunk *chunk;
555 if(!tPtr->view->flags.realized || !para) return 0;
557 where.x = 0, where.y =0, nitems = 0;
558 m.left = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
559 m.right = WMGetRulerMargin(tPtr->ruler, WRulerRight) - m.left;
560 m.first = para->fmargin, m.body = para->bmargin;
562 if(draw) {
563 W_Screen *screen = tPtr->view->screen;
564 if(para->drawbuffer)
565 XFreePixmap(screen->display, para->drawbuffer);
566 if(para->width<2*tPtr->dFont->height) para->width = 2*tPtr->dFont->height;
567 if(para->height<tPtr->dFont->height) para->height = tPtr->dFont->height;
568 para->drawbuffer = XCreatePixmap(screen->display,
569 tPtr->view->window, para->width, para->height, screen->depth);
570 XFillRectangle(screen->display, para->drawbuffer,
571 WMColorGC(tPtr->bg), 0, 0, para->width, para->height);
575 //if(para->align != tPtr->dAlignment)
576 // para->align = tPtr->dAlignment;
578 /* draw the bullet if appropriate */
579 if(m.body>m.first && !tPtr->monoFont) {
580 lmargin = m.body - m.first;
581 if(draw) {
582 if(para->bulletPix)
583 WMDrawPixmap(para->bulletPix, para->drawbuffer, lmargin-10, 5);
584 else
585 WMDrawPixmap(tPtr->dBulletPix, para->drawbuffer, lmargin-10, 5);
587 /* NeXT sez next tab, I say the m.body - m.first margin */
588 } else {
589 lmargin = WMAX(0, m.first - m.body);
592 if(tPtr->findingClickPoint && !para->chunks) {
593 tPtr->currentChunk = NULL;
594 tPtr->foundClickPoint = True;
595 tPtr->tpos = 0;
596 tPtr->clicked.x = lmargin;
597 tPtr->clicked.y = 5;
598 tPtr->clheight = para->height;
599 tPtr->clwidth = 0;
600 return 0;
603 chunk = para->chunks;
604 while(chunk) {
606 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0;
608 if(chunk->type == ctImage && !tPtr->monoFont ) {
609 drawPChunkPart(tPtr, chunk, m, para, items, &nitems,
610 &lmargin, &where, draw);
611 } else if(chunk->text && chunk->type == ctText) {
612 if(tPtr->wrapping == wrNone) {
613 drawTChunkPart(tPtr, chunk, chunk->text, m, para, items, &nitems,
614 chunk->chars, 0, &lmargin, &where, draw, spacepos);
615 } else if(tPtr->wrapping == wrWord) {
616 spacepos=0, i=0, start=0;
617 while(spacepos < chunk->chars) {
618 bufr[i] = chunk->text[spacepos];
619 if( bufr[i] == ' ' || i >= MAX_WORD_LENGTH ) {
620 if(bufr[i] == ' ') s=1; else s=0;
621 drawTChunkPart(tPtr, chunk, bufr, m, para,
622 items, &nitems, i+s, start, &lmargin, &where,
623 draw, spacepos);
624 start = spacepos+s;
625 if(i > MAX_WORD_LENGTH-1)
626 spacepos--;
627 i=0;
628 } else i++;
629 spacepos++;
631 /* catch that last onery one. */
632 drawTChunkPart(tPtr, chunk, bufr, m, para,
633 items, &nitems, i, start, &lmargin, &where, draw, spacepos);
634 } } chunk = chunk->next;
636 /* we might have a few leftover items that need drawing */
637 if(nitems >0) {
638 where.y += myDrawText(tPtr, para, items,
639 nitems, m.right-m.left-lmargin, where.y, draw, spacepos);
640 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0;
642 return where.y;
645 static int
646 calcParaExtents(Text *tPtr, Paragraph *para)
648 if(!para) return 0;
650 if(tPtr->monoFont) {
651 para->width = tPtr->visibleW;
652 para->fmargin = 0;
653 para->bmargin = 0;
654 para->rmargin = tPtr->visibleW;
655 } else {
656 para->width = WMGetRulerMargin(tPtr->ruler, WRulerRight) -
657 WMIN(para->fmargin, para->bmargin)
658 - WMGetRulerOffset(tPtr->ruler);
661 if(!para->chunks)
662 para->height = tPtr->dFont->height;
663 else
664 para->height = putParagraphOnPixmap(tPtr, para, False);
666 if(para->height<tPtr->dFont->height)
667 para->height = tPtr->dFont->height;
668 para->bottom = para->top + para->height;
669 return para->height;
673 /* rather than bother with redrawing _all_ the pixmaps, simply
674 rearrange (i.e., push down or pull up) paragraphs after this one */
675 static void
676 affectNextParas(Text *tPtr, Paragraph *para, int move_y)
678 Paragraph *next;
679 int old_y = 0;
681 if(!para || move_y==0) return;
682 if(move_y == -23) {
683 old_y = para->bottom;
684 calcParaExtents(tPtr, para);
685 old_y -= para->bottom;
686 if(old_y == 0) return;
687 move_y = -old_y;
688 }if(move_y == 0) return;
690 next = para->next;
691 while(next) {
692 next->top += move_y;
693 next->bottom = next->top + next->height;
694 next = next->next; // I know, I know
695 }tPtr->docHeight += move_y;
697 #if 0
698 tPtr->vpos += move_y;
699 if(tPtr->vpos < 0) tPtr->vpos = 0;
700 if(tPtr->vpos > tPtr->docHeight - tPtr->visibleH)
701 tPtr->vpos = tPtr->docHeight - tPtr->visibleH;
702 #endif
707 static void
708 calcDocExtents(Text *tPtr)
710 Paragraph *para;
712 if(tPtr->monoFont) {
713 tPtr->docWidth = tPtr->visibleW;
714 } else {
715 tPtr->docWidth = WMGetRulerMargin(tPtr->ruler, WRulerRight) -
716 WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
718 tPtr->docHeight = 0;
719 para = tPtr->paragraphs;
720 if(para) {
721 while(para) {
722 para->top = tPtr->docHeight;
723 tPtr->docHeight += calcParaExtents(tPtr, para);
724 para->bottom = tPtr->docHeight;
725 para = para->next;
727 } else { /* default to this if no paragraphs */
728 tPtr->docHeight = tPtr->dFont->height;
730 #if 0
731 if(tPtr->editable) /* add space at bottom to enter new stuff */
732 tPtr->docHeight += tPtr->dFont->height;
733 #endif
737 /* If any part of a paragraph is viewable, the entire
738 paragraph is drawn on an otherwise empty (XFreePixmap) pixmap.
739 The actual viewable parts of the paragraph(s) are then pieced
740 together via paintText:
742 -------------------------------------------
743 || this is a paragraph in this document||
744 ||========================================||
745 || | only part of it is visible though. ||
746 || |-------------------------------------||
747 ||[.| This is another paragraph ||
748 || | which I'll make relatively long ||
749 || | just for the sake of writing a long ||
750 || | paragraph with a picture: ^_^ ||
751 || |-------------------------------------||
752 ||==| Of the three paragraphs, only ||
753 ||/\| the preceding was totally copied to ||
754 ||\/| totally copied to the window, even ||
755 ==========================================||
756 though they are all on pixmaps.
757 -------------------------------------------
758 This paragraph exists only in
759 memory and so has a NULL pixmap.
760 -------------------------------------------
763 simple, right? Performance: the best of both worlds...
764 o fast scrolling: no need to rewrite what's already
765 on the screen, simply XCopy it.
766 o fast typing: only change current para, then simply
767 affect other (i.e., subsequent) paragraphs.
768 o If no part of para is on screen, gdbFree pixmap; else draw on
769 individual pixmap per para then piece several paras together
770 o Keep track of who to XCopy to window (see paintText) */
771 static void
772 drawDocumentPartsOnPixmap(Text *tPtr, Bool all)
774 Paragraph *para;
776 para = tPtr->paragraphs;
777 while(para) {
778 /* the 32 reduces jitter on the human eye by preparing paragraphs
779 in anticipation of when the _moving_ scrollbar reaches them */
780 if(para->bottom + 32 < tPtr->vpos ||
781 para->top > tPtr->visibleH + tPtr->vpos + 32 ) {
782 if(para->drawbuffer) {
783 XFreePixmap(tPtr->view->screen->display, para->drawbuffer);
784 para->drawbuffer = (Pixmap) NULL;
786 } else {
787 if(!para->drawbuffer || all)
788 putParagraphOnPixmap(tPtr, para, True);
790 para = para->next;
796 /* this function blindly copies the "visible" parts of a pragraph
797 unto the view, (top-down approach). It starts drawing from
798 the top of the view (which paragraph to draw is determined by
799 drawDocumentPartsOnPixmap); it stops at the bottom of the view. */
800 static void
801 paintText(Text *tPtr)
803 short lmargin, para_lmargin;
804 int from=5, to=5, height;
805 Paragraph *para;
806 short vS=0, hS=0, rh=0;
808 if(!tPtr->view->flags.realized) return;
810 if(tPtr->rulerShown) rh = 40;
811 to += rh;
813 if(tPtr->hasVscroller) vS = 21;
814 if(tPtr->hasHscroller) hS = 21;
816 //XClearWindow(tPtr->view->screen->display, tPtr->view->window);
818 lmargin = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
819 if(tPtr->paragraphs) {
820 para = tPtr->paragraphs;
821 while(para) {
822 if(para->drawbuffer) {
823 from = (para->top<=tPtr->vpos)?tPtr->vpos-para->top:0;
824 height = para->height - from;
825 if(from>=0 && height>0 ) {
826 para_lmargin = WMIN(para->fmargin, para->bmargin);
827 if(lmargin-vS<WMIN(para->fmargin, para->bmargin)) {
828 #if 0
829 XClearArea(tPtr->view->screen->display, tPtr->view->window,
830 lmargin, to, 2+para_lmargin, height, False);
831 #else
832 XFillRectangle(tPtr->view->screen->display, tPtr->view->window,
833 WMColorGC(tPtr->dColor), lmargin, to, 2+para_lmargin, height);
834 #endif
836 XCopyArea(tPtr->view->screen->display, para->drawbuffer,
837 tPtr->view->window, WMColorGC(tPtr->bg), 0, from,
838 para->width-4, height, lmargin+para_lmargin+2, to);
839 if( (to+=height) > tPtr->visibleH+rh)
840 break;
841 } }
842 para = para->next;
846 #if 0
847 /* clear any left over space (esp. during para deletes/ ruler changes) */
848 if(tPtr->docHeight < tPtr->visibleH && tPtr->visibleH+rh+5-to>0) {
849 XClearArea(tPtr->view->screen->display, tPtr->view->window, vS, to,
850 tPtr->view->size.width-vS, tPtr->visibleH+rh+hS+5-to, False);
853 if(lmargin>vS)
854 XClearArea(tPtr->view->screen->display, tPtr->view->window,
855 vS+1, rh+5, lmargin-vS, tPtr->visibleH+rh+5-vS, False);
858 // from the "selection" days...
859 W_DrawRelief(tPtr->view->screen, WMWidgetXID(tPtr),
860 tPtr->sRect.pos.x, tPtr->sRect.pos.y,
861 tPtr->sRect.size.width, tPtr->sRect.size.height, tPtr->relief);
862 #endif
864 W_DrawRelief(tPtr->view->screen, WMWidgetXID(tPtr), 0, rh,
865 tPtr->visibleW+vS, tPtr->visibleH+hS, tPtr->relief);
867 if(tPtr->editable && tPtr->clheight > 0) {
868 int top = tPtr->cursor.y-tPtr->vpos;
869 int bot = top+tPtr->clheight;
870 if(bot>5) {
871 if(top<5) top=5;
872 if(bot>tPtr->visibleH+hS-2) bot = tPtr->visibleH+hS-2;
873 if(bot-top>1) {
874 //do something about italic text...
875 XDrawLine(tPtr->view->screen->display, tPtr->view->window,
876 WMColorGC(tPtr->dColor), lmargin+tPtr->cursor.x, top,
877 lmargin+tPtr->cursor.x, bot);
878 } } }
884 /* called anytime either the ruler, vscroller or hscroller is hidden/shown,
885 or when the widget is resized by some user action */
886 static void
887 resizeText(W_ViewDelegate *self, WMView *view)
889 Text *tPtr = (Text *)view->self;
890 short rh=0;
892 if(!tPtr->monoFont && tPtr->rulerShown)
893 rh = 40;
895 W_ResizeView(view, view->size.width, view->size.height);
896 WMResizeWidget(tPtr->ruler, view->size.width, 40);
899 if(tPtr->hasVscroller) {
900 WMMoveWidget(tPtr->vscroller, 1, 1+rh);
901 WMResizeWidget(tPtr->vscroller, 20, view->size.height-rh-2);
902 tPtr->visibleW = view->size.width-21;
904 if(tPtr->hasHscroller) {
905 WMMoveWidget(tPtr->hscroller, 20, view->size.height-21);
906 WMResizeWidget(tPtr->hscroller, view->size.width-21, 20);
907 tPtr->visibleH = view->size.height-21-rh;
908 } else tPtr->visibleH = view->size.height-rh;
909 } else {
910 tPtr->visibleW = view->size.width;
911 if(tPtr->hasHscroller) {
912 WMMoveWidget(tPtr->hscroller, 1, view->size.height-21);
913 WMResizeWidget(tPtr->hscroller, view->size.width-2, 20);
914 tPtr->visibleH = view->size.height-21-rh;
915 } else tPtr->visibleH = view->size.width-2-rh;
917 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
920 W_ViewDelegate _TextViewDelegate =
922 NULL,
923 NULL,
924 resizeText,
925 NULL,
930 /* a plain text parser */
931 /* this gives useful hints on how to make a more
932 interesting parser for say HTML, RTF */
933 static void
934 defaultParser(Text *tPtr, void *data, short type)
936 char *start, *mark, *text = (char *) data;
937 Chunk *chunk = NULL;
938 Paragraph *para = NULL;
940 start = text;
941 while(start) {
942 mark = strchr(start, '\n');
943 if(mark) {
944 /* there is a newline, indicating the need for a new paragraph */
945 /* attach the chunk to the current paragraph */
946 if((short)(mark-start) > 1) {
947 /* ignore chunks with just a single newline but still make a
948 blank paragraph */
949 chunk = (tPtr->funcs.createTChunk) (start, (short)(mark-start),
950 tPtr->dFont, tPtr->dColor, 0, False);
951 (tPtr->funcs.insertChunk) (tPtr, chunk, type);
953 /* _then_ create a new paragraph for the _next_ chunk */
954 para = (tPtr->funcs.createParagraph) (0, 0, tPtr->visibleW,
955 NULL, 0, WALeft);
956 (tPtr->funcs.insertParagraph) (tPtr, para, type);
957 start = mark+1;
958 } else {
959 /* just attach the chunk to the current paragraph */
960 if(strlen(start) > 0) {
961 chunk = (tPtr->funcs.createTChunk) (start, strlen(start),
962 tPtr->dFont, tPtr->dColor, 0, False);
963 (tPtr->funcs.insertChunk) (tPtr, chunk, type);
964 } start = mark;
965 } }
969 static void
970 updateScrollers(Text *tPtr)
972 if(tPtr->hasVscroller) {
973 if(tPtr->docHeight < tPtr->visibleH) {
974 WMSetScrollerParameters(tPtr->vscroller, 0, 1);
975 tPtr->vpos = 0;
976 } else {
977 float vmax = (float)(tPtr->docHeight);
978 WMSetScrollerParameters(tPtr->vscroller,
979 ((float)tPtr->vpos)/(vmax - (float)tPtr->visibleH),
980 (float)tPtr->visibleH/vmax);
984 if(tPtr->hasHscroller)
988 static void
989 scrollersCallBack(WMWidget *w, void *self)
991 Text *tPtr = (Text *)self;
992 Bool scroll = False;
993 Bool dimple = False;
995 if(!tPtr->view->flags.realized) return;
997 if(w == tPtr->vscroller) {
998 float vmax;
999 int height;
1000 vmax = (float)(tPtr->docHeight);
1001 height = tPtr->visibleH;
1002 if(height>7)
1003 height -= 7; /* the top border (5) + bottom (2) */
1005 switch(WMGetScrollerHitPart(tPtr->vscroller)) {
1006 case WSDecrementLine:
1007 if(tPtr->vpos > 0) {
1008 if(tPtr->vpos>16) tPtr->vpos-=16;
1009 else tPtr->vpos=0;
1010 scroll=True;
1011 }break;
1012 case WSIncrementLine: {
1013 int limit = tPtr->docHeight - height;
1014 if(tPtr->vpos < limit) {
1015 if(tPtr->vpos<limit-16) tPtr->vpos+=16;
1016 else tPtr->vpos=limit;
1017 scroll = True;
1018 }}break;
1019 case WSDecrementPage:
1020 tPtr->vpos -= height;
1022 if(tPtr->vpos < 0)
1023 tPtr->vpos = 0;
1024 dimple = True;
1025 scroll = True;
1026 printf("dimple needs to jump to mouse location ;-/\n");
1027 break;
1028 case WSIncrementPage:
1029 tPtr->vpos += height;
1030 if(tPtr->vpos > (tPtr->docHeight - height))
1031 tPtr->vpos = tPtr->docHeight - height;
1032 dimple = True;
1033 scroll = True;
1034 printf("dimple needs to jump to mouse location ;-/\n");
1035 break;
1038 case WSKnob:
1039 tPtr->vpos = WMGetScrollerValue(tPtr->vscroller)
1040 * (float)(tPtr->docHeight - height);
1041 scroll = True;
1042 break;
1044 #if 0
1045 case WSKnobSlot:
1046 case WSNoPart:
1047 float vmax = (float)(tPtr->docHeight);
1048 ((float)tPtr->vpos)/(vmax - (float)tPtr->visibleH),
1049 (float)tPtr->visibleH/vmax);
1050 dimple =where mouse is.
1051 #endif
1052 break;
1054 scroll = (tPtr->vpos != tPtr->prevVpos);
1055 tPtr->prevVpos = tPtr->vpos;
1058 if(w == tPtr->hscroller)
1061 //need scrollv || scrollh
1062 if(scroll) {
1064 if(0&&dimple) {
1065 if(tPtr->rulerShown)
1066 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
1067 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
1068 else
1069 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
1070 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
1073 updateScrollers(tPtr);
1074 drawDocumentPartsOnPixmap(tPtr, False);
1075 paintText(tPtr);
1080 void
1081 W_InsertText(WMText *tPtr, void *data, int position)
1083 if(!tPtr) return;
1084 if(!data) {
1085 Paragraph *para = tPtr->paragraphs, *ptmp;
1086 Chunk *chunk, *ctmp;
1087 WMFreezeText(tPtr);
1088 while(para) {
1089 chunk = para->chunks;
1090 while(chunk) {
1091 if(chunk->type == ctText && chunk->text)
1092 wgdbFree(chunk->text);
1093 else if(chunk->pixmap)
1094 WMReleasePixmap(chunk->pixmap);
1095 ctmp = chunk;
1096 chunk = chunk->next;
1097 wgdbFree(ctmp);
1099 ptmp = para;
1100 para = para->next;
1101 if(ptmp->drawbuffer)
1102 XFreePixmap(tPtr->view->screen->display, ptmp->drawbuffer);
1103 wgdbFree(ptmp);
1105 tPtr->paragraphs = NULL;
1106 tPtr->currentPara = NULL;
1107 tPtr->currentChunk = NULL;
1108 WMThawText(tPtr);
1109 WMRefreshText(tPtr, 0, 0);
1110 return;
1113 if(tPtr->parser)
1114 (tPtr->parser)(tPtr, data, position >= 0 ? 1 : 0);
1115 else
1116 defaultParser(tPtr, data, position >= 0 ? 1 : 0);
1119 static void
1120 cursorToTextPosition(Text *tPtr, int x, int y)
1122 Paragraph *para = NULL;
1123 Chunk *chunk = NULL;
1124 WMFont *font;
1125 short line_width=0;
1126 short orig_x, orig_y;
1128 if(x<(tPtr->hasVscroller?21:1)) {
1129 y -= tPtr->clheight;
1130 x = tPtr->view->size.width; //tPtr->visibleW;
1131 } else if(x>tPtr->clwidth && x<tPtr->clicked.x) {
1132 //x = (tPtr->hasVscroller)?21:1;
1133 //y += tPtr->clheight;
1136 if(x<0) x=0;
1137 orig_x = x;
1139 if(y<0 || y>tPtr->view->size.height-3) return;
1140 orig_y = y;
1141 tPtr->clicked.x = orig_x;
1142 tPtr->clicked.y = y;
1143 tPtr->clicked.y += tPtr->vpos;
1144 tPtr->clicked.y -= tPtr->rulerShown?40:0;
1145 para = tPtr->paragraphs;
1146 if(!para) return;
1147 while(para->next) {
1148 if( tPtr->clicked.y>= para->top-4 &&
1149 tPtr->clicked.y < para->bottom+4) break;
1150 para = para->next;
1151 } if(!(tPtr->currentPara = para)) return;
1153 tPtr->clicked.y -= para->top;
1154 if(tPtr->clicked.y<0) tPtr->clicked.y=0;
1155 if(tPtr->hasVscroller) x -= 21;
1156 if(x<0) x=0;
1158 tPtr->findingClickPoint = True;
1159 tPtr->foundClickPoint = False;
1160 /* also affects tPtr->currentChunk, tPtr->clicked.x and y,
1161 tPtr->clheight and ->width */
1162 putParagraphOnPixmap(tPtr, para, False);
1163 tPtr->findingClickPoint = False;
1164 tPtr->clicked.y += para->top;
1166 if(tPtr->currentChunk) {
1167 short _width=0, start=tPtr->tpos, done=False, w=0;
1168 chunk = tPtr->currentChunk;
1169 while(!done && chunk && line_width<tPtr->clwidth) {
1170 if(chunk->type == ctText) {
1171 font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
1172 for (w=start; w<chunk->chars; w++) {
1173 _width = WMWidthOfString(font, &chunk->text[w], 1);
1174 line_width += _width;
1175 if(line_width+tPtr->clicked.x >= x) {
1176 line_width -= _width;
1177 done = True;
1178 printf("break\n");
1179 break;
1182 if(0&&chunk->next) {
1183 if(chunk->next->type == ctImage) {
1184 if(x+10 < line_width+chunk->next->pixmap->width) {
1185 printf("true\n");
1186 done = True;
1187 } } }
1188 } else {
1189 _width = chunk->pixmap->width;
1190 line_width += _width;
1191 if(line_width+tPtr->clicked.x >= x) {
1192 line_width -= _width;
1193 tPtr->tpos = 0;
1194 done = True;
1197 if(!done) {
1198 chunk = chunk->next;
1199 start = w = 0;
1200 } else {
1201 tPtr->tpos = w;
1202 tPtr->currentChunk = chunk;
1203 break;
1204 } } } else {
1205 short vS = (tPtr->hasVscroller)?32:12;
1206 if(para->align == WARight) {
1207 tPtr->clicked.x = tPtr->view->size.width-vS;
1208 } else if (para->align == WACenter) {
1209 tPtr->clicked.x = -(vS/2)+(tPtr->view->size.width-vS)/2;
1210 } else {
1211 tPtr->clicked.x = 2;
1214 tPtr->cursor.x = tPtr->clicked.x+2+line_width;
1215 tPtr->cursor.y = tPtr->clicked.y;
1216 tPtr->clicked.y = orig_y;
1217 tPtr->clicked.x = orig_x;
1218 putParagraphOnPixmap(tPtr, para, True);
1219 paintText(tPtr);
1222 static void
1223 deleteTextInteractively(Text *tPtr, DeleteType type)
1225 Paragraph *para;
1226 Chunk *chunk;
1227 short pos,w=0,h=0, doprev=False, doprevpara=False;
1228 WMFont *font;
1229 int current = WMGetTextCurrentChunk(tPtr);
1231 if(!(para = tPtr->currentPara)) return;
1232 if(!(chunk = tPtr->currentChunk)) return;
1233 font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
1234 doprev = (tPtr->tpos < 2);
1236 switch(type) {
1237 case dtDelete: /* delete _after_ cursor ... implement later */
1238 case dtBackSpace: /* delete _before_ cursor */
1239 if(chunk->chars > 1) {
1240 pos = tPtr->tpos-1;
1241 printf("here %d\n", pos);
1242 if(pos>0) {
1243 w = WMWidthOfString(font, &chunk->text[pos], 1);
1244 memmove(&(chunk->text[pos]),
1245 &(chunk->text[pos+1]), chunk->chars-pos+1);
1246 tPtr->tpos--;
1247 chunk->chars--;
1248 } } else {
1249 WMRemoveTextChunk(tPtr, current);
1250 doprev = True;
1253 if(doprev) {
1254 if(current > 0) {
1255 WMSetTextCurrentChunk(tPtr, current-1);
1256 if(!tPtr->currentChunk) {
1257 printf("PREV PARA\n");
1258 } else {
1259 tPtr->tpos = tPtr->currentChunk->chars;
1261 } else if(0){
1262 int currentp = WMGetTextCurrentParagraph(tPtr);
1263 doprevpara = True;
1264 if(currentp > 1) {
1265 para->chunks = NULL;
1266 WMRemoveTextParagraph(tPtr, currentp);
1267 WMSetTextCurrentParagraph(tPtr, currentp-1);
1268 WMSetTextCurrentChunk(tPtr, -1);
1269 para = tPtr->currentPara;
1270 if(para) {
1271 if(!tPtr->currentChunk || !para->chunks) {
1272 para->chunks = chunk;
1273 tPtr->currentChunk = chunk;
1274 } else
1275 tPtr->currentChunk->next = chunk;
1276 } } } } }
1278 if(1) { //if(1||(para && !doprevpara)) {
1279 affectNextParas(tPtr, para, -23);
1280 putParagraphOnPixmap(tPtr, para, True);
1281 drawDocumentPartsOnPixmap(tPtr, False);
1282 updateScrollers(tPtr);
1283 paintText(tPtr);
1284 //cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1285 } else WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1289 /* give us nice chunk sizes (multiples of 16) */
1290 static short
1291 reqBlockSize(short requested)
1293 return requested+16-(requested%16);
1296 static void
1297 insertTextInteractively(Text *tPtr, char *text)
1299 Paragraph *para=NULL;
1300 Chunk *chunk=NULL, *newchunk=NULL;
1301 int height = -23; /* should only be changed upon newline */
1302 short w=0,h=0;
1303 WMFont *font;
1305 if(!tPtr->editable) return;
1306 if(*text == '\n' && tPtr->ignoreNewLine)
1307 return;
1309 para = tPtr->currentPara;
1310 chunk = tPtr->currentChunk;
1311 font = (tPtr->monoFont || !chunk)?tPtr->dFont:chunk->font;
1313 if(*text == '\n') {
1314 int new_top=0;
1315 if(chunk) { /* there's a chunk (or part of it) to detach from old */
1316 int current = WMGetTextCurrentChunk(tPtr);
1317 if(tPtr->tpos <=0) { /* at start of chunk */
1318 if(current<1) { /* the first chunk... make old para blank */
1319 newchunk = para->chunks;
1320 para->chunks = NULL;
1321 putParagraphOnPixmap(tPtr, para, True);
1322 } else { /* not first chunk... */
1323 printf("cut me out \n");
1325 } else if(tPtr->tpos < chunk->chars && chunk->type == ctText) {
1326 /* not at start of chunk */
1327 char text[chunk->chars-tPtr->tpos+1];
1328 int i=0;
1329 do {
1330 text[i] = chunk->text[tPtr->tpos+i];
1331 } while(++i < chunk->chars-tPtr->tpos);
1332 chunk->chars -= i;
1333 newchunk = (tPtr->funcs.createTChunk) (text, i, chunk->font,
1334 chunk->color, chunk->script, chunk->ul);
1335 newchunk->next = chunk->next;
1336 chunk->next = NULL;
1337 /* might want to demalloc for LARGE cuts */
1338 //calcParaExtents(tPtr, para);
1339 para->height = putParagraphOnPixmap(tPtr, para, True);
1340 //putParagraphOnPixmap(tPtr, para, True);
1341 } else if(tPtr->tpos >= chunk->chars) {
1342 Chunk *prev;
1343 WMSetTextCurrentChunk(tPtr, current-1);
1344 prev = tPtr->currentChunk;
1345 if(!prev) return;
1346 newchunk = prev->next;
1347 prev->next = NULL;
1348 putParagraphOnPixmap(tPtr, para, True);
1350 } else newchunk = NULL;
1352 if(para) /* the preceeding one */
1353 new_top = para->bottom;
1355 WMAppendTextStream(tPtr, "\n");
1356 para = tPtr->currentPara;
1357 if(!para) return;
1358 para->chunks = newchunk;
1359 tPtr->currentChunk = newchunk;
1360 tPtr->tpos = 0;
1361 para->top = new_top;
1362 calcParaExtents(tPtr, para);
1363 height = para->height;
1364 } else {
1365 if(!para) {
1366 WMAppendTextStream(tPtr, text);
1367 para = tPtr->currentPara;
1368 } else if(!para->chunks || !chunk) {
1369 //WMPrependTextStream(tPtr, text);
1370 WMAppendTextStream(tPtr, text);
1371 } else if(chunk->type == ctImage) {
1372 WMPrependTextStream(tPtr, text);
1374 printf("\n\nprepe\n\n");
1375 } else {
1376 if(tPtr->tpos > chunk->chars) {
1377 printf("\n\nmore\n\n");
1378 tPtr->tpos = chunk->chars;
1381 if(chunk->chars+1 >= chunk->mallocedSize) {
1382 chunk->mallocedSize = reqBlockSize(chunk->chars+1);
1383 chunk->text = wrealloc(chunk->text, chunk->mallocedSize);
1386 memmove(&(chunk->text[tPtr->tpos+1]), &chunk->text[tPtr->tpos],
1387 chunk->chars-tPtr->tpos+1);
1388 w = WMWidthOfString(font, text, 1);
1389 memmove(&chunk->text[tPtr->tpos], text, 1);
1390 chunk->chars++;
1391 tPtr->tpos++;
1392 //doc->clickstart.cursor.x +=
1393 //WMWidthOfString(chunk->fmt->font, text,len);
1398 if(para) {
1399 affectNextParas(tPtr, para, height);
1400 putParagraphOnPixmap(tPtr, para, True);
1401 drawDocumentPartsOnPixmap(tPtr, False);
1402 updateScrollers(tPtr);
1403 paintText(tPtr);
1404 //cursorToTextPosition(tPtr, tPtr->clicked.x+w, tPtr->clicked.y);
1405 //check for "sneppah tahw" with blank paras...
1406 //paintText(tPtr);
1411 static void
1412 selectRegion(Text *tPtr, int x, int y)
1414 tPtr->sRect.pos.x = WMIN(tPtr->clicked.x, x);
1415 tPtr->sRect.size.width = abs(tPtr->clicked.x-x);
1416 tPtr->sRect.pos.y = WMIN(tPtr->clicked.y, y);
1417 if(tPtr->sRect.pos.y<0) tPtr->sRect.pos.y=0;
1418 tPtr->sRect.size.height = abs(tPtr->clicked.y-y);
1421 while(y>tPtr->visibleH && tPtr->vpos < tPtr->docHeight-tPtr->visibleH) {
1422 WMRefreshText(tPtr, tPtr->vpos+16, tPtr->hpos);
1425 //printf("%d %d \n", y, tPtr->vpos);
1427 //foreach para in selection...
1428 drawDocumentPartsOnPixmap(tPtr, True);
1429 paintText(tPtr);
1436 #define WM_EMACSKEYMASK ControlMask
1437 #define WM_EMACSKEY_LEFT XK_b
1438 #define WM_EMACSKEY_RIGHT XK_f
1439 #define WM_EMACSKEY_HOME XK_a
1440 #define WM_EMACSKEY_END XK_e
1441 #define WM_EMACSKEY_BS XK_h
1442 #define WM_EMACSKEY_DEL XK_d
1444 static void
1445 handleTextKeyPress(Text *tPtr, XEvent *event)
1447 char buffer[2];
1448 KeySym ksym;
1449 int control_pressed = False;
1451 if(!tPtr->editable) return;
1453 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
1454 control_pressed = True;
1455 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = '\0';
1457 switch(ksym) {
1459 case XK_Right:
1460 case XK_Left:
1461 if(tPtr->currentChunk) {
1462 short w;
1463 Chunk *chunk = tPtr->currentChunk;
1464 if(chunk->type == ctText) {
1465 WMFont *font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
1466 if(ksym==XK_Right) {
1467 short pos = (tPtr->tpos<chunk->chars)?tPtr->tpos+1:
1468 chunk->chars;
1469 w = WMWidthOfString(font,&chunk->text[pos],1);
1470 } else {
1471 short pos = (tPtr->tpos>0)?tPtr->tpos-1:0;
1472 w = WMWidthOfString(font,&chunk->text[pos],1);
1474 } else { w = chunk->pixmap->width; }
1475 if(ksym==XK_Right) w = -w;
1476 cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1477 } else {
1478 if(ksym==XK_Right) ksym = XK_Down;
1479 else ksym = XK_Up;
1480 goto noCChunk;
1482 break;
1484 case XK_Down:
1485 case XK_Up:
1486 noCChunk: { short h = tPtr->clheight-2;
1487 if(ksym==XK_Down) h = -h;
1488 cursorToTextPosition(tPtr, tPtr->clicked.x, tPtr->clicked.y-h);
1489 } break;
1491 case XK_BackSpace:
1492 deleteTextInteractively(tPtr, dtBackSpace);
1493 break;
1495 case XK_Delete:
1496 case XK_KP_Delete:
1497 deleteTextInteractively(tPtr, dtDelete);
1498 break;
1500 case XK_Return:
1501 buffer[0] = '\n';
1502 default:
1503 if(buffer[0] != '\0' && (buffer[0] == '\n' || !iscntrl(buffer[0])))
1504 insertTextInteractively(tPtr, buffer);
1505 else if(control_pressed && ksym==XK_r)
1506 {Bool i = !tPtr->rulerShown; WMShowTextRuler(tPtr, i);
1507 tPtr->rulerShown = i; }
1514 static void
1515 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1516 void *cdata, WMData *data)
1518 Text *tPtr = (Text *)view->self;
1519 char *str;
1522 tPtr->waitingForSelection = False;
1523 if(data) {
1524 str = (char*)WMDataBytes(data);
1525 if(tPtr->tpos<1) WMPrependTextStream(tPtr, str);
1526 else WMAppendTextStream(tPtr, str);
1527 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1528 } else {
1529 int n;
1530 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1531 if(str) {
1532 str[n] = 0;
1533 if(tPtr->tpos<1) WMPrependTextStream(tPtr, str);
1534 else WMAppendTextStream(tPtr, str);
1535 XFree(str);
1536 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1542 static void
1543 releaseSelection(Text *tPtr)
1545 Paragraph *para = tPtr->paragraphs;
1546 Chunk *chunk;
1547 while(para) {
1548 chunk = para->chunks;
1549 while(chunk) {
1550 chunk->selected = False;
1551 chunk = chunk->next;
1553 para = para->next;
1555 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1556 tPtr->ownsSelection = False;
1557 drawDocumentPartsOnPixmap(tPtr, True);
1558 paintText(tPtr);
1562 static WMData*
1563 requestHandler(WMView *view, Atom selection, Atom target,
1564 void *cdata, Atom *type)
1566 Text *tPtr = view->self;
1567 int count;
1568 Display *dpy = tPtr->view->screen->display;
1569 Atom _TARGETS;
1570 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1571 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1572 WMData *data = NULL;
1575 if(!tPtr->ownsSelection || !tPtr->paragraphs) return NULL;
1576 //printf("got here\n");
1578 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
1579 //for bleh in selection...
1580 char *s = NULL;
1581 Paragraph *para = tPtr->paragraphs;
1582 Chunk *chunk = NULL;
1583 char pixmap[] = "[pixmap]";
1584 Bool first=True;
1585 short len;
1587 while(para) {
1588 chunk = para->chunks;
1589 while(chunk) {
1591 if(chunk->selected && chunk->type == ctText) {
1592 len = chunk->chars; //chunk->sEnd - chunk->sStart;
1593 if(len>0) {
1594 s = wmalloc(len+1);
1595 if(s) {
1596 memcpy(s, &chunk->text[0*chunk->sStart], len);
1597 s[len] = 0;
1598 if(first) {
1599 data = WMCreateDataWithBytes(s, strlen(s));
1600 first = False;
1601 } else {
1602 printf("append: %c %d\n", *s, strlen(s));
1603 WMAppendDataBytes(data, s, strlen(s));
1605 //gdbFree(s);
1606 } } }
1607 #if 0
1608 printf("len is %d [%d %d] %d \n", len, chunk->sStart, chunk->sEnd,
1609 chunk->chars);
1610 #endif
1611 chunk = chunk->next;
1613 para = para->next;
1616 if(data) {
1617 WMSetDataFormat(data, 8);
1618 *type = target;
1620 return data;
1623 #if 0
1624 _TARGETS = XInternAtom(dpy, "TARGETS", False);
1625 if (target == _TARGETS) {
1626 Atom *ptr = wmalloc(4 * sizeof(Atom));
1627 ptr[0] = _TARGETS;
1628 ptr[1] = XA_STRING;
1629 ptr[2] = TEXT;
1630 ptr[3] = COMPOUND_TEXT;
1632 data = WMCreateDataWithBytes(ptr, 4*4);
1633 WMSetDataFormat(data, 32);
1635 *type = target;
1636 return data;
1638 #endif
1640 return NULL;
1645 static void
1646 lostHandler(WMView *view, Atom selection, void *cdata)
1648 WMText *tPtr = (WMText *)view->self;
1649 releaseSelection(tPtr);
1652 static WMSelectionProcs selectionHandler = {
1653 requestHandler, lostHandler, NULL };
1655 static void
1656 _notification(void *observerData, WMNotification *notification)
1658 WMText *to = (WMText *)observerData;
1659 WMText *tw = (WMText *)WMGetNotificationClientData(notification);
1660 if (to != tw) lostHandler(to->view, XA_PRIMARY, NULL);
1663 static void
1664 handleTextEvents(XEvent *event, void *data)
1666 Text *tPtr = (Text *)data;
1667 Display *dpy = event->xany.display;
1669 if(tPtr->waitingForSelection) return;
1671 switch (event->type) {
1672 case KeyPress:
1673 if(!tPtr->editable || tPtr->buttonHeld) {
1674 XBell(dpy, 0);
1675 return;
1677 if(tPtr->ownsSelection) releaseSelection(tPtr);
1678 //if (tPtr->waitingForSelection) return;
1679 if(tPtr->focused) {
1680 #if 0
1681 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1682 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1683 GrabModeAsync, GrabModeAsync, None,
1684 W_VIEW(tPtr)->screen->invisibleCursor, CurrentTime);
1685 tPtr->pointerGrabbed = True;
1686 #endif
1687 handleTextKeyPress(tPtr, event);
1688 } break;
1690 case MotionNotify:
1691 if(tPtr->pointerGrabbed) {
1692 tPtr->pointerGrabbed = False;
1693 XUngrabPointer(dpy, CurrentTime);
1695 if((event->xmotion.state & Button1Mask)) {
1696 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1697 if(!tPtr->ownsSelection) {
1698 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1699 event->xbutton.time, &selectionHandler, NULL);
1700 tPtr->ownsSelection = True;
1702 break;
1704 case ButtonPress:
1705 if(event->xbutton.button == Button1) {
1706 if(tPtr->ownsSelection) releaseSelection(tPtr);
1707 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1708 if (tPtr->pointerGrabbed) {
1709 tPtr->pointerGrabbed = False;
1710 XUngrabPointer(dpy, CurrentTime);
1711 break;
1714 if(!tPtr->focused) {
1715 WMSetFocusToWidget(tPtr);
1716 tPtr->focused = True;
1717 break;
1719 if(event->xbutton.button == 4)
1720 WMScrollText(tPtr, -16);
1721 else if(event->xbutton.button == 5)
1722 WMScrollText(tPtr, 16);
1724 break;
1726 case ButtonRelease:
1727 tPtr->buttonHeld = False;
1728 if (tPtr->pointerGrabbed) {
1729 tPtr->pointerGrabbed = False;
1730 XUngrabPointer(dpy, CurrentTime);
1731 break;
1733 if(event->xbutton.button == 4 || event->xbutton.button == 5)
1734 break;
1735 if(event->xbutton.button == Button2 && tPtr->editable) {
1736 char *text = NULL;
1737 int n;
1738 if(!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1739 event->xbutton.time, pasteText, NULL)) {
1740 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1741 if(text) {
1742 text[n] = 0;
1743 WMAppendTextStream(tPtr, text);
1744 XFree(text);
1745 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1746 } else tPtr->waitingForSelection = True;
1747 } } break;
1754 static void
1755 handleNonTextEvents(XEvent *event, void *data)
1757 Text *tPtr = (Text *)data;
1759 switch(event->type) {
1760 case Expose:
1761 if(!event->xexpose.count && tPtr->view->flags.realized)
1762 paintText(tPtr);
1763 break;
1765 case FocusIn:
1766 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
1767 return;
1768 tPtr->focused = True;
1769 //cursor...paintText(tPtr);
1770 break;
1772 case FocusOut:
1773 tPtr->focused = False;
1774 //cursor...paintText(tPtr);
1775 break;
1777 case DestroyNotify:
1778 printf("destroy");
1779 //for(...)WMRemoveTextParagraph(tPtr, para);
1780 break;
1784 //printf("handleNonTextEvents\n");
1789 static void
1790 rulerCallBack(WMWidget *w, void *self)
1792 Text *tPtr = (Text *)self;
1793 short which;
1795 if(tPtr->currentPara) {
1796 Paragraph *para = tPtr->currentPara;
1797 para->fmargin = WMGetRulerMargin(tPtr->ruler, WRulerFirst);
1798 para->bmargin = WMGetRulerMargin(tPtr->ruler, WRulerBody);
1799 para->rmargin = WMGetRulerMargin(tPtr->ruler, WRulerRight);
1800 affectNextParas(tPtr, para, -23);
1801 putParagraphOnPixmap(tPtr, para, True);
1803 #if 0
1804 which = WMGetReleasedRulerMargin(tPtr->ruler);
1805 if(which != WRulerDocLeft && which != WRulerRight
1806 /* && Selection.para.count > 0 */ ) {
1807 printf(""
1808 "//for(i=0; i<Selection.para.count; i++) {"
1809 "affect"
1810 "//calcParaExtents(tPtr, para);}\n");
1811 } else {
1812 WMRefreshText(tPtr, 0, 0);
1814 #endif
1818 static void
1819 rulerMoveCallBack(WMWidget *w, void *self)
1821 Text *tPtr = (Text *)self;
1822 short rmargin = WMGetRulerMargin(tPtr->ruler, WRulerRight);
1825 if(WMGetGrabbedRulerMargin(tPtr->ruler) == WRulerLeft) {
1826 short lmargin = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
1827 XClearArea(tPtr->view->screen->display, tPtr->view->window,
1828 22, 42, lmargin-21, tPtr->visibleH, True);
1829 } else if(WMGetGrabbedRulerMargin(tPtr->ruler) == WRulerRight &&
1830 tPtr->docWidth+11 < rmargin) {
1831 XClearArea(tPtr->view->screen->display, tPtr->view->window,
1832 rmargin-3, 42, 10, tPtr->visibleH, True);
1834 paintText(tPtr);
1839 /* ------------- non-static functions that are "friends" ------------- */
1840 /* ------------- called as (tPtr->funcs.foo)(bars...) ------------- */
1842 /* create a new paragraph. Don't do anything with it just yet */
1843 //Paragraph *
1844 void *
1845 createParagraph(short fmargin, short bmargin, short rmargin,
1846 short *tabstops, short numTabs, WMAlignment alignment)
1848 Paragraph *para = wmalloc(sizeof(Paragraph));
1849 if(!para) return NULL;
1851 para->chunks = NULL;
1852 para->next = NULL;
1855 para->fmargin = (fmargin>=0)?fmargin:0;
1856 para->bmargin = (bmargin>=0)?bmargin:0;
1857 if(rmargin-bmargin >= 100 && rmargin-fmargin >= 100)
1858 para->rmargin = rmargin;
1859 else
1860 para->rmargin = 100;
1861 para->tabstops = tabstops;
1862 para->numTabs = (tabstops)?numTabs:0;
1864 para->drawbuffer = (Pixmap)NULL;
1865 para->bulletPix = NULL;
1866 para->top = para->bottom = 0;
1867 para->width = para->height = 0;
1869 para->align = alignment;
1871 return para;
1874 /* insert the new paragraph in the tPtr, either right before
1875 or after the currentPara. It's the responsibility of the
1876 calling code to set what currentPara is. via WMSetTextCurrentParagraph.
1877 If currentPara is not set, set it as the first in the document.
1878 This function then sets currentPara as _this_ paragraph.
1879 NOTE: this means careless parser implementors might lose previous
1880 paragraphs... but this keeps stuff small and non-buggy :-) */
1881 void
1882 insertParagraph(WMText *tPtr, void *v, InsertType type)
1883 //insertParagraph(WMText *tPtr, Paragraph *para, InsertType type)
1885 Paragraph *tmp;
1886 Paragraph *para = (Paragraph *)v;
1887 if(!para || !tPtr) return;
1889 if(!tPtr->currentPara) {
1890 tPtr->paragraphs = para;
1891 } else {
1892 tmp = tPtr->paragraphs;
1893 if(type == itAppend) {
1894 while(tmp->next && tmp != tPtr->currentPara)
1895 tmp = tmp->next;
1897 para->next = tmp->next;
1898 tmp->next = para;
1899 } else { /* must be prepend */
1900 /* this "prior" member is that "doing things the hard way"
1901 I spoke of. See? it's not too bad afterall... */
1902 Paragraph *prior = NULL;
1903 while(tmp->next && tmp != tPtr->currentPara) {
1904 prior = tmp;
1905 tmp = tmp->next;
1907 /* if this is the first */
1908 if(tmp == tPtr->paragraphs) {
1909 para->next = tmp;
1910 tPtr->paragraphs = para;
1911 } else {
1912 prior->next = para;
1913 para->next = tmp;
1914 } } }
1915 tPtr->currentPara = para;
1919 /* create a new chunk to contain exactly ONE pixmap */
1920 void *
1921 //Chunk *
1922 createPChunk(WMPixmap *pixmap, short script, ushort ul)
1924 Chunk *chunk;
1926 chunk = wmalloc(sizeof(Chunk));
1927 if(!chunk)
1928 return NULL;
1930 chunk->text = NULL;
1931 if(!pixmap)
1932 chunk->pixmap = NULL; /* if it's NULL, we'll draw the "broken" pixmap... */
1933 else chunk->pixmap = WMRetainPixmap(pixmap);
1934 chunk->chars = 0;
1935 chunk->mallocedSize = 0;
1936 chunk->type = ctImage;
1937 chunk->font = NULL;
1938 chunk->color = NULL;
1939 chunk->script = script;
1940 chunk->ul = ul;
1941 chunk->selected = False;
1942 chunk->next = NULL;
1943 return chunk;
1947 /* create a new chunk to contain some text with the given format */
1948 void *
1949 //Chunk *
1950 createTChunk(char *text, short chars, WMFont *font,
1951 WMColor *color, short script, ushort ul)
1953 Chunk *chunk;
1955 if(!text || chars<0 || !font || !color) return NULL;
1956 chunk = wmalloc(sizeof(Chunk));
1957 if(!chunk) return NULL;
1959 chunk->mallocedSize = reqBlockSize(chars);
1960 chunk->text = (char *)wmalloc(chunk->mallocedSize);
1961 memcpy(chunk->text, text, chars);
1962 chunk->pixmap = NULL;
1963 chunk->chars = chars;
1964 chunk->type = ctText;
1965 chunk->font = WMRetainFont(font);
1966 chunk->color = WMRetainColor(color);
1967 chunk->script = script;
1968 chunk->ul = ul;
1969 chunk->selected = False;
1970 chunk->next = NULL;
1972 return chunk;
1975 /* insert the new chunk in the paragraph, either right before
1976 or after the currentChunk. It's the responsibility of the
1977 calling code to set what currentChunk is via WMSetTextCurrentChunk.
1978 If currentChunk is not set, set it as the first in the existing
1979 paragraph... if not even that, you lose... try again.
1980 This function then sets currentChunk as _this_ chunk.
1981 NOTE: this means careless parser implementors might lose previous
1982 paragraphs/chunks... but this keeps stuff small and non-buggy :-) */
1983 void
1984 insertChunk(WMText *tPtr, void *v, InsertType type)
1986 Chunk *tmp;
1987 Chunk *chunk = (Chunk *)v;
1989 if(!tPtr || !chunk) return;
1991 if(!tPtr->paragraphs) { /* i.e., first chunk via insertTextInteractively */
1992 Paragraph *para = (tPtr->funcs.createParagraph) (0, 0, tPtr->visibleW,
1993 NULL, 0, WALeft);
1994 (tPtr->funcs.insertParagraph) (tPtr, para, itAppend);
1997 if(!tPtr->currentPara)
1998 return;
1999 if(!tPtr->currentChunk) { /* there is a current chunk */
2000 tPtr->currentPara->chunks = chunk;
2001 } else if(!tPtr->currentPara->chunks) {
2002 /* but it's not of this paragraph */
2003 tPtr->currentPara->chunks = chunk;
2004 } else {
2005 tmp = tPtr->currentPara->chunks;
2007 if(type == itAppend) {
2008 while(tmp->next && tmp != tPtr->currentChunk)
2009 tmp = tmp->next;
2011 chunk->next = tmp->next;
2012 tmp->next = chunk;
2014 } else { /* must be prepend */
2015 /* this "prior" member is that "doing things the hard way"
2016 I spoke of. See? it's not too bad afterall... */
2017 Chunk *prior = NULL;
2018 while(tmp->next && tmp != tPtr->currentChunk) {
2019 prior = tmp;
2020 tmp = tmp->next;
2022 /* if this is the first */
2023 if(tmp == tPtr->currentPara->chunks) {
2024 chunk->next = tmp;
2025 tPtr->currentPara->chunks = chunk;
2026 } else {
2027 prior->next = chunk;
2028 chunk->next = tmp;
2029 } } }
2030 tPtr->currentChunk = chunk;
2031 tPtr->tpos = chunk->chars;
2035 /* ------------- non-static functions (i.e., APIs) ------------- */
2036 /* ------------- called as WMVerbText[Subject] ------------- */
2038 #define DEFAULT_TEXT_WIDTH 250
2039 #define DEFAULT_TEXT_HEIGHT 200
2043 WMText*
2044 WMCreateText(WMWidget *parent)
2046 Text *tPtr = wmalloc(sizeof(Text));
2047 if(!tPtr) {
2048 perror("could not create text widget\n");
2049 return NULL;
2051 memset(tPtr, 0, sizeof(Text));
2052 tPtr->widgetClass = WC_Text;
2053 tPtr->view = W_CreateView(W_VIEW(parent));
2054 if (!tPtr->view) {
2055 perror("could not create text's view\n");
2056 wgdbFree(tPtr);
2057 return NULL;
2059 tPtr->view->self = tPtr;
2060 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
2061 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
2062 W_ResizeView(tPtr->view, DEFAULT_TEXT_WIDTH, DEFAULT_TEXT_HEIGHT);
2063 tPtr->bg = tPtr->view->screen->white;
2064 W_SetViewBackgroundColor(tPtr->view, tPtr->bg);
2067 tPtr->ruler = WMCreateRuler(tPtr);
2068 (W_VIEW(tPtr->ruler))->attribs.cursor = tPtr->view->screen->defaultCursor;
2069 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
2070 WMMoveWidget(tPtr->ruler, 0, 0);
2071 WMResizeWidget(tPtr->ruler, W_VIEW(parent)->size.width, 40);
2072 WMShowRulerTabs(tPtr->ruler, True);
2073 WMSetRulerAction(tPtr->ruler, rulerCallBack, tPtr);
2074 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
2076 tPtr->vpos = 0;
2077 tPtr->prevVpos = 0;
2078 tPtr->vscroller = WMCreateScroller(tPtr);
2079 (W_VIEW(tPtr->vscroller))->attribs.cursor =
2080 tPtr->view->screen->defaultCursor;
2081 (W_VIEW(tPtr->vscroller))->attribFlags |= CWOverrideRedirect | CWCursor;
2082 WMMoveWidget(tPtr->vscroller, 1, 1);
2083 WMResizeWidget(tPtr->vscroller, 20, tPtr->view->size.height - 2);
2084 WMSetScrollerArrowsPosition(tPtr->vscroller, WSAMaxEnd);
2085 WMSetScrollerAction(tPtr->vscroller, scrollersCallBack, tPtr);
2087 tPtr->hpos = 0;
2088 tPtr->prevHpos = 0;
2089 tPtr->hscroller = WMCreateScroller(tPtr);
2090 (W_VIEW(tPtr->hscroller))->attribs.cursor =
2091 tPtr->view->screen->defaultCursor;
2092 (W_VIEW(tPtr->hscroller))->attribFlags |= CWOverrideRedirect | CWCursor;
2093 WMMoveWidget(tPtr->hscroller, 1, tPtr->view->size.height-21);
2094 WMResizeWidget(tPtr->hscroller, tPtr->view->size.width - 2, 20);
2095 WMSetScrollerArrowsPosition(tPtr->hscroller, WSAMaxEnd);
2096 WMSetScrollerAction(tPtr->hscroller, scrollersCallBack, tPtr);
2098 tPtr->visibleW = tPtr->view->size.width;
2099 tPtr->visibleH = tPtr->view->size.height;
2101 tPtr->paragraphs = NULL;
2102 tPtr->docWidth = 0;
2103 tPtr->docHeight = 0;
2104 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
2105 default_bullet);
2106 tPtr->dUnknownImg = WMCreatePixmapFromXPMData(tPtr->view->screen,
2107 unk_xpm);
2109 tPtr->sRect.pos.x = tPtr->sRect.pos.y = 0;
2110 tPtr->sRect.size.width = tPtr->sRect.size.height = 0;
2111 tPtr->currentPara = NULL;
2112 tPtr->currentChunk = NULL;
2113 tPtr->tpos = 0;
2115 tPtr->parser = NULL;
2116 tPtr->writer = NULL;
2117 tPtr->funcs.createParagraph = createParagraph;
2118 tPtr->funcs.insertParagraph = insertParagraph;
2119 tPtr->funcs.createPChunk = createPChunk;
2120 tPtr->funcs.createTChunk = createTChunk;
2121 tPtr->funcs.insertChunk = insertChunk;
2123 tPtr->clicked.x = tPtr->clicked.y = -23;
2124 tPtr->cursor.x = tPtr->cursor.y = -23;
2126 tPtr->relief = WRSunken;
2127 tPtr->wrapping = wrWord;
2128 tPtr->editable = False;
2129 tPtr->cursorShown = False;
2130 tPtr->frozen = False;
2131 tPtr->focused = False;
2132 tPtr->pointerGrabbed = False;
2133 tPtr->buttonHeld = False;
2134 tPtr->ignoreNewLine = False;
2135 tPtr->waitingForSelection = False;
2136 tPtr->findingClickPoint = False;
2137 tPtr->foundClickPoint = False;
2138 tPtr->ownsSelection = False;
2139 tPtr->clheight = 0;
2140 tPtr->clwidth = 0;
2142 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2143 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2145 tPtr->view->delegate = &_TextViewDelegate;
2146 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
2147 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
2148 handleNonTextEvents, tPtr);
2149 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
2150 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
2151 handleTextEvents, tPtr);
2153 WMAddNotificationObserver(_notification, tPtr, "_lostOwnership", tPtr);
2155 WMSetTextMonoFont(tPtr, True);
2156 WMShowTextRuler(tPtr, False);
2157 WMSetTextHasHorizontalScroller(tPtr, False);
2158 WMSetTextHasVerticalScroller(tPtr, True);
2159 //printf("the sizeof chunk is %d\n", sizeof(Chunk));
2160 //printf("the sizeof para is %d\n", sizeof(Paragraph));
2161 //printf("the sizeof text is %d\n", sizeof(Text));
2162 return tPtr;
2165 //WMSetTextBullet()
2166 //WRetainPixmap(tPtr->dBulletPix);
2168 void
2169 WMRemoveTextParagraph(WMText *tPtr, int which)
2171 Paragraph *prior, *removed;
2172 if(!tPtr || which<0) return;
2174 WMSetTextCurrentParagraph(tPtr, which);
2175 removed = tPtr->currentPara;
2176 if(!removed) return;
2177 if(removed->chunks)printf("WMRemoveTextChunks\n");
2178 if(removed == tPtr->paragraphs || which==0) {
2179 tPtr->paragraphs = removed->next;
2180 } else {
2181 WMSetTextCurrentParagraph(tPtr, which-1);
2182 prior = tPtr->currentPara;
2183 if(!prior) return;
2184 prior->next = removed->next;
2186 wgdbFree(removed);
2187 // removeChunks
2188 removed = NULL;
2193 /* set what is known as the currentPara in the tPtr. */
2194 /* negative number means: "gib me last chunk" */
2195 void
2196 WMSetTextCurrentParagraph(WMText *tPtr, int current)
2198 Paragraph *tmp;
2199 int i=0;
2201 if(!tPtr || current<0) return;
2202 if(current == 0) {
2203 tPtr->currentPara = tPtr->paragraphs;
2204 return;
2206 tmp = tPtr->paragraphs;
2207 while(tmp->next && ((current==-23)?1:i++<current)) {
2208 //while(tmp && i++<current) {
2209 tmp = tmp->next;
2210 } tPtr->currentPara = tmp;
2211 //? want to do this?if(tmp) tPtr->currentChunk = tmp
2215 int
2216 WMGetTextParagraphs(WMText *tPtr)
2218 int current=0;
2219 Paragraph *tmp;
2220 if(!tPtr) return 0;
2221 tmp = tPtr->paragraphs;
2222 while(tmp) {
2223 tmp = tmp->next;
2224 current++;
2225 } return current;
2230 int
2231 WMGetTextCurrentParagraph(WMText *tPtr)
2233 int current=-1;
2234 Paragraph *tmp;
2236 if(!tPtr) return current;
2237 if(!tPtr->currentPara) return current;
2238 if(!tPtr->paragraphs) return current;
2239 tmp = tPtr->paragraphs;
2240 while(tmp) {
2241 current++;
2242 if(tmp == tPtr->currentPara)
2243 break;
2244 tmp = tmp->next;
2245 } return current;
2248 /* set what is known as the currentChunk within the currently
2249 selected currentPara (or the first paragraph in the document). */
2250 void
2251 WMSetTextCurrentChunk(WMText *tPtr, int current)
2253 Chunk *tmp;
2254 int i=0;
2256 if(!tPtr) return;
2257 tPtr->currentChunk = NULL;
2258 if(!tPtr->currentPara) {
2259 tPtr->currentPara = tPtr->paragraphs;
2260 if(!tPtr->currentPara)
2261 return;
2264 if(current == 0) {
2265 tPtr->currentChunk = tPtr->currentPara->chunks;
2266 return;
2268 tmp = tPtr->currentPara->chunks;
2269 if(tmp) {
2270 while(tmp->next && ((current<0)?1:i++<current))
2271 tmp = tmp->next;
2272 } tPtr->currentChunk = tmp;
2276 void
2277 WMRemoveTextChunk(WMText *tPtr, int which)
2279 Chunk *prior, *removed;
2280 Paragraph *para;
2281 if(!tPtr || which<0) return;
2282 para = tPtr->currentPara;
2283 if(!para) return;
2285 WMSetTextCurrentChunk(tPtr, which);
2286 removed = tPtr->currentChunk;
2287 if(!removed) return;
2288 if(removed == tPtr->currentPara->chunks || which==0) {
2289 para->chunks = removed->next;
2290 } else {
2291 WMSetTextCurrentChunk(tPtr, which-1);
2292 prior = tPtr->currentChunk;
2293 if(!prior) return;
2294 prior->next = removed->next;
2296 if(removed->type == ctText) {
2297 wgdbFree(removed->text);
2298 WMReleaseFont(removed->font);
2299 WMReleaseColor(removed->color);
2300 } else {
2301 WMReleasePixmap(removed->pixmap);
2303 wgdbFree(removed);
2304 removed = NULL;
2307 int
2308 WMGetTextCurrentChunk(WMText *tPtr)
2310 int current=0;
2311 Chunk *tmp;
2313 if(!tPtr) return 0;
2314 if(!tPtr->currentChunk) return 0;
2315 if(!tPtr->currentPara) {
2316 tPtr->currentPara = tPtr->paragraphs;
2317 if(!tPtr->currentPara)
2318 return 0;
2321 tmp = tPtr->currentPara->chunks;
2322 while(tmp) {
2323 if(tmp == tPtr->currentChunk)
2324 break;
2325 tmp = tmp->next;
2326 current++;
2328 return current;
2331 int
2332 WMGetTextChunks(WMText *tPtr)
2334 short current=0;
2335 Chunk *tmp;
2336 if(!tPtr || !tPtr->currentPara) return 0;
2337 tmp = tPtr->currentPara->chunks;
2338 while(tmp) {
2339 tmp = tmp->next;
2340 current++;
2341 } return current;
2344 void
2345 WMShowTextRuler(WMText *tPtr, Bool show)
2347 if(!tPtr) return;
2348 if(tPtr->monoFont) show = False;
2350 tPtr->rulerShown = show;
2351 if(show) WMMapWidget(tPtr->ruler);
2352 else WMUnmapWidget(tPtr->ruler);
2353 resizeText(tPtr->view->delegate, tPtr->view);
2356 Bool
2357 WMGetTextRulerShown(WMText *tPtr)
2359 if(!tPtr) return False;
2360 return tPtr->rulerShown;
2363 void
2364 WMSetTextRulerMargin(WMText *tPtr, char which, short pixels)
2366 if(!tPtr) return;
2367 if(tPtr->monoFont) return;
2368 WMSetRulerMargin(tPtr->ruler, which, pixels);
2369 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2372 short
2373 WMGetTextRulerMargin(WMText *tPtr, char which)
2375 if(!tPtr) return 0;
2376 if(tPtr->monoFont)
2377 return 0;
2378 return WMGetRulerMargin(tPtr->ruler, which);
2382 void
2383 WMShowTextRulerTabs(WMText *tPtr, Bool show)
2385 if(!tPtr) return;
2386 if(tPtr->monoFont) return;
2387 WMShowRulerTabs(tPtr->ruler, show);
2390 void
2391 WMSetTextMonoFont(WMText *tPtr, Bool mono)
2393 if(!tPtr) return;
2394 if(mono && tPtr->rulerShown)
2395 WMShowTextRuler(tPtr, False);
2397 tPtr->monoFont = mono;
2400 Bool
2401 WMGetTextMonoFont(WMText *tPtr)
2403 if(!tPtr) return True;
2404 return tPtr->monoFont;
2407 void
2408 WMForceTextFocus(WMText *tPtr)
2410 if(!tPtr) return;
2412 if(tPtr->clicked.x == -23 || tPtr->clicked.y == 23)
2413 cursorToTextPosition(tPtr, 100, 100); /* anyplace */
2414 else
2415 cursorToTextPosition(tPtr, tPtr->clicked.x, tPtr->clicked.y);
2419 void
2420 WMSetTextEditable(WMText *tPtr, Bool editable)
2422 if(!tPtr) return;
2423 tPtr->editable = editable;
2427 Bool
2428 WMGetTextEditable(WMText *tPtr)
2430 if(!tPtr) return 0;
2431 return tPtr->editable;
2435 Bool
2436 WMScrollText(WMText *tPtr, int amount)
2438 Bool scroll=False;
2439 if(amount == 0 || !tPtr) return;
2440 if(!tPtr->view->flags.realized) return;
2442 if(amount < 0) {
2443 if(tPtr->vpos > 0) {
2444 if(tPtr->vpos > amount) tPtr->vpos += amount;
2445 else tPtr->vpos=0;
2446 scroll=True;
2447 } } else {
2448 int limit = tPtr->docHeight - tPtr->visibleH;
2449 if(tPtr->vpos < limit) {
2450 if(tPtr->vpos < limit-amount) tPtr->vpos += amount;
2451 else tPtr->vpos = limit;
2452 scroll = True;
2453 } }
2455 if(scroll && tPtr->vpos != tPtr->prevVpos) {
2456 updateScrollers(tPtr);
2457 drawDocumentPartsOnPixmap(tPtr, False);
2458 paintText(tPtr);
2460 tPtr->prevVpos = tPtr->vpos;
2461 return scroll;
2464 Bool
2465 WMPageText(WMText *tPtr, Bool scrollUp)
2467 if(!tPtr) return;
2468 if(!tPtr->view->flags.realized) return;
2470 return WMScrollText(tPtr, scrollUp
2471 ? tPtr->visibleH:-tPtr->visibleH);
2474 void
2475 WMIgnoreTextNewline(WMText *tPtr, Bool ignore)
2477 if(!tPtr) return;
2478 tPtr->ignoreNewLine = ignore;
2482 void
2483 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool flag)
2485 if(tPtr) {
2486 short rh;
2487 if(tPtr->monoFont)
2488 return;
2489 rh = tPtr->rulerShown?40:0;
2490 tPtr->hasHscroller = flag;
2491 if(flag) {
2492 WMMapWidget(tPtr->hscroller);
2493 tPtr->visibleH = tPtr->view->size.height-rh-22;
2494 } else {
2495 WMUnmapWidget(tPtr->hscroller);
2496 tPtr->visibleH = tPtr->view->size.height-rh;
2498 resizeText(tPtr->view->delegate, tPtr->view);
2503 void
2504 WMSetTextHasVerticalScroller(WMText *tPtr, Bool flag)
2506 if(tPtr) {
2507 tPtr->hasVscroller = flag;
2508 if(flag) {
2509 WMMapWidget(tPtr->vscroller);
2510 tPtr->visibleW = tPtr->view->size.width-22;
2511 WMSetRulerOffset(tPtr->ruler, 22); /* scrollbar width + 2 */
2512 } else {
2513 WMUnmapWidget(tPtr->vscroller);
2514 tPtr->visibleW = tPtr->view->size.width;
2515 WMSetRulerOffset(tPtr->ruler, 2);
2517 resizeText(tPtr->view->delegate, tPtr->view);
2523 void
2524 WMRefreshText(WMText *tPtr, int vpos, int hpos)
2527 if(!tPtr)
2528 return;
2530 if(tPtr->frozen || !tPtr->view->flags.realized)
2531 return;
2534 XClearArea(tPtr->view->screen->display, tPtr->view->window,
2535 22, (tPtr->rulerShown)?45:5,
2536 tPtr->visibleW, tPtr->visibleH, True);
2538 calcDocExtents(tPtr);
2540 printf("vpos:%d tPtr->docHeight%d tPtr->visibleH%d \n",
2541 vpos, tPtr->docHeight, tPtr->visibleH);
2544 // tPtr->vpos = vpos;
2546 if(vpos < 0 || tPtr->docHeight < tPtr->visibleH)
2547 tPtr->vpos = 0;
2548 else if(vpos-tPtr->visibleH>tPtr->docHeight)
2549 tPtr->vpos = vpos-tPtr->docHeight-tPtr->visibleH-tPtr->docHeight;
2550 else
2551 tPtr->vpos = tPtr->docHeight-tPtr->visibleH;
2555 if(hpos < 0 || hpos > tPtr->docWidth)
2556 tPtr->hpos = 0;
2557 else
2558 tPtr->hpos = hpos;
2560 drawDocumentPartsOnPixmap(tPtr, True);
2561 updateScrollers(tPtr);
2562 paintText(tPtr);
2565 /* would be nice to have in WINGs proper... */
2566 static void
2567 changeFontProp(char *fname, char *newprop, short which)
2569 char before[128], prop[128], after[128];
2570 char *ptr, *bptr;
2571 int part=0;
2573 if(!fname || !prop)
2574 return;
2576 ptr = fname;
2577 bptr = before;
2578 while (*ptr) {
2579 if(*ptr == '-') {
2580 *bptr = 0;
2581 if(part==which) bptr = prop;
2582 else if(part==which+1) bptr = after;
2583 *bptr++ = *ptr;
2584 part++;
2585 } else {
2586 *bptr++ = *ptr;
2587 } ptr++;
2588 }*bptr = 0;
2589 snprintf(fname, 255, "%s-%s%s", before, newprop, after);
2592 /* TODO: put in wfont? */
2593 WMFont *
2594 WMGetFontPlain(WMScreen *scrPtr, WMFont *font)
2596 WMFont *nfont=NULL;
2597 if(!scrPtr|| !font)
2598 return NULL;
2599 return font;
2603 WMFont *
2604 WMGetFontBold(WMScreen *scrPtr, WMFont *font)
2606 WMFont *newfont=NULL;
2607 char fname[256];
2608 if(!scrPtr || !font)
2609 return NULL;
2610 snprintf(fname, 255, font->name);
2611 changeFontProp(fname, "bold", 2);
2612 newfont = WMCreateNormalFont(scrPtr, fname);
2613 if(!newfont)
2614 newfont = font;
2615 return newfont;
2618 WMFont *
2619 WMGetFontItalic(WMScreen *scrPtr, WMFont *font)
2621 WMFont *newfont=NULL;
2622 char fname[256];
2623 if(!scrPtr || !font)
2624 return NULL;
2625 snprintf(fname, 255, font->name);
2626 changeFontProp(fname, "o", 3);
2627 newfont = WMCreateNormalFont(scrPtr, fname);
2628 if(!newfont)
2629 newfont = font;
2630 return newfont;
2633 WMFont *
2634 WMGetFontOfSize(WMScreen *scrPtr, WMFont *font, short size)
2636 WMFont *nfont=NULL;
2637 if(!scrPtr || !font || size<1)
2638 return NULL;
2639 return font;
2641 /* */
2643 void
2644 WMFreezeText(WMText *tPtr)
2646 if(!tPtr)
2647 return;
2648 tPtr->frozen = True;
2651 void
2652 WMThawText(WMText *tPtr)
2654 if(!tPtr)
2655 return;
2656 tPtr->frozen = False;
2660 void
2661 WMSetTextDefaultAlignment(WMText *tPtr, WMAlignment alignment)
2663 if(!tPtr) return;
2664 if(tPtr->monoFont) return;
2666 tPtr->dAlignment = alignment;
2667 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2672 void
2673 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
2675 if(!tPtr)
2676 return;
2678 if(color)
2679 tPtr->bg = color;
2680 else
2681 tPtr->bg = WMWhiteColor(tPtr->view->screen);
2683 W_SetViewBackgroundColor(tPtr->view, tPtr->bg);
2684 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2687 void
2688 WMSetTextDefaultColor(WMText *tPtr, WMColor *color)
2690 if(!tPtr)
2691 return;
2693 if(color)
2694 tPtr->dColor = color;
2695 else
2696 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2699 void
2700 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
2702 if(!tPtr)
2703 return;
2705 if(font)
2706 tPtr->dFont = font;
2707 else
2708 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2711 void
2712 WMSetTextUseFixedPitchFont(Text *tPtr, Bool fixed)
2714 if(!tPtr)
2715 return;
2716 if(fixed)
2717 tPtr->dFont = WMCreateFontSet(tPtr->view->screen,
2718 "lucidasanstypewriter-12");
2719 else
2720 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2721 tPtr->fixedPitch = fixed;
2724 void
2725 WMSetTextParser(WMText *tPtr, WMParseAction *parser)
2727 if(!tPtr) return;
2728 if(tPtr->monoFont) return;
2729 tPtr->parser = parser;
2733 WMParserActions
2734 WMGetTextParserActions(WMText *tPtr)
2736 WMParserActions null;
2737 if(!tPtr) return null;
2738 return tPtr->funcs;
2742 char *
2743 WMGetTextAll(WMText *tPtr)
2745 char *text;
2746 int length=0;
2747 int where=0;
2748 Paragraph *para;
2749 Chunk *chunk;
2751 if(!tPtr) return NULL;
2753 para = tPtr->paragraphs;
2754 while(para) {
2755 chunk = para->chunks;
2756 while(chunk) {
2757 if(chunk->type == ctText) {
2758 if(chunk->text) length += chunk->chars;
2759 } else {
2760 printf("getting image \n");
2762 chunk = chunk->next;
2765 if(tPtr->ignoreNewLine) break;
2766 length += 4; // newlines
2767 para = para->next;
2770 text = wmalloc(length+1);
2772 para = tPtr->paragraphs;
2773 while(para) {
2774 chunk = para->chunks;
2775 while(chunk) {
2776 if(chunk->type == ctText) {
2777 if(chunk->text) {
2778 snprintf(&text[where], chunk->chars+1, "%s", chunk->text);
2779 where += chunk->chars;
2781 } else {
2782 printf("writing image \n");
2784 chunk = chunk->next;
2786 if(tPtr->ignoreNewLine) break;
2787 snprintf(&text[where++], 2, "\n");
2788 para = para->next;
2789 } text[where] = '\0';
2791 return text;
2794 void
2795 WMSetTextWriter(WMText *tPtr, WMParseAction *writer)
2797 if(!tPtr)
2798 return;
2799 if(tPtr->monoFont)
2800 return;
2801 tPtr->writer = writer;