Yet another fix for the wmtext class. Thank you, thank you, thank you.
[wmaker-crm.git] / WINGs / wtext.c
blob0c8200b49f7df5dcab0fdae97959bc264bccf625
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 #if 0
24 #include "wruler.h"
25 #include "wtext.h"
26 #endif
28 void wgdbFree(void *ptr)
29 { if(!ptr) printf("err... cannot ");
30 printf("gdbFree [%p]\n", ptr);
31 wfree(ptr);
35 typedef enum {ctText=0, ctImage=1} ChunkType;
36 typedef enum { dtDelete=0, dtBackSpace } DeleteType;
37 typedef enum {wrWord=0, wrChar=1, wrNone=2} Wrapping;
39 /* Why singly-linked and not say doubly-linked?
40 99% of the time (draw, append), the "prior"
41 member would have been a useless memory and CPU overhead,
42 and deletes _are_ relatively infrequent.
43 When the "prior" member needs to be used, the overhead of
44 doing things the hard way will be incurred... but seldomly. */
47 /* a Chunk is a singly-linked list of chunks containing:
48 o text with a given format
49 o or an image
50 o but NOT both */
51 typedef struct _Chunk {
52 char *text; /* the text in the chunk */
53 WMPixmap *pixmap; /* OR the pixmap it holds */
54 short chars; /* the number of characters in this chunk */
55 short mallocedSize; /* the number of characters that can be held */
57 WMFont *font; /* the chunk's font */
58 WMColor *color; /* the chunk's color */
59 short ul:1; /* underlined or not */
60 ChunkType type:1; /* a "Text" or "Image" chunk */
61 short script:4; /* script in points: negative for subscript */
62 //hrmm selec...
63 ushort selected;
64 ushort sStart;
65 ushort sEnd;
66 ushort RESERVED:10;
67 struct _Chunk *next;/*the next member in this list */
69 }Chunk;
73 /* a Paragraph is a singly-linked list of paragraphs containing:
74 o a list of chunks in that paragraph
75 o the formats for that paragraph
76 o its (draw) position relative to the entire document */
77 typedef struct _Paragraph {
78 Chunk *chunks; /* the list of text and/or image chunks */
79 short fmargin; /* the start position of the first line */
80 short bmargin; /* the start positions of the rest of the lines */
81 short rmargin; /* the end position of the entire paragraph */
82 short numTabs; /* the number of tabstops */
83 short *tabstops; /* an array of tabstops */
85 Pixmap drawbuffer; /* the pixmap onto which the (entire)
86 paragraph will be drawn */
87 WMPixmap *bulletPix;/* the pixmap to use for bulleting */
88 int top; /* the top of the paragraph relative to document */
89 int bottom; /* the bottom of the paragraph relative to document */
90 int width; /* the width of the paragraph */
91 int height; /* the height of the paragraph */
92 WMAlignment align:2;/* justification of this paragraph */
93 ushort RESERVED:14;
95 struct _Paragraph *next; /* the next member in this list */
96 } Paragraph;
99 static char *default_bullet[] = {
100 "6 6 4 1",
101 " c None s None", ". c black",
102 "X c white", "o c #808080",
103 " ... ",
104 ".XX.. ",
105 ".XX..o",
106 ".....o",
107 " ...oo",
108 " ooo "};
110 /* this is really a shrunk down version of the original
111 "broken" icon... I did not draw it, I simply shrunk it */
112 static char * unk_xpm[] = {
113 "24 24 17 1",
114 " c None", ". c #0B080C", "+ c #13A015", "@ c #5151B8",
115 "# c #992719", "$ c #5B1C20", "% c #1DF51D", "& c #D1500D", "* c #2F304A",
116 "= c #0C6A0C", "- c #F2F1DE", "; c #D59131", "> c #B2B083", ", c #DD731A",
117 "' c #CC3113", ") c #828238", "! c #6A6A94",
118 "......!@@@@@@@....$$....",
119 "...@!@@@@@@@**...$#'....",
120 "..!!@@@@@@@@.......#....",
121 "..!@@@@@@@@@*.......$...",
122 ".!@@@#,,#*@@*..*>.*.#...",
123 "*@@@@#'',,@@@...---!....",
124 "!@@@@@*.#;*@@..!--->....",
125 "@@@@@@@@#,.@@..!----@...",
126 "!@@@@@@*#;'$...!----@...",
127 "*@@@@@@..'&;;#.)----)...",
128 ".@@@@@@..$..&'.>----)...",
129 ".@@@@@@**---,'>-----!...",
130 ".@@@@@@**---,'>-----@...",
131 "..@@@@@@@---;;;,;---....",
132 "..*@@@@*@--->#',;,-*.)..",
133 "........)---->)@;#!..>..",
134 ".....)----------;$..>)..",
135 "=%%%*.*!-------);..)-*..",
136 "=%%%%+...*)>!@*$,.>--...",
137 "*+++++++.......*$@-->...",
138 "............**@)!)>->...",
139 "........................",
140 "........................",
141 "........................"};
143 typedef struct W_Text {
144 W_Class widgetClass; /* the class number of this widget */
145 W_View *view; /* the view referring to this instance */
146 WMColor *bg; /* the background color to use when drawing */
148 WMRuler *ruler; /* the ruler subwiget to maipulate paragraphs */
150 WMScroller *hscroller; /* the horizontal scroller */
151 short hpos; /* the current horizontal position */
152 short prevHpos; /* the previous horizontal position */
154 WMScroller *vscroller; /* the vertical scroller */
155 int vpos; /* the current vertical position */
156 int prevVpos; /* the previous vertical position */
158 int visibleW; /* the actual horizontal space available */
159 int visibleH; /* the actual vertical space available */
161 Paragraph *paragraphs; /* the linked list of the paragraphs in the doc. */
162 int docWidth; /* the width of the entire document */
163 int docHeight; /* the height of the entire document */
165 WMFont *dFont; /* the default font */
166 WMColor *dColor; /* the default color */
167 WMPixmap *dBulletPix; /* the default pixmap for bullets */
168 WMPixmap *dUnknownImg; /* the pixmap for (missing/broken) images */
170 WMRect sRect; /* the selected area */
171 Paragraph *currentPara; /* the current paragraph, in which actions occur */
172 Chunk *currentChunk; /* the current chunk, about which actions occur */
173 short tpos; /* the cursor position (text position) */
174 WMParseAction *parser; /* what action to use to parse input text */
175 WMParseAction *writer; /* what action to use to write text */
176 WMParserActions funcs; /* the "things" that parsers/writers might do */
177 XPoint clicked; /* the position of the last mouse click */
178 XPoint cursor; /* where the cursor is "placed" */
179 short clheight; /* the height of the "line" clicked on */
180 short clwidth; /* the width of the "line" clicked on */
182 WMReliefType relief:2; /* the relief to display with */
183 Wrapping wrapping:2; /* the type of wrapping to use in drawing */
184 WMAlignment dAlignment:2;/* default justification */
185 ushort monoFont:1; /* whether to ignore "rich" commands */
186 ushort fixedPitch:1; /* assume each char in dFont is the same size */
187 ushort editable:1; /* whether to accept user changes or not*/
188 ushort rulerShown:1; /* whether the ruler is shown or not */
189 ushort cursorShown:1; /* whether the cursor is currently being shown */
190 ushort frozen:1; /* whether screen updates are to be made */
191 ushort focused:1; /* whether this instance has input focus */
192 ushort pointerGrabbed:1;/* whether this instance has the pointer */
193 ushort buttonHeld:1; /* the user is still holding down the button */
194 ushort ignoreNewLine:1; /* whether to ignore the newline character */
195 ushort waitingForSelection:1; /* whether there is a pending paste event */
196 ushort ownsSelection:1; /* whether it ownz the current selection */
197 ushort findingClickPoint:1;/* whether the search for a clickpoint is on */
198 ushort foundClickPoint:1;/* whether the clickpoint has been found */
199 ushort hasVscroller:1; /* whether to enable the vertical scroller */
200 ushort hasHscroller:1; /* whether to enable the horizontal scroller */
201 ushort RESERVED:10;
202 } Text;
206 /* --------- static functions that are "private". don't touch :-) --------- */
209 /* max "characters per chunk that will be drawn at a time" */
210 #define MAX_WORD_LENGTH 100
211 /* max on a line */
212 #define MAX_CHUNX 64
213 #define MIN_DOC_WIDTH 200
214 typedef struct _LocalMargins {
215 short left, right, first, body;
216 } LocalMargins;
218 typedef struct _MyTextItems {
219 char text[MAX_WORD_LENGTH+1];
220 WMPixmap *pix;
221 short chars;
222 short x;
223 Chunk *chunk;/* used for "click" events */
224 short start; /* ditto... where in the chunk we start (ie. wrapped chunk) */
225 ushort type:1;
226 ushort RESERVED:15;
227 } MyTextItems;
230 static WMRect
231 chunkSelectionRect(Text *tPtr, Paragraph *para, MyTextItems item,
232 short y, short j, short lh)
234 WMRect rect;
235 short type=0; /* 0:none 1:partial: 2:all */
236 short rh=(tPtr->rulerShown)?45:5;
237 short w, lm;
238 WMFont *font = (tPtr->monoFont || item.chunk->type != ctText)?
239 tPtr->dFont:item.chunk->font;
241 rect.pos.x = -23;
242 if(y+para->top+rh > tPtr->sRect.pos.y+tPtr->sRect.size.height
243 || y+para->top+rh+lh < tPtr->sRect.pos.y)
244 return rect;
246 if(item.chunk->type == ctText)
247 w = WMWidthOfString(font, item.text, item.chars);
248 else w = item.chunk->pixmap->width;
250 if(y+para->top+rh >= tPtr->sRect.pos.y && (y+para->top+rh+lh
251 <= tPtr->sRect.pos.y+tPtr->sRect.size.height))
252 //&& item.x+j >= tPtr->sRect.pos.x+tPtr->sRect.size.width))
253 type = 2;
254 else type = 1;
257 #if 0
258 if(item.x+j >= tPtr->sRect.pos.x &&
259 item.x+j+w < tPtr->sRect.pos.x+tPtr->sRect.size.width)
260 type = 2;
262 if(type == 1 && y+para->top+rh+lh <=
263 tPtr->sRect.pos.y+tPtr->sRect.size.height)
264 type = 2;
265 #endif
268 if(type == 1 && item.chunk->type == ctText) { /* partial coverage */
269 lm = 2+WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
270 /* even I am still confused, so don't ask please */
271 if( (item.x+j+lm >= tPtr->sRect.pos.x &&
272 item.x+j+lm <= tPtr->sRect.pos.x+tPtr->sRect.size.width)
273 || (item.x+j+lm >= tPtr->sRect.pos.x+tPtr->sRect.size.width
274 && y+para->top+rh+lh <=
275 tPtr->sRect.pos.y+tPtr->sRect.size.height)
276 || (tPtr->sRect.pos.y < y+para->top+rh
277 && tPtr->sRect.pos.x+tPtr->sRect.size.width >
278 item.x+j+lm) ){
279 rect.size.width = w;
280 rect.pos.x = item.x+j;
281 item.chunk->selected = True;
282 if(item.chunk->chars > 6) {
283 item.chunk->sStart = 3;
284 item.chunk->sEnd = item.chunk->chars;
285 } else {
286 item.chunk->sStart = 0;
287 item.chunk->sEnd = item.chunk->chars;
290 } else if(type == 2) {
291 rect.pos.x = item.x+j;
292 item.chunk->selected = True;
293 if(item.chunk->type == ctText) {
294 item.chunk->sStart = 0;
295 item.chunk->sStart = item.chunk->chars;
296 rect.size.width = WMWidthOfString(font,
297 item.text, item.chars);
298 } else {
299 rect.size.width = item.chunk->pixmap->width;
303 rect.pos.y = y;
304 rect.size.height = lh;
305 return rect;
308 static int
309 myDrawText(Text *tPtr, Paragraph *para, MyTextItems *items,
310 short nitems, short pwidth, int y, short draw, short spacepos)
312 short i, ul_thick, u, j=0; /* j = justification */
313 short line_width = 0, line_height=0, mx_descent=0;
314 WMScreen *screen = tPtr->view->screen;
315 GC gc;
316 WMFont *font;
318 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0;
319 for(i=0; i<nitems; i++) {
320 if(items[i].type == ctText) {
321 font = (tPtr->monoFont)?tPtr->dFont:items[i].chunk->font;
322 mx_descent = WMIN(mx_descent, -font->y);
323 line_height = WMAX(line_height, font->height);
324 //printf("chunk.x %d xpoint.x %d\n",
325 // items[i].x, tPtr->clicked.x);
327 line_width += WMWidthOfString(font,
328 items[i].text, items[i].chars);
329 } else {
330 mx_descent = WMIN(mx_descent, -(items[i].pix->height-3));
331 /* replace -3 wif descent... */
332 line_height = WMAX(line_height, items[i].pix->height);
333 if(para->align == WARight || para->align == WACenter) {
334 line_width += items[i].pix->width;
335 } } }
337 if(para->align == WARight) {
338 j = pwidth - line_width;
339 } else if (para->align == WACenter) {
340 j = (short) ((float)(pwidth - line_width))/2.0;
343 if(tPtr->findingClickPoint && (y+line_height >= tPtr->clicked.y)) {
344 tPtr->foundClickPoint = True;
345 tPtr->currentChunk = items[0].chunk; /* just first on this "line" */
346 tPtr->tpos = items[0].start; /* where to "start" counting from */
347 tPtr->clicked.x = j+items[0].x;
348 tPtr->clicked.y = y+line_height+mx_descent;
349 tPtr->clheight = line_height; /* to draw the cursor */
350 tPtr->clwidth = line_width; /* where to stop searching */
351 return 0;
352 } if(!draw) return line_height;
354 for(i=0; i<nitems; i++) {
356 //account for vpos
357 if(tPtr->ownsSelection) {
358 WMRect rect = chunkSelectionRect(tPtr, para,
359 items[i], y, j, line_height);
360 if(rect.pos.x != -23) { /* has been selected */
361 XFillRectangle(tPtr->view->screen->display, para->drawbuffer,
362 WMColorGC(WMGrayColor(tPtr->view->screen)),
363 rect.pos.x, rect.pos.y, rect.size.width, rect.size.height);
367 if(items[i].type == ctText) {
368 gc = WMColorGC(items[i].chunk->color);
369 font = (tPtr->monoFont)?tPtr->dFont:items[i].chunk->font;
370 WMDrawString(screen, para->drawbuffer, gc, font,
371 items[i].x+j, y - font->y - mx_descent,
372 items[i].text, items[i].chars);
373 if(items[i].chunk->ul && !tPtr->monoFont) {
374 ul_thick = (short) ((float)font->height)/12.0;
375 if (ul_thick < 1) ul_thick = 1;
376 for(u=0; u<ul_thick; u++) {
377 XDrawLine(screen->display, para->drawbuffer, gc, items[i].x+j,
378 y + 1 + u - mx_descent,
379 items[i].x + j + WMWidthOfString(font,
380 items[i].text, items[i].chars), y + 1 + u - mx_descent);
383 } } else {
384 WMDrawPixmap(items[i].pix, para->drawbuffer, items[i].x+j,
385 y + 3 - mx_descent - items[i].pix->height);
386 } }
387 return line_height;
390 static void
391 drawPChunkPart(Text *tPtr, Chunk *chunk, LocalMargins m, Paragraph *para,
392 MyTextItems *items, short *nitems, short *Lmargin, XPoint *where, short draw)
394 short p_width;
396 if(!chunk) return;
397 if(!chunk->pixmap)
398 chunk->pixmap = WMRetainPixmap(tPtr->dUnknownImg);
400 p_width = m.right - WMIN(m.first, m.body) - WMGetRulerOffset(tPtr->ruler);
401 if(p_width < MIN_DOC_WIDTH) // need WMRuler to take care of this...
402 return;
403 if(where->x + chunk->pixmap->width <= p_width - *Lmargin) {
404 /* it can fit on rest of line */
405 items[*nitems].pix = chunk->pixmap;
406 items[*nitems].type = ctImage;
407 items[*nitems].chars = 0;
408 items[*nitems].x = *Lmargin+where->x;
409 items[*nitems].chunk = chunk;
410 items[*nitems].start = 0;
412 if(*nitems >= MAX_CHUNX) {
413 items[*nitems].chars = 0;
414 items[*nitems].x = *Lmargin+where->x;
415 items[*nitems].chunk = chunk;
416 items[*nitems].start = 0;
417 where->y += myDrawText(tPtr, para, items, *nitems+1,
418 p_width-*Lmargin, where->y, draw, 0);
419 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
420 *nitems = 0;
421 where->x = 0;
422 } else {
423 (*nitems)++;
424 where->x += chunk->pixmap->width;
426 } else if(chunk->pixmap->width <= p_width - *Lmargin) {
427 /* it can fit on an entire line, flush the myDrawText then wrap it */
428 where->y += myDrawText(tPtr, para, items, *nitems+1,
429 p_width-*Lmargin, where->y, draw, 0);
430 *nitems = 0;
431 *Lmargin = WMAX(0, m.body - m.first);
432 where->x = 0;
433 drawPChunkPart(tPtr, chunk, m, para, items, nitems,
434 Lmargin, where, draw);
435 } else {
436 #if 1
437 *nitems = 0;
438 where->x = 0;
439 *Lmargin = WMAX(0, m.body - m.first);
440 items[*nitems].pix = chunk->pixmap;
441 items[*nitems].type = ctImage;
442 items[*nitems].chars = 0;
443 items[*nitems].x = *Lmargin+where->x;
444 items[*nitems].chunk = chunk;
445 items[*nitems].start = 0;
446 where->y += myDrawText(tPtr, para, items, *nitems+1,
447 p_width-*Lmargin, where->y, draw, 0);
448 #endif
450 /* scale image to fit, call self again */
451 /* deprecated - the management */
456 static void
457 drawTChunkPart(Text *tPtr, Chunk *chunk, char *bufr, LocalMargins m,
458 Paragraph *para, MyTextItems *items, short *nitems, short len, short start,
459 short *Lmargin, XPoint *where, short draw, short spacepos)
461 short t_chunk_width, p_width, chars;
462 WMFont *font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
463 /* if(doc->clickstart.yes && doc->clickstart.done) return; */
465 if(len==0) return;
466 p_width = m.right - WMIN(m.first, m.body);
467 if(p_width < MIN_DOC_WIDTH) // need WMRuler to take care of this...
468 return;
471 t_chunk_width = WMWidthOfString(font, bufr, len);
472 if((where->x + t_chunk_width <= p_width - *Lmargin)
473 || (tPtr->wrapping == wrNone)) {
474 /* if it can fit on rest of line, append to line */
475 chars = WMIN(len, MAX_WORD_LENGTH);
476 snprintf(items[*nitems].text, chars+1, "%s", bufr);
477 items[*nitems].chars = chars;
478 items[*nitems].x = *Lmargin+where->x;
479 items[*nitems].type = ctText;
480 items[*nitems].chunk = chunk;
481 items[*nitems].start = start;
483 if(*nitems >= MAX_CHUNX) {
484 chars = WMIN(len, MAX_WORD_LENGTH);
485 snprintf(items[*nitems].text, chars+1, "%s", bufr);
486 items[*nitems].chars = chars;
487 items[*nitems].x = *Lmargin+where->x;
488 items[*nitems].type = ctText;
489 items[*nitems].chunk = chunk;
490 items[*nitems].start = start;
491 where->y += myDrawText(tPtr, para, items, *nitems+1,
492 p_width-*Lmargin, where->y, draw, spacepos);
493 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
494 *nitems = 0;
495 where->x = 0;
496 } else {
497 (*nitems)++;
498 where->x += t_chunk_width;
500 } else if(t_chunk_width <= p_width - *Lmargin) {
501 /* it can fit on an entire line, flush and wrap it to a new line */
502 where->y += myDrawText(tPtr, para, items, *nitems,
503 p_width-*Lmargin, where->y, draw, spacepos);
504 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
505 *nitems = 0;
506 *Lmargin = WMAX(0, m.body - m.first);
507 where->x = 0;
508 drawTChunkPart(tPtr, chunk, bufr, m, para, items, nitems,
509 len, start, Lmargin, where, draw, spacepos);
510 } else {
511 /* otherwise, chop line, call ourself recursively until it's all gone */
512 short J=0; /* bufr */
513 short j=0; /* local tmp buffer */
514 char tmp[len];
515 short diff = p_width - *Lmargin - where->x;
516 short _start=0;
518 if(diff < 20) {
519 where->y += myDrawText(tPtr, para, items, *nitems,
520 p_width-*Lmargin, where->y, draw, spacepos);
521 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return;
522 *nitems = 0;
523 *Lmargin = WMAX(0, m.body - m.first);
524 where->x = 0;
525 diff = p_width - *Lmargin - where->x;
528 for(J=0; J<len; J++) {
529 tmp[j] = bufr[J];
530 if(WMWidthOfString(font, tmp, j+1) > diff) {
531 drawTChunkPart(tPtr, chunk, tmp, m, para, items, nitems,
532 j, start+_start, Lmargin, where, draw, spacepos);
533 _start = J;
534 J--; j=0;
535 } else j++;
537 /* and there's always that last chunk, get it too */
538 drawTChunkPart(tPtr, chunk, tmp, m, para, items, nitems,
539 j, start+_start, Lmargin, where, draw, spacepos);
543 /* this function does what it's called :-)
544 o It is also used for calculating extents of para,
545 (returns height) so watch out for (Bool) draw
546 o Also used to determine where mouse was clicked */
547 static int
548 putParagraphOnPixmap(Text *tPtr, Paragraph *para, Bool draw)
550 char bufr[MAX_WORD_LENGTH+1]; /* a single word + '\0' */
551 MyTextItems items[MAX_CHUNX+1];
552 short lmargin, spacepos, i, s, nitems, start;
553 LocalMargins m;
554 XPoint where;
555 Chunk *chunk;
557 if(!tPtr->view->flags.realized || !para) return 0;
559 where.x = 0, where.y =0, nitems = 0;
560 m.left = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
561 m.right = WMGetRulerMargin(tPtr->ruler, WRulerRight) - m.left;
562 m.first = para->fmargin, m.body = para->bmargin;
564 if(draw) {
565 W_Screen *screen = tPtr->view->screen;
566 if(para->drawbuffer)
567 XFreePixmap(screen->display, para->drawbuffer);
568 if(para->width<2*tPtr->dFont->height) para->width = 2*tPtr->dFont->height;
569 if(para->height<tPtr->dFont->height) para->height = tPtr->dFont->height;
570 para->drawbuffer = XCreatePixmap(screen->display,
571 tPtr->view->window, para->width, para->height, screen->depth);
572 XFillRectangle(screen->display, para->drawbuffer,
573 WMColorGC(tPtr->bg), 0, 0, para->width, para->height);
577 //if(para->align != tPtr->dAlignment)
578 // para->align = tPtr->dAlignment;
580 /* draw the bullet if appropriate */
581 if(m.body>m.first && !tPtr->monoFont) {
582 lmargin = m.body - m.first;
583 if(draw) {
584 if(para->bulletPix)
585 WMDrawPixmap(para->bulletPix, para->drawbuffer, lmargin-10, 5);
586 else
587 WMDrawPixmap(tPtr->dBulletPix, para->drawbuffer, lmargin-10, 5);
589 /* NeXT sez next tab, I say the m.body - m.first margin */
590 } else {
591 lmargin = WMAX(0, m.first - m.body);
594 if(tPtr->findingClickPoint && !para->chunks) {
595 tPtr->currentChunk = NULL;
596 tPtr->foundClickPoint = True;
597 tPtr->tpos = 0;
598 tPtr->clicked.x = lmargin;
599 tPtr->clicked.y = 5;
600 tPtr->clheight = para->height;
601 tPtr->clwidth = 0;
602 return 0;
605 chunk = para->chunks;
606 while(chunk) {
608 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0;
610 if(chunk->type == ctImage && !tPtr->monoFont ) {
611 drawPChunkPart(tPtr, chunk, m, para, items, &nitems,
612 &lmargin, &where, draw);
613 } else if(chunk->text && chunk->type == ctText) {
614 if(tPtr->wrapping == wrNone) {
615 drawTChunkPart(tPtr, chunk, chunk->text, m, para, items, &nitems,
616 chunk->chars, 0, &lmargin, &where, draw, spacepos);
617 } else if(tPtr->wrapping == wrWord) {
618 spacepos=0, i=0, start=0;
619 while(spacepos < chunk->chars) {
620 bufr[i] = chunk->text[spacepos];
621 if( bufr[i] == ' ' || i >= MAX_WORD_LENGTH ) {
622 if(bufr[i] == ' ') s=1; else s=0;
623 drawTChunkPart(tPtr, chunk, bufr, m, para,
624 items, &nitems, i+s, start, &lmargin, &where,
625 draw, spacepos);
626 start = spacepos+s;
627 if(i > MAX_WORD_LENGTH-1)
628 spacepos--;
629 i=0;
630 } else i++;
631 spacepos++;
633 /* catch that last onery one. */
634 drawTChunkPart(tPtr, chunk, bufr, m, para,
635 items, &nitems, i, start, &lmargin, &where, draw, spacepos);
636 } } chunk = chunk->next;
638 /* we might have a few leftover items that need drawing */
639 if(nitems >0) {
640 where.y += myDrawText(tPtr, para, items,
641 nitems, m.right-m.left-lmargin, where.y, draw, spacepos);
642 if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0;
644 return where.y;
647 static int
648 calcParaExtents(Text *tPtr, Paragraph *para)
650 if(!para) return 0;
652 if(tPtr->monoFont) {
653 para->width = tPtr->visibleW;
654 para->fmargin = 0;
655 para->bmargin = 0;
656 para->rmargin = tPtr->visibleW;
657 } else {
658 para->width = WMGetRulerMargin(tPtr->ruler, WRulerRight) -
659 WMIN(para->fmargin, para->bmargin)
660 - WMGetRulerOffset(tPtr->ruler);
663 if(!para->chunks)
664 para->height = tPtr->dFont->height;
665 else
666 para->height = putParagraphOnPixmap(tPtr, para, False);
668 if(para->height<tPtr->dFont->height)
669 para->height = tPtr->dFont->height;
670 para->bottom = para->top + para->height;
671 return para->height;
675 /* rather than bother with redrawing _all_ the pixmaps, simply
676 rearrange (i.e., push down or pull up) paragraphs after this one */
677 static void
678 affectNextParas(Text *tPtr, Paragraph *para, int move_y)
680 Paragraph *next;
681 int old_y = 0;
683 if(!para || move_y==0) return;
684 if(move_y == -23) {
685 old_y = para->bottom;
686 calcParaExtents(tPtr, para);
687 old_y -= para->bottom;
688 if(old_y == 0) return;
689 move_y = -old_y;
690 }if(move_y == 0) return;
692 next = para->next;
693 while(next) {
694 next->top += move_y;
695 next->bottom = next->top + next->height;
696 next = next->next; // I know, I know
697 }tPtr->docHeight += move_y;
699 #if 0
700 tPtr->vpos += move_y;
701 if(tPtr->vpos < 0) tPtr->vpos = 0;
702 if(tPtr->vpos > tPtr->docHeight - tPtr->visibleH)
703 tPtr->vpos = tPtr->docHeight - tPtr->visibleH;
704 #endif
709 static void
710 calcDocExtents(Text *tPtr)
712 Paragraph *para;
714 if(tPtr->monoFont) {
715 tPtr->docWidth = tPtr->visibleW;
716 } else {
717 tPtr->docWidth = WMGetRulerMargin(tPtr->ruler, WRulerRight) -
718 WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
720 tPtr->docHeight = 0;
721 para = tPtr->paragraphs;
722 if(para) {
723 while(para) {
724 para->top = tPtr->docHeight;
725 tPtr->docHeight += calcParaExtents(tPtr, para);
726 para->bottom = tPtr->docHeight;
727 para = para->next;
729 } else { /* default to this if no paragraphs */
730 tPtr->docHeight = tPtr->dFont->height;
732 #if 0
733 if(tPtr->editable) /* add space at bottom to enter new stuff */
734 tPtr->docHeight += tPtr->dFont->height;
735 #endif
739 /* If any part of a paragraph is viewable, the entire
740 paragraph is drawn on an otherwise empty (XFreePixmap) pixmap.
741 The actual viewable parts of the paragraph(s) are then pieced
742 together via paintText:
744 -------------------------------------------
745 || this is a paragraph in this document||
746 ||========================================||
747 || | only part of it is visible though. ||
748 || |-------------------------------------||
749 ||[.| This is another paragraph ||
750 || | which I'll make relatively long ||
751 || | just for the sake of writing a long ||
752 || | paragraph with a picture: ^_^ ||
753 || |-------------------------------------||
754 ||==| Of the three paragraphs, only ||
755 ||/\| the preceding was totally copied to ||
756 ||\/| totally copied to the window, even ||
757 ==========================================||
758 though they are all on pixmaps.
759 -------------------------------------------
760 This paragraph exists only in
761 memory and so has a NULL pixmap.
762 -------------------------------------------
765 simple, right? Performance: the best of both worlds...
766 o fast scrolling: no need to rewrite what's already
767 on the screen, simply XCopy it.
768 o fast typing: only change current para, then simply
769 affect other (i.e., subsequent) paragraphs.
770 o If no part of para is on screen, gdbFree pixmap; else draw on
771 individual pixmap per para then piece several paras together
772 o Keep track of who to XCopy to window (see paintText) */
773 static void
774 drawDocumentPartsOnPixmap(Text *tPtr, Bool all)
776 Paragraph *para;
778 para = tPtr->paragraphs;
779 while(para) {
780 /* the 32 reduces jitter on the human eye by preparing paragraphs
781 in anticipation of when the _moving_ scrollbar reaches them */
782 if(para->bottom + 32 < tPtr->vpos ||
783 para->top > tPtr->visibleH + tPtr->vpos + 32 ) {
784 if(para->drawbuffer) {
785 XFreePixmap(tPtr->view->screen->display, para->drawbuffer);
786 para->drawbuffer = (Pixmap) NULL;
788 } else {
789 if(!para->drawbuffer || all)
790 putParagraphOnPixmap(tPtr, para, True);
792 para = para->next;
798 /* this function blindly copies the "visible" parts of a pragraph
799 unto the view, (top-down approach). It starts drawing from
800 the top of the view (which paragraph to draw is determined by
801 drawDocumentPartsOnPixmap); it stops at the bottom of the view. */
802 static void
803 paintText(Text *tPtr)
805 short lmargin, para_lmargin;
806 int from=5, to=5, height;
807 Paragraph *para;
808 short vS=0, hS=0, rh=0;
810 if(!tPtr->view->flags.realized) return;
812 if(tPtr->rulerShown) rh = 40;
813 to += rh;
815 if(tPtr->hasVscroller) vS = 21;
816 if(tPtr->hasHscroller) hS = 21;
818 //XClearWindow(tPtr->view->screen->display, tPtr->view->window);
820 lmargin = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
821 if(tPtr->paragraphs) {
822 para = tPtr->paragraphs;
823 while(para) {
824 if(para->drawbuffer) {
825 from = (para->top<=tPtr->vpos)?tPtr->vpos-para->top:0;
826 height = para->height - from;
827 if(from>=0 && height>0 ) {
828 para_lmargin = WMIN(para->fmargin, para->bmargin);
829 if(lmargin-vS<WMIN(para->fmargin, para->bmargin)) {
830 #if 0
831 XClearArea(tPtr->view->screen->display, tPtr->view->window,
832 lmargin, to, 2+para_lmargin, height, False);
833 #else
834 XFillRectangle(tPtr->view->screen->display, tPtr->view->window,
835 WMColorGC(tPtr->dColor), lmargin, to, 2+para_lmargin, height);
836 #endif
838 XCopyArea(tPtr->view->screen->display, para->drawbuffer,
839 tPtr->view->window, WMColorGC(tPtr->bg), 0, from,
840 para->width-4, height, lmargin+para_lmargin+2, to);
841 if( (to+=height) > tPtr->visibleH+rh)
842 break;
843 } }
844 para = para->next;
848 #if 0
849 /* clear any left over space (esp. during para deletes/ ruler changes) */
850 if(tPtr->docHeight < tPtr->visibleH && tPtr->visibleH+rh+5-to>0) {
851 XClearArea(tPtr->view->screen->display, tPtr->view->window, vS, to,
852 tPtr->view->size.width-vS, tPtr->visibleH+rh+hS+5-to, False);
855 if(lmargin>vS)
856 XClearArea(tPtr->view->screen->display, tPtr->view->window,
857 vS+1, rh+5, lmargin-vS, tPtr->visibleH+rh+5-vS, False);
860 // from the "selection" days...
861 W_DrawRelief(tPtr->view->screen, WMWidgetXID(tPtr),
862 tPtr->sRect.pos.x, tPtr->sRect.pos.y,
863 tPtr->sRect.size.width, tPtr->sRect.size.height, tPtr->relief);
864 #endif
866 W_DrawRelief(tPtr->view->screen, WMWidgetXID(tPtr), 0, rh,
867 tPtr->visibleW+vS, tPtr->visibleH+hS, tPtr->relief);
869 if(tPtr->editable && tPtr->clheight > 0) {
870 int top = tPtr->cursor.y-tPtr->vpos;
871 int bot = top+tPtr->clheight;
872 if(bot>5) {
873 if(top<5) top=5;
874 if(bot>tPtr->visibleH+hS-2) bot = tPtr->visibleH+hS-2;
875 if(bot-top>1) {
876 //do something about italic text...
877 XDrawLine(tPtr->view->screen->display, tPtr->view->window,
878 WMColorGC(tPtr->dColor), lmargin+tPtr->cursor.x, top,
879 lmargin+tPtr->cursor.x, bot);
880 } } }
886 /* called anytime either the ruler, vscroller or hscroller is hidden/shown,
887 or when the widget is resized by some user action */
888 static void
889 resizeText(W_ViewDelegate *self, WMView *view)
891 Text *tPtr = (Text *)view->self;
892 short rh=0;
894 if(!tPtr->monoFont && tPtr->rulerShown)
895 rh = 40;
897 W_ResizeView(view, view->size.width, view->size.height);
898 WMResizeWidget(tPtr->ruler, view->size.width, 40);
901 if(tPtr->hasVscroller) {
902 WMMoveWidget(tPtr->vscroller, 1, 1+rh);
903 WMResizeWidget(tPtr->vscroller, 20, view->size.height-rh-2);
904 tPtr->visibleW = view->size.width-21;
906 if(tPtr->hasHscroller) {
907 WMMoveWidget(tPtr->hscroller, 20, view->size.height-21);
908 WMResizeWidget(tPtr->hscroller, view->size.width-21, 20);
909 tPtr->visibleH = view->size.height-21-rh;
910 } else tPtr->visibleH = view->size.height-rh;
911 } else {
912 tPtr->visibleW = view->size.width;
913 if(tPtr->hasHscroller) {
914 WMMoveWidget(tPtr->hscroller, 1, view->size.height-21);
915 WMResizeWidget(tPtr->hscroller, view->size.width-2, 20);
916 tPtr->visibleH = view->size.height-21-rh;
917 } else tPtr->visibleH = view->size.width-2-rh;
919 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
922 W_ViewDelegate _TextViewDelegate =
924 NULL,
925 NULL,
926 resizeText,
927 NULL,
932 /* a plain text parser */
933 /* this gives useful hints on how to make a more
934 interesting parser for say HTML, RTF */
935 static void
936 defaultParser(Text *tPtr, void *data, short type)
938 char *start, *mark, *text = (char *) data;
939 Chunk *chunk = NULL;
940 Paragraph *para = NULL;
942 start = text;
943 while(start) {
944 mark = strchr(start, '\n');
945 if(mark) {
946 /* there is a newline, indicating the need for a new paragraph */
947 /* attach the chunk to the current paragraph */
948 if((short)(mark-start) > 1) {
949 /* ignore chunks with just a single newline but still make a
950 blank paragraph */
951 chunk = (tPtr->funcs.createTChunk) (start, (short)(mark-start),
952 tPtr->dFont, tPtr->dColor, 0, False);
953 (tPtr->funcs.insertChunk) (tPtr, chunk, type);
955 /* _then_ create a new paragraph for the _next_ chunk */
956 para = (tPtr->funcs.createParagraph) (0, 0, tPtr->visibleW,
957 NULL, 0, WALeft);
958 (tPtr->funcs.insertParagraph) (tPtr, para, type);
959 start = mark+1;
960 } else {
961 /* just attach the chunk to the current paragraph */
962 if(strlen(start) > 0) {
963 chunk = (tPtr->funcs.createTChunk) (start, strlen(start),
964 tPtr->dFont, tPtr->dColor, 0, False);
965 (tPtr->funcs.insertChunk) (tPtr, chunk, type);
966 } start = mark;
967 } }
971 static void
972 updateScrollers(Text *tPtr)
974 if(tPtr->hasVscroller) {
975 if(tPtr->docHeight < tPtr->visibleH) {
976 WMSetScrollerParameters(tPtr->vscroller, 0, 1);
977 tPtr->vpos = 0;
978 } else {
979 float vmax = (float)(tPtr->docHeight);
980 WMSetScrollerParameters(tPtr->vscroller,
981 ((float)tPtr->vpos)/(vmax - (float)tPtr->visibleH),
982 (float)tPtr->visibleH/vmax);
986 if(tPtr->hasHscroller)
990 static void
991 scrollersCallBack(WMWidget *w, void *self)
993 Text *tPtr = (Text *)self;
994 Bool scroll = False;
995 Bool dimple = False;
997 if(!tPtr->view->flags.realized) return;
999 if(w == tPtr->vscroller) {
1000 float vmax;
1001 int height;
1002 vmax = (float)(tPtr->docHeight);
1003 height = tPtr->visibleH;
1004 if(height>7)
1005 height -= 7; /* the top border (5) + bottom (2) */
1007 switch(WMGetScrollerHitPart(tPtr->vscroller)) {
1008 case WSDecrementLine:
1009 if(tPtr->vpos > 0) {
1010 if(tPtr->vpos>16) tPtr->vpos-=16;
1011 else tPtr->vpos=0;
1012 scroll=True;
1013 }break;
1014 case WSIncrementLine: {
1015 int limit = tPtr->docHeight - height;
1016 if(tPtr->vpos < limit) {
1017 if(tPtr->vpos<limit-16) tPtr->vpos+=16;
1018 else tPtr->vpos=limit;
1019 scroll = True;
1020 }}break;
1021 case WSDecrementPage:
1022 tPtr->vpos -= height;
1024 if(tPtr->vpos < 0)
1025 tPtr->vpos = 0;
1026 dimple = True;
1027 scroll = True;
1028 printf("dimple needs to jump to mouse location ;-/\n");
1029 break;
1030 case WSIncrementPage:
1031 tPtr->vpos += height;
1032 if(tPtr->vpos > (tPtr->docHeight - height))
1033 tPtr->vpos = tPtr->docHeight - height;
1034 dimple = True;
1035 scroll = True;
1036 printf("dimple needs to jump to mouse location ;-/\n");
1037 break;
1040 case WSKnob:
1041 tPtr->vpos = WMGetScrollerValue(tPtr->vscroller)
1042 * (float)(tPtr->docHeight - height);
1043 scroll = True;
1044 break;
1046 #if 0
1047 case WSKnobSlot:
1048 case WSNoPart:
1049 float vmax = (float)(tPtr->docHeight);
1050 ((float)tPtr->vpos)/(vmax - (float)tPtr->visibleH),
1051 (float)tPtr->visibleH/vmax);
1052 dimple =where mouse is.
1053 #endif
1054 break;
1056 scroll = (tPtr->vpos != tPtr->prevVpos);
1057 tPtr->prevVpos = tPtr->vpos;
1060 if(w == tPtr->hscroller)
1063 //need scrollv || scrollh
1064 if(scroll) {
1066 if(0&&dimple) {
1067 if(tPtr->rulerShown)
1068 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
1069 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
1070 else
1071 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
1072 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
1075 updateScrollers(tPtr);
1076 drawDocumentPartsOnPixmap(tPtr, False);
1077 paintText(tPtr);
1082 void
1083 W_InsertText(WMText *tPtr, void *data, int position)
1085 if(!tPtr) return;
1086 if(!data) {
1087 Paragraph *para = tPtr->paragraphs, *ptmp;
1088 Chunk *chunk, *ctmp;
1089 WMFreezeText(tPtr);
1090 while(para) {
1091 chunk = para->chunks;
1092 while(chunk) {
1093 if(chunk->type == ctText && chunk->text)
1094 wgdbFree(chunk->text);
1095 else if(chunk->pixmap)
1096 WMReleasePixmap(chunk->pixmap);
1097 ctmp = chunk;
1098 chunk = chunk->next;
1099 wgdbFree(ctmp);
1101 ptmp = para;
1102 para = para->next;
1103 if(ptmp->drawbuffer)
1104 XFreePixmap(tPtr->view->screen->display, ptmp->drawbuffer);
1105 wgdbFree(ptmp);
1107 tPtr->paragraphs = NULL;
1108 tPtr->currentPara = NULL;
1109 tPtr->currentChunk = NULL;
1110 WMThawText(tPtr);
1111 WMRefreshText(tPtr, 0, 0);
1112 return;
1115 if(tPtr->parser)
1116 (tPtr->parser)(tPtr, data, position >= 0 ? 1 : 0);
1117 else
1118 defaultParser(tPtr, data, position >= 0 ? 1 : 0);
1121 static void
1122 cursorToTextPosition(Text *tPtr, int x, int y)
1124 Paragraph *para = NULL;
1125 Chunk *chunk = NULL;
1126 WMFont *font;
1127 short line_width=0;
1128 short orig_x, orig_y;
1130 if(x<(tPtr->hasVscroller?21:1)) {
1131 y -= tPtr->clheight;
1132 x = tPtr->view->size.width; //tPtr->visibleW;
1133 } else if(x>tPtr->clwidth && x<tPtr->clicked.x) {
1134 //x = (tPtr->hasVscroller)?21:1;
1135 //y += tPtr->clheight;
1138 if(x<0) x=0;
1139 orig_x = x;
1141 if(y<0 || y>tPtr->view->size.height-3) return;
1142 orig_y = y;
1143 tPtr->clicked.x = orig_x;
1144 tPtr->clicked.y = y;
1145 tPtr->clicked.y += tPtr->vpos;
1146 tPtr->clicked.y -= tPtr->rulerShown?40:0;
1147 para = tPtr->paragraphs;
1148 if(!para) return;
1149 while(para->next) {
1150 if( tPtr->clicked.y>= para->top-4 &&
1151 tPtr->clicked.y < para->bottom+4) break;
1152 para = para->next;
1153 } if(!(tPtr->currentPara = para)) return;
1155 tPtr->clicked.y -= para->top;
1156 if(tPtr->clicked.y<0) tPtr->clicked.y=0;
1157 if(tPtr->hasVscroller) x -= 21;
1158 if(x<0) x=0;
1160 tPtr->findingClickPoint = True;
1161 tPtr->foundClickPoint = False;
1162 /* also affects tPtr->currentChunk, tPtr->clicked.x and y,
1163 tPtr->clheight and ->width */
1164 putParagraphOnPixmap(tPtr, para, False);
1165 tPtr->findingClickPoint = False;
1166 tPtr->clicked.y += para->top;
1168 if(tPtr->currentChunk) {
1169 short _width=0, start=tPtr->tpos, done=False, w=0;
1170 chunk = tPtr->currentChunk;
1171 while(!done && chunk && line_width<tPtr->clwidth) {
1172 if(chunk->type == ctText) {
1173 font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
1174 for (w=start; w<chunk->chars; w++) {
1175 _width = WMWidthOfString(font, &chunk->text[w], 1);
1176 line_width += _width;
1177 if(line_width+tPtr->clicked.x >= x) {
1178 line_width -= _width;
1179 done = True;
1180 printf("break\n");
1181 break;
1184 if(0&&chunk->next) {
1185 if(chunk->next->type == ctImage) {
1186 if(x+10 < line_width+chunk->next->pixmap->width) {
1187 printf("true\n");
1188 done = True;
1189 } } }
1190 } else {
1191 _width = chunk->pixmap->width;
1192 line_width += _width;
1193 if(line_width+tPtr->clicked.x >= x) {
1194 line_width -= _width;
1195 tPtr->tpos = 0;
1196 done = True;
1199 if(!done) {
1200 chunk = chunk->next;
1201 start = w = 0;
1202 } else {
1203 tPtr->tpos = w;
1204 tPtr->currentChunk = chunk;
1205 break;
1206 } } } else {
1207 short vS = (tPtr->hasVscroller)?32:12;
1208 if(para->align == WARight) {
1209 tPtr->clicked.x = tPtr->view->size.width-vS;
1210 } else if (para->align == WACenter) {
1211 tPtr->clicked.x = -(vS/2)+(tPtr->view->size.width-vS)/2;
1212 } else {
1213 tPtr->clicked.x = 2;
1216 tPtr->cursor.x = tPtr->clicked.x+2+line_width;
1217 tPtr->cursor.y = tPtr->clicked.y;
1218 tPtr->clicked.y = orig_y;
1219 tPtr->clicked.x = orig_x;
1220 putParagraphOnPixmap(tPtr, para, True);
1221 paintText(tPtr);
1224 static void
1225 deleteTextInteractively(Text *tPtr, DeleteType type)
1227 Paragraph *para;
1228 Chunk *chunk;
1229 short pos,w=0,h=0, doprev=False, doprevpara=False;
1230 WMFont *font;
1231 int current = WMGetTextCurrentChunk(tPtr);
1233 if(!(para = tPtr->currentPara)) return;
1234 if(!(chunk = tPtr->currentChunk)) return;
1235 font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
1236 doprev = (tPtr->tpos < 2);
1238 switch(type) {
1239 case dtDelete: /* delete _after_ cursor ... implement later */
1240 case dtBackSpace: /* delete _before_ cursor */
1241 if(chunk->chars > 1) {
1242 pos = tPtr->tpos-1;
1243 printf("here %d\n", pos);
1244 if(pos>0) {
1245 w = WMWidthOfString(font, &chunk->text[pos], 1);
1246 memmove(&(chunk->text[pos]),
1247 &(chunk->text[pos+1]), chunk->chars-pos+1);
1248 tPtr->tpos--;
1249 chunk->chars--;
1250 } } else {
1251 WMRemoveTextChunk(tPtr, current);
1252 doprev = True;
1255 if(doprev) {
1256 if(current > 0) {
1257 WMSetTextCurrentChunk(tPtr, current-1);
1258 if(!tPtr->currentChunk) {
1259 printf("PREV PARA\n");
1260 } else {
1261 tPtr->tpos = tPtr->currentChunk->chars;
1263 } else if(0){
1264 int currentp = WMGetTextCurrentParagraph(tPtr);
1265 doprevpara = True;
1266 if(currentp > 1) {
1267 para->chunks = NULL;
1268 WMRemoveTextParagraph(tPtr, currentp);
1269 WMSetTextCurrentParagraph(tPtr, currentp-1);
1270 WMSetTextCurrentChunk(tPtr, -1);
1271 para = tPtr->currentPara;
1272 if(para) {
1273 if(!tPtr->currentChunk || !para->chunks) {
1274 para->chunks = chunk;
1275 tPtr->currentChunk = chunk;
1276 } else
1277 tPtr->currentChunk->next = chunk;
1278 } } } } }
1280 if(1) { //if(1||(para && !doprevpara)) {
1281 affectNextParas(tPtr, para, -23);
1282 putParagraphOnPixmap(tPtr, para, True);
1283 drawDocumentPartsOnPixmap(tPtr, False);
1284 updateScrollers(tPtr);
1285 paintText(tPtr);
1286 //cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1287 } else WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1291 /* give us nice chunk sizes (multiples of 16) */
1292 static short
1293 reqBlockSize(short requested)
1295 return requested+16-(requested%16);
1298 static void
1299 insertTextInteractively(Text *tPtr, char *text)
1301 Paragraph *para=NULL;
1302 Chunk *chunk=NULL, *newchunk=NULL;
1303 int height = -23; /* should only be changed upon newline */
1304 short w=0,h=0;
1305 WMFont *font;
1307 if(!tPtr->editable) return;
1308 if(*text == '\n' && tPtr->ignoreNewLine)
1309 return;
1311 para = tPtr->currentPara;
1312 chunk = tPtr->currentChunk;
1313 font = (tPtr->monoFont || !chunk)?tPtr->dFont:chunk->font;
1315 if(*text == '\n') {
1316 int new_top=0;
1317 if(chunk) { /* there's a chunk (or part of it) to detach from old */
1318 int current = WMGetTextCurrentChunk(tPtr);
1319 if(tPtr->tpos <=0) { /* at start of chunk */
1320 if(current<1) { /* the first chunk... make old para blank */
1321 newchunk = para->chunks;
1322 para->chunks = NULL;
1323 putParagraphOnPixmap(tPtr, para, True);
1324 } else { /* not first chunk... */
1325 printf("cut me out \n");
1327 } else if(tPtr->tpos < chunk->chars && chunk->type == ctText) {
1328 /* not at start of chunk */
1329 char text[chunk->chars-tPtr->tpos+1];
1330 int i=0;
1331 do {
1332 text[i] = chunk->text[tPtr->tpos+i];
1333 } while(++i < chunk->chars-tPtr->tpos);
1334 chunk->chars -= i;
1335 newchunk = (tPtr->funcs.createTChunk) (text, i, chunk->font,
1336 chunk->color, chunk->script, chunk->ul);
1337 newchunk->next = chunk->next;
1338 chunk->next = NULL;
1339 /* might want to demalloc for LARGE cuts */
1340 //calcParaExtents(tPtr, para);
1341 para->height = putParagraphOnPixmap(tPtr, para, True);
1342 //putParagraphOnPixmap(tPtr, para, True);
1343 } else if(tPtr->tpos >= chunk->chars) {
1344 Chunk *prev;
1345 WMSetTextCurrentChunk(tPtr, current-1);
1346 prev = tPtr->currentChunk;
1347 if(!prev) return;
1348 newchunk = prev->next;
1349 prev->next = NULL;
1350 putParagraphOnPixmap(tPtr, para, True);
1352 } else newchunk = NULL;
1354 if(para) /* the preceeding one */
1355 new_top = para->bottom;
1357 WMAppendTextStream(tPtr, "\n");
1358 para = tPtr->currentPara;
1359 if(!para) return;
1360 para->chunks = newchunk;
1361 tPtr->currentChunk = newchunk;
1362 tPtr->tpos = 0;
1363 para->top = new_top;
1364 calcParaExtents(tPtr, para);
1365 height = para->height;
1366 } else {
1367 if(!para) {
1368 WMAppendTextStream(tPtr, text);
1369 para = tPtr->currentPara;
1370 } else if(!para->chunks || !chunk) {
1371 //WMPrependTextStream(tPtr, text);
1372 WMAppendTextStream(tPtr, text);
1373 } else if(chunk->type == ctImage) {
1374 WMPrependTextStream(tPtr, text);
1376 printf("\n\nprepe\n\n");
1377 } else {
1378 if(tPtr->tpos > chunk->chars) {
1379 printf("\n\nmore\n\n");
1380 tPtr->tpos = chunk->chars;
1383 if(chunk->chars+1 >= chunk->mallocedSize) {
1384 chunk->mallocedSize = reqBlockSize(chunk->chars+1);
1385 chunk->text = wrealloc(chunk->text, chunk->mallocedSize);
1388 memmove(&(chunk->text[tPtr->tpos+1]), &chunk->text[tPtr->tpos],
1389 chunk->chars-tPtr->tpos+1);
1390 w = WMWidthOfString(font, text, 1);
1391 memmove(&chunk->text[tPtr->tpos], text, 1);
1392 chunk->chars++;
1393 tPtr->tpos++;
1394 //doc->clickstart.cursor.x +=
1395 //WMWidthOfString(chunk->fmt->font, text,len);
1400 if(para) {
1401 affectNextParas(tPtr, para, height);
1402 putParagraphOnPixmap(tPtr, para, True);
1403 drawDocumentPartsOnPixmap(tPtr, False);
1404 updateScrollers(tPtr);
1405 paintText(tPtr);
1406 //cursorToTextPosition(tPtr, tPtr->clicked.x+w, tPtr->clicked.y);
1407 //check for "sneppah tahw" with blank paras...
1408 //paintText(tPtr);
1413 static void
1414 selectRegion(Text *tPtr, int x, int y)
1416 tPtr->sRect.pos.x = WMIN(tPtr->clicked.x, x);
1417 tPtr->sRect.size.width = abs(tPtr->clicked.x-x);
1418 tPtr->sRect.pos.y = WMIN(tPtr->clicked.y, y);
1419 if(tPtr->sRect.pos.y<0) tPtr->sRect.pos.y=0;
1420 tPtr->sRect.size.height = abs(tPtr->clicked.y-y);
1423 while(y>tPtr->visibleH && tPtr->vpos < tPtr->docHeight-tPtr->visibleH) {
1424 WMRefreshText(tPtr, tPtr->vpos+16, tPtr->hpos);
1427 //printf("%d %d \n", y, tPtr->vpos);
1429 //foreach para in selection...
1430 drawDocumentPartsOnPixmap(tPtr, True);
1431 paintText(tPtr);
1438 #define WM_EMACSKEYMASK ControlMask
1439 #define WM_EMACSKEY_LEFT XK_b
1440 #define WM_EMACSKEY_RIGHT XK_f
1441 #define WM_EMACSKEY_HOME XK_a
1442 #define WM_EMACSKEY_END XK_e
1443 #define WM_EMACSKEY_BS XK_h
1444 #define WM_EMACSKEY_DEL XK_d
1446 static void
1447 handleTextKeyPress(Text *tPtr, XEvent *event)
1449 char buffer[2];
1450 KeySym ksym;
1451 int control_pressed = False;
1453 if(!tPtr->editable) return;
1455 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
1456 control_pressed = True;
1457 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = '\0';
1459 switch(ksym) {
1461 case XK_Right:
1462 case XK_Left:
1463 if(tPtr->currentChunk) {
1464 short w;
1465 Chunk *chunk = tPtr->currentChunk;
1466 if(chunk->type == ctText) {
1467 WMFont *font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
1468 if(ksym==XK_Right) {
1469 short pos = (tPtr->tpos<chunk->chars)?tPtr->tpos+1:
1470 chunk->chars;
1471 w = WMWidthOfString(font,&chunk->text[pos],1);
1472 } else {
1473 short pos = (tPtr->tpos>0)?tPtr->tpos-1:0;
1474 w = WMWidthOfString(font,&chunk->text[pos],1);
1476 } else { w = chunk->pixmap->width; }
1477 if(ksym==XK_Right) w = -w;
1478 cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1479 } else {
1480 if(ksym==XK_Right) ksym = XK_Down;
1481 else ksym = XK_Up;
1482 goto noCChunk;
1484 break;
1486 case XK_Down:
1487 case XK_Up:
1488 noCChunk: { short h = tPtr->clheight-2;
1489 if(ksym==XK_Down) h = -h;
1490 cursorToTextPosition(tPtr, tPtr->clicked.x, tPtr->clicked.y-h);
1491 } break;
1493 case XK_BackSpace:
1494 deleteTextInteractively(tPtr, dtBackSpace);
1495 break;
1497 case XK_Delete:
1498 case XK_KP_Delete:
1499 deleteTextInteractively(tPtr, dtDelete);
1500 break;
1502 case XK_Return:
1503 buffer[0] = '\n';
1504 default:
1505 if(buffer[0] != '\0' && (buffer[0] == '\n' || !iscntrl(buffer[0])))
1506 insertTextInteractively(tPtr, buffer);
1507 else if(control_pressed && ksym==XK_r)
1508 {Bool i = !tPtr->rulerShown; WMShowTextRuler(tPtr, i);
1509 tPtr->rulerShown = i; }
1516 static void
1517 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1518 void *cdata, WMData *data)
1520 Text *tPtr = (Text *)view->self;
1521 char *str;
1524 tPtr->waitingForSelection = False;
1525 if(data) {
1526 str = (char*)WMDataBytes(data);
1527 if(tPtr->tpos<1) WMPrependTextStream(tPtr, str);
1528 else WMAppendTextStream(tPtr, str);
1529 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1530 } else {
1531 int n;
1532 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1533 if(str) {
1534 str[n] = 0;
1535 if(tPtr->tpos<1) WMPrependTextStream(tPtr, str);
1536 else WMAppendTextStream(tPtr, str);
1537 XFree(str);
1538 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1544 static void
1545 releaseSelection(Text *tPtr)
1547 Paragraph *para = tPtr->paragraphs;
1548 Chunk *chunk;
1549 while(para) {
1550 chunk = para->chunks;
1551 while(chunk) {
1552 chunk->selected = False;
1553 chunk = chunk->next;
1555 para = para->next;
1557 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1558 tPtr->ownsSelection = False;
1559 drawDocumentPartsOnPixmap(tPtr, True);
1560 paintText(tPtr);
1564 static WMData*
1565 requestHandler(WMView *view, Atom selection, Atom target,
1566 void *cdata, Atom *type)
1568 Text *tPtr = view->self;
1569 int count;
1570 Display *dpy = tPtr->view->screen->display;
1571 Atom _TARGETS;
1572 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1573 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1574 WMData *data = NULL;
1577 if(!tPtr->ownsSelection || !tPtr->paragraphs) return NULL;
1578 //printf("got here\n");
1580 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
1581 //for bleh in selection...
1582 char *s = NULL;
1583 Paragraph *para = tPtr->paragraphs;
1584 Chunk *chunk = NULL;
1585 char pixmap[] = "[pixmap]";
1586 Bool first=True;
1587 short len;
1589 while(para) {
1590 chunk = para->chunks;
1591 while(chunk) {
1593 if(chunk->selected && chunk->type == ctText) {
1594 len = chunk->chars; //chunk->sEnd - chunk->sStart;
1595 if(len>0) {
1596 s = wmalloc(len+1);
1597 if(s) {
1598 memcpy(s, &chunk->text[0*chunk->sStart], len);
1599 s[len] = 0;
1600 if(first) {
1601 data = WMCreateDataWithBytes(s, strlen(s));
1602 first = False;
1603 } else {
1604 printf("append: %c %d\n", *s, strlen(s));
1605 WMAppendDataBytes(data, s, strlen(s));
1607 //gdbFree(s);
1608 } } }
1609 #if 0
1610 printf("len is %d [%d %d] %d \n", len, chunk->sStart, chunk->sEnd,
1611 chunk->chars);
1612 #endif
1613 chunk = chunk->next;
1615 para = para->next;
1618 if(data) {
1619 WMSetDataFormat(data, 8);
1620 *type = target;
1622 return data;
1625 #if 0
1626 _TARGETS = XInternAtom(dpy, "TARGETS", False);
1627 if (target == _TARGETS) {
1628 Atom *ptr = wmalloc(4 * sizeof(Atom));
1629 ptr[0] = _TARGETS;
1630 ptr[1] = XA_STRING;
1631 ptr[2] = TEXT;
1632 ptr[3] = COMPOUND_TEXT;
1634 data = WMCreateDataWithBytes(ptr, 4*4);
1635 WMSetDataFormat(data, 32);
1637 *type = target;
1638 return data;
1640 #endif
1642 return NULL;
1647 static void
1648 lostHandler(WMView *view, Atom selection, void *cdata)
1650 WMText *tPtr = (WMText *)view->self;
1651 releaseSelection(tPtr);
1654 static WMSelectionProcs selectionHandler = {
1655 requestHandler, lostHandler, NULL };
1657 static void
1658 _notification(void *observerData, WMNotification *notification)
1660 WMText *to = (WMText *)observerData;
1661 WMText *tw = (WMText *)WMGetNotificationClientData(notification);
1662 if (to != tw) lostHandler(to->view, XA_PRIMARY, NULL);
1665 static void
1666 handleTextEvents(XEvent *event, void *data)
1668 Text *tPtr = (Text *)data;
1669 Display *dpy = event->xany.display;
1671 if(tPtr->waitingForSelection) return;
1673 switch (event->type) {
1674 case KeyPress:
1675 if(!tPtr->editable || tPtr->buttonHeld) {
1676 XBell(dpy, 0);
1677 return;
1679 if(tPtr->ownsSelection) releaseSelection(tPtr);
1680 //if (tPtr->waitingForSelection) return;
1681 if(tPtr->focused) {
1682 #if 0
1683 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1684 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1685 GrabModeAsync, GrabModeAsync, None,
1686 W_VIEW(tPtr)->screen->invisibleCursor, CurrentTime);
1687 tPtr->pointerGrabbed = True;
1688 #endif
1689 handleTextKeyPress(tPtr, event);
1690 } break;
1692 case MotionNotify:
1693 if(tPtr->pointerGrabbed) {
1694 tPtr->pointerGrabbed = False;
1695 XUngrabPointer(dpy, CurrentTime);
1697 if((event->xmotion.state & Button1Mask)) {
1698 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1699 if(!tPtr->ownsSelection) {
1700 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1701 event->xbutton.time, &selectionHandler, NULL);
1702 tPtr->ownsSelection = True;
1704 break;
1706 case ButtonPress:
1707 if(event->xbutton.button == Button1) {
1708 if(tPtr->ownsSelection) releaseSelection(tPtr);
1709 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1710 if (tPtr->pointerGrabbed) {
1711 tPtr->pointerGrabbed = False;
1712 XUngrabPointer(dpy, CurrentTime);
1713 break;
1716 if(!tPtr->focused) {
1717 WMSetFocusToWidget(tPtr);
1718 tPtr->focused = True;
1719 break;
1721 if(event->xbutton.button == 4)
1722 WMScrollText(tPtr, -16);
1723 else if(event->xbutton.button == 5)
1724 WMScrollText(tPtr, 16);
1726 break;
1728 case ButtonRelease:
1729 tPtr->buttonHeld = False;
1730 if (tPtr->pointerGrabbed) {
1731 tPtr->pointerGrabbed = False;
1732 XUngrabPointer(dpy, CurrentTime);
1733 break;
1735 if(event->xbutton.button == 4 || event->xbutton.button == 5)
1736 break;
1737 if(event->xbutton.button == Button2 && tPtr->editable) {
1738 char *text = NULL;
1739 int n;
1740 if(!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1741 event->xbutton.time, pasteText, NULL)) {
1742 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1743 if(text) {
1744 text[n] = 0;
1745 WMAppendTextStream(tPtr, text);
1746 XFree(text);
1747 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1748 } else tPtr->waitingForSelection = True;
1749 } } break;
1756 static void
1757 handleNonTextEvents(XEvent *event, void *data)
1759 Text *tPtr = (Text *)data;
1761 switch(event->type) {
1762 case Expose:
1763 if(!event->xexpose.count && tPtr->view->flags.realized)
1764 paintText(tPtr);
1765 break;
1767 case FocusIn:
1768 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
1769 return;
1770 tPtr->focused = True;
1771 //cursor...paintText(tPtr);
1772 break;
1774 case FocusOut:
1775 tPtr->focused = False;
1776 //cursor...paintText(tPtr);
1777 break;
1779 case DestroyNotify:
1780 printf("destroy");
1781 //for(...)WMRemoveTextParagraph(tPtr, para);
1782 break;
1786 //printf("handleNonTextEvents\n");
1791 static void
1792 rulerCallBack(WMWidget *w, void *self)
1794 Text *tPtr = (Text *)self;
1795 short which;
1797 if(tPtr->currentPara) {
1798 Paragraph *para = tPtr->currentPara;
1799 para->fmargin = WMGetRulerMargin(tPtr->ruler, WRulerFirst);
1800 para->bmargin = WMGetRulerMargin(tPtr->ruler, WRulerBody);
1801 para->rmargin = WMGetRulerMargin(tPtr->ruler, WRulerRight);
1802 affectNextParas(tPtr, para, -23);
1803 putParagraphOnPixmap(tPtr, para, True);
1805 #if 0
1806 which = WMGetReleasedRulerMargin(tPtr->ruler);
1807 if(which != WRulerDocLeft && which != WRulerRight
1808 /* && Selection.para.count > 0 */ ) {
1809 printf(""
1810 "//for(i=0; i<Selection.para.count; i++) {"
1811 "affect"
1812 "//calcParaExtents(tPtr, para);}\n");
1813 } else {
1814 WMRefreshText(tPtr, 0, 0);
1816 #endif
1820 static void
1821 rulerMoveCallBack(WMWidget *w, void *self)
1823 Text *tPtr = (Text *)self;
1824 short rmargin = WMGetRulerMargin(tPtr->ruler, WRulerRight);
1827 if(WMGetGrabbedRulerMargin(tPtr->ruler) == WRulerLeft) {
1828 short lmargin = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
1829 XClearArea(tPtr->view->screen->display, tPtr->view->window,
1830 22, 42, lmargin-21, tPtr->visibleH, True);
1831 } else if(WMGetGrabbedRulerMargin(tPtr->ruler) == WRulerRight &&
1832 tPtr->docWidth+11 < rmargin) {
1833 XClearArea(tPtr->view->screen->display, tPtr->view->window,
1834 rmargin-3, 42, 10, tPtr->visibleH, True);
1836 paintText(tPtr);
1841 /* ------------- non-static functions that are "friends" ------------- */
1842 /* ------------- called as (tPtr->funcs.foo)(bars...) ------------- */
1844 /* create a new paragraph. Don't do anything with it just yet */
1845 //Paragraph *
1846 void *
1847 createParagraph(short fmargin, short bmargin, short rmargin,
1848 short *tabstops, short numTabs, WMAlignment alignment)
1850 Paragraph *para = wmalloc(sizeof(Paragraph));
1851 if(!para) return NULL;
1853 para->chunks = NULL;
1854 para->next = NULL;
1857 para->fmargin = (fmargin>=0)?fmargin:0;
1858 para->bmargin = (bmargin>=0)?bmargin:0;
1859 if(rmargin-bmargin >= 100 && rmargin-fmargin >= 100)
1860 para->rmargin = rmargin;
1861 else
1862 para->rmargin = 100;
1863 para->tabstops = tabstops;
1864 para->numTabs = (tabstops)?numTabs:0;
1866 para->drawbuffer = (Pixmap)NULL;
1867 para->bulletPix = NULL;
1868 para->top = para->bottom = 0;
1869 para->width = para->height = 0;
1871 para->align = alignment;
1873 return para;
1876 /* insert the new paragraph in the tPtr, either right before
1877 or after the currentPara. It's the responsibility of the
1878 calling code to set what currentPara is. via WMSetTextCurrentParagraph.
1879 If currentPara is not set, set it as the first in the document.
1880 This function then sets currentPara as _this_ paragraph.
1881 NOTE: this means careless parser implementors might lose previous
1882 paragraphs... but this keeps stuff small and non-buggy :-) */
1883 void
1884 insertParagraph(WMText *tPtr, void *v, InsertType type)
1885 //insertParagraph(WMText *tPtr, Paragraph *para, InsertType type)
1887 Paragraph *tmp;
1888 Paragraph *para = (Paragraph *)v;
1889 if(!para || !tPtr) return;
1891 if(!tPtr->currentPara) {
1892 tPtr->paragraphs = para;
1893 } else {
1894 tmp = tPtr->paragraphs;
1895 if(type == itAppend) {
1896 while(tmp->next && tmp != tPtr->currentPara)
1897 tmp = tmp->next;
1899 para->next = tmp->next;
1900 tmp->next = para;
1901 } else { /* must be prepend */
1902 /* this "prior" member is that "doing things the hard way"
1903 I spoke of. See? it's not too bad afterall... */
1904 Paragraph *prior = NULL;
1905 while(tmp->next && tmp != tPtr->currentPara) {
1906 prior = tmp;
1907 tmp = tmp->next;
1909 /* if this is the first */
1910 if(tmp == tPtr->paragraphs) {
1911 para->next = tmp;
1912 tPtr->paragraphs = para;
1913 } else {
1914 prior->next = para;
1915 para->next = tmp;
1916 } } }
1917 tPtr->currentPara = para;
1921 /* create a new chunk to contain exactly ONE pixmap */
1922 void *
1923 //Chunk *
1924 createPChunk(WMPixmap *pixmap, short script, ushort ul)
1926 Chunk *chunk;
1928 chunk = wmalloc(sizeof(Chunk));
1929 if(!chunk)
1930 return NULL;
1932 chunk->text = NULL;
1933 if(!pixmap)
1934 chunk->pixmap = NULL; /* if it's NULL, we'll draw the "broken" pixmap... */
1935 else chunk->pixmap = WMRetainPixmap(pixmap);
1936 chunk->chars = 0;
1937 chunk->mallocedSize = 0;
1938 chunk->type = ctImage;
1939 chunk->font = NULL;
1940 chunk->color = NULL;
1941 chunk->script = script;
1942 chunk->ul = ul;
1943 chunk->selected = False;
1944 chunk->next = NULL;
1945 return chunk;
1949 /* create a new chunk to contain some text with the given format */
1950 void *
1951 //Chunk *
1952 createTChunk(char *text, short chars, WMFont *font,
1953 WMColor *color, short script, ushort ul)
1955 Chunk *chunk;
1957 if(!text || chars<0 || !font || !color) return NULL;
1958 chunk = wmalloc(sizeof(Chunk));
1959 if(!chunk) return NULL;
1961 chunk->mallocedSize = reqBlockSize(chars);
1962 chunk->text = (char *)wmalloc(chunk->mallocedSize);
1963 memcpy(chunk->text, text, chars);
1964 chunk->pixmap = NULL;
1965 chunk->chars = chars;
1966 chunk->type = ctText;
1967 chunk->font = WMRetainFont(font);
1968 chunk->color = WMRetainColor(color);
1969 chunk->script = script;
1970 chunk->ul = ul;
1971 chunk->selected = False;
1972 chunk->next = NULL;
1974 return chunk;
1977 /* insert the new chunk in the paragraph, either right before
1978 or after the currentChunk. It's the responsibility of the
1979 calling code to set what currentChunk is via WMSetTextCurrentChunk.
1980 If currentChunk is not set, set it as the first in the existing
1981 paragraph... if not even that, you lose... try again.
1982 This function then sets currentChunk as _this_ chunk.
1983 NOTE: this means careless parser implementors might lose previous
1984 paragraphs/chunks... but this keeps stuff small and non-buggy :-) */
1985 void
1986 insertChunk(WMText *tPtr, void *v, InsertType type)
1988 Chunk *tmp;
1989 Chunk *chunk = (Chunk *)v;
1991 if(!tPtr || !chunk) return;
1993 if(!tPtr->paragraphs) { /* i.e., first chunk via insertTextInteractively */
1994 Paragraph *para = (tPtr->funcs.createParagraph) (0, 0, tPtr->visibleW,
1995 NULL, 0, WALeft);
1996 (tPtr->funcs.insertParagraph) (tPtr, para, itAppend);
1999 if(!tPtr->currentPara)
2000 return;
2001 if(!tPtr->currentChunk) { /* there is a current chunk */
2002 tPtr->currentPara->chunks = chunk;
2003 } else if(!tPtr->currentPara->chunks) {
2004 /* but it's not of this paragraph */
2005 tPtr->currentPara->chunks = chunk;
2006 } else {
2007 tmp = tPtr->currentPara->chunks;
2009 if(type == itAppend) {
2010 while(tmp->next && tmp != tPtr->currentChunk)
2011 tmp = tmp->next;
2013 chunk->next = tmp->next;
2014 tmp->next = chunk;
2016 } else { /* must be prepend */
2017 /* this "prior" member is that "doing things the hard way"
2018 I spoke of. See? it's not too bad afterall... */
2019 Chunk *prior = NULL;
2020 while(tmp->next && tmp != tPtr->currentChunk) {
2021 prior = tmp;
2022 tmp = tmp->next;
2024 /* if this is the first */
2025 if(tmp == tPtr->currentPara->chunks) {
2026 chunk->next = tmp;
2027 tPtr->currentPara->chunks = chunk;
2028 } else {
2029 prior->next = chunk;
2030 chunk->next = tmp;
2031 } } }
2032 tPtr->currentChunk = chunk;
2033 tPtr->tpos = chunk->chars;
2037 /* ------------- non-static functions (i.e., APIs) ------------- */
2038 /* ------------- called as WMVerbText[Subject] ------------- */
2040 #define DEFAULT_TEXT_WIDTH 250
2041 #define DEFAULT_TEXT_HEIGHT 200
2045 WMText*
2046 WMCreateText(WMWidget *parent)
2048 Text *tPtr = wmalloc(sizeof(Text));
2049 if(!tPtr) {
2050 perror("could not create text widget\n");
2051 return NULL;
2053 memset(tPtr, 0, sizeof(Text));
2054 tPtr->widgetClass = WC_Text;
2055 tPtr->view = W_CreateView(W_VIEW(parent));
2056 if (!tPtr->view) {
2057 perror("could not create text's view\n");
2058 wgdbFree(tPtr);
2059 return NULL;
2061 tPtr->view->self = tPtr;
2062 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
2063 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
2064 W_ResizeView(tPtr->view, DEFAULT_TEXT_WIDTH, DEFAULT_TEXT_HEIGHT);
2065 tPtr->bg = tPtr->view->screen->white;
2066 W_SetViewBackgroundColor(tPtr->view, tPtr->bg);
2069 tPtr->ruler = WMCreateRuler(tPtr);
2070 (W_VIEW(tPtr->ruler))->attribs.cursor = tPtr->view->screen->defaultCursor;
2071 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
2072 WMMoveWidget(tPtr->ruler, 0, 0);
2073 WMResizeWidget(tPtr->ruler, W_VIEW(parent)->size.width, 40);
2074 WMShowRulerTabs(tPtr->ruler, True);
2075 WMSetRulerAction(tPtr->ruler, rulerCallBack, tPtr);
2076 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
2078 tPtr->vpos = 0;
2079 tPtr->prevVpos = 0;
2080 tPtr->vscroller = WMCreateScroller(tPtr);
2081 (W_VIEW(tPtr->vscroller))->attribs.cursor =
2082 tPtr->view->screen->defaultCursor;
2083 (W_VIEW(tPtr->vscroller))->attribFlags |= CWOverrideRedirect | CWCursor;
2084 WMMoveWidget(tPtr->vscroller, 1, 1);
2085 WMResizeWidget(tPtr->vscroller, 20, tPtr->view->size.height - 2);
2086 WMSetScrollerArrowsPosition(tPtr->vscroller, WSAMaxEnd);
2087 WMSetScrollerAction(tPtr->vscroller, scrollersCallBack, tPtr);
2089 tPtr->hpos = 0;
2090 tPtr->prevHpos = 0;
2091 tPtr->hscroller = WMCreateScroller(tPtr);
2092 (W_VIEW(tPtr->hscroller))->attribs.cursor =
2093 tPtr->view->screen->defaultCursor;
2094 (W_VIEW(tPtr->hscroller))->attribFlags |= CWOverrideRedirect | CWCursor;
2095 WMMoveWidget(tPtr->hscroller, 1, tPtr->view->size.height-21);
2096 WMResizeWidget(tPtr->hscroller, tPtr->view->size.width - 2, 20);
2097 WMSetScrollerArrowsPosition(tPtr->hscroller, WSAMaxEnd);
2098 WMSetScrollerAction(tPtr->hscroller, scrollersCallBack, tPtr);
2100 tPtr->visibleW = tPtr->view->size.width;
2101 tPtr->visibleH = tPtr->view->size.height;
2103 tPtr->paragraphs = NULL;
2104 tPtr->docWidth = 0;
2105 tPtr->docHeight = 0;
2106 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
2107 default_bullet);
2108 tPtr->dUnknownImg = WMCreatePixmapFromXPMData(tPtr->view->screen,
2109 unk_xpm);
2111 tPtr->sRect.pos.x = tPtr->sRect.pos.y = 0;
2112 tPtr->sRect.size.width = tPtr->sRect.size.height = 0;
2113 tPtr->currentPara = NULL;
2114 tPtr->currentChunk = NULL;
2115 tPtr->tpos = 0;
2117 tPtr->parser = NULL;
2118 tPtr->writer = NULL;
2119 tPtr->funcs.createParagraph = createParagraph;
2120 tPtr->funcs.insertParagraph = insertParagraph;
2121 tPtr->funcs.createPChunk = createPChunk;
2122 tPtr->funcs.createTChunk = createTChunk;
2123 tPtr->funcs.insertChunk = insertChunk;
2125 tPtr->clicked.x = tPtr->clicked.y = -23;
2126 tPtr->cursor.x = tPtr->cursor.y = -23;
2128 tPtr->relief = WRSunken;
2129 tPtr->wrapping = wrWord;
2130 tPtr->editable = False;
2131 tPtr->cursorShown = False;
2132 tPtr->frozen = False;
2133 tPtr->focused = False;
2134 tPtr->pointerGrabbed = False;
2135 tPtr->buttonHeld = False;
2136 tPtr->ignoreNewLine = False;
2137 tPtr->waitingForSelection = False;
2138 tPtr->findingClickPoint = False;
2139 tPtr->foundClickPoint = False;
2140 tPtr->ownsSelection = False;
2141 tPtr->clheight = 0;
2142 tPtr->clwidth = 0;
2144 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2145 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2147 tPtr->view->delegate = &_TextViewDelegate;
2148 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
2149 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
2150 handleNonTextEvents, tPtr);
2151 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
2152 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
2153 handleTextEvents, tPtr);
2155 WMAddNotificationObserver(_notification, tPtr, "_lostOwnership", tPtr);
2157 WMSetTextMonoFont(tPtr, True);
2158 WMShowTextRuler(tPtr, False);
2159 WMSetTextHasHorizontalScroller(tPtr, False);
2160 WMSetTextHasVerticalScroller(tPtr, True);
2161 //printf("the sizeof chunk is %d\n", sizeof(Chunk));
2162 //printf("the sizeof para is %d\n", sizeof(Paragraph));
2163 //printf("the sizeof text is %d\n", sizeof(Text));
2164 return tPtr;
2167 //WMSetTextBullet()
2168 //WRetainPixmap(tPtr->dBulletPix);
2170 void
2171 WMRemoveTextParagraph(WMText *tPtr, int which)
2173 Paragraph *prior, *removed;
2174 if(!tPtr || which<0) return;
2176 WMSetTextCurrentParagraph(tPtr, which);
2177 removed = tPtr->currentPara;
2178 if(!removed) return;
2179 if(removed->chunks)printf("WMRemoveTextChunks\n");
2180 if(removed == tPtr->paragraphs || which==0) {
2181 tPtr->paragraphs = removed->next;
2182 } else {
2183 WMSetTextCurrentParagraph(tPtr, which-1);
2184 prior = tPtr->currentPara;
2185 if(!prior) return;
2186 prior->next = removed->next;
2188 wgdbFree(removed);
2189 // removeChunks
2190 removed = NULL;
2195 /* set what is known as the currentPara in the tPtr. */
2196 /* negative number means: "gib me last chunk" */
2197 void
2198 WMSetTextCurrentParagraph(WMText *tPtr, int current)
2200 Paragraph *tmp;
2201 int i=0;
2203 if(!tPtr || current<0) return;
2204 if(current == 0) {
2205 tPtr->currentPara = tPtr->paragraphs;
2206 return;
2208 tmp = tPtr->paragraphs;
2209 while(tmp->next && ((current==-23)?1:i++<current)) {
2210 //while(tmp && i++<current) {
2211 tmp = tmp->next;
2212 } tPtr->currentPara = tmp;
2213 //? want to do this?if(tmp) tPtr->currentChunk = tmp
2217 int
2218 WMGetTextParagraphs(WMText *tPtr)
2220 int current=0;
2221 Paragraph *tmp;
2222 if(!tPtr) return 0;
2223 tmp = tPtr->paragraphs;
2224 while(tmp) {
2225 tmp = tmp->next;
2226 current++;
2227 } return current;
2232 int
2233 WMGetTextCurrentParagraph(WMText *tPtr)
2235 int current=-1;
2236 Paragraph *tmp;
2238 if(!tPtr) return current;
2239 if(!tPtr->currentPara) return current;
2240 if(!tPtr->paragraphs) return current;
2241 tmp = tPtr->paragraphs;
2242 while(tmp) {
2243 current++;
2244 if(tmp == tPtr->currentPara)
2245 break;
2246 tmp = tmp->next;
2247 } return current;
2250 /* set what is known as the currentChunk within the currently
2251 selected currentPara (or the first paragraph in the document). */
2252 void
2253 WMSetTextCurrentChunk(WMText *tPtr, int current)
2255 Chunk *tmp;
2256 int i=0;
2258 if(!tPtr) return;
2259 tPtr->currentChunk = NULL;
2260 if(!tPtr->currentPara) {
2261 tPtr->currentPara = tPtr->paragraphs;
2262 if(!tPtr->currentPara)
2263 return;
2266 if(current == 0) {
2267 tPtr->currentChunk = tPtr->currentPara->chunks;
2268 return;
2270 tmp = tPtr->currentPara->chunks;
2271 if(tmp) {
2272 while(tmp->next && ((current<0)?1:i++<current))
2273 tmp = tmp->next;
2274 } tPtr->currentChunk = tmp;
2278 void
2279 WMRemoveTextChunk(WMText *tPtr, int which)
2281 Chunk *prior, *removed;
2282 Paragraph *para;
2283 if(!tPtr || which<0) return;
2284 para = tPtr->currentPara;
2285 if(!para) return;
2287 WMSetTextCurrentChunk(tPtr, which);
2288 removed = tPtr->currentChunk;
2289 if(!removed) return;
2290 if(removed == tPtr->currentPara->chunks || which==0) {
2291 para->chunks = removed->next;
2292 } else {
2293 WMSetTextCurrentChunk(tPtr, which-1);
2294 prior = tPtr->currentChunk;
2295 if(!prior) return;
2296 prior->next = removed->next;
2298 if(removed->type == ctText) {
2299 wgdbFree(removed->text);
2300 WMReleaseFont(removed->font);
2301 WMReleaseColor(removed->color);
2302 } else {
2303 WMReleasePixmap(removed->pixmap);
2305 wgdbFree(removed);
2306 removed = NULL;
2309 int
2310 WMGetTextCurrentChunk(WMText *tPtr)
2312 int current=0;
2313 Chunk *tmp;
2315 if(!tPtr) return 0;
2316 if(!tPtr->currentChunk) return 0;
2317 if(!tPtr->currentPara) {
2318 tPtr->currentPara = tPtr->paragraphs;
2319 if(!tPtr->currentPara)
2320 return 0;
2323 tmp = tPtr->currentPara->chunks;
2324 while(tmp) {
2325 if(tmp == tPtr->currentChunk)
2326 break;
2327 tmp = tmp->next;
2328 current++;
2330 return current;
2333 int
2334 WMGetTextChunks(WMText *tPtr)
2336 short current=0;
2337 Chunk *tmp;
2338 if(!tPtr || !tPtr->currentPara) return 0;
2339 tmp = tPtr->currentPara->chunks;
2340 while(tmp) {
2341 tmp = tmp->next;
2342 current++;
2343 } return current;
2346 void
2347 WMShowTextRuler(WMText *tPtr, Bool show)
2349 if(!tPtr) return;
2350 if(tPtr->monoFont) show = False;
2352 tPtr->rulerShown = show;
2353 if(show) WMMapWidget(tPtr->ruler);
2354 else WMUnmapWidget(tPtr->ruler);
2355 resizeText(tPtr->view->delegate, tPtr->view);
2358 Bool
2359 WMGetTextRulerShown(WMText *tPtr)
2361 if(!tPtr) return False;
2362 return tPtr->rulerShown;
2365 void
2366 WMSetTextRulerMargin(WMText *tPtr, char which, short pixels)
2368 if(!tPtr) return;
2369 if(tPtr->monoFont) return;
2370 WMSetRulerMargin(tPtr->ruler, which, pixels);
2371 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2374 short
2375 WMGetTextRulerMargin(WMText *tPtr, char which)
2377 if(!tPtr) return 0;
2378 if(tPtr->monoFont)
2379 return 0;
2380 return WMGetRulerMargin(tPtr->ruler, which);
2384 void
2385 WMShowTextRulerTabs(WMText *tPtr, Bool show)
2387 if(!tPtr) return;
2388 if(tPtr->monoFont) return;
2389 WMShowRulerTabs(tPtr->ruler, show);
2392 void
2393 WMSetTextMonoFont(WMText *tPtr, Bool mono)
2395 if(!tPtr) return;
2396 if(mono && tPtr->rulerShown)
2397 WMShowTextRuler(tPtr, False);
2399 tPtr->monoFont = mono;
2402 Bool
2403 WMGetTextMonoFont(WMText *tPtr)
2405 if(!tPtr) return True;
2406 return tPtr->monoFont;
2409 void
2410 WMForceTextFocus(WMText *tPtr)
2412 if(!tPtr) return;
2414 if(tPtr->clicked.x == -23 || tPtr->clicked.y == 23)
2415 cursorToTextPosition(tPtr, 100, 100); /* anyplace */
2416 else
2417 cursorToTextPosition(tPtr, tPtr->clicked.x, tPtr->clicked.y);
2421 void
2422 WMSetTextEditable(WMText *tPtr, Bool editable)
2424 if(!tPtr) return;
2425 tPtr->editable = editable;
2429 Bool
2430 WMGetTextEditable(WMText *tPtr)
2432 if(!tPtr) return 0;
2433 return tPtr->editable;
2437 Bool
2438 WMScrollText(WMText *tPtr, int amount)
2440 Bool scroll=False;
2441 if(amount == 0 || !tPtr) return;
2442 if(!tPtr->view->flags.realized) return;
2444 if(amount < 0) {
2445 if(tPtr->vpos > 0) {
2446 if(tPtr->vpos > amount) tPtr->vpos += amount;
2447 else tPtr->vpos=0;
2448 scroll=True;
2449 } } else {
2450 int limit = tPtr->docHeight - tPtr->visibleH;
2451 if(tPtr->vpos < limit) {
2452 if(tPtr->vpos < limit-amount) tPtr->vpos += amount;
2453 else tPtr->vpos = limit;
2454 scroll = True;
2455 } }
2457 if(scroll && tPtr->vpos != tPtr->prevVpos) {
2458 updateScrollers(tPtr);
2459 drawDocumentPartsOnPixmap(tPtr, False);
2460 paintText(tPtr);
2462 tPtr->prevVpos = tPtr->vpos;
2463 return scroll;
2466 Bool
2467 WMPageText(WMText *tPtr, Bool scrollUp)
2469 if(!tPtr) return;
2470 if(!tPtr->view->flags.realized) return;
2472 return WMScrollText(tPtr, scrollUp
2473 ? tPtr->visibleH:-tPtr->visibleH);
2476 void
2477 WMIgnoreTextNewline(WMText *tPtr, Bool ignore)
2479 if(!tPtr) return;
2480 tPtr->ignoreNewLine = ignore;
2484 void
2485 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool flag)
2487 if(tPtr) {
2488 short rh;
2489 if(tPtr->monoFont)
2490 return;
2491 rh = tPtr->rulerShown?40:0;
2492 tPtr->hasHscroller = flag;
2493 if(flag) {
2494 WMMapWidget(tPtr->hscroller);
2495 tPtr->visibleH = tPtr->view->size.height-rh-22;
2496 } else {
2497 WMUnmapWidget(tPtr->hscroller);
2498 tPtr->visibleH = tPtr->view->size.height-rh;
2500 resizeText(tPtr->view->delegate, tPtr->view);
2505 void
2506 WMSetTextHasVerticalScroller(WMText *tPtr, Bool flag)
2508 if(tPtr) {
2509 tPtr->hasVscroller = flag;
2510 if(flag) {
2511 WMMapWidget(tPtr->vscroller);
2512 tPtr->visibleW = tPtr->view->size.width-22;
2513 WMSetRulerOffset(tPtr->ruler, 22); /* scrollbar width + 2 */
2514 } else {
2515 WMUnmapWidget(tPtr->vscroller);
2516 tPtr->visibleW = tPtr->view->size.width;
2517 WMSetRulerOffset(tPtr->ruler, 2);
2519 resizeText(tPtr->view->delegate, tPtr->view);
2525 void
2526 WMRefreshText(WMText *tPtr, int vpos, int hpos)
2529 if(!tPtr)
2530 return;
2532 if(tPtr->frozen || !tPtr->view->flags.realized)
2533 return;
2536 XClearArea(tPtr->view->screen->display, tPtr->view->window,
2537 22, (tPtr->rulerShown)?45:5,
2538 tPtr->visibleW, tPtr->visibleH, True);
2540 calcDocExtents(tPtr);
2542 printf("vpos:%d tPtr->docHeight%d tPtr->visibleH%d \n",
2543 vpos, tPtr->docHeight, tPtr->visibleH);
2546 // tPtr->vpos = vpos;
2548 if(vpos < 0 || tPtr->docHeight < tPtr->visibleH)
2549 tPtr->vpos = 0;
2550 else if(vpos-tPtr->visibleH>tPtr->docHeight)
2551 tPtr->vpos = vpos-tPtr->docHeight-tPtr->visibleH-tPtr->docHeight;
2552 else
2553 tPtr->vpos = tPtr->docHeight-tPtr->visibleH;
2557 if(hpos < 0 || hpos > tPtr->docWidth)
2558 tPtr->hpos = 0;
2559 else
2560 tPtr->hpos = hpos;
2562 drawDocumentPartsOnPixmap(tPtr, True);
2563 updateScrollers(tPtr);
2564 paintText(tPtr);
2567 /* would be nice to have in WINGs proper... */
2568 static void
2569 changeFontProp(char *fname, char *newprop, short which)
2571 char before[128], prop[128], after[128];
2572 char *ptr, *bptr;
2573 int part=0;
2575 if(!fname || !prop)
2576 return;
2578 ptr = fname;
2579 bptr = before;
2580 while (*ptr) {
2581 if(*ptr == '-') {
2582 *bptr = 0;
2583 if(part==which) bptr = prop;
2584 else if(part==which+1) bptr = after;
2585 *bptr++ = *ptr;
2586 part++;
2587 } else {
2588 *bptr++ = *ptr;
2589 } ptr++;
2590 }*bptr = 0;
2591 snprintf(fname, 255, "%s-%s%s", before, newprop, after);
2594 /* TODO: put in wfont? */
2595 WMFont *
2596 WMGetFontPlain(WMScreen *scrPtr, WMFont *font)
2598 WMFont *nfont=NULL;
2599 if(!scrPtr|| !font)
2600 return NULL;
2601 return font;
2605 WMFont *
2606 WMGetFontBold(WMScreen *scrPtr, WMFont *font)
2608 WMFont *newfont=NULL;
2609 char fname[256];
2610 if(!scrPtr || !font)
2611 return NULL;
2612 snprintf(fname, 255, font->name);
2613 changeFontProp(fname, "bold", 2);
2614 newfont = WMCreateNormalFont(scrPtr, fname);
2615 if(!newfont)
2616 newfont = font;
2617 return newfont;
2620 WMFont *
2621 WMGetFontItalic(WMScreen *scrPtr, WMFont *font)
2623 WMFont *newfont=NULL;
2624 char fname[256];
2625 if(!scrPtr || !font)
2626 return NULL;
2627 snprintf(fname, 255, font->name);
2628 changeFontProp(fname, "o", 3);
2629 newfont = WMCreateNormalFont(scrPtr, fname);
2630 if(!newfont)
2631 newfont = font;
2632 return newfont;
2635 WMFont *
2636 WMGetFontOfSize(WMScreen *scrPtr, WMFont *font, short size)
2638 WMFont *nfont=NULL;
2639 if(!scrPtr || !font || size<1)
2640 return NULL;
2641 return font;
2643 /* */
2645 void
2646 WMFreezeText(WMText *tPtr)
2648 if(!tPtr)
2649 return;
2650 tPtr->frozen = True;
2653 void
2654 WMThawText(WMText *tPtr)
2656 if(!tPtr)
2657 return;
2658 tPtr->frozen = False;
2662 void
2663 WMSetTextDefaultAlignment(WMText *tPtr, WMAlignment alignment)
2665 if(!tPtr) return;
2666 if(tPtr->monoFont) return;
2668 tPtr->dAlignment = alignment;
2669 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2674 void
2675 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
2677 if(!tPtr)
2678 return;
2680 if(color)
2681 tPtr->bg = color;
2682 else
2683 tPtr->bg = WMWhiteColor(tPtr->view->screen);
2685 W_SetViewBackgroundColor(tPtr->view, tPtr->bg);
2686 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2689 void
2690 WMSetTextDefaultColor(WMText *tPtr, WMColor *color)
2692 if(!tPtr)
2693 return;
2695 if(color)
2696 tPtr->dColor = color;
2697 else
2698 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2701 void
2702 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
2704 if(!tPtr)
2705 return;
2707 if(font)
2708 tPtr->dFont = font;
2709 else
2710 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2713 void
2714 WMSetTextUseFixedPitchFont(Text *tPtr, Bool fixed)
2716 if(!tPtr)
2717 return;
2718 if(fixed)
2719 tPtr->dFont = WMCreateFontSet(tPtr->view->screen,
2720 "lucidasanstypewriter-12");
2721 else
2722 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2723 tPtr->fixedPitch = fixed;
2726 void
2727 WMSetTextParser(WMText *tPtr, WMParseAction *parser)
2729 if(!tPtr) return;
2730 if(tPtr->monoFont) return;
2731 tPtr->parser = parser;
2735 WMParserActions
2736 WMGetTextParserActions(WMText *tPtr)
2738 WMParserActions null;
2739 if(!tPtr) return null;
2740 return tPtr->funcs;
2744 char *
2745 WMGetTextAll(WMText *tPtr)
2747 char *text;
2748 int length=0;
2749 int where=0;
2750 Paragraph *para;
2751 Chunk *chunk;
2753 if(!tPtr) return NULL;
2755 para = tPtr->paragraphs;
2756 while(para) {
2757 chunk = para->chunks;
2758 while(chunk) {
2759 if(chunk->type == ctText) {
2760 if(chunk->text) length += chunk->chars;
2761 } else {
2762 printf("getting image \n");
2764 chunk = chunk->next;
2767 if(tPtr->ignoreNewLine) break;
2768 length += 4; // newlines
2769 para = para->next;
2772 text = wmalloc(length+1);
2774 para = tPtr->paragraphs;
2775 while(para) {
2776 chunk = para->chunks;
2777 while(chunk) {
2778 if(chunk->type == ctText) {
2779 if(chunk->text) {
2780 snprintf(&text[where], chunk->chars+1, "%s", chunk->text);
2781 where += chunk->chars;
2783 } else {
2784 printf("writing image \n");
2786 chunk = chunk->next;
2788 if(tPtr->ignoreNewLine) break;
2789 snprintf(&text[where++], 2, "\n");
2790 para = para->next;
2791 } text[where] = '\0';
2793 return text;
2796 void
2797 WMSetTextWriter(WMText *tPtr, WMParseAction *writer)
2799 if(!tPtr)
2800 return;
2801 if(tPtr->monoFont)
2802 return;
2803 tPtr->writer = writer;