all new stuff
[wmaker-crm.git] / WINGs / wtext.c
blobecbeebaca29b2d32bdcd3b27082ce3bb6623790e
1 /*
2 * WINGs WMText: multi-line/font/color/graphic text widget
4 * Copyright (c) 1999-2000 Nwanua Elumeze
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #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 markers 0xFA and 0xCE
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);
1334 static void
1335 insertTextInteractively(Text *tPtr, char *text, int len)
1337 TextBlock *tb;
1338 char *newline = NULL;
1340 if (!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1341 XBell(tPtr->view->screen->display, 0);
1342 return;
1345 #if 0
1346 if(*text == 'c') {
1347 WMColor *color = WMCreateNamedColor(W_VIEW_SCREEN(tPtr->view),
1348 "Blue", True);
1349 WMSetTextSelectionColor(tPtr, color);
1350 return;
1352 #endif
1354 if (len < 1 || !text)
1355 return;
1357 if (tPtr->flags.ownsSelection)
1358 removeSelection(tPtr);
1360 if(tPtr->flags.ignoreNewLine && *text == '\n' && len == 1)
1361 return;
1363 if (tPtr->flags.ignoreNewLine) {
1364 int i;
1365 for(i=0; i<len; i++) {
1366 if (text[i] == '\n')
1367 text[i] = ' ';
1371 tb = tPtr->currentTextBlock;
1372 if (!tb || tb->graphic) {
1373 text[len] = 0;
1374 WMAppendTextStream(tPtr, text);
1375 if (tPtr->currentTextBlock) {
1376 tPtr->tpos = tPtr->currentTextBlock->used;
1378 return;
1381 if ((newline = strchr(text, '\n'))) {
1382 int nlen = (int)(newline-text);
1383 int s = tb->used - tPtr->tpos;
1384 char save[s];
1386 if (!tb->blank && nlen>0) {
1387 if (s > 0) {
1388 memcpy(save, &tb->text[tPtr->tpos], s);
1389 tb->used -= (tb->used - tPtr->tpos);
1391 text[nlen] = 0;
1392 insertTextInteractively(tPtr, text, nlen);
1393 newline++;
1394 WMAppendTextStream(tPtr, newline);
1395 if (s>0)
1396 insertTextInteractively(tPtr, save, s);
1398 } else {
1399 if (tPtr->tpos>0 && tPtr->tpos < tb->used
1400 && !tb->graphic && tb->text) {
1402 void *ntb = WMCreateTextBlockWithText(&tb->text[tPtr->tpos],
1403 tb->d.font, tb->color, True, tb->used - tPtr->tpos);
1404 tb->used = tPtr->tpos;
1405 WMAppendTextBlock(tPtr, ntb);
1406 tPtr->tpos = 0;
1407 } else if (tPtr->tpos == tb->used || tPtr->tpos == 0) {
1408 void *ntb = WMCreateTextBlockWithText(NULL,
1409 tb->d.font, tb->color, True, 0);
1411 if (tPtr->tpos>0)
1412 WMAppendTextBlock(tPtr, ntb);
1413 else
1414 WMPrependTextBlock(tPtr, ntb);
1415 tPtr->tpos = 1;
1419 } else {
1421 if (tb->used + len >= tb->allocated) {
1422 tb->allocated = reqBlockSize(tb->used+len);
1423 tb->text = wrealloc(tb->text, tb->allocated);
1426 if (tb->blank) {
1427 memcpy(tb->text, text, len);
1428 tb->used = len;
1429 tPtr->tpos = len;
1430 tb->blank = False;
1431 } else {
1432 memmove(&(tb->text[tPtr->tpos+len]), &tb->text[tPtr->tpos],
1433 tb->used-tPtr->tpos+1);
1434 memmove(&tb->text[tPtr->tpos], text, len);
1435 tb->used += len;
1436 tPtr->tpos += len;
1443 static void
1444 selectRegion(Text *tPtr, int x, int y)
1447 if (x < 0 || y < 0)
1448 return;
1450 y += (tPtr->flags.rulerShown? 40: 0);
1451 y += tPtr->vpos;
1452 if (y>10)
1453 y -= 10; /* the original offset */
1455 x -= tPtr->visible.x-2;
1456 if (x<0)
1457 x=0;
1459 tPtr->sel.x = WMAX(0, WMIN(tPtr->clicked.x, x));
1460 tPtr->sel.w = abs(tPtr->clicked.x - x);
1461 tPtr->sel.y = WMAX(0, WMIN(tPtr->clicked.y, y));
1462 tPtr->sel.h = abs(tPtr->clicked.y - y);
1464 tPtr->flags.ownsSelection = True;
1465 paintText(tPtr);
1469 static void
1470 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1471 void *cdata, WMData *data)
1473 Text *tPtr = (Text *)view->self;
1474 char *str;
1476 tPtr->flags.waitingForSelection = False;
1477 if (data) {
1478 str = (char*)WMDataBytes(data);
1479 if (0&&tPtr->parser) {
1480 /* parser is not yet well behaved to do this properly..*/
1481 (tPtr->parser) (tPtr, (void *) str);
1482 } else {
1483 insertTextInteractively(tPtr, str, strlen(str));
1485 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1486 } else {
1487 int n;
1488 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1489 if (str) {
1490 str[n] = 0;
1491 if (0&&tPtr->parser) {
1492 /* parser is not yet well behaved to do this properly..*/
1493 (tPtr->parser) (tPtr, (void *) str);
1494 } else {
1495 insertTextInteractively(tPtr, str, n);
1497 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1498 XFree(str);
1504 static void
1505 releaseSelection(Text *tPtr)
1507 TextBlock *tb = tPtr->firstTextBlock;
1509 while(tb) {
1510 tb->selected = False;
1511 tb = tb->next;
1513 tPtr->flags.ownsSelection = False;
1514 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY,
1515 CurrentTime);
1517 paintText(tPtr);
1520 static WMData *
1521 requestHandler(WMView *view, Atom selection, Atom target,
1522 void *cdata, Atom *type)
1524 Text *tPtr = view->self;
1525 Display *dpy = tPtr->view->screen->display;
1526 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1527 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1528 Atom _TARGETS;
1529 WMData *data;
1531 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
1532 char *text = WMGetTextSelected(tPtr);
1534 if(text) {
1535 WMData *data = WMCreateDataWithBytes(text, strlen(text));
1536 WMSetDataFormat(data, 8);
1537 wfree(text);
1538 *type = target;
1539 return data;
1542 } else if(target == XInternAtom(dpy, "PIXMAP", False)) {
1543 data = WMCreateDataWithBytes("paste a pixmap", 14);
1544 WMSetDataFormat(data, 8);
1545 *type = target;
1546 return data;
1549 _TARGETS = XInternAtom(dpy, "TARGETS", False);
1550 if (target == _TARGETS) {
1551 Atom *ptr;
1553 ptr = wmalloc(4 * sizeof(Atom));
1554 ptr[0] = _TARGETS;
1555 ptr[1] = XA_STRING;
1556 ptr[2] = TEXT;
1557 ptr[3] = COMPOUND_TEXT;
1559 data = WMCreateDataWithBytes(ptr, 4*4);
1560 WMSetDataFormat(data, 32);
1562 *type = target;
1563 return data;
1566 return NULL;
1570 static void
1571 lostHandler(WMView *view, Atom selection, void *cdata)
1573 releaseSelection((WMText *)view->self);
1576 static WMSelectionProcs selectionHandler = {
1577 requestHandler, lostHandler, NULL
1580 static void
1581 ownershipObserver(void *observerData, WMNotification *notification)
1583 WMText *to = (WMText *)observerData;
1584 WMText *tw = (WMText *)WMGetNotificationClientData(notification);
1585 if (to != tw)
1586 lostHandler(to->view, XA_PRIMARY, NULL);
1590 static void
1591 handleTextKeyPress(Text *tPtr, XEvent *event)
1593 char buffer[2];
1594 KeySym ksym;
1595 int control_pressed = False;
1596 // int h=1;
1598 if (((XKeyEvent *) event)->state & ControlMask)
1599 control_pressed = True;
1600 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = 0;
1602 switch(ksym) {
1604 case XK_Right:
1605 case XK_Left: {
1606 TextBlock *tb = tPtr->currentTextBlock;
1607 int x = tPtr->cursor.x + tPtr->visible.x;
1608 int y = tPtr->visible.y + tPtr->cursor.y + tPtr->cursor.h;
1609 int w, pos;
1611 #if 0
1612 if(!tb)
1613 break;
1614 if(tb->graphic) {
1615 if(tb->object) {
1616 w = WMWidgetWidth(tb->d.widget);
1617 } else {
1618 w = tb->d.pixmap->width;
1620 } else {
1621 pos = tPtr->tpos;
1622 w = WMWidthOfString(tb->d.font, &tb->text[pos], 1);
1625 cursorToTextPosition(tPtr, w + tPtr->cursor.x + tPtr->visible.x,
1626 3 + tPtr->visible.y + tPtr->cursor.y
1627 + tPtr->cursor.h - tPtr->vpos);
1628 if(x == tPtr->cursor.x + tPtr->visible.x) {
1629 printf("same %d %d\n", x, tPtr->cursor.x + tPtr->visible.x);
1630 cursorToTextPosition(tPtr, tPtr->visible.x,
1631 3 + tPtr->visible.y + tPtr->cursor.y + tPtr->cursor.h);
1633 paintText(tPtr);
1634 #endif
1636 break;
1638 case XK_Down:
1639 cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
1640 tPtr->clicked.y + tPtr->cursor.h - tPtr->vpos);
1641 paintText(tPtr);
1642 break;
1644 case XK_Up:
1645 cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
1646 tPtr->visible.y + tPtr->cursor.y - tPtr->vpos - 3);
1647 paintText(tPtr);
1648 break;
1650 case XK_BackSpace:
1651 case XK_Delete:
1652 case XK_KP_Delete:
1653 deleteTextInteractively(tPtr, ksym);
1654 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1655 break;
1657 case XK_Control_R :
1658 case XK_Control_L :
1659 control_pressed = True;
1660 break;
1662 case XK_Return:
1663 buffer[0] = '\n';
1664 default:
1665 if (buffer[0] != 0 && !control_pressed) {
1666 insertTextInteractively(tPtr, buffer, 1);
1667 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1669 } else if (control_pressed && ksym==XK_r) {
1670 Bool i = !tPtr->flags.rulerShown;
1671 WMShowTextRuler(tPtr, i);
1672 tPtr->flags.rulerShown = i;
1674 else if (control_pressed && buffer[0] == '\a')
1675 XBell(tPtr->view->screen->display, 0);
1678 if (!control_pressed && tPtr->flags.ownsSelection)
1679 releaseSelection(tPtr);
1682 static void
1683 handleWidgetPress(XEvent *event, void *data)
1685 TextBlock *tb = (TextBlock *)data;
1686 Text *tPtr;
1687 WMWidget *w;
1689 if (!tb)
1690 return;
1691 /* this little bit of nastiness here saves a boatload of trouble */
1692 w = (WMWidget *)(((W_VIEW(tb->d.widget))->parent)->self);
1693 if (W_CLASS(w) != WC_Text)
1694 return;
1695 tPtr = (Text*)w;
1696 tPtr->currentTextBlock = getFirstNonGraphicBlockFor(tb, 1);
1697 if (!tPtr->currentTextBlock)
1698 tPtr->currentTextBlock = tb;
1699 tPtr->tpos = 0;
1700 output(tPtr->currentTextBlock->text, tPtr->currentTextBlock->used);
1701 //if (!tPtr->flags.focused) {
1702 // WMSetFocusToWidget(tPtr);
1703 // tPtr->flags.focused = True;
1704 //}
1708 static void
1709 handleActionEvents(XEvent *event, void *data)
1711 Text *tPtr = (Text *)data;
1712 Display *dpy = event->xany.display;
1713 KeySym ksym;
1716 switch (event->type) {
1717 case KeyPress:
1718 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1719 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
1720 tPtr->flags.extendSelection = True;
1721 return;
1724 if (tPtr->flags.waitingForSelection)
1725 return;
1726 if (tPtr->flags.focused) {
1727 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1728 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1729 GrabModeAsync, GrabModeAsync, None,
1730 tPtr->view->screen->invisibleCursor, CurrentTime);
1731 tPtr->flags.pointerGrabbed = True;
1732 handleTextKeyPress(tPtr, event);
1734 } break;
1736 case KeyRelease:
1737 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1738 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
1739 tPtr->flags.extendSelection = False;
1740 return;
1741 //end modify flag so selection can be extended
1743 break;
1746 case MotionNotify:
1747 if (tPtr->flags.pointerGrabbed) {
1748 tPtr->flags.pointerGrabbed = False;
1749 XUngrabPointer(dpy, CurrentTime);
1752 if ((event->xmotion.state & Button1Mask)) {
1753 if (!tPtr->flags.ownsSelection) {
1754 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1755 event->xbutton.time, &selectionHandler, NULL);
1756 tPtr->flags.ownsSelection = True;
1758 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1760 break;
1763 case ButtonPress:
1764 tPtr->flags.buttonHeld = True;
1765 if (tPtr->flags.extendSelection) {
1766 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1767 return;
1769 if (event->xbutton.button == Button1) {
1771 if (!tPtr->flags.focused) {
1772 WMSetFocusToWidget(tPtr);
1773 tPtr->flags.focused = True;
1776 if (tPtr->flags.ownsSelection)
1777 releaseSelection(tPtr);
1778 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1779 paintText(tPtr);
1780 if (tPtr->flags.pointerGrabbed) {
1781 tPtr->flags.pointerGrabbed = False;
1782 XUngrabPointer(dpy, CurrentTime);
1783 break;
1787 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown)
1788 WMScrollText(tPtr, -16);
1789 else if (event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1790 WMScrollText(tPtr, 16);
1791 break;
1793 case ButtonRelease:
1794 tPtr->flags.buttonHeld = False;
1795 if (tPtr->flags.pointerGrabbed) {
1796 tPtr->flags.pointerGrabbed = False;
1797 XUngrabPointer(dpy, CurrentTime);
1798 break;
1800 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown
1801 || event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1802 break;
1804 if (event->xbutton.button == Button2) {
1805 char *text = NULL;
1806 int n;
1808 if (!tPtr->flags.editable) {
1809 XBell(dpy, 0);
1810 break;
1813 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1814 event->xbutton.time, pasteText, NULL)) {
1815 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1816 if (text) {
1817 text[n] = 0;
1818 if (0&&tPtr->parser) {
1819 /* parser is not yet well behaved to do this properly..*/
1820 (tPtr->parser) (tPtr, (void *) text);
1821 } else {
1822 insertTextInteractively(tPtr, text, n-1);
1824 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
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 += 1; /* field markers 0xFA and 0xCE */
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 memcpy(&text[where], tb->text, tb->used);
2124 where += tb->used;
2125 if(tb->graphic)
2126 text[where++] = 0xCE;
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, *ce = NULL;
2150 WMBag *bag;
2151 WMData *data;
2153 if (!tPtr)
2154 return NULL;
2156 stream = getStream(tPtr, sel);
2157 if(!stream)
2158 return NULL;
2160 bag = WMCreateArrayBagWithDestructor(4, releaseBagData);
2162 start = stream;
2163 while (start) {
2164 fa = strchr(start, 0xFA);
2165 if (fa) {
2166 data = WMCreateDataWithBytes((void *)start, (int)(fa - start));
2167 WMSetDataFormat(data, 8);
2168 //desc = wmalloc((int)(fa - start));
2169 //memcpy(desc, start, (int)(fa - start));
2170 WMPutInBag(bag, (void *) data);
2172 ce = strchr(fa, 0xCE);
2173 if (ce) {
2174 data = WMCreateDataWithBytes(fa+1, ((int)(ce - fa))-1);
2175 WMSetDataFormat(data, 32);
2176 WMPutInBag(bag, (void *) data);
2177 start = ce+1;
2178 } else {
2179 start = fa + 1;
2182 } else {
2183 if (start && strlen(start)) {
2184 data = WMCreateDataWithBytes((void *)start, strlen(start));
2185 WMSetDataFormat(data, 8);
2186 // desc = wmalloc(strlen(start));
2187 // memcpy(desc, start, strlen(start));
2188 WMPutInBag(bag, (void *) data);
2190 start = fa;
2194 wfree(stream);
2195 return bag;
2199 WMText *
2200 WMCreateText(WMWidget *parent)
2202 Text *tPtr = wmalloc(sizeof(Text));
2203 if (!tPtr) {
2204 printf("could not create text widget\n");
2205 return NULL;
2208 #if 0
2209 printf("sizeof:\n");
2210 printf(" TextBlock %d\n", sizeof(TextBlock));
2211 printf(" Section %d\n", sizeof(Section));
2212 printf(" WMRulerMargins %d\n", sizeof(WMRulerMargins));
2213 printf(" char * %d\n", sizeof(char *));
2214 printf(" void * %d\n", sizeof(void *));
2215 printf(" short %d\n", sizeof(short));
2216 printf(" Text %d\n", sizeof(Text));
2217 #endif
2219 memset(tPtr, 0, sizeof(Text));
2220 tPtr->widgetClass = WC_Text;
2221 tPtr->view = W_CreateView(W_VIEW(parent));
2222 if (!tPtr->view) {
2223 perror("could not create text's view\n");
2224 free(tPtr);
2225 return NULL;
2227 tPtr->view->self = tPtr;
2228 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
2229 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
2230 W_ResizeView(tPtr->view, 250, 200);
2231 tPtr->bgGC = WMColorGC(tPtr->view->screen->white);
2232 tPtr->fgGC = WMColorGC(tPtr->view->screen->black);
2233 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
2235 tPtr->ruler = NULL;
2236 tPtr->vS = NULL;
2237 tPtr->hS = NULL;
2239 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2241 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2243 tPtr->view->delegate = &_TextViewDelegate;
2245 #if DO_BLINK
2246 tPtr->timerID = NULL;
2247 #endif
2249 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
2250 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
2251 handleEvents, tPtr);
2253 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
2254 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
2255 handleActionEvents, tPtr);
2257 WMAddNotificationObserver(ownershipObserver, tPtr, "_lostOwnership", tPtr);
2259 #if 0
2260 WMSetViewDragDestinationProcs(tPtr->view, &_DragDestinationProcs);
2262 char *types[2] = {"application/X-color", NULL};
2263 WMRegisterViewForDraggedTypes(tPtr->view, types);
2265 #endif
2267 tPtr->firstTextBlock = NULL;
2268 tPtr->lastTextBlock = NULL;
2269 tPtr->currentTextBlock = NULL;
2270 tPtr->tpos = 0;
2272 tPtr->gfxItems = WMCreateArrayBag(4);
2274 tPtr->parser = NULL;
2275 tPtr->writer = NULL;
2277 tPtr->sel.x = tPtr->sel.y = 2;
2278 tPtr->sel.w = tPtr->sel.h = 0;
2280 tPtr->clicked.x = tPtr->clicked.y = 2;
2282 tPtr->visible.x = tPtr->visible.y = 2;
2283 tPtr->visible.h = tPtr->view->size.height;
2284 tPtr->visible.w = tPtr->view->size.width - 4;
2286 tPtr->cursor.x = -23;
2288 tPtr->docWidth = 0;
2289 tPtr->docHeight = 0;
2290 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
2291 default_bullet);
2292 tPtr->db = (Pixmap) NULL;
2294 tPtr->dmargins = WMGetRulerMargins(tPtr->ruler);
2296 tPtr->flags.rulerShown = False;
2297 tPtr->flags.monoFont = False;
2298 tPtr->flags.focused = False;
2299 tPtr->flags.editable = True;
2300 tPtr->flags.ownsSelection = False;
2301 tPtr->flags.pointerGrabbed = False;
2302 tPtr->flags.buttonHeld = False;
2303 tPtr->flags.waitingForSelection = False;
2304 tPtr->flags.extendSelection = False;
2305 tPtr->flags.frozen = False;
2306 tPtr->flags.cursorShown = True;
2307 tPtr->flags.clickPos = 1;
2308 tPtr->flags.ignoreNewLine = False;
2309 tPtr->flags.laidOut = False;
2310 tPtr->flags.prepend = False;
2311 tPtr->flags.relief = WRFlat;
2312 tPtr->flags.alignment = WALeft;
2314 return tPtr;
2317 void
2318 WMPrependTextStream(WMText *tPtr, char *text)
2320 if (!tPtr)
2321 return;
2323 if(!text)
2324 releaseSelection(tPtr);
2326 tPtr->flags.prepend = True;
2327 if (text && tPtr->parser)
2328 (tPtr->parser) (tPtr, (void *) text);
2329 else
2330 insertPlainText(tPtr, text);
2335 void
2336 WMAppendTextStream(WMText *tPtr, char *text)
2338 if (!tPtr)
2339 return;
2341 if(!text)
2342 releaseSelection(tPtr);
2344 tPtr->flags.prepend = False;
2345 if (text && tPtr->parser)
2346 (tPtr->parser) (tPtr, (void *) text);
2347 else
2348 insertPlainText(tPtr, text);
2353 char *
2354 WMGetTextStream(WMText *tPtr)
2356 return getStream(tPtr, 0);
2359 char *
2360 WMGetTextSelected(WMText *tPtr)
2362 return getStream(tPtr, 1);
2365 WMBag *
2366 WMGetTextStreamIntoBag(WMText *tPtr)
2368 return getStreamIntoBag(tPtr, 0);
2371 WMBag *
2372 WMGetTextSelectedIntoBag(WMText *tPtr)
2374 return getStreamIntoBag(tPtr, 1);
2378 void *
2379 WMCreateTextBlockWithObject(WMWidget *w, char *description, WMColor *color,
2380 unsigned short first, unsigned short reserved)
2382 TextBlock *tb;
2383 unsigned short length;
2385 if (!w || !description || !color)
2386 return NULL;
2388 tb = wmalloc(sizeof(TextBlock));
2389 if (!tb)
2390 return NULL;
2392 length = strlen(description);
2393 tb->text = (char *)wmalloc(length);
2394 memset(tb->text, 0, length);
2395 memcpy(tb->text, description, length);
2396 tb->used = length;
2397 tb->blank = False;
2398 tb->d.widget = w;
2399 tb->color = WMRetainColor(color);
2400 //&tb->margins = NULL;
2401 tb->allocated = 0;
2402 tb->first = first;
2403 tb->kanji = False;
2404 tb->graphic = True;
2405 tb->object = True;
2406 tb->underlined = False;
2407 tb->selected = False;
2408 tb->script = 0;
2409 tb->sections = NULL;
2410 tb->nsections = 0;
2411 tb->prior = NULL;
2412 tb->next = NULL;
2414 return tb;
2418 void *
2419 WMCreateTextBlockWithPixmap(WMPixmap *p, char *description, WMColor *color,
2420 unsigned short first, unsigned short reserved)
2422 TextBlock *tb;
2423 unsigned short length;
2425 if (!p || !description || !color)
2426 return NULL;
2428 tb = wmalloc(sizeof(TextBlock));
2429 if (!tb)
2430 return NULL;
2432 length = strlen(description);
2433 tb->text = (char *)wmalloc(length);
2434 memset(tb->text, 0, length);
2435 memcpy(tb->text, description, length);
2436 tb->used = length;
2437 tb->blank = False;
2438 tb->d.pixmap = p;
2439 tb->color = WMRetainColor(color);
2440 //&tb->margins = NULL;
2441 tb->allocated = 0;
2442 tb->first = first;
2443 tb->kanji = False;
2444 tb->graphic = True;
2445 tb->object = False;
2446 tb->underlined = False;
2447 tb->selected = False;
2448 tb->script = 0;
2449 tb->sections = NULL;
2450 tb->nsections = 0;
2451 tb->prior = NULL;
2452 tb->next = NULL;
2454 return tb;
2457 void *
2458 WMCreateTextBlockWithText(char *text, WMFont *font, WMColor *color,
2459 unsigned short first, unsigned short length)
2461 TextBlock *tb;
2463 if (!font || !color)
2464 return NULL;
2466 tb = wmalloc(sizeof(TextBlock));
2467 if (!tb)
2468 return NULL;
2470 tb->allocated = reqBlockSize(length);
2471 tb->text = (char *)wmalloc(tb->allocated);
2472 memset(tb->text, 0, tb->allocated);
2474 if (length < 1|| !text ) { // || *text == '\n') {
2475 *tb->text = ' ';
2476 tb->used = 1;
2477 tb->blank = True;
2478 } else {
2479 memcpy(tb->text, text, length);
2480 tb->used = length;
2481 tb->blank = False;
2484 tb->d.font = WMRetainFont(font);
2485 tb->color = WMRetainColor(color);
2486 tb->first = first;
2487 tb->kanji = False;
2488 tb->graphic = False;
2489 tb->underlined = False;
2490 tb->selected = False;
2491 tb->script = 0;
2492 tb->sections = NULL;
2493 tb->nsections = 0;
2494 tb->prior = NULL;
2495 tb->next = NULL;
2496 return tb;
2499 void
2500 WMSetTextBlockProperties(void *vtb, unsigned int first,
2501 unsigned int kanji, unsigned int underlined, int script,
2502 WMRulerMargins margins)
2504 TextBlock *tb = (TextBlock *) vtb;
2505 if (!tb)
2506 return;
2508 tb->first = first;
2509 tb->kanji = kanji;
2510 tb->underlined = underlined;
2511 tb->script = script;
2512 #if 0
2513 tb->margins.left = margins.left;
2514 tb->margins.first = margins.first;
2515 tb->margins.body = margins.body;
2516 tb->margins.right = margins.right;
2517 for: tb->margins.tabs = margins.tabs;
2518 #endif
2521 void
2522 WMGetTextBlockProperties(void *vtb, unsigned int *first,
2523 unsigned int *kanji, unsigned int *underlined, int *script,
2524 WMRulerMargins *margins)
2526 TextBlock *tb = (TextBlock *) vtb;
2527 if (!tb)
2528 return;
2530 if (first) *first = tb->first;
2531 if (kanji) *kanji = tb->kanji;
2532 if (underlined) *underlined = tb->underlined;
2533 if (script) *script = tb->script;
2535 #if 0
2536 if (margins) {
2537 (*margins).left = tb->margins.left;
2538 (*margins).first = tb->margins.first;
2539 (*margins).body = tb->margins.body;
2540 (*margins).right = tb->margins.right;
2541 //for: (*margins).tabs = tb->margins.tabs;
2543 #endif
2548 void
2549 WMPrependTextBlock(WMText *tPtr, void *vtb)
2551 TextBlock *tb = (TextBlock *)vtb;
2553 if (!tPtr || !tb)
2554 return;
2556 if (tb->graphic) {
2557 if(tb->object) {
2558 WMWidget *w = tb->d.widget;
2559 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
2560 handleWidgetPress, tb);
2561 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
2562 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
2563 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
2566 WMPutInBag(tPtr->gfxItems, (void *)tb);
2567 tPtr->tpos = 0;
2568 } else tPtr->tpos = tb->used;
2570 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
2571 tb->next = tb->prior = NULL;
2572 tPtr->lastTextBlock = tPtr->firstTextBlock
2573 = tPtr->currentTextBlock = tb;
2574 return;
2577 tb->next = tPtr->currentTextBlock;
2578 tb->prior = tPtr->currentTextBlock->prior;
2579 if (tPtr->currentTextBlock->prior)
2580 tPtr->currentTextBlock->prior->next = tb;
2582 tPtr->currentTextBlock->prior = tb;
2583 if (!tb->prior)
2584 tPtr->firstTextBlock = tb;
2586 tPtr->currentTextBlock = tb;
2590 void
2591 WMAppendTextBlock(WMText *tPtr, void *vtb)
2593 TextBlock *tb = (TextBlock *)vtb;
2595 if (!tPtr || !tb)
2596 return;
2598 if (tb->graphic) {
2599 if(tb->object) {
2600 WMWidget *w = tb->d.widget;
2601 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
2602 handleWidgetPress, tb);
2603 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
2604 (W_VIEW(w))->attribs.cursor =
2605 tPtr->view->screen->defaultCursor;
2606 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
2609 WMPutInBag(tPtr->gfxItems, (void *)tb);
2610 tPtr->tpos = 0;
2611 } else tPtr->tpos = tb->used;
2613 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
2614 tb->next = tb->prior = NULL;
2615 tPtr->lastTextBlock = tPtr->firstTextBlock
2616 = tPtr->currentTextBlock = tb;
2617 return;
2620 tb->next = tPtr->currentTextBlock->next;
2621 tb->prior = tPtr->currentTextBlock;
2622 if (tPtr->currentTextBlock->next)
2623 tPtr->currentTextBlock->next->prior = tb;
2625 tPtr->currentTextBlock->next = tb;
2627 if (!tb->next)
2628 tPtr->lastTextBlock = tb;
2630 tPtr->currentTextBlock = tb;
2633 void *
2634 WMRemoveTextBlock(WMText *tPtr)
2636 TextBlock *tb = NULL;
2638 if (!tPtr || !tPtr->firstTextBlock || !tPtr->lastTextBlock
2639 || !tPtr->currentTextBlock) {
2640 printf("cannot remove non existent TextBlock!\b");
2641 return tb;
2644 tb = tPtr->currentTextBlock;
2645 if (tb->graphic) {
2646 WMRemoveFromBag(tPtr->gfxItems, (void *)tb);
2648 if(tb->object) {
2649 WMDeleteEventHandler(W_VIEW(tb->d.widget), ButtonPressMask,
2650 handleWidgetPress, tb);
2651 WMUnmapWidget(tb->d.widget);
2655 if (tPtr->currentTextBlock == tPtr->firstTextBlock) {
2656 if (tPtr->currentTextBlock->next)
2657 tPtr->currentTextBlock->next->prior = NULL;
2659 tPtr->firstTextBlock = tPtr->currentTextBlock->next;
2660 tPtr->currentTextBlock = tPtr->firstTextBlock;
2662 } else if (tPtr->currentTextBlock == tPtr->lastTextBlock) {
2663 tPtr->currentTextBlock->prior->next = NULL;
2664 tPtr->lastTextBlock = tPtr->currentTextBlock->prior;
2665 tPtr->currentTextBlock = tPtr->lastTextBlock;
2666 } else {
2667 tPtr->currentTextBlock->prior->next = tPtr->currentTextBlock->next;
2668 tPtr->currentTextBlock->next->prior = tPtr->currentTextBlock->prior;
2669 tPtr->currentTextBlock = tPtr->currentTextBlock->next;
2672 return (void *)tb;
2675 void
2676 WMDestroyTextBlock(WMText *tPtr, void *vtb)
2678 TextBlock *tb = (TextBlock *)vtb;
2679 if (!tPtr || !tb)
2680 return;
2682 if (tb->graphic) {
2683 if(tb->object) {
2684 /* naturally, there's a danger to destroying
2685 widgets whose action brings us here:
2686 ie. press a button to destroy it... need to
2687 find a safer way. till then... this stays commented out */
2688 //WMDestroyWidget(tb->d.widget);
2689 //wfree(tb->d.widget);
2690 tb->d.widget = NULL;
2691 } else {
2692 WMReleasePixmap(tb->d.pixmap);
2693 tb->d.pixmap = NULL;
2695 } else {
2696 WMReleaseFont(tb->d.font);
2699 WMReleaseColor(tb->color);
2700 if (tb->sections && tb->nsections > 0)
2701 wfree(tb->sections);
2702 wfree(tb->text);
2703 wfree(tb);
2704 tb = NULL;
2708 void
2709 WMRefreshText(WMText *tPtr, int vpos, int hpos)
2711 if (!tPtr || vpos<0 || hpos<0)
2712 return;
2714 if (tPtr->flags.frozen)
2715 return;
2717 if(tPtr->flags.monoFont) {
2718 int j, c = WMGetBagItemCount(tPtr->gfxItems);
2719 TextBlock *tb;
2721 /* make sure to unmap widgets no matter where they are */
2722 for(j=0; j<c; j++) {
2723 if ((tb = (TextBlock *) WMGetFromBag(tPtr->gfxItems, j))) {
2724 if (tb->object && ((W_VIEW(tb->d.widget))->flags.mapped))
2725 WMUnmapWidget(tb->d.widget);
2731 if (tPtr->vpos != vpos) {
2732 if (vpos < 0 || tPtr->docHeight < tPtr->visible.h) {
2733 tPtr->vpos = 0;
2734 } else if(tPtr->docHeight - vpos > tPtr->visible.h - tPtr->visible.y) {
2735 tPtr->vpos = vpos;
2736 } else {
2737 tPtr->vpos = tPtr->docHeight - tPtr->visible.h;
2741 tPtr->flags.laidOut = False;
2742 layOutDocument(tPtr);
2743 updateScrollers(tPtr);
2744 paintText(tPtr);
2748 void
2749 WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
2751 if (!tPtr)
2752 return;
2754 if (color)
2755 tPtr->fgGC = WMColorGC(color);
2756 else
2757 tPtr->fgGC = WMColorGC(WMBlackColor(tPtr->view->screen));
2759 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2762 void
2763 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
2765 if (!tPtr)
2766 return;
2768 if (color) {
2769 tPtr->bgGC = WMColorGC(color);
2770 W_SetViewBackgroundColor(tPtr->view, color);
2771 } else {
2772 tPtr->bgGC = WMColorGC(WMWhiteColor(tPtr->view->screen));
2773 W_SetViewBackgroundColor(tPtr->view,
2774 WMWhiteColor(tPtr->view->screen));
2777 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2780 void
2781 WMSetTextRelief(WMText *tPtr, WMReliefType relief)
2783 if (!tPtr)
2784 return;
2785 tPtr->flags.relief = relief;
2786 paintText(tPtr);
2789 void
2790 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
2792 if (!tPtr)
2793 return;
2795 if (shouldhave && !tPtr->hS) {
2796 tPtr->hS = WMCreateScroller(tPtr);
2797 (W_VIEW(tPtr->hS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2798 (W_VIEW(tPtr->hS))->attribFlags |= CWOverrideRedirect | CWCursor;
2799 WMSetScrollerArrowsPosition(tPtr->hS, WSAMaxEnd);
2800 WMSetScrollerAction(tPtr->hS, scrollersCallBack, tPtr);
2801 } else if (!shouldhave && tPtr->hS) {
2802 WMUnmapWidget(tPtr->hS);
2803 WMDestroyWidget(tPtr->hS);
2804 tPtr->hS = NULL;
2807 tPtr->hpos = 0;
2808 tPtr->prevHpos = 0;
2809 textDidResize(tPtr->view->delegate, tPtr->view);
2813 void
2814 WMSetTextHasRuler(WMText *tPtr, Bool shouldhave)
2816 if (!tPtr)
2817 return;
2819 if(shouldhave && !tPtr->ruler) {
2820 tPtr->ruler = WMCreateRuler(tPtr);
2821 (W_VIEW(tPtr->ruler))->attribs.cursor =
2822 tPtr->view->screen->defaultCursor;
2823 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
2824 WMSetRulerReleaseAction(tPtr->ruler, rulerReleaseCallBack, tPtr);
2825 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
2826 } else if(!shouldhave && tPtr->ruler) {
2827 WMShowTextRuler(tPtr, False);
2828 WMDestroyWidget(tPtr->ruler);
2829 tPtr->ruler = NULL;
2831 textDidResize(tPtr->view->delegate, tPtr->view);
2834 void
2835 WMShowTextRuler(WMText *tPtr, Bool show)
2837 if(!tPtr)
2838 return;
2839 if(!tPtr->ruler)
2840 return;
2842 if(tPtr->flags.monoFont)
2843 show = False;
2845 tPtr->flags.rulerShown = show;
2846 if(show) {
2847 WMMapWidget(tPtr->ruler);
2848 } else {
2849 WMUnmapWidget(tPtr->ruler);
2852 textDidResize(tPtr->view->delegate, tPtr->view);
2855 Bool
2856 WMGetTextRulerShown(WMText *tPtr)
2858 if(!tPtr)
2859 return 0;
2861 if(!tPtr->ruler)
2862 return 0;
2864 return tPtr->flags.rulerShown;
2868 void
2869 WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
2871 if (!tPtr)
2872 return;
2874 if (shouldhave && !tPtr->vS) {
2875 tPtr->vS = WMCreateScroller(tPtr);
2876 (W_VIEW(tPtr->vS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2877 (W_VIEW(tPtr->vS))->attribFlags |= CWOverrideRedirect | CWCursor;
2878 WMSetScrollerArrowsPosition(tPtr->vS, WSAMaxEnd);
2879 WMSetScrollerAction(tPtr->vS, scrollersCallBack, tPtr);
2880 } else if (!shouldhave && tPtr->vS) {
2881 WMUnmapWidget(tPtr->vS);
2882 WMDestroyWidget(tPtr->vS);
2883 tPtr->vS = NULL;
2886 tPtr->vpos = 0;
2887 tPtr->prevVpos = 0;
2888 textDidResize(tPtr->view->delegate, tPtr->view);
2893 Bool
2894 WMScrollText(WMText *tPtr, int amount)
2896 Bool scroll=False;
2897 if (!tPtr)
2898 return False;
2899 if (amount == 0 || !tPtr->view->flags.realized)
2900 return False;
2902 if (amount < 0) {
2903 if (tPtr->vpos > 0) {
2904 if (tPtr->vpos > amount) tPtr->vpos += amount;
2905 else tPtr->vpos=0;
2906 scroll=True;
2907 } } else {
2908 int limit = tPtr->docHeight - tPtr->visible.h;
2909 if (tPtr->vpos < limit) {
2910 if (tPtr->vpos < limit-amount) tPtr->vpos += amount;
2911 else tPtr->vpos = limit;
2912 scroll = True;
2913 } }
2915 if (scroll && tPtr->vpos != tPtr->prevVpos) {
2916 updateScrollers(tPtr);
2917 paintText(tPtr);
2919 tPtr->prevVpos = tPtr->vpos;
2920 return scroll;
2923 Bool
2924 WMPageText(WMText *tPtr, Bool direction)
2926 if (!tPtr) return False;
2927 if (!tPtr->view->flags.realized) return False;
2929 return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
2932 void
2933 WMSetTextEditable(WMText *tPtr, Bool editable)
2935 if (!tPtr)
2936 return;
2937 tPtr->flags.editable = editable;
2940 int
2941 WMGetTextEditable(WMText *tPtr)
2943 if (!tPtr)
2944 return 0;
2945 return tPtr->flags.editable;
2948 void
2949 WMSetTextIgnoresNewline(WMText *tPtr, Bool ignore)
2951 if (!tPtr)
2952 return;
2953 tPtr->flags.ignoreNewLine = ignore;
2956 Bool
2957 WMGetTextIgnoresNewline(WMText *tPtr)
2959 if (!tPtr)
2960 return True;
2961 return tPtr->flags.ignoreNewLine;
2964 void
2965 WMSetTextUsesMonoFont(WMText *tPtr, Bool mono)
2967 if (!tPtr)
2968 return;
2969 if (mono && tPtr->flags.rulerShown)
2970 WMShowTextRuler(tPtr, False);
2972 tPtr->flags.monoFont = mono;
2973 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2976 Bool
2977 WMGetTextUsesMonoFont(WMText *tPtr)
2979 if (!tPtr)
2980 return True;
2981 return tPtr->flags.monoFont;
2985 void
2986 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
2988 if (!tPtr)
2989 return;
2991 WMReleaseFont(tPtr->dFont);
2992 if (font)
2993 tPtr->dFont = font;
2994 else
2995 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2998 WMFont *
2999 WMGetTextDefaultFont(WMText *tPtr)
3001 if (!tPtr)
3002 return NULL;
3003 else
3004 return tPtr->dFont;
3007 void
3008 WMSetTextAlignment(WMText *tPtr, WMAlignment alignment)
3010 if (!tPtr)
3011 return;
3012 tPtr->flags.alignment = alignment;
3013 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
3016 void
3017 WMSetTextParser(WMText *tPtr, WMAction *parser)
3019 if (!tPtr)
3020 return;
3021 tPtr->parser = parser;
3024 void
3025 WMSetTextWriter(WMText *tPtr, WMAction *writer)
3027 if (!tPtr)
3028 return;
3029 tPtr->writer = writer;
3032 int
3033 WMGetTextInsertType(WMText *tPtr)
3035 if (!tPtr)
3036 return 0;
3037 return tPtr->flags.prepend;
3041 void
3042 WMSetTextSelectionColor(WMText *tPtr, WMColor *color)
3044 TextBlock *tb;
3045 if (!tPtr || !color)
3046 return;
3048 tb = tPtr->firstTextBlock;
3049 if (!tb || !tPtr->flags.ownsSelection)
3050 return;
3052 while (tb) {
3053 if (tb->selected) {
3055 if ( (tb->s_end - tb->s_begin == tb->used) || tb->graphic) {
3056 if(tb->color)
3057 WMReleaseColor(tb->color);
3058 tb->color = WMRetainColor(color);
3060 } else if (tb->s_end <= tb->used) {
3062 TextBlock *otb = tb;
3063 int count=0;
3064 TextBlock *ntb = (TextBlock *) WMCreateTextBlockWithText(
3065 &(tb->text[tb->s_begin]),
3066 tb->d.font, color, False, (tb->s_end - tb->s_begin));
3068 if (ntb) {
3069 ntb->selected = True;
3070 ntb->s_begin = 0;
3071 ntb->s_end = ntb->used;
3072 WMAppendTextBlock(tPtr, ntb);
3073 count++;
3076 #if 0
3077 if (tb->used > tb->s_end) {
3078 ntb = (TextBlock *) WMCreateTextBlockWithText(
3079 &(tb->text[tb->s_end]),
3080 tb->d.font, tb->color, False, tb->used - tb->s_end);
3081 if (ntb) {
3082 ntb->selected = True;
3083 ntb->s_begin = 0;
3084 ntb->s_end = ntb->used;
3085 WMAppendTextBlock(tPtr, ntb);
3086 count++;
3089 #endif
3091 if (count == 1)
3092 tb = otb->next;
3093 else if (count == 2)
3094 tb = otb->next->next;
3096 tb->used = tb->s_end = tb->s_begin;
3100 tb = tb->next;
3103 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
3106 void
3107 WMSetTextSelectionFont(WMText *tPtr, WMFont *font)
3109 TextBlock *tb;
3110 if (!tPtr || !font)
3111 return;
3113 tb = tPtr->firstTextBlock;
3114 if (!tb || !tPtr->flags.ownsSelection)
3115 return;
3117 while (tb) {
3118 if (!tb->graphic)
3119 tb->d.font = WMRetainFont(font);
3120 tb = tb->next;
3122 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
3125 void
3126 WMFreezeText(WMText *tPtr)
3128 if (!tPtr)
3129 return;
3131 tPtr->flags.frozen = True;
3134 void
3135 WMThawText(WMText *tPtr)
3137 if (!tPtr)
3138 return;
3140 tPtr->flags.frozen = False;
3144 Bool
3145 WMFindInTextStream(WMText *tPtr, char *needle)
3147 char *haystack = NULL;
3148 Bool result;
3150 if (!tPtr || !needle)
3151 return False;
3153 if ( !(haystack = "WMGetTextStream(tPtr)"))
3154 return False;
3156 result = (Bool) strstr(haystack, needle);
3157 wfree(haystack);
3158 return result;