added string utils
[wmaker-crm.git] / WINGs / wtext.c
bloba00adfab60097344442fc24ab240da3fdb8c6510
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.
21 /* README README README README README README README
22 * Nwanua: dont use // style comments please!
23 * It doesnt work in lots of compilers out there :/
24 * -Alfredo
25 * README README README README README README README
28 #include "WINGsP.h"
29 #include <X11/keysym.h>
30 #include <X11/Xatom.h>
33 //_______
34 //TODO:
36 #if 0
38 use currentTextBlock and neighbours for fast paint and layout
40 WMGetTextStreamAll... WMGetTextStream WMGetTextSelection(if(selected) )
42 the bitfield arrangement in this code assumes a little-endian
43 machine... might need a __BIG_ENDIAN__ define for arranging
44 the bitfields efficiently for those big boys.
46 make a file named fontman.c, put that kind of
47 stuff in there and not put the APIs in WINGs.h
48 WMGetFontItalic() should some day be part of the font manager
49 instead, just put a bunch of extern WMGetFontbla in the top of wtext.c
51 #endif
53 //_______
58 /* a Section is a section of a TextBlock that describes what parts
59 of a TextBlock has be layout on which "line"...
60 o this greatly aids redraw, scroll and selection.
61 o this is created during layoutLine, but may be later modified.
62 o there may be many regions per TextBlock, hence the array */
63 typedef struct {
64 int x, y; /* where to draw it from */
65 int w, h; /* it's width and height (to aid selection) */
66 int _y;
67 unsigned short begin, end; /* what part of the text block */
68 } Section;
71 /* a TextBlock is a doubly-linked list of TextBlocks containing:
72 o text for the block, color and font
73 o or a pointer to the widget and the (text) description for its graphic
74 o but NOT both */
76 typedef struct _TextBlock {
77 struct _TextBlock *next; /* next text block in linked list */
78 struct _TextBlock *prior; /* prior text block in linked list */
80 char *text; /* pointer to 8- or 16-bit text */
81 /* or to the object's description */
82 union {
83 WMFont *font; /* the font */
84 WMWidget *widget; /* the embedded widget */
85 } d; /* description */
87 WMColor *color; /* the color */
88 Section *sections; /* the region for layouts (a growable array) */
89 /* an _array_! of size _nsections_ */
91 unsigned short used; /* number of chars in this block */
92 unsigned short allocated; /* size of allocation (in chars) */
94 unsigned int first:1; /* first TextBlock in paragraph */
95 unsigned int blank:1; /* ie. blank paragraph */
96 unsigned int kanji:1; /* is of 16-bit characters or not */
97 unsigned int graphic:1; /* embedded object or text: text=0 */
98 unsigned int underlined:1; /* underlined or not */
99 unsigned int nsections:8; /* over how many "lines" a TexBlock wraps */
100 int script:8; /* script in points: negative for subscript */
101 unsigned int marginN:10; /* which of the margins in WMText to use */
102 unsigned int RESERVED:1;
103 } TextBlock;
106 /* somehow visible.h beats the hell outta visible.size.height :-) */
107 typedef struct {
108 unsigned int y;
109 unsigned int x;
110 unsigned int h;
111 unsigned int w;
112 } myRect;
115 typedef struct W_Text {
116 W_Class widgetClass; /* the class number of this widget */
117 W_View *view; /* the view referring to this instance */
119 WMRuler *ruler; /* the ruler subwiget to manipulate paragraphs */
121 WMScroller *vS; /* the vertical scroller */
122 int vpos; /* the current vertical position */
123 int prevVpos; /* the previous vertical position */
125 WMScroller *hS; /* the horizontal scroller */
126 int hpos; /* the current horizontal position */
127 int prevHpos; /* the previous horizontal position */
128 /* in short: tPtr->hS?nowrap:wrap */
130 WMFont *dFont; /* the default font */
131 WMColor *dColor; /* the default color */
132 WMPixmap *dBulletPix; /* the default pixmap for bullets */
134 GC bgGC; /* the background GC to draw with */
135 GC fgGC; /* the foreground GC to draw with */
136 Pixmap db; /* the buffer on which to draw */
138 WMRulerMargins *margins;/* a (growable) array of margins to be used */
139 /* by the various TextBlocks */
141 myRect visible; /* the actual rectangle that can be drawn into */
142 myRect sel; /* the selection rectangle */
143 int docWidth; /* the width of the entire document */
144 int docHeight; /* the height of the entire document */
147 TextBlock *firstTextBlock;
148 TextBlock *lastTextBlock;
149 TextBlock *currentTextBlock;
152 WMBag *gfxItems; /* a nice bag containing graphic items */
154 WMPoint clicked; /* where in the _document_ was clicked */
155 unsigned short tpos; /* the character position in the currentTextBlock */
156 unsigned short RESERVED;/* space taker upper... */
159 WMAction *parser;
160 WMAction *writer;
162 struct {
163 unsigned int monoFont:1; /* whether to ignore formats */
164 unsigned int focused:1; /* whether this instance has input focus */
165 unsigned int editable:1; /* "silly user, you can't edit me" */
166 unsigned int ownsSelection:1; /* "I ownz the current selection!" */
167 unsigned int pointerGrabbed:1;/* "heh, gib me pointer" */
168 unsigned int buttonHeld:1; /* the user is holding down the button */
169 unsigned int waitingForSelection:1; /* dum dee dumm... */
170 unsigned int extendSelection:1; /* shift-drag to select more regions */
172 unsigned int rulerShown:1; /* whether the ruler is shown or not */
173 unsigned int frozen:1; /* whether screen updates are to be made */
174 unsigned int cursorShown:1; /* whether to show the cursor */
175 unsigned int clickPos:1; /* clicked before=0/after=1 a graphic: */
176 /* within counts as after too */
178 unsigned int ignoreNewLine:1;/* "bleh XK_Return" ignore it when typed */
179 unsigned int laidOut:1; /* have the TextBlocks all been laid out */
180 unsigned int prepend:1; /* prepend=1, append=0 (for parsers) */
181 WMAlignment alignment:2; /* the alignment for text */
182 WMReliefType relief:3; /* the relief to display with */
183 unsigned int RESERVED:4;
184 unsigned int nmargins:10; /* the number of margin arrays */
185 } flags;
186 } Text;
188 static char *default_bullet[] = {
189 "6 6 4 1",
190 " c None s None", ". c black",
191 "X c white", "o c #808080",
192 " ... ",
193 ".XX.. ",
194 ".XX..o",
195 ".....o",
196 " ...oo",
197 " ooo "};
200 /* done purely for speed ... mostly same as WMWidthOfString */
201 static inline unsigned int
202 myWidthOfString(WMFont *font, char *text, unsigned int length)
204 if (font->notFontSet)
205 return XTextWidth(font->font.normal, text, length);
206 else {
207 XRectangle rect, AIXsucks;
208 XmbTextExtents(font->font.set, text, length, &AIXsucks, &rect);
209 return rect.width;
214 static void
215 paintText(Text *tPtr)
217 TextBlock *tb = tPtr->firstTextBlock;
218 WMFont *font;
219 GC gc, greyGC;
220 char *text;
221 int len, y, c, s, done=False;
222 int prev_y=-23;
223 WMScreen *scr = tPtr->view->screen;
224 Display *dpy = tPtr->view->screen->display;
225 Window win = tPtr->view->window;
229 if(!tPtr->view->flags.realized || !tPtr->db)
230 return;
232 XFillRectangle(dpy, tPtr->db, tPtr->bgGC,
233 0, 0, tPtr->visible.w, tPtr->visible.h);
236 tb = tPtr->firstTextBlock;
237 if(!tb)
238 goto _copy_area;
241 if(tPtr->flags.ownsSelection) {
242 greyGC = WMColorGC(WMGrayColor(scr));
243 //XFillRectangle(dpy, tPtr->db, greyGC,
244 // tPtr->sel.x, tPtr->sel.y-tPtr->vpos, tPtr->sel.w, tPtr->sel.h);
245 // XDrawRectangle(dpy, tPtr->db, tPtr->fgGC,
246 // tPtr->sel.x, tPtr->sel.y-tPtr->vpos, tPtr->sel.w, tPtr->sel.h);
249 done = False;
250 while(!done && tb) {
252 if(!tb->sections || (!tPtr->flags.monoFont && tb->graphic)) {
253 tb = tb->next;
254 continue;
257 for(s=0; s<tb->nsections && !done; s++) {
260 if(tb->sections[s]._y > tPtr->vpos + tPtr->visible.h) {
261 done = True;
262 break;
265 if( tb->sections[s].y + tb->sections[s].h < tPtr->vpos)
266 continue;
268 if(tPtr->flags.monoFont) {
269 font = tPtr->dFont;
270 gc = tPtr->fgGC;
271 } else {
272 font = tb->d.font;
273 gc = WMColorGC(tb->color);
276 if(tPtr->flags.ownsSelection) {
278 if(prev_y != tb->sections[s]._y
279 && (tb->sections[s]._y >= tPtr->sel.y)
280 && (tb->sections[s]._y + tb->sections[s].h
281 <= tPtr->sel.y + tPtr->sel.h)) {
282 XFillRectangle(dpy, tPtr->db, greyGC,
283 tPtr->visible.x,
284 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
285 tPtr->visible.w, tb->sections[s].h);
287 } else if( prev_y != tb->sections[s]._y
288 && (tb->sections[s]._y <= tPtr->sel.y)
289 && (tb->sections[s]._y + tb->sections[s].h
290 >= tPtr->sel.y)
291 && (tPtr->sel.x >= tb->sections[s].x)
292 && (tPtr->sel.y + tPtr->sel.h
293 >= tb->sections[s]._y + tb->sections[s].h)) {
294 XFillRectangle(dpy, tPtr->db, greyGC,
295 tPtr->clicked.x,
296 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
297 tPtr->visible.w - tPtr->sel.x, tb->sections[s].h);
299 } else if(prev_y != tb->sections[s]._y
300 && (tb->sections[s]._y <= tPtr->sel.y + tPtr->sel.h)
301 && (tb->sections[s]._y >= tPtr->sel.y)) {
302 XFillRectangle(dpy, tPtr->db, greyGC,
303 tPtr->visible.x,
304 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
305 tPtr->sel.x + tPtr->sel.w -tPtr->visible.x,
306 tb->sections[s].h);
308 } else if( prev_y != tb->sections[s]._y
309 && (tb->sections[s]._y <= tPtr->sel.y)
310 && (tb->sections[s]._y + tb->sections[s].h
311 >= tPtr->sel.y + tPtr->sel.h) ) {
312 XFillRectangle(dpy, tPtr->db, greyGC,
313 tPtr->sel.x,
314 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
315 tPtr->sel.w,tb->sections[s].h);
320 prev_y = tb->sections[s]._y;
322 len = tb->sections[s].end - tb->sections[s].begin;
323 text = &(tb->text[tb->sections[s].begin]);
324 y = tb->sections[s].y - tPtr->vpos;
325 WMDrawString(scr, tPtr->db, gc, font,
326 tb->sections[s].x, y, text, len);
328 if(tb->underlined) {
329 XDrawLine(dpy, tPtr->db, gc,
330 tb->sections[s].x, y + font->y + 1,
331 tb->sections[s].x + tb->sections[s].w, y + font->y + 1);
336 tb = (!done? tb->next : NULL);
340 c = WMGetBagItemCount(tPtr->gfxItems);
341 if(c > 0 && !tPtr->flags.monoFont) {
342 int j;
343 WMWidget *wdt;
344 for(j=0; j<c; j++) {
345 tb = (TextBlock *)WMGetFromBag(tPtr->gfxItems, j);
346 if(!tb || !tb->sections)
347 continue;
348 wdt = tb->d.widget;
349 if(tb->sections[0]._y + tb->sections[0].h
350 <= tPtr->vpos
351 || tb->sections[0]._y
352 >= tPtr->vpos + tPtr->visible.h ) {
354 if((W_VIEW(wdt))->flags.mapped) {
355 WMUnmapWidget(wdt);
357 } else {
358 if(!(W_VIEW(wdt))->flags.mapped) {
359 WMMapWidget(wdt);
360 XLowerWindow(dpy,
361 (W_VIEW(wdt))->window);
364 if(tPtr->flags.ownsSelection && 0
365 //&& (tb->sections[s]._y >= tPtr->sel.y)
366 //&& (tb->sections[s]._y + tb->sections[s].h
367 ){ // <= tPtr->sel.y + tPtr->sel.h)) {
368 XFillRectangle(dpy, tPtr->db, greyGC,
369 tb->sections[0].x, tb->sections[0].y - tPtr->vpos,
370 tb->sections[0].w, tb->sections[0].h);
373 WMMoveWidget(wdt, 3 + tb->sections[0].x + tPtr->visible.x,
374 tb->sections[0].y - tPtr->vpos);
376 if(tb->underlined) {
377 XDrawLine(dpy, tPtr->db, WMColorGC(tb->color),
378 tb->sections[0].x,
379 tb->sections[0].y + WMWidgetHeight(wdt) + 1,
380 tb->sections[0].x + tb->sections[0].w,
381 tb->sections[0].y + WMWidgetHeight(wdt) + 1);
382 } } } }
385 _copy_area:
389 XCopyArea(dpy, tPtr->db, win, tPtr->bgGC,
390 0, 0,
391 tPtr->visible.w, tPtr->visible.h,
392 tPtr->visible.x, tPtr->visible.y);
394 W_DrawRelief(scr, win, 0, 0,
395 tPtr->view->size.width, tPtr->view->size.height,
396 tPtr->flags.relief);
398 if(tPtr->ruler && tPtr->flags.rulerShown)
399 XDrawLine(dpy, win,
400 tPtr->fgGC, 2, 42,
401 tPtr->view->size.width-4, 42);
404 XFillRectangle(tPtr->view->screen->display, tPtr->view->window,
405 tPtr->bgGC,
406 2, tPtr->view->size.height-3,
407 tPtr->view->size.width-4, 3);
413 static void
414 cursorToTextPosition(Text *tPtr, int x, int y)
416 TextBlock *tb = NULL;
417 int done=False, s, len, _w, _y, dir=1; /* 1 == "down" */
418 WMFont *font;
419 char *text;
422 y += tPtr->vpos - tPtr->visible.y;
423 if(y<0) y = 0;
425 x -= tPtr->visible.x-2;
426 if(x<0) x=0;
428 tPtr->clicked.x = x;
429 tPtr->clicked.y = y;
431 /* first, which direction?, most likely, newly clicked
432 position will be close to previous */
433 tb = tPtr->currentTextBlock;
434 if(!tb)
435 tb = tPtr->firstTextBlock;
436 if(!tb || !tb->sections)
437 return;
439 if(y < tb->sections[0].y)
440 dir = 0; /* "up" */
442 //tb = tPtr->firstTextBlock;
443 //dir = 1;
446 if(y == tb->sections[0].y)
447 goto _doneV; /* yeah yeah, goto, whatever... :-P */
449 /* get the first section of the first TextBlock based on v. position */
450 done = False;
451 while(!done && tb) {
452 if(tPtr->flags.monoFont && tb->graphic) {
453 tb = tb->next;
454 continue;
457 printf("tb %p t[%c] blank%d graphic %d\n", tb,
458 *tb->text, tb->blank, tb->graphic);
459 if(!tb->sections) {
460 printf("we have a bad thing!\n");
461 exit(1);
463 s = (dir? 0 : tb->nsections-1);
464 while( (dir? (s<tb->nsections) : (s>=0) )) {
465 if( y >= tb->sections[s]._y
466 && y <= tb->sections[s]._y + tb->sections[s].h) {
467 done = True;
468 break;
469 } else
470 dir? s++ : s--;
472 if(!done) tb = (dir ? tb->next : tb->prior);
475 _doneV:
476 /* we have the line, which TextBlock on that line is it? */
477 if(tb)
478 _y = tb->sections[s]._y;
480 while(tb) {
481 if(!tb->sections)
482 break;
483 if(_y != tb->sections[s]._y)
484 break;
486 if(tb->graphic) {
487 _w = WMWidgetWidth(tb->d.widget);
488 } else {
489 text = &(tb->text[tb->sections[s].begin]);
490 len = tb->sections[s].end - tb->sections[s].begin;
491 font = tb->d.font;
492 _w = myWidthOfString(font, text, len);
494 if(tb->sections[s].x + _w >= x)
495 break;
496 s = 0;
497 tb = tb->next;
500 /* we have said TextBlock, now where within it? */
501 if(tb && !tb->graphic) {
502 int begin = tb->sections[s].begin;
503 int end = tb->sections[s].end;
504 int i=0;
505 len = end-begin;
506 text = &(tb->text[begin]);
507 font = tb->d.font;
509 _w = x - tb->sections[s].x;
511 i = 0;
512 while(i<len && myWidthOfString(font, text, i+1) < _w)
513 i++;
515 i += begin;
516 tPtr->tpos = (i<tb->used)?i:tb->used;
519 // if(!tb)
520 //tb = tPtr->firstTextBlock;
521 tPtr->currentTextBlock = tb;
523 if(tb &&tb->graphic) printf("graphic\n");
526 static void
527 updateScrollers(Text *tPtr)
530 if(tPtr->vS) {
531 if(tPtr->docHeight < tPtr->visible.h) {
532 WMSetScrollerParameters(tPtr->vS, 0, 1);
533 tPtr->vpos = 0;
534 } else {
535 float vmax = (float)(tPtr->docHeight);
536 WMSetScrollerParameters(tPtr->vS,
537 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
538 (float)tPtr->visible.h/vmax);
540 } else tPtr->vpos = 0;
542 if(tPtr->hS)
546 static void
547 scrollersCallBack(WMWidget *w, void *self)
549 Text *tPtr = (Text *)self;
550 Bool scroll = False;
551 Bool dimple = False;
552 int which;
554 if(!tPtr->view->flags.realized) return;
556 if(w == tPtr->vS) {
557 float vmax;
558 int height;
559 vmax = (float)(tPtr->docHeight);
560 height = tPtr->visible.h;
562 which = WMGetScrollerHitPart(tPtr->vS);
563 switch(which) {
564 case WSDecrementLine:
565 if(tPtr->vpos > 0) {
566 if(tPtr->vpos>16) tPtr->vpos-=16;
567 else tPtr->vpos=0;
568 scroll=True;
569 }break;
570 case WSIncrementLine: {
571 int limit = tPtr->docHeight - height;
572 if(tPtr->vpos < limit) {
573 if(tPtr->vpos<limit-16) tPtr->vpos+=16;
574 else tPtr->vpos=limit;
575 scroll = True;
576 }}break;
577 case WSDecrementPage:
578 tPtr->vpos -= height;
580 if(tPtr->vpos < 0)
581 tPtr->vpos = 0;
582 dimple = True;
583 scroll = True;
584 printf("dimple needs to jump to mouse location ;-/\n");
585 break;
586 case WSIncrementPage:
587 tPtr->vpos += height;
588 if(tPtr->vpos > (tPtr->docHeight - height))
589 tPtr->vpos = tPtr->docHeight - height;
590 dimple = True;
591 scroll = True;
592 printf("dimple needs to jump to mouse location ;-/\n");
593 break;
596 case WSKnob:
597 tPtr->vpos = WMGetScrollerValue(tPtr->vS)
598 * (float)(tPtr->docHeight - height);
599 scroll = True;
600 break;
602 case WSKnobSlot:
603 case WSNoPart:
604 printf("WSNoPart, WSKnobSlot\n");
605 #if 0
606 float vmax = (float)(tPtr->docHeight);
607 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
608 (float)tPtr->visible.h/vmax;
609 dimple =where mouse is.
610 #endif
611 break;
613 scroll = (tPtr->vpos != tPtr->prevVpos);
614 tPtr->prevVpos = tPtr->vpos;
617 if(w == tPtr->hS)
620 if(scroll) {
622 if(0&&dimple) {
623 if(tPtr->rulerShown)
624 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
625 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
626 else
627 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
628 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
631 if(which == WSDecrementLine || which == WSIncrementLine)
632 updateScrollers(tPtr);
633 paintText(tPtr);
639 typedef struct {
640 TextBlock *tb;
641 unsigned short begin, end; /* what part of the text block */
643 } myLineItems;
646 static int
647 layOutLine(Text *tPtr, myLineItems *items, int nitems, int x, int y,
648 int pwidth, WMAlignment align)
650 int i, j=0; /* j = justification */
651 int line_width = 0, line_height=0, max_descent=0;
652 WMFont *font;
653 char *text;
654 int len;
655 TextBlock *tb;
656 Bool gfx=0;
657 TextBlock *tbsame=NULL;
659 for(i=0; i<nitems; i++) {
660 tb = items[i].tb;
662 if(tb->graphic) {
663 if(!tPtr->flags.monoFont) {
664 WMWidget *wdt = tb->d.widget;
665 line_height = WMAX(line_height, WMWidgetHeight(wdt));
666 if(align != WALeft)
667 line_width += WMWidgetWidth(wdt);
668 gfx = True;
671 } else {
672 font = (tPtr->flags.monoFont)?tPtr->dFont : tb->d.font;
673 max_descent = WMAX(max_descent, font->height-font->y);
674 line_height = WMAX(line_height, font->height); //+font->height-font->y);
675 text = &(tb->text[items[i].begin]);
676 len = items[i].end - items[i].begin;
677 if(align != WALeft)
678 line_width += myWidthOfString(font, text, len);
682 if(align == WARight) {
683 j = pwidth - line_width;
684 } else if (align == WACenter) {
685 j = (int) ((float)(pwidth - line_width))/2.0;
687 if(gfx)
688 y+=10;
690 for(i=0; i<nitems; i++) {
691 tb = items[i].tb;
693 if(tbsame == tb) { /*extend it, since it's on same line */
694 tb->sections[tb->nsections-1].end = items[i].end;
695 } else {
696 tb->sections = wrealloc(tb->sections,
697 (++tb->nsections)*sizeof(Section));
698 tb->sections[tb->nsections-1]._y = y;
699 tb->sections[tb->nsections-1].x = x+j;
700 tb->sections[tb->nsections-1].h = line_height;
701 tb->sections[tb->nsections-1].begin = items[i].begin;
702 tb->sections[tb->nsections-1].end = items[i].end;
706 if(tb->graphic) {
707 if(!tPtr->flags.monoFont) {
708 WMWidget *wdt = tb->d.widget;
709 tb->sections[tb->nsections-1].y = 1 +max_descent +
710 y + line_height - WMWidgetHeight(wdt);
711 tb->sections[tb->nsections-1].w = WMWidgetWidth(wdt);
712 x += tb->sections[tb->nsections-1].w;
714 } else {
715 font = (tPtr->flags.monoFont)? tPtr->dFont : tb->d.font;
716 len = items[i].end - items[i].begin;
718 text = &(tb->text[items[i].begin]);
720 tb->sections[tb->nsections-1].y = y+line_height-font->y;
721 tb->sections[tb->nsections-1].w =
722 myWidthOfString(font,
723 &(tb->text[tb->sections[tb->nsections-1].begin]),
724 tb->sections[tb->nsections-1].end -
725 tb->sections[tb->nsections-1].begin);
727 x += myWidthOfString(font, text, len);
730 tbsame = tb;
733 return line_height+(gfx?10:0);
738 static void
739 output(char *ptr, int len)
741 char s[len+1];
742 memcpy(s, ptr, len);
743 s[len] = 0;
744 printf(" s is [%s] (%d)\n", s, strlen(s));
749 #define MAX_TB_PER_LINE 64
751 static void
752 layOutDocument(Text *tPtr)
754 TextBlock *tb;
755 myLineItems items[MAX_TB_PER_LINE];
756 WMAlignment align = WALeft;
757 WMFont *font;
758 Bool lhc = !tPtr->flags.laidOut; /* line height changed? */
759 int prev_y;
761 int nitems=0, x=0, y=0, line_width = 0, width=0;
762 int pwidth = tPtr->visible.w - tPtr->visible.x;
764 char *start=NULL, *mark=NULL;
765 int begin, end;
767 if(!(tb = tPtr->firstTextBlock)) {
768 printf("clear view... *pos=0\n");
769 return;
772 if(0&&tPtr->flags.laidOut) {
773 tb = tPtr->currentTextBlock;
774 if(tb->sections && tb->nsections>0)
775 prev_y = tb->sections[tb->nsections-1]._y;
776 y+=10;
777 printf("1 prev_y %d \n", prev_y);
779 /* search backwards for textblocks on same line */
780 while(tb) {
781 if(!tb->sections || tb->nsections<1) {
782 tb = tPtr->firstTextBlock;
783 break;
785 if(tb->sections[tb->nsections-1]._y != prev_y) {
786 tb = tb->next;
787 break;
789 // prev_y = tb->sections[tb->nsections-1]._y;
790 tb = tb->prior;
792 y = 0;//tb->sections[tb->nsections-1]._y;
793 printf("2 prev_y %d \n\n", tb->sections[tb->nsections-1]._y);
797 while(tb) {
799 if(tb->sections && tb->nsections>0) {
800 wfree(tb->sections);
801 tb->sections = NULL;
802 tb->nsections = 0;
805 if(tb->first) {
806 y += layOutLine(tPtr, items, nitems, x, y, pwidth, align);
807 x = 0;//tPtr->visible.x+2;
808 nitems = 0;
809 line_width = 0;
812 if(tb->graphic) {
813 if(!tPtr->flags.monoFont) {
814 width = WMWidgetWidth(tb->d.widget);
815 if(width > pwidth)printf("rescale graphix to fit?\n");
816 line_width += width;
817 if(line_width >= pwidth - x
818 || nitems >= MAX_TB_PER_LINE) {
819 y += layOutLine(tPtr, items, nitems, x, y,
820 pwidth, align);
821 nitems = 0;
822 x = 0;//tPtr->visible.x+2;
823 line_width = width;
826 items[nitems].tb = tb;
827 items[nitems].begin = 0;
828 items[nitems].end = 0;
829 nitems++;
832 } else if((start = tb->text)) {
833 begin = end = 0;
834 font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
836 while(start) {
837 mark = strchr(start, ' ');
838 if(mark) {
839 end += (int)(mark-start)+1;
840 start = mark+1;
841 } else {
842 end += strlen(start);
843 start = mark;
846 if(end-begin > 0) {
848 width = myWidthOfString(font,
849 &tb->text[begin], end-begin);
851 if(width > pwidth) { /* break this tb up */
852 char *t = &tb->text[begin];
853 int l=end-begin, i=0;
854 do {
855 width = myWidthOfString(font, t, ++i);
856 } while (width < pwidth && i < l);
857 end = begin+i;
858 if(start) // and since (nil)-4 = 0xfffffffd
859 start -= l-i;
863 line_width += width;
866 if((line_width >= pwidth - x)
867 || nitems >= MAX_TB_PER_LINE) {
868 y += layOutLine(tPtr, items, nitems, x, y,
869 pwidth, align);
870 line_width = width;
871 x = 0; //tPtr->visible.x+2;
872 nitems = 0;
875 items[nitems].tb = tb;
876 items[nitems].begin = begin;
877 items[nitems].end = end;
878 nitems++;
880 begin = end;
883 tb = tb->next;
887 if(nitems > 0)
888 y += layOutLine(tPtr, items, nitems, x, y, pwidth, align);
889 if(lhc) {
890 tPtr->docHeight = y+10;
891 updateScrollers(tPtr);
893 tPtr->flags.laidOut = True;
898 static void
899 textDidResize(W_ViewDelegate *self, WMView *view)
901 Text *tPtr = (Text *)view->self;
902 unsigned short w = WMWidgetWidth(tPtr);
903 unsigned short h = WMWidgetHeight(tPtr);
904 unsigned short rh = 0, vw = 0;
906 if(tPtr->ruler && tPtr->flags.rulerShown) {
907 WMMoveWidget(tPtr->ruler, 20, 2);
908 WMResizeWidget(tPtr->ruler, w - 22, 40);
909 rh = 40;
912 if(tPtr->vS) {
913 WMMoveWidget(tPtr->vS, 1, rh + 2);
914 WMResizeWidget(tPtr->vS, 20, h - rh - 3);
915 vw = 20;
916 WMSetRulerOffset(tPtr->ruler, 22);
917 } else WMSetRulerOffset(tPtr->ruler, 2);
919 if(tPtr->hS) {
920 if(tPtr->vS) {
921 WMMoveWidget(tPtr->hS, vw, h - 21);
922 WMResizeWidget(tPtr->hS, w - vw - 1, 20);
923 } else {
924 WMMoveWidget(tPtr->hS, vw+1, h - 21);
925 WMResizeWidget(tPtr->hS, w - vw - 2, 20);
929 tPtr->visible.x = (tPtr->vS)?22:0;
930 tPtr->visible.y = (tPtr->ruler && tPtr->flags.rulerShown)?43:3;
931 tPtr->visible.w = tPtr->view->size.width - tPtr->visible.x - 12;
932 tPtr->visible.h = tPtr->view->size.height - tPtr->visible.y;
933 tPtr->visible.h -= (tPtr->hS)?20:0;
935 tPtr->margins[0].left = tPtr->margins[0].right = tPtr->visible.x;
936 tPtr->margins[0].body = tPtr->visible.x;
937 tPtr->margins[0].right = tPtr->visible.w;
939 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
941 if(tPtr->db) {
942 //if(tPtr->view->flags.realized)
943 //XFreePixmap(tPtr->view->screen->display, tPtr->db);
946 //if(size did not change
947 if(tPtr->visible.w < 10) tPtr->visible.w = 10;
948 if(tPtr->visible.h < 10) tPtr->visible.h = 10;
950 tPtr->db = XCreatePixmap(tPtr->view->screen->display,
951 tPtr->view->window, tPtr->visible.w,
952 tPtr->visible.h, tPtr->view->screen->depth);
954 paintText(tPtr);
957 W_ViewDelegate _TextViewDelegate =
959 NULL,
960 NULL,
961 textDidResize,
962 NULL,
965 /* nice, divisble-by-16 memory */
966 static inline unsigned short
967 reqBlockSize(unsigned short requested)
969 return requested+16-(requested%16);
972 static void
973 deleteTextInteractively(Text *tPtr, KeySym ksym)
975 printf("deleting %ld\n", ksym);
978 static void
979 insertTextInteractively(Text *tPtr, char *text, int len)
981 TextBlock *tb;
983 // Chunk *tb=NULL, *newtb=NULL;
984 int height = -23; /* should only be changed upon newline */
985 int w=0;
986 WMFont *font;
987 char *mark = NULL;
989 if(!tPtr->flags.editable || len < 1 || !text
990 || (*text == '\n' && tPtr->flags.ignoreNewLine))
991 return;
993 tb = tPtr->currentTextBlock;
994 if(!tb) {
995 WMAppendTextStream(tPtr, text);
996 WMRefreshText(tPtr, 0, 0);
997 return;
1000 if(tb->graphic)
1001 return;
1003 if(len > 1) {
1004 mark = strchr(text, '\n');
1005 if(mark) {
1006 len = (int)(mark-text);
1007 mark++;
1009 if(len<1 && mark) {
1010 printf("problem pasting text %d\n", len);
1011 len = strlen(text);
1012 mark = NULL;
1016 font = (tPtr->flags.monoFont || !tb)?tPtr->dFont:tb->d.font;
1018 #if 0
1019 if(*text == '\n') {
1020 int new_top=0;
1021 if(tb) { /* there's a tb (or part of it) to detach from old */
1022 int current = WMGetTextCurrentChunk(tPtr);
1023 if(tPtr->tpos <=0) { /* at start of tb */
1024 if(current<1) { /* the first tb... make old para blank */
1025 newtb = para->tbs;
1026 para->tbs = NULL;
1027 putParagraphOnPixmap(tPtr, para, True);
1028 } else { /* not first tb... */
1029 printf("cut me out \n");
1031 } else if(tPtr->tpos < tb->chars && tb->type == ctText) {
1032 /* not at start of tb */
1033 char text[tb->chars-tPtr->tpos+1];
1034 int i=0;
1035 do {
1036 text[i] = tb->text[tPtr->tpos+i];
1037 } while(++i < tb->chars-tPtr->tpos);
1038 tb->chars -= i;
1039 newtb = (tPtr->funcs.createTChunk) (text, i, tb->font,
1040 tb->color, tb->script, tb->ul);
1041 newtb->next = tb->next;
1042 tb->next = NULL;
1043 /* might want to demalloc for LARGE cuts */
1044 //calcParaExtents(tPtr, para);
1045 para->height = putParagraphOnPixmap(tPtr, para, True);
1046 //putParagraphOnPixmap(tPtr, para, True);
1047 } else if(tPtr->tpos >= tb->chars) {
1048 Chunk *prev;
1049 WMSetTextCurrentChunk(tPtr, current-1);
1050 prev = tPtr->currentChunk;
1051 if(!prev) return;
1052 newtb = prev->next;
1053 prev->next = NULL;
1054 putParagraphOnPixmap(tPtr, para, True);
1056 } else newtb = NULL;
1058 if(para) /* the preceeding one */
1059 new_top = para->bottom;
1061 WMAppendTextStream(tPtr, "\n");
1062 para = tPtr->currentPara;
1063 if(!para) return;
1064 para->tbs = newtb;
1065 tPtr->currentChunk = newtb;
1066 tPtr->tpos = 0;
1067 para->top = new_top;
1068 calcParaExtents(tPtr, para);
1069 height = para->height;
1070 } else {
1071 if(!para) {
1072 WMAppendTextStream(tPtr, text);
1073 para = tPtr->currentPara;
1074 } else if(!para->tbs || !tb) {
1075 //WMPrependTextStream(tPtr, text);
1076 WMAppendTextStream(tPtr, text);
1077 } else if(tb->type == ctImage) {
1078 WMPrependTextStream(tPtr, text);
1080 printf("\n\nprepe\n\n");
1081 } else {
1082 if(tPtr->tpos > tb->chars) {
1083 printf("\n\nmore\n\n");
1084 tPtr->tpos = tb->chars;
1086 #endif
1088 printf("len is %d\n", len);
1089 if(tb->used+len >= tb->allocated) {
1090 tb->allocated = reqBlockSize(tb->used+len);
1091 printf("ralloced %d\n", tb->allocated);
1092 tb->text = wrealloc(tb->text, tb->allocated);
1095 if(tb->blank) {
1096 memmove(tb->text, text, len);
1097 tb->used = len;
1098 tPtr->tpos = len;
1099 tb->blank = False;
1100 } else {
1101 memmove(&(tb->text[tPtr->tpos+len]), &tb->text[tPtr->tpos],
1102 tb->used-tPtr->tpos+1);
1103 memmove(&tb->text[tPtr->tpos], text, len);
1104 tb->used += len;
1105 tPtr->tpos += len;
1107 w = myWidthOfString(font, text, len);
1110 if(mark) {
1111 WMAppendTextStream(tPtr, mark);
1112 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1113 printf("paste: use prev/post chunk's fmt...\n");
1114 } else {
1115 layOutDocument(tPtr);
1116 paintText(tPtr);
1118 #if 0
1119 //doc->clickstart.cursor.x +=
1120 //myWidthOfString(tb->fmt->font, text,len);
1123 #endif
1127 static void
1128 selectRegion(Text *tPtr, int x, int y)
1130 if(x < 0 || y < 0)
1131 return;
1132 y += tPtr->vpos;
1133 if(y>10) y -= 10; /* the original offset */
1135 x -= tPtr->visible.x-2;
1136 if(x<0) x=0;
1138 tPtr->sel.x = WMAX(0, WMIN(tPtr->clicked.x, x));
1139 tPtr->sel.w = abs(tPtr->clicked.x - x);
1140 tPtr->sel.y = WMAX(0, WMIN(tPtr->clicked.y, y));
1141 tPtr->sel.h = abs(tPtr->clicked.y - y);
1143 tPtr->flags.ownsSelection = True;
1144 paintText(tPtr);
1148 static void
1149 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1150 void *cdata, WMData *data)
1152 Text *tPtr = (Text *)view->self;
1153 char *str;
1155 tPtr->flags.waitingForSelection = False;
1157 if(data) {
1158 str = (char*)WMDataBytes(data);
1159 insertTextInteractively(tPtr, str, strlen(str));
1160 } else {
1161 int n;
1162 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1163 if(str) {
1164 str[n] = 0;
1165 insertTextInteractively(tPtr, str, n);
1166 XFree(str);
1171 static void
1172 releaseSelection(Text *tPtr)
1174 printf("I have %d selection\n", 1);
1175 tPtr->flags.ownsSelection = False;
1176 paintText(tPtr);
1179 static WMData *
1180 requestHandler(WMView *view, Atom selection, Atom target,
1181 void *cdata, Atom *type)
1183 Text *tPtr = view->self;
1184 int count;
1185 Display *dpy = tPtr->view->screen->display;
1186 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1187 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1189 *type = target;
1190 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT)
1191 return WMGetTextSelected(tPtr);
1192 else {
1193 WMData *data = WMCreateDataWithBytes("bleh", 4);
1194 return data;
1197 return NULL;
1201 static void
1202 lostHandler(WMView *view, Atom selection, void *cdata)
1204 releaseSelection((WMText *)view->self);
1207 static WMSelectionProcs selectionHandler = {
1208 requestHandler, lostHandler, NULL
1211 static void
1212 _notification(void *observerData, WMNotification *notification)
1214 WMText *to = (WMText *)observerData;
1215 WMText *tw = (WMText *)WMGetNotificationClientData(notification);
1216 if (to != tw)
1217 lostHandler(to->view, XA_PRIMARY, NULL);
1220 static void
1221 handleTextKeyPress(Text *tPtr, XEvent *event)
1223 char buffer[2];
1224 KeySym ksym;
1225 int control_pressed = False;
1226 // int h=1;
1228 if (((XKeyEvent *) event)->state & ControlMask)
1229 control_pressed = True;
1230 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = '\0';
1232 switch(ksym) {
1233 case XK_Right:
1234 case XK_Left:
1235 case XK_Down:
1236 case XK_Up:
1237 printf("arrows %ld\n", ksym);
1238 break;
1240 case XK_BackSpace:
1241 case XK_Delete:
1242 case XK_KP_Delete:
1243 deleteTextInteractively(tPtr, ksym);
1244 break;
1247 case XK_Return:
1248 buffer[0] = '\n';
1249 default:
1250 if(buffer[0] != '\0' && (buffer[0] == '\n' || !control_pressed))
1251 insertTextInteractively(tPtr, buffer, 1);
1252 else if(control_pressed && ksym==XK_r) {
1253 // Bool i = !tPtr->rulerShown; WMShowTextRuler(tPtr, i);
1254 // tPtr->rulerShown = i;
1255 printf("toggle ruler\n");
1257 else if(control_pressed && buffer[0] == '\a')
1258 XBell(tPtr->view->screen->display, 0);
1261 if(tPtr->flags.ownsSelection)
1262 releaseSelection(tPtr);
1266 static void
1267 handleWidgetPress(XEvent *event, void *data)
1269 TextBlock *tb = (TextBlock *)data;
1270 Text *tPtr;
1271 WMWidget *w;
1272 if(!tb)
1273 return;
1274 #if 0
1275 /* this little bit of nastiness here saves a boatload of trouble */
1276 w = (WMWidget *)(W_VIEW((W_VIEW(tb->d.widget))->parent))->self;
1277 //if(W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text)
1278 if( (((W_WidgetType*)(w))->widgetClass) != WC_Text)
1279 return;
1280 *tPtr = (Text*)w;
1281 printf("%p clicked on tb %p wif: (%c)%c", tPtr, tb,
1282 tPtr->firstTextBlock->text[0], tPtr->firstTextBlock->text[1]);
1283 output(tb->text, tb->used);
1284 #endif
1287 static void
1288 handleActionEvents(XEvent *event, void *data)
1290 Text *tPtr = (Text *)data;
1291 Display *dpy = event->xany.display;
1292 KeySym ksym;
1295 if(tPtr->flags.waitingForSelection)
1296 return;
1298 switch (event->type) {
1299 case KeyPress:
1300 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1301 if(ksym == XK_Shift_R || ksym == XK_Shift_L) {
1302 tPtr->flags.extendSelection = True;
1303 return;
1305 if(!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1306 XBell(dpy, 0);
1307 return;
1310 if (tPtr->flags.waitingForSelection)
1311 return;
1312 if(tPtr->flags.focused) {
1313 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1314 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1315 GrabModeAsync, GrabModeAsync, None,
1316 W_VIEW(tPtr)->screen->invisibleCursor, CurrentTime);
1317 tPtr->flags.pointerGrabbed = True;
1318 handleTextKeyPress(tPtr, event);
1320 } break;
1322 case KeyRelease:
1323 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1324 if(ksym == XK_Shift_R || ksym == XK_Shift_L) {
1325 tPtr->flags.extendSelection = False;
1326 return;
1327 //end modify flag so selection can be extended
1329 break;
1332 case MotionNotify:
1333 if(tPtr->flags.pointerGrabbed) {
1334 tPtr->flags.pointerGrabbed = False;
1335 XUngrabPointer(dpy, CurrentTime);
1337 if((event->xmotion.state & Button1Mask)) {
1338 if(!tPtr->flags.ownsSelection) {
1339 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1340 event->xbutton.time, &selectionHandler, NULL);
1341 tPtr->flags.ownsSelection = True;
1343 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1345 break;
1348 case ButtonPress:
1349 tPtr->flags.buttonHeld = True;
1350 if(tPtr->flags.extendSelection) {
1351 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1352 return;
1354 if(event->xbutton.button == Button1) {
1355 if(tPtr->flags.ownsSelection)
1356 releaseSelection(tPtr);
1357 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1358 if (tPtr->flags.pointerGrabbed) {
1359 tPtr->flags.pointerGrabbed = False;
1360 XUngrabPointer(dpy, CurrentTime);
1361 break;
1364 if(!tPtr->flags.focused) {
1365 WMSetFocusToWidget(tPtr);
1366 tPtr->flags.focused = True;
1367 break;
1370 if(event->xbutton.button == WINGsConfiguration.mouseWheelDown)
1371 WMScrollText(tPtr, -16);
1372 else if(event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1373 WMScrollText(tPtr, 16);
1374 break;
1376 case ButtonRelease:
1377 tPtr->flags.buttonHeld = False;
1378 if (tPtr->flags.pointerGrabbed) {
1379 tPtr->flags.pointerGrabbed = False;
1380 XUngrabPointer(dpy, CurrentTime);
1381 break;
1383 if(event->xbutton.button == WINGsConfiguration.mouseWheelDown
1384 || event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1385 break;
1387 if(event->xbutton.button == Button2 && tPtr->flags.editable) {
1388 char *text = NULL;
1389 int n;
1390 if(!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1391 event->xbutton.time, pasteText, NULL)) {
1392 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1393 if(text) {
1394 text[n] = 0;
1395 insertTextInteractively(tPtr, text, n-1);
1396 XFree(text);
1397 } else tPtr->flags.waitingForSelection = True;
1400 break;
1407 static void
1408 handleEvents(XEvent *event, void *data)
1410 Text *tPtr = (Text *)data;
1412 switch(event->type) {
1413 case Expose:
1414 if(!event->xexpose.count && tPtr->view->flags.realized)
1415 paintText(tPtr);
1416 break;
1418 case FocusIn:
1419 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
1420 return;
1421 tPtr->flags.focused = True;
1422 break;
1424 case FocusOut:
1425 tPtr->flags.focused = False;
1426 break;
1428 case DestroyNotify:
1429 printf("destroy");
1430 //for(...)WMRemoveTextParagraph(tPtr, para);
1431 break;
1438 static void
1439 clearText(Text *tPtr)
1441 void *tb;
1442 if(!tPtr->firstTextBlock)
1443 return;
1445 while(tPtr->currentTextBlock)
1446 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1448 printf("yadda clearText\n");
1450 printf("remove the document\n");
1451 tPtr->firstTextBlock = NULL;
1452 tPtr->currentTextBlock = NULL;
1453 tPtr->lastTextBlock = NULL;
1454 //WMThawText(tPtr);
1455 WMRefreshText(tPtr, 0, 0);
1459 static void
1460 insertPlainText(WMText *tPtr, char *text)
1462 char *start, *mark;
1463 void *tb = NULL;
1466 if(!text) {
1467 clearText(tPtr);
1468 return;
1472 start = text;
1473 while(start) {
1474 mark = strchr(start, '\n');
1475 if(mark) {
1476 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1477 tPtr->dColor, True, (int)(mark-start));
1478 start = mark+1;
1479 } else {
1480 if(start && strlen(start)) {
1481 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1482 tPtr->dColor, False, strlen(start));
1483 } else tb = NULL;
1484 start = mark;
1487 if(tPtr->flags.prepend)
1488 WMPrependTextBlock(tPtr, tb);
1489 else
1490 WMAppendTextBlock(tPtr, tb);
1492 return;
1501 WMText *
1502 WMCreateText(WMWidget *parent)
1504 Text *tPtr = wmalloc(sizeof(Text));
1505 if(!tPtr) {
1506 printf("could not create text widget\n");
1507 return NULL;
1510 #if 0
1511 printf("sizeof:\n");
1512 printf(" TextBlock %d\n", sizeof(TextBlock));
1513 printf(" TextBlock *%d\n", sizeof(TextBlock *));
1514 printf(" Section %d\n", sizeof(Section));
1515 printf(" char * %d\n", sizeof(char *));
1516 printf(" void * %d\n", sizeof(void *));
1517 printf(" short %d\n", sizeof(short));
1518 printf(" Text %d\n", sizeof(Text));
1519 #endif
1521 memset(tPtr, 0, sizeof(Text));
1522 tPtr->widgetClass = WC_Text;
1523 tPtr->view = W_CreateView(W_VIEW(parent));
1524 if (!tPtr->view) {
1525 perror("could not create text's view\n");
1526 free(tPtr);
1527 return NULL;
1529 tPtr->view->self = tPtr;
1530 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
1531 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
1532 W_ResizeView(tPtr->view, 250, 200);
1533 tPtr->bgGC = WMColorGC(tPtr->view->screen->white);
1534 tPtr->fgGC = WMColorGC(tPtr->view->screen->black);
1535 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
1537 tPtr->ruler = NULL;
1538 tPtr->vS = NULL;
1539 tPtr->hS = NULL;
1541 tPtr->dFont = NULL;
1542 //tPtr->dFont = WMCreateFont(tPtr->view->screen,
1543 // "-*-fixed-medium-r-normal--26-*-*-*-*-*-*-*");
1544 //"-sony-fixed-medium-r-normal--24-230-75-75-c-120-jisx0201.1976-0");
1545 // "-*-times-bold-r-*-*-12-*-*-*-*-*-*-*,"
1546 // "-*-fixed-medium-r-normal-*-12-*");
1547 if (!tPtr->dFont)
1548 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
1550 tPtr->dColor = WMBlackColor(tPtr->view->screen);
1552 tPtr->view->delegate = &_TextViewDelegate;
1554 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
1555 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
1556 handleEvents, tPtr);
1558 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
1559 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
1560 handleActionEvents, tPtr);
1562 WMAddNotificationObserver(_notification, tPtr, "_lostOwnership", tPtr);
1565 tPtr->firstTextBlock = NULL;
1566 tPtr->lastTextBlock = NULL;
1567 tPtr->currentTextBlock = NULL;
1568 tPtr->tpos = 0;
1570 tPtr->gfxItems = WMCreateArrayBag(4);
1572 tPtr->parser = NULL;
1573 tPtr->writer = NULL;
1575 tPtr->sel.x = tPtr->sel.y = 2;
1576 tPtr->sel.w = tPtr->sel.h = 0;
1578 tPtr->clicked.x = tPtr->clicked.y = 2;
1580 tPtr->visible.x = tPtr->visible.y = 2;
1581 tPtr->visible.h = tPtr->view->size.height;
1582 tPtr->visible.w = tPtr->view->size.width - 12;
1584 tPtr->docWidth = 0;
1585 tPtr->docHeight = 0;
1586 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
1587 default_bullet);
1588 tPtr->db = (Pixmap) NULL;
1590 tPtr->margins = wmalloc(sizeof(WMRulerMargins));
1591 tPtr->margins[0].left = tPtr->margins[0].right = tPtr->visible.x;
1592 tPtr->margins[0].body = tPtr->visible.x;
1593 tPtr->margins[0].right = tPtr->visible.w;
1595 tPtr->flags.nmargins = 1;
1596 tPtr->flags.rulerShown = False;
1597 tPtr->flags.monoFont = !True;
1598 tPtr->flags.focused = False;
1599 tPtr->flags.editable = True;
1600 tPtr->flags.ownsSelection = False;
1601 tPtr->flags.pointerGrabbed = False;
1602 tPtr->flags.buttonHeld = False;
1603 tPtr->flags.waitingForSelection = False;
1604 tPtr->flags.extendSelection = False;
1605 tPtr->flags.rulerShown = False;
1606 tPtr->flags.frozen = False;
1607 tPtr->flags.cursorShown = True;
1608 tPtr->flags.clickPos = 1;
1609 tPtr->flags.ignoreNewLine = False;
1610 tPtr->flags.laidOut = False;
1611 tPtr->flags.prepend = False;
1612 tPtr->flags.relief = WRFlat;
1613 tPtr->flags.alignment = WALeft;
1615 return tPtr;
1619 void
1620 WMPrependTextStream(WMText *tPtr, char *text)
1622 if(!tPtr)
1623 return;
1624 //check for "{\rtf0" in the text...
1625 //insertRTF
1626 //else
1627 tPtr->flags.prepend = True;
1628 if(text && tPtr->parser)
1629 (tPtr->parser) (tPtr, (void *) text);
1630 else
1631 insertPlainText(tPtr, text);
1635 void
1636 WMAppendTextStream(WMText *tPtr, char *text)
1638 if(!tPtr)
1639 return;
1640 //check for "{\rtf0" in the text...
1641 //insertRTF
1642 //else
1643 tPtr->flags.prepend = False;
1644 if(text && tPtr->parser)
1645 (tPtr->parser) (tPtr, (void *) text);
1646 else
1647 insertPlainText(tPtr, text);
1651 WMData *
1652 WMGetTextSelected(WMText *tPtr)
1654 WMData *data = NULL;
1655 TextBlock *tb;
1657 if(!tPtr)
1658 return NULL;
1660 //tb = tPtr->firstTextBlock;
1661 tb = tPtr->currentTextBlock;
1662 if(!tb)
1663 return NULL;
1665 data = WMCreateDataWithBytes(tb->text, tb->used);
1666 if(data)
1667 WMSetDataFormat(data, 8);
1668 return data;
1671 void *
1672 WMCreateTextBlockWithObject(WMWidget *w, char *description, WMColor *color,
1673 unsigned short first, unsigned short reserved)
1675 TextBlock *tb;
1676 unsigned short length;
1678 if(!w || !description || !color)
1679 return NULL;
1681 tb = wmalloc(sizeof(TextBlock));
1682 if(!tb)
1683 return NULL;
1685 length = strlen(description);
1686 tb->text = (char *)wmalloc(length);
1687 memset(tb->text, 0, length);
1688 memcpy(tb->text, description, length);
1689 tb->used = length;
1690 tb->blank = False;
1691 tb->d.widget = w;
1692 tb->color = WMRetainColor(color);
1693 tb->marginN = 0;
1694 tb->allocated = 0;
1695 tb->first = first;
1696 tb->kanji = False;
1697 tb->graphic = True;
1698 tb->underlined = False;
1699 tb->script = 0;
1700 tb->sections = NULL;
1701 tb->nsections = 0;
1702 tb->prior = NULL;
1703 tb->next = NULL;
1705 return tb;
1708 void *
1709 WMCreateTextBlockWithText(char *text, WMFont *font, WMColor *color,
1710 unsigned short first, unsigned short length)
1712 TextBlock *tb;
1714 if(!font || !color)
1715 return NULL;
1717 tb = wmalloc(sizeof(TextBlock));
1718 if(!tb)
1719 return NULL;
1721 tb->allocated = reqBlockSize(length);
1722 tb->text = (char *)wmalloc(tb->allocated);
1723 memset(tb->text, 0, tb->allocated);
1725 if(length < 1|| !text ) { // || *text == '\n') {
1726 *tb->text = ' ';
1727 tb->used = 1;
1728 tb->blank = True;
1729 } else {
1730 memcpy(tb->text, text, length);
1731 tb->used = length;
1732 tb->blank = False;
1735 tb->d.font = WMRetainFont(font);
1736 tb->color = WMRetainColor(color);
1737 tb->marginN = 0;
1738 tb->first = first;
1739 tb->kanji = False;
1740 tb->graphic = False;
1741 tb->underlined = False;
1742 tb->script = 0;
1743 tb->sections = NULL;
1744 tb->nsections = 0;
1745 tb->prior = NULL;
1746 tb->next = NULL;
1747 return tb;
1750 void
1751 WMSetTextBlockProperties(void *vtb, unsigned int first,
1752 unsigned int kanji, unsigned int underlined, int script,
1753 unsigned int marginN)
1755 TextBlock *tb = (TextBlock *) vtb;
1756 if(!tb)
1757 return;
1759 tb->first = first;
1760 tb->kanji = kanji;
1761 tb->underlined = underlined;
1762 tb->script = script;
1763 tb->marginN = marginN;
1766 void
1767 WMGetTextBlockProperties(void *vtb, unsigned int *first,
1768 unsigned int *kanji, unsigned int *underlined, int *script,
1769 unsigned int *marginN)
1771 TextBlock *tb = (TextBlock *) vtb;
1772 if(!tb)
1773 return;
1775 if(first) *first = tb->first;
1776 if(kanji) *kanji = tb->kanji;
1777 if(underlined) *underlined = tb->underlined;
1778 if(script) *script = tb->script;
1779 if(marginN) *marginN = tb->marginN;
1784 void
1785 WMPrependTextBlock(WMText *tPtr, void *vtb)
1787 TextBlock *tb = (TextBlock *)vtb;
1790 if(!tPtr || !tb)
1791 return;
1793 if(tb->graphic) {
1794 WMWidget *w = tb->d.widget;
1795 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
1796 handleWidgetPress, tb);
1797 //if(W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
1798 if(W_CLASS(w) != WC_TextField &&
1799 (((W_WidgetType*)(w))->widgetClass) != WC_Text) {
1800 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
1801 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
1803 WMPutInBag(tPtr->gfxItems, (void *)tb);
1804 WMRealizeWidget(w);
1807 if(!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
1808 tb->next = tb->prior = NULL;
1809 tPtr->lastTextBlock = tPtr->firstTextBlock
1810 = tPtr->currentTextBlock = tb;
1811 return;
1814 tb->next = tPtr->currentTextBlock;
1815 tb->prior = tPtr->currentTextBlock->prior;
1816 if(tPtr->currentTextBlock->prior)
1817 tPtr->currentTextBlock->prior->next = tb;
1819 tPtr->currentTextBlock->prior = tb;
1820 if(!tb->prior)
1821 tPtr->firstTextBlock = tb;
1823 tPtr->currentTextBlock = tb;
1827 void
1828 WMAppendTextBlock(WMText *tPtr, void *vtb)
1830 TextBlock *tb = (TextBlock *)vtb;
1832 if(!tPtr || !tb)
1833 return;
1835 if(tb->graphic) {
1836 WMWidget *w = tb->d.widget;
1837 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
1838 handleWidgetPress, tb);
1839 //if(W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
1840 if(W_CLASS(w) != WC_TextField &&
1841 (((W_WidgetType*)(w))->widgetClass) != WC_Text) {
1842 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
1843 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
1845 WMPutInBag(tPtr->gfxItems, (void *)tb);
1846 WMRealizeWidget(w);
1849 if(!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
1850 tb->next = tb->prior = NULL;
1851 tPtr->lastTextBlock = tPtr->firstTextBlock
1852 = tPtr->currentTextBlock = tb;
1853 return;
1856 tb->next = tPtr->currentTextBlock->next;
1857 tb->prior = tPtr->currentTextBlock;
1858 if(tPtr->currentTextBlock->next)
1859 tPtr->currentTextBlock->next->prior = tb;
1861 tPtr->currentTextBlock->next = tb;
1863 if(!tb->next)
1864 tPtr->lastTextBlock = tb;
1866 tPtr->currentTextBlock = tb;
1869 void *
1870 WMRemoveTextBlock(WMText *tPtr)
1872 TextBlock *tb = NULL;
1874 if(!tPtr || !tPtr->firstTextBlock || !tPtr->lastTextBlock
1875 || !tPtr->currentTextBlock) {
1876 printf("cannot remove non existent TextBlock!\b");
1877 return tb;
1880 tb = tPtr->currentTextBlock;
1881 if(tb->graphic) {
1882 WMDeleteEventHandler(W_VIEW(tb->d.widget), ButtonPressMask,
1883 handleWidgetPress, tb);
1884 WMRemoveFromBag(tPtr->gfxItems, (void *)tb);
1885 WMUnmapWidget(tb->d.widget);
1888 if(tPtr->currentTextBlock == tPtr->firstTextBlock) {
1889 if(tPtr->currentTextBlock->next)
1890 tPtr->currentTextBlock->next->prior = NULL;
1892 tPtr->firstTextBlock = tPtr->currentTextBlock->next;
1893 tPtr->currentTextBlock = tPtr->firstTextBlock;
1895 } else if(tPtr->currentTextBlock == tPtr->lastTextBlock) {
1896 tPtr->currentTextBlock->prior->next = NULL;
1897 tPtr->lastTextBlock = tPtr->currentTextBlock->prior;
1898 tPtr->currentTextBlock = tPtr->lastTextBlock;
1899 } else {
1900 tPtr->currentTextBlock->prior->next = tPtr->currentTextBlock->next;
1901 tPtr->currentTextBlock->next->prior = tPtr->currentTextBlock->prior;
1902 tPtr->currentTextBlock = tPtr->currentTextBlock->next;
1905 return (void *)tb;
1908 void
1909 WMDestroyTextBlock(WMText *tPtr, void *vtb)
1911 TextBlock *tb = (TextBlock *)vtb;
1912 if(!tPtr || !tb)
1913 return;
1915 if(tb->graphic) {
1916 return;
1917 WMDestroyWidget(tb->d.widget);
1918 wfree(tb->d.widget);
1919 } else {
1920 WMReleaseFont(tb->d.font);
1923 WMReleaseColor(tb->color);
1924 if(tb->sections && tb->nsections > 0)
1925 wfree(tb->sections);
1926 wfree(tb->text);
1927 wfree(tb);
1931 void
1932 WMRefreshText(WMText *tPtr, int vpos, int hpos)
1934 //TextBlock *tb;
1936 if(!tPtr || vpos<0 || hpos<0)
1937 return;
1939 tPtr->flags.laidOut = False;
1940 layOutDocument(tPtr);
1941 updateScrollers(tPtr);
1942 paintText(tPtr);
1947 void
1948 WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
1950 if(!tPtr)
1951 return;
1953 if(color)
1954 tPtr->fgGC = WMColorGC(color);
1955 else
1956 tPtr->fgGC = WMColorGC(WMBlackColor(tPtr->view->screen));
1958 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1961 void
1962 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
1964 if(!tPtr)
1965 return;
1967 if(color) {
1968 tPtr->bgGC = WMColorGC(color);
1969 W_SetViewBackgroundColor(tPtr->view, color);
1970 } else {
1971 tPtr->bgGC = WMColorGC(WMWhiteColor(tPtr->view->screen));
1972 W_SetViewBackgroundColor(tPtr->view,
1973 WMWhiteColor(tPtr->view->screen));
1976 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1979 void
1980 WMSetTextRelief(WMText *tPtr, WMReliefType relief)
1982 if(!tPtr)
1983 return;
1984 tPtr->flags.relief = relief;
1985 paintText(tPtr);
1988 void
1989 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
1991 if(!tPtr)
1992 return;
1994 if(shouldhave && !tPtr->hS) {
1995 tPtr->hS = WMCreateScroller(tPtr);
1996 (W_VIEW(tPtr->hS))->attribs.cursor = tPtr->view->screen->defaultCursor;
1997 (W_VIEW(tPtr->hS))->attribFlags |= CWOverrideRedirect | CWCursor;
1998 WMSetScrollerArrowsPosition(tPtr->hS, WSAMaxEnd);
1999 WMSetScrollerAction(tPtr->hS, scrollersCallBack, tPtr);
2000 WMRealizeWidget(tPtr->hS);
2001 WMMapWidget(tPtr->hS);
2002 } else if(!shouldhave && tPtr->hS) {
2003 WMUnmapWidget(tPtr->hS);
2004 WMDestroyWidget(tPtr->hS);
2005 tPtr->hS = NULL;
2008 tPtr->hpos = 0;
2009 tPtr->prevHpos = 0;
2010 textDidResize(tPtr->view->delegate, tPtr->view);
2014 void
2015 WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
2017 if(!tPtr)
2018 return;
2020 if(shouldhave && !tPtr->vS) {
2021 tPtr->vS = WMCreateScroller(tPtr);
2022 (W_VIEW(tPtr->vS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2023 (W_VIEW(tPtr->vS))->attribFlags |= CWOverrideRedirect | CWCursor;
2024 WMSetScrollerArrowsPosition(tPtr->vS, WSAMaxEnd);
2025 WMSetScrollerAction(tPtr->vS, scrollersCallBack, tPtr);
2026 WMRealizeWidget(tPtr->vS);
2027 WMMapWidget(tPtr->vS);
2028 } else if(!shouldhave && tPtr->vS) {
2029 WMUnmapWidget(tPtr->vS);
2030 WMDestroyWidget(tPtr->vS);
2031 tPtr->vS = NULL;
2034 tPtr->vpos = 0;
2035 tPtr->prevVpos = 0;
2036 textDidResize(tPtr->view->delegate, tPtr->view);
2041 Bool
2042 WMScrollText(WMText *tPtr, int amount)
2044 Bool scroll=False;
2045 if(!tPtr)
2046 return False;
2047 if(amount == 0 || !tPtr->view->flags.realized)
2048 return False;
2050 if(amount < 0) {
2051 if(tPtr->vpos > 0) {
2052 if(tPtr->vpos > amount) tPtr->vpos += amount;
2053 else tPtr->vpos=0;
2054 scroll=True;
2055 } } else {
2056 int limit = tPtr->docHeight - tPtr->visible.h;
2057 if(tPtr->vpos < limit) {
2058 if(tPtr->vpos < limit-amount) tPtr->vpos += amount;
2059 else tPtr->vpos = limit;
2060 scroll = True;
2061 } }
2063 if(scroll && tPtr->vpos != tPtr->prevVpos) {
2064 updateScrollers(tPtr);
2065 paintText(tPtr);
2067 tPtr->prevVpos = tPtr->vpos;
2068 return scroll;
2071 Bool
2072 WMPageText(WMText *tPtr, Bool direction)
2074 if(!tPtr) return False;
2075 if(!tPtr->view->flags.realized) return False;
2077 return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
2081 void
2082 WMSetTextUseMonoFont(WMText *tPtr, Bool mono)
2084 if(!tPtr)
2085 return;
2086 if(mono && tPtr->flags.rulerShown)
2087 ;//WMShowTextRuler(tPtr, False);
2089 tPtr->flags.monoFont = mono;
2090 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2093 Bool
2094 WMGetTextUsesMonoFont(WMText *tPtr)
2096 if(!tPtr)
2097 return True;
2098 return tPtr->flags.monoFont;
2101 void
2102 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
2104 if(!tPtr)
2105 return;
2107 if(font)
2108 tPtr->dFont = font;
2109 else
2110 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2113 WMFont *
2114 WMGetTextDefaultFont(WMText *tPtr)
2116 if(!tPtr)
2117 return NULL;
2118 else
2119 return tPtr->dFont;
2122 void
2123 WMSetTextParser(WMText *tPtr, WMAction *parser)
2125 if(!tPtr)
2126 return;
2127 tPtr->parser = parser;
2131 void
2132 WMSetTextWriter(WMText *tPtr, WMAction *writer)
2134 if(!tPtr)
2135 return;
2136 tPtr->writer = writer;
2139 int
2140 WMGetTextInsertType(WMText *tPtr)
2142 if(!tPtr)
2143 return 0;
2144 return tPtr->flags.prepend;