i
[wmaker-crm.git] / WINGs / wtext.c
blob07cdd71baff4efab3c0a4ea5f1a65e04979986e2
1 /*
2 * WINGs WMText: multi-line/font/color/graphic text widget
4 * Copyright (c) 1999-2000 Nwanua Elumeze <nwanua@windowmaker.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "WINGsP.h"
23 #include <X11/keysym.h>
24 #include <X11/Xatom.h>
26 #define DO_BLINK 0
29 /* TODO:
31 * - FIX wrap... long lines that don't fit are not char wrapped yet.
32 * - FIX html parser: 1. <b>foo</i> should STILL BE BOLD!
33 * - 2. " foo > bar " should not confuse it.
34 * - change Array stuffs to WMArray
35 * - assess danger of destroying widgets whose actions link to other pages
36 * - integrate WMConvertFontTo* functions into WINGs proper (fontpanel)?
37 * - change cursor shape around pixmaps
38 * - redo blink code to reduce paint event... use pixmap buffer...
39 * - add paragraph support (full) and '\n' code in getStream..
40 * - use currentTextBlock and neighbours for fast paint and layout
41 * - replace copious uses of Refreshtext with appropriate layOut()...
42 * - WMFindInTextStream should also highlight found text...
43 * - add full support for Horizontal Scroll
47 /* a Section is a section of a TextBlock that describes what parts
48 of a TextBlock has been laid out on which "line"...
49 o this greatly aids redraw, scroll and selection.
50 o this is created during layoutLine, but may be later modified.
51 o there may be many Sections per TextBlock, hence the array */
52 typedef struct {
53 unsigned int x, y; /* where to draw it from */
54 unsigned short w, h; /* its width and height */
55 unsigned short begin; /* where the layout begins */
56 unsigned short end ; /* where it ends */
57 unsigned short last:1; /* is it the last section on a "line"? */
58 unsigned int _y:31; /* the "line" it and other textblocks are on */
59 } Section;
62 /* a TextBlock is a doubly-linked list of TextBlocks containing:
63 o text for the block, color and font
64 o or a pointer to the pixmap
65 o OR a pointer to the widget and the (text) description for its graphic
68 typedef struct _TextBlock {
69 struct _TextBlock *next; /* next text block in linked list */
70 struct _TextBlock *prior; /* prior text block in linked list */
72 char *text; /* pointer to text (could be kanji) */
73 /* or to the object's description */
74 union {
75 WMFont *font; /* the font */
76 WMWidget *widget; /* the embedded widget */
77 WMPixmap *pixmap; /* the pixmap */
78 } d; /* description */
80 unsigned short used; /* number of chars in this block */
81 unsigned short allocated; /* size of allocation (in chars) */
82 WMColor *color; /* the color */
84 Section *sections; /* the region for layouts (a growable array) */
85 /* an _array_! of size _nsections_ */
87 unsigned short s_begin; /* where the selection begins */
88 unsigned short s_end; /* where it ends */
90 unsigned int first:1; /* first TextBlock in paragraph */
91 unsigned int blank:1; /* ie. blank paragraph */
92 unsigned int kanji:1; /* is of 16-bit characters or not */
93 unsigned int graphic:1; /* graphic or text: text=0 */
94 unsigned int object:1; /* embedded object or pixmap */
95 unsigned int underlined:1; /* underlined or not */
96 unsigned int selected:1; /* selected or not */
97 unsigned int nsections:8; /* over how many "lines" a TextBlock wraps */
98 int script:8; /* script in points: negative for subscript */
99 unsigned int marginN:8; /* which of the margins in the tPtr to use */
100 unsigned int nClicks:2; /* single, double, triple clicks */
101 unsigned int RESERVED:7;
102 } TextBlock;
105 /* somehow visible.h beats the hell outta visible.size.height :-) */
106 typedef struct {
107 unsigned int y;
108 unsigned int x;
109 unsigned int h;
110 unsigned int w;
111 } myRect;
114 typedef struct W_Text {
115 W_Class widgetClass; /* the class number of this widget */
116 W_View *view; /* the view referring to this instance */
118 WMRuler *ruler; /* the ruler widget to manipulate paragraphs */
120 WMScroller *vS; /* the vertical scroller */
121 unsigned int vpos; /* the current vertical position */
122 unsigned int prevVpos; /* the previous vertical position */
124 WMScroller *hS; /* the horizontal scroller */
125 unsigned int hpos; /* the current horizontal position */
126 unsigned int prevHpos; /* the previous horizontal position */
128 WMFont *dFont; /* the default font */
129 WMColor *dColor; /* the default color */
130 WMPixmap *dBulletPix; /* the default pixmap for bullets */
132 GC bgGC; /* the background GC to draw with */
133 GC fgGC; /* the foreground GC to draw with */
134 Pixmap db; /* the buffer on which to draw */
136 myRect visible; /* the actual rectangle that can be drawn into */
137 myRect cursor; /* the position and (height) of cursor */
138 myRect sel; /* the selection rectangle */
140 WMPoint clicked; /* where in the _document_ was clicked */
142 unsigned short tpos; /* the position in the currentTextBlock */
143 unsigned short docWidth; /* the width of the entire document */
144 unsigned int docHeight; /* the height of the entire document */
146 TextBlock *firstTextBlock;
147 TextBlock *lastTextBlock;
148 TextBlock *currentTextBlock;
150 WMArray *gfxItems; /* a nice array of graphic items */
152 #if DO_BLINK
153 WMHandlerID timerID; /* for nice twinky-winky */
154 #endif
156 WMAction *parser;
157 WMAction *writer;
158 WMTextDelegate *delegate;
159 Time lastClickTime;
161 WMRulerMargins *margins; /* an array of margins */
163 unsigned int nMargins:8; /* the total number of margins in use */
164 struct {
165 unsigned int monoFont:1; /* whether to ignore formats */
166 unsigned int focused:1; /* whether this instance has input focus */
167 unsigned int editable:1; /* "silly user, you can't edit me" */
168 unsigned int ownsSelection:1; /* "I ownz the current selection!" */
169 unsigned int pointerGrabbed:1;/* "heh, gib me pointer" */
170 unsigned int buttonHeld:1; /* the user is holding down the button */
171 unsigned int extendSelection:1; /* shift-drag to select more regions */
173 unsigned int rulerShown:1; /* whether the ruler is shown or not */
174 unsigned int frozen:1; /* whether screen updates are to be made */
175 unsigned int cursorShown:1; /* whether to show the cursor */
176 unsigned int clickPos:1; /* clicked before=0 or after=1 a graphic: */
177 /* (within counts as after too) */
179 unsigned int horizOnDemand:1;/* if a large image should appear*/
180 unsigned int needsRefresh:1; /* in case of Append/Deletes */
181 unsigned int ignoreNewLine:1;/* turn it into a ' ' in streams > 1 */
182 unsigned int laidOut:1; /* have the TextBlocks all been laid out */
183 unsigned int waitingForSelection:1; /* I don't wanna wait in vain... */
184 unsigned int prepend:1; /* prepend=1, append=0 (for parsers) */
185 unsigned int parsingHTML:1; /* how to interpret a stream of text */
186 WMAlignment alignment:2; /* the alignment for text */
187 WMReliefType relief:3; /* the relief to display with */
188 unsigned int RESERVED:2;
189 } flags;
190 } Text;
193 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
194 if ((T)->delegate && (T)->delegate->C)\
195 (*(T)->delegate->C)((T)->delegate,notif);\
196 WMPostNotification(notif);\
197 WMReleaseNotification(notif);}
202 * A hack to speed up caseless_equal. Thanks to Quincey Koziol for
203 * developing it for the "chimera" folks so I could use it 7 years later ;-)
204 * Constraint: nothing but '\0' may map to 0
206 static unsigned char map_table[256] = {
207 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,
208 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
209 52,53,54,55,56,57,58,59,60,61,62,63,64,97,98,99,100,101,102,103,104,105,
210 106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,
211 92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
212 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,
213 130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,
214 148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,
215 166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,
216 184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,
217 202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,
218 220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,
219 238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};
221 void HTMLParser(Text *tPtr, char *stream);
222 #define MAX_TOKEN_SIZE 255
223 #define MAX_TEXT_SIZE 1023
225 #define TOLOWER(x) (map_table[(int)x])
227 #define ISALNUM(x) ( ((x>='0') && (x<='9')) \
228 || ((x>='a') && (x<='z')) || ((x>='A') && x<='Z'))
230 static void
231 output(char *ptr, int len)
233 char s[len+1];
234 memcpy(s, ptr, len);
235 s[len] = 0;
236 /* printf(" s is [%s] (%d)\n", s, strlen(s)); */
237 printf("[%s]\n", s);
241 /* tb->text doesn't necessarily end in '\0' hmph! (strchr) */
242 static inline char *
243 mystrchr(char *s, char needle, unsigned short len)
245 char *haystack = s;
247 if (!haystack || len < 1)
248 return NULL;
250 while ( (int) (haystack - s) < len ) {
251 if (*haystack == needle)
252 return haystack;
253 haystack++;
255 return NULL;
258 /* tb->text doesn't necessarily end in '\0' hmph! (strstr) */
259 static inline char *
260 mystrstr(char *haystack, char *needle, unsigned short len)
262 if (!haystack || !needle || len < 1) {
263 return NULL;
264 } else {
265 char *s = wmalloc(len+1);
266 char *r;
268 memcpy(s, haystack, len);
269 s[len] = 0;
270 r = (strstr(s, needle));
271 wfree(s);
272 return r;
276 static int
277 mystrcasecmp(const unsigned char *s1, const unsigned char *s2,
278 int len, Bool caseSensitive)
280 if (!*s1 || !*s2 || len<1)
281 return 0;
283 while (*s2 != '\0' && len>0) {
284 if ( (caseSensitive? (*s1) : TOLOWER (*s1))
285 != (caseSensitive? (*s2) : TOLOWER (*s2)))
286 return 0;
288 s1++;
289 s2++;
290 len--;
292 return (*s1=='\0' || !ISALNUM(*s1))?1:0;
297 #if DO_BLINK
298 #define CURSOR_BLINK_ON_DELAY 600
299 #define CURSOR_BLINK_OFF_DELAY 400
300 #endif
302 static char *default_bullet[] = {
303 "6 6 4 1",
304 " c None s None", ". c black",
305 "X c white", "o c #808080",
306 " ... ",
307 ".XX.. ",
308 ".XX..o",
309 ".....o",
310 " ...oo",
311 " ooo "};
313 static void
314 handleEvents(XEvent *event, void *data);
317 static int
318 getMarginNumber(Text *tPtr, WMRulerMargins *margins)
320 unsigned int i=0;
322 for(i=0; i < tPtr->nMargins; i++) {
324 if(WMIsMarginEqualToMargin(&tPtr->margins[i], margins))
325 return i;
328 return -1;
333 static int
334 newMargin(Text *tPtr, WMRulerMargins *margins)
336 int n;
338 if (!margins) {
339 tPtr->margins[0].retainCount++;
340 return 0;
343 n = getMarginNumber(tPtr, margins);
345 if (n == -1) {
347 tPtr->margins = wrealloc(tPtr->margins,
348 (++tPtr->nMargins)*sizeof(WMRulerMargins));
350 n = tPtr->nMargins-1;
351 tPtr->margins[n].left = margins->left;
352 tPtr->margins[n].first = margins->first;
353 tPtr->margins[n].body = margins->body;
354 tPtr->margins[n].right = margins->right;
355 /* for each tab... */
356 tPtr->margins[n].retainCount = 1;
357 } else {
358 tPtr->margins[n].retainCount++;
361 return n;
364 static Bool
365 sectionWasSelected(Text *tPtr, TextBlock *tb, XRectangle *rect, int s)
367 unsigned short i, w, lw, selected = False, extend = False;
368 myRect sel;
371 /* if selection rectangle completely encloses the section */
372 if ((tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
373 && (tb->sections[s]._y + tb->sections[s].h
374 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
375 sel.x = 0;
376 sel.w = tPtr->visible.w;
377 selected = extend = True;
379 /* or if it starts on a line and then goes further down */
380 } else if ((tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
381 && (tb->sections[s]._y + tb->sections[s].h
382 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)
383 && (tb->sections[s]._y + tb->sections[s].h
384 >= tPtr->visible.y + tPtr->sel.y) ) {
385 sel.x = WMAX(tPtr->sel.x, tPtr->clicked.x);
386 sel.w = tPtr->visible.w;
387 selected = extend = True;
389 /* or if it begins before a line, but ends on it */
390 } else if ((tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
391 && (tb->sections[s]._y + tb->sections[s].h
392 >= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)
393 && (tb->sections[s]._y
394 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
396 if (1||tPtr->sel.x + tPtr->sel.w > tPtr->clicked.x)
397 sel.w = tPtr->sel.x + tPtr->sel.w;
398 else
399 sel.w = tPtr->sel.x;
401 sel.x = 0;
402 selected = True;
404 /* or if the selection rectangle lies entirely within a line */
405 } else if ((tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
406 && (tPtr->sel.w >= 2)
407 && (tb->sections[s]._y + tb->sections[s].h
408 >= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
409 sel.x = tPtr->sel.x;
410 sel.w = tPtr->sel.w;
411 selected = True;
414 if (selected) {
415 selected = False;
417 /* if not within (modified) selection rectangle */
418 if ( tb->sections[s].x > sel.x + sel.w
419 || tb->sections[s].x + tb->sections[s].w < sel.x)
420 return False;
422 if (tb->graphic) {
423 if ( tb->sections[s].x + tb->sections[s].w <= sel.x + sel.w
424 && tb->sections[s].x >= sel.x) {
425 rect->width = tb->sections[s].w;
426 rect->x = tb->sections[s].x;
427 selected = True;
429 } else {
431 i = tb->sections[s].begin;
432 lw = 0;
434 if (0&& tb->sections[s].x >= sel.x) {
435 tb->s_begin = tb->sections[s].begin;
436 goto _selEnd;
439 while (++i <= tb->sections[s].end) {
441 w = WMWidthOfString(tb->d.font, &(tb->text[i-1]), 1);
442 lw += w;
444 if (lw + tb->sections[s].x >= sel.x
445 || i == tb->sections[s].end ) {
446 lw -= w;
447 i--;
448 tb->s_begin = (tb->selected? WMIN(tb->s_begin, i) : i);
449 break;
453 if (i > tb->sections[s].end) {
454 printf("WasSelected: (i > tb->sections[s].end) \n");
455 return False;
458 _selEnd: rect->x = tb->sections[s].x + lw;
459 lw = 0;
460 while(++i <= tb->sections[s].end) {
462 w = WMWidthOfString(tb->d.font, &(tb->text[i-1]), 1);
463 lw += w;
465 if (lw + rect->x >= sel.x + sel.w
466 || i == tb->sections[s].end ) {
468 if (i != tb->sections[s].end) {
469 lw -= w;
470 i--;
473 rect->width = lw;
474 if (tb->sections[s].last && sel.x + sel.w
475 >= tb->sections[s].x + tb->sections[s].w
476 && extend ) {
477 rect->width += (tPtr->visible.w - rect->x - lw);
480 tb->s_end = (tb->selected? WMAX(tb->s_end, i) : i);
481 selected = True;
482 break;
483 } } } }
485 if (selected) {
486 rect->y = tb->sections[s]._y - tPtr->vpos;
487 rect->height = tb->sections[s].h;
488 if(tb->graphic) { printf("graphic s%d h%d\n", s,tb->sections[s].h);}
490 return selected;
494 static void
495 setSelectionProperty(WMText *tPtr, WMFont *font, WMColor *color)
497 TextBlock *tb;
498 int isFont=False;
500 if((font && color) || (!font && !color))
501 return;
503 if(font && !color)
504 isFont = True;
506 tb = tPtr->firstTextBlock;
507 if (!tb || !tPtr->flags.ownsSelection)
508 return;
510 while (tb) {
511 if (tPtr->flags.monoFont || tb->selected) {
513 if (tPtr->flags.monoFont || (tb->s_end - tb->s_begin == tb->used)
514 || tb->graphic) {
516 if(isFont) {
517 if(!tb->graphic) {
518 WMReleaseFont(tb->d.font);
519 tb->d.font = WMRetainFont(font);
521 } else {
522 WMReleaseColor(tb->color);
523 tb->color = WMRetainColor(color);
526 } else if (tb->s_end <= tb->used && tb->s_begin < tb->s_end) {
528 TextBlock *otb = tb;
530 TextBlock *ntb = (TextBlock *)
531 WMCreateTextBlockWithText(tPtr,
532 &(tb->text[tb->s_begin]),
533 (isFont?font:tb->d.font),
534 (isFont?tb->color:color),
535 False, (tb->s_end - tb->s_begin));
537 if (ntb) {
538 ntb->selected = True;
539 ntb->s_begin = 0;
540 ntb->s_end = ntb->used;
541 tPtr->currentTextBlock = tb;
542 WMAppendTextBlock(tPtr, ntb);
543 tb = tPtr->currentTextBlock;
546 if (otb->used - otb->s_end > 0) {
547 ntb = (TextBlock *)
548 WMCreateTextBlockWithText(tPtr,
549 &(otb->text[otb->s_end]), otb->d.font, otb->color,
550 False, otb->used - otb->s_end);
552 if (ntb) {
553 ntb->selected = False;
554 WMAppendTextBlock(tPtr, ntb);
555 tb = tPtr->currentTextBlock;
559 otb->selected = False;
560 otb->used = otb->s_begin;
564 tb = tb->next;
567 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
571 static void
572 removeSelection(Text *tPtr)
574 TextBlock *tb = NULL;
576 if (!(tb = tPtr->firstTextBlock))
577 return;
579 while (tb) {
580 if (tb->selected) {
582 if ( (tb->s_end - tb->s_begin == tb->used) || tb->graphic) {
583 tPtr->currentTextBlock = tb;
584 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
585 tb = tPtr->currentTextBlock;
586 if (tb)
587 tPtr->tpos = 0;
588 continue;
590 } else if (tb->s_end <= tb->used) {
591 memmove(&(tb->text[tb->s_begin]),
592 &(tb->text[tb->s_end]), tb->used - tb->s_end);
593 tb->used -= (tb->s_end - tb->s_begin);
594 tb->selected = False;
595 tPtr->tpos = tb->s_begin;
600 tb = tb->next;
605 static void
606 paintText(Text *tPtr)
608 TextBlock *tb;
609 WMFont *font;
610 GC gc, greyGC;
611 char *text;
612 int len, y, c, s, done=False, prev_y=-23;
613 WMScreen *scr = tPtr->view->screen;
614 Display *dpy = tPtr->view->screen->display;
615 Window win = tPtr->view->window;
617 if (!tPtr->view->flags.realized || !tPtr->db || tPtr->flags.frozen)
618 return;
620 XFillRectangle(dpy, tPtr->db, tPtr->bgGC,
621 0, 0, tPtr->visible.w, tPtr->visible.h);
623 if(!(tb = tPtr->firstTextBlock))
624 goto _copy_area;
626 if (tPtr->flags.ownsSelection)
627 greyGC = WMColorGC(WMGrayColor(scr));
629 done = False;
631 /* first, place all text that can be viewed */
632 while (!done && tb) {
634 if (tb->graphic) {
635 tb = tb->next;
636 continue;
639 tb->selected = False;
641 for(s=0; s<tb->nsections && !done; s++) {
643 if (tb->sections[s]._y > tPtr->vpos + tPtr->visible.h) {
644 done = True;
645 break;
648 if ( tb->sections[s].y + tb->sections[s].h < tPtr->vpos)
649 continue;
651 if (tPtr->flags.monoFont) {
652 font = tPtr->dFont;
653 gc = tPtr->fgGC;
654 } else {
655 font = tb->d.font;
656 gc = WMColorGC(tb->color);
659 if (tPtr->flags.ownsSelection) {
660 XRectangle rect;
662 if ( sectionWasSelected(tPtr, tb, &rect, s)) {
663 tb->selected = True;
664 XFillRectangle(dpy, tPtr->db, greyGC,
665 rect.x, rect.y, rect.width, rect.height);
669 prev_y = tb->sections[s]._y;
671 len = tb->sections[s].end - tb->sections[s].begin;
672 text = &(tb->text[tb->sections[s].begin]);
673 y = tb->sections[s].y - tPtr->vpos;
674 WMDrawString(scr, tPtr->db, gc, font,
675 tb->sections[s].x - tPtr->hpos, y, text, len);
677 if (tb->underlined) {
678 XDrawLine(dpy, tPtr->db, gc,
679 tb->sections[s].x - tPtr->hpos,
680 y + font->y + 1,
681 tb->sections[s].x + tb->sections[s].w - tPtr->hpos,
682 y + font->y + 1);
687 tb = (!done? tb->next : NULL);
691 /* now , show all graphic items that can be viewed */
692 c = WMGetArrayItemCount(tPtr->gfxItems);
693 if (c > 0 && !tPtr->flags.monoFont) {
694 int j, h;
696 for(j=0; j<c; j++) {
697 tb = (TextBlock *) WMGetFromArray(tPtr->gfxItems, j);
698 if (tb->sections[0]._y + tb->sections[0].h <= tPtr->vpos
699 || tb->sections[0]._y >= tPtr->vpos + tPtr->visible.h ) {
701 if(tb->object) {
702 if ((W_VIEW(tb->d.widget))->flags.mapped) {
703 WMUnmapWidget(tb->d.widget);
706 } else {
707 if(tb->object) {
708 if (!(W_VIEW(tb->d.widget))->flags.mapped) {
709 if (!(W_VIEW(tb->d.widget))->flags.realized)
710 WMRealizeWidget(tb->d.widget);
711 WMMapWidget(tb->d.widget);
712 WMLowerWidget(tb->d.widget);
716 if (tPtr->flags.ownsSelection) {
717 XRectangle rect;
719 if ( sectionWasSelected(tPtr, tb, &rect, s)) {
720 tb->selected = True;
721 XFillRectangle(dpy, tPtr->db, greyGC,
722 rect.x, rect.y, rect.width, rect.height);
726 if(tb->object) {
727 WMMoveWidget(tb->d.widget,
728 tb->sections[0].x - tPtr->hpos,
729 tb->sections[0].y - tPtr->vpos);
730 h = WMWidgetHeight(tb->d.widget) + 1;
732 } else {
733 WMDrawPixmap(tb->d.pixmap, tPtr->db,
734 tb->sections[0].x - tPtr->hpos,
735 tb->sections[0].y - tPtr->vpos);
736 h = tb->d.pixmap->height + 1;
740 if (tb->underlined) {
741 XDrawLine(dpy, tPtr->db, WMColorGC(tb->color),
742 tb->sections[0].x,
743 tb->sections[0].y + h,
744 tb->sections[0].x + tb->sections[0].w,
745 tb->sections[0].y + h);
746 } } } }
749 _copy_area:
750 if (tPtr->flags.editable && tPtr->flags.cursorShown
751 && tPtr->cursor.x != -23 && tPtr->flags.focused) {
752 int y = tPtr->cursor.y - tPtr->vpos;
753 XDrawLine(dpy, tPtr->db, tPtr->fgGC,
754 tPtr->cursor.x, y,
755 tPtr->cursor.x, y + tPtr->cursor.h);
758 XCopyArea(dpy, tPtr->db, win, tPtr->bgGC, 0, 0,
759 tPtr->visible.w, tPtr->visible.h,
760 tPtr->visible.x, tPtr->visible.y);
762 W_DrawRelief(scr, win, 0, 0,
763 tPtr->view->size.width, tPtr->view->size.height,
764 tPtr->flags.relief);
766 if (tPtr->ruler && tPtr->flags.rulerShown)
767 XDrawLine(dpy, win, tPtr->fgGC,
768 2, 42, tPtr->view->size.width-4, 42);
773 #if DO_BLINK
775 static void
776 blinkCursor(void *data)
778 Text *tPtr = (Text*)data;
780 if (tPtr->flags.cursorShown) {
781 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY,
782 blinkCursor, data);
783 } else {
784 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
785 blinkCursor, data);
787 paintText(tPtr);
788 tPtr->flags.cursorShown = !tPtr->flags.cursorShown;
790 #endif
792 static TextBlock *
793 getFirstNonGraphicBlockFor(TextBlock *tb, short dir)
795 if (!tb)
796 return NULL;
797 while (tb) {
798 if (!tb->graphic)
799 break;
800 tb = (dir? tb->next : tb->prior);
803 return tb;
807 static void
808 updateCursorPosition(Text *tPtr)
810 TextBlock *tb = NULL;
811 int x, y, h, s;
813 if(tPtr->flags.needsRefresh)
814 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
816 if (! (tb = tPtr->currentTextBlock)) {
817 if (! (tb = tPtr->firstTextBlock)) {
818 tPtr->tpos = 0;
819 tPtr->cursor.h = tPtr->dFont->height;
820 tPtr->cursor.y = 2;
821 tPtr->cursor.x = 2;
822 return;
826 if(tb->graphic) {
827 y = tb->sections[0].y;
828 h = tb->sections[0].h;
829 x = tb->sections[0].x;
830 } else {
831 if(tPtr->tpos > tb->used)
832 tPtr->tpos = tb->used;
834 for(s=0; s<tb->nsections; s++) {
836 if(tPtr->tpos >= tb->sections[s].begin
837 && tPtr->tpos <= tb->sections[s].end)
838 break;
841 y = tb->sections[s]._y;
842 h = tb->sections[s].h;
843 x = tb->sections[s].x + WMWidthOfString(
844 (tPtr->flags.monoFont?tPtr->dFont:tb->d.font),
845 &tb->text[tb->sections[s].begin],
846 tPtr->tpos - tb->sections[s].begin);
849 tPtr->cursor.y = y;
850 tPtr->cursor.h = h;
851 tPtr->cursor.x = x;
852 paintText(tPtr);
856 static void
857 cursorToTextPosition(Text *tPtr, int x, int y)
859 TextBlock *tb = NULL;
860 int done=False, s, pos, len, _w, _y, dir=1; /* 1 == "down" */
861 char *text;
863 if(tPtr->flags.needsRefresh)
864 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
866 y += (tPtr->vpos - tPtr->visible.y);
867 if (y<0)
868 y = 0;
870 x -= (tPtr->visible.x - 2);
871 if (x<0)
872 x=0;
874 /* clicked is relative to document, not window... */
875 tPtr->clicked.x = x;
876 tPtr->clicked.y = y;
878 if (! (tb = tPtr->currentTextBlock)) {
879 if (! (tb = tPtr->firstTextBlock)) {
880 tPtr->tpos = 0;
881 tPtr->cursor.h = tPtr->dFont->height;
882 tPtr->cursor.y = 2;
883 tPtr->cursor.x = 2;
884 return;
888 /* first, which direction? Most likely, newly clicked
889 position will be close to previous */
890 dir = !(y <= tb->sections[0].y);
891 if ( ( y <= tb->sections[0]._y + tb->sections[0].h )
892 && (y >= tb->sections[0]._y ) ) {
893 /* if it's on the same line */
894 if(x < tb->sections[0].x)
895 dir = 0;
896 if(x >= tb->sections[0].x)
897 dir = 1;
900 tb = tPtr->firstTextBlock;
901 dir = 1;
903 if (tPtr->flags.monoFont && tb->graphic) {
904 tb = getFirstNonGraphicBlockFor(tb, 1);
905 if (!tb) {
906 tPtr->currentTextBlock =
907 (dir? tPtr->lastTextBlock : tPtr->firstTextBlock);
908 tPtr->tpos = 0;
909 return;
913 s = (dir? 0 : tb->nsections-1);
914 if ( y >= tb->sections[s]._y
915 && y <= tb->sections[s]._y + tb->sections[s].h) {
916 goto _doneV;
919 /* get the first section of the TextBlock that lies about
920 the vertical click point */
921 done = False;
922 while (!done && tb) {
924 if (tPtr->flags.monoFont && tb->graphic) {
925 if(tb->next)
926 tb = tb->next;
927 continue;
930 s = (dir? 0 : tb->nsections-1);
931 while (!done && (dir? (s<tb->nsections) : (s>=0) )) {
933 if ( (dir? (y <= tb->sections[s]._y + tb->sections[s].h) :
934 ( y >= tb->sections[s]._y ) ) ) {
935 done = True;
936 } else {
937 dir? s++ : s--;
941 if (!done) {
942 if ( (dir? tb->next : tb->prior)) {
943 tb = (dir ? tb->next : tb->prior);
944 } else {
945 pos = tb->used;
946 break; /* goto _doneH; */
952 if (s<0 || s>=tb->nsections) {
953 s = (dir? tb->nsections-1 : 0);
956 _doneV:
957 /* we have the line, which TextBlock on that line is it? */
958 pos = 0;
959 if (tPtr->flags.monoFont && tb->graphic)
960 tb = getFirstNonGraphicBlockFor(tb, dir);
961 if (tb) {
962 if ((dir? tb->sections[s].x >= x : tb->sections[s].x < x))
963 goto _doneH;
965 #if 0
966 if(tb->blank) {
967 _w = 0;
968 printf("blank\n");
969 } else {
970 text = &(tb->text[tb->sections[s].begin]);
971 len = tb->sections[s].end - tb->sections[s].begin;
972 _w = WMWidthOfString(tb->d.font, text, len);
974 printf("here %d %d \n", tb->sections[s].x + _w, x);
975 if ((dir? tb->sections[s].x + _w < x : tb->sections[s].x + _w >= x)) {
976 pos = tb->sections[s].end;
977 tPtr->cursor.x = tb->sections[s].x + _w;
978 goto _doneH;
980 #endif
981 _y = tb->sections[s]._y;
984 while (tb) {
986 if (tPtr->flags.monoFont && tb->graphic) {
987 tb = (dir ? tb->next : tb->prior);
988 continue;
991 if (dir) {
992 if (tb->graphic) {
993 if(tb->object)
994 _w = WMWidgetWidth(tb->d.widget);
995 else
996 _w = tb->d.pixmap->width;
997 } else {
998 text = &(tb->text[tb->sections[s].begin]);
999 len = tb->sections[s].end - tb->sections[s].begin;
1000 _w = WMWidthOfString(tb->d.font, text, len);
1001 if (tb->sections[s].x + _w >= x)
1002 break;
1005 } else {
1006 if (tb->sections[s].x <= x)
1007 break;
1010 if ((dir? tb->next : tb->prior)) {
1011 TextBlock *nxt = (dir? tb->next : tb->prior);
1012 if (tPtr->flags.monoFont && nxt->graphic) {
1013 nxt = getFirstNonGraphicBlockFor(nxt, dir);
1014 if (!nxt) {
1015 pos = 0;
1016 tPtr->cursor.x = tb->sections[s].x;
1017 goto _doneH;
1021 if (_y != nxt->sections[0]._y) {
1022 /* this must be the last/first on this line. stop */
1023 pos = (dir? tb->sections[s].end : 0);
1024 tPtr->cursor.x = tb->sections[s].x;
1025 if (!tb->blank) {
1026 if (tb->graphic) {
1027 if(tb->object)
1028 tPtr->cursor.x += WMWidgetWidth(tb->d.widget);
1029 else
1030 tPtr->cursor.x += tb->d.pixmap->width;
1031 } else if (pos > tb->sections[s].begin) {
1032 tPtr->cursor.x +=
1033 WMWidthOfString(tb->d.font,
1034 &(tb->text[tb->sections[s].begin]),
1035 pos - tb->sections[s].begin);
1038 goto _doneH;
1042 if ( (dir? tb->next : tb->prior)) {
1043 tb = (dir ? tb->next : tb->prior);
1044 } else {
1045 done = True;
1046 break;
1049 if (tb)
1050 s = (dir? 0 : tb->nsections-1);
1053 /* we have said TextBlock, now where within it? */
1054 if (tb && !tb->graphic) {
1055 WMFont *f = tb->d.font;
1056 len = tb->sections[s].end - tb->sections[s].begin;
1057 text = &(tb->text[tb->sections[s].begin]);
1059 _w = x - tb->sections[s].x;
1060 pos = 0;
1062 while (pos<len && WMWidthOfString(f, text, pos+1) < _w)
1063 pos++;
1065 tPtr->cursor.x = tb->sections[s].x +
1066 (pos? WMWidthOfString(f, text, pos) : 0);
1068 pos += tb->sections[s].begin;
1069 _doneH:
1070 tPtr->tpos = (pos<tb->used)? pos : tb->used;
1073 if (!tb)
1074 printf("this app will surely crash :-)\n");
1076 tPtr->currentTextBlock = tb;
1077 tPtr->cursor.h = tb->sections[s].h;
1078 tPtr->cursor.y = tb->sections[s]._y;
1082 static void
1083 autoSelectText(Text *tPtr, int clicks)
1085 int x, y;
1086 TextBlock *tb;
1087 int start, end;
1088 char *mark = NULL;
1090 if(!(tb = tPtr->currentTextBlock))
1091 return;
1093 if(clicks == 2) {
1095 if(tb->text[tPtr->tpos] == ' ')
1096 return;
1098 tPtr->sel.y = tPtr->cursor.y+5;
1099 tPtr->sel.h = tPtr->cursor.h-10;
1101 if(tb->graphic) {
1102 tPtr->sel.x = tb->sections[0].x;
1103 tPtr->sel.w = tb->sections[0].w;
1104 } else {
1105 WMFont *font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
1107 start = tPtr->tpos;
1108 while(start > 0 && tb->text[start-1] != ' ')
1109 start--;
1111 x = tPtr->cursor.x + 0*WMWidthOfString(font, &tb->text[start], 1);
1112 if(tPtr->tpos > start){
1113 output(&tb->text[start], tPtr->tpos - start);
1114 x -= WMWidthOfString(font, &tb->text[start],
1115 tPtr->tpos - start);
1117 tPtr->sel.x = (x<0?0:x);
1119 if((mark = mystrchr(&tb->text[start], ' ', tb->used-start))) {
1120 tPtr->sel.w = WMWidthOfString(font, &tb->text[start],
1121 (int)(mark - &tb->text[start]));
1122 } else {
1123 tPtr->sel.w = tPtr->docWidth - tPtr->sel.x;
1127 } else if(clicks == 3) {
1128 tPtr->sel.x = tPtr->visible.x;
1129 tPtr->sel.w = tPtr->docWidth;
1132 tPtr->flags.ownsSelection = True;
1133 paintText(tPtr);
1137 static void
1138 updateScrollers(Text *tPtr)
1141 if (tPtr->flags.frozen)
1142 return;
1144 if (tPtr->vS) {
1145 if (tPtr->docHeight < tPtr->visible.h) {
1146 WMSetScrollerParameters(tPtr->vS, 0, 1);
1147 tPtr->vpos = 0;
1148 } else {
1149 float hmax = (float)(tPtr->docHeight);
1150 WMSetScrollerParameters(tPtr->vS,
1151 ((float)tPtr->vpos)/(hmax - (float)tPtr->visible.h),
1152 (float)tPtr->visible.h/hmax);
1154 } else tPtr->vpos = 0;
1156 if (tPtr->hS) {
1157 if (tPtr->docWidth < tPtr->visible.w) {
1158 WMSetScrollerParameters(tPtr->hS, 0, 1);
1159 tPtr->hpos = 0;
1160 } else {
1161 float wmax = (float)(tPtr->docWidth);
1162 WMSetScrollerParameters(tPtr->hS,
1163 ((float)tPtr->hpos)/(wmax - (float)tPtr->visible.w),
1164 (float)tPtr->visible.w/wmax);
1166 } else tPtr->hpos = 0;
1169 static void
1170 scrollersCallBack(WMWidget *w, void *self)
1172 Text *tPtr = (Text *)self;
1173 Bool scroll = False;
1174 Bool dimple = False;
1175 int which;
1177 if (!tPtr->view->flags.realized || tPtr->flags.frozen)
1178 return;
1180 if (w == tPtr->vS) {
1181 int height;
1182 height = tPtr->visible.h;
1184 which = WMGetScrollerHitPart(tPtr->vS);
1185 switch(which) {
1186 case WSDecrementLine:
1187 if (tPtr->vpos > 0) {
1188 if (tPtr->vpos>16) tPtr->vpos-=16;
1189 else tPtr->vpos=0;
1190 scroll=True;
1191 }break;
1192 case WSIncrementLine: {
1193 int limit = tPtr->docHeight - height;
1194 if (tPtr->vpos < limit) {
1195 if (tPtr->vpos<limit-16) tPtr->vpos+=16;
1196 else tPtr->vpos=limit;
1197 scroll = True;
1198 }}break;
1199 case WSDecrementPage:
1200 tPtr->vpos -= height;
1202 if (tPtr->vpos < 0)
1203 tPtr->vpos = 0;
1204 dimple = True;
1205 scroll = True;
1206 printf("dimple needs to jump to mouse location ;-/\n");
1207 break;
1208 case WSIncrementPage:
1209 tPtr->vpos += height;
1210 if (tPtr->vpos > (tPtr->docHeight - height))
1211 tPtr->vpos = tPtr->docHeight - height;
1212 dimple = True;
1213 scroll = True;
1214 printf("dimple needs to jump to mouse location ;-/\n");
1215 break;
1218 case WSKnob:
1219 tPtr->vpos = WMGetScrollerValue(tPtr->vS)
1220 * (float)(tPtr->docHeight - height);
1221 scroll = True;
1222 break;
1224 case WSKnobSlot:
1225 case WSNoPart:
1226 printf("WSNoPart, WSKnobSlot\n");
1227 #if 0
1228 float hmax = (float)(tPtr->docHeight);
1229 ((float)tPtr->vpos)/(hmax - (float)tPtr->visible.h),
1230 (float)tPtr->visible.h/hmax;
1231 dimple =where mouse is.
1232 #endif
1233 break;
1235 scroll = (tPtr->vpos != tPtr->prevVpos);
1236 tPtr->prevVpos = tPtr->vpos;
1239 if (w == tPtr->hS) {
1240 int width = tPtr->visible.w;
1242 which = WMGetScrollerHitPart(tPtr->hS);
1243 switch(which) {
1244 case WSDecrementLine:
1245 if (tPtr->hpos > 0) {
1246 if (tPtr->hpos>16) tPtr->hpos-=16;
1247 else tPtr->hpos=0;
1248 scroll=True;
1249 }break;
1250 case WSIncrementLine: {
1251 int limit = tPtr->docWidth - width;
1252 if (tPtr->hpos < limit) {
1253 if (tPtr->hpos<limit-16) tPtr->hpos+=16;
1254 else tPtr->hpos=limit;
1255 scroll = True;
1256 }}break;
1257 case WSDecrementPage:
1258 tPtr->hpos -= width;
1260 if (tPtr->hpos < 0)
1261 tPtr->hpos = 0;
1262 dimple = True;
1263 scroll = True;
1264 printf("dimple needs to jump to mouse location ;-/\n");
1265 break;
1266 case WSIncrementPage:
1267 tPtr->hpos += width;
1268 if (tPtr->hpos > (tPtr->docWidth - width))
1269 tPtr->hpos = tPtr->docWidth - width;
1270 dimple = True;
1271 scroll = True;
1272 printf("dimple needs to jump to mouse location ;-/\n");
1273 break;
1276 case WSKnob:
1277 tPtr->hpos = WMGetScrollerValue(tPtr->hS)
1278 * (float)(tPtr->docWidth - width);
1279 scroll = True;
1280 break;
1282 case WSKnobSlot:
1283 case WSNoPart:
1284 printf("WSNoPart, WSKnobSlot\n");
1285 #if 0
1286 float wmax = (float)(tPtr->docWidth);
1287 ((float)tPtr->vpos)/(wmax - (float)tPtr->visible.w),
1288 (float)tPtr->visible.w/wmax;
1289 dimple =where mouse is.
1290 #endif
1291 break;
1293 scroll = (tPtr->hpos != tPtr->prevHpos);
1294 tPtr->prevHpos = tPtr->hpos;
1297 if (scroll) {
1299 if (0&&dimple) {
1300 if (tPtr->rulerShown)
1301 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
1302 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
1303 else
1304 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
1305 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
1308 if (dimple || which == WSDecrementLine || which == WSIncrementLine)
1309 updateScrollers(tPtr);
1310 paintText(tPtr);
1316 typedef struct {
1317 TextBlock *tb;
1318 unsigned short begin, end; /* what part of the text block */
1319 } myLineItems;
1322 static int
1323 layOutLine(Text *tPtr, myLineItems *items, int nitems, int x, int y)
1325 int i, j=0, lw = 0, line_height=0, max_d=0, len, n;
1326 WMFont *font;
1327 char *text;
1328 TextBlock *tb;
1329 TextBlock *tbsame=NULL;
1331 if(!items || nitems == 0)
1332 return 0;
1334 for(i=0; i<nitems; i++) {
1335 tb = items[i].tb;
1337 if (tb->graphic) {
1338 if (!tPtr->flags.monoFont) {
1339 if(tb->object) {
1340 WMWidget *wdt = tb->d.widget;
1341 line_height = WMAX(line_height, WMWidgetHeight(wdt));
1342 if (tPtr->flags.alignment != WALeft)
1343 lw += WMWidgetWidth(wdt);
1344 } else {
1345 line_height = WMAX(line_height, tb->d.pixmap->height + max_d);
1346 if (tPtr->flags.alignment != WALeft)
1347 lw += tb->d.pixmap->width;
1351 } else {
1352 font = (tPtr->flags.monoFont)?tPtr->dFont : tb->d.font;
1353 max_d = WMAX(max_d, abs(font->height-font->y));
1354 line_height = WMAX(line_height, font->height + max_d);
1355 text = &(tb->text[items[i].begin]);
1356 len = items[i].end - items[i].begin;
1357 if (tPtr->flags.alignment != WALeft)
1358 lw += WMWidthOfString(font, text, len);
1362 if (tPtr->flags.alignment == WARight) {
1363 j = tPtr->visible.w - lw;
1364 } else if (tPtr->flags.alignment == WACenter) {
1365 j = (int) ((float)(tPtr->visible.w - lw))/2.0;
1368 for(i=0; i<nitems; i++) {
1369 tb = items[i].tb;
1371 if (tbsame == tb) { /*extend it, since it's on same line */
1372 tb->sections[tb->nsections-1].end = items[i].end;
1373 n = tb->nsections-1;
1374 } else {
1375 tb->sections = wrealloc(tb->sections,
1376 (++tb->nsections)*sizeof(Section));
1377 n = tb->nsections-1;
1378 tb->sections[n]._y = y + max_d;
1379 tb->sections[n].x = x+j;
1380 tb->sections[n].h = line_height;
1381 tb->sections[n].begin = items[i].begin;
1382 tb->sections[n].end = items[i].end;
1384 if (tb->graphic && tb->object) {
1385 tb->sections[n].x += tPtr->visible.x;
1386 tb->sections[n].y += tPtr->visible.y;
1390 tb->sections[n].last = (i+1 == nitems);
1392 if (tb->graphic) {
1393 if (!tPtr->flags.monoFont) {
1394 if(tb->object) {
1395 WMWidget *wdt = tb->d.widget;
1396 tb->sections[n].y = max_d + y
1397 + line_height - WMWidgetHeight(wdt);
1398 tb->sections[n].w = WMWidgetWidth(wdt);
1399 } else {
1400 tb->sections[n].y = y + max_d;
1401 tb->sections[n].w = tb->d.pixmap->width;
1403 x += tb->sections[n].w;
1405 } else {
1406 font = (tPtr->flags.monoFont)? tPtr->dFont : tb->d.font;
1407 len = items[i].end - items[i].begin;
1408 text = &(tb->text[items[i].begin]);
1410 tb->sections[n].y = y+line_height-font->y;
1411 tb->sections[n].w =
1412 WMWidthOfString(font,
1413 &(tb->text[tb->sections[n].begin]),
1414 tb->sections[n].end - tb->sections[n].begin);
1416 x += WMWidthOfString(font, text, len);
1419 tbsame = tb;
1422 return line_height;
1427 static void
1428 layOutDocument(Text *tPtr)
1430 TextBlock *tb;
1431 myLineItems *items = NULL;
1432 unsigned int itemsSize=0, nitems=0;
1433 WMFont *font;
1434 unsigned int x, y=0, prev_y, lw = 0, width=0, bmargin;
1437 Bool lhc = !tPtr->flags.laidOut; /* line height changed? */
1439 char *start=NULL, *mark=NULL;
1440 unsigned int begin, end;
1442 if (tPtr->flags.frozen)
1443 return;
1445 if (!(tb = tPtr->firstTextBlock))
1446 return;
1448 tPtr->docWidth = tPtr->visible.w;
1449 x = 0; /* tPtr->margins[tb->marginN].first; */
1450 printf("x:%d\n", x);
1451 bmargin = tPtr->margins[tb->marginN].body;
1453 if (0&&tPtr->flags.laidOut) {
1454 tb = tPtr->currentTextBlock;
1455 if (tb->sections && tb->nsections>0)
1456 prev_y = tb->sections[tb->nsections-1]._y;
1457 y+=10;
1458 printf("1 prev_y %d \n", prev_y);
1460 /* search backwards for textblocks on same line */
1461 while (tb) {
1462 if (!tb->sections || tb->nsections<1) {
1463 tb = tPtr->firstTextBlock;
1464 break;
1466 if (tb->sections[tb->nsections-1]._y != prev_y) {
1467 tb = tb->next;
1468 break;
1470 /* prev_y = tb->sections[tb->nsections-1]._y; */
1471 tb = tb->prior;
1473 y = 0; /* tb->sections[tb->nsections-1]._y; */
1474 printf("2 prev_y %d \n\n", tb->sections[tb->nsections-1]._y);
1478 while (tb) {
1480 if (tb->sections && tb->nsections>0) {
1481 wfree(tb->sections);
1482 tb->sections = NULL;
1483 tb->nsections = 0;
1486 if (tb->first && tb != tPtr->firstTextBlock) {
1487 y += layOutLine(tPtr, items, nitems, x, y);
1488 x = 0*tPtr->margins[tb->marginN].first;
1489 bmargin = tPtr->margins[tb->marginN].body;
1490 nitems = 0;
1491 lw = 0;
1494 if (tb->graphic) {
1495 if (!tPtr->flags.monoFont) {
1496 if(tb->object)
1497 width = WMWidgetWidth(tb->d.widget);
1498 else
1499 width = tb->d.pixmap->width;
1501 if (width > tPtr->docWidth)
1502 tPtr->docWidth = width;
1504 lw += width;
1505 if (lw >= tPtr->visible.w - x ) {
1506 y += layOutLine(tPtr, items, nitems, x, y);
1507 nitems = 0;
1508 x = 0*bmargin;
1509 lw = width;
1512 if(nitems + 1> itemsSize) {
1513 items = wrealloc(items,
1514 (++itemsSize)*sizeof(myLineItems));
1517 items[nitems].tb = tb;
1518 items[nitems].begin = 0;
1519 items[nitems].end = 0;
1520 nitems++;
1523 } else if ((start = tb->text)) {
1524 begin = end = 0;
1525 font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
1527 while (start) {
1528 mark = mystrchr(start, ' ', tb->used);
1529 if (mark) {
1530 end += (int)(mark-start)+1;
1531 start = mark+1;
1532 } else {
1533 end += strlen(start);
1534 start = mark;
1537 if (end > tb->used)
1538 end = tb->used;
1540 if (end-begin > 0) {
1542 width = WMWidthOfString(font,
1543 &tb->text[begin], end-begin);
1545 /* if it won't fit, break it up */
1546 if (width > tPtr->visible.w) {
1547 char *t = &tb->text[begin];
1548 int l=end-begin, i=0;
1549 do {
1550 width = WMWidthOfString(font, t, ++i);
1551 } while (width < tPtr->visible.w && i < l);
1552 end = begin+i;
1553 if (start)
1554 start -= l-i;
1557 lw += width;
1560 if (lw >= tPtr->visible.w - x) {
1561 y += layOutLine(tPtr, items, nitems, x, y);
1562 lw = width;
1563 x = bmargin;
1564 nitems = 0;
1567 if(nitems + 1 > itemsSize) {
1568 items = wrealloc(items,
1569 (++itemsSize)*sizeof(myLineItems));
1572 items[nitems].tb = tb;
1573 items[nitems].begin = begin;
1574 items[nitems].end = end;
1575 nitems++;
1577 begin = end;
1580 tb = tb->next;
1584 if (nitems > 0)
1585 y += layOutLine(tPtr, items, nitems, x, y);
1586 if (lhc) {
1587 tPtr->docHeight = y+10;
1588 updateScrollers(tPtr);
1591 if(tPtr->docWidth > tPtr->visible.w && !tPtr->hS) {
1592 XEvent event;
1594 tPtr->flags.horizOnDemand = True;
1595 WMSetTextHasHorizontalScroller((WMText*)tPtr, True);
1596 event.type = Expose;
1597 handleEvents(&event, (void *)tPtr);
1599 } else if(tPtr->docWidth <= tPtr->visible.w
1600 && tPtr->hS && tPtr->flags.horizOnDemand ) {
1601 tPtr->flags.horizOnDemand = False;
1602 WMSetTextHasHorizontalScroller((WMText*)tPtr, False);
1604 tPtr->flags.laidOut = True;
1607 if(items && itemsSize > 0)
1608 wfree(items);
1612 static void
1613 textDidResize(W_ViewDelegate *self, WMView *view)
1615 Text *tPtr = (Text *)view->self;
1616 unsigned short w = tPtr->view->size.width;
1617 unsigned short h = tPtr->view->size.height;
1618 unsigned short rh = 0, vw = 0;
1620 if (tPtr->ruler && tPtr->flags.rulerShown) {
1621 WMMoveWidget(tPtr->ruler, 2, 2);
1622 WMResizeWidget(tPtr->ruler, w - 4, 40);
1623 rh = 40;
1626 if (tPtr->vS) {
1627 WMMoveWidget(tPtr->vS, 1, rh + 1);
1628 WMResizeWidget(tPtr->vS, 20, h - rh - 2);
1629 vw = 20;
1630 WMSetRulerOffset(tPtr->ruler,22);
1631 } else WMSetRulerOffset(tPtr->ruler, 2);
1633 if (tPtr->hS) {
1634 if (tPtr->vS) {
1635 WMMoveWidget(tPtr->hS, vw, h - 21);
1636 WMResizeWidget(tPtr->hS, w - vw - 1, 20);
1637 } else {
1638 WMMoveWidget(tPtr->hS, vw+1, h - 21);
1639 WMResizeWidget(tPtr->hS, w - vw - 2, 20);
1643 tPtr->visible.x = (tPtr->vS)?24:4;
1644 tPtr->visible.y = (tPtr->ruler && tPtr->flags.rulerShown)?43:3;
1645 tPtr->visible.w = tPtr->view->size.width - tPtr->visible.x - 8;
1646 tPtr->visible.h = tPtr->view->size.height - tPtr->visible.y;
1647 tPtr->visible.h -= (tPtr->hS)?20:0;
1648 tPtr->margins[0].right = tPtr->visible.w;
1650 if (tPtr->view->flags.realized) {
1652 if (tPtr->db) {
1653 XFreePixmap(tPtr->view->screen->display, tPtr->db);
1654 tPtr->db = (Pixmap) NULL;
1657 if (tPtr->visible.w < 40)
1658 tPtr->visible.w = 40;
1659 if (tPtr->visible.h < 20)
1660 tPtr->visible.h = 20;
1662 if(!tPtr->db) {
1663 tPtr->db = XCreatePixmap(tPtr->view->screen->display,
1664 tPtr->view->window, tPtr->visible.w,
1665 tPtr->visible.h, tPtr->view->screen->depth);
1669 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1672 W_ViewDelegate _TextViewDelegate =
1674 NULL,
1675 NULL,
1676 textDidResize,
1677 NULL,
1680 /* nice, divisble-by-16 blocks */
1681 static inline unsigned short
1682 reqBlockSize(unsigned short requested)
1684 return requested + 16 - (requested%16);
1688 static void
1689 clearText(Text *tPtr)
1691 if (!tPtr->firstTextBlock)
1692 return;
1694 while (tPtr->currentTextBlock)
1695 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1697 tPtr->firstTextBlock = NULL;
1698 tPtr->currentTextBlock = NULL;
1699 tPtr->lastTextBlock = NULL;
1702 static void
1703 deleteTextInteractively(Text *tPtr, KeySym ksym)
1705 TextBlock *tb = tPtr->currentTextBlock;
1706 Bool back = (Bool) (ksym == XK_BackSpace);
1707 Bool done = 1;
1709 if (!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1710 XBell(tPtr->view->screen->display, 0);
1711 return;
1714 if (!tb)
1715 return;
1717 tPtr->flags.needsRefresh = True;
1719 if (tPtr->flags.ownsSelection) {
1720 removeSelection(tPtr);
1721 return;
1724 if (back && tPtr->tpos < 1) {
1725 if (tb->prior) {
1726 tb = tb->prior;
1727 tb->first = False;
1728 tPtr->tpos = tb->used;
1729 tPtr->currentTextBlock = tb;
1730 done = 1;
1734 if ( (tb->used > 0) && ((back?tPtr->tpos > 0:1))
1735 && (tPtr->tpos <= tb->used) && !tb->graphic) {
1736 if (back)
1737 tPtr->tpos--;
1738 memmove(&(tb->text[tPtr->tpos]),
1739 &(tb->text[tPtr->tpos + 1]), tb->used - tPtr->tpos);
1740 tb->used--;
1741 done = 0;
1744 if ( (back? (tPtr->tpos < 1 && !done) : ( tPtr->tpos >= tb->used))
1745 || tb->graphic) {
1747 TextBlock *sibling = (back? tb->prior : tb->next);
1749 if(tb->used == 0 || tb->graphic)
1750 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1752 if (sibling) {
1753 tPtr->currentTextBlock = sibling;
1754 tPtr->tpos = (back? sibling->used : 0);
1758 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1762 static void
1763 insertTextInteractively(Text *tPtr, char *text, int len)
1765 TextBlock *tb;
1766 char *newline = NULL;
1768 if (!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1769 XBell(tPtr->view->screen->display, 0);
1770 return;
1773 if (len < 1 || !text)
1774 return;
1777 if(tPtr->flags.ignoreNewLine && *text == '\n' && len == 1)
1778 return;
1780 if (tPtr->flags.ownsSelection)
1781 removeSelection(tPtr);
1783 tPtr->flags.needsRefresh = True;
1785 if (tPtr->flags.ignoreNewLine) {
1786 int i;
1787 for(i=0; i<len; i++) {
1788 if (text[i] == '\n')
1789 text[i] = ' ';
1793 tb = tPtr->currentTextBlock;
1794 if (!tb || tb->graphic) {
1795 text[len] = 0;
1796 WMAppendTextStream(tPtr, text);
1797 if (tPtr->currentTextBlock) {
1798 tPtr->tpos = tPtr->currentTextBlock->used;
1800 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1801 return;
1804 if ((newline = mystrchr(text, '\n', len))) {
1805 int nlen = (int)(newline-text);
1806 int s = tb->used - tPtr->tpos;
1807 char save[s];
1809 if (!tb->blank && nlen>0) {
1810 if (s > 0) {
1811 memcpy(save, &tb->text[tPtr->tpos], s);
1812 tb->used -= (tb->used - tPtr->tpos);
1814 text[nlen] = 0;
1815 insertTextInteractively(tPtr, text, nlen);
1816 newline++;
1817 WMAppendTextStream(tPtr, newline);
1818 if (s>0)
1819 insertTextInteractively(tPtr, save, s);
1821 } else {
1822 if (tPtr->tpos>0 && tPtr->tpos < tb->used
1823 && !tb->graphic && tb->text) {
1825 void *ntb = WMCreateTextBlockWithText(
1826 tPtr, &tb->text[tPtr->tpos],
1827 tb->d.font, tb->color, True, tb->used - tPtr->tpos);
1828 tb->used = tPtr->tpos;
1829 WMAppendTextBlock(tPtr, ntb);
1830 tPtr->tpos = 0;
1831 } else if (tPtr->tpos == tb->used || tPtr->tpos == 0) {
1832 void *ntb = WMCreateTextBlockWithText(tPtr,
1833 NULL, tb->d.font, tb->color, True, 0);
1835 if (tPtr->tpos>0)
1836 WMAppendTextBlock(tPtr, ntb);
1837 else
1838 WMPrependTextBlock(tPtr, ntb);
1839 tPtr->tpos = 1;
1843 } else {
1845 if (tb->used + len >= tb->allocated) {
1846 tb->allocated = reqBlockSize(tb->used+len);
1847 tb->text = wrealloc(tb->text, tb->allocated);
1850 if (tb->blank) {
1851 memcpy(tb->text, text, len);
1852 tb->used = len;
1853 tPtr->tpos = len;
1854 tb->blank = False;
1855 } else {
1856 memmove(&(tb->text[tPtr->tpos+len]), &tb->text[tPtr->tpos],
1857 tb->used-tPtr->tpos+1);
1858 memmove(&tb->text[tPtr->tpos], text, len);
1859 tb->used += len;
1860 tPtr->tpos += len;
1865 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1869 static void
1870 selectRegion(Text *tPtr, int x, int y)
1873 if (x < 0 || y < 0)
1874 return;
1876 y += (tPtr->flags.rulerShown? 40: 0);
1877 y += tPtr->vpos;
1878 if (y>10)
1879 y -= 10; /* the original offset */
1881 x -= tPtr->visible.x-2;
1882 if (x<0)
1883 x=0;
1885 tPtr->sel.x = WMAX(0, WMIN(tPtr->clicked.x, x));
1886 tPtr->sel.w = abs(tPtr->clicked.x - x);
1887 tPtr->sel.y = WMAX(0, WMIN(tPtr->clicked.y, y));
1888 tPtr->sel.h = abs(tPtr->clicked.y - y);
1890 tPtr->flags.ownsSelection = True;
1891 paintText(tPtr);
1895 static void
1896 releaseSelection(Text *tPtr)
1898 TextBlock *tb = tPtr->firstTextBlock;
1900 while(tb) {
1901 tb->selected = False;
1902 tb = tb->next;
1904 tPtr->flags.ownsSelection = False;
1905 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY,
1906 CurrentTime);
1908 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1912 WMData*
1913 requestHandler(WMView *view, Atom selection, Atom target, void *cdata,
1914 Atom *type)
1916 Text *tPtr = view->self;
1917 Display *dpy = tPtr->view->screen->display;
1918 Atom _TARGETS;
1919 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1920 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1921 WMData *data = NULL;
1924 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
1925 char *text = WMGetTextSelected(tPtr);
1927 if (text) {
1928 printf("got text [%s]\n", text);
1929 data = WMCreateDataWithBytes(text, strlen(text));
1930 WMSetDataFormat(data, 8);
1932 *type = target;
1933 return data;
1934 } else printf("didn't get it\n");
1936 _TARGETS = XInternAtom(dpy, "TARGETS", False);
1937 if (target == _TARGETS) {
1938 Atom *ptr;
1940 ptr = wmalloc(4 * sizeof(Atom));
1941 ptr[0] = _TARGETS;
1942 ptr[1] = XA_STRING;
1943 ptr[2] = TEXT;
1944 ptr[3] = COMPOUND_TEXT;
1946 data = WMCreateDataWithBytes(ptr, 4*4);
1947 WMSetDataFormat(data, 32);
1949 *type = target;
1950 return data;
1953 return NULL;
1956 static void
1957 lostHandler(WMView *view, Atom selection, void *cdata)
1959 releaseSelection((WMText *)view->self);
1962 static WMSelectionProcs selectionHandler = {
1963 requestHandler, lostHandler, NULL
1967 static void
1968 ownershipObserver(void *observerData, WMNotification *notification)
1970 if (observerData != WMGetNotificationClientData(notification))
1971 lostHandler(WMWidgetView(observerData), XA_PRIMARY, NULL);
1975 static void
1976 fontChanged(void *observerData, WMNotification *notification)
1978 WMText *tPtr = (WMText *) observerData;
1979 WMFont *font = (WMFont *)WMGetNotificationClientData(notification);
1980 printf("fontChanged\n");
1982 if(!tPtr || !font)
1983 return;
1985 if (tPtr->flags.ownsSelection)
1986 WMSetTextSelectionFont(tPtr, font);
1990 static void
1991 handleTextKeyPress(Text *tPtr, XEvent *event)
1993 char buffer[2];
1994 KeySym ksym;
1995 int control_pressed = False;
1996 TextBlock *tb = NULL;
1998 if (((XKeyEvent *) event)->state & ControlMask)
1999 control_pressed = True;
2000 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = 0;
2002 switch(ksym) {
2004 case XK_Left:
2005 if(!(tb = tPtr->currentTextBlock))
2006 break;
2007 if(tb->graphic)
2008 goto L_imaGFX;
2010 if(tPtr->tpos==0) {
2011 L_imaGFX: if(tb->prior) {
2012 tPtr->currentTextBlock = tb->prior;
2013 tPtr->tpos = tPtr->currentTextBlock->used -1;
2014 } else tPtr->tpos = 0;
2015 } else tPtr->tpos--;
2016 updateCursorPosition(tPtr);
2017 break;
2019 case XK_Right:
2020 printf("foind %d \n", WMFindInTextStream(tPtr, "It", 1, 0));
2021 return;
2022 if(!(tb = tPtr->currentTextBlock))
2023 break;
2024 if(tb->graphic)
2025 goto R_imaGFX;
2026 if(tPtr->tpos == tb->used) {
2027 R_imaGFX: if(tb->next) {
2028 tPtr->currentTextBlock = tb->next;
2029 tPtr->tpos = 1;
2030 } else tPtr->tpos = tb->used;
2031 } else tPtr->tpos++;
2032 updateCursorPosition(tPtr);
2033 break;
2035 case XK_Down:
2036 cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
2037 tPtr->clicked.y + tPtr->cursor.h - tPtr->vpos);
2038 paintText(tPtr);
2039 break;
2041 case XK_Up:
2042 cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
2043 tPtr->visible.y + tPtr->cursor.y - tPtr->vpos - 3);
2044 paintText(tPtr);
2045 break;
2047 case XK_BackSpace:
2048 case XK_Delete:
2049 case XK_KP_Delete:
2050 deleteTextInteractively(tPtr, ksym);
2051 updateCursorPosition(tPtr);
2052 break;
2054 case XK_Control_R :
2055 case XK_Control_L :
2056 control_pressed = True;
2057 break;
2059 case XK_Return:
2060 buffer[0] = '\n';
2061 default:
2062 if (buffer[0] != 0 && !control_pressed) {
2063 insertTextInteractively(tPtr, buffer, 1);
2064 updateCursorPosition(tPtr);
2066 } else if (control_pressed && ksym==XK_r) {
2067 Bool i = !tPtr->flags.rulerShown;
2068 WMShowTextRuler(tPtr, i);
2069 tPtr->flags.rulerShown = i;
2071 else if (control_pressed && buffer[0] == '\a')
2072 XBell(tPtr->view->screen->display, 0);
2075 if (!control_pressed && tPtr->flags.ownsSelection)
2076 releaseSelection(tPtr);
2079 static void
2080 handleWidgetPress(XEvent *event, void *data)
2082 TextBlock *tb = (TextBlock *)data;
2083 Text *tPtr;
2084 WMWidget *w;
2086 if (!tb)
2087 return;
2088 /* this little bit of nastiness here saves a boatload of trouble */
2089 w = (WMWidget *)(((W_VIEW(tb->d.widget))->parent)->self);
2090 if (W_CLASS(w) != WC_Text)
2091 return;
2092 tPtr = (Text*)w;
2093 tPtr->currentTextBlock = getFirstNonGraphicBlockFor(tb, 1);
2094 if (!tPtr->currentTextBlock)
2095 tPtr->currentTextBlock = tb;
2096 tPtr->tpos = 0;
2097 output(tPtr->currentTextBlock->text, tPtr->currentTextBlock->used);
2098 #if 0
2099 if (!tPtr->flags.focused) {
2100 WMSetFocusToWidget(tPtr);
2101 tPtr->flags.focused = True;
2103 #endif
2107 static void
2108 handleActionEvents(XEvent *event, void *data)
2110 Text *tPtr = (Text *)data;
2111 Display *dpy = event->xany.display;
2112 KeySym ksym;
2115 switch (event->type) {
2116 case KeyPress:
2117 ksym = XLookupKeysym((XKeyEvent*)event, 0);
2118 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
2119 tPtr->flags.extendSelection = True;
2120 return;
2123 if (tPtr->flags.focused) {
2124 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
2125 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
2126 GrabModeAsync, GrabModeAsync, None,
2127 tPtr->view->screen->invisibleCursor, CurrentTime);
2128 tPtr->flags.pointerGrabbed = True;
2129 handleTextKeyPress(tPtr, event);
2131 } break;
2133 case KeyRelease:
2134 ksym = XLookupKeysym((XKeyEvent*)event, 0);
2135 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
2136 tPtr->flags.extendSelection = False;
2137 return;
2138 /* end modify flag so selection can be extended */
2140 break;
2143 case MotionNotify:
2145 if (tPtr->flags.pointerGrabbed) {
2146 tPtr->flags.pointerGrabbed = False;
2147 XUngrabPointer(dpy, CurrentTime);
2150 if(tPtr->flags.waitingForSelection)
2151 break;
2153 if ((event->xmotion.state & Button1Mask)) {
2154 if (!tPtr->flags.ownsSelection) {
2155 WMCreateSelectionHandler(tPtr->view,
2156 XA_PRIMARY, event->xbutton.time,
2157 &selectionHandler, NULL);
2158 tPtr->flags.ownsSelection = True;
2160 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
2162 break;
2165 case ButtonPress:
2167 tPtr->flags.buttonHeld = True;
2169 if (tPtr->flags.pointerGrabbed) {
2170 tPtr->flags.pointerGrabbed = False;
2171 XUngrabPointer(dpy, CurrentTime);
2172 break;
2175 if (tPtr->flags.waitingForSelection)
2176 break;
2178 if (tPtr->flags.extendSelection) {
2179 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
2180 return;
2184 if (event->xbutton.button == Button1) {
2186 if(WMIsDoubleClick(event)) {
2187 printf("lastClickTime: %d \n", event->xbutton.time -tPtr->lastClickTime);
2188 autoSelectText(tPtr, 2);
2189 tPtr->lastClickTime = event->xbutton.time;
2190 break;
2192 if(0&&event->xbutton.time - tPtr->lastClickTime
2193 < 1.5*WINGsConfiguration.doubleClickDelay) {
2194 ;// autoSelectText(tPtr, 3);
2195 break;
2198 //WMGetTextStreamIntoArray(tPtr);
2200 if (!tPtr->flags.focused) {
2201 WMSetFocusToWidget(tPtr);
2202 tPtr->flags.focused = True;
2205 if (tPtr->flags.ownsSelection)
2206 releaseSelection(tPtr);
2208 tPtr->lastClickTime = event->xbutton.time;
2209 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
2210 paintText(tPtr);
2213 if (event->xbutton.button
2214 == WINGsConfiguration.mouseWheelDown) {
2215 WMScrollText(tPtr, -16);
2216 break;
2219 if (event->xbutton.button
2220 == WINGsConfiguration.mouseWheelUp) {
2221 WMScrollText(tPtr, 16);
2222 break;
2225 if (event->xbutton.button == Button2) {
2226 char *text = NULL;
2227 int n;
2229 if (!tPtr->flags.editable) {
2230 XBell(dpy, 0);
2231 break;
2234 #if 0
2235 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
2236 event->xbutton.time, pasteText, NULL)) {
2237 #endif
2240 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
2242 if (text) {
2243 text[n] = 0;
2245 if (tPtr->parser)
2246 (tPtr->parser) (tPtr, (void *) text);
2247 else
2248 insertTextInteractively(tPtr, text, n);
2250 XFree(text);
2251 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
2252 (void*)WMInsertTextEvent);
2254 } else {
2255 tPtr->flags.waitingForSelection = True;
2258 break;
2262 case ButtonRelease:
2264 tPtr->flags.buttonHeld = False;
2266 if (tPtr->flags.pointerGrabbed) {
2267 tPtr->flags.pointerGrabbed = False;
2268 XUngrabPointer(dpy, CurrentTime);
2269 break;
2272 if (tPtr->flags.waitingForSelection)
2273 break;
2279 static void
2280 handleEvents(XEvent *event, void *data)
2282 Text *tPtr = (Text *)data;
2284 switch(event->type) {
2285 case Expose:
2287 if (event->xexpose.count!=0)
2288 break;
2290 if(tPtr->hS) {
2291 if (!(W_VIEW(tPtr->hS))->flags.realized)
2292 WMRealizeWidget(tPtr->hS);
2293 if (!((W_VIEW(tPtr->hS))->flags.mapped))
2294 WMMapWidget(tPtr->hS);
2297 if(tPtr->vS) {
2298 if (!(W_VIEW(tPtr->vS))->flags.realized)
2299 WMRealizeWidget(tPtr->vS);
2300 if (!((W_VIEW(tPtr->vS))->flags.mapped))
2301 WMMapWidget(tPtr->vS);
2304 if(tPtr->ruler) {
2305 if (!(W_VIEW(tPtr->ruler))->flags.realized)
2306 WMRealizeWidget(tPtr->ruler);
2308 if (!((W_VIEW(tPtr->ruler))->flags.mapped)
2309 && tPtr->flags.rulerShown)
2310 WMMapWidget(tPtr->ruler);
2313 if(!tPtr->db)
2314 textDidResize(tPtr->view->delegate, tPtr->view);
2316 paintText(tPtr);
2317 break;
2319 case FocusIn:
2320 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))
2321 != tPtr->view)
2322 return;
2323 tPtr->flags.focused = True;
2324 #if DO_BLINK
2325 if (tPtr->flags.editable && !tPtr->timerID) {
2326 tPtr->timerID = WMAddTimerHandler(12+0*CURSOR_BLINK_ON_DELAY,
2327 blinkCursor, tPtr);
2329 #endif
2331 break;
2333 case FocusOut:
2334 tPtr->flags.focused = False;
2335 paintText(tPtr);
2336 #if DO_BLINK
2337 if (tPtr->timerID) {
2338 WMDeleteTimerHandler(tPtr->timerID);
2339 tPtr->timerID = NULL;
2341 #endif
2342 break;
2345 case DestroyNotify:
2346 clearText(tPtr);
2347 if(tPtr->hS)
2348 WMDestroyWidget(tPtr->hS);
2349 if(tPtr->vS)
2350 WMDestroyWidget(tPtr->vS);
2351 if(tPtr->ruler)
2352 WMDestroyWidget(tPtr->ruler);
2353 if(tPtr->db)
2354 XFreePixmap(tPtr->view->screen->display, tPtr->db);
2355 if(tPtr->gfxItems)
2356 WMFreeArray(tPtr->gfxItems);
2357 #if DO_BLINK
2358 if (tPtr->timerID)
2359 WMDeleteTimerHandler(tPtr->timerID);
2360 #endif
2361 WMReleaseFont(tPtr->dFont);
2362 WMReleaseColor(tPtr->dColor);
2363 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
2364 WMRemoveNotificationObserver(tPtr);
2366 wfree(tPtr);
2368 break;
2374 static void
2375 insertText(WMText *tPtr, char *stream)
2377 char *ptr, *begin, *end;
2378 void *tb = NULL;
2379 char c;
2380 char b, e, again=False;
2381 unsigned long tokenLength, tk;
2382 unsigned long textLength, tx;
2384 if (!stream) {
2385 clearText(tPtr);
2386 return;
2389 WMAppendTextBlock(tPtr,
2390 WMCreateTextBlockWithText(tPtr, stream, tPtr->dFont, tPtr->dColor,
2391 1, strlen(stream)));
2392 return;
2393 ptr = stream;
2394 while (ptr) {
2395 again = False;
2396 b = '<'; e = '>';
2397 again:
2398 begin = strchr(ptr, b);
2399 if (begin) {
2400 end = strchr(begin+1, e);
2401 if(end) {
2402 if((int)(end-begin)-1 > 1)
2403 output(begin+1, (int)(end-begin)-1);
2405 ptr = begin+1;
2406 } else if(!again) {
2407 b = '>'; e = '<';
2408 again = True;
2409 printf("again!\n");
2410 goto again;
2411 } else {
2412 if (ptr && strlen(ptr)) {
2413 printf("boo\n");
2415 ptr = begin;
2419 #if 0
2420 while( (c=*(stream++))) {
2421 //printf("%c", c);
2422 if((c == '\n' && tPtr->flags.parsingHTML) || c =='\t')
2423 //c = ' '; //continue;
2424 continue;
2425 if(c == ' ') {
2426 if(wasspace)
2427 continue;
2428 wasspace = 1;
2429 }else wasspace = 0;
2431 if(c == '\n') {
2432 parseToken(tPtr, token, -1);
2434 } else if(c == '<' && !open) {
2435 open=1;
2436 if(textlen>0) {
2437 text[textlen] = 0;
2438 tb = WMCreateTextBlockWithText(tPtr, text, cfmt.cfont,
2439 cfmt.ccolor, cfmt.first, textlen);
2440 //WMSetTextBlockProperties(tb, cfmt.first, False, (cfmt.u?1:0), 0, cfmt.margins);
2441 WMAppendTextBlock(tPtr, tb);
2442 cfmt.first = False;
2443 //printf("%s\n", text);
2445 textlen = 0;
2446 } else if(c == '>' && open) {
2447 token[tk] = 0;
2448 if(tk>0) parseToken(tPtr, token, tk);
2449 open=0;
2450 tk=0;
2451 } else {
2452 if(open) {
2453 if(tk < MAX_TOKEN_SIZE) token[tk++] = c;
2454 } else if(textlen < MAX_TEXT_SIZE) text[textlen++] = c;
2458 if(tk>0) { token[tk] = 0; parseToken(tPtr, token, tk);}
2459 if(textlen>0) {
2460 text[textlen] = 0;
2461 //printf("%s\n", text);
2462 tb = WMCreateTextBlockWithText(tPtr, text,
2463 (WMFont *)WMGetFromArray(cfmt.fonts,
2464 WMGetArrayItemCount(cfmt.fonts)-1),
2465 (WMColor *)WMGetFromArray(cfmt.colors,
2466 WMGetArrayItemCount(cfmt.colors)-1),
2467 cfmt.first, textlen);
2468 //WMSetTextBlockProperties(tb, cfmt.first, False, (cfmt.u?1:0), 0, cfmt.margins);
2469 WMAppendTextBlock(tPtr, tb);
2470 cfmt.first = False;
2472 #endif
2477 #if 0
2478 start = text;
2479 while (start) {
2480 mark = strchr(start, '\n');
2481 if (mark) {
2482 tb = WMCreateTextBlockWithText(tPtr,
2483 start, tPtr->dFont,
2484 tPtr->dColor, True, (int)(mark-start));
2485 start = mark+1;
2486 } else {
2487 if (start && strlen(start)) {
2488 tb = WMCreateTextBlockWithText(tPtr, start, tPtr->dFont,
2489 tPtr->dColor, False, strlen(start));
2490 } else tb = NULL;
2491 start = mark;
2494 if (tPtr->flags.prepend)
2495 WMPrependTextBlock(tPtr, tb);
2496 else
2497 WMAppendTextBlock(tPtr, tb);
2499 return;
2500 #endif
2505 static void
2506 rulerMoveCallBack(WMWidget *w, void *self)
2508 Text *tPtr = (Text *)self;
2509 if (!tPtr)
2510 return;
2511 if (W_CLASS(tPtr) != WC_Text)
2512 return;
2514 paintText(tPtr);
2518 static void
2519 rulerReleaseCallBack(WMWidget *w, void *self)
2521 Text *tPtr = (Text *)self;
2522 if (!tPtr)
2523 return;
2524 if (W_CLASS(tPtr) != WC_Text)
2525 return;
2527 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2528 return;
2532 static unsigned
2533 draggingEntered(WMView *self, WMDraggingInfo *info)
2535 printf("draggingEntered\n");
2536 return WDOperationCopy;
2540 static unsigned
2541 draggingUpdated(WMView *self, WMDraggingInfo *info)
2543 return WDOperationCopy;
2547 static void
2548 draggingExited(WMView *self, WMDraggingInfo *info)
2550 printf("draggingExited\n");
2553 static Bool
2554 prepareForDragOperation(WMView *self, WMDraggingInfo *info)
2556 printf("prepareForDragOperation\n");
2557 return True;
2561 char *badbadbad;
2563 receivedData(WMView *view, Atom selection, Atom target, Time timestamp,
2564 void *cdata, WMData *data)
2566 badbadbad = wstrdup((char *)WMDataBytes(data));
2570 /* when it's done in WINGs, remove this */
2572 Bool requestDroppedData(WMView *view, WMDraggingInfo *info, char *type)
2574 WMScreen *scr = W_VIEW_SCREEN(view);
2576 if (!WMRequestSelection(scr->dragInfo.destView,
2577 scr->xdndSelectionAtom,
2578 XInternAtom(scr->display, type, False),
2579 scr->dragInfo.timestamp,
2580 receivedData, &scr->dragInfo)) {
2581 wwarning("could not request data for dropped data");
2586 XEvent ev;
2588 ev.type = ClientMessage;
2589 ev.xclient.message_type = scr->xdndFinishedAtom;
2590 ev.xclient.format = 32;
2591 ev.xclient.window = info->destinationWindow;
2592 ev.xclient.data.l[0] = 0;
2593 ev.xclient.data.l[1] = 0;
2594 ev.xclient.data.l[2] = 0;
2595 ev.xclient.data.l[3] = 0;
2596 ev.xclient.data.l[4] = 0;
2598 XSendEvent(scr->display, info->sourceWindow, False, 0, &ev);
2599 XFlush(scr->display);
2603 static Bool
2604 performDragOperation(WMView *self, WMDraggingInfo *info, WMData *data)
2606 WMColor *color;
2607 WMText *tPtr = (WMText *)self->self;
2609 if (!tPtr)
2610 return True;
2612 requestDroppedData(tPtr->view, info, "application/X-color");
2613 color = WMCreateNamedColor(W_VIEW_SCREEN(self), badbadbad, True);
2614 if(color) {
2615 WMSetTextSelectionColor(tPtr, color);
2616 WMReleaseColor(color);
2621 return True;
2624 static void
2625 concludeDragOperation(WMView *self, WMDraggingInfo *info)
2627 printf("concludeDragOperation\n");
2631 static WMDragDestinationProcs _DragDestinationProcs = {
2632 draggingEntered,
2633 draggingUpdated,
2634 draggingExited,
2635 prepareForDragOperation,
2636 performDragOperation,
2637 concludeDragOperation
2640 static void
2641 releaseArrayData(void *data)
2643 if(data)
2644 wfree(data);
2648 char *
2649 getStream(WMText *tPtr, int sel, int array)
2651 TextBlock *tb = NULL;
2652 char *text = NULL;
2653 unsigned long where = 0;
2655 if (!tPtr)
2656 return NULL;
2658 if (!(tb = tPtr->firstTextBlock))
2659 return NULL;
2661 /* this might be tricky to get right... not yet implemented */
2662 if (tPtr->writer) {
2663 (tPtr->writer) (tPtr, (void *) text);
2664 return text;
2667 tb = tPtr->firstTextBlock;
2668 while (tb) {
2670 if (!tb->graphic || (tb->graphic && !tPtr->flags.monoFont)) {
2672 if (!sel || (tb->graphic && tb->selected)) {
2674 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank)) {
2675 text = wrealloc(text, where+1);
2676 text[where++] = '\n';
2679 if(tb->graphic && array) {
2680 text = wrealloc(text, where+3);
2681 text[where++] = 'G';//0xFA;
2682 text[where++] = 'A';//tb->used;
2683 text[where++] = 'H';//18+tb->allocated;
2686 text = wrealloc(text, where+tb->used);
2687 memcpy(&text[where], tb->text, tb->used);
2688 where += tb->used;
2691 } else if (sel && tb->selected) {
2693 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank)) {
2694 text = wrealloc(text, where+1);
2695 text[where++] = '\n';
2698 text = wrealloc(text, where+(tb->s_end - tb->s_begin));
2699 memcpy(&text[where], &tb->text[tb->s_begin],
2700 tb->s_end - tb->s_begin);
2701 where += tb->s_end - tb->s_begin;
2706 tb = tb->next;
2709 /* +1 for the end of string, let's be nice */
2710 text = wrealloc(text, where+1);
2711 text[where] = 0;
2712 return text;
2717 WMArray *
2718 getStreamIntoArray(WMText *tPtr, int sel)
2720 WMArray *array = WMCreateArray(4);
2721 WMData *data;
2722 char *stream;
2723 unsigned long loc=0, len;
2724 unsigned long end;
2725 int i, count;
2728 stream = getStream(tPtr, sel, 0);
2729 if(!stream)
2730 return NULL;
2733 printf(stream);
2734 return;
2735 end = strlen(stream);
2737 #if 0
2738 data = WMCreateDataWithBytes((void *)(fa+2), len);
2739 WMSetDataFormat(data, 32);
2740 WMAddToArray(array, (void *) data);
2741 start = fa + len + 2;
2743 } else {
2744 if (start && strlen(start)) {
2745 data = WMCreateDataWithBytes((void *)start, strlen(start));
2746 WMSetDataFormat(data, 8);
2747 WMAddToArray(array, (void *) data);
2749 start = fa;
2753 wfree(stream);
2754 return array;
2755 #endif
2756 WMFreeArray(array);
2760 WMText *
2761 WMCreateText(WMWidget *parent)
2763 Text *tPtr = wmalloc(sizeof(Text));
2764 if (!tPtr) {
2765 printf("could not create text widget\n");
2766 return NULL;
2770 memset(tPtr, 0, sizeof(Text));
2771 tPtr->widgetClass = WC_Text;
2772 tPtr->view = W_CreateView(W_VIEW(parent));
2773 if (!tPtr->view) {
2774 perror("could not create text's view\n");
2775 free(tPtr);
2776 return NULL;
2778 tPtr->view->self = tPtr;
2779 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
2780 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
2781 W_ResizeView(tPtr->view, 250, 200);
2783 tPtr->dColor = WMWhiteColor(tPtr->view->screen);
2784 tPtr->bgGC = WMColorGC(tPtr->dColor);
2785 W_SetViewBackgroundColor(tPtr->view, tPtr->dColor);
2786 WMReleaseColor(tPtr->dColor);
2788 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2789 tPtr->fgGC = WMColorGC(tPtr->dColor);
2791 tPtr->ruler = NULL;
2792 tPtr->vS = NULL;
2793 tPtr->hS = NULL;
2795 tPtr->dFont = WMRetainFont(WMSystemFontOfSize(tPtr->view->screen, 12));
2797 tPtr->view->delegate = &_TextViewDelegate;
2799 #if DO_BLINK
2800 tPtr->timerID = NULL;
2801 #endif
2803 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
2804 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
2805 handleEvents, tPtr);
2807 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
2808 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
2809 handleActionEvents, tPtr);
2811 WMAddNotificationObserver(ownershipObserver, tPtr,
2812 "_lostOwnership", tPtr);
2814 WMSetViewDragDestinationProcs(tPtr->view, &_DragDestinationProcs);
2816 char *types[2] = {"application/X-color", NULL};
2817 WMRegisterViewForDraggedTypes(tPtr->view, types);
2820 WMAddNotificationObserver(fontChanged, tPtr,
2821 "WMFontPanelDidChangeNotification", tPtr);
2823 tPtr->firstTextBlock = NULL;
2824 tPtr->lastTextBlock = NULL;
2825 tPtr->currentTextBlock = NULL;
2826 tPtr->tpos = 0;
2828 tPtr->gfxItems = WMCreateArray(4);
2830 tPtr->parser = NULL;
2831 tPtr->writer = NULL;
2833 tPtr->sel.x = tPtr->sel.y = 2;
2834 tPtr->sel.w = tPtr->sel.h = 0;
2836 tPtr->clicked.x = tPtr->clicked.y = 2;
2838 tPtr->visible.x = tPtr->visible.y = 2;
2839 tPtr->visible.h = tPtr->view->size.height;
2840 tPtr->visible.w = tPtr->view->size.width - 4;
2842 tPtr->cursor.x = -23;
2844 tPtr->docWidth = 0;
2845 tPtr->docHeight = 0;
2846 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
2847 default_bullet);
2848 tPtr->db = (Pixmap) NULL;
2850 tPtr->margins = WMGetRulerMargins(NULL);
2851 tPtr->margins->right = tPtr->visible.w;
2852 tPtr->nMargins = 1;
2854 tPtr->flags.rulerShown = False;
2855 tPtr->flags.monoFont = False;
2856 tPtr->flags.focused = False;
2857 tPtr->flags.editable = True;
2858 tPtr->flags.ownsSelection = False;
2859 tPtr->flags.pointerGrabbed = False;
2860 tPtr->flags.buttonHeld = False;
2861 tPtr->flags.extendSelection = False;
2862 tPtr->flags.frozen = False;
2863 tPtr->flags.cursorShown = True;
2864 tPtr->flags.clickPos = 1;
2865 tPtr->flags.horizOnDemand = False;
2866 tPtr->flags.needsRefresh = False;
2867 tPtr->flags.ignoreNewLine = False;
2868 tPtr->flags.laidOut = False;
2869 tPtr->flags.waitingForSelection = False;
2870 tPtr->flags.prepend = False;
2871 tPtr->flags.parsingHTML = False;
2872 tPtr->flags.relief = WRSunken;
2873 tPtr->flags.alignment = WALeft;
2875 return tPtr;
2878 void
2879 WMPrependTextStream(WMText *tPtr, char *text)
2881 CHECK_CLASS(tPtr, WC_Text);
2883 if(!text)
2884 releaseSelection(tPtr);
2886 tPtr->flags.prepend = True;
2887 if (text && tPtr->parser)
2888 (tPtr->parser) (tPtr, (void *) text);
2889 else
2890 insertText(tPtr, text);
2892 tPtr->flags.needsRefresh = True;
2896 void
2897 WMAppendTextStream(WMText *tPtr, char *text)
2899 CHECK_CLASS(tPtr, WC_Text);
2901 if(!text)
2902 releaseSelection(tPtr);
2904 tPtr->flags.prepend = False;
2905 if (text && tPtr->parser)
2906 (tPtr->parser) (tPtr, (void *) text);
2907 else
2908 insertText(tPtr, text);
2910 tPtr->flags.needsRefresh = True;
2915 char *
2916 WMGetTextStream(WMText *tPtr)
2918 CHECK_CLASS(tPtr, WC_Text);
2919 return getStream(tPtr, 0, 0);
2922 char *
2923 WMGetTextSelected(WMText *tPtr)
2925 CHECK_CLASS(tPtr, WC_Text);
2926 return getStream(tPtr, 1, 0);
2929 WMArray *
2930 WMGetTextStreamIntoArray(WMText *tPtr)
2932 CHECK_CLASS(tPtr, WC_Text);
2933 return getStreamIntoArray(tPtr, 0);
2936 WMArray *
2937 WMGetTextSelectedIntoArray(WMText *tPtr)
2939 CHECK_CLASS(tPtr, WC_Text);
2940 return getStreamIntoArray(tPtr, 1);
2944 void
2945 WMSetTextDelegate(WMText *tPtr, WMTextDelegate *delegate)
2947 CHECK_CLASS(tPtr, WC_Text);
2949 tPtr->delegate = delegate;
2953 void *
2954 WMCreateTextBlockWithObject(WMText *tPtr, WMWidget *w,
2955 char *description, WMColor *color,
2956 unsigned short first, unsigned short extraInfo)
2958 TextBlock *tb;
2959 unsigned short length;
2961 if (!w || !description || !color)
2962 return NULL;
2964 tb = wmalloc(sizeof(TextBlock));
2965 if (!tb)
2966 return NULL;
2968 length = strlen(description);
2969 tb->text = (char *)wmalloc(length);
2970 memset(tb->text, 0, length);
2971 memcpy(tb->text, description, length);
2972 tb->used = length;
2973 tb->blank = False;
2974 tb->d.widget = w;
2975 tb->color = WMRetainColor(color);
2976 tb->marginN = newMargin(tPtr, NULL);
2977 tb->allocated = extraInfo;
2978 tb->first = first;
2979 tb->kanji = False;
2980 tb->graphic = True;
2981 tb->object = True;
2982 tb->underlined = False;
2983 tb->selected = False;
2984 tb->script = 0;
2985 tb->sections = NULL;
2986 tb->nsections = 0;
2987 tb->prior = NULL;
2988 tb->next = NULL;
2990 return tb;
2994 void *
2995 WMCreateTextBlockWithPixmap(WMText *tPtr, WMPixmap *p,
2996 char *description, WMColor *color,
2997 unsigned short first, unsigned short extraInfo)
2999 TextBlock *tb;
3000 unsigned short length;
3002 if (!p || !description || !color)
3003 return NULL;
3005 tb = wmalloc(sizeof(TextBlock));
3006 if (!tb)
3007 return NULL;
3009 length = strlen(description);
3010 tb->text = (char *)wmalloc(length);
3011 memset(tb->text, 0, length);
3012 memcpy(tb->text, description, length);
3013 tb->used = length;
3014 tb->blank = False;
3015 tb->d.pixmap = p;
3016 tb->color = WMRetainColor(color);
3017 tb->marginN = newMargin(tPtr, NULL);
3018 tb->allocated = extraInfo;
3019 tb->first = first;
3020 tb->kanji = False;
3021 tb->graphic = True;
3022 tb->object = False;
3023 tb->underlined = False;
3024 tb->selected = False;
3025 tb->script = 0;
3026 tb->sections = NULL;
3027 tb->nsections = 0;
3028 tb->prior = NULL;
3029 tb->next = NULL;
3031 return tb;
3034 void *
3035 WMCreateTextBlockWithText(WMText *tPtr, char *text, WMFont *font, WMColor *color,
3036 unsigned short first, unsigned short length)
3038 TextBlock *tb;
3040 if (!font || !color)
3041 return NULL;
3043 tb = wmalloc(sizeof(TextBlock));
3044 if (!tb)
3045 return NULL;
3047 tb->allocated = reqBlockSize(length);
3048 tb->text = (char *)wmalloc(tb->allocated);
3049 memset(tb->text, 0, tb->allocated);
3051 if (length < 1|| !text ) {
3052 *tb->text = ' ';
3053 tb->used = 1;
3054 tb->blank = True;
3055 } else {
3056 memcpy(tb->text, text, length);
3057 tb->used = length;
3058 tb->blank = False;
3061 tb->d.font = WMRetainFont(font);
3062 tb->color = WMRetainColor(color);
3063 tb->marginN = newMargin(tPtr, NULL);
3064 tb->first = first;
3065 tb->kanji = False;
3066 tb->graphic = False;
3067 tb->underlined = False;
3068 tb->selected = False;
3069 tb->script = 0;
3070 tb->sections = NULL;
3071 tb->nsections = 0;
3072 tb->prior = NULL;
3073 tb->next = NULL;
3074 return tb;
3077 void
3078 WMSetTextBlockProperties(WMText *tPtr, void *vtb, unsigned int first,
3079 unsigned int kanji, unsigned int underlined, int script,
3080 WMRulerMargins *margins)
3082 TextBlock *tb = (TextBlock *) vtb;
3083 if (!tb)
3084 return;
3086 tb->first = first;
3087 tb->kanji = kanji;
3088 tb->underlined = underlined;
3089 tb->script = script;
3090 tb->marginN = newMargin(tPtr, margins);
3093 void
3094 WMGetTextBlockProperties(WMText *tPtr, void *vtb, unsigned int *first,
3095 unsigned int *kanji, unsigned int *underlined, int *script,
3096 WMRulerMargins *margins)
3098 TextBlock *tb = (TextBlock *) vtb;
3099 if (!tb)
3100 return;
3102 if (first) *first = tb->first;
3103 if (kanji) *kanji = tb->kanji;
3104 if (underlined) *underlined = tb->underlined;
3105 if (script) *script = tb->script;
3106 if (margins) margins = &tPtr->margins[tb->marginN];
3111 void
3112 WMPrependTextBlock(WMText *tPtr, void *vtb)
3114 TextBlock *tb = (TextBlock *)vtb;
3116 if (!tPtr || !tb)
3117 return;
3119 if (tb->graphic) {
3120 if(tb->object) {
3121 WMWidget *w = tb->d.widget;
3122 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
3123 handleWidgetPress, tb);
3124 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
3125 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
3126 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
3129 WMAddToArray(tPtr->gfxItems, (void *)tb);
3130 tPtr->tpos = 0;
3131 } else tPtr->tpos = tb->used;
3133 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
3134 tb->next = tb->prior = NULL;
3135 tb->first = True;
3136 tPtr->lastTextBlock = tPtr->firstTextBlock
3137 = tPtr->currentTextBlock = tb;
3138 return;
3141 if(!tb->first) {
3142 tb->marginN = tPtr->currentTextBlock->marginN;
3145 tb->next = tPtr->currentTextBlock;
3146 tb->prior = tPtr->currentTextBlock->prior;
3147 if (tPtr->currentTextBlock->prior)
3148 tPtr->currentTextBlock->prior->next = tb;
3150 tPtr->currentTextBlock->prior = tb;
3151 if (!tb->prior)
3152 tPtr->firstTextBlock = tb;
3154 tPtr->currentTextBlock = tb;
3158 void
3159 WMAppendTextBlock(WMText *tPtr, void *vtb)
3161 TextBlock *tb = (TextBlock *)vtb;
3163 if (!tPtr || !tb)
3164 return;
3166 if (tb->graphic) {
3167 if(tb->object) {
3168 WMWidget *w = tb->d.widget;
3169 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
3170 handleWidgetPress, tb);
3171 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
3172 (W_VIEW(w))->attribs.cursor =
3173 tPtr->view->screen->defaultCursor;
3174 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
3177 WMAddToArray(tPtr->gfxItems, (void *)tb);
3178 tPtr->tpos = 0;
3179 } else tPtr->tpos = tb->used;
3181 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
3182 tb->next = tb->prior = NULL;
3183 tb->first = True;
3184 tPtr->lastTextBlock = tPtr->firstTextBlock
3185 = tPtr->currentTextBlock = tb;
3186 return;
3189 if(!tb->first) {
3190 tb->marginN = tPtr->currentTextBlock->marginN;
3193 tb->next = tPtr->currentTextBlock->next;
3194 tb->prior = tPtr->currentTextBlock;
3195 if (tPtr->currentTextBlock->next)
3196 tPtr->currentTextBlock->next->prior = tb;
3198 tPtr->currentTextBlock->next = tb;
3200 if (!tb->next)
3201 tPtr->lastTextBlock = tb;
3203 tPtr->currentTextBlock = tb;
3206 void *
3207 WMRemoveTextBlock(WMText *tPtr)
3209 TextBlock *tb = NULL;
3211 if (!tPtr || !tPtr->firstTextBlock || !tPtr->lastTextBlock
3212 || !tPtr->currentTextBlock) {
3213 printf("cannot remove non existent TextBlock!\b");
3214 return tb;
3217 tb = tPtr->currentTextBlock;
3218 if (tb->graphic) {
3219 WMRemoveFromArray(tPtr->gfxItems, (void *)tb);
3221 if(tb->object) {
3222 WMDeleteEventHandler(W_VIEW(tb->d.widget), ButtonPressMask,
3223 handleWidgetPress, tb);
3224 WMUnmapWidget(tb->d.widget);
3228 if (tPtr->currentTextBlock == tPtr->firstTextBlock) {
3229 if (tPtr->currentTextBlock->next)
3230 tPtr->currentTextBlock->next->prior = NULL;
3232 tPtr->firstTextBlock = tPtr->currentTextBlock->next;
3233 tPtr->currentTextBlock = tPtr->firstTextBlock;
3235 } else if (tPtr->currentTextBlock == tPtr->lastTextBlock) {
3236 tPtr->currentTextBlock->prior->next = NULL;
3237 tPtr->lastTextBlock = tPtr->currentTextBlock->prior;
3238 tPtr->currentTextBlock = tPtr->lastTextBlock;
3239 } else {
3240 tPtr->currentTextBlock->prior->next = tPtr->currentTextBlock->next;
3241 tPtr->currentTextBlock->next->prior = tPtr->currentTextBlock->prior;
3242 tPtr->currentTextBlock = tPtr->currentTextBlock->next;
3245 return (void *)tb;
3248 void
3249 WMDestroyTextBlock(WMText *tPtr, void *vtb)
3251 TextBlock *tb = (TextBlock *)vtb;
3252 if (!tPtr || !tb)
3253 return;
3255 if (tb->graphic) {
3256 if(tb->object) {
3257 /* naturally, there's a danger to destroying
3258 widgets whose action brings us here:
3259 ie. press a button to destroy it... need to
3260 find a safer way. till then... this stays commented out */
3261 /* WMDestroyWidget(tb->d.widget);
3262 wfree(tb->d.widget); */
3263 tb->d.widget = NULL;
3264 } else {
3265 WMReleasePixmap(tb->d.pixmap);
3266 tb->d.pixmap = NULL;
3268 } else {
3269 WMReleaseFont(tb->d.font);
3272 WMReleaseColor(tb->color);
3273 if (tb->sections && tb->nsections > 0)
3274 wfree(tb->sections);
3275 wfree(tb->text);
3276 wfree(tb);
3277 tb = NULL;
3281 void
3282 WMRefreshText(WMText *tPtr, int vpos, int hpos)
3284 if (!tPtr || vpos<0 || hpos<0)
3285 return;
3287 if (tPtr->flags.frozen && !tPtr->flags.needsRefresh)
3288 return;
3290 if(tPtr->flags.monoFont) {
3291 int j, c = WMGetArrayItemCount(tPtr->gfxItems);
3292 TextBlock *tb;
3294 /* make sure to unmap widgets no matter where they are */
3295 for(j=0; j<c; j++) {
3296 if ((tb = (TextBlock *) WMGetFromArray(tPtr->gfxItems, j))) {
3297 if (tb->object && ((W_VIEW(tb->d.widget))->flags.mapped))
3298 WMUnmapWidget(tb->d.widget);
3304 if (tPtr->vpos != vpos) {
3305 if (vpos < 0 || tPtr->docHeight < tPtr->visible.h) {
3306 tPtr->vpos = 0;
3307 } else if(tPtr->docHeight - vpos > tPtr->visible.h - tPtr->visible.y) {
3308 tPtr->vpos = vpos;
3309 } else {
3310 tPtr->vpos = tPtr->docHeight - tPtr->visible.h;
3314 if (tPtr->hpos != hpos) {
3315 if (hpos < 0 || tPtr->docWidth < tPtr->visible.w) {
3316 tPtr->hpos = 0;
3317 } else if(tPtr->docWidth - hpos > tPtr->visible.w - tPtr->visible.x) {
3318 tPtr->hpos = hpos;
3319 } else {
3320 tPtr->hpos = tPtr->docWidth - tPtr->visible.w;
3325 tPtr->flags.laidOut = False;
3326 layOutDocument(tPtr);
3327 updateScrollers(tPtr);
3328 paintText(tPtr);
3329 tPtr->flags.needsRefresh = False;
3333 void
3334 WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
3336 if (!tPtr)
3337 return;
3339 if (color)
3340 tPtr->fgGC = WMColorGC(color);
3341 else
3342 tPtr->fgGC = WMColorGC(WMBlackColor(tPtr->view->screen));
3344 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
3347 void
3348 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
3350 if (!tPtr)
3351 return;
3353 if (color) {
3354 tPtr->bgGC = WMColorGC(color);
3355 W_SetViewBackgroundColor(tPtr->view, color);
3356 } else {
3357 tPtr->bgGC = WMColorGC(WMWhiteColor(tPtr->view->screen));
3358 W_SetViewBackgroundColor(tPtr->view,
3359 WMWhiteColor(tPtr->view->screen));
3362 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
3365 void
3366 WMSetTextRelief(WMText *tPtr, WMReliefType relief)
3368 if (!tPtr)
3369 return;
3370 tPtr->flags.relief = relief;
3371 paintText(tPtr);
3374 void
3375 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
3377 if (!tPtr)
3378 return;
3380 if (shouldhave && !tPtr->hS) {
3381 tPtr->hS = WMCreateScroller(tPtr);
3382 (W_VIEW(tPtr->hS))->attribs.cursor = tPtr->view->screen->defaultCursor;
3383 (W_VIEW(tPtr->hS))->attribFlags |= CWOverrideRedirect | CWCursor;
3384 WMSetScrollerArrowsPosition(tPtr->hS, WSAMinEnd);
3385 WMSetScrollerAction(tPtr->hS, scrollersCallBack, tPtr);
3386 WMMapWidget(tPtr->hS);
3387 } else if (!shouldhave && tPtr->hS) {
3388 WMUnmapWidget(tPtr->hS);
3389 WMDestroyWidget(tPtr->hS);
3390 tPtr->hS = NULL;
3393 tPtr->hpos = 0;
3394 tPtr->prevHpos = 0;
3395 textDidResize(tPtr->view->delegate, tPtr->view);
3399 void
3400 WMSetTextHasRuler(WMText *tPtr, Bool shouldhave)
3402 if (!tPtr)
3403 return;
3405 if(shouldhave && !tPtr->ruler) {
3406 tPtr->ruler = WMCreateRuler(tPtr);
3407 (W_VIEW(tPtr->ruler))->attribs.cursor =
3408 tPtr->view->screen->defaultCursor;
3409 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
3410 WMSetRulerReleaseAction(tPtr->ruler, rulerReleaseCallBack, tPtr);
3411 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
3412 } else if(!shouldhave && tPtr->ruler) {
3413 WMShowTextRuler(tPtr, False);
3414 WMDestroyWidget(tPtr->ruler);
3415 tPtr->ruler = NULL;
3417 textDidResize(tPtr->view->delegate, tPtr->view);
3420 void
3421 WMShowTextRuler(WMText *tPtr, Bool show)
3423 if(!tPtr)
3424 return;
3425 if(!tPtr->ruler)
3426 return;
3428 if(tPtr->flags.monoFont)
3429 show = False;
3431 tPtr->flags.rulerShown = show;
3432 if(show) {
3433 WMMapWidget(tPtr->ruler);
3434 } else {
3435 WMUnmapWidget(tPtr->ruler);
3438 textDidResize(tPtr->view->delegate, tPtr->view);
3441 Bool
3442 WMGetTextRulerShown(WMText *tPtr)
3444 if(!tPtr)
3445 return 0;
3447 if(!tPtr->ruler)
3448 return 0;
3450 return tPtr->flags.rulerShown;
3454 void
3455 WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
3457 if (!tPtr)
3458 return;
3460 if (shouldhave && !tPtr->vS) {
3461 tPtr->vS = WMCreateScroller(tPtr);
3462 (W_VIEW(tPtr->vS))->attribs.cursor = tPtr->view->screen->defaultCursor;
3463 (W_VIEW(tPtr->vS))->attribFlags |= CWOverrideRedirect | CWCursor;
3464 WMSetScrollerArrowsPosition(tPtr->vS, WSAMaxEnd);
3465 WMSetScrollerAction(tPtr->vS, scrollersCallBack, tPtr);
3466 WMMapWidget(tPtr->vS);
3467 } else if (!shouldhave && tPtr->vS) {
3468 WMUnmapWidget(tPtr->vS);
3469 WMDestroyWidget(tPtr->vS);
3470 tPtr->vS = NULL;
3473 tPtr->vpos = 0;
3474 tPtr->prevVpos = 0;
3475 textDidResize(tPtr->view->delegate, tPtr->view);
3480 Bool
3481 WMScrollText(WMText *tPtr, int amount)
3483 Bool scroll=False;
3484 if (!tPtr)
3485 return False;
3486 if (amount == 0 || !tPtr->view->flags.realized)
3487 return False;
3489 if (amount < 0) {
3490 if (tPtr->vpos > 0) {
3491 if (tPtr->vpos > abs(amount)) tPtr->vpos += amount;
3492 else tPtr->vpos=0;
3493 scroll=True;
3494 } } else {
3495 int limit = tPtr->docHeight - tPtr->visible.h;
3496 if (tPtr->vpos < limit) {
3497 if (tPtr->vpos < limit-amount) tPtr->vpos += amount;
3498 else tPtr->vpos = limit;
3499 scroll = True;
3500 } }
3502 if (scroll && tPtr->vpos != tPtr->prevVpos) {
3503 updateScrollers(tPtr);
3504 paintText(tPtr);
3506 tPtr->prevVpos = tPtr->vpos;
3507 return scroll;
3510 Bool
3511 WMPageText(WMText *tPtr, Bool direction)
3513 if (!tPtr) return False;
3514 if (!tPtr->view->flags.realized) return False;
3516 return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
3519 void
3520 WMSetTextEditable(WMText *tPtr, Bool editable)
3522 if (!tPtr)
3523 return;
3524 tPtr->flags.editable = editable;
3527 int
3528 WMGetTextEditable(WMText *tPtr)
3530 if (!tPtr)
3531 return 0;
3532 return tPtr->flags.editable;
3535 void
3536 WMSetTextIgnoresNewline(WMText *tPtr, Bool ignore)
3538 if (!tPtr)
3539 return;
3540 tPtr->flags.ignoreNewLine = ignore;
3543 Bool
3544 WMGetTextIgnoresNewline(WMText *tPtr)
3546 if (!tPtr)
3547 return True;
3548 return tPtr->flags.ignoreNewLine;
3551 void
3552 WMSetTextUsesMonoFont(WMText *tPtr, Bool mono)
3554 if (!tPtr)
3555 return;
3556 if (mono && tPtr->flags.rulerShown)
3557 WMShowTextRuler(tPtr, False);
3559 tPtr->flags.monoFont = mono;
3560 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
3563 Bool
3564 WMGetTextUsesMonoFont(WMText *tPtr)
3566 if (!tPtr)
3567 return True;
3568 return tPtr->flags.monoFont;
3572 void
3573 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
3575 if (!tPtr)
3576 return;
3578 WMReleaseFont(tPtr->dFont);
3579 if (font)
3580 tPtr->dFont = WMRetainFont(font);
3581 else
3582 tPtr->dFont = WMRetainFont(WMSystemFontOfSize(tPtr->view->screen, 12));
3585 WMFont *
3586 WMGetTextDefaultFont(WMText *tPtr)
3588 if (!tPtr)
3589 return NULL;
3590 else
3591 return tPtr->dFont;
3594 void
3595 WMSetTextAlignment(WMText *tPtr, WMAlignment alignment)
3597 if (!tPtr)
3598 return;
3599 tPtr->flags.alignment = alignment;
3600 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
3603 void
3604 WMSetTextParser(WMText *tPtr, WMAction *parser)
3606 if (!tPtr)
3607 return;
3608 tPtr->parser = parser;
3611 void
3612 WMSetTextWriter(WMText *tPtr, WMAction *writer)
3614 if (!tPtr)
3615 return;
3616 tPtr->writer = writer;
3619 int
3620 WMGetTextInsertType(WMText *tPtr)
3622 if (!tPtr)
3623 return 0;
3624 return tPtr->flags.prepend;
3628 void
3629 WMSetTextSelectionColor(WMText *tPtr, WMColor *color)
3631 if (!tPtr || !color)
3632 return;
3634 setSelectionProperty(tPtr, NULL, color);
3639 void
3640 WMSetTextSelectionFont(WMText *tPtr, WMFont *font)
3642 if (!tPtr || !font)
3643 return;
3645 setSelectionProperty(tPtr, font, NULL);
3649 void
3650 WMFreezeText(WMText *tPtr)
3652 if (!tPtr)
3653 return;
3655 tPtr->flags.frozen = True;
3658 void
3659 WMThawText(WMText *tPtr)
3661 if (!tPtr)
3662 return;
3664 tPtr->flags.frozen = False;
3668 Bool
3669 WMFindInTextStream(WMText *tPtr, char *needle, Bool direction,
3670 Bool caseSensitive)
3672 TextBlock *tb = NULL;
3673 char *haystack = NULL, *mark = NULL;
3674 unsigned short pos;
3676 if (!tPtr || !needle)
3677 return False;
3679 if (! (tb = tPtr->currentTextBlock)) {
3680 if (! (tb = ( (direction > 0) ?
3681 tPtr->firstTextBlock : tPtr->lastTextBlock) ) ){
3682 return False;
3684 } else {
3685 if(tb != ((direction>0) ?tPtr->firstTextBlock : tPtr->lastTextBlock))
3686 tb = (direction>0) ? tb->next : tb->prior;
3689 printf("\n%p\n\n", tb);
3691 while(tb) {
3692 if (!tb->graphic) {
3693 pos = tPtr->tpos;
3694 if(pos+1 < tb->used)
3695 pos++;
3696 printf("here\n");
3697 output(tb->text, tb->used);
3698 mark = strstr(&tb->text[pos], needle); //, tb->used);
3699 if(mark) {
3700 tPtr->currentTextBlock = tb;
3701 tPtr->tpos = (int)(mark - tb->text);
3702 updateCursorPosition(tPtr);
3703 return True;
3706 tb = (direction>0) ? tb->next : tb->prior;
3707 pos = 0;
3710 return False;
3714 typedef struct _currentFormat {
3715 WMArray *fonts;
3716 WMArray *colors;
3717 WMColor *ccolor;
3718 WMFont *cfont;
3719 WMRulerMargins margins;
3720 //WMArray *aligns; // for tables...
3721 /* the following are "nested"
3722 i.e.: <b><b><i></b><b></i>
3723 1 2 1 1 2 0 get it? */
3724 short i;
3725 short b;
3726 short u;
3727 short fmargin;
3728 short bmargin;
3729 short first:1;
3730 short type:1;
3731 WMAlignment align:2;
3732 short ul:3; /* how "nested"... up to 8 levels deep */
3733 short comment:1; /* ignore text till --> */
3734 short RESERVED:10;
3735 } CFMT;
3736 CFMT cfmt;
3740 #if 0
3741 getArg(char *t, short type, void *arg)
3743 short d=0;
3744 while(*(++t) && !d) {
3745 if(type==0) {
3746 if(*t>='0' && *t<='9') {
3747 sscanf(t, "%d", arg);
3748 while(*t&& (*t<'0' || *t>'9'))
3749 t++;
3750 d=1;
3755 #endif
3757 static void
3758 parseToken(WMText *tPtr, char *token, short tk)
3760 short open=0; /* 0 starts, 1 closes */
3761 void *tb= NULL;
3762 int prepend = WMGetTextInsertType(tPtr);
3763 WMScreen *scr = tPtr->view->screen;
3765 if(tk == -1) {
3766 WMAppendTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
3767 NULL, cfmt.cfont, cfmt.ccolor, True, 0));
3768 return;
3772 while(*token && *token == ' ')
3773 token++;
3775 if(*token == '/') {
3776 token++;
3777 open = 1;
3779 while(*token == ' ')
3780 token++;
3783 if(!tPtr->flags.parsingHTML) {
3784 if(mystrcasecmp(token, "html", 4, False)) {
3785 printf("got HTMLLLL: [%s]\n", token);
3786 tPtr->flags.parsingHTML = True;
3787 } else {
3788 WMAppendTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
3789 token, cfmt.cfont, cfmt.ccolor, cfmt.first, strlen(token)));
3792 return;
3796 if(strlen(token)==1) {
3797 /* nice and fast for small tokens... no need for too much brain
3798 power here */
3799 switch(TOLOWER(*token)) {
3800 case 'i':
3801 if(!open) {
3802 cfmt.cfont = WMConvertFontToItalic(scr, cfmt.cfont);
3803 WMAddToArray(cfmt.fonts, (void *)cfmt.cfont);
3804 } else { /*dun wanna remove the baseFont eh? */
3805 int count = WMGetArrayItemCount(cfmt.fonts);
3806 if(count>1) {
3807 WMDeleteFromArray(cfmt.fonts, count-1);
3809 cfmt.cfont = (WMFont *)WMGetFromArray(cfmt.fonts,
3810 WMGetArrayItemCount(cfmt.fonts)-1);
3811 }printf("i\n"); break;
3813 case 'b':
3814 if(!open) {
3815 cfmt.cfont = WMConvertFontToBold(scr, cfmt.cfont);
3816 WMAddToArray(cfmt.fonts, (void *)cfmt.cfont);
3817 } else { /*dun wanna remove the baseFont eh? */
3818 int count = WMGetArrayItemCount(cfmt.fonts);
3819 if(count>1)
3820 WMDeleteFromArray(cfmt.fonts, count-1);
3821 cfmt.cfont = (WMFont *)WMGetFromArray(cfmt.fonts,
3822 WMGetArrayItemCount(cfmt.fonts)-1);
3823 } break;
3824 case 'p':
3825 cfmt.first = True;
3826 tb = WMCreateTextBlockWithText(tPtr, NULL, cfmt.cfont,
3827 cfmt.ccolor, cfmt.first, 0);
3828 // WMSetTextBlockProperties(tb, cfmt.first, False, (cfmt.u?1:0), 0, cfmt.margins);
3829 WMAppendTextBlock(tPtr, tb);
3830 cfmt.first = False;
3831 break;
3832 case 'u': cfmt.u = !open; break;
3834 } else {
3835 if(mystrcasecmp(token, "br", 2, False)) {
3836 cfmt.first = True;
3838 else if(mystrcasecmp(token, "ul", 2, False)) {
3839 if(open) {
3840 if(cfmt.ul>1) cfmt.ul--;
3841 } else cfmt.ul++;
3842 if(cfmt.ul) {
3843 cfmt.bmargin = cfmt.ul*30;
3844 cfmt.fmargin = cfmt.bmargin-10;
3845 } else cfmt.fmargin = cfmt.bmargin = 0;
3846 } else if(mystrcasecmp(token, "li", 2, False)) {
3847 cfmt.first = True;
3848 //change margins... create a new margin....
3849 //(cfmt.fmargin, cfmt.bmargin,
3850 } else if(mystrcasecmp(token, "html", 4, False)) {
3851 tPtr->flags.parsingHTML = !open;
3853 } else if(mystrcasecmp(token, "align", 5, False))
3854 ;//printf("align");
3855 else if(mystrcasecmp(token, "img", 3, False)) {
3856 if(!open) {
3857 char *mark=NULL;
3858 WMPixmap *pixmap;
3859 token+=3;
3860 while(*token == ' ') token++;
3861 do {
3862 switch(TOLOWER(*token)) {
3863 case 's':
3864 if(TOLOWER(*(1+token)) == 'r' && TOLOWER(*(2+token)) == 'c') {
3865 mark = strchr(token, '=');
3866 if(mark) {
3867 char img[256], *iptr;
3868 token = mark+1;
3869 if(!token) return;
3870 sscanf(token, "%s", img);
3871 iptr = img;
3872 if(*img == '\"') { img[strlen(img)-1] = 0; iptr++;}
3873 pixmap = WMCreatePixmapFromFile(scr, iptr);
3874 if(pixmap) {
3875 tb = WMCreateTextBlockWithPixmap(tPtr, pixmap,
3876 iptr, cfmt.ccolor, cfmt.first, 0);
3877 // WMSetTextBlockProperties(tb, cfmt.first,
3878 // False, (cfmt.u?1:0), 0, cfmt.margins);
3879 WMAppendTextBlock(tPtr, tb);
3880 cfmt.first = False;
3882 //printf("[%s]\n", iptr);
3883 } } break; } } while(*(token++));
3885 } else if(mystrcasecmp(token, "font", 4, False)) {
3886 #if 0
3887 if(open) {
3888 cfmt.cfont = (WMFont *)WMGetFromArray(cfmt.fonts,
3889 WMGetArrayItemCount(cfmt.fonts)-1);
3890 } else
3891 (WMColor *)WMGetFromArray(cfmt.colors,
3892 WMGetArrayItemCount(cfmt.colors)-1),
3893 #endif
3895 else if(mystrcasecmp(token, "center", 6, False)) {
3896 printf("center\n");
3897 if(open) cfmt.align = WALeft;
3898 else cfmt.align = WACenter;
3899 cfmt.first = True;
3900 //change margins...
3907 //printf("parse token (%s)[%s]\n", open?"close":"open", token);
3908 #if 0
3909 i=0;
3910 //while(*token && !isspace(*(token))) token++;
3911 //printf("A:%d a:%d z%d Z%d\n", '1', 'a', 'Z', 'z');
3912 do {
3913 if(!mm) {
3914 if(c>=65 && c<=122) { major[i++] = c;
3915 } else if(c==' ' || c=='='){ major[i] = 0; i=0; mm=1;
3916 printf("\nmajor: [%s]", major);}
3917 } else {
3918 if(c!=' ') {
3919 minor[i++] = c;
3920 } else { minor[i] = 0; i=0; printf(" minor: [%s] ", minor);}
3922 }while((c = *(++token)));
3923 #endif
3926 //printf("parse token (%s)[%s]\n", open?"close":"open", token);