changes to wmtext
[wmaker-crm.git] / WINGs / wtext.c
blobfaab15501ddf4cbc9cdf1ba77f1a29c598c38425
1 /*
2 * WINGs WMText multi-line/font/color/graphic text widget
4 * Copyright (c) 1999-2000 Nwanua Elumeze
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>
27 //_______
28 //TODO:
30 #if 0
32 use currentTextBlock and neighbours for fast paint and layout
34 WMGetTextStreamAll... WMGetTextStream WMGetTextSelection(if(selected) )
36 the bitfield arrangement in this code assumes a little-endian
37 machine... might need a __BIG_ENDIAN__ define for arranging
38 the bitfields efficiently for those big boys.
40 make a file named fontman.c, put that kind of
41 stuff in there and not put the APIs in WINGs.h
42 WMGetFontItalic() should some day be part of the font manager
43 instead, just put a bunch of extern WMGetFontbla in the top of wtext.c
45 #endif
47 //_______
52 /* a Section is a section of a TextBlock that describes what parts
53 of a TextBlock has be layout on which "line"...
54 o this greatly aids redraw, scroll and selection.
55 o this is created during layoutLine, but may be later modified.
56 o there may be many regions per TextBlock, hence the array */
57 typedef struct {
58 int x, y; /* where to draw it from */
59 int w, h; /* it's width and height (to aid selection) */
60 int _y;
61 unsigned short begin, end; /* what part of the text block */
62 } Section;
65 /* a TextBlock is a doubly-linked list of TextBlocks containing:
66 o text for the block, color and font
67 o or a pointer to the widget and the (text) description for its graphic
68 o but NOT both */
70 typedef struct _TextBlock {
71 struct _TextBlock *next; /* next text block in linked list */
72 struct _TextBlock *prior; /* prior text block in linked list */
74 char *text; /* pointer to 8- or 16-bit text */
75 /* or to the object's description */
76 union {
77 WMFont *font; /* the font */
78 WMWidget *widget; /* the embedded widget */
79 } d; /* description */
81 WMColor *color; /* the color */
82 Section *sections; /* the region for layouts (a growable array) */
83 /* an _array_! of size _nsections_ */
85 unsigned short used; /* number of chars in this block */
86 unsigned short allocated; /* size of allocation (in chars) */
88 unsigned int first:1; /* first TextBlock in paragraph */
89 unsigned int blank:1; /* ie. blank paragraph */
90 unsigned int kanji:1; /* is of 16-bit characters or not */
91 unsigned int graphic:1; /* embedded object or text: text=0 */
92 unsigned int underlined:1; /* underlined or not */
93 unsigned int nsections:8; /* over how many "lines" a TexBlock wraps */
94 int script:8; /* script in points: negative for subscript */
95 unsigned int marginN:10; /* which of the margins in WMText to use */
96 unsigned int RESERVED:1;
97 } TextBlock;
100 /* somehow visible.h beats the hell outta visible.size.height :-) */
101 typedef struct {
102 unsigned int y;
103 unsigned int x;
104 unsigned int h;
105 unsigned int w;
106 } myRect;
109 typedef struct W_Text {
110 W_Class widgetClass; /* the class number of this widget */
111 W_View *view; /* the view referring to this instance */
113 WMRuler *ruler; /* the ruler subwiget to manipulate paragraphs */
115 WMScroller *vS; /* the vertical scroller */
116 int vpos; /* the current vertical position */
117 int prevVpos; /* the previous vertical position */
119 WMScroller *hS; /* the horizontal scroller */
120 int hpos; /* the current horizontal position */
121 int prevHpos; /* the previous horizontal position */
122 /* in short: tPtr->hS?nowrap:wrap */
124 WMFont *dFont; /* the default font */
125 WMColor *dColor; /* the default color */
126 WMPixmap *dBulletPix; /* the default pixmap for bullets */
128 GC bgGC; /* the background GC to draw with */
129 GC fgGC; /* the foreground GC to draw with */
130 Pixmap db; /* the buffer on which to draw */
132 WMRulerMargins *margins;/* a (growable) array of margins to be used */
133 /* by the various TextBlocks */
135 myRect visible; /* the actual rectangle that can be drawn into */
136 myRect sel; /* the selection rectangle */
137 int docWidth; /* the width of the entire document */
138 int docHeight; /* the height of the entire document */
141 TextBlock *firstTextBlock;
142 TextBlock *lastTextBlock;
143 TextBlock *currentTextBlock;
146 WMBag *gfxItems; /* a nice bag containing graphic items */
148 WMPoint clicked; /* where in the _document_ was clicked */
149 unsigned short tpos; /* the character position in the currentTextBlock */
150 unsigned short RESERVED;/* space taker upper... */
153 WMAction *parser;
154 WMAction *writer;
156 struct {
157 unsigned int monoFont:1; /* whether to ignore formats */
158 unsigned int focused:1; /* whether this instance has input focus */
159 unsigned int editable:1; /* "silly user, you can't edit me" */
160 unsigned int ownsSelection:1; /* "I ownz the current selection!" */
161 unsigned int pointerGrabbed:1;/* "heh, gib me pointer" */
162 unsigned int buttonHeld:1; /* the user is holding down the button */
163 unsigned int waitingForSelection:1; /* dum dee dumm... */
164 unsigned int extendSelection:1; /* shift-drag to select more regions */
166 unsigned int rulerShown:1; /* whether the ruler is shown or not */
167 unsigned int frozen:1; /* whether screen updates are to be made */
168 unsigned int cursorShown:1; /* whether to show the cursor */
169 unsigned int clickPos:1; /* clicked before=0/after=1 a graphic: */
170 /* within counts as after too */
172 unsigned int ignoreNewLine:1;/* "bleh XK_Return" ignore it when typed */
173 unsigned int laidOut:1; /* have the TextBlocks all been laid out */
174 unsigned int prepend:1; /* prepend=1, append=0 (for parsers) */
175 WMAlignment alignment:2; /* the alignment for text */
176 WMReliefType relief:3; /* the relief to display with */
177 unsigned int RESERVED:4;
178 unsigned int nmargins:10; /* the number of margin arrays */
179 } flags;
180 } Text;
182 static char *default_bullet[] = {
183 "6 6 4 1",
184 " c None s None", ". c black",
185 "X c white", "o c #808080",
186 " ... ",
187 ".XX.. ",
188 ".XX..o",
189 ".....o",
190 " ...oo",
191 " ooo "};
194 /* done purely for speed ... mostly same as WMWidthOfString */
195 static inline unsigned int
196 myWidthOfString(WMFont *font, char *text, unsigned int length)
198 if (font->notFontSet)
199 return XTextWidth(font->font.normal, text, length);
200 else {
201 XRectangle rect, AIXsucks;
202 XmbTextExtents(font->font.set, text, length, &AIXsucks, &rect);
203 return rect.width;
208 static void
209 paintText(Text *tPtr)
211 TextBlock *tb = tPtr->firstTextBlock;
212 WMFont *font;
213 GC gc, greyGC;
214 char *text;
215 int len, y, c, s, done=False;
216 int prev_y=-23;
217 WMScreen *scr = tPtr->view->screen;
218 Display *dpy = tPtr->view->screen->display;
219 Window win = tPtr->view->window;
223 if(!tPtr->view->flags.realized || !tPtr->db)
224 return;
226 XFillRectangle(dpy, tPtr->db, tPtr->bgGC,
227 0, 0, tPtr->visible.w, tPtr->visible.h);
230 tb = tPtr->firstTextBlock;
231 if(!tb)
232 goto _copy_area;
235 if(tPtr->flags.ownsSelection) {
236 greyGC = WMColorGC(WMGrayColor(scr));
237 //XFillRectangle(dpy, tPtr->db, greyGC,
238 // tPtr->sel.x, tPtr->sel.y-tPtr->vpos, tPtr->sel.w, tPtr->sel.h);
239 // XDrawRectangle(dpy, tPtr->db, tPtr->fgGC,
240 // tPtr->sel.x, tPtr->sel.y-tPtr->vpos, tPtr->sel.w, tPtr->sel.h);
243 done = False;
244 while(!done && tb) {
246 if(!tb->sections || (!tPtr->flags.monoFont && tb->graphic)) {
247 tb = tb->next;
248 continue;
251 for(s=0; s<tb->nsections && !done; s++) {
254 if(tb->sections[s]._y > tPtr->vpos + tPtr->visible.h) {
255 done = True;
256 break;
259 if( tb->sections[s].y + tb->sections[s].h < tPtr->vpos)
260 continue;
262 if(tPtr->flags.monoFont) {
263 font = tPtr->dFont;
264 gc = tPtr->fgGC;
265 } else {
266 font = tb->d.font;
267 gc = WMColorGC(tb->color);
270 if(tPtr->flags.ownsSelection) {
272 if(prev_y != tb->sections[s]._y
273 && (tb->sections[s]._y >= tPtr->sel.y)
274 && (tb->sections[s]._y + tb->sections[s].h
275 <= tPtr->sel.y + tPtr->sel.h)) {
276 XFillRectangle(dpy, tPtr->db, greyGC,
277 tPtr->visible.x,
278 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
279 tPtr->visible.w, tb->sections[s].h);
281 } else if( prev_y != tb->sections[s]._y
282 && (tb->sections[s]._y <= tPtr->sel.y)
283 && (tb->sections[s]._y + tb->sections[s].h
284 >= tPtr->sel.y)
285 && (tPtr->sel.x >= tb->sections[s].x)
286 && (tPtr->sel.y + tPtr->sel.h
287 >= tb->sections[s]._y + tb->sections[s].h)) {
288 XFillRectangle(dpy, tPtr->db, greyGC,
289 tPtr->clicked.x,
290 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
291 tPtr->visible.w - tPtr->sel.x, tb->sections[s].h);
293 } else if(prev_y != tb->sections[s]._y
294 && (tb->sections[s]._y <= tPtr->sel.y + tPtr->sel.h)
295 && (tb->sections[s]._y >= tPtr->sel.y)) {
296 XFillRectangle(dpy, tPtr->db, greyGC,
297 tPtr->visible.x,
298 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
299 tPtr->sel.x + tPtr->sel.w -tPtr->visible.x,
300 tb->sections[s].h);
302 } else if( prev_y != tb->sections[s]._y
303 && (tb->sections[s]._y <= tPtr->sel.y)
304 && (tb->sections[s]._y + tb->sections[s].h
305 >= tPtr->sel.y + tPtr->sel.h) ) {
306 XFillRectangle(dpy, tPtr->db, greyGC,
307 tPtr->sel.x,
308 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
309 tPtr->sel.w,tb->sections[s].h);
314 prev_y = tb->sections[s]._y;
316 len = tb->sections[s].end - tb->sections[s].begin;
317 text = &(tb->text[tb->sections[s].begin]);
318 y = tb->sections[s].y - tPtr->vpos;
319 WMDrawString(scr, tPtr->db, gc, font,
320 tb->sections[s].x, y, text, len);
322 if(tb->underlined) {
323 XDrawLine(dpy, tPtr->db, gc,
324 tb->sections[s].x, y + font->y + 1,
325 tb->sections[s].x + tb->sections[s].w, y + font->y + 1);
330 tb = (!done? tb->next : NULL);
334 c = WMGetBagItemCount(tPtr->gfxItems);
335 if(c > 0 && !tPtr->flags.monoFont) {
336 int j;
337 WMWidget *wdt;
338 for(j=0; j<c; j++) {
339 tb = (TextBlock *)WMGetFromBag(tPtr->gfxItems, j);
340 if(!tb || !tb->sections)
341 continue;
342 wdt = tb->d.widget;
343 if(tb->sections[0]._y + tb->sections[0].h
344 <= tPtr->vpos
345 || tb->sections[0]._y
346 >= tPtr->vpos + tPtr->visible.h ) {
348 if((W_VIEW(wdt))->flags.mapped) {
349 WMUnmapWidget(wdt);
351 } else {
352 if(!(W_VIEW(wdt))->flags.mapped) {
353 WMMapWidget(wdt);
354 XLowerWindow(dpy,
355 (W_VIEW(wdt))->window);
358 if(tPtr->flags.ownsSelection && 0
359 //&& (tb->sections[s]._y >= tPtr->sel.y)
360 //&& (tb->sections[s]._y + tb->sections[s].h
361 ){ // <= tPtr->sel.y + tPtr->sel.h)) {
362 XFillRectangle(dpy, tPtr->db, greyGC,
363 tb->sections[0].x, tb->sections[0].y - tPtr->vpos,
364 tb->sections[0].w, tb->sections[0].h);
367 WMMoveWidget(wdt, 3 + tb->sections[0].x + tPtr->visible.x,
368 tb->sections[0].y - tPtr->vpos);
370 if(tb->underlined) {
371 XDrawLine(dpy, tPtr->db, WMColorGC(tb->color),
372 tb->sections[0].x,
373 tb->sections[0].y + WMWidgetHeight(wdt) + 1,
374 tb->sections[0].x + tb->sections[0].w,
375 tb->sections[0].y + WMWidgetHeight(wdt) + 1);
376 } } } }
379 _copy_area:
383 XCopyArea(dpy, tPtr->db, win, tPtr->bgGC,
384 0, 0,
385 tPtr->visible.w, tPtr->visible.h,
386 tPtr->visible.x, tPtr->visible.y);
388 W_DrawRelief(scr, win, 0, 0,
389 tPtr->view->size.width, tPtr->view->size.height,
390 tPtr->flags.relief);
392 if(tPtr->ruler && tPtr->flags.rulerShown)
393 XDrawLine(dpy, win,
394 tPtr->fgGC, 2, 42,
395 tPtr->view->size.width-4, 42);
398 XFillRectangle(tPtr->view->screen->display, tPtr->view->window,
399 tPtr->bgGC,
400 2, tPtr->view->size.height-3,
401 tPtr->view->size.width-4, 3);
407 static void
408 cursorToTextPosition(Text *tPtr, int x, int y)
410 TextBlock *tb = NULL;
411 int done=False, s, len, _w, _y, dir=1; /* 1 == "down" */
412 WMFont *font;
413 char *text;
416 y += tPtr->vpos - tPtr->visible.y;
417 if(y<0) y = 0;
419 x -= tPtr->visible.x-2;
420 if(x<0) x=0;
422 tPtr->clicked.x = x;
423 tPtr->clicked.y = y;
425 /* first, which direction?, most likely, newly clicked
426 position will be close to previous */
427 tb = tPtr->currentTextBlock;
428 if(!tb)
429 tb = tPtr->firstTextBlock;
430 if(!tb || !tb->sections)
431 return;
433 if(y < tb->sections[0].y)
434 dir = 0; /* "up" */
436 //tb = tPtr->firstTextBlock;
437 //dir = 1;
440 if(y == tb->sections[0].y)
441 goto _doneV; /* yeah yeah, goto, whatever... :-P */
443 /* get the first section of the first TextBlock based on v. position */
444 done = False;
445 while(!done && tb) {
446 if(tPtr->flags.monoFont && tb->graphic) {
447 tb = tb->next;
448 continue;
451 printf("tb %p t[%c] blank%d graphic %d\n", tb,
452 *tb->text, tb->blank, tb->graphic);
453 if(!tb->sections) {
454 printf("we have a bad thing!\n");
455 exit(1);
457 s = (dir? 0 : tb->nsections-1);
458 while( (dir? (s<tb->nsections) : (s>=0) )) {
459 if( y >= tb->sections[s]._y
460 && y <= tb->sections[s]._y + tb->sections[s].h) {
461 done = True;
462 break;
463 } else
464 dir? s++ : s--;
466 if(!done) tb = (dir ? tb->next : tb->prior);
469 _doneV:
470 /* we have the line, which TextBlock on that line is it? */
471 if(tb)
472 _y = tb->sections[s]._y;
474 while(tb) {
475 if(!tb->sections)
476 break;
477 if(_y != tb->sections[s]._y)
478 break;
480 if(tb->graphic) {
481 _w = WMWidgetWidth(tb->d.widget);
482 } else {
483 text = &(tb->text[tb->sections[s].begin]);
484 len = tb->sections[s].end - tb->sections[s].begin;
485 font = tb->d.font;
486 _w = myWidthOfString(font, text, len);
488 if(tb->sections[s].x + _w >= x)
489 break;
490 s = 0;
491 tb = tb->next;
494 /* we have said TextBlock, now where within it? */
495 if(tb && !tb->graphic) {
496 int begin = tb->sections[s].begin;
497 int end = tb->sections[s].end;
498 int i=0;
499 len = end-begin;
500 text = &(tb->text[begin]);
501 font = tb->d.font;
503 _w = x - tb->sections[s].x;
505 i = 0;
506 while(i<len && myWidthOfString(font, text, i+1) < _w)
507 i++;
509 i += begin;
510 tPtr->tpos = (i<tb->used)?i:tb->used;
513 // if(!tb)
514 //tb = tPtr->firstTextBlock;
515 tPtr->currentTextBlock = tb;
517 if(tb &&tb->graphic) printf("graphic\n");
520 static void
521 updateScrollers(Text *tPtr)
524 if(tPtr->vS) {
525 if(tPtr->docHeight < tPtr->visible.h) {
526 WMSetScrollerParameters(tPtr->vS, 0, 1);
527 tPtr->vpos = 0;
528 } else {
529 float vmax = (float)(tPtr->docHeight);
530 WMSetScrollerParameters(tPtr->vS,
531 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
532 (float)tPtr->visible.h/vmax);
534 } else tPtr->vpos = 0;
536 if(tPtr->hS)
540 static void
541 scrollersCallBack(WMWidget *w, void *self)
543 Text *tPtr = (Text *)self;
544 Bool scroll = False;
545 Bool dimple = False;
546 int which;
548 if(!tPtr->view->flags.realized) return;
550 if(w == tPtr->vS) {
551 float vmax;
552 int height;
553 vmax = (float)(tPtr->docHeight);
554 height = tPtr->visible.h;
556 which = WMGetScrollerHitPart(tPtr->vS);
557 switch(which) {
558 case WSDecrementLine:
559 if(tPtr->vpos > 0) {
560 if(tPtr->vpos>16) tPtr->vpos-=16;
561 else tPtr->vpos=0;
562 scroll=True;
563 }break;
564 case WSIncrementLine: {
565 int limit = tPtr->docHeight - height;
566 if(tPtr->vpos < limit) {
567 if(tPtr->vpos<limit-16) tPtr->vpos+=16;
568 else tPtr->vpos=limit;
569 scroll = True;
570 }}break;
571 case WSDecrementPage:
572 tPtr->vpos -= height;
574 if(tPtr->vpos < 0)
575 tPtr->vpos = 0;
576 dimple = True;
577 scroll = True;
578 printf("dimple needs to jump to mouse location ;-/\n");
579 break;
580 case WSIncrementPage:
581 tPtr->vpos += height;
582 if(tPtr->vpos > (tPtr->docHeight - height))
583 tPtr->vpos = tPtr->docHeight - height;
584 dimple = True;
585 scroll = True;
586 printf("dimple needs to jump to mouse location ;-/\n");
587 break;
590 case WSKnob:
591 tPtr->vpos = WMGetScrollerValue(tPtr->vS)
592 * (float)(tPtr->docHeight - height);
593 scroll = True;
594 break;
596 case WSKnobSlot:
597 case WSNoPart:
598 printf("WSNoPart, WSKnobSlot\n");
599 #if 0
600 float vmax = (float)(tPtr->docHeight);
601 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
602 (float)tPtr->visible.h/vmax;
603 dimple =where mouse is.
604 #endif
605 break;
607 scroll = (tPtr->vpos != tPtr->prevVpos);
608 tPtr->prevVpos = tPtr->vpos;
611 if(w == tPtr->hS)
614 if(scroll) {
616 if(0&&dimple) {
617 if(tPtr->rulerShown)
618 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
619 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
620 else
621 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
622 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
625 if(which == WSDecrementLine || which == WSIncrementLine)
626 updateScrollers(tPtr);
627 paintText(tPtr);
633 typedef struct {
634 TextBlock *tb;
635 unsigned short begin, end; /* what part of the text block */
637 } myLineItems;
640 static int
641 layOutLine(Text *tPtr, myLineItems *items, int nitems, int x, int y,
642 int pwidth, WMAlignment align)
644 int i, j=0; /* j = justification */
645 int line_width = 0, line_height=0, max_descent=0;
646 WMFont *font;
647 char *text;
648 int len;
649 TextBlock *tb;
650 Bool gfx=0;
651 TextBlock *tbsame=NULL;
653 for(i=0; i<nitems; i++) {
654 tb = items[i].tb;
656 if(tb->graphic) {
657 if(!tPtr->flags.monoFont) {
658 WMWidget *wdt = tb->d.widget;
659 line_height = WMAX(line_height, WMWidgetHeight(wdt));
660 if(align != WALeft)
661 line_width += WMWidgetWidth(wdt);
662 gfx = True;
665 } else {
666 font = (tPtr->flags.monoFont)?tPtr->dFont : tb->d.font;
667 max_descent = WMAX(max_descent, font->height-font->y);
668 line_height = WMAX(line_height, font->height); //+font->height-font->y);
669 text = &(tb->text[items[i].begin]);
670 len = items[i].end - items[i].begin;
671 if(align != WALeft)
672 line_width += myWidthOfString(font, text, len);
676 if(align == WARight) {
677 j = pwidth - line_width;
678 } else if (align == WACenter) {
679 j = (int) ((float)(pwidth - line_width))/2.0;
681 if(gfx)
682 y+=10;
684 for(i=0; i<nitems; i++) {
685 tb = items[i].tb;
687 if(tbsame == tb) { /*extend it, since it's on same line */
688 tb->sections[tb->nsections-1].end = items[i].end;
689 } else {
690 tb->sections = wrealloc(tb->sections,
691 (++tb->nsections)*sizeof(Section));
692 tb->sections[tb->nsections-1]._y = y;
693 tb->sections[tb->nsections-1].x = x+j;
694 tb->sections[tb->nsections-1].h = line_height;
695 tb->sections[tb->nsections-1].begin = items[i].begin;
696 tb->sections[tb->nsections-1].end = items[i].end;
700 if(tb->graphic) {
701 if(!tPtr->flags.monoFont) {
702 WMWidget *wdt = tb->d.widget;
703 tb->sections[tb->nsections-1].y = 1 +max_descent +
704 y + line_height - WMWidgetHeight(wdt);
705 tb->sections[tb->nsections-1].w = WMWidgetWidth(wdt);
706 x += tb->sections[tb->nsections-1].w;
708 } else {
709 font = (tPtr->flags.monoFont)? tPtr->dFont : tb->d.font;
710 len = items[i].end - items[i].begin;
712 text = &(tb->text[items[i].begin]);
714 tb->sections[tb->nsections-1].y = y+line_height-font->y;
715 tb->sections[tb->nsections-1].w =
716 myWidthOfString(font,
717 &(tb->text[tb->sections[tb->nsections-1].begin]),
718 tb->sections[tb->nsections-1].end -
719 tb->sections[tb->nsections-1].begin);
721 x += myWidthOfString(font, text, len);
724 tbsame = tb;
727 return line_height+(gfx?10:0);
732 static void
733 output(char *ptr, int len)
735 char s[len+1];
736 memcpy(s, ptr, len);
737 s[len] = 0;
738 printf(" s is [%s] (%d)\n", s, strlen(s));
743 #define MAX_TB_PER_LINE 64
745 static void
746 layOutDocument(Text *tPtr)
748 TextBlock *tb;
749 myLineItems items[MAX_TB_PER_LINE];
750 WMAlignment align = WALeft;
751 WMFont *font;
752 Bool lhc = !tPtr->flags.laidOut; /* line height changed? */
753 int prev_y;
755 int nitems=0, x=0, y=0, line_width = 0, width=0;
756 int pwidth = tPtr->visible.w - tPtr->visible.x;
758 char *start=NULL, *mark=NULL;
759 int begin, end;
761 if(!(tb = tPtr->firstTextBlock)) {
762 printf("clear view... *pos=0\n");
763 return;
766 if(0&&tPtr->flags.laidOut) {
767 tb = tPtr->currentTextBlock;
768 if(tb->sections && tb->nsections>0)
769 prev_y = tb->sections[tb->nsections-1]._y;
770 y+=10;
771 printf("1 prev_y %d \n", prev_y);
773 /* search backwards for textblocks on same line */
774 while(tb) {
775 if(!tb->sections || tb->nsections<1) {
776 tb = tPtr->firstTextBlock;
777 break;
779 if(tb->sections[tb->nsections-1]._y != prev_y) {
780 tb = tb->next;
781 break;
783 // prev_y = tb->sections[tb->nsections-1]._y;
784 tb = tb->prior;
786 y = 0;//tb->sections[tb->nsections-1]._y;
787 printf("2 prev_y %d \n\n", tb->sections[tb->nsections-1]._y);
791 while(tb) {
793 if(tb->sections && tb->nsections>0) {
794 wfree(tb->sections);
795 tb->sections = NULL;
796 tb->nsections = 0;
799 if(tb->first) {
800 y += layOutLine(tPtr, items, nitems, x, y, pwidth, align);
801 x = 0;//tPtr->visible.x+2;
802 nitems = 0;
803 line_width = 0;
806 if(tb->graphic) {
807 if(!tPtr->flags.monoFont) {
808 width = WMWidgetWidth(tb->d.widget);
809 if(width > pwidth)printf("rescale graphix to fit?\n");
810 line_width += width;
811 if(line_width >= pwidth - x
812 || nitems >= MAX_TB_PER_LINE) {
813 y += layOutLine(tPtr, items, nitems, x, y,
814 pwidth, align);
815 nitems = 0;
816 x = 0;//tPtr->visible.x+2;
817 line_width = width;
820 items[nitems].tb = tb;
821 items[nitems].begin = 0;
822 items[nitems].end = 0;
823 nitems++;
826 } else if((start = tb->text)) {
827 begin = end = 0;
828 font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
830 while(start) {
831 mark = strchr(start, ' ');
832 if(mark) {
833 end += (int)(mark-start)+1;
834 start = mark+1;
835 } else {
836 end += strlen(start);
837 start = mark;
840 if(end-begin > 0) {
842 width = myWidthOfString(font,
843 &tb->text[begin], end-begin);
845 if(width > pwidth) { /* break this tb up */
846 char *t = &tb->text[begin];
847 int l=end-begin, i=0;
848 do {
849 width = myWidthOfString(font, t, ++i);
850 } while (width < pwidth && i < l);
851 end = begin+i;
852 if(start) // and since (nil)-4 = 0xfffffffd
853 start -= l-i;
857 line_width += width;
860 if((line_width >= pwidth - x)
861 || nitems >= MAX_TB_PER_LINE) {
862 y += layOutLine(tPtr, items, nitems, x, y,
863 pwidth, align);
864 line_width = width;
865 x = 0; //tPtr->visible.x+2;
866 nitems = 0;
869 items[nitems].tb = tb;
870 items[nitems].begin = begin;
871 items[nitems].end = end;
872 nitems++;
874 begin = end;
877 tb = tb->next;
881 if(nitems > 0)
882 y += layOutLine(tPtr, items, nitems, x, y, pwidth, align);
883 if(lhc) {
884 tPtr->docHeight = y+10;
885 updateScrollers(tPtr);
887 tPtr->flags.laidOut = True;
892 static void
893 textDidResize(W_ViewDelegate *self, WMView *view)
895 Text *tPtr = (Text *)view->self;
896 unsigned short w = WMWidgetWidth(tPtr);
897 unsigned short h = WMWidgetHeight(tPtr);
898 unsigned short rh = 0, vw = 0;
900 if(tPtr->ruler && tPtr->flags.rulerShown) {
901 WMMoveWidget(tPtr->ruler, 20, 2);
902 WMResizeWidget(tPtr->ruler, w - 22, 40);
903 rh = 40;
906 if(tPtr->vS) {
907 WMMoveWidget(tPtr->vS, 1, rh + 2);
908 WMResizeWidget(tPtr->vS, 20, h - rh - 3);
909 vw = 20;
910 WMSetRulerOffset(tPtr->ruler, 22);
911 } else WMSetRulerOffset(tPtr->ruler, 2);
913 if(tPtr->hS) {
914 if(tPtr->vS) {
915 WMMoveWidget(tPtr->hS, vw, h - 21);
916 WMResizeWidget(tPtr->hS, w - vw - 1, 20);
917 } else {
918 WMMoveWidget(tPtr->hS, vw+1, h - 21);
919 WMResizeWidget(tPtr->hS, w - vw - 2, 20);
923 tPtr->visible.x = (tPtr->vS)?22:0;
924 tPtr->visible.y = (tPtr->ruler && tPtr->flags.rulerShown)?43:3;
925 tPtr->visible.w = tPtr->view->size.width - tPtr->visible.x - 12;
926 tPtr->visible.h = tPtr->view->size.height - tPtr->visible.y;
927 tPtr->visible.h -= (tPtr->hS)?20:0;
929 tPtr->margins[0].left = tPtr->margins[0].right = tPtr->visible.x;
930 tPtr->margins[0].body = tPtr->visible.x;
931 tPtr->margins[0].right = tPtr->visible.w;
933 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
935 if(tPtr->db) {
936 //if(tPtr->view->flags.realized)
937 //XFreePixmap(tPtr->view->screen->display, tPtr->db);
940 //if(size did not change
941 if(tPtr->visible.w < 10) tPtr->visible.w = 10;
942 if(tPtr->visible.h < 10) tPtr->visible.h = 10;
944 tPtr->db = XCreatePixmap(tPtr->view->screen->display,
945 tPtr->view->window, tPtr->visible.w,
946 tPtr->visible.h, tPtr->view->screen->depth);
948 paintText(tPtr);
951 W_ViewDelegate _TextViewDelegate =
953 NULL,
954 NULL,
955 textDidResize,
956 NULL,
959 /* nice, divisble-by-16 memory */
960 static inline unsigned short
961 reqBlockSize(unsigned short requested)
963 return requested+16-(requested%16);
966 static void
967 deleteTextInteractively(Text *tPtr, KeySym ksym)
969 printf("deleting %ld\n", ksym);
972 static void
973 insertTextInteractively(Text *tPtr, char *text, int len)
975 TextBlock *tb;
977 // Chunk *tb=NULL, *newtb=NULL;
978 int height = -23; /* should only be changed upon newline */
979 int w=0;
980 WMFont *font;
981 char *mark = NULL;
983 if(!tPtr->flags.editable || len < 1 || !text
984 || (*text == '\n' && tPtr->flags.ignoreNewLine))
985 return;
987 tb = tPtr->currentTextBlock;
988 if(!tb) {
989 WMAppendTextStream(tPtr, text);
990 WMRefreshText(tPtr, 0, 0);
991 return;
994 if(tb->graphic)
995 return;
997 if(len > 1) {
998 mark = strchr(text, '\n');
999 if(mark) {
1000 len = (int)(mark-text);
1001 mark++;
1003 if(len<1 && mark) {
1004 printf("problem pasting text %d\n", len);
1005 len = strlen(text);
1006 mark = NULL;
1010 font = (tPtr->flags.monoFont || !tb)?tPtr->dFont:tb->d.font;
1012 #if 0
1013 if(*text == '\n') {
1014 int new_top=0;
1015 if(tb) { /* there's a tb (or part of it) to detach from old */
1016 int current = WMGetTextCurrentChunk(tPtr);
1017 if(tPtr->tpos <=0) { /* at start of tb */
1018 if(current<1) { /* the first tb... make old para blank */
1019 newtb = para->tbs;
1020 para->tbs = NULL;
1021 putParagraphOnPixmap(tPtr, para, True);
1022 } else { /* not first tb... */
1023 printf("cut me out \n");
1025 } else if(tPtr->tpos < tb->chars && tb->type == ctText) {
1026 /* not at start of tb */
1027 char text[tb->chars-tPtr->tpos+1];
1028 int i=0;
1029 do {
1030 text[i] = tb->text[tPtr->tpos+i];
1031 } while(++i < tb->chars-tPtr->tpos);
1032 tb->chars -= i;
1033 newtb = (tPtr->funcs.createTChunk) (text, i, tb->font,
1034 tb->color, tb->script, tb->ul);
1035 newtb->next = tb->next;
1036 tb->next = NULL;
1037 /* might want to demalloc for LARGE cuts */
1038 //calcParaExtents(tPtr, para);
1039 para->height = putParagraphOnPixmap(tPtr, para, True);
1040 //putParagraphOnPixmap(tPtr, para, True);
1041 } else if(tPtr->tpos >= tb->chars) {
1042 Chunk *prev;
1043 WMSetTextCurrentChunk(tPtr, current-1);
1044 prev = tPtr->currentChunk;
1045 if(!prev) return;
1046 newtb = prev->next;
1047 prev->next = NULL;
1048 putParagraphOnPixmap(tPtr, para, True);
1050 } else newtb = NULL;
1052 if(para) /* the preceeding one */
1053 new_top = para->bottom;
1055 WMAppendTextStream(tPtr, "\n");
1056 para = tPtr->currentPara;
1057 if(!para) return;
1058 para->tbs = newtb;
1059 tPtr->currentChunk = newtb;
1060 tPtr->tpos = 0;
1061 para->top = new_top;
1062 calcParaExtents(tPtr, para);
1063 height = para->height;
1064 } else {
1065 if(!para) {
1066 WMAppendTextStream(tPtr, text);
1067 para = tPtr->currentPara;
1068 } else if(!para->tbs || !tb) {
1069 //WMPrependTextStream(tPtr, text);
1070 WMAppendTextStream(tPtr, text);
1071 } else if(tb->type == ctImage) {
1072 WMPrependTextStream(tPtr, text);
1074 printf("\n\nprepe\n\n");
1075 } else {
1076 if(tPtr->tpos > tb->chars) {
1077 printf("\n\nmore\n\n");
1078 tPtr->tpos = tb->chars;
1080 #endif
1082 printf("len is %d\n", len);
1083 if(tb->used+len >= tb->allocated) {
1084 tb->allocated = reqBlockSize(tb->used+len);
1085 printf("ralloced %d\n", tb->allocated);
1086 tb->text = wrealloc(tb->text, tb->allocated);
1089 if(tb->blank) {
1090 memmove(tb->text, text, len);
1091 tb->used = len;
1092 tPtr->tpos = len;
1093 tb->blank = False;
1094 } else {
1095 memmove(&(tb->text[tPtr->tpos+len]), &tb->text[tPtr->tpos],
1096 tb->used-tPtr->tpos+1);
1097 memmove(&tb->text[tPtr->tpos], text, len);
1098 tb->used += len;
1099 tPtr->tpos += len;
1101 w = myWidthOfString(font, text, len);
1104 if(mark) {
1105 WMAppendTextStream(tPtr, mark);
1106 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1107 printf("paste: use prev/post chunk's fmt...\n");
1108 } else {
1109 layOutDocument(tPtr);
1110 paintText(tPtr);
1112 #if 0
1113 //doc->clickstart.cursor.x +=
1114 //myWidthOfString(tb->fmt->font, text,len);
1117 #endif
1121 static void
1122 selectRegion(Text *tPtr, int x, int y)
1124 if(x < 0 || y < 0)
1125 return;
1126 y += tPtr->vpos;
1127 if(y>10) y -= 10; /* the original offset */
1129 x -= tPtr->visible.x-2;
1130 if(x<0) x=0;
1132 tPtr->sel.x = WMAX(0, WMIN(tPtr->clicked.x, x));
1133 tPtr->sel.w = abs(tPtr->clicked.x - x);
1134 tPtr->sel.y = WMAX(0, WMIN(tPtr->clicked.y, y));
1135 tPtr->sel.h = abs(tPtr->clicked.y - y);
1137 tPtr->flags.ownsSelection = True;
1138 paintText(tPtr);
1142 static void
1143 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1144 void *cdata, WMData *data)
1146 Text *tPtr = (Text *)view->self;
1147 char *str;
1149 tPtr->flags.waitingForSelection = False;
1151 if(data) {
1152 str = (char*)WMDataBytes(data);
1153 insertTextInteractively(tPtr, str, strlen(str));
1154 } else {
1155 int n;
1156 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1157 if(str) {
1158 str[n] = 0;
1159 insertTextInteractively(tPtr, str, n);
1160 XFree(str);
1165 static void
1166 releaseSelection(Text *tPtr)
1168 printf("I have %d selection\n", 1);
1169 tPtr->flags.ownsSelection = False;
1170 paintText(tPtr);
1173 static WMData *
1174 requestHandler(WMView *view, Atom selection, Atom target,
1175 void *cdata, Atom *type)
1177 Text *tPtr = view->self;
1178 int count;
1179 Display *dpy = tPtr->view->screen->display;
1180 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1181 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1183 *type = target;
1184 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT)
1185 return WMGetTextSelected(tPtr);
1186 else {
1187 WMData *data = WMCreateDataWithBytes("bleh", 4);
1188 return data;
1191 return NULL;
1195 static void
1196 lostHandler(WMView *view, Atom selection, void *cdata)
1198 releaseSelection((WMText *)view->self);
1201 static WMSelectionProcs selectionHandler = {
1202 requestHandler, lostHandler, NULL
1205 static void
1206 _notification(void *observerData, WMNotification *notification)
1208 WMText *to = (WMText *)observerData;
1209 WMText *tw = (WMText *)WMGetNotificationClientData(notification);
1210 if (to != tw)
1211 lostHandler(to->view, XA_PRIMARY, NULL);
1214 static void
1215 handleTextKeyPress(Text *tPtr, XEvent *event)
1217 char buffer[2];
1218 KeySym ksym;
1219 int control_pressed = False;
1220 // int h=1;
1222 if (((XKeyEvent *) event)->state & ControlMask)
1223 control_pressed = True;
1224 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = '\0';
1226 switch(ksym) {
1227 case XK_Right:
1228 case XK_Left:
1229 case XK_Down:
1230 case XK_Up:
1231 printf("arrows %ld\n", ksym);
1232 break;
1234 case XK_BackSpace:
1235 case XK_Delete:
1236 case XK_KP_Delete:
1237 deleteTextInteractively(tPtr, ksym);
1238 break;
1241 case XK_Return:
1242 buffer[0] = '\n';
1243 default:
1244 if(buffer[0] != '\0' && (buffer[0] == '\n' || !control_pressed))
1245 insertTextInteractively(tPtr, buffer, 1);
1246 else if(control_pressed && ksym==XK_r) {
1247 // Bool i = !tPtr->rulerShown; WMShowTextRuler(tPtr, i);
1248 // tPtr->rulerShown = i;
1249 printf("toggle ruler\n");
1251 else if(control_pressed && buffer[0] == '\a')
1252 XBell(tPtr->view->screen->display, 0);
1255 if(tPtr->flags.ownsSelection)
1256 releaseSelection(tPtr);
1260 static void
1261 handleWidgetPress(XEvent *event, void *data)
1263 TextBlock *tb = (TextBlock *)data;
1264 Text *tPtr;
1265 WMWidget *w;
1266 if(!tb)
1267 return;
1268 #if 0
1269 /* this little bit of nastiness here saves a boatload of trouble */
1270 w = (WMWidget *)(W_VIEW((W_VIEW(tb->d.widget))->parent))->self;
1271 //if(W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text)
1272 if( (((W_WidgetType*)(w))->widgetClass) != WC_Text)
1273 return;
1274 *tPtr = (Text*)w;
1275 printf("%p clicked on tb %p wif: (%c)%c", tPtr, tb,
1276 tPtr->firstTextBlock->text[0], tPtr->firstTextBlock->text[1]);
1277 output(tb->text, tb->used);
1278 #endif
1281 static void
1282 handleActionEvents(XEvent *event, void *data)
1284 Text *tPtr = (Text *)data;
1285 Display *dpy = event->xany.display;
1286 KeySym ksym;
1289 if(tPtr->flags.waitingForSelection)
1290 return;
1292 switch (event->type) {
1293 case KeyPress:
1294 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1295 if(ksym == XK_Shift_R || ksym == XK_Shift_L) {
1296 tPtr->flags.extendSelection = True;
1297 return;
1299 if(!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1300 XBell(dpy, 0);
1301 return;
1304 if (tPtr->flags.waitingForSelection)
1305 return;
1306 if(tPtr->flags.focused) {
1307 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1308 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1309 GrabModeAsync, GrabModeAsync, None,
1310 W_VIEW(tPtr)->screen->invisibleCursor, CurrentTime);
1311 tPtr->flags.pointerGrabbed = True;
1312 handleTextKeyPress(tPtr, event);
1314 } break;
1316 case KeyRelease:
1317 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1318 if(ksym == XK_Shift_R || ksym == XK_Shift_L) {
1319 tPtr->flags.extendSelection = False;
1320 return;
1321 //end modify flag so selection can be extended
1323 break;
1326 case MotionNotify:
1327 if(tPtr->flags.pointerGrabbed) {
1328 tPtr->flags.pointerGrabbed = False;
1329 XUngrabPointer(dpy, CurrentTime);
1331 if((event->xmotion.state & Button1Mask)) {
1332 if(!tPtr->flags.ownsSelection) {
1333 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1334 event->xbutton.time, &selectionHandler, NULL);
1335 tPtr->flags.ownsSelection = True;
1337 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1339 break;
1342 case ButtonPress:
1343 tPtr->flags.buttonHeld = True;
1344 if(tPtr->flags.extendSelection) {
1345 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1346 return;
1348 if(event->xbutton.button == Button1) {
1349 if(tPtr->flags.ownsSelection)
1350 releaseSelection(tPtr);
1351 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1352 if (tPtr->flags.pointerGrabbed) {
1353 tPtr->flags.pointerGrabbed = False;
1354 XUngrabPointer(dpy, CurrentTime);
1355 break;
1358 if(!tPtr->flags.focused) {
1359 WMSetFocusToWidget(tPtr);
1360 tPtr->flags.focused = True;
1361 break;
1364 if(event->xbutton.button == WINGsConfiguration.mouseWheelDown)
1365 WMScrollText(tPtr, -16);
1366 else if(event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1367 WMScrollText(tPtr, 16);
1368 break;
1370 case ButtonRelease:
1371 tPtr->flags.buttonHeld = False;
1372 if (tPtr->flags.pointerGrabbed) {
1373 tPtr->flags.pointerGrabbed = False;
1374 XUngrabPointer(dpy, CurrentTime);
1375 break;
1377 if(event->xbutton.button == WINGsConfiguration.mouseWheelDown
1378 || event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1379 break;
1381 if(event->xbutton.button == Button2 && tPtr->flags.editable) {
1382 char *text = NULL;
1383 int n;
1384 if(!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1385 event->xbutton.time, pasteText, NULL)) {
1386 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1387 if(text) {
1388 text[n] = 0;
1389 insertTextInteractively(tPtr, text, n-1);
1390 XFree(text);
1391 } else tPtr->flags.waitingForSelection = True;
1394 break;
1401 static void
1402 handleEvents(XEvent *event, void *data)
1404 Text *tPtr = (Text *)data;
1406 switch(event->type) {
1407 case Expose:
1408 if(!event->xexpose.count && tPtr->view->flags.realized)
1409 paintText(tPtr);
1410 break;
1412 case FocusIn:
1413 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
1414 return;
1415 tPtr->flags.focused = True;
1416 break;
1418 case FocusOut:
1419 tPtr->flags.focused = False;
1420 break;
1422 case DestroyNotify:
1423 printf("destroy");
1424 //for(...)WMRemoveTextParagraph(tPtr, para);
1425 break;
1432 static void
1433 clearText(Text *tPtr)
1435 void *tb;
1436 if(!tPtr->firstTextBlock)
1437 return;
1439 while(tPtr->currentTextBlock)
1440 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1442 printf("yadda clearText\n");
1444 printf("remove the document\n");
1445 tPtr->firstTextBlock = NULL;
1446 tPtr->currentTextBlock = NULL;
1447 tPtr->lastTextBlock = NULL;
1448 //WMThawText(tPtr);
1449 WMRefreshText(tPtr, 0, 0);
1453 static void
1454 insertPlainText(WMText *tPtr, char *text)
1456 char *start, *mark;
1457 void *tb = NULL;
1460 if(!text) {
1461 clearText(tPtr);
1462 return;
1466 start = text;
1467 while(start) {
1468 mark = strchr(start, '\n');
1469 if(mark) {
1470 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1471 tPtr->dColor, True, (int)(mark-start));
1472 start = mark+1;
1473 } else {
1474 if(start && strlen(start)) {
1475 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1476 tPtr->dColor, False, strlen(start));
1477 } else tb = NULL;
1478 start = mark;
1481 if(tPtr->flags.prepend)
1482 WMPrependTextBlock(tPtr, tb);
1483 else
1484 WMAppendTextBlock(tPtr, tb);
1486 return;
1495 WMText *
1496 WMCreateText(WMWidget *parent)
1498 Text *tPtr = wmalloc(sizeof(Text));
1499 if(!tPtr) {
1500 printf("could not create text widget\n");
1501 return NULL;
1504 #if 0
1505 printf("sizeof:\n");
1506 printf(" TextBlock %d\n", sizeof(TextBlock));
1507 printf(" TextBlock *%d\n", sizeof(TextBlock *));
1508 printf(" Section %d\n", sizeof(Section));
1509 printf(" char * %d\n", sizeof(char *));
1510 printf(" void * %d\n", sizeof(void *));
1511 printf(" short %d\n", sizeof(short));
1512 printf(" Text %d\n", sizeof(Text));
1513 #endif
1515 memset(tPtr, 0, sizeof(Text));
1516 tPtr->widgetClass = WC_Text;
1517 tPtr->view = W_CreateView(W_VIEW(parent));
1518 if (!tPtr->view) {
1519 perror("could not create text's view\n");
1520 free(tPtr);
1521 return NULL;
1523 tPtr->view->self = tPtr;
1524 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
1525 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
1526 W_ResizeView(tPtr->view, 250, 200);
1527 tPtr->bgGC = WMColorGC(tPtr->view->screen->white);
1528 tPtr->fgGC = WMColorGC(tPtr->view->screen->black);
1529 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
1531 tPtr->ruler = NULL;
1532 tPtr->vS = NULL;
1533 tPtr->hS = NULL;
1535 tPtr->dFont = NULL;
1536 //tPtr->dFont = WMCreateFont(tPtr->view->screen,
1537 // "-*-fixed-medium-r-normal--26-*-*-*-*-*-*-*");
1538 //"-sony-fixed-medium-r-normal--24-230-75-75-c-120-jisx0201.1976-0");
1539 // "-*-times-bold-r-*-*-12-*-*-*-*-*-*-*,"
1540 // "-*-fixed-medium-r-normal-*-12-*");
1541 if (!tPtr->dFont)
1542 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
1544 tPtr->dColor = WMBlackColor(tPtr->view->screen);
1546 tPtr->view->delegate = &_TextViewDelegate;
1548 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
1549 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
1550 handleEvents, tPtr);
1552 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
1553 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
1554 handleActionEvents, tPtr);
1556 WMAddNotificationObserver(_notification, tPtr, "_lostOwnership", tPtr);
1559 tPtr->firstTextBlock = NULL;
1560 tPtr->lastTextBlock = NULL;
1561 tPtr->currentTextBlock = NULL;
1562 tPtr->tpos = 0;
1564 tPtr->gfxItems = WMCreateArrayBag(4);
1566 tPtr->parser = NULL;
1567 tPtr->writer = NULL;
1569 tPtr->sel.x = tPtr->sel.y = 2;
1570 tPtr->sel.w = tPtr->sel.h = 0;
1572 tPtr->clicked.x = tPtr->clicked.y = 2;
1574 tPtr->visible.x = tPtr->visible.y = 2;
1575 tPtr->visible.h = tPtr->view->size.height;
1576 tPtr->visible.w = tPtr->view->size.width - 12;
1578 tPtr->docWidth = 0;
1579 tPtr->docHeight = 0;
1580 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
1581 default_bullet);
1582 tPtr->db = (Pixmap) NULL;
1584 tPtr->margins = wmalloc(sizeof(WMRulerMargins));
1585 tPtr->margins[0].left = tPtr->margins[0].right = tPtr->visible.x;
1586 tPtr->margins[0].body = tPtr->visible.x;
1587 tPtr->margins[0].right = tPtr->visible.w;
1589 tPtr->flags.nmargins = 1;
1590 tPtr->flags.rulerShown = False;
1591 tPtr->flags.monoFont = !True;
1592 tPtr->flags.focused = False;
1593 tPtr->flags.editable = True;
1594 tPtr->flags.ownsSelection = False;
1595 tPtr->flags.pointerGrabbed = False;
1596 tPtr->flags.buttonHeld = False;
1597 tPtr->flags.waitingForSelection = False;
1598 tPtr->flags.extendSelection = False;
1599 tPtr->flags.rulerShown = False;
1600 tPtr->flags.frozen = False;
1601 tPtr->flags.cursorShown = True;
1602 tPtr->flags.clickPos = 1;
1603 tPtr->flags.ignoreNewLine = False;
1604 tPtr->flags.laidOut = False;
1605 tPtr->flags.prepend = False;
1606 tPtr->flags.relief = WRFlat;
1607 tPtr->flags.alignment = WALeft;
1609 return tPtr;
1613 void
1614 WMPrependTextStream(WMText *tPtr, char *text)
1616 if(!tPtr)
1617 return;
1618 //check for "{\rtf0" in the text...
1619 //insertRTF
1620 //else
1621 tPtr->flags.prepend = True;
1622 if(text && tPtr->parser)
1623 (tPtr->parser) (tPtr, (void *) text);
1624 else
1625 insertPlainText(tPtr, text);
1629 void
1630 WMAppendTextStream(WMText *tPtr, char *text)
1632 if(!tPtr)
1633 return;
1634 //check for "{\rtf0" in the text...
1635 //insertRTF
1636 //else
1637 tPtr->flags.prepend = False;
1638 if(text && tPtr->parser)
1639 (tPtr->parser) (tPtr, (void *) text);
1640 else
1641 insertPlainText(tPtr, text);
1645 WMData *
1646 WMGetTextSelected(WMText *tPtr)
1648 WMData *data = NULL;
1649 TextBlock *tb;
1651 if(!tPtr)
1652 return NULL;
1654 //tb = tPtr->firstTextBlock;
1655 tb = tPtr->currentTextBlock;
1656 if(!tb)
1657 return NULL;
1659 data = WMCreateDataWithBytes(tb->text, tb->used);
1660 if(data)
1661 WMSetDataFormat(data, 8);
1662 return data;
1665 void *
1666 WMCreateTextBlockWithObject(WMWidget *w, char *description, WMColor *color,
1667 unsigned short first, unsigned short reserved)
1669 TextBlock *tb;
1670 unsigned short length;
1672 if(!w || !description || !color)
1673 return NULL;
1675 tb = wmalloc(sizeof(TextBlock));
1676 if(!tb)
1677 return NULL;
1679 length = strlen(description);
1680 tb->text = (char *)wmalloc(length);
1681 memset(tb->text, 0, length);
1682 memcpy(tb->text, description, length);
1683 tb->used = length;
1684 tb->blank = False;
1685 tb->d.widget = w;
1686 tb->color = WMRetainColor(color);
1687 tb->marginN = 0;
1688 tb->allocated = 0;
1689 tb->first = first;
1690 tb->kanji = False;
1691 tb->graphic = True;
1692 tb->underlined = False;
1693 tb->script = 0;
1694 tb->sections = NULL;
1695 tb->nsections = 0;
1696 tb->prior = NULL;
1697 tb->next = NULL;
1699 return tb;
1702 void *
1703 WMCreateTextBlockWithText(char *text, WMFont *font, WMColor *color,
1704 unsigned short first, unsigned short length)
1706 TextBlock *tb;
1708 if(!font || !color)
1709 return NULL;
1711 tb = wmalloc(sizeof(TextBlock));
1712 if(!tb)
1713 return NULL;
1715 tb->allocated = reqBlockSize(length);
1716 tb->text = (char *)wmalloc(tb->allocated);
1717 memset(tb->text, 0, tb->allocated);
1719 if(length < 1|| !text ) { // || *text == '\n') {
1720 *tb->text = ' ';
1721 tb->used = 1;
1722 tb->blank = True;
1723 } else {
1724 memcpy(tb->text, text, length);
1725 tb->used = length;
1726 tb->blank = False;
1729 tb->d.font = WMRetainFont(font);
1730 tb->color = WMRetainColor(color);
1731 tb->marginN = 0;
1732 tb->first = first;
1733 tb->kanji = False;
1734 tb->graphic = False;
1735 tb->underlined = False;
1736 tb->script = 0;
1737 tb->sections = NULL;
1738 tb->nsections = 0;
1739 tb->prior = NULL;
1740 tb->next = NULL;
1741 return tb;
1744 void
1745 WMSetTextBlockProperties(void *vtb, unsigned int first,
1746 unsigned int kanji, unsigned int underlined, int script,
1747 unsigned int marginN)
1749 TextBlock *tb = (TextBlock *) vtb;
1750 if(!tb)
1751 return;
1753 tb->first = first;
1754 tb->kanji = kanji;
1755 tb->underlined = underlined;
1756 tb->script = script;
1757 tb->marginN = marginN;
1760 void
1761 WMGetTextBlockProperties(void *vtb, unsigned int *first,
1762 unsigned int *kanji, unsigned int *underlined, int *script,
1763 unsigned int *marginN)
1765 TextBlock *tb = (TextBlock *) vtb;
1766 if(!tb)
1767 return;
1769 if(first) *first = tb->first;
1770 if(kanji) *kanji = tb->kanji;
1771 if(underlined) *underlined = tb->underlined;
1772 if(script) *script = tb->script;
1773 if(marginN) *marginN = tb->marginN;
1778 void
1779 WMPrependTextBlock(WMText *tPtr, void *vtb)
1781 TextBlock *tb = (TextBlock *)vtb;
1784 if(!tPtr || !tb)
1785 return;
1787 if(tb->graphic) {
1788 WMWidget *w = tb->d.widget;
1789 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
1790 handleWidgetPress, tb);
1791 //if(W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
1792 if(W_CLASS(w) != WC_TextField &&
1793 (((W_WidgetType*)(w))->widgetClass) != WC_Text) {
1794 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
1795 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
1797 WMPutInBag(tPtr->gfxItems, (void *)tb);
1798 WMRealizeWidget(w);
1801 if(!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
1802 tb->next = tb->prior = NULL;
1803 tPtr->lastTextBlock = tPtr->firstTextBlock
1804 = tPtr->currentTextBlock = tb;
1805 return;
1808 tb->next = tPtr->currentTextBlock;
1809 tb->prior = tPtr->currentTextBlock->prior;
1810 if(tPtr->currentTextBlock->prior)
1811 tPtr->currentTextBlock->prior->next = tb;
1813 tPtr->currentTextBlock->prior = tb;
1814 if(!tb->prior)
1815 tPtr->firstTextBlock = tb;
1817 tPtr->currentTextBlock = tb;
1821 void
1822 WMAppendTextBlock(WMText *tPtr, void *vtb)
1824 TextBlock *tb = (TextBlock *)vtb;
1826 if(!tPtr || !tb)
1827 return;
1829 if(tb->graphic) {
1830 WMWidget *w = tb->d.widget;
1831 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
1832 handleWidgetPress, tb);
1833 //if(W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
1834 if(W_CLASS(w) != WC_TextField &&
1835 (((W_WidgetType*)(w))->widgetClass) != WC_Text) {
1836 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
1837 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
1839 WMPutInBag(tPtr->gfxItems, (void *)tb);
1840 WMRealizeWidget(w);
1843 if(!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
1844 tb->next = tb->prior = NULL;
1845 tPtr->lastTextBlock = tPtr->firstTextBlock
1846 = tPtr->currentTextBlock = tb;
1847 return;
1850 tb->next = tPtr->currentTextBlock->next;
1851 tb->prior = tPtr->currentTextBlock;
1852 if(tPtr->currentTextBlock->next)
1853 tPtr->currentTextBlock->next->prior = tb;
1855 tPtr->currentTextBlock->next = tb;
1857 if(!tb->next)
1858 tPtr->lastTextBlock = tb;
1860 tPtr->currentTextBlock = tb;
1863 void *
1864 WMRemoveTextBlock(WMText *tPtr)
1866 TextBlock *tb = NULL;
1868 if(!tPtr || !tPtr->firstTextBlock || !tPtr->lastTextBlock
1869 || !tPtr->currentTextBlock) {
1870 printf("cannot remove non existent TextBlock!\b");
1871 return tb;
1874 tb = tPtr->currentTextBlock;
1875 if(tb->graphic) {
1876 WMDeleteEventHandler(W_VIEW(tb->d.widget), ButtonPressMask,
1877 handleWidgetPress, tb);
1878 WMRemoveFromBag(tPtr->gfxItems, (void *)tb);
1879 WMUnmapWidget(tb->d.widget);
1882 if(tPtr->currentTextBlock == tPtr->firstTextBlock) {
1883 if(tPtr->currentTextBlock->next)
1884 tPtr->currentTextBlock->next->prior = NULL;
1886 tPtr->firstTextBlock = tPtr->currentTextBlock->next;
1887 tPtr->currentTextBlock = tPtr->firstTextBlock;
1889 } else if(tPtr->currentTextBlock == tPtr->lastTextBlock) {
1890 tPtr->currentTextBlock->prior->next = NULL;
1891 tPtr->lastTextBlock = tPtr->currentTextBlock->prior;
1892 tPtr->currentTextBlock = tPtr->lastTextBlock;
1893 } else {
1894 tPtr->currentTextBlock->prior->next = tPtr->currentTextBlock->next;
1895 tPtr->currentTextBlock->next->prior = tPtr->currentTextBlock->prior;
1896 tPtr->currentTextBlock = tPtr->currentTextBlock->next;
1899 return (void *)tb;
1902 void
1903 WMDestroyTextBlock(WMText *tPtr, void *vtb)
1905 TextBlock *tb = (TextBlock *)vtb;
1906 if(!tPtr || !tb)
1907 return;
1909 if(tb->graphic) {
1910 return;
1911 WMDestroyWidget(tb->d.widget);
1912 wfree(tb->d.widget);
1913 } else {
1914 WMReleaseFont(tb->d.font);
1917 WMReleaseColor(tb->color);
1918 if(tb->sections && tb->nsections > 0)
1919 wfree(tb->sections);
1920 wfree(tb->text);
1921 wfree(tb);
1925 void
1926 WMRefreshText(WMText *tPtr, int vpos, int hpos)
1928 //TextBlock *tb;
1930 if(!tPtr || vpos<0 || hpos<0)
1931 return;
1933 tPtr->flags.laidOut = False;
1934 layOutDocument(tPtr);
1935 updateScrollers(tPtr);
1936 paintText(tPtr);
1941 void
1942 WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
1944 if(!tPtr)
1945 return;
1947 if(color)
1948 tPtr->fgGC = WMColorGC(color);
1949 else
1950 tPtr->fgGC = WMColorGC(WMBlackColor(tPtr->view->screen));
1952 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1955 void
1956 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
1958 if(!tPtr)
1959 return;
1961 if(color) {
1962 tPtr->bgGC = WMColorGC(color);
1963 W_SetViewBackgroundColor(tPtr->view, color);
1964 } else {
1965 tPtr->bgGC = WMColorGC(WMWhiteColor(tPtr->view->screen));
1966 W_SetViewBackgroundColor(tPtr->view,
1967 WMWhiteColor(tPtr->view->screen));
1970 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1973 void
1974 WMSetTextRelief(WMText *tPtr, WMReliefType relief)
1976 if(!tPtr)
1977 return;
1978 tPtr->flags.relief = relief;
1979 paintText(tPtr);
1982 void
1983 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
1985 if(!tPtr)
1986 return;
1988 if(shouldhave && !tPtr->hS) {
1989 tPtr->hS = WMCreateScroller(tPtr);
1990 (W_VIEW(tPtr->hS))->attribs.cursor = tPtr->view->screen->defaultCursor;
1991 (W_VIEW(tPtr->hS))->attribFlags |= CWOverrideRedirect | CWCursor;
1992 WMSetScrollerArrowsPosition(tPtr->hS, WSAMaxEnd);
1993 WMSetScrollerAction(tPtr->hS, scrollersCallBack, tPtr);
1994 WMRealizeWidget(tPtr->hS);
1995 WMMapWidget(tPtr->hS);
1996 } else if(!shouldhave && tPtr->hS) {
1997 WMUnmapWidget(tPtr->hS);
1998 WMDestroyWidget(tPtr->hS);
1999 tPtr->hS = NULL;
2002 tPtr->hpos = 0;
2003 tPtr->prevHpos = 0;
2004 textDidResize(tPtr->view->delegate, tPtr->view);
2008 void
2009 WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
2011 if(!tPtr)
2012 return;
2014 if(shouldhave && !tPtr->vS) {
2015 tPtr->vS = WMCreateScroller(tPtr);
2016 (W_VIEW(tPtr->vS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2017 (W_VIEW(tPtr->vS))->attribFlags |= CWOverrideRedirect | CWCursor;
2018 WMSetScrollerArrowsPosition(tPtr->vS, WSAMaxEnd);
2019 WMSetScrollerAction(tPtr->vS, scrollersCallBack, tPtr);
2020 WMRealizeWidget(tPtr->vS);
2021 WMMapWidget(tPtr->vS);
2022 } else if(!shouldhave && tPtr->vS) {
2023 WMUnmapWidget(tPtr->vS);
2024 WMDestroyWidget(tPtr->vS);
2025 tPtr->vS = NULL;
2028 tPtr->vpos = 0;
2029 tPtr->prevVpos = 0;
2030 textDidResize(tPtr->view->delegate, tPtr->view);
2035 Bool
2036 WMScrollText(WMText *tPtr, int amount)
2038 Bool scroll=False;
2039 if(!tPtr)
2040 return False;
2041 if(amount == 0 || !tPtr->view->flags.realized)
2042 return False;
2044 if(amount < 0) {
2045 if(tPtr->vpos > 0) {
2046 if(tPtr->vpos > amount) tPtr->vpos += amount;
2047 else tPtr->vpos=0;
2048 scroll=True;
2049 } } else {
2050 int limit = tPtr->docHeight - tPtr->visible.h;
2051 if(tPtr->vpos < limit) {
2052 if(tPtr->vpos < limit-amount) tPtr->vpos += amount;
2053 else tPtr->vpos = limit;
2054 scroll = True;
2055 } }
2057 if(scroll && tPtr->vpos != tPtr->prevVpos) {
2058 updateScrollers(tPtr);
2059 paintText(tPtr);
2061 tPtr->prevVpos = tPtr->vpos;
2062 return scroll;
2065 Bool
2066 WMPageText(WMText *tPtr, Bool direction)
2068 if(!tPtr) return False;
2069 if(!tPtr->view->flags.realized) return False;
2071 return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
2075 void
2076 WMSetTextUseMonoFont(WMText *tPtr, Bool mono)
2078 if(!tPtr)
2079 return;
2080 if(mono && tPtr->flags.rulerShown)
2081 ;//WMShowTextRuler(tPtr, False);
2083 tPtr->flags.monoFont = mono;
2084 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2087 Bool
2088 WMGetTextUsesMonoFont(WMText *tPtr)
2090 if(!tPtr)
2091 return True;
2092 return tPtr->flags.monoFont;
2095 void
2096 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
2098 if(!tPtr)
2099 return;
2101 if(font)
2102 tPtr->dFont = font;
2103 else
2104 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2107 WMFont *
2108 WMGetTextDefaultFont(WMText *tPtr)
2110 if(!tPtr)
2111 return NULL;
2112 else
2113 return tPtr->dFont;
2116 void
2117 WMSetTextParser(WMText *tPtr, WMAction *parser)
2119 if(!tPtr)
2120 return;
2121 tPtr->parser = parser;
2125 void
2126 WMSetTextWriter(WMText *tPtr, WMAction *writer)
2128 if(!tPtr)
2129 return;
2130 tPtr->writer = writer;
2133 int
2134 WMGetTextInsertType(WMText *tPtr)
2136 if(!tPtr)
2137 return 0;
2138 return tPtr->flags.prepend;