WMSetTextSeelctionFont/Color
[wmaker-crm.git] / WINGs / wtext.c
blobe503b331aa08c3b997c3a6d3bd0c716a84bd3f8d
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) )
37 #endif
39 //_______
44 /* a Section is a section of a TextBlock that describes what parts
45 of a TextBlock has been laid out on which "line"...
46 o this greatly aids redraw, scroll and selection.
47 o this is created during layoutLine, but may be later modified.
48 o there may be many Sections per TextBlock, hence the array */
49 typedef struct {
50 int x, y; /* where to draw it from */
51 int w, h; /* it's width and height (to aid selection) */
52 int _y;
53 unsigned short begin, end; /* what part of the text block */
54 } Section;
57 /* a TextBlock is a doubly-linked list of TextBlocks containing:
58 o text for the block, color and font
59 o or a pointer to the widget and the (text) description for its graphic
60 o but NOT both */
62 typedef struct _TextBlock {
63 struct _TextBlock *next; /* next text block in linked list */
64 struct _TextBlock *prior; /* prior text block in linked list */
66 char *text; /* pointer to 8- or 16-bit text */
67 /* or to the object's description */
68 union {
69 WMFont *font; /* the font */
70 WMWidget *widget; /* the embedded widget */
71 } d; /* description */
73 WMColor *color; /* the color */
74 Section *sections; /* the region for layouts (a growable array) */
75 /* an _array_! of size _nsections_ */
78 unsigned short used; /* number of chars in this block */
79 unsigned short allocated; /* size of allocation (in chars) */
81 unsigned int first:1; /* first TextBlock in paragraph */
82 unsigned int blank:1; /* ie. blank paragraph */
83 unsigned int kanji:1; /* is of 16-bit characters or not */
84 unsigned int graphic:1; /* embedded object or text: text=0 */
85 unsigned int underlined:1; /* underlined or not */
86 unsigned int nsections:8; /* over how many "lines" a TexBlock wraps */
87 int script:8; /* script in points: negative for subscript */
88 unsigned int marginN:10; /* which of the margins in WMText to use */
89 unsigned int RESERVED:1;
90 } TextBlock;
93 /* somehow visible.h beats the hell outta visible.size.height :-) */
94 typedef struct {
95 unsigned int y;
96 unsigned int x;
97 unsigned int h;
98 unsigned int w;
99 } myRect;
102 typedef struct W_Text {
103 W_Class widgetClass; /* the class number of this widget */
104 W_View *view; /* the view referring to this instance */
106 WMRuler *ruler; /* the ruler subwiget to manipulate paragraphs */
108 WMScroller *vS; /* the vertical scroller */
109 int vpos; /* the current vertical position */
110 int prevVpos; /* the previous vertical position */
112 WMScroller *hS; /* the horizontal scroller */
113 int hpos; /* the current horizontal position */
114 int prevHpos; /* the previous horizontal position */
115 /* in short: tPtr->hS?nowrap:wrap */
117 WMFont *dFont; /* the default font */
118 WMColor *dColor; /* the default color */
119 WMPixmap *dBulletPix; /* the default pixmap for bullets */
121 GC bgGC; /* the background GC to draw with */
122 GC fgGC; /* the foreground GC to draw with */
123 Pixmap db; /* the buffer on which to draw */
125 WMRulerMargins *margins;/* a (growable) array of margins to be used */
126 /* by the various TextBlocks */
128 myRect visible; /* the actual rectangle that can be drawn into */
129 myRect cursor; /* the position and (height) of cursor */
130 myRect sel; /* the selection rectangle */
131 int docWidth; /* the width of the entire document */
132 int docHeight; /* the height of the entire document */
135 TextBlock *firstTextBlock;
136 TextBlock *lastTextBlock;
137 TextBlock *currentTextBlock;
140 WMBag *gfxItems; /* a nice bag containing graphic items */
142 WMHandlerID timerID; /* for nice twinky-winky */
143 WMPoint clicked; /* where in the _document_ was clicked */
144 unsigned short tpos; /* the character position in the currentTextBlock */
145 unsigned short RESERVED;/* space taker upper... */
148 WMAction *parser;
149 WMAction *writer;
151 struct {
152 unsigned int monoFont:1; /* whether to ignore formats */
153 unsigned int focused:1; /* whether this instance has input focus */
154 unsigned int editable:1; /* "silly user, you can't edit me" */
155 unsigned int ownsSelection:1; /* "I ownz the current selection!" */
156 unsigned int pointerGrabbed:1;/* "heh, gib me pointer" */
157 unsigned int buttonHeld:1; /* the user is holding down the button */
158 unsigned int waitingForSelection:1; /* dum dee dumm... */
159 unsigned int extendSelection:1; /* shift-drag to select more regions */
161 unsigned int rulerShown:1; /* whether the ruler is shown or not */
162 unsigned int frozen:1; /* whether screen updates are to be made */
163 unsigned int cursorShown:1; /* whether to show the cursor */
164 unsigned int clickPos:1; /* clicked before=0 or after=1 a graphic: */
165 /* within counts as after too */
167 unsigned int ignoreNewLine:1;/* "bleh XK_Return" ignore it when typed */
168 unsigned int laidOut:1; /* have the TextBlocks all been laid out */
169 unsigned int prepend:1; /* prepend=1, append=0 (for parsers) */
170 WMAlignment alignment:2; /* the alignment for text */
171 WMReliefType relief:3; /* the relief to display with */
172 unsigned int RESERVED:4;
173 unsigned int nmargins:10; /* the number of margin arrays */
174 } flags;
175 } Text;
177 static char *default_bullet[] = {
178 "6 6 4 1",
179 " c None s None", ". c black",
180 "X c white", "o c #808080",
181 " ... ",
182 ".XX.. ",
183 ".XX..o",
184 ".....o",
185 " ...oo",
186 " ooo "};
189 static void
190 paintText(Text *tPtr)
192 TextBlock *tb = tPtr->firstTextBlock;
193 WMFont *font;
194 GC gc, greyGC;
195 char *text;
196 int len, y, c, s, done=False;
197 int prev_y=-23;
198 WMScreen *scr = tPtr->view->screen;
199 Display *dpy = tPtr->view->screen->display;
200 Window win = tPtr->view->window;
203 if (!tPtr->view->flags.realized || !tPtr->db)
204 return;
206 XFillRectangle(dpy, tPtr->db, tPtr->bgGC,
207 0, 0, tPtr->visible.w, tPtr->visible.h);
210 tb = tPtr->firstTextBlock;
211 if (!tb)
212 goto _copy_area;
215 if (tPtr->flags.ownsSelection) {
216 greyGC = WMColorGC(WMGrayColor(scr));
217 //XFillRectangle(dpy, tPtr->db, greyGC,
218 // tPtr->sel.x, tPtr->sel.y-tPtr->vpos, tPtr->sel.w, tPtr->sel.h);
219 // XDrawRectangle(dpy, tPtr->db, tPtr->fgGC,
220 // tPtr->sel.x, tPtr->sel.y-tPtr->vpos, tPtr->sel.w, tPtr->sel.h);
223 done = False;
224 while(!done && tb) {
226 if (tb->graphic) {
227 tb = tb->next;
228 continue;
231 for(s=0; s<tb->nsections && !done; s++) {
234 if (tb->sections[s]._y > tPtr->vpos + tPtr->visible.h) {
235 done = True;
236 break;
239 if ( tb->sections[s].y + tb->sections[s].h < tPtr->vpos)
240 continue;
242 if (tPtr->flags.monoFont) {
243 font = tPtr->dFont;
244 gc = tPtr->fgGC;
245 } else {
246 font = tb->d.font;
247 gc = WMColorGC(tb->color);
250 if (tPtr->flags.ownsSelection) {
252 if (prev_y != tb->sections[s]._y
253 && (tb->sections[s]._y >= tPtr->sel.y)
254 && (tb->sections[s]._y + tb->sections[s].h
255 <= tPtr->sel.y + tPtr->sel.h)) {
256 XFillRectangle(dpy, tPtr->db, greyGC,
257 tPtr->visible.x,
258 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
259 tPtr->visible.w, tb->sections[s].h);
261 } else if ( prev_y != tb->sections[s]._y
262 && (tb->sections[s]._y <= tPtr->sel.y)
263 && (tb->sections[s]._y + tb->sections[s].h
264 >= tPtr->sel.y)
265 && (tPtr->sel.x >= tb->sections[s].x)
266 && (tPtr->sel.y + tPtr->sel.h
267 >= tb->sections[s]._y + tb->sections[s].h)) {
268 XFillRectangle(dpy, tPtr->db, greyGC,
269 tPtr->clicked.x,
270 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
271 tPtr->visible.w - tPtr->sel.x, tb->sections[s].h);
273 } else if (prev_y != tb->sections[s]._y
274 && (tb->sections[s]._y <= tPtr->sel.y + tPtr->sel.h)
275 && (tb->sections[s]._y >= tPtr->sel.y)) {
276 XFillRectangle(dpy, tPtr->db, greyGC,
277 tPtr->visible.x,
278 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
279 tPtr->sel.x + tPtr->sel.w -tPtr->visible.x,
280 tb->sections[s].h);
282 } else if ( prev_y != tb->sections[s]._y
283 && (tb->sections[s]._y <= tPtr->sel.y)
284 && (tb->sections[s]._y + tb->sections[s].h
285 >= tPtr->sel.y + tPtr->sel.h) ) {
286 XFillRectangle(dpy, tPtr->db, greyGC,
287 tPtr->sel.x,
288 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
289 tPtr->sel.w,tb->sections[s].h);
294 prev_y = tb->sections[s]._y;
296 len = tb->sections[s].end - tb->sections[s].begin;
297 text = &(tb->text[tb->sections[s].begin]);
298 y = tb->sections[s].y - tPtr->vpos;
299 WMDrawString(scr, tPtr->db, gc, font,
300 tb->sections[s].x, y, text, len);
302 if (tb->underlined) {
303 XDrawLine(dpy, tPtr->db, gc,
304 tb->sections[s].x, y + font->y + 1,
305 tb->sections[s].x + tb->sections[s].w, y + font->y + 1);
310 tb = (!done? tb->next : NULL);
314 c = WMGetBagItemCount(tPtr->gfxItems);
315 if (c > 0 && !tPtr->flags.monoFont) {
316 int j;
317 WMWidget *wdt;
318 for(j=0; j<c; j++) {
319 tb = (TextBlock *) WMGetFromBag(tPtr->gfxItems, j);
320 wdt = tb->d.widget;
321 if (tb->sections[0]._y + tb->sections[0].h <= tPtr->vpos
322 || tb->sections[0]._y >= tPtr->vpos + tPtr->visible.h ) {
324 if ((W_VIEW(wdt))->flags.mapped) {
325 WMUnmapWidget(wdt);
327 } else {
328 if (!(W_VIEW(wdt))->flags.mapped) {
329 WMMapWidget(wdt);
330 //WMLowerWidget(wdt);
333 if (tPtr->flags.ownsSelection && 0
334 //&& (tb->sections[s]._y >= tPtr->sel.y)
335 //&& (tb->sections[s]._y + tb->sections[s].h
336 ){ // <= tPtr->sel.y + tPtr->sel.h)) {
337 XFillRectangle(dpy, tPtr->db, greyGC,
338 tb->sections[0].x, tb->sections[0].y - tPtr->vpos,
339 tb->sections[0].w, tb->sections[0].h);
342 WMMoveWidget(wdt, 3 + tb->sections[0].x + tPtr->visible.x,
343 tb->sections[0].y - tPtr->vpos);
345 if (tb->underlined) {
346 XDrawLine(dpy, tPtr->db, WMColorGC(tb->color),
347 tb->sections[0].x,
348 tb->sections[0].y + WMWidgetHeight(wdt) + 1,
349 tb->sections[0].x + tb->sections[0].w,
350 tb->sections[0].y + WMWidgetHeight(wdt) + 1);
351 } } } }
354 if (tPtr->flags.cursorShown && tPtr->cursor.x != -23
355 && tPtr->flags.focused) {
356 int y = tPtr->cursor.y - tPtr->vpos;
357 XDrawLine(dpy, tPtr->db, tPtr->fgGC,
358 tPtr->cursor.x, y,
359 tPtr->cursor.x, y + tPtr->cursor.h);
360 //printf("%d %d %d\n", tPtr->cursor.x, tPtr->cursor.y, tPtr->cursor.h);
363 _copy_area:
365 XCopyArea(dpy, tPtr->db, win, tPtr->bgGC,
366 0, 0,
367 tPtr->visible.w, tPtr->visible.h,
368 tPtr->visible.x, tPtr->visible.y);
370 W_DrawRelief(scr, win, 0, 0,
371 tPtr->view->size.width, tPtr->view->size.height,
372 tPtr->flags.relief);
374 if (tPtr->ruler && tPtr->flags.rulerShown)
375 XDrawLine(dpy, win,
376 tPtr->fgGC, 2, 42,
377 tPtr->view->size.width-4, 42);
380 XFillRectangle(tPtr->view->screen->display, tPtr->view->window,
381 tPtr->bgGC,
382 2, tPtr->view->size.height-3,
383 tPtr->view->size.width-4, 3);
388 #define CURSOR_BLINK_ON_DELAY 600
389 #define CURSOR_BLINK_OFF_DELAY 400
391 static void
392 blinkCursor(void *data)
394 Text *tPtr = (Text*)data;
396 if (tPtr->flags.cursorShown) {
397 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY,
398 blinkCursor, data);
399 } else {
400 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
401 blinkCursor, data);
403 paintText(tPtr);
404 tPtr->flags.cursorShown = !tPtr->flags.cursorShown;
407 static TextBlock *
408 getFirstNonGraphicBlockFor(TextBlock *tb, short dir)
410 if (!tb)
411 return NULL;
412 while(tb) {
413 if (!tb->graphic)
414 break;
415 tb = (dir? tb->next : tb->prior);
418 return tb;
422 static void
423 cursorToTextPosition(Text *tPtr, int x, int y)
425 TextBlock *tb = NULL;
426 int done=False, s, pos, len, _w, _y, dir=1; /* 1 == "down" */
427 char *text;
429 y += (tPtr->vpos - tPtr->visible.y);
430 if (y<0)
431 y = 0;
433 x -= (tPtr->visible.x - 2);
434 if (x<0)
435 x=0;
437 /* clicked is relative to document, not window... */
438 tPtr->clicked.x = x;
439 tPtr->clicked.y = y;
441 if (! (tb = tPtr->currentTextBlock)) {
442 if (! (tb = tPtr->firstTextBlock)) {
443 tPtr->tpos = 0;
444 return;
448 /* first, which direction? Most likely, newly clicked
449 position will be close to previous */
450 dir = !(y < tb->sections[0].y);
451 dir = 0;
452 tb = tPtr->lastTextBlock;
453 printf("%s\n", dir?"down":"up");
455 if (tPtr->flags.monoFont && tb->graphic) {
456 tb = getFirstNonGraphicBlockFor(tb, 1);
457 if (!tb) {
458 tPtr->currentTextBlock =
459 (dir? tPtr->lastTextBlock : tPtr->firstTextBlock);
460 tPtr->tpos = 0;
461 return;
465 s = (dir? 0 : tb->nsections-1);
466 if ( y >= tb->sections[s]._y
467 && y <= tb->sections[s]._y + tb->sections[s].h) {
468 printf("got it\n");
469 goto _doneV;
472 /* get the first section of the TextBlock that lies about
473 the vertical click point */
474 done = False;
475 while(!done && tb) {
477 if (tPtr->flags.monoFont && tb->graphic) {
478 tb = tb->next;
479 continue;
482 s = (dir? 0 : tb->nsections-1);
483 while(!done && (dir? (s<tb->nsections) : (s>=0) )) {
485 if ( y >= tb->sections[s]._y
486 && y <= tb->sections[s]._y + tb->sections[s].h) {
487 done = True;
488 } else {
489 dir? s++ : s--;
493 if (!done) {
494 if ( (dir? tb->next : tb->prior)) {
495 tb = (dir ? tb->next : tb->prior);
496 } else {
497 pos = tb->used;
498 break; //goto _doneH;
504 if (s<0 || s>=tb->nsections) {
505 s = (dir? tb->nsections-1 : 0);
508 _doneV:
509 /* we have the line, which TextBlock on that line is it? */
510 pos = 0;
511 if (tPtr->flags.monoFont && tb->graphic)
512 tb = getFirstNonGraphicBlockFor(tb, dir);
513 if (tb) {
514 if ((dir? tb->sections[s].x >= x : tb->sections[s].x < x))
515 ; //goto _doneH;
516 _y = tb->sections[s]._y;
519 while(tb) {
521 if (tPtr->flags.monoFont && tb->graphic) {
522 tb = (dir ? tb->next : tb->prior);
523 continue;
526 if (dir) {
527 if (tb->graphic) {
528 _w = WMWidgetWidth(tb->d.widget);
529 } else {
530 text = &(tb->text[tb->sections[s].begin]);
531 len = tb->sections[s].end - tb->sections[s].begin;
532 _w = WMWidthOfString(tb->d.font, text, len);
533 if (tb->sections[s].x + _w >= x)
534 break;
537 } else {
538 if (tb->sections[s].x <= x)
539 break;
542 #if 0
543 if ((dir? tb->next : tb->prior)) {
544 TextBlock *nxt = (dir? tb->next : tb->prior);
545 if (tPtr->flags.monoFont && nxt->graphic) {
546 nxt = getFirstNonGraphicBlockFor(nxt, dir);
547 if (!nxt) {
548 pos = 0;
549 goto _doneH;
552 if (_y != nxt->sections[s]._y) {
553 /* this must be the last/first on this line. stop */
554 pos = (dir? tb->used : 0);
555 goto _doneH;
558 #endif
560 tb = (dir ? tb->next : tb->prior);
561 if (tb)
562 s = (dir? 0 : tb->nsections-1);
565 /* we have said TextBlock, now where within it? */
566 if (tb && !tb->graphic) {
567 WMFont *f = tb->d.font;
568 len = tb->sections[s].end - tb->sections[s].begin;
569 text = &(tb->text[tb->sections[s].begin]);
571 _w = x - tb->sections[s].x;
572 pos = 0;
574 while(pos<len && WMWidthOfString(f, text, pos+1) < _w)
575 pos++;
577 tPtr->cursor.x = tb->sections[s].x +
578 (pos? WMWidthOfString(f, text, pos) : 0);
579 pos += tb->sections[s].begin;
580 _doneH:
581 tPtr->tpos = (pos<tb->used)? pos : tb->used;
584 tPtr->currentTextBlock = tb;
585 tPtr->flags.cursorShown;
586 tPtr->cursor.h = tb->sections[s].h;
587 tPtr->cursor.y = tb->sections[s]._y;
588 paintText(tPtr);
591 if (!tb)
592 printf("will hang :-)\n");
596 static void
597 updateScrollers(Text *tPtr)
600 if (tPtr->vS) {
601 if (tPtr->docHeight < tPtr->visible.h) {
602 WMSetScrollerParameters(tPtr->vS, 0, 1);
603 tPtr->vpos = 0;
604 } else {
605 float vmax = (float)(tPtr->docHeight);
606 WMSetScrollerParameters(tPtr->vS,
607 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
608 (float)tPtr->visible.h/vmax);
610 } else tPtr->vpos = 0;
612 if (tPtr->hS)
616 static void
617 scrollersCallBack(WMWidget *w, void *self)
619 Text *tPtr = (Text *)self;
620 Bool scroll = False;
621 Bool dimple = False;
622 int which;
624 if (!tPtr->view->flags.realized) return;
626 if (w == tPtr->vS) {
627 float vmax;
628 int height;
629 vmax = (float)(tPtr->docHeight);
630 height = tPtr->visible.h;
632 which = WMGetScrollerHitPart(tPtr->vS);
633 switch(which) {
634 case WSDecrementLine:
635 if (tPtr->vpos > 0) {
636 if (tPtr->vpos>16) tPtr->vpos-=16;
637 else tPtr->vpos=0;
638 scroll=True;
639 }break;
640 case WSIncrementLine: {
641 int limit = tPtr->docHeight - height;
642 if (tPtr->vpos < limit) {
643 if (tPtr->vpos<limit-16) tPtr->vpos+=16;
644 else tPtr->vpos=limit;
645 scroll = True;
646 }}break;
647 case WSDecrementPage:
648 tPtr->vpos -= height;
650 if (tPtr->vpos < 0)
651 tPtr->vpos = 0;
652 dimple = True;
653 scroll = True;
654 printf("dimple needs to jump to mouse location ;-/\n");
655 break;
656 case WSIncrementPage:
657 tPtr->vpos += height;
658 if (tPtr->vpos > (tPtr->docHeight - height))
659 tPtr->vpos = tPtr->docHeight - height;
660 dimple = True;
661 scroll = True;
662 printf("dimple needs to jump to mouse location ;-/\n");
663 break;
666 case WSKnob:
667 tPtr->vpos = WMGetScrollerValue(tPtr->vS)
668 * (float)(tPtr->docHeight - height);
669 scroll = True;
670 break;
672 case WSKnobSlot:
673 case WSNoPart:
674 printf("WSNoPart, WSKnobSlot\n");
675 #if 0
676 float vmax = (float)(tPtr->docHeight);
677 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
678 (float)tPtr->visible.h/vmax;
679 dimple =where mouse is.
680 #endif
681 break;
683 scroll = (tPtr->vpos != tPtr->prevVpos);
684 tPtr->prevVpos = tPtr->vpos;
687 if (w == tPtr->hS)
690 if (scroll) {
692 if (0&&dimple) {
693 if (tPtr->rulerShown)
694 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
695 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
696 else
697 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
698 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
701 if (which == WSDecrementLine || which == WSIncrementLine)
702 updateScrollers(tPtr);
703 paintText(tPtr);
709 typedef struct {
710 TextBlock *tb;
711 unsigned short begin, end; /* what part of the text block */
713 } myLineItems;
716 static int
717 layOutLine(Text *tPtr, myLineItems *items, int nitems, int x, int y,
718 int pwidth, WMAlignment align)
720 int i, j=0, lw = 0, line_height=0, max_d=0, len, n;
721 WMFont *font;
722 char *text;
723 TextBlock *tb;
724 Bool gfx=0;
725 TextBlock *tbsame=NULL;
727 for(i=0; i<nitems; i++) {
728 tb = items[i].tb;
730 if (tb->graphic) {
731 if (!tPtr->flags.monoFont) {
732 WMWidget *wdt = tb->d.widget;
733 line_height = WMAX(line_height, WMWidgetHeight(wdt));
734 if (align != WALeft)
735 lw += WMWidgetWidth(wdt);
736 gfx = True;
739 } else {
740 font = (tPtr->flags.monoFont)?tPtr->dFont : tb->d.font;
741 max_d = WMAX(max_d, font->height-font->y);
742 line_height = WMAX(line_height, font->height);
743 text = &(tb->text[items[i].begin]);
744 len = items[i].end - items[i].begin;
745 if (align != WALeft)
746 lw += WMWidthOfString(font, text, len);
750 if (align == WARight) {
751 j = pwidth - lw;
752 } else if (align == WACenter) {
753 j = (int) ((float)(pwidth - lw))/2.0;
755 if (gfx)
756 y+=10;
758 for(i=0; i<nitems; i++) {
759 tb = items[i].tb;
761 if (tbsame == tb) { /*extend it, since it's on same line */
762 tb->sections[tb->nsections-1].end = items[i].end;
763 n = tb->nsections-1;
764 } else {
765 tb->sections = wrealloc(tb->sections,
766 (++tb->nsections)*sizeof(Section));
767 n = tb->nsections-1;
768 tb->sections[n]._y = y;
769 tb->sections[n].x = x+j;
770 tb->sections[n].h = line_height;
771 tb->sections[n].begin = items[i].begin;
772 tb->sections[n].end = items[i].end;
774 if (tb->graphic) {
775 if (!tPtr->flags.monoFont) {
776 WMWidget *wdt = tb->d.widget;
777 tb->sections[n].y = 1 + max_d +
778 y + line_height - WMWidgetHeight(wdt);
779 tb->sections[n].w = WMWidgetWidth(wdt);
780 x += tb->sections[n].w;
782 } else {
783 font = (tPtr->flags.monoFont)? tPtr->dFont : tb->d.font;
784 len = items[i].end - items[i].begin;
785 text = &(tb->text[items[i].begin]);
787 tb->sections[n].y = y+line_height-font->y;
788 tb->sections[n].w =
789 WMWidthOfString(font,
790 &(tb->text[tb->sections[n].begin]),
791 tb->sections[n].end - tb->sections[n].begin);
793 x += WMWidthOfString(font, text, len);
796 tbsame = tb;
799 return line_height+(gfx?10:0);
804 static void
805 output(char *ptr, int len)
807 char s[len+1];
808 memcpy(s, ptr, len);
809 s[len] = 0;
810 //printf(" s is [%s] (%d)\n", s, strlen(s));
811 printf("[%s]\n", s);
816 #define MAX_TB_PER_LINE 64
818 static void
819 layOutDocument(Text *tPtr)
821 TextBlock *tb;
822 myLineItems items[MAX_TB_PER_LINE];
823 WMAlignment align = WALeft;
824 WMFont *font;
825 Bool lhc = !tPtr->flags.laidOut; /* line height changed? */
826 int prev_y;
828 int nitems=0, x=0, y=0, lw = 0, width=0;
829 int pwidth = tPtr->visible.w - tPtr->visible.x;
831 char *start=NULL, *mark=NULL;
832 int begin, end;
834 if (!(tb = tPtr->firstTextBlock))
835 return;
837 if (0&&tPtr->flags.laidOut) {
838 tb = tPtr->currentTextBlock;
839 if (tb->sections && tb->nsections>0)
840 prev_y = tb->sections[tb->nsections-1]._y;
841 y+=10;
842 printf("1 prev_y %d \n", prev_y);
844 /* search backwards for textblocks on same line */
845 while(tb) {
846 if (!tb->sections || tb->nsections<1) {
847 tb = tPtr->firstTextBlock;
848 break;
850 if (tb->sections[tb->nsections-1]._y != prev_y) {
851 tb = tb->next;
852 break;
854 // prev_y = tb->sections[tb->nsections-1]._y;
855 tb = tb->prior;
857 y = 0;//tb->sections[tb->nsections-1]._y;
858 printf("2 prev_y %d \n\n", tb->sections[tb->nsections-1]._y);
862 while(tb) {
864 if (tb->sections && tb->nsections>0) {
865 wfree(tb->sections);
866 tb->sections = NULL;
867 tb->nsections = 0;
870 if (tb->first) {
871 output(tb->text, tb->used);
872 y += layOutLine(tPtr, items, nitems, x, y, pwidth, align);
873 x = 0;//tPtr->visible.x+2;
874 nitems = 0;
875 lw = 0;
878 if (tb->graphic) {
879 if (!tPtr->flags.monoFont) {
880 width = WMWidgetWidth(tb->d.widget);
881 if (width > pwidth)printf("rescale graphix to fit?\n");
882 lw += width;
883 if (lw >= pwidth - x
884 || nitems >= MAX_TB_PER_LINE) {
885 y += layOutLine(tPtr, items, nitems, x, y,
886 pwidth, align);
887 nitems = 0;
888 x = 0;//tPtr->visible.x+2;
889 lw = width;
892 items[nitems].tb = tb;
893 items[nitems].begin = 0;
894 items[nitems].end = 0;
895 nitems++;
898 } else if ((start = tb->text)) {
899 begin = end = 0;
900 font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
902 while(start) {
903 mark = strchr(start, ' ');
904 if (mark) {
905 end += (int)(mark-start)+1;
906 start = mark+1;
907 } else {
908 end += strlen(start);
909 start = mark;
912 if (end > tb->used)
913 end = tb->used;
915 if (end-begin > 0) {
917 width = WMWidthOfString(font,
918 &tb->text[begin], end-begin);
920 if (width > pwidth) { /* break this tb up */
921 char *t = &tb->text[begin];
922 int l=end-begin, i=0;
923 do {
924 width = WMWidthOfString(font, t, ++i);
925 } while (width < pwidth && i < l);
926 end = begin+i;
927 if (start) // and since (nil)-4 = 0xfffffffd
928 start -= l-i;
932 lw += width;
935 if ((lw >= pwidth - x)
936 || nitems >= MAX_TB_PER_LINE) {
937 y += layOutLine(tPtr, items, nitems, x, y,
938 pwidth, align);
939 lw = width;
940 x = 0; //tPtr->visible.x+2;
941 nitems = 0;
944 items[nitems].tb = tb;
945 items[nitems].begin = begin;
946 items[nitems].end = end;
947 nitems++;
949 begin = end;
952 tb = tb->next;
956 if (nitems > 0)
957 y += layOutLine(tPtr, items, nitems, x, y, pwidth, align);
958 if (lhc) {
959 tPtr->docHeight = y+10;
960 updateScrollers(tPtr);
962 tPtr->flags.laidOut = True;
967 static void
968 textDidResize(W_ViewDelegate *self, WMView *view)
970 Text *tPtr = (Text *)view->self;
971 unsigned short w = WMWidgetWidth(tPtr);
972 unsigned short h = WMWidgetHeight(tPtr);
973 unsigned short rh = 0, vw = 0;
975 if (tPtr->ruler && tPtr->flags.rulerShown) {
976 WMMoveWidget(tPtr->ruler, 20, 2);
977 WMResizeWidget(tPtr->ruler, w - 22, 40);
978 rh = 40;
981 if (tPtr->vS) {
982 WMMoveWidget(tPtr->vS, 1, rh + 2);
983 WMResizeWidget(tPtr->vS, 20, h - rh - 3);
984 vw = 20;
985 WMSetRulerOffset(tPtr->ruler, 22);
986 } else WMSetRulerOffset(tPtr->ruler, 2);
988 if (tPtr->hS) {
989 if (tPtr->vS) {
990 WMMoveWidget(tPtr->hS, vw, h - 21);
991 WMResizeWidget(tPtr->hS, w - vw - 1, 20);
992 } else {
993 WMMoveWidget(tPtr->hS, vw+1, h - 21);
994 WMResizeWidget(tPtr->hS, w - vw - 2, 20);
998 tPtr->visible.x = (tPtr->vS)?22:0;
999 tPtr->visible.y = (tPtr->ruler && tPtr->flags.rulerShown)?43:3;
1000 tPtr->visible.w = tPtr->view->size.width - tPtr->visible.x - 12;
1001 tPtr->visible.h = tPtr->view->size.height - tPtr->visible.y;
1002 tPtr->visible.h -= (tPtr->hS)?20:0;
1004 tPtr->margins[0].left = tPtr->margins[0].right = tPtr->visible.x;
1005 tPtr->margins[0].body = tPtr->visible.x;
1006 tPtr->margins[0].right = tPtr->visible.w;
1008 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1010 if (tPtr->db) {
1011 //if (tPtr->view->flags.realized)
1012 //XFreePixmap(tPtr->view->screen->display, tPtr->db);
1015 if (tPtr->visible.w < 10) tPtr->visible.w = 10;
1016 if (tPtr->visible.h < 10) tPtr->visible.h = 10;
1018 //if (size change or !db
1019 tPtr->db = XCreatePixmap(tPtr->view->screen->display,
1020 tPtr->view->window, tPtr->visible.w,
1021 tPtr->visible.h, tPtr->view->screen->depth);
1023 paintText(tPtr);
1026 W_ViewDelegate _TextViewDelegate =
1028 NULL,
1029 NULL,
1030 textDidResize,
1031 NULL,
1034 /* nice, divisble-by-16 blocks */
1035 static inline unsigned short
1036 reqBlockSize(unsigned short requested)
1038 return requested + 16 - (requested%16);
1041 static void
1042 clearText(Text *tPtr)
1044 void *tb;
1045 if (!tPtr->firstTextBlock)
1046 return;
1048 while(tPtr->currentTextBlock)
1049 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1051 printf("yadda clearText\n");
1053 printf("remove the document\n");
1054 tPtr->firstTextBlock = NULL;
1055 tPtr->currentTextBlock = NULL;
1056 tPtr->lastTextBlock = NULL;
1057 //WMThawText(tPtr);
1058 WMRefreshText(tPtr, 0, 0);
1061 static void
1062 deleteTextInteractively(Text *tPtr, KeySym ksym)
1064 printf("deleting %ld\n", ksym);
1068 static void
1069 insertTextInteractively(Text *tPtr, char *text, int len)
1071 TextBlock *tb;
1072 char *newline = NULL;
1074 if (!tPtr->flags.editable || len < 1 || !text)
1075 return;
1077 if (tPtr->flags.ignoreNewLine) {
1078 int i;
1079 for(i=0; i<len; i++) {
1080 if (text[i] == '\n')
1081 text[i] = ' ';
1085 if (*text == 'a')
1086 WMSetTextSelectionColor(tPtr, WMCreateNamedColor(tPtr->view->screen,
1087 "blue", False));
1089 tb = tPtr->currentTextBlock;
1090 if (!tb || tb->graphic) {
1091 WMAppendTextStream(tPtr, text);
1092 if (tPtr->currentTextBlock) {
1093 tPtr->tpos = tPtr->currentTextBlock->used;
1095 return;
1098 if ((newline = strchr(text, '\n'))) {
1099 int nlen = (int)(newline-text);
1100 int s = tb->used - tPtr->tpos;
1101 char save[s];
1103 if (!tb->blank && nlen>0 ) {
1104 if (s > 0 ) {
1105 memcpy(save, &tb->text[tPtr->tpos], s);
1106 tb->used -= (tb->used - tPtr->tpos);
1108 text[nlen] = 0;
1109 insertTextInteractively(tPtr, text, nlen);
1110 newline++;
1111 WMAppendTextStream(tPtr, newline);
1112 if (s>0)
1113 insertTextInteractively(tPtr, save, s);
1114 } else WMAppendTextStream(tPtr, text);
1116 } else {
1117 if (tb->used + len >= tb->allocated) {
1118 tb->allocated = reqBlockSize(tb->used+len);
1119 tb->text = wrealloc(tb->text, tb->allocated);
1122 if (tb->blank) {
1123 memcpy(tb->text, text, len);
1124 tb->used = len;
1125 tPtr->tpos = len;
1126 tb->blank = False;
1127 } else {
1128 memmove(&(tb->text[tPtr->tpos+len]), &tb->text[tPtr->tpos],
1129 tb->used-tPtr->tpos+1);
1130 memmove(&tb->text[tPtr->tpos], text, len);
1131 tb->used += len;
1132 tPtr->tpos += len;
1139 static void
1140 selectRegion(Text *tPtr, int x, int y)
1142 if (x < 0 || y < 0)
1143 return;
1144 y += tPtr->vpos;
1145 if (y>10) y -= 10; /* the original offset */
1147 x -= tPtr->visible.x-2;
1148 if (x<0) x=0;
1150 tPtr->sel.x = WMAX(0, WMIN(tPtr->clicked.x, x));
1151 tPtr->sel.w = abs(tPtr->clicked.x - x);
1152 tPtr->sel.y = WMAX(0, WMIN(tPtr->clicked.y, y));
1153 tPtr->sel.h = abs(tPtr->clicked.y - y);
1155 tPtr->flags.ownsSelection = True;
1156 paintText(tPtr);
1160 static void
1161 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1162 void *cdata, WMData *data)
1164 Text *tPtr = (Text *)view->self;
1165 char *str;
1167 tPtr->flags.waitingForSelection = False;
1169 if (data) {
1170 str = (char*)WMDataBytes(data);
1171 if (tPtr->parser) {
1172 (tPtr->parser) (tPtr, (void *) str);
1173 } else {
1174 insertTextInteractively(tPtr, str, strlen(str));
1176 WMRefreshText(tPtr, 0, 0);
1177 } else {
1178 int n;
1179 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1180 if (str) {
1181 str[n] = 0;
1182 if (tPtr->parser) {
1183 (tPtr->parser) (tPtr, (void *) str);
1184 } else {
1185 insertTextInteractively(tPtr, str, n);
1187 WMRefreshText(tPtr, 0, 0);
1188 XFree(str);
1193 static void
1194 releaseSelection(Text *tPtr)
1196 tPtr->flags.ownsSelection = False;
1197 paintText(tPtr);
1200 static WMData *
1201 requestHandler(WMView *view, Atom selection, Atom target,
1202 void *cdata, Atom *type)
1204 Text *tPtr = view->self;
1205 int count;
1206 Display *dpy = tPtr->view->screen->display;
1207 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1208 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1210 *type = target;
1211 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT)
1212 return WMGetTextSelected(tPtr);
1213 else {
1214 WMData *data = WMCreateDataWithBytes("bleh", 4);
1215 return data;
1218 return NULL;
1222 static void
1223 lostHandler(WMView *view, Atom selection, void *cdata)
1225 releaseSelection((WMText *)view->self);
1228 static WMSelectionProcs selectionHandler = {
1229 requestHandler, lostHandler, NULL
1232 static void
1233 _notification(void *observerData, WMNotification *notification)
1235 WMText *to = (WMText *)observerData;
1236 WMText *tw = (WMText *)WMGetNotificationClientData(notification);
1237 if (to != tw)
1238 lostHandler(to->view, XA_PRIMARY, NULL);
1241 static void
1242 handleTextKeyPress(Text *tPtr, XEvent *event)
1244 char buffer[2];
1245 KeySym ksym;
1246 int control_pressed = False;
1247 // int h=1;
1249 if (((XKeyEvent *) event)->state & ControlMask)
1250 control_pressed = True;
1251 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = '\0';
1253 switch(ksym) {
1254 case XK_Right:
1255 case XK_Left:
1256 case XK_Down:
1257 case XK_Up:
1258 printf("arrows %ld\n", ksym);
1259 break;
1261 case XK_BackSpace:
1262 case XK_Delete:
1263 case XK_KP_Delete:
1264 deleteTextInteractively(tPtr, ksym);
1265 break;
1268 case XK_Return:
1269 buffer[0] = '\n';
1270 default:
1271 if (buffer[0] != '\0' && !control_pressed) {
1272 insertTextInteractively(tPtr, buffer, 1);
1273 WMRefreshText(tPtr, 0, 0);
1275 } else if (control_pressed && ksym==XK_r) {
1276 // Bool i = !tPtr->rulerShown; WMShowTextRuler(tPtr, i);
1277 // tPtr->rulerShown = i;
1278 printf("toggle ruler\n");
1280 else if (control_pressed && buffer[0] == '\a')
1281 XBell(tPtr->view->screen->display, 0);
1284 if (tPtr->flags.ownsSelection)
1285 releaseSelection(tPtr);
1288 static void
1289 handleWidgetPress(XEvent *event, void *data)
1291 TextBlock *tb = (TextBlock *)data;
1292 Text *tPtr;
1293 WMWidget *w;
1295 if (!tb)
1296 return;
1297 /* this little bit of nastiness here saves a boatload of trouble */
1298 w = (WMWidget *)(((W_VIEW(tb->d.widget))->parent)->self);
1299 if (W_CLASS(w) != WC_Text)
1300 return;
1301 tPtr = (Text*)w;
1302 printf("%p clicked on tb %p wif: (%c)%c", tPtr, tb,
1303 tPtr->firstTextBlock->text[0], tPtr->firstTextBlock->text[1]);
1304 tPtr->currentTextBlock = getFirstNonGraphicBlockFor(tb, 1);
1305 if (!tPtr->currentTextBlock)
1306 tPtr->currentTextBlock = tb;
1307 tPtr->tpos = 0;
1308 output(tPtr->currentTextBlock->text, tPtr->currentTextBlock->used);
1309 //if (!tPtr->flags.focused) {
1310 // WMSetFocusToWidget(tPtr);
1311 // tPtr->flags.focused = True;
1312 //}
1316 static void
1317 handleActionEvents(XEvent *event, void *data)
1319 Text *tPtr = (Text *)data;
1320 Display *dpy = event->xany.display;
1321 KeySym ksym;
1324 if (tPtr->flags.waitingForSelection)
1325 return;
1327 switch (event->type) {
1328 case KeyPress:
1329 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1330 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
1331 WMSetTextSelectionFont(tPtr, WMBoldSystemFontOfSize(
1332 tPtr->view->screen, 18));
1333 tPtr->flags.extendSelection = True;
1334 return;
1336 if (!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1337 XBell(dpy, 0);
1338 return;
1341 if (tPtr->flags.waitingForSelection)
1342 return;
1343 if (tPtr->flags.focused) {
1344 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1345 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1346 GrabModeAsync, GrabModeAsync, None,
1347 tPtr->view->screen->invisibleCursor, CurrentTime);
1348 tPtr->flags.pointerGrabbed = True;
1349 handleTextKeyPress(tPtr, event);
1351 } break;
1353 case KeyRelease:
1354 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1355 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
1356 tPtr->flags.extendSelection = False;
1357 return;
1358 //end modify flag so selection can be extended
1360 break;
1363 case MotionNotify:
1364 if (tPtr->flags.pointerGrabbed) {
1365 tPtr->flags.pointerGrabbed = False;
1366 XUngrabPointer(dpy, CurrentTime);
1368 if ((event->xmotion.state & Button1Mask)) {
1369 if (!tPtr->flags.ownsSelection) {
1370 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1371 event->xbutton.time, &selectionHandler, NULL);
1372 tPtr->flags.ownsSelection = True;
1374 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1376 break;
1379 case ButtonPress:
1380 tPtr->flags.buttonHeld = True;
1381 if (tPtr->flags.extendSelection) {
1382 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1383 return;
1385 if (event->xbutton.button == Button1) {
1387 if (!tPtr->flags.focused) {
1388 WMSetFocusToWidget(tPtr);
1389 tPtr->flags.focused = True;
1392 if (tPtr->flags.ownsSelection)
1393 releaseSelection(tPtr);
1394 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1395 if (tPtr->flags.pointerGrabbed) {
1396 tPtr->flags.pointerGrabbed = False;
1397 XUngrabPointer(dpy, CurrentTime);
1398 break;
1402 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown)
1403 WMScrollText(tPtr, -16);
1404 else if (event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1405 WMScrollText(tPtr, 16);
1406 break;
1408 case ButtonRelease:
1409 tPtr->flags.buttonHeld = False;
1410 if (tPtr->flags.pointerGrabbed) {
1411 tPtr->flags.pointerGrabbed = False;
1412 XUngrabPointer(dpy, CurrentTime);
1413 break;
1415 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown
1416 || event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1417 break;
1419 if (event->xbutton.button == Button2 && tPtr->flags.editable) {
1420 char *text = NULL;
1421 int n;
1422 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1423 event->xbutton.time, pasteText, NULL)) {
1424 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1425 if (text) {
1426 text[n] = 0;
1427 if (tPtr->parser) {
1428 (tPtr->parser) (tPtr, (void *) text);
1429 } else {
1430 insertTextInteractively(tPtr, text, n-1);
1432 WMRefreshText(tPtr, 0, 0);
1433 XFree(text);
1434 } else tPtr->flags.waitingForSelection = True;
1437 break;
1444 static void
1445 handleEvents(XEvent *event, void *data)
1447 Text *tPtr = (Text *)data;
1449 switch(event->type) {
1450 case Expose:
1451 if (!event->xexpose.count && tPtr->view->flags.realized)
1452 paintText(tPtr);
1453 break;
1455 case FocusIn:
1456 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))
1457 != tPtr->view)
1458 return;
1459 tPtr->flags.focused = True;
1460 if (!tPtr->timerID) {
1461 tPtr->timerID = WMAddTimerHandler(12+0*CURSOR_BLINK_ON_DELAY,
1462 blinkCursor, tPtr);
1465 break;
1467 case FocusOut:
1468 tPtr->flags.focused = False;
1469 paintText(tPtr);
1470 if (tPtr->timerID) {
1471 WMDeleteTimerHandler(tPtr->timerID);
1472 tPtr->timerID = NULL;
1474 break;
1476 case DestroyNotify:
1477 printf("destroy");
1478 //for(...)WMRemoveTextParagraph(tPtr, para);
1479 break;
1487 static void
1488 insertPlainText(WMText *tPtr, char *text)
1490 char *start, *mark;
1491 void *tb = NULL;
1494 if (!text) {
1495 clearText(tPtr);
1496 return;
1500 start = text;
1501 while(start) {
1502 mark = strchr(start, '\n');
1503 if (mark) {
1504 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1505 tPtr->dColor, True, (int)(mark-start));
1506 start = mark+1;
1507 } else {
1508 if (start && strlen(start)) {
1509 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1510 tPtr->dColor, False, strlen(start));
1511 } else tb = NULL;
1512 start = mark;
1515 if (tPtr->flags.prepend)
1516 WMPrependTextBlock(tPtr, tb);
1517 else
1518 WMAppendTextBlock(tPtr, tb);
1520 return;
1529 WMText *
1530 WMCreateText(WMWidget *parent)
1532 Text *tPtr = wmalloc(sizeof(Text));
1533 if (!tPtr) {
1534 printf("could not create text widget\n");
1535 return NULL;
1538 #if 0
1539 printf("sizeof:\n");
1540 printf(" TextBlock %d\n", sizeof(TextBlock));
1541 printf(" Section %d\n", sizeof(Section));
1542 printf(" char * %d\n", sizeof(char *));
1543 printf(" void * %d\n", sizeof(void *));
1544 printf(" short %d\n", sizeof(short));
1545 printf(" Text %d\n", sizeof(Text));
1546 #endif
1548 memset(tPtr, 0, sizeof(Text));
1549 tPtr->widgetClass = WC_Text;
1550 tPtr->view = W_CreateView(W_VIEW(parent));
1551 if (!tPtr->view) {
1552 perror("could not create text's view\n");
1553 free(tPtr);
1554 return NULL;
1556 tPtr->view->self = tPtr;
1557 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
1558 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
1559 W_ResizeView(tPtr->view, 250, 200);
1560 tPtr->bgGC = WMColorGC(tPtr->view->screen->white);
1561 tPtr->fgGC = WMColorGC(tPtr->view->screen->black);
1562 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
1564 tPtr->ruler = NULL;
1565 tPtr->vS = NULL;
1566 tPtr->hS = NULL;
1568 tPtr->dFont = NULL;
1569 //tPtr->dFont = WMCreateFont(tPtr->view->screen,
1570 // "-*-fixed-medium-r-normal--26-*-*-*-*-*-*-*");
1571 //"-sony-fixed-medium-r-normal--24-230-75-75-c-120-jisx0201.1976-0");
1572 // "-*-times-bold-r-*-*-12-*-*-*-*-*-*-*,"
1573 // "-*-fixed-medium-r-normal-*-12-*");
1574 if (!tPtr->dFont)
1575 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
1577 tPtr->dColor = WMBlackColor(tPtr->view->screen);
1579 tPtr->view->delegate = &_TextViewDelegate;
1581 tPtr->timerID = NULL;
1583 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
1584 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
1585 handleEvents, tPtr);
1587 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
1588 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
1589 handleActionEvents, tPtr);
1591 WMAddNotificationObserver(_notification, tPtr, "_lostOwnership", tPtr);
1594 tPtr->firstTextBlock = NULL;
1595 tPtr->lastTextBlock = NULL;
1596 tPtr->currentTextBlock = NULL;
1597 tPtr->tpos = 0;
1599 tPtr->gfxItems = WMCreateArrayBag(4);
1601 tPtr->parser = NULL;
1602 tPtr->writer = NULL;
1604 tPtr->sel.x = tPtr->sel.y = 2;
1605 tPtr->sel.w = tPtr->sel.h = 0;
1607 tPtr->clicked.x = tPtr->clicked.y = 2;
1609 tPtr->visible.x = tPtr->visible.y = 2;
1610 tPtr->visible.h = tPtr->view->size.height;
1611 tPtr->visible.w = tPtr->view->size.width - 12;
1613 tPtr->cursor.x = -23;
1615 tPtr->docWidth = 0;
1616 tPtr->docHeight = 0;
1617 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
1618 default_bullet);
1619 tPtr->db = (Pixmap) NULL;
1621 tPtr->margins = wmalloc(sizeof(WMRulerMargins));
1622 tPtr->margins[0].left = tPtr->margins[0].right = tPtr->visible.x;
1623 tPtr->margins[0].body = tPtr->visible.x;
1624 tPtr->margins[0].right = tPtr->visible.w;
1626 tPtr->flags.nmargins = 1;
1627 tPtr->flags.rulerShown = False;
1628 tPtr->flags.monoFont = False;
1629 tPtr->flags.focused = False;
1630 tPtr->flags.editable = True;
1631 tPtr->flags.ownsSelection = False;
1632 tPtr->flags.pointerGrabbed = False;
1633 tPtr->flags.buttonHeld = False;
1634 tPtr->flags.waitingForSelection = False;
1635 tPtr->flags.extendSelection = False;
1636 tPtr->flags.rulerShown = False;
1637 tPtr->flags.frozen = False;
1638 tPtr->flags.cursorShown = True;
1639 tPtr->flags.clickPos = 1;
1640 tPtr->flags.ignoreNewLine = False;
1641 tPtr->flags.laidOut = False;
1642 tPtr->flags.prepend = False;
1643 tPtr->flags.relief = WRFlat;
1644 tPtr->flags.alignment = WALeft;
1646 return tPtr;
1650 void
1651 WMPrependTextStream(WMText *tPtr, char *text)
1653 if (!tPtr)
1654 return;
1655 //check for "{\rtf0" in the text...
1656 //insertRTF
1657 //else
1658 tPtr->flags.prepend = True;
1659 if (text && tPtr->parser)
1660 (tPtr->parser) (tPtr, (void *) text);
1661 else
1662 insertPlainText(tPtr, text);
1666 void
1667 WMAppendTextStream(WMText *tPtr, char *text)
1669 if (!tPtr)
1670 return;
1671 //check for "{\rtf0" in the text...
1672 //insertRTF
1673 //else
1674 tPtr->flags.prepend = False;
1675 if (text && tPtr->parser)
1676 (tPtr->parser) (tPtr, (void *) text);
1677 else
1678 insertPlainText(tPtr, text);
1682 WMData *
1683 WMGetTextSelected(WMText *tPtr)
1685 WMData *data = NULL;
1686 TextBlock *tb;
1688 if (!tPtr)
1689 return NULL;
1691 //tb = tPtr->firstTextBlock;
1692 tb = tPtr->currentTextBlock;
1693 if (!tb)
1694 return NULL;
1696 data = WMCreateDataWithBytes(tb->text, tb->used);
1697 if (data)
1698 WMSetDataFormat(data, 8);
1699 return data;
1702 void *
1703 WMCreateTextBlockWithObject(WMWidget *w, char *description, WMColor *color,
1704 unsigned short first, unsigned short reserved)
1706 TextBlock *tb;
1707 unsigned short length;
1709 if (!w || !description || !color)
1710 return NULL;
1712 tb = wmalloc(sizeof(TextBlock));
1713 if (!tb)
1714 return NULL;
1716 length = strlen(description);
1717 tb->text = (char *)wmalloc(length);
1718 memset(tb->text, 0, length);
1719 memcpy(tb->text, description, length);
1720 tb->used = length;
1721 tb->blank = False;
1722 tb->d.widget = w;
1723 tb->color = WMRetainColor(color);
1724 tb->marginN = 0;
1725 tb->allocated = 0;
1726 tb->first = first;
1727 tb->kanji = False;
1728 tb->graphic = True;
1729 tb->underlined = False;
1730 tb->script = 0;
1731 tb->sections = NULL;
1732 tb->nsections = 0;
1733 tb->prior = NULL;
1734 tb->next = NULL;
1736 return tb;
1739 void *
1740 WMCreateTextBlockWithText(char *text, WMFont *font, WMColor *color,
1741 unsigned short first, unsigned short length)
1743 TextBlock *tb;
1745 if (!font || !color)
1746 return NULL;
1748 tb = wmalloc(sizeof(TextBlock));
1749 if (!tb)
1750 return NULL;
1752 tb->allocated = reqBlockSize(length);
1753 tb->text = (char *)wmalloc(tb->allocated);
1754 memset(tb->text, 0, tb->allocated);
1756 if (length < 1|| !text ) { // || *text == '\n') {
1757 *tb->text = ' ';
1758 tb->used = 1;
1759 tb->blank = True;
1760 } else {
1761 memcpy(tb->text, text, length);
1762 tb->used = length;
1763 tb->blank = False;
1766 tb->d.font = WMRetainFont(font);
1767 tb->color = WMRetainColor(color);
1768 tb->marginN = 0;
1769 tb->first = first;
1770 tb->kanji = False;
1771 tb->graphic = False;
1772 tb->underlined = False;
1773 tb->script = 0;
1774 tb->sections = NULL;
1775 tb->nsections = 0;
1776 tb->prior = NULL;
1777 tb->next = NULL;
1778 return tb;
1781 void
1782 WMSetTextBlockProperties(void *vtb, unsigned int first,
1783 unsigned int kanji, unsigned int underlined, int script,
1784 unsigned int marginN)
1786 TextBlock *tb = (TextBlock *) vtb;
1787 if (!tb)
1788 return;
1790 tb->first = first;
1791 tb->kanji = kanji;
1792 tb->underlined = underlined;
1793 tb->script = script;
1794 tb->marginN = marginN;
1797 void
1798 WMGetTextBlockProperties(void *vtb, unsigned int *first,
1799 unsigned int *kanji, unsigned int *underlined, int *script,
1800 unsigned int *marginN)
1802 TextBlock *tb = (TextBlock *) vtb;
1803 if (!tb)
1804 return;
1806 if (first) *first = tb->first;
1807 if (kanji) *kanji = tb->kanji;
1808 if (underlined) *underlined = tb->underlined;
1809 if (script) *script = tb->script;
1810 if (marginN) *marginN = tb->marginN;
1815 void
1816 WMPrependTextBlock(WMText *tPtr, void *vtb)
1818 TextBlock *tb = (TextBlock *)vtb;
1821 if (!tPtr || !tb)
1822 return;
1824 if (tb->graphic) {
1825 WMWidget *w = tb->d.widget;
1826 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
1827 handleWidgetPress, tb);
1828 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
1829 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
1830 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
1832 WMPutInBag(tPtr->gfxItems, (void *)tb);
1833 WMRealizeWidget(w);
1834 tPtr->tpos = 0;
1835 } else tPtr->tpos = tb->used;
1837 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
1838 tb->next = tb->prior = NULL;
1839 tPtr->lastTextBlock = tPtr->firstTextBlock
1840 = tPtr->currentTextBlock = tb;
1841 return;
1844 tb->next = tPtr->currentTextBlock;
1845 tb->prior = tPtr->currentTextBlock->prior;
1846 if (tPtr->currentTextBlock->prior)
1847 tPtr->currentTextBlock->prior->next = tb;
1849 tPtr->currentTextBlock->prior = tb;
1850 if (!tb->prior)
1851 tPtr->firstTextBlock = tb;
1853 tPtr->currentTextBlock = tb;
1857 void
1858 WMAppendTextBlock(WMText *tPtr, void *vtb)
1860 TextBlock *tb = (TextBlock *)vtb;
1862 if (!tPtr || !tb)
1863 return;
1865 if (tb->graphic) {
1866 WMWidget *w = tb->d.widget;
1867 return;
1868 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
1869 handleWidgetPress, tb);
1870 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
1871 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
1872 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
1874 WMPutInBag(tPtr->gfxItems, (void *)tb);
1875 WMRealizeWidget(w);
1876 tPtr->tpos = 0;
1877 } else tPtr->tpos = tb->used;
1879 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
1880 tb->next = tb->prior = NULL;
1881 tPtr->lastTextBlock = tPtr->firstTextBlock
1882 = tPtr->currentTextBlock = tb;
1883 return;
1886 tb->next = tPtr->currentTextBlock->next;
1887 tb->prior = tPtr->currentTextBlock;
1888 if (tPtr->currentTextBlock->next)
1889 tPtr->currentTextBlock->next->prior = tb;
1891 tPtr->currentTextBlock->next = tb;
1893 if (!tb->next)
1894 tPtr->lastTextBlock = tb;
1896 tPtr->currentTextBlock = tb;
1899 void *
1900 WMRemoveTextBlock(WMText *tPtr)
1902 TextBlock *tb = NULL;
1904 if (!tPtr || !tPtr->firstTextBlock || !tPtr->lastTextBlock
1905 || !tPtr->currentTextBlock) {
1906 printf("cannot remove non existent TextBlock!\b");
1907 return tb;
1910 tb = tPtr->currentTextBlock;
1911 if (tb->graphic) {
1912 WMDeleteEventHandler(W_VIEW(tb->d.widget), ButtonPressMask,
1913 handleWidgetPress, tb);
1914 WMRemoveFromBag(tPtr->gfxItems, (void *)tb);
1915 WMUnmapWidget(tb->d.widget);
1918 if (tPtr->currentTextBlock == tPtr->firstTextBlock) {
1919 if (tPtr->currentTextBlock->next)
1920 tPtr->currentTextBlock->next->prior = NULL;
1922 tPtr->firstTextBlock = tPtr->currentTextBlock->next;
1923 tPtr->currentTextBlock = tPtr->firstTextBlock;
1925 } else if (tPtr->currentTextBlock == tPtr->lastTextBlock) {
1926 tPtr->currentTextBlock->prior->next = NULL;
1927 tPtr->lastTextBlock = tPtr->currentTextBlock->prior;
1928 tPtr->currentTextBlock = tPtr->lastTextBlock;
1929 } else {
1930 tPtr->currentTextBlock->prior->next = tPtr->currentTextBlock->next;
1931 tPtr->currentTextBlock->next->prior = tPtr->currentTextBlock->prior;
1932 tPtr->currentTextBlock = tPtr->currentTextBlock->next;
1935 return (void *)tb;
1938 void
1939 WMDestroyTextBlock(WMText *tPtr, void *vtb)
1941 TextBlock *tb = (TextBlock *)vtb;
1942 if (!tPtr || !tb)
1943 return;
1945 if (tb->graphic) {
1946 /* naturally, there's a danger to destroying
1947 widgets whose action brings us here:
1948 ie. press a button to destroy it... need to
1949 find a safer way. till then... this stays commented out */
1950 //WMDestroyWidget(tb->d.widget);
1951 //wfree(tb->d.widget);
1952 tb->d.widget = NULL;
1953 } else {
1954 WMReleaseFont(tb->d.font);
1957 WMReleaseColor(tb->color);
1958 if (tb->sections && tb->nsections > 0)
1959 wfree(tb->sections);
1960 wfree(tb->text);
1961 wfree(tb);
1965 void
1966 WMRefreshText(WMText *tPtr, int vpos, int hpos)
1968 //TextBlock *tb;
1970 if (!tPtr || vpos<0 || hpos<0)
1971 return;
1973 tPtr->flags.laidOut = False;
1974 layOutDocument(tPtr);
1975 updateScrollers(tPtr);
1976 paintText(tPtr);
1981 void
1982 WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
1984 if (!tPtr)
1985 return;
1987 if (color)
1988 tPtr->fgGC = WMColorGC(color);
1989 else
1990 tPtr->fgGC = WMColorGC(WMBlackColor(tPtr->view->screen));
1992 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1995 void
1996 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
1998 if (!tPtr)
1999 return;
2001 if (color) {
2002 tPtr->bgGC = WMColorGC(color);
2003 W_SetViewBackgroundColor(tPtr->view, color);
2004 } else {
2005 tPtr->bgGC = WMColorGC(WMWhiteColor(tPtr->view->screen));
2006 W_SetViewBackgroundColor(tPtr->view,
2007 WMWhiteColor(tPtr->view->screen));
2010 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2013 void
2014 WMSetTextRelief(WMText *tPtr, WMReliefType relief)
2016 if (!tPtr)
2017 return;
2018 tPtr->flags.relief = relief;
2019 paintText(tPtr);
2022 void
2023 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
2025 if (!tPtr)
2026 return;
2028 if (shouldhave && !tPtr->hS) {
2029 tPtr->hS = WMCreateScroller(tPtr);
2030 (W_VIEW(tPtr->hS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2031 (W_VIEW(tPtr->hS))->attribFlags |= CWOverrideRedirect | CWCursor;
2032 WMSetScrollerArrowsPosition(tPtr->hS, WSAMaxEnd);
2033 WMSetScrollerAction(tPtr->hS, scrollersCallBack, tPtr);
2034 WMRealizeWidget(tPtr->hS);
2035 WMMapWidget(tPtr->hS);
2036 } else if (!shouldhave && tPtr->hS) {
2037 WMUnmapWidget(tPtr->hS);
2038 WMDestroyWidget(tPtr->hS);
2039 tPtr->hS = NULL;
2042 tPtr->hpos = 0;
2043 tPtr->prevHpos = 0;
2044 textDidResize(tPtr->view->delegate, tPtr->view);
2048 void
2049 WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
2051 if (!tPtr)
2052 return;
2054 if (shouldhave && !tPtr->vS) {
2055 tPtr->vS = WMCreateScroller(tPtr);
2056 (W_VIEW(tPtr->vS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2057 (W_VIEW(tPtr->vS))->attribFlags |= CWOverrideRedirect | CWCursor;
2058 WMSetScrollerArrowsPosition(tPtr->vS, WSAMaxEnd);
2059 WMSetScrollerAction(tPtr->vS, scrollersCallBack, tPtr);
2060 WMRealizeWidget(tPtr->vS);
2061 WMMapWidget(tPtr->vS);
2062 } else if (!shouldhave && tPtr->vS) {
2063 WMUnmapWidget(tPtr->vS);
2064 WMDestroyWidget(tPtr->vS);
2065 tPtr->vS = NULL;
2068 tPtr->vpos = 0;
2069 tPtr->prevVpos = 0;
2070 textDidResize(tPtr->view->delegate, tPtr->view);
2075 Bool
2076 WMScrollText(WMText *tPtr, int amount)
2078 Bool scroll=False;
2079 if (!tPtr)
2080 return False;
2081 if (amount == 0 || !tPtr->view->flags.realized)
2082 return False;
2084 if (amount < 0) {
2085 if (tPtr->vpos > 0) {
2086 if (tPtr->vpos > amount) tPtr->vpos += amount;
2087 else tPtr->vpos=0;
2088 scroll=True;
2089 } } else {
2090 int limit = tPtr->docHeight - tPtr->visible.h;
2091 if (tPtr->vpos < limit) {
2092 if (tPtr->vpos < limit-amount) tPtr->vpos += amount;
2093 else tPtr->vpos = limit;
2094 scroll = True;
2095 } }
2097 if (scroll && tPtr->vpos != tPtr->prevVpos) {
2098 updateScrollers(tPtr);
2099 paintText(tPtr);
2101 tPtr->prevVpos = tPtr->vpos;
2102 return scroll;
2105 Bool
2106 WMPageText(WMText *tPtr, Bool direction)
2108 if (!tPtr) return False;
2109 if (!tPtr->view->flags.realized) return False;
2111 return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
2115 void
2116 WMSetTextUseMonoFont(WMText *tPtr, Bool mono)
2118 if (!tPtr)
2119 return;
2120 if (mono && tPtr->flags.rulerShown)
2121 ;//WMShowTextRuler(tPtr, False);
2123 tPtr->flags.monoFont = mono;
2124 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2127 Bool
2128 WMGetTextUsesMonoFont(WMText *tPtr)
2130 if (!tPtr)
2131 return True;
2132 return tPtr->flags.monoFont;
2135 void
2136 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
2138 if (!tPtr)
2139 return;
2141 if (font)
2142 tPtr->dFont = font;
2143 else
2144 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2147 WMFont *
2148 WMGetTextDefaultFont(WMText *tPtr)
2150 if (!tPtr)
2151 return NULL;
2152 else
2153 return tPtr->dFont;
2156 void
2157 WMSetTextParser(WMText *tPtr, WMAction *parser)
2159 if (!tPtr)
2160 return;
2161 tPtr->parser = parser;
2165 void
2166 WMSetTextWriter(WMText *tPtr, WMAction *writer)
2168 if (!tPtr)
2169 return;
2170 tPtr->writer = writer;
2173 int
2174 WMGetTextInsertType(WMText *tPtr)
2176 if (!tPtr)
2177 return 0;
2178 return tPtr->flags.prepend;
2182 void
2183 WMSetTextSelectionColor(WMText *tPtr, WMColor *color)
2185 TextBlock *tb;
2186 if (!tPtr || !color)
2187 return;
2189 tb = tPtr->firstTextBlock;
2190 if (!tb || !tPtr->flags.ownsSelection)
2191 return;
2193 while(tb) {
2194 tb->color = WMRetainColor(color);
2195 tb = tb->next;
2197 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2200 void
2201 WMSetTextSelectionFont(WMText *tPtr, WMFont *font)
2203 TextBlock *tb;
2204 if (!tPtr || !font)
2205 return;
2207 tb = tPtr->firstTextBlock;
2208 if (!tb || !tPtr->flags.ownsSelection)
2209 return;
2211 while(tb) {
2212 if (!tb->graphic)
2213 tb->d.font = WMRetainFont(font);
2214 tb = tb->next;
2216 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);