1 /* WMText: multi-line/font/color text widget for WINGs */
2 /* Copyleft (>) 1999, 2000 Nwanua Elumeze <nwanua@colorado.edu> */
13 /* if monoFont, ignore pixmaps, colors, fonts, script, underline */
18 #include <X11/keysym.h>
19 #include <X11/Xatom.h>
40 void wgdbFree(void *ptr
)
43 printf("err... cannot ");
44 printf("gdbFree [%p]\n", ptr
);
49 /* Why single-linked and not say double-linked?
50 99% of the time (draw, append), the "prior"
51 member would have been a useless memory and CPU overhead,
52 and deletes _are_ relatively infrequent.
53 When the "prior" member needs to be used, the overhead of
54 doing things the hard way will be incurred... but seldomly. */
57 /* a Chunk is a single-linked list of chunks containing:
58 * o text with a given format
62 typedef struct _Chunk
{
63 char *text
; /* the text in the chunk */
64 WMPixmap
*pixmap
; /* OR the pixmap it holds */
65 short chars
; /* the number of characters in this chunk */
66 short mallocedSize
; /* the number of characters that can be held */
68 WMFont
*font
; /* the chunk's font */
69 WMColor
*color
; /* the chunk's color */
70 short ul
:1; /* underlined or not */
71 ChunkType type
:1; /* a "Text" or "Image" chunk */
72 short script
:4; /* script in points: negative for subscript */
78 struct _Chunk
*next
; /*the next member in this list */
84 /* a Paragraph is a singly-linked list of paragraphs containing:
85 * o a list of chunks in that paragraph
86 * o the formats for that paragraph
87 * o its (draw) position relative to the entire document
89 typedef struct _Paragraph
{
90 Chunk
*chunks
; /* the list of text and/or image chunks */
91 short fmargin
; /* the start position of the first line */
92 short bmargin
; /* the start positions of the rest of the lines */
93 short rmargin
; /* the end position of the entire paragraph */
94 short numTabs
; /* the number of tabstops */
95 short *tabstops
; /* an array of tabstops */
97 Pixmap drawbuffer
; /* the pixmap onto which the (entire)
98 paragraph will be drawn */
99 WMPixmap
*bulletPix
; /* the pixmap to use for bulleting */
100 int top
; /* the top of the paragraph relative to document */
101 int bottom
; /* the bottom of the paragraph relative to document */
102 int width
; /* the width of the paragraph */
103 int height
; /* the height of the paragraph */
104 WMAlignment align
:2; /* justification of this paragraph */
107 struct _Paragraph
*next
; /* the next member in this list */
111 static char *default_bullet
[] =
114 " c None s None", ". c black",
115 "X c white", "o c #808080",
124 /* this is really a shrunk down version of the original
125 "broken" icon... I did not draw it, I simply shrunk it */
126 static char *unk_xpm
[] =
129 " c None", ". c #0B080C", "+ c #13A015", "@ c #5151B8",
130 "# c #992719", "$ c #5B1C20", "% c #1DF51D", "& c #D1500D", "* c #2F304A",
131 "= c #0C6A0C", "- c #F2F1DE", "; c #D59131", "> c #B2B083", ", c #DD731A",
132 "' c #CC3113", ") c #828238", "! c #6A6A94",
133 "......!@@@@@@@....$$....",
134 "...@!@@@@@@@**...$#'....",
135 "..!!@@@@@@@@.......#....",
136 "..!@@@@@@@@@*.......$...",
137 ".!@@@#,,#*@@*..*>.*.#...",
138 "*@@@@#'',,@@@...---!....",
139 "!@@@@@*.#;*@@..!--->....",
140 "@@@@@@@@#,.@@..!----@...",
141 "!@@@@@@*#;'$...!----@...",
142 "*@@@@@@..'&;;#.)----)...",
143 ".@@@@@@..$..&'.>----)...",
144 ".@@@@@@**---,'>-----!...",
145 ".@@@@@@**---,'>-----@...",
146 "..@@@@@@@---;;;,;---....",
147 "..*@@@@*@--->#',;,-*.)..",
148 "........)---->)@;#!..>..",
149 ".....)----------;$..>)..",
150 "=%%%*.*!-------);..)-*..",
151 "=%%%%+...*)>!@*$,.>--...",
152 "*+++++++.......*$@-->...",
153 "............**@)!)>->...",
154 "........................",
155 "........................",
156 "........................"
159 typedef struct W_Text
{
160 W_Class widgetClass
; /* the class number of this widget */
161 W_View
*view
; /* the view referring to this instance */
162 WMColor
*bg
; /* the background color to use when drawing */
164 WMRuler
*ruler
; /* the ruler subwiget to maipulate paragraphs */
166 WMScroller
*hscroller
; /* the horizontal scroller */
167 short hpos
; /* the current horizontal position */
168 short prevHpos
; /* the previous horizontal position */
170 WMScroller
*vscroller
; /* the vertical scroller */
171 int vpos
; /* the current vertical position */
172 int prevVpos
; /* the previous vertical position */
174 int visibleW
; /* the actual horizontal space available */
175 int visibleH
; /* the actual vertical space available */
177 Paragraph
*paragraphs
; /* the linked list of the paragraphs in the doc. */
178 int docWidth
; /* the width of the entire document */
179 int docHeight
; /* the height of the entire document */
181 WMFont
*dFont
; /* the default font */
182 WMColor
*dColor
; /* the default color */
183 WMPixmap
*dBulletPix
; /* the default pixmap for bullets */
184 WMPixmap
*dUnknownImg
; /* the pixmap for (missing/broken) images */
186 WMRect sRect
; /* the selected area */
187 Paragraph
*currentPara
; /* the current paragraph, in which actions occur */
188 Chunk
*currentChunk
; /* the current chunk, about which actions occur */
189 short tpos
; /* the cursor position (text position) */
190 WMParseAction
*parser
; /* what action to use to parse input text */
191 WMParseAction
*writer
; /* what action to use to write text */
192 WMParserActions funcs
; /* the "things" that parsers/writers might do */
193 XPoint clicked
; /* the position of the last mouse click */
194 XPoint cursor
; /* where the cursor is "placed" */
195 short clheight
; /* the height of the "line" clicked on */
196 short clwidth
; /* the width of the "line" clicked on */
198 WMReliefType relief
:3; /* the relief to display with */
199 Wrapping wrapping
:2; /* the type of wrapping to use in drawing */
200 WMAlignment dAlignment
:2; /* default justification */
201 ushort monoFont
:1; /* whether to ignore "rich" commands */
202 ushort fixedPitch
:1; /* assume each char in dFont is the same size */
203 ushort editable
:1; /* whether to accept user changes or not */
204 ushort rulerShown
:1; /* whether the ruler is shown or not */
205 ushort cursorShown
:1; /* whether the cursor is currently being shown */
206 ushort frozen
:1; /* whether screen updates are to be made */
207 ushort focused
:1; /* whether this instance has input focus */
208 ushort pointerGrabbed
:1; /* whether this instance has the pointer */
209 ushort buttonHeld
:1; /* the user is still holding down the button */
210 ushort ignoreNewLine
:1; /* whether to ignore the newline character */
211 ushort waitingForSelection
:1; /* whether there is a pending paste event */
212 ushort ownsSelection
:1; /* whether it ownz the current selection */
213 ushort findingClickPoint
:1; /* whether the search for a clickpoint is on */
214 ushort foundClickPoint
:1; /* whether the clickpoint has been found */
215 ushort hasVscroller
:1; /* whether to enable the vertical scroller */
216 ushort hasHscroller
:1; /* whether to enable the horizontal scroller */
222 /* --------- static functions that are "private". don't touch :-) --------- */
225 /* max "characters per chunk that will be drawn at a time" */
226 #define MAX_WORD_LENGTH 100
229 #define MIN_DOC_WIDTH 200
231 typedef struct _LocalMargins
{
232 short left
, right
, first
, body
;
235 typedef struct _MyTextItems
{
236 char text
[MAX_WORD_LENGTH
+ 1];
240 Chunk
*chunk
; /* used for "click" events */
241 short start
; /* ditto... where in the chunk we start (ie. wrapped chunk) */
248 chunkSelectionRect(Text
* tPtr
, Paragraph
* para
, MyTextItems item
,
249 short y
, short j
, short lh
)
252 short type
= 0; /* 0:none 1:partial: 2:all */
253 short rh
= (tPtr
->rulerShown
) ? 45 : 5;
255 WMFont
*font
= (tPtr
->monoFont
|| item
.chunk
->type
!= ctText
) ?
256 tPtr
->dFont
: item
.chunk
->font
;
259 if (y
+ para
->top
+ rh
> tPtr
->sRect
.pos
.y
+ tPtr
->sRect
.size
.height
260 || y
+ para
->top
+ rh
+ lh
< tPtr
->sRect
.pos
.y
)
263 if (item
.chunk
->type
== ctText
)
264 w
= WMWidthOfString(font
, item
.text
, item
.chars
);
266 w
= item
.chunk
->pixmap
->width
;
268 if (y
+ para
->top
+ rh
>= tPtr
->sRect
.pos
.y
&& (y
+ para
->top
+ rh
+ lh
269 <= tPtr
->sRect
.pos
.y
+ tPtr
->sRect
.size
.height
))
270 //&& item.x+j >= tPtr->sRect.pos.x+tPtr->sRect.size.width))
277 if (item
.x
+ j
>= tPtr
->sRect
.pos
.x
&&
278 item
.x
+ j
+ w
< tPtr
->sRect
.pos
.x
+ tPtr
->sRect
.size
.width
)
281 if (type
== 1 && y
+ para
->top
+ rh
+ lh
<=
282 tPtr
->sRect
.pos
.y
+ tPtr
->sRect
.size
.height
)
287 if (type
== 1 && item
.chunk
->type
== ctText
) { /* partial coverage */
288 lm
= 2 + WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
289 /* even I am still confused, so don't ask please */
290 if ((item
.x
+ j
+ lm
>= tPtr
->sRect
.pos
.x
&&
291 item
.x
+ j
+ lm
<= tPtr
->sRect
.pos
.x
+ tPtr
->sRect
.size
.width
)
292 || (item
.x
+ j
+ lm
>= tPtr
->sRect
.pos
.x
+ tPtr
->sRect
.size
.width
293 && y
+ para
->top
+ rh
+ lh
<=
294 tPtr
->sRect
.pos
.y
+ tPtr
->sRect
.size
.height
)
295 || (tPtr
->sRect
.pos
.y
< y
+ para
->top
+ rh
296 && tPtr
->sRect
.pos
.x
+ tPtr
->sRect
.size
.width
>
299 rect
.pos
.x
= item
.x
+ j
;
300 item
.chunk
->selected
= True
;
301 if (item
.chunk
->chars
> 6) {
302 item
.chunk
->sStart
= 3;
303 item
.chunk
->sEnd
= item
.chunk
->chars
;
305 item
.chunk
->sStart
= 0;
306 item
.chunk
->sEnd
= item
.chunk
->chars
;
309 } else if (type
== 2) {
310 rect
.pos
.x
= item
.x
+ j
;
311 item
.chunk
->selected
= True
;
312 if (item
.chunk
->type
== ctText
) {
313 item
.chunk
->sStart
= 0;
314 item
.chunk
->sStart
= item
.chunk
->chars
;
315 rect
.size
.width
= WMWidthOfString(font
,
316 item
.text
, item
.chars
);
318 rect
.size
.width
= item
.chunk
->pixmap
->width
;
322 rect
.size
.height
= lh
;
326 static int myDrawText(Text
* tPtr
, Paragraph
* para
, MyTextItems
* items
,
327 short nitems
, short pwidth
, int y
, short draw
, short spacepos
)
329 short i
, ul_thick
, u
, j
= 0; /* j = justification */
330 short line_width
= 0, line_height
= 0, mx_descent
= 0;
331 WMScreen
*screen
= tPtr
->view
->screen
;
335 if (tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
)
337 for (i
= 0; i
< nitems
; i
++) {
338 if (items
[i
].type
== ctText
) {
339 font
= (tPtr
->monoFont
) ? tPtr
->dFont
: items
[i
].chunk
->font
;
340 mx_descent
= WMIN(mx_descent
, -font
->y
);
341 line_height
= WMAX(line_height
, font
->height
);
342 //printf("chunk.x %d xpoint.x %d\n",
343 // items[i].x, tPtr->clicked.x);
345 line_width
+= WMWidthOfString(font
,
346 items
[i
].text
, items
[i
].chars
);
348 mx_descent
= WMIN(mx_descent
, -(items
[i
].pix
->height
- 3));
349 /* replace -3 wif descent... */
350 line_height
= WMAX(line_height
, items
[i
].pix
->height
);
351 if (para
->align
== WARight
|| para
->align
== WACenter
) {
352 line_width
+= items
[i
].pix
->width
;
357 if (para
->align
== WARight
) {
358 j
= pwidth
- line_width
;
359 } else if (para
->align
== WACenter
) {
360 j
= (short) ((float) (pwidth
- line_width
)) / 2.0;
362 if (tPtr
->findingClickPoint
&& (y
+ line_height
>= tPtr
->clicked
.y
)) {
363 tPtr
->foundClickPoint
= True
;
364 tPtr
->currentChunk
= items
[0].chunk
; /* just first on this "line" */
365 tPtr
->tpos
= items
[0].start
; /* where to "start" counting from */
366 tPtr
->clicked
.x
= j
+ items
[0].x
;
367 tPtr
->clicked
.y
= y
+ line_height
+ mx_descent
;
368 tPtr
->clheight
= line_height
; /* to draw the cursor */
369 tPtr
->clwidth
= line_width
; /* where to stop searching */
375 for (i
= 0; i
< nitems
; i
++) {
378 if (tPtr
->ownsSelection
) {
379 WMRect rect
= chunkSelectionRect(tPtr
, para
,
380 items
[i
], y
, j
, line_height
);
382 if (rect
.pos
.x
!= -23) { /* has been selected */
383 XFillRectangle(tPtr
->view
->screen
->display
, para
->drawbuffer
,
384 WMColorGC(WMGrayColor(tPtr
->view
->screen
)),
385 rect
.pos
.x
, rect
.pos
.y
, rect
.size
.width
, rect
.size
.height
);
388 if (items
[i
].type
== ctText
) {
389 gc
= WMColorGC(items
[i
].chunk
->color
);
390 font
= (tPtr
->monoFont
) ? tPtr
->dFont
: items
[i
].chunk
->font
;
391 WMDrawString(screen
, para
->drawbuffer
, gc
, font
,
392 items
[i
].x
+ j
, y
- font
->y
- mx_descent
,
393 items
[i
].text
, items
[i
].chars
);
394 if (items
[i
].chunk
->ul
&& !tPtr
->monoFont
) {
395 ul_thick
= (short) ((float) font
->height
) / 12.0;
398 for (u
= 0; u
< ul_thick
; u
++) {
399 XDrawLine(screen
->display
, para
->drawbuffer
, gc
, items
[i
].x
+ j
,
400 y
+ 1 + u
- mx_descent
,
401 items
[i
].x
+ j
+ WMWidthOfString(font
,
402 items
[i
].text
, items
[i
].chars
), y
+ 1 + u
- mx_descent
);
407 WMDrawPixmap(items
[i
].pix
, para
->drawbuffer
, items
[i
].x
+ j
,
408 y
+ 3 - mx_descent
- items
[i
].pix
->height
);
414 static void drawPChunkPart(Text
* tPtr
, Chunk
* chunk
, LocalMargins m
, Paragraph
* para
,
415 MyTextItems
* items
, short *nitems
, short *Lmargin
, XPoint
* where
, short draw
)
422 chunk
->pixmap
= WMRetainPixmap(tPtr
->dUnknownImg
);
424 p_width
= m
.right
- WMIN(m
.first
, m
.body
) - WMGetRulerOffset(tPtr
->ruler
);
425 if (p_width
< MIN_DOC_WIDTH
) // need WMRuler to take care of this...
428 if (where
->x
+ chunk
->pixmap
->width
<= p_width
- *Lmargin
) {
429 /* it can fit on rest of line */
430 items
[*nitems
].pix
= chunk
->pixmap
;
431 items
[*nitems
].type
= ctImage
;
432 items
[*nitems
].chars
= 0;
433 items
[*nitems
].x
= *Lmargin
+ where
->x
;
434 items
[*nitems
].chunk
= chunk
;
435 items
[*nitems
].start
= 0;
437 if (*nitems
>= MAX_CHUNX
) {
438 items
[*nitems
].chars
= 0;
439 items
[*nitems
].x
= *Lmargin
+ where
->x
;
440 items
[*nitems
].chunk
= chunk
;
441 items
[*nitems
].start
= 0;
442 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+ 1,
443 p_width
- *Lmargin
, where
->y
, draw
, 0);
444 if (tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
)
450 where
->x
+= chunk
->pixmap
->width
;
452 } else if (chunk
->pixmap
->width
<= p_width
- *Lmargin
) {
453 /* it can fit on an entire line, flush the myDrawText then wrap it */
454 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+ 1,
455 p_width
- *Lmargin
, where
->y
, draw
, 0);
457 *Lmargin
= WMAX(0, m
.body
- m
.first
);
459 drawPChunkPart(tPtr
, chunk
, m
, para
, items
, nitems
,
460 Lmargin
, where
, draw
);
465 *Lmargin
= WMAX(0, m
.body
- m
.first
);
466 items
[*nitems
].pix
= chunk
->pixmap
;
467 items
[*nitems
].type
= ctImage
;
468 items
[*nitems
].chars
= 0;
469 items
[*nitems
].x
= *Lmargin
+ where
->x
;
470 items
[*nitems
].chunk
= chunk
;
471 items
[*nitems
].start
= 0;
472 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+ 1,
473 p_width
- *Lmargin
, where
->y
, draw
, 0);
476 /* scale image to fit, call self again */
477 /* deprecated - the management */
482 static void drawTChunkPart(Text
* tPtr
, Chunk
* chunk
, char *bufr
, LocalMargins m
,
483 Paragraph
* para
, MyTextItems
* items
, short *nitems
, short len
, short start
,
484 short *Lmargin
, XPoint
* where
, short draw
, short spacepos
)
486 short t_chunk_width
, p_width
, chars
;
487 WMFont
*font
= (tPtr
->monoFont
) ? tPtr
->dFont
: chunk
->font
;
489 /* if(doc->clickstart.yes && doc->clickstart.done) return; */
493 p_width
= m
.right
- WMIN(m
.first
, m
.body
);
494 if (p_width
< MIN_DOC_WIDTH
) // need WMRuler to take care of this...
499 t_chunk_width
= WMWidthOfString(font
, bufr
, len
);
500 if ((where
->x
+ t_chunk_width
<= p_width
- *Lmargin
)
501 || (tPtr
->wrapping
== wrNone
)) {
502 /* if it can fit on rest of line, append to line */
503 chars
= WMIN(len
, MAX_WORD_LENGTH
);
504 snprintf(items
[*nitems
].text
, chars
+ 1, "%s", bufr
);
505 items
[*nitems
].chars
= chars
;
506 items
[*nitems
].x
= *Lmargin
+ where
->x
;
507 items
[*nitems
].type
= ctText
;
508 items
[*nitems
].chunk
= chunk
;
509 items
[*nitems
].start
= start
;
511 if (*nitems
>= MAX_CHUNX
) {
512 chars
= WMIN(len
, MAX_WORD_LENGTH
);
513 snprintf(items
[*nitems
].text
, chars
+ 1, "%s", bufr
);
514 items
[*nitems
].chars
= chars
;
515 items
[*nitems
].x
= *Lmargin
+ where
->x
;
516 items
[*nitems
].type
= ctText
;
517 items
[*nitems
].chunk
= chunk
;
518 items
[*nitems
].start
= start
;
519 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+ 1,
520 p_width
- *Lmargin
, where
->y
, draw
, spacepos
);
521 if (tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
)
527 where
->x
+= t_chunk_width
;
529 } else if (t_chunk_width
<= p_width
- *Lmargin
) {
530 /* it can fit on an entire line, flush and wrap it to a new line */
531 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
,
532 p_width
- *Lmargin
, where
->y
, draw
, spacepos
);
533 if (tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
)
536 *Lmargin
= WMAX(0, m
.body
- m
.first
);
538 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
, items
, nitems
,
539 len
, start
, Lmargin
, where
, draw
, spacepos
);
541 /* otherwise, chop line, call ourself recursively until it's all gone */
542 short J
= 0; /* bufr */
543 short j
= 0; /* local tmp buffer */
545 short diff
= p_width
- *Lmargin
- where
->x
;
549 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
,
550 p_width
- *Lmargin
, where
->y
, draw
, spacepos
);
551 if (tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
)
554 *Lmargin
= WMAX(0, m
.body
- m
.first
);
556 diff
= p_width
- *Lmargin
- where
->x
;
558 for (J
= 0; J
< len
; J
++) {
560 if (WMWidthOfString(font
, tmp
, j
+ 1) > diff
) {
561 drawTChunkPart(tPtr
, chunk
, tmp
, m
, para
, items
, nitems
,
562 j
, start
+ _start
, Lmargin
, where
, draw
, spacepos
);
569 /* and there's always that last chunk, get it too */
570 drawTChunkPart(tPtr
, chunk
, tmp
, m
, para
, items
, nitems
,
571 j
, start
+ _start
, Lmargin
, where
, draw
, spacepos
);
575 /* this function does what it's called :-)
576 o It is also used for calculating extents of para,
577 (returns height) so watch out for (Bool) draw
578 o Also used to determine where mouse was clicked */
579 static int putParagraphOnPixmap(Text
* tPtr
, Paragraph
* para
, Bool draw
)
581 char bufr
[MAX_WORD_LENGTH
+ 1]; /* a single word + '\0' */
582 MyTextItems items
[MAX_CHUNX
+ 1];
583 short lmargin
, spacepos
, i
, s
, nitems
, start
;
588 if (!tPtr
->view
->flags
.realized
|| !para
)
591 where
.x
= 0, where
.y
= 0, nitems
= 0;
592 m
.left
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
593 m
.right
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) - m
.left
;
594 m
.first
= para
->fmargin
, m
.body
= para
->bmargin
;
597 W_Screen
*screen
= tPtr
->view
->screen
;
599 if (para
->drawbuffer
)
600 XFreePixmap(screen
->display
, para
->drawbuffer
);
601 if (para
->width
< 2 * tPtr
->dFont
->height
)
602 para
->width
= 2 * tPtr
->dFont
->height
;
603 if (para
->height
< tPtr
->dFont
->height
)
604 para
->height
= tPtr
->dFont
->height
;
605 para
->drawbuffer
= XCreatePixmap(screen
->display
,
606 tPtr
->view
->window
, para
->width
, para
->height
, screen
->depth
);
607 XFillRectangle(screen
->display
, para
->drawbuffer
,
608 WMColorGC(tPtr
->bg
), 0, 0, para
->width
, para
->height
);
611 //if(para->align != tPtr->dAlignment)
612 // para->align = tPtr->dAlignment;
614 /* draw the bullet if appropriate */
615 if (m
.body
> m
.first
&& !tPtr
->monoFont
) {
616 lmargin
= m
.body
- m
.first
;
619 WMDrawPixmap(para
->bulletPix
, para
->drawbuffer
, lmargin
- 10, 5);
621 WMDrawPixmap(tPtr
->dBulletPix
, para
->drawbuffer
, lmargin
- 10, 5);
623 /* NeXT sez next tab, I say the m.body - m.first margin */
625 lmargin
= WMAX(0, m
.first
- m
.body
);
628 if (tPtr
->findingClickPoint
&& !para
->chunks
) {
629 tPtr
->currentChunk
= NULL
;
630 tPtr
->foundClickPoint
= True
;
632 tPtr
->clicked
.x
= lmargin
;
634 tPtr
->clheight
= para
->height
;
638 chunk
= para
->chunks
;
641 if (tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
)
644 if (chunk
->type
== ctImage
&& !tPtr
->monoFont
) {
645 drawPChunkPart(tPtr
, chunk
, m
, para
, items
, &nitems
,
646 &lmargin
, &where
, draw
);
647 } else if (chunk
->text
&& chunk
->type
== ctText
) {
648 if (tPtr
->wrapping
== wrNone
) {
649 drawTChunkPart(tPtr
, chunk
, chunk
->text
, m
, para
, items
, &nitems
,
650 chunk
->chars
, 0, &lmargin
, &where
, draw
, spacepos
);
651 } else if (tPtr
->wrapping
== wrWord
) {
652 spacepos
= 0, i
= 0, start
= 0;
653 while (spacepos
< chunk
->chars
) {
654 bufr
[i
] = chunk
->text
[spacepos
];
655 if (bufr
[i
] == ' ' || i
>= MAX_WORD_LENGTH
) {
660 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
,
661 items
, &nitems
, i
+ s
, start
, &lmargin
, &where
,
663 start
= spacepos
+ s
;
664 if (i
> MAX_WORD_LENGTH
- 1)
671 /* catch that last onery one. */
672 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
,
673 items
, &nitems
, i
, start
, &lmargin
, &where
, draw
, spacepos
);
678 /* we might have a few leftover items that need drawing */
680 where
.y
+= myDrawText(tPtr
, para
, items
,
681 nitems
, m
.right
- m
.left
- lmargin
, where
.y
, draw
, spacepos
);
682 if (tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
)
688 static int calcParaExtents(Text
* tPtr
, Paragraph
* para
)
693 if (tPtr
->monoFont
) {
694 para
->width
= tPtr
->visibleW
;
697 para
->rmargin
= tPtr
->visibleW
;
699 para
->width
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) -
700 WMIN(para
->fmargin
, para
->bmargin
) -
701 WMGetRulerOffset(tPtr
->ruler
);
705 para
->height
= tPtr
->dFont
->height
;
707 para
->height
= putParagraphOnPixmap(tPtr
, para
, False
);
709 if (para
->height
< tPtr
->dFont
->height
)
710 para
->height
= tPtr
->dFont
->height
;
711 para
->bottom
= para
->top
+ para
->height
;
716 /* rather than bother with redrawing _all_ the pixmaps, simply
717 rearrange (i.e., push down or pull up) paragraphs after this one */
718 static void affectNextParas(Text
* tPtr
, Paragraph
* para
, int move_y
)
723 if (!para
|| move_y
== 0)
726 old_y
= para
->bottom
;
727 calcParaExtents(tPtr
, para
);
728 old_y
-= para
->bottom
;
739 next
->bottom
= next
->top
+ next
->height
;
740 next
= next
->next
; // I know, I know
743 tPtr
->docHeight
+= move_y
;
746 tPtr
->vpos
+= move_y
;
749 if (tPtr
->vpos
> tPtr
->docHeight
- tPtr
->visibleH
)
750 tPtr
->vpos
= tPtr
->docHeight
- tPtr
->visibleH
;
756 static void calcDocExtents(Text
* tPtr
)
760 if (tPtr
->monoFont
) {
761 tPtr
->docWidth
= tPtr
->visibleW
;
763 tPtr
->docWidth
= WMGetRulerMargins(tPtr
->ruler
, WRulerRight
) -
764 WMGetRulerMargins(tPtr
->ruler
, WRulerDocLeft
);
767 para
= tPtr
->paragraphs
;
770 para
->top
= tPtr
->docHeight
;
771 tPtr
->docHeight
+= calcParaExtents(tPtr
, para
);
772 para
->bottom
= tPtr
->docHeight
;
775 } else { /* default to this if no paragraphs */
776 tPtr
->docHeight
= tPtr
->dFont
->height
;
779 if (tPtr
->editable
) /* add space at bottom to enter new stuff */
780 tPtr
->docHeight
+= tPtr
->dFont
->height
;
785 /* If any part of a paragraph is viewable, the entire
786 paragraph is drawn on an otherwise empty (XFreePixmap) pixmap.
787 The actual viewable parts of the paragraph(s) are then pieced
788 together via paintText:
790 -------------------------------------------
791 || this is a paragraph in this document||
792 ||========================================||
793 || | only part of it is visible though. ||
794 || |-------------------------------------||
795 ||[.| This is another paragraph ||
796 || | which I'll make relatively long ||
797 || | just for the sake of writing a long ||
798 || | paragraph with a picture: ^_^ ||
799 || |-------------------------------------||
800 ||==| Of the three paragraphs, only ||
801 ||/\| the preceding was totally copied to ||
802 ||\/| totally copied to the window, even ||
803 ==========================================||
804 though they are all on pixmaps.
805 -------------------------------------------
806 This paragraph exists only in
807 memory and so has a NULL pixmap.
808 -------------------------------------------
811 simple, right? Performance: the best of both worlds...
812 o fast scrolling: no need to rewrite what's already
813 on the screen, simply XCopy it.
814 o fast typing: only change current para, then simply
815 affect other (i.e., subsequent) paragraphs.
816 o If no part of para is on screen, gdbFree pixmap; else draw on
817 individual pixmap per para then piece several paras together
818 o Keep track of who to XCopy to window (see paintText) */
819 static void drawDocumentPartsOnPixmap(Text
* tPtr
, Bool all
)
823 para
= tPtr
->paragraphs
;
825 /* the 32 reduces jitter on the human eye by preparing paragraphs
826 in anticipation of when the _moving_ scrollbar reaches them */
827 if (para
->bottom
+ 32 < tPtr
->vpos
||
828 para
->top
> tPtr
->visibleH
+ tPtr
->vpos
+ 32) {
829 if (para
->drawbuffer
) {
830 XFreePixmap(tPtr
->view
->screen
->display
, para
->drawbuffer
);
831 para
->drawbuffer
= (Pixmap
) NULL
;
834 if (!para
->drawbuffer
|| all
)
835 putParagraphOnPixmap(tPtr
, para
, True
);
843 /* this function blindly copies the "visible" parts of a pragraph
844 unto the view, (top-down approach). It starts drawing from
845 the top of the view (which paragraph to draw is determined by
846 drawDocumentPartsOnPixmap); it stops at the bottom of the view. */
847 static void paintText(Text
* tPtr
)
849 short lmargin
, para_lmargin
;
850 int from
= 5, to
= 5, height
;
852 short vS
= 0, hS
= 0, rh
= 0;
854 if (!tPtr
->view
->flags
.realized
)
857 if (tPtr
->rulerShown
)
861 if (tPtr
->hasVscroller
)
863 if (tPtr
->hasHscroller
)
866 //XClearWindow(tPtr->view->screen->display, tPtr->view->window);
868 lmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
869 if (tPtr
->paragraphs
) {
870 para
= tPtr
->paragraphs
;
872 if (para
->drawbuffer
) {
873 from
= (para
->top
<= tPtr
->vpos
) ? tPtr
->vpos
- para
->top
: 0;
874 height
= para
->height
- from
;
875 if (from
>= 0 && height
> 0) {
876 para_lmargin
= WMIN(para
->fmargin
, para
->bmargin
);
877 if (lmargin
- vS
< WMIN(para
->fmargin
, para
->bmargin
)) {
879 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
880 lmargin
, to
, 2 + para_lmargin
, height
, False
);
882 XFillRectangle(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
883 WMColorGC(tPtr
->dColor
), lmargin
, to
, 2 + para_lmargin
, height
);
886 XCopyArea(tPtr
->view
->screen
->display
, para
->drawbuffer
,
887 tPtr
->view
->window
, WMColorGC(tPtr
->bg
), 0, from
,
888 para
->width
- 4, height
, lmargin
+ para_lmargin
+ 2, to
);
889 if ((to
+= height
) > tPtr
->visibleH
+ rh
)
897 /* clear any left over space (esp. during para deletes/ ruler changes) */
898 if (tPtr
->docHeight
< tPtr
->visibleH
&& tPtr
->visibleH
+ rh
+ 5 - to
> 0) {
899 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
, vS
, to
,
900 tPtr
->view
->size
.width
- vS
, tPtr
->visibleH
+ rh
+ hS
+ 5 - to
, False
);
903 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
904 vS
+ 1, rh
+ 5, lmargin
- vS
, tPtr
->visibleH
+ rh
+ 5 - vS
, False
);
907 // from the "selection" days...
908 W_DrawRelief(tPtr
->view
->screen
, WMWidgetXID(tPtr
),
909 tPtr
->sRect
.pos
.x
, tPtr
->sRect
.pos
.y
,
910 tPtr
->sRect
.size
.width
, tPtr
->sRect
.size
.height
, tPtr
->relief
);
913 W_DrawRelief(tPtr
->view
->screen
, WMWidgetXID(tPtr
), 0, rh
,
914 tPtr
->visibleW
+ vS
, tPtr
->visibleH
+ hS
, tPtr
->relief
);
916 if (tPtr
->editable
&& tPtr
->clheight
> 0) {
917 int top
= tPtr
->cursor
.y
- tPtr
->vpos
;
918 int bot
= top
+ tPtr
->clheight
;
923 if (bot
> tPtr
->visibleH
+ hS
- 2)
924 bot
= tPtr
->visibleH
+ hS
- 2;
926 //do something about italic text...
927 XDrawLine(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
928 WMColorGC(tPtr
->dColor
), lmargin
+ tPtr
->cursor
.x
, top
,
929 lmargin
+ tPtr
->cursor
.x
, bot
);
936 /* called anytime either the ruler, vscroller or hscroller is hidden/shown,
937 or when the widget is resized by some user action */
938 static void resizeText(W_ViewDelegate
* self
, WMView
* view
)
940 Text
*tPtr
= (Text
*) view
->self
;
943 if (!tPtr
->monoFont
&& tPtr
->rulerShown
)
946 W_ResizeView(view
, view
->size
.width
, view
->size
.height
);
947 WMResizeWidget(tPtr
->ruler
, view
->size
.width
, 40);
950 if (tPtr
->hasVscroller
) {
951 WMMoveWidget(tPtr
->vscroller
, 1, 1 + rh
);
952 WMResizeWidget(tPtr
->vscroller
, 20, view
->size
.height
- rh
- 2);
953 tPtr
->visibleW
= view
->size
.width
- 21;
955 if (tPtr
->hasHscroller
) {
956 WMMoveWidget(tPtr
->hscroller
, 20, view
->size
.height
- 21);
957 WMResizeWidget(tPtr
->hscroller
, view
->size
.width
- 21, 20);
958 tPtr
->visibleH
= view
->size
.height
- 21 - rh
;
960 tPtr
->visibleH
= view
->size
.height
- rh
;
962 tPtr
->visibleW
= view
->size
.width
;
963 if (tPtr
->hasHscroller
) {
964 WMMoveWidget(tPtr
->hscroller
, 1, view
->size
.height
- 21);
965 WMResizeWidget(tPtr
->hscroller
, view
->size
.width
- 2, 20);
966 tPtr
->visibleH
= view
->size
.height
- 21 - rh
;
968 tPtr
->visibleH
= view
->size
.width
- 2 - rh
;
970 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
973 W_ViewDelegate _TextViewDelegate
=
983 /* a plain text parser */
984 /* this gives useful hints on how to make a more
985 interesting parser for say HTML, RTF */
986 static void defaultParser(Text
* tPtr
, void *data
, short type
)
988 char *start
, *mark
, *text
= (char *) data
;
990 Paragraph
*para
= NULL
;
994 mark
= strchr(start
, '\n');
996 /* there is a newline, indicating the need for a new paragraph */
997 /* attach the chunk to the current paragraph */
998 if ((short) (mark
- start
) > 1) {
999 /* ignore chunks with just a single newline but still make a
1001 chunk
= (tPtr
->funcs
.createTChunk
) (start
, (short) (mark
- start
),
1002 tPtr
->dFont
, tPtr
->dColor
, 0, False
);
1003 (tPtr
->funcs
.insertChunk
) (tPtr
, chunk
, type
);
1005 /* _then_ create a new paragraph for the _next_ chunk */
1006 para
= (tPtr
->funcs
.createParagraph
) (0, 0, tPtr
->visibleW
,
1008 (tPtr
->funcs
.insertParagraph
) (tPtr
, para
, type
);
1011 /* just attach the chunk to the current paragraph */
1012 if (strlen(start
) > 0) {
1013 chunk
= (tPtr
->funcs
.createTChunk
) (start
, strlen(start
),
1014 tPtr
->dFont
, tPtr
->dColor
, 0, False
);
1015 (tPtr
->funcs
.insertChunk
) (tPtr
, chunk
, type
);
1023 static void updateScrollers(Text
* tPtr
)
1025 if (tPtr
->hasVscroller
) {
1026 if (tPtr
->docHeight
< tPtr
->visibleH
) {
1027 WMSetScrollerParameters(tPtr
->vscroller
, 0, 1);
1030 float vmax
= (float) (tPtr
->docHeight
);
1032 WMSetScrollerParameters(tPtr
->vscroller
,
1033 ((float) tPtr
->vpos
) / (vmax
- (float) tPtr
->visibleH
),
1034 (float) tPtr
->visibleH
/ vmax
);
1037 if (tPtr
->hasHscroller
);
1040 static void scrollersCallBack(WMWidget
* w
, void *self
)
1042 Text
*tPtr
= (Text
*) self
;
1043 Bool scroll
= False
;
1044 Bool dimple
= False
;
1046 if (!tPtr
->view
->flags
.realized
)
1049 if (w
== tPtr
->vscroller
) {
1053 vmax
= (float) (tPtr
->docHeight
);
1054 height
= tPtr
->visibleH
;
1056 height
-= 7; /* the top border (5) + bottom (2) */
1058 switch (WMGetScrollerHitPart(tPtr
->vscroller
)) {
1059 case WSDecrementLine
:
1060 if (tPtr
->vpos
> 0) {
1061 if (tPtr
->vpos
> 16)
1068 case WSIncrementLine
:{
1069 int limit
= tPtr
->docHeight
- height
;
1071 if (tPtr
->vpos
< limit
) {
1072 if (tPtr
->vpos
< limit
- 16)
1080 case WSDecrementPage
:
1081 tPtr
->vpos
-= height
;
1087 printf("dimple needs to jump to mouse location ;-/\n");
1089 case WSIncrementPage
:
1090 tPtr
->vpos
+= height
;
1091 if (tPtr
->vpos
> (tPtr
->docHeight
- height
))
1092 tPtr
->vpos
= tPtr
->docHeight
- height
;
1095 printf("dimple needs to jump to mouse location ;-/\n");
1100 tPtr
->vpos
= WMGetScrollerValue(tPtr
->vscroller
)
1101 * (float) (tPtr
->docHeight
- height
);
1108 float vmax
= (float) (tPtr
->docHeight
);
1110 ((float) tPtr
->vpos
) / (vmax
- (float) tPtr
->visibleH
),
1111 (float) tPtr
->visibleH
/ vmax
);
1112 dimple
= where mouse is
.
1116 scroll
= (tPtr
->vpos
!= tPtr
->prevVpos
);
1117 tPtr
->prevVpos
= tPtr
->vpos
;
1119 if (w
== tPtr
->hscroller
);
1121 //need scrollv || scrollh
1125 if(tPtr->rulerShown)
1126 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
1127 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
1129 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
1130 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
1133 updateScrollers(tPtr
);
1134 drawDocumentPartsOnPixmap(tPtr
, False
);
1141 W_InsertText(WMText
* tPtr
, void *data
, Bool prepend
) {
1145 Paragraph
*para
= tPtr
->paragraphs
, *ptmp
;
1146 Chunk
*chunk
, *ctmp
;
1149 chunk
= para
->chunks
;
1151 if (chunk
->type
== ctText
&& chunk
->text
)
1152 wgdbFree(chunk
->text
);
1153 else if (chunk
->pixmap
)
1154 WMReleasePixmap(chunk
->pixmap
);
1156 chunk
= chunk
->next
;
1161 if (ptmp
->drawbuffer
)
1162 XFreePixmap(tPtr
->view
->screen
->display
, ptmp
->drawbuffer
);
1165 tPtr
->paragraphs
= NULL
;
1166 tPtr
->currentPara
= NULL
;
1167 tPtr
->currentChunk
= NULL
;
1169 WMRefreshText(tPtr
, 0, 0);
1173 (tPtr
->parser
) (tPtr
, data
, prepend
);
1175 defaultParser(tPtr
, data
, prepend
);
1179 cursorToTextPosition(Text
* tPtr
, int x
, int y
) {
1180 Paragraph
*para
= NULL
;
1181 Chunk
*chunk
= NULL
;
1183 short line_width
= 0;
1184 short orig_x
, orig_y
;
1186 if (x
< (tPtr
->hasVscroller
? 21 : 1)) {
1187 y
-= tPtr
->clheight
;
1188 x
= tPtr
->view
->size
.width
; //tPtr->visibleW;
1190 } else if (x
> tPtr
->clwidth
&& x
< tPtr
->clicked
.x
) {
1191 //x = (tPtr->hasVscroller)?21:1;
1192 //y += tPtr->clheight;
1198 if (y
< 0 || y
> tPtr
->view
->size
.height
- 3)
1201 tPtr
->clicked
.x
= orig_x
;
1202 tPtr
->clicked
.y
= y
;
1203 tPtr
->clicked
.y
+= tPtr
->vpos
;
1204 tPtr
->clicked
.y
-= tPtr
->rulerShown
? 40 : 0;
1205 para
= tPtr
->paragraphs
;
1208 while (para
->next
) {
1209 if (tPtr
->clicked
.y
>= para
->top
- 4 &&
1210 tPtr
->clicked
.y
< para
->bottom
+ 4)
1214 if (!(tPtr
->currentPara
= para
))
1217 tPtr
->clicked
.y
-= para
->top
;
1218 if (tPtr
->clicked
.y
< 0)
1219 tPtr
->clicked
.y
= 0;
1220 if (tPtr
->hasVscroller
)
1225 tPtr
->findingClickPoint
= True
;
1226 tPtr
->foundClickPoint
= False
;
1227 /* also affects tPtr->currentChunk, tPtr->clicked.x and y,
1228 tPtr->clheight and ->width */
1229 putParagraphOnPixmap(tPtr
, para
, False
);
1230 tPtr
->findingClickPoint
= False
;
1231 tPtr
->clicked
.y
+= para
->top
;
1233 if (tPtr
->currentChunk
) {
1234 short _width
= 0, start
= tPtr
->tpos
, done
= False
, w
= 0;
1236 chunk
= tPtr
->currentChunk
;
1237 while (!done
&& chunk
&& line_width
< tPtr
->clwidth
) {
1238 if (chunk
->type
== ctText
) {
1239 font
= (tPtr
->monoFont
) ? tPtr
->dFont
: chunk
->font
;
1240 for (w
= start
; w
< chunk
->chars
; w
++) {
1241 _width
= WMWidthOfString(font
, &chunk
->text
[w
], 1);
1242 line_width
+= _width
;
1243 if (line_width
+ tPtr
->clicked
.x
>= x
) {
1244 line_width
-= _width
;
1251 if (0 && chunk
->next
) {
1252 if (chunk
->next
->type
== ctImage
) {
1253 if (x
+ 10 < line_width
+ chunk
->next
->pixmap
->width
) {
1260 _width
= chunk
->pixmap
->width
;
1261 line_width
+= _width
;
1262 if (line_width
+ tPtr
->clicked
.x
>= x
) {
1263 line_width
-= _width
;
1270 chunk
= chunk
->next
;
1274 tPtr
->currentChunk
= chunk
;
1279 short vS
= (tPtr
->hasVscroller
) ? 32 : 12;
1281 if (para
->align
== WARight
) {
1282 tPtr
->clicked
.x
= tPtr
->view
->size
.width
- vS
;
1283 } else if (para
->align
== WACenter
) {
1284 tPtr
->clicked
.x
= -(vS
/ 2) + (tPtr
->view
->size
.width
- vS
) / 2;
1286 tPtr
->clicked
.x
= 2;
1290 tPtr
->cursor
.x
= tPtr
->clicked
.x
+ 2 + line_width
;
1291 tPtr
->cursor
.y
= tPtr
->clicked
.y
;
1292 tPtr
->clicked
.y
= orig_y
;
1293 tPtr
->clicked
.x
= orig_x
;
1294 putParagraphOnPixmap(tPtr
, para
, True
);
1299 deleteTextInteractively(Text
* tPtr
, DeleteType type
) {
1302 short pos
, w
= 0, h
= 0, doprev
= False
, doprevpara
= False
;
1304 int current
= WMGetTextCurrentChunk(tPtr
);
1306 if (!(para
= tPtr
->currentPara
))
1308 if (!(chunk
= tPtr
->currentChunk
))
1310 font
= (tPtr
->monoFont
) ? tPtr
->dFont
: chunk
->font
;
1311 doprev
= (tPtr
->tpos
< 2);
1314 case dtDelete
: /* delete _after_ cursor ... implement later */
1315 case dtBackSpace
: /* delete _before_ cursor */
1316 if (chunk
->chars
> 1) {
1317 pos
= tPtr
->tpos
- 1;
1318 printf("here %d\n", pos
);
1320 w
= WMWidthOfString(font
, &chunk
->text
[pos
], 1);
1321 memmove(&(chunk
->text
[pos
]),
1322 &(chunk
->text
[pos
+ 1]), chunk
->chars
- pos
+ 1);
1327 WMRemoveTextChunk(tPtr
, current
);
1333 WMSetTextCurrentChunk(tPtr
, current
- 1);
1334 if (!tPtr
->currentChunk
) {
1335 printf("PREV PARA\n");
1337 tPtr
->tpos
= tPtr
->currentChunk
->chars
;
1340 int currentp
= WMGetTextCurrentParagraph(tPtr
);
1344 para
->chunks
= NULL
;
1345 WMRemoveTextParagraph(tPtr
, currentp
);
1346 WMSetTextCurrentParagraph(tPtr
, currentp
- 1);
1347 WMSetTextCurrentChunk(tPtr
, -1);
1348 para
= tPtr
->currentPara
;
1350 if (!tPtr
->currentChunk
|| !para
->chunks
) {
1351 para
->chunks
= chunk
;
1352 tPtr
->currentChunk
= chunk
;
1354 tPtr
->currentChunk
->next
= chunk
;
1361 if (1) { //if(1||(para && !doprevpara)) {
1363 affectNextParas(tPtr
, para
, -23);
1364 putParagraphOnPixmap(tPtr
, para
, True
);
1365 drawDocumentPartsOnPixmap(tPtr
, False
);
1366 updateScrollers(tPtr
);
1368 //cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1370 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1374 /* give us nice chunk sizes (multiples of 16) */
1376 reqBlockSize(short requested
) {
1377 return requested
+ 16 - (requested
% 16);
1379 insertTextInteractively(Text
* tPtr
, char *text
) {
1380 Paragraph
*para
= NULL
;
1381 Chunk
*chunk
= NULL
, *newchunk
= NULL
;
1382 int height
= -23; /* should only be changed upon newline */
1386 if (!tPtr
->editable
)
1388 if (*text
== '\n' && tPtr
->ignoreNewLine
)
1391 para
= tPtr
->currentPara
;
1392 chunk
= tPtr
->currentChunk
;
1393 font
= (tPtr
->monoFont
|| !chunk
) ? tPtr
->dFont
: chunk
->font
;
1395 if (*text
== '\n') {
1397 if (chunk
) { /* there's a chunk (or part of it) to detach from old */
1398 int current
= WMGetTextCurrentChunk(tPtr
);
1399 if (tPtr
->tpos
<= 0) { /* at start of chunk */
1400 if (current
< 1) { /* the first chunk... make old para blank */
1401 newchunk
= para
->chunks
;
1402 para
->chunks
= NULL
;
1403 putParagraphOnPixmap(tPtr
, para
, True
);
1404 } else { /* not first chunk... */
1405 printf("cut me out \n");
1407 } else if (tPtr
->tpos
< chunk
->chars
&& chunk
->type
== ctText
) {
1408 /* not at start of chunk */
1409 char text
[chunk
->chars
- tPtr
->tpos
+ 1];
1413 text
[i
] = chunk
->text
[tPtr
->tpos
+ i
];
1414 } while (++i
< chunk
->chars
- tPtr
->tpos
);
1416 newchunk
= (tPtr
->funcs
.createTChunk
) (text
, i
, chunk
->font
,
1417 chunk
->color
, chunk
->script
, chunk
->ul
);
1418 newchunk
->next
= chunk
->next
;
1420 /* might want to demalloc for LARGE cuts */
1421 //calcParaExtents(tPtr, para);
1422 para
->height
= putParagraphOnPixmap(tPtr
, para
, True
);
1423 //putParagraphOnPixmap(tPtr, para, True);
1424 } else if (tPtr
->tpos
>= chunk
->chars
) {
1427 WMSetTextCurrentChunk(tPtr
, current
- 1);
1428 prev
= tPtr
->currentChunk
;
1431 newchunk
= prev
->next
;
1433 putParagraphOnPixmap(tPtr
, para
, True
);
1438 if (para
) /* the preceeding one */
1439 new_top
= para
->bottom
;
1441 WMAppendTextStream(tPtr
, "\n");
1442 para
= tPtr
->currentPara
;
1445 para
->chunks
= newchunk
;
1446 tPtr
->currentChunk
= newchunk
;
1448 para
->top
= new_top
;
1449 calcParaExtents(tPtr
, para
);
1450 height
= para
->height
;
1453 WMAppendTextStream(tPtr
, text
);
1454 para
= tPtr
->currentPara
;
1455 } else if (!para
->chunks
|| !chunk
) {
1456 //WMPrependTextStream(tPtr, text);
1457 WMAppendTextStream(tPtr
, text
);
1458 } else if (chunk
->type
== ctImage
) {
1459 WMPrependTextStream(tPtr
, text
);
1461 printf("\n\nprepe\n\n");
1463 if (tPtr
->tpos
> chunk
->chars
) {
1464 printf("\n\nmore\n\n");
1465 tPtr
->tpos
= chunk
->chars
;
1467 if (chunk
->chars
+ 1 >= chunk
->mallocedSize
) {
1468 chunk
->mallocedSize
= reqBlockSize(chunk
->chars
+ 1);
1469 chunk
->text
= wrealloc(chunk
->text
, chunk
->mallocedSize
);
1471 memmove(&(chunk
->text
[tPtr
->tpos
+ 1]), &chunk
->text
[tPtr
->tpos
],
1472 chunk
->chars
- tPtr
->tpos
+ 1);
1473 w
= WMWidthOfString(font
, text
, 1);
1474 memmove(&chunk
->text
[tPtr
->tpos
], text
, 1);
1477 //doc->clickstart.cursor.x +=
1478 //WMWidthOfString(chunk->fmt->font, text,len);
1484 affectNextParas(tPtr
, para
, height
);
1485 putParagraphOnPixmap(tPtr
, para
, True
);
1486 drawDocumentPartsOnPixmap(tPtr
, False
);
1487 updateScrollers(tPtr
);
1489 //cursorToTextPosition(tPtr, tPtr->clicked.x+w, tPtr->clicked.y);
1490 //check for "sneppah tahw" with blank paras...
1496 selectRegion(Text
* tPtr
, int x
, int y
) {
1497 tPtr
->sRect
.pos
.x
= WMIN(tPtr
->clicked
.x
, x
);
1498 tPtr
->sRect
.size
.width
= abs(tPtr
->clicked
.x
- x
);
1499 tPtr
->sRect
.pos
.y
= WMIN(tPtr
->clicked
.y
, y
);
1500 if (tPtr
->sRect
.pos
.y
< 0)
1501 tPtr
->sRect
.pos
.y
= 0;
1502 tPtr
->sRect
.size
.height
= abs(tPtr
->clicked
.y
- y
);
1505 while(y>tPtr->visibleH && tPtr->vpos < tPtr->docHeight-tPtr->visibleH) {
1506 WMRefreshText(tPtr, tPtr->vpos+16, tPtr->hpos);
1509 //printf("%d %d \n", y, tPtr->vpos);
1511 //foreach para in selection...
1512 drawDocumentPartsOnPixmap(tPtr
, True
);
1515 #define WM_EMACSKEYMASK ControlMask
1516 #define WM_EMACSKEY_LEFT XK_b
1517 #define WM_EMACSKEY_RIGHT XK_f
1518 #define WM_EMACSKEY_HOME XK_a
1519 #define WM_EMACSKEY_END XK_e
1520 #define WM_EMACSKEY_BS XK_h
1521 #define WM_EMACSKEY_DEL XK_d static void
1522 handleTextKeyPress(Text
* tPtr
, XEvent
* event
) {
1525 int control_pressed
= False
;
1527 if (!tPtr
->editable
)
1530 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
)
1531 control_pressed
= True
;
1532 buffer
[XLookupString(&event
->xkey
, buffer
, 1, &ksym
, NULL
)] = '\0';
1538 if (tPtr
->currentChunk
) {
1540 Chunk
*chunk
= tPtr
->currentChunk
;
1542 if (chunk
->type
== ctText
) {
1543 WMFont
*font
= (tPtr
->monoFont
) ? tPtr
->dFont
: chunk
->font
;
1545 if (ksym
== XK_Right
) {
1546 short pos
= (tPtr
->tpos
< chunk
->chars
) ? tPtr
->tpos
+ 1 :
1549 w
= WMWidthOfString(font
, &chunk
->text
[pos
], 1);
1551 short pos
= (tPtr
->tpos
> 0) ? tPtr
->tpos
- 1 : 0;
1553 w
= WMWidthOfString(font
, &chunk
->text
[pos
], 1);
1556 w
= chunk
->pixmap
->width
;
1558 if (ksym
== XK_Right
)
1560 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
- w
, tPtr
->clicked
.y
);
1562 if (ksym
== XK_Right
)
1573 short h
= tPtr
->clheight
- 2;
1575 if (ksym
== XK_Down
)
1577 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
, tPtr
->clicked
.y
- h
);
1582 deleteTextInteractively(tPtr
, dtBackSpace
);
1587 deleteTextInteractively(tPtr
, dtDelete
);
1593 if (buffer
[0] != '\0' && (buffer
[0] == '\n' || !iscntrl(buffer
[0])))
1594 insertTextInteractively(tPtr
, buffer
);
1595 else if (control_pressed
&& ksym
== XK_r
) {
1596 Bool i
= !tPtr
->rulerShown
;
1598 WMShowTextRuler(tPtr
, i
);
1599 tPtr
->rulerShown
= i
;
1608 pasteText(WMView
* view
, Atom selection
, Atom target
, Time timestamp
,
1609 void *cdata
, WMData
* data
) {
1610 Text
*tPtr
= (Text
*) view
->self
;
1614 tPtr
->waitingForSelection
= False
;
1616 str
= (char *) WMDataBytes(data
);
1618 WMPrependTextStream(tPtr
, str
);
1620 WMAppendTextStream(tPtr
, str
);
1621 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1625 str
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1629 WMPrependTextStream(tPtr
, str
);
1631 WMAppendTextStream(tPtr
, str
);
1633 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1640 releaseSelection(Text
* tPtr
) {
1641 Paragraph
*para
= tPtr
->paragraphs
;
1645 chunk
= para
->chunks
;
1647 chunk
->selected
= False
;
1648 chunk
= chunk
->next
;
1652 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
1653 tPtr
->ownsSelection
= False
;
1654 drawDocumentPartsOnPixmap(tPtr
, True
);
1660 requestHandler(WMView
* view
, Atom selection
, Atom target
,
1661 void *cdata
, Atom
* type
) {
1662 Text
*tPtr
= view
->self
;
1664 Display
*dpy
= tPtr
->view
->screen
->display
;
1666 Atom TEXT
= XInternAtom(dpy
, "TEXT", False
);
1667 Atom COMPOUND_TEXT
= XInternAtom(dpy
, "COMPOUND_TEXT", False
);
1668 WMData
*data
= NULL
;
1671 if (!tPtr
->ownsSelection
|| !tPtr
->paragraphs
)
1673 //printf("got here\n");
1675 if (target
== XA_STRING
|| target
== TEXT
|| target
== COMPOUND_TEXT
) {
1676 //for bleh in selection...
1678 Paragraph
*para
= tPtr
->paragraphs
;
1679 Chunk
*chunk
= NULL
;
1680 char pixmap
[] = "[pixmap]";
1685 chunk
= para
->chunks
;
1688 if (chunk
->selected
&& chunk
->type
== ctText
) {
1689 len
= chunk
->chars
; //chunk->sEnd - chunk->sStart;
1692 s
= wmalloc(len
+ 1);
1694 memcpy(s
, &chunk
->text
[0 * chunk
->sStart
], len
);
1697 data
= WMCreateDataWithBytes(s
, strlen(s
));
1700 printf("append: %c %d\n", *s
, strlen(s
));
1701 WMAppendDataBytes(data
, s
, strlen(s
));
1708 printf("len is %d [%d %d] %d \n", len
, chunk
->sStart
, chunk
->sEnd
,
1711 chunk
= chunk
->next
;
1717 WMSetDataFormat(data
, 8);
1723 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
1724 if (target
== _TARGETS
) {
1725 Atom
*ptr
= wmalloc(4 * sizeof(Atom
));
1730 ptr
[3] = COMPOUND_TEXT
;
1732 data
= WMCreateDataWithBytes(ptr
, 4 * 4);
1733 WMSetDataFormat(data
, 32);
1746 lostHandler(WMView
* view
, Atom selection
, void *cdata
) {
1747 WMText
*tPtr
= (WMText
*) view
->self
;
1748 releaseSelection(tPtr
);
1749 } static WMSelectionProcs selectionHandler
= {
1751 requestHandler
, lostHandler
, NULL
1755 _notification(void *observerData
, WMNotification
* notification
) {
1756 WMText
*to
= (WMText
*) observerData
;
1757 WMText
*tw
= (WMText
*) WMGetNotificationClientData(notification
);
1759 lostHandler(to
->view
, XA_PRIMARY
, NULL
);
1761 handleTextEvents(XEvent
* event
, void *data
) {
1762 Text
*tPtr
= (Text
*) data
;
1763 Display
*dpy
= event
->xany
.display
;
1765 if (tPtr
->waitingForSelection
)
1768 switch (event
->type
) {
1770 if (!tPtr
->editable
|| tPtr
->buttonHeld
) {
1774 if (tPtr
->ownsSelection
)
1775 releaseSelection(tPtr
);
1777 //if (tPtr->waitingForSelection) return;
1778 if (tPtr
->focused
) {
1780 XGrabPointer(dpy
, W_VIEW(tPtr
)->window
, False
,
1781 PointerMotionMask
| ButtonPressMask
| ButtonReleaseMask
,
1782 GrabModeAsync
, GrabModeAsync
, None
,
1783 W_VIEW(tPtr
)->screen
->invisibleCursor
, CurrentTime
);
1784 tPtr
->pointerGrabbed
= True
;
1786 handleTextKeyPress(tPtr
, event
);
1791 if (tPtr
->pointerGrabbed
) {
1792 tPtr
->pointerGrabbed
= False
;
1793 XUngrabPointer(dpy
, CurrentTime
);
1795 if ((event
->xmotion
.state
& Button1Mask
)) {
1796 selectRegion(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
1797 if (!tPtr
->ownsSelection
) {
1798 WMCreateSelectionHandler(tPtr
->view
, XA_PRIMARY
,
1799 event
->xbutton
.time
, &selectionHandler
, NULL
);
1800 tPtr
->ownsSelection
= True
;
1805 if (event
->xbutton
.button
== Button1
) {
1806 if (tPtr
->ownsSelection
)
1807 releaseSelection(tPtr
);
1808 cursorToTextPosition(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
1809 if (tPtr
->pointerGrabbed
) {
1810 tPtr
->pointerGrabbed
= False
;
1811 XUngrabPointer(dpy
, CurrentTime
);
1815 if (!tPtr
->focused
) {
1816 WMSetFocusToWidget(tPtr
);
1817 tPtr
->focused
= True
;
1820 if (event
->xbutton
.button
== 4)
1821 WMScrollText(tPtr
, -16);
1822 else if (event
->xbutton
.button
== 5)
1823 WMScrollText(tPtr
, 16);
1828 tPtr
->buttonHeld
= False
;
1829 if (tPtr
->pointerGrabbed
) {
1830 tPtr
->pointerGrabbed
= False
;
1831 XUngrabPointer(dpy
, CurrentTime
);
1834 if (event
->xbutton
.button
== 4 || event
->xbutton
.button
== 5)
1836 if (event
->xbutton
.button
== Button2
&& tPtr
->editable
) {
1840 if (!WMRequestSelection(tPtr
->view
, XA_PRIMARY
, XA_STRING
,
1841 event
->xbutton
.time
, pasteText
, NULL
)) {
1842 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1845 WMAppendTextStream(tPtr
, text
);
1847 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1849 tPtr
->waitingForSelection
= True
;
1860 handleNonTextEvents(XEvent
* event
, void *data
) {
1861 Text
*tPtr
= (Text
*) data
;
1863 switch (event
->type
) {
1865 if (!event
->xexpose
.count
&& tPtr
->view
->flags
.realized
)
1870 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
)) != tPtr
->view
)
1872 tPtr
->focused
= True
;
1873 //cursor...paintText(tPtr);
1877 tPtr
->focused
= False
;
1878 //cursor...paintText(tPtr);
1883 //for(...)WMRemoveTextParagraph(tPtr, para);
1886 } //printf("handleNonTextEvents\n");
1888 rulerCallBack(WMWidget
* w
, void *self
) {
1889 Text
*tPtr
= (Text
*) self
;
1892 if (tPtr
->currentPara
) {
1893 Paragraph
*para
= tPtr
->currentPara
;
1894 para
->fmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerFirst
);
1895 para
->bmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerBody
);
1896 para
->rmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
);
1897 affectNextParas(tPtr
, para
, -23);
1898 putParagraphOnPixmap(tPtr
, para
, True
);
1901 which
= WMGetReleasedRulerMargin(tPtr
->ruler
);
1903 if (which
!= WRulerDocLeft
&& which
!= WRulerRight
1904 /* && Selection.para.count > 0 */ ) {
1906 "//for(i=0; i<Selection.para.count; i++) {"
1908 "//calcParaExtents(tPtr, para);}\n");
1910 WMRefreshText(tPtr
, 0, 0);
1917 rulerMoveCallBack(WMWidget
* w
, void *self
) {
1918 Text
*tPtr
= (Text
*) self
;
1919 short rmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
);
1922 if (WMGetGrabbedRulerMargin(tPtr
->ruler
) == WRulerLeft
) {
1923 short lmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
1924 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
1925 22, 42, lmargin
- 21, tPtr
->visibleH
, True
);
1926 } else if (WMGetGrabbedRulerMargin(tPtr
->ruler
) == WRulerRight
&&
1927 tPtr
->docWidth
+ 11 < rmargin
) {
1928 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
1929 rmargin
- 3, 42, 10, tPtr
->visibleH
, True
);
1936 /* ------------- non-static functions that are "friends" ------------- */
1937 /* ------------- called as (tPtr->funcs.foo)(bars...) ------------- */
1939 /* create a new paragraph. Don't do anything with it just yet */
1942 createParagraph(short fmargin
, short bmargin
, short rmargin
,
1943 short *tabstops
, short numTabs
, WMAlignment alignment
) {
1944 Paragraph
*para
= wmalloc(sizeof(Paragraph
));
1948 para
->chunks
= NULL
;
1952 para
->fmargin
= (fmargin
>= 0) ? fmargin
: 0;
1953 para
->bmargin
= (bmargin
>= 0) ? bmargin
: 0;
1954 if (rmargin
- bmargin
>= 100 && rmargin
- fmargin
>= 100)
1955 para
->rmargin
= rmargin
;
1957 para
->rmargin
= 100;
1958 para
->tabstops
= tabstops
;
1959 para
->numTabs
= (tabstops
) ? numTabs
: 0;
1961 para
->drawbuffer
= (Pixmap
) NULL
;
1962 para
->bulletPix
= NULL
;
1963 para
->top
= para
->bottom
= 0;
1964 para
->width
= para
->height
= 0;
1966 para
->align
= alignment
;
1969 } /* insert the new paragraph in the tPtr, either right before
1970 or after the currentPara. It's the responsibility of the
1971 calling code to set what currentPara is. via WMSetTextCurrentParagraph.
1972 If currentPara is not set, set it as the first in the document.
1973 This function then sets currentPara as _this_ paragraph.
1974 NOTE: this means careless parser implementors might lose previous
1975 paragraphs... but this keeps stuff small and non-buggy :-) */ void
1976 insertParagraph(WMText
* tPtr
, void *v
, Bool prepend
)
1977 //insertParagraph(WMText *tPtr, Paragraph *para, InsertType type)
1980 Paragraph
*para
= (Paragraph
*) v
;
1984 if (!tPtr
->currentPara
) {
1985 tPtr
->paragraphs
= para
;
1987 tmp
= tPtr
->paragraphs
;
1989 while (tmp
->next
&& tmp
!= tPtr
->currentPara
)
1992 para
->next
= tmp
->next
;
1994 } else { /* must be prepend */
1995 /* this "prior" member is that "doing things the hard way"
1996 I spoke of. See? it's not too bad afterall... */
1997 Paragraph
*prior
= NULL
;
1999 while (tmp
->next
&& tmp
!= tPtr
->currentPara
) {
2003 /* if this is the first */
2004 if (tmp
== tPtr
->paragraphs
) {
2006 tPtr
->paragraphs
= para
;
2013 tPtr
->currentPara
= para
;
2017 /* create a new chunk to contain exactly ONE pixmap */
2020 createPChunk(WMPixmap
* pixmap
, short script
, ushort ul
) {
2023 chunk
= wmalloc(sizeof(Chunk
));
2029 chunk
->pixmap
= NULL
; /* if it's NULL, we'll draw the "broken" pixmap... */
2031 chunk
->pixmap
= WMRetainPixmap(pixmap
);
2033 chunk
->mallocedSize
= 0;
2034 chunk
->type
= ctImage
;
2036 chunk
->color
= NULL
;
2037 chunk
->script
= script
;
2039 chunk
->selected
= False
;
2042 } /* create a new chunk to contain some text with the given format */ void *
2044 createTChunk(char *text
, short chars
, WMFont
* font
,
2045 WMColor
* color
, short script
, ushort ul
) {
2048 if (!text
|| chars
< 0 || !font
|| !color
)
2050 chunk
= wmalloc(sizeof(Chunk
));
2054 chunk
->mallocedSize
= reqBlockSize(chars
);
2055 chunk
->text
= (char *) wmalloc(chunk
->mallocedSize
);
2056 memcpy(chunk
->text
, text
, chars
);
2057 chunk
->pixmap
= NULL
;
2058 chunk
->chars
= chars
;
2059 chunk
->type
= ctText
;
2060 chunk
->font
= WMRetainFont(font
);
2061 chunk
->color
= WMRetainColor(color
);
2062 chunk
->script
= script
;
2064 chunk
->selected
= False
;
2068 } /* insert the new chunk in the paragraph, either right before
2069 or after the currentChunk. It's the responsibility of the
2070 calling code to set what currentChunk is via WMSetTextCurrentChunk.
2071 If currentChunk is not set, set it as the first in the existing
2072 paragraph... if not even that, you lose... try again.
2073 This function then sets currentChunk as _this_ chunk.
2074 NOTE: this means careless parser implementors might lose previous
2075 paragraphs/chunks... but this keeps stuff small and non-buggy :-) */ void
2076 insertChunk(WMText
* tPtr
, void *v
, Bool prepend
) {
2078 Chunk
*chunk
= (Chunk
*) v
;
2080 if (!tPtr
|| !chunk
)
2083 if (!tPtr
->paragraphs
) { /* i.e., first chunk via insertTextInteractively */
2084 Paragraph
*para
= (tPtr
->funcs
.createParagraph
) (0, 0, tPtr
->visibleW
,
2086 (tPtr
->funcs
.insertParagraph
) (tPtr
, para
, False
);
2088 if (!tPtr
->currentPara
)
2091 if (!tPtr
->currentChunk
) { /* there is a current chunk */
2092 tPtr
->currentPara
->chunks
= chunk
;
2093 } else if (!tPtr
->currentPara
->chunks
) {
2094 /* but it's not of this paragraph */
2095 tPtr
->currentPara
->chunks
= chunk
;
2097 tmp
= tPtr
->currentPara
->chunks
;
2100 while (tmp
->next
&& tmp
!= tPtr
->currentChunk
)
2103 chunk
->next
= tmp
->next
;
2106 } else { /* must be prepend */
2107 /* this "prior" member is that "doing things the hard way"
2108 I spoke of. See? it's not too bad afterall... */
2109 Chunk
*prior
= NULL
;
2111 while (tmp
->next
&& tmp
!= tPtr
->currentChunk
) {
2115 /* if this is the first */
2116 if (tmp
== tPtr
->currentPara
->chunks
) {
2118 tPtr
->currentPara
->chunks
= chunk
;
2120 prior
->next
= chunk
;
2125 tPtr
->currentChunk
= chunk
;
2126 tPtr
->tpos
= chunk
->chars
;
2130 /* ------------- non-static functions (i.e., APIs) ------------- */
2131 /* ------------- called as WMVerbText[Subject] ------------- */
2133 #define DEFAULT_TEXT_WIDTH 250
2134 #define DEFAULT_TEXT_HEIGHT 200
2139 WMCreateText(WMWidget
* parent
) {
2140 Text
*tPtr
= wmalloc(sizeof(Text
));
2143 perror("could not create text widget\n");
2146 memset(tPtr
, 0, sizeof(Text
));
2147 tPtr
->widgetClass
= WC_Text
;
2148 tPtr
->view
= W_CreateView(W_VIEW(parent
));
2150 perror("could not create text's view\n");
2154 tPtr
->view
->self
= tPtr
;
2155 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
2156 tPtr
->view
->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2157 W_ResizeView(tPtr
->view
, DEFAULT_TEXT_WIDTH
, DEFAULT_TEXT_HEIGHT
);
2158 tPtr
->bg
= tPtr
->view
->screen
->white
;
2159 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->bg
);
2162 tPtr
->ruler
= WMCreateRuler(tPtr
);
2163 (W_VIEW(tPtr
->ruler
))->attribs
.cursor
= tPtr
->view
->screen
->defaultCursor
;
2164 (W_VIEW(tPtr
->ruler
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2165 WMMoveWidget(tPtr
->ruler
, 0, 0);
2166 WMResizeWidget(tPtr
->ruler
, W_VIEW(parent
)->size
.width
, 40);
2167 WMShowRulerTabs(tPtr
->ruler
, True
);
2168 WMSetRulerAction(tPtr
->ruler
, rulerCallBack
, tPtr
);
2169 WMSetRulerMoveAction(tPtr
->ruler
, rulerMoveCallBack
, tPtr
);
2173 tPtr
->vscroller
= WMCreateScroller(tPtr
);
2174 (W_VIEW(tPtr
->vscroller
))->attribs
.cursor
=
2175 tPtr
->view
->screen
->defaultCursor
;
2176 (W_VIEW(tPtr
->vscroller
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2177 WMMoveWidget(tPtr
->vscroller
, 1, 1);
2178 WMResizeWidget(tPtr
->vscroller
, 20, tPtr
->view
->size
.height
- 2);
2179 WMSetScrollerArrowsPosition(tPtr
->vscroller
, WSAMaxEnd
);
2180 WMSetScrollerAction(tPtr
->vscroller
, scrollersCallBack
, tPtr
);
2184 tPtr
->hscroller
= WMCreateScroller(tPtr
);
2185 (W_VIEW(tPtr
->hscroller
))->attribs
.cursor
=
2186 tPtr
->view
->screen
->defaultCursor
;
2187 (W_VIEW(tPtr
->hscroller
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2188 WMMoveWidget(tPtr
->hscroller
, 1, tPtr
->view
->size
.height
- 21);
2189 WMResizeWidget(tPtr
->hscroller
, tPtr
->view
->size
.width
- 2, 20);
2190 WMSetScrollerArrowsPosition(tPtr
->hscroller
, WSAMaxEnd
);
2191 WMSetScrollerAction(tPtr
->hscroller
, scrollersCallBack
, tPtr
);
2193 tPtr
->visibleW
= tPtr
->view
->size
.width
;
2194 tPtr
->visibleH
= tPtr
->view
->size
.height
;
2196 tPtr
->paragraphs
= NULL
;
2198 tPtr
->docHeight
= 0;
2199 tPtr
->dBulletPix
= WMCreatePixmapFromXPMData(tPtr
->view
->screen
,
2201 tPtr
->dUnknownImg
= WMCreatePixmapFromXPMData(tPtr
->view
->screen
,
2204 tPtr
->sRect
.pos
.x
= tPtr
->sRect
.pos
.y
= 0;
2205 tPtr
->sRect
.size
.width
= tPtr
->sRect
.size
.height
= 0;
2206 tPtr
->currentPara
= NULL
;
2207 tPtr
->currentChunk
= NULL
;
2210 tPtr
->parser
= NULL
;
2211 tPtr
->writer
= NULL
;
2212 tPtr
->funcs
.createParagraph
= createParagraph
;
2213 tPtr
->funcs
.insertParagraph
= insertParagraph
;
2214 tPtr
->funcs
.createPChunk
= createPChunk
;
2215 tPtr
->funcs
.createTChunk
= createTChunk
;
2216 tPtr
->funcs
.insertChunk
= insertChunk
;
2218 tPtr
->clicked
.x
= tPtr
->clicked
.y
= -23;
2219 tPtr
->cursor
.x
= tPtr
->cursor
.y
= -23;
2221 tPtr
->relief
= WRSunken
;
2222 tPtr
->wrapping
= wrWord
;
2223 tPtr
->editable
= False
;
2224 tPtr
->cursorShown
= False
;
2225 tPtr
->frozen
= False
;
2226 tPtr
->focused
= False
;
2227 tPtr
->pointerGrabbed
= False
;
2228 tPtr
->buttonHeld
= False
;
2229 tPtr
->ignoreNewLine
= False
;
2230 tPtr
->waitingForSelection
= False
;
2231 tPtr
->findingClickPoint
= False
;
2232 tPtr
->foundClickPoint
= False
;
2233 tPtr
->ownsSelection
= False
;
2237 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2238 tPtr
->dColor
= WMBlackColor(tPtr
->view
->screen
);
2240 tPtr
->view
->delegate
= &_TextViewDelegate
;
2241 WMCreateEventHandler(tPtr
->view
, ExposureMask
| StructureNotifyMask
2242 | EnterWindowMask
| LeaveWindowMask
| FocusChangeMask
,
2243 handleNonTextEvents
, tPtr
);
2244 WMCreateEventHandler(tPtr
->view
, ButtonReleaseMask
| ButtonPressMask
2245 | KeyReleaseMask
| KeyPressMask
| Button1MotionMask
,
2246 handleTextEvents
, tPtr
);
2248 WMAddNotificationObserver(_notification
, tPtr
, "_lostOwnership", tPtr
);
2250 WMSetTextMonoFont(tPtr
, True
);
2251 WMShowTextRuler(tPtr
, False
);
2252 WMSetTextHasHorizontalScroller(tPtr
, False
);
2253 WMSetTextHasVerticalScroller(tPtr
, True
);
2254 //printf("the sizeof chunk is %d\n", sizeof(Chunk));
2255 //printf("the sizeof para is %d\n", sizeof(Paragraph));
2256 //printf("the sizeof text is %d\n", sizeof(Text));
2261 //WRetainPixmap(tPtr->dBulletPix);
2264 WMRemoveTextParagraph(WMText
* tPtr
, int which
) {
2265 Paragraph
*prior
, *removed
;
2266 if (!tPtr
|| which
< 0)
2269 WMSetTextCurrentParagraph(tPtr
, which
);
2270 removed
= tPtr
->currentPara
;
2273 if (removed
->chunks
)
2274 printf("WMRemoveTextChunks\n");
2275 if (removed
== tPtr
->paragraphs
|| which
== 0) {
2276 tPtr
->paragraphs
= removed
->next
;
2278 WMSetTextCurrentParagraph(tPtr
, which
- 1);
2279 prior
= tPtr
->currentPara
;
2282 prior
->next
= removed
->next
;
2291 /* set what is known as the currentPara in the tPtr. */
2292 /* negative number means: "gib me last chunk" */
2294 WMSetTextCurrentParagraph(WMText
* tPtr
, int current
) {
2298 if (!tPtr
|| current
< 0)
2301 tPtr
->currentPara
= tPtr
->paragraphs
;
2304 tmp
= tPtr
->paragraphs
;
2306 while (tmp
->next
&& ((current
== -23) ? 1 : i
++ < current
)) {
2307 //while(tmp && i++<current) {
2310 tPtr
->currentPara
= tmp
;
2311 //? want to do this?if(tmp) tPtr->currentChunk = tmp
2316 WMGetTextParagraphs(WMText
* tPtr
) {
2322 tmp
= tPtr
->paragraphs
;
2333 WMGetTextCurrentParagraph(WMText
* tPtr
) {
2339 if (!tPtr
->currentPara
)
2341 if (!tPtr
->paragraphs
)
2343 tmp
= tPtr
->paragraphs
;
2346 if (tmp
== tPtr
->currentPara
)
2353 /* set what is known as the currentChunk within the currently
2354 selected currentPara (or the first paragraph in the document). */
2356 WMSetTextCurrentChunk(WMText
* tPtr
, int current
) {
2362 tPtr
->currentChunk
= NULL
;
2363 if (!tPtr
->currentPara
) {
2364 tPtr
->currentPara
= tPtr
->paragraphs
;
2365 if (!tPtr
->currentPara
)
2369 tPtr
->currentChunk
= tPtr
->currentPara
->chunks
;
2372 tmp
= tPtr
->currentPara
->chunks
;
2374 while (tmp
->next
&& ((current
< 0) ? 1 : i
++ < current
))
2377 tPtr
->currentChunk
= tmp
;
2382 WMRemoveTextChunk(WMText
* tPtr
, int which
) {
2383 Chunk
*prior
, *removed
;
2385 if (!tPtr
|| which
< 0)
2387 para
= tPtr
->currentPara
;
2391 WMSetTextCurrentChunk(tPtr
, which
);
2392 removed
= tPtr
->currentChunk
;
2395 if (removed
== tPtr
->currentPara
->chunks
|| which
== 0) {
2396 para
->chunks
= removed
->next
;
2398 WMSetTextCurrentChunk(tPtr
, which
- 1);
2399 prior
= tPtr
->currentChunk
;
2402 prior
->next
= removed
->next
;
2404 if (removed
->type
== ctText
) {
2405 wgdbFree(removed
->text
);
2406 WMReleaseFont(removed
->font
);
2407 WMReleaseColor(removed
->color
);
2409 WMReleasePixmap(removed
->pixmap
);
2416 WMGetTextCurrentChunk(WMText
* tPtr
) {
2422 if (!tPtr
->currentChunk
)
2424 if (!tPtr
->currentPara
) {
2425 tPtr
->currentPara
= tPtr
->paragraphs
;
2426 if (!tPtr
->currentPara
)
2429 tmp
= tPtr
->currentPara
->chunks
;
2431 if (tmp
== tPtr
->currentChunk
)
2440 WMGetTextChunks(WMText
* tPtr
) {
2444 if (!tPtr
|| !tPtr
->currentPara
)
2446 tmp
= tPtr
->currentPara
->chunks
;
2455 WMShowTextRuler(WMText
* tPtr
, Bool show
) {
2461 tPtr
->rulerShown
= show
;
2463 WMMapWidget(tPtr
->ruler
);
2465 WMUnmapWidget(tPtr
->ruler
);
2466 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2470 WMGetTextRulerShown(WMText
* tPtr
) {
2473 return tPtr
->rulerShown
;
2477 WMSetTextRulerMargin(WMText
* tPtr
, char which
, short pixels
) {
2482 WMSetRulerMargins(tPtr
->ruler
, which
, pixels
);
2483 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2485 WMGetTextRulerMargin(WMText
* tPtr
, char which
) {
2490 return WMGetRulerMargins(tPtr
->ruler
, which
);
2492 WMShowTextRulerTabs(WMText
* tPtr
, Bool show
) {
2497 WMShowRulerTabs(tPtr
->ruler
, show
);
2501 WMSetTextMonoFont(WMText
* tPtr
, Bool mono
) {
2504 if (mono
&& tPtr
->rulerShown
)
2505 WMShowTextRuler(tPtr
, False
);
2507 tPtr
->monoFont
= mono
;
2511 WMGetTextMonoFont(WMText
* tPtr
) {
2514 return tPtr
->monoFont
;
2518 WMForceTextFocus(WMText
* tPtr
) {
2522 if (tPtr
->clicked
.x
== -23 || tPtr
->clicked
.y
== 23)
2523 cursorToTextPosition(tPtr
, 100, 100); /* anyplace */
2525 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
, tPtr
->clicked
.y
);
2530 WMSetTextEditable(WMText
* tPtr
, Bool editable
) {
2533 tPtr
->editable
= editable
;
2538 WMGetTextEditable(WMText
* tPtr
) {
2541 return tPtr
->editable
;
2546 WMScrollText(WMText
* tPtr
, int amount
) {
2547 Bool scroll
= False
;
2549 if (amount
== 0 || !tPtr
)
2551 if (!tPtr
->view
->flags
.realized
)
2555 if (tPtr
->vpos
> 0) {
2556 if (tPtr
->vpos
> amount
)
2557 tPtr
->vpos
+= amount
;
2563 int limit
= tPtr
->docHeight
- tPtr
->visibleH
;
2565 if (tPtr
->vpos
< limit
) {
2566 if (tPtr
->vpos
< limit
- amount
)
2567 tPtr
->vpos
+= amount
;
2574 if (scroll
&& tPtr
->vpos
!= tPtr
->prevVpos
) {
2575 updateScrollers(tPtr
);
2576 drawDocumentPartsOnPixmap(tPtr
, False
);
2579 tPtr
->prevVpos
= tPtr
->vpos
;
2584 WMPageText(WMText
* tPtr
, Bool scrollUp
) {
2587 if (!tPtr
->view
->flags
.realized
)
2590 return WMScrollText(tPtr
, scrollUp
2591 ? tPtr
->visibleH
: -tPtr
->visibleH
);
2595 WMIgnoreTextNewline(WMText
* tPtr
, Bool ignore
) {
2598 tPtr
->ignoreNewLine
= ignore
;
2603 WMSetTextHasHorizontalScroller(WMText
* tPtr
, Bool flag
) {
2609 rh
= tPtr
->rulerShown
? 40 : 0;
2610 tPtr
->hasHscroller
= flag
;
2612 WMMapWidget(tPtr
->hscroller
);
2613 tPtr
->visibleH
= tPtr
->view
->size
.height
- rh
- 22;
2615 WMUnmapWidget(tPtr
->hscroller
);
2616 tPtr
->visibleH
= tPtr
->view
->size
.height
- rh
;
2618 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2624 WMSetTextHasVerticalScroller(WMText
* tPtr
, Bool flag
) {
2626 tPtr
->hasVscroller
= flag
;
2628 WMMapWidget(tPtr
->vscroller
);
2629 tPtr
->visibleW
= tPtr
->view
->size
.width
- 22;
2630 WMSetRulerOffset(tPtr
->ruler
, 22); /* scrollbar width + 2 */
2632 WMUnmapWidget(tPtr
->vscroller
);
2633 tPtr
->visibleW
= tPtr
->view
->size
.width
;
2634 WMSetRulerOffset(tPtr
->ruler
, 2);
2636 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2643 WMRefreshText(WMText
* tPtr
, int vpos
, int hpos
) {
2648 if (tPtr
->frozen
|| !tPtr
->view
->flags
.realized
)
2652 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
2653 22, (tPtr
->rulerShown
) ? 45 : 5,
2654 tPtr
->visibleW
, tPtr
->visibleH
, True
);
2656 calcDocExtents(tPtr
);
2658 printf("vpos:%d tPtr->docHeight%d tPtr->visibleH%d \n",
2659 vpos, tPtr->docHeight, tPtr->visibleH);
2662 // tPtr->vpos = vpos;
2664 if(vpos < 0 || tPtr->docHeight < tPtr->visibleH)
2666 else if(vpos-tPtr->visibleH>tPtr->docHeight)
2667 tPtr->vpos = vpos-tPtr->docHeight-tPtr->visibleH-tPtr->docHeight;
2669 tPtr->vpos = tPtr->docHeight-tPtr->visibleH;
2673 if (hpos
< 0 || hpos
> tPtr
->docWidth
)
2678 drawDocumentPartsOnPixmap(tPtr
, True
);
2679 updateScrollers(tPtr
);
2681 } /* would be nice to have in WINGs proper... */ static void
2682 changeFontProp(char *fname
, char *newprop
, short which
) {
2683 char before
[128], prop
[128], after
[128];
2687 if (!fname
|| !prop
)
2697 else if (part
== which
+ 1)
2707 snprintf(fname
, 255, "%s-%s%s", before
, newprop
, after
);
2710 /* TODO: put in wfont? */
2712 WMGetFontPlain(WMScreen
* scrPtr
, WMFont
* font
) {
2713 WMFont
*nfont
= NULL
;
2715 if (!scrPtr
|| !font
)
2722 WMGetFontBold(WMScreen
* scrPtr
, WMFont
* font
) {
2723 WMFont
*newfont
= NULL
;
2726 if (!scrPtr
|| !font
)
2728 snprintf(fname
, 255, font
->name
);
2729 changeFontProp(fname
, "bold", 2);
2730 newfont
= WMCreateNormalFont(scrPtr
, fname
);
2737 WMGetFontItalic(WMScreen
* scrPtr
, WMFont
* font
) {
2738 WMFont
*newfont
= NULL
;
2741 if (!scrPtr
|| !font
)
2743 snprintf(fname
, 255, font
->name
);
2744 changeFontProp(fname
, "o", 3);
2745 newfont
= WMCreateNormalFont(scrPtr
, fname
);
2752 WMGetFontOfSize(WMScreen
* scrPtr
, WMFont
* font
, short size
) {
2753 WMFont
*nfont
= NULL
;
2755 if (!scrPtr
|| !font
|| size
< 1)
2762 WMFreezeText(WMText
* tPtr
) {
2765 tPtr
->frozen
= True
;
2769 WMThawText(WMText
* tPtr
) {
2772 tPtr
->frozen
= False
;
2777 WMSetTextDefaultAlignment(WMText
* tPtr
, WMAlignment alignment
) {
2783 tPtr
->dAlignment
= alignment
;
2784 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2790 WMSetTextBackgroundColor(WMText
* tPtr
, WMColor
* color
) {
2797 tPtr
->bg
= WMWhiteColor(tPtr
->view
->screen
);
2799 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->bg
);
2800 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2804 WMSetTextDefaultColor(WMText
* tPtr
, WMColor
* color
) {
2809 tPtr
->dColor
= color
;
2811 tPtr
->dColor
= WMBlackColor(tPtr
->view
->screen
);
2815 WMSetTextDefaultFont(WMText
* tPtr
, WMFont
* font
) {
2822 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2826 WMSetTextUseFixedPitchFont(Text
* tPtr
, Bool fixed
) {
2830 tPtr
->dFont
= WMCreateFontSet(tPtr
->view
->screen
,
2831 "lucidasanstypewriter-12");
2833 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2834 tPtr
->fixedPitch
= fixed
;
2838 WMSetTextParser(WMText
* tPtr
, WMParseAction
* parser
) {
2843 tPtr
->parser
= parser
;
2848 WMGetTextParserActions(WMText
* tPtr
) {
2849 WMParserActions null
;
2858 WMGetTextAll(WMText
* tPtr
) {
2868 para
= tPtr
->paragraphs
;
2870 chunk
= para
->chunks
;
2872 if (chunk
->type
== ctText
) {
2874 length
+= chunk
->chars
;
2876 printf("getting image \n");
2878 chunk
= chunk
->next
;
2881 if (tPtr
->ignoreNewLine
)
2883 length
+= 4; // newlines
2888 text
= wmalloc(length
+ 1);
2890 para
= tPtr
->paragraphs
;
2892 chunk
= para
->chunks
;
2894 if (chunk
->type
== ctText
) {
2896 snprintf(&text
[where
], chunk
->chars
+ 1, "%s", chunk
->text
);
2897 where
+= chunk
->chars
;
2900 printf("writing image \n");
2902 chunk
= chunk
->next
;
2904 if (tPtr
->ignoreNewLine
)
2906 snprintf(&text
[where
++], 2, "\n");
2915 WMSetTextWriter(WMText
* tPtr
, WMParseAction
* writer
) {
2920 tPtr
->writer
= writer
;