- Check whether libXft is at least version 2.1.2 else refuse to compile.
[wmaker-crm.git] / WINGs / wtext.c
blob342fae7042084173831c5edef3e45dc468a404aa
2 /* WINGs WMText: multi-line/font/color/graphic text widget, by Nwanua. */
5 #include "WINGsP.h"
6 #include <ctype.h>
7 #include <X11/keysym.h>
8 #include <X11/Xatom.h>
10 #define DO_BLINK 0
12 /* TODO:
13 * - verify what happens with XK_return in insertTextInt...
14 * - selection code... selects can be funny if it crosses over. use rect?
15 * - also inspect behaviour for WACenter and WARight
16 * - what if a widget grabs the click... howto say: "pressed me"?
17 * note that WMCreateEventHandler takes one data, but need widget & tPtr
18 * - FIX: graphix blocks MUST be skipped if monoFont even though they exist!
19 * - check if support for Horizontal Scroll is complete
20 * - Tabs now are simply replaced by 4 spaces...
21 * - redo blink code to reduce paint event... use pixmap buffer...
22 * - add paragraph support (full) and '\n' code in getStream..
26 /* a Section is a section of a TextBlock that describes what parts
27 of a TextBlock has been laid out on which "line"...
28 o this greatly aids redraw, scroll and selection.
29 o this is created during layoutLine, but may be later modified.
30 o there may be many Sections per TextBlock, hence the array */
31 typedef struct {
32 unsigned int x, y; /* where to draw it from */
33 unsigned short w, h; /* its width and height */
34 unsigned short begin; /* where the layout begins */
35 unsigned short end ; /* where it ends */
36 unsigned short max_d; /* a quick hack for layOut if(laidOut) */
37 unsigned short last:1; /* is it the last section on a "line"? */
38 unsigned int _y:31; /* the "line" it and other textblocks are on */
39 } Section;
42 /* a TextBlock is a node in a doubly-linked list of TextBlocks containing:
43 o text for the block, color and font
44 o or a pointer to the pixmap
45 o OR a pointer to the widget and the (text) description for its graphic
48 typedef struct _TextBlock {
49 struct _TextBlock *next; /* next text block in linked list */
50 struct _TextBlock *prior; /* prior text block in linked list */
52 char *text; /* pointer to text (could be kanji) */
53 /* or to the object's description */
54 union {
55 WMFont *font; /* the font */
56 WMWidget *widget; /* the embedded widget */
57 WMPixmap *pixmap; /* the pixmap */
58 } d; /* description */
60 unsigned short used; /* number of chars in this block */
61 unsigned short allocated; /* size of allocation (in chars) */
62 WMColor *color; /* the color */
64 Section *sections; /* the region for layouts (a growable array) */
65 /* an _array_! of size _nsections_ */
67 unsigned short s_begin; /* where the selection begins */
68 unsigned short s_end; /* where it ends */
70 unsigned int first:1; /* first TextBlock in paragraph */
71 unsigned int blank:1; /* ie. blank paragraph */
72 unsigned int kanji:1; /* is of 16-bit characters or not */
73 unsigned int graphic:1; /* graphic or text: text=0 */
74 unsigned int object:1; /* embedded object or pixmap */
75 unsigned int underlined:1; /* underlined or not */
76 unsigned int selected:1; /* selected or not */
77 unsigned int nsections:8; /* over how many "lines" a TextBlock wraps */
78 int script:8; /* script in points: negative for subscript */
79 unsigned int marginN:8; /* which of the margins in the tPtr to use */
80 unsigned int nClicks:2; /* single, double, triple clicks */
81 unsigned int RESERVED:7;
82 } TextBlock;
85 /* I'm lazy: visible.h vs. visible.size.height :-) */
86 typedef struct {
87 int y, x, h, w;
88 } myRect;
91 typedef struct W_Text {
92 W_Class widgetClass; /* the class number of this widget */
93 W_View *view; /* the view referring to this instance */
95 WMRuler *ruler; /* the ruler widget to manipulate paragraphs */
97 WMScroller *vS; /* the vertical scroller */
98 unsigned int vpos; /* the current vertical position */
99 unsigned int prevVpos; /* the previous vertical position */
101 WMScroller *hS; /* the horizontal scroller */
102 unsigned int hpos; /* the current horizontal position */
103 unsigned int prevHpos; /* the previous horizontal position */
105 WMFont *dFont; /* the default font */
106 WMColor *dColor; /* the default color */
107 WMPixmap *dBulletPix; /* the default pixmap for bullets */
109 WMColor *fgColor; /* The current foreground color */
110 WMColor *bgColor; /* The background color */
112 GC stippledGC; /* the GC to overlay selected graphics with */
113 Pixmap db; /* the buffer on which to draw */
114 WMPixmap *bgPixmap; /* the background pixmap */
116 myRect visible; /* the actual rectangle that can be drawn into */
117 myRect cursor; /* the position and (height) of cursor */
118 myRect sel; /* the selection rectangle */
120 WMPoint clicked; /* where in the _document_ was clicked */
122 unsigned short tpos; /* the position in the currentTextBlock */
123 unsigned short docWidth; /* the width of the entire document */
124 unsigned int docHeight; /* the height of the entire document */
126 TextBlock *firstTextBlock;
127 TextBlock *lastTextBlock;
128 TextBlock *currentTextBlock;
130 WMArray *gfxItems; /* a nice array of graphic items */
132 #if DO_BLINK
133 WMHandlerID timerID; /* for nice twinky-winky */
134 #endif
136 WMAction *parser;
137 WMAction *writer;
138 WMTextDelegate *delegate;
139 Time lastClickTime;
141 WMRulerMargins *margins; /* an array of margins */
143 unsigned int nMargins:7; /* the total number of margins in use */
144 struct {
145 unsigned int monoFont:1; /* whether to ignore formats and graphic */
146 unsigned int focused:1; /* whether this instance has input focus */
147 unsigned int editable:1; /* "silly user, you can't edit me" */
148 unsigned int ownsSelection:1; /* "I ownz the current selection!" */
149 unsigned int pointerGrabbed:1;/* "heh, gib me pointer" */
150 unsigned int extendSelection:1; /* shift-drag to select more regions */
152 unsigned int rulerShown:1; /* whether the ruler is shown or not */
153 unsigned int frozen:1; /* whether screen updates are to be made */
154 unsigned int cursorShown:1; /* whether to show the cursor */
155 unsigned int acceptsGraphic:1;/* accept graphic when dropped */
156 unsigned int horizOnDemand:1;/* if a large image should appear*/
157 unsigned int needsLayOut:1; /* in case of Append/Deletes */
158 unsigned int ignoreNewLine:1;/* turn it into a ' ' in streams > 1 */
159 unsigned int indentNewLine:1;/* add " " for a newline typed */
160 unsigned int laidOut:1; /* have the TextBlocks all been laid out */
161 unsigned int waitingForSelection:1; /* I don't wanna wait in vain... */
162 unsigned int prepend:1; /* prepend=1, append=0 (for parsers) */
163 WMAlignment alignment:2; /* the alignment for text */
164 WMReliefType relief:3; /* the relief to display with */
165 unsigned int isOverGraphic:2;/* the mouse is over a graphic */
166 unsigned int first:1; /* for plain text parsing, newline? */
167 /* unsigned int RESERVED:1; */
168 } flags;
170 WMArray *xdndSourceTypes;
171 WMArray *xdndDestinationTypes;
172 } Text;
175 #define NOTIFY(T,C,N,A) {\
176 WMNotification *notif = WMCreateNotification(N,T,A);\
177 if ((T)->delegate && (T)->delegate->C)\
178 (*(T)->delegate->C)((T)->delegate,notif);\
179 WMPostNotification(notif);\
180 WMReleaseNotification(notif);}
183 #define TYPETEXT 0
185 #if 0
186 /* just to print blocks of text not terminated by \0 */
187 static void
188 output(char *ptr, int len)
190 char *s;
192 s = wmalloc(len+1);
193 memcpy(s, ptr, len);
194 s[len] = 0;
195 /* printf(" s is [%s] (%d)\n", s, strlen(s)); */
196 printf("[%s]\n", s);
197 wfree(s);
199 #endif
202 #if DO_BLINK
203 #define CURSOR_BLINK_ON_DELAY 600
204 #define CURSOR_BLINK_OFF_DELAY 400
205 #endif
208 #define STIPPLE_WIDTH 8
209 #define STIPPLE_HEIGHT 8
210 static unsigned char STIPPLE_BITS[] = {
211 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa
216 static char *default_bullet[] = {
217 "6 6 4 1",
218 " c None s None", ". c black",
219 "X c white", "o c #808080",
220 " ... ",
221 ".XX.. ",
222 ".XX..o",
223 ".....o",
224 " ...oo",
225 " ooo "};
228 static void handleEvents(XEvent *event, void *data);
229 static void layOutDocument(Text *tPtr);
230 static void updateScrollers(Text *tPtr);
233 static int
234 getMarginNumber(Text *tPtr, WMRulerMargins *margins)
236 unsigned int i=0;
238 for(i=0; i < tPtr->nMargins; i++) {
240 if(WMIsMarginEqualToMargin(&tPtr->margins[i], margins))
241 return i;
244 return -1;
249 static int
250 newMargin(Text *tPtr, WMRulerMargins *margins)
252 int n;
254 if (!margins) {
255 tPtr->margins[0].retainCount++;
256 return 0;
259 n = getMarginNumber(tPtr, margins);
261 if (n == -1) {
263 if(tPtr->nMargins >= 127) {
264 n = tPtr->nMargins-1;
265 return n;
268 tPtr->margins = wrealloc(tPtr->margins,
269 (++tPtr->nMargins)*sizeof(WMRulerMargins));
271 n = tPtr->nMargins-1;
272 tPtr->margins[n].left = margins->left;
273 tPtr->margins[n].first = margins->first;
274 tPtr->margins[n].body = margins->body;
275 tPtr->margins[n].right = margins->right;
276 /* for each tab... */
277 tPtr->margins[n].retainCount = 1;
278 } else {
279 tPtr->margins[n].retainCount++;
282 return n;
285 static Bool
286 sectionWasSelected(Text *tPtr, TextBlock *tb, XRectangle *rect, int s)
288 unsigned short i, w, lw, selected = False, extend = False;
289 myRect sel;
292 /* if selection rectangle completely encloses the section */
293 if ((tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
294 && (tb->sections[s]._y + tb->sections[s].h
295 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
296 sel.x = 0;
297 sel.w = tPtr->visible.w;
298 selected = extend = True;
300 /* or if it starts on a line and then goes further down */
301 } else if ((tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
302 && (tb->sections[s]._y + tb->sections[s].h
303 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)
304 && (tb->sections[s]._y + tb->sections[s].h
305 >= tPtr->visible.y + tPtr->sel.y) ) {
306 sel.x = WMAX(tPtr->sel.x, tPtr->clicked.x);
307 sel.w = tPtr->visible.w;
308 selected = extend = True;
310 /* or if it begins before a line, but ends on it */
311 } else if ((tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
312 && (tb->sections[s]._y + tb->sections[s].h
313 >= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)
314 && (tb->sections[s]._y
315 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
317 if (1||tPtr->sel.x + tPtr->sel.w > tPtr->clicked.x)
318 sel.w = tPtr->sel.x + tPtr->sel.w;
319 else
320 sel.w = tPtr->sel.x;
322 sel.x = 0;
323 selected = True;
325 /* or if the selection rectangle lies entirely within a line */
326 } else if ((tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
327 && (tPtr->sel.w >= 2)
328 && (tb->sections[s]._y + tb->sections[s].h
329 >= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
330 sel.x = tPtr->sel.x;
331 sel.w = tPtr->sel.w;
332 selected = True;
335 if (selected) {
336 selected = False;
338 /* if not within (modified) selection rectangle */
339 if ( tb->sections[s].x > sel.x + sel.w
340 || tb->sections[s].x + tb->sections[s].w < sel.x)
341 return False;
343 if (tb->graphic) {
344 if ( tb->sections[s].x + tb->sections[s].w <= sel.x + sel.w
345 && tb->sections[s].x >= sel.x) {
346 rect->width = tb->sections[s].w;
347 rect->x = tb->sections[s].x;
348 selected = True;
350 } else {
352 i = tb->sections[s].begin;
353 lw = 0;
355 if (0&& tb->sections[s].x >= sel.x) {
356 tb->s_begin = tb->sections[s].begin;
357 goto _selEnd;
360 while (++i <= tb->sections[s].end) {
362 w = WMWidthOfString(tb->d.font, &(tb->text[i-1]), 1);
363 lw += w;
365 if (lw + tb->sections[s].x >= sel.x
366 || i == tb->sections[s].end ) {
367 lw -= w;
368 i--;
369 tb->s_begin = (tb->selected? WMIN(tb->s_begin, i) : i);
370 break;
374 if (i > tb->sections[s].end) {
375 printf("WasSelected: (i > tb->sections[s].end) \n");
376 return False;
379 _selEnd: rect->x = tb->sections[s].x + lw;
380 lw = 0;
381 while(++i <= tb->sections[s].end) {
383 w = WMWidthOfString(tb->d.font, &(tb->text[i-1]), 1);
384 lw += w;
386 if (lw + rect->x >= sel.x + sel.w
387 || i == tb->sections[s].end ) {
389 if (i != tb->sections[s].end) {
390 lw -= w;
391 i--;
394 rect->width = lw;
395 if (tb->sections[s].last && sel.x + sel.w
396 >= tb->sections[s].x + tb->sections[s].w
397 && extend ) {
398 rect->width += (tPtr->visible.w - rect->x - lw);
401 tb->s_end = (tb->selected? WMAX(tb->s_end, i) : i);
402 selected = True;
403 break;
409 if (selected) {
410 rect->y = tb->sections[s]._y - tPtr->vpos;
411 rect->height = tb->sections[s].h;
412 if(tb->graphic) { printf("DEBUG: graphic s%d h%d\n", s,tb->sections[s].h);}
414 return selected;
418 static void
419 setSelectionProperty(WMText *tPtr, WMFont *font, WMColor *color, int underlined)
421 TextBlock *tb;
422 int isFont=False;
424 tb = tPtr->firstTextBlock;
425 if (!tb || !tPtr->flags.ownsSelection)
426 return;
428 if (font && (!color || underlined==-1))
429 isFont = True;
431 while (tb) {
432 if (tPtr->flags.monoFont || tb->selected) {
434 if (tPtr->flags.monoFont || (tb->s_end - tb->s_begin == tb->used)
435 || tb->graphic) {
437 if(isFont) {
438 if(!tb->graphic) {
439 WMReleaseFont(tb->d.font);
440 tb->d.font = WMRetainFont(font);
442 } else if(underlined !=-1) {
443 tb->underlined = underlined;
444 } else {
445 WMReleaseColor(tb->color);
446 tb->color = WMRetainColor(color);
449 } else if (tb->s_end <= tb->used && tb->s_begin < tb->s_end) {
451 TextBlock *midtb, *otb = tb;
453 if(underlined != -1) {
454 midtb = (TextBlock *) WMCreateTextBlockWithText(tPtr,
455 &(tb->text[tb->s_begin]), tb->d.font, tb->color,
456 False, (tb->s_end - tb->s_begin));
457 } else {
458 midtb = (TextBlock *) WMCreateTextBlockWithText(tPtr,
459 &(tb->text[tb->s_begin]),
460 (isFont?font:tb->d.font),
461 (isFont?tb->color:color),
462 False, (tb->s_end - tb->s_begin));
466 if (midtb) {
467 if(underlined != -1) {
468 midtb->underlined = underlined;
469 } else {
470 midtb->underlined = otb->underlined;
473 midtb->selected = !True;
474 midtb->s_begin = 0;
475 midtb->s_end = midtb->used;
476 tPtr->currentTextBlock = tb;
477 WMAppendTextBlock(tPtr, midtb);
478 tb = tPtr->currentTextBlock;
481 if (otb->used - otb->s_end > 0) {
482 TextBlock *ntb;
483 ntb = (TextBlock *)
484 WMCreateTextBlockWithText(tPtr,
485 &(otb->text[otb->s_end]), otb->d.font, otb->color,
486 False, otb->used - otb->s_end);
488 if (ntb) {
489 ntb->underlined = otb->underlined;
490 ntb->selected = False;
491 WMAppendTextBlock(tPtr, ntb);
492 tb = tPtr->currentTextBlock;
496 if (midtb) {
497 tPtr->currentTextBlock = midtb;
500 otb->selected = False;
501 otb->used = otb->s_begin;
505 tb = tb->next;
508 tPtr->flags.needsLayOut = True;
509 WMThawText(tPtr);
511 /* in case the size changed... */
512 if(isFont && tPtr->currentTextBlock) {
513 TextBlock *tb = tPtr->currentTextBlock;
515 printf("%d %d %d\n", tPtr->sel.y, tPtr->sel.h, tPtr->sel.w);
516 tPtr->sel.y = 3 + tb->sections[0]._y;
517 tPtr->sel.h = tb->sections[tb->nsections-1]._y - tb->sections[0]._y;
518 tPtr->sel.w = tb->sections[tb->nsections-1].w;
519 if(tb->sections[tb->nsections-1]._y != tb->sections[0]._y) {
520 tPtr->sel.x = 0;
522 printf("%d %d %d\n\n\n", tPtr->sel.y, tPtr->sel.h, tPtr->sel.w);
528 static Bool
529 removeSelection(Text *tPtr)
531 TextBlock *tb = NULL;
532 Bool first = False;
534 if (!(tb = tPtr->firstTextBlock))
535 return False;
537 while (tb) {
538 if (tb->selected) {
539 if(!first && !tb->graphic) {
540 WMReleaseFont(tPtr->dFont);
541 tPtr->dFont = WMRetainFont(tb->d.font);
542 first = True;
545 if ( (tb->s_end - tb->s_begin == tb->used) || tb->graphic) {
546 tPtr->currentTextBlock = tb;
547 if(tb->next) {
548 tPtr->tpos = 0;
549 } else if(tb->prior) {
550 if(tb->prior->graphic)
551 tPtr->tpos = 1;
552 else
553 tPtr->tpos = tb->prior->used;
554 } else tPtr->tpos = 0;
556 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
557 tb = tPtr->currentTextBlock;
558 continue;
560 } else if (tb->s_end <= tb->used) {
561 memmove(&(tb->text[tb->s_begin]),
562 &(tb->text[tb->s_end]), tb->used - tb->s_end);
563 tb->used -= (tb->s_end - tb->s_begin);
564 tb->selected = False;
565 tPtr->tpos = tb->s_begin;
570 tb = tb->next;
572 return True;
576 static TextBlock*
577 getFirstNonGraphicBlockFor(TextBlock *tb, short dir)
579 TextBlock *hold = tb;
581 if (!tb)
582 return NULL;
584 while (tb) {
585 if (!tb->graphic)
586 break;
587 tb = (dir? tb->next : tb->prior);
590 if(!tb) {
591 tb = hold;
592 while (tb) {
593 if (!tb->graphic)
594 break;
595 tb = (dir? tb->prior : tb->next);
599 if(!tb)
600 return NULL;
602 return tb;
606 static Bool
607 updateStartForCurrentTextBlock(Text *tPtr, int x, int y, int *dir,
608 TextBlock *tb)
610 if (tPtr->flags.monoFont && tb->graphic) {
611 tb = getFirstNonGraphicBlockFor(tb, *dir);
612 if(!tb)
613 return 0;
615 if (tb->graphic) {
616 tPtr->currentTextBlock =
617 (dir? tPtr->lastTextBlock : tPtr->firstTextBlock);
618 tPtr->tpos = 0;
619 return 0;
624 if(!tb->sections) {
625 layOutDocument(tPtr);
626 return 0;
629 *dir = !(y <= tb->sections[0].y);
630 if(*dir) {
631 if ( ( y <= tb->sections[0]._y + tb->sections[0].h )
632 && (y >= tb->sections[0]._y ) ) {
633 /* if it's on the same line */
634 if(x < tb->sections[0].x)
635 *dir = 0;
637 } else {
638 if ( ( y <= tb->sections[tb->nsections-1]._y
639 + tb->sections[tb->nsections-1].h )
640 && (y >= tb->sections[tb->nsections-1]._y ) ) {
641 /* if it's on the same line */
642 if(x > tb->sections[tb->nsections-1].x)
643 *dir = 1;
647 return 1;
651 static void
652 paintText(Text *tPtr)
654 TextBlock *tb;
655 WMFont *font;
656 char *text;
657 int len, y, c, s, done=False, prev_y=-23, dir /* 1 = down */;
658 WMScreen *scr = tPtr->view->screen;
659 Display *dpy = tPtr->view->screen->display;
660 Window win = tPtr->view->window;
661 WMColor *color;
663 if (!tPtr->view->flags.realized || !tPtr->db || tPtr->flags.frozen)
664 return;
667 XFillRectangle(dpy, tPtr->db, WMColorGC(tPtr->bgColor), 0, 0,
668 tPtr->visible.w, tPtr->visible.h);
670 if (tPtr->bgPixmap) {
671 WMDrawPixmap(tPtr->bgPixmap, tPtr->db,
672 (tPtr->visible.w-tPtr->visible.x-tPtr->bgPixmap->width)/2,
673 (tPtr->visible.h-tPtr->visible.y-tPtr->bgPixmap->height)/2);
676 if (! (tb = tPtr->currentTextBlock)) {
677 if (! (tb = tPtr->firstTextBlock)) {
678 goto _copy_area;
682 done = False;
686 /* first, which direction? Don't waste time looking all over,
687 since the parts to be drawn will most likely be near what
688 was previously drawn */
689 if(!updateStartForCurrentTextBlock(tPtr, 0, tPtr->vpos, &dir, tb))
690 goto _copy_area;
692 while(tb) {
694 if (tb->graphic && tPtr->flags.monoFont)
695 goto _getSibling;
697 if(dir) {
698 if(tPtr->vpos <= tb->sections[tb->nsections-1]._y
699 + tb->sections[tb->nsections-1].h)
700 break;
701 } else {
702 if(tPtr->vpos >= tb->sections[tb->nsections-1]._y
703 + tb->sections[tb->nsections-1].h)
704 break;
707 _getSibling:
708 if(dir) {
709 if(tb->next)
710 tb = tb->next;
711 else break;
712 } else {
713 if(tb->prior)
714 tb = tb->prior;
715 else break;
720 /* first, place all text that can be viewed */
721 while (!done && tb) {
722 if (tb->graphic) {
723 tb = tb->next;
724 continue;
727 tb->selected = False;
729 for(s=0; s<tb->nsections && !done; s++) {
731 if (tb->sections[s]._y > tPtr->vpos + tPtr->visible.h) {
732 done = True;
733 break;
736 if ( tb->sections[s].y + tb->sections[s].h < tPtr->vpos)
737 continue;
739 if (tPtr->flags.monoFont) {
740 font = tPtr->dFont;
741 color = tPtr->fgColor;
742 } else {
743 font = tb->d.font;
744 color = tb->color;
747 if (tPtr->flags.ownsSelection) {
748 XRectangle rect;
750 if (sectionWasSelected(tPtr, tb, &rect, s)) {
751 tb->selected = True;
752 XFillRectangle(dpy, tPtr->db, WMColorGC(scr->gray),
753 rect.x, rect.y, rect.width, rect.height);
757 prev_y = tb->sections[s]._y;
759 len = tb->sections[s].end - tb->sections[s].begin;
760 text = &(tb->text[tb->sections[s].begin]);
761 y = tb->sections[s].y - tPtr->vpos;
762 WMDrawString(scr, tPtr->db, color, font,
763 tb->sections[s].x - tPtr->hpos, y, text, len);
765 if (!tPtr->flags.monoFont && tb->underlined) {
766 XDrawLine(dpy, tPtr->db, WMColorGC(color),
767 tb->sections[s].x - tPtr->hpos,
768 y + font->y + 1,
769 tb->sections[s].x + tb->sections[s].w - tPtr->hpos,
770 y + font->y + 1);
773 tb = (!done? tb->next : NULL);
776 /* now , show all graphic items that can be viewed */
777 c = WMGetArrayItemCount(tPtr->gfxItems);
778 if (c > 0 && !tPtr->flags.monoFont) {
779 int j, h;
781 for(j=0; j<c; j++) {
782 tb = (TextBlock *) WMGetFromArray(tPtr->gfxItems, j);
784 /* if it's not viewable, and mapped, unmap it */
785 if (tb->sections[0]._y + tb->sections[0].h <= tPtr->vpos
786 || tb->sections[0]._y >= tPtr->vpos + tPtr->visible.h ) {
788 if(tb->object) {
789 if ((W_VIEW(tb->d.widget))->flags.mapped) {
790 WMUnmapWidget(tb->d.widget);
793 } else {
794 /* if it's viewable, and not mapped, map it */
795 if(tb->object) {
796 W_View *view = W_VIEW(tb->d.widget);
798 if (!view->flags.realized)
799 WMRealizeWidget(tb->d.widget);
800 if(!view->flags.mapped) {
801 XMapWindow(view->screen->display, view->window);
802 XFlush(view->screen->display);
803 view->flags.mapped = 1;
807 if(tb->object) {
808 WMMoveWidget(tb->d.widget,
809 tb->sections[0].x + tPtr->visible.x - tPtr->hpos,
810 tb->sections[0].y + tPtr->visible.y - tPtr->vpos);
811 h = WMWidgetHeight(tb->d.widget) + 1;
813 } else {
814 WMDrawPixmap(tb->d.pixmap, tPtr->db,
815 tb->sections[0].x - tPtr->hpos,
816 tb->sections[0].y - tPtr->vpos);
817 h = tb->d.pixmap->height + 1;
821 if (tPtr->flags.ownsSelection) {
822 XRectangle rect;
824 if ( sectionWasSelected(tPtr, tb, &rect, 0)) {
825 Drawable d = (0&&tb->object?
826 (WMWidgetView(tb->d.widget))->window : tPtr->db);
828 tb->selected = True;
829 XFillRectangle(dpy, d, tPtr->stippledGC,
830 /*XFillRectangle(dpy, tPtr->db, tPtr->stippledGC,*/
831 rect.x, rect.y, rect.width, rect.height);
835 if (!tPtr->flags.monoFont && tb->underlined) {
836 XDrawLine(dpy, tPtr->db, WMColorGC(tb->color),
837 tb->sections[0].x - tPtr->hpos,
838 tb->sections[0].y + h - tPtr->vpos,
839 tb->sections[0].x + tb->sections[0].w - tPtr->hpos,
840 tb->sections[0].y + h - tPtr->vpos);
847 _copy_area:
848 if (tPtr->flags.editable && tPtr->flags.cursorShown
849 && tPtr->cursor.x != -23 && tPtr->flags.focused) {
850 int y = tPtr->cursor.y - tPtr->vpos;
851 XDrawLine(dpy, tPtr->db, WMColorGC(tPtr->fgColor),
852 tPtr->cursor.x, y,
853 tPtr->cursor.x, y + tPtr->cursor.h);
856 XCopyArea(dpy, tPtr->db, win, WMColorGC(tPtr->bgColor), 0, 0,
857 tPtr->visible.w, tPtr->visible.h,
858 tPtr->visible.x, tPtr->visible.y);
860 W_DrawRelief(scr, win, 0, 0,
861 tPtr->view->size.width, tPtr->view->size.height,
862 tPtr->flags.relief);
864 if (tPtr->ruler && tPtr->flags.rulerShown)
865 XDrawLine(dpy, win, WMColorGC(tPtr->fgColor),
866 2, 42, tPtr->view->size.width-4, 42);
870 static void
871 mouseOverObject(Text *tPtr, int x, int y)
873 TextBlock *tb;
874 Bool result = False;
876 x -= tPtr->visible.x;
877 x += tPtr->hpos;
878 y -= tPtr->visible.y;
879 y += tPtr->vpos;
881 if(tPtr->flags.ownsSelection) {
882 if(tPtr->sel.x <= x
883 && tPtr->sel.y <= y
884 && tPtr->sel.x + tPtr->sel.w >= x
885 && tPtr->sel.y + tPtr->sel.h >= y) {
886 tPtr->flags.isOverGraphic = 1;
887 result = True;
892 if(!result) {
893 int j, c = WMGetArrayItemCount(tPtr->gfxItems);
895 if (c<1)
896 tPtr->flags.isOverGraphic = 0;
899 for(j=0; j<c; j++) {
900 tb = (TextBlock *) WMGetFromArray(tPtr->gfxItems, j);
902 if(!tb || !tb->sections) {
903 tPtr->flags.isOverGraphic = 0;
904 return;
907 if(!tb->object) {
908 if(tb->sections[0].x <= x
909 && tb->sections[0].y <= y
910 && tb->sections[0].x + tb->sections[0].w >= x
911 && tb->sections[0].y + tb->d.pixmap->height >= y ) {
912 tPtr->flags.isOverGraphic = 3;
913 result = True;
914 break;
922 if(!result)
923 tPtr->flags.isOverGraphic = 0;
926 tPtr->view->attribs.cursor = (result?
927 tPtr->view->screen->defaultCursor
928 : tPtr->view->screen->textCursor);
930 XSetWindowAttributes attribs;
931 attribs.cursor = tPtr->view->attribs.cursor;
932 XChangeWindowAttributes(tPtr->view->screen->display,
933 tPtr->view->window, CWCursor,
934 &attribs);
938 #if DO_BLINK
940 static void
941 blinkCursor(void *data)
943 Text *tPtr = (Text*)data;
945 if (tPtr->flags.cursorShown) {
946 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY,
947 blinkCursor, data);
948 } else {
949 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
950 blinkCursor, data);
952 paintText(tPtr);
953 tPtr->flags.cursorShown = !tPtr->flags.cursorShown;
955 #endif
957 static void
958 updateCursorPosition(Text *tPtr)
960 TextBlock *tb = NULL;
961 int x, y, h, s;
963 if(tPtr->flags.needsLayOut)
964 layOutDocument(tPtr);
966 if (! (tb = tPtr->currentTextBlock)) {
967 if (! (tb = tPtr->firstTextBlock)) {
968 WMFont *font = tPtr->dFont;
969 tPtr->tpos = 0;
970 tPtr->cursor.h = font->height + abs(font->height-font->y);
972 tPtr->cursor.y = 2;
973 tPtr->cursor.x = 2;
974 return;
979 if(tb->blank) {
980 tPtr->tpos = 0;
981 y = tb->sections[0].y;
982 h = tb->sections[0].h;
983 x = tb->sections[0].x;
985 } else if(tb->graphic) {
986 y = tb->sections[0].y;
987 h = tb->sections[0].h;
988 x = tb->sections[0].x;
989 if(tPtr->tpos == 1)
990 x += tb->sections[0].w;
992 } else {
993 if(tPtr->tpos > tb->used)
994 tPtr->tpos = tb->used;
996 for(s=0; s<tb->nsections-1; s++) {
998 if(tPtr->tpos >= tb->sections[s].begin
999 && tPtr->tpos <= tb->sections[s].end)
1000 break;
1003 y = tb->sections[s]._y;
1004 h = tb->sections[s].h;
1005 x = tb->sections[s].x + WMWidthOfString(
1006 (tPtr->flags.monoFont?tPtr->dFont:tb->d.font),
1007 &tb->text[tb->sections[s].begin],
1008 tPtr->tpos - tb->sections[s].begin);
1011 tPtr->cursor.y = y;
1012 tPtr->cursor.h = h;
1013 tPtr->cursor.x = x;
1016 /* scroll the bars if the cursor is not visible */
1017 if(tPtr->flags.editable && tPtr->cursor.x != -23) {
1018 if(tPtr->cursor.y+tPtr->cursor.h
1019 > tPtr->vpos+tPtr->visible.y+tPtr->visible.h) {
1020 tPtr->vpos +=
1021 (tPtr->cursor.y+tPtr->cursor.h+10
1022 - (tPtr->vpos+tPtr->visible.y+tPtr->visible.h));
1023 } else if(tPtr->cursor.y < tPtr->vpos+tPtr->visible.y) {
1024 tPtr->vpos -= (tPtr->vpos+tPtr->visible.y-tPtr->cursor.y);
1029 updateScrollers(tPtr);
1033 static void
1034 cursorToTextPosition(Text *tPtr, int x, int y)
1036 TextBlock *tb = NULL;
1037 int done=False, s, pos, len, _w, _y, dir=1; /* 1 == "down" */
1038 char *text;
1040 if(tPtr->flags.needsLayOut)
1041 layOutDocument(tPtr);
1043 y += (tPtr->vpos - tPtr->visible.y);
1044 if (y<0)
1045 y = 0;
1047 x -= (tPtr->visible.x - 2);
1048 if (x<0)
1049 x=0;
1051 /* clicked is relative to document, not window... */
1052 tPtr->clicked.x = x;
1053 tPtr->clicked.y = y;
1055 if (! (tb = tPtr->currentTextBlock)) {
1056 if (! (tb = tPtr->firstTextBlock)) {
1057 WMFont *font = tPtr->dFont;
1058 tPtr->tpos = 0;
1059 tPtr->cursor.h = font->height + abs(font->height-font->y);
1060 tPtr->cursor.y = 2;
1061 tPtr->cursor.x = 2;
1062 return;
1066 /* first, which direction? Most likely, newly clicked
1067 position will be close to previous */
1068 if(!updateStartForCurrentTextBlock(tPtr, x, y, &dir, tb))
1069 return;
1072 s = (dir? 0 : tb->nsections-1);
1073 if ( y >= tb->sections[s]._y
1074 && y <= tb->sections[s]._y + tb->sections[s].h) {
1075 goto _doneV;
1078 /* get the first (or last) section of the TextBlock that
1079 lies about the vertical click point */
1080 done = False;
1081 while (!done && tb) {
1083 if (tPtr->flags.monoFont && tb->graphic) {
1084 if( (dir?tb->next:tb->prior))
1085 tb = (dir?tb->next:tb->prior);
1086 continue;
1089 s = (dir? 0 : tb->nsections-1);
1090 while (!done && (dir? (s<tb->nsections) : (s>=0) )) {
1092 if ( (dir? (y <= tb->sections[s]._y + tb->sections[s].h) :
1093 ( y >= tb->sections[s]._y ) ) ) {
1094 done = True;
1095 } else {
1096 dir? s++ : s--;
1100 if (!done) {
1101 if ( (dir? tb->next : tb->prior)) {
1102 tb = (dir ? tb->next : tb->prior);
1103 } else {
1104 pos = tb->used;
1105 break; /* goto _doneH; */
1111 if (s<0 || s>=tb->nsections) {
1112 s = (dir? tb->nsections-1 : 0);
1115 _doneV:
1116 /* we have the line, which TextBlock on that line is it? */
1117 pos = (dir?0:tb->sections[s].begin);
1118 if (tPtr->flags.monoFont && tb->graphic) {
1119 TextBlock *hold = tb;
1120 tb = getFirstNonGraphicBlockFor(hold, dir);
1122 if(!tb) {
1123 tPtr->tpos = 0;
1124 tb = hold;
1125 s = 0;
1126 goto _doNothing;
1131 if(tb->blank)
1132 _w = 0;
1134 _y = tb->sections[s]._y;
1136 while (tb) {
1138 if (tPtr->flags.monoFont && tb->graphic) {
1139 tb = (dir ? tb->next : tb->prior);
1140 continue;
1143 if (dir) {
1144 if (tb->graphic) {
1145 if(tb->object)
1146 _w = WMWidgetWidth(tb->d.widget)-5;
1147 else
1148 _w = tb->d.pixmap->width-5;
1150 if (tb->sections[0].x + _w >= x)
1151 break;
1152 } else {
1153 text = &(tb->text[tb->sections[s].begin]);
1154 len = tb->sections[s].end - tb->sections[s].begin;
1155 _w = WMWidthOfString(tb->d.font, text, len);
1156 if (tb->sections[s].x + _w >= x)
1157 break;
1160 } else {
1161 if (tb->sections[s].x <= x)
1162 break;
1165 if ((dir? tb->next : tb->prior)) {
1166 TextBlock *nxt = (dir? tb->next : tb->prior);
1167 if (tPtr->flags.monoFont && nxt->graphic) {
1168 nxt = getFirstNonGraphicBlockFor(nxt, dir);
1169 if (!nxt) {
1170 pos = (dir?0:tb->sections[s].begin);
1171 tPtr->cursor.x = tb->sections[s].x;
1172 goto _doneH;
1176 if (_y != nxt->sections[dir?0:nxt->nsections-1]._y) {
1177 /* this must be the last/first on this line. stop */
1178 pos = (dir? tb->sections[s].end : 0);
1179 tPtr->cursor.x = tb->sections[s].x;
1180 if (!tb->blank) {
1181 if (tb->graphic) {
1182 if(tb->object)
1183 tPtr->cursor.x += WMWidgetWidth(tb->d.widget);
1184 else
1185 tPtr->cursor.x += tb->d.pixmap->width;
1186 } else if (pos > tb->sections[s].begin) {
1187 tPtr->cursor.x +=
1188 WMWidthOfString(tb->d.font,
1189 &(tb->text[tb->sections[s].begin]),
1190 pos - tb->sections[s].begin);
1193 goto _doneH;
1197 if ( (dir? tb->next : tb->prior)) {
1198 tb = (dir ? tb->next : tb->prior);
1199 } else {
1200 done = True;
1201 break;
1204 if (tb)
1205 s = (dir? 0 : tb->nsections-1);
1208 /* we have said TextBlock, now where within it? */
1209 if (tb) {
1210 if(tb->graphic) {
1211 int gw = (tb->object ?
1212 WMWidgetWidth(tb->d.widget) : tb->d.pixmap->width);
1214 tPtr->cursor.x = tb->sections[0].x;
1216 if(x > tPtr->cursor.x + gw/2) {
1217 pos = 1;
1218 tPtr->cursor.x += gw;
1219 } else {
1220 printf("first %d\n", tb->first);
1221 if(tb->prior) {
1222 if(tb->prior->graphic) pos = 1;
1223 else pos = tb->prior->used;
1224 tb = tb->prior;
1225 } else pos = 0;
1229 s = 0;
1230 goto _doneH;
1232 } else {
1233 WMFont *f = tb->d.font;
1234 len = tb->sections[s].end - tb->sections[s].begin;
1235 text = &(tb->text[tb->sections[s].begin]);
1237 _w = x - tb->sections[s].x;
1238 pos = 0;
1240 while (pos<len && WMWidthOfString(f, text, pos+1) < _w)
1241 pos++;
1243 tPtr->cursor.x = tb->sections[s].x +
1244 (pos? WMWidthOfString(f, text, pos) : 0);
1246 pos += tb->sections[s].begin;
1250 _doneH:
1251 if(tb->graphic) {
1252 tPtr->tpos = (pos<=1)? pos : 0;
1253 } else {
1254 tPtr->tpos = (pos<tb->used)? pos : tb->used;
1256 _doNothing:
1257 if (!tb)
1258 printf("...for this app will surely crash :-)\n");
1260 tPtr->currentTextBlock = tb;
1261 tPtr->cursor.h = tb->sections[s].h;
1262 tPtr->cursor.y = tb->sections[s]._y;
1264 /* scroll the bars if the cursor is not visible */
1265 if(tPtr->flags.editable && tPtr->cursor.x != -23) {
1266 if(tPtr->cursor.y+tPtr->cursor.h
1267 > tPtr->vpos+tPtr->visible.y+tPtr->visible.h) {
1268 tPtr->vpos +=
1269 (tPtr->cursor.y+tPtr->cursor.h+10
1270 - (tPtr->vpos+tPtr->visible.y+tPtr->visible.h));
1271 updateScrollers(tPtr);
1272 } else if(tPtr->cursor.y < tPtr->vpos+tPtr->visible.y) {
1273 tPtr->vpos -= (tPtr->vpos+tPtr->visible.y-tPtr->cursor.y);
1274 updateScrollers(tPtr);
1282 static void
1283 updateScrollers(Text *tPtr)
1286 if (tPtr->flags.frozen)
1287 return;
1289 if (tPtr->vS) {
1290 if (tPtr->docHeight <= tPtr->visible.h) {
1291 WMSetScrollerParameters(tPtr->vS, 0, 1);
1292 tPtr->vpos = 0;
1293 } else {
1294 float hmax = (float)(tPtr->docHeight);
1295 WMSetScrollerParameters(tPtr->vS,
1296 ((float)tPtr->vpos)/(hmax - (float)tPtr->visible.h),
1297 (float)tPtr->visible.h/hmax);
1299 } else tPtr->vpos = 0;
1301 if (tPtr->hS) {
1302 if (tPtr->docWidth <= tPtr->visible.w) {
1303 WMSetScrollerParameters(tPtr->hS, 0, 1);
1304 tPtr->hpos = 0;
1305 } else {
1306 float wmax = (float)(tPtr->docWidth);
1307 WMSetScrollerParameters(tPtr->hS,
1308 ((float)tPtr->hpos)/(wmax - (float)tPtr->visible.w),
1309 (float)tPtr->visible.w/wmax);
1311 } else tPtr->hpos = 0;
1314 static void
1315 scrollersCallBack(WMWidget *w, void *self)
1317 Text *tPtr = (Text *)self;
1318 Bool scroll = False;
1319 int which;
1321 if (!tPtr->view->flags.realized || tPtr->flags.frozen)
1322 return;
1324 if (w == tPtr->vS) {
1325 int height;
1326 height = tPtr->visible.h;
1328 which = WMGetScrollerHitPart(tPtr->vS);
1329 switch(which) {
1331 case WSDecrementLine:
1332 if (tPtr->vpos > 0) {
1333 if (tPtr->vpos>16) tPtr->vpos-=16;
1334 else tPtr->vpos=0;
1335 scroll=True;
1337 break;
1339 case WSIncrementLine: {
1340 int limit = tPtr->docHeight - height;
1341 if (tPtr->vpos < limit) {
1342 if (tPtr->vpos<limit-16) tPtr->vpos+=16;
1343 else tPtr->vpos=limit;
1344 scroll = True;
1347 break;
1349 case WSDecrementPage:
1350 if(((int)tPtr->vpos - (int)height) >= 0)
1351 tPtr->vpos -= height;
1352 else
1353 tPtr->vpos = 0;
1355 scroll = True;
1356 break;
1358 case WSIncrementPage:
1359 tPtr->vpos += height;
1360 if (tPtr->vpos > (tPtr->docHeight - height))
1361 tPtr->vpos = tPtr->docHeight - height;
1362 scroll = True;
1363 break;
1366 case WSKnob:
1367 tPtr->vpos = WMGetScrollerValue(tPtr->vS)
1368 * (float)(tPtr->docHeight - height);
1369 scroll = True;
1370 break;
1372 case WSKnobSlot:
1373 case WSNoPart:
1374 break;
1376 scroll = (tPtr->vpos != tPtr->prevVpos);
1377 tPtr->prevVpos = tPtr->vpos;
1381 if (w == tPtr->hS) {
1382 int width = tPtr->visible.w;
1384 which = WMGetScrollerHitPart(tPtr->hS);
1385 switch(which) {
1387 case WSDecrementLine:
1388 if (tPtr->hpos > 0) {
1389 if (tPtr->hpos>16) tPtr->hpos-=16;
1390 else tPtr->hpos=0;
1391 scroll=True;
1392 }break;
1394 case WSIncrementLine: {
1395 int limit = tPtr->docWidth - width;
1396 if (tPtr->hpos < limit) {
1397 if (tPtr->hpos<limit-16) tPtr->hpos+=16;
1398 else tPtr->hpos=limit;
1399 scroll = True;
1401 }break;
1403 case WSDecrementPage:
1404 if(((int)tPtr->hpos - (int)width) >= 0)
1405 tPtr->hpos -= width;
1406 else
1407 tPtr->hpos = 0;
1409 scroll = True;
1410 break;
1412 case WSIncrementPage:
1413 tPtr->hpos += width;
1414 if (tPtr->hpos > (tPtr->docWidth - width))
1415 tPtr->hpos = tPtr->docWidth - width;
1416 scroll = True;
1417 break;
1420 case WSKnob:
1421 tPtr->hpos = WMGetScrollerValue(tPtr->hS)
1422 * (float)(tPtr->docWidth - width);
1423 scroll = True;
1424 break;
1426 case WSKnobSlot:
1427 case WSNoPart:
1428 break;
1430 scroll = (tPtr->hpos != tPtr->prevHpos);
1431 tPtr->prevHpos = tPtr->hpos;
1434 if (scroll) {
1435 updateScrollers(tPtr);
1436 paintText(tPtr);
1442 typedef struct {
1443 TextBlock *tb;
1444 unsigned short begin, end; /* what part of the text block */
1445 } myLineItems;
1448 static int
1449 layOutLine(Text *tPtr, myLineItems *items, int nitems, int x, int y)
1451 int i, j=0, lw = 0, line_height=0, max_d=0, len, n;
1452 WMFont *font;
1453 char *text;
1454 TextBlock *tb, *tbsame=NULL;
1456 if(!items || nitems == 0)
1457 return 0;
1459 for(i=0; i<nitems; i++) {
1460 tb = items[i].tb;
1462 if (tb->graphic) {
1463 if (!tPtr->flags.monoFont) {
1464 if(tb->object) {
1465 WMWidget *wdt = tb->d.widget;
1466 line_height = WMAX(line_height, WMWidgetHeight(wdt));
1467 if (tPtr->flags.alignment != WALeft)
1468 lw += WMWidgetWidth(wdt);
1469 } else {
1470 line_height = WMAX(line_height,
1471 tb->d.pixmap->height + max_d);
1472 if (tPtr->flags.alignment != WALeft)
1473 lw += tb->d.pixmap->width;
1477 } else {
1478 font = (tPtr->flags.monoFont)?tPtr->dFont : tb->d.font;
1479 /*max_d = WMAX(max_d, abs(font->height-font->y));*/
1480 max_d = 2;
1481 line_height = WMAX(line_height, font->height + max_d);
1482 text = &(tb->text[items[i].begin]);
1483 len = items[i].end - items[i].begin;
1484 if (tPtr->flags.alignment != WALeft)
1485 lw += WMWidthOfString(font, text, len);
1489 if (tPtr->flags.alignment == WARight) {
1490 j = tPtr->visible.w - lw;
1491 } else if (tPtr->flags.alignment == WACenter) {
1492 j = (int) ((float)(tPtr->visible.w - lw))/2.0;
1495 for(i=0; i<nitems; i++) {
1496 tb = items[i].tb;
1498 if (tbsame == tb) { /* extend it, since it's on same line */
1499 tb->sections[tb->nsections-1].end = items[i].end;
1500 n = tb->nsections-1;
1501 } else {
1502 tb->sections = wrealloc(tb->sections,
1503 (++tb->nsections)*sizeof(Section));
1504 n = tb->nsections-1;
1505 tb->sections[n]._y = y + max_d;
1506 tb->sections[n].max_d = max_d;
1507 tb->sections[n].x = x+j;
1508 tb->sections[n].h = line_height;
1509 tb->sections[n].begin = items[i].begin;
1510 tb->sections[n].end = items[i].end;
1513 tb->sections[n].last = (i+1 == nitems);
1515 if (tb->graphic) {
1516 if (!tPtr->flags.monoFont) {
1517 if(tb->object) {
1518 WMWidget *wdt = tb->d.widget;
1519 tb->sections[n].y = max_d + y
1520 + line_height - WMWidgetHeight(wdt);
1521 tb->sections[n].w = WMWidgetWidth(wdt);
1522 } else {
1523 tb->sections[n].y = y + line_height
1524 + max_d - tb->d.pixmap->height;
1525 tb->sections[n].w = tb->d.pixmap->width;
1527 x += tb->sections[n].w;
1529 } else {
1530 font = (tPtr->flags.monoFont)? tPtr->dFont : tb->d.font;
1531 len = items[i].end - items[i].begin;
1532 text = &(tb->text[items[i].begin]);
1534 tb->sections[n].y = y+line_height-font->y;
1535 tb->sections[n].w =
1536 WMWidthOfString(font,
1537 &(tb->text[tb->sections[n].begin]),
1538 tb->sections[n].end - tb->sections[n].begin);
1540 x += WMWidthOfString(font, text, len);
1543 tbsame = tb;
1546 return line_height;
1551 static void
1552 layOutDocument(Text *tPtr)
1554 TextBlock *tb;
1555 myLineItems *items = NULL;
1556 unsigned int itemsSize=0, nitems=0, begin, end;
1557 WMFont *font;
1558 unsigned int x, y=0, lw = 0, width=0, bmargin;
1559 char *start=NULL, *mark=NULL;
1561 if ( tPtr->flags.frozen || (!(tb = tPtr->firstTextBlock)) )
1562 return;
1564 assert(tPtr->visible.w > 20);
1566 tPtr->docWidth = tPtr->visible.w;
1567 x = tPtr->margins[tb->marginN].first;
1568 bmargin = tPtr->margins[tb->marginN].body;
1570 /* only partial layOut needed: re-Lay only affected textblocks */
1571 if (tPtr->flags.laidOut) {
1572 tb = tPtr->currentTextBlock;
1574 /* search backwards for textblocks on same line */
1575 while (tb->prior) {
1576 if (!tb->sections || tb->nsections<1) {
1577 tb = tPtr->firstTextBlock;
1578 tPtr->flags.laidOut = False;
1579 y = 0;
1580 goto _layOut;
1583 if(!tb->prior->sections || tb->prior->nsections<1) {
1584 tb = tPtr->firstTextBlock;
1585 tPtr->flags.laidOut = False;
1586 y = 0;
1587 goto _layOut;
1590 if (tb->sections[0]._y !=
1591 tb->prior->sections[tb->prior->nsections-1]._y) {
1592 break;
1594 tb = tb->prior;
1597 if(tb->prior && tb->prior->sections && tb->prior->nsections>0) {
1598 y = tb->prior->sections[tb->prior->nsections-1]._y +
1599 tb->prior->sections[tb->prior->nsections-1].h -
1600 tb->prior->sections[tb->prior->nsections-1].max_d;
1601 } else {
1602 y = 0;
1606 _layOut:
1607 while (tb) {
1609 if (tb->sections && tb->nsections>0) {
1610 wfree(tb->sections);
1611 tb->sections = NULL;
1612 tb->nsections = 0;
1615 if (tb->first && tb->blank && tb->next && !tb->next->first) {
1616 TextBlock *next = tb->next;
1617 tPtr->currentTextBlock = tb;
1618 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1619 tb = next;
1620 tb->first = True;
1621 continue;
1624 if (tb->first && tb != tPtr->firstTextBlock) {
1625 y += layOutLine(tPtr, items, nitems, x, y);
1626 x = tPtr->margins[tb->marginN].first;
1627 bmargin = tPtr->margins[tb->marginN].body;
1628 nitems = 0;
1629 lw = 0;
1632 if (tb->graphic) {
1633 if (!tPtr->flags.monoFont) {
1634 if(tb->object)
1635 width = WMWidgetWidth(tb->d.widget);
1636 else
1637 width = tb->d.pixmap->width;
1639 if (width > tPtr->docWidth)
1640 tPtr->docWidth = width;
1642 lw += width;
1643 if (lw >= tPtr->visible.w - x ) {
1644 y += layOutLine(tPtr, items, nitems, x, y);
1645 nitems = 0;
1646 x = bmargin;
1647 lw = width;
1650 if(nitems + 1> itemsSize) {
1651 items = wrealloc(items,
1652 (++itemsSize)*sizeof(myLineItems));
1655 items[nitems].tb = tb;
1656 items[nitems].begin = 0;
1657 items[nitems].end = 0;
1658 nitems++;
1661 } else if ((start = tb->text)) {
1662 begin = end = 0;
1663 font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
1665 while (start) {
1666 mark = strchr(start, ' ');
1667 if (mark) {
1668 end += (int)(mark-start)+1;
1669 start = mark+1;
1670 } else {
1671 end += strlen(start);
1672 start = mark;
1675 if (end > tb->used)
1676 end = tb->used;
1678 if (end-begin > 0) {
1680 width = WMWidthOfString(font,
1681 &tb->text[begin], end-begin);
1683 /* if it won't fit, char wrap it */
1684 if (width >= tPtr->visible.w) {
1685 char *t = &tb->text[begin];
1686 int l=end-begin, i=0;
1687 do {
1688 width = WMWidthOfString(font, t, ++i);
1689 } while (width < tPtr->visible.w && i < l);
1690 if(i>2) i--;
1691 end = begin+i;
1692 start = &tb->text[end];
1695 lw += width;
1698 if (lw >= tPtr->visible.w - x) {
1699 y += layOutLine(tPtr, items, nitems, x, y);
1700 lw = width;
1701 x = bmargin;
1702 nitems = 0;
1705 if(nitems + 1 > itemsSize) {
1706 items = wrealloc(items,
1707 (++itemsSize)*sizeof(myLineItems));
1710 items[nitems].tb = tb;
1711 items[nitems].begin = begin;
1712 items[nitems].end = end;
1713 nitems++;
1715 begin = end;
1720 /* not yet fully ready. but is already VERY FAST for a 3Mbyte file ;-) */
1721 if(0&&tPtr->flags.laidOut
1722 && tb->next && tb->next->sections && tb->next->nsections>0
1723 && (tPtr->vpos + tPtr->visible.h
1724 < tb->next->sections[0]._y)) {
1725 if(tPtr->lastTextBlock->sections
1726 && tPtr->lastTextBlock->nsections > 0 ) {
1727 TextBlock *ltb = tPtr->lastTextBlock;
1728 int ly = ltb->sections[ltb->nsections-1]._y;
1729 int lh = ltb->sections[ltb->nsections-1].h;
1730 int ss, sd;
1732 lh += 1 + tPtr->visible.y + ltb->sections[ltb->nsections-1].max_d;
1733 printf("it's %d\n", tPtr->visible.y + ltb->sections[ltb->nsections-1].max_d);
1735 y += layOutLine(tPtr, items, nitems, x, y);
1736 ss= ly+lh-y;
1737 sd = tPtr->docHeight-y;
1739 printf("dif %d-%d: %d\n", ss, sd, ss-sd);
1740 y += tb->next->sections[0]._y-y;
1741 nitems = 0;
1742 printf("nitems%d\n", nitems);
1743 if(ss-sd!=0)
1744 y = tPtr->docHeight+ss-sd;
1746 break;
1747 } else {
1748 tPtr->flags.laidOut = False;
1752 tb = tb->next;
1756 if (nitems > 0)
1757 y += layOutLine(tPtr, items, nitems, x, y);
1759 if (tPtr->docHeight != y+10) {
1760 tPtr->docHeight = y+10;
1761 updateScrollers(tPtr);
1764 if(tPtr->docWidth > tPtr->visible.w && !tPtr->hS) {
1765 XEvent event;
1767 tPtr->flags.horizOnDemand = True;
1768 WMSetTextHasHorizontalScroller((WMText*)tPtr, True);
1769 event.type = Expose;
1770 handleEvents(&event, (void *)tPtr);
1772 } else if(tPtr->docWidth <= tPtr->visible.w
1773 && tPtr->hS && tPtr->flags.horizOnDemand ) {
1774 tPtr->flags.horizOnDemand = False;
1775 WMSetTextHasHorizontalScroller((WMText*)tPtr, False);
1778 tPtr->flags.laidOut = True;
1780 if(items && itemsSize > 0)
1781 wfree(items);
1786 static void
1787 textDidResize(W_ViewDelegate *self, WMView *view)
1789 Text *tPtr = (Text *)view->self;
1790 unsigned short w = tPtr->view->size.width;
1791 unsigned short h = tPtr->view->size.height;
1792 unsigned short rh = 0, vw = 0, rel;
1794 rel = (tPtr->flags.relief == WRFlat);
1796 if (tPtr->ruler && tPtr->flags.rulerShown) {
1797 WMMoveWidget(tPtr->ruler, 2, 2);
1798 WMResizeWidget(tPtr->ruler, w - 4, 40);
1799 rh = 40;
1802 if (tPtr->vS) {
1803 WMMoveWidget(tPtr->vS, 1 - (rel?1:0), rh + 1 - (rel?1:0));
1804 WMResizeWidget(tPtr->vS, 20, h - rh - 2 + (rel?2:0));
1805 vw = 20;
1806 WMSetRulerOffset(tPtr->ruler,22);
1807 } else WMSetRulerOffset(tPtr->ruler, 2);
1809 if (tPtr->hS) {
1810 if (tPtr->vS) {
1811 WMMoveWidget(tPtr->hS, vw, h - 21);
1812 WMResizeWidget(tPtr->hS, w - vw - 1, 20);
1813 } else {
1814 WMMoveWidget(tPtr->hS, vw+1, h - 21);
1815 WMResizeWidget(tPtr->hS, w - vw - 2, 20);
1819 tPtr->visible.x = (tPtr->vS)?24:4;
1820 tPtr->visible.y = (tPtr->ruler && tPtr->flags.rulerShown)?43:3;
1821 tPtr->visible.w = tPtr->view->size.width - tPtr->visible.x - 8;
1822 tPtr->visible.h = tPtr->view->size.height - tPtr->visible.y;
1823 tPtr->visible.h -= (tPtr->hS)?20:0;
1824 tPtr->margins[0].right = tPtr->visible.w;
1826 if (tPtr->view->flags.realized) {
1828 if (tPtr->db) {
1829 XFreePixmap(tPtr->view->screen->display, tPtr->db);
1830 tPtr->db = (Pixmap) NULL;
1833 if (tPtr->visible.w < 40)
1834 tPtr->visible.w = 40;
1835 if (tPtr->visible.h < 20)
1836 tPtr->visible.h = 20;
1838 if(!tPtr->db) {
1839 tPtr->db = XCreatePixmap(tPtr->view->screen->display,
1840 tPtr->view->window, tPtr->visible.w,
1841 tPtr->visible.h, tPtr->view->screen->depth);
1845 WMThawText(tPtr);
1848 W_ViewDelegate _TextViewDelegate =
1850 NULL,
1851 NULL,
1852 textDidResize,
1853 NULL,
1856 #define TEXT_BUFFER_INCR 8
1857 #define reqBlockSize(requested) (requested + TEXT_BUFFER_INCR)
1859 static void
1860 clearText(Text *tPtr)
1862 tPtr->vpos = tPtr->hpos = 0;
1863 tPtr->docHeight = tPtr->docWidth = 0;
1864 tPtr->cursor.x = -23;
1866 if (!tPtr->firstTextBlock)
1867 return;
1869 while (tPtr->currentTextBlock)
1870 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1872 tPtr->firstTextBlock = NULL;
1873 tPtr->currentTextBlock = NULL;
1874 tPtr->lastTextBlock = NULL;
1875 WMEmptyArray(tPtr->gfxItems);
1878 /* possibly remove a single character from the currentTextBlock,
1879 or if there's a selection, remove it...
1880 note that Delete and Backspace are treated differently */
1881 static void
1882 deleteTextInteractively(Text *tPtr, KeySym ksym)
1884 TextBlock *tb;
1885 Bool back = (Bool) (ksym == XK_BackSpace);
1886 Bool done = 1, wasFirst = 0;
1888 if (!tPtr->flags.editable)
1889 return;
1891 if ( !(tb = tPtr->currentTextBlock) )
1892 return;
1894 if (tPtr->flags.ownsSelection) {
1895 if(removeSelection(tPtr))
1896 layOutDocument(tPtr);
1897 return;
1900 wasFirst = tb->first;
1901 if (back && tPtr->tpos < 1) {
1902 if (tb->prior) {
1903 if(tb->prior->blank) {
1904 tPtr->currentTextBlock = tb->prior;
1905 WMRemoveTextBlock(tPtr);
1906 tPtr->currentTextBlock = tb;
1907 tb->first = True;
1908 layOutDocument(tPtr);
1909 return;
1910 } else {
1911 if(tb->blank) {
1912 TextBlock *prior = tb->prior;
1913 tPtr->currentTextBlock = tb;
1914 WMRemoveTextBlock(tPtr);
1915 tb = prior;
1916 } else {
1917 tb = tb->prior;
1920 if(tb->graphic)
1921 tPtr->tpos = 1;
1922 else
1923 tPtr->tpos = tb->used;
1925 tPtr->currentTextBlock = tb;
1926 done = 1;
1927 if(wasFirst) {
1928 if(tb->next)
1929 tb->next->first = False;
1930 layOutDocument(tPtr);
1931 return;
1937 if ( (tb->used > 0) && ((back?tPtr->tpos > 0:1))
1938 && (tPtr->tpos <= tb->used) && !tb->graphic) {
1939 if (back)
1940 tPtr->tpos--;
1941 memmove(&(tb->text[tPtr->tpos]),
1942 &(tb->text[tPtr->tpos + 1]), tb->used - tPtr->tpos);
1943 tb->used--;
1944 done = 0;
1947 /* if there are no characters left to back over in the textblock,
1948 but it still has characters to the right of the cursor: */
1949 if ( (back? (tPtr->tpos == 0 && !done) : ( tPtr->tpos >= tb->used))
1950 || tb->graphic) {
1952 /* no more chars, and it's marked as blank? */
1953 if(tb->blank) {
1954 TextBlock *sibling = (back? tb->prior : tb->next);
1956 if(tb->used == 0 || tb->graphic)
1957 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1959 if (sibling) {
1960 tPtr->currentTextBlock = sibling;
1961 if(tb->graphic)
1962 tPtr->tpos = (back? 1 : 0);
1963 else
1964 tPtr->tpos = (back? sibling->used : 0);
1966 /* no more chars, so mark it as blank */
1967 } else if(tb->used == 0) {
1968 tb->blank = 1;
1969 } else if(tb->graphic) {
1970 Bool hasNext = (Bool)(tb->next);
1972 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1973 if(hasNext) {
1974 tPtr->tpos = 0;
1975 } else if(tPtr->currentTextBlock) {
1976 tPtr->tpos = (tPtr->currentTextBlock->graphic?
1977 1 : tPtr->currentTextBlock->used);
1979 } else printf("DEBUG: unaccounted for... catch this!\n");
1982 layOutDocument(tPtr);
1986 static void
1987 insertTextInteractively(Text *tPtr, char *text, int len)
1989 TextBlock *tb;
1990 char *newline = NULL;
1992 if (!tPtr->flags.editable) {
1993 return;
1996 if (len < 1 || !text)
1997 return;
2000 if(tPtr->flags.ignoreNewLine && *text == '\n' && len == 1)
2001 return;
2004 if (tPtr->flags.ownsSelection)
2005 removeSelection(tPtr);
2008 if (tPtr->flags.ignoreNewLine) {
2009 int i;
2010 for(i=0; i<len; i++) {
2011 if (text[i] == '\n')
2012 text[i] = ' ';
2016 tb = tPtr->currentTextBlock;
2017 if (!tb || tb->graphic) {
2018 tPtr->tpos = 0;
2019 WMAppendTextStream(tPtr, text);
2020 layOutDocument(tPtr);
2021 return;
2024 if ((newline = strchr(text, '\n'))) {
2025 int nlen = (int)(newline-text);
2026 int s = tb->used - tPtr->tpos;
2028 if (!tb->blank && nlen>0) {
2029 char *save=NULL;
2031 if (s > 0) {
2032 save = wmalloc(s);
2033 memcpy(save, &tb->text[tPtr->tpos], s);
2034 tb->used -= (tb->used - tPtr->tpos);
2036 insertTextInteractively(tPtr, text, nlen);
2037 newline++;
2038 WMAppendTextStream(tPtr, newline);
2039 if (s>0) {
2040 insertTextInteractively(tPtr, save, s);
2041 wfree(save);
2043 } else {
2044 if (tPtr->tpos>0 && tPtr->tpos < tb->used
2045 && !tb->graphic && tb->text) {
2047 unsigned short savePos = tPtr->tpos;
2048 void *ntb = WMCreateTextBlockWithText(
2049 tPtr, &tb->text[tPtr->tpos],
2050 tb->d.font, tb->color, True, tb->used - tPtr->tpos);
2052 if(tb->sections[0].end == tPtr->tpos)
2053 WMAppendTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
2054 NULL, tb->d.font, tb->color, True, 0));
2056 tb->used = savePos;
2057 WMAppendTextBlock(tPtr, ntb);
2058 tPtr->tpos = 0;
2060 } else if (tPtr->tpos == tb->used) {
2061 if(tPtr->flags.indentNewLine) {
2062 WMAppendTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
2063 " ", tb->d.font, tb->color, True, 4));
2064 tPtr->tpos = 4;
2065 } else {
2066 WMAppendTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
2067 NULL, tb->d.font, tb->color, True, 0));
2068 tPtr->tpos = 0;
2070 } else if (tPtr->tpos == 0) {
2071 if(tPtr->flags.indentNewLine) {
2072 WMPrependTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
2073 " ", tb->d.font, tb->color, True, 4));
2074 } else {
2075 WMPrependTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
2076 NULL, tb->d.font, tb->color, True, 0));
2078 tPtr->tpos = 0;
2079 if(tPtr->currentTextBlock->next)
2080 tPtr->currentTextBlock = tPtr->currentTextBlock->next;
2083 } else {
2084 if (tb->used + len >= tb->allocated) {
2085 tb->allocated = reqBlockSize(tb->used+len);
2086 tb->text = wrealloc(tb->text, tb->allocated);
2089 if (tb->blank) {
2090 memcpy(tb->text, text, len);
2091 tb->used = len;
2092 tPtr->tpos = len;
2093 tb->text[tb->used] = 0;
2094 tb->blank = False;
2096 } else {
2097 memmove(&(tb->text[tPtr->tpos+len]), &tb->text[tPtr->tpos],
2098 tb->used-tPtr->tpos+1);
2099 memmove(&tb->text[tPtr->tpos], text, len);
2100 tb->used += len;
2101 tPtr->tpos += len;
2102 tb->text[tb->used] = 0;
2107 layOutDocument(tPtr);
2111 static void
2112 selectRegion(Text *tPtr, int x, int y)
2115 if (x < 0 || y < 0)
2116 return;
2118 y += (tPtr->flags.rulerShown? 40: 0);
2119 y += tPtr->vpos;
2120 if (y>10)
2121 y -= 10; /* the original offset */
2123 x -= tPtr->visible.x-2;
2124 if (x<0)
2125 x=0;
2127 tPtr->sel.x = WMAX(0, WMIN(tPtr->clicked.x, x));
2128 tPtr->sel.w = abs(tPtr->clicked.x - x);
2129 tPtr->sel.y = WMAX(0, WMIN(tPtr->clicked.y, y));
2130 tPtr->sel.h = abs(tPtr->clicked.y - y);
2132 tPtr->flags.ownsSelection = True;
2133 paintText(tPtr);
2137 static void
2138 releaseSelection(Text *tPtr)
2140 TextBlock *tb = tPtr->firstTextBlock;
2142 while(tb) {
2143 tb->selected = False;
2144 tb = tb->next;
2146 tPtr->flags.ownsSelection = False;
2147 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY,
2148 CurrentTime);
2150 paintText(tPtr);
2154 WMData*
2155 requestHandler(WMView *view, Atom selection, Atom target, void *cdata,
2156 Atom *type)
2158 Text *tPtr = view->self;
2159 Display *dpy = tPtr->view->screen->display;
2160 Atom _TARGETS;
2161 Atom TEXT = XInternAtom(dpy, "TEXT", False);
2162 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
2163 WMData *data = NULL;
2166 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
2167 char *text = WMGetTextSelectedStream(tPtr);
2169 if (text) {
2170 data = WMCreateDataWithBytes(text, strlen(text));
2171 WMSetDataFormat(data, TYPETEXT);
2173 *type = target;
2174 return data;
2175 } else printf("didn't get it\n");
2177 _TARGETS = XInternAtom(dpy, "TARGETS", False);
2178 if (target == _TARGETS) {
2179 Atom *ptr;
2181 ptr = wmalloc(4 * sizeof(Atom));
2182 ptr[0] = _TARGETS;
2183 ptr[1] = XA_STRING;
2184 ptr[2] = TEXT;
2185 ptr[3] = COMPOUND_TEXT;
2187 data = WMCreateDataWithBytes(ptr, 4*4);
2188 WMSetDataFormat(data, 32);
2190 *type = target;
2191 return data;
2194 return NULL;
2198 static void
2199 lostHandler(WMView *view, Atom selection, void *cdata)
2201 releaseSelection((WMText *)view->self);
2205 static WMSelectionProcs selectionHandler = {
2206 requestHandler, lostHandler, NULL
2210 static void
2211 ownershipObserver(void *observerData, WMNotification *notification)
2213 if (observerData != WMGetNotificationClientData(notification))
2214 lostHandler(WMWidgetView(observerData), XA_PRIMARY, NULL);
2218 static void
2219 autoSelectText(Text *tPtr, int clicks)
2221 int x, start;
2222 TextBlock *tb;
2223 char *mark = NULL, behind, ahead;
2225 if(!(tb = tPtr->currentTextBlock))
2226 return;
2228 if(clicks == 2) {
2231 switch(tb->text[tPtr->tpos]) {
2232 case ' ': return;
2234 case '<': case '>': behind = '<'; ahead = '>'; break;
2235 case '{': case '}': behind = '{'; ahead = '}'; break;
2236 case '[': case ']': behind = '['; ahead = ']'; break;
2238 default: behind = ahead = ' ';
2241 tPtr->sel.y = tPtr->cursor.y+5;
2242 tPtr->sel.h = 6;/*tPtr->cursor.h-10;*/
2244 if(tb->graphic) {
2245 tPtr->sel.x = tb->sections[0].x;
2246 tPtr->sel.w = tb->sections[0].w;
2247 } else {
2248 WMFont *font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
2250 start = tPtr->tpos;
2251 while(start > 0 && tb->text[start-1] != behind)
2252 start--;
2254 x = tPtr->cursor.x;
2255 if(tPtr->tpos > start){
2256 x -= WMWidthOfString(font, &tb->text[start],
2257 tPtr->tpos - start);
2259 tPtr->sel.x = (x<0?0:x)+1;
2261 if((mark = strchr(&tb->text[start], ahead))) {
2262 tPtr->sel.w = WMWidthOfString(font, &tb->text[start],
2263 (int)(mark - &tb->text[start]));
2264 } else if(tb->used > start) {
2265 tPtr->sel.w = WMWidthOfString(font, &tb->text[start],
2266 tb->used - start);
2270 } else if(clicks == 3) {
2271 TextBlock *cur = tb;
2273 while(tb && !tb->first) {
2274 tb = tb->prior;
2276 tPtr->sel.y = tb->sections[0]._y;
2278 tb = cur;
2279 while(tb->next && !tb->next->first) {
2280 tb = tb->next;
2282 tPtr->sel.h = tb->sections[tb->nsections-1]._y
2283 + 5 - tPtr->sel.y;
2285 tPtr->sel.x = 0;
2286 tPtr->sel.w = tPtr->docWidth;
2287 tPtr->clicked.x = 0; /* only for now, fix sel. code */
2290 if (!tPtr->flags.ownsSelection) {
2291 WMCreateSelectionHandler(tPtr->view,
2292 XA_PRIMARY, tPtr->lastClickTime, &selectionHandler, NULL);
2293 tPtr->flags.ownsSelection = True;
2295 paintText(tPtr);
2300 static void
2301 fontChanged(void *observerData, WMNotification *notification)
2303 WMText *tPtr = (WMText *) observerData;
2304 WMFont *font = (WMFont *)WMGetNotificationClientData(notification);
2305 printf("fontChanged\n");
2307 if(!tPtr || !font)
2308 return;
2310 if (tPtr->flags.ownsSelection)
2311 WMSetTextSelectionFont(tPtr, font);
2315 static void
2316 handleTextKeyPress(Text *tPtr, XEvent *event)
2318 char buffer[64];
2319 KeySym ksym;
2320 int control_pressed = False;
2321 TextBlock *tb = NULL;
2323 if (((XKeyEvent *) event)->state & ControlMask)
2324 control_pressed = True;
2325 buffer[XLookupString(&event->xkey, buffer, 63, &ksym, NULL)] = 0;
2327 switch(ksym) {
2329 case XK_Home:
2330 if((tPtr->currentTextBlock = tPtr->firstTextBlock))
2331 tPtr->tpos = 0;
2332 updateCursorPosition(tPtr);
2333 paintText(tPtr);
2334 break;
2336 case XK_End:
2337 if((tPtr->currentTextBlock = tPtr->lastTextBlock)) {
2338 if(tPtr->currentTextBlock->graphic)
2339 tPtr->tpos = 1;
2340 else
2341 tPtr->tpos = tPtr->currentTextBlock->used;
2343 updateCursorPosition(tPtr);
2344 paintText(tPtr);
2345 break;
2347 case XK_Left:
2348 if(!(tb = tPtr->currentTextBlock))
2349 break;
2350 if(tb->graphic)
2351 goto L_imaGFX;
2353 if(tPtr->tpos==0) {
2354 L_imaGFX:
2355 if(tb->prior) {
2356 tPtr->currentTextBlock = tb->prior;
2357 if(tPtr->currentTextBlock->graphic)
2358 tPtr->tpos = 1;
2359 else
2360 tPtr->tpos = tPtr->currentTextBlock->used;
2362 if(!tb->first && tPtr->tpos > 0)
2363 tPtr->tpos--;
2364 } else tPtr->tpos = 0;
2365 } else tPtr->tpos--;
2366 updateCursorPosition(tPtr);
2367 paintText(tPtr);
2368 break;
2370 case XK_Right:
2371 if(!(tb = tPtr->currentTextBlock))
2372 break;
2373 if(tb->graphic)
2374 goto R_imaGFX;
2375 if(tPtr->tpos == tb->used) {
2376 R_imaGFX:
2377 if(tb->next) {
2378 tPtr->currentTextBlock = tb->next;
2379 tPtr->tpos = 0;
2380 if(!tb->next->first && tb->next->used>0)
2381 tPtr->tpos++;
2382 } else {
2383 if(tb->graphic)
2384 tPtr->tpos = 1;
2385 else
2386 tPtr->tpos = tb->used;
2388 } else tPtr->tpos++;
2389 updateCursorPosition(tPtr);
2390 paintText(tPtr);
2391 break;
2393 case XK_Down:
2394 cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
2395 tPtr->clicked.y + tPtr->cursor.h - tPtr->vpos);
2396 paintText(tPtr);
2397 break;
2399 case XK_Up:
2400 cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
2401 tPtr->visible.y + tPtr->cursor.y - tPtr->vpos - 3);
2402 paintText(tPtr);
2403 break;
2405 case XK_BackSpace:
2406 case XK_Delete:
2407 #ifdef XK_KP_Delete
2408 case XK_KP_Delete:
2409 #endif
2410 deleteTextInteractively(tPtr, ksym);
2411 updateCursorPosition(tPtr);
2412 paintText(tPtr);
2413 break;
2415 case XK_Control_R :
2416 case XK_Control_L :
2417 control_pressed = True;
2418 break;
2420 case XK_Tab:
2421 insertTextInteractively(tPtr, " ", 4);
2422 updateCursorPosition(tPtr);
2423 paintText(tPtr);
2424 break;
2426 case XK_Return:
2427 *buffer = '\n';
2428 default:
2429 if (*buffer != 0 && !control_pressed) {
2430 insertTextInteractively(tPtr, buffer, strlen(buffer));
2431 updateCursorPosition(tPtr);
2432 paintText(tPtr);
2434 } else if (control_pressed && ksym==XK_r) {
2435 Bool i = !tPtr->flags.rulerShown;
2436 WMShowTextRuler(tPtr, i);
2437 tPtr->flags.rulerShown = i;
2438 } else if (control_pressed && *buffer == '\a') {
2439 XBell(tPtr->view->screen->display, 0);
2440 } else {
2441 WMRelayToNextResponder(tPtr->view, event);
2445 if (!control_pressed && tPtr->flags.ownsSelection) {
2446 releaseSelection(tPtr);
2451 static void
2452 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
2453 void *cdata, WMData *data)
2455 Text *tPtr = (Text *)view->self;
2456 char *text;
2458 tPtr->flags.waitingForSelection = 0;
2460 if (data) {
2461 text = (char*)WMDataBytes(data);
2463 if (tPtr->parser) {
2464 (tPtr->parser) (tPtr, (void *) text);
2465 layOutDocument(tPtr);
2466 } else insertTextInteractively(tPtr, text, strlen(text));
2467 updateCursorPosition(tPtr);
2468 paintText(tPtr);
2470 } else {
2471 int n;
2473 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
2475 if (text) {
2476 text[n] = 0;
2477 if (tPtr->parser) {
2478 (tPtr->parser) (tPtr, (void *) text);
2479 layOutDocument(tPtr);
2480 } else insertTextInteractively(tPtr, text, n);
2481 updateCursorPosition(tPtr);
2482 paintText(tPtr);
2484 XFree(text);
2492 static void
2493 handleActionEvents(XEvent *event, void *data)
2495 Text *tPtr = (Text *)data;
2496 Display *dpy = event->xany.display;
2497 KeySym ksym;
2500 switch (event->type) {
2501 case KeyPress:
2502 ksym = XLookupKeysym((XKeyEvent*)event, 0);
2503 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
2504 tPtr->flags.extendSelection = True;
2505 return;
2508 if (tPtr->flags.focused) {
2509 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
2510 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
2511 GrabModeAsync, GrabModeAsync, None,
2512 tPtr->view->screen->invisibleCursor, CurrentTime);
2513 tPtr->flags.pointerGrabbed = True;
2514 handleTextKeyPress(tPtr, event);
2516 } break;
2518 case KeyRelease:
2519 ksym = XLookupKeysym((XKeyEvent*)event, 0);
2520 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
2521 tPtr->flags.extendSelection = False;
2522 return;
2523 /* end modify flag so selection can be extended */
2525 break;
2528 case MotionNotify:
2530 if (tPtr->flags.pointerGrabbed) {
2531 tPtr->flags.pointerGrabbed = False;
2532 XUngrabPointer(dpy, CurrentTime);
2535 if(tPtr->flags.waitingForSelection)
2536 break;
2538 if ((event->xmotion.state & Button1Mask)) {
2540 if (WMIsDraggingFromView(tPtr->view)) {
2541 WMDragImageFromView(tPtr->view, event);
2542 break;
2545 if (!tPtr->flags.ownsSelection) {
2546 WMCreateSelectionHandler(tPtr->view,
2547 XA_PRIMARY, event->xbutton.time,
2548 &selectionHandler, NULL);
2549 tPtr->flags.ownsSelection = True;
2551 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
2552 break;
2555 mouseOverObject(tPtr, event->xmotion.x, event->xmotion.y);
2556 break;
2559 case ButtonPress:
2561 if (tPtr->flags.pointerGrabbed) {
2562 tPtr->flags.pointerGrabbed = False;
2563 XUngrabPointer(dpy, CurrentTime);
2564 break;
2567 if (tPtr->flags.waitingForSelection)
2568 break;
2570 if (tPtr->flags.extendSelection && tPtr->flags.ownsSelection) {
2571 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
2572 return;
2575 if (tPtr->flags.ownsSelection)
2576 releaseSelection(tPtr);
2579 if (event->xbutton.button == Button1) {
2580 TextBlock *tb = tPtr->currentTextBlock;
2582 if(WMIsDoubleClick(event)) {
2584 tPtr->lastClickTime = event->xbutton.time;
2585 if(tb && tb->graphic && !tb->object) {
2586 if(tPtr->delegate && tPtr->delegate->didDoubleClickOnPicture) {
2587 char *desc;
2589 desc = wmalloc(tb->used+1);
2590 memcpy(desc, tb->text, tb->used);
2591 desc[tb->used] = 0;
2592 (*tPtr->delegate->didDoubleClickOnPicture)(tPtr->delegate, desc);
2593 wfree(desc);
2595 } else {
2596 autoSelectText(tPtr, 2);
2598 break;
2599 } else if (event->xbutton.time - tPtr->lastClickTime
2600 < WINGsConfiguration.doubleClickDelay) {
2601 tPtr->lastClickTime = event->xbutton.time;
2602 autoSelectText(tPtr, 3);
2603 break;
2606 if (!tPtr->flags.focused) {
2607 WMSetFocusToWidget(tPtr);
2608 tPtr->flags.focused = True;
2609 } else if (tb && tPtr->flags.isOverGraphic &&
2610 tb->graphic && !tb->object && tb->d.pixmap) {
2612 WMSetViewDragImage(tPtr->view, tb->d.pixmap);
2613 WMDragImageFromView(tPtr->view, event);
2614 break;
2617 tPtr->lastClickTime = event->xbutton.time;
2618 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
2619 paintText(tPtr);
2622 if (event->xbutton.button
2623 == WINGsConfiguration.mouseWheelDown) {
2624 WMScrollText(tPtr, 16);
2625 break;
2628 if (event->xbutton.button
2629 == WINGsConfiguration.mouseWheelUp) {
2630 WMScrollText(tPtr, -16);
2631 break;
2634 if (event->xbutton.button == Button2) {
2635 char *text = NULL;
2636 int n;
2638 if (!tPtr->flags.editable) {
2639 XBell(dpy, 0);
2640 break;
2643 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
2644 event->xbutton.time, pasteText, NULL)) {
2646 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
2647 tPtr->flags.waitingForSelection = 0;
2649 if (text) {
2650 text[n] = 0;
2652 if (tPtr->parser) {
2653 (tPtr->parser) (tPtr, (void *) text);
2654 layOutDocument(tPtr);
2656 else
2657 insertTextInteractively(tPtr, text, n);
2659 XFree(text);
2660 #if 0
2661 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
2662 (void*)WMInsertTextEvent);
2663 #endif
2664 updateCursorPosition(tPtr);
2665 paintText(tPtr);
2667 } else {
2668 tPtr->flags.waitingForSelection = True;
2671 break;
2675 case ButtonRelease:
2676 if (tPtr->flags.pointerGrabbed) {
2677 tPtr->flags.pointerGrabbed = False;
2678 XUngrabPointer(dpy, CurrentTime);
2679 break;
2682 if (tPtr->flags.waitingForSelection)
2683 break;
2685 if (WMIsDraggingFromView(tPtr->view))
2686 WMDragImageFromView(tPtr->view, event);
2692 static void
2693 handleEvents(XEvent *event, void *data)
2695 Text *tPtr = (Text *)data;
2697 switch(event->type) {
2698 case Expose:
2700 if (event->xexpose.count!=0)
2701 break;
2703 if(tPtr->hS) {
2704 if (!(W_VIEW(tPtr->hS))->flags.realized)
2705 WMRealizeWidget(tPtr->hS);
2708 if(tPtr->vS) {
2709 if (!(W_VIEW(tPtr->vS))->flags.realized)
2710 WMRealizeWidget(tPtr->vS);
2713 if(tPtr->ruler) {
2714 if (!(W_VIEW(tPtr->ruler))->flags.realized)
2715 WMRealizeWidget(tPtr->ruler);
2719 if(!tPtr->db)
2720 textDidResize(tPtr->view->delegate, tPtr->view);
2722 paintText(tPtr);
2723 break;
2725 case FocusIn:
2726 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))
2727 != tPtr->view)
2728 return;
2729 tPtr->flags.focused = True;
2730 #if DO_BLINK
2731 if (tPtr->flags.editable && !tPtr->timerID) {
2732 tPtr->timerID = WMAddTimerHandler(12+0*CURSOR_BLINK_ON_DELAY,
2733 blinkCursor, tPtr);
2735 #endif
2737 break;
2739 case FocusOut:
2740 tPtr->flags.focused = False;
2741 paintText(tPtr);
2742 #if DO_BLINK
2743 if (tPtr->timerID) {
2744 WMDeleteTimerHandler(tPtr->timerID);
2745 tPtr->timerID = NULL;
2747 #endif
2748 break;
2751 case DestroyNotify:
2752 clearText(tPtr);
2753 if(tPtr->db)
2754 XFreePixmap(tPtr->view->screen->display, tPtr->db);
2755 if(tPtr->gfxItems)
2756 WMEmptyArray(tPtr->gfxItems);
2757 #if DO_BLINK
2758 if (tPtr->timerID)
2759 WMDeleteTimerHandler(tPtr->timerID);
2760 #endif
2761 WMReleaseFont(tPtr->dFont);
2762 WMReleaseColor(tPtr->dColor);
2763 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
2764 WMRemoveNotificationObserver(tPtr);
2766 WMFreeArray(tPtr->xdndSourceTypes);
2767 WMFreeArray(tPtr->xdndDestinationTypes);
2769 wfree(tPtr);
2771 break;
2777 static void
2778 insertPlainText(Text *tPtr, char *text)
2780 char *start, *mark;
2781 void *tb = NULL;
2783 start = text;
2784 while (start) {
2785 mark = strchr(start, '\n');
2786 if (mark) {
2787 tb = WMCreateTextBlockWithText(tPtr,
2788 start, tPtr->dFont,
2789 tPtr->dColor, tPtr->flags.first, (int)(mark-start));
2790 start = mark+1;
2791 tPtr->flags.first = True;
2792 } else {
2793 if (start && strlen(start)) {
2794 tb = WMCreateTextBlockWithText(tPtr, start, tPtr->dFont,
2795 tPtr->dColor, tPtr->flags.first, strlen(start));
2796 } else tb = NULL;
2797 tPtr->flags.first = False;
2798 start = mark;
2801 if (tPtr->flags.prepend)
2802 WMPrependTextBlock(tPtr, tb);
2803 else
2804 WMAppendTextBlock(tPtr, tb);
2809 static void
2810 rulerMoveCallBack(WMWidget *w, void *self)
2812 Text *tPtr = (Text *)self;
2814 if (!tPtr)
2815 return;
2816 if (W_CLASS(tPtr) != WC_Text)
2817 return;
2819 paintText(tPtr);
2823 static void
2824 rulerReleaseCallBack(WMWidget *w, void *self)
2826 Text *tPtr = (Text *)self;
2828 if (!tPtr)
2829 return;
2830 if (W_CLASS(tPtr) != WC_Text)
2831 return;
2833 WMThawText(tPtr);
2834 return;
2838 static WMArray*
2839 dropDataTypes(WMView *self)
2841 return ((Text*)self->self)->xdndSourceTypes;
2845 static WMDragOperationType
2846 wantedDropOperation(WMView *self)
2848 return WDOperationCopy;
2852 static Bool
2853 acceptDropOperation(WMView *self, WMDragOperationType allowedOperation)
2855 return (allowedOperation == WDOperationCopy);
2859 static WMData*
2860 fetchDragData(WMView *self, char *type)
2862 TextBlock *tb = ((WMText *)self->self)->currentTextBlock;
2863 char *desc;
2864 WMData *data;
2866 if (strcmp(type, "text/plain")) {
2867 if (!tb)
2868 return NULL;
2870 desc = wmalloc(tb->used+1);
2871 memcpy(desc, tb->text, tb->used);
2872 desc[tb->used] = 0;
2873 data = WMCreateDataWithBytes(desc, strlen(desc)+1);
2875 wfree(desc);
2877 return data;
2880 return NULL;
2884 static WMDragSourceProcs _DragSourceProcs = {
2885 dropDataTypes,
2886 wantedDropOperation,
2887 NULL,
2888 acceptDropOperation,
2889 NULL,
2890 NULL,
2891 fetchDragData
2895 static WMArray*
2896 requiredDataTypes(WMView *self, WMDragOperationType request, WMArray *sourceDataTypes)
2898 return ((Text*)self->self)->xdndDestinationTypes;
2902 static WMDragOperationType
2903 allowedOperation(WMView *self, WMDragOperationType request, WMArray *sourceDataTypes)
2905 return WDOperationCopy;
2909 static void
2910 performDragOperation(WMView *self, WMArray *dropData, WMArray *operations,
2911 WMPoint* dropLocation)
2913 WMText *tPtr = (WMText *)self->self;
2914 WMData* data;
2915 char* colorName;
2916 WMColor *color;
2918 if (tPtr) {
2920 /* only one required type, implies only one drop data */
2922 /* get application/X-color if any */
2923 data = (WMData*)WMPopFromArray(dropData);
2924 if (data != NULL) {
2925 colorName = (char*)WMDataBytes(data);
2926 color = WMCreateNamedColor(W_VIEW_SCREEN(self), colorName, True);
2928 if(color) {
2929 WMSetTextSelectionColor(tPtr, color);
2930 WMReleaseColor(color);
2936 static WMDragDestinationProcs _DragDestinationProcs = {
2937 NULL,
2938 requiredDataTypes,
2939 allowedOperation,
2940 NULL,
2941 performDragOperation,
2942 NULL
2946 char *
2947 getStream(WMText *tPtr, int sel, int array)
2949 TextBlock *tb = NULL;
2950 char *text = NULL;
2951 unsigned long where = 0;
2953 if (!tPtr)
2954 return NULL;
2956 if (!(tb = tPtr->firstTextBlock))
2957 return NULL;
2959 if (tPtr->writer) {
2960 (tPtr->writer) (tPtr, (void *) text);
2961 return text;
2964 tb = tPtr->firstTextBlock;
2965 while (tb) {
2967 if (!tb->graphic || (tb->graphic && !tPtr->flags.monoFont)) {
2969 if (!sel || (tb->graphic && tb->selected)) {
2971 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank)
2972 && tb != tPtr->firstTextBlock) {
2973 text = wrealloc(text, where+1);
2974 text[where++] = '\n';
2977 if(tb->blank)
2978 goto _gSnext;
2980 if(tb->graphic && array) {
2981 text = wrealloc(text, where+4);
2982 text[where++] = 0xFA;
2983 text[where++] = (tb->used>>8)&0x0ff;
2984 text[where++] = tb->used&0x0ff;
2985 text[where++] = tb->allocated; /* extra info */
2987 text = wrealloc(text, where+tb->used);
2988 memcpy(&text[where], tb->text, tb->used);
2989 where += tb->used;
2992 } else if (sel && tb->selected) {
2994 if (!tPtr->flags.ignoreNewLine && tb->blank) {
2995 text = wrealloc(text, where+1);
2996 text[where++] = '\n';
2999 if(tb->blank)
3000 goto _gSnext;
3002 text = wrealloc(text, where+(tb->s_end - tb->s_begin));
3003 memcpy(&text[where], &tb->text[tb->s_begin],
3004 tb->s_end - tb->s_begin);
3005 where += tb->s_end - tb->s_begin;
3010 _gSnext:tb = tb->next;
3013 /* +1 for the end of string, let's be nice */
3014 text = wrealloc(text, where+1);
3015 text[where] = 0;
3016 return text;
3020 static void
3021 releaseStreamObjects(void *data)
3023 if(data)
3024 wfree(data);
3027 WMArray *
3028 getStreamObjects(WMText *tPtr, int sel)
3030 WMArray *array = WMCreateArrayWithDestructor(4, releaseStreamObjects);
3031 WMData *data;
3032 char *stream;
3033 unsigned short len;
3034 char *start, *fa, *desc;
3036 stream = getStream(tPtr, sel, 1);
3037 if(!stream)
3038 return NULL;
3040 start = stream;
3041 while (start) {
3043 fa = strchr(start, 0xFA);
3044 if (fa) {
3045 if((int)(fa - start)>0) {
3046 desc = start;
3047 desc[(int)(fa - start)] = 0;
3048 data = WMCreateDataWithBytes((void *)desc, (int)(fa - start));
3049 WMSetDataFormat(data, TYPETEXT);
3050 WMAddToArray(array, (void *) data);
3053 len = *(fa+1)*0xff + *(fa+2);
3054 data = WMCreateDataWithBytes((void *)(fa+4), len);
3055 WMSetDataFormat(data, *(fa+3));
3056 WMAddToArray(array, (void *) data);
3057 start = fa + len + 4;
3059 } else {
3060 if (start && strlen(start)) {
3061 data = WMCreateDataWithBytes((void *)start, strlen(start));
3062 WMSetDataFormat(data, TYPETEXT);
3063 WMAddToArray(array, (void *) data);
3065 start = fa;
3069 wfree(stream);
3070 return array;
3074 #define XDND_TEXT_DATA_TYPE "text/plain"
3075 #define XDND_COLOR_DATA_TYPE "application/X-color"
3076 static WMArray*
3077 getXdndSourceTypeArray()
3079 WMArray *types = WMCreateArray(1);
3080 WMAddToArray(types, XDND_TEXT_DATA_TYPE);
3081 return types;
3085 static WMArray*
3086 getXdndDestinationTypeArray()
3088 WMArray *types = WMCreateArray(1);
3089 WMAddToArray(types, XDND_COLOR_DATA_TYPE);
3090 return types;
3094 WMText*
3095 WMCreateTextForDocumentType(WMWidget *parent, WMAction *parser, WMAction *writer)
3097 Text *tPtr;
3098 Display *dpy;
3099 WMScreen *scr;
3100 XGCValues gcv;
3102 tPtr = wmalloc(sizeof(Text));
3103 memset(tPtr, 0, sizeof(Text));
3104 tPtr->widgetClass = WC_Text;
3105 tPtr->view = W_CreateView(W_VIEW(parent));
3106 if (!tPtr->view) {
3107 perror("could not create text's view\n");
3108 wfree(tPtr);
3109 return NULL;
3112 dpy = tPtr->view->screen->display;
3113 scr = tPtr->view->screen;
3115 tPtr->view->self = tPtr;
3116 tPtr->view->attribs.cursor = scr->textCursor;
3117 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
3118 W_ResizeView(tPtr->view, 250, 200);
3120 tPtr->dColor = WMBlackColor(scr);
3121 tPtr->fgColor = WMBlackColor(scr);
3122 tPtr->bgColor = WMWhiteColor(scr);
3123 W_SetViewBackgroundColor(tPtr->view, tPtr->bgColor);
3125 gcv.graphics_exposures = False;
3126 gcv.foreground = W_PIXEL(scr->gray);
3127 gcv.background = W_PIXEL(scr->darkGray);
3128 gcv.fill_style = FillStippled;
3129 /* why not use scr->stipple here? */
3130 gcv.stipple = XCreateBitmapFromData(dpy, W_DRAWABLE(scr), STIPPLE_BITS,
3131 STIPPLE_WIDTH, STIPPLE_HEIGHT);
3132 tPtr->stippledGC = XCreateGC(dpy, W_DRAWABLE(scr),
3133 GCForeground|GCBackground|GCStipple
3134 |GCFillStyle|GCGraphicsExposures, &gcv);
3136 tPtr->ruler = NULL;
3137 tPtr->vS = NULL;
3138 tPtr->hS = NULL;
3140 tPtr->dFont = WMSystemFontOfSize(scr, 12);
3142 tPtr->view->delegate = &_TextViewDelegate;
3144 tPtr->delegate = NULL;
3146 #if DO_BLINK
3147 tPtr->timerID = NULL;
3148 #endif
3150 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
3151 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
3152 handleEvents, tPtr);
3154 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
3155 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
3156 handleActionEvents, tPtr);
3158 WMAddNotificationObserver(ownershipObserver, tPtr,
3159 WMSelectionOwnerDidChangeNotification,
3160 tPtr);
3162 WMSetViewDragSourceProcs(tPtr->view, &_DragSourceProcs);
3163 WMSetViewDragDestinationProcs(tPtr->view, &_DragDestinationProcs);
3167 WMArray *types = WMCreateArray(2);
3168 WMAddToArray(types, "application/X-color");
3169 WMAddToArray(types, "application/X-image");
3170 WMRegisterViewForDraggedTypes(tPtr->view, types);
3173 /*WMAddNotificationObserver(fontChanged, tPtr,
3174 WMFontPanelDidChangeNotification, tPtr);*/
3176 tPtr->firstTextBlock = NULL;
3177 tPtr->lastTextBlock = NULL;
3178 tPtr->currentTextBlock = NULL;
3179 tPtr->tpos = 0;
3181 tPtr->gfxItems = WMCreateArray(4);
3183 tPtr->parser = parser;
3184 tPtr->writer = writer;
3186 tPtr->sel.x = tPtr->sel.y = 2;
3187 tPtr->sel.w = tPtr->sel.h = 0;
3189 tPtr->clicked.x = tPtr->clicked.y = 2;
3191 tPtr->visible.x = tPtr->visible.y = 2;
3192 tPtr->visible.h = tPtr->view->size.height;
3193 tPtr->visible.w = tPtr->view->size.width - 4;
3195 tPtr->cursor.x = -23;
3197 tPtr->docWidth = 0;
3198 tPtr->docHeight = 0;
3199 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
3200 default_bullet);
3201 tPtr->db = (Pixmap) NULL;
3202 tPtr->bgPixmap = NULL;
3204 tPtr->margins = WMGetRulerMargins(NULL);
3205 tPtr->margins->right = tPtr->visible.w;
3206 tPtr->nMargins = 1;
3208 tPtr->flags.rulerShown = False;
3209 tPtr->flags.monoFont = False;
3210 tPtr->flags.focused = False;
3211 tPtr->flags.editable = True;
3212 tPtr->flags.ownsSelection = False;
3213 tPtr->flags.pointerGrabbed = False;
3214 tPtr->flags.extendSelection = False;
3215 tPtr->flags.frozen = False;
3216 tPtr->flags.cursorShown = True;
3217 tPtr->flags.acceptsGraphic = False;
3218 tPtr->flags.horizOnDemand = False;
3219 tPtr->flags.needsLayOut = False;
3220 tPtr->flags.ignoreNewLine = False;
3221 tPtr->flags.indentNewLine = False;
3222 tPtr->flags.laidOut = False;
3223 tPtr->flags.ownsSelection = False;
3224 tPtr->flags.waitingForSelection = False;
3225 tPtr->flags.prepend = False;
3226 tPtr->flags.isOverGraphic = False;
3227 tPtr->flags.relief = WRSunken;
3228 tPtr->flags.isOverGraphic = 0;
3229 tPtr->flags.alignment = WALeft;
3230 tPtr->flags.first = True;
3232 tPtr->xdndSourceTypes = getXdndSourceTypeArray();
3233 tPtr->xdndDestinationTypes = getXdndDestinationTypeArray();
3235 return tPtr;
3238 void
3239 WMPrependTextStream(WMText *tPtr, char *text)
3241 CHECK_CLASS(tPtr, WC_Text);
3243 if(!text) {
3244 if (tPtr->flags.ownsSelection)
3245 releaseSelection(tPtr);
3246 clearText(tPtr);
3247 updateScrollers(tPtr);
3248 return;
3251 tPtr->flags.prepend = True;
3252 if (text && tPtr->parser)
3253 (tPtr->parser) (tPtr, (void *) text);
3254 else
3255 insertPlainText(tPtr, text);
3257 tPtr->flags.needsLayOut = True;
3258 tPtr->tpos = 0;
3259 if(!tPtr->flags.frozen) {
3260 layOutDocument(tPtr);
3265 void
3266 WMAppendTextStream(WMText *tPtr, char *text)
3268 CHECK_CLASS(tPtr, WC_Text);
3270 if(!text) {
3271 if (tPtr->flags.ownsSelection)
3272 releaseSelection(tPtr);
3273 clearText(tPtr);
3274 updateScrollers(tPtr);
3275 return;
3278 tPtr->flags.prepend = False;
3279 if (text && tPtr->parser)
3280 (tPtr->parser) (tPtr, (void *) text);
3281 else
3282 insertPlainText(tPtr, text);
3284 tPtr->flags.needsLayOut = True;
3285 if(tPtr->currentTextBlock) {
3286 if(tPtr->currentTextBlock->graphic)
3287 tPtr->tpos = 1;
3288 else
3289 tPtr->tpos = tPtr->currentTextBlock->used;
3292 if(!tPtr->flags.frozen) {
3293 layOutDocument(tPtr);
3298 char*
3299 WMGetTextStream(WMText *tPtr)
3301 CHECK_CLASS(tPtr, WC_Text);
3303 return getStream(tPtr, 0, 0);
3307 char*
3308 WMGetTextSelectedStream(WMText *tPtr)
3310 CHECK_CLASS(tPtr, WC_Text);
3312 return getStream(tPtr, 1, 0);
3316 WMArray*
3317 WMGetTextObjects(WMText *tPtr)
3319 CHECK_CLASS(tPtr, WC_Text);
3321 return getStreamObjects(tPtr, 0);
3324 WMArray*
3325 WMGetTextSelectedObjects(WMText *tPtr)
3327 CHECK_CLASS(tPtr, WC_Text);
3329 return getStreamObjects(tPtr, 1);
3333 void
3334 WMSetTextDelegate(WMText *tPtr, WMTextDelegate *delegate)
3336 CHECK_CLASS(tPtr, WC_Text);
3338 tPtr->delegate = delegate;
3342 void*
3343 WMCreateTextBlockWithObject(WMText *tPtr, WMWidget *w,
3344 char *description, WMColor *color,
3345 unsigned short first, unsigned short extraInfo)
3347 TextBlock *tb;
3349 if (!w || !description || !color)
3350 return NULL;
3352 tb = wmalloc(sizeof(TextBlock));
3354 tb->text = wstrdup(description);
3355 tb->used = strlen(description);
3356 tb->blank = False;
3357 tb->d.widget = w;
3358 tb->color = WMRetainColor(color);
3359 tb->marginN = newMargin(tPtr, NULL);
3360 tb->allocated = extraInfo;
3361 tb->first = first;
3362 tb->kanji = False;
3363 tb->graphic = True;
3364 tb->object = True;
3365 tb->underlined = False;
3366 tb->selected = False;
3367 tb->script = 0;
3368 tb->sections = NULL;
3369 tb->nsections = 0;
3370 tb->prior = NULL;
3371 tb->next = NULL;
3373 return tb;
3377 void*
3378 WMCreateTextBlockWithPixmap(WMText *tPtr, WMPixmap *p,
3379 char *description, WMColor *color,
3380 unsigned short first, unsigned short extraInfo)
3382 TextBlock *tb;
3384 if (!p || !description || !color)
3385 return NULL;
3387 tb = wmalloc(sizeof(TextBlock));
3389 tb->text = wstrdup(description);
3390 tb->used = strlen(description);
3391 tb->blank = False;
3392 tb->d.pixmap = WMRetainPixmap(p);
3393 tb->color = WMRetainColor(color);
3394 tb->marginN = newMargin(tPtr, NULL);
3395 tb->allocated = extraInfo;
3396 tb->first = first;
3397 tb->kanji = False;
3398 tb->graphic = True;
3399 tb->object = False;
3400 tb->underlined = False;
3401 tb->selected = False;
3402 tb->script = 0;
3403 tb->sections = NULL;
3404 tb->nsections = 0;
3405 tb->prior = NULL;
3406 tb->next = NULL;
3408 return tb;
3412 void*
3413 WMCreateTextBlockWithText(WMText *tPtr, char *text, WMFont *font, WMColor *color,
3414 unsigned short first, unsigned short len)
3416 TextBlock *tb;
3418 if (!font || !color)
3419 return NULL;
3421 tb = wmalloc(sizeof(TextBlock));
3423 tb->allocated = reqBlockSize(len);
3424 tb->text = (char *)wmalloc(tb->allocated);
3425 memset(tb->text, 0, tb->allocated);
3427 if (len < 1|| !text || (*text == '\n' && len==1 )) {
3428 *tb->text = ' ';
3429 tb->used = 1;
3430 tb->blank = True;
3431 } else {
3432 memcpy(tb->text, text, len);
3433 tb->used = len;
3434 tb->blank = False;
3436 tb->text[tb->used] = 0;
3438 tb->d.font = WMRetainFont(font);
3439 tb->color = WMRetainColor(color);
3440 tb->marginN = newMargin(tPtr, NULL);
3441 tb->first = first;
3442 tb->kanji = False;
3443 tb->graphic = False;
3444 tb->underlined = False;
3445 tb->selected = False;
3446 tb->script = 0;
3447 tb->sections = NULL;
3448 tb->nsections = 0;
3449 tb->prior = NULL;
3450 tb->next = NULL;
3451 return tb;
3455 void
3456 WMSetTextBlockProperties(WMText *tPtr, void *vtb, unsigned int first,
3457 unsigned int kanji, unsigned int underlined, int script,
3458 WMRulerMargins *margins)
3460 TextBlock *tb = (TextBlock *) vtb;
3461 if (!tb)
3462 return;
3464 tb->first = first;
3465 tb->kanji = kanji;
3466 tb->underlined = underlined;
3467 tb->script = script;
3468 tb->marginN = newMargin(tPtr, margins);
3472 void
3473 WMGetTextBlockProperties(WMText *tPtr, void *vtb, unsigned int *first,
3474 unsigned int *kanji, unsigned int *underlined, int *script,
3475 WMRulerMargins *margins)
3477 TextBlock *tb = (TextBlock *) vtb;
3478 if (!tb)
3479 return;
3481 if (first) *first = tb->first;
3482 if (kanji) *kanji = tb->kanji;
3483 if (underlined) *underlined = tb->underlined;
3484 if (script) *script = tb->script;
3485 if (margins) margins = &tPtr->margins[tb->marginN];
3489 void
3490 WMPrependTextBlock(WMText *tPtr, void *vtb)
3492 TextBlock *tb = (TextBlock *)vtb;
3494 if (!tb)
3495 return;
3497 if (tb->graphic) {
3498 if(tb->object) {
3499 WMWidget *w = tb->d.widget;
3500 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
3501 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
3502 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
3505 WMAddToArray(tPtr->gfxItems, (void *)tb);
3506 tPtr->tpos = 1;
3508 } else {
3509 tPtr->tpos = tb->used;
3512 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
3513 tb->next = tb->prior = NULL;
3514 tb->first = True;
3515 tPtr->lastTextBlock = tPtr->firstTextBlock
3516 = tPtr->currentTextBlock = tb;
3517 return;
3520 if(!tb->first) {
3521 tb->marginN = tPtr->currentTextBlock->marginN;
3524 tb->next = tPtr->currentTextBlock;
3525 tb->prior = tPtr->currentTextBlock->prior;
3526 if (tPtr->currentTextBlock->prior)
3527 tPtr->currentTextBlock->prior->next = tb;
3529 tPtr->currentTextBlock->prior = tb;
3530 if (!tb->prior)
3531 tPtr->firstTextBlock = tb;
3533 tPtr->currentTextBlock = tb;
3537 void
3538 WMAppendTextBlock(WMText *tPtr, void *vtb)
3540 TextBlock *tb = (TextBlock *)vtb;
3542 if (!tb)
3543 return;
3545 if (tb->graphic) {
3546 if(tb->object) {
3547 WMWidget *w = tb->d.widget;
3548 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
3549 (W_VIEW(w))->attribs.cursor =
3550 tPtr->view->screen->defaultCursor;
3551 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
3554 WMAddToArray(tPtr->gfxItems, (void *)tb);
3555 tPtr->tpos = 1;
3557 } else {
3558 tPtr->tpos = tb->used;
3562 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
3563 tb->next = tb->prior = NULL;
3564 tb->first = True;
3565 tPtr->lastTextBlock = tPtr->firstTextBlock
3566 = tPtr->currentTextBlock = tb;
3567 return;
3570 if(!tb->first) {
3571 tb->marginN = tPtr->currentTextBlock->marginN;
3574 tb->next = tPtr->currentTextBlock->next;
3575 tb->prior = tPtr->currentTextBlock;
3576 if (tPtr->currentTextBlock->next)
3577 tPtr->currentTextBlock->next->prior = tb;
3579 tPtr->currentTextBlock->next = tb;
3581 if (!tb->next)
3582 tPtr->lastTextBlock = tb;
3584 tPtr->currentTextBlock = tb;
3588 void*
3589 WMRemoveTextBlock(WMText *tPtr)
3591 TextBlock *tb = NULL;
3593 if (!tPtr->firstTextBlock || !tPtr->lastTextBlock ||
3594 !tPtr->currentTextBlock) {
3595 return NULL;
3598 tb = tPtr->currentTextBlock;
3599 if (tb->graphic) {
3600 WMRemoveFromArray(tPtr->gfxItems, (void *)tb);
3602 if(tb->object) {
3603 WMUnmapWidget(tb->d.widget);
3607 if (tPtr->currentTextBlock == tPtr->firstTextBlock) {
3608 if (tPtr->currentTextBlock->next)
3609 tPtr->currentTextBlock->next->prior = NULL;
3611 tPtr->firstTextBlock = tPtr->currentTextBlock->next;
3612 tPtr->currentTextBlock = tPtr->firstTextBlock;
3614 } else if (tPtr->currentTextBlock == tPtr->lastTextBlock) {
3615 tPtr->currentTextBlock->prior->next = NULL;
3616 tPtr->lastTextBlock = tPtr->currentTextBlock->prior;
3617 tPtr->currentTextBlock = tPtr->lastTextBlock;
3618 } else {
3619 tPtr->currentTextBlock->prior->next = tPtr->currentTextBlock->next;
3620 tPtr->currentTextBlock->next->prior = tPtr->currentTextBlock->prior;
3621 tPtr->currentTextBlock = tPtr->currentTextBlock->next;
3624 return (void *)tb;
3628 #if 0
3629 static void
3630 destroyWidget(WMWidget *widget)
3632 WMDestroyWidget(widget);
3633 // -- never do this -- wfree(widget);
3635 #endif
3638 void
3639 WMDestroyTextBlock(WMText *tPtr, void *vtb)
3641 TextBlock *tb = (TextBlock *)vtb;
3642 if (!tb)
3643 return;
3645 if (tb->graphic) {
3646 if(tb->object) {
3647 /* naturally, there's a danger to destroying widgets whose action
3648 * brings us here: ie. press a button to destroy it...
3649 * need to find a safer way. till then... this stays commented out */
3650 /* 5 months later... destroy it 10 seconds after now which should
3651 * be enough time for the widget's action to be completed... :-) */
3652 /* This is a bad assumption. Just destroy the widget here.
3653 * if the caller needs it, it can protect it with W_RetainView()
3654 * WMAddTimerHandler(10000, destroyWidget, (void *)tb->d.widget);*/
3655 WMDestroyWidget(tb->d.widget);
3656 } else {
3657 WMReleasePixmap(tb->d.pixmap);
3659 } else {
3660 WMReleaseFont(tb->d.font);
3663 WMReleaseColor(tb->color);
3664 /* isn't this going to memleak if nsections==0? if (tb->sections && tb->nsections > 0) */
3665 if (tb->sections)
3666 wfree(tb->sections);
3667 wfree(tb->text);
3668 wfree(tb);
3672 void
3673 WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
3675 if (tPtr->fgColor)
3676 WMReleaseColor(tPtr->fgColor);
3678 tPtr->fgColor = WMRetainColor(color ? color : tPtr->view->screen->black);
3680 paintText(tPtr);
3684 void
3685 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
3687 if (tPtr->bgColor)
3688 WMReleaseColor(tPtr->bgColor);
3690 tPtr->bgColor = WMRetainColor(color ? color : tPtr->view->screen->white);
3691 W_SetViewBackgroundColor(tPtr->view, tPtr->bgColor);
3693 paintText(tPtr);
3697 void
3698 WMSetTextBackgroundPixmap(WMText *tPtr, WMPixmap *pixmap)
3700 if (tPtr->bgPixmap)
3701 WMReleasePixmap(tPtr->bgPixmap);
3703 if (pixmap)
3704 tPtr->bgPixmap = WMRetainPixmap(pixmap);
3705 else
3706 tPtr->bgPixmap = NULL;
3710 void
3711 WMSetTextRelief(WMText *tPtr, WMReliefType relief)
3713 tPtr->flags.relief = relief;
3714 textDidResize(tPtr->view->delegate, tPtr->view);
3718 void
3719 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
3721 if (shouldhave && !tPtr->hS) {
3722 tPtr->hS = WMCreateScroller(tPtr);
3723 (W_VIEW(tPtr->hS))->attribs.cursor = tPtr->view->screen->defaultCursor;
3724 (W_VIEW(tPtr->hS))->attribFlags |= CWOverrideRedirect | CWCursor;
3725 WMSetScrollerArrowsPosition(tPtr->hS, WSAMinEnd);
3726 WMSetScrollerAction(tPtr->hS, scrollersCallBack, tPtr);
3727 WMMapWidget(tPtr->hS);
3728 } else if (!shouldhave && tPtr->hS) {
3729 WMUnmapWidget(tPtr->hS);
3730 WMDestroyWidget(tPtr->hS);
3731 tPtr->hS = NULL;
3734 tPtr->hpos = 0;
3735 tPtr->prevHpos = 0;
3736 textDidResize(tPtr->view->delegate, tPtr->view);
3740 void
3741 WMSetTextHasRuler(WMText *tPtr, Bool shouldhave)
3743 if(shouldhave && !tPtr->ruler) {
3744 tPtr->ruler = WMCreateRuler(tPtr);
3745 (W_VIEW(tPtr->ruler))->attribs.cursor =
3746 tPtr->view->screen->defaultCursor;
3747 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
3748 WMSetRulerReleaseAction(tPtr->ruler, rulerReleaseCallBack, tPtr);
3749 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
3750 } else if(!shouldhave && tPtr->ruler) {
3751 WMShowTextRuler(tPtr, False);
3752 WMDestroyWidget(tPtr->ruler);
3753 tPtr->ruler = NULL;
3755 textDidResize(tPtr->view->delegate, tPtr->view);
3758 void
3759 WMShowTextRuler(WMText *tPtr, Bool show)
3761 if(!tPtr->ruler)
3762 return;
3764 if(tPtr->flags.monoFont)
3765 show = False;
3767 tPtr->flags.rulerShown = show;
3768 if(show) {
3769 WMMapWidget(tPtr->ruler);
3770 } else {
3771 WMUnmapWidget(tPtr->ruler);
3774 textDidResize(tPtr->view->delegate, tPtr->view);
3778 Bool
3779 WMGetTextRulerShown(WMText *tPtr)
3781 if(!tPtr->ruler)
3782 return False;
3784 return tPtr->flags.rulerShown;
3788 void
3789 WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
3791 if (shouldhave && !tPtr->vS) {
3792 tPtr->vS = WMCreateScroller(tPtr);
3793 (W_VIEW(tPtr->vS))->attribs.cursor = tPtr->view->screen->defaultCursor;
3794 (W_VIEW(tPtr->vS))->attribFlags |= CWOverrideRedirect | CWCursor;
3795 WMSetScrollerArrowsPosition(tPtr->vS, WSAMaxEnd);
3796 WMSetScrollerAction(tPtr->vS, scrollersCallBack, tPtr);
3797 WMMapWidget(tPtr->vS);
3798 } else if (!shouldhave && tPtr->vS) {
3799 WMUnmapWidget(tPtr->vS);
3800 WMDestroyWidget(tPtr->vS);
3801 tPtr->vS = NULL;
3804 tPtr->vpos = 0;
3805 tPtr->prevVpos = 0;
3806 textDidResize(tPtr->view->delegate, tPtr->view);
3810 Bool
3811 WMScrollText(WMText *tPtr, int amount)
3813 Bool scroll=False;
3815 if (amount == 0 || !tPtr->view->flags.realized)
3816 return False;
3818 if (amount < 0) {
3819 if (tPtr->vpos > 0) {
3820 if (tPtr->vpos > abs(amount)) tPtr->vpos += amount;
3821 else tPtr->vpos=0;
3822 scroll=True;
3824 } else {
3825 int limit = tPtr->docHeight - tPtr->visible.h;
3826 if (tPtr->vpos < limit) {
3827 if (tPtr->vpos < limit-amount) tPtr->vpos += amount;
3828 else tPtr->vpos = limit;
3829 scroll = True;
3833 if (scroll && tPtr->vpos != tPtr->prevVpos) {
3834 updateScrollers(tPtr);
3835 paintText(tPtr);
3837 tPtr->prevVpos = tPtr->vpos;
3838 return scroll;
3842 Bool
3843 WMPageText(WMText *tPtr, Bool direction)
3845 if (!tPtr->view->flags.realized)
3846 return False;
3848 return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
3852 void
3853 WMSetTextEditable(WMText *tPtr, Bool editable)
3855 tPtr->flags.editable = editable;
3860 WMGetTextEditable(WMText *tPtr)
3862 return tPtr->flags.editable;
3865 void
3866 WMSetTextIndentNewLines(WMText *tPtr, Bool indent)
3868 tPtr->flags.indentNewLine = indent;
3871 void
3872 WMSetTextIgnoresNewline(WMText *tPtr, Bool ignore)
3874 tPtr->flags.ignoreNewLine = ignore;
3877 Bool
3878 WMGetTextIgnoresNewline(WMText *tPtr)
3880 return tPtr->flags.ignoreNewLine;
3883 void
3884 WMSetTextUsesMonoFont(WMText *tPtr, Bool mono)
3886 if (mono) {
3887 if(tPtr->flags.rulerShown)
3888 WMShowTextRuler(tPtr, False);
3889 if(tPtr->flags.alignment != WALeft)
3890 tPtr->flags.alignment = WALeft;
3893 tPtr->flags.monoFont = mono;
3894 textDidResize(tPtr->view->delegate, tPtr->view);
3897 Bool
3898 WMGetTextUsesMonoFont(WMText *tPtr)
3900 return tPtr->flags.monoFont;
3904 void
3905 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
3907 if (tPtr->dFont)
3908 WMReleaseFont(tPtr->dFont);
3910 if (font) {
3911 tPtr->dFont = WMRetainFont(font);
3912 } else {
3913 tPtr->dFont = WMSystemFontOfSize(tPtr->view->screen, 12);
3918 WMFont*
3919 WMGetTextDefaultFont(WMText *tPtr)
3921 return WMRetainFont(tPtr->dFont);
3925 void
3926 WMSetTextDefaultColor(WMText *tPtr, WMColor *color)
3928 if (tPtr->dColor)
3929 WMReleaseColor(tPtr->dColor);
3931 if (color) {
3932 tPtr->dColor = WMRetainColor(color);
3933 } else {
3934 tPtr->dColor = WMBlackColor(tPtr->view->screen);
3939 WMColor*
3940 WMGetTextDefaultColor(WMText *tPtr)
3942 return tPtr->dColor;
3946 void
3947 WMSetTextAlignment(WMText *tPtr, WMAlignment alignment)
3949 if(tPtr->flags.monoFont)
3950 tPtr->flags.alignment = WALeft;
3951 else
3952 tPtr->flags.alignment = alignment;
3953 WMThawText(tPtr);
3958 WMGetTextInsertType(WMText *tPtr)
3960 return tPtr->flags.prepend;
3964 void
3965 WMSetTextSelectionColor(WMText *tPtr, WMColor *color)
3967 setSelectionProperty(tPtr, NULL, color, -1);
3971 WMColor*
3972 WMGetTextSelectionColor(WMText *tPtr)
3974 TextBlock *tb;
3976 tb = tPtr->currentTextBlock;
3978 if (!tb || !tPtr->flags.ownsSelection)
3979 return NULL;
3981 if(!tb->selected)
3982 return NULL;
3984 return tb->color;
3988 void
3989 WMSetTextSelectionFont(WMText *tPtr, WMFont *font)
3991 setSelectionProperty(tPtr, font, NULL, -1) ;
3995 WMFont*
3996 WMGetTextSelectionFont(WMText *tPtr)
3998 TextBlock *tb;
4000 tb = tPtr->currentTextBlock;
4002 if (!tb || !tPtr->flags.ownsSelection)
4003 return NULL;
4005 if(!tb->selected)
4006 return NULL;
4008 if(tb->graphic) {
4009 tb = getFirstNonGraphicBlockFor(tb, 1);
4010 if(!tb)
4011 return NULL;
4013 return (tb->selected ? tb->d.font : NULL);
4017 void
4018 WMSetTextSelectionUnderlined(WMText *tPtr, int underlined)
4020 // check this
4021 if (underlined!=0 && underlined!=1)
4022 return;
4024 setSelectionProperty(tPtr, NULL, NULL, underlined);
4029 WMGetTextSelectionUnderlined(WMText *tPtr)
4031 TextBlock *tb;
4033 tb = tPtr->currentTextBlock;
4035 if (!tb || !tPtr->flags.ownsSelection)
4036 return 0;
4038 if(!tb->selected)
4039 return 0;
4041 return tb->underlined;
4045 void
4046 WMFreezeText(WMText *tPtr)
4048 tPtr->flags.frozen = True;
4052 void
4053 WMThawText(WMText *tPtr)
4055 tPtr->flags.frozen = False;
4057 if(tPtr->flags.monoFont) {
4058 int j, c = WMGetArrayItemCount(tPtr->gfxItems);
4059 TextBlock *tb;
4061 /* make sure to unmap widgets no matter where they are */
4062 /* they'll be later remapped if needed by paintText */
4063 for(j=0; j<c; j++) {
4064 if ((tb = (TextBlock *) WMGetFromArray(tPtr->gfxItems, j))) {
4065 if (tb->object && ((W_VIEW(tb->d.widget))->flags.mapped))
4066 WMUnmapWidget(tb->d.widget);
4072 tPtr->flags.laidOut = False;
4073 layOutDocument(tPtr);
4074 updateScrollers(tPtr);
4075 paintText(tPtr);
4076 tPtr->flags.needsLayOut = False;
4080 /* find first occurence of a string */
4081 static char *
4082 mystrstr(char *haystack, char *needle, unsigned short len, char *end,
4083 Bool caseSensitive)
4085 char *ptr;
4087 if(!haystack || !needle || !end)
4088 return NULL;
4090 for (ptr = haystack; ptr < end; ptr++) {
4091 if(caseSensitive) {
4092 if (*ptr == *needle && !strncmp(ptr, needle, len))
4093 return ptr;
4095 } else {
4096 if (tolower(*ptr) == tolower(*needle) &&
4097 !strncasecmp(ptr, needle, len))
4098 return ptr;
4102 return NULL;
4105 /* find last occurence of a string */
4106 static char *
4107 mystrrstr(char *haystack, char *needle, unsigned short len, char *end,
4108 Bool caseSensitive)
4110 char *ptr;
4112 if(!haystack || !needle || !end)
4113 return NULL;
4115 for (ptr = haystack-2; ptr > end; ptr--) {
4116 if(caseSensitive) {
4117 if (*ptr == *needle && !strncmp(ptr, needle, len))
4118 return ptr;
4119 } else {
4120 if (tolower(*ptr) == tolower(*needle) &&
4121 !strncasecmp(ptr, needle, len))
4122 return ptr;
4126 return NULL;
4130 Bool
4131 WMFindInTextStream(WMText *tPtr, char *needle, Bool direction,
4132 Bool caseSensitive)
4134 TextBlock *tb;
4135 char *mark=NULL;
4136 unsigned short pos;
4139 #if 0
4140 if (! (tb = tPtr->currentTextBlock)) {
4141 if (! (tb = ( (direction > 0) ?
4142 tPtr->firstTextBlock : tPtr->lastTextBlock) ) ){
4143 return False;
4145 } else {
4146 /* if(tb != ((direction>0) ?tPtr->firstTextBlock : tPtr->lastTextBlock))
4147 tb = (direction>0) ? tb->next : tb->prior; */
4148 if(tb != tPtr->lastTextBlock)
4149 tb = tb->prior;
4151 #endif
4152 tb = tPtr->currentTextBlock;
4153 pos = tPtr->tpos;
4156 while(tb) {
4157 if (!tb->graphic) {
4159 if(direction > 0) {
4160 if(pos+1 < tb->used)
4161 pos++;
4163 if(tb->used - pos> 0 && pos > 0) {
4164 mark = mystrstr(&tb->text[pos], needle,
4165 strlen(needle), &tb->text[tb->used], caseSensitive);
4167 } else {
4168 tb = tb->next;
4169 pos = 0;
4170 continue;
4173 } else {
4174 if(pos-1 > 0)
4175 pos--;
4177 if(pos > 0) {
4178 mark = mystrrstr(&tb->text[pos], needle,
4179 strlen(needle), tb->text, caseSensitive);
4180 } else {
4181 tb = tb->prior;
4182 if(!tb)
4183 return False;
4184 pos = tb->used;
4185 continue;
4190 if(mark) {
4191 WMFont *font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
4193 tPtr->tpos = (int)(mark - tb->text);
4194 tPtr->currentTextBlock = tb;
4195 updateCursorPosition(tPtr);
4196 tPtr->sel.y = tPtr->cursor.y+5;
4197 tPtr->sel.h = tPtr->cursor.h-10;
4198 tPtr->sel.x = tPtr->cursor.x +1;
4199 tPtr->sel.w = WMIN(WMWidthOfString(font,
4200 &tb->text[tPtr->tpos], strlen(needle)),
4201 tPtr->docWidth - tPtr->sel.x);
4202 tPtr->flags.ownsSelection = True;
4203 paintText(tPtr);
4205 return True;
4209 tb = (direction>0) ? tb->next : tb->prior;
4210 if(tb) {
4211 pos = (direction>0) ? 0 : tb->used;
4215 return False;
4219 Bool
4220 WMReplaceTextSelection(WMText *tPtr, char *replacement)
4222 if (!tPtr->flags.ownsSelection)
4223 return False;
4225 removeSelection(tPtr);
4227 if(replacement) {
4228 insertTextInteractively(tPtr, replacement, strlen(replacement));
4229 updateCursorPosition(tPtr);
4230 paintText(tPtr);
4233 return True;