- added WMGetLabelText()
[wmaker-crm.git] / WINGs / wtext.c
blob431da534b7b082d3ef77d3ed5dbf76de4a903166
1 /* WMText: multi-line/font/color text widget for WINGs */
2 /* Copyleft (>) 1999, 2000 Nwanua Elumeze <nwanua@colorado.edu> */
5 /* .( * .
6 .* . ) .
8 . . POOF .* .
9 '* . ( .) '
10 jgs ` ( . * */
13 /* if monoFont, ignore pixmaps, colors, fonts, script, underline */
16 #include <WMaker.h>
17 #include <WINGsP.h>
18 #include <X11/keysym.h>
19 #include <X11/Xatom.h>
20 #include <ctype.h>
23 typedef enum {
24 ctText = 0,
25 ctImage = 1
26 } ChunkType;
28 typedef enum {
29 dtDelete = 0,
30 dtBackSpace
31 } DeleteType;
33 typedef enum {
34 wrWord = 0,
35 wrChar = 1,
36 wrNone = 2
37 } Wrapping;
40 void wgdbFree(void *ptr)
42 if (!ptr)
43 printf("err... cannot ");
44 printf("gdbFree [%p]\n", ptr);
45 wfree(ptr);
49 /* Why single-linked and not say double-linked?
50 99% of the time (draw, append), the "prior"
51 member would have been a useless memory and CPU overhead,
52 and deletes _are_ relatively infrequent.
53 When the "prior" member needs to be used, the overhead of
54 doing things the hard way will be incurred... but seldomly. */
57 /* a Chunk is a single-linked list of chunks containing:
58 * o text with a given format
59 * o or an image
60 * o but NOT both
62 typedef struct _Chunk {
63 char *text; /* the text in the chunk */
64 WMPixmap *pixmap; /* OR the pixmap it holds */
65 short chars; /* the number of characters in this chunk */
66 short mallocedSize; /* the number of characters that can be held */
68 WMFont *font; /* the chunk's font */
69 WMColor *color; /* the chunk's color */
70 short ul:1; /* underlined or not */
71 ChunkType type:1; /* a "Text" or "Image" chunk */
72 short script:4; /* script in points: negative for subscript */
73 /* hrmm selec... */
74 ushort selected;
75 ushort sStart;
76 ushort sEnd;
77 ushort RESERVED:10;
78 struct _Chunk *next; /*the next member in this list */
80 } Chunk;
84 /* a Paragraph is a singly-linked list of paragraphs containing:
85 * o a list of chunks in that paragraph
86 * o the formats for that paragraph
87 * o its (draw) position relative to the entire document
89 typedef struct _Paragraph {
90 Chunk *chunks; /* the list of text and/or image chunks */
91 short fmargin; /* the start position of the first line */
92 short bmargin; /* the start positions of the rest of the lines */
93 short rmargin; /* the end position of the entire paragraph */
94 short numTabs; /* the number of tabstops */
95 short *tabstops; /* an array of tabstops */
97 Pixmap drawbuffer; /* the pixmap onto which the (entire)
98 paragraph will be drawn */
99 WMPixmap *bulletPix; /* the pixmap to use for bulleting */
100 int top; /* the top of the paragraph relative to document */
101 int bottom; /* the bottom of the paragraph relative to document */
102 int width; /* the width of the paragraph */
103 int height; /* the height of the paragraph */
104 WMAlignment align:2; /* justification of this paragraph */
105 ushort RESERVED:14;
107 struct _Paragraph *next; /* the next member in this list */
108 } Paragraph;
111 static char *default_bullet[] =
113 "6 6 4 1",
114 " c None s None", ". c black",
115 "X c white", "o c #808080",
116 " ... ",
117 ".XX.. ",
118 ".XX..o",
119 ".....o",
120 " ...oo",
121 " ooo "
124 /* this is really a shrunk down version of the original
125 "broken" icon... I did not draw it, I simply shrunk it */
126 static char *unk_xpm[] =
128 "24 24 17 1",
129 " c None", ". c #0B080C", "+ c #13A015", "@ c #5151B8",
130 "# c #992719", "$ c #5B1C20", "% c #1DF51D", "& c #D1500D", "* c #2F304A",
131 "= c #0C6A0C", "- c #F2F1DE", "; c #D59131", "> c #B2B083", ", c #DD731A",
132 "' c #CC3113", ") c #828238", "! c #6A6A94",
133 "......!@@@@@@@....$$....",
134 "...@!@@@@@@@**...$#'....",
135 "..!!@@@@@@@@.......#....",
136 "..!@@@@@@@@@*.......$...",
137 ".!@@@#,,#*@@*..*>.*.#...",
138 "*@@@@#'',,@@@...---!....",
139 "!@@@@@*.#;*@@..!--->....",
140 "@@@@@@@@#,.@@..!----@...",
141 "!@@@@@@*#;'$...!----@...",
142 "*@@@@@@..'&;;#.)----)...",
143 ".@@@@@@..$..&'.>----)...",
144 ".@@@@@@**---,'>-----!...",
145 ".@@@@@@**---,'>-----@...",
146 "..@@@@@@@---;;;,;---....",
147 "..*@@@@*@--->#',;,-*.)..",
148 "........)---->)@;#!..>..",
149 ".....)----------;$..>)..",
150 "=%%%*.*!-------);..)-*..",
151 "=%%%%+...*)>!@*$,.>--...",
152 "*+++++++.......*$@-->...",
153 "............**@)!)>->...",
154 "........................",
155 "........................",
156 "........................"
159 typedef struct W_Text {
160 W_Class widgetClass; /* the class number of this widget */
161 W_View *view; /* the view referring to this instance */
162 WMColor *bg; /* the background color to use when drawing */
164 WMRuler *ruler; /* the ruler subwiget to maipulate paragraphs */
166 WMScroller *hscroller; /* the horizontal scroller */
167 short hpos; /* the current horizontal position */
168 short prevHpos; /* the previous horizontal position */
170 WMScroller *vscroller; /* the vertical scroller */
171 int vpos; /* the current vertical position */
172 int prevVpos; /* the previous vertical position */
174 int visibleW; /* the actual horizontal space available */
175 int visibleH; /* the actual vertical space available */
177 Paragraph *paragraphs; /* the linked list of the paragraphs in the doc. */
178 int docWidth; /* the width of the entire document */
179 int docHeight; /* the height of the entire document */
181 WMFont *dFont; /* the default font */
182 WMColor *dColor; /* the default color */
183 WMPixmap *dBulletPix; /* the default pixmap for bullets */
184 WMPixmap *dUnknownImg; /* the pixmap for (missing/broken) images */
186 WMRect sRect; /* the selected area */
187 Paragraph *currentPara; /* the current paragraph, in which actions occur */
188 Chunk *currentChunk; /* the current chunk, about which actions occur */
189 short tpos; /* the cursor position (text position) */
190 WMParseAction *parser; /* what action to use to parse input text */
191 WMParseAction *writer; /* what action to use to write text */
192 WMParserActions funcs; /* the "things" that parsers/writers might do */
193 XPoint clicked; /* the position of the last mouse click */
194 XPoint cursor; /* where the cursor is "placed" */
195 short clheight; /* the height of the "line" clicked on */
196 short clwidth; /* the width of the "line" clicked on */
198 WMReliefType relief:3; /* the relief to display with */
199 Wrapping wrapping:2; /* the type of wrapping to use in drawing */
200 WMAlignment dAlignment:2; /* default justification */
201 ushort monoFont:1; /* whether to ignore "rich" commands */
202 ushort fixedPitch:1; /* assume each char in dFont is the same size */
203 ushort editable:1; /* whether to accept user changes or not */
204 ushort rulerShown:1; /* whether the ruler is shown or not */
205 ushort cursorShown:1; /* whether the cursor is currently being shown */
206 ushort frozen:1; /* whether screen updates are to be made */
207 ushort focused:1; /* whether this instance has input focus */
208 ushort pointerGrabbed:1; /* whether this instance has the pointer */
209 ushort buttonHeld:1; /* the user is still holding down the button */
210 ushort ignoreNewLine:1; /* whether to ignore the newline character */
211 ushort waitingForSelection:1; /* whether there is a pending paste event */
212 ushort ownsSelection:1; /* whether it ownz the current selection */
213 ushort findingClickPoint:1; /* whether the search for a clickpoint is on */
214 ushort foundClickPoint:1; /* whether the clickpoint has been found */
215 ushort hasVscroller:1; /* whether to enable the vertical scroller */
216 ushort hasHscroller:1; /* whether to enable the horizontal scroller */
217 ushort RESERVED:10;
218 } Text;
222 /* --------- static functions that are "private". don't touch :-) --------- */
225 /* max "characters per chunk that will be drawn at a time" */
226 #define MAX_WORD_LENGTH 100
227 /* max on a line */
228 #define MAX_CHUNX 64
229 #define MIN_DOC_WIDTH 200
231 typedef struct _LocalMargins {
232 short left, right, first, body;
233 } LocalMargins;
235 typedef struct _MyTextItems {
236 char text[MAX_WORD_LENGTH + 1];
237 WMPixmap *pix;
238 short chars;
239 short x;
240 Chunk *chunk; /* used for "click" events */
241 short start; /* ditto... where in the chunk we start (ie. wrapped chunk) */
242 ushort type:1;
243 ushort RESERVED:15;
244 } MyTextItems;
247 static WMRect
248 chunkSelectionRect(Text * tPtr, Paragraph * para, MyTextItems item,
249 short y, short j, short lh)
251 WMRect rect;
252 short type = 0; /* 0:none 1:partial: 2:all */
253 short rh = (tPtr->rulerShown) ? 45 : 5;
254 short w, lm;
255 WMFont *font = (tPtr->monoFont || item.chunk->type != ctText) ?
256 tPtr->dFont : item.chunk->font;
258 rect.pos.x = -23;
259 if (y + para->top + rh > tPtr->sRect.pos.y + tPtr->sRect.size.height
260 || y + para->top + rh + lh < tPtr->sRect.pos.y)
261 return rect;
263 if (item.chunk->type == ctText)
264 w = WMWidthOfString(font, item.text, item.chars);
265 else
266 w = item.chunk->pixmap->width;
268 if (y + para->top + rh >= tPtr->sRect.pos.y && (y + para->top + rh + lh
269 <= tPtr->sRect.pos.y + tPtr->sRect.size.height))
270 //&& item.x+j >= tPtr->sRect.pos.x+tPtr->sRect.size.width))
271 type = 2;
272 else
273 type = 1;
276 #if 0
277 if (item.x + j >= tPtr->sRect.pos.x &&
278 item.x + j + w < tPtr->sRect.pos.x + tPtr->sRect.size.width)
279 type = 2;
281 if (type == 1 && y + para->top + rh + lh <=
282 tPtr->sRect.pos.y + tPtr->sRect.size.height)
283 type = 2;
284 #endif
287 if (type == 1 && item.chunk->type == ctText) { /* partial coverage */
288 lm = 2 + WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
289 /* even I am still confused, so don't ask please */
290 if ((item.x + j + lm >= tPtr->sRect.pos.x &&
291 item.x + j + lm <= tPtr->sRect.pos.x + tPtr->sRect.size.width)
292 || (item.x + j + lm >= tPtr->sRect.pos.x + tPtr->sRect.size.width
293 && y + para->top + rh + lh <=
294 tPtr->sRect.pos.y + tPtr->sRect.size.height)
295 || (tPtr->sRect.pos.y < y + para->top + rh
296 && tPtr->sRect.pos.x + tPtr->sRect.size.width >
297 item.x + j + lm)) {
298 rect.size.width = w;
299 rect.pos.x = item.x + j;
300 item.chunk->selected = True;
301 if (item.chunk->chars > 6) {
302 item.chunk->sStart = 3;
303 item.chunk->sEnd = item.chunk->chars;
304 } else {
305 item.chunk->sStart = 0;
306 item.chunk->sEnd = item.chunk->chars;
309 } else if (type == 2) {
310 rect.pos.x = item.x + j;
311 item.chunk->selected = True;
312 if (item.chunk->type == ctText) {
313 item.chunk->sStart = 0;
314 item.chunk->sStart = item.chunk->chars;
315 rect.size.width = WMWidthOfString(font,
316 item.text, item.chars);
317 } else {
318 rect.size.width = item.chunk->pixmap->width;
321 rect.pos.y = y;
322 rect.size.height = lh;
323 return rect;
326 static int myDrawText(Text * tPtr, Paragraph * para, MyTextItems * items,
327 short nitems, short pwidth, int y, short draw, short spacepos)
329 short i, ul_thick, u, j = 0; /* j = justification */
330 short line_width = 0, line_height = 0, mx_descent = 0;
331 WMScreen *screen = tPtr->view->screen;
332 GC gc;
333 WMFont *font;
335 if (tPtr->findingClickPoint && tPtr->foundClickPoint)
336 return 0;
337 for (i = 0; i < nitems; i++) {
338 if (items[i].type == ctText) {
339 font = (tPtr->monoFont) ? tPtr->dFont : items[i].chunk->font;
340 mx_descent = WMIN(mx_descent, -font->y);
341 line_height = WMAX(line_height, font->height);
342 //printf("chunk.x %d xpoint.x %d\n",
343 // items[i].x, tPtr->clicked.x);
345 line_width += WMWidthOfString(font,
346 items[i].text, items[i].chars);
347 } else {
348 mx_descent = WMIN(mx_descent, -(items[i].pix->height - 3));
349 /* replace -3 wif descent... */
350 line_height = WMAX(line_height, items[i].pix->height);
351 if (para->align == WARight || para->align == WACenter) {
352 line_width += items[i].pix->width;
357 if (para->align == WARight) {
358 j = pwidth - line_width;
359 } else if (para->align == WACenter) {
360 j = (short) ((float) (pwidth - line_width)) / 2.0;
362 if (tPtr->findingClickPoint && (y + line_height >= tPtr->clicked.y)) {
363 tPtr->foundClickPoint = True;
364 tPtr->currentChunk = items[0].chunk; /* just first on this "line" */
365 tPtr->tpos = items[0].start; /* where to "start" counting from */
366 tPtr->clicked.x = j + items[0].x;
367 tPtr->clicked.y = y + line_height + mx_descent;
368 tPtr->clheight = line_height; /* to draw the cursor */
369 tPtr->clwidth = line_width; /* where to stop searching */
370 return 0;
372 if (!draw)
373 return line_height;
375 for (i = 0; i < nitems; i++) {
377 //account for vpos
378 if (tPtr->ownsSelection) {
379 WMRect rect = chunkSelectionRect(tPtr, para,
380 items[i], y, j, line_height);
382 if (rect.pos.x != -23) { /* has been selected */
383 XFillRectangle(tPtr->view->screen->display, para->drawbuffer,
384 WMColorGC(WMGrayColor(tPtr->view->screen)),
385 rect.pos.x, rect.pos.y, rect.size.width, rect.size.height);
388 if (items[i].type == ctText) {
389 gc = WMColorGC(items[i].chunk->color);
390 font = (tPtr->monoFont) ? tPtr->dFont : items[i].chunk->font;
391 WMDrawString(screen, para->drawbuffer, gc, font,
392 items[i].x + j, y - font->y - mx_descent,
393 items[i].text, items[i].chars);
394 if (items[i].chunk->ul && !tPtr->monoFont) {
395 ul_thick = (short) ((float) font->height) / 12.0;
396 if (ul_thick < 1)
397 ul_thick = 1;
398 for (u = 0; u < ul_thick; u++) {
399 XDrawLine(screen->display, para->drawbuffer, gc, items[i].x + j,
400 y + 1 + u - mx_descent,
401 items[i].x + j + WMWidthOfString(font,
402 items[i].text, items[i].chars), y + 1 + u - mx_descent);
406 } else {
407 WMDrawPixmap(items[i].pix, para->drawbuffer, items[i].x + j,
408 y + 3 - mx_descent - items[i].pix->height);
411 return line_height;
414 static void drawPChunkPart(Text * tPtr, Chunk * chunk, LocalMargins m, Paragraph * para,
415 MyTextItems * items, short *nitems, short *Lmargin, XPoint * where, short draw)
417 short p_width;
419 if (!chunk)
420 return;
421 if (!chunk->pixmap)
422 chunk->pixmap = WMRetainPixmap(tPtr->dUnknownImg);
424 p_width = m.right - WMIN(m.first, m.body) - WMGetRulerOffset(tPtr->ruler);
425 if (p_width < MIN_DOC_WIDTH) // need WMRuler to take care of this...
427 return;
428 if (where->x + chunk->pixmap->width <= p_width - *Lmargin) {
429 /* it can fit on rest of line */
430 items[*nitems].pix = chunk->pixmap;
431 items[*nitems].type = ctImage;
432 items[*nitems].chars = 0;
433 items[*nitems].x = *Lmargin + where->x;
434 items[*nitems].chunk = chunk;
435 items[*nitems].start = 0;
437 if (*nitems >= MAX_CHUNX) {
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 if (tPtr->findingClickPoint && tPtr->foundClickPoint)
445 return;
446 *nitems = 0;
447 where->x = 0;
448 } else {
449 (*nitems)++;
450 where->x += chunk->pixmap->width;
452 } else if (chunk->pixmap->width <= p_width - *Lmargin) {
453 /* it can fit on an entire line, flush the myDrawText then wrap it */
454 where->y += myDrawText(tPtr, para, items, *nitems + 1,
455 p_width - *Lmargin, where->y, draw, 0);
456 *nitems = 0;
457 *Lmargin = WMAX(0, m.body - m.first);
458 where->x = 0;
459 drawPChunkPart(tPtr, chunk, m, para, items, nitems,
460 Lmargin, where, draw);
461 } else {
462 #if 1
463 *nitems = 0;
464 where->x = 0;
465 *Lmargin = WMAX(0, m.body - m.first);
466 items[*nitems].pix = chunk->pixmap;
467 items[*nitems].type = ctImage;
468 items[*nitems].chars = 0;
469 items[*nitems].x = *Lmargin + where->x;
470 items[*nitems].chunk = chunk;
471 items[*nitems].start = 0;
472 where->y += myDrawText(tPtr, para, items, *nitems + 1,
473 p_width - *Lmargin, where->y, draw, 0);
474 #endif
476 /* scale image to fit, call self again */
477 /* deprecated - the management */
482 static void drawTChunkPart(Text * tPtr, Chunk * chunk, char *bufr, LocalMargins m,
483 Paragraph * para, MyTextItems * items, short *nitems, short len, short start,
484 short *Lmargin, XPoint * where, short draw, short spacepos)
486 short t_chunk_width, p_width, chars;
487 WMFont *font = (tPtr->monoFont) ? tPtr->dFont : chunk->font;
489 /* if(doc->clickstart.yes && doc->clickstart.done) return; */
491 if (len == 0)
492 return;
493 p_width = m.right - WMIN(m.first, m.body);
494 if (p_width < MIN_DOC_WIDTH) // need WMRuler to take care of this...
496 return;
499 t_chunk_width = WMWidthOfString(font, bufr, len);
500 if ((where->x + t_chunk_width <= p_width - *Lmargin)
501 || (tPtr->wrapping == wrNone)) {
502 /* if it can fit on rest of line, append to line */
503 chars = WMIN(len, MAX_WORD_LENGTH);
504 snprintf(items[*nitems].text, chars + 1, "%s", bufr);
505 items[*nitems].chars = chars;
506 items[*nitems].x = *Lmargin + where->x;
507 items[*nitems].type = ctText;
508 items[*nitems].chunk = chunk;
509 items[*nitems].start = start;
511 if (*nitems >= MAX_CHUNX) {
512 chars = WMIN(len, MAX_WORD_LENGTH);
513 snprintf(items[*nitems].text, chars + 1, "%s", bufr);
514 items[*nitems].chars = chars;
515 items[*nitems].x = *Lmargin + where->x;
516 items[*nitems].type = ctText;
517 items[*nitems].chunk = chunk;
518 items[*nitems].start = start;
519 where->y += myDrawText(tPtr, para, items, *nitems + 1,
520 p_width - *Lmargin, where->y, draw, spacepos);
521 if (tPtr->findingClickPoint && tPtr->foundClickPoint)
522 return;
523 *nitems = 0;
524 where->x = 0;
525 } else {
526 (*nitems)++;
527 where->x += t_chunk_width;
529 } else if (t_chunk_width <= p_width - *Lmargin) {
530 /* it can fit on an entire line, flush and wrap it to a new line */
531 where->y += myDrawText(tPtr, para, items, *nitems,
532 p_width - *Lmargin, where->y, draw, spacepos);
533 if (tPtr->findingClickPoint && tPtr->foundClickPoint)
534 return;
535 *nitems = 0;
536 *Lmargin = WMAX(0, m.body - m.first);
537 where->x = 0;
538 drawTChunkPart(tPtr, chunk, bufr, m, para, items, nitems,
539 len, start, Lmargin, where, draw, spacepos);
540 } else {
541 /* otherwise, chop line, call ourself recursively until it's all gone */
542 short J = 0; /* bufr */
543 short j = 0; /* local tmp buffer */
544 char tmp[len];
545 short diff = p_width - *Lmargin - where->x;
546 short _start = 0;
548 if (diff < 20) {
549 where->y += myDrawText(tPtr, para, items, *nitems,
550 p_width - *Lmargin, where->y, draw, spacepos);
551 if (tPtr->findingClickPoint && tPtr->foundClickPoint)
552 return;
553 *nitems = 0;
554 *Lmargin = WMAX(0, m.body - m.first);
555 where->x = 0;
556 diff = p_width - *Lmargin - where->x;
558 for (J = 0; J < len; J++) {
559 tmp[j] = bufr[J];
560 if (WMWidthOfString(font, tmp, j + 1) > diff) {
561 drawTChunkPart(tPtr, chunk, tmp, m, para, items, nitems,
562 j, start + _start, Lmargin, where, draw, spacepos);
563 _start = J;
564 J--;
565 j = 0;
566 } else
567 j++;
569 /* and there's always that last chunk, get it too */
570 drawTChunkPart(tPtr, chunk, tmp, m, para, items, nitems,
571 j, start + _start, Lmargin, where, draw, spacepos);
575 /* this function does what it's called :-)
576 o It is also used for calculating extents of para,
577 (returns height) so watch out for (Bool) draw
578 o Also used to determine where mouse was clicked */
579 static int putParagraphOnPixmap(Text * tPtr, Paragraph * para, Bool draw)
581 char bufr[MAX_WORD_LENGTH + 1]; /* a single word + '\0' */
582 MyTextItems items[MAX_CHUNX + 1];
583 short lmargin, spacepos, i, s, nitems, start;
584 LocalMargins m;
585 XPoint where;
586 Chunk *chunk;
588 if (!tPtr->view->flags.realized || !para)
589 return 0;
591 where.x = 0, where.y = 0, nitems = 0;
592 m.left = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
593 m.right = WMGetRulerMargin(tPtr->ruler, WRulerRight) - m.left;
594 m.first = para->fmargin, m.body = para->bmargin;
596 if (draw) {
597 W_Screen *screen = tPtr->view->screen;
599 if (para->drawbuffer)
600 XFreePixmap(screen->display, para->drawbuffer);
601 if (para->width < 2 * tPtr->dFont->height)
602 para->width = 2 * tPtr->dFont->height;
603 if (para->height < tPtr->dFont->height)
604 para->height = tPtr->dFont->height;
605 para->drawbuffer = XCreatePixmap(screen->display,
606 tPtr->view->window, para->width, para->height, screen->depth);
607 XFillRectangle(screen->display, para->drawbuffer,
608 WMColorGC(tPtr->bg), 0, 0, para->width, para->height);
611 //if(para->align != tPtr->dAlignment)
612 // para->align = tPtr->dAlignment;
614 /* draw the bullet if appropriate */
615 if (m.body > m.first && !tPtr->monoFont) {
616 lmargin = m.body - m.first;
617 if (draw) {
618 if (para->bulletPix)
619 WMDrawPixmap(para->bulletPix, para->drawbuffer, lmargin - 10, 5);
620 else
621 WMDrawPixmap(tPtr->dBulletPix, para->drawbuffer, lmargin - 10, 5);
623 /* NeXT sez next tab, I say the m.body - m.first margin */
624 } else {
625 lmargin = WMAX(0, m.first - m.body);
628 if (tPtr->findingClickPoint && !para->chunks) {
629 tPtr->currentChunk = NULL;
630 tPtr->foundClickPoint = True;
631 tPtr->tpos = 0;
632 tPtr->clicked.x = lmargin;
633 tPtr->clicked.y = 5;
634 tPtr->clheight = para->height;
635 tPtr->clwidth = 0;
636 return 0;
638 chunk = para->chunks;
639 while (chunk) {
641 if (tPtr->findingClickPoint && tPtr->foundClickPoint)
642 return 0;
644 if (chunk->type == ctImage && !tPtr->monoFont) {
645 drawPChunkPart(tPtr, chunk, m, para, items, &nitems,
646 &lmargin, &where, draw);
647 } else if (chunk->text && chunk->type == ctText) {
648 if (tPtr->wrapping == wrNone) {
649 drawTChunkPart(tPtr, chunk, chunk->text, m, para, items, &nitems,
650 chunk->chars, 0, &lmargin, &where, draw, spacepos);
651 } else if (tPtr->wrapping == wrWord) {
652 spacepos = 0, i = 0, start = 0;
653 while (spacepos < chunk->chars) {
654 bufr[i] = chunk->text[spacepos];
655 if (bufr[i] == ' ' || i >= MAX_WORD_LENGTH) {
656 if (bufr[i] == ' ')
657 s = 1;
658 else
659 s = 0;
660 drawTChunkPart(tPtr, chunk, bufr, m, para,
661 items, &nitems, i + s, start, &lmargin, &where,
662 draw, spacepos);
663 start = spacepos + s;
664 if (i > MAX_WORD_LENGTH - 1)
665 spacepos--;
666 i = 0;
667 } else
668 i++;
669 spacepos++;
671 /* catch that last onery one. */
672 drawTChunkPart(tPtr, chunk, bufr, m, para,
673 items, &nitems, i, start, &lmargin, &where, draw, spacepos);
676 chunk = chunk->next;
678 /* we might have a few leftover items that need drawing */
679 if (nitems > 0) {
680 where.y += myDrawText(tPtr, para, items,
681 nitems, m.right - m.left - lmargin, where.y, draw, spacepos);
682 if (tPtr->findingClickPoint && tPtr->foundClickPoint)
683 return 0;
685 return where.y;
688 static int calcParaExtents(Text * tPtr, Paragraph * para)
690 if (!para)
691 return 0;
693 if (tPtr->monoFont) {
694 para->width = tPtr->visibleW;
695 para->fmargin = 0;
696 para->bmargin = 0;
697 para->rmargin = tPtr->visibleW;
698 } else {
699 para->width = WMGetRulerMargin(tPtr->ruler, WRulerRight) -
700 WMIN(para->fmargin, para->bmargin) -
701 WMGetRulerOffset(tPtr->ruler);
704 if (!para->chunks)
705 para->height = tPtr->dFont->height;
706 else
707 para->height = putParagraphOnPixmap(tPtr, para, False);
709 if (para->height < tPtr->dFont->height)
710 para->height = tPtr->dFont->height;
711 para->bottom = para->top + para->height;
712 return para->height;
716 /* rather than bother with redrawing _all_ the pixmaps, simply
717 rearrange (i.e., push down or pull up) paragraphs after this one */
718 static void affectNextParas(Text * tPtr, Paragraph * para, int move_y)
720 Paragraph *next;
721 int old_y = 0;
723 if (!para || move_y == 0)
724 return;
725 if (move_y == -23) {
726 old_y = para->bottom;
727 calcParaExtents(tPtr, para);
728 old_y -= para->bottom;
729 if (old_y == 0)
730 return;
731 move_y = -old_y;
733 if (move_y == 0)
734 return;
736 next = para->next;
737 while (next) {
738 next->top += move_y;
739 next->bottom = next->top + next->height;
740 next = next->next; // I know, I know
743 tPtr->docHeight += move_y;
745 #if 0
746 tPtr->vpos += move_y;
747 if (tPtr->vpos < 0)
748 tPtr->vpos = 0;
749 if (tPtr->vpos > tPtr->docHeight - tPtr->visibleH)
750 tPtr->vpos = tPtr->docHeight - tPtr->visibleH;
751 #endif
756 static void calcDocExtents(Text * tPtr)
758 Paragraph *para;
760 if (tPtr->monoFont) {
761 tPtr->docWidth = tPtr->visibleW;
762 } else {
763 tPtr->docWidth = WMGetRulerMargins(tPtr->ruler, WRulerRight) -
764 WMGetRulerMargins(tPtr->ruler, WRulerDocLeft);
766 tPtr->docHeight = 0;
767 para = tPtr->paragraphs;
768 if (para) {
769 while (para) {
770 para->top = tPtr->docHeight;
771 tPtr->docHeight += calcParaExtents(tPtr, para);
772 para->bottom = tPtr->docHeight;
773 para = para->next;
775 } else { /* default to this if no paragraphs */
776 tPtr->docHeight = tPtr->dFont->height;
778 #if 0
779 if (tPtr->editable) /* add space at bottom to enter new stuff */
780 tPtr->docHeight += tPtr->dFont->height;
781 #endif
785 /* If any part of a paragraph is viewable, the entire
786 paragraph is drawn on an otherwise empty (XFreePixmap) pixmap.
787 The actual viewable parts of the paragraph(s) are then pieced
788 together via paintText:
790 -------------------------------------------
791 || this is a paragraph in this document||
792 ||========================================||
793 || | only part of it is visible though. ||
794 || |-------------------------------------||
795 ||[.| This is another paragraph ||
796 || | which I'll make relatively long ||
797 || | just for the sake of writing a long ||
798 || | paragraph with a picture: ^_^ ||
799 || |-------------------------------------||
800 ||==| Of the three paragraphs, only ||
801 ||/\| the preceding was totally copied to ||
802 ||\/| totally copied to the window, even ||
803 ==========================================||
804 though they are all on pixmaps.
805 -------------------------------------------
806 This paragraph exists only in
807 memory and so has a NULL pixmap.
808 -------------------------------------------
811 simple, right? Performance: the best of both worlds...
812 o fast scrolling: no need to rewrite what's already
813 on the screen, simply XCopy it.
814 o fast typing: only change current para, then simply
815 affect other (i.e., subsequent) paragraphs.
816 o If no part of para is on screen, gdbFree pixmap; else draw on
817 individual pixmap per para then piece several paras together
818 o Keep track of who to XCopy to window (see paintText) */
819 static void drawDocumentPartsOnPixmap(Text * tPtr, Bool all)
821 Paragraph *para;
823 para = tPtr->paragraphs;
824 while (para) {
825 /* the 32 reduces jitter on the human eye by preparing paragraphs
826 in anticipation of when the _moving_ scrollbar reaches them */
827 if (para->bottom + 32 < tPtr->vpos ||
828 para->top > tPtr->visibleH + tPtr->vpos + 32) {
829 if (para->drawbuffer) {
830 XFreePixmap(tPtr->view->screen->display, para->drawbuffer);
831 para->drawbuffer = (Pixmap) NULL;
833 } else {
834 if (!para->drawbuffer || all)
835 putParagraphOnPixmap(tPtr, para, True);
837 para = para->next;
843 /* this function blindly copies the "visible" parts of a pragraph
844 unto the view, (top-down approach). It starts drawing from
845 the top of the view (which paragraph to draw is determined by
846 drawDocumentPartsOnPixmap); it stops at the bottom of the view. */
847 static void paintText(Text * tPtr)
849 short lmargin, para_lmargin;
850 int from = 5, to = 5, height;
851 Paragraph *para;
852 short vS = 0, hS = 0, rh = 0;
854 if (!tPtr->view->flags.realized)
855 return;
857 if (tPtr->rulerShown)
858 rh = 40;
859 to += rh;
861 if (tPtr->hasVscroller)
862 vS = 21;
863 if (tPtr->hasHscroller)
864 hS = 21;
866 //XClearWindow(tPtr->view->screen->display, tPtr->view->window);
868 lmargin = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
869 if (tPtr->paragraphs) {
870 para = tPtr->paragraphs;
871 while (para) {
872 if (para->drawbuffer) {
873 from = (para->top <= tPtr->vpos) ? tPtr->vpos - para->top : 0;
874 height = para->height - from;
875 if (from >= 0 && height > 0) {
876 para_lmargin = WMIN(para->fmargin, para->bmargin);
877 if (lmargin - vS < WMIN(para->fmargin, para->bmargin)) {
878 #if 0
879 XClearArea(tPtr->view->screen->display, tPtr->view->window,
880 lmargin, to, 2 + para_lmargin, height, False);
881 #else
882 XFillRectangle(tPtr->view->screen->display, tPtr->view->window,
883 WMColorGC(tPtr->dColor), lmargin, to, 2 + para_lmargin, height);
884 #endif
886 XCopyArea(tPtr->view->screen->display, para->drawbuffer,
887 tPtr->view->window, WMColorGC(tPtr->bg), 0, from,
888 para->width - 4, height, lmargin + para_lmargin + 2, to);
889 if ((to += height) > tPtr->visibleH + rh)
890 break;
893 para = para->next;
896 #if 0
897 /* clear any left over space (esp. during para deletes/ ruler changes) */
898 if (tPtr->docHeight < tPtr->visibleH && tPtr->visibleH + rh + 5 - to > 0) {
899 XClearArea(tPtr->view->screen->display, tPtr->view->window, vS, to,
900 tPtr->view->size.width - vS, tPtr->visibleH + rh + hS + 5 - to, False);
902 if (lmargin > vS)
903 XClearArea(tPtr->view->screen->display, tPtr->view->window,
904 vS + 1, rh + 5, lmargin - vS, tPtr->visibleH + rh + 5 - vS, False);
907 // from the "selection" days...
908 W_DrawRelief(tPtr->view->screen, WMWidgetXID(tPtr),
909 tPtr->sRect.pos.x, tPtr->sRect.pos.y,
910 tPtr->sRect.size.width, tPtr->sRect.size.height, tPtr->relief);
911 #endif
913 W_DrawRelief(tPtr->view->screen, WMWidgetXID(tPtr), 0, rh,
914 tPtr->visibleW + vS, tPtr->visibleH + hS, tPtr->relief);
916 if (tPtr->editable && tPtr->clheight > 0) {
917 int top = tPtr->cursor.y - tPtr->vpos;
918 int bot = top + tPtr->clheight;
920 if (bot > 5) {
921 if (top < 5)
922 top = 5;
923 if (bot > tPtr->visibleH + hS - 2)
924 bot = tPtr->visibleH + hS - 2;
925 if (bot - top > 1) {
926 //do something about italic text...
927 XDrawLine(tPtr->view->screen->display, tPtr->view->window,
928 WMColorGC(tPtr->dColor), lmargin + tPtr->cursor.x, top,
929 lmargin + tPtr->cursor.x, bot);
936 /* called anytime either the ruler, vscroller or hscroller is hidden/shown,
937 or when the widget is resized by some user action */
938 static void resizeText(W_ViewDelegate * self, WMView * view)
940 Text *tPtr = (Text *) view->self;
941 short rh = 0;
943 if (!tPtr->monoFont && tPtr->rulerShown)
944 rh = 40;
946 W_ResizeView(view, view->size.width, view->size.height);
947 WMResizeWidget(tPtr->ruler, view->size.width, 40);
950 if (tPtr->hasVscroller) {
951 WMMoveWidget(tPtr->vscroller, 1, 1 + rh);
952 WMResizeWidget(tPtr->vscroller, 20, view->size.height - rh - 2);
953 tPtr->visibleW = view->size.width - 21;
955 if (tPtr->hasHscroller) {
956 WMMoveWidget(tPtr->hscroller, 20, view->size.height - 21);
957 WMResizeWidget(tPtr->hscroller, view->size.width - 21, 20);
958 tPtr->visibleH = view->size.height - 21 - rh;
959 } else
960 tPtr->visibleH = view->size.height - rh;
961 } else {
962 tPtr->visibleW = view->size.width;
963 if (tPtr->hasHscroller) {
964 WMMoveWidget(tPtr->hscroller, 1, view->size.height - 21);
965 WMResizeWidget(tPtr->hscroller, view->size.width - 2, 20);
966 tPtr->visibleH = view->size.height - 21 - rh;
967 } else
968 tPtr->visibleH = view->size.width - 2 - rh;
970 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
973 W_ViewDelegate _TextViewDelegate =
975 NULL,
976 NULL,
977 resizeText,
978 NULL,
983 /* a plain text parser */
984 /* this gives useful hints on how to make a more
985 interesting parser for say HTML, RTF */
986 static void defaultParser(Text * tPtr, void *data, short type)
988 char *start, *mark, *text = (char *) data;
989 Chunk *chunk = NULL;
990 Paragraph *para = NULL;
992 start = text;
993 while (start) {
994 mark = strchr(start, '\n');
995 if (mark) {
996 /* there is a newline, indicating the need for a new paragraph */
997 /* attach the chunk to the current paragraph */
998 if ((short) (mark - start) > 1) {
999 /* ignore chunks with just a single newline but still make a
1000 blank paragraph */
1001 chunk = (tPtr->funcs.createTChunk) (start, (short) (mark - start),
1002 tPtr->dFont, tPtr->dColor, 0, False);
1003 (tPtr->funcs.insertChunk) (tPtr, chunk, type);
1005 /* _then_ create a new paragraph for the _next_ chunk */
1006 para = (tPtr->funcs.createParagraph) (0, 0, tPtr->visibleW,
1007 NULL, 0, WALeft);
1008 (tPtr->funcs.insertParagraph) (tPtr, para, type);
1009 start = mark + 1;
1010 } else {
1011 /* just attach the chunk to the current paragraph */
1012 if (strlen(start) > 0) {
1013 chunk = (tPtr->funcs.createTChunk) (start, strlen(start),
1014 tPtr->dFont, tPtr->dColor, 0, False);
1015 (tPtr->funcs.insertChunk) (tPtr, chunk, type);
1017 start = mark;
1023 static void updateScrollers(Text * tPtr)
1025 if (tPtr->hasVscroller) {
1026 if (tPtr->docHeight < tPtr->visibleH) {
1027 WMSetScrollerParameters(tPtr->vscroller, 0, 1);
1028 tPtr->vpos = 0;
1029 } else {
1030 float vmax = (float) (tPtr->docHeight);
1032 WMSetScrollerParameters(tPtr->vscroller,
1033 ((float) tPtr->vpos) / (vmax - (float) tPtr->visibleH),
1034 (float) tPtr->visibleH / vmax);
1037 if (tPtr->hasHscroller);
1040 static void scrollersCallBack(WMWidget * w, void *self)
1042 Text *tPtr = (Text *) self;
1043 Bool scroll = False;
1044 Bool dimple = False;
1046 if (!tPtr->view->flags.realized)
1047 return;
1049 if (w == tPtr->vscroller) {
1050 float vmax;
1051 int height;
1053 vmax = (float) (tPtr->docHeight);
1054 height = tPtr->visibleH;
1055 if (height > 7)
1056 height -= 7; /* the top border (5) + bottom (2) */
1058 switch (WMGetScrollerHitPart(tPtr->vscroller)) {
1059 case WSDecrementLine:
1060 if (tPtr->vpos > 0) {
1061 if (tPtr->vpos > 16)
1062 tPtr->vpos -= 16;
1063 else
1064 tPtr->vpos = 0;
1065 scroll = True;
1067 break;
1068 case WSIncrementLine:{
1069 int limit = tPtr->docHeight - height;
1071 if (tPtr->vpos < limit) {
1072 if (tPtr->vpos < limit - 16)
1073 tPtr->vpos += 16;
1074 else
1075 tPtr->vpos = limit;
1076 scroll = True;
1079 break;
1080 case WSDecrementPage:
1081 tPtr->vpos -= height;
1083 if (tPtr->vpos < 0)
1084 tPtr->vpos = 0;
1085 dimple = True;
1086 scroll = True;
1087 printf("dimple needs to jump to mouse location ;-/\n");
1088 break;
1089 case WSIncrementPage:
1090 tPtr->vpos += height;
1091 if (tPtr->vpos > (tPtr->docHeight - height))
1092 tPtr->vpos = tPtr->docHeight - height;
1093 dimple = True;
1094 scroll = True;
1095 printf("dimple needs to jump to mouse location ;-/\n");
1096 break;
1099 case WSKnob:
1100 tPtr->vpos = WMGetScrollerValue(tPtr->vscroller)
1101 * (float) (tPtr->docHeight - height);
1102 scroll = True;
1103 break;
1105 #if 0
1106 case WSKnobSlot:
1107 case WSNoPart:
1108 float vmax = (float) (tPtr->docHeight);
1110 ((float) tPtr->vpos) / (vmax - (float) tPtr->visibleH),
1111 (float) tPtr->visibleH / vmax);
1112 dimple = where mouse is.
1113 #endif
1114 break;
1116 scroll = (tPtr->vpos != tPtr->prevVpos);
1117 tPtr->prevVpos = tPtr->vpos;
1119 if (w == tPtr->hscroller);
1121 //need scrollv || scrollh
1122 if (scroll) {
1124 if(0&&dimple) {
1125 if(tPtr->rulerShown)
1126 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
1127 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
1128 else
1129 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
1130 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
1133 updateScrollers(tPtr);
1134 drawDocumentPartsOnPixmap(tPtr, False);
1135 paintText(tPtr);
1140 void
1141 W_InsertText(WMText * tPtr, void *data, Bool prepend) {
1142 if (!tPtr)
1143 return;
1144 if (!data) {
1145 Paragraph *para = tPtr->paragraphs, *ptmp;
1146 Chunk *chunk, *ctmp;
1147 WMFreezeText(tPtr);
1148 while (para) {
1149 chunk = para->chunks;
1150 while (chunk) {
1151 if (chunk->type == ctText && chunk->text)
1152 wgdbFree(chunk->text);
1153 else if (chunk->pixmap)
1154 WMReleasePixmap(chunk->pixmap);
1155 ctmp = chunk;
1156 chunk = chunk->next;
1157 wgdbFree(ctmp);
1158 } ptmp = para;
1160 para = para->next;
1161 if (ptmp->drawbuffer)
1162 XFreePixmap(tPtr->view->screen->display, ptmp->drawbuffer);
1163 wgdbFree(ptmp);
1165 tPtr->paragraphs = NULL;
1166 tPtr->currentPara = NULL;
1167 tPtr->currentChunk = NULL;
1168 WMThawText(tPtr);
1169 WMRefreshText(tPtr, 0, 0);
1170 return;
1172 if (tPtr->parser)
1173 (tPtr->parser) (tPtr, data, prepend);
1174 else
1175 defaultParser(tPtr, data, prepend);
1178 static void
1179 cursorToTextPosition(Text * tPtr, int x, int y) {
1180 Paragraph *para = NULL;
1181 Chunk *chunk = NULL;
1182 WMFont *font;
1183 short line_width = 0;
1184 short orig_x, orig_y;
1186 if (x < (tPtr->hasVscroller ? 21 : 1)) {
1187 y -= tPtr->clheight;
1188 x = tPtr->view->size.width; //tPtr->visibleW;
1190 } else if (x > tPtr->clwidth && x < tPtr->clicked.x) {
1191 //x = (tPtr->hasVscroller)?21:1;
1192 //y += tPtr->clheight;
1194 if (x < 0)
1195 x = 0;
1196 orig_x = x;
1198 if (y < 0 || y > tPtr->view->size.height - 3)
1199 return;
1200 orig_y = y;
1201 tPtr->clicked.x = orig_x;
1202 tPtr->clicked.y = y;
1203 tPtr->clicked.y += tPtr->vpos;
1204 tPtr->clicked.y -= tPtr->rulerShown ? 40 : 0;
1205 para = tPtr->paragraphs;
1206 if (!para)
1207 return;
1208 while (para->next) {
1209 if (tPtr->clicked.y >= para->top - 4 &&
1210 tPtr->clicked.y < para->bottom + 4)
1211 break;
1212 para = para->next;
1214 if (!(tPtr->currentPara = para))
1215 return;
1217 tPtr->clicked.y -= para->top;
1218 if (tPtr->clicked.y < 0)
1219 tPtr->clicked.y = 0;
1220 if (tPtr->hasVscroller)
1221 x -= 21;
1222 if (x < 0)
1223 x = 0;
1225 tPtr->findingClickPoint = True;
1226 tPtr->foundClickPoint = False;
1227 /* also affects tPtr->currentChunk, tPtr->clicked.x and y,
1228 tPtr->clheight and ->width */
1229 putParagraphOnPixmap(tPtr, para, False);
1230 tPtr->findingClickPoint = False;
1231 tPtr->clicked.y += para->top;
1233 if (tPtr->currentChunk) {
1234 short _width = 0, start = tPtr->tpos, done = False, w = 0;
1236 chunk = tPtr->currentChunk;
1237 while (!done && chunk && line_width < tPtr->clwidth) {
1238 if (chunk->type == ctText) {
1239 font = (tPtr->monoFont) ? tPtr->dFont : chunk->font;
1240 for (w = start; w < chunk->chars; w++) {
1241 _width = WMWidthOfString(font, &chunk->text[w], 1);
1242 line_width += _width;
1243 if (line_width + tPtr->clicked.x >= x) {
1244 line_width -= _width;
1245 done = True;
1246 printf("break\n");
1247 break;
1251 if (0 && chunk->next) {
1252 if (chunk->next->type == ctImage) {
1253 if (x + 10 < line_width + chunk->next->pixmap->width) {
1254 printf("true\n");
1255 done = True;
1259 } else {
1260 _width = chunk->pixmap->width;
1261 line_width += _width;
1262 if (line_width + tPtr->clicked.x >= x) {
1263 line_width -= _width;
1264 tPtr->tpos = 0;
1265 done = True;
1269 if (!done) {
1270 chunk = chunk->next;
1271 start = w = 0;
1272 } else {
1273 tPtr->tpos = w;
1274 tPtr->currentChunk = chunk;
1275 break;
1278 } else {
1279 short vS = (tPtr->hasVscroller) ? 32 : 12;
1281 if (para->align == WARight) {
1282 tPtr->clicked.x = tPtr->view->size.width - vS;
1283 } else if (para->align == WACenter) {
1284 tPtr->clicked.x = -(vS / 2) + (tPtr->view->size.width - vS) / 2;
1285 } else {
1286 tPtr->clicked.x = 2;
1290 tPtr->cursor.x = tPtr->clicked.x + 2 + line_width;
1291 tPtr->cursor.y = tPtr->clicked.y;
1292 tPtr->clicked.y = orig_y;
1293 tPtr->clicked.x = orig_x;
1294 putParagraphOnPixmap(tPtr, para, True);
1295 paintText(tPtr);
1298 static void
1299 deleteTextInteractively(Text * tPtr, DeleteType type) {
1300 Paragraph *para;
1301 Chunk *chunk;
1302 short pos, w = 0, h = 0, doprev = False, doprevpara = False;
1303 WMFont *font;
1304 int current = WMGetTextCurrentChunk(tPtr);
1306 if (!(para = tPtr->currentPara))
1307 return;
1308 if (!(chunk = tPtr->currentChunk))
1309 return;
1310 font = (tPtr->monoFont) ? tPtr->dFont : chunk->font;
1311 doprev = (tPtr->tpos < 2);
1313 switch (type) {
1314 case dtDelete: /* delete _after_ cursor ... implement later */
1315 case dtBackSpace: /* delete _before_ cursor */
1316 if (chunk->chars > 1) {
1317 pos = tPtr->tpos - 1;
1318 printf("here %d\n", pos);
1319 if (pos > 0) {
1320 w = WMWidthOfString(font, &chunk->text[pos], 1);
1321 memmove(&(chunk->text[pos]),
1322 &(chunk->text[pos + 1]), chunk->chars - pos + 1);
1323 tPtr->tpos--;
1324 chunk->chars--;
1326 } else {
1327 WMRemoveTextChunk(tPtr, current);
1328 doprev = True;
1331 if (doprev) {
1332 if (current > 0) {
1333 WMSetTextCurrentChunk(tPtr, current - 1);
1334 if (!tPtr->currentChunk) {
1335 printf("PREV PARA\n");
1336 } else {
1337 tPtr->tpos = tPtr->currentChunk->chars;
1339 } else if (0) {
1340 int currentp = WMGetTextCurrentParagraph(tPtr);
1342 doprevpara = True;
1343 if (currentp > 1) {
1344 para->chunks = NULL;
1345 WMRemoveTextParagraph(tPtr, currentp);
1346 WMSetTextCurrentParagraph(tPtr, currentp - 1);
1347 WMSetTextCurrentChunk(tPtr, -1);
1348 para = tPtr->currentPara;
1349 if (para) {
1350 if (!tPtr->currentChunk || !para->chunks) {
1351 para->chunks = chunk;
1352 tPtr->currentChunk = chunk;
1353 } else
1354 tPtr->currentChunk->next = chunk;
1361 if (1) { //if(1||(para && !doprevpara)) {
1363 affectNextParas(tPtr, para, -23);
1364 putParagraphOnPixmap(tPtr, para, True);
1365 drawDocumentPartsOnPixmap(tPtr, False);
1366 updateScrollers(tPtr);
1367 paintText(tPtr);
1368 //cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1369 } else
1370 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1374 /* give us nice chunk sizes (multiples of 16) */
1375 static short
1376 reqBlockSize(short requested) {
1377 return requested + 16 - (requested % 16);
1378 } static void
1379 insertTextInteractively(Text * tPtr, char *text) {
1380 Paragraph *para = NULL;
1381 Chunk *chunk = NULL, *newchunk = NULL;
1382 int height = -23; /* should only be changed upon newline */
1383 short w = 0, h = 0;
1384 WMFont *font;
1386 if (!tPtr->editable)
1387 return;
1388 if (*text == '\n' && tPtr->ignoreNewLine)
1389 return;
1391 para = tPtr->currentPara;
1392 chunk = tPtr->currentChunk;
1393 font = (tPtr->monoFont || !chunk) ? tPtr->dFont : chunk->font;
1395 if (*text == '\n') {
1396 int new_top = 0;
1397 if (chunk) { /* there's a chunk (or part of it) to detach from old */
1398 int current = WMGetTextCurrentChunk(tPtr);
1399 if (tPtr->tpos <= 0) { /* at start of chunk */
1400 if (current < 1) { /* the first chunk... make old para blank */
1401 newchunk = para->chunks;
1402 para->chunks = NULL;
1403 putParagraphOnPixmap(tPtr, para, True);
1404 } else { /* not first chunk... */
1405 printf("cut me out \n");
1407 } else if (tPtr->tpos < chunk->chars && chunk->type == ctText) {
1408 /* not at start of chunk */
1409 char text[chunk->chars - tPtr->tpos + 1];
1410 int i = 0;
1412 do {
1413 text[i] = chunk->text[tPtr->tpos + i];
1414 } while (++i < chunk->chars - tPtr->tpos);
1415 chunk->chars -= i;
1416 newchunk = (tPtr->funcs.createTChunk) (text, i, chunk->font,
1417 chunk->color, chunk->script, chunk->ul);
1418 newchunk->next = chunk->next;
1419 chunk->next = NULL;
1420 /* might want to demalloc for LARGE cuts */
1421 //calcParaExtents(tPtr, para);
1422 para->height = putParagraphOnPixmap(tPtr, para, True);
1423 //putParagraphOnPixmap(tPtr, para, True);
1424 } else if (tPtr->tpos >= chunk->chars) {
1425 Chunk *prev;
1427 WMSetTextCurrentChunk(tPtr, current - 1);
1428 prev = tPtr->currentChunk;
1429 if (!prev)
1430 return;
1431 newchunk = prev->next;
1432 prev->next = NULL;
1433 putParagraphOnPixmap(tPtr, para, True);
1435 } else
1436 newchunk = NULL;
1438 if (para) /* the preceeding one */
1439 new_top = para->bottom;
1441 WMAppendTextStream(tPtr, "\n");
1442 para = tPtr->currentPara;
1443 if (!para)
1444 return;
1445 para->chunks = newchunk;
1446 tPtr->currentChunk = newchunk;
1447 tPtr->tpos = 0;
1448 para->top = new_top;
1449 calcParaExtents(tPtr, para);
1450 height = para->height;
1451 } else {
1452 if (!para) {
1453 WMAppendTextStream(tPtr, text);
1454 para = tPtr->currentPara;
1455 } else if (!para->chunks || !chunk) {
1456 //WMPrependTextStream(tPtr, text);
1457 WMAppendTextStream(tPtr, text);
1458 } else if (chunk->type == ctImage) {
1459 WMPrependTextStream(tPtr, text);
1461 printf("\n\nprepe\n\n");
1462 } else {
1463 if (tPtr->tpos > chunk->chars) {
1464 printf("\n\nmore\n\n");
1465 tPtr->tpos = chunk->chars;
1467 if (chunk->chars + 1 >= chunk->mallocedSize) {
1468 chunk->mallocedSize = reqBlockSize(chunk->chars + 1);
1469 chunk->text = wrealloc(chunk->text, chunk->mallocedSize);
1471 memmove(&(chunk->text[tPtr->tpos + 1]), &chunk->text[tPtr->tpos],
1472 chunk->chars - tPtr->tpos + 1);
1473 w = WMWidthOfString(font, text, 1);
1474 memmove(&chunk->text[tPtr->tpos], text, 1);
1475 chunk->chars++;
1476 tPtr->tpos++;
1477 //doc->clickstart.cursor.x +=
1478 //WMWidthOfString(chunk->fmt->font, text,len);
1483 if (para) {
1484 affectNextParas(tPtr, para, height);
1485 putParagraphOnPixmap(tPtr, para, True);
1486 drawDocumentPartsOnPixmap(tPtr, False);
1487 updateScrollers(tPtr);
1488 paintText(tPtr);
1489 //cursorToTextPosition(tPtr, tPtr->clicked.x+w, tPtr->clicked.y);
1490 //check for "sneppah tahw" with blank paras...
1491 //paintText(tPtr);
1495 static void
1496 selectRegion(Text * tPtr, int x, int y) {
1497 tPtr->sRect.pos.x = WMIN(tPtr->clicked.x, x);
1498 tPtr->sRect.size.width = abs(tPtr->clicked.x - x);
1499 tPtr->sRect.pos.y = WMIN(tPtr->clicked.y, y);
1500 if (tPtr->sRect.pos.y < 0)
1501 tPtr->sRect.pos.y = 0;
1502 tPtr->sRect.size.height = abs(tPtr->clicked.y - y);
1505 while(y>tPtr->visibleH && tPtr->vpos < tPtr->docHeight-tPtr->visibleH) {
1506 WMRefreshText(tPtr, tPtr->vpos+16, tPtr->hpos);
1509 //printf("%d %d \n", y, tPtr->vpos);
1511 //foreach para in selection...
1512 drawDocumentPartsOnPixmap(tPtr, True);
1513 paintText(tPtr);
1515 #define WM_EMACSKEYMASK ControlMask
1516 #define WM_EMACSKEY_LEFT XK_b
1517 #define WM_EMACSKEY_RIGHT XK_f
1518 #define WM_EMACSKEY_HOME XK_a
1519 #define WM_EMACSKEY_END XK_e
1520 #define WM_EMACSKEY_BS XK_h
1521 #define WM_EMACSKEY_DEL XK_d static void
1522 handleTextKeyPress(Text * tPtr, XEvent * event) {
1523 char buffer[2];
1524 KeySym ksym;
1525 int control_pressed = False;
1527 if (!tPtr->editable)
1528 return;
1530 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
1531 control_pressed = True;
1532 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = '\0';
1534 switch (ksym) {
1536 case XK_Right:
1537 case XK_Left:
1538 if (tPtr->currentChunk) {
1539 short w;
1540 Chunk *chunk = tPtr->currentChunk;
1542 if (chunk->type == ctText) {
1543 WMFont *font = (tPtr->monoFont) ? tPtr->dFont : chunk->font;
1545 if (ksym == XK_Right) {
1546 short pos = (tPtr->tpos < chunk->chars) ? tPtr->tpos + 1 :
1547 chunk->chars;
1549 w = WMWidthOfString(font, &chunk->text[pos], 1);
1550 } else {
1551 short pos = (tPtr->tpos > 0) ? tPtr->tpos - 1 : 0;
1553 w = WMWidthOfString(font, &chunk->text[pos], 1);
1555 } else {
1556 w = chunk->pixmap->width;
1558 if (ksym == XK_Right)
1559 w = -w;
1560 cursorToTextPosition(tPtr, tPtr->clicked.x - w, tPtr->clicked.y);
1561 } else {
1562 if (ksym == XK_Right)
1563 ksym = XK_Down;
1564 else
1565 ksym = XK_Up;
1566 goto noCChunk;
1568 break;
1570 case XK_Down:
1571 case XK_Up:
1572 noCChunk:{
1573 short h = tPtr->clheight - 2;
1575 if (ksym == XK_Down)
1576 h = -h;
1577 cursorToTextPosition(tPtr, tPtr->clicked.x, tPtr->clicked.y - h);
1579 break;
1581 case XK_BackSpace:
1582 deleteTextInteractively(tPtr, dtBackSpace);
1583 break;
1585 case XK_Delete:
1586 case XK_KP_Delete:
1587 deleteTextInteractively(tPtr, dtDelete);
1588 break;
1590 case XK_Return:
1591 buffer[0] = '\n';
1592 default:
1593 if (buffer[0] != '\0' && (buffer[0] == '\n' || !iscntrl(buffer[0])))
1594 insertTextInteractively(tPtr, buffer);
1595 else if (control_pressed && ksym == XK_r) {
1596 Bool i = !tPtr->rulerShown;
1598 WMShowTextRuler(tPtr, i);
1599 tPtr->rulerShown = i;
1607 static void
1608 pasteText(WMView * view, Atom selection, Atom target, Time timestamp,
1609 void *cdata, WMData * data) {
1610 Text *tPtr = (Text *) view->self;
1611 char *str;
1614 tPtr->waitingForSelection = False;
1615 if (data) {
1616 str = (char *) WMDataBytes(data);
1617 if (tPtr->tpos < 1)
1618 WMPrependTextStream(tPtr, str);
1619 else
1620 WMAppendTextStream(tPtr, str);
1621 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1622 } else {
1623 int n;
1625 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1626 if (str) {
1627 str[n] = 0;
1628 if (tPtr->tpos < 1)
1629 WMPrependTextStream(tPtr, str);
1630 else
1631 WMAppendTextStream(tPtr, str);
1632 XFree(str);
1633 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1639 static void
1640 releaseSelection(Text * tPtr) {
1641 Paragraph *para = tPtr->paragraphs;
1642 Chunk *chunk;
1644 while (para) {
1645 chunk = para->chunks;
1646 while (chunk) {
1647 chunk->selected = False;
1648 chunk = chunk->next;
1650 para = para->next;
1652 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1653 tPtr->ownsSelection = False;
1654 drawDocumentPartsOnPixmap(tPtr, True);
1655 paintText(tPtr);
1659 static WMData *
1660 requestHandler(WMView * view, Atom selection, Atom target,
1661 void *cdata, Atom * type) {
1662 Text *tPtr = view->self;
1663 int count;
1664 Display *dpy = tPtr->view->screen->display;
1665 Atom _TARGETS;
1666 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1667 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1668 WMData *data = NULL;
1671 if (!tPtr->ownsSelection || !tPtr->paragraphs)
1672 return NULL;
1673 //printf("got here\n");
1675 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
1676 //for bleh in selection...
1677 char *s = NULL;
1678 Paragraph *para = tPtr->paragraphs;
1679 Chunk *chunk = NULL;
1680 char pixmap[] = "[pixmap]";
1681 Bool first = True;
1682 short len;
1684 while (para) {
1685 chunk = para->chunks;
1686 while (chunk) {
1688 if (chunk->selected && chunk->type == ctText) {
1689 len = chunk->chars; //chunk->sEnd - chunk->sStart;
1691 if (len > 0) {
1692 s = wmalloc(len + 1);
1693 if (s) {
1694 memcpy(s, &chunk->text[0 * chunk->sStart], len);
1695 s[len] = 0;
1696 if (first) {
1697 data = WMCreateDataWithBytes(s, strlen(s));
1698 first = False;
1699 } else {
1700 printf("append: %c %d\n", *s, strlen(s));
1701 WMAppendDataBytes(data, s, strlen(s));
1703 //gdbFree(s);
1707 #if 0
1708 printf("len is %d [%d %d] %d \n", len, chunk->sStart, chunk->sEnd,
1709 chunk->chars);
1710 #endif
1711 chunk = chunk->next;
1713 para = para->next;
1716 if (data) {
1717 WMSetDataFormat(data, 8);
1718 *type = target;
1720 return data;
1722 #if 0
1723 _TARGETS = XInternAtom(dpy, "TARGETS", False);
1724 if (target == _TARGETS) {
1725 Atom *ptr = wmalloc(4 * sizeof(Atom));
1727 ptr[0] = _TARGETS;
1728 ptr[1] = XA_STRING;
1729 ptr[2] = TEXT;
1730 ptr[3] = COMPOUND_TEXT;
1732 data = WMCreateDataWithBytes(ptr, 4 * 4);
1733 WMSetDataFormat(data, 32);
1735 *type = target;
1736 return data;
1738 #endif
1740 return NULL;
1745 static void
1746 lostHandler(WMView * view, Atom selection, void *cdata) {
1747 WMText *tPtr = (WMText *) view->self;
1748 releaseSelection(tPtr);
1749 } static WMSelectionProcs selectionHandler = {
1751 requestHandler, lostHandler, NULL
1754 static void
1755 _notification(void *observerData, WMNotification * notification) {
1756 WMText *to = (WMText *) observerData;
1757 WMText *tw = (WMText *) WMGetNotificationClientData(notification);
1758 if (to != tw)
1759 lostHandler(to->view, XA_PRIMARY, NULL);
1760 } static void
1761 handleTextEvents(XEvent * event, void *data) {
1762 Text *tPtr = (Text *) data;
1763 Display *dpy = event->xany.display;
1765 if (tPtr->waitingForSelection)
1766 return;
1768 switch (event->type) {
1769 case KeyPress:
1770 if (!tPtr->editable || tPtr->buttonHeld) {
1771 XBell(dpy, 0);
1772 return;
1774 if (tPtr->ownsSelection)
1775 releaseSelection(tPtr);
1777 //if (tPtr->waitingForSelection) return;
1778 if (tPtr->focused) {
1779 #if 0
1780 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1781 PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
1782 GrabModeAsync, GrabModeAsync, None,
1783 W_VIEW(tPtr)->screen->invisibleCursor, CurrentTime);
1784 tPtr->pointerGrabbed = True;
1785 #endif
1786 handleTextKeyPress(tPtr, event);
1788 break;
1790 case MotionNotify:
1791 if (tPtr->pointerGrabbed) {
1792 tPtr->pointerGrabbed = False;
1793 XUngrabPointer(dpy, CurrentTime);
1795 if ((event->xmotion.state & Button1Mask)) {
1796 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1797 if (!tPtr->ownsSelection) {
1798 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1799 event->xbutton.time, &selectionHandler, NULL);
1800 tPtr->ownsSelection = True;
1802 break;
1804 case ButtonPress:
1805 if (event->xbutton.button == Button1) {
1806 if (tPtr->ownsSelection)
1807 releaseSelection(tPtr);
1808 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1809 if (tPtr->pointerGrabbed) {
1810 tPtr->pointerGrabbed = False;
1811 XUngrabPointer(dpy, CurrentTime);
1812 break;
1815 if (!tPtr->focused) {
1816 WMSetFocusToWidget(tPtr);
1817 tPtr->focused = True;
1818 break;
1820 if (event->xbutton.button == 4)
1821 WMScrollText(tPtr, -16);
1822 else if (event->xbutton.button == 5)
1823 WMScrollText(tPtr, 16);
1825 break;
1827 case ButtonRelease:
1828 tPtr->buttonHeld = False;
1829 if (tPtr->pointerGrabbed) {
1830 tPtr->pointerGrabbed = False;
1831 XUngrabPointer(dpy, CurrentTime);
1832 break;
1834 if (event->xbutton.button == 4 || event->xbutton.button == 5)
1835 break;
1836 if (event->xbutton.button == Button2 && tPtr->editable) {
1837 char *text = NULL;
1838 int n;
1840 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1841 event->xbutton.time, pasteText, NULL)) {
1842 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1843 if (text) {
1844 text[n] = 0;
1845 WMAppendTextStream(tPtr, text);
1846 XFree(text);
1847 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1848 } else
1849 tPtr->waitingForSelection = True;
1852 break;
1859 static void
1860 handleNonTextEvents(XEvent * event, void *data) {
1861 Text *tPtr = (Text *) data;
1863 switch (event->type) {
1864 case Expose:
1865 if (!event->xexpose.count && tPtr->view->flags.realized)
1866 paintText(tPtr);
1867 break;
1869 case FocusIn:
1870 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view)) != tPtr->view)
1871 return;
1872 tPtr->focused = True;
1873 //cursor...paintText(tPtr);
1874 break;
1876 case FocusOut:
1877 tPtr->focused = False;
1878 //cursor...paintText(tPtr);
1879 break;
1881 case DestroyNotify:
1882 printf("destroy");
1883 //for(...)WMRemoveTextParagraph(tPtr, para);
1884 break;
1886 } //printf("handleNonTextEvents\n");
1887 } static void
1888 rulerCallBack(WMWidget * w, void *self) {
1889 Text *tPtr = (Text *) self;
1890 short which;
1892 if (tPtr->currentPara) {
1893 Paragraph *para = tPtr->currentPara;
1894 para->fmargin = WMGetRulerMargin(tPtr->ruler, WRulerFirst);
1895 para->bmargin = WMGetRulerMargin(tPtr->ruler, WRulerBody);
1896 para->rmargin = WMGetRulerMargin(tPtr->ruler, WRulerRight);
1897 affectNextParas(tPtr, para, -23);
1898 putParagraphOnPixmap(tPtr, para, True);
1900 #if 0
1901 which = WMGetReleasedRulerMargin(tPtr->ruler);
1903 if (which != WRulerDocLeft && which != WRulerRight
1904 /* && Selection.para.count > 0 */ ) {
1905 printf(""
1906 "//for(i=0; i<Selection.para.count; i++) {"
1907 "affect"
1908 "//calcParaExtents(tPtr, para);}\n");
1909 } else {
1910 WMRefreshText(tPtr, 0, 0);
1912 #endif
1916 static void
1917 rulerMoveCallBack(WMWidget * w, void *self) {
1918 Text *tPtr = (Text *) self;
1919 short rmargin = WMGetRulerMargin(tPtr->ruler, WRulerRight);
1922 if (WMGetGrabbedRulerMargin(tPtr->ruler) == WRulerLeft) {
1923 short lmargin = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft);
1924 XClearArea(tPtr->view->screen->display, tPtr->view->window,
1925 22, 42, lmargin - 21, tPtr->visibleH, True);
1926 } else if (WMGetGrabbedRulerMargin(tPtr->ruler) == WRulerRight &&
1927 tPtr->docWidth + 11 < rmargin) {
1928 XClearArea(tPtr->view->screen->display, tPtr->view->window,
1929 rmargin - 3, 42, 10, tPtr->visibleH, True);
1931 paintText(tPtr);
1936 /* ------------- non-static functions that are "friends" ------------- */
1937 /* ------------- called as (tPtr->funcs.foo)(bars...) ------------- */
1939 /* create a new paragraph. Don't do anything with it just yet */
1940 //Paragraph *
1941 void *
1942 createParagraph(short fmargin, short bmargin, short rmargin,
1943 short *tabstops, short numTabs, WMAlignment alignment) {
1944 Paragraph *para = wmalloc(sizeof(Paragraph));
1945 if (!para)
1946 return NULL;
1948 para->chunks = NULL;
1949 para->next = NULL;
1952 para->fmargin = (fmargin >= 0) ? fmargin : 0;
1953 para->bmargin = (bmargin >= 0) ? bmargin : 0;
1954 if (rmargin - bmargin >= 100 && rmargin - fmargin >= 100)
1955 para->rmargin = rmargin;
1956 else
1957 para->rmargin = 100;
1958 para->tabstops = tabstops;
1959 para->numTabs = (tabstops) ? numTabs : 0;
1961 para->drawbuffer = (Pixmap) NULL;
1962 para->bulletPix = NULL;
1963 para->top = para->bottom = 0;
1964 para->width = para->height = 0;
1966 para->align = alignment;
1968 return para;
1969 } /* insert the new paragraph in the tPtr, either right before
1970 or after the currentPara. It's the responsibility of the
1971 calling code to set what currentPara is. via WMSetTextCurrentParagraph.
1972 If currentPara is not set, set it as the first in the document.
1973 This function then sets currentPara as _this_ paragraph.
1974 NOTE: this means careless parser implementors might lose previous
1975 paragraphs... but this keeps stuff small and non-buggy :-) */ void
1976 insertParagraph(WMText * tPtr, void *v, Bool prepend)
1977 //insertParagraph(WMText *tPtr, Paragraph *para, InsertType type)
1979 Paragraph *tmp;
1980 Paragraph *para = (Paragraph *) v;
1981 if (!para || !tPtr)
1982 return;
1984 if (!tPtr->currentPara) {
1985 tPtr->paragraphs = para;
1986 } else {
1987 tmp = tPtr->paragraphs;
1988 if (!prepend) {
1989 while (tmp->next && tmp != tPtr->currentPara)
1990 tmp = tmp->next;
1992 para->next = tmp->next;
1993 tmp->next = para;
1994 } else { /* must be prepend */
1995 /* this "prior" member is that "doing things the hard way"
1996 I spoke of. See? it's not too bad afterall... */
1997 Paragraph *prior = NULL;
1999 while (tmp->next && tmp != tPtr->currentPara) {
2000 prior = tmp;
2001 tmp = tmp->next;
2003 /* if this is the first */
2004 if (tmp == tPtr->paragraphs) {
2005 para->next = tmp;
2006 tPtr->paragraphs = para;
2007 } else {
2008 prior->next = para;
2009 para->next = tmp;
2013 tPtr->currentPara = para;
2017 /* create a new chunk to contain exactly ONE pixmap */
2018 void *
2019 //Chunk *
2020 createPChunk(WMPixmap * pixmap, short script, ushort ul) {
2021 Chunk *chunk;
2023 chunk = wmalloc(sizeof(Chunk));
2024 if (!chunk)
2025 return NULL;
2027 chunk->text = NULL;
2028 if (!pixmap)
2029 chunk->pixmap = NULL; /* if it's NULL, we'll draw the "broken" pixmap... */
2030 else
2031 chunk->pixmap = WMRetainPixmap(pixmap);
2032 chunk->chars = 0;
2033 chunk->mallocedSize = 0;
2034 chunk->type = ctImage;
2035 chunk->font = NULL;
2036 chunk->color = NULL;
2037 chunk->script = script;
2038 chunk->ul = ul;
2039 chunk->selected = False;
2040 chunk->next = NULL;
2041 return chunk;
2042 } /* create a new chunk to contain some text with the given format */ void *
2043 //Chunk *
2044 createTChunk(char *text, short chars, WMFont * font,
2045 WMColor * color, short script, ushort ul) {
2046 Chunk *chunk;
2048 if (!text || chars < 0 || !font || !color)
2049 return NULL;
2050 chunk = wmalloc(sizeof(Chunk));
2051 if (!chunk)
2052 return NULL;
2054 chunk->mallocedSize = reqBlockSize(chars);
2055 chunk->text = (char *) wmalloc(chunk->mallocedSize);
2056 memcpy(chunk->text, text, chars);
2057 chunk->pixmap = NULL;
2058 chunk->chars = chars;
2059 chunk->type = ctText;
2060 chunk->font = WMRetainFont(font);
2061 chunk->color = WMRetainColor(color);
2062 chunk->script = script;
2063 chunk->ul = ul;
2064 chunk->selected = False;
2065 chunk->next = NULL;
2067 return chunk;
2068 } /* insert the new chunk in the paragraph, either right before
2069 or after the currentChunk. It's the responsibility of the
2070 calling code to set what currentChunk is via WMSetTextCurrentChunk.
2071 If currentChunk is not set, set it as the first in the existing
2072 paragraph... if not even that, you lose... try again.
2073 This function then sets currentChunk as _this_ chunk.
2074 NOTE: this means careless parser implementors might lose previous
2075 paragraphs/chunks... but this keeps stuff small and non-buggy :-) */ void
2076 insertChunk(WMText * tPtr, void *v, Bool prepend) {
2077 Chunk *tmp;
2078 Chunk *chunk = (Chunk *) v;
2080 if (!tPtr || !chunk)
2081 return;
2083 if (!tPtr->paragraphs) { /* i.e., first chunk via insertTextInteractively */
2084 Paragraph *para = (tPtr->funcs.createParagraph) (0, 0, tPtr->visibleW,
2085 NULL, 0, WALeft);
2086 (tPtr->funcs.insertParagraph) (tPtr, para, False);
2088 if (!tPtr->currentPara)
2089 return;
2091 if (!tPtr->currentChunk) { /* there is a current chunk */
2092 tPtr->currentPara->chunks = chunk;
2093 } else if (!tPtr->currentPara->chunks) {
2094 /* but it's not of this paragraph */
2095 tPtr->currentPara->chunks = chunk;
2096 } else {
2097 tmp = tPtr->currentPara->chunks;
2099 if (!prepend) {
2100 while (tmp->next && tmp != tPtr->currentChunk)
2101 tmp = tmp->next;
2103 chunk->next = tmp->next;
2104 tmp->next = chunk;
2106 } else { /* must be prepend */
2107 /* this "prior" member is that "doing things the hard way"
2108 I spoke of. See? it's not too bad afterall... */
2109 Chunk *prior = NULL;
2111 while (tmp->next && tmp != tPtr->currentChunk) {
2112 prior = tmp;
2113 tmp = tmp->next;
2115 /* if this is the first */
2116 if (tmp == tPtr->currentPara->chunks) {
2117 chunk->next = tmp;
2118 tPtr->currentPara->chunks = chunk;
2119 } else {
2120 prior->next = chunk;
2121 chunk->next = tmp;
2125 tPtr->currentChunk = chunk;
2126 tPtr->tpos = chunk->chars;
2130 /* ------------- non-static functions (i.e., APIs) ------------- */
2131 /* ------------- called as WMVerbText[Subject] ------------- */
2133 #define DEFAULT_TEXT_WIDTH 250
2134 #define DEFAULT_TEXT_HEIGHT 200
2138 WMText *
2139 WMCreateText(WMWidget * parent) {
2140 Text *tPtr = wmalloc(sizeof(Text));
2142 if (!tPtr) {
2143 perror("could not create text widget\n");
2144 return NULL;
2146 memset(tPtr, 0, sizeof(Text));
2147 tPtr->widgetClass = WC_Text;
2148 tPtr->view = W_CreateView(W_VIEW(parent));
2149 if (!tPtr->view) {
2150 perror("could not create text's view\n");
2151 wgdbFree(tPtr);
2152 return NULL;
2154 tPtr->view->self = tPtr;
2155 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
2156 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
2157 W_ResizeView(tPtr->view, DEFAULT_TEXT_WIDTH, DEFAULT_TEXT_HEIGHT);
2158 tPtr->bg = tPtr->view->screen->white;
2159 W_SetViewBackgroundColor(tPtr->view, tPtr->bg);
2162 tPtr->ruler = WMCreateRuler(tPtr);
2163 (W_VIEW(tPtr->ruler))->attribs.cursor = tPtr->view->screen->defaultCursor;
2164 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
2165 WMMoveWidget(tPtr->ruler, 0, 0);
2166 WMResizeWidget(tPtr->ruler, W_VIEW(parent)->size.width, 40);
2167 WMShowRulerTabs(tPtr->ruler, True);
2168 WMSetRulerAction(tPtr->ruler, rulerCallBack, tPtr);
2169 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
2171 tPtr->vpos = 0;
2172 tPtr->prevVpos = 0;
2173 tPtr->vscroller = WMCreateScroller(tPtr);
2174 (W_VIEW(tPtr->vscroller))->attribs.cursor =
2175 tPtr->view->screen->defaultCursor;
2176 (W_VIEW(tPtr->vscroller))->attribFlags |= CWOverrideRedirect | CWCursor;
2177 WMMoveWidget(tPtr->vscroller, 1, 1);
2178 WMResizeWidget(tPtr->vscroller, 20, tPtr->view->size.height - 2);
2179 WMSetScrollerArrowsPosition(tPtr->vscroller, WSAMaxEnd);
2180 WMSetScrollerAction(tPtr->vscroller, scrollersCallBack, tPtr);
2182 tPtr->hpos = 0;
2183 tPtr->prevHpos = 0;
2184 tPtr->hscroller = WMCreateScroller(tPtr);
2185 (W_VIEW(tPtr->hscroller))->attribs.cursor =
2186 tPtr->view->screen->defaultCursor;
2187 (W_VIEW(tPtr->hscroller))->attribFlags |= CWOverrideRedirect | CWCursor;
2188 WMMoveWidget(tPtr->hscroller, 1, tPtr->view->size.height - 21);
2189 WMResizeWidget(tPtr->hscroller, tPtr->view->size.width - 2, 20);
2190 WMSetScrollerArrowsPosition(tPtr->hscroller, WSAMaxEnd);
2191 WMSetScrollerAction(tPtr->hscroller, scrollersCallBack, tPtr);
2193 tPtr->visibleW = tPtr->view->size.width;
2194 tPtr->visibleH = tPtr->view->size.height;
2196 tPtr->paragraphs = NULL;
2197 tPtr->docWidth = 0;
2198 tPtr->docHeight = 0;
2199 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
2200 default_bullet);
2201 tPtr->dUnknownImg = WMCreatePixmapFromXPMData(tPtr->view->screen,
2202 unk_xpm);
2204 tPtr->sRect.pos.x = tPtr->sRect.pos.y = 0;
2205 tPtr->sRect.size.width = tPtr->sRect.size.height = 0;
2206 tPtr->currentPara = NULL;
2207 tPtr->currentChunk = NULL;
2208 tPtr->tpos = 0;
2210 tPtr->parser = NULL;
2211 tPtr->writer = NULL;
2212 tPtr->funcs.createParagraph = createParagraph;
2213 tPtr->funcs.insertParagraph = insertParagraph;
2214 tPtr->funcs.createPChunk = createPChunk;
2215 tPtr->funcs.createTChunk = createTChunk;
2216 tPtr->funcs.insertChunk = insertChunk;
2218 tPtr->clicked.x = tPtr->clicked.y = -23;
2219 tPtr->cursor.x = tPtr->cursor.y = -23;
2221 tPtr->relief = WRSunken;
2222 tPtr->wrapping = wrWord;
2223 tPtr->editable = False;
2224 tPtr->cursorShown = False;
2225 tPtr->frozen = False;
2226 tPtr->focused = False;
2227 tPtr->pointerGrabbed = False;
2228 tPtr->buttonHeld = False;
2229 tPtr->ignoreNewLine = False;
2230 tPtr->waitingForSelection = False;
2231 tPtr->findingClickPoint = False;
2232 tPtr->foundClickPoint = False;
2233 tPtr->ownsSelection = False;
2234 tPtr->clheight = 0;
2235 tPtr->clwidth = 0;
2237 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2238 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2240 tPtr->view->delegate = &_TextViewDelegate;
2241 WMCreateEventHandler(tPtr->view, ExposureMask | StructureNotifyMask
2242 | EnterWindowMask | LeaveWindowMask | FocusChangeMask,
2243 handleNonTextEvents, tPtr);
2244 WMCreateEventHandler(tPtr->view, ButtonReleaseMask | ButtonPressMask
2245 | KeyReleaseMask | KeyPressMask | Button1MotionMask,
2246 handleTextEvents, tPtr);
2248 WMAddNotificationObserver(_notification, tPtr, "_lostOwnership", tPtr);
2250 WMSetTextMonoFont(tPtr, True);
2251 WMShowTextRuler(tPtr, False);
2252 WMSetTextHasHorizontalScroller(tPtr, False);
2253 WMSetTextHasVerticalScroller(tPtr, True);
2254 //printf("the sizeof chunk is %d\n", sizeof(Chunk));
2255 //printf("the sizeof para is %d\n", sizeof(Paragraph));
2256 //printf("the sizeof text is %d\n", sizeof(Text));
2257 return tPtr;
2260 //WMSetTextBullet()
2261 //WRetainPixmap(tPtr->dBulletPix);
2263 void
2264 WMRemoveTextParagraph(WMText * tPtr, int which) {
2265 Paragraph *prior, *removed;
2266 if (!tPtr || which < 0)
2267 return;
2269 WMSetTextCurrentParagraph(tPtr, which);
2270 removed = tPtr->currentPara;
2271 if (!removed)
2272 return;
2273 if (removed->chunks)
2274 printf("WMRemoveTextChunks\n");
2275 if (removed == tPtr->paragraphs || which == 0) {
2276 tPtr->paragraphs = removed->next;
2277 } else {
2278 WMSetTextCurrentParagraph(tPtr, which - 1);
2279 prior = tPtr->currentPara;
2280 if (!prior)
2281 return;
2282 prior->next = removed->next;
2284 wgdbFree(removed);
2285 // removeChunks
2286 removed = NULL;
2291 /* set what is known as the currentPara in the tPtr. */
2292 /* negative number means: "gib me last chunk" */
2293 void
2294 WMSetTextCurrentParagraph(WMText * tPtr, int current) {
2295 Paragraph *tmp;
2296 int i = 0;
2298 if (!tPtr || current < 0)
2299 return;
2300 if (current == 0) {
2301 tPtr->currentPara = tPtr->paragraphs;
2302 return;
2304 tmp = tPtr->paragraphs;
2306 while (tmp->next && ((current == -23) ? 1 : i++ < current)) {
2307 //while(tmp && i++<current) {
2308 tmp = tmp->next;
2310 tPtr->currentPara = tmp;
2311 //? want to do this?if(tmp) tPtr->currentChunk = tmp
2316 WMGetTextParagraphs(WMText * tPtr) {
2317 int current = 0;
2318 Paragraph *tmp;
2320 if (!tPtr)
2321 return 0;
2322 tmp = tPtr->paragraphs;
2323 while (tmp) {
2324 tmp = tmp->next;
2325 current++;
2327 return current;
2333 WMGetTextCurrentParagraph(WMText * tPtr) {
2334 int current = -1;
2335 Paragraph *tmp;
2337 if (!tPtr)
2338 return current;
2339 if (!tPtr->currentPara)
2340 return current;
2341 if (!tPtr->paragraphs)
2342 return current;
2343 tmp = tPtr->paragraphs;
2344 while (tmp) {
2345 current++;
2346 if (tmp == tPtr->currentPara)
2347 break;
2348 tmp = tmp->next;
2350 return current;
2353 /* set what is known as the currentChunk within the currently
2354 selected currentPara (or the first paragraph in the document). */
2355 void
2356 WMSetTextCurrentChunk(WMText * tPtr, int current) {
2357 Chunk *tmp;
2358 int i = 0;
2360 if (!tPtr)
2361 return;
2362 tPtr->currentChunk = NULL;
2363 if (!tPtr->currentPara) {
2364 tPtr->currentPara = tPtr->paragraphs;
2365 if (!tPtr->currentPara)
2366 return;
2368 if (current == 0) {
2369 tPtr->currentChunk = tPtr->currentPara->chunks;
2370 return;
2372 tmp = tPtr->currentPara->chunks;
2373 if (tmp) {
2374 while (tmp->next && ((current < 0) ? 1 : i++ < current))
2375 tmp = tmp->next;
2377 tPtr->currentChunk = tmp;
2381 void
2382 WMRemoveTextChunk(WMText * tPtr, int which) {
2383 Chunk *prior, *removed;
2384 Paragraph *para;
2385 if (!tPtr || which < 0)
2386 return;
2387 para = tPtr->currentPara;
2388 if (!para)
2389 return;
2391 WMSetTextCurrentChunk(tPtr, which);
2392 removed = tPtr->currentChunk;
2393 if (!removed)
2394 return;
2395 if (removed == tPtr->currentPara->chunks || which == 0) {
2396 para->chunks = removed->next;
2397 } else {
2398 WMSetTextCurrentChunk(tPtr, which - 1);
2399 prior = tPtr->currentChunk;
2400 if (!prior)
2401 return;
2402 prior->next = removed->next;
2404 if (removed->type == ctText) {
2405 wgdbFree(removed->text);
2406 WMReleaseFont(removed->font);
2407 WMReleaseColor(removed->color);
2408 } else {
2409 WMReleasePixmap(removed->pixmap);
2411 wgdbFree(removed);
2412 removed = NULL;
2416 WMGetTextCurrentChunk(WMText * tPtr) {
2417 int current = 0;
2418 Chunk *tmp;
2420 if (!tPtr)
2421 return 0;
2422 if (!tPtr->currentChunk)
2423 return 0;
2424 if (!tPtr->currentPara) {
2425 tPtr->currentPara = tPtr->paragraphs;
2426 if (!tPtr->currentPara)
2427 return 0;
2429 tmp = tPtr->currentPara->chunks;
2430 while (tmp) {
2431 if (tmp == tPtr->currentChunk)
2432 break;
2433 tmp = tmp->next;
2434 current++;
2436 return current;
2440 WMGetTextChunks(WMText * tPtr) {
2441 short current = 0;
2442 Chunk *tmp;
2444 if (!tPtr || !tPtr->currentPara)
2445 return 0;
2446 tmp = tPtr->currentPara->chunks;
2447 while (tmp) {
2448 tmp = tmp->next;
2449 current++;
2451 return current;
2454 void
2455 WMShowTextRuler(WMText * tPtr, Bool show) {
2456 if (!tPtr)
2457 return;
2458 if (tPtr->monoFont)
2459 show = False;
2461 tPtr->rulerShown = show;
2462 if (show)
2463 WMMapWidget(tPtr->ruler);
2464 else
2465 WMUnmapWidget(tPtr->ruler);
2466 resizeText(tPtr->view->delegate, tPtr->view);
2469 Bool
2470 WMGetTextRulerShown(WMText * tPtr) {
2471 if (!tPtr)
2472 return False;
2473 return tPtr->rulerShown;
2476 void
2477 WMSetTextRulerMargin(WMText * tPtr, char which, short pixels) {
2478 if (!tPtr)
2479 return;
2480 if (tPtr->monoFont)
2481 return;
2482 WMSetRulerMargins(tPtr->ruler, which, pixels);
2483 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2484 } short
2485 WMGetTextRulerMargin(WMText * tPtr, char which) {
2486 if (!tPtr)
2487 return 0;
2488 if (tPtr->monoFont)
2489 return 0;
2490 return WMGetRulerMargins(tPtr->ruler, which);
2491 } void
2492 WMShowTextRulerTabs(WMText * tPtr, Bool show) {
2493 if (!tPtr)
2494 return;
2495 if (tPtr->monoFont)
2496 return;
2497 WMShowRulerTabs(tPtr->ruler, show);
2500 void
2501 WMSetTextMonoFont(WMText * tPtr, Bool mono) {
2502 if (!tPtr)
2503 return;
2504 if (mono && tPtr->rulerShown)
2505 WMShowTextRuler(tPtr, False);
2507 tPtr->monoFont = mono;
2510 Bool
2511 WMGetTextMonoFont(WMText * tPtr) {
2512 if (!tPtr)
2513 return True;
2514 return tPtr->monoFont;
2517 void
2518 WMForceTextFocus(WMText * tPtr) {
2519 if (!tPtr)
2520 return;
2522 if (tPtr->clicked.x == -23 || tPtr->clicked.y == 23)
2523 cursorToTextPosition(tPtr, 100, 100); /* anyplace */
2524 else
2525 cursorToTextPosition(tPtr, tPtr->clicked.x, tPtr->clicked.y);
2529 void
2530 WMSetTextEditable(WMText * tPtr, Bool editable) {
2531 if (!tPtr)
2532 return;
2533 tPtr->editable = editable;
2537 Bool
2538 WMGetTextEditable(WMText * tPtr) {
2539 if (!tPtr)
2540 return 0;
2541 return tPtr->editable;
2545 Bool
2546 WMScrollText(WMText * tPtr, int amount) {
2547 Bool scroll = False;
2549 if (amount == 0 || !tPtr)
2550 return;
2551 if (!tPtr->view->flags.realized)
2552 return;
2554 if (amount < 0) {
2555 if (tPtr->vpos > 0) {
2556 if (tPtr->vpos > amount)
2557 tPtr->vpos += amount;
2558 else
2559 tPtr->vpos = 0;
2560 scroll = True;
2562 } else {
2563 int limit = tPtr->docHeight - tPtr->visibleH;
2565 if (tPtr->vpos < limit) {
2566 if (tPtr->vpos < limit - amount)
2567 tPtr->vpos += amount;
2568 else
2569 tPtr->vpos = limit;
2570 scroll = True;
2574 if (scroll && tPtr->vpos != tPtr->prevVpos) {
2575 updateScrollers(tPtr);
2576 drawDocumentPartsOnPixmap(tPtr, False);
2577 paintText(tPtr);
2579 tPtr->prevVpos = tPtr->vpos;
2580 return scroll;
2583 Bool
2584 WMPageText(WMText * tPtr, Bool scrollUp) {
2585 if (!tPtr)
2586 return;
2587 if (!tPtr->view->flags.realized)
2588 return;
2590 return WMScrollText(tPtr, scrollUp
2591 ? tPtr->visibleH : -tPtr->visibleH);
2594 void
2595 WMIgnoreTextNewline(WMText * tPtr, Bool ignore) {
2596 if (!tPtr)
2597 return;
2598 tPtr->ignoreNewLine = ignore;
2602 void
2603 WMSetTextHasHorizontalScroller(WMText * tPtr, Bool flag) {
2604 if (tPtr) {
2605 short rh;
2607 if (tPtr->monoFont)
2608 return;
2609 rh = tPtr->rulerShown ? 40 : 0;
2610 tPtr->hasHscroller = flag;
2611 if (flag) {
2612 WMMapWidget(tPtr->hscroller);
2613 tPtr->visibleH = tPtr->view->size.height - rh - 22;
2614 } else {
2615 WMUnmapWidget(tPtr->hscroller);
2616 tPtr->visibleH = tPtr->view->size.height - rh;
2618 resizeText(tPtr->view->delegate, tPtr->view);
2623 void
2624 WMSetTextHasVerticalScroller(WMText * tPtr, Bool flag) {
2625 if (tPtr) {
2626 tPtr->hasVscroller = flag;
2627 if (flag) {
2628 WMMapWidget(tPtr->vscroller);
2629 tPtr->visibleW = tPtr->view->size.width - 22;
2630 WMSetRulerOffset(tPtr->ruler, 22); /* scrollbar width + 2 */
2631 } else {
2632 WMUnmapWidget(tPtr->vscroller);
2633 tPtr->visibleW = tPtr->view->size.width;
2634 WMSetRulerOffset(tPtr->ruler, 2);
2636 resizeText(tPtr->view->delegate, tPtr->view);
2642 void
2643 WMRefreshText(WMText * tPtr, int vpos, int hpos) {
2645 if (!tPtr)
2646 return;
2648 if (tPtr->frozen || !tPtr->view->flags.realized)
2649 return;
2652 XClearArea(tPtr->view->screen->display, tPtr->view->window,
2653 22, (tPtr->rulerShown) ? 45 : 5,
2654 tPtr->visibleW, tPtr->visibleH, True);
2656 calcDocExtents(tPtr);
2658 printf("vpos:%d tPtr->docHeight%d tPtr->visibleH%d \n",
2659 vpos, tPtr->docHeight, tPtr->visibleH);
2662 // tPtr->vpos = vpos;
2664 if(vpos < 0 || tPtr->docHeight < tPtr->visibleH)
2665 tPtr->vpos = 0;
2666 else if(vpos-tPtr->visibleH>tPtr->docHeight)
2667 tPtr->vpos = vpos-tPtr->docHeight-tPtr->visibleH-tPtr->docHeight;
2668 else
2669 tPtr->vpos = tPtr->docHeight-tPtr->visibleH;
2673 if (hpos < 0 || hpos > tPtr->docWidth)
2674 tPtr->hpos = 0;
2675 else
2676 tPtr->hpos = hpos;
2678 drawDocumentPartsOnPixmap(tPtr, True);
2679 updateScrollers(tPtr);
2680 paintText(tPtr);
2681 } /* would be nice to have in WINGs proper... */ static void
2682 changeFontProp(char *fname, char *newprop, short which) {
2683 char before[128], prop[128], after[128];
2684 char *ptr, *bptr;
2685 int part = 0;
2687 if (!fname || !prop)
2688 return;
2690 ptr = fname;
2691 bptr = before;
2692 while (*ptr) {
2693 if (*ptr == '-') {
2694 *bptr = 0;
2695 if (part == which)
2696 bptr = prop;
2697 else if (part == which + 1)
2698 bptr = after;
2699 *bptr++ = *ptr;
2700 part++;
2701 } else {
2702 *bptr++ = *ptr;
2704 ptr++;
2706 *bptr = 0;
2707 snprintf(fname, 255, "%s-%s%s", before, newprop, after);
2710 /* TODO: put in wfont? */
2711 WMFont *
2712 WMGetFontPlain(WMScreen * scrPtr, WMFont * font) {
2713 WMFont *nfont = NULL;
2715 if (!scrPtr || !font)
2716 return NULL;
2717 return font;
2721 WMFont *
2722 WMGetFontBold(WMScreen * scrPtr, WMFont * font) {
2723 WMFont *newfont = NULL;
2724 char fname[256];
2726 if (!scrPtr || !font)
2727 return NULL;
2728 snprintf(fname, 255, font->name);
2729 changeFontProp(fname, "bold", 2);
2730 newfont = WMCreateNormalFont(scrPtr, fname);
2731 if (!newfont)
2732 newfont = font;
2733 return newfont;
2736 WMFont *
2737 WMGetFontItalic(WMScreen * scrPtr, WMFont * font) {
2738 WMFont *newfont = NULL;
2739 char fname[256];
2741 if (!scrPtr || !font)
2742 return NULL;
2743 snprintf(fname, 255, font->name);
2744 changeFontProp(fname, "o", 3);
2745 newfont = WMCreateNormalFont(scrPtr, fname);
2746 if (!newfont)
2747 newfont = font;
2748 return newfont;
2751 WMFont *
2752 WMGetFontOfSize(WMScreen * scrPtr, WMFont * font, short size) {
2753 WMFont *nfont = NULL;
2755 if (!scrPtr || !font || size < 1)
2756 return NULL;
2757 return font;
2759 /* */
2761 void
2762 WMFreezeText(WMText * tPtr) {
2763 if (!tPtr)
2764 return;
2765 tPtr->frozen = True;
2768 void
2769 WMThawText(WMText * tPtr) {
2770 if (!tPtr)
2771 return;
2772 tPtr->frozen = False;
2776 void
2777 WMSetTextDefaultAlignment(WMText * tPtr, WMAlignment alignment) {
2778 if (!tPtr)
2779 return;
2780 if (tPtr->monoFont)
2781 return;
2783 tPtr->dAlignment = alignment;
2784 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2789 void
2790 WMSetTextBackgroundColor(WMText * tPtr, WMColor * color) {
2791 if (!tPtr)
2792 return;
2794 if (color)
2795 tPtr->bg = color;
2796 else
2797 tPtr->bg = WMWhiteColor(tPtr->view->screen);
2799 W_SetViewBackgroundColor(tPtr->view, tPtr->bg);
2800 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2803 void
2804 WMSetTextDefaultColor(WMText * tPtr, WMColor * color) {
2805 if (!tPtr)
2806 return;
2808 if (color)
2809 tPtr->dColor = color;
2810 else
2811 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2814 void
2815 WMSetTextDefaultFont(WMText * tPtr, WMFont * font) {
2816 if (!tPtr)
2817 return;
2819 if (font)
2820 tPtr->dFont = font;
2821 else
2822 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2825 void
2826 WMSetTextUseFixedPitchFont(Text * tPtr, Bool fixed) {
2827 if (!tPtr)
2828 return;
2829 if (fixed)
2830 tPtr->dFont = WMCreateFontSet(tPtr->view->screen,
2831 "lucidasanstypewriter-12");
2832 else
2833 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2834 tPtr->fixedPitch = fixed;
2837 void
2838 WMSetTextParser(WMText * tPtr, WMParseAction * parser) {
2839 if (!tPtr)
2840 return;
2841 if (tPtr->monoFont)
2842 return;
2843 tPtr->parser = parser;
2847 WMParserActions
2848 WMGetTextParserActions(WMText * tPtr) {
2849 WMParserActions null;
2851 if (!tPtr)
2852 return null;
2853 return tPtr->funcs;
2857 char *
2858 WMGetTextAll(WMText * tPtr) {
2859 char *text;
2860 int length = 0;
2861 int where = 0;
2862 Paragraph *para;
2863 Chunk *chunk;
2865 if (!tPtr)
2866 return NULL;
2868 para = tPtr->paragraphs;
2869 while (para) {
2870 chunk = para->chunks;
2871 while (chunk) {
2872 if (chunk->type == ctText) {
2873 if (chunk->text)
2874 length += chunk->chars;
2875 } else {
2876 printf("getting image \n");
2878 chunk = chunk->next;
2881 if (tPtr->ignoreNewLine)
2882 break;
2883 length += 4; // newlines
2885 para = para->next;
2888 text = wmalloc(length + 1);
2890 para = tPtr->paragraphs;
2891 while (para) {
2892 chunk = para->chunks;
2893 while (chunk) {
2894 if (chunk->type == ctText) {
2895 if (chunk->text) {
2896 snprintf(&text[where], chunk->chars + 1, "%s", chunk->text);
2897 where += chunk->chars;
2899 } else {
2900 printf("writing image \n");
2902 chunk = chunk->next;
2904 if (tPtr->ignoreNewLine)
2905 break;
2906 snprintf(&text[where++], 2, "\n");
2907 para = para->next;
2909 text[where] = '\0';
2911 return text;
2914 void
2915 WMSetTextWriter(WMText * tPtr, WMParseAction * writer) {
2916 if (!tPtr)
2917 return;
2918 if (tPtr->monoFont)
2919 return;
2920 tPtr->writer = writer;