2 /* WINGs WMText: multi-line/font/color/graphic text widget, by Nwanua. */
6 #include <X11/keysym.h>
12 * - verify what happens with XK_return in insertTextInt...
13 * - selection code... selects can be funny if it crosses over. use rect?
14 * - also inspect behaviour for WACenter and WARight
15 * - what if a widget grabs the click... howto say: "pressed me"?
16 * note that WMCreateEventHandler takes one data, but need widget & tPtr
17 * - FIX: graphix blocks MUST be skipped if monoFont even though they exist!
18 * - check if support for Horizontal Scroll is complete
19 * - Tabs now are simply replaced by 4 spaces...
20 * - redo blink code to reduce paint event... use pixmap buffer...
21 * - add paragraph support (full) and '\n' code in getStream..
24 /* a Section is a section of a TextBlock that describes what parts
25 of a TextBlock has been laid out on which "line"...
26 o this greatly aids redraw, scroll and selection.
27 o this is created during layoutLine, but may be later modified.
28 o there may be many Sections per TextBlock, hence the array */
30 unsigned int x
, y
; /* where to draw it from */
31 unsigned short w
, h
; /* its width and height */
32 unsigned short begin
; /* where the layout begins */
33 unsigned short end
; /* where it ends */
34 unsigned short max_d
; /* a quick hack for layOut if(laidOut) */
35 unsigned short last
:1; /* is it the last section on a "line"? */
36 unsigned int _y
:31; /* the "line" it and other textblocks are on */
39 /* a TextBlock is a node in a doubly-linked list of TextBlocks containing:
40 o text for the block, color and font
41 o or a pointer to the pixmap
42 o OR a pointer to the widget and the (text) description for its graphic
45 typedef struct _TextBlock
{
46 struct _TextBlock
*next
; /* next text block in linked list */
47 struct _TextBlock
*prior
; /* prior text block in linked list */
49 char *text
; /* pointer to text (could be kanji) */
50 /* or to the object's description */
52 WMFont
*font
; /* the font */
53 WMWidget
*widget
; /* the embedded widget */
54 WMPixmap
*pixmap
; /* the pixmap */
55 } d
; /* description */
57 unsigned short used
; /* number of chars in this block */
58 unsigned short allocated
; /* size of allocation (in chars) */
59 WMColor
*color
; /* the color */
61 Section
*sections
; /* the region for layouts (a growable array) */
62 /* an _array_! of size _nsections_ */
64 unsigned short s_begin
; /* where the selection begins */
65 unsigned short s_end
; /* where it ends */
67 unsigned int first
:1; /* first TextBlock in paragraph */
68 unsigned int blank
:1; /* ie. blank paragraph */
69 unsigned int kanji
:1; /* is of 16-bit characters or not */
70 unsigned int graphic
:1; /* graphic or text: text=0 */
71 unsigned int object
:1; /* embedded object or pixmap */
72 unsigned int underlined
:1; /* underlined or not */
73 unsigned int selected
:1; /* selected or not */
74 unsigned int nsections
:8; /* over how many "lines" a TextBlock wraps */
75 int script
:8; /* script in points: negative for subscript */
76 unsigned int marginN
:8; /* which of the margins in the tPtr to use */
77 unsigned int nClicks
:2; /* single, double, triple clicks */
78 unsigned int RESERVED
:7;
81 /* I'm lazy: visible.h vs. visible.size.height :-) */
86 typedef struct W_Text
{
87 W_Class widgetClass
; /* the class number of this widget */
88 W_View
*view
; /* the view referring to this instance */
90 WMRuler
*ruler
; /* the ruler widget to manipulate paragraphs */
92 WMScroller
*vS
; /* the vertical scroller */
93 unsigned int vpos
; /* the current vertical position */
94 unsigned int prevVpos
; /* the previous vertical position */
96 WMScroller
*hS
; /* the horizontal scroller */
97 unsigned int hpos
; /* the current horizontal position */
98 unsigned int prevHpos
; /* the previous horizontal position */
100 WMFont
*dFont
; /* the default font */
101 WMColor
*dColor
; /* the default color */
102 WMPixmap
*dBulletPix
; /* the default pixmap for bullets */
104 WMColor
*fgColor
; /* The current foreground color */
105 WMColor
*bgColor
; /* The background color */
107 GC stippledGC
; /* the GC to overlay selected graphics with */
108 Pixmap db
; /* the buffer on which to draw */
109 WMPixmap
*bgPixmap
; /* the background pixmap */
111 myRect visible
; /* the actual rectangle that can be drawn into */
112 myRect cursor
; /* the position and (height) of cursor */
113 myRect sel
; /* the selection rectangle */
115 WMPoint clicked
; /* where in the _document_ was clicked */
117 unsigned short tpos
; /* the position in the currentTextBlock */
118 unsigned short docWidth
; /* the width of the entire document */
119 unsigned int docHeight
; /* the height of the entire document */
121 TextBlock
*firstTextBlock
;
122 TextBlock
*lastTextBlock
;
123 TextBlock
*currentTextBlock
;
125 WMArray
*gfxItems
; /* a nice array of graphic items */
128 WMHandlerID timerID
; /* for nice twinky-winky */
133 WMTextDelegate
*delegate
;
136 WMRulerMargins
*margins
; /* an array of margins */
138 unsigned int nMargins
:7; /* the total number of margins in use */
140 unsigned int monoFont
:1; /* whether to ignore formats and graphic */
141 unsigned int focused
:1; /* whether this instance has input focus */
142 unsigned int editable
:1; /* "silly user, you can't edit me" */
143 unsigned int ownsSelection
:1; /* "I ownz the current selection!" */
144 unsigned int pointerGrabbed
:1; /* "heh, gib me pointer" */
145 unsigned int extendSelection
:1; /* shift-drag to select more regions */
147 unsigned int rulerShown
:1; /* whether the ruler is shown or not */
148 unsigned int frozen
:1; /* whether screen updates are to be made */
149 unsigned int cursorShown
:1; /* whether to show the cursor */
150 unsigned int acceptsGraphic
:1; /* accept graphic when dropped */
151 unsigned int horizOnDemand
:1; /* if a large image should appear */
152 unsigned int needsLayOut
:1; /* in case of Append/Deletes */
153 unsigned int ignoreNewLine
:1; /* turn it into a ' ' in streams > 1 */
154 unsigned int indentNewLine
:1; /* add " " for a newline typed */
155 unsigned int laidOut
:1; /* have the TextBlocks all been laid out */
156 unsigned int waitingForSelection
:1; /* I don't wanna wait in vain... */
157 unsigned int prepend
:1; /* prepend=1, append=0 (for parsers) */
158 WMAlignment alignment
:2; /* the alignment for text */
159 WMReliefType relief
:3; /* the relief to display with */
160 unsigned int isOverGraphic
:2; /* the mouse is over a graphic */
161 unsigned int first
:1; /* for plain text parsing, newline? */
162 /* unsigned int RESERVED:1; */
165 WMArray
*xdndSourceTypes
;
166 WMArray
*xdndDestinationTypes
;
171 #define NOTIFY(T,C,N,A) {\
172 WMNotification *notif = WMCreateNotification(N,T,A);\
173 if ((T)->delegate && (T)->delegate->C)\
174 (*(T)->delegate->C)((T)->delegate,notif);\
175 WMPostNotification(notif);\
176 WMReleaseNotification(notif);}
182 /* just to print blocks of text not terminated by \0 */
183 static void output(char *ptr
, int len
)
187 s
= wmalloc(len
+ 1);
190 /* printf(" s is [%s] (%d)\n", s, strlen(s)); */
197 #define CURSOR_BLINK_ON_DELAY 600
198 #define CURSOR_BLINK_OFF_DELAY 400
201 #define STIPPLE_WIDTH 8
202 #define STIPPLE_HEIGHT 8
203 static char STIPPLE_BITS
[] = {
204 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa
207 static char *default_bullet
[] = {
221 /* These id are used when sharing the selected text between applications */
222 static Atom XA_Targets
= None
;
223 static Atom XA_Format_Text
= None
;
224 static Atom XA_Format_Compound_Text
= None
;
226 static void handleEvents(XEvent
* event
, void *data
);
227 static void layOutDocument(Text
* tPtr
);
228 static void updateScrollers(Text
* tPtr
);
230 static int getMarginNumber(Text
* tPtr
, WMRulerMargins
* margins
)
234 for (i
= 0; i
< tPtr
->nMargins
; i
++) {
236 if (WMIsMarginEqualToMargin(&tPtr
->margins
[i
], margins
))
243 static int newMargin(Text
* tPtr
, WMRulerMargins
* margins
)
248 tPtr
->margins
[0].retainCount
++;
252 n
= getMarginNumber(tPtr
, margins
);
256 if (tPtr
->nMargins
>= 127) {
257 n
= tPtr
->nMargins
- 1;
261 tPtr
->margins
= wrealloc(tPtr
->margins
, (++tPtr
->nMargins
) * sizeof(WMRulerMargins
));
263 n
= tPtr
->nMargins
- 1;
264 tPtr
->margins
[n
].left
= margins
->left
;
265 tPtr
->margins
[n
].first
= margins
->first
;
266 tPtr
->margins
[n
].body
= margins
->body
;
267 tPtr
->margins
[n
].right
= margins
->right
;
268 /* for each tab... */
269 tPtr
->margins
[n
].retainCount
= 1;
271 tPtr
->margins
[n
].retainCount
++;
277 static Bool
sectionWasSelected(Text
* tPtr
, TextBlock
* tb
, XRectangle
* rect
, int s
)
279 unsigned short i
, w
, lw
, selected
= False
, extend
= False
;
282 /* if selection rectangle completely encloses the section */
283 if ((tb
->sections
[s
]._y
>= tPtr
->visible
.y
+ tPtr
->sel
.y
)
284 && (tb
->sections
[s
]._y
+ tb
->sections
[s
].h
<= tPtr
->visible
.y
+ tPtr
->sel
.y
+ tPtr
->sel
.h
)) {
286 sel
.w
= tPtr
->visible
.w
;
287 selected
= extend
= True
;
289 /* or if it starts on a line and then goes further down */
290 } else if ((tb
->sections
[s
]._y
<= tPtr
->visible
.y
+ tPtr
->sel
.y
)
291 && (tb
->sections
[s
]._y
+ tb
->sections
[s
].h
<= tPtr
->visible
.y
+ tPtr
->sel
.y
+ tPtr
->sel
.h
)
292 && (tb
->sections
[s
]._y
+ tb
->sections
[s
].h
>= tPtr
->visible
.y
+ tPtr
->sel
.y
)) {
293 sel
.x
= WMAX(tPtr
->sel
.x
, tPtr
->clicked
.x
);
294 sel
.w
= tPtr
->visible
.w
;
295 selected
= extend
= True
;
297 /* or if it begins before a line, but ends on it */
298 } else if ((tb
->sections
[s
]._y
>= tPtr
->visible
.y
+ tPtr
->sel
.y
)
299 && (tb
->sections
[s
]._y
+ tb
->sections
[s
].h
>= tPtr
->visible
.y
+ tPtr
->sel
.y
+ tPtr
->sel
.h
)
300 && (tb
->sections
[s
]._y
<= tPtr
->visible
.y
+ tPtr
->sel
.y
+ tPtr
->sel
.h
)) {
302 if (1 || tPtr
->sel
.x
+ tPtr
->sel
.w
> tPtr
->clicked
.x
)
303 sel
.w
= tPtr
->sel
.x
+ tPtr
->sel
.w
;
310 /* or if the selection rectangle lies entirely within a line */
311 } else if ((tb
->sections
[s
]._y
<= tPtr
->visible
.y
+ tPtr
->sel
.y
)
312 && (tPtr
->sel
.w
>= 2)
313 && (tb
->sections
[s
]._y
+ tb
->sections
[s
].h
>= tPtr
->visible
.y
+ tPtr
->sel
.y
+ tPtr
->sel
.h
)) {
322 /* if not within (modified) selection rectangle */
323 if (tb
->sections
[s
].x
> sel
.x
+ sel
.w
|| tb
->sections
[s
].x
+ tb
->sections
[s
].w
< sel
.x
)
327 if (tb
->sections
[s
].x
+ tb
->sections
[s
].w
<= sel
.x
+ sel
.w
&& tb
->sections
[s
].x
>= sel
.x
) {
328 rect
->width
= tb
->sections
[s
].w
;
329 rect
->x
= tb
->sections
[s
].x
;
334 i
= tb
->sections
[s
].begin
;
337 if (0 && tb
->sections
[s
].x
>= sel
.x
) {
338 tb
->s_begin
= tb
->sections
[s
].begin
;
342 while (++i
<= tb
->sections
[s
].end
) {
344 w
= WMWidthOfString(tb
->d
.font
, &(tb
->text
[i
- 1]), 1);
347 if (lw
+ tb
->sections
[s
].x
>= sel
.x
|| i
== tb
->sections
[s
].end
) {
350 tb
->s_begin
= (tb
->selected
? WMIN(tb
->s_begin
, i
) : i
);
355 if (i
> tb
->sections
[s
].end
) {
356 printf("WasSelected: (i > tb->sections[s].end) \n");
360 _selEnd
: rect
->x
= tb
->sections
[s
].x
+ lw
;
362 while (++i
<= tb
->sections
[s
].end
) {
364 w
= WMWidthOfString(tb
->d
.font
, &(tb
->text
[i
- 1]), 1);
367 if (lw
+ rect
->x
>= sel
.x
+ sel
.w
|| i
== tb
->sections
[s
].end
) {
369 if (i
!= tb
->sections
[s
].end
) {
375 if (tb
->sections
[s
].last
&& sel
.x
+ sel
.w
376 >= tb
->sections
[s
].x
+ tb
->sections
[s
].w
&& extend
) {
377 rect
->width
+= (tPtr
->visible
.w
- rect
->x
- lw
);
380 tb
->s_end
= (tb
->selected
? WMAX(tb
->s_end
, i
) : i
);
389 rect
->y
= tb
->sections
[s
]._y
- tPtr
->vpos
;
390 rect
->height
= tb
->sections
[s
].h
;
392 printf("DEBUG: graphic s%d h%d\n", s
, tb
->sections
[s
].h
);
399 static void setSelectionProperty(WMText
* tPtr
, WMFont
* font
, WMColor
* color
, int underlined
)
404 tb
= tPtr
->firstTextBlock
;
405 if (!tb
|| !tPtr
->flags
.ownsSelection
)
408 if (font
&& (!color
|| underlined
== -1))
412 if (tPtr
->flags
.monoFont
|| tb
->selected
) {
414 if (tPtr
->flags
.monoFont
|| (tb
->s_end
- tb
->s_begin
== tb
->used
)
419 WMReleaseFont(tb
->d
.font
);
420 tb
->d
.font
= WMRetainFont(font
);
422 } else if (underlined
!= -1) {
423 tb
->underlined
= underlined
;
425 WMReleaseColor(tb
->color
);
426 tb
->color
= WMRetainColor(color
);
429 } else if (tb
->s_end
<= tb
->used
&& tb
->s_begin
< tb
->s_end
) {
431 TextBlock
*midtb
, *otb
= tb
;
433 if (underlined
!= -1) {
434 midtb
= (TextBlock
*) WMCreateTextBlockWithText(tPtr
,
435 &(tb
->text
[tb
->s_begin
]),
436 tb
->d
.font
, tb
->color
,
438 (tb
->s_end
- tb
->s_begin
));
440 midtb
= (TextBlock
*) WMCreateTextBlockWithText(tPtr
,
441 &(tb
->text
[tb
->s_begin
]),
442 (isFont
? font
: tb
->d
.
445 color
: color
), False
,
446 (tb
->s_end
- tb
->s_begin
));
450 if (underlined
!= -1) {
451 midtb
->underlined
= underlined
;
453 midtb
->underlined
= otb
->underlined
;
456 midtb
->selected
= !True
;
458 midtb
->s_end
= midtb
->used
;
459 tPtr
->currentTextBlock
= tb
;
460 WMAppendTextBlock(tPtr
, midtb
);
461 tb
= tPtr
->currentTextBlock
;
464 if (otb
->used
- otb
->s_end
> 0) {
467 WMCreateTextBlockWithText(tPtr
,
468 &(otb
->text
[otb
->s_end
]), otb
->d
.font
,
469 otb
->color
, False
, otb
->used
- otb
->s_end
);
472 ntb
->underlined
= otb
->underlined
;
473 ntb
->selected
= False
;
474 WMAppendTextBlock(tPtr
, ntb
);
475 tb
= tPtr
->currentTextBlock
;
480 tPtr
->currentTextBlock
= midtb
;
483 otb
->selected
= False
;
484 otb
->used
= otb
->s_begin
;
491 tPtr
->flags
.needsLayOut
= True
;
494 /* in case the size changed... */
495 if (isFont
&& tPtr
->currentTextBlock
) {
496 TextBlock
*tb
= tPtr
->currentTextBlock
;
498 printf("%d %d %d\n", tPtr
->sel
.y
, tPtr
->sel
.h
, tPtr
->sel
.w
);
499 tPtr
->sel
.y
= 3 + tb
->sections
[0]._y
;
500 tPtr
->sel
.h
= tb
->sections
[tb
->nsections
- 1]._y
- tb
->sections
[0]._y
;
501 tPtr
->sel
.w
= tb
->sections
[tb
->nsections
- 1].w
;
502 if (tb
->sections
[tb
->nsections
- 1]._y
!= tb
->sections
[0]._y
) {
505 printf("%d %d %d\n\n\n", tPtr
->sel
.y
, tPtr
->sel
.h
, tPtr
->sel
.w
);
510 static Bool
removeSelection(Text
* tPtr
)
512 TextBlock
*tb
= NULL
;
515 if (!(tb
= tPtr
->firstTextBlock
))
520 if (!first
&& !tb
->graphic
) {
521 WMReleaseFont(tPtr
->dFont
);
522 tPtr
->dFont
= WMRetainFont(tb
->d
.font
);
526 if ((tb
->s_end
- tb
->s_begin
== tb
->used
) || tb
->graphic
) {
527 tPtr
->currentTextBlock
= tb
;
530 } else if (tb
->prior
) {
531 if (tb
->prior
->graphic
)
534 tPtr
->tpos
= tb
->prior
->used
;
538 WMDestroyTextBlock(tPtr
, WMRemoveTextBlock(tPtr
));
539 tb
= tPtr
->currentTextBlock
;
542 } else if (tb
->s_end
<= tb
->used
) {
543 memmove(&(tb
->text
[tb
->s_begin
]), &(tb
->text
[tb
->s_end
]), tb
->used
- tb
->s_end
);
544 tb
->used
-= (tb
->s_end
- tb
->s_begin
);
545 tb
->selected
= False
;
546 tPtr
->tpos
= tb
->s_begin
;
556 static TextBlock
*getFirstNonGraphicBlockFor(TextBlock
* tb
, short dir
)
558 TextBlock
*hold
= tb
;
566 tb
= (dir
? tb
->next
: tb
->prior
);
574 tb
= (dir
? tb
->prior
: tb
->next
);
584 static Bool
updateStartForCurrentTextBlock(Text
* tPtr
, int x
, int y
, int *dir
, TextBlock
* tb
)
586 if (tPtr
->flags
.monoFont
&& tb
->graphic
) {
587 tb
= getFirstNonGraphicBlockFor(tb
, *dir
);
592 tPtr
->currentTextBlock
= (*dir
? tPtr
->lastTextBlock
: tPtr
->firstTextBlock
);
599 layOutDocument(tPtr
);
603 *dir
= !(y
<= tb
->sections
[0].y
);
605 if ((y
<= tb
->sections
[0]._y
+ tb
->sections
[0].h
)
606 && (y
>= tb
->sections
[0]._y
)) {
607 /* if it's on the same line */
608 if (x
< tb
->sections
[0].x
)
612 if ((y
<= tb
->sections
[tb
->nsections
- 1]._y
+ tb
->sections
[tb
->nsections
- 1].h
)
613 && (y
>= tb
->sections
[tb
->nsections
- 1]._y
)) {
614 /* if it's on the same line */
615 if (x
> tb
->sections
[tb
->nsections
- 1].x
)
623 static void paintText(Text
* tPtr
)
628 int len
, y
, c
, s
, done
= False
, dir
; /* dir 1 = down */
629 WMScreen
*scr
= tPtr
->view
->screen
;
630 Display
*dpy
= tPtr
->view
->screen
->display
;
631 Window win
= tPtr
->view
->window
;
634 if (!tPtr
->view
->flags
.realized
|| !tPtr
->db
|| tPtr
->flags
.frozen
)
637 XFillRectangle(dpy
, tPtr
->db
, WMColorGC(tPtr
->bgColor
), 0, 0, tPtr
->visible
.w
, tPtr
->visible
.h
);
639 if (tPtr
->bgPixmap
) {
640 WMDrawPixmap(tPtr
->bgPixmap
, tPtr
->db
,
641 (tPtr
->visible
.w
- tPtr
->visible
.x
- tPtr
->bgPixmap
->width
) / 2,
642 (tPtr
->visible
.h
- tPtr
->visible
.y
- tPtr
->bgPixmap
->height
) / 2);
645 if (!(tb
= tPtr
->currentTextBlock
)) {
646 if (!(tb
= tPtr
->firstTextBlock
)) {
653 /* first, which direction? Don't waste time looking all over,
654 since the parts to be drawn will most likely be near what
655 was previously drawn */
656 if (!updateStartForCurrentTextBlock(tPtr
, 0, tPtr
->vpos
, &dir
, tb
))
661 if (tb
->graphic
&& tPtr
->flags
.monoFont
)
665 if (tPtr
->vpos
<= tb
->sections
[tb
->nsections
- 1]._y
+ tb
->sections
[tb
->nsections
- 1].h
)
668 if (tPtr
->vpos
>= tb
->sections
[tb
->nsections
- 1]._y
+ tb
->sections
[tb
->nsections
- 1].h
)
686 /* first, place all text that can be viewed */
687 while (!done
&& tb
) {
693 tb
->selected
= False
;
695 for (s
= 0; s
< tb
->nsections
&& !done
; s
++) {
697 if (tb
->sections
[s
]._y
> tPtr
->vpos
+ tPtr
->visible
.h
) {
702 if (tb
->sections
[s
].y
+ tb
->sections
[s
].h
< tPtr
->vpos
)
705 if (tPtr
->flags
.monoFont
) {
707 color
= tPtr
->fgColor
;
713 if (tPtr
->flags
.ownsSelection
) {
716 if (sectionWasSelected(tPtr
, tb
, &rect
, s
)) {
718 XFillRectangle(dpy
, tPtr
->db
, WMColorGC(scr
->gray
),
719 rect
.x
, rect
.y
, rect
.width
, rect
.height
);
723 len
= tb
->sections
[s
].end
- tb
->sections
[s
].begin
;
724 text
= &(tb
->text
[tb
->sections
[s
].begin
]);
725 y
= tb
->sections
[s
].y
- tPtr
->vpos
;
726 WMDrawString(scr
, tPtr
->db
, color
, font
, tb
->sections
[s
].x
- tPtr
->hpos
, y
, text
, len
);
728 if (!tPtr
->flags
.monoFont
&& tb
->underlined
) {
729 XDrawLine(dpy
, tPtr
->db
, WMColorGC(color
),
730 tb
->sections
[s
].x
- tPtr
->hpos
,
732 tb
->sections
[s
].x
+ tb
->sections
[s
].w
- tPtr
->hpos
, y
+ font
->y
+ 1);
735 tb
= (!done
? tb
->next
: NULL
);
738 /* now , show all graphic items that can be viewed */
739 c
= WMGetArrayItemCount(tPtr
->gfxItems
);
740 if (c
> 0 && !tPtr
->flags
.monoFont
) {
743 for (j
= 0; j
< c
; j
++) {
744 tb
= (TextBlock
*) WMGetFromArray(tPtr
->gfxItems
, j
);
746 /* if it's not viewable, and mapped, unmap it */
747 if (tb
->sections
[0]._y
+ tb
->sections
[0].h
<= tPtr
->vpos
748 || tb
->sections
[0]._y
>= tPtr
->vpos
+ tPtr
->visible
.h
) {
751 if ((W_VIEW(tb
->d
.widget
))->flags
.mapped
) {
752 WMUnmapWidget(tb
->d
.widget
);
756 /* if it's viewable, and not mapped, map it */
758 W_View
*view
= W_VIEW(tb
->d
.widget
);
760 if (!view
->flags
.realized
)
761 WMRealizeWidget(tb
->d
.widget
);
762 if (!view
->flags
.mapped
) {
763 XMapWindow(view
->screen
->display
, view
->window
);
764 XFlush(view
->screen
->display
);
765 view
->flags
.mapped
= 1;
770 WMMoveWidget(tb
->d
.widget
,
771 tb
->sections
[0].x
+ tPtr
->visible
.x
- tPtr
->hpos
,
772 tb
->sections
[0].y
+ tPtr
->visible
.y
- tPtr
->vpos
);
773 h
= WMWidgetHeight(tb
->d
.widget
) + 1;
776 WMDrawPixmap(tb
->d
.pixmap
, tPtr
->db
,
777 tb
->sections
[0].x
- tPtr
->hpos
,
778 tb
->sections
[0].y
- tPtr
->vpos
);
779 h
= tb
->d
.pixmap
->height
+ 1;
783 if (tPtr
->flags
.ownsSelection
) {
786 if (sectionWasSelected(tPtr
, tb
, &rect
, 0)) {
787 Drawable d
= (0 && tb
->object
?
788 (WMWidgetView(tb
->d
.widget
))->window
: tPtr
->db
);
791 XFillRectangle(dpy
, d
, tPtr
->stippledGC
,
792 /*XFillRectangle(dpy, tPtr->db, tPtr->stippledGC, */
793 rect
.x
, rect
.y
, rect
.width
, rect
.height
);
797 if (!tPtr
->flags
.monoFont
&& tb
->underlined
) {
798 XDrawLine(dpy
, tPtr
->db
, WMColorGC(tb
->color
),
799 tb
->sections
[0].x
- tPtr
->hpos
,
800 tb
->sections
[0].y
+ h
- tPtr
->vpos
,
801 tb
->sections
[0].x
+ tb
->sections
[0].w
- tPtr
->hpos
,
802 tb
->sections
[0].y
+ h
- tPtr
->vpos
);
809 if (tPtr
->flags
.editable
&& tPtr
->flags
.cursorShown
&& tPtr
->cursor
.x
!= -23 && tPtr
->flags
.focused
) {
810 int y
= tPtr
->cursor
.y
- tPtr
->vpos
;
811 XDrawLine(dpy
, tPtr
->db
, WMColorGC(tPtr
->fgColor
),
812 tPtr
->cursor
.x
, y
, tPtr
->cursor
.x
, y
+ tPtr
->cursor
.h
);
815 XCopyArea(dpy
, tPtr
->db
, win
, WMColorGC(tPtr
->bgColor
), 0, 0,
816 tPtr
->visible
.w
, tPtr
->visible
.h
, tPtr
->visible
.x
, tPtr
->visible
.y
);
818 W_DrawRelief(scr
, win
, 0, 0, tPtr
->view
->size
.width
, tPtr
->view
->size
.height
, tPtr
->flags
.relief
);
820 if (tPtr
->ruler
&& tPtr
->flags
.rulerShown
)
821 XDrawLine(dpy
, win
, WMColorGC(tPtr
->fgColor
), 2, 42, tPtr
->view
->size
.width
- 4, 42);
825 static void mouseOverObject(Text
* tPtr
, int x
, int y
)
830 x
-= tPtr
->visible
.x
;
832 y
-= tPtr
->visible
.y
;
835 if (tPtr
->flags
.ownsSelection
) {
837 && tPtr
->sel
.y
<= y
&& tPtr
->sel
.x
+ tPtr
->sel
.w
>= x
&& tPtr
->sel
.y
+ tPtr
->sel
.h
>= y
) {
838 tPtr
->flags
.isOverGraphic
= 1;
844 int j
, c
= WMGetArrayItemCount(tPtr
->gfxItems
);
847 tPtr
->flags
.isOverGraphic
= 0;
849 for (j
= 0; j
< c
; j
++) {
850 tb
= (TextBlock
*) WMGetFromArray(tPtr
->gfxItems
, j
);
852 if (!tb
|| !tb
->sections
) {
853 tPtr
->flags
.isOverGraphic
= 0;
858 if (tb
->sections
[0].x
<= x
859 && tb
->sections
[0].y
<= y
860 && tb
->sections
[0].x
+ tb
->sections
[0].w
>= x
861 && tb
->sections
[0].y
+ tb
->d
.pixmap
->height
>= y
) {
862 tPtr
->flags
.isOverGraphic
= 3;
872 tPtr
->flags
.isOverGraphic
= 0;
874 tPtr
->view
->attribs
.cursor
= (result
? tPtr
->view
->screen
->defaultCursor
: tPtr
->view
->screen
->textCursor
);
876 XSetWindowAttributes attribs
;
877 attribs
.cursor
= tPtr
->view
->attribs
.cursor
;
878 XChangeWindowAttributes(tPtr
->view
->screen
->display
, tPtr
->view
->window
, CWCursor
, &attribs
);
884 static void blinkCursor(void *data
)
886 Text
*tPtr
= (Text
*) data
;
888 if (tPtr
->flags
.cursorShown
) {
889 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
, data
);
891 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
, data
);
894 tPtr
->flags
.cursorShown
= !tPtr
->flags
.cursorShown
;
898 static void updateCursorPosition(Text
* tPtr
)
900 TextBlock
*tb
= NULL
;
903 if (tPtr
->flags
.needsLayOut
)
904 layOutDocument(tPtr
);
906 if (!(tb
= tPtr
->currentTextBlock
)) {
907 if (!(tb
= tPtr
->firstTextBlock
)) {
908 WMFont
*font
= tPtr
->dFont
;
910 tPtr
->cursor
.h
= font
->height
+ abs(font
->height
- font
->y
);
920 y
= tb
->sections
[0].y
;
921 h
= tb
->sections
[0].h
;
922 x
= tb
->sections
[0].x
;
924 } else if (tb
->graphic
) {
925 y
= tb
->sections
[0].y
;
926 h
= tb
->sections
[0].h
;
927 x
= tb
->sections
[0].x
;
929 x
+= tb
->sections
[0].w
;
932 if (tPtr
->tpos
> tb
->used
)
933 tPtr
->tpos
= tb
->used
;
935 for (s
= 0; s
< tb
->nsections
- 1; s
++) {
937 if (tPtr
->tpos
>= tb
->sections
[s
].begin
&& tPtr
->tpos
<= tb
->sections
[s
].end
)
941 y
= tb
->sections
[s
]._y
;
942 h
= tb
->sections
[s
].h
;
943 x
= tb
->sections
[s
].x
+ WMWidthOfString((tPtr
->flags
.monoFont
? tPtr
->dFont
: tb
->d
.font
),
944 &tb
->text
[tb
->sections
[s
].begin
],
945 tPtr
->tpos
- tb
->sections
[s
].begin
);
952 /* scroll the bars if the cursor is not visible */
953 if (tPtr
->flags
.editable
&& tPtr
->cursor
.x
!= -23) {
954 if (tPtr
->cursor
.y
+ tPtr
->cursor
.h
> tPtr
->vpos
+ tPtr
->visible
.y
+ tPtr
->visible
.h
) {
956 (tPtr
->cursor
.y
+ tPtr
->cursor
.h
+ 10
957 - (tPtr
->vpos
+ tPtr
->visible
.y
+ tPtr
->visible
.h
));
958 } else if (tPtr
->cursor
.y
< tPtr
->vpos
+ tPtr
->visible
.y
) {
959 tPtr
->vpos
-= (tPtr
->vpos
+ tPtr
->visible
.y
- tPtr
->cursor
.y
);
964 updateScrollers(tPtr
);
967 static void cursorToTextPosition(Text
* tPtr
, int x
, int y
)
969 TextBlock
*tb
= NULL
;
970 int done
= False
, s
, pos
, len
, _w
, _y
, dir
= 1; /* 1 == "down" */
973 if (tPtr
->flags
.needsLayOut
)
974 layOutDocument(tPtr
);
976 y
+= (tPtr
->vpos
- tPtr
->visible
.y
);
980 x
-= (tPtr
->visible
.x
- 2);
984 /* clicked is relative to document, not window... */
988 if (!(tb
= tPtr
->currentTextBlock
)) {
989 if (!(tb
= tPtr
->firstTextBlock
)) {
990 WMFont
*font
= tPtr
->dFont
;
992 tPtr
->cursor
.h
= font
->height
+ abs(font
->height
- font
->y
);
999 /* first, which direction? Most likely, newly clicked
1000 position will be close to previous */
1001 if (!updateStartForCurrentTextBlock(tPtr
, x
, y
, &dir
, tb
))
1004 s
= (dir
? 0 : tb
->nsections
- 1);
1005 if (y
>= tb
->sections
[s
]._y
&& y
<= tb
->sections
[s
]._y
+ tb
->sections
[s
].h
) {
1009 /* get the first (or last) section of the TextBlock that
1010 lies about the vertical click point */
1012 while (!done
&& tb
) {
1014 if (tPtr
->flags
.monoFont
&& tb
->graphic
) {
1015 if ((dir
? tb
->next
: tb
->prior
))
1016 tb
= (dir
? tb
->next
: tb
->prior
);
1020 s
= (dir
? 0 : tb
->nsections
- 1);
1021 while (!done
&& (dir
? (s
< tb
->nsections
) : (s
>= 0))) {
1023 if ((dir
? (y
<= tb
->sections
[s
]._y
+ tb
->sections
[s
].h
) : (y
>= tb
->sections
[s
]._y
))) {
1031 if ((dir
? tb
->next
: tb
->prior
)) {
1032 tb
= (dir
? tb
->next
: tb
->prior
);
1034 break; /* goto _doneH; */
1039 if (s
< 0 || s
>= tb
->nsections
) {
1040 s
= (dir
? tb
->nsections
- 1 : 0);
1044 /* we have the line, which TextBlock on that line is it? */
1045 pos
= (dir
? 0 : tb
->sections
[s
].begin
);
1046 if (tPtr
->flags
.monoFont
&& tb
->graphic
) {
1047 TextBlock
*hold
= tb
;
1048 tb
= getFirstNonGraphicBlockFor(hold
, dir
);
1058 _y
= tb
->sections
[s
]._y
;
1062 if (tPtr
->flags
.monoFont
&& tb
->graphic
) {
1063 tb
= (dir
? tb
->next
: tb
->prior
);
1070 _w
= WMWidgetWidth(tb
->d
.widget
) - 5;
1072 _w
= tb
->d
.pixmap
->width
- 5;
1074 if (tb
->sections
[0].x
+ _w
>= x
)
1077 text
= &(tb
->text
[tb
->sections
[s
].begin
]);
1078 len
= tb
->sections
[s
].end
- tb
->sections
[s
].begin
;
1079 _w
= WMWidthOfString(tb
->d
.font
, text
, len
);
1080 if (tb
->sections
[s
].x
+ _w
>= x
)
1085 if (tb
->sections
[s
].x
<= x
)
1089 if ((dir
? tb
->next
: tb
->prior
)) {
1090 TextBlock
*nxt
= (dir
? tb
->next
: tb
->prior
);
1091 if (tPtr
->flags
.monoFont
&& nxt
->graphic
) {
1092 nxt
= getFirstNonGraphicBlockFor(nxt
, dir
);
1094 pos
= (dir
? 0 : tb
->sections
[s
].begin
);
1095 tPtr
->cursor
.x
= tb
->sections
[s
].x
;
1100 if (_y
!= nxt
->sections
[dir
? 0 : nxt
->nsections
- 1]._y
) {
1101 /* this must be the last/first on this line. stop */
1102 pos
= (dir
? tb
->sections
[s
].end
: 0);
1103 tPtr
->cursor
.x
= tb
->sections
[s
].x
;
1107 tPtr
->cursor
.x
+= WMWidgetWidth(tb
->d
.widget
);
1109 tPtr
->cursor
.x
+= tb
->d
.pixmap
->width
;
1110 } else if (pos
> tb
->sections
[s
].begin
) {
1112 WMWidthOfString(tb
->d
.font
,
1113 &(tb
->text
[tb
->sections
[s
].begin
]),
1114 pos
- tb
->sections
[s
].begin
);
1121 if ((dir
? tb
->next
: tb
->prior
)) {
1122 tb
= (dir
? tb
->next
: tb
->prior
);
1128 s
= (dir
? 0 : tb
->nsections
- 1);
1131 /* we have said TextBlock, now where within it? */
1134 int gw
= (tb
->object
? WMWidgetWidth(tb
->d
.widget
) : tb
->d
.pixmap
->width
);
1136 tPtr
->cursor
.x
= tb
->sections
[0].x
;
1138 if (x
> tPtr
->cursor
.x
+ gw
/ 2) {
1140 tPtr
->cursor
.x
+= gw
;
1142 printf("first %d\n", tb
->first
);
1144 if (tb
->prior
->graphic
)
1147 pos
= tb
->prior
->used
;
1158 WMFont
*f
= tb
->d
.font
;
1159 len
= tb
->sections
[s
].end
- tb
->sections
[s
].begin
;
1160 text
= &(tb
->text
[tb
->sections
[s
].begin
]);
1162 _w
= x
- tb
->sections
[s
].x
;
1165 while (pos
< len
&& WMWidthOfString(f
, text
, pos
+ 1) < _w
)
1168 tPtr
->cursor
.x
= tb
->sections
[s
].x
+ (pos
? WMWidthOfString(f
, text
, pos
) : 0);
1170 pos
+= tb
->sections
[s
].begin
;
1176 tPtr
->tpos
= (pos
<= 1) ? pos
: 0;
1178 tPtr
->tpos
= (pos
< tb
->used
) ? pos
: tb
->used
;
1182 printf("...for this app will surely crash :-)\n");
1184 tPtr
->currentTextBlock
= tb
;
1185 tPtr
->cursor
.h
= tb
->sections
[s
].h
;
1186 tPtr
->cursor
.y
= tb
->sections
[s
]._y
;
1188 /* scroll the bars if the cursor is not visible */
1189 if (tPtr
->flags
.editable
&& tPtr
->cursor
.x
!= -23) {
1190 if (tPtr
->cursor
.y
+ tPtr
->cursor
.h
> tPtr
->vpos
+ tPtr
->visible
.y
+ tPtr
->visible
.h
) {
1192 (tPtr
->cursor
.y
+ tPtr
->cursor
.h
+ 10
1193 - (tPtr
->vpos
+ tPtr
->visible
.y
+ tPtr
->visible
.h
));
1194 updateScrollers(tPtr
);
1195 } else if (tPtr
->cursor
.y
< tPtr
->vpos
+ tPtr
->visible
.y
) {
1196 tPtr
->vpos
-= (tPtr
->vpos
+ tPtr
->visible
.y
- tPtr
->cursor
.y
);
1197 updateScrollers(tPtr
);
1204 static void updateScrollers(Text
* tPtr
)
1207 if (tPtr
->flags
.frozen
)
1211 if (tPtr
->docHeight
<= tPtr
->visible
.h
) {
1212 WMSetScrollerParameters(tPtr
->vS
, 0, 1);
1215 float hmax
= (float)(tPtr
->docHeight
);
1216 WMSetScrollerParameters(tPtr
->vS
,
1217 ((float)tPtr
->vpos
) / (hmax
- (float)tPtr
->visible
.h
),
1218 (float)tPtr
->visible
.h
/ hmax
);
1224 if (tPtr
->docWidth
<= tPtr
->visible
.w
) {
1225 WMSetScrollerParameters(tPtr
->hS
, 0, 1);
1228 float wmax
= (float)(tPtr
->docWidth
);
1229 WMSetScrollerParameters(tPtr
->hS
,
1230 ((float)tPtr
->hpos
) / (wmax
- (float)tPtr
->visible
.w
),
1231 (float)tPtr
->visible
.w
/ wmax
);
1237 static void scrollersCallBack(WMWidget
* w
, void *self
)
1239 Text
*tPtr
= (Text
*) self
;
1240 Bool scroll
= False
;
1243 if (!tPtr
->view
->flags
.realized
|| tPtr
->flags
.frozen
)
1246 if (w
== tPtr
->vS
) {
1248 height
= tPtr
->visible
.h
;
1250 which
= WMGetScrollerHitPart(tPtr
->vS
);
1253 case WSDecrementLine
:
1254 if (tPtr
->vpos
> 0) {
1255 if (tPtr
->vpos
> 16)
1262 case WSIncrementLine
:{
1263 int limit
= tPtr
->docHeight
- height
;
1264 if (tPtr
->vpos
< limit
) {
1265 if (tPtr
->vpos
< limit
- 16)
1273 case WSDecrementPage
:
1274 if (((int)tPtr
->vpos
- (int)height
) >= 0)
1275 tPtr
->vpos
-= height
;
1280 case WSIncrementPage
:
1281 tPtr
->vpos
+= height
;
1282 if (tPtr
->vpos
> (tPtr
->docHeight
- height
))
1283 tPtr
->vpos
= tPtr
->docHeight
- height
;
1287 tPtr
->vpos
= WMGetScrollerValue(tPtr
->vS
)
1288 * (float)(tPtr
->docHeight
- height
);
1295 scroll
= (tPtr
->vpos
!= tPtr
->prevVpos
);
1296 tPtr
->prevVpos
= tPtr
->vpos
;
1299 if (w
== tPtr
->hS
) {
1300 int width
= tPtr
->visible
.w
;
1302 which
= WMGetScrollerHitPart(tPtr
->hS
);
1305 case WSDecrementLine
:
1306 if (tPtr
->hpos
> 0) {
1307 if (tPtr
->hpos
> 16)
1314 case WSIncrementLine
:{
1315 int limit
= tPtr
->docWidth
- width
;
1316 if (tPtr
->hpos
< limit
) {
1317 if (tPtr
->hpos
< limit
- 16)
1325 case WSDecrementPage
:
1326 if (((int)tPtr
->hpos
- (int)width
) >= 0)
1327 tPtr
->hpos
-= width
;
1332 case WSIncrementPage
:
1333 tPtr
->hpos
+= width
;
1334 if (tPtr
->hpos
> (tPtr
->docWidth
- width
))
1335 tPtr
->hpos
= tPtr
->docWidth
- width
;
1339 tPtr
->hpos
= WMGetScrollerValue(tPtr
->hS
)
1340 * (float)(tPtr
->docWidth
- width
);
1347 scroll
= (tPtr
->hpos
!= tPtr
->prevHpos
);
1348 tPtr
->prevHpos
= tPtr
->hpos
;
1352 updateScrollers(tPtr
);
1359 unsigned short begin
, end
; /* what part of the text block */
1362 static int layOutLine(Text
* tPtr
, myLineItems
* items
, int nitems
, int x
, int y
)
1364 int i
, j
= 0, lw
= 0, line_height
= 0, max_d
= 0, len
, n
;
1367 TextBlock
*tb
, *tbsame
= NULL
;
1369 if (!items
|| nitems
== 0)
1372 for (i
= 0; i
< nitems
; i
++) {
1376 if (!tPtr
->flags
.monoFont
) {
1378 WMWidget
*wdt
= tb
->d
.widget
;
1379 line_height
= WMAX(line_height
, WMWidgetHeight(wdt
));
1380 if (tPtr
->flags
.alignment
!= WALeft
)
1381 lw
+= WMWidgetWidth(wdt
);
1383 line_height
= WMAX(line_height
, tb
->d
.pixmap
->height
+ max_d
);
1384 if (tPtr
->flags
.alignment
!= WALeft
)
1385 lw
+= tb
->d
.pixmap
->width
;
1390 font
= (tPtr
->flags
.monoFont
) ? tPtr
->dFont
: tb
->d
.font
;
1391 /*max_d = WMAX(max_d, abs(font->height-font->y)); */
1393 line_height
= WMAX(line_height
, font
->height
+ max_d
);
1394 text
= &(tb
->text
[items
[i
].begin
]);
1395 len
= items
[i
].end
- items
[i
].begin
;
1396 if (tPtr
->flags
.alignment
!= WALeft
)
1397 lw
+= WMWidthOfString(font
, text
, len
);
1401 if (tPtr
->flags
.alignment
== WARight
) {
1402 j
= tPtr
->visible
.w
- lw
;
1403 } else if (tPtr
->flags
.alignment
== WACenter
) {
1404 j
= (int)((float)(tPtr
->visible
.w
- lw
)) / 2.0;
1407 for (i
= 0; i
< nitems
; i
++) {
1410 if (tbsame
== tb
) { /* extend it, since it's on same line */
1411 tb
->sections
[tb
->nsections
- 1].end
= items
[i
].end
;
1412 n
= tb
->nsections
- 1;
1414 tb
->sections
= wrealloc(tb
->sections
, (++tb
->nsections
) * sizeof(Section
));
1415 n
= tb
->nsections
- 1;
1416 tb
->sections
[n
]._y
= y
+ max_d
;
1417 tb
->sections
[n
].max_d
= max_d
;
1418 tb
->sections
[n
].x
= x
+ j
;
1419 tb
->sections
[n
].h
= line_height
;
1420 tb
->sections
[n
].begin
= items
[i
].begin
;
1421 tb
->sections
[n
].end
= items
[i
].end
;
1424 tb
->sections
[n
].last
= (i
+ 1 == nitems
);
1427 if (!tPtr
->flags
.monoFont
) {
1429 WMWidget
*wdt
= tb
->d
.widget
;
1430 tb
->sections
[n
].y
= max_d
+ y
+ line_height
- WMWidgetHeight(wdt
);
1431 tb
->sections
[n
].w
= WMWidgetWidth(wdt
);
1433 tb
->sections
[n
].y
= y
+ line_height
+ max_d
- tb
->d
.pixmap
->height
;
1434 tb
->sections
[n
].w
= tb
->d
.pixmap
->width
;
1436 x
+= tb
->sections
[n
].w
;
1439 font
= (tPtr
->flags
.monoFont
) ? tPtr
->dFont
: tb
->d
.font
;
1440 len
= items
[i
].end
- items
[i
].begin
;
1441 text
= &(tb
->text
[items
[i
].begin
]);
1443 tb
->sections
[n
].y
= y
+ line_height
- font
->y
;
1445 WMWidthOfString(font
,
1446 &(tb
->text
[tb
->sections
[n
].begin
]),
1447 tb
->sections
[n
].end
- tb
->sections
[n
].begin
);
1449 x
+= WMWidthOfString(font
, text
, len
);
1459 static void layOutDocument(Text
* tPtr
)
1462 myLineItems
*items
= NULL
;
1463 unsigned int itemsSize
= 0, nitems
= 0, begin
, end
;
1465 unsigned int x
, y
= 0, lw
= 0, width
= 0, bmargin
;
1466 const char *start
= NULL
, *mark
= NULL
;
1468 if (tPtr
->flags
.frozen
|| (!(tb
= tPtr
->firstTextBlock
)))
1471 assert(tPtr
->visible
.w
> 20);
1473 tPtr
->docWidth
= tPtr
->visible
.w
;
1474 x
= tPtr
->margins
[tb
->marginN
].first
;
1475 bmargin
= tPtr
->margins
[tb
->marginN
].body
;
1477 /* only partial layOut needed: re-Lay only affected textblocks */
1478 if (tPtr
->flags
.laidOut
) {
1479 tb
= tPtr
->currentTextBlock
;
1481 /* search backwards for textblocks on same line */
1483 if (!tb
->sections
|| tb
->nsections
< 1) {
1484 tb
= tPtr
->firstTextBlock
;
1485 tPtr
->flags
.laidOut
= False
;
1490 if (!tb
->prior
->sections
|| tb
->prior
->nsections
< 1) {
1491 tb
= tPtr
->firstTextBlock
;
1492 tPtr
->flags
.laidOut
= False
;
1497 if (tb
->sections
[0]._y
!= tb
->prior
->sections
[tb
->prior
->nsections
- 1]._y
) {
1503 if (tb
->prior
&& tb
->prior
->sections
&& tb
->prior
->nsections
> 0) {
1504 y
= tb
->prior
->sections
[tb
->prior
->nsections
- 1]._y
+
1505 tb
->prior
->sections
[tb
->prior
->nsections
- 1].h
-
1506 tb
->prior
->sections
[tb
->prior
->nsections
- 1].max_d
;
1515 if (tb
->sections
&& tb
->nsections
> 0) {
1516 wfree(tb
->sections
);
1517 tb
->sections
= NULL
;
1521 if (tb
->first
&& tb
->blank
&& tb
->next
&& !tb
->next
->first
) {
1522 TextBlock
*next
= tb
->next
;
1523 tPtr
->currentTextBlock
= tb
;
1524 WMDestroyTextBlock(tPtr
, WMRemoveTextBlock(tPtr
));
1530 if (tb
->first
&& tb
!= tPtr
->firstTextBlock
) {
1531 y
+= layOutLine(tPtr
, items
, nitems
, x
, y
);
1532 x
= tPtr
->margins
[tb
->marginN
].first
;
1533 bmargin
= tPtr
->margins
[tb
->marginN
].body
;
1539 if (!tPtr
->flags
.monoFont
) {
1541 width
= WMWidgetWidth(tb
->d
.widget
);
1543 width
= tb
->d
.pixmap
->width
;
1545 if (width
> tPtr
->docWidth
)
1546 tPtr
->docWidth
= width
;
1549 if (lw
>= tPtr
->visible
.w
- x
) {
1550 y
+= layOutLine(tPtr
, items
, nitems
, x
, y
);
1556 if (nitems
+ 1 > itemsSize
) {
1557 items
= wrealloc(items
, (++itemsSize
) * sizeof(myLineItems
));
1560 items
[nitems
].tb
= tb
;
1561 items
[nitems
].begin
= 0;
1562 items
[nitems
].end
= 0;
1566 } else if ((start
= tb
->text
)) {
1568 font
= tPtr
->flags
.monoFont
? tPtr
->dFont
: tb
->d
.font
;
1571 mark
= strchr(start
, ' ');
1573 end
+= (int)(mark
- start
) + 1;
1576 end
+= strlen(start
);
1583 if (end
- begin
> 0) {
1585 width
= WMWidthOfString(font
, &tb
->text
[begin
], end
- begin
);
1587 /* if it won't fit, char wrap it */
1588 if (width
>= tPtr
->visible
.w
) {
1589 char *t
= &tb
->text
[begin
];
1590 int l
= end
- begin
, i
= 0;
1592 width
= WMWidthOfString(font
, t
, ++i
);
1593 } while (width
< tPtr
->visible
.w
&& i
< l
);
1597 start
= &tb
->text
[end
];
1603 if (lw
>= tPtr
->visible
.w
- x
) {
1604 y
+= layOutLine(tPtr
, items
, nitems
, x
, y
);
1610 if (nitems
+ 1 > itemsSize
) {
1611 items
= wrealloc(items
, (++itemsSize
) * sizeof(myLineItems
));
1614 items
[nitems
].tb
= tb
;
1615 items
[nitems
].begin
= begin
;
1616 items
[nitems
].end
= end
;
1623 /* not yet fully ready. but is already VERY FAST for a 3Mbyte file ;-) */
1624 if (0 && tPtr
->flags
.laidOut
1625 && tb
->next
&& tb
->next
->sections
&& tb
->next
->nsections
> 0
1626 && (tPtr
->vpos
+ tPtr
->visible
.h
< tb
->next
->sections
[0]._y
)) {
1627 if (tPtr
->lastTextBlock
->sections
&& tPtr
->lastTextBlock
->nsections
> 0) {
1628 TextBlock
*ltb
= tPtr
->lastTextBlock
;
1629 int ly
= ltb
->sections
[ltb
->nsections
- 1]._y
;
1630 int lh
= ltb
->sections
[ltb
->nsections
- 1].h
;
1633 lh
+= 1 + tPtr
->visible
.y
+ ltb
->sections
[ltb
->nsections
- 1].max_d
;
1634 printf("it's %d\n", tPtr
->visible
.y
+ ltb
->sections
[ltb
->nsections
- 1].max_d
);
1636 y
+= layOutLine(tPtr
, items
, nitems
, x
, y
);
1638 sd
= tPtr
->docHeight
- y
;
1640 printf("dif %d-%d: %d\n", ss
, sd
, ss
- sd
);
1641 y
+= tb
->next
->sections
[0]._y
- y
;
1643 printf("nitems%d\n", nitems
);
1645 y
= tPtr
->docHeight
+ ss
- sd
;
1649 tPtr
->flags
.laidOut
= False
;
1657 y
+= layOutLine(tPtr
, items
, nitems
, x
, y
);
1659 if (tPtr
->docHeight
!= y
+ 10) {
1660 tPtr
->docHeight
= y
+ 10;
1661 updateScrollers(tPtr
);
1664 if (tPtr
->docWidth
> tPtr
->visible
.w
&& !tPtr
->hS
) {
1667 tPtr
->flags
.horizOnDemand
= True
;
1668 WMSetTextHasHorizontalScroller((WMText
*) tPtr
, True
);
1669 event
.type
= Expose
;
1670 handleEvents(&event
, (void *)tPtr
);
1672 } else if (tPtr
->docWidth
<= tPtr
->visible
.w
&& tPtr
->hS
&& tPtr
->flags
.horizOnDemand
) {
1673 tPtr
->flags
.horizOnDemand
= False
;
1674 WMSetTextHasHorizontalScroller((WMText
*) tPtr
, False
);
1677 tPtr
->flags
.laidOut
= True
;
1679 if (items
&& itemsSize
> 0)
1684 static void textDidResize(W_ViewDelegate
* self
, WMView
* view
)
1686 Text
*tPtr
= (Text
*) view
->self
;
1687 unsigned short w
= tPtr
->view
->size
.width
;
1688 unsigned short h
= tPtr
->view
->size
.height
;
1689 unsigned short rh
= 0, vw
= 0, rel
;
1691 /* Parameter not used, but tell the compiler that it is ok */
1694 rel
= (tPtr
->flags
.relief
== WRFlat
);
1696 if (tPtr
->ruler
&& tPtr
->flags
.rulerShown
) {
1697 WMMoveWidget(tPtr
->ruler
, 2, 2);
1698 WMResizeWidget(tPtr
->ruler
, w
- 4, 40);
1703 WMMoveWidget(tPtr
->vS
, 1 - (rel
? 1 : 0), rh
+ 1 - (rel
? 1 : 0));
1704 WMResizeWidget(tPtr
->vS
, 20, h
- rh
- 2 + (rel
? 2 : 0));
1706 WMSetRulerOffset(tPtr
->ruler
, 22);
1708 WMSetRulerOffset(tPtr
->ruler
, 2);
1712 WMMoveWidget(tPtr
->hS
, vw
, h
- 21);
1713 WMResizeWidget(tPtr
->hS
, w
- vw
- 1, 20);
1715 WMMoveWidget(tPtr
->hS
, vw
+ 1, h
- 21);
1716 WMResizeWidget(tPtr
->hS
, w
- vw
- 2, 20);
1720 tPtr
->visible
.x
= (tPtr
->vS
) ? 24 : 4;
1721 tPtr
->visible
.y
= (tPtr
->ruler
&& tPtr
->flags
.rulerShown
) ? 43 : 3;
1722 tPtr
->visible
.w
= tPtr
->view
->size
.width
- tPtr
->visible
.x
- 8;
1723 tPtr
->visible
.h
= tPtr
->view
->size
.height
- tPtr
->visible
.y
;
1724 tPtr
->visible
.h
-= (tPtr
->hS
) ? 20 : 0;
1725 tPtr
->margins
[0].right
= tPtr
->visible
.w
;
1727 if (tPtr
->view
->flags
.realized
) {
1730 XFreePixmap(tPtr
->view
->screen
->display
, tPtr
->db
);
1731 tPtr
->db
= (Pixmap
) NULL
;
1734 if (tPtr
->visible
.w
< 40)
1735 tPtr
->visible
.w
= 40;
1736 if (tPtr
->visible
.h
< 20)
1737 tPtr
->visible
.h
= 20;
1740 tPtr
->db
= XCreatePixmap(tPtr
->view
->screen
->display
,
1741 tPtr
->view
->window
, tPtr
->visible
.w
,
1742 tPtr
->visible
.h
, tPtr
->view
->screen
->depth
);
1749 W_ViewDelegate _TextViewDelegate
= {
1757 #define TEXT_BUFFER_INCR 8
1758 #define reqBlockSize(requested) (requested + TEXT_BUFFER_INCR)
1760 static void clearText(Text
* tPtr
)
1762 tPtr
->vpos
= tPtr
->hpos
= 0;
1763 tPtr
->docHeight
= tPtr
->docWidth
= 0;
1764 tPtr
->cursor
.x
= -23;
1766 if (!tPtr
->firstTextBlock
)
1769 while (tPtr
->currentTextBlock
)
1770 WMDestroyTextBlock(tPtr
, WMRemoveTextBlock(tPtr
));
1772 tPtr
->firstTextBlock
= NULL
;
1773 tPtr
->currentTextBlock
= NULL
;
1774 tPtr
->lastTextBlock
= NULL
;
1775 WMEmptyArray(tPtr
->gfxItems
);
1778 /* possibly remove a single character from the currentTextBlock,
1779 or if there's a selection, remove it...
1780 note that Delete and Backspace are treated differently */
1781 static void deleteTextInteractively(Text
* tPtr
, KeySym ksym
)
1784 Bool back
= (Bool
) (ksym
== XK_BackSpace
);
1785 Bool done
= 1, wasFirst
= 0;
1787 if (!tPtr
->flags
.editable
)
1790 if (!(tb
= tPtr
->currentTextBlock
))
1793 if (tPtr
->flags
.ownsSelection
) {
1794 if (removeSelection(tPtr
))
1795 layOutDocument(tPtr
);
1799 wasFirst
= tb
->first
;
1800 if (back
&& tPtr
->tpos
< 1) {
1802 if (tb
->prior
->blank
) {
1803 tPtr
->currentTextBlock
= tb
->prior
;
1804 WMRemoveTextBlock(tPtr
);
1805 tPtr
->currentTextBlock
= tb
;
1807 layOutDocument(tPtr
);
1811 TextBlock
*prior
= tb
->prior
;
1812 tPtr
->currentTextBlock
= tb
;
1813 WMRemoveTextBlock(tPtr
);
1822 tPtr
->tpos
= tb
->used
;
1824 tPtr
->currentTextBlock
= tb
;
1828 tb
->next
->first
= False
;
1829 layOutDocument(tPtr
);
1836 if ((tb
->used
> 0) && ((back
? tPtr
->tpos
> 0 : 1))
1837 && (tPtr
->tpos
<= tb
->used
) && !tb
->graphic
) {
1840 memmove(&(tb
->text
[tPtr
->tpos
]), &(tb
->text
[tPtr
->tpos
+ 1]), tb
->used
- tPtr
->tpos
);
1845 /* if there are no characters left to back over in the textblock,
1846 but it still has characters to the right of the cursor: */
1847 if ((back
? (tPtr
->tpos
== 0 && !done
) : (tPtr
->tpos
>= tb
->used
))
1850 /* no more chars, and it's marked as blank? */
1852 TextBlock
*sibling
= (back
? tb
->prior
: tb
->next
);
1854 if (tb
->used
== 0 || tb
->graphic
)
1855 WMDestroyTextBlock(tPtr
, WMRemoveTextBlock(tPtr
));
1858 tPtr
->currentTextBlock
= sibling
;
1860 tPtr
->tpos
= (back
? 1 : 0);
1862 tPtr
->tpos
= (back
? sibling
->used
: 0);
1864 /* no more chars, so mark it as blank */
1865 } else if (tb
->used
== 0) {
1867 } else if (tb
->graphic
) {
1868 Bool hasNext
= (tb
->next
!= NULL
);
1870 WMDestroyTextBlock(tPtr
, WMRemoveTextBlock(tPtr
));
1873 } else if (tPtr
->currentTextBlock
) {
1874 tPtr
->tpos
= (tPtr
->currentTextBlock
->graphic
? 1 : tPtr
->currentTextBlock
->used
);
1877 printf("DEBUG: unaccounted for... catch this!\n");
1880 layOutDocument(tPtr
);
1883 static void insertTextInteractively(Text
* tPtr
, char *text
, int len
)
1886 char *newline
= NULL
;
1888 if (!tPtr
->flags
.editable
) {
1892 if (len
< 1 || !text
)
1895 if (tPtr
->flags
.ignoreNewLine
&& *text
== '\n' && len
== 1)
1898 if (tPtr
->flags
.ownsSelection
)
1899 removeSelection(tPtr
);
1901 if (tPtr
->flags
.ignoreNewLine
) {
1903 for (i
= 0; i
< len
; i
++) {
1904 if (text
[i
] == '\n')
1909 tb
= tPtr
->currentTextBlock
;
1910 if (!tb
|| tb
->graphic
) {
1912 WMAppendTextStream(tPtr
, text
);
1913 layOutDocument(tPtr
);
1917 if ((newline
= strchr(text
, '\n'))) {
1918 int nlen
= (int)(newline
- text
);
1919 int s
= tb
->used
- tPtr
->tpos
;
1921 if (!tb
->blank
&& nlen
> 0) {
1926 memcpy(save
, &tb
->text
[tPtr
->tpos
], s
);
1927 tb
->used
-= (tb
->used
- tPtr
->tpos
);
1929 insertTextInteractively(tPtr
, text
, nlen
);
1931 WMAppendTextStream(tPtr
, newline
);
1933 insertTextInteractively(tPtr
, save
, s
);
1937 if (tPtr
->tpos
> 0 && tPtr
->tpos
< tb
->used
&& !tb
->graphic
&& tb
->text
) {
1939 unsigned short savePos
= tPtr
->tpos
;
1940 void *ntb
= WMCreateTextBlockWithText(tPtr
, &tb
->text
[tPtr
->tpos
],
1941 tb
->d
.font
, tb
->color
, True
,
1942 tb
->used
- tPtr
->tpos
);
1944 if (tb
->sections
[0].end
== tPtr
->tpos
)
1945 WMAppendTextBlock(tPtr
, WMCreateTextBlockWithText(tPtr
,
1947 tb
->color
, True
, 0));
1950 WMAppendTextBlock(tPtr
, ntb
);
1953 } else if (tPtr
->tpos
== tb
->used
) {
1954 if (tPtr
->flags
.indentNewLine
) {
1955 WMAppendTextBlock(tPtr
, WMCreateTextBlockWithText(tPtr
,
1957 tb
->color
, True
, 4));
1960 WMAppendTextBlock(tPtr
, WMCreateTextBlockWithText(tPtr
,
1962 tb
->color
, True
, 0));
1965 } else if (tPtr
->tpos
== 0) {
1966 if (tPtr
->flags
.indentNewLine
) {
1967 WMPrependTextBlock(tPtr
, WMCreateTextBlockWithText(tPtr
,
1969 tb
->color
, True
, 4));
1971 WMPrependTextBlock(tPtr
, WMCreateTextBlockWithText(tPtr
,
1973 tb
->color
, True
, 0));
1976 if (tPtr
->currentTextBlock
->next
)
1977 tPtr
->currentTextBlock
= tPtr
->currentTextBlock
->next
;
1981 if (tb
->used
+ len
>= tb
->allocated
) {
1982 tb
->allocated
= reqBlockSize(tb
->used
+ len
);
1983 tb
->text
= wrealloc(tb
->text
, tb
->allocated
);
1987 memcpy(tb
->text
, text
, len
);
1990 tb
->text
[tb
->used
] = 0;
1994 memmove(&(tb
->text
[tPtr
->tpos
+ len
]), &tb
->text
[tPtr
->tpos
], tb
->used
- tPtr
->tpos
+ 1);
1995 memmove(&tb
->text
[tPtr
->tpos
], text
, len
);
1998 tb
->text
[tb
->used
] = 0;
2003 layOutDocument(tPtr
);
2006 static void selectRegion(Text
* tPtr
, int x
, int y
)
2012 y
+= (tPtr
->flags
.rulerShown
? 40 : 0);
2015 y
-= 10; /* the original offset */
2017 x
-= tPtr
->visible
.x
- 2;
2021 tPtr
->sel
.x
= WMAX(0, WMIN(tPtr
->clicked
.x
, x
));
2022 tPtr
->sel
.w
= abs(tPtr
->clicked
.x
- x
);
2023 tPtr
->sel
.y
= WMAX(0, WMIN(tPtr
->clicked
.y
, y
));
2024 tPtr
->sel
.h
= abs(tPtr
->clicked
.y
- y
);
2026 tPtr
->flags
.ownsSelection
= True
;
2030 static void releaseSelection(Text
* tPtr
)
2032 TextBlock
*tb
= tPtr
->firstTextBlock
;
2035 tb
->selected
= False
;
2038 tPtr
->flags
.ownsSelection
= False
;
2039 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
2044 static WMData
*requestHandler(WMView
* view
, Atom selection
, Atom target
, void *cdata
, Atom
* type
)
2046 Text
*tPtr
= view
->self
;
2047 WMData
*data
= NULL
;
2049 /* Parameter not used, but tell the compiler that it is ok */
2053 if (target
== XA_STRING
|| target
== XA_Format_Text
|| target
== XA_Format_Compound_Text
) {
2054 char *text
= WMGetTextSelectedStream(tPtr
);
2057 data
= WMCreateDataWithBytes(text
, strlen(text
));
2058 WMSetDataFormat(data
, TYPETEXT
);
2064 printf("didn't get it\n");
2066 if (target
== XA_Targets
) {
2069 array
[0] = XA_Targets
;
2070 array
[1] = XA_STRING
;
2071 array
[2] = XA_Format_Text
;
2072 array
[3] = XA_Format_Compound_Text
;
2074 data
= WMCreateDataWithBytes(&array
, sizeof(array
));
2075 WMSetDataFormat(data
, 32);
2084 static void lostHandler(WMView
* view
, Atom selection
, void *cdata
)
2086 /* Parameter not used, but tell the compiler that it is ok */
2090 releaseSelection((WMText
*) view
->self
);
2093 static WMSelectionProcs selectionHandler
= {
2094 requestHandler
, lostHandler
, NULL
2097 static void ownershipObserver(void *observerData
, WMNotification
* notification
)
2099 if (observerData
!= WMGetNotificationClientData(notification
))
2100 lostHandler(WMWidgetView(observerData
), XA_PRIMARY
, NULL
);
2103 static void autoSelectText(Text
* tPtr
, int clicks
)
2107 char *mark
= NULL
, behind
, ahead
;
2109 if (!(tb
= tPtr
->currentTextBlock
))
2114 switch (tb
->text
[tPtr
->tpos
]) {
2118 case '<': case '>': behind = '<'; ahead = '>'; break;
2119 case '{': case '}': behind = '{'; ahead = '}'; break;
2120 case '[': case ']': behind = '['; ahead = ']'; break;
2123 behind
= ahead
= ' ';
2126 tPtr
->sel
.y
= tPtr
->cursor
.y
+ 5;
2127 tPtr
->sel
.h
= 6; /*tPtr->cursor.h-10; */
2130 tPtr
->sel
.x
= tb
->sections
[0].x
;
2131 tPtr
->sel
.w
= tb
->sections
[0].w
;
2133 WMFont
*font
= tPtr
->flags
.monoFont
? tPtr
->dFont
: tb
->d
.font
;
2136 while (start
> 0 && tb
->text
[start
- 1] != behind
)
2140 if (tPtr
->tpos
> start
) {
2141 x
-= WMWidthOfString(font
, &tb
->text
[start
], tPtr
->tpos
- start
);
2143 tPtr
->sel
.x
= (x
< 0 ? 0 : x
) + 1;
2145 if ((mark
= strchr(&tb
->text
[start
], ahead
))) {
2146 tPtr
->sel
.w
= WMWidthOfString(font
, &tb
->text
[start
],
2147 (int)(mark
- &tb
->text
[start
]));
2148 } else if (tb
->used
> start
) {
2149 tPtr
->sel
.w
= WMWidthOfString(font
, &tb
->text
[start
], tb
->used
- start
);
2153 } else if (clicks
== 3) {
2154 TextBlock
*cur
= tb
;
2156 while (!tb
->first
) {
2160 wwarning("corrupted list of text blocks in WMText, while searching for first block");
2162 goto error_select_3clicks
;
2165 tPtr
->sel
.y
= tb
->sections
[0]._y
;
2168 while (tb
->next
&& !tb
->next
->first
) {
2171 tPtr
->sel
.h
= tb
->sections
[tb
->nsections
- 1]._y
+ 5 - tPtr
->sel
.y
;
2173 error_select_3clicks
:
2175 tPtr
->sel
.w
= tPtr
->docWidth
;
2176 tPtr
->clicked
.x
= 0; /* only for now, fix sel. code */
2179 if (!tPtr
->flags
.ownsSelection
) {
2180 WMCreateSelectionHandler(tPtr
->view
, XA_PRIMARY
, tPtr
->lastClickTime
, &selectionHandler
, NULL
);
2181 tPtr
->flags
.ownsSelection
= True
;
2188 static void fontChanged(void *observerData
, WMNotification
* notification
)
2190 WMText
*tPtr
= (WMText
*) observerData
;
2191 WMFont
*font
= (WMFont
*) WMGetNotificationClientData(notification
);
2192 printf("fontChanged\n");
2197 if (tPtr
->flags
.ownsSelection
)
2198 WMSetTextSelectionFont(tPtr
, font
);
2202 static void handleTextKeyPress(Text
* tPtr
, XEvent
* event
)
2206 int control_pressed
= False
;
2207 TextBlock
*tb
= NULL
;
2209 if (((XKeyEvent
*) event
)->state
& ControlMask
)
2210 control_pressed
= True
;
2211 buffer
[XLookupString(&event
->xkey
, buffer
, 63, &ksym
, NULL
)] = 0;
2216 if ((tPtr
->currentTextBlock
= tPtr
->firstTextBlock
))
2218 updateCursorPosition(tPtr
);
2223 if ((tPtr
->currentTextBlock
= tPtr
->lastTextBlock
)) {
2224 if (tPtr
->currentTextBlock
->graphic
)
2227 tPtr
->tpos
= tPtr
->currentTextBlock
->used
;
2229 updateCursorPosition(tPtr
);
2234 if (!(tb
= tPtr
->currentTextBlock
))
2237 if (tb
->graphic
|| tPtr
->tpos
== 0) {
2239 tPtr
->currentTextBlock
= tb
->prior
;
2240 if (tPtr
->currentTextBlock
->graphic
)
2243 tPtr
->tpos
= tPtr
->currentTextBlock
->used
;
2245 if (!tb
->first
&& tPtr
->tpos
> 0)
2251 updateCursorPosition(tPtr
);
2256 if (!(tb
= tPtr
->currentTextBlock
))
2258 if (tb
->graphic
|| tPtr
->tpos
== tb
->used
) {
2260 tPtr
->currentTextBlock
= tb
->next
;
2262 if (!tb
->next
->first
&& tb
->next
->used
> 0)
2268 tPtr
->tpos
= tb
->used
;
2272 updateCursorPosition(tPtr
);
2277 cursorToTextPosition(tPtr
, tPtr
->cursor
.x
+ tPtr
->visible
.x
,
2278 tPtr
->clicked
.y
+ tPtr
->cursor
.h
- tPtr
->vpos
);
2283 cursorToTextPosition(tPtr
, tPtr
->cursor
.x
+ tPtr
->visible
.x
,
2284 tPtr
->visible
.y
+ tPtr
->cursor
.y
- tPtr
->vpos
- 3);
2293 deleteTextInteractively(tPtr
, ksym
);
2294 updateCursorPosition(tPtr
);
2300 control_pressed
= True
;
2304 insertTextInteractively(tPtr
, " ", 4);
2305 updateCursorPosition(tPtr
);
2313 if (*buffer
!= 0 && !control_pressed
) {
2314 insertTextInteractively(tPtr
, buffer
, strlen(buffer
));
2315 updateCursorPosition(tPtr
);
2318 } else if (control_pressed
&& ksym
== XK_r
) {
2319 Bool i
= !tPtr
->flags
.rulerShown
;
2320 WMShowTextRuler(tPtr
, i
);
2321 tPtr
->flags
.rulerShown
= i
;
2322 } else if (control_pressed
&& *buffer
== '\a') {
2323 XBell(tPtr
->view
->screen
->display
, 0);
2325 WMRelayToNextResponder(tPtr
->view
, event
);
2329 if (!control_pressed
&& tPtr
->flags
.ownsSelection
) {
2330 releaseSelection(tPtr
);
2334 static void pasteText(WMView
* view
, Atom selection
, Atom target
, Time timestamp
, void *cdata
, WMData
* data
)
2336 Text
*tPtr
= (Text
*) view
->self
;
2339 /* Parameter not used, but tell the compiler that it is ok */
2345 tPtr
->flags
.waitingForSelection
= 0;
2348 text
= (char *)WMDataBytes(data
);
2351 (tPtr
->parser
) (tPtr
, (void *)text
);
2352 layOutDocument(tPtr
);
2354 insertTextInteractively(tPtr
, text
, strlen(text
));
2355 updateCursorPosition(tPtr
);
2361 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
2366 (tPtr
->parser
) (tPtr
, (void *)text
);
2367 layOutDocument(tPtr
);
2369 insertTextInteractively(tPtr
, text
, n
);
2370 updateCursorPosition(tPtr
);
2379 static void handleActionEvents(XEvent
* event
, void *data
)
2381 Text
*tPtr
= (Text
*) data
;
2382 Display
*dpy
= event
->xany
.display
;
2385 switch (event
->type
) {
2387 ksym
= XLookupKeysym((XKeyEvent
*) event
, 0);
2388 if (ksym
== XK_Shift_R
|| ksym
== XK_Shift_L
) {
2389 tPtr
->flags
.extendSelection
= True
;
2393 if (tPtr
->flags
.focused
) {
2394 XGrabPointer(dpy
, W_VIEW(tPtr
)->window
, False
,
2395 PointerMotionMask
| ButtonPressMask
| ButtonReleaseMask
,
2396 GrabModeAsync
, GrabModeAsync
, None
,
2397 tPtr
->view
->screen
->invisibleCursor
, CurrentTime
);
2398 tPtr
->flags
.pointerGrabbed
= True
;
2399 handleTextKeyPress(tPtr
, event
);
2405 ksym
= XLookupKeysym((XKeyEvent
*) event
, 0);
2406 if (ksym
== XK_Shift_R
|| ksym
== XK_Shift_L
) {
2407 tPtr
->flags
.extendSelection
= False
;
2409 /* end modify flag so selection can be extended */
2415 if (tPtr
->flags
.pointerGrabbed
) {
2416 tPtr
->flags
.pointerGrabbed
= False
;
2417 XUngrabPointer(dpy
, CurrentTime
);
2420 if (tPtr
->flags
.waitingForSelection
)
2423 if ((event
->xmotion
.state
& Button1Mask
)) {
2425 if (WMIsDraggingFromView(tPtr
->view
)) {
2426 WMDragImageFromView(tPtr
->view
, event
);
2430 if (!tPtr
->flags
.ownsSelection
) {
2431 WMCreateSelectionHandler(tPtr
->view
,
2432 XA_PRIMARY
, event
->xbutton
.time
, &selectionHandler
, NULL
);
2433 tPtr
->flags
.ownsSelection
= True
;
2435 selectRegion(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
2439 mouseOverObject(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
2444 if (tPtr
->flags
.pointerGrabbed
) {
2445 tPtr
->flags
.pointerGrabbed
= False
;
2446 XUngrabPointer(dpy
, CurrentTime
);
2450 if (tPtr
->flags
.waitingForSelection
)
2453 if (tPtr
->flags
.extendSelection
&& tPtr
->flags
.ownsSelection
) {
2454 selectRegion(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
2458 if (tPtr
->flags
.ownsSelection
)
2459 releaseSelection(tPtr
);
2461 if (event
->xbutton
.button
== Button1
) {
2462 TextBlock
*tb
= tPtr
->currentTextBlock
;
2464 if (WMIsDoubleClick(event
)) {
2466 tPtr
->lastClickTime
= event
->xbutton
.time
;
2467 if (tb
&& tb
->graphic
&& !tb
->object
) {
2468 if (tPtr
->delegate
&& tPtr
->delegate
->didDoubleClickOnPicture
) {
2471 desc
= wmalloc(tb
->used
+ 1);
2472 memcpy(desc
, tb
->text
, tb
->used
);
2474 (*tPtr
->delegate
->didDoubleClickOnPicture
) (tPtr
->delegate
, desc
);
2478 autoSelectText(tPtr
, 2);
2481 } else if (event
->xbutton
.time
- tPtr
->lastClickTime
< WINGsConfiguration
.doubleClickDelay
) {
2482 tPtr
->lastClickTime
= event
->xbutton
.time
;
2483 autoSelectText(tPtr
, 3);
2487 if (!tPtr
->flags
.focused
) {
2488 WMSetFocusToWidget(tPtr
);
2489 tPtr
->flags
.focused
= True
;
2490 } else if (tb
&& tPtr
->flags
.isOverGraphic
&& tb
->graphic
&& !tb
->object
&& tb
->d
.pixmap
) {
2492 WMSetViewDragImage(tPtr
->view
, tb
->d
.pixmap
);
2493 WMDragImageFromView(tPtr
->view
, event
);
2497 tPtr
->lastClickTime
= event
->xbutton
.time
;
2498 cursorToTextPosition(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
2502 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelDown
) {
2503 WMScrollText(tPtr
, 16);
2507 if (event
->xbutton
.button
== WINGsConfiguration
.mouseWheelUp
) {
2508 WMScrollText(tPtr
, -16);
2512 if (event
->xbutton
.button
== Button2
) {
2516 if (!tPtr
->flags
.editable
) {
2521 if (!WMRequestSelection(tPtr
->view
, XA_PRIMARY
, XA_STRING
,
2522 event
->xbutton
.time
, pasteText
, NULL
)) {
2524 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
2525 tPtr
->flags
.waitingForSelection
= 0;
2531 (tPtr
->parser
) (tPtr
, (void *)text
);
2532 layOutDocument(tPtr
);
2534 insertTextInteractively(tPtr
, text
, n
);
2538 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
2539 (void *)WMInsertTextEvent
);
2541 updateCursorPosition(tPtr
);
2545 tPtr
->flags
.waitingForSelection
= True
;
2553 if (tPtr
->flags
.pointerGrabbed
) {
2554 tPtr
->flags
.pointerGrabbed
= False
;
2555 XUngrabPointer(dpy
, CurrentTime
);
2559 if (tPtr
->flags
.waitingForSelection
)
2562 if (WMIsDraggingFromView(tPtr
->view
))
2563 WMDragImageFromView(tPtr
->view
, event
);
2568 static void handleEvents(XEvent
* event
, void *data
)
2570 Text
*tPtr
= (Text
*) data
;
2572 switch (event
->type
) {
2575 if (event
->xexpose
.count
!= 0)
2579 if (!(W_VIEW(tPtr
->hS
))->flags
.realized
)
2580 WMRealizeWidget(tPtr
->hS
);
2584 if (!(W_VIEW(tPtr
->vS
))->flags
.realized
)
2585 WMRealizeWidget(tPtr
->vS
);
2589 if (!(W_VIEW(tPtr
->ruler
))->flags
.realized
)
2590 WMRealizeWidget(tPtr
->ruler
);
2595 textDidResize(tPtr
->view
->delegate
, tPtr
->view
);
2601 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))
2604 tPtr
->flags
.focused
= True
;
2606 if (tPtr
->flags
.editable
&& !tPtr
->timerID
) {
2607 tPtr
->timerID
= WMAddTimerHandler(12 + 0 * CURSOR_BLINK_ON_DELAY
, blinkCursor
, tPtr
);
2614 tPtr
->flags
.focused
= False
;
2617 if (tPtr
->timerID
) {
2618 WMDeleteTimerHandler(tPtr
->timerID
);
2619 tPtr
->timerID
= NULL
;
2627 XFreePixmap(tPtr
->view
->screen
->display
, tPtr
->db
);
2629 WMEmptyArray(tPtr
->gfxItems
);
2632 WMDeleteTimerHandler(tPtr
->timerID
);
2634 WMReleaseFont(tPtr
->dFont
);
2635 WMReleaseColor(tPtr
->dColor
);
2636 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
2637 WMRemoveNotificationObserver(tPtr
);
2639 WMFreeArray(tPtr
->xdndSourceTypes
);
2640 WMFreeArray(tPtr
->xdndDestinationTypes
);
2649 static void insertPlainText(Text
* tPtr
, const char *text
)
2651 const char *start
, *mark
;
2656 mark
= strchr(start
, '\n');
2658 tb
= WMCreateTextBlockWithText(tPtr
,
2660 tPtr
->dColor
, tPtr
->flags
.first
, (int)(mark
- start
));
2662 tPtr
->flags
.first
= True
;
2664 if (start
&& strlen(start
)) {
2665 tb
= WMCreateTextBlockWithText(tPtr
, start
, tPtr
->dFont
,
2666 tPtr
->dColor
, tPtr
->flags
.first
, strlen(start
));
2669 tPtr
->flags
.first
= False
;
2673 if (tPtr
->flags
.prepend
)
2674 WMPrependTextBlock(tPtr
, tb
);
2676 WMAppendTextBlock(tPtr
, tb
);
2680 static void rulerMoveCallBack(WMWidget
* w
, void *self
)
2682 Text
*tPtr
= (Text
*) self
;
2684 /* Parameter not used, but tell the compiler that it is ok */
2689 if (W_CLASS(tPtr
) != WC_Text
)
2695 static void rulerReleaseCallBack(WMWidget
* w
, void *self
)
2697 Text
*tPtr
= (Text
*) self
;
2699 /* Parameter not used, but tell the compiler that it is ok */
2704 if (W_CLASS(tPtr
) != WC_Text
)
2711 static WMArray
*dropDataTypes(WMView
* self
)
2713 return ((Text
*) self
->self
)->xdndSourceTypes
;
2716 static WMDragOperationType
wantedDropOperation(WMView
* self
)
2718 /* Parameter not used, but tell the compiler that it is ok */
2721 return WDOperationCopy
;
2724 static Bool
acceptDropOperation(WMView
* self
, WMDragOperationType allowedOperation
)
2726 /* Parameter not used, but tell the compiler that it is ok */
2729 return (allowedOperation
== WDOperationCopy
);
2732 static WMData
*fetchDragData(WMView
* self
, char *type
)
2734 TextBlock
*tb
= ((WMText
*) self
->self
)->currentTextBlock
;
2738 if (strcmp(type
, "text/plain")) {
2742 desc
= wmalloc(tb
->used
+ 1);
2743 memcpy(desc
, tb
->text
, tb
->used
);
2745 data
= WMCreateDataWithBytes(desc
, strlen(desc
) + 1);
2755 static WMDragSourceProcs _DragSourceProcs
= {
2757 wantedDropOperation
,
2759 acceptDropOperation
,
2765 static WMArray
*requiredDataTypes(WMView
* self
, WMDragOperationType request
, WMArray
* sourceDataTypes
)
2767 /* Parameter not used, but tell the compiler that it is ok */
2769 (void) sourceDataTypes
;
2771 return ((Text
*) self
->self
)->xdndDestinationTypes
;
2774 static WMDragOperationType
allowedOperation(WMView
* self
, WMDragOperationType request
, WMArray
* sourceDataTypes
)
2776 /* Parameter not used, but tell the compiler that it is ok */
2779 (void) sourceDataTypes
;
2781 return WDOperationCopy
;
2784 static void performDragOperation(WMView
* self
, WMArray
* dropData
, WMArray
* operations
, WMPoint
* dropLocation
)
2786 WMText
*tPtr
= (WMText
*) self
->self
;
2791 /* Parameter not used, but tell the compiler that it is ok */
2793 (void) dropLocation
;
2797 /* only one required type, implies only one drop data */
2799 /* get application/X-color if any */
2800 data
= (WMData
*) WMPopFromArray(dropData
);
2802 colorName
= (char *)WMDataBytes(data
);
2803 color
= WMCreateNamedColor(W_VIEW_SCREEN(self
), colorName
, True
);
2806 WMSetTextSelectionColor(tPtr
, color
);
2807 WMReleaseColor(color
);
2813 static WMDragDestinationProcs _DragDestinationProcs
= {
2818 performDragOperation
,
2822 static char *getStream(WMText
* tPtr
, int sel
, int array
)
2824 TextBlock
*tb
= NULL
;
2826 unsigned long where
= 0;
2831 if (!(tb
= tPtr
->firstTextBlock
))
2835 (tPtr
->writer
) (tPtr
, (void *)text
);
2839 tb
= tPtr
->firstTextBlock
;
2842 if (!tb
->graphic
|| (tb
->graphic
&& !tPtr
->flags
.monoFont
)) {
2844 if (!sel
|| (tb
->graphic
&& tb
->selected
)) {
2846 if (!tPtr
->flags
.ignoreNewLine
&& (tb
->first
|| tb
->blank
)
2847 && tb
!= tPtr
->firstTextBlock
) {
2848 text
= wrealloc(text
, where
+ 1);
2849 text
[where
++] = '\n';
2855 if (tb
->graphic
&& array
) {
2856 text
= wrealloc(text
, where
+ 4);
2857 text
[where
++] = 0xFA;
2858 text
[where
++] = (tb
->used
>> 8) & 0x0ff;
2859 text
[where
++] = tb
->used
& 0x0ff;
2860 text
[where
++] = tb
->allocated
; /* extra info */
2862 text
= wrealloc(text
, where
+ tb
->used
);
2863 memcpy(&text
[where
], tb
->text
, tb
->used
);
2866 } else if (sel
&& tb
->selected
) {
2868 if (!tPtr
->flags
.ignoreNewLine
&& tb
->blank
) {
2869 text
= wrealloc(text
, where
+ 1);
2870 text
[where
++] = '\n';
2876 text
= wrealloc(text
, where
+ (tb
->s_end
- tb
->s_begin
));
2877 memcpy(&text
[where
], &tb
->text
[tb
->s_begin
], tb
->s_end
- tb
->s_begin
);
2878 where
+= tb
->s_end
- tb
->s_begin
;
2883 _gSnext
: tb
= tb
->next
;
2886 /* +1 for the end of string, let's be nice */
2887 text
= wrealloc(text
, where
+ 1);
2892 static void releaseStreamObjects(void *data
)
2898 static WMArray
*getStreamObjects(WMText
* tPtr
, int sel
)
2904 char *start
, *fa
, *desc
;
2906 stream
= getStream(tPtr
, sel
, 1);
2910 array
= WMCreateArrayWithDestructor(4, releaseStreamObjects
);
2915 fa
= strchr(start
, 0xFA);
2917 if ((int)(fa
- start
) > 0) {
2919 desc
[(int)(fa
- start
)] = 0;
2920 data
= WMCreateDataWithBytes((void *)desc
, (int)(fa
- start
));
2921 WMSetDataFormat(data
, TYPETEXT
);
2922 WMAddToArray(array
, (void *)data
);
2925 len
= *(fa
+ 1) * 0xff + *(fa
+ 2);
2926 data
= WMCreateDataWithBytes((void *)(fa
+ 4), len
);
2927 WMSetDataFormat(data
, *(fa
+ 3));
2928 WMAddToArray(array
, (void *)data
);
2929 start
= fa
+ len
+ 4;
2932 if (start
&& strlen(start
)) {
2933 data
= WMCreateDataWithBytes((void *)start
, strlen(start
));
2934 WMSetDataFormat(data
, TYPETEXT
);
2935 WMAddToArray(array
, (void *)data
);
2945 #define XDND_TEXT_DATA_TYPE "text/plain"
2946 #define XDND_COLOR_DATA_TYPE "application/X-color"
2947 static WMArray
*getXdndSourceTypeArray(void)
2949 WMArray
*types
= WMCreateArray(1);
2950 WMAddToArray(types
, XDND_TEXT_DATA_TYPE
);
2954 static WMArray
*getXdndDestinationTypeArray(void)
2956 WMArray
*types
= WMCreateArray(1);
2957 WMAddToArray(types
, XDND_COLOR_DATA_TYPE
);
2961 WMText
*WMCreateTextForDocumentType(WMWidget
* parent
, WMAction
* parser
, WMAction
* writer
)
2968 tPtr
= wmalloc(sizeof(Text
));
2969 tPtr
->widgetClass
= WC_Text
;
2970 tPtr
->view
= W_CreateView(W_VIEW(parent
));
2972 perror("could not create text's view\n");
2977 dpy
= tPtr
->view
->screen
->display
;
2978 scr
= tPtr
->view
->screen
;
2980 if (XA_Targets
== None
) {
2982 * Because the X protocol guaranties that the value will never change in
2983 * the lifespan of the server, we query the values only the first time a
2986 XA_Targets
= XInternAtom(dpy
, "TARGETS", False
);
2987 XA_Format_Text
= XInternAtom(dpy
, "TEXT", False
);
2988 XA_Format_Compound_Text
= XInternAtom(dpy
, "COMPOUND_TEXT", False
);
2991 tPtr
->view
->self
= tPtr
;
2992 tPtr
->view
->attribs
.cursor
= scr
->textCursor
;
2993 tPtr
->view
->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2994 W_ResizeView(tPtr
->view
, 250, 200);
2996 tPtr
->dColor
= WMBlackColor(scr
);
2997 tPtr
->fgColor
= WMBlackColor(scr
);
2998 tPtr
->bgColor
= WMWhiteColor(scr
);
2999 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->bgColor
);
3001 gcv
.graphics_exposures
= False
;
3002 gcv
.foreground
= W_PIXEL(scr
->gray
);
3003 gcv
.background
= W_PIXEL(scr
->darkGray
);
3004 gcv
.fill_style
= FillStippled
;
3005 /* why not use scr->stipple here? */
3006 gcv
.stipple
= XCreateBitmapFromData(dpy
, W_DRAWABLE(scr
), STIPPLE_BITS
, STIPPLE_WIDTH
, STIPPLE_HEIGHT
);
3007 tPtr
->stippledGC
= XCreateGC(dpy
, W_DRAWABLE(scr
),
3008 GCForeground
| GCBackground
| GCStipple
3009 | GCFillStyle
| GCGraphicsExposures
, &gcv
);
3015 tPtr
->dFont
= WMSystemFontOfSize(scr
, 12);
3017 tPtr
->view
->delegate
= &_TextViewDelegate
;
3019 tPtr
->delegate
= NULL
;
3022 tPtr
->timerID
= NULL
;
3025 WMCreateEventHandler(tPtr
->view
, ExposureMask
| StructureNotifyMask
3026 | EnterWindowMask
| LeaveWindowMask
| FocusChangeMask
, handleEvents
, tPtr
);
3028 WMCreateEventHandler(tPtr
->view
, ButtonReleaseMask
| ButtonPressMask
3029 | KeyReleaseMask
| KeyPressMask
| Button1MotionMask
, handleActionEvents
, tPtr
);
3031 WMAddNotificationObserver(ownershipObserver
, tPtr
, WMSelectionOwnerDidChangeNotification
, tPtr
);
3033 WMSetViewDragSourceProcs(tPtr
->view
, &_DragSourceProcs
);
3034 WMSetViewDragDestinationProcs(tPtr
->view
, &_DragDestinationProcs
);
3037 WMArray
*types
= WMCreateArray(2);
3038 WMAddToArray(types
, "application/X-color");
3039 WMAddToArray(types
, "application/X-image");
3040 WMRegisterViewForDraggedTypes(tPtr
->view
, types
);
3044 /*WMAddNotificationObserver(fontChanged, tPtr,
3045 WMFontPanelDidChangeNotification, tPtr); */
3047 tPtr
->firstTextBlock
= NULL
;
3048 tPtr
->lastTextBlock
= NULL
;
3049 tPtr
->currentTextBlock
= NULL
;
3052 tPtr
->gfxItems
= WMCreateArray(4);
3054 tPtr
->parser
= parser
;
3055 tPtr
->writer
= writer
;
3057 tPtr
->sel
.x
= tPtr
->sel
.y
= 2;
3058 tPtr
->sel
.w
= tPtr
->sel
.h
= 0;
3060 tPtr
->clicked
.x
= tPtr
->clicked
.y
= 2;
3062 tPtr
->visible
.x
= tPtr
->visible
.y
= 2;
3063 tPtr
->visible
.h
= tPtr
->view
->size
.height
;
3064 tPtr
->visible
.w
= tPtr
->view
->size
.width
- 4;
3066 tPtr
->cursor
.x
= -23;
3069 tPtr
->docHeight
= 0;
3070 tPtr
->dBulletPix
= WMCreatePixmapFromXPMData(tPtr
->view
->screen
, default_bullet
);
3071 tPtr
->db
= (Pixmap
) NULL
;
3072 tPtr
->bgPixmap
= NULL
;
3074 tPtr
->margins
= WMGetRulerMargins(NULL
);
3075 tPtr
->margins
->right
= tPtr
->visible
.w
;
3078 tPtr
->flags
.rulerShown
= False
;
3079 tPtr
->flags
.monoFont
= False
;
3080 tPtr
->flags
.focused
= False
;
3081 tPtr
->flags
.editable
= True
;
3082 tPtr
->flags
.ownsSelection
= False
;
3083 tPtr
->flags
.pointerGrabbed
= False
;
3084 tPtr
->flags
.extendSelection
= False
;
3085 tPtr
->flags
.frozen
= False
;
3086 tPtr
->flags
.cursorShown
= True
;
3087 tPtr
->flags
.acceptsGraphic
= False
;
3088 tPtr
->flags
.horizOnDemand
= False
;
3089 tPtr
->flags
.needsLayOut
= False
;
3090 tPtr
->flags
.ignoreNewLine
= False
;
3091 tPtr
->flags
.indentNewLine
= False
;
3092 tPtr
->flags
.laidOut
= False
;
3093 tPtr
->flags
.ownsSelection
= False
;
3094 tPtr
->flags
.waitingForSelection
= False
;
3095 tPtr
->flags
.prepend
= False
;
3096 tPtr
->flags
.isOverGraphic
= False
;
3097 tPtr
->flags
.relief
= WRSunken
;
3098 tPtr
->flags
.isOverGraphic
= 0;
3099 tPtr
->flags
.alignment
= WALeft
;
3100 tPtr
->flags
.first
= True
;
3102 tPtr
->xdndSourceTypes
= getXdndSourceTypeArray();
3103 tPtr
->xdndDestinationTypes
= getXdndDestinationTypeArray();
3108 void WMPrependTextStream(WMText
* tPtr
, const char *text
)
3110 CHECK_CLASS(tPtr
, WC_Text
);
3113 if (tPtr
->flags
.ownsSelection
)
3114 releaseSelection(tPtr
);
3116 updateScrollers(tPtr
);
3120 tPtr
->flags
.prepend
= True
;
3121 if (text
&& tPtr
->parser
)
3122 (tPtr
->parser
) (tPtr
, (void *)text
);
3124 insertPlainText(tPtr
, text
);
3126 tPtr
->flags
.needsLayOut
= True
;
3128 if (!tPtr
->flags
.frozen
) {
3129 layOutDocument(tPtr
);
3133 void WMAppendTextStream(WMText
* tPtr
, const char *text
)
3135 CHECK_CLASS(tPtr
, WC_Text
);
3138 if (tPtr
->flags
.ownsSelection
)
3139 releaseSelection(tPtr
);
3141 updateScrollers(tPtr
);
3145 tPtr
->flags
.prepend
= False
;
3146 if (text
&& tPtr
->parser
)
3147 (tPtr
->parser
) (tPtr
, (void *)text
);
3149 insertPlainText(tPtr
, text
);
3151 tPtr
->flags
.needsLayOut
= True
;
3152 if (tPtr
->currentTextBlock
) {
3153 if (tPtr
->currentTextBlock
->graphic
)
3156 tPtr
->tpos
= tPtr
->currentTextBlock
->used
;
3159 if (!tPtr
->flags
.frozen
) {
3160 layOutDocument(tPtr
);
3164 char *WMGetTextStream(WMText
* tPtr
)
3166 CHECK_CLASS(tPtr
, WC_Text
);
3168 return getStream(tPtr
, 0, 0);
3171 char *WMGetTextSelectedStream(WMText
* tPtr
)
3173 CHECK_CLASS(tPtr
, WC_Text
);
3175 return getStream(tPtr
, 1, 0);
3178 WMArray
*WMGetTextObjects(WMText
* tPtr
)
3180 CHECK_CLASS(tPtr
, WC_Text
);
3182 return getStreamObjects(tPtr
, 0);
3185 WMArray
*WMGetTextSelectedObjects(WMText
* tPtr
)
3187 CHECK_CLASS(tPtr
, WC_Text
);
3189 return getStreamObjects(tPtr
, 1);
3192 void WMSetTextDelegate(WMText
* tPtr
, WMTextDelegate
* delegate
)
3194 CHECK_CLASS(tPtr
, WC_Text
);
3196 tPtr
->delegate
= delegate
;
3199 void *WMCreateTextBlockWithObject(WMText
* tPtr
, WMWidget
* w
,
3200 const char *description
, WMColor
* color
,
3201 unsigned short first
, unsigned short extraInfo
)
3205 if (!w
|| !description
|| !color
)
3208 tb
= wmalloc(sizeof(TextBlock
));
3210 tb
->text
= wstrdup(description
);
3211 tb
->used
= strlen(description
);
3214 tb
->color
= WMRetainColor(color
);
3215 tb
->marginN
= newMargin(tPtr
, NULL
);
3216 tb
->allocated
= extraInfo
;
3221 tb
->underlined
= False
;
3222 tb
->selected
= False
;
3224 tb
->sections
= NULL
;
3232 void *WMCreateTextBlockWithPixmap(WMText
* tPtr
, WMPixmap
* p
,
3233 const char *description
, WMColor
* color
,
3234 unsigned short first
, unsigned short extraInfo
)
3238 if (!p
|| !description
|| !color
)
3241 tb
= wmalloc(sizeof(TextBlock
));
3243 tb
->text
= wstrdup(description
);
3244 tb
->used
= strlen(description
);
3246 tb
->d
.pixmap
= WMRetainPixmap(p
);
3247 tb
->color
= WMRetainColor(color
);
3248 tb
->marginN
= newMargin(tPtr
, NULL
);
3249 tb
->allocated
= extraInfo
;
3254 tb
->underlined
= False
;
3255 tb
->selected
= False
;
3257 tb
->sections
= NULL
;
3265 void *WMCreateTextBlockWithText(WMText
* tPtr
, const char *text
, WMFont
* font
, WMColor
* color
,
3266 unsigned short first
, unsigned short len
)
3270 if (!font
|| !color
)
3273 tb
= wmalloc(sizeof(TextBlock
));
3275 tb
->allocated
= reqBlockSize(len
);
3276 tb
->text
= (char *)wmalloc(tb
->allocated
);
3278 if (len
< 1 || !text
|| (*text
== '\n' && len
== 1)) {
3283 memcpy(tb
->text
, text
, len
);
3287 tb
->text
[tb
->used
] = 0;
3289 tb
->d
.font
= WMRetainFont(font
);
3290 tb
->color
= WMRetainColor(color
);
3291 tb
->marginN
= newMargin(tPtr
, NULL
);
3294 tb
->graphic
= False
;
3295 tb
->underlined
= False
;
3296 tb
->selected
= False
;
3298 tb
->sections
= NULL
;
3306 WMSetTextBlockProperties(WMText
* tPtr
, void *vtb
, unsigned int first
,
3307 unsigned int kanji
, unsigned int underlined
, int script
, WMRulerMargins
* margins
)
3309 TextBlock
*tb
= (TextBlock
*) vtb
;
3315 tb
->underlined
= underlined
;
3316 tb
->script
= script
;
3317 tb
->marginN
= newMargin(tPtr
, margins
);
3321 WMGetTextBlockProperties(WMText
* tPtr
, void *vtb
, unsigned int *first
,
3322 unsigned int *kanji
, unsigned int *underlined
, int *script
, WMRulerMargins
*margins
)
3324 TextBlock
*tb
= (TextBlock
*) vtb
;
3333 *underlined
= tb
->underlined
;
3335 *script
= tb
->script
;
3337 *margins
= tPtr
->margins
[tb
->marginN
];
3340 static int prepareTextBlock(WMText
*tPtr
, TextBlock
*tb
)
3344 WMWidget
*w
= tb
->d
.widget
;
3345 if (W_CLASS(w
) != WC_TextField
&& W_CLASS(w
) != WC_Text
) {
3346 (W_VIEW(w
))->attribs
.cursor
= tPtr
->view
->screen
->defaultCursor
;
3347 (W_VIEW(w
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
3350 WMAddToArray(tPtr
->gfxItems
, (void *)tb
);
3354 tPtr
->tpos
= tb
->used
;
3357 if (!tPtr
->lastTextBlock
|| !tPtr
->firstTextBlock
) {
3358 tb
->next
= tb
->prior
= NULL
;
3360 tPtr
->lastTextBlock
= tPtr
->firstTextBlock
= tPtr
->currentTextBlock
= tb
;
3365 tb
->marginN
= tPtr
->currentTextBlock
->marginN
;
3372 void WMPrependTextBlock(WMText
*tPtr
, void *vtb
)
3374 TextBlock
*tb
= (TextBlock
*) vtb
;
3376 if (!tb
|| !prepareTextBlock(tPtr
, tb
))
3379 tb
->next
= tPtr
->currentTextBlock
;
3380 tb
->prior
= tPtr
->currentTextBlock
->prior
;
3381 if (tPtr
->currentTextBlock
->prior
)
3382 tPtr
->currentTextBlock
->prior
->next
= tb
;
3384 tPtr
->currentTextBlock
->prior
= tb
;
3386 tPtr
->firstTextBlock
= tb
;
3388 tPtr
->currentTextBlock
= tb
;
3391 void WMAppendTextBlock(WMText
*tPtr
, void *vtb
)
3393 TextBlock
*tb
= (TextBlock
*) vtb
;
3395 if (!tb
|| !prepareTextBlock(tPtr
, tb
))
3398 tb
->next
= tPtr
->currentTextBlock
->next
;
3399 tb
->prior
= tPtr
->currentTextBlock
;
3400 if (tPtr
->currentTextBlock
->next
)
3401 tPtr
->currentTextBlock
->next
->prior
= tb
;
3403 tPtr
->currentTextBlock
->next
= tb
;
3406 tPtr
->lastTextBlock
= tb
;
3408 tPtr
->currentTextBlock
= tb
;
3411 void *WMRemoveTextBlock(WMText
* tPtr
)
3413 TextBlock
*tb
= NULL
;
3415 if (!tPtr
->firstTextBlock
|| !tPtr
->lastTextBlock
|| !tPtr
->currentTextBlock
) {
3419 tb
= tPtr
->currentTextBlock
;
3421 WMRemoveFromArray(tPtr
->gfxItems
, (void *)tb
);
3424 WMUnmapWidget(tb
->d
.widget
);
3428 if (tPtr
->currentTextBlock
== tPtr
->firstTextBlock
) {
3429 if (tPtr
->currentTextBlock
->next
)
3430 tPtr
->currentTextBlock
->next
->prior
= NULL
;
3432 tPtr
->firstTextBlock
= tPtr
->currentTextBlock
->next
;
3433 tPtr
->currentTextBlock
= tPtr
->firstTextBlock
;
3435 } else if (tPtr
->currentTextBlock
== tPtr
->lastTextBlock
) {
3436 tPtr
->currentTextBlock
->prior
->next
= NULL
;
3437 tPtr
->lastTextBlock
= tPtr
->currentTextBlock
->prior
;
3438 tPtr
->currentTextBlock
= tPtr
->lastTextBlock
;
3440 tPtr
->currentTextBlock
->prior
->next
= tPtr
->currentTextBlock
->next
;
3441 tPtr
->currentTextBlock
->next
->prior
= tPtr
->currentTextBlock
->prior
;
3442 tPtr
->currentTextBlock
= tPtr
->currentTextBlock
->next
;
3449 static void destroyWidget(WMWidget
* widget
)
3451 WMDestroyWidget(widget
);
3452 // -- never do this -- wfree(widget);
3456 void WMDestroyTextBlock(WMText
* tPtr
, void *vtb
)
3458 TextBlock
*tb
= (TextBlock
*) vtb
;
3460 /* Parameter not used, but tell the compiler that it is ok */
3468 /* naturally, there's a danger to destroying widgets whose action
3469 * brings us here: ie. press a button to destroy it...
3470 * need to find a safer way. till then... this stays commented out */
3471 /* 5 months later... destroy it 10 seconds after now which should
3472 * be enough time for the widget's action to be completed... :-) */
3473 /* This is a bad assumption. Just destroy the widget here.
3474 * if the caller needs it, it can protect it with W_RetainView()
3475 * WMAddTimerHandler(10000, destroyWidget, (void *)tb->d.widget);*/
3476 WMDestroyWidget(tb
->d
.widget
);
3478 WMReleasePixmap(tb
->d
.pixmap
);
3481 WMReleaseFont(tb
->d
.font
);
3484 WMReleaseColor(tb
->color
);
3485 /* isn't this going to memleak if nsections==0? if (tb->sections && tb->nsections > 0) */
3487 wfree(tb
->sections
);
3492 void WMSetTextForegroundColor(WMText
* tPtr
, WMColor
* color
)
3495 WMReleaseColor(tPtr
->fgColor
);
3497 tPtr
->fgColor
= WMRetainColor(color
? color
: tPtr
->view
->screen
->black
);
3502 void WMSetTextBackgroundColor(WMText
* tPtr
, WMColor
* color
)
3505 WMReleaseColor(tPtr
->bgColor
);
3507 tPtr
->bgColor
= WMRetainColor(color
? color
: tPtr
->view
->screen
->white
);
3508 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->bgColor
);
3513 void WMSetTextBackgroundPixmap(WMText
* tPtr
, WMPixmap
* pixmap
)
3516 WMReleasePixmap(tPtr
->bgPixmap
);
3519 tPtr
->bgPixmap
= WMRetainPixmap(pixmap
);
3521 tPtr
->bgPixmap
= NULL
;
3524 void WMSetTextRelief(WMText
* tPtr
, WMReliefType relief
)
3526 tPtr
->flags
.relief
= relief
;
3527 textDidResize(tPtr
->view
->delegate
, tPtr
->view
);
3530 void WMSetTextHasHorizontalScroller(WMText
* tPtr
, Bool shouldhave
)
3532 if (shouldhave
&& !tPtr
->hS
) {
3533 tPtr
->hS
= WMCreateScroller(tPtr
);
3534 (W_VIEW(tPtr
->hS
))->attribs
.cursor
= tPtr
->view
->screen
->defaultCursor
;
3535 (W_VIEW(tPtr
->hS
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
3536 WMSetScrollerArrowsPosition(tPtr
->hS
, WSAMinEnd
);
3537 WMSetScrollerAction(tPtr
->hS
, scrollersCallBack
, tPtr
);
3538 WMMapWidget(tPtr
->hS
);
3539 } else if (!shouldhave
&& tPtr
->hS
) {
3540 WMUnmapWidget(tPtr
->hS
);
3541 WMDestroyWidget(tPtr
->hS
);
3547 textDidResize(tPtr
->view
->delegate
, tPtr
->view
);
3550 void WMSetTextHasRuler(WMText
* tPtr
, Bool shouldhave
)
3552 if (shouldhave
&& !tPtr
->ruler
) {
3553 tPtr
->ruler
= WMCreateRuler(tPtr
);
3554 (W_VIEW(tPtr
->ruler
))->attribs
.cursor
= tPtr
->view
->screen
->defaultCursor
;
3555 (W_VIEW(tPtr
->ruler
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
3556 WMSetRulerReleaseAction(tPtr
->ruler
, rulerReleaseCallBack
, tPtr
);
3557 WMSetRulerMoveAction(tPtr
->ruler
, rulerMoveCallBack
, tPtr
);
3558 } else if (!shouldhave
&& tPtr
->ruler
) {
3559 WMShowTextRuler(tPtr
, False
);
3560 WMDestroyWidget(tPtr
->ruler
);
3563 textDidResize(tPtr
->view
->delegate
, tPtr
->view
);
3566 void WMShowTextRuler(WMText
* tPtr
, Bool show
)
3571 if (tPtr
->flags
.monoFont
)
3574 tPtr
->flags
.rulerShown
= show
;
3576 WMMapWidget(tPtr
->ruler
);
3578 WMUnmapWidget(tPtr
->ruler
);
3581 textDidResize(tPtr
->view
->delegate
, tPtr
->view
);
3584 Bool
WMGetTextRulerShown(WMText
* tPtr
)
3589 return tPtr
->flags
.rulerShown
;
3592 void WMSetTextHasVerticalScroller(WMText
* tPtr
, Bool shouldhave
)
3594 if (shouldhave
&& !tPtr
->vS
) {
3595 tPtr
->vS
= WMCreateScroller(tPtr
);
3596 (W_VIEW(tPtr
->vS
))->attribs
.cursor
= tPtr
->view
->screen
->defaultCursor
;
3597 (W_VIEW(tPtr
->vS
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
3598 WMSetScrollerArrowsPosition(tPtr
->vS
, WSAMaxEnd
);
3599 WMSetScrollerAction(tPtr
->vS
, scrollersCallBack
, tPtr
);
3600 WMMapWidget(tPtr
->vS
);
3601 } else if (!shouldhave
&& tPtr
->vS
) {
3602 WMUnmapWidget(tPtr
->vS
);
3603 WMDestroyWidget(tPtr
->vS
);
3609 textDidResize(tPtr
->view
->delegate
, tPtr
->view
);
3612 Bool
WMScrollText(WMText
* tPtr
, int amount
)
3614 Bool scroll
= False
;
3616 if (amount
== 0 || !tPtr
->view
->flags
.realized
)
3620 if (tPtr
->vpos
> 0) {
3621 if (tPtr
->vpos
> abs(amount
))
3622 tPtr
->vpos
+= amount
;
3628 int limit
= tPtr
->docHeight
- tPtr
->visible
.h
;
3629 if (tPtr
->vpos
< limit
) {
3630 if (tPtr
->vpos
< limit
- amount
)
3631 tPtr
->vpos
+= amount
;
3638 if (scroll
&& tPtr
->vpos
!= tPtr
->prevVpos
) {
3639 updateScrollers(tPtr
);
3642 tPtr
->prevVpos
= tPtr
->vpos
;
3646 Bool
WMPageText(WMText
* tPtr
, Bool direction
)
3648 if (!tPtr
->view
->flags
.realized
)
3651 return WMScrollText(tPtr
, direction
? tPtr
->visible
.h
: -tPtr
->visible
.h
);
3654 void WMSetTextEditable(WMText
* tPtr
, Bool editable
)
3656 tPtr
->flags
.editable
= editable
;
3659 int WMGetTextEditable(WMText
* tPtr
)
3661 return tPtr
->flags
.editable
;
3664 void WMSetTextIndentNewLines(WMText
* tPtr
, Bool indent
)
3666 tPtr
->flags
.indentNewLine
= indent
;
3669 void WMSetTextIgnoresNewline(WMText
* tPtr
, Bool ignore
)
3671 tPtr
->flags
.ignoreNewLine
= ignore
;
3674 Bool
WMGetTextIgnoresNewline(WMText
* tPtr
)
3676 return tPtr
->flags
.ignoreNewLine
;
3679 void WMSetTextUsesMonoFont(WMText
* tPtr
, Bool mono
)
3682 if (tPtr
->flags
.rulerShown
)
3683 WMShowTextRuler(tPtr
, False
);
3684 if (tPtr
->flags
.alignment
!= WALeft
)
3685 tPtr
->flags
.alignment
= WALeft
;
3688 tPtr
->flags
.monoFont
= mono
;
3689 textDidResize(tPtr
->view
->delegate
, tPtr
->view
);
3692 Bool
WMGetTextUsesMonoFont(WMText
* tPtr
)
3694 return tPtr
->flags
.monoFont
;
3697 void WMSetTextDefaultFont(WMText
* tPtr
, WMFont
* font
)
3700 WMReleaseFont(tPtr
->dFont
);
3703 tPtr
->dFont
= WMRetainFont(font
);
3705 tPtr
->dFont
= WMSystemFontOfSize(tPtr
->view
->screen
, 12);
3709 WMFont
*WMGetTextDefaultFont(WMText
* tPtr
)
3711 return WMRetainFont(tPtr
->dFont
);
3714 void WMSetTextDefaultColor(WMText
* tPtr
, WMColor
* color
)
3717 WMReleaseColor(tPtr
->dColor
);
3720 tPtr
->dColor
= WMRetainColor(color
);
3722 tPtr
->dColor
= WMBlackColor(tPtr
->view
->screen
);
3726 WMColor
*WMGetTextDefaultColor(WMText
* tPtr
)
3728 return tPtr
->dColor
;
3731 void WMSetTextAlignment(WMText
* tPtr
, WMAlignment alignment
)
3733 if (tPtr
->flags
.monoFont
)
3734 tPtr
->flags
.alignment
= WALeft
;
3736 tPtr
->flags
.alignment
= alignment
;
3740 int WMGetTextInsertType(WMText
* tPtr
)
3742 return tPtr
->flags
.prepend
;
3745 void WMSetTextSelectionColor(WMText
* tPtr
, WMColor
* color
)
3747 setSelectionProperty(tPtr
, NULL
, color
, -1);
3750 WMColor
*WMGetTextSelectionColor(WMText
* tPtr
)
3754 tb
= tPtr
->currentTextBlock
;
3756 if (!tb
|| !tPtr
->flags
.ownsSelection
)
3765 void WMSetTextSelectionFont(WMText
* tPtr
, WMFont
* font
)
3767 setSelectionProperty(tPtr
, font
, NULL
, -1);
3770 WMFont
*WMGetTextSelectionFont(WMText
* tPtr
)
3774 tb
= tPtr
->currentTextBlock
;
3776 if (!tb
|| !tPtr
->flags
.ownsSelection
)
3783 tb
= getFirstNonGraphicBlockFor(tb
, 1);
3787 return (tb
->selected
? tb
->d
.font
: NULL
);
3790 void WMSetTextSelectionUnderlined(WMText
* tPtr
, int underlined
)
3793 if (underlined
!= 0 && underlined
!= 1)
3796 setSelectionProperty(tPtr
, NULL
, NULL
, underlined
);
3799 int WMGetTextSelectionUnderlined(WMText
* tPtr
)
3803 tb
= tPtr
->currentTextBlock
;
3805 if (!tb
|| !tPtr
->flags
.ownsSelection
)
3811 return tb
->underlined
;
3814 void WMFreezeText(WMText
* tPtr
)
3816 tPtr
->flags
.frozen
= True
;
3819 void WMThawText(WMText
* tPtr
)
3821 tPtr
->flags
.frozen
= False
;
3823 if (tPtr
->flags
.monoFont
) {
3824 int j
, c
= WMGetArrayItemCount(tPtr
->gfxItems
);
3827 /* make sure to unmap widgets no matter where they are */
3828 /* they'll be later remapped if needed by paintText */
3829 for (j
= 0; j
< c
; j
++) {
3830 if ((tb
= (TextBlock
*) WMGetFromArray(tPtr
->gfxItems
, j
))) {
3831 if (tb
->object
&& ((W_VIEW(tb
->d
.widget
))->flags
.mapped
))
3832 WMUnmapWidget(tb
->d
.widget
);
3837 tPtr
->flags
.laidOut
= False
;
3838 layOutDocument(tPtr
);
3839 updateScrollers(tPtr
);
3841 tPtr
->flags
.needsLayOut
= False
;
3845 /* find first occurence of a string */
3846 static const char *mystrstr(const char *haystack
, const char *needle
, unsigned short len
, const char *end
, Bool caseSensitive
)
3850 if (!haystack
|| !needle
|| !end
)
3853 for (ptr
= haystack
; ptr
< end
; ptr
++) {
3854 if (caseSensitive
) {
3855 if (*ptr
== *needle
&& !strncmp(ptr
, needle
, len
))
3859 if (tolower(*ptr
) == tolower(*needle
) && !strncasecmp(ptr
, needle
, len
))
3867 /* find last occurence of a string */
3868 static const char *mystrrstr(const char *haystack
, const char *needle
, unsigned short len
, const char *end
, Bool caseSensitive
)
3872 if (!haystack
|| !needle
|| !end
)
3875 for (ptr
= haystack
- 2; ptr
> end
; ptr
--) {
3876 if (caseSensitive
) {
3877 if (*ptr
== *needle
&& !strncmp(ptr
, needle
, len
))
3880 if (tolower(*ptr
) == tolower(*needle
) && !strncasecmp(ptr
, needle
, len
))
3888 Bool
WMFindInTextStream(WMText
* tPtr
, const char *needle
, Bool direction
, Bool caseSensitive
)
3891 const char *mark
= NULL
;
3895 if (!(tb
= tPtr
->currentTextBlock
)) {
3896 if (!(tb
= ((direction
> 0) ? tPtr
->firstTextBlock
: tPtr
->lastTextBlock
))) {
3900 /* if(tb != ((direction>0) ?tPtr->firstTextBlock : tPtr->lastTextBlock))
3901 tb = (direction>0) ? tb->next : tb->prior; */
3902 if (tb
!= tPtr
->lastTextBlock
)
3906 tb
= tPtr
->currentTextBlock
;
3912 if (direction
> 0) {
3913 if (pos
+ 1 < tb
->used
)
3916 if (tb
->used
- pos
> 0 && pos
> 0) {
3917 mark
= mystrstr(&tb
->text
[pos
], needle
,
3918 strlen(needle
), &tb
->text
[tb
->used
], caseSensitive
);
3931 mark
= mystrrstr(&tb
->text
[pos
], needle
,
3932 strlen(needle
), tb
->text
, caseSensitive
);
3943 WMFont
*font
= tPtr
->flags
.monoFont
? tPtr
->dFont
: tb
->d
.font
;
3945 tPtr
->tpos
= (int)(mark
- tb
->text
);
3946 tPtr
->currentTextBlock
= tb
;
3947 updateCursorPosition(tPtr
);
3948 tPtr
->sel
.y
= tPtr
->cursor
.y
+ 5;
3949 tPtr
->sel
.h
= tPtr
->cursor
.h
- 10;
3950 tPtr
->sel
.x
= tPtr
->cursor
.x
+ 1;
3951 tPtr
->sel
.w
= WMIN(WMWidthOfString(font
,
3952 &tb
->text
[tPtr
->tpos
], strlen(needle
)),
3953 tPtr
->docWidth
- tPtr
->sel
.x
);
3954 tPtr
->flags
.ownsSelection
= True
;
3961 tb
= (direction
> 0) ? tb
->next
: tb
->prior
;
3963 pos
= (direction
> 0) ? 0 : tb
->used
;
3970 Bool
WMReplaceTextSelection(WMText
* tPtr
, char *replacement
)
3972 if (!tPtr
->flags
.ownsSelection
)
3975 removeSelection(tPtr
);
3978 insertTextInteractively(tPtr
, replacement
, strlen(replacement
));
3979 updateCursorPosition(tPtr
);