Guess what? Yep, another wmtext update. Now this was all, please go to
[wmaker-crm.git] / WINGs / wtext.c
blob5d7eee0022a0bf194faf336dd8aba09b17f91506
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 void wgdbFree(void *ptr)
25 if(!ptr) printf("err... cannot ");
26 printf("gdbFree [%p]\n", ptr);
27 wfree(ptr);
31 typedef enum {ctText=0, ctImage=1} ChunkType;
32 typedef enum { dtDelete=0, dtBackSpace } DeleteType;
33 typedef enum {wrWord=0, wrChar=1, wrNone=2} Wrapping;
35 /* Why singly-linked and not say doubly-linked?
36 99% of the time (draw, append), the "prior"
37 member would have been a useless memory and CPU overhead,
38 and deletes _are_ relatively infrequent.
39 When the "prior" member needs to be used, the overhead of
40 doing things the hard way will be incurred... but seldomly. */
43 /* a Chunk is a singly-linked list of chunks containing:
44 o text with a given format
45 o or an image
46 o but NOT both */
47 typedef struct _Chunk {
48 char *text; /* the text in the chunk */
49 WMPixmap *pixmap; /* OR the pixmap it holds */
50 short chars; /* the number of characters in this chunk */
51 short mallocedSize; /* the number of characters that can be held */
53 WMFont *font; /* the chunk's font */
54 WMColor *color; /* the chunk's color */
55 short ul:1; /* underlined or not */
56 ChunkType type:1; /* a "Text" or "Image" chunk */
57 short script:4; /* script in points: negative for subscript */
58 /* hrmm selec... */
59 ushort selected;
60 ushort sStart;
61 ushort sEnd;
62 ushort RESERVED:10;
63 struct _Chunk *next;/*the next member in this list */
65 }Chunk;
69 /* a Paragraph is a singly-linked list of paragraphs containing:
70 o a list of chunks in that paragraph
71 o the formats for that paragraph
72 o its (draw) position relative to the entire document */
73 typedef struct _Paragraph {
74 Chunk *chunks; /* the list of text and/or image chunks */
75 short fmargin; /* the start position of the first line */
76 short bmargin; /* the start positions of the rest of the lines */
77 short rmargin; /* the end position of the entire paragraph */
78 short numTabs; /* the number of tabstops */
79 short *tabstops; /* an array of tabstops */
81 Pixmap drawbuffer; /* the pixmap onto which the (entire)
82 paragraph will be drawn */
83 WMPixmap *bulletPix;/* the pixmap to use for bulleting */
84 int top; /* the top of the paragraph relative to document */
85 int bottom; /* the bottom of the paragraph relative to document */
86 int width; /* the width of the paragraph */
87 int height; /* the height of the paragraph */
88 WMAlignment align:2;/* justification of this paragraph */
89 ushort RESERVED:14;
91 struct _Paragraph *next; /* the next member in this list */
92 } Paragraph;
95 static char *default_bullet[] = {
96 "6 6 4 1",
97 " c None s None", ". c black",
98 "X c white", "o c #808080",
99 " ... ",
100 ".XX.. ",
101 ".XX..o",
102 ".....o",
103 " ...oo",
104 " ooo "};
106 /* this is really a shrunk down version of the original
107 "broken" icon... I did not draw it, I simply shrunk it */
108 static char * unk_xpm[] = {
109 "24 24 17 1",
110 " c None", ". c #0B080C", "+ c #13A015", "@ c #5151B8",
111 "# c #992719", "$ c #5B1C20", "% c #1DF51D", "& c #D1500D", "* c #2F304A",
112 "= c #0C6A0C", "- c #F2F1DE", "; c #D59131", "> c #B2B083", ", c #DD731A",
113 "' c #CC3113", ") c #828238", "! c #6A6A94",
114 "......!@@@@@@@....$$....",
115 "...@!@@@@@@@**...$#'....",
116 "..!!@@@@@@@@.......#....",
117 "..!@@@@@@@@@*.......$...",
118 ".!@@@#,,#*@@*..*>.*.#...",
119 "*@@@@#'',,@@@...---!....",
120 "!@@@@@*.#;*@@..!--->....",
121 "@@@@@@@@#,.@@..!----@...",
122 "!@@@@@@*#;'$...!----@...",
123 "*@@@@@@..'&;;#.)----)...",
124 ".@@@@@@..$..&'.>----)...",
125 ".@@@@@@**---,'>-----!...",
126 ".@@@@@@**---,'>-----@...",
127 "..@@@@@@@---;;;,;---....",
128 "..*@@@@*@--->#',;,-*.)..",
129 "........)---->)@;#!..>..",
130 ".....)----------;$..>)..",
131 "=%%%*.*!-------);..)-*..",
132 "=%%%%+...*)>!@*$,.>--...",
133 "*+++++++.......*$@-->...",
134 "............**@)!)>->...",
135 "........................",
136 "........................",
137 "........................"};
139 typedef struct W_Text {
140 W_Class widgetClass; /* the class number of this widget */
141 W_View *view; /* the view referring to this instance */
142 WMColor *bg; /* the background color to use when drawing */
144 WMRuler *ruler; /* the ruler subwiget to maipulate paragraphs */
146 WMScroller *hscroller; /* the horizontal scroller */
147 short hpos; /* the current horizontal position */
148 short prevHpos; /* the previous horizontal position */
150 WMScroller *vscroller; /* the vertical scroller */
151 int vpos; /* the current vertical position */
152 int prevVpos; /* the previous vertical position */
154 int visibleW; /* the actual horizontal space available */
155 int visibleH; /* the actual vertical space available */
157 Paragraph *paragraphs; /* the linked list of the paragraphs in the doc. */
158 int docWidth; /* the width of the entire document */
159 int docHeight; /* the height of the entire document */
161 WMFont *dFont; /* the default font */
162 WMColor *dColor; /* the default color */
163 WMPixmap *dBulletPix; /* the default pixmap for bullets */
164 WMPixmap *dUnknownImg; /* the pixmap for (missing/broken) images */
166 WMRect sRect; /* the selected area */
167 Paragraph *currentPara; /* the current paragraph, in which actions occur */
168 Chunk *currentChunk; /* the current chunk, about which actions occur */
169 short tpos; /* the cursor position (text position) */
170 WMParseAction *parser; /* what action to use to parse input text */
171 WMParseAction *writer; /* what action to use to write text */
172 WMParserActions funcs; /* the "things" that parsers/writers might do */
173 XPoint clicked; /* the position of the last mouse click */
174 XPoint cursor; /* where the cursor is "placed" */
175 short clheight; /* the height of the "line" clicked on */
176 short clwidth; /* the width of the "line" clicked on */
178 WMReliefType relief:2; /* the relief to display with */
179 Wrapping wrapping:2; /* the type of wrapping to use in drawing */
180 WMAlignment dAlignment:2;/* default justification */
181 ushort monoFont:1; /* whether to ignore "rich" commands */
182 ushort fixedPitch:1; /* assume each char in dFont is the same size */
183 ushort editable:1; /* whether to accept user changes or not*/
184 ushort rulerShown:1; /* whether the ruler is shown or not */
185 ushort cursorShown:1; /* whether the cursor is currently being shown */
186 ushort frozen:1; /* whether screen updates are to be made */
187 ushort focused:1; /* whether this instance has input focus */
188 ushort pointerGrabbed:1;/* whether this instance has the pointer */
189 ushort buttonHeld:1; /* the user is still holding down the button */
190 ushort ignoreNewLine:1; /* whether to ignore the newline character */
191 ushort waitingForSelection:1; /* whether there is a pending paste event */
192 ushort ownsSelection:1; /* whether it ownz the current selection */
193 ushort findingClickPoint:1;/* whether the search for a clickpoint is on */
194 ushort foundClickPoint:1;/* whether the clickpoint has been found */
195 ushort hasVscroller:1; /* whether to enable the vertical scroller */
196 ushort hasHscroller:1; /* whether to enable the horizontal scroller */
197 ushort RESERVED:10;
198 } Text;
202 /* --------- static functions that are "private". don't touch :-) --------- */
205 /* max "characters per chunk that will be drawn at a time" */
206 #define MAX_WORD_LENGTH 100
207 /* max on a line */
208 #define MAX_CHUNX 64
209 #define MIN_DOC_WIDTH 200
210 typedef struct _LocalMargins {
211 short left, right, first, body;
212 } LocalMargins;
214 typedef struct _MyTextItems {
215 char text[MAX_WORD_LENGTH+1];
216 WMPixmap *pix;
217 short chars;
218 short x;
219 Chunk *chunk;/* used for "click" events */
220 short start; /* ditto... where in the chunk we start (ie. wrapped chunk) */
221 ushort type:1;
222 ushort RESERVED:15;
223 } MyTextItems;
226 static WMRect
227 chunkSelectionRect(Text *tPtr, Paragraph *para, MyTextItems item,
228 short y, short j, short lh)
230 WMRect rect;
231 short type=0; /* 0:none 1:partial: 2:all */
232 short rh=(tPtr->rulerShown)?45:5;
233 short w, lm;
234 WMFont *font = (tPtr->monoFont || item.chunk->type != ctText)?
235 tPtr->dFont:item.chunk->font;
237 rect.pos.x = -23;
238 if(y+para->top+rh > tPtr->sRect.pos.y+tPtr->sRect.size.height
239 || y+para->top+rh+lh < tPtr->sRect.pos.y)
240 return rect;
242 if(item.chunk->type == ctText)
243 w = WMWidthOfString(font, item.text, item.chars);
244 else w = item.chunk->pixmap->width;
246 if(y+para->top+rh >= tPtr->sRect.pos.y && (y+para->top+rh+lh
247 <= tPtr->sRect.pos.y+tPtr->sRect.size.height))
248 //&& item.x+j >= tPtr->sRect.pos.x+tPtr->sRect.size.width))
249 type = 2;
250 else type = 1;
253 #if 0
254 if(item.x+j >= tPtr->sRect.pos.x &&
255 item.x+j+w < tPtr->sRect.pos.x+tPtr->sRect.size.width)
256 type = 2;
258 if(type == 1 && y+para->top+rh+lh <=
259 tPtr->sRect.pos.y+tPtr->sRect.size.height)
260 type = 2;
261 #endif
264 if(type == 1 && item.chunk->type == ctText) { /* partial coverage */
265 lm = 2+WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
266 /* even I am still confused, so don't ask please */
267 if( (item.x+j+lm >= tPtr->sRect.pos.x &&
268 item.x+j+lm <= tPtr->sRect.pos.x+tPtr->sRect.size.width)
269 || (item.x+j+lm >= tPtr->sRect.pos.x+tPtr->sRect.size.width
270 && y+para->top+rh+lh <=
271 tPtr->sRect.pos.y+tPtr->sRect.size.height)
272 || (tPtr->sRect.pos.y < y+para->top+rh
273 && tPtr->sRect.pos.x+tPtr->sRect.size.width >
274 item.x+j+lm) ){
275 rect.size.width = w;
276 rect.pos.x = item.x+j;
277 item.chunk->selected = True;
278 if(item.chunk->chars > 6) {
279 item.chunk->sStart = 3;
280 item.chunk->sEnd = item.chunk->chars;
281 } else {
282 item.chunk->sStart = 0;
283 item.chunk->sEnd = item.chunk->chars;
286 } else if(type == 2) {
287 rect.pos.x = item.x+j;
288 item.chunk->selected = True;
289 if(item.chunk->type == ctText) {
290 item.chunk->sStart = 0;
291 item.chunk->sStart = item.chunk->chars;
292 rect.size.width = WMWidthOfString(font,
293 item.text, item.chars);
294 } else {
295 rect.size.width = item.chunk->pixmap->width;
299 rect.pos.y = y;
300 rect.size.height = lh;
301 return rect;
304 static int
305 myDrawText(Text *tPtr, Paragraph *para, MyTextItems *items,
306 short nitems, short pwidth, int y, short draw, short spacepos)
308 short i, ul_thick, u, j=0; /* j = justification */
309 short line_width = 0, line_height=0, mx_descent=0;
310 WMScreen *screen = tPtr->view->screen;
311 GC gc;
312 WMFont *font;
314 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0;
315 for(i=0; i<nitems; i++) {
316 if(items[i].type == ctText) {
317 font = (tPtr->monoFont)?tPtr->dFont:items[i].chunk->font;
318 mx_descent = WMIN(mx_descent, -font->y);
319 line_height = WMAX(line_height, font->height);
320 //printf("chunk.x %d xpoint.x %d\n",
321 // items[i].x, tPtr->clicked.x);
323 line_width += WMWidthOfString(font,
324 items[i].text, items[i].chars);
325 } else {
326 mx_descent = WMIN(mx_descent, -(items[i].pix->height-3));
327 /* replace -3 wif descent... */
328 line_height = WMAX(line_height, items[i].pix->height);
329 if(para->align == WARight || para->align == WACenter) {
330 line_width += items[i].pix->width;
331 } } }
333 if(para->align == WARight) {
334 j = pwidth - line_width;
335 } else if (para->align == WACenter) {
336 j = (short) ((float)(pwidth - line_width))/2.0;
339 if(tPtr->findingClickPoint && (y+line_height >= tPtr->clicked.y)) {
340 tPtr->foundClickPoint = True;
341 tPtr->currentChunk = items[0].chunk; /* just first on this "line" */
342 tPtr->tpos = items[0].start; /* where to "start" counting from */
343 tPtr->clicked.x = j+items[0].x;
344 tPtr->clicked.y = y+line_height+mx_descent;
345 tPtr->clheight = line_height; /* to draw the cursor */
346 tPtr->clwidth = line_width; /* where to stop searching */
347 return 0;
348 } if(!draw) return line_height;
350 for(i=0; i<nitems; i++) {
352 //account for vpos
353 if(tPtr->ownsSelection) {
354 WMRect rect = chunkSelectionRect(tPtr, para,
355 items[i], y, j, line_height);
356 if(rect.pos.x != -23) { /* has been selected */
357 XFillRectangle(tPtr->view->screen->display, para->drawbuffer,
358 WMColorGC(WMGrayColor(tPtr->view->screen)),
359 rect.pos.x, rect.pos.y, rect.size.width, rect.size.height);
363 if(items[i].type == ctText) {
364 gc = WMColorGC(items[i].chunk->color);
365 font = (tPtr->monoFont)?tPtr->dFont:items[i].chunk->font;
366 WMDrawString(screen, para->drawbuffer, gc, font,
367 items[i].x+j, y - font->y - mx_descent,
368 items[i].text, items[i].chars);
369 if(items[i].chunk->ul && !tPtr->monoFont) {
370 ul_thick = (short) ((float)font->height)/12.0;
371 if (ul_thick < 1) ul_thick = 1;
372 for(u=0; u<ul_thick; u++) {
373 XDrawLine(screen->display, para->drawbuffer, gc, items[i].x+j,
374 y + 1 + u - mx_descent,
375 items[i].x + j + WMWidthOfString(font,
376 items[i].text, items[i].chars), y + 1 + u - mx_descent);
379 } } else {
380 WMDrawPixmap(items[i].pix, para->drawbuffer, items[i].x+j,
381 y + 3 - mx_descent - items[i].pix->height);
383 return line_height;
386 static void
387 drawPChunkPart(Text *tPtr, Chunk *chunk, LocalMargins m, Paragraph *para,
388 MyTextItems *items, short *nitems, short *Lmargin, XPoint *where, short draw)
390 short p_width;
392 if(!chunk) return;
393 if(!chunk->pixmap)
394 chunk->pixmap = WMRetainPixmap(tPtr->dUnknownImg);
396 p_width = m.right - WMIN(m.first, m.body) - WMGetRulerOffset(tPtr->ruler);
397 if(p_width < MIN_DOC_WIDTH) // need WMRuler to take care of this...
398 return;
399 if(where->x + chunk->pixmap->width <= p_width - *Lmargin) {
400 /* it can fit on rest of line */
401 items[*nitems].pix = chunk->pixmap;
402 items[*nitems].type = ctImage;
403 items[*nitems].chars = 0;
404 items[*nitems].x = *Lmargin+where->x;
405 items[*nitems].chunk = chunk;
406 items[*nitems].start = 0;
408 if(*nitems >= MAX_CHUNX) {
409 items[*nitems].chars = 0;
410 items[*nitems].x = *Lmargin+where->x;
411 items[*nitems].chunk = chunk;
412 items[*nitems].start = 0;
413 where->y += myDrawText(tPtr, para, items, *nitems+1,
414 p_width-*Lmargin, where->y, draw, 0);
415 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
416 *nitems = 0;
417 where->x = 0;
418 } else {
419 (*nitems)++;
420 where->x += chunk->pixmap->width;
422 } else if(chunk->pixmap->width <= p_width - *Lmargin) {
423 /* it can fit on an entire line, flush the myDrawText then wrap it */
424 where->y += myDrawText(tPtr, para, items, *nitems+1,
425 p_width-*Lmargin, where->y, draw, 0);
426 *nitems = 0;
427 *Lmargin = WMAX(0, m.body - m.first);
428 where->x = 0;
429 drawPChunkPart(tPtr, chunk, m, para, items, nitems,
430 Lmargin, where, draw);
431 } else {
432 #if 1
433 *nitems = 0;
434 where->x = 0;
435 *Lmargin = WMAX(0, m.body - m.first);
436 items[*nitems].pix = chunk->pixmap;
437 items[*nitems].type = ctImage;
438 items[*nitems].chars = 0;
439 items[*nitems].x = *Lmargin+where->x;
440 items[*nitems].chunk = chunk;
441 items[*nitems].start = 0;
442 where->y += myDrawText(tPtr, para, items, *nitems+1,
443 p_width-*Lmargin, where->y, draw, 0);
444 #endif
446 /* scale image to fit, call self again */
447 /* deprecated - the management */
452 static void
453 drawTChunkPart(Text *tPtr, Chunk *chunk, char *bufr, LocalMargins m,
454 Paragraph *para, MyTextItems *items, short *nitems, short len, short start,
455 short *Lmargin, XPoint *where, short draw, short spacepos)
457 short t_chunk_width, p_width, chars;
458 WMFont *font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
459 /* if(doc->clickstart.yes && doc->clickstart.done) return; */
461 if(len==0) return;
462 p_width = m.right - WMIN(m.first, m.body);
463 if(p_width < MIN_DOC_WIDTH) // need WMRuler to take care of this...
464 return;
467 t_chunk_width = WMWidthOfString(font, bufr, len);
468 if((where->x + t_chunk_width <= p_width - *Lmargin)
469 || (tPtr->wrapping == wrNone)) {
470 /* if it can fit on rest of line, append to line */
471 chars = WMIN(len, MAX_WORD_LENGTH);
472 snprintf(items[*nitems].text, chars+1, "%s", bufr);
473 items[*nitems].chars = chars;
474 items[*nitems].x = *Lmargin+where->x;
475 items[*nitems].type = ctText;
476 items[*nitems].chunk = chunk;
477 items[*nitems].start = start;
479 if(*nitems >= MAX_CHUNX) {
480 chars = WMIN(len, MAX_WORD_LENGTH);
481 snprintf(items[*nitems].text, chars+1, "%s", bufr);
482 items[*nitems].chars = chars;
483 items[*nitems].x = *Lmargin+where->x;
484 items[*nitems].type = ctText;
485 items[*nitems].chunk = chunk;
486 items[*nitems].start = start;
487 where->y += myDrawText(tPtr, para, items, *nitems+1,
488 p_width-*Lmargin, where->y, draw, spacepos);
489 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
490 *nitems = 0;
491 where->x = 0;
492 } else {
493 (*nitems)++;
494 where->x += t_chunk_width;
496 } else if(t_chunk_width <= p_width - *Lmargin) {
497 /* it can fit on an entire line, flush and wrap it to a new line */
498 where->y += myDrawText(tPtr, para, items, *nitems,
499 p_width-*Lmargin, where->y, draw, spacepos);
500 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
501 *nitems = 0;
502 *Lmargin = WMAX(0, m.body - m.first);
503 where->x = 0;
504 drawTChunkPart(tPtr, chunk, bufr, m, para, items, nitems,
505 len, start, Lmargin, where, draw, spacepos);
506 } else {
507 /* otherwise, chop line, call ourself recursively until it's all gone */
508 short J=0; /* bufr */
509 short j=0; /* local tmp buffer */
510 char tmp[len];
511 short diff = p_width - *Lmargin - where->x;
512 short _start=0;
514 if(diff < 20) {
515 where->y += myDrawText(tPtr, para, items, *nitems,
516 p_width-*Lmargin, where->y, draw, spacepos);
517 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
518 *nitems = 0;
519 *Lmargin = WMAX(0, m.body - m.first);
520 where->x = 0;
521 diff = p_width - *Lmargin - where->x;
524 for(J=0; J<len; J++) {
525 tmp[j] = bufr[J];
526 if(WMWidthOfString(font, tmp, j+1) > diff) {
527 drawTChunkPart(tPtr, chunk, tmp, m, para, items, nitems,
528 j, start+_start, Lmargin, where, draw, spacepos);
529 _start = J;
530 J--; j=0;
531 } else j++;
533 /* and there's always that last chunk, get it too */
534 drawTChunkPart(tPtr, chunk, tmp, m, para, items, nitems,
535 j, start+_start, Lmargin, where, draw, spacepos);
539 /* this function does what it's called :-)
540 o It is also used for calculating extents of para,
541 (returns height) so watch out for (Bool) draw
542 o Also used to determine where mouse was clicked */
543 static int
544 putParagraphOnPixmap(Text *tPtr, Paragraph *para, Bool draw)
546 char bufr[MAX_WORD_LENGTH+1]; /* a single word + '\0' */
547 MyTextItems items[MAX_CHUNX+1];
548 short lmargin, spacepos, i, s, nitems, start;
549 LocalMargins m;
550 XPoint where;
551 Chunk *chunk;
553 if(!tPtr->view->flags.realized || !para) return 0;
555 where.x = 0, where.y =0, nitems = 0;
556 m.left = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
557 m.right = WMGetRulerMargin(tPtr->ruler, WRulerRight) - m.left;
558 m.first = para->fmargin, m.body = para->bmargin;
560 if(draw) {
561 W_Screen *screen = tPtr->view->screen;
562 if(para->drawbuffer)
563 XFreePixmap(screen->display, para->drawbuffer);
564 if(para->width<2*tPtr->dFont->height) para->width = 2*tPtr->dFont->height;
565 if(para->height<tPtr->dFont->height) para->height = tPtr->dFont->height;
566 para->drawbuffer = XCreatePixmap(screen->display,
567 tPtr->view->window, para->width, para->height, screen->depth);
568 XFillRectangle(screen->display, para->drawbuffer,
569 WMColorGC(tPtr->bg), 0, 0, para->width, para->height);
573 //if(para->align != tPtr->dAlignment)
574 // para->align = tPtr->dAlignment;
576 /* draw the bullet if appropriate */
577 if(m.body>m.first && !tPtr->monoFont) {
578 lmargin = m.body - m.first;
579 if(draw) {
580 if(para->bulletPix)
581 WMDrawPixmap(para->bulletPix, para->drawbuffer, lmargin-10, 5);
582 else
583 WMDrawPixmap(tPtr->dBulletPix, para->drawbuffer, lmargin-10, 5);
585 /* NeXT sez next tab, I say the m.body - m.first margin */
586 } else {
587 lmargin = WMAX(0, m.first - m.body);
590 if(tPtr->findingClickPoint && !para->chunks) {
591 tPtr->currentChunk = NULL;
592 tPtr->foundClickPoint = True;
593 tPtr->tpos = 0;
594 tPtr->clicked.x = lmargin;
595 tPtr->clicked.y = 5;
596 tPtr->clheight = para->height;
597 tPtr->clwidth = 0;
598 return 0;
601 chunk = para->chunks;
602 while(chunk) {
604 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0;
606 if(chunk->type == ctImage && !tPtr->monoFont ) {
607 drawPChunkPart(tPtr, chunk, m, para, items, &nitems,
608 &lmargin, &where, draw);
609 } else if(chunk->text && chunk->type == ctText) {
610 if(tPtr->wrapping == wrNone) {
611 drawTChunkPart(tPtr, chunk, chunk->text, m, para, items, &nitems,
612 chunk->chars, 0, &lmargin, &where, draw, spacepos);
613 } else if(tPtr->wrapping == wrWord) {
614 spacepos=0, i=0, start=0;
615 while(spacepos < chunk->chars) {
616 bufr[i] = chunk->text[spacepos];
617 if( bufr[i] == ' ' || i >= MAX_WORD_LENGTH ) {
618 if(bufr[i] == ' ') s=1; else s=0;
619 drawTChunkPart(tPtr, chunk, bufr, m, para,
620 items, &nitems, i+s, start, &lmargin, &where,
621 draw, spacepos);
622 start = spacepos+s;
623 if(i > MAX_WORD_LENGTH-1)
624 spacepos--;
625 i=0;
626 } else i++;
627 spacepos++;
629 /* catch that last onery one. */
630 drawTChunkPart(tPtr, chunk, bufr, m, para,
631 items, &nitems, i, start, &lmargin, &where, draw, spacepos);
632 } } chunk = chunk->next;
634 /* we might have a few leftover items that need drawing */
635 if(nitems >0) {
636 where.y += myDrawText(tPtr, para, items,
637 nitems, m.right-m.left-lmargin, where.y, draw, spacepos);
638 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0;
640 return where.y;
643 static int
644 calcParaExtents(Text *tPtr, Paragraph *para)
646 if(!para) return 0;
648 if(tPtr->monoFont) {
649 para->width = tPtr->visibleW;
650 para->fmargin = 0;
651 para->bmargin = 0;
652 para->rmargin = tPtr->visibleW;
653 } else {
654 para->width = WMGetRulerMargin(tPtr->ruler, WRulerRight) -
655 WMIN(para->fmargin, para->bmargin)
656 - WMGetRulerOffset(tPtr->ruler);
659 if(!para->chunks)
660 para->height = tPtr->dFont->height;
661 else
662 para->height = putParagraphOnPixmap(tPtr, para, False);
664 if(para->height<tPtr->dFont->height)
665 para->height = tPtr->dFont->height;
666 para->bottom = para->top + para->height;
667 return para->height;
671 /* rather than bother with redrawing _all_ the pixmaps, simply
672 rearrange (i.e., push down or pull up) paragraphs after this one */
673 static void
674 affectNextParas(Text *tPtr, Paragraph *para, int move_y)
676 Paragraph *next;
677 int old_y = 0;
679 if(!para || move_y==0) return;
680 if(move_y == -23) {
681 old_y = para->bottom;
682 calcParaExtents(tPtr, para);
683 old_y -= para->bottom;
684 if(old_y == 0) return;
685 move_y = -old_y;
686 }if(move_y == 0) return;
688 next = para->next;
689 while(next) {
690 next->top += move_y;
691 next->bottom = next->top + next->height;
692 next = next->next; // I know, I know
693 }tPtr->docHeight += move_y;
695 #if 0
696 tPtr->vpos += move_y;
697 if(tPtr->vpos < 0) tPtr->vpos = 0;
698 if(tPtr->vpos > tPtr->docHeight - tPtr->visibleH)
699 tPtr->vpos = tPtr->docHeight - tPtr->visibleH;
700 #endif
705 static void
706 calcDocExtents(Text *tPtr)
708 Paragraph *para;
710 if(tPtr->monoFont) {
711 tPtr->docWidth = tPtr->visibleW;
712 } else {
713 tPtr->docWidth = WMGetRulerMargin(tPtr->ruler, WRulerRight) -
714 WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
716 tPtr->docHeight = 0;
717 para = tPtr->paragraphs;
718 if(para) {
719 while(para) {
720 para->top = tPtr->docHeight;
721 tPtr->docHeight += calcParaExtents(tPtr, para);
722 para->bottom = tPtr->docHeight;
723 para = para->next;
725 } else { /* default to this if no paragraphs */
726 tPtr->docHeight = tPtr->dFont->height;
728 #if 0
729 if(tPtr->editable) /* add space at bottom to enter new stuff */
730 tPtr->docHeight += tPtr->dFont->height;
731 #endif
735 /* If any part of a paragraph is viewable, the entire
736 paragraph is drawn on an otherwise empty (XFreePixmap) pixmap.
737 The actual viewable parts of the paragraph(s) are then pieced
738 together via paintText:
740 -------------------------------------------
741 || this is a paragraph in this document||
742 ||========================================||
743 || | only part of it is visible though. ||
744 || |-------------------------------------||
745 ||[.| This is another paragraph ||
746 || | which I'll make relatively long ||
747 || | just for the sake of writing a long ||
748 || | paragraph with a picture: ^_^ ||
749 || |-------------------------------------||
750 ||==| Of the three paragraphs, only ||
751 ||/\| the preceding was totally copied to ||
752 ||\/| totally copied to the window, even ||
753 ==========================================||
754 though they are all on pixmaps.
755 -------------------------------------------
756 This paragraph exists only in
757 memory and so has a NULL pixmap.
758 -------------------------------------------
761 simple, right? Performance: the best of both worlds...
762 o fast scrolling: no need to rewrite what's already
763 on the screen, simply XCopy it.
764 o fast typing: only change current para, then simply
765 affect other (i.e., subsequent) paragraphs.
766 o If no part of para is on screen, gdbFree pixmap; else draw on
767 individual pixmap per para then piece several paras together
768 o Keep track of who to XCopy to window (see paintText) */
769 static void
770 drawDocumentPartsOnPixmap(Text *tPtr, Bool all)
772 Paragraph *para;
774 para = tPtr->paragraphs;
775 while(para) {
776 /* the 32 reduces jitter on the human eye by preparing paragraphs
777 in anticipation of when the _moving_ scrollbar reaches them */
778 if(para->bottom + 32 < tPtr->vpos ||
779 para->top > tPtr->visibleH + tPtr->vpos + 32 ) {
780 if(para->drawbuffer) {
781 XFreePixmap(tPtr->view->screen->display, para->drawbuffer);
782 para->drawbuffer = (Pixmap) NULL;
784 } else {
785 if(!para->drawbuffer || all)
786 putParagraphOnPixmap(tPtr, para, True);
788 para = para->next;
794 /* this function blindly copies the "visible" parts of a pragraph
795 unto the view, (top-down approach). It starts drawing from
796 the top of the view (which paragraph to draw is determined by
797 drawDocumentPartsOnPixmap); it stops at the bottom of the view. */
798 static void
799 paintText(Text *tPtr)
801 short lmargin, para_lmargin;
802 int from=5, to=5, height;
803 Paragraph *para;
804 short vS=0, hS=0, rh=0;
806 if(!tPtr->view->flags.realized) return;
808 if(tPtr->rulerShown) rh = 40;
809 to += rh;
811 if(tPtr->hasVscroller) vS = 21;
812 if(tPtr->hasHscroller) hS = 21;
814 //XClearWindow(tPtr->view->screen->display, tPtr->view->window);
816 lmargin = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
817 if(tPtr->paragraphs) {
818 para = tPtr->paragraphs;
819 while(para) {
820 if(para->drawbuffer) {
821 from = (para->top<=tPtr->vpos)?tPtr->vpos-para->top:0;
822 height = para->height - from;
823 if(from>=0 && height>0 ) {
824 para_lmargin = WMIN(para->fmargin, para->bmargin);
825 if(lmargin-vS<WMIN(para->fmargin, para->bmargin)) {
826 #if 0
827 XClearArea(tPtr->view->screen->display, tPtr->view->window,
828 lmargin, to, 2+para_lmargin, height, False);
829 #else
830 XFillRectangle(tPtr->view->screen->display, tPtr->view->window,
831 WMColorGC(tPtr->dColor), lmargin, to, 2+para_lmargin, height);
832 #endif
834 XCopyArea(tPtr->view->screen->display, para->drawbuffer,
835 tPtr->view->window, WMColorGC(tPtr->bg), 0, from,
836 para->width-4, height, lmargin+para_lmargin+2, to);
837 if( (to+=height) > tPtr->visibleH+rh)
838 break;
840 para = para->next;
844 #if 0
845 /* clear any left over space (esp. during para deletes/ ruler changes) */
846 if(tPtr->docHeight < tPtr->visibleH && tPtr->visibleH+rh+5-to>0) {
847 XClearArea(tPtr->view->screen->display, tPtr->view->window, vS, to,
848 tPtr->view->size.width-vS, tPtr->visibleH+rh+hS+5-to, False);
851 if(lmargin>vS)
852 XClearArea(tPtr->view->screen->display, tPtr->view->window,
853 vS+1, rh+5, lmargin-vS, tPtr->visibleH+rh+5-vS, False);
856 // from the "selection" days...
857 W_DrawRelief(tPtr->view->screen, WMWidgetXID(tPtr),
858 tPtr->sRect.pos.x, tPtr->sRect.pos.y,
859 tPtr->sRect.size.width, tPtr->sRect.size.height, tPtr->relief);
860 #endif
862 W_DrawRelief(tPtr->view->screen, WMWidgetXID(tPtr), 0, rh,
863 tPtr->visibleW+vS, tPtr->visibleH+hS, tPtr->relief);
865 if(tPtr->editable && tPtr->clheight > 0) {
866 int top = tPtr->cursor.y-tPtr->vpos;
867 int bot = top+tPtr->clheight;
868 if(bot>5) {
869 if(top<5) top=5;
870 if(bot>tPtr->visibleH+hS-2) bot = tPtr->visibleH+hS-2;
871 if(bot-top>1) {
872 //do something about italic text...
873 XDrawLine(tPtr->view->screen->display, tPtr->view->window,
874 WMColorGC(tPtr->dColor), lmargin+tPtr->cursor.x, top,
875 lmargin+tPtr->cursor.x, bot);
876 } } }
882 /* called anytime either the ruler, vscroller or hscroller is hidden/shown,
883 or when the widget is resized by some user action */
884 static void
885 resizeText(W_ViewDelegate *self, WMView *view)
887 Text *tPtr = (Text *)view->self;
888 short rh=0;
890 if(!tPtr->monoFont && tPtr->rulerShown)
891 rh = 40;
893 W_ResizeView(view, view->size.width, view->size.height);
894 WMResizeWidget(tPtr->ruler, view->size.width, 40);
897 if(tPtr->hasVscroller) {
898 WMMoveWidget(tPtr->vscroller, 1, 1+rh);
899 WMResizeWidget(tPtr->vscroller, 20, view->size.height-rh-2);
900 tPtr->visibleW = view->size.width-21;
902 if(tPtr->hasHscroller) {
903 WMMoveWidget(tPtr->hscroller, 20, view->size.height-21);
904 WMResizeWidget(tPtr->hscroller, view->size.width-21, 20);
905 tPtr->visibleH = view->size.height-21-rh;
906 } else tPtr->visibleH = view->size.height-rh;
907 } else {
908 tPtr->visibleW = view->size.width;
909 if(tPtr->hasHscroller) {
910 WMMoveWidget(tPtr->hscroller, 1, view->size.height-21);
911 WMResizeWidget(tPtr->hscroller, view->size.width-2, 20);
912 tPtr->visibleH = view->size.height-21-rh;
913 } else tPtr->visibleH = view->size.width-2-rh;
915 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
918 W_ViewDelegate _TextViewDelegate =
920 NULL,
921 NULL,
922 resizeText,
923 NULL,
928 /* a plain text parser */
929 /* this gives useful hints on how to make a more
930 interesting parser for say HTML, RTF */
931 static void
932 defaultParser(Text *tPtr, void *data, short type)
934 char *start, *mark, *text = (char *) data;
935 Chunk *chunk = NULL;
936 Paragraph *para = NULL;
938 start = text;
939 while(start) {
940 mark = strchr(start, '\n');
941 if(mark) {
942 /* there is a newline, indicating the need for a new paragraph */
943 /* attach the chunk to the current paragraph */
944 if((short)(mark-start) > 1) {
945 /* ignore chunks with just a single newline but still make a
946 blank paragraph */
947 chunk = (tPtr->funcs.createTChunk) (start, (short)(mark-start),
948 tPtr->dFont, tPtr->dColor, 0, False);
949 (tPtr->funcs.insertChunk) (tPtr, chunk, type);
951 /* _then_ create a new paragraph for the _next_ chunk */
952 para = (tPtr->funcs.createParagraph) (0, 0, tPtr->visibleW,
953 NULL, 0, WALeft);
954 (tPtr->funcs.insertParagraph) (tPtr, para, type);
955 start = mark+1;
956 } else {
957 /* just attach the chunk to the current paragraph */
958 if(strlen(start) > 0) {
959 chunk = (tPtr->funcs.createTChunk) (start, strlen(start),
960 tPtr->dFont, tPtr->dColor, 0, False);
961 (tPtr->funcs.insertChunk) (tPtr, chunk, type);
962 } start = mark;
967 static void
968 updateScrollers(Text *tPtr)
970 if(tPtr->hasVscroller) {
971 if(tPtr->docHeight < tPtr->visibleH) {
972 WMSetScrollerParameters(tPtr->vscroller, 0, 1);
973 tPtr->vpos = 0;
974 } else {
975 float vmax = (float)(tPtr->docHeight);
976 WMSetScrollerParameters(tPtr->vscroller,
977 ((float)tPtr->vpos)/(vmax - (float)tPtr->visibleH),
978 (float)tPtr->visibleH/vmax);
982 if(tPtr->hasHscroller)
986 static void
987 scrollersCallBack(WMWidget *w, void *self)
989 Text *tPtr = (Text *)self;
990 Bool scroll = False;
991 Bool dimple = False;
993 if(!tPtr->view->flags.realized) return;
995 if(w == tPtr->vscroller) {
996 float vmax;
997 int height;
998 vmax = (float)(tPtr->docHeight);
999 height = tPtr->visibleH;
1000 if(height>7)
1001 height -= 7; /* the top border (5) + bottom (2) */
1003 switch(WMGetScrollerHitPart(tPtr->vscroller)) {
1004 case WSDecrementLine:
1005 if(tPtr->vpos > 0) {
1006 if(tPtr->vpos>16) tPtr->vpos-=16;
1007 else tPtr->vpos=0;
1008 scroll=True;
1009 }break;
1010 case WSIncrementLine: {
1011 int limit = tPtr->docHeight - height;
1012 if(tPtr->vpos < limit) {
1013 if(tPtr->vpos<limit-16) tPtr->vpos+=16;
1014 else tPtr->vpos=limit;
1015 scroll = True;
1016 }}break;
1017 case WSDecrementPage:
1018 tPtr->vpos -= height;
1020 if(tPtr->vpos < 0)
1021 tPtr->vpos = 0;
1022 dimple = True;
1023 scroll = True;
1024 printf("dimple needs to jump to mouse location ;-/\n");
1025 break;
1026 case WSIncrementPage:
1027 tPtr->vpos += height;
1028 if(tPtr->vpos > (tPtr->docHeight - height))
1029 tPtr->vpos = tPtr->docHeight - height;
1030 dimple = True;
1031 scroll = True;
1032 printf("dimple needs to jump to mouse location ;-/\n");
1033 break;
1036 case WSKnob:
1037 tPtr->vpos = WMGetScrollerValue(tPtr->vscroller)
1038 * (float)(tPtr->docHeight - height);
1039 scroll = True;
1040 break;
1042 #if 0
1043 case WSKnobSlot:
1044 case WSNoPart:
1045 float vmax = (float)(tPtr->docHeight);
1046 ((float)tPtr->vpos)/(vmax - (float)tPtr->visibleH),
1047 (float)tPtr->visibleH/vmax);
1048 dimple =where mouse is.
1049 #endif
1050 break;
1052 scroll = (tPtr->vpos != tPtr->prevVpos);
1053 tPtr->prevVpos = tPtr->vpos;
1056 if(w == tPtr->hscroller)
1059 //need scrollv || scrollh
1060 if(scroll) {
1062 if(0&&dimple) {
1063 if(tPtr->rulerShown)
1064 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
1065 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
1066 else
1067 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
1068 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
1071 updateScrollers(tPtr);
1072 drawDocumentPartsOnPixmap(tPtr, False);
1073 paintText(tPtr);
1078 void
1079 W_InsertText(WMText *tPtr, void *data, int position)
1081 if(!tPtr) return;
1082 if(!data) {
1083 Paragraph *para = tPtr->paragraphs, *ptmp;
1084 Chunk *chunk, *ctmp;
1085 WMFreezeText(tPtr);
1086 while(para) {
1087 chunk = para->chunks;
1088 while(chunk) {
1089 if(chunk->type == ctText && chunk->text)
1090 wgdbFree(chunk->text);
1091 else if(chunk->pixmap)
1092 WMReleasePixmap(chunk->pixmap);
1093 ctmp = chunk;
1094 chunk = chunk->next;
1095 wgdbFree(ctmp);
1097 ptmp = para;
1098 para = para->next;
1099 if(ptmp->drawbuffer)
1100 XFreePixmap(tPtr->view->screen->display, ptmp->drawbuffer);
1101 wgdbFree(ptmp);
1103 tPtr->paragraphs = NULL;
1104 tPtr->currentPara = NULL;
1105 tPtr->currentChunk = NULL;
1106 WMThawText(tPtr);
1107 WMRefreshText(tPtr, 0, 0);
1108 return;
1111 if(tPtr->parser)
1112 (tPtr->parser)(tPtr, data, position >= 0 ? 1 : 0);
1113 else
1114 defaultParser(tPtr, data, position >= 0 ? 1 : 0);
1117 static void
1118 cursorToTextPosition(Text *tPtr, int x, int y)
1120 Paragraph *para = NULL;
1121 Chunk *chunk = NULL;
1122 WMFont *font;
1123 short line_width=0;
1124 short orig_x, orig_y;
1126 if(x<(tPtr->hasVscroller?21:1)) {
1127 y -= tPtr->clheight;
1128 x = tPtr->view->size.width; //tPtr->visibleW;
1129 } else if(x>tPtr->clwidth && x<tPtr->clicked.x) {
1130 //x = (tPtr->hasVscroller)?21:1;
1131 //y += tPtr->clheight;
1134 if(x<0) x=0;
1135 orig_x = x;
1137 if(y<0 || y>tPtr->view->size.height-3) return;
1138 orig_y = y;
1139 tPtr->clicked.x = orig_x;
1140 tPtr->clicked.y = y;
1141 tPtr->clicked.y += tPtr->vpos;
1142 tPtr->clicked.y -= tPtr->rulerShown?40:0;
1143 para = tPtr->paragraphs;
1144 if(!para) return;
1145 while(para->next) {
1146 if( tPtr->clicked.y>= para->top-4 &&
1147 tPtr->clicked.y < para->bottom+4) break;
1148 para = para->next;
1149 } if(!(tPtr->currentPara = para)) return;
1151 tPtr->clicked.y -= para->top;
1152 if(tPtr->clicked.y<0) tPtr->clicked.y=0;
1153 if(tPtr->hasVscroller) x -= 21;
1154 if(x<0) x=0;
1156 tPtr->findingClickPoint = True;
1157 tPtr->foundClickPoint = False;
1158 /* also affects tPtr->currentChunk, tPtr->clicked.x and y,
1159 tPtr->clheight and ->width */
1160 putParagraphOnPixmap(tPtr, para, False);
1161 tPtr->findingClickPoint = False;
1162 tPtr->clicked.y += para->top;
1164 if(tPtr->currentChunk) {
1165 short _width=0, start=tPtr->tpos, done=False, w=0;
1166 chunk = tPtr->currentChunk;
1167 while(!done && chunk && line_width<tPtr->clwidth) {
1168 if(chunk->type == ctText) {
1169 font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
1170 for (w=start; w<chunk->chars; w++) {
1171 _width = WMWidthOfString(font, &chunk->text[w], 1);
1172 line_width += _width;
1173 if(line_width+tPtr->clicked.x >= x) {
1174 line_width -= _width;
1175 done = True;
1176 printf("break\n");
1177 break;
1180 if(0&&chunk->next) {
1181 if(chunk->next->type == ctImage) {
1182 if(x+10 < line_width+chunk->next->pixmap->width) {
1183 printf("true\n");
1184 done = True;
1185 } } }
1186 } else {
1187 _width = chunk->pixmap->width;
1188 line_width += _width;
1189 if(line_width+tPtr->clicked.x >= x) {
1190 line_width -= _width;
1191 tPtr->tpos = 0;
1192 done = True;
1195 if(!done) {
1196 chunk = chunk->next;
1197 start = w = 0;
1198 } else {
1199 tPtr->tpos = w;
1200 tPtr->currentChunk = chunk;
1201 break;
1202 } } } else {
1203 short vS = (tPtr->hasVscroller)?32:12;
1204 if(para->align == WARight) {
1205 tPtr->clicked.x = tPtr->view->size.width-vS;
1206 } else if (para->align == WACenter) {
1207 tPtr->clicked.x = -(vS/2)+(tPtr->view->size.width-vS)/2;
1208 } else {
1209 tPtr->clicked.x = 2;
1212 tPtr->cursor.x = tPtr->clicked.x+2+line_width;
1213 tPtr->cursor.y = tPtr->clicked.y;
1214 tPtr->clicked.y = orig_y;
1215 tPtr->clicked.x = orig_x;
1216 putParagraphOnPixmap(tPtr, para, True);
1217 paintText(tPtr);
1220 static void
1221 deleteTextInteractively(Text *tPtr, DeleteType type)
1223 Paragraph *para;
1224 Chunk *chunk;
1225 short pos,w=0,h=0, doprev=False, doprevpara=False;
1226 WMFont *font;
1227 int current = WMGetTextCurrentChunk(tPtr);
1229 if(!(para = tPtr->currentPara)) return;
1230 if(!(chunk = tPtr->currentChunk)) return;
1231 font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
1232 doprev = (tPtr->tpos < 2);
1234 switch(type) {
1235 case dtDelete: /* delete _after_ cursor ... implement later */
1236 case dtBackSpace: /* delete _before_ cursor */
1237 if(chunk->chars > 1) {
1238 pos = tPtr->tpos-1;
1239 printf("here %d\n", pos);
1240 if(pos>0) {
1241 w = WMWidthOfString(font, &chunk->text[pos], 1);
1242 memmove(&(chunk->text[pos]),
1243 &(chunk->text[pos+1]), chunk->chars-pos+1);
1244 tPtr->tpos--;
1245 chunk->chars--;
1246 } } else {
1247 WMRemoveTextChunk(tPtr, current);
1248 doprev = True;
1251 if(doprev) {
1252 if(current > 0) {
1253 WMSetTextCurrentChunk(tPtr, current-1);
1254 if(!tPtr->currentChunk) {
1255 printf("PREV PARA\n");
1256 } else {
1257 tPtr->tpos = tPtr->currentChunk->chars;
1259 } else if(0){
1260 int currentp = WMGetTextCurrentParagraph(tPtr);
1261 doprevpara = True;
1262 if(currentp > 1) {
1263 para->chunks = NULL;
1264 WMRemoveTextParagraph(tPtr, currentp);
1265 WMSetTextCurrentParagraph(tPtr, currentp-1);
1266 WMSetTextCurrentChunk(tPtr, -1);
1267 para = tPtr->currentPara;
1268 if(para) {
1269 if(!tPtr->currentChunk || !para->chunks) {
1270 para->chunks = chunk;
1271 tPtr->currentChunk = chunk;
1272 } else
1273 tPtr->currentChunk->next = chunk;
1274 } } } } }
1276 if(1) { //if(1||(para && !doprevpara)) {
1277 affectNextParas(tPtr, para, -23);
1278 putParagraphOnPixmap(tPtr, para, True);
1279 drawDocumentPartsOnPixmap(tPtr, False);
1280 updateScrollers(tPtr);
1281 paintText(tPtr);
1282 //cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1283 } else WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1287 /* give us nice chunk sizes (multiples of 16) */
1288 static short
1289 reqBlockSize(short requested)
1291 return requested+16-(requested%16);
1294 static void
1295 insertTextInteractively(Text *tPtr, char *text)
1297 Paragraph *para=NULL;
1298 Chunk *chunk=NULL, *newchunk=NULL;
1299 int height = -23; /* should only be changed upon newline */
1300 short w=0,h=0;
1301 WMFont *font;
1303 if(!tPtr->editable) return;
1304 if(*text == '\n' && tPtr->ignoreNewLine)
1305 return;
1307 para = tPtr->currentPara;
1308 chunk = tPtr->currentChunk;
1309 font = (tPtr->monoFont || !chunk)?tPtr->dFont:chunk->font;
1311 if(*text == '\n') {
1312 int new_top=0;
1313 if(chunk) { /* there's a chunk (or part of it) to detach from old */
1314 int current = WMGetTextCurrentChunk(tPtr);
1315 if(tPtr->tpos <=0) { /* at start of chunk */
1316 if(current<1) { /* the first chunk... make old para blank */
1317 newchunk = para->chunks;
1318 para->chunks = NULL;
1319 putParagraphOnPixmap(tPtr, para, True);
1320 } else { /* not first chunk... */
1321 printf("cut me out \n");
1323 } else if(tPtr->tpos < chunk->chars && chunk->type == ctText) {
1324 /* not at start of chunk */
1325 char text[chunk->chars-tPtr->tpos+1];
1326 int i=0;
1327 do {
1328 text[i] = chunk->text[tPtr->tpos+i];
1329 } while(++i < chunk->chars-tPtr->tpos);
1330 chunk->chars -= i;
1331 newchunk = (tPtr->funcs.createTChunk) (text, i, chunk->font,
1332 chunk->color, chunk->script, chunk->ul);
1333 newchunk->next = chunk->next;
1334 chunk->next = NULL;
1335 /* might want to demalloc for LARGE cuts */
1336 //calcParaExtents(tPtr, para);
1337 para->height = putParagraphOnPixmap(tPtr, para, True);
1338 //putParagraphOnPixmap(tPtr, para, True);
1339 } else if(tPtr->tpos >= chunk->chars) {
1340 Chunk *prev;
1341 WMSetTextCurrentChunk(tPtr, current-1);
1342 prev = tPtr->currentChunk;
1343 if(!prev) return;
1344 newchunk = prev->next;
1345 prev->next = NULL;
1346 putParagraphOnPixmap(tPtr, para, True);
1348 } else newchunk = NULL;
1350 if(para) /* the preceeding one */
1351 new_top = para->bottom;
1353 WMAppendTextStream(tPtr, "\n");
1354 para = tPtr->currentPara;
1355 if(!para) return;
1356 para->chunks = newchunk;
1357 tPtr->currentChunk = newchunk;
1358 tPtr->tpos = 0;
1359 para->top = new_top;
1360 calcParaExtents(tPtr, para);
1361 height = para->height;
1362 } else {
1363 if(!para) {
1364 WMAppendTextStream(tPtr, text);
1365 para = tPtr->currentPara;
1366 } else if(!para->chunks || !chunk) {
1367 //WMPrependTextStream(tPtr, text);
1368 WMAppendTextStream(tPtr, text);
1369 } else if(chunk->type == ctImage) {
1370 WMPrependTextStream(tPtr, text);
1372 printf("\n\nprepe\n\n");
1373 } else {
1374 if(tPtr->tpos > chunk->chars) {
1375 printf("\n\nmore\n\n");
1376 tPtr->tpos = chunk->chars;
1379 if(chunk->chars+1 >= chunk->mallocedSize) {
1380 chunk->mallocedSize = reqBlockSize(chunk->chars+1);
1381 chunk->text = wrealloc(chunk->text, chunk->mallocedSize);
1384 memmove(&(chunk->text[tPtr->tpos+1]), &chunk->text[tPtr->tpos],
1385 chunk->chars-tPtr->tpos+1);
1386 w = WMWidthOfString(font, text, 1);
1387 memmove(&chunk->text[tPtr->tpos], text, 1);
1388 chunk->chars++;
1389 tPtr->tpos++;
1390 //doc->clickstart.cursor.x +=
1391 //WMWidthOfString(chunk->fmt->font, text,len);
1396 if(para) {
1397 affectNextParas(tPtr, para, height);
1398 putParagraphOnPixmap(tPtr, para, True);
1399 drawDocumentPartsOnPixmap(tPtr, False);
1400 updateScrollers(tPtr);
1401 paintText(tPtr);
1402 //cursorToTextPosition(tPtr, tPtr->clicked.x+w, tPtr->clicked.y);
1403 //check for "sneppah tahw" with blank paras...
1404 //paintText(tPtr);
1409 static void
1410 selectRegion(Text *tPtr, int x, int y)
1412 tPtr->sRect.pos.x = WMIN(tPtr->clicked.x, x);
1413 tPtr->sRect.size.width = abs(tPtr->clicked.x-x);
1414 tPtr->sRect.pos.y = WMIN(tPtr->clicked.y, y);
1415 if(tPtr->sRect.pos.y<0) tPtr->sRect.pos.y=0;
1416 tPtr->sRect.size.height = abs(tPtr->clicked.y-y);
1419 while(y>tPtr->visibleH && tPtr->vpos < tPtr->docHeight-tPtr->visibleH) {
1420 WMRefreshText(tPtr, tPtr->vpos+16, tPtr->hpos);
1423 //printf("%d %d \n", y, tPtr->vpos);
1425 //foreach para in selection...
1426 drawDocumentPartsOnPixmap(tPtr, True);
1427 paintText(tPtr);
1434 #define WM_EMACSKEYMASK ControlMask
1435 #define WM_EMACSKEY_LEFT XK_b
1436 #define WM_EMACSKEY_RIGHT XK_f
1437 #define WM_EMACSKEY_HOME XK_a
1438 #define WM_EMACSKEY_END XK_e
1439 #define WM_EMACSKEY_BS XK_h
1440 #define WM_EMACSKEY_DEL XK_d
1442 static void
1443 handleTextKeyPress(Text *tPtr, XEvent *event)
1445 char buffer[2];
1446 KeySym ksym;
1447 int control_pressed = False;
1449 if(!tPtr->editable) return;
1451 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
1452 control_pressed = True;
1453 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = '\0';
1455 switch(ksym) {
1457 case XK_Right:
1458 case XK_Left:
1459 if(tPtr->currentChunk) {
1460 short w;
1461 Chunk *chunk = tPtr->currentChunk;
1462 if(chunk->type == ctText) {
1463 WMFont *font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
1464 if(ksym==XK_Right) {
1465 short pos = (tPtr->tpos<chunk->chars)?tPtr->tpos+1:
1466 chunk->chars;
1467 w = WMWidthOfString(font,&chunk->text[pos],1);
1468 } else {
1469 short pos = (tPtr->tpos>0)?tPtr->tpos-1:0;
1470 w = WMWidthOfString(font,&chunk->text[pos],1);
1472 } else { w = chunk->pixmap->width; }
1473 if(ksym==XK_Right) w = -w;
1474 cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1475 } else {
1476 if(ksym==XK_Right) ksym = XK_Down;
1477 else ksym = XK_Up;
1478 goto noCChunk;
1480 break;
1482 case XK_Down:
1483 case XK_Up:
1484 noCChunk: { short h = tPtr->clheight-2;
1485 if(ksym==XK_Down) h = -h;
1486 cursorToTextPosition(tPtr, tPtr->clicked.x, tPtr->clicked.y-h);
1487 } break;
1489 case XK_BackSpace:
1490 deleteTextInteractively(tPtr, dtBackSpace);
1491 break;
1493 case XK_Delete:
1494 case XK_KP_Delete:
1495 deleteTextInteractively(tPtr, dtDelete);
1496 break;
1498 case XK_Return:
1499 buffer[0] = '\n';
1500 default:
1501 if(buffer[0] != '\0' && (buffer[0] == '\n' || !iscntrl(buffer[0])))
1502 insertTextInteractively(tPtr, buffer);
1503 else if(control_pressed && ksym==XK_r)
1504 {Bool i = !tPtr->rulerShown; WMShowTextRuler(tPtr, i);
1505 tPtr->rulerShown = i; }
1512 static void
1513 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1514 void *cdata, WMData *data)
1516 Text *tPtr = (Text *)view->self;
1517 char *str;
1520 tPtr->waitingForSelection = False;
1521 if(data) {
1522 str = (char*)WMDataBytes(data);
1523 if(tPtr->tpos<1) WMPrependTextStream(tPtr, str);
1524 else WMAppendTextStream(tPtr, str);
1525 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1526 } else {
1527 int n;
1528 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1529 if(str) {
1530 str[n] = 0;
1531 if(tPtr->tpos<1) WMPrependTextStream(tPtr, str);
1532 else WMAppendTextStream(tPtr, str);
1533 XFree(str);
1534 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1540 static void
1541 releaseSelection(Text *tPtr)
1543 Paragraph *para = tPtr->paragraphs;
1544 Chunk *chunk;
1545 while(para) {
1546 chunk = para->chunks;
1547 while(chunk) {
1548 chunk->selected = False;
1549 chunk = chunk->next;
1551 para = para->next;
1553 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1554 tPtr->ownsSelection = False;
1555 drawDocumentPartsOnPixmap(tPtr, True);
1556 paintText(tPtr);
1560 static WMData*
1561 requestHandler(WMView *view, Atom selection, Atom target,
1562 void *cdata, Atom *type)
1564 Text *tPtr = view->self;
1565 int count;
1566 Display *dpy = tPtr->view->screen->display;
1567 Atom _TARGETS;
1568 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1569 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1570 WMData *data = NULL;
1573 if(!tPtr->ownsSelection || !tPtr->paragraphs) return NULL;
1574 //printf("got here\n");
1576 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
1577 //for bleh in selection...
1578 char *s = NULL;
1579 Paragraph *para = tPtr->paragraphs;
1580 Chunk *chunk = NULL;
1581 char pixmap[] = "[pixmap]";
1582 Bool first=True;
1583 short len;
1585 while(para) {
1586 chunk = para->chunks;
1587 while(chunk) {
1589 if(chunk->selected && chunk->type == ctText) {
1590 len = chunk->chars; //chunk->sEnd - chunk->sStart;
1591 if(len>0) {
1592 s = wmalloc(len+1);
1593 if(s) {
1594 memcpy(s, &chunk->text[0*chunk->sStart], len);
1595 s[len] = 0;
1596 if(first) {
1597 data = WMCreateDataWithBytes(s, strlen(s));
1598 first = False;
1599 } else {
1600 printf("append: %c %d\n", *s, strlen(s));
1601 WMAppendDataBytes(data, s, strlen(s));
1603 //gdbFree(s);
1604 } } }
1605 #if 0
1606 printf("len is %d [%d %d] %d \n", len, chunk->sStart, chunk->sEnd,
1607 chunk->chars);
1608 #endif
1609 chunk = chunk->next;
1611 para = para->next;
1614 if(data) {
1615 WMSetDataFormat(data, 8);
1616 *type = target;
1618 return data;
1621 #if 0
1622 _TARGETS = XInternAtom(dpy, "TARGETS", False);
1623 if (target == _TARGETS) {
1624 Atom *ptr = wmalloc(4 * sizeof(Atom));
1625 ptr[0] = _TARGETS;
1626 ptr[1] = XA_STRING;
1627 ptr[2] = TEXT;
1628 ptr[3] = COMPOUND_TEXT;
1630 data = WMCreateDataWithBytes(ptr, 4*4);
1631 WMSetDataFormat(data, 32);
1633 *type = target;
1634 return data;
1636 #endif
1638 return NULL;
1643 static void
1644 lostHandler(WMView *view, Atom selection, void *cdata)
1646 WMText *tPtr = (WMText *)view->self;
1647 releaseSelection(tPtr);
1650 static WMSelectionProcs selectionHandler = {
1651 requestHandler, lostHandler, NULL };
1653 static void
1654 _notification(void *observerData, WMNotification *notification)
1656 WMText *to = (WMText *)observerData;
1657 WMText *tw = (WMText *)WMGetNotificationClientData(notification);
1658 if (to != tw) lostHandler(to->view, XA_PRIMARY, NULL);
1661 static void
1662 handleTextEvents(XEvent *event, void *data)
1664 Text *tPtr = (Text *)data;
1665 Display *dpy = event->xany.display;
1667 if(tPtr->waitingForSelection) return;
1669 switch (event->type) {
1670 case KeyPress:
1671 if(!tPtr->editable || tPtr->buttonHeld) {
1672 XBell(dpy, 0);
1673 return;
1675 if(tPtr->ownsSelection) releaseSelection(tPtr);
1676 //if (tPtr->waitingForSelection) return;
1677 if(tPtr->focused) {
1678 #if 0
1679 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1680 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1681 GrabModeAsync, GrabModeAsync, None,
1682 W_VIEW(tPtr)->screen->invisibleCursor, CurrentTime);
1683 tPtr->pointerGrabbed = True;
1684 #endif
1685 handleTextKeyPress(tPtr, event);
1686 } break;
1688 case MotionNotify:
1689 if(tPtr->pointerGrabbed) {
1690 tPtr->pointerGrabbed = False;
1691 XUngrabPointer(dpy, CurrentTime);
1693 if((event->xmotion.state & Button1Mask)) {
1694 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1695 if(!tPtr->ownsSelection) {
1696 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1697 event->xbutton.time, &selectionHandler, NULL);
1698 tPtr->ownsSelection = True;
1700 break;
1702 case ButtonPress:
1703 if(event->xbutton.button == Button1) {
1704 if(tPtr->ownsSelection) releaseSelection(tPtr);
1705 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1706 if (tPtr->pointerGrabbed) {
1707 tPtr->pointerGrabbed = False;
1708 XUngrabPointer(dpy, CurrentTime);
1709 break;
1712 if(!tPtr->focused) {
1713 WMSetFocusToWidget(tPtr);
1714 tPtr->focused = True;
1715 break;
1717 if(event->xbutton.button == 4)
1718 WMScrollText(tPtr, -16);
1719 else if(event->xbutton.button == 5)
1720 WMScrollText(tPtr, 16);
1722 break;
1724 case ButtonRelease:
1725 tPtr->buttonHeld = False;
1726 if (tPtr->pointerGrabbed) {
1727 tPtr->pointerGrabbed = False;
1728 XUngrabPointer(dpy, CurrentTime);
1729 break;
1731 if(event->xbutton.button == 4 || event->xbutton.button == 5)
1732 break;
1733 if(event->xbutton.button == Button2 && tPtr->editable) {
1734 char *text = NULL;
1735 int n;
1736 if(!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1737 event->xbutton.time, pasteText, NULL)) {
1738 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1739 if(text) {
1740 text[n] = 0;
1741 WMAppendTextStream(tPtr, text);
1742 XFree(text);
1743 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1744 } else tPtr->waitingForSelection = True;
1745 } } break;
1752 static void
1753 handleNonTextEvents(XEvent *event, void *data)
1755 Text *tPtr = (Text *)data;
1757 switch(event->type) {
1758 case Expose:
1759 if(!event->xexpose.count && tPtr->view->flags.realized)
1760 paintText(tPtr);
1761 break;
1763 case FocusIn:
1764 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
1765 return;
1766 tPtr->focused = True;
1767 //cursor...paintText(tPtr);
1768 break;
1770 case FocusOut:
1771 tPtr->focused = False;
1772 //cursor...paintText(tPtr);
1773 break;
1775 case DestroyNotify:
1776 printf("destroy");
1777 //for(...)WMRemoveTextParagraph(tPtr, para);
1778 break;
1782 //printf("handleNonTextEvents\n");
1787 static void
1788 rulerCallBack(WMWidget *w, void *self)
1790 Text *tPtr = (Text *)self;
1791 short which;
1793 if(tPtr->currentPara) {
1794 Paragraph *para = tPtr->currentPara;
1795 para->fmargin = WMGetRulerMargin(tPtr->ruler, WRulerFirst);
1796 para->bmargin = WMGetRulerMargin(tPtr->ruler, WRulerBody);
1797 para->rmargin = WMGetRulerMargin(tPtr->ruler, WRulerRight);
1798 affectNextParas(tPtr, para, -23);
1799 putParagraphOnPixmap(tPtr, para, True);
1801 #if 0
1802 which = WMGetReleasedRulerMargin(tPtr->ruler);
1803 if(which != WRulerDocLeft && which != WRulerRight
1804 /* && Selection.para.count > 0 */ ) {
1805 printf(""
1806 "//for(i=0; i<Selection.para.count; i++) {"
1807 "affect"
1808 "//calcParaExtents(tPtr, para);}\n");
1809 } else {
1810 WMRefreshText(tPtr, 0, 0);
1812 #endif
1816 static void
1817 rulerMoveCallBack(WMWidget *w, void *self)
1819 Text *tPtr = (Text *)self;
1820 short rmargin = WMGetRulerMargin(tPtr->ruler, WRulerRight);
1823 if(WMGetGrabbedRulerMargin(tPtr->ruler) == WRulerLeft) {
1824 short lmargin = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
1825 XClearArea(tPtr->view->screen->display, tPtr->view->window,
1826 22, 42, lmargin-21, tPtr->visibleH, True);
1827 } else if(WMGetGrabbedRulerMargin(tPtr->ruler) == WRulerRight &&
1828 tPtr->docWidth+11 < rmargin) {
1829 XClearArea(tPtr->view->screen->display, tPtr->view->window,
1830 rmargin-3, 42, 10, tPtr->visibleH, True);
1832 paintText(tPtr);
1837 /* ------------- non-static functions that are "friends" ------------- */
1838 /* ------------- called as (tPtr->funcs.foo)(bars...) ------------- */
1840 /* create a new paragraph. Don't do anything with it just yet */
1841 //Paragraph *
1842 void *
1843 createParagraph(short fmargin, short bmargin, short rmargin,
1844 short *tabstops, short numTabs, WMAlignment alignment)
1846 Paragraph *para = wmalloc(sizeof(Paragraph));
1847 if(!para) return NULL;
1849 para->chunks = NULL;
1850 para->next = NULL;
1853 para->fmargin = (fmargin>=0)?fmargin:0;
1854 para->bmargin = (bmargin>=0)?bmargin:0;
1855 if(rmargin-bmargin >= 100 && rmargin-fmargin >= 100)
1856 para->rmargin = rmargin;
1857 else
1858 para->rmargin = 100;
1859 para->tabstops = tabstops;
1860 para->numTabs = (tabstops)?numTabs:0;
1862 para->drawbuffer = (Pixmap)NULL;
1863 para->bulletPix = NULL;
1864 para->top = para->bottom = 0;
1865 para->width = para->height = 0;
1867 para->align = alignment;
1869 return para;
1872 /* insert the new paragraph in the tPtr, either right before
1873 or after the currentPara. It's the responsibility of the
1874 calling code to set what currentPara is. via WMSetTextCurrentParagraph.
1875 If currentPara is not set, set it as the first in the document.
1876 This function then sets currentPara as _this_ paragraph.
1877 NOTE: this means careless parser implementors might lose previous
1878 paragraphs... but this keeps stuff small and non-buggy :-) */
1879 void
1880 insertParagraph(WMText *tPtr, void *v, InsertType type)
1881 //insertParagraph(WMText *tPtr, Paragraph *para, InsertType type)
1883 Paragraph *tmp;
1884 Paragraph *para = (Paragraph *)v;
1885 if(!para || !tPtr) return;
1887 if(!tPtr->currentPara) {
1888 tPtr->paragraphs = para;
1889 } else {
1890 tmp = tPtr->paragraphs;
1891 if(type == itAppend) {
1892 while(tmp->next && tmp != tPtr->currentPara)
1893 tmp = tmp->next;
1895 para->next = tmp->next;
1896 tmp->next = para;
1897 } else { /* must be prepend */
1898 /* this "prior" member is that "doing things the hard way"
1899 I spoke of. See? it's not too bad afterall... */
1900 Paragraph *prior = NULL;
1901 while(tmp->next && tmp != tPtr->currentPara) {
1902 prior = tmp;
1903 tmp = tmp->next;
1905 /* if this is the first */
1906 if(tmp == tPtr->paragraphs) {
1907 para->next = tmp;
1908 tPtr->paragraphs = para;
1909 } else {
1910 prior->next = para;
1911 para->next = tmp;
1912 } } }
1913 tPtr->currentPara = para;
1917 /* create a new chunk to contain exactly ONE pixmap */
1918 void *
1919 //Chunk *
1920 createPChunk(WMPixmap *pixmap, short script, ushort ul)
1922 Chunk *chunk;
1924 chunk = wmalloc(sizeof(Chunk));
1925 if(!chunk)
1926 return NULL;
1928 chunk->text = NULL;
1929 if(!pixmap)
1930 chunk->pixmap = NULL; /* if it's NULL, we'll draw the "broken" pixmap... */
1931 else chunk->pixmap = WMRetainPixmap(pixmap);
1932 chunk->chars = 0;
1933 chunk->mallocedSize = 0;
1934 chunk->type = ctImage;
1935 chunk->font = NULL;
1936 chunk->color = NULL;
1937 chunk->script = script;
1938 chunk->ul = ul;
1939 chunk->selected = False;
1940 chunk->next = NULL;
1941 return chunk;
1945 /* create a new chunk to contain some text with the given format */
1946 void *
1947 //Chunk *
1948 createTChunk(char *text, short chars, WMFont *font,
1949 WMColor *color, short script, ushort ul)
1951 Chunk *chunk;
1953 if(!text || chars<0 || !font || !color) return NULL;
1954 chunk = wmalloc(sizeof(Chunk));
1955 if(!chunk) return NULL;
1957 chunk->mallocedSize = reqBlockSize(chars);
1958 chunk->text = (char *)wmalloc(chunk->mallocedSize);
1959 memcpy(chunk->text, text, chars);
1960 chunk->pixmap = NULL;
1961 chunk->chars = chars;
1962 chunk->type = ctText;
1963 chunk->font = WMRetainFont(font);
1964 chunk->color = WMRetainColor(color);
1965 chunk->script = script;
1966 chunk->ul = ul;
1967 chunk->selected = False;
1968 chunk->next = NULL;
1970 return chunk;
1973 /* insert the new chunk in the paragraph, either right before
1974 or after the currentChunk. It's the responsibility of the
1975 calling code to set what currentChunk is via WMSetTextCurrentChunk.
1976 If currentChunk is not set, set it as the first in the existing
1977 paragraph... if not even that, you lose... try again.
1978 This function then sets currentChunk as _this_ chunk.
1979 NOTE: this means careless parser implementors might lose previous
1980 paragraphs/chunks... but this keeps stuff small and non-buggy :-) */
1981 void
1982 insertChunk(WMText *tPtr, void *v, InsertType type)
1984 Chunk *tmp;
1985 Chunk *chunk = (Chunk *)v;
1987 if(!tPtr || !chunk) return;
1989 if(!tPtr->paragraphs) { /* i.e., first chunk via insertTextInteractively */
1990 Paragraph *para = (tPtr->funcs.createParagraph) (0, 0, tPtr->visibleW,
1991 NULL, 0, WALeft);
1992 (tPtr->funcs.insertParagraph) (tPtr, para, itAppend);
1995 if(!tPtr->currentPara)
1996 return;
1997 if(!tPtr->currentChunk) { /* there is a current chunk */
1998 tPtr->currentPara->chunks = chunk;
1999 } else if(!tPtr->currentPara->chunks) {
2000 /* but it's not of this paragraph */
2001 tPtr->currentPara->chunks = chunk;
2002 } else {
2003 tmp = tPtr->currentPara->chunks;
2005 if(type == itAppend) {
2006 while(tmp->next && tmp != tPtr->currentChunk)
2007 tmp = tmp->next;
2009 chunk->next = tmp->next;
2010 tmp->next = chunk;
2012 } else { /* must be prepend */
2013 /* this "prior" member is that "doing things the hard way"
2014 I spoke of. See? it's not too bad afterall... */
2015 Chunk *prior = NULL;
2016 while(tmp->next && tmp != tPtr->currentChunk) {
2017 prior = tmp;
2018 tmp = tmp->next;
2020 /* if this is the first */
2021 if(tmp == tPtr->currentPara->chunks) {
2022 chunk->next = tmp;
2023 tPtr->currentPara->chunks = chunk;
2024 } else {
2025 prior->next = chunk;
2026 chunk->next = tmp;
2027 } } }
2028 tPtr->currentChunk = chunk;
2029 tPtr->tpos = chunk->chars;
2033 /* ------------- non-static functions (i.e., APIs) ------------- */
2034 /* ------------- called as WMVerbText[Subject] ------------- */
2036 #define DEFAULT_TEXT_WIDTH 250
2037 #define DEFAULT_TEXT_HEIGHT 200
2041 WMText*
2042 WMCreateText(WMWidget *parent)
2044 Text *tPtr = wmalloc(sizeof(Text));
2045 if(!tPtr) {
2046 perror("could not create text widget\n");
2047 return NULL;
2049 memset(tPtr, 0, sizeof(Text));
2050 tPtr->widgetClass = WC_Text;
2051 tPtr->view = W_CreateView(W_VIEW(parent));
2052 if (!tPtr->view) {
2053 perror("could not create text's view\n");
2054 wgdbFree(tPtr);
2055 return NULL;
2057 tPtr->view->self = tPtr;
2058 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
2059 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
2060 W_ResizeView(tPtr->view, DEFAULT_TEXT_WIDTH, DEFAULT_TEXT_HEIGHT);
2061 tPtr->bg = tPtr->view->screen->white;
2062 W_SetViewBackgroundColor(tPtr->view, tPtr->bg);
2065 tPtr->ruler = WMCreateRuler(tPtr);
2066 (W_VIEW(tPtr->ruler))->attribs.cursor = tPtr->view->screen->defaultCursor;
2067 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
2068 WMMoveWidget(tPtr->ruler, 0, 0);
2069 WMResizeWidget(tPtr->ruler, W_VIEW(parent)->size.width, 40);
2070 WMShowRulerTabs(tPtr->ruler, True);
2071 WMSetRulerAction(tPtr->ruler, rulerCallBack, tPtr);
2072 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
2074 tPtr->vpos = 0;
2075 tPtr->prevVpos = 0;
2076 tPtr->vscroller = WMCreateScroller(tPtr);
2077 (W_VIEW(tPtr->vscroller))->attribs.cursor =
2078 tPtr->view->screen->defaultCursor;
2079 (W_VIEW(tPtr->vscroller))->attribFlags |= CWOverrideRedirect | CWCursor;
2080 WMMoveWidget(tPtr->vscroller, 1, 1);
2081 WMResizeWidget(tPtr->vscroller, 20, tPtr->view->size.height - 2);
2082 WMSetScrollerArrowsPosition(tPtr->vscroller, WSAMaxEnd);
2083 WMSetScrollerAction(tPtr->vscroller, scrollersCallBack, tPtr);
2085 tPtr->hpos = 0;
2086 tPtr->prevHpos = 0;
2087 tPtr->hscroller = WMCreateScroller(tPtr);
2088 (W_VIEW(tPtr->hscroller))->attribs.cursor =
2089 tPtr->view->screen->defaultCursor;
2090 (W_VIEW(tPtr->hscroller))->attribFlags |= CWOverrideRedirect | CWCursor;
2091 WMMoveWidget(tPtr->hscroller, 1, tPtr->view->size.height-21);
2092 WMResizeWidget(tPtr->hscroller, tPtr->view->size.width - 2, 20);
2093 WMSetScrollerArrowsPosition(tPtr->hscroller, WSAMaxEnd);
2094 WMSetScrollerAction(tPtr->hscroller, scrollersCallBack, tPtr);
2096 tPtr->visibleW = tPtr->view->size.width;
2097 tPtr->visibleH = tPtr->view->size.height;
2099 tPtr->paragraphs = NULL;
2100 tPtr->docWidth = 0;
2101 tPtr->docHeight = 0;
2102 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
2103 default_bullet);
2104 tPtr->dUnknownImg = WMCreatePixmapFromXPMData(tPtr->view->screen,
2105 unk_xpm);
2107 tPtr->sRect.pos.x = tPtr->sRect.pos.y = 0;
2108 tPtr->sRect.size.width = tPtr->sRect.size.height = 0;
2109 tPtr->currentPara = NULL;
2110 tPtr->currentChunk = NULL;
2111 tPtr->tpos = 0;
2113 tPtr->parser = NULL;
2114 tPtr->writer = NULL;
2115 tPtr->funcs.createParagraph = createParagraph;
2116 tPtr->funcs.insertParagraph = insertParagraph;
2117 tPtr->funcs.createPChunk = createPChunk;
2118 tPtr->funcs.createTChunk = createTChunk;
2119 tPtr->funcs.insertChunk = insertChunk;
2121 tPtr->clicked.x = tPtr->clicked.y = -23;
2122 tPtr->cursor.x = tPtr->cursor.y = -23;
2124 tPtr->relief = WRSunken;
2125 tPtr->wrapping = wrWord;
2126 tPtr->editable = False;
2127 tPtr->cursorShown = False;
2128 tPtr->frozen = False;
2129 tPtr->focused = False;
2130 tPtr->pointerGrabbed = False;
2131 tPtr->buttonHeld = False;
2132 tPtr->ignoreNewLine = False;
2133 tPtr->waitingForSelection = False;
2134 tPtr->findingClickPoint = False;
2135 tPtr->foundClickPoint = False;
2136 tPtr->ownsSelection = False;
2137 tPtr->clheight = 0;
2138 tPtr->clwidth = 0;
2140 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2141 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2143 tPtr->view->delegate = &_TextViewDelegate;
2144 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
2145 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
2146 handleNonTextEvents, tPtr);
2147 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
2148 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
2149 handleTextEvents, tPtr);
2151 WMAddNotificationObserver(_notification, tPtr, "_lostOwnership", tPtr);
2153 WMSetTextMonoFont(tPtr, True);
2154 WMShowTextRuler(tPtr, False);
2155 WMSetTextHasHorizontalScroller(tPtr, False);
2156 WMSetTextHasVerticalScroller(tPtr, True);
2157 //printf("the sizeof chunk is %d\n", sizeof(Chunk));
2158 //printf("the sizeof para is %d\n", sizeof(Paragraph));
2159 //printf("the sizeof text is %d\n", sizeof(Text));
2160 return tPtr;
2163 //WMSetTextBullet()
2164 //WRetainPixmap(tPtr->dBulletPix);
2166 void
2167 WMRemoveTextParagraph(WMText *tPtr, int which)
2169 Paragraph *prior, *removed;
2170 if(!tPtr || which<0) return;
2172 WMSetTextCurrentParagraph(tPtr, which);
2173 removed = tPtr->currentPara;
2174 if(!removed) return;
2175 if(removed->chunks)printf("WMRemoveTextChunks\n");
2176 if(removed == tPtr->paragraphs || which==0) {
2177 tPtr->paragraphs = removed->next;
2178 } else {
2179 WMSetTextCurrentParagraph(tPtr, which-1);
2180 prior = tPtr->currentPara;
2181 if(!prior) return;
2182 prior->next = removed->next;
2184 wgdbFree(removed);
2185 // removeChunks
2186 removed = NULL;
2191 /* set what is known as the currentPara in the tPtr. */
2192 /* negative number means: "gib me last chunk" */
2193 void
2194 WMSetTextCurrentParagraph(WMText *tPtr, int current)
2196 Paragraph *tmp;
2197 int i=0;
2199 if(!tPtr || current<0) return;
2200 if(current == 0) {
2201 tPtr->currentPara = tPtr->paragraphs;
2202 return;
2204 tmp = tPtr->paragraphs;
2205 while(tmp->next && ((current==-23)?1:i++<current)) {
2206 //while(tmp && i++<current) {
2207 tmp = tmp->next;
2208 } tPtr->currentPara = tmp;
2209 //? want to do this?if(tmp) tPtr->currentChunk = tmp
2214 WMGetTextParagraphs(WMText *tPtr)
2216 int current=0;
2217 Paragraph *tmp;
2218 if(!tPtr) return 0;
2219 tmp = tPtr->paragraphs;
2220 while(tmp) {
2221 tmp = tmp->next;
2222 current++;
2223 } return current;
2229 WMGetTextCurrentParagraph(WMText *tPtr)
2231 int current=-1;
2232 Paragraph *tmp;
2234 if(!tPtr) return current;
2235 if(!tPtr->currentPara) return current;
2236 if(!tPtr->paragraphs) return current;
2237 tmp = tPtr->paragraphs;
2238 while(tmp) {
2239 current++;
2240 if(tmp == tPtr->currentPara)
2241 break;
2242 tmp = tmp->next;
2243 } return current;
2246 /* set what is known as the currentChunk within the currently
2247 selected currentPara (or the first paragraph in the document). */
2248 void
2249 WMSetTextCurrentChunk(WMText *tPtr, int current)
2251 Chunk *tmp;
2252 int i=0;
2254 if(!tPtr) return;
2255 tPtr->currentChunk = NULL;
2256 if(!tPtr->currentPara) {
2257 tPtr->currentPara = tPtr->paragraphs;
2258 if(!tPtr->currentPara)
2259 return;
2262 if(current == 0) {
2263 tPtr->currentChunk = tPtr->currentPara->chunks;
2264 return;
2266 tmp = tPtr->currentPara->chunks;
2267 if(tmp) {
2268 while(tmp->next && ((current<0)?1:i++<current))
2269 tmp = tmp->next;
2270 } tPtr->currentChunk = tmp;
2274 void
2275 WMRemoveTextChunk(WMText *tPtr, int which)
2277 Chunk *prior, *removed;
2278 Paragraph *para;
2279 if(!tPtr || which<0) return;
2280 para = tPtr->currentPara;
2281 if(!para) return;
2283 WMSetTextCurrentChunk(tPtr, which);
2284 removed = tPtr->currentChunk;
2285 if(!removed) return;
2286 if(removed == tPtr->currentPara->chunks || which==0) {
2287 para->chunks = removed->next;
2288 } else {
2289 WMSetTextCurrentChunk(tPtr, which-1);
2290 prior = tPtr->currentChunk;
2291 if(!prior) return;
2292 prior->next = removed->next;
2294 if(removed->type == ctText) {
2295 wgdbFree(removed->text);
2296 WMReleaseFont(removed->font);
2297 WMReleaseColor(removed->color);
2298 } else {
2299 WMReleasePixmap(removed->pixmap);
2301 wgdbFree(removed);
2302 removed = NULL;
2306 WMGetTextCurrentChunk(WMText *tPtr)
2308 int current=0;
2309 Chunk *tmp;
2311 if(!tPtr) return 0;
2312 if(!tPtr->currentChunk) return 0;
2313 if(!tPtr->currentPara) {
2314 tPtr->currentPara = tPtr->paragraphs;
2315 if(!tPtr->currentPara)
2316 return 0;
2319 tmp = tPtr->currentPara->chunks;
2320 while(tmp) {
2321 if(tmp == tPtr->currentChunk)
2322 break;
2323 tmp = tmp->next;
2324 current++;
2326 return current;
2330 WMGetTextChunks(WMText *tPtr)
2332 short current=0;
2333 Chunk *tmp;
2334 if(!tPtr || !tPtr->currentPara) return 0;
2335 tmp = tPtr->currentPara->chunks;
2336 while(tmp) {
2337 tmp = tmp->next;
2338 current++;
2339 } return current;
2342 void
2343 WMShowTextRuler(WMText *tPtr, Bool show)
2345 if(!tPtr) return;
2346 if(tPtr->monoFont) show = False;
2348 tPtr->rulerShown = show;
2349 if(show) WMMapWidget(tPtr->ruler);
2350 else WMUnmapWidget(tPtr->ruler);
2351 resizeText(tPtr->view->delegate, tPtr->view);
2354 Bool
2355 WMGetTextRulerShown(WMText *tPtr)
2357 if(!tPtr) return False;
2358 return tPtr->rulerShown;
2361 void
2362 WMSetTextRulerMargin(WMText *tPtr, char which, short pixels)
2364 if(!tPtr) return;
2365 if(tPtr->monoFont) return;
2366 WMSetRulerMargin(tPtr->ruler, which, pixels);
2367 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2370 short
2371 WMGetTextRulerMargin(WMText *tPtr, char which)
2373 if(!tPtr) return 0;
2374 if(tPtr->monoFont)
2375 return 0;
2376 return WMGetRulerMargin(tPtr->ruler, which);
2380 void
2381 WMShowTextRulerTabs(WMText *tPtr, Bool show)
2383 if(!tPtr) return;
2384 if(tPtr->monoFont) return;
2385 WMShowRulerTabs(tPtr->ruler, show);
2388 void
2389 WMSetTextMonoFont(WMText *tPtr, Bool mono)
2391 if(!tPtr) return;
2392 if(mono && tPtr->rulerShown)
2393 WMShowTextRuler(tPtr, False);
2395 tPtr->monoFont = mono;
2398 Bool
2399 WMGetTextMonoFont(WMText *tPtr)
2401 if(!tPtr) return True;
2402 return tPtr->monoFont;
2405 void
2406 WMForceTextFocus(WMText *tPtr)
2408 if(!tPtr) return;
2410 if(tPtr->clicked.x == -23 || tPtr->clicked.y == 23)
2411 cursorToTextPosition(tPtr, 100, 100); /* anyplace */
2412 else
2413 cursorToTextPosition(tPtr, tPtr->clicked.x, tPtr->clicked.y);
2417 void
2418 WMSetTextEditable(WMText *tPtr, Bool editable)
2420 if(!tPtr) return;
2421 tPtr->editable = editable;
2425 Bool
2426 WMGetTextEditable(WMText *tPtr)
2428 if(!tPtr) return 0;
2429 return tPtr->editable;
2433 Bool
2434 WMScrollText(WMText *tPtr, int amount)
2436 Bool scroll=False;
2437 if(amount == 0 || !tPtr) return;
2438 if(!tPtr->view->flags.realized) return;
2440 if(amount < 0) {
2441 if(tPtr->vpos > 0) {
2442 if(tPtr->vpos > amount) tPtr->vpos += amount;
2443 else tPtr->vpos=0;
2444 scroll=True;
2445 } } else {
2446 int limit = tPtr->docHeight - tPtr->visibleH;
2447 if(tPtr->vpos < limit) {
2448 if(tPtr->vpos < limit-amount) tPtr->vpos += amount;
2449 else tPtr->vpos = limit;
2450 scroll = True;
2453 if(scroll && tPtr->vpos != tPtr->prevVpos) {
2454 updateScrollers(tPtr);
2455 drawDocumentPartsOnPixmap(tPtr, False);
2456 paintText(tPtr);
2458 tPtr->prevVpos = tPtr->vpos;
2459 return scroll;
2462 Bool
2463 WMPageText(WMText *tPtr, Bool scrollUp)
2465 if(!tPtr) return;
2466 if(!tPtr->view->flags.realized) return;
2468 return WMScrollText(tPtr, scrollUp
2469 ? tPtr->visibleH:-tPtr->visibleH);
2472 void
2473 WMIgnoreTextNewline(WMText *tPtr, Bool ignore)
2475 if(!tPtr) return;
2476 tPtr->ignoreNewLine = ignore;
2480 void
2481 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool flag)
2483 if(tPtr) {
2484 short rh;
2485 if(tPtr->monoFont)
2486 return;
2487 rh = tPtr->rulerShown?40:0;
2488 tPtr->hasHscroller = flag;
2489 if(flag) {
2490 WMMapWidget(tPtr->hscroller);
2491 tPtr->visibleH = tPtr->view->size.height-rh-22;
2492 } else {
2493 WMUnmapWidget(tPtr->hscroller);
2494 tPtr->visibleH = tPtr->view->size.height-rh;
2496 resizeText(tPtr->view->delegate, tPtr->view);
2501 void
2502 WMSetTextHasVerticalScroller(WMText *tPtr, Bool flag)
2504 if(tPtr) {
2505 tPtr->hasVscroller = flag;
2506 if(flag) {
2507 WMMapWidget(tPtr->vscroller);
2508 tPtr->visibleW = tPtr->view->size.width-22;
2509 WMSetRulerOffset(tPtr->ruler, 22); /* scrollbar width + 2 */
2510 } else {
2511 WMUnmapWidget(tPtr->vscroller);
2512 tPtr->visibleW = tPtr->view->size.width;
2513 WMSetRulerOffset(tPtr->ruler, 2);
2515 resizeText(tPtr->view->delegate, tPtr->view);
2521 void
2522 WMRefreshText(WMText *tPtr, int vpos, int hpos)
2525 if(!tPtr)
2526 return;
2528 if(tPtr->frozen || !tPtr->view->flags.realized)
2529 return;
2532 XClearArea(tPtr->view->screen->display, tPtr->view->window,
2533 22, (tPtr->rulerShown)?45:5,
2534 tPtr->visibleW, tPtr->visibleH, True);
2536 calcDocExtents(tPtr);
2538 printf("vpos:%d tPtr->docHeight%d tPtr->visibleH%d \n",
2539 vpos, tPtr->docHeight, tPtr->visibleH);
2542 // tPtr->vpos = vpos;
2544 if(vpos < 0 || tPtr->docHeight < tPtr->visibleH)
2545 tPtr->vpos = 0;
2546 else if(vpos-tPtr->visibleH>tPtr->docHeight)
2547 tPtr->vpos = vpos-tPtr->docHeight-tPtr->visibleH-tPtr->docHeight;
2548 else
2549 tPtr->vpos = tPtr->docHeight-tPtr->visibleH;
2553 if(hpos < 0 || hpos > tPtr->docWidth)
2554 tPtr->hpos = 0;
2555 else
2556 tPtr->hpos = hpos;
2558 drawDocumentPartsOnPixmap(tPtr, True);
2559 updateScrollers(tPtr);
2560 paintText(tPtr);
2563 /* would be nice to have in WINGs proper... */
2564 static void
2565 changeFontProp(char *fname, char *newprop, short which)
2567 char before[128], prop[128], after[128];
2568 char *ptr, *bptr;
2569 int part=0;
2571 if(!fname || !prop)
2572 return;
2574 ptr = fname;
2575 bptr = before;
2576 while (*ptr) {
2577 if(*ptr == '-') {
2578 *bptr = 0;
2579 if(part==which) bptr = prop;
2580 else if(part==which+1) bptr = after;
2581 *bptr++ = *ptr;
2582 part++;
2583 } else {
2584 *bptr++ = *ptr;
2585 } ptr++;
2586 }*bptr = 0;
2587 snprintf(fname, 255, "%s-%s%s", before, newprop, after);
2590 /* TODO: put in wfont? */
2591 WMFont *
2592 WMGetFontPlain(WMScreen *scrPtr, WMFont *font)
2594 WMFont *nfont=NULL;
2595 if(!scrPtr|| !font)
2596 return NULL;
2597 return font;
2601 WMFont *
2602 WMGetFontBold(WMScreen *scrPtr, WMFont *font)
2604 WMFont *newfont=NULL;
2605 char fname[256];
2606 if(!scrPtr || !font)
2607 return NULL;
2608 snprintf(fname, 255, font->name);
2609 changeFontProp(fname, "bold", 2);
2610 newfont = WMCreateNormalFont(scrPtr, fname);
2611 if(!newfont)
2612 newfont = font;
2613 return newfont;
2616 WMFont *
2617 WMGetFontItalic(WMScreen *scrPtr, WMFont *font)
2619 WMFont *newfont=NULL;
2620 char fname[256];
2621 if(!scrPtr || !font)
2622 return NULL;
2623 snprintf(fname, 255, font->name);
2624 changeFontProp(fname, "o", 3);
2625 newfont = WMCreateNormalFont(scrPtr, fname);
2626 if(!newfont)
2627 newfont = font;
2628 return newfont;
2631 WMFont *
2632 WMGetFontOfSize(WMScreen *scrPtr, WMFont *font, short size)
2634 WMFont *nfont=NULL;
2635 if(!scrPtr || !font || size<1)
2636 return NULL;
2637 return font;
2639 /* */
2641 void
2642 WMFreezeText(WMText *tPtr)
2644 if(!tPtr)
2645 return;
2646 tPtr->frozen = True;
2649 void
2650 WMThawText(WMText *tPtr)
2652 if(!tPtr)
2653 return;
2654 tPtr->frozen = False;
2658 void
2659 WMSetTextDefaultAlignment(WMText *tPtr, WMAlignment alignment)
2661 if(!tPtr) return;
2662 if(tPtr->monoFont) return;
2664 tPtr->dAlignment = alignment;
2665 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2670 void
2671 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
2673 if(!tPtr)
2674 return;
2676 if(color)
2677 tPtr->bg = color;
2678 else
2679 tPtr->bg = WMWhiteColor(tPtr->view->screen);
2681 W_SetViewBackgroundColor(tPtr->view, tPtr->bg);
2682 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2685 void
2686 WMSetTextDefaultColor(WMText *tPtr, WMColor *color)
2688 if(!tPtr)
2689 return;
2691 if(color)
2692 tPtr->dColor = color;
2693 else
2694 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2697 void
2698 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
2700 if(!tPtr)
2701 return;
2703 if(font)
2704 tPtr->dFont = font;
2705 else
2706 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2709 void
2710 WMSetTextUseFixedPitchFont(Text *tPtr, Bool fixed)
2712 if(!tPtr)
2713 return;
2714 if(fixed)
2715 tPtr->dFont = WMCreateFontSet(tPtr->view->screen,
2716 "lucidasanstypewriter-12");
2717 else
2718 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2719 tPtr->fixedPitch = fixed;
2722 void
2723 WMSetTextParser(WMText *tPtr, WMParseAction *parser)
2725 if(!tPtr) return;
2726 if(tPtr->monoFont) return;
2727 tPtr->parser = parser;
2731 WMParserActions
2732 WMGetTextParserActions(WMText *tPtr)
2734 WMParserActions null;
2735 if(!tPtr) return null;
2736 return tPtr->funcs;
2740 char *
2741 WMGetTextAll(WMText *tPtr)
2743 char *text;
2744 int length=0;
2745 int where=0;
2746 Paragraph *para;
2747 Chunk *chunk;
2749 if(!tPtr) return NULL;
2751 para = tPtr->paragraphs;
2752 while(para) {
2753 chunk = para->chunks;
2754 while(chunk) {
2755 if(chunk->type == ctText) {
2756 if(chunk->text) length += chunk->chars;
2757 } else {
2758 printf("getting image \n");
2760 chunk = chunk->next;
2763 if(tPtr->ignoreNewLine) break;
2764 length += 4; // newlines
2765 para = para->next;
2768 text = wmalloc(length+1);
2770 para = tPtr->paragraphs;
2771 while(para) {
2772 chunk = para->chunks;
2773 while(chunk) {
2774 if(chunk->type == ctText) {
2775 if(chunk->text) {
2776 snprintf(&text[where], chunk->chars+1, "%s", chunk->text);
2777 where += chunk->chars;
2779 } else {
2780 printf("writing image \n");
2782 chunk = chunk->next;
2784 if(tPtr->ignoreNewLine) break;
2785 snprintf(&text[where++], 2, "\n");
2786 para = para->next;
2787 } text[where] = '\0';
2789 return text;
2792 void
2793 WMSetTextWriter(WMText *tPtr, WMParseAction *writer)
2795 if(!tPtr)
2796 return;
2797 if(tPtr->monoFont)
2798 return;
2799 tPtr->writer = writer;