Removed array bag, and restructured the tree bag to be WMBag
[wmaker-crm.git] / WINGs / wtext.c
blobd0675d437bc71bcd5e320e83e674b1b6698ceb50
1 /*
2 * WINGs WMText: multi-line/font/color/graphic text widget
4 * Copyright (c) 1999-2000 Nwanua Elumeze <nwanua@windowmaker.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "WINGsP.h"
22 #include <X11/keysym.h>
23 #include <X11/Xatom.h>
25 #define DO_BLINK 0
27 /* TODO:
29 * - assess danger of destroying widgets whose actions link to other pages
30 * - change cursor shape around pixmaps
31 * - redo blink code to reduce paint event... use pixmap buffer...
32 * - confirm with Alfredo et. all about field marker 0xFA
33 * - add paragraph support (full) and '\n' code in getStream..
34 * - use currentTextBlock and neighbours for fast paint and layout
35 * - replace copious uses of Refreshtext with appropriate layOut()...
36 * - WMFindInTextStream should also highlight found text...
37 * - add full support for Horizontal Scroll
41 /* a Section is a section of a TextBlock that describes what parts
42 of a TextBlock has been laid out on which "line"...
43 o this greatly aids redraw, scroll and selection.
44 o this is created during layoutLine, but may be later modified.
45 o there may be many Sections per TextBlock, hence the array */
46 typedef struct {
47 unsigned int x, y; /* where to draw it from */
48 unsigned short w, h; /* its width and height */
49 unsigned short begin; /* where the layout begins */
50 unsigned short end ; /* where it ends */
51 unsigned short last:1; /* is it the last section on a "line"? */
52 unsigned int _y:31; /* the "line" it and other textblocks are on */
53 } Section;
56 /* a TextBlock is a doubly-linked list of TextBlocks containing:
57 o text for the block, color and font
58 o or a pointer to the pixmap
59 o OR a pointer to the widget and the (text) description for its graphic
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 text (could be kanji) */
67 /* or to the object's description */
68 union {
69 WMFont *font; /* the font */
70 WMWidget *widget; /* the embedded widget */
71 WMPixmap *pixmap; /* the pixmap */
72 } d; /* description */
74 unsigned short used; /* number of chars in this block */
75 unsigned short allocated; /* size of allocation (in chars) */
76 WMColor *color; /* the color */
77 // WMRulerMargins margins; /* first & body indentations, tabstops, etc... */
79 Section *sections; /* the region for layouts (a growable array) */
80 /* an _array_! of size _nsections_ */
82 unsigned short s_begin; /* where the selection begins */
83 unsigned short s_end; /* where it ends */
85 unsigned int first:1; /* first TextBlock in paragraph */
86 unsigned int blank:1; /* ie. blank paragraph */
87 unsigned int kanji:1; /* is of 16-bit characters or not */
88 unsigned int graphic:1; /* graphic or text: text=0 */
89 unsigned int object:1; /* embedded object or pixmap */
90 unsigned int underlined:1; /* underlined or not */
91 unsigned int selected:1; /* selected or not */
92 unsigned int nsections:8; /* over how many "lines" a TextBlock wraps */
93 int script:8; /* script in points: negative for subscript */
94 unsigned int RESERVED:9;
95 } TextBlock;
98 /* somehow visible.h beats the hell outta visible.size.height :-) */
99 typedef struct {
100 unsigned int y;
101 unsigned int x;
102 unsigned int h;
103 unsigned int w;
104 } myRect;
107 typedef struct W_Text {
108 W_Class widgetClass; /* the class number of this widget */
109 W_View *view; /* the view referring to this instance */
111 WMRuler *ruler; /* the ruler widget to manipulate paragraphs */
113 WMScroller *vS; /* the vertical scroller */
114 unsigned int vpos; /* the current vertical position */
115 unsigned int prevVpos; /* the previous vertical position */
117 WMScroller *hS; /* the horizontal scroller */
118 unsigned short hpos; /* the current horizontal position */
119 unsigned short prevHpos; /* the previous horizontal position */
121 WMFont *dFont; /* the default font */
122 WMColor *dColor; /* the default color */
123 WMPixmap *dBulletPix; /* the default pixmap for bullets */
124 WMRulerMargins dmargins; /* default margins */
126 GC bgGC; /* the background GC to draw with */
127 GC fgGC; /* the foreground GC to draw with */
128 Pixmap db; /* the buffer on which to draw */
130 myRect visible; /* the actual rectangle that can be drawn into */
131 myRect cursor; /* the position and (height) of cursor */
132 myRect sel; /* the selection rectangle */
134 WMPoint clicked; /* where in the _document_ was clicked */
136 unsigned short tpos; /* the position in the currentTextBlock */
137 unsigned short docWidth; /* the width of the entire document */
138 unsigned int docHeight; /* the height of the entire document */
140 TextBlock *firstTextBlock;
141 TextBlock *lastTextBlock;
142 TextBlock *currentTextBlock;
144 WMBag *gfxItems; /* a nice bag containing graphic items */
146 #if DO_BLINK
147 WMHandlerID timerID; /* for nice twinky-winky */
148 #endif
150 WMAction *parser;
151 WMAction *writer;
153 struct {
154 unsigned int monoFont:1; /* whether to ignore formats */
155 unsigned int focused:1; /* whether this instance has input focus */
156 unsigned int editable:1; /* "silly user, you can't edit me" */
157 unsigned int ownsSelection:1; /* "I ownz the current selection!" */
158 unsigned int pointerGrabbed:1;/* "heh, gib me pointer" */
159 unsigned int buttonHeld:1; /* the user is holding down the button */
160 unsigned int waitingForSelection:1; /* dum dee dumm... */
161 unsigned int extendSelection:1; /* shift-drag to select more regions */
163 unsigned int rulerShown:1; /* whether the ruler is shown or not */
164 unsigned int frozen:1; /* whether screen updates are to be made */
165 unsigned int cursorShown:1; /* whether to show the cursor */
166 unsigned int clickPos:1; /* clicked before=0 or after=1 a graphic: */
167 /* (within counts as after too) */
169 unsigned int ignoreNewLine:1;/* turn it into a ' ' in streams > 1 */
170 unsigned int laidOut:1; /* have the TextBlocks all been laid out */
171 unsigned int prepend:1; /* prepend=1, append=0 (for parsers) */
172 WMAlignment alignment:2; /* the alignment for text */
173 WMReliefType relief:3; /* the relief to display with */
174 unsigned int RESERVED:12;
175 } flags;
176 } Text;
179 static char *default_bullet[] = {
180 "6 6 4 1",
181 " c None s None", ". c black",
182 "X c white", "o c #808080",
183 " ... ",
184 ".XX.. ",
185 ".XX..o",
186 ".....o",
187 " ...oo",
188 " ooo "};
191 static Bool
192 sectionWasSelected(Text *tPtr, TextBlock *tb, XRectangle *rect, int s)
194 unsigned short i, w, lw, selected = False, extend = False;
195 myRect sel;
198 /* if selection rectangle completely encloses the section */
199 if ((tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
200 && (tb->sections[s]._y + tb->sections[s].h
201 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
202 sel.x = 0;
203 sel.w = tPtr->visible.w;
204 selected = extend = True;
206 /* or if it starts on a line and then goes further down */
207 } else if ((tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
208 && (tb->sections[s]._y + tb->sections[s].h
209 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)
210 && (tb->sections[s]._y + tb->sections[s].h
211 >= tPtr->visible.y + tPtr->sel.y) ) {
212 sel.x = WMAX(tPtr->sel.x, tPtr->clicked.x);
213 sel.w = tPtr->visible.w;
214 selected = extend = True;
216 /* or if it begins before a line, but ends on it */
217 } else if ((tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
218 && (tb->sections[s]._y + tb->sections[s].h
219 >= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)
220 && (tb->sections[s]._y
221 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
223 if (1||tPtr->sel.x + tPtr->sel.w > tPtr->clicked.x)
224 sel.w = tPtr->sel.x + tPtr->sel.w;
225 else
226 sel.w = tPtr->sel.x;
228 sel.x = 0;
229 selected = True;
231 /* or if the selection rectangle lies entirely within a line */
232 } else if ((tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
233 && (tPtr->sel.w >= 2)
234 && (tb->sections[s]._y + tb->sections[s].h
235 >= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
236 sel.x = tPtr->sel.x;
237 sel.w = tPtr->sel.w;
238 selected = True;
241 if (selected) {
242 selected = False;
244 /* if not within (modified) selection rectangle */
245 if ( tb->sections[s].x > sel.x + sel.w
246 || tb->sections[s].x + tb->sections[s].w < sel.x)
247 return False;
249 if (tb->graphic) {
250 if ( tb->sections[s].x + tb->sections[s].w <= sel.x + sel.w
251 && tb->sections[s].x >= sel.x) {
252 rect->width = tb->sections[s].w;
253 rect->x = tb->sections[s].x;
254 selected = True;
256 } else {
258 i = tb->sections[s].begin;
259 lw = 0;
261 if (0&& tb->sections[s].x >= sel.x) {
262 tb->s_begin = tb->sections[s].begin;
263 goto _selEnd;
266 while (++i <= tb->sections[s].end) {
268 w = WMWidthOfString(tb->d.font, &(tb->text[i-1]), 1);
269 lw += w;
271 if (lw + tb->sections[s].x >= sel.x
272 || i == tb->sections[s].end ) {
273 lw -= w;
274 i--;
275 tb->s_begin = (tb->selected? WMIN(tb->s_begin, i) : i);
276 break;
280 if (i > tb->sections[s].end) {
281 printf("WasSelected: (i > tb->sections[s].end) \n");
282 return False;
285 _selEnd: rect->x = tb->sections[s].x + lw;
286 lw = 0;
287 while(++i <= tb->sections[s].end) {
289 w = WMWidthOfString(tb->d.font, &(tb->text[i-1]), 1);
290 lw += w;
292 if (lw + rect->x >= sel.x + sel.w
293 || i == tb->sections[s].end ) {
295 if (i != tb->sections[s].end) {
296 lw -= w;
297 i--;
300 rect->width = lw;
301 if (tb->sections[s].last && sel.x + sel.w
302 >= tb->sections[s].x + tb->sections[s].w
303 && extend ) {
304 rect->width += (tPtr->visible.w - rect->x - lw);
307 tb->s_end = (tb->selected? WMAX(tb->s_end, i) : i);
308 selected = True;
309 break;
310 } } } }
312 if (selected) {
313 rect->y = tb->sections[s]._y - tPtr->vpos;
314 rect->height = tb->sections[s].h;
315 if(tb->graphic) { printf("graphic s%d h%d\n", s,tb->sections[s].h);}
317 return selected;
321 static void
322 removeSelection(Text *tPtr)
324 TextBlock *tb = NULL;
326 if (!(tb = tPtr->firstTextBlock))
327 return;
329 while (tb) {
330 if (tb->selected) {
332 if ( (tb->s_end - tb->s_begin == tb->used) || tb->graphic) {
333 tPtr->currentTextBlock = tb;
334 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
335 tb = tPtr->currentTextBlock;
336 if (tb)
337 tPtr->tpos = 0;
338 continue;
340 } else if (tb->s_end <= tb->used) {
341 memmove(&(tb->text[tb->s_begin]),
342 &(tb->text[tb->s_end]), tb->used - tb->s_end);
343 tb->used -= (tb->s_end - tb->s_begin);
344 tb->selected = False;
345 tPtr->tpos = tb->s_begin;
350 tb = tb->next;
355 static void
356 paintText(Text *tPtr)
358 TextBlock *tb = tPtr->firstTextBlock;
359 WMFont *font;
360 GC gc, greyGC;
361 char *text;
362 int len, y, c, s, done=False;
363 int prev_y=-23;
364 WMScreen *scr = tPtr->view->screen;
365 Display *dpy = tPtr->view->screen->display;
366 Window win = tPtr->view->window;
368 if (!tPtr->view->flags.realized || !tPtr->db || tPtr->flags.frozen)
369 return;
371 XFillRectangle(dpy, tPtr->db, tPtr->bgGC,
372 0, 0, WMWidgetWidth(tPtr), WMWidgetWidth(tPtr));
373 //tPtr->visible.w, tPtr->visible.h);
375 tb = tPtr->firstTextBlock;
376 if (!tb)
377 goto _copy_area;
380 if (tPtr->flags.ownsSelection)
381 greyGC = WMColorGC(WMGrayColor(scr));
383 done = False;
385 while (!done && tb) {
387 if (tb->graphic) {
388 tb = tb->next;
389 continue;
392 tb->selected = False;
394 for(s=0; s<tb->nsections && !done; s++) {
396 if (tb->sections[s]._y > tPtr->vpos + tPtr->visible.h) {
397 done = True;
398 break;
401 if ( tb->sections[s].y + tb->sections[s].h < tPtr->vpos)
402 continue;
404 if (tPtr->flags.monoFont) {
405 font = tPtr->dFont;
406 gc = tPtr->fgGC;
407 } else {
408 font = tb->d.font;
409 gc = WMColorGC(tb->color);
412 if (tPtr->flags.ownsSelection) {
413 XRectangle rect;
415 if ( sectionWasSelected(tPtr, tb, &rect, s)) {
416 tb->selected = True;
417 XFillRectangle(dpy, tPtr->db, greyGC,
418 rect.x, rect.y, rect.width, rect.height);
422 prev_y = tb->sections[s]._y;
424 len = tb->sections[s].end - tb->sections[s].begin;
425 text = &(tb->text[tb->sections[s].begin]);
426 y = tb->sections[s].y - tPtr->vpos;
427 WMDrawString(scr, tPtr->db, gc, font,
428 tb->sections[s].x, y, text, len);
430 if (tb->underlined) {
431 XDrawLine(dpy, tPtr->db, gc,
432 tb->sections[s].x, y + font->y + 1,
433 tb->sections[s].x + tb->sections[s].w, y + font->y + 1);
438 tb = (!done? tb->next : NULL);
442 c = WMGetBagItemCount(tPtr->gfxItems);
443 if (c > 0 && !tPtr->flags.monoFont) {
444 int j, h;
446 for(j=0; j<c; j++) {
447 tb = (TextBlock *) WMGetFromBag(tPtr->gfxItems, j);
448 if (tb->sections[0]._y + tb->sections[0].h <= tPtr->vpos
449 || tb->sections[0]._y >= tPtr->vpos + tPtr->visible.h ) {
451 if(tb->object) {
452 if ((W_VIEW(tb->d.widget))->flags.mapped) {
453 WMUnmapWidget(tb->d.widget);
456 } else {
457 if(tb->object) {
458 if (!(W_VIEW(tb->d.widget))->flags.mapped) {
459 if (!(W_VIEW(tb->d.widget))->flags.realized)
460 WMRealizeWidget(tb->d.widget);
461 WMMapWidget(tb->d.widget);
462 WMLowerWidget(tb->d.widget);
466 if (tPtr->flags.ownsSelection) {
467 XRectangle rect;
469 if ( sectionWasSelected(tPtr, tb, &rect, s)) {
470 tb->selected = True;
471 XFillRectangle(dpy, tPtr->db, greyGC,
472 rect.x, rect.y, rect.width, rect.height);
476 if(tb->object) {
477 WMMoveWidget(tb->d.widget,
478 tb->sections[0].x,
479 tb->sections[0].y - tPtr->vpos);
480 h = WMWidgetHeight(tb->d.widget) + 1;
482 } else {
483 WMDrawPixmap(tb->d.pixmap, tPtr->db,
484 tb->sections[0].x,
485 tb->sections[0].y - tPtr->vpos);
486 h = tb->d.pixmap->height + 1;
489 if (tb->underlined) {
490 XDrawLine(dpy, tPtr->db, WMColorGC(tb->color),
491 tb->sections[0].x,
492 tb->sections[0].y + h,
493 tb->sections[0].x + tb->sections[0].w,
494 tb->sections[0].y + h);
495 } } } }
498 _copy_area:
499 if (tPtr->flags.editable && tPtr->flags.cursorShown
500 && tPtr->cursor.x != -23 && tPtr->flags.focused) {
501 int y = tPtr->cursor.y - tPtr->vpos;
502 XDrawLine(dpy, tPtr->db, tPtr->fgGC,
503 tPtr->cursor.x, y,
504 tPtr->cursor.x, y + tPtr->cursor.h);
507 XCopyArea(dpy, tPtr->db, win, tPtr->bgGC,
508 0, 0,
509 tPtr->visible.w, tPtr->visible.h,
510 tPtr->visible.x, tPtr->visible.y);
512 W_DrawRelief(scr, win, 0, 0,
513 tPtr->view->size.width, tPtr->view->size.height,
514 tPtr->flags.relief);
516 if (tPtr->ruler && tPtr->flags.rulerShown)
517 XDrawLine(dpy, win,
518 tPtr->fgGC, 2, 42,
519 tPtr->view->size.width-4, 42);
524 #if DO_BLINK
526 #define CURSOR_BLINK_ON_DELAY 600
527 #define CURSOR_BLINK_OFF_DELAY 400
529 static void
530 blinkCursor(void *data)
532 Text *tPtr = (Text*)data;
534 if (tPtr->flags.cursorShown) {
535 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY,
536 blinkCursor, data);
537 } else {
538 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
539 blinkCursor, data);
541 paintText(tPtr);
542 tPtr->flags.cursorShown = !tPtr->flags.cursorShown;
544 #endif
546 static TextBlock *
547 getFirstNonGraphicBlockFor(TextBlock *tb, short dir)
549 if (!tb)
550 return NULL;
551 while (tb) {
552 if (!tb->graphic)
553 break;
554 tb = (dir? tb->next : tb->prior);
557 return tb;
561 static void
562 cursorToTextPosition(Text *tPtr, int x, int y)
564 TextBlock *tb = NULL;
565 int done=False, s, pos, len, _w, _y, dir=1; /* 1 == "down" */
566 char *text;
568 y += (tPtr->vpos - tPtr->visible.y);
569 if (y<0)
570 y = 0;
572 x -= (tPtr->visible.x - 2);
573 if (x<0)
574 x=0;
576 /* clicked is relative to document, not window... */
577 tPtr->clicked.x = x;
578 tPtr->clicked.y = y;
580 if (! (tb = tPtr->currentTextBlock)) {
581 if (! (tb = tPtr->firstTextBlock)) {
582 tPtr->tpos = 0;
583 tPtr->cursor.h = tPtr->dFont->height;
584 tPtr->cursor.y = 2;
585 tPtr->cursor.x = 2;
586 return;
590 /* first, which direction? Most likely, newly clicked
591 position will be close to previous */
592 dir = !(y <= tb->sections[0].y);
593 if ( ( y <= tb->sections[0]._y + tb->sections[0].h )
594 && (y >= tb->sections[0]._y ) ) {
595 /* if it's on the same line */
596 if(x < tb->sections[0].x)
597 dir = 0;
598 if(x >= tb->sections[0].x)
599 dir = 1;
602 tb = tPtr->firstTextBlock;
603 dir = 1;
605 if (tPtr->flags.monoFont && tb->graphic) {
606 tb = getFirstNonGraphicBlockFor(tb, 1);
607 if (!tb) {
608 tPtr->currentTextBlock =
609 (dir? tPtr->lastTextBlock : tPtr->firstTextBlock);
610 tPtr->tpos = 0;
611 return;
615 s = (dir? 0 : tb->nsections-1);
616 if ( y >= tb->sections[s]._y
617 && y <= tb->sections[s]._y + tb->sections[s].h) {
618 goto _doneV;
621 /* get the first section of the TextBlock that lies about
622 the vertical click point */
623 done = False;
624 while (!done && tb) {
626 if (tPtr->flags.monoFont && tb->graphic) {
627 if(tb->next)
628 tb = tb->next;
629 continue;
632 s = (dir? 0 : tb->nsections-1);
633 while (!done && (dir? (s<tb->nsections) : (s>=0) )) {
635 if ( (dir? (y <= tb->sections[s]._y + tb->sections[s].h) :
636 ( y >= tb->sections[s]._y ) ) ) {
637 done = True;
638 } else {
639 dir? s++ : s--;
643 if (!done) {
644 if ( (dir? tb->next : tb->prior)) {
645 tb = (dir ? tb->next : tb->prior);
646 } else {
647 pos = tb->used;
648 break; //goto _doneH;
654 if (s<0 || s>=tb->nsections) {
655 s = (dir? tb->nsections-1 : 0);
658 _doneV:
659 /* we have the line, which TextBlock on that line is it? */
660 pos = 0;
661 if (tPtr->flags.monoFont && tb->graphic)
662 tb = getFirstNonGraphicBlockFor(tb, dir);
663 if (tb) {
664 if ((dir? tb->sections[s].x >= x : tb->sections[s].x < x))
665 goto _doneH;
667 #if 0
668 if(tb->blank) {
669 _w = 0;
670 printf("blank\n");
671 } else {
672 text = &(tb->text[tb->sections[s].begin]);
673 len = tb->sections[s].end - tb->sections[s].begin;
674 _w = WMWidthOfString(tb->d.font, text, len);
676 printf("here %d %d \n", tb->sections[s].x + _w, x);
677 if ((dir? tb->sections[s].x + _w < x : tb->sections[s].x + _w >= x)) {
678 pos = tb->sections[s].end;
679 tPtr->cursor.x = tb->sections[s].x + _w;
680 goto _doneH;
682 #endif
683 _y = tb->sections[s]._y;
686 while (tb) {
688 if (tPtr->flags.monoFont && tb->graphic) {
689 tb = (dir ? tb->next : tb->prior);
690 continue;
693 if (dir) {
694 if (tb->graphic) {
695 if(tb->object)
696 _w = WMWidgetWidth(tb->d.widget);
697 else
698 _w = tb->d.pixmap->width;
699 } else {
700 text = &(tb->text[tb->sections[s].begin]);
701 len = tb->sections[s].end - tb->sections[s].begin;
702 _w = WMWidthOfString(tb->d.font, text, len);
703 if (tb->sections[s].x + _w >= x)
704 break;
707 } else {
708 if (tb->sections[s].x <= x)
709 break;
712 if ((dir? tb->next : tb->prior)) {
713 TextBlock *nxt = (dir? tb->next : tb->prior);
714 if (tPtr->flags.monoFont && nxt->graphic) {
715 nxt = getFirstNonGraphicBlockFor(nxt, dir);
716 if (!nxt) {
717 pos = 0;
718 tPtr->cursor.x = tb->sections[s].x;
719 goto _doneH;
723 if (_y != nxt->sections[0]._y) {
724 /* this must be the last/first on this line. stop */
725 pos = (dir? tb->sections[s].end : 0);
726 tPtr->cursor.x = tb->sections[s].x;
727 if (!tb->blank) {
728 if (tb->graphic) {
729 if(tb->object)
730 tPtr->cursor.x += WMWidgetWidth(tb->d.widget);
731 else
732 tPtr->cursor.x += tb->d.pixmap->width;
733 } else if (pos > tb->sections[s].begin) {
734 tPtr->cursor.x +=
735 WMWidthOfString(tb->d.font,
736 &(tb->text[tb->sections[s].begin]),
737 pos - tb->sections[s].begin);
740 goto _doneH;
744 if ( (dir? tb->next : tb->prior)) {
745 tb = (dir ? tb->next : tb->prior);
746 } else {
747 done = True;
748 break;
751 if (tb)
752 s = (dir? 0 : tb->nsections-1);
755 /* we have said TextBlock, now where within it? */
756 if (tb && !tb->graphic) {
757 WMFont *f = tb->d.font;
758 len = tb->sections[s].end - tb->sections[s].begin;
759 text = &(tb->text[tb->sections[s].begin]);
761 _w = x - tb->sections[s].x;
762 pos = 0;
764 while (pos<len && WMWidthOfString(f, text, pos+1) < _w)
765 pos++;
767 tPtr->cursor.x = tb->sections[s].x +
768 (pos? WMWidthOfString(f, text, pos) : 0);
770 pos += tb->sections[s].begin;
771 _doneH:
772 tPtr->tpos = (pos<tb->used)? pos : tb->used;
775 tPtr->currentTextBlock = tb;
776 tPtr->cursor.h = tb->sections[s].h;
777 tPtr->cursor.y = tb->sections[s]._y;
779 if (!tb)
780 printf("will hang :-)\n");
784 static void
785 updateScrollers(Text *tPtr)
788 if (tPtr->flags.frozen)
789 return;
791 if (tPtr->vS) {
792 if (tPtr->docHeight < tPtr->visible.h) {
793 WMSetScrollerParameters(tPtr->vS, 0, 1);
794 tPtr->vpos = 0;
795 } else {
796 float vmax = (float)(tPtr->docHeight);
797 WMSetScrollerParameters(tPtr->vS,
798 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
799 (float)tPtr->visible.h/vmax);
801 } else tPtr->vpos = 0;
803 if (tPtr->hS)
807 static void
808 scrollersCallBack(WMWidget *w, void *self)
810 Text *tPtr = (Text *)self;
811 Bool scroll = False;
812 Bool dimple = False;
813 int which;
815 if (!tPtr->view->flags.realized || tPtr->flags.frozen)
816 return;
818 if (w == tPtr->vS) {
819 float vmax;
820 int height;
821 vmax = (float)(tPtr->docHeight);
822 height = tPtr->visible.h;
824 which = WMGetScrollerHitPart(tPtr->vS);
825 switch(which) {
826 case WSDecrementLine:
827 if (tPtr->vpos > 0) {
828 if (tPtr->vpos>16) tPtr->vpos-=16;
829 else tPtr->vpos=0;
830 scroll=True;
831 }break;
832 case WSIncrementLine: {
833 int limit = tPtr->docHeight - height;
834 if (tPtr->vpos < limit) {
835 if (tPtr->vpos<limit-16) tPtr->vpos+=16;
836 else tPtr->vpos=limit;
837 scroll = True;
838 }}break;
839 case WSDecrementPage:
840 tPtr->vpos -= height;
842 if (tPtr->vpos < 0)
843 tPtr->vpos = 0;
844 dimple = True;
845 scroll = True;
846 printf("dimple needs to jump to mouse location ;-/\n");
847 break;
848 case WSIncrementPage:
849 tPtr->vpos += height;
850 if (tPtr->vpos > (tPtr->docHeight - height))
851 tPtr->vpos = tPtr->docHeight - height;
852 dimple = True;
853 scroll = True;
854 printf("dimple needs to jump to mouse location ;-/\n");
855 break;
858 case WSKnob:
859 tPtr->vpos = WMGetScrollerValue(tPtr->vS)
860 * (float)(tPtr->docHeight - height);
861 scroll = True;
862 break;
864 case WSKnobSlot:
865 case WSNoPart:
866 printf("WSNoPart, WSKnobSlot\n");
867 #if 0
868 float vmax = (float)(tPtr->docHeight);
869 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
870 (float)tPtr->visible.h/vmax;
871 dimple =where mouse is.
872 #endif
873 break;
875 scroll = (tPtr->vpos != tPtr->prevVpos);
876 tPtr->prevVpos = tPtr->vpos;
879 if (w == tPtr->hS)
882 if (scroll) {
884 if (0&&dimple) {
885 if (tPtr->rulerShown)
886 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
887 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
888 else
889 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
890 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
893 if (dimple || which == WSDecrementLine || which == WSIncrementLine)
894 updateScrollers(tPtr);
895 paintText(tPtr);
901 typedef struct {
902 TextBlock *tb;
903 unsigned short begin, end; /* what part of the text block */
904 } myLineItems;
907 static int
908 layOutLine(Text *tPtr, myLineItems *items, int nitems, int x, int y)
910 int i, j=0, lw = 0, line_height=0, max_d=0, len, n;
911 WMFont *font;
912 char *text;
913 TextBlock *tb;
914 TextBlock *tbsame=NULL;
916 for(i=0; i<nitems; i++) {
917 tb = items[i].tb;
919 if (tb->graphic) {
920 if (!tPtr->flags.monoFont) {
921 if(tb->object) {
922 WMWidget *wdt = tb->d.widget;
923 line_height = WMAX(line_height, WMWidgetHeight(wdt));
924 if (tPtr->flags.alignment != WALeft)
925 lw += WMWidgetWidth(wdt);
926 } else {
927 line_height = WMAX(line_height, tb->d.pixmap->height + max_d);
928 if (tPtr->flags.alignment != WALeft)
929 lw += tb->d.pixmap->width;
933 } else {
934 font = (tPtr->flags.monoFont)?tPtr->dFont : tb->d.font;
935 max_d = WMAX(max_d, abs(font->height-font->y));
936 line_height = WMAX(line_height, font->height + max_d);
937 text = &(tb->text[items[i].begin]);
938 len = items[i].end - items[i].begin;
939 if (tPtr->flags.alignment != WALeft)
940 lw += WMWidthOfString(font, text, len);
944 if (tPtr->flags.alignment == WARight) {
945 j = tPtr->visible.w - lw;
946 } else if (tPtr->flags.alignment == WACenter) {
947 j = (int) ((float)(tPtr->visible.w - lw))/2.0;
950 for(i=0; i<nitems; i++) {
951 tb = items[i].tb;
953 if (tbsame == tb) { /*extend it, since it's on same line */
954 tb->sections[tb->nsections-1].end = items[i].end;
955 n = tb->nsections-1;
956 } else {
957 tb->sections = wrealloc(tb->sections,
958 (++tb->nsections)*sizeof(Section));
959 n = tb->nsections-1;
960 tb->sections[n]._y = y + max_d;
961 tb->sections[n].x = x+j;
962 tb->sections[n].h = line_height;
963 tb->sections[n].begin = items[i].begin;
964 tb->sections[n].end = items[i].end;
966 if (tb->graphic && tb->object) {
967 tb->sections[n].x += tPtr->visible.x;
968 tb->sections[n].y += tPtr->visible.y;
972 tb->sections[n].last = (i+1 == nitems);
974 if (tb->graphic) {
975 if (!tPtr->flags.monoFont) {
976 if(tb->object) {
977 WMWidget *wdt = tb->d.widget;
978 tb->sections[n].y = max_d + y
979 + line_height - WMWidgetHeight(wdt);
980 tb->sections[n].w = WMWidgetWidth(wdt);
981 } else {
982 tb->sections[n].y = y + max_d;
983 tb->sections[n].w = tb->d.pixmap->width;
985 x += tb->sections[n].w;
987 } else {
988 font = (tPtr->flags.monoFont)? tPtr->dFont : tb->d.font;
989 len = items[i].end - items[i].begin;
990 text = &(tb->text[items[i].begin]);
992 tb->sections[n].y = y+line_height-font->y;
993 tb->sections[n].w =
994 WMWidthOfString(font,
995 &(tb->text[tb->sections[n].begin]),
996 tb->sections[n].end - tb->sections[n].begin);
998 x += WMWidthOfString(font, text, len);
1001 tbsame = tb;
1004 return line_height;
1009 static void
1010 output(char *ptr, int len)
1012 char s[len+1];
1013 memcpy(s, ptr, len);
1014 s[len] = 0;
1015 //printf(" s is [%s] (%d)\n", s, strlen(s));
1016 printf("[%s]\n", s);
1020 /* tb->text doesn't necessarily end in '\0' hmph! (strchr) */
1021 static inline char *
1022 mystrchr(char *s, char needle, unsigned short len)
1024 char *haystack = s;
1026 if (!haystack || len < 1)
1027 return NULL;
1029 while ( (int) (haystack - s) < len ) {
1030 if (*haystack == needle)
1031 return haystack;
1032 haystack++;
1034 return NULL;
1037 #define MAX_TB_PER_LINE 64
1039 static void
1040 layOutDocument(Text *tPtr)
1042 TextBlock *tb;
1043 myLineItems items[MAX_TB_PER_LINE];
1044 WMFont *font;
1045 Bool lhc = !tPtr->flags.laidOut; /* line height changed? */
1046 int prev_y, nitems=0, x=0, y=0, lw = 0, width=0;
1048 char *start=NULL, *mark=NULL;
1049 int begin, end;
1051 if (tPtr->flags.frozen)
1052 return;
1054 if (!(tb = tPtr->firstTextBlock))
1055 return;
1057 if (0&&tPtr->flags.laidOut) {
1058 tb = tPtr->currentTextBlock;
1059 if (tb->sections && tb->nsections>0)
1060 prev_y = tb->sections[tb->nsections-1]._y;
1061 y+=10;
1062 printf("1 prev_y %d \n", prev_y);
1064 /* search backwards for textblocks on same line */
1065 while (tb) {
1066 if (!tb->sections || tb->nsections<1) {
1067 tb = tPtr->firstTextBlock;
1068 break;
1070 if (tb->sections[tb->nsections-1]._y != prev_y) {
1071 tb = tb->next;
1072 break;
1074 // prev_y = tb->sections[tb->nsections-1]._y;
1075 tb = tb->prior;
1077 y = 0;//tb->sections[tb->nsections-1]._y;
1078 printf("2 prev_y %d \n\n", tb->sections[tb->nsections-1]._y);
1082 while (tb) {
1084 if (tb->sections && tb->nsections>0) {
1085 wfree(tb->sections);
1086 tb->sections = NULL;
1087 tb->nsections = 0;
1090 if (tb->first) {
1091 y += layOutLine(tPtr, items, nitems, x, y);
1092 x = 0;//tb->margins.first;
1093 nitems = 0;
1094 lw = 0;
1097 if (tb->graphic) {
1098 if (!tPtr->flags.monoFont) {
1099 if(tb->object)
1100 width = WMWidgetWidth(tb->d.widget);
1101 else
1102 width = tb->d.pixmap->width;
1104 if (width > tPtr->visible.w)printf("rescale graphix to fit?\n");
1105 lw += width;
1106 if (lw >= tPtr->visible.w - x
1107 || nitems >= MAX_TB_PER_LINE) {
1108 y += layOutLine(tPtr, items, nitems, x, y);
1109 nitems = 0;
1110 x = 0;//tb->margins.first;
1111 lw = width;
1114 items[nitems].tb = tb;
1115 items[nitems].begin = 0;
1116 items[nitems].end = 0;
1117 nitems++;
1120 } else if ((start = tb->text)) {
1121 begin = end = 0;
1122 font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
1124 while (start) {
1125 mark = mystrchr(start, ' ', tb->used);
1126 if (mark) {
1127 end += (int)(mark-start)+1;
1128 start = mark+1;
1129 } else {
1130 end += strlen(start);
1131 start = mark;
1134 if (end > tb->used)
1135 end = tb->used;
1137 if (end-begin > 0) {
1139 width = WMWidthOfString(font,
1140 &tb->text[begin], end-begin);
1142 if (width > tPtr->visible.w) { /* break this tb up */
1143 char *t = &tb->text[begin];
1144 int l=end-begin, i=0;
1145 do {
1146 width = WMWidthOfString(font, t, ++i);
1147 } while (width < tPtr->visible.w && i < l);
1148 end = begin+i;
1149 if (start) // and since (nil)-4 = 0xfffffffd
1150 start -= l-i;
1153 lw += width;
1156 if ((lw >= tPtr->visible.w - x)
1157 || nitems >= MAX_TB_PER_LINE) {
1158 y += layOutLine(tPtr, items, nitems, x, y);
1159 lw = width;
1160 x = 0;//tb->margins.first;
1161 nitems = 0;
1164 items[nitems].tb = tb;
1165 items[nitems].begin = begin;
1166 items[nitems].end = end;
1167 nitems++;
1169 begin = end;
1172 tb = tb->next;
1176 if (nitems > 0)
1177 y += layOutLine(tPtr, items, nitems, x, y);
1178 if (lhc) {
1179 tPtr->docHeight = y+10;
1180 updateScrollers(tPtr);
1182 tPtr->flags.laidOut = True;
1187 static void
1188 textDidResize(W_ViewDelegate *self, WMView *view)
1190 Text *tPtr = (Text *)view->self;
1191 unsigned short w = WMWidgetWidth(tPtr);
1192 unsigned short h = WMWidgetHeight(tPtr);
1193 unsigned short rh = 0, vw = 0;
1195 if (tPtr->ruler && tPtr->flags.rulerShown) {
1196 WMMoveWidget(tPtr->ruler, 2, 2);
1197 WMResizeWidget(tPtr->ruler, w - 4, 40);
1198 rh = 40;
1201 if (tPtr->vS) {
1202 WMMoveWidget(tPtr->vS, 1, rh + 1);
1203 WMResizeWidget(tPtr->vS, 20, h - rh - 2);
1204 vw = 20;
1205 WMSetRulerOffset(tPtr->ruler,22);
1206 } else WMSetRulerOffset(tPtr->ruler, 2);
1208 if (tPtr->hS) {
1209 if (tPtr->vS) {
1210 WMMoveWidget(tPtr->hS, vw, h - 21);
1211 WMResizeWidget(tPtr->hS, w - vw - 1, 20);
1212 } else {
1213 WMMoveWidget(tPtr->hS, vw+1, h - 21);
1214 WMResizeWidget(tPtr->hS, w - vw - 2, 20);
1218 tPtr->visible.x = (tPtr->vS)?22:2;
1219 tPtr->visible.y = (tPtr->ruler && tPtr->flags.rulerShown)?43:3;
1220 tPtr->visible.w = tPtr->view->size.width - tPtr->visible.x - 4;
1221 tPtr->visible.h = tPtr->view->size.height - tPtr->visible.y;
1222 tPtr->visible.h -= (tPtr->hS)?20:0;
1224 tPtr->dmargins = WMGetRulerMargins(tPtr->ruler);
1226 if (tPtr->view->flags.realized) {
1228 if (tPtr->db) {
1229 XFreePixmap(tPtr->view->screen->display, tPtr->db);
1230 tPtr->db = (Pixmap) NULL;
1233 if (tPtr->visible.w < 40)
1234 tPtr->visible.w = 40;
1235 if (tPtr->visible.h < 20)
1236 tPtr->visible.h = 20;
1238 //if (size change or !db
1239 if(!tPtr->db) {
1240 tPtr->db = XCreatePixmap(tPtr->view->screen->display,
1241 tPtr->view->window, tPtr->visible.w,
1242 tPtr->visible.h, tPtr->view->screen->depth);
1246 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1249 W_ViewDelegate _TextViewDelegate =
1251 NULL,
1252 NULL,
1253 textDidResize,
1254 NULL,
1257 /* nice, divisble-by-16 blocks */
1258 static inline unsigned short
1259 reqBlockSize(unsigned short requested)
1261 return requested + 16 - (requested%16);
1265 static void
1266 clearText(Text *tPtr)
1268 if (!tPtr->firstTextBlock)
1269 return;
1271 while (tPtr->currentTextBlock)
1272 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1274 tPtr->firstTextBlock = NULL;
1275 tPtr->currentTextBlock = NULL;
1276 tPtr->lastTextBlock = NULL;
1279 static void
1280 deleteTextInteractively(Text *tPtr, KeySym ksym)
1282 TextBlock *tb = tPtr->currentTextBlock;
1283 Bool back = (Bool) (ksym == XK_BackSpace);
1284 Bool done = 1;
1286 if (!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1287 XBell(tPtr->view->screen->display, 0);
1288 return;
1291 if (!tb)
1292 return;
1294 if (tPtr->flags.ownsSelection) {
1295 removeSelection(tPtr);
1296 return;
1299 if (back && tPtr->tpos < 1) {
1300 if (tb->prior) {
1301 tb = tb->prior;
1302 tPtr->tpos = tb->used;
1303 tPtr->currentTextBlock = tb;
1304 done = 1;
1308 if ( (tb->used > 0) && ((back?tPtr->tpos > 0:1))
1309 && (tPtr->tpos <= tb->used) && !tb->graphic) {
1310 if (back)
1311 tPtr->tpos--;
1312 memmove(&(tb->text[tPtr->tpos]),
1313 &(tb->text[tPtr->tpos + 1]), tb->used - tPtr->tpos);
1314 tb->used--;
1315 done = 0;
1318 if ( (back? (tPtr->tpos < 1 && !done) : ( tPtr->tpos >= tb->used))
1319 || tb->graphic) {
1321 TextBlock *sibling = (back? tb->prior : tb->next);
1323 if(tb->used == 0 || tb->graphic)
1324 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1326 if (sibling) {
1327 tPtr->currentTextBlock = sibling;
1328 tPtr->tpos = (back? sibling->used : 0);
1332 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1336 static void
1337 insertTextInteractively(Text *tPtr, char *text, int len)
1339 TextBlock *tb;
1340 char *newline = NULL;
1342 if (!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1343 XBell(tPtr->view->screen->display, 0);
1344 return;
1347 #if 0
1348 if(*text == 'c') {
1349 WMColor *color = WMCreateNamedColor(W_VIEW_SCREEN(tPtr->view),
1350 "Blue", True);
1351 WMSetTextSelectionColor(tPtr, color);
1352 return;
1354 #endif
1356 if (len < 1 || !text)
1357 return;
1359 if (tPtr->flags.ownsSelection)
1360 removeSelection(tPtr);
1362 if(tPtr->flags.ignoreNewLine && *text == '\n' && len == 1)
1363 return;
1365 if (tPtr->flags.ignoreNewLine) {
1366 int i;
1367 for(i=0; i<len; i++) {
1368 if (text[i] == '\n')
1369 text[i] = ' ';
1373 tb = tPtr->currentTextBlock;
1374 if (!tb || tb->graphic) {
1375 text[len] = 0;
1376 WMAppendTextStream(tPtr, text);
1377 if (tPtr->currentTextBlock) {
1378 tPtr->tpos = tPtr->currentTextBlock->used;
1380 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1381 return;
1384 if ((newline = strchr(text, '\n'))) {
1385 int nlen = (int)(newline-text);
1386 int s = tb->used - tPtr->tpos;
1387 char save[s];
1389 if (!tb->blank && nlen>0) {
1390 if (s > 0) {
1391 memcpy(save, &tb->text[tPtr->tpos], s);
1392 tb->used -= (tb->used - tPtr->tpos);
1394 text[nlen] = 0;
1395 insertTextInteractively(tPtr, text, nlen);
1396 newline++;
1397 WMAppendTextStream(tPtr, newline);
1398 if (s>0)
1399 insertTextInteractively(tPtr, save, s);
1401 } else {
1402 if (tPtr->tpos>0 && tPtr->tpos < tb->used
1403 && !tb->graphic && tb->text) {
1405 void *ntb = WMCreateTextBlockWithText(&tb->text[tPtr->tpos],
1406 tb->d.font, tb->color, True, tb->used - tPtr->tpos);
1407 tb->used = tPtr->tpos;
1408 WMAppendTextBlock(tPtr, ntb);
1409 tPtr->tpos = 0;
1410 } else if (tPtr->tpos == tb->used || tPtr->tpos == 0) {
1411 void *ntb = WMCreateTextBlockWithText(NULL,
1412 tb->d.font, tb->color, True, 0);
1414 if (tPtr->tpos>0)
1415 WMAppendTextBlock(tPtr, ntb);
1416 else
1417 WMPrependTextBlock(tPtr, ntb);
1418 tPtr->tpos = 1;
1422 } else {
1424 if (tb->used + len >= tb->allocated) {
1425 tb->allocated = reqBlockSize(tb->used+len);
1426 tb->text = wrealloc(tb->text, tb->allocated);
1429 if (tb->blank) {
1430 memcpy(tb->text, text, len);
1431 tb->used = len;
1432 tPtr->tpos = len;
1433 tb->blank = False;
1434 } else {
1435 memmove(&(tb->text[tPtr->tpos+len]), &tb->text[tPtr->tpos],
1436 tb->used-tPtr->tpos+1);
1437 memmove(&tb->text[tPtr->tpos], text, len);
1438 tb->used += len;
1439 tPtr->tpos += len;
1444 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1448 static void
1449 selectRegion(Text *tPtr, int x, int y)
1452 if (x < 0 || y < 0)
1453 return;
1455 y += (tPtr->flags.rulerShown? 40: 0);
1456 y += tPtr->vpos;
1457 if (y>10)
1458 y -= 10; /* the original offset */
1460 x -= tPtr->visible.x-2;
1461 if (x<0)
1462 x=0;
1464 tPtr->sel.x = WMAX(0, WMIN(tPtr->clicked.x, x));
1465 tPtr->sel.w = abs(tPtr->clicked.x - x);
1466 tPtr->sel.y = WMAX(0, WMIN(tPtr->clicked.y, y));
1467 tPtr->sel.h = abs(tPtr->clicked.y - y);
1469 tPtr->flags.ownsSelection = True;
1470 paintText(tPtr);
1474 static void
1475 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1476 void *cdata, WMData *data)
1478 Text *tPtr = (Text *)view->self;
1479 char *str;
1481 tPtr->flags.waitingForSelection = False;
1482 if (data) {
1483 str = (char*)WMDataBytes(data);
1484 if (0&&tPtr->parser) {
1485 /* parser is not yet well behaved to do this properly..*/
1486 (tPtr->parser) (tPtr, (void *) str);
1487 } else {
1488 insertTextInteractively(tPtr, str, strlen(str));
1490 } else {
1491 int n;
1492 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1493 if (str) {
1494 str[n] = 0;
1495 if (0&&tPtr->parser) {
1496 /* parser is not yet well behaved to do this properly..*/
1497 (tPtr->parser) (tPtr, (void *) str);
1498 } else {
1499 insertTextInteractively(tPtr, str, n);
1501 XFree(str);
1507 static void
1508 releaseSelection(Text *tPtr)
1510 TextBlock *tb = tPtr->firstTextBlock;
1512 while(tb) {
1513 tb->selected = False;
1514 tb = tb->next;
1516 tPtr->flags.ownsSelection = False;
1517 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY,
1518 CurrentTime);
1520 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1523 static WMData *
1524 requestHandler(WMView *view, Atom selection, Atom target,
1525 void *cdata, Atom *type)
1527 Text *tPtr = view->self;
1528 Display *dpy = tPtr->view->screen->display;
1529 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1530 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1531 Atom _TARGETS;
1532 WMData *data;
1534 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
1535 char *text = WMGetTextSelected(tPtr);
1537 if(text) {
1538 WMData *data = WMCreateDataWithBytes(text, strlen(text));
1539 WMSetDataFormat(data, 8);
1540 wfree(text);
1541 *type = target;
1542 return data;
1545 } else if(target == XInternAtom(dpy, "PIXMAP", False)) {
1546 data = WMCreateDataWithBytes("paste a pixmap", 14);
1547 WMSetDataFormat(data, 8);
1548 *type = target;
1549 return data;
1552 _TARGETS = XInternAtom(dpy, "TARGETS", False);
1553 if (target == _TARGETS) {
1554 Atom *ptr;
1556 ptr = wmalloc(4 * sizeof(Atom));
1557 ptr[0] = _TARGETS;
1558 ptr[1] = XA_STRING;
1559 ptr[2] = TEXT;
1560 ptr[3] = COMPOUND_TEXT;
1562 data = WMCreateDataWithBytes(ptr, 4*4);
1563 WMSetDataFormat(data, 32);
1565 *type = target;
1566 return data;
1569 return NULL;
1573 static void
1574 lostHandler(WMView *view, Atom selection, void *cdata)
1576 releaseSelection((WMText *)view->self);
1579 static WMSelectionProcs selectionHandler = {
1580 requestHandler, lostHandler, NULL
1583 static void
1584 ownershipObserver(void *observerData, WMNotification *notification)
1586 WMText *to = (WMText *)observerData;
1587 WMText *tw = (WMText *)WMGetNotificationClientData(notification);
1588 if (to != tw)
1589 lostHandler(to->view, XA_PRIMARY, NULL);
1593 static void
1594 handleTextKeyPress(Text *tPtr, XEvent *event)
1596 char buffer[2];
1597 KeySym ksym;
1598 int control_pressed = False;
1599 // int h=1;
1601 if (((XKeyEvent *) event)->state & ControlMask)
1602 control_pressed = True;
1603 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = 0;
1605 switch(ksym) {
1607 case XK_Right:
1608 case XK_Left: {
1609 TextBlock *tb = tPtr->currentTextBlock;
1610 int x = tPtr->cursor.x + tPtr->visible.x;
1611 int y = tPtr->visible.y + tPtr->cursor.y + tPtr->cursor.h;
1612 int w, pos;
1614 #if 0
1615 if(!tb)
1616 break;
1617 if(tb->graphic) {
1618 if(tb->object) {
1619 w = WMWidgetWidth(tb->d.widget);
1620 } else {
1621 w = tb->d.pixmap->width;
1623 } else {
1624 pos = tPtr->tpos;
1625 w = WMWidthOfString(tb->d.font, &tb->text[pos], 1);
1628 cursorToTextPosition(tPtr, w + tPtr->cursor.x + tPtr->visible.x,
1629 3 + tPtr->visible.y + tPtr->cursor.y
1630 + tPtr->cursor.h - tPtr->vpos);
1631 if(x == tPtr->cursor.x + tPtr->visible.x) {
1632 printf("same %d %d\n", x, tPtr->cursor.x + tPtr->visible.x);
1633 cursorToTextPosition(tPtr, tPtr->visible.x,
1634 3 + tPtr->visible.y + tPtr->cursor.y + tPtr->cursor.h);
1636 paintText(tPtr);
1637 #endif
1639 break;
1641 case XK_Down:
1642 cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
1643 tPtr->clicked.y + tPtr->cursor.h - tPtr->vpos);
1644 paintText(tPtr);
1645 break;
1647 case XK_Up:
1648 cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
1649 tPtr->visible.y + tPtr->cursor.y - tPtr->vpos - 3);
1650 paintText(tPtr);
1651 break;
1653 case XK_BackSpace:
1654 case XK_Delete:
1655 case XK_KP_Delete:
1656 deleteTextInteractively(tPtr, ksym);
1657 break;
1659 case XK_Control_R :
1660 case XK_Control_L :
1661 control_pressed = True;
1662 break;
1664 case XK_Return:
1665 buffer[0] = '\n';
1666 default:
1667 if (buffer[0] != 0 && !control_pressed) {
1668 insertTextInteractively(tPtr, buffer, 1);
1670 } else if (control_pressed && ksym==XK_r) {
1671 Bool i = !tPtr->flags.rulerShown;
1672 WMShowTextRuler(tPtr, i);
1673 tPtr->flags.rulerShown = i;
1675 else if (control_pressed && buffer[0] == '\a')
1676 XBell(tPtr->view->screen->display, 0);
1679 if (!control_pressed && tPtr->flags.ownsSelection)
1680 releaseSelection(tPtr);
1683 static void
1684 handleWidgetPress(XEvent *event, void *data)
1686 TextBlock *tb = (TextBlock *)data;
1687 Text *tPtr;
1688 WMWidget *w;
1690 if (!tb)
1691 return;
1692 /* this little bit of nastiness here saves a boatload of trouble */
1693 w = (WMWidget *)(((W_VIEW(tb->d.widget))->parent)->self);
1694 if (W_CLASS(w) != WC_Text)
1695 return;
1696 tPtr = (Text*)w;
1697 tPtr->currentTextBlock = getFirstNonGraphicBlockFor(tb, 1);
1698 if (!tPtr->currentTextBlock)
1699 tPtr->currentTextBlock = tb;
1700 tPtr->tpos = 0;
1701 output(tPtr->currentTextBlock->text, tPtr->currentTextBlock->used);
1702 //if (!tPtr->flags.focused) {
1703 // WMSetFocusToWidget(tPtr);
1704 // tPtr->flags.focused = True;
1705 //}
1709 static void
1710 handleActionEvents(XEvent *event, void *data)
1712 Text *tPtr = (Text *)data;
1713 Display *dpy = event->xany.display;
1714 KeySym ksym;
1717 switch (event->type) {
1718 case KeyPress:
1719 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1720 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
1721 tPtr->flags.extendSelection = True;
1722 return;
1725 if (tPtr->flags.waitingForSelection)
1726 return;
1727 if (tPtr->flags.focused) {
1728 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1729 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1730 GrabModeAsync, GrabModeAsync, None,
1731 tPtr->view->screen->invisibleCursor, CurrentTime);
1732 tPtr->flags.pointerGrabbed = True;
1733 handleTextKeyPress(tPtr, event);
1735 } break;
1737 case KeyRelease:
1738 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1739 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
1740 tPtr->flags.extendSelection = False;
1741 return;
1742 //end modify flag so selection can be extended
1744 break;
1747 case MotionNotify:
1748 if (tPtr->flags.pointerGrabbed) {
1749 tPtr->flags.pointerGrabbed = False;
1750 XUngrabPointer(dpy, CurrentTime);
1753 if ((event->xmotion.state & Button1Mask)) {
1754 if (!tPtr->flags.ownsSelection) {
1755 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1756 event->xbutton.time, &selectionHandler, NULL);
1757 tPtr->flags.ownsSelection = True;
1759 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1761 break;
1764 case ButtonPress:
1765 tPtr->flags.buttonHeld = True;
1766 if (tPtr->flags.extendSelection) {
1767 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1768 return;
1770 if (event->xbutton.button == Button1) {
1772 if (!tPtr->flags.focused) {
1773 WMSetFocusToWidget(tPtr);
1774 tPtr->flags.focused = True;
1777 if (tPtr->flags.ownsSelection)
1778 releaseSelection(tPtr);
1779 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1780 paintText(tPtr);
1781 if (tPtr->flags.pointerGrabbed) {
1782 tPtr->flags.pointerGrabbed = False;
1783 XUngrabPointer(dpy, CurrentTime);
1784 break;
1788 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown)
1789 WMScrollText(tPtr, -16);
1790 else if (event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1791 WMScrollText(tPtr, 16);
1792 break;
1794 case ButtonRelease:
1795 tPtr->flags.buttonHeld = False;
1796 if (tPtr->flags.pointerGrabbed) {
1797 tPtr->flags.pointerGrabbed = False;
1798 XUngrabPointer(dpy, CurrentTime);
1799 break;
1801 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown
1802 || event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1803 break;
1805 if (event->xbutton.button == Button2) {
1806 char *text = NULL;
1807 int n;
1809 if (!tPtr->flags.editable) {
1810 XBell(dpy, 0);
1811 break;
1814 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1815 event->xbutton.time, pasteText, NULL)) {
1816 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1817 if (text) {
1818 text[n] = 0;
1819 if (0&&tPtr->parser) {
1820 /* parser is not yet well behaved to do this properly..*/
1821 (tPtr->parser) (tPtr, (void *) text);
1822 } else {
1823 insertTextInteractively(tPtr, text, n-1);
1825 XFree(text);
1826 } else tPtr->flags.waitingForSelection = True;
1829 break;
1836 static void
1837 handleEvents(XEvent *event, void *data)
1839 Text *tPtr = (Text *)data;
1841 switch(event->type) {
1842 case Expose:
1844 if(tPtr->hS) {
1845 if (!(W_VIEW(tPtr->hS))->flags.realized)
1846 WMRealizeWidget(tPtr->hS);
1847 if (!((W_VIEW(tPtr->hS))->flags.mapped))
1848 WMMapWidget(tPtr->hS);
1851 if(tPtr->vS) {
1852 if (!(W_VIEW(tPtr->vS))->flags.realized)
1853 WMRealizeWidget(tPtr->vS);
1854 if (!((W_VIEW(tPtr->vS))->flags.mapped))
1855 WMMapWidget(tPtr->vS);
1858 if(tPtr->ruler) {
1859 if (!(W_VIEW(tPtr->ruler))->flags.realized)
1860 WMRealizeWidget(tPtr->ruler);
1862 if (!((W_VIEW(tPtr->ruler))->flags.mapped)
1863 && tPtr->flags.rulerShown)
1864 WMMapWidget(tPtr->ruler);
1866 if(!tPtr->db)
1867 textDidResize(tPtr->view->delegate, tPtr->view);
1869 if (!event->xexpose.count && tPtr->view->flags.realized)
1870 paintText(tPtr);
1871 break;
1873 case FocusIn:
1874 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))
1875 != tPtr->view)
1876 return;
1877 tPtr->flags.focused = True;
1878 #if DO_BLINK
1879 if (tPtr->flags.editable && !tPtr->timerID) {
1880 tPtr->timerID = WMAddTimerHandler(12+0*CURSOR_BLINK_ON_DELAY,
1881 blinkCursor, tPtr);
1883 #endif
1885 break;
1887 case FocusOut:
1888 tPtr->flags.focused = False;
1889 paintText(tPtr);
1890 #if DO_BLINK
1891 if (tPtr->timerID) {
1892 WMDeleteTimerHandler(tPtr->timerID);
1893 tPtr->timerID = NULL;
1895 #endif
1896 break;
1898 case DestroyNotify:
1899 clearText(tPtr);
1900 if(tPtr->hS)
1901 WMDestroyWidget(tPtr->hS);
1902 if(tPtr->vS)
1903 WMDestroyWidget(tPtr->vS);
1904 if(tPtr->ruler)
1905 WMDestroyWidget(tPtr->ruler);
1906 if(tPtr->db)
1907 XFreePixmap(tPtr->view->screen->display, tPtr->db);
1908 if(tPtr->gfxItems)
1909 WMFreeBag(tPtr->gfxItems);
1910 #if DO_BLINK
1911 if (tPtr->timerID)
1912 WMDeleteTimerHandler(tPtr->timerID);
1913 #endif
1914 WMReleaseFont(tPtr->dFont);
1915 WMReleaseColor(tPtr->dColor);
1916 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1917 WMRemoveNotificationObserver(tPtr);
1919 wfree(tPtr);
1921 break;
1927 static void
1928 insertPlainText(WMText *tPtr, char *text)
1930 char *start, *mark;
1931 void *tb = NULL;
1933 if (!text) {
1934 clearText(tPtr);
1935 return;
1938 start = text;
1939 while (start) {
1940 mark = strchr(start, '\n');
1941 if (mark) {
1942 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1943 tPtr->dColor, True, (int)(mark-start));
1944 start = mark+1;
1945 } else {
1946 if (start && strlen(start)) {
1947 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1948 tPtr->dColor, False, strlen(start));
1949 } else tb = NULL;
1950 start = mark;
1953 if (tPtr->flags.prepend)
1954 WMPrependTextBlock(tPtr, tb);
1955 else
1956 WMAppendTextBlock(tPtr, tb);
1958 return;
1963 static void
1964 rulerMoveCallBack(WMWidget *w, void *self)
1966 Text *tPtr = (Text *)self;
1967 if (!tPtr)
1968 return;
1969 if (W_CLASS(tPtr) != WC_Text)
1970 return;
1972 paintText(tPtr);
1976 static void
1977 rulerReleaseCallBack(WMWidget *w, void *self)
1979 Text *tPtr = (Text *)self;
1980 if (!tPtr)
1981 return;
1982 if (W_CLASS(tPtr) != WC_Text)
1983 return;
1985 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1986 return;
1990 #if 0
1991 static unsigned
1992 draggingEntered(WMView *self, WMDraggingInfo *info)
1994 printf("draggingEntered\n");
1995 return WDOperationCopy;
1999 static unsigned
2000 draggingUpdated(WMView *self, WMDraggingInfo *info)
2002 printf("draggingUpdated\n");
2003 return WDOperationCopy;
2007 static void
2008 draggingExited(WMView *self, WMDraggingInfo *info)
2010 printf("draggingExited\n");
2013 static void
2014 prepareForDragOperation(WMView *self, WMDraggingInfo *info)
2016 printf("draggingExited\n");
2017 return;//"bll"; //"application/X-color";
2021 static Bool
2022 performDragOperation(WMView *self, WMDraggingInfo *info) //, WMData *data)
2024 char *colorName = "Blue";// (char*)WMDataBytes(data);
2025 WMColor *color;
2026 WMText *tPtr = (WMText *)self->self;
2028 if (!tPtr)
2029 return False;
2031 if (tPtr->flags.monoFont)
2032 return False;
2034 color = WMCreateNamedColor(W_VIEW_SCREEN(self), colorName, True);
2035 printf("color [%s] %p\n", colorName, color);
2036 if(color) {
2037 WMSetTextSelectionColor(tPtr, color);
2038 WMReleaseColor(color);
2041 return True;
2044 static void
2045 concludeDragOperation(WMView *self, WMDraggingInfo *info)
2047 printf("concludeDragOperation\n");
2051 static WMDragDestinationProcs _DragDestinationProcs = {
2052 draggingEntered,
2053 draggingUpdated,
2054 draggingExited,
2055 prepareForDragOperation,
2056 performDragOperation,
2057 concludeDragOperation
2059 #endif
2061 static void
2062 releaseBagData(void *data)
2064 if(data)
2065 wfree(data);
2069 char *
2070 getStream(WMText *tPtr, int sel)
2072 TextBlock *tb = NULL;
2073 char *text = NULL;
2074 unsigned long length = 0, where = 0;
2076 if (!tPtr)
2077 return NULL;
2079 if (!(tb = tPtr->firstTextBlock))
2080 return NULL;
2082 /* this might be tricky to get right... not yet implemented */
2083 if (tPtr->writer) {
2084 (tPtr->writer) (tPtr, (void *) text);
2085 return text;
2089 /* first, how large a buffer would we want? */
2090 while (tb) {
2092 if (!tb->graphic || (tb->graphic && !tPtr->flags.monoFont)) {
2095 if (!sel || (tb->graphic && tb->selected)) {
2096 length += tb->used;
2097 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank))
2098 length += 1;
2099 if (tb->graphic)
2100 length += 2; /* field markers 0xFA and size */
2101 } else if (sel && tb->selected) {
2102 length += (tb->s_end - tb->s_begin);
2103 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank))
2104 length += 1;
2108 tb = tb->next;
2111 text = wmalloc(length+1); /* +1 for the end of string, let's be nice */
2112 tb = tPtr->firstTextBlock;
2113 while (tb) {
2115 if (!tb->graphic || (tb->graphic && !tPtr->flags.monoFont)) {
2118 if (!sel || (tb->graphic && tb->selected)) {
2119 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank))
2120 text[where++] = '\n';
2121 if(tb->graphic) {
2122 text[where++] = 0xFA;
2123 text[where++] = tb->used;
2125 memcpy(&text[where], tb->text, tb->used);
2126 where += tb->used;
2128 } else if (sel && tb->selected) {
2129 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank))
2130 text[where++] = '\n';
2131 memcpy(&text[where], &tb->text[tb->s_begin],
2132 tb->s_end - tb->s_begin);
2133 where += tb->s_end - tb->s_begin;
2137 tb = tb->next;
2140 text[where] = 0;
2141 return text;
2146 WMBag *
2147 getStreamIntoBag(WMText *tPtr, int sel)
2149 char *stream, *start = NULL, *fa = NULL;
2150 WMBag *bag;
2151 WMData *data;
2153 if (!tPtr)
2154 return NULL;
2157 stream = getStream(tPtr, sel);
2158 if(!stream)
2159 return NULL;
2161 bag = WMCreateBagWithDestructor(4, releaseBagData);
2163 start = stream;
2164 while (start) {
2165 fa = strchr(start, 0xFA);
2166 if (fa) {
2167 unsigned char len = *(fa+1);
2169 if(start != fa) {
2170 data = WMCreateDataWithBytes((void *)start, (int)(fa - start));
2171 WMSetDataFormat(data, 8);
2172 WMPutInBag(bag, (void *) data);
2175 data = WMCreateDataWithBytes((void *)(fa+2), len);
2176 WMSetDataFormat(data, 32);
2177 WMPutInBag(bag, (void *) data);
2178 start = fa + len + 2;
2180 } else {
2181 if (start && strlen(start)) {
2182 data = WMCreateDataWithBytes((void *)start, strlen(start));
2183 WMSetDataFormat(data, 8);
2184 WMPutInBag(bag, (void *) data);
2186 start = fa;
2190 wfree(stream);
2191 return bag;
2195 WMText *
2196 WMCreateText(WMWidget *parent)
2198 Text *tPtr = wmalloc(sizeof(Text));
2199 if (!tPtr) {
2200 printf("could not create text widget\n");
2201 return NULL;
2204 #if 0
2205 printf("sizeof:\n");
2206 printf(" TextBlock %d\n", sizeof(TextBlock));
2207 printf(" Section %d\n", sizeof(Section));
2208 printf(" WMRulerMargins %d\n", sizeof(WMRulerMargins));
2209 printf(" char * %d\n", sizeof(char *));
2210 printf(" void * %d\n", sizeof(void *));
2211 printf(" short %d\n", sizeof(short));
2212 printf(" Text %d\n", sizeof(Text));
2213 #endif
2215 memset(tPtr, 0, sizeof(Text));
2216 tPtr->widgetClass = WC_Text;
2217 tPtr->view = W_CreateView(W_VIEW(parent));
2218 if (!tPtr->view) {
2219 perror("could not create text's view\n");
2220 free(tPtr);
2221 return NULL;
2223 tPtr->view->self = tPtr;
2224 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
2225 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
2226 W_ResizeView(tPtr->view, 250, 200);
2227 tPtr->bgGC = WMColorGC(tPtr->view->screen->white);
2228 tPtr->fgGC = WMColorGC(tPtr->view->screen->black);
2229 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
2231 tPtr->ruler = NULL;
2232 tPtr->vS = NULL;
2233 tPtr->hS = NULL;
2235 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2237 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2239 tPtr->view->delegate = &_TextViewDelegate;
2241 #if DO_BLINK
2242 tPtr->timerID = NULL;
2243 #endif
2245 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
2246 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
2247 handleEvents, tPtr);
2249 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
2250 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
2251 handleActionEvents, tPtr);
2253 WMAddNotificationObserver(ownershipObserver, tPtr, "_lostOwnership", tPtr);
2255 #if 0
2256 WMSetViewDragDestinationProcs(tPtr->view, &_DragDestinationProcs);
2258 char *types[2] = {"application/X-color", NULL};
2259 WMRegisterViewForDraggedTypes(tPtr->view, types);
2261 #endif
2263 tPtr->firstTextBlock = NULL;
2264 tPtr->lastTextBlock = NULL;
2265 tPtr->currentTextBlock = NULL;
2266 tPtr->tpos = 0;
2268 tPtr->gfxItems = WMCreateBag(4);
2270 tPtr->parser = NULL;
2271 tPtr->writer = NULL;
2273 tPtr->sel.x = tPtr->sel.y = 2;
2274 tPtr->sel.w = tPtr->sel.h = 0;
2276 tPtr->clicked.x = tPtr->clicked.y = 2;
2278 tPtr->visible.x = tPtr->visible.y = 2;
2279 tPtr->visible.h = tPtr->view->size.height;
2280 tPtr->visible.w = tPtr->view->size.width - 4;
2282 tPtr->cursor.x = -23;
2284 tPtr->docWidth = 0;
2285 tPtr->docHeight = 0;
2286 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
2287 default_bullet);
2288 tPtr->db = (Pixmap) NULL;
2290 tPtr->dmargins = WMGetRulerMargins(tPtr->ruler);
2292 tPtr->flags.rulerShown = False;
2293 tPtr->flags.monoFont = False;
2294 tPtr->flags.focused = False;
2295 tPtr->flags.editable = True;
2296 tPtr->flags.ownsSelection = False;
2297 tPtr->flags.pointerGrabbed = False;
2298 tPtr->flags.buttonHeld = False;
2299 tPtr->flags.waitingForSelection = False;
2300 tPtr->flags.extendSelection = False;
2301 tPtr->flags.frozen = False;
2302 tPtr->flags.cursorShown = True;
2303 tPtr->flags.clickPos = 1;
2304 tPtr->flags.ignoreNewLine = False;
2305 tPtr->flags.laidOut = False;
2306 tPtr->flags.prepend = False;
2307 tPtr->flags.relief = WRFlat;
2308 tPtr->flags.alignment = WALeft;
2310 return tPtr;
2313 void
2314 WMPrependTextStream(WMText *tPtr, char *text)
2316 if (!tPtr)
2317 return;
2319 if(!text)
2320 releaseSelection(tPtr);
2322 tPtr->flags.prepend = True;
2323 if (text && tPtr->parser)
2324 (tPtr->parser) (tPtr, (void *) text);
2325 else
2326 insertPlainText(tPtr, text);
2331 void
2332 WMAppendTextStream(WMText *tPtr, char *text)
2334 if (!tPtr)
2335 return;
2337 if(!text)
2338 releaseSelection(tPtr);
2340 tPtr->flags.prepend = False;
2341 if (text && tPtr->parser)
2342 (tPtr->parser) (tPtr, (void *) text);
2343 else
2344 insertPlainText(tPtr, text);
2350 char *
2351 WMGetTextStream(WMText *tPtr)
2353 return getStream(tPtr, 0);
2356 char *
2357 WMGetTextSelected(WMText *tPtr)
2359 return getStream(tPtr, 1);
2362 WMBag *
2363 WMGetTextStreamIntoBag(WMText *tPtr)
2365 return getStreamIntoBag(tPtr, 0);
2368 WMBag *
2369 WMGetTextSelectedIntoBag(WMText *tPtr)
2371 return getStreamIntoBag(tPtr, 1);
2375 void *
2376 WMCreateTextBlockWithObject(WMWidget *w, char *description, WMColor *color,
2377 unsigned short first, unsigned short reserved)
2379 TextBlock *tb;
2380 unsigned short length;
2382 if (!w || !description || !color)
2383 return NULL;
2385 tb = wmalloc(sizeof(TextBlock));
2386 if (!tb)
2387 return NULL;
2389 length = strlen(description);
2390 tb->text = (char *)wmalloc(length);
2391 memset(tb->text, 0, length);
2392 memcpy(tb->text, description, length);
2393 tb->used = length;
2394 tb->blank = False;
2395 tb->d.widget = w;
2396 tb->color = WMRetainColor(color);
2397 //&tb->margins = NULL;
2398 tb->allocated = 0;
2399 tb->first = first;
2400 tb->kanji = False;
2401 tb->graphic = True;
2402 tb->object = True;
2403 tb->underlined = False;
2404 tb->selected = False;
2405 tb->script = 0;
2406 tb->sections = NULL;
2407 tb->nsections = 0;
2408 tb->prior = NULL;
2409 tb->next = NULL;
2411 return tb;
2415 void *
2416 WMCreateTextBlockWithPixmap(WMPixmap *p, char *description, WMColor *color,
2417 unsigned short first, unsigned short reserved)
2419 TextBlock *tb;
2420 unsigned short length;
2422 if (!p || !description || !color)
2423 return NULL;
2425 tb = wmalloc(sizeof(TextBlock));
2426 if (!tb)
2427 return NULL;
2429 length = strlen(description);
2430 tb->text = (char *)wmalloc(length);
2431 memset(tb->text, 0, length);
2432 memcpy(tb->text, description, length);
2433 tb->used = length;
2434 tb->blank = False;
2435 tb->d.pixmap = p;
2436 tb->color = WMRetainColor(color);
2437 //&tb->margins = NULL;
2438 tb->allocated = 0;
2439 tb->first = first;
2440 tb->kanji = False;
2441 tb->graphic = True;
2442 tb->object = False;
2443 tb->underlined = False;
2444 tb->selected = False;
2445 tb->script = 0;
2446 tb->sections = NULL;
2447 tb->nsections = 0;
2448 tb->prior = NULL;
2449 tb->next = NULL;
2451 return tb;
2454 void *
2455 WMCreateTextBlockWithText(char *text, WMFont *font, WMColor *color,
2456 unsigned short first, unsigned short length)
2458 TextBlock *tb;
2460 if (!font || !color)
2461 return NULL;
2463 tb = wmalloc(sizeof(TextBlock));
2464 if (!tb)
2465 return NULL;
2467 tb->allocated = reqBlockSize(length);
2468 tb->text = (char *)wmalloc(tb->allocated);
2469 memset(tb->text, 0, tb->allocated);
2471 if (length < 1|| !text ) { // || *text == '\n') {
2472 *tb->text = ' ';
2473 tb->used = 1;
2474 tb->blank = True;
2475 } else {
2476 memcpy(tb->text, text, length);
2477 tb->used = length;
2478 tb->blank = False;
2481 tb->d.font = WMRetainFont(font);
2482 tb->color = WMRetainColor(color);
2483 tb->first = first;
2484 tb->kanji = False;
2485 tb->graphic = False;
2486 tb->underlined = False;
2487 tb->selected = False;
2488 tb->script = 0;
2489 tb->sections = NULL;
2490 tb->nsections = 0;
2491 tb->prior = NULL;
2492 tb->next = NULL;
2493 return tb;
2496 void
2497 WMSetTextBlockProperties(void *vtb, unsigned int first,
2498 unsigned int kanji, unsigned int underlined, int script,
2499 WMRulerMargins margins)
2501 TextBlock *tb = (TextBlock *) vtb;
2502 if (!tb)
2503 return;
2505 tb->first = first;
2506 tb->kanji = kanji;
2507 tb->underlined = underlined;
2508 tb->script = script;
2509 #if 0
2510 tb->margins.left = margins.left;
2511 tb->margins.first = margins.first;
2512 tb->margins.body = margins.body;
2513 tb->margins.right = margins.right;
2514 for: tb->margins.tabs = margins.tabs;
2515 #endif
2518 void
2519 WMGetTextBlockProperties(void *vtb, unsigned int *first,
2520 unsigned int *kanji, unsigned int *underlined, int *script,
2521 WMRulerMargins *margins)
2523 TextBlock *tb = (TextBlock *) vtb;
2524 if (!tb)
2525 return;
2527 if (first) *first = tb->first;
2528 if (kanji) *kanji = tb->kanji;
2529 if (underlined) *underlined = tb->underlined;
2530 if (script) *script = tb->script;
2532 #if 0
2533 if (margins) {
2534 (*margins).left = tb->margins.left;
2535 (*margins).first = tb->margins.first;
2536 (*margins).body = tb->margins.body;
2537 (*margins).right = tb->margins.right;
2538 //for: (*margins).tabs = tb->margins.tabs;
2540 #endif
2545 void
2546 WMPrependTextBlock(WMText *tPtr, void *vtb)
2548 TextBlock *tb = (TextBlock *)vtb;
2550 if (!tPtr || !tb)
2551 return;
2553 if (tb->graphic) {
2554 if(tb->object) {
2555 WMWidget *w = tb->d.widget;
2556 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
2557 handleWidgetPress, tb);
2558 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
2559 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
2560 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
2563 WMPutInBag(tPtr->gfxItems, (void *)tb);
2564 tPtr->tpos = 0;
2565 } else tPtr->tpos = tb->used;
2567 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
2568 tb->next = tb->prior = NULL;
2569 tPtr->lastTextBlock = tPtr->firstTextBlock
2570 = tPtr->currentTextBlock = tb;
2571 return;
2574 tb->next = tPtr->currentTextBlock;
2575 tb->prior = tPtr->currentTextBlock->prior;
2576 if (tPtr->currentTextBlock->prior)
2577 tPtr->currentTextBlock->prior->next = tb;
2579 tPtr->currentTextBlock->prior = tb;
2580 if (!tb->prior)
2581 tPtr->firstTextBlock = tb;
2583 tPtr->currentTextBlock = tb;
2587 void
2588 WMAppendTextBlock(WMText *tPtr, void *vtb)
2590 TextBlock *tb = (TextBlock *)vtb;
2592 if (!tPtr || !tb)
2593 return;
2595 if (tb->graphic) {
2596 if(tb->object) {
2597 WMWidget *w = tb->d.widget;
2598 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
2599 handleWidgetPress, tb);
2600 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
2601 (W_VIEW(w))->attribs.cursor =
2602 tPtr->view->screen->defaultCursor;
2603 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
2606 WMPutInBag(tPtr->gfxItems, (void *)tb);
2607 tPtr->tpos = 0;
2608 } else tPtr->tpos = tb->used;
2610 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
2611 tb->next = tb->prior = NULL;
2612 tPtr->lastTextBlock = tPtr->firstTextBlock
2613 = tPtr->currentTextBlock = tb;
2614 return;
2617 tb->next = tPtr->currentTextBlock->next;
2618 tb->prior = tPtr->currentTextBlock;
2619 if (tPtr->currentTextBlock->next)
2620 tPtr->currentTextBlock->next->prior = tb;
2622 tPtr->currentTextBlock->next = tb;
2624 if (!tb->next)
2625 tPtr->lastTextBlock = tb;
2627 tPtr->currentTextBlock = tb;
2630 void *
2631 WMRemoveTextBlock(WMText *tPtr)
2633 TextBlock *tb = NULL;
2635 if (!tPtr || !tPtr->firstTextBlock || !tPtr->lastTextBlock
2636 || !tPtr->currentTextBlock) {
2637 printf("cannot remove non existent TextBlock!\b");
2638 return tb;
2641 tb = tPtr->currentTextBlock;
2642 if (tb->graphic) {
2643 WMRemoveFromBag(tPtr->gfxItems, (void *)tb);
2645 if(tb->object) {
2646 WMDeleteEventHandler(W_VIEW(tb->d.widget), ButtonPressMask,
2647 handleWidgetPress, tb);
2648 WMUnmapWidget(tb->d.widget);
2652 if (tPtr->currentTextBlock == tPtr->firstTextBlock) {
2653 if (tPtr->currentTextBlock->next)
2654 tPtr->currentTextBlock->next->prior = NULL;
2656 tPtr->firstTextBlock = tPtr->currentTextBlock->next;
2657 tPtr->currentTextBlock = tPtr->firstTextBlock;
2659 } else if (tPtr->currentTextBlock == tPtr->lastTextBlock) {
2660 tPtr->currentTextBlock->prior->next = NULL;
2661 tPtr->lastTextBlock = tPtr->currentTextBlock->prior;
2662 tPtr->currentTextBlock = tPtr->lastTextBlock;
2663 } else {
2664 tPtr->currentTextBlock->prior->next = tPtr->currentTextBlock->next;
2665 tPtr->currentTextBlock->next->prior = tPtr->currentTextBlock->prior;
2666 tPtr->currentTextBlock = tPtr->currentTextBlock->next;
2669 return (void *)tb;
2672 void
2673 WMDestroyTextBlock(WMText *tPtr, void *vtb)
2675 TextBlock *tb = (TextBlock *)vtb;
2676 if (!tPtr || !tb)
2677 return;
2679 if (tb->graphic) {
2680 if(tb->object) {
2681 /* naturally, there's a danger to destroying
2682 widgets whose action brings us here:
2683 ie. press a button to destroy it... need to
2684 find a safer way. till then... this stays commented out */
2685 //WMDestroyWidget(tb->d.widget);
2686 //wfree(tb->d.widget);
2687 tb->d.widget = NULL;
2688 } else {
2689 WMReleasePixmap(tb->d.pixmap);
2690 tb->d.pixmap = NULL;
2692 } else {
2693 WMReleaseFont(tb->d.font);
2696 WMReleaseColor(tb->color);
2697 if (tb->sections && tb->nsections > 0)
2698 wfree(tb->sections);
2699 wfree(tb->text);
2700 wfree(tb);
2701 tb = NULL;
2705 void
2706 WMRefreshText(WMText *tPtr, int vpos, int hpos)
2708 if (!tPtr || vpos<0 || hpos<0)
2709 return;
2711 if (tPtr->flags.frozen)
2712 return;
2714 if(tPtr->flags.monoFont) {
2715 int j, c = WMGetBagItemCount(tPtr->gfxItems);
2716 TextBlock *tb;
2718 /* make sure to unmap widgets no matter where they are */
2719 for(j=0; j<c; j++) {
2720 if ((tb = (TextBlock *) WMGetFromBag(tPtr->gfxItems, j))) {
2721 if (tb->object && ((W_VIEW(tb->d.widget))->flags.mapped))
2722 WMUnmapWidget(tb->d.widget);
2728 if (tPtr->vpos != vpos) {
2729 if (vpos < 0 || tPtr->docHeight < tPtr->visible.h) {
2730 tPtr->vpos = 0;
2731 } else if(tPtr->docHeight - vpos > tPtr->visible.h - tPtr->visible.y) {
2732 tPtr->vpos = vpos;
2733 } else {
2734 tPtr->vpos = tPtr->docHeight - tPtr->visible.h;
2738 tPtr->flags.laidOut = False;
2739 layOutDocument(tPtr);
2740 updateScrollers(tPtr);
2741 paintText(tPtr);
2745 void
2746 WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
2748 if (!tPtr)
2749 return;
2751 if (color)
2752 tPtr->fgGC = WMColorGC(color);
2753 else
2754 tPtr->fgGC = WMColorGC(WMBlackColor(tPtr->view->screen));
2756 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2759 void
2760 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
2762 if (!tPtr)
2763 return;
2765 if (color) {
2766 tPtr->bgGC = WMColorGC(color);
2767 W_SetViewBackgroundColor(tPtr->view, color);
2768 } else {
2769 tPtr->bgGC = WMColorGC(WMWhiteColor(tPtr->view->screen));
2770 W_SetViewBackgroundColor(tPtr->view,
2771 WMWhiteColor(tPtr->view->screen));
2774 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2777 void
2778 WMSetTextRelief(WMText *tPtr, WMReliefType relief)
2780 if (!tPtr)
2781 return;
2782 tPtr->flags.relief = relief;
2783 paintText(tPtr);
2786 void
2787 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
2789 if (!tPtr)
2790 return;
2792 if (shouldhave && !tPtr->hS) {
2793 tPtr->hS = WMCreateScroller(tPtr);
2794 (W_VIEW(tPtr->hS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2795 (W_VIEW(tPtr->hS))->attribFlags |= CWOverrideRedirect | CWCursor;
2796 WMSetScrollerArrowsPosition(tPtr->hS, WSAMaxEnd);
2797 WMSetScrollerAction(tPtr->hS, scrollersCallBack, tPtr);
2798 } else if (!shouldhave && tPtr->hS) {
2799 WMUnmapWidget(tPtr->hS);
2800 WMDestroyWidget(tPtr->hS);
2801 tPtr->hS = NULL;
2804 tPtr->hpos = 0;
2805 tPtr->prevHpos = 0;
2806 textDidResize(tPtr->view->delegate, tPtr->view);
2810 void
2811 WMSetTextHasRuler(WMText *tPtr, Bool shouldhave)
2813 if (!tPtr)
2814 return;
2816 if(shouldhave && !tPtr->ruler) {
2817 tPtr->ruler = WMCreateRuler(tPtr);
2818 (W_VIEW(tPtr->ruler))->attribs.cursor =
2819 tPtr->view->screen->defaultCursor;
2820 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
2821 WMSetRulerReleaseAction(tPtr->ruler, rulerReleaseCallBack, tPtr);
2822 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
2823 } else if(!shouldhave && tPtr->ruler) {
2824 WMShowTextRuler(tPtr, False);
2825 WMDestroyWidget(tPtr->ruler);
2826 tPtr->ruler = NULL;
2828 textDidResize(tPtr->view->delegate, tPtr->view);
2831 void
2832 WMShowTextRuler(WMText *tPtr, Bool show)
2834 if(!tPtr)
2835 return;
2836 if(!tPtr->ruler)
2837 return;
2839 if(tPtr->flags.monoFont)
2840 show = False;
2842 tPtr->flags.rulerShown = show;
2843 if(show) {
2844 WMMapWidget(tPtr->ruler);
2845 } else {
2846 WMUnmapWidget(tPtr->ruler);
2849 textDidResize(tPtr->view->delegate, tPtr->view);
2852 Bool
2853 WMGetTextRulerShown(WMText *tPtr)
2855 if(!tPtr)
2856 return 0;
2858 if(!tPtr->ruler)
2859 return 0;
2861 return tPtr->flags.rulerShown;
2865 void
2866 WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
2868 if (!tPtr)
2869 return;
2871 if (shouldhave && !tPtr->vS) {
2872 tPtr->vS = WMCreateScroller(tPtr);
2873 (W_VIEW(tPtr->vS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2874 (W_VIEW(tPtr->vS))->attribFlags |= CWOverrideRedirect | CWCursor;
2875 WMSetScrollerArrowsPosition(tPtr->vS, WSAMaxEnd);
2876 WMSetScrollerAction(tPtr->vS, scrollersCallBack, tPtr);
2877 } else if (!shouldhave && tPtr->vS) {
2878 WMUnmapWidget(tPtr->vS);
2879 WMDestroyWidget(tPtr->vS);
2880 tPtr->vS = NULL;
2883 tPtr->vpos = 0;
2884 tPtr->prevVpos = 0;
2885 textDidResize(tPtr->view->delegate, tPtr->view);
2890 Bool
2891 WMScrollText(WMText *tPtr, int amount)
2893 Bool scroll=False;
2894 if (!tPtr)
2895 return False;
2896 if (amount == 0 || !tPtr->view->flags.realized)
2897 return False;
2899 if (amount < 0) {
2900 if (tPtr->vpos > 0) {
2901 if (tPtr->vpos > amount) tPtr->vpos += amount;
2902 else tPtr->vpos=0;
2903 scroll=True;
2904 } } else {
2905 int limit = tPtr->docHeight - tPtr->visible.h;
2906 if (tPtr->vpos < limit) {
2907 if (tPtr->vpos < limit-amount) tPtr->vpos += amount;
2908 else tPtr->vpos = limit;
2909 scroll = True;
2910 } }
2912 if (scroll && tPtr->vpos != tPtr->prevVpos) {
2913 updateScrollers(tPtr);
2914 paintText(tPtr);
2916 tPtr->prevVpos = tPtr->vpos;
2917 return scroll;
2920 Bool
2921 WMPageText(WMText *tPtr, Bool direction)
2923 if (!tPtr) return False;
2924 if (!tPtr->view->flags.realized) return False;
2926 return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
2929 void
2930 WMSetTextEditable(WMText *tPtr, Bool editable)
2932 if (!tPtr)
2933 return;
2934 tPtr->flags.editable = editable;
2937 int
2938 WMGetTextEditable(WMText *tPtr)
2940 if (!tPtr)
2941 return 0;
2942 return tPtr->flags.editable;
2945 void
2946 WMSetTextIgnoresNewline(WMText *tPtr, Bool ignore)
2948 if (!tPtr)
2949 return;
2950 tPtr->flags.ignoreNewLine = ignore;
2953 Bool
2954 WMGetTextIgnoresNewline(WMText *tPtr)
2956 if (!tPtr)
2957 return True;
2958 return tPtr->flags.ignoreNewLine;
2961 void
2962 WMSetTextUsesMonoFont(WMText *tPtr, Bool mono)
2964 if (!tPtr)
2965 return;
2966 if (mono && tPtr->flags.rulerShown)
2967 WMShowTextRuler(tPtr, False);
2969 tPtr->flags.monoFont = mono;
2970 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2973 Bool
2974 WMGetTextUsesMonoFont(WMText *tPtr)
2976 if (!tPtr)
2977 return True;
2978 return tPtr->flags.monoFont;
2982 void
2983 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
2985 if (!tPtr)
2986 return;
2988 WMReleaseFont(tPtr->dFont);
2989 if (font)
2990 tPtr->dFont = font;
2991 else
2992 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2995 WMFont *
2996 WMGetTextDefaultFont(WMText *tPtr)
2998 if (!tPtr)
2999 return NULL;
3000 else
3001 return tPtr->dFont;
3004 void
3005 WMSetTextAlignment(WMText *tPtr, WMAlignment alignment)
3007 if (!tPtr)
3008 return;
3009 tPtr->flags.alignment = alignment;
3010 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
3013 void
3014 WMSetTextParser(WMText *tPtr, WMAction *parser)
3016 if (!tPtr)
3017 return;
3018 tPtr->parser = parser;
3021 void
3022 WMSetTextWriter(WMText *tPtr, WMAction *writer)
3024 if (!tPtr)
3025 return;
3026 tPtr->writer = writer;
3029 int
3030 WMGetTextInsertType(WMText *tPtr)
3032 if (!tPtr)
3033 return 0;
3034 return tPtr->flags.prepend;
3038 void
3039 WMSetTextSelectionColor(WMText *tPtr, WMColor *color)
3041 TextBlock *tb;
3042 if (!tPtr || !color)
3043 return;
3045 tb = tPtr->firstTextBlock;
3046 if (!tb || !tPtr->flags.ownsSelection)
3047 return;
3049 while (tb) {
3050 if (tb->selected) {
3052 if ( (tb->s_end - tb->s_begin == tb->used) || tb->graphic) {
3053 if(tb->color)
3054 WMReleaseColor(tb->color);
3055 tb->color = WMRetainColor(color);
3057 } else if (tb->s_end <= tb->used) {
3059 TextBlock *otb = tb;
3060 int count=0;
3061 TextBlock *ntb = (TextBlock *) WMCreateTextBlockWithText(
3062 &(tb->text[tb->s_begin]),
3063 tb->d.font, color, False, (tb->s_end - tb->s_begin));
3065 if (ntb) {
3066 ntb->selected = True;
3067 ntb->s_begin = 0;
3068 ntb->s_end = ntb->used;
3069 WMAppendTextBlock(tPtr, ntb);
3070 count++;
3073 #if 0
3074 if (tb->used > tb->s_end) {
3075 ntb = (TextBlock *) WMCreateTextBlockWithText(
3076 &(tb->text[tb->s_end]),
3077 tb->d.font, tb->color, False, tb->used - tb->s_end);
3078 if (ntb) {
3079 ntb->selected = True;
3080 ntb->s_begin = 0;
3081 ntb->s_end = ntb->used;
3082 WMAppendTextBlock(tPtr, ntb);
3083 count++;
3086 #endif
3088 if (count == 1)
3089 tb = otb->next;
3090 else if (count == 2)
3091 tb = otb->next->next;
3093 tb->used = tb->s_end = tb->s_begin;
3097 tb = tb->next;
3100 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
3103 void
3104 WMSetTextSelectionFont(WMText *tPtr, WMFont *font)
3106 TextBlock *tb;
3107 if (!tPtr || !font)
3108 return;
3110 tb = tPtr->firstTextBlock;
3111 if (!tb || !tPtr->flags.ownsSelection)
3112 return;
3114 while (tb) {
3115 if (!tb->graphic)
3116 tb->d.font = WMRetainFont(font);
3117 tb = tb->next;
3119 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
3122 void
3123 WMFreezeText(WMText *tPtr)
3125 if (!tPtr)
3126 return;
3128 tPtr->flags.frozen = True;
3131 void
3132 WMThawText(WMText *tPtr)
3134 if (!tPtr)
3135 return;
3137 tPtr->flags.frozen = False;
3141 Bool
3142 WMFindInTextStream(WMText *tPtr, char *needle)
3144 char *haystack = NULL;
3145 Bool result;
3147 if (!tPtr || !needle)
3148 return False;
3150 if ( !(haystack = "WMGetTextStream(tPtr)"))
3151 return False;
3153 result = (Bool) strstr(haystack, needle);
3154 wfree(haystack);
3155 return result;