removed compile warnings in wmtext :-)
[wmaker-crm.git] / WINGs / wtext.c
blob13485cd24d88019d9a5b01329e09977928a4508b
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...
40 /* a Section is a section of a TextBlock that describes what parts
41 of a TextBlock has been laid out on which "line"...
42 o this greatly aids redraw, scroll and selection.
43 o this is created during layoutLine, but may be later modified.
44 o there may be many Sections per TextBlock, hence the array */
45 typedef struct {
46 int x, y; /* where to draw it from */
47 int w, h; /* it's width and height (to aid selection) */
48 int _y;
49 unsigned short begin, end; /* what part of the text block */
50 } Section;
53 /* a TextBlock is a doubly-linked list of TextBlocks containing:
54 o text for the block, color and font
55 o or a pointer to the widget and the (text) description for its graphic
56 o but NOT both */
58 typedef struct _TextBlock {
59 struct _TextBlock *next; /* next text block in linked list */
60 struct _TextBlock *prior; /* prior text block in linked list */
62 char *text; /* pointer to 8- or 16-bit text */
63 /* or to the object's description */
64 union {
65 WMFont *font; /* the font */
66 WMWidget *widget; /* the embedded widget */
67 } d; /* description */
69 WMColor *color; /* the color */
70 Section *sections; /* the region for layouts (a growable array) */
71 /* an _array_! of size _nsections_ */
74 unsigned short used; /* number of chars in this block */
75 unsigned short allocated; /* size of allocation (in chars) */
77 unsigned int first:1; /* first TextBlock in paragraph */
78 unsigned int blank:1; /* ie. blank paragraph */
79 unsigned int kanji:1; /* is of 16-bit characters or not */
80 unsigned int graphic:1; /* embedded object or text: text=0 */
81 unsigned int underlined:1; /* underlined or not */
82 unsigned int nsections:8; /* over how many "lines" a TexBlock wraps */
83 int script:8; /* script in points: negative for subscript */
84 unsigned int marginN:10; /* which of the margins in WMText to use */
85 unsigned int RESERVED:1;
86 } TextBlock;
89 /* somehow visible.h beats the hell outta visible.size.height :-) */
90 typedef struct {
91 unsigned int y;
92 unsigned int x;
93 unsigned int h;
94 unsigned int w;
95 } myRect;
98 typedef struct W_Text {
99 W_Class widgetClass; /* the class number of this widget */
100 W_View *view; /* the view referring to this instance */
102 WMRuler *ruler; /* the ruler subwiget to manipulate paragraphs */
104 WMScroller *vS; /* the vertical scroller */
105 int vpos; /* the current vertical position */
106 int prevVpos; /* the previous vertical position */
108 WMScroller *hS; /* the horizontal scroller */
109 int hpos; /* the current horizontal position */
110 int prevHpos; /* the previous horizontal position */
111 /* in short: tPtr->hS?nowrap:wrap */
113 WMFont *dFont; /* the default font */
114 WMColor *dColor; /* the default color */
115 WMPixmap *dBulletPix; /* the default pixmap for bullets */
117 GC bgGC; /* the background GC to draw with */
118 GC fgGC; /* the foreground GC to draw with */
119 Pixmap db; /* the buffer on which to draw */
121 WMRulerMargins *margins;/* a (growable) array of margins to be used */
122 /* by the various TextBlocks */
124 myRect visible; /* the actual rectangle that can be drawn into */
125 myRect cursor; /* the position and (height) of cursor */
126 myRect sel; /* the selection rectangle */
127 int docWidth; /* the width of the entire document */
128 int docHeight; /* the height of the entire document */
131 TextBlock *firstTextBlock;
132 TextBlock *lastTextBlock;
133 TextBlock *currentTextBlock;
136 WMBag *gfxItems; /* a nice bag containing graphic items */
138 #if DO_BLINK
139 WMHandlerID timerID; /* for nice twinky-winky */
140 #endif
141 WMPoint clicked; /* where in the _document_ was clicked */
142 unsigned short tpos; /* the character position in the currentTextBlock */
143 unsigned short RESERVED;/* space taker upper... */
146 WMAction *parser;
147 WMAction *writer;
149 struct {
150 unsigned int monoFont:1; /* whether to ignore formats */
151 unsigned int focused:1; /* whether this instance has input focus */
152 unsigned int editable:1; /* "silly user, you can't edit me" */
153 unsigned int ownsSelection:1; /* "I ownz the current selection!" */
154 unsigned int pointerGrabbed:1;/* "heh, gib me pointer" */
155 unsigned int buttonHeld:1; /* the user is holding down the button */
156 unsigned int waitingForSelection:1; /* dum dee dumm... */
157 unsigned int extendSelection:1; /* shift-drag to select more regions */
159 unsigned int rulerShown:1; /* whether the ruler is shown or not */
160 unsigned int frozen:1; /* whether screen updates are to be made */
161 unsigned int cursorShown:1; /* whether to show the cursor */
162 unsigned int clickPos:1; /* clicked before=0 or after=1 a graphic: */
163 /* within counts as after too */
165 unsigned int ignoreNewLine:1;/* turn it into a ' ' when typed */
166 unsigned int laidOut:1; /* have the TextBlocks all been laid out */
167 unsigned int prepend:1; /* prepend=1, append=0 (for parsers) */
168 WMAlignment alignment:2; /* the alignment for text */
169 WMReliefType relief:3; /* the relief to display with */
170 unsigned int RESERVED:4;
171 unsigned int nmargins:10; /* the number of margin arrays */
172 } flags;
173 } Text;
175 static char *default_bullet[] = {
176 "6 6 4 1",
177 " c None s None", ". c black",
178 "X c white", "o c #808080",
179 " ... ",
180 ".XX.. ",
181 ".XX..o",
182 ".....o",
183 " ...oo",
184 " ooo "};
187 static void
188 paintText(Text *tPtr)
190 TextBlock *tb = tPtr->firstTextBlock;
191 WMFont *font;
192 GC gc, greyGC;
193 char *text;
194 int len, y, c, s, done=False;
195 int prev_y=-23;
196 WMScreen *scr = tPtr->view->screen;
197 Display *dpy = tPtr->view->screen->display;
198 Window win = tPtr->view->window;
201 if (!tPtr->view->flags.realized || !tPtr->db || tPtr->flags.frozen)
202 return;
204 XFillRectangle(dpy, tPtr->db, tPtr->bgGC,
205 0, 0, WMWidgetWidth(tPtr), WMWidgetWidth(tPtr));
206 //tPtr->visible.w, tPtr->visible.h);
208 tb = tPtr->firstTextBlock;
209 if (!tb)
210 goto _copy_area;
213 if (tPtr->flags.ownsSelection) {
214 greyGC = WMColorGC(WMGrayColor(scr));
215 //XFillRectangle(dpy, tPtr->db, greyGC,
216 // tPtr->sel.x, tPtr->sel.y-tPtr->vpos, tPtr->sel.w, tPtr->sel.h);
217 // XDrawRectangle(dpy, tPtr->db, tPtr->fgGC,
218 // tPtr->sel.x, tPtr->sel.y-tPtr->vpos, tPtr->sel.w, tPtr->sel.h);
221 done = False;
222 while (!done && tb) {
224 if (tb->graphic) {
225 tb = tb->next;
226 continue;
229 for(s=0; s<tb->nsections && !done; s++) {
232 if (tb->sections[s]._y > tPtr->vpos + tPtr->visible.h) {
233 done = True;
234 break;
237 if ( tb->sections[s].y + tb->sections[s].h < tPtr->vpos)
238 continue;
240 if (tPtr->flags.monoFont) {
241 font = tPtr->dFont;
242 gc = tPtr->fgGC;
243 } else {
244 font = tb->d.font;
245 gc = WMColorGC(tb->color);
248 if (tPtr->flags.ownsSelection) {
250 if (prev_y != tb->sections[s]._y
251 && (tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
252 && (tb->sections[s]._y + tb->sections[s].h
253 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)) {
254 XFillRectangle(dpy, tPtr->db, greyGC,
255 tPtr->visible.x,
256 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
257 tPtr->visible.w, tb->sections[s].h);
259 } else if ( prev_y != tb->sections[s]._y
260 && (tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
261 && (tb->sections[s]._y + tb->sections[s].h
262 >= tPtr->visible.y + tPtr->sel.y)
263 && (tPtr->sel.x >= tb->sections[s].x)
264 && (tPtr->visible.y + tPtr->sel.y + tPtr->sel.h
265 >= tb->sections[s]._y + tb->sections[s].h)) {
266 XFillRectangle(dpy, tPtr->db, greyGC,
267 tPtr->clicked.x,
268 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
269 tPtr->visible.w - tPtr->sel.x, tb->sections[s].h);
271 } else if (prev_y != tb->sections[s]._y
272 && (tb->sections[s]._y <= tPtr->sel.y + tPtr->sel.h)
273 && (tb->sections[s]._y >= tPtr->sel.y)) {
274 XFillRectangle(dpy, tPtr->db, greyGC,
275 tPtr->visible.x,
276 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
277 tPtr->sel.x + tPtr->sel.w -tPtr->visible.x,
278 tb->sections[s].h);
280 } else if ( prev_y != tb->sections[s]._y
281 && (tb->sections[s]._y <= tPtr->sel.y)
282 && (tb->sections[s]._y + tb->sections[s].h
283 >= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
284 XFillRectangle(dpy, tPtr->db, greyGC,
285 tPtr->sel.x,
286 tPtr->visible.y + tb->sections[s]._y - tPtr->vpos,
287 tPtr->sel.w,tb->sections[s].h);
292 prev_y = tb->sections[s]._y;
294 len = tb->sections[s].end - tb->sections[s].begin;
295 text = &(tb->text[tb->sections[s].begin]);
296 y = tb->sections[s].y - tPtr->vpos;
297 WMDrawString(scr, tPtr->db, gc, font,
298 tb->sections[s].x, y, text, len);
300 if (tb->underlined) {
301 XDrawLine(dpy, tPtr->db, gc,
302 tb->sections[s].x, y + font->y + 1,
303 tb->sections[s].x + tb->sections[s].w, y + font->y + 1);
308 tb = (!done? tb->next : NULL);
312 c = WMGetBagItemCount(tPtr->gfxItems);
313 if (c > 0 && !tPtr->flags.monoFont) {
314 int j;
315 WMWidget *wdt;
316 for(j=0; j<c; j++) {
317 tb = (TextBlock *) WMGetFromBag(tPtr->gfxItems, j);
318 wdt = tb->d.widget;
319 if (tb->sections[0]._y + tb->sections[0].h <= tPtr->vpos
320 || tb->sections[0]._y >= tPtr->vpos + tPtr->visible.h ) {
322 if ((W_VIEW(wdt))->flags.mapped) {
323 WMUnmapWidget(wdt);
325 } else {
326 if (!(W_VIEW(wdt))->flags.mapped) {
327 WMMapWidget(wdt);
328 //WMLowerWidget(wdt);
331 if (tPtr->flags.ownsSelection && 0
332 //&& (tb->sections[s]._y >= tPtr->sel.y)
333 //&& (tb->sections[s]._y + tb->sections[s].h
334 ){ // <= tPtr->sel.y + tPtr->sel.h)) {
335 XFillRectangle(dpy, tPtr->db, greyGC,
336 tb->sections[0].x, tb->sections[0].y - tPtr->vpos,
337 tb->sections[0].w, tb->sections[0].h);
340 WMMoveWidget(wdt, 3 + tb->sections[0].x + tPtr->visible.x,
341 tb->sections[0].y - tPtr->vpos);
343 if (tb->underlined) {
344 XDrawLine(dpy, tPtr->db, WMColorGC(tb->color),
345 tb->sections[0].x,
346 tb->sections[0].y + WMWidgetHeight(wdt) + 1,
347 tb->sections[0].x + tb->sections[0].w,
348 tb->sections[0].y + WMWidgetHeight(wdt) + 1);
349 } } } }
352 _copy_area:
354 if (tPtr->flags.editable && tPtr->flags.cursorShown
355 && tPtr->cursor.x != -23 && tPtr->flags.focused) {
356 int y = tPtr->cursor.y - tPtr->vpos;
357 XDrawLine(dpy, tPtr->db, tPtr->fgGC,
358 tPtr->cursor.x, y,
359 tPtr->cursor.x, y + tPtr->cursor.h);
361 //printf("%d %d %d\n", tPtr->cursor.x, tPtr->cursor.y, tPtr->cursor.h);
364 XCopyArea(dpy, tPtr->db, win, tPtr->bgGC,
365 0, 0,
366 tPtr->visible.w, tPtr->visible.h,
367 tPtr->visible.x, tPtr->visible.y);
369 W_DrawRelief(scr, win, 0, 0,
370 tPtr->view->size.width, tPtr->view->size.height,
371 tPtr->flags.relief);
373 if (tPtr->ruler && tPtr->flags.rulerShown)
374 XDrawLine(dpy, win,
375 tPtr->fgGC, 2, 42,
376 tPtr->view->size.width-4, 42);
381 #if DO_BLINK
383 #define CURSOR_BLINK_ON_DELAY 600
384 #define CURSOR_BLINK_OFF_DELAY 400
386 static void
387 blinkCursor(void *data)
389 Text *tPtr = (Text*)data;
391 if (tPtr->flags.cursorShown) {
392 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY,
393 blinkCursor, data);
394 } else {
395 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
396 blinkCursor, data);
398 paintText(tPtr);
399 tPtr->flags.cursorShown = !tPtr->flags.cursorShown;
401 #endif
403 static TextBlock *
404 getFirstNonGraphicBlockFor(TextBlock *tb, short dir)
406 if (!tb)
407 return NULL;
408 while (tb) {
409 if (!tb->graphic)
410 break;
411 tb = (dir? tb->next : tb->prior);
414 return tb;
418 static void
419 cursorToTextPosition(Text *tPtr, int x, int y)
421 TextBlock *tb = NULL;
422 int done=False, s, pos, len, _w, _y, dir=1; /* 1 == "down" */
423 char *text;
425 y += (tPtr->vpos - tPtr->visible.y);
426 if (y<0)
427 y = 0;
429 x -= (tPtr->visible.x - 2);
430 if (x<0)
431 x=0;
433 /* clicked is relative to document, not window... */
434 tPtr->clicked.x = x;
435 tPtr->clicked.y = y;
437 if (! (tb = tPtr->currentTextBlock)) {
438 if (! (tb = tPtr->firstTextBlock)) {
439 tPtr->tpos = 0;
440 tPtr->cursor.h = tPtr->dFont->height;
441 tPtr->cursor.y = 2;
442 tPtr->cursor.x = 2;
443 return;
447 /* first, which direction? Most likely, newly clicked
448 position will be close to previous */
449 dir = !(y < tb->sections[0].y);
451 if (tPtr->flags.monoFont && tb->graphic) {
452 tb = getFirstNonGraphicBlockFor(tb, 1);
453 if (!tb) {
454 tPtr->currentTextBlock =
455 (dir? tPtr->lastTextBlock : tPtr->firstTextBlock);
456 tPtr->tpos = 0;
457 return;
461 s = (dir? 0 : tb->nsections-1);
462 if ( y >= tb->sections[s]._y
463 && y <= tb->sections[s]._y + tb->sections[s].h) {
464 printf("got it\n");
465 goto _doneV;
468 /* get the first section of the TextBlock that lies about
469 the vertical click point */
470 done = False;
471 while (!done && tb) {
473 if (tPtr->flags.monoFont && tb->graphic) {
474 if(tb->next)
475 tb = tb->next;
476 continue;
479 s = (dir? 0 : tb->nsections-1);
480 while (!done && (dir? (s<tb->nsections) : (s>=0) )) {
482 if ( y >= tb->sections[s]._y
483 && y <= tb->sections[s]._y + tb->sections[s].h) {
484 done = True;
485 } else {
486 dir? s++ : s--;
490 if (!done) {
491 if ( (dir? tb->next : tb->prior)) {
492 tb = (dir ? tb->next : tb->prior);
493 } else {
494 pos = tb->used;
495 break; //goto _doneH;
501 if (s<0 || s>=tb->nsections) {
502 s = (dir? tb->nsections-1 : 0);
505 _doneV:
506 /* we have the line, which TextBlock on that line is it? */
507 pos = 0;
508 if (tPtr->flags.monoFont && tb->graphic)
509 tb = getFirstNonGraphicBlockFor(tb, dir);
510 if (tb) {
511 if ((dir? tb->sections[s].x >= x : tb->sections[s].x < x))
512 ; //goto _doneH;
513 _y = tb->sections[s]._y;
516 while (tb) {
518 if (tPtr->flags.monoFont && tb->graphic) {
519 tb = (dir ? tb->next : tb->prior);
520 continue;
523 if (dir) {
524 if (tb->graphic) {
525 _w = WMWidgetWidth(tb->d.widget);
526 } else {
527 text = &(tb->text[tb->sections[s].begin]);
528 len = tb->sections[s].end - tb->sections[s].begin;
529 _w = WMWidthOfString(tb->d.font, text, len);
530 if (tb->sections[s].x + _w >= x)
531 break;
534 } else {
535 if (tb->sections[s].x <= x)
536 break;
539 #if 0
540 if ((dir? tb->next : tb->prior)) {
541 TextBlock *nxt = (dir? tb->next : tb->prior);
542 if (tPtr->flags.monoFont && nxt->graphic) {
543 nxt = getFirstNonGraphicBlockFor(nxt, dir);
544 if (!nxt) {
545 pos = 0;
546 goto _doneH;
549 if (_y != nxt->sections[s]._y) {
550 /* this must be the last/first on this line. stop */
551 pos = (dir? tb->used : 0);
552 goto _doneH;
555 #endif
557 if ( (dir? tb->next : tb->prior)) {
558 tb = (dir ? tb->next : tb->prior);
559 } else {
560 done = True;
561 break;
564 if (tb)
565 s = (dir? 0 : tb->nsections-1);
568 /* we have said TextBlock, now where within it? */
569 if (tb && !tb->graphic) {
570 WMFont *f = tb->d.font;
571 len = tb->sections[s].end - tb->sections[s].begin;
572 text = &(tb->text[tb->sections[s].begin]);
574 _w = x - tb->sections[s].x;
575 pos = 0;
577 while (pos<len && WMWidthOfString(f, text, pos+1) < _w)
578 pos++;
580 tPtr->cursor.x = tb->sections[s].x +
581 (pos? WMWidthOfString(f, text, pos) : 0);
582 pos += tb->sections[s].begin;
583 tPtr->tpos = (pos<tb->used)? pos : tb->used;
586 tPtr->currentTextBlock = tb;
587 tPtr->cursor.h = tb->sections[s].h;
588 tPtr->cursor.y = tb->sections[s]._y;
591 if (!tb)
592 printf("will hang :-)\n");
596 static void
597 updateScrollers(Text *tPtr)
600 if (tPtr->flags.frozen)
601 return;
603 if (tPtr->vS) {
604 if (tPtr->docHeight < tPtr->visible.h) {
605 WMSetScrollerParameters(tPtr->vS, 0, 1);
606 tPtr->vpos = 0;
607 } else {
608 float vmax = (float)(tPtr->docHeight);
609 WMSetScrollerParameters(tPtr->vS,
610 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
611 (float)tPtr->visible.h/vmax);
613 } else tPtr->vpos = 0;
615 if (tPtr->hS)
619 static void
620 scrollersCallBack(WMWidget *w, void *self)
622 Text *tPtr = (Text *)self;
623 Bool scroll = False;
624 Bool dimple = False;
625 int which;
627 if (!tPtr->view->flags.realized || tPtr->flags.frozen)
628 return;
630 if (w == tPtr->vS) {
631 float vmax;
632 int height;
633 vmax = (float)(tPtr->docHeight);
634 height = tPtr->visible.h;
636 which = WMGetScrollerHitPart(tPtr->vS);
637 switch(which) {
638 case WSDecrementLine:
639 if (tPtr->vpos > 0) {
640 if (tPtr->vpos>16) tPtr->vpos-=16;
641 else tPtr->vpos=0;
642 scroll=True;
643 }break;
644 case WSIncrementLine: {
645 int limit = tPtr->docHeight - height;
646 if (tPtr->vpos < limit) {
647 if (tPtr->vpos<limit-16) tPtr->vpos+=16;
648 else tPtr->vpos=limit;
649 scroll = True;
650 }}break;
651 case WSDecrementPage:
652 tPtr->vpos -= height;
654 if (tPtr->vpos < 0)
655 tPtr->vpos = 0;
656 dimple = True;
657 scroll = True;
658 printf("dimple needs to jump to mouse location ;-/\n");
659 break;
660 case WSIncrementPage:
661 tPtr->vpos += height;
662 if (tPtr->vpos > (tPtr->docHeight - height))
663 tPtr->vpos = tPtr->docHeight - height;
664 dimple = True;
665 scroll = True;
666 printf("dimple needs to jump to mouse location ;-/\n");
667 break;
670 case WSKnob:
671 tPtr->vpos = WMGetScrollerValue(tPtr->vS)
672 * (float)(tPtr->docHeight - height);
673 scroll = True;
674 break;
676 case WSKnobSlot:
677 case WSNoPart:
678 printf("WSNoPart, WSKnobSlot\n");
679 #if 0
680 float vmax = (float)(tPtr->docHeight);
681 ((float)tPtr->vpos)/(vmax - (float)tPtr->visible.h),
682 (float)tPtr->visible.h/vmax;
683 dimple =where mouse is.
684 #endif
685 break;
687 scroll = (tPtr->vpos != tPtr->prevVpos);
688 tPtr->prevVpos = tPtr->vpos;
691 if (w == tPtr->hS)
694 if (scroll) {
696 if (0&&dimple) {
697 if (tPtr->rulerShown)
698 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
699 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
700 else
701 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
702 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
705 if (which == WSDecrementLine || which == WSIncrementLine)
706 updateScrollers(tPtr);
707 paintText(tPtr);
713 typedef struct {
714 TextBlock *tb;
715 unsigned short begin, end; /* what part of the text block */
717 } myLineItems;
720 static int
721 layOutLine(Text *tPtr, myLineItems *items, int nitems, int x, int y,
722 int pwidth)
724 int i, j=0, lw = 0, line_height=0, max_d=0, len, n;
725 WMFont *font;
726 char *text;
727 TextBlock *tb;
728 Bool gfx=0;
729 TextBlock *tbsame=NULL;
731 for(i=0; i<nitems; i++) {
732 tb = items[i].tb;
734 if (tb->graphic) {
735 if (!tPtr->flags.monoFont) {
736 WMWidget *wdt = tb->d.widget;
737 line_height = WMAX(line_height, WMWidgetHeight(wdt));
738 if (tPtr->flags.alignment != WALeft)
739 lw += WMWidgetWidth(wdt);
740 gfx = True;
743 } else {
744 font = (tPtr->flags.monoFont)?tPtr->dFont : tb->d.font;
745 max_d = WMAX(max_d, font->height-font->y);
746 line_height = WMAX(line_height, font->height);
747 text = &(tb->text[items[i].begin]);
748 len = items[i].end - items[i].begin;
749 if (tPtr->flags.alignment != WALeft)
750 lw += WMWidthOfString(font, text, len);
754 if (tPtr->flags.alignment == WARight) {
755 j = pwidth - lw;
756 } else if (tPtr->flags.alignment == WACenter) {
757 j = (int) ((float)(pwidth - lw))/2.0;
759 if (gfx)
760 y+=10;
762 for(i=0; i<nitems; i++) {
763 tb = items[i].tb;
765 if (tbsame == tb) { /*extend it, since it's on same line */
766 tb->sections[tb->nsections-1].end = items[i].end;
767 n = tb->nsections-1;
768 } else {
769 tb->sections = wrealloc(tb->sections,
770 (++tb->nsections)*sizeof(Section));
771 n = tb->nsections-1;
772 tb->sections[n]._y = y;
773 tb->sections[n].x = x+j;
774 tb->sections[n].h = line_height;
775 tb->sections[n].begin = items[i].begin;
776 tb->sections[n].end = items[i].end;
778 if (tb->graphic) {
779 if (!tPtr->flags.monoFont) {
780 WMWidget *wdt = tb->d.widget;
781 tb->sections[n].y = 1 + max_d +
782 y + line_height - WMWidgetHeight(wdt);
783 tb->sections[n].w = WMWidgetWidth(wdt);
784 x += tb->sections[n].w;
786 } else {
787 font = (tPtr->flags.monoFont)? tPtr->dFont : tb->d.font;
788 len = items[i].end - items[i].begin;
789 text = &(tb->text[items[i].begin]);
791 tb->sections[n].y = y+line_height-font->y;
792 tb->sections[n].w =
793 WMWidthOfString(font,
794 &(tb->text[tb->sections[n].begin]),
795 tb->sections[n].end - tb->sections[n].begin);
797 x += WMWidthOfString(font, text, len);
800 tbsame = tb;
803 return line_height+(gfx?10:0);
808 static void
809 output(char *ptr, int len)
811 char s[len+1];
812 memcpy(s, ptr, len);
813 s[len] = 0;
814 //printf(" s is [%s] (%d)\n", s, strlen(s));
815 printf("[%s]\n", s);
819 /* tb->text doesn't necessarily end in '\0' hmph! (strchr) */
820 static inline char *
821 mystrchr(char *s, char needle, unsigned short len)
823 char *haystack = s;
825 if (!haystack || len < 1)
826 return NULL;
828 while ( (int) (haystack - s) < len ) {
829 if (*haystack == needle)
830 return haystack;
831 haystack++;
833 return NULL;
836 #define MAX_TB_PER_LINE 64
838 static void
839 layOutDocument(Text *tPtr)
841 TextBlock *tb;
842 myLineItems items[MAX_TB_PER_LINE];
843 WMFont *font;
844 Bool lhc = !tPtr->flags.laidOut; /* line height changed? */
845 int prev_y;
847 int nitems=0, x=0, y=0, lw = 0, width=0;
848 int pwidth = tPtr->visible.w - tPtr->visible.x;
850 char *start=NULL, *mark=NULL;
851 int begin, end;
853 if (tPtr->flags.frozen)
854 return;
856 if (!(tb = tPtr->firstTextBlock))
857 return;
859 if (0&&tPtr->flags.laidOut) {
860 tb = tPtr->currentTextBlock;
861 if (tb->sections && tb->nsections>0)
862 prev_y = tb->sections[tb->nsections-1]._y;
863 y+=10;
864 printf("1 prev_y %d \n", prev_y);
866 /* search backwards for textblocks on same line */
867 while (tb) {
868 if (!tb->sections || tb->nsections<1) {
869 tb = tPtr->firstTextBlock;
870 break;
872 if (tb->sections[tb->nsections-1]._y != prev_y) {
873 tb = tb->next;
874 break;
876 // prev_y = tb->sections[tb->nsections-1]._y;
877 tb = tb->prior;
879 y = 0;//tb->sections[tb->nsections-1]._y;
880 printf("2 prev_y %d \n\n", tb->sections[tb->nsections-1]._y);
884 while (tb) {
886 if (tb->sections && tb->nsections>0) {
887 wfree(tb->sections);
888 tb->sections = NULL;
889 tb->nsections = 0;
892 if (tb->first) {
893 y += layOutLine(tPtr, items, nitems, x, y, pwidth);
894 x = 0;//tPtr->visible.x+2;
895 nitems = 0;
896 lw = 0;
899 if (tb->graphic) {
900 if (!tPtr->flags.monoFont) {
901 width = WMWidgetWidth(tb->d.widget);
902 if (width > pwidth)printf("rescale graphix to fit?\n");
903 lw += width;
904 if (lw >= pwidth - x
905 || nitems >= MAX_TB_PER_LINE) {
906 y += layOutLine(tPtr, items, nitems, x, y, pwidth);
907 nitems = 0;
908 x = 0;//tPtr->visible.x+2;
909 lw = width;
912 items[nitems].tb = tb;
913 items[nitems].begin = 0;
914 items[nitems].end = 0;
915 nitems++;
918 } else if ((start = tb->text)) {
919 begin = end = 0;
920 font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
922 while (start) {
923 mark = mystrchr(start, ' ', tb->used);
924 if (mark) {
925 end += (int)(mark-start)+1;
926 start = mark+1;
927 } else {
928 end += strlen(start);
929 start = mark;
932 if (end > tb->used)
933 end = tb->used;
935 if (end-begin > 0) {
937 width = WMWidthOfString(font,
938 &tb->text[begin], end-begin);
940 if (width > pwidth) { /* break this tb up */
941 char *t = &tb->text[begin];
942 int l=end-begin, i=0;
943 do {
944 width = WMWidthOfString(font, t, ++i);
945 } while (width < pwidth && i < l);
946 end = begin+i;
947 if (start) // and since (nil)-4 = 0xfffffffd
948 start -= l-i;
951 lw += width;
954 if ((lw >= pwidth - x)
955 || nitems >= MAX_TB_PER_LINE) {
956 y += layOutLine(tPtr, items, nitems, x, y, pwidth);
957 lw = width;
958 x = 0; //tPtr->visible.x+2;
959 nitems = 0;
962 items[nitems].tb = tb;
963 items[nitems].begin = begin;
964 items[nitems].end = end;
965 nitems++;
967 begin = end;
970 tb = tb->next;
974 if (nitems > 0)
975 y += layOutLine(tPtr, items, nitems, x, y, pwidth);
976 if (lhc) {
977 tPtr->docHeight = y+10;
978 updateScrollers(tPtr);
980 tPtr->flags.laidOut = True;
985 static void
986 textDidResize(W_ViewDelegate *self, WMView *view)
988 Text *tPtr = (Text *)view->self;
989 unsigned short w = WMWidgetWidth(tPtr);
990 unsigned short h = WMWidgetHeight(tPtr);
991 unsigned short rh = 0, vw = 0;
993 if (tPtr->ruler && tPtr->flags.rulerShown) {
994 WMMoveWidget(tPtr->ruler, 2, 2);
995 WMResizeWidget(tPtr->ruler, w - 4, 40);
996 rh = 40;
999 if (tPtr->vS) {
1000 WMMoveWidget(tPtr->vS, 1, rh + 2);
1001 WMResizeWidget(tPtr->vS, 20, h - rh - 3);
1002 vw = 20;
1003 WMSetRulerOffset(tPtr->ruler,22);
1004 } else WMSetRulerOffset(tPtr->ruler, 2);
1006 if (tPtr->hS) {
1007 if (tPtr->vS) {
1008 WMMoveWidget(tPtr->hS, vw, h - 21);
1009 WMResizeWidget(tPtr->hS, w - vw - 1, 20);
1010 } else {
1011 WMMoveWidget(tPtr->hS, vw+1, h - 21);
1012 WMResizeWidget(tPtr->hS, w - vw - 2, 20);
1016 tPtr->visible.x = (tPtr->vS)?22:2;
1017 tPtr->visible.y = (tPtr->ruler && tPtr->flags.rulerShown)?43:3;
1018 tPtr->visible.w = tPtr->view->size.width - tPtr->visible.x - 12;
1019 tPtr->visible.h = tPtr->view->size.height - tPtr->visible.y;
1020 tPtr->visible.h -= (tPtr->hS)?20:0;
1022 tPtr->margins[0].left = tPtr->margins[0].right = tPtr->visible.x;
1023 tPtr->margins[0].body = tPtr->visible.x;
1024 tPtr->margins[0].right = tPtr->visible.w;
1027 if (tPtr->view->flags.realized) {
1029 if (tPtr->db) {
1030 XFreePixmap(tPtr->view->screen->display, tPtr->db);
1031 tPtr->db = (Pixmap) NULL;
1034 if (tPtr->visible.w < 10) tPtr->visible.w = 10;
1035 if (tPtr->visible.h < 10) tPtr->visible.h = 10;
1037 //if (size change or !db
1038 if(!tPtr->db) {
1039 tPtr->db = XCreatePixmap(tPtr->view->screen->display,
1040 tPtr->view->window, tPtr->visible.w,
1041 tPtr->visible.h, tPtr->view->screen->depth);
1045 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1048 W_ViewDelegate _TextViewDelegate =
1050 NULL,
1051 NULL,
1052 textDidResize,
1053 NULL,
1056 /* nice, divisble-by-16 blocks */
1057 static inline unsigned short
1058 reqBlockSize(unsigned short requested)
1060 return requested + 16 - (requested%16);
1064 static void
1065 clearText(Text *tPtr)
1067 if (!tPtr->firstTextBlock)
1068 return;
1070 while (tPtr->currentTextBlock)
1071 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1073 tPtr->firstTextBlock = NULL;
1074 tPtr->currentTextBlock = NULL;
1075 tPtr->lastTextBlock = NULL;
1078 static void
1079 deleteTextInteractively(Text *tPtr, KeySym ksym)
1081 TextBlock *tb = tPtr->currentTextBlock;
1083 if (!tb)
1084 return;
1086 if (ksym == XK_BackSpace) {
1087 if ( (tb->used > 0) && (tPtr->tpos > 0)
1088 && (tPtr->tpos <= tb->used) && !tb->graphic) {
1089 tPtr->tpos--;
1090 memmove(&(tb->text[tPtr->tpos]),
1091 &(tb->text[tPtr->tpos + 1]), tb->used - tPtr->tpos);
1092 tb->used--;
1095 if (tPtr->tpos < 1) {
1096 TextBlock *prior = tb->prior;
1097 if (tb->used == 0)
1098 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1099 if (prior || !tPtr->currentTextBlock) {
1100 tPtr->currentTextBlock = prior;
1101 if(tPtr->currentTextBlock)
1102 tPtr->tpos = tPtr->currentTextBlock->used;
1103 } else tPtr->tpos = 0;
1110 #if 0
1111 } else if (!back && tb->next) {
1112 TextBlock *next = tb->next;
1113 tPtr->currentTextBlock = next;
1114 tPtr->tpos = 0;
1117 #endif
1124 static void
1125 insertTextInteractively(Text *tPtr, char *text, int len)
1127 TextBlock *tb;
1128 char *newline = NULL;
1130 if (!tPtr->flags.editable || len < 1 || !text)
1131 return;
1133 if(tPtr->flags.ignoreNewLine && *text == '\n' && len == 1)
1134 return;
1136 if (tPtr->flags.ignoreNewLine) {
1137 int i;
1138 for(i=0; i<len; i++) {
1139 if (text[i] == '\n')
1140 text[i] = ' ';
1144 tb = tPtr->currentTextBlock;
1145 if (!tb || tb->graphic) {
1146 text[len] = 0;
1147 WMAppendTextStream(tPtr, text);
1148 if (tPtr->currentTextBlock) {
1149 tPtr->tpos = tPtr->currentTextBlock->used;
1151 return;
1154 if ((newline = strchr(text, '\n'))) {
1155 int nlen = (int)(newline-text);
1156 int s = tb->used - tPtr->tpos;
1157 char save[s];
1159 if (!tb->blank && (1||nlen>0 )) {
1160 if (1||(s > 0) ) {
1161 memcpy(save, &tb->text[tPtr->tpos], s);
1162 tb->used -= (tb->used - tPtr->tpos);
1164 text[nlen] = 0;
1165 insertTextInteractively(tPtr, text, nlen);
1166 newline++;
1167 WMAppendTextStream(tPtr, newline);
1168 if (s>0)
1169 insertTextInteractively(tPtr, save, s);
1170 } else {
1171 if(tb->blank) {
1172 WMAppendTextStream(tPtr, text);
1173 } else {
1174 printf("split me\n");
1179 } else {
1180 if (tb->used + len >= tb->allocated) {
1181 tb->allocated = reqBlockSize(tb->used+len);
1182 tb->text = wrealloc(tb->text, tb->allocated);
1185 if (tb->blank) {
1186 memcpy(tb->text, text, len);
1187 tb->used = len;
1188 tPtr->tpos = len;
1189 tb->blank = False;
1190 } else {
1191 memmove(&(tb->text[tPtr->tpos+len]), &tb->text[tPtr->tpos],
1192 tb->used-tPtr->tpos+1);
1193 memmove(&tb->text[tPtr->tpos], text, len);
1194 tb->used += len;
1195 tPtr->tpos += len;
1202 static void
1203 selectRegion(Text *tPtr, int x, int y)
1205 if (x < 0 || y < 0)
1206 return;
1207 y += (tPtr->flags.rulerShown? 40: 0);
1208 y += tPtr->vpos;
1209 if (y>10) y -= 10; /* the original offset */
1211 x -= tPtr->visible.x-2;
1212 if (x<0) x=0;
1214 tPtr->sel.x = WMAX(0, WMIN(tPtr->clicked.x, x));
1215 tPtr->sel.w = abs(tPtr->clicked.x - x);
1216 tPtr->sel.y = WMAX(0, WMIN(tPtr->clicked.y, y));
1217 tPtr->sel.h = abs(tPtr->clicked.y - y);
1219 tPtr->flags.ownsSelection = True;
1220 paintText(tPtr);
1224 static void
1225 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1226 void *cdata, WMData *data)
1228 Text *tPtr = (Text *)view->self;
1229 char *str;
1231 tPtr->flags.waitingForSelection = False;
1233 if (data) {
1234 str = (char*)WMDataBytes(data);
1235 if (tPtr->parser) {
1236 (tPtr->parser) (tPtr, (void *) str);
1237 } else {
1238 insertTextInteractively(tPtr, str, strlen(str));
1240 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1241 } else {
1242 int n;
1243 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1244 if (str) {
1245 str[n] = 0;
1246 if (tPtr->parser) {
1247 (tPtr->parser) (tPtr, (void *) str);
1248 } else {
1249 insertTextInteractively(tPtr, str, n);
1251 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1252 XFree(str);
1257 static void
1258 releaseSelection(Text *tPtr)
1260 tPtr->flags.ownsSelection = False;
1261 paintText(tPtr);
1264 static WMData *
1265 requestHandler(WMView *view, Atom selection, Atom target,
1266 void *cdata, Atom *type)
1268 Text *tPtr = view->self;
1269 Display *dpy = tPtr->view->screen->display;
1270 Atom TEXT = XInternAtom(dpy, "TEXT", False);
1271 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
1273 *type = target;
1274 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
1275 char *text = WMGetTextSelected(tPtr);
1276 WMData *data = WMCreateDataWithBytes(text, strlen(text));
1277 wfree(text);
1278 return data;
1279 } else {
1280 Atom PIXMAP = XInternAtom(dpy, "PIXMAP", False);
1281 WMData *data = WMCreateDataWithBytes("bleh", 4);
1282 if(target == PIXMAP) {
1283 printf("whoa! pixmap WMGetTextSelectedObjects\n");
1285 return data;
1288 return NULL;
1292 static void
1293 lostHandler(WMView *view, Atom selection, void *cdata)
1295 releaseSelection((WMText *)view->self);
1298 static WMSelectionProcs selectionHandler = {
1299 requestHandler, lostHandler, NULL
1302 static void
1303 ownershipObserver(void *observerData, WMNotification *notification)
1305 WMText *to = (WMText *)observerData;
1306 WMText *tw = (WMText *)WMGetNotificationClientData(notification);
1307 if (to != tw)
1308 lostHandler(to->view, XA_PRIMARY, NULL);
1312 static void
1313 handleTextKeyPress(Text *tPtr, XEvent *event)
1315 char buffer[2];
1316 KeySym ksym;
1317 int control_pressed = False;
1318 // int h=1;
1320 if (((XKeyEvent *) event)->state & ControlMask)
1321 control_pressed = True;
1322 buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = 0;
1324 switch(ksym) {
1325 case XK_Right:
1326 case XK_Left:
1327 case XK_Down:
1328 case XK_Up:
1329 printf("arrows %ld\n", ksym);
1330 break;
1332 case XK_BackSpace:
1333 case XK_Delete:
1334 case XK_KP_Delete:
1335 deleteTextInteractively(tPtr, ksym);
1336 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1337 break;
1339 case XK_Control_R :
1340 case XK_Control_L :
1341 control_pressed = True;
1342 break;
1344 case XK_Return:
1345 buffer[0] = '\n';
1346 default:
1347 if (buffer[0] != 0 && !control_pressed) {
1348 insertTextInteractively(tPtr, buffer, 1);
1349 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1351 } else if (control_pressed && ksym==XK_r) {
1352 Bool i = !tPtr->flags.rulerShown;
1353 WMShowTextRuler(tPtr, i);
1354 tPtr->flags.rulerShown = i;
1356 else if (control_pressed && buffer[0] == '\a')
1357 XBell(tPtr->view->screen->display, 0);
1360 if (!control_pressed && tPtr->flags.ownsSelection)
1361 releaseSelection(tPtr);
1364 static void
1365 handleWidgetPress(XEvent *event, void *data)
1367 TextBlock *tb = (TextBlock *)data;
1368 Text *tPtr;
1369 WMWidget *w;
1371 if (!tb)
1372 return;
1373 /* this little bit of nastiness here saves a boatload of trouble */
1374 w = (WMWidget *)(((W_VIEW(tb->d.widget))->parent)->self);
1375 if (W_CLASS(w) != WC_Text)
1376 return;
1377 tPtr = (Text*)w;
1378 tPtr->currentTextBlock = getFirstNonGraphicBlockFor(tb, 1);
1379 if (!tPtr->currentTextBlock)
1380 tPtr->currentTextBlock = tb;
1381 tPtr->tpos = 0;
1382 output(tPtr->currentTextBlock->text, tPtr->currentTextBlock->used);
1383 //if (!tPtr->flags.focused) {
1384 // WMSetFocusToWidget(tPtr);
1385 // tPtr->flags.focused = True;
1386 //}
1390 static void
1391 handleActionEvents(XEvent *event, void *data)
1393 Text *tPtr = (Text *)data;
1394 Display *dpy = event->xany.display;
1395 KeySym ksym;
1398 switch (event->type) {
1399 case KeyPress:
1400 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1401 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
1402 tPtr->flags.extendSelection = True;
1403 return;
1405 if (!tPtr->flags.editable || tPtr->flags.buttonHeld) {
1406 XBell(dpy, 0);
1407 return;
1410 if (tPtr->flags.waitingForSelection)
1411 return;
1412 if (tPtr->flags.focused) {
1413 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
1414 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1415 GrabModeAsync, GrabModeAsync, None,
1416 tPtr->view->screen->invisibleCursor, CurrentTime);
1417 tPtr->flags.pointerGrabbed = True;
1418 handleTextKeyPress(tPtr, event);
1420 } break;
1422 case KeyRelease:
1423 ksym = XLookupKeysym((XKeyEvent*)event, 0);
1424 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
1425 tPtr->flags.extendSelection = False;
1426 return;
1427 //end modify flag so selection can be extended
1429 break;
1432 case MotionNotify:
1433 if (tPtr->flags.pointerGrabbed) {
1434 tPtr->flags.pointerGrabbed = False;
1435 XUngrabPointer(dpy, CurrentTime);
1437 if ((event->xmotion.state & Button1Mask)) {
1438 if (!tPtr->flags.ownsSelection) {
1439 WMCreateSelectionHandler(tPtr->view, XA_PRIMARY,
1440 event->xbutton.time, &selectionHandler, NULL);
1441 tPtr->flags.ownsSelection = True;
1443 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1445 break;
1448 case ButtonPress:
1449 tPtr->flags.buttonHeld = True;
1450 if (tPtr->flags.extendSelection) {
1451 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
1452 return;
1454 if (event->xbutton.button == Button1) {
1456 if (!tPtr->flags.focused) {
1457 WMSetFocusToWidget(tPtr);
1458 tPtr->flags.focused = True;
1461 if (tPtr->flags.ownsSelection)
1462 releaseSelection(tPtr);
1463 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
1464 paintText(tPtr);
1465 if (tPtr->flags.pointerGrabbed) {
1466 tPtr->flags.pointerGrabbed = False;
1467 XUngrabPointer(dpy, CurrentTime);
1468 break;
1472 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown)
1473 WMScrollText(tPtr, -16);
1474 else if (event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1475 WMScrollText(tPtr, 16);
1476 break;
1478 case ButtonRelease:
1479 tPtr->flags.buttonHeld = False;
1480 if (tPtr->flags.pointerGrabbed) {
1481 tPtr->flags.pointerGrabbed = False;
1482 XUngrabPointer(dpy, CurrentTime);
1483 break;
1485 if (event->xbutton.button == WINGsConfiguration.mouseWheelDown
1486 || event->xbutton.button == WINGsConfiguration.mouseWheelUp)
1487 break;
1489 if (event->xbutton.button == Button2 && tPtr->flags.editable) {
1490 char *text = NULL;
1491 int n;
1492 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1493 event->xbutton.time, pasteText, NULL)) {
1494 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1495 if (text) {
1496 text[n] = 0;
1497 if (tPtr->parser) {
1498 (tPtr->parser) (tPtr, (void *) text);
1499 } else {
1500 insertTextInteractively(tPtr, text, n-1);
1502 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1503 XFree(text);
1504 } else tPtr->flags.waitingForSelection = True;
1507 break;
1514 static void
1515 handleEvents(XEvent *event, void *data)
1517 Text *tPtr = (Text *)data;
1519 switch(event->type) {
1520 case Expose:
1521 if(!tPtr->db) {
1522 tPtr->db = XCreatePixmap(tPtr->view->screen->display,
1523 tPtr->view->window, tPtr->visible.w,
1524 tPtr->visible.h, tPtr->view->screen->depth);
1526 if(tPtr->hS) {
1527 if (!((W_VIEW(tPtr->hS))->flags.mapped)) {
1528 WMRealizeWidget(tPtr->hS);
1529 WMMapWidget(tPtr->hS);
1532 if(tPtr->vS) {
1533 if (!((W_VIEW(tPtr->vS))->flags.mapped)) {
1534 WMRealizeWidget(tPtr->vS);
1535 WMMapWidget(tPtr->vS);
1538 if (!event->xexpose.count && tPtr->view->flags.realized)
1539 paintText(tPtr);
1540 break;
1542 case FocusIn:
1543 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))
1544 != tPtr->view)
1545 return;
1546 tPtr->flags.focused = True;
1547 #if DO_BLINK
1548 if (tPtr->flags.editable && !tPtr->timerID) {
1549 tPtr->timerID = WMAddTimerHandler(12+0*CURSOR_BLINK_ON_DELAY,
1550 blinkCursor, tPtr);
1552 #endif
1554 break;
1556 case FocusOut:
1557 tPtr->flags.focused = False;
1558 paintText(tPtr);
1559 #if DO_BLINK
1560 if (tPtr->timerID) {
1561 WMDeleteTimerHandler(tPtr->timerID);
1562 tPtr->timerID = NULL;
1564 #endif
1565 break;
1567 case DestroyNotify:
1568 printf("destroy");
1569 //for(...)WMRemoveTextParagraph(tPtr, para);
1570 break;
1578 static void
1579 insertPlainText(WMText *tPtr, char *text)
1581 char *start, *mark;
1582 void *tb = NULL;
1584 if (!text) {
1585 clearText(tPtr);
1586 return;
1589 start = text;
1590 while (start) {
1591 mark = strchr(start, '\n');
1592 if (mark) {
1593 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1594 tPtr->dColor, True, (int)(mark-start));
1595 start = mark+1;
1596 } else {
1597 if (start && strlen(start)) {
1598 tb = WMCreateTextBlockWithText(start, tPtr->dFont,
1599 tPtr->dColor, False, strlen(start));
1600 } else tb = NULL;
1601 start = mark;
1604 if (tPtr->flags.prepend)
1605 WMPrependTextBlock(tPtr, tb);
1606 else
1607 WMAppendTextBlock(tPtr, tb);
1609 return;
1614 static void
1615 rulerMoveCallBack(WMWidget *w, void *self)
1617 Text *tPtr = (Text *)self;
1618 if (!tPtr)
1619 return;
1620 if (W_CLASS(tPtr) != WC_Text)
1621 return;
1623 paintText(tPtr);
1627 static void
1628 rulerReleaseCallBack(WMWidget *w, void *self)
1630 Text *tPtr = (Text *)self;
1631 if (!tPtr)
1632 return;
1633 if (W_CLASS(tPtr) != WC_Text)
1634 return;
1636 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
1637 return;
1641 #if 0
1642 static unsigned
1643 draggingEntered(WMView *self, WMDraggingInfo *info)
1645 printf("draggingEntered\n");
1646 return WDOperationCopy;
1650 static unsigned
1651 draggingUpdated(WMView *self, WMDraggingInfo *info)
1653 printf("draggingUpdated\n");
1654 return WDOperationCopy;
1658 static void
1659 draggingExited(WMView *self, WMDraggingInfo *info)
1661 printf("draggingExited\n");
1664 static void
1665 prepareForDragOperation(WMView *self, WMDraggingInfo *info)
1667 printf("draggingExited\n");
1668 return;//"bll"; //"application/X-color";
1672 static Bool
1673 performDragOperation(WMView *self, WMDraggingInfo *info) //, WMData *data)
1675 char *colorName = "Blue";// (char*)WMDataBytes(data);
1676 WMColor *color;
1677 WMText *tPtr = (WMText *)self->self;
1679 if (!tPtr)
1680 return False;
1682 if (tPtr->flags.monoFont)
1683 return False;
1685 color = WMCreateNamedColor(W_VIEW_SCREEN(self), colorName, True);
1686 printf("color [%s] %p\n", colorName, color);
1687 if(color) {
1688 WMSetTextSelectionColor(tPtr, color);
1689 WMReleaseColor(color);
1692 return;// True;
1695 static void
1696 concludeDragOperation(WMView *self, WMDraggingInfo *info)
1698 printf("concludeDragOperation\n");
1702 static WMDragDestinationProcs _DragDestinationProcs = {
1703 draggingEntered,
1704 draggingUpdated,
1705 draggingExited,
1706 prepareForDragOperation,
1707 performDragOperation,
1708 concludeDragOperation
1710 #endif
1713 WMText *
1714 WMCreateText(WMWidget *parent)
1716 Text *tPtr = wmalloc(sizeof(Text));
1717 if (!tPtr) {
1718 printf("could not create text widget\n");
1719 return NULL;
1722 #if 0
1723 printf("sizeof:\n");
1724 printf(" TextBlock %d\n", sizeof(TextBlock));
1725 printf(" Section %d\n", sizeof(Section));
1726 printf(" char * %d\n", sizeof(char *));
1727 printf(" void * %d\n", sizeof(void *));
1728 printf(" short %d\n", sizeof(short));
1729 printf(" Text %d\n", sizeof(Text));
1730 #endif
1732 memset(tPtr, 0, sizeof(Text));
1733 tPtr->widgetClass = WC_Text;
1734 tPtr->view = W_CreateView(W_VIEW(parent));
1735 if (!tPtr->view) {
1736 perror("could not create text's view\n");
1737 free(tPtr);
1738 return NULL;
1740 tPtr->view->self = tPtr;
1741 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
1742 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
1743 W_ResizeView(tPtr->view, 250, 200);
1744 tPtr->bgGC = WMColorGC(tPtr->view->screen->white);
1745 tPtr->fgGC = WMColorGC(tPtr->view->screen->black);
1746 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
1748 tPtr->ruler = NULL;
1749 tPtr->vS = NULL;
1750 tPtr->hS = NULL;
1752 tPtr->dFont = NULL;
1753 //tPtr->dFont = WMCreateFont(tPtr->view->screen,
1754 // "-*-fixed-medium-r-normal--26-*-*-*-*-*-*-*");
1755 //"-sony-fixed-medium-r-normal--24-230-75-75-c-120-jisx0201.1976-0");
1756 // "-*-times-bold-r-*-*-12-*-*-*-*-*-*-*,"
1757 // "-*-fixed-medium-r-normal-*-12-*");
1758 if (!tPtr->dFont)
1759 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
1761 tPtr->dColor = WMBlackColor(tPtr->view->screen);
1763 tPtr->view->delegate = &_TextViewDelegate;
1765 #if DO_BLINK
1766 tPtr->timerID = NULL;
1767 #endif
1769 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
1770 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
1771 handleEvents, tPtr);
1773 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
1774 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
1775 handleActionEvents, tPtr);
1777 WMAddNotificationObserver(ownershipObserver, tPtr, "_lostOwnership", tPtr);
1779 #if 0
1780 WMSetViewDragDestinationProcs(tPtr->view, &_DragDestinationProcs);
1782 char *types[2] = {"application/X-color", NULL};
1783 WMRegisterViewForDraggedTypes(tPtr->view, types);
1785 #endif
1790 tPtr->firstTextBlock = NULL;
1791 tPtr->lastTextBlock = NULL;
1792 tPtr->currentTextBlock = NULL;
1793 tPtr->tpos = 0;
1795 tPtr->gfxItems = WMCreateArrayBag(4);
1797 tPtr->parser = NULL;
1798 tPtr->writer = NULL;
1800 tPtr->sel.x = tPtr->sel.y = 2;
1801 tPtr->sel.w = tPtr->sel.h = 0;
1803 tPtr->clicked.x = tPtr->clicked.y = 2;
1805 tPtr->visible.x = tPtr->visible.y = 2;
1806 tPtr->visible.h = tPtr->view->size.height;
1807 tPtr->visible.w = tPtr->view->size.width - 12;
1809 tPtr->cursor.x = -23;
1811 tPtr->docWidth = 0;
1812 tPtr->docHeight = 0;
1813 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
1814 default_bullet);
1815 tPtr->db = (Pixmap) NULL;
1817 tPtr->margins = wmalloc(sizeof(WMRulerMargins));
1818 tPtr->margins[0].left = tPtr->margins[0].right = tPtr->visible.x;
1819 tPtr->margins[0].body = tPtr->visible.x;
1820 tPtr->margins[0].right = tPtr->visible.w;
1822 tPtr->flags.nmargins = 1;
1823 tPtr->flags.rulerShown = False;
1824 tPtr->flags.monoFont = False;
1825 tPtr->flags.focused = False;
1826 tPtr->flags.editable = True;
1827 tPtr->flags.ownsSelection = False;
1828 tPtr->flags.pointerGrabbed = False;
1829 tPtr->flags.buttonHeld = False;
1830 tPtr->flags.waitingForSelection = False;
1831 tPtr->flags.extendSelection = False;
1832 tPtr->flags.rulerShown = False;
1833 tPtr->flags.frozen = False;
1834 tPtr->flags.cursorShown = True;
1835 tPtr->flags.clickPos = 1;
1836 tPtr->flags.ignoreNewLine = False;
1837 tPtr->flags.laidOut = False;
1838 tPtr->flags.prepend = False;
1839 tPtr->flags.relief = WRFlat;
1840 tPtr->flags.alignment = WALeft;
1842 return tPtr;
1846 void
1847 WMPrependTextStream(WMText *tPtr, char *text)
1849 if (!tPtr)
1850 return;
1852 tPtr->flags.prepend = True;
1853 if (text && tPtr->parser)
1854 (tPtr->parser) (tPtr, (void *) text);
1855 else
1856 insertPlainText(tPtr, text);
1860 void
1861 WMAppendTextStream(WMText *tPtr, char *text)
1863 if (!tPtr)
1864 return;
1866 tPtr->flags.prepend = False;
1867 if (text && tPtr->parser)
1868 (tPtr->parser) (tPtr, (void *) text);
1869 else
1870 insertPlainText(tPtr, text);
1875 char *
1876 WMGetTextStream(WMText *tPtr)
1878 TextBlock *tb = NULL;
1879 char *text = NULL;
1880 unsigned long length = 0, where = 0;
1882 if (!tPtr)
1883 return NULL;
1885 if (!(tb = tPtr->firstTextBlock))
1886 return NULL;
1888 if (tPtr->writer) {
1889 (tPtr->writer) (tPtr, (void *) text);
1890 return text;
1893 /* first, how large a buffer would we want? */
1894 while (tb) {
1895 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank))
1896 length += 1;
1897 if (!tb->graphic)
1898 length += tb->used;
1899 tb = tb->next;
1902 text = wmalloc(length+1); /* +1 for the end of string, let's be nice */
1903 tb = tPtr->firstTextBlock;
1904 while (tb) {
1905 if (!tb->graphic) {
1906 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank))
1907 text[where++] = '\n';
1908 memcpy(&text[where], tb->text, tb->used);
1909 where += tb->used;
1911 tb = tb->next;
1914 text[where] = 0;
1916 return text;
1921 WMBag *
1922 WMGetTextStreamObjects(WMText *tPtr)
1924 if (!tPtr)
1925 return NULL;
1927 return NULL;
1931 char *
1932 WMGetTextSelected(WMText *tPtr)
1934 TextBlock *tb;
1935 char *text = NULL;
1937 if (!tPtr)
1938 return NULL;
1940 //tb = tPtr->firstTextBlock;
1941 tb = tPtr->currentTextBlock;
1942 if (!tb)
1943 return NULL;
1945 text = wmalloc(tb->used+1);
1946 memcpy(text, tb->text, tb->used);
1947 text[tb->used] = 0;
1948 return text;
1951 WMBag *
1952 WMGetTextSelectedObjects(WMText *tPtr)
1954 if (!tPtr)
1955 return NULL;
1957 return NULL;
1960 void *
1961 WMCreateTextBlockWithObject(WMWidget *w, char *description, WMColor *color,
1962 unsigned short first, unsigned short reserved)
1964 TextBlock *tb;
1965 unsigned short length;
1967 if (!w || !description || !color)
1968 return NULL;
1970 tb = wmalloc(sizeof(TextBlock));
1971 if (!tb)
1972 return NULL;
1974 length = strlen(description);
1975 tb->text = (char *)wmalloc(length);
1976 memset(tb->text, 0, length);
1977 memcpy(tb->text, description, length);
1978 tb->used = length;
1979 tb->blank = False;
1980 tb->d.widget = w;
1981 tb->color = WMRetainColor(color);
1982 tb->marginN = 0;
1983 tb->allocated = 0;
1984 tb->first = first;
1985 tb->kanji = False;
1986 tb->graphic = True;
1987 tb->underlined = False;
1988 tb->script = 0;
1989 tb->sections = NULL;
1990 tb->nsections = 0;
1991 tb->prior = NULL;
1992 tb->next = NULL;
1994 return tb;
1997 void *
1998 WMCreateTextBlockWithText(char *text, WMFont *font, WMColor *color,
1999 unsigned short first, unsigned short length)
2001 TextBlock *tb;
2003 if (!font || !color)
2004 return NULL;
2006 tb = wmalloc(sizeof(TextBlock));
2007 if (!tb)
2008 return NULL;
2010 tb->allocated = reqBlockSize(length);
2011 tb->text = (char *)wmalloc(tb->allocated);
2012 memset(tb->text, 0, tb->allocated);
2014 if (length < 1|| !text ) { // || *text == '\n') {
2015 *tb->text = ' ';
2016 tb->used = 1;
2017 tb->blank = True;
2018 } else {
2019 memcpy(tb->text, text, length);
2020 tb->used = length;
2021 tb->blank = False;
2024 tb->d.font = WMRetainFont(font);
2025 tb->color = WMRetainColor(color);
2026 tb->marginN = 0;
2027 tb->first = first;
2028 tb->kanji = False;
2029 tb->graphic = False;
2030 tb->underlined = False;
2031 tb->script = 0;
2032 tb->sections = NULL;
2033 tb->nsections = 0;
2034 tb->prior = NULL;
2035 tb->next = NULL;
2036 return tb;
2039 void
2040 WMSetTextBlockProperties(void *vtb, unsigned int first,
2041 unsigned int kanji, unsigned int underlined, int script,
2042 unsigned int marginN)
2044 TextBlock *tb = (TextBlock *) vtb;
2045 if (!tb)
2046 return;
2048 tb->first = first;
2049 tb->kanji = kanji;
2050 tb->underlined = underlined;
2051 tb->script = script;
2052 tb->marginN = marginN;
2055 void
2056 WMGetTextBlockProperties(void *vtb, unsigned int *first,
2057 unsigned int *kanji, unsigned int *underlined, int *script,
2058 unsigned int *marginN)
2060 TextBlock *tb = (TextBlock *) vtb;
2061 if (!tb)
2062 return;
2064 if (first) *first = tb->first;
2065 if (kanji) *kanji = tb->kanji;
2066 if (underlined) *underlined = tb->underlined;
2067 if (script) *script = tb->script;
2068 if (marginN) *marginN = tb->marginN;
2073 void
2074 WMPrependTextBlock(WMText *tPtr, void *vtb)
2076 TextBlock *tb = (TextBlock *)vtb;
2079 if (!tPtr || !tb)
2080 return;
2082 if (tb->graphic) {
2083 WMWidget *w = tb->d.widget;
2084 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
2085 handleWidgetPress, tb);
2086 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
2087 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
2088 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
2090 WMPutInBag(tPtr->gfxItems, (void *)tb);
2091 tPtr->tpos = 0;
2092 } else tPtr->tpos = tb->used;
2094 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
2095 tb->next = tb->prior = NULL;
2096 tPtr->lastTextBlock = tPtr->firstTextBlock
2097 = tPtr->currentTextBlock = tb;
2098 return;
2101 tb->next = tPtr->currentTextBlock;
2102 tb->prior = tPtr->currentTextBlock->prior;
2103 if (tPtr->currentTextBlock->prior)
2104 tPtr->currentTextBlock->prior->next = tb;
2106 tPtr->currentTextBlock->prior = tb;
2107 if (!tb->prior)
2108 tPtr->firstTextBlock = tb;
2110 tPtr->currentTextBlock = tb;
2114 void
2115 WMAppendTextBlock(WMText *tPtr, void *vtb)
2117 TextBlock *tb = (TextBlock *)vtb;
2119 if (!tPtr || !tb)
2120 return;
2122 if (tb->graphic) {
2123 WMWidget *w = tb->d.widget;
2124 return;
2125 WMCreateEventHandler(W_VIEW(w), ButtonPressMask,
2126 handleWidgetPress, tb);
2127 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
2128 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
2129 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
2131 WMPutInBag(tPtr->gfxItems, (void *)tb);
2132 tPtr->tpos = 0;
2133 } else tPtr->tpos = tb->used;
2135 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
2136 tb->next = tb->prior = NULL;
2137 tPtr->lastTextBlock = tPtr->firstTextBlock
2138 = tPtr->currentTextBlock = tb;
2139 return;
2142 tb->next = tPtr->currentTextBlock->next;
2143 tb->prior = tPtr->currentTextBlock;
2144 if (tPtr->currentTextBlock->next)
2145 tPtr->currentTextBlock->next->prior = tb;
2147 tPtr->currentTextBlock->next = tb;
2149 if (!tb->next)
2150 tPtr->lastTextBlock = tb;
2152 tPtr->currentTextBlock = tb;
2155 void *
2156 WMRemoveTextBlock(WMText *tPtr)
2158 TextBlock *tb = NULL;
2160 if (!tPtr || !tPtr->firstTextBlock || !tPtr->lastTextBlock
2161 || !tPtr->currentTextBlock) {
2162 printf("cannot remove non existent TextBlock!\b");
2163 return tb;
2166 tb = tPtr->currentTextBlock;
2167 if (tb->graphic) {
2168 WMDeleteEventHandler(W_VIEW(tb->d.widget), ButtonPressMask,
2169 handleWidgetPress, tb);
2170 WMRemoveFromBag(tPtr->gfxItems, (void *)tb);
2171 WMUnmapWidget(tb->d.widget);
2174 if (tPtr->currentTextBlock == tPtr->firstTextBlock) {
2175 if (tPtr->currentTextBlock->next)
2176 tPtr->currentTextBlock->next->prior = NULL;
2178 tPtr->firstTextBlock = tPtr->currentTextBlock->next;
2179 tPtr->currentTextBlock = tPtr->firstTextBlock;
2181 } else if (tPtr->currentTextBlock == tPtr->lastTextBlock) {
2182 tPtr->currentTextBlock->prior->next = NULL;
2183 tPtr->lastTextBlock = tPtr->currentTextBlock->prior;
2184 tPtr->currentTextBlock = tPtr->lastTextBlock;
2185 } else {
2186 tPtr->currentTextBlock->prior->next = tPtr->currentTextBlock->next;
2187 tPtr->currentTextBlock->next->prior = tPtr->currentTextBlock->prior;
2188 tPtr->currentTextBlock = tPtr->currentTextBlock->next;
2191 return (void *)tb;
2194 void
2195 WMDestroyTextBlock(WMText *tPtr, void *vtb)
2197 TextBlock *tb = (TextBlock *)vtb;
2198 if (!tPtr || !tb)
2199 return;
2201 if (tb->graphic) {
2202 /* naturally, there's a danger to destroying
2203 widgets whose action brings us here:
2204 ie. press a button to destroy it... need to
2205 find a safer way. till then... this stays commented out */
2206 //WMDestroyWidget(tb->d.widget);
2207 //wfree(tb->d.widget);
2208 tb->d.widget = NULL;
2209 } else {
2210 WMReleaseFont(tb->d.font);
2213 WMReleaseColor(tb->color);
2214 if (tb->sections && tb->nsections > 0)
2215 wfree(tb->sections);
2216 wfree(tb->text);
2217 wfree(tb);
2218 tb = NULL;
2222 void
2223 WMRefreshText(WMText *tPtr, int vpos, int hpos)
2225 //TextBlock *tb;
2227 if (!tPtr || vpos<0 || hpos<0)
2228 return;
2230 if (tPtr->flags.frozen)
2231 return;
2234 tPtr->flags.laidOut = False;
2235 layOutDocument(tPtr);
2236 updateScrollers(tPtr);
2237 paintText(tPtr);
2242 void
2243 WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
2245 if (!tPtr)
2246 return;
2248 if (color)
2249 tPtr->fgGC = WMColorGC(color);
2250 else
2251 tPtr->fgGC = WMColorGC(WMBlackColor(tPtr->view->screen));
2253 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2256 void
2257 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
2259 if (!tPtr)
2260 return;
2262 if (color) {
2263 tPtr->bgGC = WMColorGC(color);
2264 W_SetViewBackgroundColor(tPtr->view, color);
2265 } else {
2266 tPtr->bgGC = WMColorGC(WMWhiteColor(tPtr->view->screen));
2267 W_SetViewBackgroundColor(tPtr->view,
2268 WMWhiteColor(tPtr->view->screen));
2271 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2274 void
2275 WMSetTextRelief(WMText *tPtr, WMReliefType relief)
2277 if (!tPtr)
2278 return;
2279 tPtr->flags.relief = relief;
2280 paintText(tPtr);
2283 void
2284 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
2286 if (!tPtr)
2287 return;
2289 if (shouldhave && !tPtr->hS) {
2290 tPtr->hS = WMCreateScroller(tPtr);
2291 (W_VIEW(tPtr->hS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2292 (W_VIEW(tPtr->hS))->attribFlags |= CWOverrideRedirect | CWCursor;
2293 WMSetScrollerArrowsPosition(tPtr->hS, WSAMaxEnd);
2294 WMSetScrollerAction(tPtr->hS, scrollersCallBack, tPtr);
2295 } else if (!shouldhave && tPtr->hS) {
2296 WMUnmapWidget(tPtr->hS);
2297 WMDestroyWidget(tPtr->hS);
2298 tPtr->hS = NULL;
2301 tPtr->hpos = 0;
2302 tPtr->prevHpos = 0;
2303 textDidResize(tPtr->view->delegate, tPtr->view);
2307 void
2308 WMSetTextHasRuler(WMText *tPtr, Bool shouldhave)
2310 if (!tPtr)
2311 return;
2313 if(shouldhave && !tPtr->ruler) {
2314 tPtr->ruler = WMCreateRuler(tPtr);
2315 (W_VIEW(tPtr->ruler))->attribs.cursor =
2316 tPtr->view->screen->defaultCursor;
2317 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
2318 WMRealizeWidget(tPtr->ruler);
2319 WMSetRulerReleaseAction(tPtr->ruler, rulerReleaseCallBack, tPtr);
2320 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
2321 } else if(!shouldhave && tPtr->ruler) {
2322 WMShowTextRuler(tPtr, False);
2323 WMDestroyWidget(tPtr->ruler);
2324 tPtr->ruler = NULL;
2326 textDidResize(tPtr->view->delegate, tPtr->view);
2329 void
2330 WMShowTextRuler(WMText *tPtr, Bool show)
2332 if(!tPtr)
2333 return;
2334 if(!tPtr->ruler)
2335 return;
2337 if(tPtr->flags.monoFont)
2338 show = False;
2340 tPtr->flags.rulerShown = show;
2341 if(show) {
2342 WMMapWidget(tPtr->ruler);
2343 } else {
2344 WMUnmapWidget(tPtr->ruler);
2347 textDidResize(tPtr->view->delegate, tPtr->view);
2351 void
2352 WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
2354 if (!tPtr)
2355 return;
2357 if (shouldhave && !tPtr->vS) {
2358 tPtr->vS = WMCreateScroller(tPtr);
2359 (W_VIEW(tPtr->vS))->attribs.cursor = tPtr->view->screen->defaultCursor;
2360 (W_VIEW(tPtr->vS))->attribFlags |= CWOverrideRedirect | CWCursor;
2361 WMSetScrollerArrowsPosition(tPtr->vS, WSAMaxEnd);
2362 WMSetScrollerAction(tPtr->vS, scrollersCallBack, tPtr);
2363 } else if (!shouldhave && tPtr->vS) {
2364 WMUnmapWidget(tPtr->vS);
2365 WMDestroyWidget(tPtr->vS);
2366 tPtr->vS = NULL;
2369 tPtr->vpos = 0;
2370 tPtr->prevVpos = 0;
2371 textDidResize(tPtr->view->delegate, tPtr->view);
2376 Bool
2377 WMScrollText(WMText *tPtr, int amount)
2379 Bool scroll=False;
2380 if (!tPtr)
2381 return False;
2382 if (amount == 0 || !tPtr->view->flags.realized)
2383 return False;
2385 if (amount < 0) {
2386 if (tPtr->vpos > 0) {
2387 if (tPtr->vpos > amount) tPtr->vpos += amount;
2388 else tPtr->vpos=0;
2389 scroll=True;
2390 } } else {
2391 int limit = tPtr->docHeight - tPtr->visible.h;
2392 if (tPtr->vpos < limit) {
2393 if (tPtr->vpos < limit-amount) tPtr->vpos += amount;
2394 else tPtr->vpos = limit;
2395 scroll = True;
2396 } }
2398 if (scroll && tPtr->vpos != tPtr->prevVpos) {
2399 updateScrollers(tPtr);
2400 paintText(tPtr);
2402 tPtr->prevVpos = tPtr->vpos;
2403 return scroll;
2406 Bool
2407 WMPageText(WMText *tPtr, Bool direction)
2409 if (!tPtr) return False;
2410 if (!tPtr->view->flags.realized) return False;
2412 return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
2415 void
2416 WMSetTextEditable(WMText *tPtr, Bool editable)
2418 if (!tPtr)
2419 return;
2420 tPtr->flags.editable = editable;
2423 int
2424 WMGetTextEditable(WMText *tPtr)
2426 if (!tPtr)
2427 return 0;
2428 return tPtr->flags.editable;
2431 void
2432 WMSetTextIgnoresNewline(WMText *tPtr, Bool ignore)
2434 if (!tPtr)
2435 return;
2436 tPtr->flags.ignoreNewLine = ignore;
2439 Bool
2440 WMGetTextIgnoresNewline(WMText *tPtr)
2442 if (!tPtr)
2443 return True;
2444 return tPtr->flags.ignoreNewLine;
2447 void
2448 WMSetTextUsesMonoFont(WMText *tPtr, Bool mono)
2450 if (!tPtr)
2451 return;
2452 if (mono && tPtr->flags.rulerShown)
2453 ;//WMShowTextRuler(tPtr, False);
2455 tPtr->flags.monoFont = mono;
2456 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2459 Bool
2460 WMGetTextUsesMonoFont(WMText *tPtr)
2462 if (!tPtr)
2463 return True;
2464 return tPtr->flags.monoFont;
2468 void
2469 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
2471 if (!tPtr)
2472 return;
2474 if (font)
2475 tPtr->dFont = font;
2476 else
2477 tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont);
2480 WMFont *
2481 WMGetTextDefaultFont(WMText *tPtr)
2483 if (!tPtr)
2484 return NULL;
2485 else
2486 return tPtr->dFont;
2489 void
2490 WMSetTextAlignment(WMText *tPtr, WMAlignment alignment)
2492 if (!tPtr)
2493 return;
2494 tPtr->flags.alignment = alignment;
2495 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2498 void
2499 WMSetTextParser(WMText *tPtr, WMAction *parser)
2501 if (!tPtr)
2502 return;
2503 tPtr->parser = parser;
2506 void
2507 WMSetTextWriter(WMText *tPtr, WMAction *writer)
2509 if (!tPtr)
2510 return;
2511 tPtr->writer = writer;
2514 int
2515 WMGetTextInsertType(WMText *tPtr)
2517 if (!tPtr)
2518 return 0;
2519 return tPtr->flags.prepend;
2523 void
2524 WMSetTextSelectionColor(WMText *tPtr, WMColor *color)
2526 TextBlock *tb;
2527 if (!tPtr || !color)
2528 return;
2530 tb = tPtr->firstTextBlock;
2531 if (!tb || !tPtr->flags.ownsSelection)
2532 return;
2534 while (tb) {
2535 if(tb->color)
2536 WMReleaseColor(tb->color);
2537 tb->color = WMRetainColor(color);
2538 tb = tb->next;
2540 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2543 void
2544 WMSetTextSelectionFont(WMText *tPtr, WMFont *font)
2546 TextBlock *tb;
2547 if (!tPtr || !font)
2548 return;
2550 tb = tPtr->firstTextBlock;
2551 if (!tb || !tPtr->flags.ownsSelection)
2552 return;
2554 while (tb) {
2555 if (!tb->graphic)
2556 tb->d.font = WMRetainFont(font);
2557 tb = tb->next;
2559 WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos);
2562 void
2563 WMFreezeText(WMText *tPtr)
2565 if (!tPtr)
2566 return;
2568 tPtr->flags.frozen = True;
2571 void
2572 WMThawText(WMText *tPtr)
2574 if (!tPtr)
2575 return;
2577 tPtr->flags.frozen = False;
2581 Bool
2582 WMFindInTextStream(WMText *tPtr, char *needle)
2584 char *haystack = NULL;
2585 Bool result;
2587 if (!tPtr || !needle)
2588 return False;
2590 if ( !(haystack = WMGetTextStream(tPtr)))
2591 return False;
2593 result = (Bool) strstr(haystack, needle);
2594 wfree(haystack);
2595 return result;