Text Selection 99% complete, replaceSelection added
[wmaker-crm.git] / WINGs / wtext.c
blobdc5abe1db899363cb941d17205a87af5ea509341
1 /*
2 * WINGs WMText: multi-line/font/color/graphic text widget
4 * Copyright (c) 1999-2000 Nwanua Elumeze
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "WINGsP.h"
23 #include <X11/keysym.h>
24 #include <X11/Xatom.h>
27 #define DO_BLINK 0
29 /* TODO:
31 * - assess danger of destroying widgets whose actions link to other pages
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
42 /* a Section is a section of a TextBlock that describes what parts
43 of a TextBlock has been laid out on which "line"...
44 o this greatly aids redraw, scroll and selection.
45 o this is created during layoutLine, but may be later modified.
46 o there may be many Sections per TextBlock, hence the array */
47 typedef struct {
48 unsigned int x, y; /* where to draw it from */
49 unsigned short w, h; /* its width and height */
50 unsigned int _y; /* the "line" it and other textblocks are on */
51 unsigned short begin; /* where the layout begins */
52 unsigned short end ; /* where it ends */
53 unsigned short last; /* last section on a line? */
54 unsigned short RESERVED:15;
55 } Section;
58 /* a TextBlock is a doubly-linked list of TextBlocks containing:
59 o text for the block, color and font
60 o or a pointer to the pixmap
61 o OR a pointer to the widget and the (text) description for its graphic
64 typedef struct _TextBlock {
65 struct _TextBlock *next; /* next text block in linked list */
66 struct _TextBlock *prior; /* prior text block in linked list */
68 char *text; /* pointer to text (could be kanji) */
69 /* or to the object's description */
70 union {
71 WMFont *font; /* the font */
72 WMWidget *widget; /* the embedded widget */
73 WMPixmap *pixmap; /* the pixmap */
74 } d; /* description */
76 unsigned short used; /* number of chars in this block */
77 unsigned short allocated; /* size of allocation (in chars) */
78 WMColor *color; /* the color */
79 WMRulerMargins margins; /* first & body indentations, tabstops, etc... */
81 Section *sections; /* the region for layouts (a growable array) */
82 /* an _array_! of size _nsections_ */
84 unsigned short s_begin; /* where the selection begins */
85 unsigned short s_end; /* where it ends */
87 unsigned int first:1; /* first TextBlock in paragraph */
88 unsigned int blank:1; /* ie. blank paragraph */
89 unsigned int kanji:1; /* is of 16-bit characters or not */
90 unsigned int graphic:1; /* graphic or text: text=0 */
91 unsigned int object:1; /* embedded object or pixmap */
92 unsigned int underlined:1; /* underlined or not */
93 unsigned int selected:1; /* selected or not */
94 unsigned int nsections:8; /* over how many "lines" a TextBlock wraps */
95 int script:8; /* script in points: negative for subscript */
96 unsigned int RESERVED:9;
97 } TextBlock;
100 /* somehow visible.h beats the hell outta visible.size.height :-) */
101 typedef struct {
102 unsigned int y;
103 unsigned int x;
104 unsigned int h;
105 unsigned int w;
106 } myRect;
109 typedef struct W_Text {
110 W_Class widgetClass; /* the class number of this widget */
111 W_View *view; /* the view referring to this instance */
113 WMRuler *ruler; /* the ruler widget to manipulate paragraphs */
115 WMScroller *vS; /* the vertical scroller */
116 unsigned int vpos; /* the current vertical position */
117 unsigned int prevVpos; /* the previous vertical position */
119 WMScroller *hS; /* the horizontal scroller */
120 unsigned int hpos; /* the current horizontal position */
121 unsigned int prevHpos; /* the previous horizontal position */
123 WMFont *dFont; /* the default font */
124 WMColor *dColor; /* the default color */
125 WMPixmap *dBulletPix; /* the default pixmap for bullets */
126 WMRulerMargins dmargins; /* default margins */
128 GC bgGC; /* the background GC to draw with */
129 GC fgGC; /* the foreground GC to draw with */
130 Pixmap db; /* the buffer on which to draw */
132 myRect visible; /* the actual rectangle that can be drawn into */
133 myRect cursor; /* the position and (height) of cursor */
134 myRect sel; /* the selection rectangle */
135 unsigned int docWidth; /* the width of the entire document */
136 unsigned int docHeight; /* the height of the entire document */
138 TextBlock *firstTextBlock;
139 TextBlock *lastTextBlock;
140 TextBlock *currentTextBlock;
143 WMBag *gfxItems; /* a nice bag containing graphic items */
145 #if DO_BLINK
146 WMHandlerID timerID; /* for nice twinky-winky */
147 #endif
148 WMPoint clicked; /* where in the _document_ was clicked */
149 unsigned short tpos; /* the position in the currentTextBlock */
150 unsigned short RESERVED; /* space taker upper... */
152 WMAction *parser;
153 WMAction *writer;
155 struct {
156 unsigned int monoFont:1; /* whether to ignore formats */
157 unsigned int focused:1; /* whether this instance has input focus */
158 unsigned int editable:1; /* "silly user, you can't edit me" */
159 unsigned int ownsSelection:1; /* "I ownz the current selection!" */
160 unsigned int pointerGrabbed:1;/* "heh, gib me pointer" */
161 unsigned int buttonHeld:1; /* the user is holding down the button */
162 unsigned int waitingForSelection:1; /* dum dee dumm... */
163 unsigned int extendSelection:1; /* shift-drag to select more regions */
165 unsigned int rulerShown:1; /* whether the ruler is shown or not */
166 unsigned int frozen:1; /* whether screen updates are to be made */
167 unsigned int cursorShown:1; /* whether to show the cursor */
168 unsigned int clickPos:1; /* clicked before=0 or after=1 a graphic: */
169 /* (within counts as after too) */
171 unsigned int ignoreNewLine:1;/* turn it into a ' ' in streams > 1 */
172 unsigned int laidOut:1; /* have the TextBlocks all been laid out */
173 unsigned int prepend:1; /* prepend=1, append=0 (for parsers) */
174 WMAlignment alignment:2; /* the alignment for text */
175 WMReliefType relief:3; /* the relief to display with */
176 unsigned int RESERVED:12;
177 } flags;
178 } Text;
181 static char *default_bullet[] = {
182 "6 6 4 1",
183 " c None s None", ". c black",
184 "X c white", "o c #808080",
185 " ... ",
186 ".XX.. ",
187 ".XX..o",
188 ".....o",
189 " ...oo",
190 " ooo "};
193 static Bool
194 sectionWasSelected(Text *tPtr, TextBlock *tb, XRectangle *rect, int s)
196 unsigned short i, w, lw, selected = False;
197 myRect sel;
199 if (tPtr->sel.w < 2)
200 return False;
202 /* if selection rectangle completely encloses the section */
203 if ((tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
204 && (tb->sections[s]._y + tb->sections[s].h
205 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
206 sel.x = 0;
207 sel.w = tPtr->visible.w;
208 selected = True;
210 /* or if it starts on a line and then goes further down */
211 } else if ((tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
212 && (tb->sections[s]._y + tb->sections[s].h
213 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)
214 && (tb->sections[s]._y + tb->sections[s].h
215 >= tPtr->visible.y + tPtr->sel.y) ) {
216 sel.x = tPtr->sel.x;
217 sel.w = tPtr->visible.w;
218 selected = True;
220 /* or if it begins before a line, but ends on it */
221 } else if ((tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
222 && (tb->sections[s]._y + tb->sections[s].h
223 >= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)
224 && (tb->sections[s]._y
225 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
227 sel.x = 0;
228 sel.w = tPtr->sel.x + tPtr->sel.w;
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 && (tb->sections[s]._y + tb->sections[s].h
234 >= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
235 sel.x = tPtr->sel.x;
236 sel.w = tPtr->sel.w;
237 selected = True;
240 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;
250 if (tb->graphic) {
251 if ( tb->sections[s].x + tb->sections[s].w <= sel.x + sel.w
252 && tb->sections[s].x >= sel.x) {
253 rect->width = tb->sections[s].w;
254 rect->x = tb->sections[s].x;
255 selected = True;
258 } else {
260 i = tb->sections[s].begin;
261 lw = 0;
263 //if ( tb->sections[s].x >= sel.x)
264 // 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:
287 rect->x = tb->sections[s].x + lw;
288 lw = 0;
289 while(++i <= tb->sections[s].end) {
291 w = WMWidthOfString(tb->d.font, &(tb->text[i-1]), 1);
292 lw += w;
294 if (lw + rect->x >= sel.x + sel.w
295 || i == tb->sections[s].end ) {
297 if (i != tb->sections[s].end) {
298 lw -= w;
299 i--;
301 rect->width = lw;
302 tb->s_end = (tb->selected? WMAX(tb->s_end, i) : i);
303 selected = True;
304 break;
310 if (selected) {
311 rect->y = tb->sections[s]._y - tPtr->vpos;
312 rect->height = tb->sections[s].h;
314 return selected;
318 static void
319 removeSelection(Text *tPtr)
321 TextBlock *tb = NULL;
323 if (!(tb = tPtr->firstTextBlock))
324 return;
326 while (tb) {
327 if (tb->selected) {
329 if ( (tb->s_end - tb->s_begin == tb->used) || tb->graphic) {
330 tPtr->currentTextBlock = tb;
331 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
332 tb = tPtr->currentTextBlock;
333 if (tb)
334 tPtr->tpos = 0;
335 continue;
337 } else if (tb->s_end <= tb->used) {
338 memmove(&(tb->text[tb->s_begin]),
339 &(tb->text[tb->s_end]), tb->used - tb->s_end);
340 tb->used -= (tb->s_end - tb->s_begin);
341 tb->selected = False;
342 tPtr->tpos = tb->s_begin;
347 tb = tb->next;
352 static void
353 paintText(Text *tPtr)
355 TextBlock *tb = tPtr->firstTextBlock;
356 WMFont *font;
357 GC gc, greyGC;
358 char *text;
359 int len, y, c, s, done=False;
360 int prev_y=-23;
361 WMScreen *scr = tPtr->view->screen;
362 Display *dpy = tPtr->view->screen->display;
363 Window win = tPtr->view->window;
365 if (!tPtr->view->flags.realized || !tPtr->db || tPtr->flags.frozen)
366 return;
368 XFillRectangle(dpy, tPtr->db, tPtr->bgGC,
369 0, 0, WMWidgetWidth(tPtr), WMWidgetWidth(tPtr));
370 //tPtr->visible.w, tPtr->visible.h);
372 tb = tPtr->firstTextBlock;
373 if (!tb)
374 goto _copy_area;
377 if (tPtr->flags.ownsSelection)
378 greyGC = WMColorGC(WMGrayColor(scr));
380 done = False;
382 while (!done && tb) {
384 if (tb->graphic) {
385 tb = tb->next;
386 continue;
389 tb->selected = False;
391 for(s=0; s<tb->nsections && !done; s++) {
393 if (tb->sections[s]._y > tPtr->vpos + tPtr->visible.h) {
394 done = True;
395 break;
398 if ( tb->sections[s].y + tb->sections[s].h < tPtr->vpos)
399 continue;
401 if (tPtr->flags.monoFont) {
402 font = tPtr->dFont;
403 gc = tPtr->fgGC;
404 } else {
405 font = tb->d.font;
406 gc = WMColorGC(tb->color);
409 if (tPtr->flags.ownsSelection) {
410 XRectangle rect;
412 if ( sectionWasSelected(tPtr, tb, &rect, s)) {
413 tb->selected = True;
414 XFillRectangle(dpy, tPtr->db, greyGC,
415 rect.x, rect.y, rect.width, rect.height);
419 prev_y = tb->sections[s]._y;
421 len = tb->sections[s].end - tb->sections[s].begin;
422 text = &(tb->text[tb->sections[s].begin]);
423 y = tb->sections[s].y - tPtr->vpos;
424 WMDrawString(scr, tPtr->db, gc, font,
425 tb->sections[s].x, y, text, len);
427 if (tb->underlined) {
428 XDrawLine(dpy, tPtr->db, gc,
429 tb->sections[s].x, y + font->y + 1,
430 tb->sections[s].x + tb->sections[s].w, y + font->y + 1);
435 tb = (!done? tb->next : NULL);
439 c = WMGetBagItemCount(tPtr->gfxItems);
440 if (c > 0 && !tPtr->flags.monoFont) {
441 int j, h;
443 for(j=0; j<c; j++) {
444 tb = (TextBlock *) WMGetFromBag(tPtr->gfxItems, j);
445 if (tb->sections[0]._y + tb->sections[0].h <= tPtr->vpos
446 || tb->sections[0]._y >= tPtr->vpos + tPtr->visible.h ) {
448 if(tb->object) {
449 if ((W_VIEW(tb->d.widget))->flags.mapped) {
450 WMUnmapWidget(tb->d.widget);
453 } else {
454 if(tb->object) {
455 if (!(W_VIEW(tb->d.widget))->flags.mapped) {
456 if (!(W_VIEW(tb->d.widget))->flags.realized)
457 WMRealizeWidget(tb->d.widget);
458 WMMapWidget(tb->d.widget);
459 WMLowerWidget(tb->d.widget);
463 if (tPtr->flags.ownsSelection) {
464 XRectangle rect;
466 if ( sectionWasSelected(tPtr, tb, &rect, s)) {
467 tb->selected = True;
468 XFillRectangle(dpy, tPtr->db, greyGC,
469 rect.x, rect.y, rect.width, rect.height);
473 if(tb->object) {
474 WMMoveWidget(tb->d.widget,
475 3 + tb->sections[0].x + tPtr->visible.x,
476 tb->sections[0].y + tPtr->visible.y - tPtr->vpos);
477 h = WMWidgetHeight(tb->d.widget) + 1;
478 } else {
479 WMDrawPixmap(tb->d.pixmap, tPtr->db,
480 tb->sections[0].x, tb->sections[0].y - tPtr->vpos);
481 h = tb->d.pixmap->height + 1;
484 if (tb->underlined) {
485 XDrawLine(dpy, tPtr->db, WMColorGC(tb->color),
486 tb->sections[0].x,
487 tb->sections[0].y + h,
488 tb->sections[0].x + tb->sections[0].w,
489 tb->sections[0].y + h);
490 } } } }
493 _copy_area:
494 if (tPtr->flags.editable && tPtr->flags.cursorShown
495 && tPtr->cursor.x != -23 && tPtr->flags.focused) {
496 int y = tPtr->cursor.y - tPtr->vpos;
497 XDrawLine(dpy, tPtr->db, tPtr->fgGC,
498 tPtr->cursor.x, y,
499 tPtr->cursor.x, y + tPtr->cursor.h);
502 XCopyArea(dpy, tPtr->db, win, tPtr->bgGC,
503 0, 0,
504 tPtr->visible.w, tPtr->visible.h,
505 tPtr->visible.x, tPtr->visible.y);
507 W_DrawRelief(scr, win, 0, 0,
508 tPtr->view->size.width, tPtr->view->size.height,
509 tPtr->flags.relief);
511 if (tPtr->ruler && tPtr->flags.rulerShown)
512 XDrawLine(dpy, win,
513 tPtr->fgGC, 2, 42,
514 tPtr->view->size.width-4, 42);
519 #if DO_BLINK
521 #define CURSOR_BLINK_ON_DELAY 600
522 #define CURSOR_BLINK_OFF_DELAY 400
524 static void
525 blinkCursor(void *data)
527 Text *tPtr = (Text*)data;
529 if (tPtr->flags.cursorShown) {
530 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY,
531 blinkCursor, data);
532 } else {
533 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
534 blinkCursor, data);
536 paintText(tPtr);
537 tPtr->flags.cursorShown = !tPtr->flags.cursorShown;
539 #endif
541 static TextBlock *
542 getFirstNonGraphicBlockFor(TextBlock *tb, short dir)
544 if (!tb)
545 return NULL;
546 while (tb) {
547 if (!tb->graphic)
548 break;
549 tb = (dir? tb->next : tb->prior);
552 return tb;
556 static void
557 cursorToTextPosition(Text *tPtr, int x, int y)
559 TextBlock *tb = NULL;
560 int done=False, s, pos, len, _w, _y, dir=1; /* 1 == "down" */
561 char *text;
563 y += (tPtr->vpos - tPtr->visible.y);
564 if (y<0)
565 y = 0;
567 x -= (tPtr->visible.x - 2);
568 if (x<0)
569 x=0;
571 /* clicked is relative to document, not window... */
572 tPtr->clicked.x = x;
573 tPtr->clicked.y = y;
575 if (! (tb = tPtr->currentTextBlock)) {
576 if (! (tb = tPtr->firstTextBlock)) {
577 tPtr->tpos = 0;
578 tPtr->cursor.h = tPtr->dFont->height;
579 tPtr->cursor.y = 2;
580 tPtr->cursor.x = 2;
581 return;
585 /* first, which direction? Most likely, newly clicked
586 position will be close to previous */
587 dir = !(y <= tb->sections[0].y);
588 if ( ( y <= tb->sections[0]._y + tb->sections[0].h )
589 && (y >= tb->sections[0]._y ) ) {
590 /* if it's on the same line */
591 if(x < tb->sections[0].x)
592 dir = 0;
593 if(x >= tb->sections[0].x)
594 dir = 1;
597 tb = tPtr->firstTextBlock;
598 dir = 1;
600 if (tPtr->flags.monoFont && tb->graphic) {
601 tb = getFirstNonGraphicBlockFor(tb, 1);
602 if (!tb) {
603 tPtr->currentTextBlock =
604 (dir? tPtr->lastTextBlock : tPtr->firstTextBlock);
605 tPtr->tpos = 0;
606 return;
610 s = (dir? 0 : tb->nsections-1);
611 if ( y >= tb->sections[s]._y
612 && y <= tb->sections[s]._y + tb->sections[s].h) {
613 goto _doneV;
616 /* get the first section of the TextBlock that lies about
617 the vertical click point */
618 done = False;
619 while (!done && tb) {
621 if (tPtr->flags.monoFont && tb->graphic) {
622 if(tb->next)
623 tb = tb->next;
624 continue;
627 s = (dir? 0 : tb->nsections-1);
628 while (!done && (dir? (s<tb->nsections) : (s>=0) )) {
630 if ( (dir? (y <= tb->sections[s]._y + tb->sections[s].h) :
631 ( y >= tb->sections[s]._y ) ) ) {
632 done = True;
633 } else {
634 dir? s++ : s--;
638 if (!done) {
639 if ( (dir? tb->next : tb->prior)) {
640 tb = (dir ? tb->next : tb->prior);
641 } else {
642 pos = tb->used;
643 break; //goto _doneH;
649 if (s<0 || s>=tb->nsections) {
650 s = (dir? tb->nsections-1 : 0);
653 _doneV:
654 /* we have the line, which TextBlock on that line is it? */
655 pos = 0;
656 if (tPtr->flags.monoFont && tb->graphic)
657 tb = getFirstNonGraphicBlockFor(tb, dir);
658 if (tb) {
659 if ((dir? tb->sections[s].x >= x : tb->sections[s].x < x))
660 goto _doneH;
662 #if 0
663 if(tb->blank) {
664 _w = 0;
665 printf("blank\n");
666 } else {
667 text = &(tb->text[tb->sections[s].begin]);
668 len = tb->sections[s].end - tb->sections[s].begin;
669 _w = WMWidthOfString(tb->d.font, text, len);
671 printf("here %d %d \n", tb->sections[s].x + _w, x);
672 if ((dir? tb->sections[s].x + _w < x : tb->sections[s].x + _w >= x)) {
673 pos = tb->sections[s].end;
674 tPtr->cursor.x = tb->sections[s].x + _w;
675 goto _doneH;
677 #endif
678 _y = tb->sections[s]._y;
681 while (tb) {
683 if (tPtr->flags.monoFont && tb->graphic) {
684 tb = (dir ? tb->next : tb->prior);
685 continue;
688 if (dir) {
689 if (tb->graphic) {
690 if(tb->object)
691 _w = WMWidgetWidth(tb->d.widget);
692 else
693 _w = tb->d.pixmap->width;
694 } else {
695 text = &(tb->text[tb->sections[s].begin]);
696 len = tb->sections[s].end - tb->sections[s].begin;
697 _w = WMWidthOfString(tb->d.font, text, len);
698 if (tb->sections[s].x + _w >= x)
699 break;
702 } else {
703 if (tb->sections[s].x <= x)
704 break;
707 if ((dir? tb->next : tb->prior)) {
708 TextBlock *nxt = (dir? tb->next : tb->prior);
709 if (tPtr->flags.monoFont && nxt->graphic) {
710 nxt = getFirstNonGraphicBlockFor(nxt, dir);
711 if (!nxt) {
712 pos = 0;
713 tPtr->cursor.x = tb->sections[s].x;
714 goto _doneH;
718 if (_y != nxt->sections[0]._y) {
719 /* this must be the last/first on this line. stop */
720 pos = (dir? tb->sections[s].end : 0);
721 tPtr->cursor.x = tb->sections[s].x;
722 if (!tb->blank) {
723 if (tb->graphic) {
724 if(tb->object)
725 tPtr->cursor.x += WMWidgetWidth(tb->d.widget);
726 else
727 tPtr->cursor.x += tb->d.pixmap->width;
728 } else if (pos > tb->sections[s].begin) {
729 tPtr->cursor.x +=
730 WMWidthOfString(tb->d.font,
731 &(tb->text[tb->sections[s].begin]),
732 pos - tb->sections[s].begin);
735 goto _doneH;
739 if ( (dir? tb->next : tb->prior)) {
740 tb = (dir ? tb->next : tb->prior);
741 } else {
742 done = True;
743 break;
746 if (tb)
747 s = (dir? 0 : tb->nsections-1);
750 /* we have said TextBlock, now where within it? */
751 if (tb && !tb->graphic) {
752 WMFont *f = tb->d.font;
753 len = tb->sections[s].end - tb->sections[s].begin;
754 text = &(tb->text[tb->sections[s].begin]);
756 _w = x - tb->sections[s].x;
757 pos = 0;
759 while (pos<len && WMWidthOfString(f, text, pos+1) < _w)
760 pos++;
762 tPtr->cursor.x = tb->sections[s].x +
763 (pos? WMWidthOfString(f, text, pos) : 0);
765 pos += tb->sections[s].begin;
766 _doneH:
767 tPtr->tpos = (pos<tb->used)? pos : tb->used;
770 tPtr->currentTextBlock = tb;
771 tPtr->cursor.h = tb->sections[s].h;
772 tPtr->cursor.y = tb->sections[s]._y;
774 if (!tb)
775 printf("will hang :-)\n");
779 static void
780 updateScrollers(Text *tPtr)
783 if (tPtr->flags.frozen)
784 return;
786 if (tPtr->vS) {
787 if (tPtr->docHeight < tPtr->visible.h) {
788 WMSetScrollerParameters(tPtr->vS, 0, 1);
789 tPtr->vpos = 0;
790 } else {
791 float vmax = (float)(tPtr->docHeight);
792 WMSetScrollerParameters(tPtr->vS,
793 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
794 (float)tPtr->visible.h/vmax);
796 } else tPtr->vpos = 0;
798 if (tPtr->hS)
802 static void
803 scrollersCallBack(WMWidget *w, void *self)
805 Text *tPtr = (Text *)self;
806 Bool scroll = False;
807 Bool dimple = False;
808 int which;
810 if (!tPtr->view->flags.realized || tPtr->flags.frozen)
811 return;
813 if (w == tPtr->vS) {
814 float vmax;
815 int height;
816 vmax = (float)(tPtr->docHeight);
817 height = tPtr->visible.h;
819 which = WMGetScrollerHitPart(tPtr->vS);
820 switch(which) {
821 case WSDecrementLine:
822 if (tPtr->vpos > 0) {
823 if (tPtr->vpos>16) tPtr->vpos-=16;
824 else tPtr->vpos=0;
825 scroll=True;
826 }break;
827 case WSIncrementLine: {
828 int limit = tPtr->docHeight - height;
829 if (tPtr->vpos < limit) {
830 if (tPtr->vpos<limit-16) tPtr->vpos+=16;
831 else tPtr->vpos=limit;
832 scroll = True;
833 }}break;
834 case WSDecrementPage:
835 tPtr->vpos -= height;
837 if (tPtr->vpos < 0)
838 tPtr->vpos = 0;
839 dimple = True;
840 scroll = True;
841 printf("dimple needs to jump to mouse location ;-/\n");
842 break;
843 case WSIncrementPage:
844 tPtr->vpos += height;
845 if (tPtr->vpos > (tPtr->docHeight - height))
846 tPtr->vpos = tPtr->docHeight - height;
847 dimple = True;
848 scroll = True;
849 printf("dimple needs to jump to mouse location ;-/\n");
850 break;
853 case WSKnob:
854 tPtr->vpos = WMGetScrollerValue(tPtr->vS)
855 * (float)(tPtr->docHeight - height);
856 scroll = True;
857 break;
859 case WSKnobSlot:
860 case WSNoPart:
861 printf("WSNoPart, WSKnobSlot\n");
862 #if 0
863 float vmax = (float)(tPtr->docHeight);
864 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
865 (float)tPtr->visible.h/vmax;
866 dimple =where mouse is.
867 #endif
868 break;
870 scroll = (tPtr->vpos != tPtr->prevVpos);
871 tPtr->prevVpos = tPtr->vpos;
874 if (w == tPtr->hS)
877 if (scroll) {
879 if (0&&dimple) {
880 if (tPtr->rulerShown)
881 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
882 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
883 else
884 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
885 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
888 if (dimple || which == WSDecrementLine || which == WSIncrementLine)
889 updateScrollers(tPtr);
890 paintText(tPtr);
896 typedef struct {
897 TextBlock *tb;
898 unsigned short begin, end; /* what part of the text block */
899 } myLineItems;
902 static int
903 layOutLine(Text *tPtr, myLineItems *items, int nitems,
904 int x, int y, int pwidth)
906 int i, j=0, lw = 0, line_height=0, max_d=0, len, n;
907 WMFont *font;
908 char *text;
909 TextBlock *tb;
910 TextBlock *tbsame=NULL;
912 for(i=0; i<nitems; i++) {
913 tb = items[i].tb;
915 if (tb->graphic) {
916 if (!tPtr->flags.monoFont) {
917 if(tb->object) {
918 WMWidget *wdt = tb->d.widget;
919 line_height = WMAX(line_height, WMWidgetHeight(wdt));
920 if (tPtr->flags.alignment != WALeft)
921 lw += WMWidgetWidth(wdt);
922 } else {
923 line_height = WMAX(line_height, tb->d.pixmap->height + max_d);
924 if (tPtr->flags.alignment != WALeft)
925 lw += tb->d.pixmap->width;
929 } else {
930 font = (tPtr->flags.monoFont)?tPtr->dFont : tb->d.font;
931 max_d = WMAX(max_d, abs(font->height-font->y));
932 line_height = WMAX(line_height, font->height + max_d);
933 text = &(tb->text[items[i].begin]);
934 len = items[i].end - items[i].begin;
935 if (tPtr->flags.alignment != WALeft)
936 lw += WMWidthOfString(font, text, len);
940 if (tPtr->flags.alignment == WARight) {
941 j = pwidth - lw;
942 } else if (tPtr->flags.alignment == WACenter) {
943 j = (int) ((float)(pwidth - lw))/2.0;
946 for(i=0; i<nitems; i++) {
947 tb = items[i].tb;
949 if (tbsame == tb) { /*extend it, since it's on same line */
950 tb->sections[tb->nsections-1].end = items[i].end;
951 n = tb->nsections-1;
952 } else {
953 tb->sections = wrealloc(tb->sections,
954 (++tb->nsections)*sizeof(Section));
955 n = tb->nsections-1;
956 tb->sections[n]._y = y + max_d;
957 tb->sections[n].x = x+j;
958 tb->sections[n].h = line_height;
959 tb->sections[n].begin = items[i].begin;
960 tb->sections[n].end = items[i].end;
963 tb->sections[n].last = (i+1==nitems);
965 if (tb->graphic) {
966 if (!tPtr->flags.monoFont) {
967 if(tb->object) {
968 WMWidget *wdt = tb->d.widget;
969 tb->sections[n].y = max_d + y + line_height - WMWidgetHeight(wdt);
970 tb->sections[n].w = WMWidgetWidth(wdt);
971 } else {
972 tb->sections[n].y = y + max_d;// + line_height - tb->d.pixmap->height;
973 tb->sections[n].w = tb->d.pixmap->width;
975 x += tb->sections[n].w;
977 } else {
978 font = (tPtr->flags.monoFont)? tPtr->dFont : tb->d.font;
979 len = items[i].end - items[i].begin;
980 text = &(tb->text[items[i].begin]);
982 tb->sections[n].y = y+line_height-font->y;
983 tb->sections[n].w =
984 WMWidthOfString(font,
985 &(tb->text[tb->sections[n].begin]),
986 tb->sections[n].end - tb->sections[n].begin);
988 x += WMWidthOfString(font, text, len);
991 tbsame = tb;
994 return line_height;
999 static void
1000 output(char *ptr, int len)
1002 char s[len+1];
1003 memcpy(s, ptr, len);
1004 s[len] = 0;
1005 //printf(" s is [%s] (%d)\n", s, strlen(s));
1006 printf("[%s]\n", s);
1010 /* tb->text doesn't necessarily end in '\0' hmph! (strchr) */
1011 static inline char *
1012 mystrchr(char *s, char needle, unsigned short len)
1014 char *haystack = s;
1016 if (!haystack || len < 1)
1017 return NULL;
1019 while ( (int) (haystack - s) < len ) {
1020 if (*haystack == needle)
1021 return haystack;
1022 haystack++;
1024 return NULL;
1027 #define MAX_TB_PER_LINE 64
1029 static void
1030 layOutDocument(Text *tPtr)
1032 TextBlock *tb;
1033 myLineItems items[MAX_TB_PER_LINE];
1034 WMFont *font;
1035 Bool lhc = !tPtr->flags.laidOut; /* line height changed? */
1036 int prev_y;
1038 int nitems=0, x=0, y=0, lw = 0, width=0;
1039 int pwidth = tPtr->visible.w - tPtr->visible.x;
1041 char *start=NULL, *mark=NULL;
1042 int begin, end;
1044 if (tPtr->flags.frozen)
1045 return;
1047 if (!(tb = tPtr->firstTextBlock))
1048 return;
1050 if (0&&tPtr->flags.laidOut) {
1051 tb = tPtr->currentTextBlock;
1052 if (tb->sections && tb->nsections>0)
1053 prev_y = tb->sections[tb->nsections-1]._y;
1054 y+=10;
1055 printf("1 prev_y %d \n", prev_y);
1057 /* search backwards for textblocks on same line */
1058 while (tb) {
1059 if (!tb->sections || tb->nsections<1) {
1060 tb = tPtr->firstTextBlock;
1061 break;
1063 if (tb->sections[tb->nsections-1]._y != prev_y) {
1064 tb = tb->next;
1065 break;
1067 // prev_y = tb->sections[tb->nsections-1]._y;
1068 tb = tb->prior;
1070 y = 0;//tb->sections[tb->nsections-1]._y;
1071 printf("2 prev_y %d \n\n", tb->sections[tb->nsections-1]._y);
1075 while (tb) {
1077 if (tb->sections && tb->nsections>0) {
1078 wfree(tb->sections);
1079 tb->sections = NULL;
1080 tb->nsections = 0;
1083 if (tb->first) {
1084 y += layOutLine(tPtr, items, nitems, x, y, pwidth);
1085 x = 0;//tPtr->visible.x+2;
1086 nitems = 0;
1087 lw = 0;
1090 if (tb->graphic) {
1091 if (!tPtr->flags.monoFont) {
1092 if(tb->object)
1093 width = WMWidgetWidth(tb->d.widget);
1094 else
1095 width = tb->d.pixmap->width;
1097 if (width > pwidth)printf("rescale graphix to fit?\n");
1098 lw += width;
1099 if (lw >= pwidth - x
1100 || nitems >= MAX_TB_PER_LINE) {
1101 y += layOutLine(tPtr, items, nitems, x, y, pwidth);
1102 nitems = 0;
1103 x = 0;//tPtr->visible.x+2;
1104 lw = width;
1107 items[nitems].tb = tb;
1108 items[nitems].begin = 0;
1109 items[nitems].end = 0;
1110 nitems++;
1113 } else if ((start = tb->text)) {
1114 begin = end = 0;
1115 font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
1117 while (start) {
1118 mark = mystrchr(start, ' ', tb->used);
1119 if (mark) {
1120 end += (int)(mark-start)+1;
1121 start = mark+1;
1122 } else {
1123 end += strlen(start);
1124 start = mark;
1127 if (end > tb->used)
1128 end = tb->used;
1130 if (end-begin > 0) {
1132 width = WMWidthOfString(font,
1133 &tb->text[begin], end-begin);
1135 if (width > pwidth) { /* break this tb up */
1136 char *t = &tb->text[begin];
1137 int l=end-begin, i=0;
1138 do {
1139 width = WMWidthOfString(font, t, ++i);
1140 } while (width < pwidth && i < l);
1141 end = begin+i;
1142 if (start) // and since (nil)-4 = 0xfffffffd
1143 start -= l-i;
1146 lw += width;
1149 if ((lw >= pwidth - x)
1150 || nitems >= MAX_TB_PER_LINE) {
1151 y += layOutLine(tPtr, items, nitems, x, y, pwidth);
1152 lw = width;
1153 x = 0; //tPtr->visible.x+2;
1154 nitems = 0;
1157 items[nitems].tb = tb;
1158 items[nitems].begin = begin;
1159 items[nitems].end = end;
1160 nitems++;
1162 begin = end;
1165 tb = tb->next;
1169 if (nitems > 0)
1170 y += layOutLine(tPtr, items, nitems, x, y, pwidth);
1171 if (lhc) {
1172 tPtr->docHeight = y+10;
1173 updateScrollers(tPtr);
1175 tPtr->flags.laidOut = True;
1180 static void
1181 textDidResize(W_ViewDelegate *self, WMView *view)
1183 Text *tPtr = (Text *)view->self;
1184 unsigned short w = WMWidgetWidth(tPtr);
1185 unsigned short h = WMWidgetHeight(tPtr);
1186 unsigned short rh = 0, vw = 0;
1188 if (tPtr->ruler && tPtr->flags.rulerShown) {
1189 WMMoveWidget(tPtr->ruler, 2, 2);
1190 WMResizeWidget(tPtr->ruler, w - 4, 40);
1191 rh = 40;
1194 if (tPtr->vS) {
1195 WMMoveWidget(tPtr->vS, 1, rh + 2);
1196 WMResizeWidget(tPtr->vS, 20, h - rh - 3);
1197 vw = 20;
1198 WMSetRulerOffset(tPtr->ruler,22);
1199 } else WMSetRulerOffset(tPtr->ruler, 2);
1201 if (tPtr->hS) {
1202 if (tPtr->vS) {
1203 WMMoveWidget(tPtr->hS, vw, h - 21);
1204 WMResizeWidget(tPtr->hS, w - vw - 1, 20);
1205 } else {
1206 WMMoveWidget(tPtr->hS, vw+1, h - 21);
1207 WMResizeWidget(tPtr->hS, w - vw - 2, 20);
1211 tPtr->visible.x = (tPtr->vS)?22:2;
1212 tPtr->visible.y = (tPtr->ruler && tPtr->flags.rulerShown)?43:3;
1213 tPtr->visible.w = tPtr->view->size.width - tPtr->visible.x - 12;
1214 tPtr->visible.h = tPtr->view->size.height - tPtr->visible.y;
1215 tPtr->visible.h -= (tPtr->hS)?20:0;
1217 tPtr->dmargins = WMGetRulerMargins(tPtr->ruler);
1219 if (tPtr->view->flags.realized) {
1221 if (tPtr->db) {
1222 XFreePixmap(tPtr->view->screen->display, tPtr->db);
1223 tPtr->db = (Pixmap) NULL;
1226 if (tPtr->visible.w < 40)
1227 tPtr->visible.w = 40;
1228 if (tPtr->visible.h < 20)
1229 tPtr->visible.h = 20;
1231 //if (size change or !db
1232 if(!tPtr->db) {
1233 tPtr->db = XCreatePixmap(tPtr->view->screen->display,
1234 tPtr->view->window, tPtr->visible.w,
1235 tPtr->visible.h, tPtr->view->screen->depth);
1239 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1242 W_ViewDelegate _TextViewDelegate =
1244 NULL,
1245 NULL,
1246 textDidResize,
1247 NULL,
1250 /* nice, divisble-by-16 blocks */
1251 static inline unsigned short
1252 reqBlockSize(unsigned short requested)
1254 return requested + 16 - (requested%16);
1258 static void
1259 clearText(Text *tPtr)
1261 if (!tPtr->firstTextBlock)
1262 return;
1264 while (tPtr->currentTextBlock)
1265 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1267 tPtr->firstTextBlock = NULL;
1268 tPtr->currentTextBlock = NULL;
1269 tPtr->lastTextBlock = NULL;
1272 static void
1273 deleteTextInteractively(Text *tPtr, KeySym ksym)
1275 TextBlock *tb = tPtr->currentTextBlock;
1276 Bool back = (Bool) (ksym == XK_BackSpace);
1277 Bool done = 1;
1279 if (!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1280 XBell(tPtr->view->screen->display, 0);
1281 return;
1284 if (!tb)
1285 return;
1287 if (tPtr->flags.ownsSelection) {
1288 removeSelection(tPtr);
1289 return;
1292 if (back && tPtr->tpos < 1) {
1293 if (tb->prior) {
1294 tb = tb->prior;
1295 tPtr->tpos = tb->used;
1296 tPtr->currentTextBlock = tb;
1297 done = 1;
1301 if ( (tb->used > 0) && ((back?tPtr->tpos > 0:1))
1302 && (tPtr->tpos <= tb->used) && !tb->graphic) {
1303 if (back)
1304 tPtr->tpos--;
1305 memmove(&(tb->text[tPtr->tpos]),
1306 &(tb->text[tPtr->tpos + 1]), tb->used - tPtr->tpos);
1307 tb->used--;
1308 done = 0;
1311 if ( (back? (tPtr->tpos < 1 && !done) : ( tPtr->tpos >= tb->used))
1312 || tb->graphic) {
1314 TextBlock *sibling = (back? tb->prior : tb->next);
1316 if(tb->used == 0 || tb->graphic)
1317 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1319 if (sibling) {
1320 tPtr->currentTextBlock = sibling;
1321 tPtr->tpos = (back? sibling->used : 0);
1327 static void
1328 insertTextInteractively(Text *tPtr, char *text, int len)
1330 TextBlock *tb;
1331 char *newline = NULL;
1333 if (!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1334 XBell(tPtr->view->screen->display, 0);
1335 return;
1338 if(*text == 'c') {
1339 WMColor *color = WMCreateNamedColor(W_VIEW_SCREEN(tPtr->view),
1340 "Blue", True);
1341 WMSetTextSelectionColor(tPtr, color);
1342 return;
1345 if (len < 1 || !text)
1346 return;
1348 if (tPtr->flags.ownsSelection)
1349 removeSelection(tPtr);
1351 if(tPtr->flags.ignoreNewLine && *text == '\n' && len == 1)
1352 return;
1354 if (tPtr->flags.ignoreNewLine) {
1355 int i;
1356 for(i=0; i<len; i++) {
1357 if (text[i] == '\n')
1358 text[i] = ' ';
1362 tb = tPtr->currentTextBlock;
1363 if (!tb || tb->graphic) {
1364 text[len] = 0;
1365 WMAppendTextStream(tPtr, text);
1366 if (tPtr->currentTextBlock) {
1367 tPtr->tpos = tPtr->currentTextBlock->used;
1369 return;
1372 if ((newline = strchr(text, '\n'))) {
1373 int nlen = (int)(newline-text);
1374 int s = tb->used - tPtr->tpos;
1375 char save[s];
1377 if (!tb->blank && nlen>0) {
1378 if (s > 0) {
1379 memcpy(save, &tb->text[tPtr->tpos], s);
1380 tb->used -= (tb->used - tPtr->tpos);
1382 text[nlen] = 0;
1383 insertTextInteractively(tPtr, text, nlen);
1384 newline++;
1385 WMAppendTextStream(tPtr, newline);
1386 if (s>0)
1387 insertTextInteractively(tPtr, save, s);
1389 } else {
1390 if (tPtr->tpos>0 && tPtr->tpos < tb->used
1391 && !tb->graphic && tb->text) {
1393 void *ntb = WMCreateTextBlockWithText(&tb->text[tPtr->tpos],
1394 tb->d.font, tb->color, True, tb->used - tPtr->tpos);
1395 tb->used = tPtr->tpos;
1396 WMAppendTextBlock(tPtr, ntb);
1397 tPtr->tpos = 0;
1398 } else if (tPtr->tpos == tb->used || tPtr->tpos == 0) {
1399 void *ntb = WMCreateTextBlockWithText(NULL,
1400 tb->d.font, tb->color, True, 0);
1402 if (tPtr->tpos>0)
1403 WMAppendTextBlock(tPtr, ntb);
1404 else
1405 WMPrependTextBlock(tPtr, ntb);
1406 tPtr->tpos = 1;
1410 } else {
1412 if (tb->used + len >= tb->allocated) {
1413 tb->allocated = reqBlockSize(tb->used+len);
1414 tb->text = wrealloc(tb->text, tb->allocated);
1417 if (tb->blank) {
1418 memcpy(tb->text, text, len);
1419 tb->used = len;
1420 tPtr->tpos = len;
1421 tb->blank = False;
1422 } else {
1423 memmove(&(tb->text[tPtr->tpos+len]), &tb->text[tPtr->tpos],
1424 tb->used-tPtr->tpos+1);
1425 memmove(&tb->text[tPtr->tpos], text, len);
1426 tb->used += len;
1427 tPtr->tpos += len;
1434 static void
1435 selectRegion(Text *tPtr, int x, int y)
1438 printf("\n\n");
1440 if (x < 0 || y < 0)
1441 return;
1442 y += (tPtr->flags.rulerShown? 40: 0);
1443 y += tPtr->vpos;
1444 if (y>10)
1445 y -= 10; /* the original offset */
1447 x -= tPtr->visible.x-2;
1448 if (x<0)
1449 x=0;
1451 tPtr->sel.x = WMAX(0, WMIN(tPtr->clicked.x, x));
1452 tPtr->sel.w = abs(tPtr->clicked.x - x);
1453 tPtr->sel.y = WMAX(0, WMIN(tPtr->clicked.y, y));
1454 tPtr->sel.h = abs(tPtr->clicked.y - y);
1456 tPtr->flags.ownsSelection = True;
1457 paintText(tPtr);
1461 static void
1462 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1463 void *cdata, WMData *data)
1465 Text *tPtr = (Text *)view->self;
1466 char *str;
1468 tPtr->flags.waitingForSelection = False;
1470 if (data) {
1471 str = (char*)WMDataBytes(data);
1472 if (0&&tPtr->parser) {
1473 /* parser is not yet well behaved to do this properly..*/
1474 (tPtr->parser) (tPtr, (void *) str);
1475 } else {
1476 insertTextInteractively(tPtr, str, strlen(str));
1478 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1479 } else {
1480 int n;
1481 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1482 if (str) {
1483 str[n] = 0;
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, n);
1490 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1491 XFree(str);
1497 static void
1498 releaseSelection(Text *tPtr)
1500 TextBlock *tb = tPtr->firstTextBlock;
1502 while(tb) {
1503 tb->selected = False;
1504 tb = tb->next;
1506 tPtr->flags.ownsSelection = False;
1507 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY,
1508 CurrentTime);
1510 paintText(tPtr);
1513 static WMData *
1514 requestHandler(WMView *view, Atom selection, Atom target,
1515 void *cdata, Atom *type)
1517 Text *tPtr = view->self;
1518 Display *dpy = tPtr->view->screen->display;
1519 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1520 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1522 *type = target;
1523 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
1524 char *text = WMGetTextSelected(tPtr);
1525 WMData *data = WMCreateDataWithBytes(text, strlen(text));
1526 wfree(text);
1527 return data;
1528 } else {
1529 Atom PIXMAP = XInternAtom(dpy, "PIXMAP", False);
1530 WMData *data = WMCreateDataWithBytes("bleh", 4);
1531 if(target == PIXMAP) {
1532 printf("whoa! pixmap\n");
1534 return data;
1537 return NULL;
1541 static void
1542 lostHandler(WMView *view, Atom selection, void *cdata)
1544 releaseSelection((WMText *)view->self);
1547 static WMSelectionProcs selectionHandler = {
1548 requestHandler, lostHandler, NULL
1551 static void
1552 ownershipObserver(void *observerData, WMNotification *notification)
1554 WMText *to = (WMText *)observerData;
1555 WMText *tw = (WMText *)WMGetNotificationClientData(notification);
1556 if (to != tw)
1557 lostHandler(to->view, XA_PRIMARY, NULL);
1561 static void
1562 handleTextKeyPress(Text *tPtr, XEvent *event)
1564 char buffer[2];
1565 KeySym ksym;
1566 int control_pressed = False;
1567 // int h=1;
1569 if (((XKeyEvent *) event)->state & ControlMask)
1570 control_pressed = True;
1571 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = 0;
1573 switch(ksym) {
1575 case XK_Right:
1576 case XK_Left: {
1577 TextBlock *tb = tPtr->currentTextBlock;
1578 int x = tPtr->cursor.x + tPtr->visible.x;
1579 int y = tPtr->visible.y + tPtr->cursor.y + tPtr->cursor.h;
1580 int w, pos;
1582 #if 0
1583 if(!tb)
1584 break;
1585 if(tb->graphic) {
1586 if(tb->object) {
1587 w = WMWidgetWidth(tb->d.widget);
1588 } else {
1589 w = tb->d.pixmap->width;
1591 } else {
1592 pos = tPtr->tpos;
1593 w = WMWidthOfString(tb->d.font, &tb->text[pos], 1);
1596 cursorToTextPosition(tPtr, w + tPtr->cursor.x + tPtr->visible.x,
1597 3 + tPtr->visible.y + tPtr->cursor.y
1598 + tPtr->cursor.h - tPtr->vpos);
1599 if(x == tPtr->cursor.x + tPtr->visible.x) {
1600 printf("same %d %d\n", x, tPtr->cursor.x + tPtr->visible.x);
1601 cursorToTextPosition(tPtr, tPtr->visible.x,
1602 3 + tPtr->visible.y + tPtr->cursor.y + tPtr->cursor.h);
1604 paintText(tPtr);
1605 #endif
1607 break;
1609 case XK_Down:
1610 cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
1611 tPtr->clicked.y + tPtr->cursor.h - tPtr->vpos);
1612 paintText(tPtr);
1613 break;
1615 case XK_Up:
1616 cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
1617 tPtr->visible.y + tPtr->cursor.y - tPtr->vpos - 3);
1618 paintText(tPtr);
1619 break;
1621 case XK_BackSpace:
1622 case XK_Delete:
1623 case XK_KP_Delete:
1624 deleteTextInteractively(tPtr, ksym);
1625 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1626 break;
1628 case XK_Control_R :
1629 case XK_Control_L :
1630 control_pressed = True;
1631 break;
1633 case XK_Return:
1634 buffer[0] = '\n';
1635 default:
1636 if (buffer[0] != 0 && !control_pressed) {
1637 insertTextInteractively(tPtr, buffer, 1);
1638 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1640 } else if (control_pressed && ksym==XK_r) {
1641 Bool i = !tPtr->flags.rulerShown;
1642 WMShowTextRuler(tPtr, i);
1643 tPtr->flags.rulerShown = i;
1645 else if (control_pressed && buffer[0] == '\a')
1646 XBell(tPtr->view->screen->display, 0);
1649 if (!control_pressed && tPtr->flags.ownsSelection)
1650 releaseSelection(tPtr);
1653 static void
1654 handleWidgetPress(XEvent *event, void *data)
1656 TextBlock *tb = (TextBlock *)data;
1657 Text *tPtr;
1658 WMWidget *w;
1660 if (!tb)
1661 return;
1662 /* this little bit of nastiness here saves a boatload of trouble */
1663 w = (WMWidget *)(((W_VIEW(tb->d.widget))->parent)->self);
1664 if (W_CLASS(w) != WC_Text)
1665 return;
1666 tPtr = (Text*)w;
1667 tPtr->currentTextBlock = getFirstNonGraphicBlockFor(tb, 1);
1668 if (!tPtr->currentTextBlock)
1669 tPtr->currentTextBlock = tb;
1670 tPtr->tpos = 0;
1671 output(tPtr->currentTextBlock->text, tPtr->currentTextBlock->used);
1672 //if (!tPtr->flags.focused) {
1673 // WMSetFocusToWidget(tPtr);
1674 // tPtr->flags.focused = True;
1675 //}
1679 static void
1680 handleActionEvents(XEvent *event, void *data)
1682 Text *tPtr = (Text *)data;
1683 Display *dpy = event->xany.display;
1684 KeySym ksym;
1687 switch (event->type) {
1688 case KeyPress:
1689 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1690 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
1691 tPtr->flags.extendSelection = True;
1692 return;
1695 if (tPtr->flags.waitingForSelection)
1696 return;
1697 if (tPtr->flags.focused) {
1698 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1699 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1700 GrabModeAsync, GrabModeAsync, None,
1701 tPtr->view->screen->invisibleCursor, CurrentTime);
1702 tPtr->flags.pointerGrabbed = True;
1703 handleTextKeyPress(tPtr, event);
1705 } break;
1707 case KeyRelease:
1708 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1709 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
1710 tPtr->flags.extendSelection = False;
1711 return;
1712 //end modify flag so selection can be extended
1714 break;
1717 case MotionNotify:
1718 if (tPtr->flags.pointerGrabbed) {
1719 tPtr->flags.pointerGrabbed = False;
1720 XUngrabPointer(dpy, CurrentTime);
1723 if (tPtr->flags.ownsSelection) {
1724 #if 0
1725 if(1//if (event->xmotion.x >= tPtr->sel.x
1726 //&& event->xmotion.x <= tPtr->sel.x + tPtr->sel.w
1727 )// && event->xmotion.y >= tPtr->sel.y
1728 // && event->xmotion.y <= tPtr->sel.y + tPtr->sel.w)
1731 else
1732 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
1733 #endif
1734 //printf("boo\n");
1735 tPtr->view->attribs.cursor = tPtr->view->screen->defaultCursor;
1738 if ((event->xmotion.state & Button1Mask)) {
1739 if (!tPtr->flags.ownsSelection) {
1740 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1741 event->xbutton.time, &selectionHandler, NULL);
1742 tPtr->flags.ownsSelection = True;
1744 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1746 break;
1749 case ButtonPress:
1750 tPtr->flags.buttonHeld = True;
1751 if (tPtr->flags.extendSelection) {
1752 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1753 return;
1755 if (event->xbutton.button == Button1) {
1757 if (!tPtr->flags.focused) {
1758 WMSetFocusToWidget(tPtr);
1759 tPtr->flags.focused = True;
1762 if (tPtr->flags.ownsSelection)
1763 releaseSelection(tPtr);
1764 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1765 paintText(tPtr);
1766 if (tPtr->flags.pointerGrabbed) {
1767 tPtr->flags.pointerGrabbed = False;
1768 XUngrabPointer(dpy, CurrentTime);
1769 break;
1773 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown)
1774 WMScrollText(tPtr, -16);
1775 else if (event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1776 WMScrollText(tPtr, 16);
1777 break;
1779 case ButtonRelease:
1780 tPtr->flags.buttonHeld = False;
1781 if (tPtr->flags.pointerGrabbed) {
1782 tPtr->flags.pointerGrabbed = False;
1783 XUngrabPointer(dpy, CurrentTime);
1784 break;
1786 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown
1787 || event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1788 break;
1790 if (event->xbutton.button == Button2) {
1791 char *text = NULL;
1792 int n;
1794 if (!tPtr->flags.editable) {
1795 XBell(dpy, 0);
1796 break;
1799 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1800 event->xbutton.time, pasteText, NULL)) {
1801 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1802 if (text) {
1803 text[n] = 0;
1804 if (0&&tPtr->parser) {
1805 /* parser is not yet well behaved to do this properly..*/
1806 (tPtr->parser) (tPtr, (void *) text);
1807 } else {
1808 insertTextInteractively(tPtr, text, n-1);
1810 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1811 XFree(text);
1812 } else tPtr->flags.waitingForSelection = True;
1815 break;
1822 static void
1823 handleEvents(XEvent *event, void *data)
1825 Text *tPtr = (Text *)data;
1827 switch(event->type) {
1828 case Expose:
1830 if(tPtr->hS) {
1831 if (!(W_VIEW(tPtr->hS))->flags.realized)
1832 WMRealizeWidget(tPtr->hS);
1833 if (!((W_VIEW(tPtr->hS))->flags.mapped))
1834 WMMapWidget(tPtr->hS);
1837 if(tPtr->vS) {
1838 if (!(W_VIEW(tPtr->vS))->flags.realized)
1839 WMRealizeWidget(tPtr->vS);
1840 if (!((W_VIEW(tPtr->vS))->flags.mapped))
1841 WMMapWidget(tPtr->vS);
1844 if(tPtr->ruler) {
1845 if (!(W_VIEW(tPtr->ruler))->flags.realized)
1846 WMRealizeWidget(tPtr->ruler);
1848 if (!((W_VIEW(tPtr->ruler))->flags.mapped)
1849 && tPtr->flags.rulerShown)
1850 WMMapWidget(tPtr->ruler);
1852 if(!tPtr->db)
1853 textDidResize(tPtr->view->delegate, tPtr->view);
1855 if (!event->xexpose.count && tPtr->view->flags.realized)
1856 paintText(tPtr);
1857 break;
1859 case FocusIn:
1860 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))
1861 != tPtr->view)
1862 return;
1863 tPtr->flags.focused = True;
1864 #if DO_BLINK
1865 if (tPtr->flags.editable && !tPtr->timerID) {
1866 tPtr->timerID = WMAddTimerHandler(12+0*CURSOR_BLINK_ON_DELAY,
1867 blinkCursor, tPtr);
1869 #endif
1871 break;
1873 case FocusOut:
1874 tPtr->flags.focused = False;
1875 paintText(tPtr);
1876 #if DO_BLINK
1877 if (tPtr->timerID) {
1878 WMDeleteTimerHandler(tPtr->timerID);
1879 tPtr->timerID = NULL;
1881 #endif
1882 break;
1884 case DestroyNotify:
1885 clearText(tPtr);
1886 if(tPtr->hS)
1887 WMDestroyWidget(tPtr->hS);
1888 if(tPtr->vS)
1889 WMDestroyWidget(tPtr->vS);
1890 if(tPtr->ruler)
1891 WMDestroyWidget(tPtr->ruler);
1892 if(tPtr->db)
1893 XFreePixmap(tPtr->view->screen->display, tPtr->db);
1894 if(tPtr->gfxItems)
1895 WMFreeBag(tPtr->gfxItems);
1896 #if DO_BLINK
1897 if (tPtr->timerID)
1898 WMDeleteTimerHandler(tPtr->timerID);
1899 #endif
1900 WMReleaseFont(tPtr->dFont);
1901 WMReleaseColor(tPtr->dColor);
1902 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1903 WMRemoveNotificationObserver(tPtr);
1905 wfree(tPtr);
1907 break;
1913 static void
1914 insertPlainText(WMText *tPtr, char *text)
1916 char *start, *mark;
1917 void *tb = NULL;
1919 if (!text) {
1920 clearText(tPtr);
1921 return;
1924 start = text;
1925 while (start) {
1926 mark = strchr(start, '\n');
1927 if (mark) {
1928 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1929 tPtr->dColor, True, (int)(mark-start));
1930 start = mark+1;
1931 } else {
1932 if (start && strlen(start)) {
1933 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1934 tPtr->dColor, False, strlen(start));
1935 } else tb = NULL;
1936 start = mark;
1939 if (tPtr->flags.prepend)
1940 WMPrependTextBlock(tPtr, tb);
1941 else
1942 WMAppendTextBlock(tPtr, tb);
1944 return;
1949 static void
1950 rulerMoveCallBack(WMWidget *w, void *self)
1952 Text *tPtr = (Text *)self;
1953 if (!tPtr)
1954 return;
1955 if (W_CLASS(tPtr) != WC_Text)
1956 return;
1958 paintText(tPtr);
1962 static void
1963 rulerReleaseCallBack(WMWidget *w, void *self)
1965 Text *tPtr = (Text *)self;
1966 if (!tPtr)
1967 return;
1968 if (W_CLASS(tPtr) != WC_Text)
1969 return;
1971 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1972 return;
1976 #if 0
1977 static unsigned
1978 draggingEntered(WMView *self, WMDraggingInfo *info)
1980 printf("draggingEntered\n");
1981 return WDOperationCopy;
1985 static unsigned
1986 draggingUpdated(WMView *self, WMDraggingInfo *info)
1988 printf("draggingUpdated\n");
1989 return WDOperationCopy;
1993 static void
1994 draggingExited(WMView *self, WMDraggingInfo *info)
1996 printf("draggingExited\n");
1999 static void
2000 prepareForDragOperation(WMView *self, WMDraggingInfo *info)
2002 printf("draggingExited\n");
2003 return;//"bll"; //"application/X-color";
2007 static Bool
2008 performDragOperation(WMView *self, WMDraggingInfo *info) //, WMData *data)
2010 char *colorName = "Blue";// (char*)WMDataBytes(data);
2011 WMColor *color;
2012 WMText *tPtr = (WMText *)self->self;
2014 if (!tPtr)
2015 return False;
2017 if (tPtr->flags.monoFont)
2018 return False;
2020 color = WMCreateNamedColor(W_VIEW_SCREEN(self), colorName, True);
2021 printf("color [%s] %p\n", colorName, color);
2022 if(color) {
2023 WMSetTextSelectionColor(tPtr, color);
2024 WMReleaseColor(color);
2027 return True;
2030 static void
2031 concludeDragOperation(WMView *self, WMDraggingInfo *info)
2033 printf("concludeDragOperation\n");
2037 static WMDragDestinationProcs _DragDestinationProcs = {
2038 draggingEntered,
2039 draggingUpdated,
2040 draggingExited,
2041 prepareForDragOperation,
2042 performDragOperation,
2043 concludeDragOperation
2045 #endif
2047 static void
2048 releaseBagData(void *data)
2050 if(data)
2051 wfree(data);
2055 char *
2056 getStream(WMText *tPtr, int sel)
2058 TextBlock *tb = NULL;
2059 char *text = NULL;
2060 unsigned long length = 0, where = 0;
2062 if (!tPtr)
2063 return NULL;
2065 if (!(tb = tPtr->firstTextBlock))
2066 return NULL;
2068 /* this might be tricky to get right... not yet implemented */
2069 if (tPtr->writer) {
2070 (tPtr->writer) (tPtr, (void *) text);
2071 return text;
2075 /* first, how large a buffer would we want? */
2076 while (tb) {
2078 if (!tb->graphic || (tb->graphic && !tPtr->flags.monoFont)) {
2080 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank))
2081 length += 1;
2083 if (!sel || (tb->graphic && tb->selected)) {
2084 length += tb->used;
2085 if (tb->graphic)
2086 length += 1; /* field markers 0xFA and 0xCE */
2087 } else if (sel && tb->selected) {
2088 length += (tb->s_end - tb->s_begin);
2092 tb = tb->next;
2095 text = wmalloc(length+1); /* +1 for the end of string, let's be nice */
2096 tb = tPtr->firstTextBlock;
2097 while (tb) {
2099 if (!tb->graphic || (tb->graphic && !tPtr->flags.monoFont)) {
2101 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank))
2102 text[where++] = '\n';
2104 if (!sel || (tb->graphic && tb->selected)) {
2105 if(tb->graphic)
2106 text[where++] = 0xFA;
2107 memcpy(&text[where], tb->text, tb->used);
2108 where += tb->used;
2109 if(tb->graphic)
2110 text[where++] = 0xCE;
2112 } else if (sel && tb->selected) {
2113 memcpy(&text[where], &tb->text[tb->s_begin],
2114 tb->s_end - tb->s_begin);
2115 where += tb->s_end - tb->s_begin;
2119 tb = tb->next;
2122 text[where] = 0;
2123 return text;
2128 WMBag *
2129 getStreamIntoBag(WMText *tPtr, int sel)
2131 char *stream, *start = NULL, *fa = NULL, *ce = NULL, *desc = NULL;
2132 WMBag *bag;
2134 if (!tPtr)
2135 return NULL;
2137 stream = getStream(tPtr, sel);
2138 if(!stream)
2139 return NULL;
2141 bag = WMCreateArrayBagWithDestructor(4, releaseBagData);
2143 start = stream;
2144 while (start) {
2146 fa = strchr(start, 0xFA);
2147 if (fa) {
2148 desc = wmalloc((int)(fa - start));
2149 memcpy(desc, start, (int)(fa - start));
2150 WMPutInBag(bag, (void *) desc);
2152 ce = strchr(fa, 0xCE);
2153 if (ce) {
2154 desc = wmalloc(3 + (int)(ce - fa));
2155 desc[0] = 0xFA;
2156 desc[1] = 0xCE;
2157 memcpy(&desc[2], fa+1, ((int)(ce - fa))-1);
2158 desc[2 + (int)(ce-fa)] = 0;
2159 WMPutInBag(bag, (void *) desc);
2160 start = ce+1;
2161 } else {
2162 start = fa + 1;
2165 } else {
2166 if (start && strlen(start)) {
2167 desc = wmalloc(strlen(start));
2168 memcpy(desc, start, strlen(start));
2169 WMPutInBag(bag, (void *) desc);
2171 start = fa;
2175 wfree(stream);
2176 return bag;
2180 WMText *
2181 WMCreateText(WMWidget *parent)
2183 Text *tPtr = wmalloc(sizeof(Text));
2184 if (!tPtr) {
2185 printf("could not create text widget\n");
2186 return NULL;
2189 #if 0
2190 printf("sizeof:\n");
2191 printf(" TextBlock %d\n", sizeof(TextBlock));
2192 printf(" Section %d\n", sizeof(Section));
2193 printf(" char * %d\n", sizeof(char *));
2194 printf(" void * %d\n", sizeof(void *));
2195 printf(" short %d\n", sizeof(short));
2196 printf(" Text %d\n", sizeof(Text));
2197 #endif
2199 memset(tPtr, 0, sizeof(Text));
2200 tPtr->widgetClass = WC_Text;
2201 tPtr->view = W_CreateView(W_VIEW(parent));
2202 if (!tPtr->view) {
2203 perror("could not create text's view\n");
2204 free(tPtr);
2205 return NULL;
2207 tPtr->view->self = tPtr;
2208 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
2209 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
2210 W_ResizeView(tPtr->view, 250, 200);
2211 tPtr->bgGC = WMColorGC(tPtr->view->screen->white);
2212 tPtr->fgGC = WMColorGC(tPtr->view->screen->black);
2213 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
2215 tPtr->ruler = NULL;
2216 tPtr->vS = NULL;
2217 tPtr->hS = NULL;
2219 tPtr->dFont = NULL;
2220 //tPtr->dFont = WMCreateFont(tPtr->view->screen,
2221 // "-*-fixed-medium-r-normal--26-*-*-*-*-*-*-*");
2222 //"-sony-fixed-medium-r-normal--24-230-75-75-c-120-jisx0201.1976-0");
2223 // "-*-times-bold-r-*-*-12-*-*-*-*-*-*-*,"
2224 // "-*-fixed-medium-r-normal-*-12-*");
2225 if (!tPtr->dFont)
2226 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2228 tPtr->dColor = WMBlackColor(tPtr->view->screen);
2230 tPtr->view->delegate = &_TextViewDelegate;
2232 #if DO_BLINK
2233 tPtr->timerID = NULL;
2234 #endif
2236 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
2237 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
2238 handleEvents, tPtr);
2240 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
2241 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
2242 handleActionEvents, tPtr);
2244 WMAddNotificationObserver(ownershipObserver, tPtr, "_lostOwnership", tPtr);
2246 #if 0
2247 WMSetViewDragDestinationProcs(tPtr->view, &_DragDestinationProcs);
2249 char *types[2] = {"application/X-color", NULL};
2250 WMRegisterViewForDraggedTypes(tPtr->view, types);
2252 #endif
2257 tPtr->firstTextBlock = NULL;
2258 tPtr->lastTextBlock = NULL;
2259 tPtr->currentTextBlock = NULL;
2260 tPtr->tpos = 0;
2262 tPtr->gfxItems = WMCreateArrayBag(4);
2264 tPtr->parser = NULL;
2265 tPtr->writer = NULL;
2267 tPtr->sel.x = tPtr->sel.y = 2;
2268 tPtr->sel.w = tPtr->sel.h = 0;
2270 tPtr->clicked.x = tPtr->clicked.y = 2;
2272 tPtr->visible.x = tPtr->visible.y = 2;
2273 tPtr->visible.h = tPtr->view->size.height;
2274 tPtr->visible.w = tPtr->view->size.width - 12;
2276 tPtr->cursor.x = -23;
2278 tPtr->docWidth = 0;
2279 tPtr->docHeight = 0;
2280 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
2281 default_bullet);
2282 tPtr->db = (Pixmap) NULL;
2284 tPtr->dmargins = WMGetRulerMargins(tPtr->ruler);
2286 tPtr->flags.rulerShown = False;
2287 tPtr->flags.monoFont = False;
2288 tPtr->flags.focused = False;
2289 tPtr->flags.editable = True;
2290 tPtr->flags.ownsSelection = False;
2291 tPtr->flags.pointerGrabbed = False;
2292 tPtr->flags.buttonHeld = False;
2293 tPtr->flags.waitingForSelection = False;
2294 tPtr->flags.extendSelection = False;
2295 tPtr->flags.frozen = False;
2296 tPtr->flags.cursorShown = True;
2297 tPtr->flags.clickPos = 1;
2298 tPtr->flags.ignoreNewLine = False;
2299 tPtr->flags.laidOut = False;
2300 tPtr->flags.prepend = False;
2301 tPtr->flags.relief = WRFlat;
2302 tPtr->flags.alignment = WALeft;
2304 return tPtr;
2307 void
2308 WMPrependTextStream(WMText *tPtr, char *text)
2310 if (!tPtr)
2311 return;
2313 tPtr->flags.prepend = True;
2314 if (text && tPtr->parser)
2315 (tPtr->parser) (tPtr, (void *) text);
2316 else
2317 insertPlainText(tPtr, text);
2321 void
2322 WMAppendTextStream(WMText *tPtr, char *text)
2324 if (!tPtr)
2325 return;
2327 tPtr->flags.prepend = False;
2328 if (text && tPtr->parser)
2329 (tPtr->parser) (tPtr, (void *) text);
2330 else
2331 insertPlainText(tPtr, text);
2336 char *
2337 WMGetTextStream(WMText *tPtr)
2339 return getStream(tPtr, 0);
2342 char *
2343 WMGetTextSelected(WMText *tPtr)
2345 return getStream(tPtr, 1);
2348 WMBag *
2349 WMGetTextStreamIntoBag(WMText *tPtr)
2351 return getStreamIntoBag(tPtr, 0);
2354 WMBag *
2355 WMGetTextSelectedIntoBag(WMText *tPtr)
2357 return getStreamIntoBag(tPtr, 1);
2361 void *
2362 WMCreateTextBlockWithObject(WMWidget *w, char *description, WMColor *color,
2363 unsigned short first, unsigned short reserved)
2365 TextBlock *tb;
2366 unsigned short length;
2368 if (!w || !description || !color)
2369 return NULL;
2371 tb = wmalloc(sizeof(TextBlock));
2372 if (!tb)
2373 return NULL;
2375 length = strlen(description);
2376 tb->text = (char *)wmalloc(length);
2377 memset(tb->text, 0, length);
2378 memcpy(tb->text, description, length);
2379 tb->used = length;
2380 tb->blank = False;
2381 tb->d.widget = w;
2382 tb->color = WMRetainColor(color);
2383 //&tb->margins = NULL;
2384 tb->allocated = 0;
2385 tb->first = first;
2386 tb->kanji = False;
2387 tb->graphic = True;
2388 tb->object = True;
2389 tb->underlined = False;
2390 tb->selected = False;
2391 tb->script = 0;
2392 tb->sections = NULL;
2393 tb->nsections = 0;
2394 tb->prior = NULL;
2395 tb->next = NULL;
2397 return tb;
2401 void *
2402 WMCreateTextBlockWithPixmap(WMPixmap *p, char *description, WMColor *color,
2403 unsigned short first, unsigned short reserved)
2405 TextBlock *tb;
2406 unsigned short length;
2408 if (!p || !description || !color)
2409 return NULL;
2411 tb = wmalloc(sizeof(TextBlock));
2412 if (!tb)
2413 return NULL;
2415 length = strlen(description);
2416 tb->text = (char *)wmalloc(length);
2417 memset(tb->text, 0, length);
2418 memcpy(tb->text, description, length);
2419 tb->used = length;
2420 tb->blank = False;
2421 tb->d.pixmap = p;
2422 tb->color = WMRetainColor(color);
2423 //&tb->margins = NULL;
2424 tb->allocated = 0;
2425 tb->first = first;
2426 tb->kanji = False;
2427 tb->graphic = True;
2428 tb->object = False;
2429 tb->underlined = False;
2430 tb->selected = False;
2431 tb->script = 0;
2432 tb->sections = NULL;
2433 tb->nsections = 0;
2434 tb->prior = NULL;
2435 tb->next = NULL;
2437 return tb;
2440 void *
2441 WMCreateTextBlockWithText(char *text, WMFont *font, WMColor *color,
2442 unsigned short first, unsigned short length)
2444 TextBlock *tb;
2446 if (!font || !color)
2447 return NULL;
2449 tb = wmalloc(sizeof(TextBlock));
2450 if (!tb)
2451 return NULL;
2453 tb->allocated = reqBlockSize(length);
2454 tb->text = (char *)wmalloc(tb->allocated);
2455 memset(tb->text, 0, tb->allocated);
2457 if (length < 1|| !text ) { // || *text == '\n') {
2458 *tb->text = ' ';
2459 tb->used = 1;
2460 tb->blank = True;
2461 } else {
2462 memcpy(tb->text, text, length);
2463 tb->used = length;
2464 tb->blank = False;
2467 tb->d.font = WMRetainFont(font);
2468 tb->color = WMRetainColor(color);
2469 tb->first = first;
2470 tb->kanji = False;
2471 tb->graphic = False;
2472 tb->underlined = False;
2473 tb->selected = False;
2474 tb->script = 0;
2475 tb->sections = NULL;
2476 tb->nsections = 0;
2477 tb->prior = NULL;
2478 tb->next = NULL;
2479 return tb;
2482 void
2483 WMSetTextBlockProperties(void *vtb, unsigned int first,
2484 unsigned int kanji, unsigned int underlined, int script,
2485 WMRulerMargins margins)
2487 TextBlock *tb = (TextBlock *) vtb;
2488 if (!tb)
2489 return;
2491 tb->first = first;
2492 tb->kanji = kanji;
2493 tb->underlined = underlined;
2494 tb->script = script;
2495 tb->margins.left = margins.left;
2496 tb->margins.first = margins.first;
2497 tb->margins.body = margins.body;
2498 tb->margins.right = margins.right;
2499 //for: tb->margins.tabs = margins.tabs;
2502 void
2503 WMGetTextBlockProperties(void *vtb, unsigned int *first,
2504 unsigned int *kanji, unsigned int *underlined, int *script,
2505 WMRulerMargins *margins)
2507 TextBlock *tb = (TextBlock *) vtb;
2508 if (!tb)
2509 return;
2511 if (first) *first = tb->first;
2512 if (kanji) *kanji = tb->kanji;
2513 if (underlined) *underlined = tb->underlined;
2514 if (script) *script = tb->script;
2516 if (margins) {
2517 (*margins).left = tb->margins.left;
2518 (*margins).first = tb->margins.first;
2519 (*margins).body = tb->margins.body;
2520 (*margins).right = tb->margins.right;
2521 //for: (*margins).tabs = tb->margins.tabs;
2527 void
2528 WMPrependTextBlock(WMText *tPtr, void *vtb)
2530 TextBlock *tb = (TextBlock *)vtb;
2532 if (!tPtr || !tb)
2533 return;
2535 if (tb->graphic) {
2536 if(tb->object) {
2537 WMWidget *w = tb->d.widget;
2538 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
2539 handleWidgetPress, tb);
2540 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
2541 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
2542 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
2545 WMPutInBag(tPtr->gfxItems, (void *)tb);
2546 tPtr->tpos = 0;
2547 } else tPtr->tpos = tb->used;
2549 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
2550 tb->next = tb->prior = NULL;
2551 tPtr->lastTextBlock = tPtr->firstTextBlock
2552 = tPtr->currentTextBlock = tb;
2553 return;
2556 tb->next = tPtr->currentTextBlock;
2557 tb->prior = tPtr->currentTextBlock->prior;
2558 if (tPtr->currentTextBlock->prior)
2559 tPtr->currentTextBlock->prior->next = tb;
2561 tPtr->currentTextBlock->prior = tb;
2562 if (!tb->prior)
2563 tPtr->firstTextBlock = tb;
2565 tPtr->currentTextBlock = tb;
2569 void
2570 WMAppendTextBlock(WMText *tPtr, void *vtb)
2572 TextBlock *tb = (TextBlock *)vtb;
2574 if (!tPtr || !tb)
2575 return;
2577 if (tb->graphic) {
2578 if(tb->object) {
2579 WMWidget *w = tb->d.widget;
2580 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
2581 handleWidgetPress, tb);
2582 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
2583 (W_VIEW(w))->attribs.cursor =
2584 tPtr->view->screen->defaultCursor;
2585 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
2588 WMPutInBag(tPtr->gfxItems, (void *)tb);
2589 tPtr->tpos = 0;
2590 } else tPtr->tpos = tb->used;
2592 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
2593 tb->next = tb->prior = NULL;
2594 tPtr->lastTextBlock = tPtr->firstTextBlock
2595 = tPtr->currentTextBlock = tb;
2596 return;
2599 tb->next = tPtr->currentTextBlock->next;
2600 tb->prior = tPtr->currentTextBlock;
2601 if (tPtr->currentTextBlock->next)
2602 tPtr->currentTextBlock->next->prior = tb;
2604 tPtr->currentTextBlock->next = tb;
2606 if (!tb->next)
2607 tPtr->lastTextBlock = tb;
2609 tPtr->currentTextBlock = tb;
2612 void *
2613 WMRemoveTextBlock(WMText *tPtr)
2615 TextBlock *tb = NULL;
2617 if (!tPtr || !tPtr->firstTextBlock || !tPtr->lastTextBlock
2618 || !tPtr->currentTextBlock) {
2619 printf("cannot remove non existent TextBlock!\b");
2620 return tb;
2623 tb = tPtr->currentTextBlock;
2624 if (tb->graphic) {
2625 WMRemoveFromBag(tPtr->gfxItems, (void *)tb);
2627 if(tb->object) {
2628 WMDeleteEventHandler(W_VIEW(tb->d.widget), ButtonPressMask,
2629 handleWidgetPress, tb);
2630 WMUnmapWidget(tb->d.widget);
2634 if (tPtr->currentTextBlock == tPtr->firstTextBlock) {
2635 if (tPtr->currentTextBlock->next)
2636 tPtr->currentTextBlock->next->prior = NULL;
2638 tPtr->firstTextBlock = tPtr->currentTextBlock->next;
2639 tPtr->currentTextBlock = tPtr->firstTextBlock;
2641 } else if (tPtr->currentTextBlock == tPtr->lastTextBlock) {
2642 tPtr->currentTextBlock->prior->next = NULL;
2643 tPtr->lastTextBlock = tPtr->currentTextBlock->prior;
2644 tPtr->currentTextBlock = tPtr->lastTextBlock;
2645 } else {
2646 tPtr->currentTextBlock->prior->next = tPtr->currentTextBlock->next;
2647 tPtr->currentTextBlock->next->prior = tPtr->currentTextBlock->prior;
2648 tPtr->currentTextBlock = tPtr->currentTextBlock->next;
2651 return (void *)tb;
2654 void
2655 WMDestroyTextBlock(WMText *tPtr, void *vtb)
2657 TextBlock *tb = (TextBlock *)vtb;
2658 if (!tPtr || !tb)
2659 return;
2661 if (tb->graphic) {
2662 if(tb->object) {
2663 /* naturally, there's a danger to destroying
2664 widgets whose action brings us here:
2665 ie. press a button to destroy it... need to
2666 find a safer way. till then... this stays commented out */
2667 //WMDestroyWidget(tb->d.widget);
2668 //wfree(tb->d.widget);
2669 tb->d.widget = NULL;
2670 } else {
2671 WMReleasePixmap(tb->d.pixmap);
2672 tb->d.pixmap = NULL;
2674 } else {
2675 WMReleaseFont(tb->d.font);
2678 WMReleaseColor(tb->color);
2679 if (tb->sections && tb->nsections > 0)
2680 wfree(tb->sections);
2681 wfree(tb->text);
2682 wfree(tb);
2683 tb = NULL;
2687 void
2688 WMRefreshText(WMText *tPtr, int vpos, int hpos)
2690 if (!tPtr || vpos<0 || hpos<0)
2691 return;
2693 if (tPtr->flags.frozen)
2694 return;
2696 if(tPtr->flags.monoFont) {
2697 int j, c = WMGetBagItemCount(tPtr->gfxItems);
2698 TextBlock *tb;
2700 /* make sure to unmap widgets no matter where they are */
2701 for(j=0; j<c; j++) {
2702 if ((tb = (TextBlock *) WMGetFromBag(tPtr->gfxItems, j))) {
2703 if (tb->object && ((W_VIEW(tb->d.widget))->flags.mapped))
2704 WMUnmapWidget(tb->d.widget);
2710 if (tPtr->vpos != vpos) {
2711 if (vpos < 0 || tPtr->docHeight < tPtr->visible.h) {
2712 tPtr->vpos = 0;
2713 } else if(tPtr->docHeight - vpos > tPtr->visible.h - tPtr->visible.y) {
2714 tPtr->vpos = vpos;
2715 } else {
2716 tPtr->vpos = tPtr->docHeight - tPtr->visible.h;
2720 tPtr->flags.laidOut = False;
2721 layOutDocument(tPtr);
2722 updateScrollers(tPtr);
2723 paintText(tPtr);
2727 void
2728 WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
2730 if (!tPtr)
2731 return;
2733 if (color)
2734 tPtr->fgGC = WMColorGC(color);
2735 else
2736 tPtr->fgGC = WMColorGC(WMBlackColor(tPtr->view->screen));
2738 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2741 void
2742 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
2744 if (!tPtr)
2745 return;
2747 if (color) {
2748 tPtr->bgGC = WMColorGC(color);
2749 W_SetViewBackgroundColor(tPtr->view, color);
2750 } else {
2751 tPtr->bgGC = WMColorGC(WMWhiteColor(tPtr->view->screen));
2752 W_SetViewBackgroundColor(tPtr->view,
2753 WMWhiteColor(tPtr->view->screen));
2756 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2759 void
2760 WMSetTextRelief(WMText *tPtr, WMReliefType relief)
2762 if (!tPtr)
2763 return;
2764 tPtr->flags.relief = relief;
2765 paintText(tPtr);
2768 void
2769 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
2771 if (!tPtr)
2772 return;
2774 if (shouldhave && !tPtr->hS) {
2775 tPtr->hS = WMCreateScroller(tPtr);
2776 (W_VIEW(tPtr->hS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2777 (W_VIEW(tPtr->hS))->attribFlags |= CWOverrideRedirect | CWCursor;
2778 WMSetScrollerArrowsPosition(tPtr->hS, WSAMaxEnd);
2779 WMSetScrollerAction(tPtr->hS, scrollersCallBack, tPtr);
2780 } else if (!shouldhave && tPtr->hS) {
2781 WMUnmapWidget(tPtr->hS);
2782 WMDestroyWidget(tPtr->hS);
2783 tPtr->hS = NULL;
2786 tPtr->hpos = 0;
2787 tPtr->prevHpos = 0;
2788 textDidResize(tPtr->view->delegate, tPtr->view);
2792 void
2793 WMSetTextHasRuler(WMText *tPtr, Bool shouldhave)
2795 if (!tPtr)
2796 return;
2798 if(shouldhave && !tPtr->ruler) {
2799 tPtr->ruler = WMCreateRuler(tPtr);
2800 (W_VIEW(tPtr->ruler))->attribs.cursor =
2801 tPtr->view->screen->defaultCursor;
2802 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
2803 WMSetRulerReleaseAction(tPtr->ruler, rulerReleaseCallBack, tPtr);
2804 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
2805 } else if(!shouldhave && tPtr->ruler) {
2806 WMShowTextRuler(tPtr, False);
2807 WMDestroyWidget(tPtr->ruler);
2808 tPtr->ruler = NULL;
2810 textDidResize(tPtr->view->delegate, tPtr->view);
2813 void
2814 WMShowTextRuler(WMText *tPtr, Bool show)
2816 if(!tPtr)
2817 return;
2818 if(!tPtr->ruler)
2819 return;
2821 if(tPtr->flags.monoFont)
2822 show = False;
2824 tPtr->flags.rulerShown = show;
2825 if(show) {
2826 WMMapWidget(tPtr->ruler);
2827 } else {
2828 WMUnmapWidget(tPtr->ruler);
2831 textDidResize(tPtr->view->delegate, tPtr->view);
2834 Bool
2835 WMGetTextRulerShown(WMText *tPtr)
2837 if(!tPtr)
2838 return 0;
2840 if(!tPtr->ruler)
2841 return 0;
2843 return tPtr->flags.rulerShown;
2847 void
2848 WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
2850 if (!tPtr)
2851 return;
2853 if (shouldhave && !tPtr->vS) {
2854 tPtr->vS = WMCreateScroller(tPtr);
2855 (W_VIEW(tPtr->vS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2856 (W_VIEW(tPtr->vS))->attribFlags |= CWOverrideRedirect | CWCursor;
2857 WMSetScrollerArrowsPosition(tPtr->vS, WSAMaxEnd);
2858 WMSetScrollerAction(tPtr->vS, scrollersCallBack, tPtr);
2859 } else if (!shouldhave && tPtr->vS) {
2860 WMUnmapWidget(tPtr->vS);
2861 WMDestroyWidget(tPtr->vS);
2862 tPtr->vS = NULL;
2865 tPtr->vpos = 0;
2866 tPtr->prevVpos = 0;
2867 textDidResize(tPtr->view->delegate, tPtr->view);
2872 Bool
2873 WMScrollText(WMText *tPtr, int amount)
2875 Bool scroll=False;
2876 if (!tPtr)
2877 return False;
2878 if (amount == 0 || !tPtr->view->flags.realized)
2879 return False;
2881 if (amount < 0) {
2882 if (tPtr->vpos > 0) {
2883 if (tPtr->vpos > amount) tPtr->vpos += amount;
2884 else tPtr->vpos=0;
2885 scroll=True;
2886 } } else {
2887 int limit = tPtr->docHeight - tPtr->visible.h;
2888 if (tPtr->vpos < limit) {
2889 if (tPtr->vpos < limit-amount) tPtr->vpos += amount;
2890 else tPtr->vpos = limit;
2891 scroll = True;
2892 } }
2894 if (scroll && tPtr->vpos != tPtr->prevVpos) {
2895 updateScrollers(tPtr);
2896 paintText(tPtr);
2898 tPtr->prevVpos = tPtr->vpos;
2899 return scroll;
2902 Bool
2903 WMPageText(WMText *tPtr, Bool direction)
2905 if (!tPtr) return False;
2906 if (!tPtr->view->flags.realized) return False;
2908 return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
2911 void
2912 WMSetTextEditable(WMText *tPtr, Bool editable)
2914 if (!tPtr)
2915 return;
2916 tPtr->flags.editable = editable;
2919 int
2920 WMGetTextEditable(WMText *tPtr)
2922 if (!tPtr)
2923 return 0;
2924 return tPtr->flags.editable;
2927 void
2928 WMSetTextIgnoresNewline(WMText *tPtr, Bool ignore)
2930 if (!tPtr)
2931 return;
2932 tPtr->flags.ignoreNewLine = ignore;
2935 Bool
2936 WMGetTextIgnoresNewline(WMText *tPtr)
2938 if (!tPtr)
2939 return True;
2940 return tPtr->flags.ignoreNewLine;
2943 void
2944 WMSetTextUsesMonoFont(WMText *tPtr, Bool mono)
2946 if (!tPtr)
2947 return;
2948 if (mono && tPtr->flags.rulerShown)
2949 WMShowTextRuler(tPtr, False);
2951 tPtr->flags.monoFont = mono;
2952 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2955 Bool
2956 WMGetTextUsesMonoFont(WMText *tPtr)
2958 if (!tPtr)
2959 return True;
2960 return tPtr->flags.monoFont;
2964 void
2965 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
2967 if (!tPtr)
2968 return;
2970 WMReleaseFont(tPtr->dFont);
2971 if (font)
2972 tPtr->dFont = font;
2973 else
2974 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2977 WMFont *
2978 WMGetTextDefaultFont(WMText *tPtr)
2980 if (!tPtr)
2981 return NULL;
2982 else
2983 return tPtr->dFont;
2986 void
2987 WMSetTextAlignment(WMText *tPtr, WMAlignment alignment)
2989 if (!tPtr)
2990 return;
2991 tPtr->flags.alignment = alignment;
2992 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2995 void
2996 WMSetTextParser(WMText *tPtr, WMAction *parser)
2998 if (!tPtr)
2999 return;
3000 tPtr->parser = parser;
3003 void
3004 WMSetTextWriter(WMText *tPtr, WMAction *writer)
3006 if (!tPtr)
3007 return;
3008 tPtr->writer = writer;
3011 int
3012 WMGetTextInsertType(WMText *tPtr)
3014 if (!tPtr)
3015 return 0;
3016 return tPtr->flags.prepend;
3020 void
3021 WMSetTextSelectionColor(WMText *tPtr, WMColor *color)
3023 TextBlock *tb;
3024 if (!tPtr || !color)
3025 return;
3027 tb = tPtr->firstTextBlock;
3028 if (!tb || !tPtr->flags.ownsSelection)
3029 return;
3031 while (tb) {
3032 if (tb->selected) {
3034 if ( (tb->s_end - tb->s_begin == tb->used) || tb->graphic) {
3035 if(tb->color)
3036 WMReleaseColor(tb->color);
3037 tb->color = WMRetainColor(color);
3039 } else if (tb->s_end <= tb->used) {
3041 TextBlock *otb = tb;
3042 int count=0;
3043 TextBlock *ntb = (TextBlock *) WMCreateTextBlockWithText(
3044 &(tb->text[tb->s_begin]),
3045 tb->d.font, color, False, (tb->s_end - tb->s_begin));
3047 if (ntb) {
3048 ntb->selected = True;
3049 ntb->s_begin = 0;
3050 ntb->s_end = ntb->used;
3051 WMAppendTextBlock(tPtr, ntb);
3052 count++;
3055 #if 0
3056 if (tb->used > tb->s_end) {
3057 ntb = (TextBlock *) WMCreateTextBlockWithText(
3058 &(tb->text[tb->s_end]),
3059 tb->d.font, tb->color, False, tb->used - tb->s_end);
3060 if (ntb) {
3061 ntb->selected = True;
3062 ntb->s_begin = 0;
3063 ntb->s_end = ntb->used;
3064 WMAppendTextBlock(tPtr, ntb);
3065 count++;
3068 #endif
3070 if (count == 1)
3071 tb = otb->next;
3072 else if (count == 2)
3073 tb = otb->next->next;
3075 tb->used = tb->s_end = tb->s_begin;
3079 tb = tb->next;
3082 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
3085 void
3086 WMSetTextSelectionFont(WMText *tPtr, WMFont *font)
3088 TextBlock *tb;
3089 if (!tPtr || !font)
3090 return;
3092 tb = tPtr->firstTextBlock;
3093 if (!tb || !tPtr->flags.ownsSelection)
3094 return;
3096 while (tb) {
3097 if (!tb->graphic)
3098 tb->d.font = WMRetainFont(font);
3099 tb = tb->next;
3101 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
3104 void
3105 WMFreezeText(WMText *tPtr)
3107 if (!tPtr)
3108 return;
3110 tPtr->flags.frozen = True;
3113 void
3114 WMThawText(WMText *tPtr)
3116 if (!tPtr)
3117 return;
3119 tPtr->flags.frozen = False;
3123 Bool
3124 WMFindInTextStream(WMText *tPtr, char *needle)
3126 char *haystack = NULL;
3127 Bool result;
3129 if (!tPtr || !needle)
3130 return False;
3132 if ( !(haystack = "WMGetTextStream(tPtr)"))
3133 return False;
3135 result = (Bool) strstr(haystack, needle);
3136 wfree(haystack);
3137 return result;