nice additions
[wmaker-crm.git] / WINGs / wtext.c
blob1b649f718d5dfe0d8ad07685368bff91046418a6
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 * - use currentTextBlock and neighbours for fast paint and layout
32 * - replace copious uses of Refreshtext with appropriate layOut()...
33 * - code replaceSelection
34 * (deleteSelection is same cept replaceSelection(tPtr, NULL); )
35 * - WMFindInTextStream should also highlight found text...
36 * - 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 int x, y; /* where to draw it from */
48 int w, h; /* it's width and height */
49 int _y;
50 unsigned short begin, end; /* what part of the text block */
51 } Section;
54 /* a TextBlock is a doubly-linked list of TextBlocks containing:
55 o text for the block, color and font
56 o or a pointer to the pixmap
57 o OR a pointer to the widget and the (text) description for its graphic
60 typedef struct _TextBlock {
61 struct _TextBlock *next; /* next text block in linked list */
62 struct _TextBlock *prior; /* prior text block in linked list */
64 char *text; /* pointer to 8- or 16-bit text */
65 /* or to the object's description */
66 union {
67 WMFont *font; /* the font */
68 WMWidget *widget; /* the embedded widget */
69 WMPixmap *pixmap; /* the pixmap */
70 } d; /* description */
72 WMColor *color; /* the color */
73 Section *sections; /* the region for layouts (a growable array) */
74 /* an _array_! of size _nsections_ */
77 unsigned short used; /* number of chars in this block */
78 unsigned short allocated; /* size of allocation (in chars) */
80 unsigned int first:1; /* first TextBlock in paragraph */
81 unsigned int blank:1; /* ie. blank paragraph */
82 unsigned int kanji:1; /* is of 16-bit characters or not */
83 unsigned int graphic:1; /* graphic or text: text=0 */
84 unsigned int object:1; /* embedded object or pixmap */
85 unsigned int underlined:1; /* underlined or not */
86 unsigned int nsections:8; /* over how many "lines" a TexBlock wraps */
87 int script:8; /* script in points: negative for subscript */
88 unsigned int marginN:9; /* which of the margins in WMText to use */
89 unsigned int RESERVED:1;
90 } TextBlock;
93 /* somehow visible.h beats the hell outta visible.size.height :-) */
94 typedef struct {
95 unsigned int y;
96 unsigned int x;
97 unsigned int h;
98 unsigned int w;
99 } myRect;
102 typedef struct W_Text {
103 W_Class widgetClass; /* the class number of this widget */
104 W_View *view; /* the view referring to this instance */
106 WMRuler *ruler; /* the ruler subwiget to manipulate paragraphs */
108 WMScroller *vS; /* the vertical scroller */
109 int vpos; /* the current vertical position */
110 int prevVpos; /* the previous vertical position */
112 WMScroller *hS; /* the horizontal scroller */
113 int hpos; /* the current horizontal position */
114 int prevHpos; /* the previous horizontal position */
115 /* in short: tPtr->hS?nowrap:wrap */
117 WMFont *dFont; /* the default font */
118 WMColor *dColor; /* the default color */
119 WMPixmap *dBulletPix; /* the default pixmap for bullets */
121 GC bgGC; /* the background GC to draw with */
122 GC fgGC; /* the foreground GC to draw with */
123 Pixmap db; /* the buffer on which to draw */
125 WMRulerMargins *margins;/* a (growable) array of margins to be used */
126 /* by the various TextBlocks */
128 myRect visible; /* the actual rectangle that can be drawn into */
129 myRect cursor; /* the position and (height) of cursor */
130 myRect sel; /* the selection rectangle */
131 int docWidth; /* the width of the entire document */
132 int docHeight; /* the height of the entire document */
135 TextBlock *firstTextBlock;
136 TextBlock *lastTextBlock;
137 TextBlock *currentTextBlock;
140 WMBag *gfxItems; /* a nice bag containing graphic items */
142 #if DO_BLINK
143 WMHandlerID timerID; /* for nice twinky-winky */
144 #endif
145 WMPoint clicked; /* where in the _document_ was clicked */
146 unsigned short tpos; /* the character position in the currentTextBlock */
147 unsigned short RESERVED;/* space taker upper... */
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 ' ' when typed */
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:4;
175 unsigned int nmargins:10; /* the number of margin arrays */
176 } flags;
177 } 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 void
192 paintText(Text *tPtr)
194 TextBlock *tb = tPtr->firstTextBlock;
195 WMFont *font;
196 GC gc, greyGC;
197 char *text;
198 int len, y, c, s, done=False;
199 int prev_y=-23;
200 WMScreen *scr = tPtr->view->screen;
201 Display *dpy = tPtr->view->screen->display;
202 Window win = tPtr->view->window;
205 if (!tPtr->view->flags.realized || !tPtr->db || tPtr->flags.frozen)
206 return;
208 XFillRectangle(dpy, tPtr->db, tPtr->bgGC,
209 0, 0, WMWidgetWidth(tPtr), WMWidgetWidth(tPtr));
210 //tPtr->visible.w, tPtr->visible.h);
212 tb = tPtr->firstTextBlock;
213 if (!tb)
214 goto _copy_area;
217 if (tPtr->flags.ownsSelection) {
218 greyGC = WMColorGC(WMGrayColor(scr));
219 //XFillRectangle(dpy, tPtr->db, greyGC,
220 // tPtr->sel.x, tPtr->sel.y-tPtr->vpos, tPtr->sel.w, tPtr->sel.h);
221 // XDrawRectangle(dpy, tPtr->db, tPtr->fgGC,
222 // tPtr->sel.x, tPtr->sel.y-tPtr->vpos, tPtr->sel.w, tPtr->sel.h);
225 done = False;
226 while (!done && tb) {
228 if (tb->graphic) {
229 tb = tb->next;
230 continue;
233 for(s=0; s<tb->nsections && !done; s++) {
236 if (tb->sections[s]._y > tPtr->vpos + tPtr->visible.h) {
237 done = True;
238 break;
241 if ( tb->sections[s].y + tb->sections[s].h < tPtr->vpos)
242 continue;
244 if (tPtr->flags.monoFont) {
245 font = tPtr->dFont;
246 gc = tPtr->fgGC;
247 } else {
248 font = tb->d.font;
249 gc = WMColorGC(tb->color);
252 if (tPtr->flags.ownsSelection) {
254 if (prev_y != tb->sections[s]._y
255 && (tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
256 && (tb->sections[s]._y + tb->sections[s].h
257 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)) {
258 XFillRectangle(dpy, tPtr->db, greyGC,
259 tPtr->visible.x,
260 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
261 tPtr->visible.w, tb->sections[s].h);
263 } else if ( prev_y != tb->sections[s]._y
264 && (tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
265 && (tb->sections[s]._y + tb->sections[s].h
266 >= tPtr->visible.y + tPtr->sel.y)
267 && (tPtr->sel.x >= tb->sections[s].x)
268 && (tPtr->visible.y + tPtr->sel.y + tPtr->sel.h
269 >= tb->sections[s]._y + tb->sections[s].h)) {
270 XFillRectangle(dpy, tPtr->db, greyGC,
271 tPtr->clicked.x,
272 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
273 tPtr->visible.w - tPtr->sel.x, tb->sections[s].h);
275 } else if (prev_y != tb->sections[s]._y
276 && (tb->sections[s]._y <= tPtr->sel.y + tPtr->sel.h)
277 && (tb->sections[s]._y >= tPtr->sel.y)) {
278 XFillRectangle(dpy, tPtr->db, greyGC,
279 tPtr->visible.x,
280 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
281 tPtr->sel.x + tPtr->sel.w -tPtr->visible.x,
282 tb->sections[s].h);
284 } else if ( prev_y != tb->sections[s]._y
285 && (tb->sections[s]._y <= tPtr->sel.y)
286 && (tb->sections[s]._y + tb->sections[s].h
287 >= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
288 XFillRectangle(dpy, tPtr->db, greyGC,
289 tPtr->sel.x,
290 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
291 tPtr->sel.w,tb->sections[s].h);
296 prev_y = tb->sections[s]._y;
298 len = tb->sections[s].end - tb->sections[s].begin;
299 text = &(tb->text[tb->sections[s].begin]);
300 y = tb->sections[s].y - tPtr->vpos;
301 WMDrawString(scr, tPtr->db, gc, font,
302 tb->sections[s].x, y, text, len);
304 if (tb->underlined) {
305 XDrawLine(dpy, tPtr->db, gc,
306 tb->sections[s].x, y + font->y + 1,
307 tb->sections[s].x + tb->sections[s].w, y + font->y + 1);
312 tb = (!done? tb->next : NULL);
316 c = WMGetBagItemCount(tPtr->gfxItems);
317 if (c > 0 && !tPtr->flags.monoFont) {
318 int j, h;
320 for(j=0; j<c; j++) {
321 tb = (TextBlock *) WMGetFromBag(tPtr->gfxItems, j);
322 if (tb->sections[0]._y + tb->sections[0].h <= tPtr->vpos
323 || tb->sections[0]._y >= tPtr->vpos + tPtr->visible.h ) {
325 if(tb->object) {
326 if ((W_VIEW(tb->d.widget))->flags.mapped) {
327 WMUnmapWidget(tb->d.widget);
330 } else {
331 if(tb->object) {
332 if (!(W_VIEW(tb->d.widget))->flags.mapped) {
333 if (!(W_VIEW(tb->d.widget))->flags.realized)
334 WMRealizeWidget(tb->d.widget);
335 WMMapWidget(tb->d.widget);
336 WMLowerWidget(tb->d.widget);
340 if (tPtr->flags.ownsSelection && 0
341 //&& (tb->sections[s]._y >= tPtr->sel.y)
342 //&& (tb->sections[s]._y + tb->sections[s].h
343 ){ // <= tPtr->sel.y + tPtr->sel.h)) {
344 XFillRectangle(dpy, tPtr->db, greyGC,
345 tb->sections[0].x, tb->sections[0].y - tPtr->vpos,
346 tb->sections[0].w, tb->sections[0].h);
349 if(tb->object) {
350 WMMoveWidget(tb->d.widget,
351 3 + tb->sections[0].x + tPtr->visible.x,
352 tb->sections[0].y + tPtr->visible.y - tPtr->vpos);
353 h = WMWidgetHeight(tb->d.widget) + 1;
354 } else {
355 WMDrawPixmap(tb->d.pixmap, tPtr->db,
356 tb->sections[0].x, tb->sections[0].y - tPtr->vpos);
357 h = tb->d.pixmap->height + 1;
360 if (tb->underlined) {
361 XDrawLine(dpy, tPtr->db, WMColorGC(tb->color),
362 tb->sections[0].x,
363 tb->sections[0].y + h,
364 tb->sections[0].x + tb->sections[0].w,
365 tb->sections[0].y + h);
366 } } } }
369 _copy_area:
371 if (tPtr->flags.editable && tPtr->flags.cursorShown
372 && tPtr->cursor.x != -23 && tPtr->flags.focused) {
373 int y = tPtr->cursor.y - tPtr->vpos;
374 XDrawLine(dpy, tPtr->db, tPtr->fgGC,
375 tPtr->cursor.x, y,
376 tPtr->cursor.x, y + tPtr->cursor.h);
378 //printf("%d %d %d\n", tPtr->cursor.x, tPtr->cursor.y, tPtr->cursor.h);
381 XCopyArea(dpy, tPtr->db, win, tPtr->bgGC,
382 0, 0,
383 tPtr->visible.w, tPtr->visible.h,
384 tPtr->visible.x, tPtr->visible.y);
386 W_DrawRelief(scr, win, 0, 0,
387 tPtr->view->size.width, tPtr->view->size.height,
388 tPtr->flags.relief);
390 if (tPtr->ruler && tPtr->flags.rulerShown)
391 XDrawLine(dpy, win,
392 tPtr->fgGC, 2, 42,
393 tPtr->view->size.width-4, 42);
398 #if DO_BLINK
400 #define CURSOR_BLINK_ON_DELAY 600
401 #define CURSOR_BLINK_OFF_DELAY 400
403 static void
404 blinkCursor(void *data)
406 Text *tPtr = (Text*)data;
408 if (tPtr->flags.cursorShown) {
409 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY,
410 blinkCursor, data);
411 } else {
412 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
413 blinkCursor, data);
415 paintText(tPtr);
416 tPtr->flags.cursorShown = !tPtr->flags.cursorShown;
418 #endif
420 static TextBlock *
421 getFirstNonGraphicBlockFor(TextBlock *tb, short dir)
423 if (!tb)
424 return NULL;
425 while (tb) {
426 if (!tb->graphic)
427 break;
428 tb = (dir? tb->next : tb->prior);
431 return tb;
435 static void
436 cursorToTextPosition(Text *tPtr, int x, int y)
438 TextBlock *tb = NULL;
439 int done=False, s, pos, len, _w, _y, dir=1; /* 1 == "down" */
440 char *text;
442 printf("%d %d\n",x,y);
443 y += (tPtr->vpos - tPtr->visible.y);
444 if (y<0)
445 y = 0;
447 x -= (tPtr->visible.x - 2);
448 if (x<0)
449 x=0;
451 /* clicked is relative to document, not window... */
452 tPtr->clicked.x = x;
453 tPtr->clicked.y = y;
455 if (! (tb = tPtr->currentTextBlock)) {
456 if (! (tb = tPtr->firstTextBlock)) {
457 tPtr->tpos = 0;
458 tPtr->cursor.h = tPtr->dFont->height;
459 tPtr->cursor.y = 2;
460 tPtr->cursor.x = 2;
461 return;
465 /* first, which direction? Most likely, newly clicked
466 position will be close to previous */
467 dir = !(y <= tb->sections[0].y);
468 if ( ( y <= tb->sections[0]._y + tb->sections[0].h )
469 && (y >= tb->sections[0]._y ) ) {
470 /* if it's on the same line */
471 if(x < tb->sections[0].x)
472 dir = 0;
473 if(x >= tb->sections[0].x)
474 dir = 1;
477 tb = tPtr->firstTextBlock;
478 dir = 1;
480 if (tPtr->flags.monoFont && tb->graphic) {
481 tb = getFirstNonGraphicBlockFor(tb, 1);
482 if (!tb) {
483 tPtr->currentTextBlock =
484 (dir? tPtr->lastTextBlock : tPtr->firstTextBlock);
485 tPtr->tpos = 0;
486 return;
490 s = (dir? 0 : tb->nsections-1);
491 if ( y >= tb->sections[s]._y
492 && y <= tb->sections[s]._y + tb->sections[s].h) {
493 goto _doneV;
496 /* get the first section of the TextBlock that lies about
497 the vertical click point */
498 done = False;
499 while (!done && tb) {
501 if (tPtr->flags.monoFont && tb->graphic) {
502 if(tb->next)
503 tb = tb->next;
504 continue;
507 s = (dir? 0 : tb->nsections-1);
508 while (!done && (dir? (s<tb->nsections) : (s>=0) )) {
510 if ( y >= tb->sections[s]._y
511 && y <= tb->sections[s]._y + tb->sections[s].h) {
512 done = True;
513 } else {
514 dir? s++ : s--;
518 if (!done) {
519 if ( (dir? tb->next : tb->prior)) {
520 tb = (dir ? tb->next : tb->prior);
521 } else {
522 pos = tb->used;
523 break; //goto _doneH;
529 if (s<0 || s>=tb->nsections) {
530 s = (dir? tb->nsections-1 : 0);
533 _doneV:
534 /* we have the line, which TextBlock on that line is it? */
535 pos = 0;
536 if (tPtr->flags.monoFont && tb->graphic)
537 tb = getFirstNonGraphicBlockFor(tb, dir);
538 if (tb) {
539 if ((dir? tb->sections[s].x >= x : tb->sections[s].x < x))
540 goto _doneH;
542 #if 0
543 if(tb->blank) {
544 _w = 0;
545 printf("blank\n");
546 } else {
547 text = &(tb->text[tb->sections[s].begin]);
548 len = tb->sections[s].end - tb->sections[s].begin;
549 _w = WMWidthOfString(tb->d.font, text, len);
551 printf("here %d %d \n", tb->sections[s].x + _w, x);
552 if ((dir? tb->sections[s].x + _w < x : tb->sections[s].x + _w >= x)) {
553 pos = tb->sections[s].end;
554 tPtr->cursor.x = tb->sections[s].x + _w;
555 goto _doneH;
557 #endif
558 _y = tb->sections[s]._y;
561 while (tb) {
563 if (tPtr->flags.monoFont && tb->graphic) {
564 tb = (dir ? tb->next : tb->prior);
565 continue;
568 if (dir) {
569 if (tb->graphic) {
570 if(tb->object)
571 _w = WMWidgetWidth(tb->d.widget);
572 else
573 _w = tb->d.pixmap->width;
574 } else {
575 text = &(tb->text[tb->sections[s].begin]);
576 len = tb->sections[s].end - tb->sections[s].begin;
577 _w = WMWidthOfString(tb->d.font, text, len);
578 if (tb->sections[s].x + _w >= x)
579 break;
582 } else {
583 if (tb->sections[s].x <= x)
584 break;
587 if ((dir? tb->next : tb->prior)) {
588 TextBlock *nxt = (dir? tb->next : tb->prior);
589 if (tPtr->flags.monoFont && nxt->graphic) {
590 nxt = getFirstNonGraphicBlockFor(nxt, dir);
591 if (!nxt) {
592 pos = 0;
593 tPtr->cursor.x = tb->sections[s].x;
594 goto _doneH;
598 if (_y != nxt->sections[0]._y) {
599 /* this must be the last/first on this line. stop */
600 pos = (dir? tb->sections[s].end : 0);
601 tPtr->cursor.x = tb->sections[s].x;
602 if (!tb->blank) {
603 if (tb->graphic) {
604 if(tb->object)
605 tPtr->cursor.x += WMWidgetWidth(tb->d.widget);
606 else
607 tPtr->cursor.x += tb->d.pixmap->width;
608 } else if (pos > tb->sections[s].begin) {
609 tPtr->cursor.x +=
610 WMWidthOfString(tb->d.font,
611 &(tb->text[tb->sections[s].begin]),
612 pos - tb->sections[s].begin);
615 goto _doneH;
619 if ( (dir? tb->next : tb->prior)) {
620 tb = (dir ? tb->next : tb->prior);
621 } else {
622 done = True;
623 break;
626 if (tb)
627 s = (dir? 0 : tb->nsections-1);
630 /* we have said TextBlock, now where within it? */
631 if (tb && !tb->graphic) {
632 WMFont *f = tb->d.font;
633 len = tb->sections[s].end - tb->sections[s].begin;
634 text = &(tb->text[tb->sections[s].begin]);
636 _w = x - tb->sections[s].x;
637 pos = 0;
639 while (pos<len && WMWidthOfString(f, text, pos+1) < _w)
640 pos++;
642 tPtr->cursor.x = tb->sections[s].x +
643 (pos? WMWidthOfString(f, text, pos) : 0);
645 pos += tb->sections[s].begin;
646 _doneH:
647 tPtr->tpos = (pos<tb->used)? pos : tb->used;
650 tPtr->currentTextBlock = tb;
651 tPtr->cursor.h = tb->sections[s].h;
652 tPtr->cursor.y = tb->sections[s]._y;
654 if (!tb)
655 printf("will hang :-)\n");
659 static void
660 updateScrollers(Text *tPtr)
663 if (tPtr->flags.frozen)
664 return;
666 if (tPtr->vS) {
667 if (tPtr->docHeight < tPtr->visible.h) {
668 WMSetScrollerParameters(tPtr->vS, 0, 1);
669 tPtr->vpos = 0;
670 } else {
671 float vmax = (float)(tPtr->docHeight);
672 WMSetScrollerParameters(tPtr->vS,
673 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
674 (float)tPtr->visible.h/vmax);
676 } else tPtr->vpos = 0;
678 if (tPtr->hS)
682 static void
683 scrollersCallBack(WMWidget *w, void *self)
685 Text *tPtr = (Text *)self;
686 Bool scroll = False;
687 Bool dimple = False;
688 int which;
690 if (!tPtr->view->flags.realized || tPtr->flags.frozen)
691 return;
693 if (w == tPtr->vS) {
694 float vmax;
695 int height;
696 vmax = (float)(tPtr->docHeight);
697 height = tPtr->visible.h;
699 which = WMGetScrollerHitPart(tPtr->vS);
700 switch(which) {
701 case WSDecrementLine:
702 if (tPtr->vpos > 0) {
703 if (tPtr->vpos>16) tPtr->vpos-=16;
704 else tPtr->vpos=0;
705 scroll=True;
706 }break;
707 case WSIncrementLine: {
708 int limit = tPtr->docHeight - height;
709 if (tPtr->vpos < limit) {
710 if (tPtr->vpos<limit-16) tPtr->vpos+=16;
711 else tPtr->vpos=limit;
712 scroll = True;
713 }}break;
714 case WSDecrementPage:
715 tPtr->vpos -= height;
717 if (tPtr->vpos < 0)
718 tPtr->vpos = 0;
719 dimple = True;
720 scroll = True;
721 printf("dimple needs to jump to mouse location ;-/\n");
722 break;
723 case WSIncrementPage:
724 tPtr->vpos += height;
725 if (tPtr->vpos > (tPtr->docHeight - height))
726 tPtr->vpos = tPtr->docHeight - height;
727 dimple = True;
728 scroll = True;
729 printf("dimple needs to jump to mouse location ;-/\n");
730 break;
733 case WSKnob:
734 tPtr->vpos = WMGetScrollerValue(tPtr->vS)
735 * (float)(tPtr->docHeight - height);
736 scroll = True;
737 break;
739 case WSKnobSlot:
740 case WSNoPart:
741 printf("WSNoPart, WSKnobSlot\n");
742 #if 0
743 float vmax = (float)(tPtr->docHeight);
744 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
745 (float)tPtr->visible.h/vmax;
746 dimple =where mouse is.
747 #endif
748 break;
750 scroll = (tPtr->vpos != tPtr->prevVpos);
751 tPtr->prevVpos = tPtr->vpos;
754 if (w == tPtr->hS)
757 if (scroll) {
759 if (0&&dimple) {
760 if (tPtr->rulerShown)
761 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
762 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
763 else
764 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
765 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
768 if (which == WSDecrementLine || which == WSIncrementLine)
769 updateScrollers(tPtr);
770 paintText(tPtr);
776 typedef struct {
777 TextBlock *tb;
778 unsigned short begin, end; /* what part of the text block */
780 } myLineItems;
783 static int
784 layOutLine(Text *tPtr, myLineItems *items, int nitems, int x, int y,
785 int pwidth)
787 int i, j=0, lw = 0, line_height=0, max_d=0, len, n;
788 WMFont *font;
789 char *text;
790 TextBlock *tb;
791 Bool gfx=0;
792 TextBlock *tbsame=NULL;
794 for(i=0; i<nitems; i++) {
795 tb = items[i].tb;
797 if (tb->graphic) {
798 if (!tPtr->flags.monoFont) {
799 if(tb->object) {
800 WMWidget *wdt = tb->d.widget;
801 line_height = WMAX(line_height, WMWidgetHeight(wdt));
802 if (tPtr->flags.alignment != WALeft)
803 lw += WMWidgetWidth(wdt);
804 } else {
805 line_height = WMAX(line_height, tb->d.pixmap->height);
806 if (tPtr->flags.alignment != WALeft)
807 lw += tb->d.pixmap->width;
809 gfx = True;
812 } else {
813 font = (tPtr->flags.monoFont)?tPtr->dFont : tb->d.font;
814 max_d = WMAX(max_d, font->height-font->y);
815 line_height = WMAX(line_height, font->height);
816 text = &(tb->text[items[i].begin]);
817 len = items[i].end - items[i].begin;
818 if (tPtr->flags.alignment != WALeft)
819 lw += WMWidthOfString(font, text, len);
823 if (tPtr->flags.alignment == WARight) {
824 j = pwidth - lw;
825 } else if (tPtr->flags.alignment == WACenter) {
826 j = (int) ((float)(pwidth - lw))/2.0;
828 if (gfx)
829 y+=10;
831 for(i=0; i<nitems; i++) {
832 tb = items[i].tb;
834 if (tbsame == tb) { /*extend it, since it's on same line */
835 tb->sections[tb->nsections-1].end = items[i].end;
836 n = tb->nsections-1;
837 } else {
838 tb->sections = wrealloc(tb->sections,
839 (++tb->nsections)*sizeof(Section));
840 n = tb->nsections-1;
841 tb->sections[n]._y = y;
842 tb->sections[n].x = x+j;
843 tb->sections[n].h = line_height;
844 tb->sections[n].begin = items[i].begin;
845 tb->sections[n].end = items[i].end;
847 if (tb->graphic) {
848 if (!tPtr->flags.monoFont) {
849 if(tb->object) {
850 WMWidget *wdt = tb->d.widget;
851 tb->sections[n].y = 1 + max_d +
852 y + line_height - WMWidgetHeight(wdt);
853 tb->sections[n].w = WMWidgetWidth(wdt);
854 } else {
855 tb->sections[n].y = 1 + max_d +
856 y + line_height - tb->d.pixmap->height;
857 tb->sections[n].w = tb->d.pixmap->width;
859 x += tb->sections[n].w;
861 } else {
862 font = (tPtr->flags.monoFont)? tPtr->dFont : tb->d.font;
863 len = items[i].end - items[i].begin;
864 text = &(tb->text[items[i].begin]);
866 tb->sections[n].y = y+line_height-font->y;
867 tb->sections[n].w =
868 WMWidthOfString(font,
869 &(tb->text[tb->sections[n].begin]),
870 tb->sections[n].end - tb->sections[n].begin);
872 x += WMWidthOfString(font, text, len);
875 tbsame = tb;
878 return line_height+(gfx?10:0);
883 static void
884 output(char *ptr, int len)
886 char s[len+1];
887 memcpy(s, ptr, len);
888 s[len] = 0;
889 //printf(" s is [%s] (%d)\n", s, strlen(s));
890 printf("[%s]\n", s);
894 /* tb->text doesn't necessarily end in '\0' hmph! (strchr) */
895 static inline char *
896 mystrchr(char *s, char needle, unsigned short len)
898 char *haystack = s;
900 if (!haystack || len < 1)
901 return NULL;
903 while ( (int) (haystack - s) < len ) {
904 if (*haystack == needle)
905 return haystack;
906 haystack++;
908 return NULL;
911 #define MAX_TB_PER_LINE 64
913 static void
914 layOutDocument(Text *tPtr)
916 TextBlock *tb;
917 myLineItems items[MAX_TB_PER_LINE];
918 WMFont *font;
919 Bool lhc = !tPtr->flags.laidOut; /* line height changed? */
920 int prev_y;
922 int nitems=0, x=0, y=0, lw = 0, width=0;
923 int pwidth = tPtr->visible.w - tPtr->visible.x;
925 char *start=NULL, *mark=NULL;
926 int begin, end;
928 if (tPtr->flags.frozen)
929 return;
931 if (!(tb = tPtr->firstTextBlock))
932 return;
934 if (0&&tPtr->flags.laidOut) {
935 tb = tPtr->currentTextBlock;
936 if (tb->sections && tb->nsections>0)
937 prev_y = tb->sections[tb->nsections-1]._y;
938 y+=10;
939 printf("1 prev_y %d \n", prev_y);
941 /* search backwards for textblocks on same line */
942 while (tb) {
943 if (!tb->sections || tb->nsections<1) {
944 tb = tPtr->firstTextBlock;
945 break;
947 if (tb->sections[tb->nsections-1]._y != prev_y) {
948 tb = tb->next;
949 break;
951 // prev_y = tb->sections[tb->nsections-1]._y;
952 tb = tb->prior;
954 y = 0;//tb->sections[tb->nsections-1]._y;
955 printf("2 prev_y %d \n\n", tb->sections[tb->nsections-1]._y);
959 while (tb) {
961 if (tb->sections && tb->nsections>0) {
962 wfree(tb->sections);
963 tb->sections = NULL;
964 tb->nsections = 0;
967 if (tb->first) {
968 y += layOutLine(tPtr, items, nitems, x, y, pwidth);
969 x = 0;//tPtr->visible.x+2;
970 nitems = 0;
971 lw = 0;
974 if (tb->graphic) {
975 if (!tPtr->flags.monoFont) {
976 if(tb->object)
977 width = WMWidgetWidth(tb->d.widget);
978 else
979 width = tb->d.pixmap->width;
981 if (width > pwidth)printf("rescale graphix to fit?\n");
982 lw += width;
983 if (lw >= pwidth - x
984 || nitems >= MAX_TB_PER_LINE) {
985 y += layOutLine(tPtr, items, nitems, x, y, pwidth);
986 nitems = 0;
987 x = 0;//tPtr->visible.x+2;
988 lw = width;
991 items[nitems].tb = tb;
992 items[nitems].begin = 0;
993 items[nitems].end = 0;
994 nitems++;
997 } else if ((start = tb->text)) {
998 begin = end = 0;
999 font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
1001 while (start) {
1002 mark = mystrchr(start, ' ', tb->used);
1003 if (mark) {
1004 end += (int)(mark-start)+1;
1005 start = mark+1;
1006 } else {
1007 end += strlen(start);
1008 start = mark;
1011 if (end > tb->used)
1012 end = tb->used;
1014 if (end-begin > 0) {
1016 width = WMWidthOfString(font,
1017 &tb->text[begin], end-begin);
1019 if (width > pwidth) { /* break this tb up */
1020 char *t = &tb->text[begin];
1021 int l=end-begin, i=0;
1022 do {
1023 width = WMWidthOfString(font, t, ++i);
1024 } while (width < pwidth && i < l);
1025 end = begin+i;
1026 if (start) // and since (nil)-4 = 0xfffffffd
1027 start -= l-i;
1030 lw += width;
1033 if ((lw >= pwidth - x)
1034 || nitems >= MAX_TB_PER_LINE) {
1035 y += layOutLine(tPtr, items, nitems, x, y, pwidth);
1036 lw = width;
1037 x = 0; //tPtr->visible.x+2;
1038 nitems = 0;
1041 items[nitems].tb = tb;
1042 items[nitems].begin = begin;
1043 items[nitems].end = end;
1044 nitems++;
1046 begin = end;
1049 tb = tb->next;
1053 if (nitems > 0)
1054 y += layOutLine(tPtr, items, nitems, x, y, pwidth);
1055 if (lhc) {
1056 tPtr->docHeight = y+10;
1057 updateScrollers(tPtr);
1059 tPtr->flags.laidOut = True;
1064 static void
1065 textDidResize(W_ViewDelegate *self, WMView *view)
1067 Text *tPtr = (Text *)view->self;
1068 unsigned short w = WMWidgetWidth(tPtr);
1069 unsigned short h = WMWidgetHeight(tPtr);
1070 unsigned short rh = 0, vw = 0;
1072 if (tPtr->ruler && tPtr->flags.rulerShown) {
1073 WMMoveWidget(tPtr->ruler, 2, 2);
1074 WMResizeWidget(tPtr->ruler, w - 4, 40);
1075 rh = 40;
1078 if (tPtr->vS) {
1079 WMMoveWidget(tPtr->vS, 1, rh + 2);
1080 WMResizeWidget(tPtr->vS, 20, h - rh - 3);
1081 vw = 20;
1082 WMSetRulerOffset(tPtr->ruler,22);
1083 } else WMSetRulerOffset(tPtr->ruler, 2);
1085 if (tPtr->hS) {
1086 if (tPtr->vS) {
1087 WMMoveWidget(tPtr->hS, vw, h - 21);
1088 WMResizeWidget(tPtr->hS, w - vw - 1, 20);
1089 } else {
1090 WMMoveWidget(tPtr->hS, vw+1, h - 21);
1091 WMResizeWidget(tPtr->hS, w - vw - 2, 20);
1095 tPtr->visible.x = (tPtr->vS)?22:2;
1096 tPtr->visible.y = (tPtr->ruler && tPtr->flags.rulerShown)?43:3;
1097 tPtr->visible.w = tPtr->view->size.width - tPtr->visible.x - 12;
1098 tPtr->visible.h = tPtr->view->size.height - tPtr->visible.y;
1099 tPtr->visible.h -= (tPtr->hS)?20:0;
1101 tPtr->margins[0].left = tPtr->margins[0].right = tPtr->visible.x;
1102 tPtr->margins[0].body = tPtr->visible.x;
1103 tPtr->margins[0].right = tPtr->visible.w;
1106 if (tPtr->view->flags.realized) {
1108 if (tPtr->db) {
1109 XFreePixmap(tPtr->view->screen->display, tPtr->db);
1110 tPtr->db = (Pixmap) NULL;
1113 if (tPtr->visible.w < 40)
1114 tPtr->visible.w = 40;
1115 if (tPtr->visible.h < 20)
1116 tPtr->visible.h = 20;
1118 //if (size change or !db
1119 if(!tPtr->db) {
1120 tPtr->db = XCreatePixmap(tPtr->view->screen->display,
1121 tPtr->view->window, tPtr->visible.w,
1122 tPtr->visible.h, tPtr->view->screen->depth);
1126 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1129 W_ViewDelegate _TextViewDelegate =
1131 NULL,
1132 NULL,
1133 textDidResize,
1134 NULL,
1137 /* nice, divisble-by-16 blocks */
1138 static inline unsigned short
1139 reqBlockSize(unsigned short requested)
1141 return requested + 16 - (requested%16);
1145 static void
1146 clearText(Text *tPtr)
1148 if (!tPtr->firstTextBlock)
1149 return;
1151 while (tPtr->currentTextBlock)
1152 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1154 tPtr->firstTextBlock = NULL;
1155 tPtr->currentTextBlock = NULL;
1156 tPtr->lastTextBlock = NULL;
1159 static void
1160 deleteTextInteractively(Text *tPtr, KeySym ksym)
1162 TextBlock *tb = tPtr->currentTextBlock;
1163 Bool back = (Bool) (ksym == XK_BackSpace);
1164 Bool done = 1;
1166 if (!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1167 XBell(tPtr->view->screen->display, 0);
1168 return;
1171 if (!tb)
1172 return;
1174 if (back && tPtr->tpos < 1) {
1175 if (tb->prior) {
1176 tb = tb->prior;
1177 tPtr->tpos = tb->used;
1178 tPtr->currentTextBlock = tb;
1179 done = 1;
1183 if ( (tb->used > 0) && ((back?tPtr->tpos > 0:1))
1184 && (tPtr->tpos <= tb->used) && !tb->graphic) {
1185 if (back)
1186 tPtr->tpos--;
1187 memmove(&(tb->text[tPtr->tpos]),
1188 &(tb->text[tPtr->tpos + 1]), tb->used - tPtr->tpos);
1189 tb->used--;
1190 done = 0;
1193 if ( (back? (tPtr->tpos < 1 && !done) : ( tPtr->tpos >= tb->used))
1194 || tb->graphic) {
1196 TextBlock *sibling = (back? tb->prior : tb->next);
1198 printf("tb->graphic %d\n", tb->graphic);
1199 if(tb->used == 0 || tb->graphic)
1200 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1202 if (sibling) {
1203 tPtr->currentTextBlock = sibling;
1204 tPtr->tpos = (back? sibling->used : 0);
1208 #if 0
1210 if (back) {
1211 if (tPtr->tpos < 1 && !done) {
1212 TextBlock *prior = tb->prior;
1213 if (tb->used == 0)
1214 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1215 if (prior) {
1216 tPtr->currentTextBlock = prior;
1217 tPtr->tpos = prior->used;
1220 } else {
1221 if(tPtr->tpos >= tb->used) {
1222 TextBlock *next = tb->next;
1223 if (tb->used == 0)
1224 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1225 if(next) {
1226 tPtr->tpos = 0;
1227 tPtr->currentTextBlock = next;
1231 #endif
1236 static void
1237 insertTextInteractively(Text *tPtr, char *text, int len)
1239 TextBlock *tb;
1240 char *newline = NULL;
1242 if (!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1243 XBell(tPtr->view->screen->display, 0);
1244 return;
1247 if (!tPtr->flags.editable || len < 1 || !text)
1248 return;
1250 if(tPtr->flags.ignoreNewLine && *text == '\n' && len == 1)
1251 return;
1253 if (tPtr->flags.ignoreNewLine) {
1254 int i;
1255 for(i=0; i<len; i++) {
1256 if (text[i] == '\n')
1257 text[i] = ' ';
1261 tb = tPtr->currentTextBlock;
1262 if (!tb || tb->graphic) {
1263 text[len] = 0;
1264 WMAppendTextStream(tPtr, text);
1265 if (tPtr->currentTextBlock) {
1266 tPtr->tpos = tPtr->currentTextBlock->used;
1268 return;
1271 if ((newline = strchr(text, '\n'))) {
1272 int nlen = (int)(newline-text);
1273 int s = tb->used - tPtr->tpos;
1274 char save[s];
1276 printf("got newline %d\n", s);
1277 if (!tb->blank && nlen>0) {
1278 if (s > 0) {
1279 memcpy(save, &tb->text[tPtr->tpos], s);
1280 tb->used -= (tb->used - tPtr->tpos);
1282 text[nlen] = 0;
1283 insertTextInteractively(tPtr, text, nlen);
1284 newline++;
1285 WMAppendTextStream(tPtr, newline);
1286 if (s>0)
1287 insertTextInteractively(tPtr, save, s);
1288 } else {
1289 if(tb->blank) {
1290 WMAppendTextStream(tPtr, text);
1291 } else {
1292 printf("split me\n");
1297 } else {
1299 if (tb->used + len >= tb->allocated) {
1300 tb->allocated = reqBlockSize(tb->used+len);
1301 tb->text = wrealloc(tb->text, tb->allocated);
1304 if (tb->blank) {
1305 memcpy(tb->text, text, len);
1306 tb->used = len;
1307 tPtr->tpos = len;
1308 tb->blank = False;
1309 } else {
1310 memmove(&(tb->text[tPtr->tpos+len]), &tb->text[tPtr->tpos],
1311 tb->used-tPtr->tpos+1);
1312 memmove(&tb->text[tPtr->tpos], text, len);
1313 tb->used += len;
1314 tPtr->tpos += len;
1321 static void
1322 selectRegion(Text *tPtr, int x, int y)
1324 if (x < 0 || y < 0)
1325 return;
1326 y += (tPtr->flags.rulerShown? 40: 0);
1327 y += tPtr->vpos;
1328 if (y>10) y -= 10; /* the original offset */
1330 x -= tPtr->visible.x-2;
1331 if (x<0) x=0;
1333 tPtr->sel.x = WMAX(0, WMIN(tPtr->clicked.x, x));
1334 tPtr->sel.w = abs(tPtr->clicked.x - x);
1335 tPtr->sel.y = WMAX(0, WMIN(tPtr->clicked.y, y));
1336 tPtr->sel.h = abs(tPtr->clicked.y - y);
1338 tPtr->flags.ownsSelection = True;
1339 paintText(tPtr);
1343 static void
1344 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1345 void *cdata, WMData *data)
1347 Text *tPtr = (Text *)view->self;
1348 char *str;
1350 tPtr->flags.waitingForSelection = False;
1352 if (data) {
1353 str = (char*)WMDataBytes(data);
1354 if (0&&tPtr->parser) {
1355 /* parser is not yet well behaved to do this properly..*/
1356 (tPtr->parser) (tPtr, (void *) str);
1357 } else {
1358 insertTextInteractively(tPtr, str, strlen(str));
1360 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1361 } else {
1362 int n;
1363 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1364 if (str) {
1365 str[n] = 0;
1366 if (0&&tPtr->parser) {
1367 /* parser is not yet well behaved to do this properly..*/
1368 (tPtr->parser) (tPtr, (void *) str);
1369 } else {
1370 insertTextInteractively(tPtr, str, n);
1372 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1373 XFree(str);
1378 static void
1379 releaseSelection(Text *tPtr)
1381 tPtr->flags.ownsSelection = False;
1382 paintText(tPtr);
1385 static WMData *
1386 requestHandler(WMView *view, Atom selection, Atom target,
1387 void *cdata, Atom *type)
1389 Text *tPtr = view->self;
1390 Display *dpy = tPtr->view->screen->display;
1391 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1392 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1394 *type = target;
1395 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
1396 char *text = WMGetTextSelected(tPtr);
1397 WMData *data = WMCreateDataWithBytes(text, strlen(text));
1398 wfree(text);
1399 return data;
1400 } else {
1401 Atom PIXMAP = XInternAtom(dpy, "PIXMAP", False);
1402 WMData *data = WMCreateDataWithBytes("bleh", 4);
1403 if(target == PIXMAP) {
1404 printf("whoa! pixmap WMGetTextSelectedObjects\n");
1406 return data;
1409 return NULL;
1413 static void
1414 lostHandler(WMView *view, Atom selection, void *cdata)
1416 releaseSelection((WMText *)view->self);
1419 static WMSelectionProcs selectionHandler = {
1420 requestHandler, lostHandler, NULL
1423 static void
1424 ownershipObserver(void *observerData, WMNotification *notification)
1426 WMText *to = (WMText *)observerData;
1427 WMText *tw = (WMText *)WMGetNotificationClientData(notification);
1428 if (to != tw)
1429 lostHandler(to->view, XA_PRIMARY, NULL);
1433 static void
1434 handleTextKeyPress(Text *tPtr, XEvent *event)
1436 char buffer[2];
1437 KeySym ksym;
1438 int control_pressed = False;
1439 // int h=1;
1441 if (((XKeyEvent *) event)->state & ControlMask)
1442 control_pressed = True;
1443 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = 0;
1445 switch(ksym) {
1446 case XK_Right:
1447 case XK_Left:
1448 case XK_Down:
1449 case XK_Up: {
1450 TextBlock *tb = tPtr->currentTextBlock;
1451 int x = tPtr->cursor.x + tPtr->visible.x;
1452 int w, pos;
1454 if(!tb)
1455 break;
1456 if(tb->graphic) {
1457 if(tb->object) {
1458 w = WMWidgetWidth(tb->d.widget);
1459 } else {
1460 w = tb->d.pixmap->width;
1462 } else {
1463 pos = tPtr->tpos;
1464 w = WMWidthOfString(tb->d.font, &tb->text[pos], 1);
1467 cursorToTextPosition(tPtr, w + tPtr->cursor.x + tPtr->visible.x,
1468 tPtr->visible.y + tPtr->cursor.y + tPtr->cursor.h);
1469 if(x == tPtr->cursor.x + tPtr->visible.x) {
1470 printf("same %d %d\n", x, tPtr->cursor.x + tPtr->visible.x);
1471 cursorToTextPosition(tPtr, tPtr->visible.x,
1472 3 + tPtr->visible.y + tPtr->cursor.y + tPtr->cursor.h);
1474 paintText(tPtr);
1476 break;
1478 case XK_BackSpace:
1479 case XK_Delete:
1480 case XK_KP_Delete:
1481 deleteTextInteractively(tPtr, ksym);
1482 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1483 break;
1485 case XK_Control_R :
1486 case XK_Control_L :
1487 control_pressed = True;
1488 break;
1490 case XK_Return:
1491 buffer[0] = '\n';
1492 default:
1493 if (buffer[0] != 0 && !control_pressed) {
1494 insertTextInteractively(tPtr, buffer, 1);
1495 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1497 } else if (control_pressed && ksym==XK_r) {
1498 Bool i = !tPtr->flags.rulerShown;
1499 WMShowTextRuler(tPtr, i);
1500 tPtr->flags.rulerShown = i;
1502 else if (control_pressed && buffer[0] == '\a')
1503 XBell(tPtr->view->screen->display, 0);
1506 if (!control_pressed && tPtr->flags.ownsSelection)
1507 releaseSelection(tPtr);
1510 static void
1511 handleWidgetPress(XEvent *event, void *data)
1513 TextBlock *tb = (TextBlock *)data;
1514 Text *tPtr;
1515 WMWidget *w;
1517 if (!tb)
1518 return;
1519 /* this little bit of nastiness here saves a boatload of trouble */
1520 w = (WMWidget *)(((W_VIEW(tb->d.widget))->parent)->self);
1521 if (W_CLASS(w) != WC_Text)
1522 return;
1523 tPtr = (Text*)w;
1524 tPtr->currentTextBlock = getFirstNonGraphicBlockFor(tb, 1);
1525 if (!tPtr->currentTextBlock)
1526 tPtr->currentTextBlock = tb;
1527 tPtr->tpos = 0;
1528 output(tPtr->currentTextBlock->text, tPtr->currentTextBlock->used);
1529 //if (!tPtr->flags.focused) {
1530 // WMSetFocusToWidget(tPtr);
1531 // tPtr->flags.focused = True;
1532 //}
1536 static void
1537 handleActionEvents(XEvent *event, void *data)
1539 Text *tPtr = (Text *)data;
1540 Display *dpy = event->xany.display;
1541 KeySym ksym;
1544 switch (event->type) {
1545 case KeyPress:
1546 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1547 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
1548 tPtr->flags.extendSelection = True;
1549 return;
1552 if (tPtr->flags.waitingForSelection)
1553 return;
1554 if (tPtr->flags.focused) {
1555 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1556 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1557 GrabModeAsync, GrabModeAsync, None,
1558 tPtr->view->screen->invisibleCursor, CurrentTime);
1559 tPtr->flags.pointerGrabbed = True;
1560 handleTextKeyPress(tPtr, event);
1562 } break;
1564 case KeyRelease:
1565 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1566 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
1567 tPtr->flags.extendSelection = False;
1568 return;
1569 //end modify flag so selection can be extended
1571 break;
1574 case MotionNotify:
1575 if (tPtr->flags.pointerGrabbed) {
1576 tPtr->flags.pointerGrabbed = False;
1577 XUngrabPointer(dpy, CurrentTime);
1579 if ((event->xmotion.state & Button1Mask)) {
1580 if (!tPtr->flags.ownsSelection) {
1581 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1582 event->xbutton.time, &selectionHandler, NULL);
1583 tPtr->flags.ownsSelection = True;
1585 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1587 break;
1590 case ButtonPress:
1591 tPtr->flags.buttonHeld = True;
1592 if (tPtr->flags.extendSelection) {
1593 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1594 return;
1596 if (event->xbutton.button == Button1) {
1598 if (!tPtr->flags.focused) {
1599 WMSetFocusToWidget(tPtr);
1600 tPtr->flags.focused = True;
1603 if (tPtr->flags.ownsSelection)
1604 releaseSelection(tPtr);
1605 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1606 paintText(tPtr);
1607 if (tPtr->flags.pointerGrabbed) {
1608 tPtr->flags.pointerGrabbed = False;
1609 XUngrabPointer(dpy, CurrentTime);
1610 break;
1614 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown)
1615 WMScrollText(tPtr, -16);
1616 else if (event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1617 WMScrollText(tPtr, 16);
1618 break;
1620 case ButtonRelease:
1621 tPtr->flags.buttonHeld = False;
1622 if (tPtr->flags.pointerGrabbed) {
1623 tPtr->flags.pointerGrabbed = False;
1624 XUngrabPointer(dpy, CurrentTime);
1625 break;
1627 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown
1628 || event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1629 break;
1631 if (event->xbutton.button == Button2) {
1632 char *text = NULL;
1633 int n;
1635 if (!tPtr->flags.editable) {
1636 XBell(dpy, 0);
1637 break;
1640 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1641 event->xbutton.time, pasteText, NULL)) {
1642 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1643 if (text) {
1644 text[n] = 0;
1645 if (0&&tPtr->parser) {
1646 /* parser is not yet well behaved to do this properly..*/
1647 (tPtr->parser) (tPtr, (void *) text);
1648 } else {
1649 insertTextInteractively(tPtr, text, n-1);
1651 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1652 XFree(text);
1653 } else tPtr->flags.waitingForSelection = True;
1656 break;
1663 static void
1664 handleEvents(XEvent *event, void *data)
1666 Text *tPtr = (Text *)data;
1668 switch(event->type) {
1669 case Expose:
1671 if(tPtr->hS) {
1672 if (!(W_VIEW(tPtr->hS))->flags.realized)
1673 WMRealizeWidget(tPtr->hS);
1674 if (!((W_VIEW(tPtr->hS))->flags.mapped))
1675 WMMapWidget(tPtr->hS);
1678 if(tPtr->vS) {
1679 if (!(W_VIEW(tPtr->vS))->flags.realized)
1680 WMRealizeWidget(tPtr->vS);
1681 if (!((W_VIEW(tPtr->vS))->flags.mapped))
1682 WMMapWidget(tPtr->vS);
1685 if(tPtr->ruler) {
1686 if (!(W_VIEW(tPtr->ruler))->flags.realized)
1687 WMRealizeWidget(tPtr->ruler);
1689 if (!((W_VIEW(tPtr->ruler))->flags.mapped)
1690 && tPtr->flags.rulerShown)
1691 WMMapWidget(tPtr->ruler);
1693 if(!tPtr->db)
1694 textDidResize(tPtr->view->delegate, tPtr->view);
1696 if (!event->xexpose.count && tPtr->view->flags.realized)
1697 paintText(tPtr);
1698 break;
1700 case FocusIn:
1701 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))
1702 != tPtr->view)
1703 return;
1704 tPtr->flags.focused = True;
1705 #if DO_BLINK
1706 if (tPtr->flags.editable && !tPtr->timerID) {
1707 tPtr->timerID = WMAddTimerHandler(12+0*CURSOR_BLINK_ON_DELAY,
1708 blinkCursor, tPtr);
1710 #endif
1712 break;
1714 case FocusOut:
1715 tPtr->flags.focused = False;
1716 paintText(tPtr);
1717 #if DO_BLINK
1718 if (tPtr->timerID) {
1719 WMDeleteTimerHandler(tPtr->timerID);
1720 tPtr->timerID = NULL;
1722 #endif
1723 break;
1725 case DestroyNotify:
1726 clearText(tPtr);
1727 if(tPtr->hS)
1728 WMDestroyWidget(tPtr->hS);
1729 if(tPtr->vS)
1730 WMDestroyWidget(tPtr->vS);
1731 if(tPtr->ruler)
1732 WMDestroyWidget(tPtr->ruler);
1733 if(tPtr->db)
1734 XFreePixmap(tPtr->view->screen->display, tPtr->db);
1735 if(tPtr->gfxItems)
1736 WMFreeBag(tPtr->gfxItems);
1737 #if DO_BLINK
1738 if (tPtr->timerID)
1739 WMDeleteTimerHandler(tPtr->timerID);
1740 #endif
1741 WMReleaseFont(tPtr->dFont);
1742 WMReleaseColor(tPtr->dColor);
1743 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1744 WMRemoveNotificationObserver(tPtr);
1746 wfree(tPtr);
1748 break;
1754 static void
1755 insertPlainText(WMText *tPtr, char *text)
1757 char *start, *mark;
1758 void *tb = NULL;
1760 if (!text) {
1761 clearText(tPtr);
1762 return;
1765 start = text;
1766 while (start) {
1767 mark = strchr(start, '\n');
1768 if (mark) {
1769 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1770 tPtr->dColor, True, (int)(mark-start));
1771 start = mark+1;
1772 } else {
1773 if (start && strlen(start)) {
1774 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1775 tPtr->dColor, False, strlen(start));
1776 } else tb = NULL;
1777 start = mark;
1780 if (tPtr->flags.prepend)
1781 WMPrependTextBlock(tPtr, tb);
1782 else
1783 WMAppendTextBlock(tPtr, tb);
1785 return;
1790 static void
1791 rulerMoveCallBack(WMWidget *w, void *self)
1793 Text *tPtr = (Text *)self;
1794 if (!tPtr)
1795 return;
1796 if (W_CLASS(tPtr) != WC_Text)
1797 return;
1799 paintText(tPtr);
1803 static void
1804 rulerReleaseCallBack(WMWidget *w, void *self)
1806 Text *tPtr = (Text *)self;
1807 if (!tPtr)
1808 return;
1809 if (W_CLASS(tPtr) != WC_Text)
1810 return;
1812 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1813 return;
1817 #if 0
1818 static unsigned
1819 draggingEntered(WMView *self, WMDraggingInfo *info)
1821 printf("draggingEntered\n");
1822 return WDOperationCopy;
1826 static unsigned
1827 draggingUpdated(WMView *self, WMDraggingInfo *info)
1829 printf("draggingUpdated\n");
1830 return WDOperationCopy;
1834 static void
1835 draggingExited(WMView *self, WMDraggingInfo *info)
1837 printf("draggingExited\n");
1840 static void
1841 prepareForDragOperation(WMView *self, WMDraggingInfo *info)
1843 printf("draggingExited\n");
1844 return;//"bll"; //"application/X-color";
1848 static Bool
1849 performDragOperation(WMView *self, WMDraggingInfo *info) //, WMData *data)
1851 char *colorName = "Blue";// (char*)WMDataBytes(data);
1852 WMColor *color;
1853 WMText *tPtr = (WMText *)self->self;
1855 if (!tPtr)
1856 return False;
1858 if (tPtr->flags.monoFont)
1859 return False;
1861 color = WMCreateNamedColor(W_VIEW_SCREEN(self), colorName, True);
1862 printf("color [%s] %p\n", colorName, color);
1863 if(color) {
1864 WMSetTextSelectionColor(tPtr, color);
1865 WMReleaseColor(color);
1868 return;// True;
1871 static void
1872 concludeDragOperation(WMView *self, WMDraggingInfo *info)
1874 printf("concludeDragOperation\n");
1878 static WMDragDestinationProcs _DragDestinationProcs = {
1879 draggingEntered,
1880 draggingUpdated,
1881 draggingExited,
1882 prepareForDragOperation,
1883 performDragOperation,
1884 concludeDragOperation
1886 #endif
1889 WMText *
1890 WMCreateText(WMWidget *parent)
1892 Text *tPtr = wmalloc(sizeof(Text));
1893 if (!tPtr) {
1894 printf("could not create text widget\n");
1895 return NULL;
1898 #if 0
1899 printf("sizeof:\n");
1900 printf(" TextBlock %d\n", sizeof(TextBlock));
1901 printf(" Section %d\n", sizeof(Section));
1902 printf(" char * %d\n", sizeof(char *));
1903 printf(" void * %d\n", sizeof(void *));
1904 printf(" short %d\n", sizeof(short));
1905 printf(" Text %d\n", sizeof(Text));
1906 #endif
1908 memset(tPtr, 0, sizeof(Text));
1909 tPtr->widgetClass = WC_Text;
1910 tPtr->view = W_CreateView(W_VIEW(parent));
1911 if (!tPtr->view) {
1912 perror("could not create text's view\n");
1913 free(tPtr);
1914 return NULL;
1916 tPtr->view->self = tPtr;
1917 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
1918 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
1919 W_ResizeView(tPtr->view, 250, 200);
1920 tPtr->bgGC = WMColorGC(tPtr->view->screen->white);
1921 tPtr->fgGC = WMColorGC(tPtr->view->screen->black);
1922 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
1924 tPtr->ruler = NULL;
1925 tPtr->vS = NULL;
1926 tPtr->hS = NULL;
1928 tPtr->dFont = NULL;
1929 //tPtr->dFont = WMCreateFont(tPtr->view->screen,
1930 // "-*-fixed-medium-r-normal--26-*-*-*-*-*-*-*");
1931 //"-sony-fixed-medium-r-normal--24-230-75-75-c-120-jisx0201.1976-0");
1932 // "-*-times-bold-r-*-*-12-*-*-*-*-*-*-*,"
1933 // "-*-fixed-medium-r-normal-*-12-*");
1934 if (!tPtr->dFont)
1935 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
1937 tPtr->dColor = WMBlackColor(tPtr->view->screen);
1939 tPtr->view->delegate = &_TextViewDelegate;
1941 #if DO_BLINK
1942 tPtr->timerID = NULL;
1943 #endif
1945 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
1946 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
1947 handleEvents, tPtr);
1949 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
1950 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
1951 handleActionEvents, tPtr);
1953 WMAddNotificationObserver(ownershipObserver, tPtr, "_lostOwnership", tPtr);
1955 #if 0
1956 WMSetViewDragDestinationProcs(tPtr->view, &_DragDestinationProcs);
1958 char *types[2] = {"application/X-color", NULL};
1959 WMRegisterViewForDraggedTypes(tPtr->view, types);
1961 #endif
1966 tPtr->firstTextBlock = NULL;
1967 tPtr->lastTextBlock = NULL;
1968 tPtr->currentTextBlock = NULL;
1969 tPtr->tpos = 0;
1971 tPtr->gfxItems = WMCreateArrayBag(4);
1973 tPtr->parser = NULL;
1974 tPtr->writer = NULL;
1976 tPtr->sel.x = tPtr->sel.y = 2;
1977 tPtr->sel.w = tPtr->sel.h = 0;
1979 tPtr->clicked.x = tPtr->clicked.y = 2;
1981 tPtr->visible.x = tPtr->visible.y = 2;
1982 tPtr->visible.h = tPtr->view->size.height;
1983 tPtr->visible.w = tPtr->view->size.width - 12;
1985 tPtr->cursor.x = -23;
1987 tPtr->docWidth = 0;
1988 tPtr->docHeight = 0;
1989 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
1990 default_bullet);
1991 tPtr->db = (Pixmap) NULL;
1993 tPtr->margins = wmalloc(sizeof(WMRulerMargins));
1994 tPtr->margins[0].left = tPtr->margins[0].right = tPtr->visible.x;
1995 tPtr->margins[0].body = tPtr->visible.x;
1996 tPtr->margins[0].right = tPtr->visible.w;
1998 tPtr->flags.nmargins = 1;
1999 tPtr->flags.rulerShown = False;
2000 tPtr->flags.monoFont = False;
2001 tPtr->flags.focused = False;
2002 tPtr->flags.editable = True;
2003 tPtr->flags.ownsSelection = False;
2004 tPtr->flags.pointerGrabbed = False;
2005 tPtr->flags.buttonHeld = False;
2006 tPtr->flags.waitingForSelection = False;
2007 tPtr->flags.extendSelection = False;
2008 tPtr->flags.rulerShown = False;
2009 tPtr->flags.frozen = False;
2010 tPtr->flags.cursorShown = True;
2011 tPtr->flags.clickPos = 1;
2012 tPtr->flags.ignoreNewLine = False;
2013 tPtr->flags.laidOut = False;
2014 tPtr->flags.prepend = False;
2015 tPtr->flags.relief = WRFlat;
2016 tPtr->flags.alignment = WALeft;
2018 return tPtr;
2022 void
2023 WMPrependTextStream(WMText *tPtr, char *text)
2025 if (!tPtr)
2026 return;
2028 tPtr->flags.prepend = True;
2029 if (text && tPtr->parser)
2030 (tPtr->parser) (tPtr, (void *) text);
2031 else
2032 insertPlainText(tPtr, text);
2036 void
2037 WMAppendTextStream(WMText *tPtr, char *text)
2039 if (!tPtr)
2040 return;
2042 tPtr->flags.prepend = False;
2043 if (text && tPtr->parser)
2044 (tPtr->parser) (tPtr, (void *) text);
2045 else
2046 insertPlainText(tPtr, text);
2051 char *
2052 WMGetTextStream(WMText *tPtr)
2054 TextBlock *tb = NULL;
2055 char *text = NULL;
2056 unsigned long length = 0, where = 0;
2058 if (!tPtr)
2059 return NULL;
2061 if (!(tb = tPtr->firstTextBlock))
2062 return NULL;
2064 if (tPtr->writer) {
2065 (tPtr->writer) (tPtr, (void *) text);
2066 return text;
2069 /* first, how large a buffer would we want? */
2070 while (tb) {
2071 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank))
2072 length += 1;
2073 if (!tb->graphic)
2074 length += tb->used;
2075 tb = tb->next;
2078 text = wmalloc(length+1); /* +1 for the end of string, let's be nice */
2079 tb = tPtr->firstTextBlock;
2080 while (tb) {
2081 if (!tb->graphic) {
2082 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank))
2083 text[where++] = '\n';
2084 memcpy(&text[where], tb->text, tb->used);
2085 where += tb->used;
2087 tb = tb->next;
2090 text[where] = 0;
2092 return text;
2097 WMBag *
2098 WMGetTextStreamObjects(WMText *tPtr)
2100 if (!tPtr)
2101 return NULL;
2103 return NULL;
2107 char *
2108 WMGetTextSelected(WMText *tPtr)
2110 TextBlock *tb;
2111 char *text = NULL;
2113 if (!tPtr)
2114 return NULL;
2116 //tb = tPtr->firstTextBlock;
2117 tb = tPtr->currentTextBlock;
2118 if (!tb)
2119 return NULL;
2121 text = wmalloc(tb->used+1);
2122 memcpy(text, tb->text, tb->used);
2123 text[tb->used] = 0;
2124 return text;
2127 WMBag *
2128 WMGetTextSelectedObjects(WMText *tPtr)
2130 if (!tPtr)
2131 return NULL;
2133 return NULL;
2136 void *
2137 WMCreateTextBlockWithObject(WMWidget *w, char *description, WMColor *color,
2138 unsigned short first, unsigned short reserved)
2140 TextBlock *tb;
2141 unsigned short length;
2143 if (!w || !description || !color)
2144 return NULL;
2146 tb = wmalloc(sizeof(TextBlock));
2147 if (!tb)
2148 return NULL;
2150 length = strlen(description);
2151 tb->text = (char *)wmalloc(length);
2152 memset(tb->text, 0, length);
2153 memcpy(tb->text, description, length);
2154 tb->used = length;
2155 tb->blank = False;
2156 tb->d.widget = w;
2157 tb->color = WMRetainColor(color);
2158 tb->marginN = 0;
2159 tb->allocated = 0;
2160 tb->first = first;
2161 tb->kanji = False;
2162 tb->graphic = True;
2163 tb->object = True;
2164 tb->underlined = False;
2165 tb->script = 0;
2166 tb->sections = NULL;
2167 tb->nsections = 0;
2168 tb->prior = NULL;
2169 tb->next = NULL;
2171 return tb;
2175 void *
2176 WMCreateTextBlockWithPixmap(WMPixmap *p, char *description, WMColor *color,
2177 unsigned short first, unsigned short reserved)
2179 TextBlock *tb;
2180 unsigned short length;
2182 if (!p || !description || !color)
2183 return NULL;
2185 tb = wmalloc(sizeof(TextBlock));
2186 if (!tb)
2187 return NULL;
2189 length = strlen(description);
2190 tb->text = (char *)wmalloc(length);
2191 memset(tb->text, 0, length);
2192 memcpy(tb->text, description, length);
2193 tb->used = length;
2194 tb->blank = False;
2195 tb->d.pixmap = p;
2196 tb->color = WMRetainColor(color);
2197 tb->marginN = 0;
2198 tb->allocated = 0;
2199 tb->first = first;
2200 tb->kanji = False;
2201 tb->graphic = True;
2202 tb->object = False;
2203 tb->underlined = False;
2204 tb->script = 0;
2205 tb->sections = NULL;
2206 tb->nsections = 0;
2207 tb->prior = NULL;
2208 tb->next = NULL;
2210 return tb;
2213 void *
2214 WMCreateTextBlockWithText(char *text, WMFont *font, WMColor *color,
2215 unsigned short first, unsigned short length)
2217 TextBlock *tb;
2219 if (!font || !color)
2220 return NULL;
2222 tb = wmalloc(sizeof(TextBlock));
2223 if (!tb)
2224 return NULL;
2226 tb->allocated = reqBlockSize(length);
2227 tb->text = (char *)wmalloc(tb->allocated);
2228 memset(tb->text, 0, tb->allocated);
2230 if (length < 1|| !text ) { // || *text == '\n') {
2231 *tb->text = ' ';
2232 tb->used = 1;
2233 tb->blank = True;
2234 } else {
2235 memcpy(tb->text, text, length);
2236 tb->used = length;
2237 tb->blank = False;
2240 tb->d.font = WMRetainFont(font);
2241 tb->color = WMRetainColor(color);
2242 tb->marginN = 0;
2243 tb->first = first;
2244 tb->kanji = False;
2245 tb->graphic = False;
2246 tb->underlined = False;
2247 tb->script = 0;
2248 tb->sections = NULL;
2249 tb->nsections = 0;
2250 tb->prior = NULL;
2251 tb->next = NULL;
2252 return tb;
2255 void
2256 WMSetTextBlockProperties(void *vtb, unsigned int first,
2257 unsigned int kanji, unsigned int underlined, int script,
2258 unsigned int marginN)
2260 TextBlock *tb = (TextBlock *) vtb;
2261 if (!tb)
2262 return;
2264 tb->first = first;
2265 tb->kanji = kanji;
2266 tb->underlined = underlined;
2267 tb->script = script;
2268 tb->marginN = marginN;
2271 void
2272 WMGetTextBlockProperties(void *vtb, unsigned int *first,
2273 unsigned int *kanji, unsigned int *underlined, int *script,
2274 unsigned int *marginN)
2276 TextBlock *tb = (TextBlock *) vtb;
2277 if (!tb)
2278 return;
2280 if (first) *first = tb->first;
2281 if (kanji) *kanji = tb->kanji;
2282 if (underlined) *underlined = tb->underlined;
2283 if (script) *script = tb->script;
2284 if (marginN) *marginN = tb->marginN;
2289 void
2290 WMPrependTextBlock(WMText *tPtr, void *vtb)
2292 TextBlock *tb = (TextBlock *)vtb;
2295 if (!tPtr || !tb)
2296 return;
2298 if (tb->graphic) {
2299 if(tb->object) {
2300 WMWidget *w = tb->d.widget;
2301 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
2302 handleWidgetPress, tb);
2303 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
2304 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
2305 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
2308 WMPutInBag(tPtr->gfxItems, (void *)tb);
2309 tPtr->tpos = 0;
2310 } else tPtr->tpos = tb->used;
2312 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
2313 tb->next = tb->prior = NULL;
2314 tPtr->lastTextBlock = tPtr->firstTextBlock
2315 = tPtr->currentTextBlock = tb;
2316 return;
2319 tb->next = tPtr->currentTextBlock;
2320 tb->prior = tPtr->currentTextBlock->prior;
2321 if (tPtr->currentTextBlock->prior)
2322 tPtr->currentTextBlock->prior->next = tb;
2324 tPtr->currentTextBlock->prior = tb;
2325 if (!tb->prior)
2326 tPtr->firstTextBlock = tb;
2328 tPtr->currentTextBlock = tb;
2332 void
2333 WMAppendTextBlock(WMText *tPtr, void *vtb)
2335 TextBlock *tb = (TextBlock *)vtb;
2337 if (!tPtr || !tb)
2338 return;
2340 if (tb->graphic) {
2341 if(tb->object) {
2342 WMWidget *w = tb->d.widget;
2343 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
2344 handleWidgetPress, tb);
2345 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
2346 (W_VIEW(w))->attribs.cursor =
2347 tPtr->view->screen->defaultCursor;
2348 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
2351 WMPutInBag(tPtr->gfxItems, (void *)tb);
2352 tPtr->tpos = 0;
2353 } else tPtr->tpos = tb->used;
2355 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
2356 tb->next = tb->prior = NULL;
2357 tPtr->lastTextBlock = tPtr->firstTextBlock
2358 = tPtr->currentTextBlock = tb;
2359 return;
2362 tb->next = tPtr->currentTextBlock->next;
2363 tb->prior = tPtr->currentTextBlock;
2364 if (tPtr->currentTextBlock->next)
2365 tPtr->currentTextBlock->next->prior = tb;
2367 tPtr->currentTextBlock->next = tb;
2369 if (!tb->next)
2370 tPtr->lastTextBlock = tb;
2372 tPtr->currentTextBlock = tb;
2375 void *
2376 WMRemoveTextBlock(WMText *tPtr)
2378 TextBlock *tb = NULL;
2380 if (!tPtr || !tPtr->firstTextBlock || !tPtr->lastTextBlock
2381 || !tPtr->currentTextBlock) {
2382 printf("cannot remove non existent TextBlock!\b");
2383 return tb;
2386 tb = tPtr->currentTextBlock;
2387 if (tb->graphic) {
2388 WMRemoveFromBag(tPtr->gfxItems, (void *)tb);
2390 if(tb->object) {
2391 WMDeleteEventHandler(W_VIEW(tb->d.widget), ButtonPressMask,
2392 handleWidgetPress, tb);
2393 WMUnmapWidget(tb->d.widget);
2397 if (tPtr->currentTextBlock == tPtr->firstTextBlock) {
2398 if (tPtr->currentTextBlock->next)
2399 tPtr->currentTextBlock->next->prior = NULL;
2401 tPtr->firstTextBlock = tPtr->currentTextBlock->next;
2402 tPtr->currentTextBlock = tPtr->firstTextBlock;
2404 } else if (tPtr->currentTextBlock == tPtr->lastTextBlock) {
2405 tPtr->currentTextBlock->prior->next = NULL;
2406 tPtr->lastTextBlock = tPtr->currentTextBlock->prior;
2407 tPtr->currentTextBlock = tPtr->lastTextBlock;
2408 } else {
2409 tPtr->currentTextBlock->prior->next = tPtr->currentTextBlock->next;
2410 tPtr->currentTextBlock->next->prior = tPtr->currentTextBlock->prior;
2411 tPtr->currentTextBlock = tPtr->currentTextBlock->next;
2414 return (void *)tb;
2417 void
2418 WMDestroyTextBlock(WMText *tPtr, void *vtb)
2420 TextBlock *tb = (TextBlock *)vtb;
2421 if (!tPtr || !tb)
2422 return;
2424 if (tb->graphic) {
2425 if(tb->object) {
2426 /* naturally, there's a danger to destroying
2427 widgets whose action brings us here:
2428 ie. press a button to destroy it... need to
2429 find a safer way. till then... this stays commented out */
2430 //WMDestroyWidget(tb->d.widget);
2431 //wfree(tb->d.widget);
2432 tb->d.widget = NULL;
2433 } else {
2434 WMReleasePixmap(tb->d.pixmap);
2435 tb->d.pixmap = NULL;
2437 } else {
2438 WMReleaseFont(tb->d.font);
2441 WMReleaseColor(tb->color);
2442 if (tb->sections && tb->nsections > 0)
2443 wfree(tb->sections);
2444 wfree(tb->text);
2445 wfree(tb);
2446 tb = NULL;
2450 void
2451 WMRefreshText(WMText *tPtr, int vpos, int hpos)
2453 if (!tPtr || vpos<0 || hpos<0)
2454 return;
2456 if (tPtr->flags.frozen)
2457 return;
2460 if (tPtr->vpos != vpos) {
2461 if (vpos < 0 || tPtr->docHeight < tPtr->visible.h) {
2462 tPtr->vpos = 0;
2463 } else if(tPtr->docHeight - vpos > tPtr->visible.h - tPtr->visible.y) {
2464 tPtr->vpos = vpos;
2465 } else {
2466 tPtr->vpos = tPtr->docHeight - tPtr->visible.h;
2470 tPtr->flags.laidOut = False;
2471 layOutDocument(tPtr);
2472 updateScrollers(tPtr);
2473 paintText(tPtr);
2477 void
2478 WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
2480 if (!tPtr)
2481 return;
2483 if (color)
2484 tPtr->fgGC = WMColorGC(color);
2485 else
2486 tPtr->fgGC = WMColorGC(WMBlackColor(tPtr->view->screen));
2488 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2491 void
2492 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
2494 if (!tPtr)
2495 return;
2497 if (color) {
2498 tPtr->bgGC = WMColorGC(color);
2499 W_SetViewBackgroundColor(tPtr->view, color);
2500 } else {
2501 tPtr->bgGC = WMColorGC(WMWhiteColor(tPtr->view->screen));
2502 W_SetViewBackgroundColor(tPtr->view,
2503 WMWhiteColor(tPtr->view->screen));
2506 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2509 void
2510 WMSetTextRelief(WMText *tPtr, WMReliefType relief)
2512 if (!tPtr)
2513 return;
2514 tPtr->flags.relief = relief;
2515 paintText(tPtr);
2518 void
2519 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
2521 if (!tPtr)
2522 return;
2524 if (shouldhave && !tPtr->hS) {
2525 tPtr->hS = WMCreateScroller(tPtr);
2526 (W_VIEW(tPtr->hS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2527 (W_VIEW(tPtr->hS))->attribFlags |= CWOverrideRedirect | CWCursor;
2528 WMSetScrollerArrowsPosition(tPtr->hS, WSAMaxEnd);
2529 WMSetScrollerAction(tPtr->hS, scrollersCallBack, tPtr);
2530 } else if (!shouldhave && tPtr->hS) {
2531 WMUnmapWidget(tPtr->hS);
2532 WMDestroyWidget(tPtr->hS);
2533 tPtr->hS = NULL;
2536 tPtr->hpos = 0;
2537 tPtr->prevHpos = 0;
2538 textDidResize(tPtr->view->delegate, tPtr->view);
2542 void
2543 WMSetTextHasRuler(WMText *tPtr, Bool shouldhave)
2545 if (!tPtr)
2546 return;
2548 if(shouldhave && !tPtr->ruler) {
2549 tPtr->ruler = WMCreateRuler(tPtr);
2550 (W_VIEW(tPtr->ruler))->attribs.cursor =
2551 tPtr->view->screen->defaultCursor;
2552 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
2553 WMSetRulerReleaseAction(tPtr->ruler, rulerReleaseCallBack, tPtr);
2554 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
2555 } else if(!shouldhave && tPtr->ruler) {
2556 WMShowTextRuler(tPtr, False);
2557 WMDestroyWidget(tPtr->ruler);
2558 tPtr->ruler = NULL;
2560 textDidResize(tPtr->view->delegate, tPtr->view);
2563 void
2564 WMShowTextRuler(WMText *tPtr, Bool show)
2566 if(!tPtr)
2567 return;
2568 if(!tPtr->ruler)
2569 return;
2571 if(tPtr->flags.monoFont)
2572 show = False;
2574 tPtr->flags.rulerShown = show;
2575 if(show) {
2576 WMMapWidget(tPtr->ruler);
2577 } else {
2578 WMUnmapWidget(tPtr->ruler);
2581 textDidResize(tPtr->view->delegate, tPtr->view);
2584 Bool
2585 WMGetTextRulerShown(WMText *tPtr)
2587 if(!tPtr)
2588 return 0;
2590 if(!tPtr->ruler)
2591 return 0;
2593 return tPtr->flags.rulerShown;
2597 void
2598 WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
2600 if (!tPtr)
2601 return;
2603 if (shouldhave && !tPtr->vS) {
2604 tPtr->vS = WMCreateScroller(tPtr);
2605 (W_VIEW(tPtr->vS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2606 (W_VIEW(tPtr->vS))->attribFlags |= CWOverrideRedirect | CWCursor;
2607 WMSetScrollerArrowsPosition(tPtr->vS, WSAMaxEnd);
2608 WMSetScrollerAction(tPtr->vS, scrollersCallBack, tPtr);
2609 } else if (!shouldhave && tPtr->vS) {
2610 WMUnmapWidget(tPtr->vS);
2611 WMDestroyWidget(tPtr->vS);
2612 tPtr->vS = NULL;
2615 tPtr->vpos = 0;
2616 tPtr->prevVpos = 0;
2617 textDidResize(tPtr->view->delegate, tPtr->view);
2622 Bool
2623 WMScrollText(WMText *tPtr, int amount)
2625 Bool scroll=False;
2626 if (!tPtr)
2627 return False;
2628 if (amount == 0 || !tPtr->view->flags.realized)
2629 return False;
2631 if (amount < 0) {
2632 if (tPtr->vpos > 0) {
2633 if (tPtr->vpos > amount) tPtr->vpos += amount;
2634 else tPtr->vpos=0;
2635 scroll=True;
2636 } } else {
2637 int limit = tPtr->docHeight - tPtr->visible.h;
2638 if (tPtr->vpos < limit) {
2639 if (tPtr->vpos < limit-amount) tPtr->vpos += amount;
2640 else tPtr->vpos = limit;
2641 scroll = True;
2642 } }
2644 if (scroll && tPtr->vpos != tPtr->prevVpos) {
2645 updateScrollers(tPtr);
2646 paintText(tPtr);
2648 tPtr->prevVpos = tPtr->vpos;
2649 return scroll;
2652 Bool
2653 WMPageText(WMText *tPtr, Bool direction)
2655 if (!tPtr) return False;
2656 if (!tPtr->view->flags.realized) return False;
2658 return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
2661 void
2662 WMSetTextEditable(WMText *tPtr, Bool editable)
2664 if (!tPtr)
2665 return;
2666 tPtr->flags.editable = editable;
2669 int
2670 WMGetTextEditable(WMText *tPtr)
2672 if (!tPtr)
2673 return 0;
2674 return tPtr->flags.editable;
2677 void
2678 WMSetTextIgnoresNewline(WMText *tPtr, Bool ignore)
2680 if (!tPtr)
2681 return;
2682 tPtr->flags.ignoreNewLine = ignore;
2685 Bool
2686 WMGetTextIgnoresNewline(WMText *tPtr)
2688 if (!tPtr)
2689 return True;
2690 return tPtr->flags.ignoreNewLine;
2693 void
2694 WMSetTextUsesMonoFont(WMText *tPtr, Bool mono)
2696 if (!tPtr)
2697 return;
2698 if (mono && tPtr->flags.rulerShown)
2699 WMShowTextRuler(tPtr, False);
2701 tPtr->flags.monoFont = mono;
2702 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2705 Bool
2706 WMGetTextUsesMonoFont(WMText *tPtr)
2708 if (!tPtr)
2709 return True;
2710 return tPtr->flags.monoFont;
2714 void
2715 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
2717 if (!tPtr)
2718 return;
2720 WMReleaseFont(tPtr->dFont);
2721 if (font)
2722 tPtr->dFont = font;
2723 else
2724 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2727 WMFont *
2728 WMGetTextDefaultFont(WMText *tPtr)
2730 if (!tPtr)
2731 return NULL;
2732 else
2733 return tPtr->dFont;
2736 void
2737 WMSetTextAlignment(WMText *tPtr, WMAlignment alignment)
2739 if (!tPtr)
2740 return;
2741 tPtr->flags.alignment = alignment;
2742 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2745 void
2746 WMSetTextParser(WMText *tPtr, WMAction *parser)
2748 if (!tPtr)
2749 return;
2750 tPtr->parser = parser;
2753 void
2754 WMSetTextWriter(WMText *tPtr, WMAction *writer)
2756 if (!tPtr)
2757 return;
2758 tPtr->writer = writer;
2761 int
2762 WMGetTextInsertType(WMText *tPtr)
2764 if (!tPtr)
2765 return 0;
2766 return tPtr->flags.prepend;
2770 void
2771 WMSetTextSelectionColor(WMText *tPtr, WMColor *color)
2773 TextBlock *tb;
2774 if (!tPtr || !color)
2775 return;
2777 tb = tPtr->firstTextBlock;
2778 if (!tb || !tPtr->flags.ownsSelection)
2779 return;
2781 while (tb) {
2782 if(tb->color)
2783 WMReleaseColor(tb->color);
2784 tb->color = WMRetainColor(color);
2785 tb = tb->next;
2787 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2790 void
2791 WMSetTextSelectionFont(WMText *tPtr, WMFont *font)
2793 TextBlock *tb;
2794 if (!tPtr || !font)
2795 return;
2797 tb = tPtr->firstTextBlock;
2798 if (!tb || !tPtr->flags.ownsSelection)
2799 return;
2801 while (tb) {
2802 if (!tb->graphic)
2803 tb->d.font = WMRetainFont(font);
2804 tb = tb->next;
2806 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2809 void
2810 WMFreezeText(WMText *tPtr)
2812 if (!tPtr)
2813 return;
2815 tPtr->flags.frozen = True;
2818 void
2819 WMThawText(WMText *tPtr)
2821 if (!tPtr)
2822 return;
2824 tPtr->flags.frozen = False;
2828 Bool
2829 WMFindInTextStream(WMText *tPtr, char *needle)
2831 char *haystack = NULL;
2832 Bool result;
2834 if (!tPtr || !needle)
2835 return False;
2837 if ( !(haystack = WMGetTextStream(tPtr)))
2838 return False;
2840 result = (Bool) strstr(haystack, needle);
2841 wfree(haystack);
2842 return result;