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
)
42 if(!ptr
) printf("err... cannot ");
43 printf("gdbFree [%p]\n", ptr
);
48 /* Why single-linked and not say double-linked?
49 99% of the time (draw, append), the "prior"
50 member would have been a useless memory and CPU overhead,
51 and deletes _are_ relatively infrequent.
52 When the "prior" member needs to be used, the overhead of
53 doing things the hard way will be incurred... but seldomly. */
56 /* a Chunk is a single-linked list of chunks containing:
57 * o text with a given format
61 typedef struct _Chunk
{
62 char *text
; /* the text in the chunk */
63 WMPixmap
*pixmap
; /* OR the pixmap it holds */
64 short chars
; /* the number of characters in this chunk */
65 short mallocedSize
; /* the number of characters that can be held */
67 WMFont
*font
; /* the chunk's font */
68 WMColor
*color
; /* the chunk's color */
69 short ul
:1; /* underlined or not */
70 ChunkType type
:1; /* a "Text" or "Image" chunk */
71 short script
:4; /* script in points: negative for subscript */
77 struct _Chunk
*next
; /*the next member in this list */
83 /* a Paragraph is a singly-linked list of paragraphs containing:
84 * o a list of chunks in that paragraph
85 * o the formats for that paragraph
86 * o its (draw) position relative to the entire document
88 typedef struct _Paragraph
{
89 Chunk
*chunks
; /* the list of text and/or image chunks */
90 short fmargin
; /* the start position of the first line */
91 short bmargin
; /* the start positions of the rest of the lines */
92 short rmargin
; /* the end position of the entire paragraph */
93 short numTabs
; /* the number of tabstops */
94 short *tabstops
; /* an array of tabstops */
96 Pixmap drawbuffer
; /* the pixmap onto which the (entire)
97 paragraph will be drawn */
98 WMPixmap
*bulletPix
; /* the pixmap to use for bulleting */
99 int top
; /* the top of the paragraph relative to document */
100 int bottom
; /* the bottom of the paragraph relative to document */
101 int width
; /* the width of the paragraph */
102 int height
; /* the height of the paragraph */
103 WMAlignment align
:2; /* justification of this paragraph */
106 struct _Paragraph
*next
; /* the next member in this list */
110 static char *default_bullet
[] = {
112 " c None s None", ". c black",
113 "X c white", "o c #808080",
122 /* this is really a shrunk down version of the original
123 "broken" icon... I did not draw it, I simply shrunk it */
124 static char * unk_xpm
[] = {
126 " c None", ". c #0B080C", "+ c #13A015", "@ c #5151B8",
127 "# c #992719", "$ c #5B1C20", "% c #1DF51D", "& c #D1500D", "* c #2F304A",
128 "= c #0C6A0C", "- c #F2F1DE", "; c #D59131", "> c #B2B083", ", c #DD731A",
129 "' c #CC3113", ") c #828238", "! c #6A6A94",
130 "......!@@@@@@@....$$....",
131 "...@!@@@@@@@**...$#'....",
132 "..!!@@@@@@@@.......#....",
133 "..!@@@@@@@@@*.......$...",
134 ".!@@@#,,#*@@*..*>.*.#...",
135 "*@@@@#'',,@@@...---!....",
136 "!@@@@@*.#;*@@..!--->....",
137 "@@@@@@@@#,.@@..!----@...",
138 "!@@@@@@*#;'$...!----@...",
139 "*@@@@@@..'&;;#.)----)...",
140 ".@@@@@@..$..&'.>----)...",
141 ".@@@@@@**---,'>-----!...",
142 ".@@@@@@**---,'>-----@...",
143 "..@@@@@@@---;;;,;---....",
144 "..*@@@@*@--->#',;,-*.)..",
145 "........)---->)@;#!..>..",
146 ".....)----------;$..>)..",
147 "=%%%*.*!-------);..)-*..",
148 "=%%%%+...*)>!@*$,.>--...",
149 "*+++++++.......*$@-->...",
150 "............**@)!)>->...",
151 "........................",
152 "........................",
153 "........................"
156 typedef struct W_Text
{
157 W_Class widgetClass
; /* the class number of this widget */
158 W_View
*view
; /* the view referring to this instance */
159 WMColor
*bg
; /* the background color to use when drawing */
161 WMRuler
*ruler
; /* the ruler subwiget to maipulate paragraphs */
163 WMScroller
*hscroller
; /* the horizontal scroller */
164 short hpos
; /* the current horizontal position */
165 short prevHpos
; /* the previous horizontal position */
167 WMScroller
*vscroller
; /* the vertical scroller */
168 int vpos
; /* the current vertical position */
169 int prevVpos
; /* the previous vertical position */
171 int visibleW
; /* the actual horizontal space available */
172 int visibleH
; /* the actual vertical space available */
174 Paragraph
*paragraphs
; /* the linked list of the paragraphs in the doc. */
175 int docWidth
; /* the width of the entire document */
176 int docHeight
; /* the height of the entire document */
178 WMFont
*dFont
; /* the default font */
179 WMColor
*dColor
; /* the default color */
180 WMPixmap
*dBulletPix
; /* the default pixmap for bullets */
181 WMPixmap
*dUnknownImg
; /* the pixmap for (missing/broken) images */
183 WMRect sRect
; /* the selected area */
184 Paragraph
*currentPara
; /* the current paragraph, in which actions occur */
185 Chunk
*currentChunk
; /* the current chunk, about which actions occur */
186 short tpos
; /* the cursor position (text position) */
187 WMParseAction
*parser
; /* what action to use to parse input text */
188 WMParseAction
*writer
; /* what action to use to write text */
189 WMParserActions funcs
; /* the "things" that parsers/writers might do */
190 XPoint clicked
; /* the position of the last mouse click */
191 XPoint cursor
; /* where the cursor is "placed" */
192 short clheight
; /* the height of the "line" clicked on */
193 short clwidth
; /* the width of the "line" clicked on */
195 WMReliefType relief
:3; /* the relief to display with */
196 Wrapping wrapping
:2; /* the type of wrapping to use in drawing */
197 WMAlignment dAlignment
:2; /* default justification */
198 ushort monoFont
:1; /* whether to ignore "rich" commands */
199 ushort fixedPitch
:1; /* assume each char in dFont is the same size */
200 ushort editable
:1; /* whether to accept user changes or not*/
201 ushort rulerShown
:1; /* whether the ruler is shown or not */
202 ushort cursorShown
:1; /* whether the cursor is currently being shown */
203 ushort frozen
:1; /* whether screen updates are to be made */
204 ushort focused
:1; /* whether this instance has input focus */
205 ushort pointerGrabbed
:1; /* whether this instance has the pointer */
206 ushort buttonHeld
:1; /* the user is still holding down the button */
207 ushort ignoreNewLine
:1; /* whether to ignore the newline character */
208 ushort waitingForSelection
:1; /* whether there is a pending paste event */
209 ushort ownsSelection
:1; /* whether it ownz the current selection */
210 ushort findingClickPoint
:1; /* whether the search for a clickpoint is on */
211 ushort foundClickPoint
:1; /* whether the clickpoint has been found */
212 ushort hasVscroller
:1; /* whether to enable the vertical scroller */
213 ushort hasHscroller
:1; /* whether to enable the horizontal scroller */
219 /* --------- static functions that are "private". don't touch :-) --------- */
222 /* max "characters per chunk that will be drawn at a time" */
223 #define MAX_WORD_LENGTH 100
226 #define MIN_DOC_WIDTH 200
228 typedef struct _LocalMargins
{
229 short left
, right
, first
, body
;
232 typedef struct _MyTextItems
{
233 char text
[MAX_WORD_LENGTH
+1];
237 Chunk
*chunk
; /* used for "click" events */
238 short start
; /* ditto... where in the chunk we start (ie. wrapped chunk) */
245 chunkSelectionRect(Text
*tPtr
, Paragraph
*para
, MyTextItems item
,
246 short y
, short j
, short lh
)
249 short type
=0; /* 0:none 1:partial: 2:all */
250 short rh
=(tPtr
->rulerShown
)?45:5;
252 WMFont
*font
= (tPtr
->monoFont
|| item
.chunk
->type
!= ctText
)?
253 tPtr
->dFont
:item
.chunk
->font
;
256 if(y
+para
->top
+rh
> tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
257 || y
+para
->top
+rh
+lh
< tPtr
->sRect
.pos
.y
)
260 if(item
.chunk
->type
== ctText
)
261 w
= WMWidthOfString(font
, item
.text
, item
.chars
);
262 else w
= item
.chunk
->pixmap
->width
;
264 if(y
+para
->top
+rh
>= tPtr
->sRect
.pos
.y
&& (y
+para
->top
+rh
+lh
265 <= tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
))
266 //&& item.x+j >= tPtr->sRect.pos.x+tPtr->sRect.size.width))
272 if(item
.x
+j
>= tPtr
->sRect
.pos
.x
&&
273 item
.x
+j
+w
< tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
)
276 if(type
== 1 && y
+para
->top
+rh
+lh
<=
277 tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
)
282 if(type
== 1 && item
.chunk
->type
== ctText
) { /* partial coverage */
283 lm
= 2+WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
284 /* even I am still confused, so don't ask please */
285 if( (item
.x
+j
+lm
>= tPtr
->sRect
.pos
.x
&&
286 item
.x
+j
+lm
<= tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
)
287 || (item
.x
+j
+lm
>= tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
288 && y
+para
->top
+rh
+lh
<=
289 tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
)
290 || (tPtr
->sRect
.pos
.y
< y
+para
->top
+rh
291 && tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
>
294 rect
.pos
.x
= item
.x
+j
;
295 item
.chunk
->selected
= True
;
296 if(item
.chunk
->chars
> 6) {
297 item
.chunk
->sStart
= 3;
298 item
.chunk
->sEnd
= item
.chunk
->chars
;
300 item
.chunk
->sStart
= 0;
301 item
.chunk
->sEnd
= item
.chunk
->chars
;
304 } else if(type
== 2) {
305 rect
.pos
.x
= item
.x
+j
;
306 item
.chunk
->selected
= True
;
307 if(item
.chunk
->type
== ctText
) {
308 item
.chunk
->sStart
= 0;
309 item
.chunk
->sStart
= item
.chunk
->chars
;
310 rect
.size
.width
= WMWidthOfString(font
,
311 item
.text
, item
.chars
);
313 rect
.size
.width
= item
.chunk
->pixmap
->width
;
318 rect
.size
.height
= lh
;
323 myDrawText(Text
*tPtr
, Paragraph
*para
, MyTextItems
*items
,
324 short nitems
, short pwidth
, int y
, short draw
, short spacepos
)
326 short i
, ul_thick
, u
, j
=0; /* j = justification */
327 short line_width
= 0, line_height
=0, mx_descent
=0;
328 WMScreen
*screen
= tPtr
->view
->screen
;
332 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return 0;
333 for(i
=0; i
<nitems
; i
++) {
334 if(items
[i
].type
== ctText
) {
335 font
= (tPtr
->monoFont
)?tPtr
->dFont
:items
[i
].chunk
->font
;
336 mx_descent
= WMIN(mx_descent
, -font
->y
);
337 line_height
= WMAX(line_height
, font
->height
);
338 //printf("chunk.x %d xpoint.x %d\n",
339 // items[i].x, tPtr->clicked.x);
341 line_width
+= WMWidthOfString(font
,
342 items
[i
].text
, items
[i
].chars
);
344 mx_descent
= WMIN(mx_descent
, -(items
[i
].pix
->height
-3));
345 /* replace -3 wif descent... */
346 line_height
= WMAX(line_height
, items
[i
].pix
->height
);
347 if(para
->align
== WARight
|| para
->align
== WACenter
) {
348 line_width
+= items
[i
].pix
->width
;
351 if(para
->align
== WARight
) {
352 j
= pwidth
- line_width
;
353 } else if (para
->align
== WACenter
) {
354 j
= (short) ((float)(pwidth
- line_width
))/2.0;
357 if(tPtr
->findingClickPoint
&& (y
+line_height
>= tPtr
->clicked
.y
)) {
358 tPtr
->foundClickPoint
= True
;
359 tPtr
->currentChunk
= items
[0].chunk
; /* just first on this "line" */
360 tPtr
->tpos
= items
[0].start
; /* where to "start" counting from */
361 tPtr
->clicked
.x
= j
+items
[0].x
;
362 tPtr
->clicked
.y
= y
+line_height
+mx_descent
;
363 tPtr
->clheight
= line_height
; /* to draw the cursor */
364 tPtr
->clwidth
= line_width
; /* where to stop searching */
366 } if(!draw
) return line_height
;
368 for(i
=0; i
<nitems
; i
++) {
371 if(tPtr
->ownsSelection
) {
372 WMRect rect
= chunkSelectionRect(tPtr
, para
,
373 items
[i
], y
, j
, line_height
);
374 if(rect
.pos
.x
!= -23) { /* has been selected */
375 XFillRectangle(tPtr
->view
->screen
->display
, para
->drawbuffer
,
376 WMColorGC(WMGrayColor(tPtr
->view
->screen
)),
377 rect
.pos
.x
, rect
.pos
.y
, rect
.size
.width
, rect
.size
.height
);
381 if(items
[i
].type
== ctText
) {
382 gc
= WMColorGC(items
[i
].chunk
->color
);
383 font
= (tPtr
->monoFont
)?tPtr
->dFont
:items
[i
].chunk
->font
;
384 WMDrawString(screen
, para
->drawbuffer
, gc
, font
,
385 items
[i
].x
+j
, y
- font
->y
- mx_descent
,
386 items
[i
].text
, items
[i
].chars
);
387 if(items
[i
].chunk
->ul
&& !tPtr
->monoFont
) {
388 ul_thick
= (short) ((float)font
->height
)/12.0;
389 if (ul_thick
< 1) ul_thick
= 1;
390 for(u
=0; u
<ul_thick
; u
++) {
391 XDrawLine(screen
->display
, para
->drawbuffer
, gc
, items
[i
].x
+j
,
392 y
+ 1 + u
- mx_descent
,
393 items
[i
].x
+ j
+ WMWidthOfString(font
,
394 items
[i
].text
, items
[i
].chars
), y
+ 1 + u
- mx_descent
);
398 WMDrawPixmap(items
[i
].pix
, para
->drawbuffer
, items
[i
].x
+j
,
399 y
+ 3 - mx_descent
- items
[i
].pix
->height
);
405 drawPChunkPart(Text
*tPtr
, Chunk
*chunk
, LocalMargins m
, Paragraph
*para
,
406 MyTextItems
*items
, short *nitems
, short *Lmargin
, XPoint
*where
, short draw
)
412 chunk
->pixmap
= WMRetainPixmap(tPtr
->dUnknownImg
);
414 p_width
= m
.right
- WMIN(m
.first
, m
.body
) - WMGetRulerOffset(tPtr
->ruler
);
415 if(p_width
< MIN_DOC_WIDTH
) // need WMRuler to take care of this...
417 if(where
->x
+ chunk
->pixmap
->width
<= p_width
- *Lmargin
) {
418 /* it can fit on rest of line */
419 items
[*nitems
].pix
= chunk
->pixmap
;
420 items
[*nitems
].type
= ctImage
;
421 items
[*nitems
].chars
= 0;
422 items
[*nitems
].x
= *Lmargin
+where
->x
;
423 items
[*nitems
].chunk
= chunk
;
424 items
[*nitems
].start
= 0;
426 if(*nitems
>= MAX_CHUNX
) {
427 items
[*nitems
].chars
= 0;
428 items
[*nitems
].x
= *Lmargin
+where
->x
;
429 items
[*nitems
].chunk
= chunk
;
430 items
[*nitems
].start
= 0;
431 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
432 p_width
-*Lmargin
, where
->y
, draw
, 0);
433 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
438 where
->x
+= chunk
->pixmap
->width
;
440 } else if(chunk
->pixmap
->width
<= p_width
- *Lmargin
) {
441 /* it can fit on an entire line, flush the myDrawText then wrap it */
442 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
443 p_width
-*Lmargin
, where
->y
, draw
, 0);
445 *Lmargin
= WMAX(0, m
.body
- m
.first
);
447 drawPChunkPart(tPtr
, chunk
, m
, para
, items
, nitems
,
448 Lmargin
, where
, draw
);
453 *Lmargin
= WMAX(0, m
.body
- m
.first
);
454 items
[*nitems
].pix
= chunk
->pixmap
;
455 items
[*nitems
].type
= ctImage
;
456 items
[*nitems
].chars
= 0;
457 items
[*nitems
].x
= *Lmargin
+where
->x
;
458 items
[*nitems
].chunk
= chunk
;
459 items
[*nitems
].start
= 0;
460 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
461 p_width
-*Lmargin
, where
->y
, draw
, 0);
464 /* scale image to fit, call self again */
465 /* deprecated - the management */
471 drawTChunkPart(Text
*tPtr
, Chunk
*chunk
, char *bufr
, LocalMargins m
,
472 Paragraph
*para
, MyTextItems
*items
, short *nitems
, short len
, short start
,
473 short *Lmargin
, XPoint
*where
, short draw
, short spacepos
)
475 short t_chunk_width
, p_width
, chars
;
476 WMFont
*font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
477 /* if(doc->clickstart.yes && doc->clickstart.done) return; */
480 p_width
= m
.right
- WMIN(m
.first
, m
.body
);
481 if(p_width
< MIN_DOC_WIDTH
) // need WMRuler to take care of this...
485 t_chunk_width
= WMWidthOfString(font
, bufr
, len
);
486 if((where
->x
+ t_chunk_width
<= p_width
- *Lmargin
)
487 || (tPtr
->wrapping
== wrNone
)) {
488 /* if it can fit on rest of line, append to line */
489 chars
= WMIN(len
, MAX_WORD_LENGTH
);
490 snprintf(items
[*nitems
].text
, chars
+1, "%s", bufr
);
491 items
[*nitems
].chars
= chars
;
492 items
[*nitems
].x
= *Lmargin
+where
->x
;
493 items
[*nitems
].type
= ctText
;
494 items
[*nitems
].chunk
= chunk
;
495 items
[*nitems
].start
= start
;
497 if(*nitems
>= MAX_CHUNX
) {
498 chars
= WMIN(len
, MAX_WORD_LENGTH
);
499 snprintf(items
[*nitems
].text
, chars
+1, "%s", bufr
);
500 items
[*nitems
].chars
= chars
;
501 items
[*nitems
].x
= *Lmargin
+where
->x
;
502 items
[*nitems
].type
= ctText
;
503 items
[*nitems
].chunk
= chunk
;
504 items
[*nitems
].start
= start
;
505 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
506 p_width
-*Lmargin
, where
->y
, draw
, spacepos
);
507 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
512 where
->x
+= t_chunk_width
;
514 } else if(t_chunk_width
<= p_width
- *Lmargin
) {
515 /* it can fit on an entire line, flush and wrap it to a new line */
516 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
,
517 p_width
-*Lmargin
, where
->y
, draw
, spacepos
);
518 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
520 *Lmargin
= WMAX(0, m
.body
- m
.first
);
522 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
, items
, nitems
,
523 len
, start
, Lmargin
, where
, draw
, spacepos
);
525 /* otherwise, chop line, call ourself recursively until it's all gone */
526 short J
=0; /* bufr */
527 short j
=0; /* local tmp buffer */
529 short diff
= p_width
- *Lmargin
- where
->x
;
533 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
,
534 p_width
-*Lmargin
, where
->y
, draw
, spacepos
);
535 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
537 *Lmargin
= WMAX(0, m
.body
- m
.first
);
539 diff
= p_width
- *Lmargin
- where
->x
;
542 for(J
=0; J
<len
; J
++) {
544 if(WMWidthOfString(font
, tmp
, j
+1) > diff
) {
545 drawTChunkPart(tPtr
, chunk
, tmp
, m
, para
, items
, nitems
,
546 j
, start
+_start
, Lmargin
, where
, draw
, spacepos
);
551 /* and there's always that last chunk, get it too */
552 drawTChunkPart(tPtr
, chunk
, tmp
, m
, para
, items
, nitems
,
553 j
, start
+_start
, Lmargin
, where
, draw
, spacepos
);
557 /* this function does what it's called :-)
558 o It is also used for calculating extents of para,
559 (returns height) so watch out for (Bool) draw
560 o Also used to determine where mouse was clicked */
562 putParagraphOnPixmap(Text
*tPtr
, Paragraph
*para
, Bool draw
)
564 char bufr
[MAX_WORD_LENGTH
+1]; /* a single word + '\0' */
565 MyTextItems items
[MAX_CHUNX
+1];
566 short lmargin
, spacepos
, i
, s
, nitems
, start
;
571 if(!tPtr
->view
->flags
.realized
|| !para
) return 0;
573 where
.x
= 0, where
.y
=0, nitems
= 0;
574 m
.left
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
575 m
.right
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) - m
.left
;
576 m
.first
= para
->fmargin
, m
.body
= para
->bmargin
;
579 W_Screen
*screen
= tPtr
->view
->screen
;
581 XFreePixmap(screen
->display
, para
->drawbuffer
);
582 if(para
->width
<2*tPtr
->dFont
->height
) para
->width
= 2*tPtr
->dFont
->height
;
583 if(para
->height
<tPtr
->dFont
->height
) para
->height
= tPtr
->dFont
->height
;
584 para
->drawbuffer
= XCreatePixmap(screen
->display
,
585 tPtr
->view
->window
, para
->width
, para
->height
, screen
->depth
);
586 XFillRectangle(screen
->display
, para
->drawbuffer
,
587 WMColorGC(tPtr
->bg
), 0, 0, para
->width
, para
->height
);
591 //if(para->align != tPtr->dAlignment)
592 // para->align = tPtr->dAlignment;
594 /* draw the bullet if appropriate */
595 if(m
.body
>m
.first
&& !tPtr
->monoFont
) {
596 lmargin
= m
.body
- m
.first
;
599 WMDrawPixmap(para
->bulletPix
, para
->drawbuffer
, lmargin
-10, 5);
601 WMDrawPixmap(tPtr
->dBulletPix
, para
->drawbuffer
, lmargin
-10, 5);
603 /* NeXT sez next tab, I say the m.body - m.first margin */
605 lmargin
= WMAX(0, m
.first
- m
.body
);
608 if(tPtr
->findingClickPoint
&& !para
->chunks
) {
609 tPtr
->currentChunk
= NULL
;
610 tPtr
->foundClickPoint
= True
;
612 tPtr
->clicked
.x
= lmargin
;
614 tPtr
->clheight
= para
->height
;
619 chunk
= para
->chunks
;
622 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return 0;
624 if(chunk
->type
== ctImage
&& !tPtr
->monoFont
) {
625 drawPChunkPart(tPtr
, chunk
, m
, para
, items
, &nitems
,
626 &lmargin
, &where
, draw
);
627 } else if(chunk
->text
&& chunk
->type
== ctText
) {
628 if(tPtr
->wrapping
== wrNone
) {
629 drawTChunkPart(tPtr
, chunk
, chunk
->text
, m
, para
, items
, &nitems
,
630 chunk
->chars
, 0, &lmargin
, &where
, draw
, spacepos
);
631 } else if(tPtr
->wrapping
== wrWord
) {
632 spacepos
=0, i
=0, start
=0;
633 while(spacepos
< chunk
->chars
) {
634 bufr
[i
] = chunk
->text
[spacepos
];
635 if( bufr
[i
] == ' ' || i
>= MAX_WORD_LENGTH
) {
636 if(bufr
[i
] == ' ') s
=1; else s
=0;
637 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
,
638 items
, &nitems
, i
+s
, start
, &lmargin
, &where
,
641 if(i
> MAX_WORD_LENGTH
-1)
647 /* catch that last onery one. */
648 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
,
649 items
, &nitems
, i
, start
, &lmargin
, &where
, draw
, spacepos
);
650 } } chunk
= chunk
->next
;
652 /* we might have a few leftover items that need drawing */
654 where
.y
+= myDrawText(tPtr
, para
, items
,
655 nitems
, m
.right
-m
.left
-lmargin
, where
.y
, draw
, spacepos
);
656 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return 0;
662 calcParaExtents(Text
*tPtr
, Paragraph
*para
)
667 para
->width
= tPtr
->visibleW
;
670 para
->rmargin
= tPtr
->visibleW
;
672 para
->width
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) -
673 WMIN(para
->fmargin
, para
->bmargin
)
674 - WMGetRulerOffset(tPtr
->ruler
);
678 para
->height
= tPtr
->dFont
->height
;
680 para
->height
= putParagraphOnPixmap(tPtr
, para
, False
);
682 if(para
->height
<tPtr
->dFont
->height
)
683 para
->height
= tPtr
->dFont
->height
;
684 para
->bottom
= para
->top
+ para
->height
;
689 /* rather than bother with redrawing _all_ the pixmaps, simply
690 rearrange (i.e., push down or pull up) paragraphs after this one */
692 affectNextParas(Text
*tPtr
, Paragraph
*para
, int move_y
)
697 if(!para
|| move_y
==0) return;
699 old_y
= para
->bottom
;
700 calcParaExtents(tPtr
, para
);
701 old_y
-= para
->bottom
;
702 if(old_y
== 0) return;
704 }if(move_y
== 0) return;
709 next
->bottom
= next
->top
+ next
->height
;
710 next
= next
->next
; // I know, I know
711 }tPtr
->docHeight
+= move_y
;
714 tPtr
->vpos
+= move_y
;
715 if(tPtr
->vpos
< 0) tPtr
->vpos
= 0;
716 if(tPtr
->vpos
> tPtr
->docHeight
- tPtr
->visibleH
)
717 tPtr
->vpos
= tPtr
->docHeight
- tPtr
->visibleH
;
724 calcDocExtents(Text
*tPtr
)
729 tPtr
->docWidth
= tPtr
->visibleW
;
731 tPtr
->docWidth
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) -
732 WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
735 para
= tPtr
->paragraphs
;
738 para
->top
= tPtr
->docHeight
;
739 tPtr
->docHeight
+= calcParaExtents(tPtr
, para
);
740 para
->bottom
= tPtr
->docHeight
;
743 } else { /* default to this if no paragraphs */
744 tPtr
->docHeight
= tPtr
->dFont
->height
;
747 if(tPtr
->editable
) /* add space at bottom to enter new stuff */
748 tPtr
->docHeight
+= tPtr
->dFont
->height
;
753 /* If any part of a paragraph is viewable, the entire
754 paragraph is drawn on an otherwise empty (XFreePixmap) pixmap.
755 The actual viewable parts of the paragraph(s) are then pieced
756 together via paintText:
758 -------------------------------------------
759 || this is a paragraph in this document||
760 ||========================================||
761 || | only part of it is visible though. ||
762 || |-------------------------------------||
763 ||[.| This is another paragraph ||
764 || | which I'll make relatively long ||
765 || | just for the sake of writing a long ||
766 || | paragraph with a picture: ^_^ ||
767 || |-------------------------------------||
768 ||==| Of the three paragraphs, only ||
769 ||/\| the preceding was totally copied to ||
770 ||\/| totally copied to the window, even ||
771 ==========================================||
772 though they are all on pixmaps.
773 -------------------------------------------
774 This paragraph exists only in
775 memory and so has a NULL pixmap.
776 -------------------------------------------
779 simple, right? Performance: the best of both worlds...
780 o fast scrolling: no need to rewrite what's already
781 on the screen, simply XCopy it.
782 o fast typing: only change current para, then simply
783 affect other (i.e., subsequent) paragraphs.
784 o If no part of para is on screen, gdbFree pixmap; else draw on
785 individual pixmap per para then piece several paras together
786 o Keep track of who to XCopy to window (see paintText) */
788 drawDocumentPartsOnPixmap(Text
*tPtr
, Bool all
)
792 para
= tPtr
->paragraphs
;
794 /* the 32 reduces jitter on the human eye by preparing paragraphs
795 in anticipation of when the _moving_ scrollbar reaches them */
796 if(para
->bottom
+ 32 < tPtr
->vpos
||
797 para
->top
> tPtr
->visibleH
+ tPtr
->vpos
+ 32 ) {
798 if(para
->drawbuffer
) {
799 XFreePixmap(tPtr
->view
->screen
->display
, para
->drawbuffer
);
800 para
->drawbuffer
= (Pixmap
) NULL
;
803 if(!para
->drawbuffer
|| all
)
804 putParagraphOnPixmap(tPtr
, para
, True
);
812 /* this function blindly copies the "visible" parts of a pragraph
813 unto the view, (top-down approach). It starts drawing from
814 the top of the view (which paragraph to draw is determined by
815 drawDocumentPartsOnPixmap); it stops at the bottom of the view. */
817 paintText(Text
*tPtr
)
819 short lmargin
, para_lmargin
;
820 int from
=5, to
=5, height
;
822 short vS
=0, hS
=0, rh
=0;
824 if(!tPtr
->view
->flags
.realized
) return;
826 if(tPtr
->rulerShown
) rh
= 40;
829 if(tPtr
->hasVscroller
) vS
= 21;
830 if(tPtr
->hasHscroller
) hS
= 21;
832 //XClearWindow(tPtr->view->screen->display, tPtr->view->window);
834 lmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
835 if(tPtr
->paragraphs
) {
836 para
= tPtr
->paragraphs
;
838 if(para
->drawbuffer
) {
839 from
= (para
->top
<=tPtr
->vpos
)?tPtr
->vpos
-para
->top
:0;
840 height
= para
->height
- from
;
841 if(from
>=0 && height
>0 ) {
842 para_lmargin
= WMIN(para
->fmargin
, para
->bmargin
);
843 if(lmargin
-vS
<WMIN(para
->fmargin
, para
->bmargin
)) {
845 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
846 lmargin
, to
, 2+para_lmargin
, height
, False
);
848 XFillRectangle(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
849 WMColorGC(tPtr
->dColor
), lmargin
, to
, 2+para_lmargin
, height
);
852 XCopyArea(tPtr
->view
->screen
->display
, para
->drawbuffer
,
853 tPtr
->view
->window
, WMColorGC(tPtr
->bg
), 0, from
,
854 para
->width
-4, height
, lmargin
+para_lmargin
+2, to
);
855 if( (to
+=height
) > tPtr
->visibleH
+rh
)
863 /* clear any left over space (esp. during para deletes/ ruler changes) */
864 if(tPtr
->docHeight
< tPtr
->visibleH
&& tPtr
->visibleH
+rh
+5-to
>0) {
865 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
, vS
, to
,
866 tPtr
->view
->size
.width
-vS
, tPtr
->visibleH
+rh
+hS
+5-to
, False
);
870 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
871 vS
+1, rh
+5, lmargin
-vS
, tPtr
->visibleH
+rh
+5-vS
, False
);
874 // from the "selection" days...
875 W_DrawRelief(tPtr
->view
->screen
, WMWidgetXID(tPtr
),
876 tPtr
->sRect
.pos
.x
, tPtr
->sRect
.pos
.y
,
877 tPtr
->sRect
.size
.width
, tPtr
->sRect
.size
.height
, tPtr
->relief
);
880 W_DrawRelief(tPtr
->view
->screen
, WMWidgetXID(tPtr
), 0, rh
,
881 tPtr
->visibleW
+vS
, tPtr
->visibleH
+hS
, tPtr
->relief
);
883 if(tPtr
->editable
&& tPtr
->clheight
> 0) {
884 int top
= tPtr
->cursor
.y
-tPtr
->vpos
;
885 int bot
= top
+tPtr
->clheight
;
888 if(bot
>tPtr
->visibleH
+hS
-2) bot
= tPtr
->visibleH
+hS
-2;
890 //do something about italic text...
891 XDrawLine(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
892 WMColorGC(tPtr
->dColor
), lmargin
+tPtr
->cursor
.x
, top
,
893 lmargin
+tPtr
->cursor
.x
, bot
);
900 /* called anytime either the ruler, vscroller or hscroller is hidden/shown,
901 or when the widget is resized by some user action */
903 resizeText(W_ViewDelegate
*self
, WMView
*view
)
905 Text
*tPtr
= (Text
*)view
->self
;
908 if(!tPtr
->monoFont
&& tPtr
->rulerShown
)
911 W_ResizeView(view
, view
->size
.width
, view
->size
.height
);
912 WMResizeWidget(tPtr
->ruler
, view
->size
.width
, 40);
915 if(tPtr
->hasVscroller
) {
916 WMMoveWidget(tPtr
->vscroller
, 1, 1+rh
);
917 WMResizeWidget(tPtr
->vscroller
, 20, view
->size
.height
-rh
-2);
918 tPtr
->visibleW
= view
->size
.width
-21;
920 if(tPtr
->hasHscroller
) {
921 WMMoveWidget(tPtr
->hscroller
, 20, view
->size
.height
-21);
922 WMResizeWidget(tPtr
->hscroller
, view
->size
.width
-21, 20);
923 tPtr
->visibleH
= view
->size
.height
-21-rh
;
924 } else tPtr
->visibleH
= view
->size
.height
-rh
;
926 tPtr
->visibleW
= view
->size
.width
;
927 if(tPtr
->hasHscroller
) {
928 WMMoveWidget(tPtr
->hscroller
, 1, view
->size
.height
-21);
929 WMResizeWidget(tPtr
->hscroller
, view
->size
.width
-2, 20);
930 tPtr
->visibleH
= view
->size
.height
-21-rh
;
931 } else tPtr
->visibleH
= view
->size
.width
-2-rh
;
933 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
936 W_ViewDelegate _TextViewDelegate
=
946 /* a plain text parser */
947 /* this gives useful hints on how to make a more
948 interesting parser for say HTML, RTF */
950 defaultParser(Text
*tPtr
, void *data
, short type
)
952 char *start
, *mark
, *text
= (char *) data
;
954 Paragraph
*para
= NULL
;
958 mark
= strchr(start
, '\n');
960 /* there is a newline, indicating the need for a new paragraph */
961 /* attach the chunk to the current paragraph */
962 if((short)(mark
-start
) > 1) {
963 /* ignore chunks with just a single newline but still make a
965 chunk
= (tPtr
->funcs
.createTChunk
) (start
, (short)(mark
-start
),
966 tPtr
->dFont
, tPtr
->dColor
, 0, False
);
967 (tPtr
->funcs
.insertChunk
) (tPtr
, chunk
, type
);
969 /* _then_ create a new paragraph for the _next_ chunk */
970 para
= (tPtr
->funcs
.createParagraph
) (0, 0, tPtr
->visibleW
,
972 (tPtr
->funcs
.insertParagraph
) (tPtr
, para
, type
);
975 /* just attach the chunk to the current paragraph */
976 if(strlen(start
) > 0) {
977 chunk
= (tPtr
->funcs
.createTChunk
) (start
, strlen(start
),
978 tPtr
->dFont
, tPtr
->dColor
, 0, False
);
979 (tPtr
->funcs
.insertChunk
) (tPtr
, chunk
, type
);
986 updateScrollers(Text
*tPtr
)
988 if(tPtr
->hasVscroller
) {
989 if(tPtr
->docHeight
< tPtr
->visibleH
) {
990 WMSetScrollerParameters(tPtr
->vscroller
, 0, 1);
993 float vmax
= (float)(tPtr
->docHeight
);
994 WMSetScrollerParameters(tPtr
->vscroller
,
995 ((float)tPtr
->vpos
)/(vmax
- (float)tPtr
->visibleH
),
996 (float)tPtr
->visibleH
/vmax
);
1000 if(tPtr
->hasHscroller
)
1005 scrollersCallBack(WMWidget
*w
, void *self
)
1007 Text
*tPtr
= (Text
*)self
;
1008 Bool scroll
= False
;
1009 Bool dimple
= False
;
1011 if(!tPtr
->view
->flags
.realized
) return;
1013 if(w
== tPtr
->vscroller
) {
1016 vmax
= (float)(tPtr
->docHeight
);
1017 height
= tPtr
->visibleH
;
1019 height
-= 7; /* the top border (5) + bottom (2) */
1021 switch(WMGetScrollerHitPart(tPtr
->vscroller
)) {
1022 case WSDecrementLine
:
1023 if(tPtr
->vpos
> 0) {
1024 if(tPtr
->vpos
>16) tPtr
->vpos
-=16;
1028 case WSIncrementLine
: {
1029 int limit
= tPtr
->docHeight
- height
;
1030 if(tPtr
->vpos
< limit
) {
1031 if(tPtr
->vpos
<limit
-16) tPtr
->vpos
+=16;
1032 else tPtr
->vpos
=limit
;
1035 case WSDecrementPage
:
1036 tPtr
->vpos
-= height
;
1042 printf("dimple needs to jump to mouse location ;-/\n");
1044 case WSIncrementPage
:
1045 tPtr
->vpos
+= height
;
1046 if(tPtr
->vpos
> (tPtr
->docHeight
- height
))
1047 tPtr
->vpos
= tPtr
->docHeight
- height
;
1050 printf("dimple needs to jump to mouse location ;-/\n");
1055 tPtr
->vpos
= WMGetScrollerValue(tPtr
->vscroller
)
1056 * (float)(tPtr
->docHeight
- height
);
1063 float vmax
= (float)(tPtr
->docHeight
);
1064 ((float)tPtr
->vpos
)/(vmax
- (float)tPtr
->visibleH
),
1065 (float)tPtr
->visibleH
/vmax
);
1066 dimple
=where mouse is
.
1070 scroll
= (tPtr
->vpos
!= tPtr
->prevVpos
);
1071 tPtr
->prevVpos
= tPtr
->vpos
;
1074 if(w
== tPtr
->hscroller
)
1077 //need scrollv || scrollh
1081 if(tPtr->rulerShown)
1082 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
1083 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
1085 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
1086 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
1089 updateScrollers(tPtr
);
1090 drawDocumentPartsOnPixmap(tPtr
, False
);
1097 W_InsertText(WMText
*tPtr
, void *data
, Bool prepend
)
1101 Paragraph
*para
= tPtr
->paragraphs
, *ptmp
;
1102 Chunk
*chunk
, *ctmp
;
1105 chunk
= para
->chunks
;
1107 if(chunk
->type
== ctText
&& chunk
->text
)
1108 wgdbFree(chunk
->text
);
1109 else if(chunk
->pixmap
)
1110 WMReleasePixmap(chunk
->pixmap
);
1112 chunk
= chunk
->next
;
1117 if(ptmp
->drawbuffer
)
1118 XFreePixmap(tPtr
->view
->screen
->display
, ptmp
->drawbuffer
);
1121 tPtr
->paragraphs
= NULL
;
1122 tPtr
->currentPara
= NULL
;
1123 tPtr
->currentChunk
= NULL
;
1125 WMRefreshText(tPtr
, 0, 0);
1130 (tPtr
->parser
)(tPtr
, data
, prepend
);
1132 defaultParser(tPtr
, data
, prepend
);
1136 cursorToTextPosition(Text
*tPtr
, int x
, int y
)
1138 Paragraph
*para
= NULL
;
1139 Chunk
*chunk
= NULL
;
1142 short orig_x
, orig_y
;
1144 if(x
<(tPtr
->hasVscroller
?21:1)) {
1145 y
-= tPtr
->clheight
;
1146 x
= tPtr
->view
->size
.width
; //tPtr->visibleW;
1147 } else if(x
>tPtr
->clwidth
&& x
<tPtr
->clicked
.x
) {
1148 //x = (tPtr->hasVscroller)?21:1;
1149 //y += tPtr->clheight;
1155 if(y
<0 || y
>tPtr
->view
->size
.height
-3) return;
1157 tPtr
->clicked
.x
= orig_x
;
1158 tPtr
->clicked
.y
= y
;
1159 tPtr
->clicked
.y
+= tPtr
->vpos
;
1160 tPtr
->clicked
.y
-= tPtr
->rulerShown
?40:0;
1161 para
= tPtr
->paragraphs
;
1164 if( tPtr
->clicked
.y
>= para
->top
-4 &&
1165 tPtr
->clicked
.y
< para
->bottom
+4) break;
1167 } if(!(tPtr
->currentPara
= para
)) return;
1169 tPtr
->clicked
.y
-= para
->top
;
1170 if(tPtr
->clicked
.y
<0) tPtr
->clicked
.y
=0;
1171 if(tPtr
->hasVscroller
) x
-= 21;
1174 tPtr
->findingClickPoint
= True
;
1175 tPtr
->foundClickPoint
= False
;
1176 /* also affects tPtr->currentChunk, tPtr->clicked.x and y,
1177 tPtr->clheight and ->width */
1178 putParagraphOnPixmap(tPtr
, para
, False
);
1179 tPtr
->findingClickPoint
= False
;
1180 tPtr
->clicked
.y
+= para
->top
;
1182 if(tPtr
->currentChunk
) {
1183 short _width
=0, start
=tPtr
->tpos
, done
=False
, w
=0;
1184 chunk
= tPtr
->currentChunk
;
1185 while(!done
&& chunk
&& line_width
<tPtr
->clwidth
) {
1186 if(chunk
->type
== ctText
) {
1187 font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
1188 for (w
=start
; w
<chunk
->chars
; w
++) {
1189 _width
= WMWidthOfString(font
, &chunk
->text
[w
], 1);
1190 line_width
+= _width
;
1191 if(line_width
+tPtr
->clicked
.x
>= x
) {
1192 line_width
-= _width
;
1198 if(0&&chunk
->next
) {
1199 if(chunk
->next
->type
== ctImage
) {
1200 if(x
+10 < line_width
+chunk
->next
->pixmap
->width
) {
1205 _width
= chunk
->pixmap
->width
;
1206 line_width
+= _width
;
1207 if(line_width
+tPtr
->clicked
.x
>= x
) {
1208 line_width
-= _width
;
1214 chunk
= chunk
->next
;
1218 tPtr
->currentChunk
= chunk
;
1221 short vS
= (tPtr
->hasVscroller
)?32:12;
1222 if(para
->align
== WARight
) {
1223 tPtr
->clicked
.x
= tPtr
->view
->size
.width
-vS
;
1224 } else if (para
->align
== WACenter
) {
1225 tPtr
->clicked
.x
= -(vS
/2)+(tPtr
->view
->size
.width
-vS
)/2;
1227 tPtr
->clicked
.x
= 2;
1230 tPtr
->cursor
.x
= tPtr
->clicked
.x
+2+line_width
;
1231 tPtr
->cursor
.y
= tPtr
->clicked
.y
;
1232 tPtr
->clicked
.y
= orig_y
;
1233 tPtr
->clicked
.x
= orig_x
;
1234 putParagraphOnPixmap(tPtr
, para
, True
);
1239 deleteTextInteractively(Text
*tPtr
, DeleteType type
)
1243 short pos
,w
=0,h
=0, doprev
=False
, doprevpara
=False
;
1245 int current
= WMGetTextCurrentChunk(tPtr
);
1247 if(!(para
= tPtr
->currentPara
)) return;
1248 if(!(chunk
= tPtr
->currentChunk
)) return;
1249 font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
1250 doprev
= (tPtr
->tpos
< 2);
1253 case dtDelete
: /* delete _after_ cursor ... implement later */
1254 case dtBackSpace
: /* delete _before_ cursor */
1255 if(chunk
->chars
> 1) {
1257 printf("here %d\n", pos
);
1259 w
= WMWidthOfString(font
, &chunk
->text
[pos
], 1);
1260 memmove(&(chunk
->text
[pos
]),
1261 &(chunk
->text
[pos
+1]), chunk
->chars
-pos
+1);
1265 WMRemoveTextChunk(tPtr
, current
);
1271 WMSetTextCurrentChunk(tPtr
, current
-1);
1272 if(!tPtr
->currentChunk
) {
1273 printf("PREV PARA\n");
1275 tPtr
->tpos
= tPtr
->currentChunk
->chars
;
1278 int currentp
= WMGetTextCurrentParagraph(tPtr
);
1281 para
->chunks
= NULL
;
1282 WMRemoveTextParagraph(tPtr
, currentp
);
1283 WMSetTextCurrentParagraph(tPtr
, currentp
-1);
1284 WMSetTextCurrentChunk(tPtr
, -1);
1285 para
= tPtr
->currentPara
;
1287 if(!tPtr
->currentChunk
|| !para
->chunks
) {
1288 para
->chunks
= chunk
;
1289 tPtr
->currentChunk
= chunk
;
1291 tPtr
->currentChunk
->next
= chunk
;
1294 if(1) { //if(1||(para && !doprevpara)) {
1295 affectNextParas(tPtr
, para
, -23);
1296 putParagraphOnPixmap(tPtr
, para
, True
);
1297 drawDocumentPartsOnPixmap(tPtr
, False
);
1298 updateScrollers(tPtr
);
1300 //cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1301 } else WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1305 /* give us nice chunk sizes (multiples of 16) */
1307 reqBlockSize(short requested
)
1309 return requested
+16-(requested
%16);
1313 insertTextInteractively(Text
*tPtr
, char *text
)
1315 Paragraph
*para
=NULL
;
1316 Chunk
*chunk
=NULL
, *newchunk
=NULL
;
1317 int height
= -23; /* should only be changed upon newline */
1321 if(!tPtr
->editable
) return;
1322 if(*text
== '\n' && tPtr
->ignoreNewLine
)
1325 para
= tPtr
->currentPara
;
1326 chunk
= tPtr
->currentChunk
;
1327 font
= (tPtr
->monoFont
|| !chunk
)?tPtr
->dFont
:chunk
->font
;
1331 if(chunk
) { /* there's a chunk (or part of it) to detach from old */
1332 int current
= WMGetTextCurrentChunk(tPtr
);
1333 if(tPtr
->tpos
<=0) { /* at start of chunk */
1334 if(current
<1) { /* the first chunk... make old para blank */
1335 newchunk
= para
->chunks
;
1336 para
->chunks
= NULL
;
1337 putParagraphOnPixmap(tPtr
, para
, True
);
1338 } else { /* not first chunk... */
1339 printf("cut me out \n");
1341 } else if(tPtr
->tpos
< chunk
->chars
&& chunk
->type
== ctText
) {
1342 /* not at start of chunk */
1343 char text
[chunk
->chars
-tPtr
->tpos
+1];
1346 text
[i
] = chunk
->text
[tPtr
->tpos
+i
];
1347 } while(++i
< chunk
->chars
-tPtr
->tpos
);
1349 newchunk
= (tPtr
->funcs
.createTChunk
) (text
, i
, chunk
->font
,
1350 chunk
->color
, chunk
->script
, chunk
->ul
);
1351 newchunk
->next
= chunk
->next
;
1353 /* might want to demalloc for LARGE cuts */
1354 //calcParaExtents(tPtr, para);
1355 para
->height
= putParagraphOnPixmap(tPtr
, para
, True
);
1356 //putParagraphOnPixmap(tPtr, para, True);
1357 } else if(tPtr
->tpos
>= chunk
->chars
) {
1359 WMSetTextCurrentChunk(tPtr
, current
-1);
1360 prev
= tPtr
->currentChunk
;
1362 newchunk
= prev
->next
;
1364 putParagraphOnPixmap(tPtr
, para
, True
);
1366 } else newchunk
= NULL
;
1368 if(para
) /* the preceeding one */
1369 new_top
= para
->bottom
;
1371 WMAppendTextStream(tPtr
, "\n");
1372 para
= tPtr
->currentPara
;
1374 para
->chunks
= newchunk
;
1375 tPtr
->currentChunk
= newchunk
;
1377 para
->top
= new_top
;
1378 calcParaExtents(tPtr
, para
);
1379 height
= para
->height
;
1382 WMAppendTextStream(tPtr
, text
);
1383 para
= tPtr
->currentPara
;
1384 } else if(!para
->chunks
|| !chunk
) {
1385 //WMPrependTextStream(tPtr, text);
1386 WMAppendTextStream(tPtr
, text
);
1387 } else if(chunk
->type
== ctImage
) {
1388 WMPrependTextStream(tPtr
, text
);
1390 printf("\n\nprepe\n\n");
1392 if(tPtr
->tpos
> chunk
->chars
) {
1393 printf("\n\nmore\n\n");
1394 tPtr
->tpos
= chunk
->chars
;
1397 if(chunk
->chars
+1 >= chunk
->mallocedSize
) {
1398 chunk
->mallocedSize
= reqBlockSize(chunk
->chars
+1);
1399 chunk
->text
= wrealloc(chunk
->text
, chunk
->mallocedSize
);
1402 memmove(&(chunk
->text
[tPtr
->tpos
+1]), &chunk
->text
[tPtr
->tpos
],
1403 chunk
->chars
-tPtr
->tpos
+1);
1404 w
= WMWidthOfString(font
, text
, 1);
1405 memmove(&chunk
->text
[tPtr
->tpos
], text
, 1);
1408 //doc->clickstart.cursor.x +=
1409 //WMWidthOfString(chunk->fmt->font, text,len);
1415 affectNextParas(tPtr
, para
, height
);
1416 putParagraphOnPixmap(tPtr
, para
, True
);
1417 drawDocumentPartsOnPixmap(tPtr
, False
);
1418 updateScrollers(tPtr
);
1420 //cursorToTextPosition(tPtr, tPtr->clicked.x+w, tPtr->clicked.y);
1421 //check for "sneppah tahw" with blank paras...
1428 selectRegion(Text
*tPtr
, int x
, int y
)
1430 tPtr
->sRect
.pos
.x
= WMIN(tPtr
->clicked
.x
, x
);
1431 tPtr
->sRect
.size
.width
= abs(tPtr
->clicked
.x
-x
);
1432 tPtr
->sRect
.pos
.y
= WMIN(tPtr
->clicked
.y
, y
);
1433 if(tPtr
->sRect
.pos
.y
<0) tPtr
->sRect
.pos
.y
=0;
1434 tPtr
->sRect
.size
.height
= abs(tPtr
->clicked
.y
-y
);
1437 while(y>tPtr->visibleH && tPtr->vpos < tPtr->docHeight-tPtr->visibleH) {
1438 WMRefreshText(tPtr, tPtr->vpos+16, tPtr->hpos);
1441 //printf("%d %d \n", y, tPtr->vpos);
1443 //foreach para in selection...
1444 drawDocumentPartsOnPixmap(tPtr
, True
);
1452 #define WM_EMACSKEYMASK ControlMask
1453 #define WM_EMACSKEY_LEFT XK_b
1454 #define WM_EMACSKEY_RIGHT XK_f
1455 #define WM_EMACSKEY_HOME XK_a
1456 #define WM_EMACSKEY_END XK_e
1457 #define WM_EMACSKEY_BS XK_h
1458 #define WM_EMACSKEY_DEL XK_d
1461 handleTextKeyPress(Text
*tPtr
, XEvent
*event
)
1465 int control_pressed
= False
;
1467 if(!tPtr
->editable
) return;
1469 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
)
1470 control_pressed
= True
;
1471 buffer
[XLookupString(&event
->xkey
, buffer
, 1, &ksym
, NULL
)] = '\0';
1477 if(tPtr
->currentChunk
) {
1479 Chunk
*chunk
= tPtr
->currentChunk
;
1480 if(chunk
->type
== ctText
) {
1481 WMFont
*font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
1482 if(ksym
==XK_Right
) {
1483 short pos
= (tPtr
->tpos
<chunk
->chars
)?tPtr
->tpos
+1:
1485 w
= WMWidthOfString(font
,&chunk
->text
[pos
],1);
1487 short pos
= (tPtr
->tpos
>0)?tPtr
->tpos
-1:0;
1488 w
= WMWidthOfString(font
,&chunk
->text
[pos
],1);
1490 } else { w
= chunk
->pixmap
->width
; }
1491 if(ksym
==XK_Right
) w
= -w
;
1492 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
-w
, tPtr
->clicked
.y
);
1494 if(ksym
==XK_Right
) ksym
= XK_Down
;
1502 noCChunk
: { short h
= tPtr
->clheight
-2;
1503 if(ksym
==XK_Down
) h
= -h
;
1504 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
, tPtr
->clicked
.y
-h
);
1508 deleteTextInteractively(tPtr
, dtBackSpace
);
1513 deleteTextInteractively(tPtr
, dtDelete
);
1519 if(buffer
[0] != '\0' && (buffer
[0] == '\n' || !iscntrl(buffer
[0])))
1520 insertTextInteractively(tPtr
, buffer
);
1521 else if(control_pressed
&& ksym
==XK_r
)
1522 {Bool i
= !tPtr
->rulerShown
; WMShowTextRuler(tPtr
, i
);
1523 tPtr
->rulerShown
= i
; }
1531 pasteText(WMView
*view
, Atom selection
, Atom target
, Time timestamp
,
1532 void *cdata
, WMData
*data
)
1534 Text
*tPtr
= (Text
*)view
->self
;
1538 tPtr
->waitingForSelection
= False
;
1540 str
= (char*)WMDataBytes(data
);
1541 if(tPtr
->tpos
<1) WMPrependTextStream(tPtr
, str
);
1542 else WMAppendTextStream(tPtr
, str
);
1543 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1546 str
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1549 if(tPtr
->tpos
<1) WMPrependTextStream(tPtr
, str
);
1550 else WMAppendTextStream(tPtr
, str
);
1552 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1559 releaseSelection(Text
*tPtr
)
1561 Paragraph
*para
= tPtr
->paragraphs
;
1564 chunk
= para
->chunks
;
1566 chunk
->selected
= False
;
1567 chunk
= chunk
->next
;
1571 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
1572 tPtr
->ownsSelection
= False
;
1573 drawDocumentPartsOnPixmap(tPtr
, True
);
1579 requestHandler(WMView
*view
, Atom selection
, Atom target
,
1580 void *cdata
, Atom
*type
)
1582 Text
*tPtr
= view
->self
;
1584 Display
*dpy
= tPtr
->view
->screen
->display
;
1586 Atom TEXT
= XInternAtom(dpy
, "TEXT", False
);
1587 Atom COMPOUND_TEXT
= XInternAtom(dpy
, "COMPOUND_TEXT", False
);
1588 WMData
*data
= NULL
;
1591 if(!tPtr
->ownsSelection
|| !tPtr
->paragraphs
) return NULL
;
1592 //printf("got here\n");
1594 if (target
== XA_STRING
|| target
== TEXT
|| target
== COMPOUND_TEXT
) {
1595 //for bleh in selection...
1597 Paragraph
*para
= tPtr
->paragraphs
;
1598 Chunk
*chunk
= NULL
;
1599 char pixmap
[] = "[pixmap]";
1604 chunk
= para
->chunks
;
1607 if(chunk
->selected
&& chunk
->type
== ctText
) {
1608 len
= chunk
->chars
; //chunk->sEnd - chunk->sStart;
1612 memcpy(s
, &chunk
->text
[0*chunk
->sStart
], len
);
1615 data
= WMCreateDataWithBytes(s
, strlen(s
));
1618 printf("append: %c %d\n", *s
, strlen(s
));
1619 WMAppendDataBytes(data
, s
, strlen(s
));
1624 printf("len is %d [%d %d] %d \n", len
, chunk
->sStart
, chunk
->sEnd
,
1627 chunk
= chunk
->next
;
1633 WMSetDataFormat(data
, 8);
1640 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
1641 if (target
== _TARGETS
) {
1642 Atom
*ptr
= wmalloc(4 * sizeof(Atom
));
1646 ptr
[3] = COMPOUND_TEXT
;
1648 data
= WMCreateDataWithBytes(ptr
, 4*4);
1649 WMSetDataFormat(data
, 32);
1662 lostHandler(WMView
*view
, Atom selection
, void *cdata
)
1664 WMText
*tPtr
= (WMText
*)view
->self
;
1665 releaseSelection(tPtr
);
1668 static WMSelectionProcs selectionHandler
= {
1669 requestHandler
, lostHandler
, NULL
};
1672 _notification(void *observerData
, WMNotification
*notification
)
1674 WMText
*to
= (WMText
*)observerData
;
1675 WMText
*tw
= (WMText
*)WMGetNotificationClientData(notification
);
1676 if (to
!= tw
) lostHandler(to
->view
, XA_PRIMARY
, NULL
);
1680 handleTextEvents(XEvent
*event
, void *data
)
1682 Text
*tPtr
= (Text
*)data
;
1683 Display
*dpy
= event
->xany
.display
;
1685 if(tPtr
->waitingForSelection
) return;
1687 switch (event
->type
) {
1689 if(!tPtr
->editable
|| tPtr
->buttonHeld
) {
1693 if(tPtr
->ownsSelection
) releaseSelection(tPtr
);
1694 //if (tPtr->waitingForSelection) return;
1697 XGrabPointer(dpy
, W_VIEW(tPtr
)->window
, False
,
1698 PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
1699 GrabModeAsync
, GrabModeAsync
, None
,
1700 W_VIEW(tPtr
)->screen
->invisibleCursor
, CurrentTime
);
1701 tPtr
->pointerGrabbed
= True
;
1703 handleTextKeyPress(tPtr
, event
);
1707 if(tPtr
->pointerGrabbed
) {
1708 tPtr
->pointerGrabbed
= False
;
1709 XUngrabPointer(dpy
, CurrentTime
);
1711 if((event
->xmotion
.state
& Button1Mask
)) {
1712 selectRegion(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
1713 if(!tPtr
->ownsSelection
) {
1714 WMCreateSelectionHandler(tPtr
->view
, XA_PRIMARY
,
1715 event
->xbutton
.time
, &selectionHandler
, NULL
);
1716 tPtr
->ownsSelection
= True
;
1721 if(event
->xbutton
.button
== Button1
) {
1722 if(tPtr
->ownsSelection
) releaseSelection(tPtr
);
1723 cursorToTextPosition(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
1724 if (tPtr
->pointerGrabbed
) {
1725 tPtr
->pointerGrabbed
= False
;
1726 XUngrabPointer(dpy
, CurrentTime
);
1730 if(!tPtr
->focused
) {
1731 WMSetFocusToWidget(tPtr
);
1732 tPtr
->focused
= True
;
1735 if(event
->xbutton
.button
== 4)
1736 WMScrollText(tPtr
, -16);
1737 else if(event
->xbutton
.button
== 5)
1738 WMScrollText(tPtr
, 16);
1743 tPtr
->buttonHeld
= False
;
1744 if (tPtr
->pointerGrabbed
) {
1745 tPtr
->pointerGrabbed
= False
;
1746 XUngrabPointer(dpy
, CurrentTime
);
1749 if(event
->xbutton
.button
== 4 || event
->xbutton
.button
== 5)
1751 if(event
->xbutton
.button
== Button2
&& tPtr
->editable
) {
1754 if(!WMRequestSelection(tPtr
->view
, XA_PRIMARY
, XA_STRING
,
1755 event
->xbutton
.time
, pasteText
, NULL
)) {
1756 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1759 WMAppendTextStream(tPtr
, text
);
1761 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1762 } else tPtr
->waitingForSelection
= True
;
1771 handleNonTextEvents(XEvent
*event
, void *data
)
1773 Text
*tPtr
= (Text
*)data
;
1775 switch(event
->type
) {
1777 if(!event
->xexpose
.count
&& tPtr
->view
->flags
.realized
)
1782 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
1784 tPtr
->focused
= True
;
1785 //cursor...paintText(tPtr);
1789 tPtr
->focused
= False
;
1790 //cursor...paintText(tPtr);
1795 //for(...)WMRemoveTextParagraph(tPtr, para);
1800 //printf("handleNonTextEvents\n");
1806 rulerCallBack(WMWidget
*w
, void *self
)
1808 Text
*tPtr
= (Text
*)self
;
1811 if(tPtr
->currentPara
) {
1812 Paragraph
*para
= tPtr
->currentPara
;
1813 para
->fmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerFirst
);
1814 para
->bmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerBody
);
1815 para
->rmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
);
1816 affectNextParas(tPtr
, para
, -23);
1817 putParagraphOnPixmap(tPtr
, para
, True
);
1820 which
= WMGetReleasedRulerMargin(tPtr
->ruler
);
1821 if(which
!= WRulerDocLeft
&& which
!= WRulerRight
1822 /* && Selection.para.count > 0 */ ) {
1824 "//for(i=0; i<Selection.para.count; i++) {"
1826 "//calcParaExtents(tPtr, para);}\n");
1828 WMRefreshText(tPtr
, 0, 0);
1835 rulerMoveCallBack(WMWidget
*w
, void *self
)
1837 Text
*tPtr
= (Text
*)self
;
1838 short rmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
);
1841 if(WMGetGrabbedRulerMargin(tPtr
->ruler
) == WRulerLeft
) {
1842 short lmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
1843 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
1844 22, 42, lmargin
-21, tPtr
->visibleH
, True
);
1845 } else if(WMGetGrabbedRulerMargin(tPtr
->ruler
) == WRulerRight
&&
1846 tPtr
->docWidth
+11 < rmargin
) {
1847 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
1848 rmargin
-3, 42, 10, tPtr
->visibleH
, True
);
1855 /* ------------- non-static functions that are "friends" ------------- */
1856 /* ------------- called as (tPtr->funcs.foo)(bars...) ------------- */
1858 /* create a new paragraph. Don't do anything with it just yet */
1861 createParagraph(short fmargin
, short bmargin
, short rmargin
,
1862 short *tabstops
, short numTabs
, WMAlignment alignment
)
1864 Paragraph
*para
= wmalloc(sizeof(Paragraph
));
1865 if(!para
) return NULL
;
1867 para
->chunks
= NULL
;
1871 para
->fmargin
= (fmargin
>=0)?fmargin
:0;
1872 para
->bmargin
= (bmargin
>=0)?bmargin
:0;
1873 if(rmargin
-bmargin
>= 100 && rmargin
-fmargin
>= 100)
1874 para
->rmargin
= rmargin
;
1876 para
->rmargin
= 100;
1877 para
->tabstops
= tabstops
;
1878 para
->numTabs
= (tabstops
)?numTabs
:0;
1880 para
->drawbuffer
= (Pixmap
)NULL
;
1881 para
->bulletPix
= NULL
;
1882 para
->top
= para
->bottom
= 0;
1883 para
->width
= para
->height
= 0;
1885 para
->align
= alignment
;
1890 /* insert the new paragraph in the tPtr, either right before
1891 or after the currentPara. It's the responsibility of the
1892 calling code to set what currentPara is. via WMSetTextCurrentParagraph.
1893 If currentPara is not set, set it as the first in the document.
1894 This function then sets currentPara as _this_ paragraph.
1895 NOTE: this means careless parser implementors might lose previous
1896 paragraphs... but this keeps stuff small and non-buggy :-) */
1899 insertParagraph(WMText
*tPtr
, void *v
, Bool prepend
)
1900 //insertParagraph(WMText *tPtr, Paragraph *para, InsertType type)
1903 Paragraph
*para
= (Paragraph
*)v
;
1904 if(!para
|| !tPtr
) return;
1906 if(!tPtr
->currentPara
) {
1907 tPtr
->paragraphs
= para
;
1909 tmp
= tPtr
->paragraphs
;
1911 while(tmp
->next
&& tmp
!= tPtr
->currentPara
)
1914 para
->next
= tmp
->next
;
1916 } else { /* must be prepend */
1917 /* this "prior" member is that "doing things the hard way"
1918 I spoke of. See? it's not too bad afterall... */
1919 Paragraph
*prior
= NULL
;
1920 while(tmp
->next
&& tmp
!= tPtr
->currentPara
) {
1924 /* if this is the first */
1925 if(tmp
== tPtr
->paragraphs
) {
1927 tPtr
->paragraphs
= para
;
1932 tPtr
->currentPara
= para
;
1936 /* create a new chunk to contain exactly ONE pixmap */
1939 createPChunk(WMPixmap
*pixmap
, short script
, ushort ul
)
1943 chunk
= wmalloc(sizeof(Chunk
));
1949 chunk
->pixmap
= NULL
; /* if it's NULL, we'll draw the "broken" pixmap... */
1950 else chunk
->pixmap
= WMRetainPixmap(pixmap
);
1952 chunk
->mallocedSize
= 0;
1953 chunk
->type
= ctImage
;
1955 chunk
->color
= NULL
;
1956 chunk
->script
= script
;
1958 chunk
->selected
= False
;
1964 /* create a new chunk to contain some text with the given format */
1967 createTChunk(char *text
, short chars
, WMFont
*font
,
1968 WMColor
*color
, short script
, ushort ul
)
1972 if(!text
|| chars
<0 || !font
|| !color
) return NULL
;
1973 chunk
= wmalloc(sizeof(Chunk
));
1974 if(!chunk
) return NULL
;
1976 chunk
->mallocedSize
= reqBlockSize(chars
);
1977 chunk
->text
= (char *)wmalloc(chunk
->mallocedSize
);
1978 memcpy(chunk
->text
, text
, chars
);
1979 chunk
->pixmap
= NULL
;
1980 chunk
->chars
= chars
;
1981 chunk
->type
= ctText
;
1982 chunk
->font
= WMRetainFont(font
);
1983 chunk
->color
= WMRetainColor(color
);
1984 chunk
->script
= script
;
1986 chunk
->selected
= False
;
1992 /* insert the new chunk in the paragraph, either right before
1993 or after the currentChunk. It's the responsibility of the
1994 calling code to set what currentChunk is via WMSetTextCurrentChunk.
1995 If currentChunk is not set, set it as the first in the existing
1996 paragraph... if not even that, you lose... try again.
1997 This function then sets currentChunk as _this_ chunk.
1998 NOTE: this means careless parser implementors might lose previous
1999 paragraphs/chunks... but this keeps stuff small and non-buggy :-) */
2001 insertChunk(WMText
*tPtr
, void *v
, Bool prepend
)
2004 Chunk
*chunk
= (Chunk
*)v
;
2006 if(!tPtr
|| !chunk
) return;
2008 if(!tPtr
->paragraphs
) { /* i.e., first chunk via insertTextInteractively */
2009 Paragraph
*para
= (tPtr
->funcs
.createParagraph
) (0, 0, tPtr
->visibleW
,
2011 (tPtr
->funcs
.insertParagraph
) (tPtr
, para
, False
);
2014 if(!tPtr
->currentPara
)
2016 if(!tPtr
->currentChunk
) { /* there is a current chunk */
2017 tPtr
->currentPara
->chunks
= chunk
;
2018 } else if(!tPtr
->currentPara
->chunks
) {
2019 /* but it's not of this paragraph */
2020 tPtr
->currentPara
->chunks
= chunk
;
2022 tmp
= tPtr
->currentPara
->chunks
;
2025 while(tmp
->next
&& tmp
!= tPtr
->currentChunk
)
2028 chunk
->next
= tmp
->next
;
2031 } else { /* must be prepend */
2032 /* this "prior" member is that "doing things the hard way"
2033 I spoke of. See? it's not too bad afterall... */
2034 Chunk
*prior
= NULL
;
2035 while(tmp
->next
&& tmp
!= tPtr
->currentChunk
) {
2039 /* if this is the first */
2040 if(tmp
== tPtr
->currentPara
->chunks
) {
2042 tPtr
->currentPara
->chunks
= chunk
;
2044 prior
->next
= chunk
;
2047 tPtr
->currentChunk
= chunk
;
2048 tPtr
->tpos
= chunk
->chars
;
2052 /* ------------- non-static functions (i.e., APIs) ------------- */
2053 /* ------------- called as WMVerbText[Subject] ------------- */
2055 #define DEFAULT_TEXT_WIDTH 250
2056 #define DEFAULT_TEXT_HEIGHT 200
2061 WMCreateText(WMWidget
*parent
)
2063 Text
*tPtr
= wmalloc(sizeof(Text
));
2065 perror("could not create text widget\n");
2068 memset(tPtr
, 0, sizeof(Text
));
2069 tPtr
->widgetClass
= WC_Text
;
2070 tPtr
->view
= W_CreateView(W_VIEW(parent
));
2072 perror("could not create text's view\n");
2076 tPtr
->view
->self
= tPtr
;
2077 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
2078 tPtr
->view
->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2079 W_ResizeView(tPtr
->view
, DEFAULT_TEXT_WIDTH
, DEFAULT_TEXT_HEIGHT
);
2080 tPtr
->bg
= tPtr
->view
->screen
->white
;
2081 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->bg
);
2084 tPtr
->ruler
= WMCreateRuler(tPtr
);
2085 (W_VIEW(tPtr
->ruler
))->attribs
.cursor
= tPtr
->view
->screen
->defaultCursor
;
2086 (W_VIEW(tPtr
->ruler
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2087 WMMoveWidget(tPtr
->ruler
, 0, 0);
2088 WMResizeWidget(tPtr
->ruler
, W_VIEW(parent
)->size
.width
, 40);
2089 WMShowRulerTabs(tPtr
->ruler
, True
);
2090 WMSetRulerAction(tPtr
->ruler
, rulerCallBack
, tPtr
);
2091 WMSetRulerMoveAction(tPtr
->ruler
, rulerMoveCallBack
, tPtr
);
2095 tPtr
->vscroller
= WMCreateScroller(tPtr
);
2096 (W_VIEW(tPtr
->vscroller
))->attribs
.cursor
=
2097 tPtr
->view
->screen
->defaultCursor
;
2098 (W_VIEW(tPtr
->vscroller
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2099 WMMoveWidget(tPtr
->vscroller
, 1, 1);
2100 WMResizeWidget(tPtr
->vscroller
, 20, tPtr
->view
->size
.height
- 2);
2101 WMSetScrollerArrowsPosition(tPtr
->vscroller
, WSAMaxEnd
);
2102 WMSetScrollerAction(tPtr
->vscroller
, scrollersCallBack
, tPtr
);
2106 tPtr
->hscroller
= WMCreateScroller(tPtr
);
2107 (W_VIEW(tPtr
->hscroller
))->attribs
.cursor
=
2108 tPtr
->view
->screen
->defaultCursor
;
2109 (W_VIEW(tPtr
->hscroller
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2110 WMMoveWidget(tPtr
->hscroller
, 1, tPtr
->view
->size
.height
-21);
2111 WMResizeWidget(tPtr
->hscroller
, tPtr
->view
->size
.width
- 2, 20);
2112 WMSetScrollerArrowsPosition(tPtr
->hscroller
, WSAMaxEnd
);
2113 WMSetScrollerAction(tPtr
->hscroller
, scrollersCallBack
, tPtr
);
2115 tPtr
->visibleW
= tPtr
->view
->size
.width
;
2116 tPtr
->visibleH
= tPtr
->view
->size
.height
;
2118 tPtr
->paragraphs
= NULL
;
2120 tPtr
->docHeight
= 0;
2121 tPtr
->dBulletPix
= WMCreatePixmapFromXPMData(tPtr
->view
->screen
,
2123 tPtr
->dUnknownImg
= WMCreatePixmapFromXPMData(tPtr
->view
->screen
,
2126 tPtr
->sRect
.pos
.x
= tPtr
->sRect
.pos
.y
= 0;
2127 tPtr
->sRect
.size
.width
= tPtr
->sRect
.size
.height
= 0;
2128 tPtr
->currentPara
= NULL
;
2129 tPtr
->currentChunk
= NULL
;
2132 tPtr
->parser
= NULL
;
2133 tPtr
->writer
= NULL
;
2134 tPtr
->funcs
.createParagraph
= createParagraph
;
2135 tPtr
->funcs
.insertParagraph
= insertParagraph
;
2136 tPtr
->funcs
.createPChunk
= createPChunk
;
2137 tPtr
->funcs
.createTChunk
= createTChunk
;
2138 tPtr
->funcs
.insertChunk
= insertChunk
;
2140 tPtr
->clicked
.x
= tPtr
->clicked
.y
= -23;
2141 tPtr
->cursor
.x
= tPtr
->cursor
.y
= -23;
2143 tPtr
->relief
= WRSunken
;
2144 tPtr
->wrapping
= wrWord
;
2145 tPtr
->editable
= False
;
2146 tPtr
->cursorShown
= False
;
2147 tPtr
->frozen
= False
;
2148 tPtr
->focused
= False
;
2149 tPtr
->pointerGrabbed
= False
;
2150 tPtr
->buttonHeld
= False
;
2151 tPtr
->ignoreNewLine
= False
;
2152 tPtr
->waitingForSelection
= False
;
2153 tPtr
->findingClickPoint
= False
;
2154 tPtr
->foundClickPoint
= False
;
2155 tPtr
->ownsSelection
= False
;
2159 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2160 tPtr
->dColor
= WMBlackColor(tPtr
->view
->screen
);
2162 tPtr
->view
->delegate
= &_TextViewDelegate
;
2163 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
2164 |EnterWindowMask
|LeaveWindowMask
|FocusChangeMask
,
2165 handleNonTextEvents
, tPtr
);
2166 WMCreateEventHandler(tPtr
->view
, ButtonReleaseMask
|ButtonPressMask
2167 |KeyReleaseMask
|KeyPressMask
|Button1MotionMask
,
2168 handleTextEvents
, tPtr
);
2170 WMAddNotificationObserver(_notification
, tPtr
, "_lostOwnership", tPtr
);
2172 WMSetTextMonoFont(tPtr
, True
);
2173 WMShowTextRuler(tPtr
, False
);
2174 WMSetTextHasHorizontalScroller(tPtr
, False
);
2175 WMSetTextHasVerticalScroller(tPtr
, True
);
2176 //printf("the sizeof chunk is %d\n", sizeof(Chunk));
2177 //printf("the sizeof para is %d\n", sizeof(Paragraph));
2178 //printf("the sizeof text is %d\n", sizeof(Text));
2183 //WRetainPixmap(tPtr->dBulletPix);
2186 WMRemoveTextParagraph(WMText
*tPtr
, int which
)
2188 Paragraph
*prior
, *removed
;
2189 if(!tPtr
|| which
<0) return;
2191 WMSetTextCurrentParagraph(tPtr
, which
);
2192 removed
= tPtr
->currentPara
;
2193 if(!removed
) return;
2194 if(removed
->chunks
)printf("WMRemoveTextChunks\n");
2195 if(removed
== tPtr
->paragraphs
|| which
==0) {
2196 tPtr
->paragraphs
= removed
->next
;
2198 WMSetTextCurrentParagraph(tPtr
, which
-1);
2199 prior
= tPtr
->currentPara
;
2201 prior
->next
= removed
->next
;
2210 /* set what is known as the currentPara in the tPtr. */
2211 /* negative number means: "gib me last chunk" */
2213 WMSetTextCurrentParagraph(WMText
*tPtr
, int current
)
2218 if(!tPtr
|| current
<0) return;
2220 tPtr
->currentPara
= tPtr
->paragraphs
;
2223 tmp
= tPtr
->paragraphs
;
2224 while(tmp
->next
&& ((current
==-23)?1:i
++<current
)) {
2225 //while(tmp && i++<current) {
2227 } tPtr
->currentPara
= tmp
;
2228 //? want to do this?if(tmp) tPtr->currentChunk = tmp
2233 WMGetTextParagraphs(WMText
*tPtr
)
2238 tmp
= tPtr
->paragraphs
;
2248 WMGetTextCurrentParagraph(WMText
*tPtr
)
2253 if(!tPtr
) return current
;
2254 if(!tPtr
->currentPara
) return current
;
2255 if(!tPtr
->paragraphs
) return current
;
2256 tmp
= tPtr
->paragraphs
;
2259 if(tmp
== tPtr
->currentPara
)
2265 /* set what is known as the currentChunk within the currently
2266 selected currentPara (or the first paragraph in the document). */
2268 WMSetTextCurrentChunk(WMText
*tPtr
, int current
)
2274 tPtr
->currentChunk
= NULL
;
2275 if(!tPtr
->currentPara
) {
2276 tPtr
->currentPara
= tPtr
->paragraphs
;
2277 if(!tPtr
->currentPara
)
2282 tPtr
->currentChunk
= tPtr
->currentPara
->chunks
;
2285 tmp
= tPtr
->currentPara
->chunks
;
2287 while(tmp
->next
&& ((current
<0)?1:i
++<current
))
2289 } tPtr
->currentChunk
= tmp
;
2294 WMRemoveTextChunk(WMText
*tPtr
, int which
)
2296 Chunk
*prior
, *removed
;
2298 if(!tPtr
|| which
<0) return;
2299 para
= tPtr
->currentPara
;
2302 WMSetTextCurrentChunk(tPtr
, which
);
2303 removed
= tPtr
->currentChunk
;
2304 if(!removed
) return;
2305 if(removed
== tPtr
->currentPara
->chunks
|| which
==0) {
2306 para
->chunks
= removed
->next
;
2308 WMSetTextCurrentChunk(tPtr
, which
-1);
2309 prior
= tPtr
->currentChunk
;
2311 prior
->next
= removed
->next
;
2313 if(removed
->type
== ctText
) {
2314 wgdbFree(removed
->text
);
2315 WMReleaseFont(removed
->font
);
2316 WMReleaseColor(removed
->color
);
2318 WMReleasePixmap(removed
->pixmap
);
2325 WMGetTextCurrentChunk(WMText
*tPtr
)
2331 if(!tPtr
->currentChunk
) return 0;
2332 if(!tPtr
->currentPara
) {
2333 tPtr
->currentPara
= tPtr
->paragraphs
;
2334 if(!tPtr
->currentPara
)
2338 tmp
= tPtr
->currentPara
->chunks
;
2340 if(tmp
== tPtr
->currentChunk
)
2349 WMGetTextChunks(WMText
*tPtr
)
2353 if(!tPtr
|| !tPtr
->currentPara
) return 0;
2354 tmp
= tPtr
->currentPara
->chunks
;
2362 WMShowTextRuler(WMText
*tPtr
, Bool show
)
2365 if(tPtr
->monoFont
) show
= False
;
2367 tPtr
->rulerShown
= show
;
2368 if(show
) WMMapWidget(tPtr
->ruler
);
2369 else WMUnmapWidget(tPtr
->ruler
);
2370 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2374 WMGetTextRulerShown(WMText
*tPtr
)
2376 if(!tPtr
) return False
;
2377 return tPtr
->rulerShown
;
2381 WMSetTextRulerMargin(WMText
*tPtr
, char which
, short pixels
)
2384 if(tPtr
->monoFont
) return;
2385 WMSetRulerMargin(tPtr
->ruler
, which
, pixels
);
2386 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2390 WMGetTextRulerMargin(WMText
*tPtr
, char which
)
2395 return WMGetRulerMargin(tPtr
->ruler
, which
);
2400 WMShowTextRulerTabs(WMText
*tPtr
, Bool show
)
2403 if(tPtr
->monoFont
) return;
2404 WMShowRulerTabs(tPtr
->ruler
, show
);
2408 WMSetTextMonoFont(WMText
*tPtr
, Bool mono
)
2411 if(mono
&& tPtr
->rulerShown
)
2412 WMShowTextRuler(tPtr
, False
);
2414 tPtr
->monoFont
= mono
;
2418 WMGetTextMonoFont(WMText
*tPtr
)
2420 if(!tPtr
) return True
;
2421 return tPtr
->monoFont
;
2425 WMForceTextFocus(WMText
*tPtr
)
2429 if(tPtr
->clicked
.x
== -23 || tPtr
->clicked
.y
== 23)
2430 cursorToTextPosition(tPtr
, 100, 100); /* anyplace */
2432 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
, tPtr
->clicked
.y
);
2437 WMSetTextEditable(WMText
*tPtr
, Bool editable
)
2440 tPtr
->editable
= editable
;
2445 WMGetTextEditable(WMText
*tPtr
)
2448 return tPtr
->editable
;
2453 WMScrollText(WMText
*tPtr
, int amount
)
2456 if(amount
== 0 || !tPtr
) return;
2457 if(!tPtr
->view
->flags
.realized
) return;
2460 if(tPtr
->vpos
> 0) {
2461 if(tPtr
->vpos
> amount
) tPtr
->vpos
+= amount
;
2465 int limit
= tPtr
->docHeight
- tPtr
->visibleH
;
2466 if(tPtr
->vpos
< limit
) {
2467 if(tPtr
->vpos
< limit
-amount
) tPtr
->vpos
+= amount
;
2468 else tPtr
->vpos
= limit
;
2472 if(scroll
&& tPtr
->vpos
!= tPtr
->prevVpos
) {
2473 updateScrollers(tPtr
);
2474 drawDocumentPartsOnPixmap(tPtr
, False
);
2477 tPtr
->prevVpos
= tPtr
->vpos
;
2482 WMPageText(WMText
*tPtr
, Bool scrollUp
)
2485 if(!tPtr
->view
->flags
.realized
) return;
2487 return WMScrollText(tPtr
, scrollUp
2488 ? tPtr
->visibleH
:-tPtr
->visibleH
);
2492 WMIgnoreTextNewline(WMText
*tPtr
, Bool ignore
)
2495 tPtr
->ignoreNewLine
= ignore
;
2500 WMSetTextHasHorizontalScroller(WMText
*tPtr
, Bool flag
)
2506 rh
= tPtr
->rulerShown
?40:0;
2507 tPtr
->hasHscroller
= flag
;
2509 WMMapWidget(tPtr
->hscroller
);
2510 tPtr
->visibleH
= tPtr
->view
->size
.height
-rh
-22;
2512 WMUnmapWidget(tPtr
->hscroller
);
2513 tPtr
->visibleH
= tPtr
->view
->size
.height
-rh
;
2515 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2521 WMSetTextHasVerticalScroller(WMText
*tPtr
, Bool flag
)
2524 tPtr
->hasVscroller
= flag
;
2526 WMMapWidget(tPtr
->vscroller
);
2527 tPtr
->visibleW
= tPtr
->view
->size
.width
-22;
2528 WMSetRulerOffset(tPtr
->ruler
, 22); /* scrollbar width + 2 */
2530 WMUnmapWidget(tPtr
->vscroller
);
2531 tPtr
->visibleW
= tPtr
->view
->size
.width
;
2532 WMSetRulerOffset(tPtr
->ruler
, 2);
2534 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2541 WMRefreshText(WMText
*tPtr
, int vpos
, int hpos
)
2547 if(tPtr
->frozen
|| !tPtr
->view
->flags
.realized
)
2551 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
2552 22, (tPtr
->rulerShown
)?45:5,
2553 tPtr
->visibleW
, tPtr
->visibleH
, True
);
2555 calcDocExtents(tPtr
);
2557 printf("vpos:%d tPtr->docHeight%d tPtr->visibleH%d \n",
2558 vpos, tPtr->docHeight, tPtr->visibleH);
2561 // tPtr->vpos = vpos;
2563 if(vpos < 0 || tPtr->docHeight < tPtr->visibleH)
2565 else if(vpos-tPtr->visibleH>tPtr->docHeight)
2566 tPtr->vpos = vpos-tPtr->docHeight-tPtr->visibleH-tPtr->docHeight;
2568 tPtr->vpos = tPtr->docHeight-tPtr->visibleH;
2572 if(hpos
< 0 || hpos
> tPtr
->docWidth
)
2577 drawDocumentPartsOnPixmap(tPtr
, True
);
2578 updateScrollers(tPtr
);
2582 /* would be nice to have in WINGs proper... */
2584 changeFontProp(char *fname
, char *newprop
, short which
)
2586 char before
[128], prop
[128], after
[128];
2598 if(part
==which
) bptr
= prop
;
2599 else if(part
==which
+1) bptr
= after
;
2606 snprintf(fname
, 255, "%s-%s%s", before
, newprop
, after
);
2609 /* TODO: put in wfont? */
2611 WMGetFontPlain(WMScreen
*scrPtr
, WMFont
*font
)
2621 WMGetFontBold(WMScreen
*scrPtr
, WMFont
*font
)
2623 WMFont
*newfont
=NULL
;
2625 if(!scrPtr
|| !font
)
2627 snprintf(fname
, 255, font
->name
);
2628 changeFontProp(fname
, "bold", 2);
2629 newfont
= WMCreateNormalFont(scrPtr
, fname
);
2636 WMGetFontItalic(WMScreen
*scrPtr
, WMFont
*font
)
2638 WMFont
*newfont
=NULL
;
2640 if(!scrPtr
|| !font
)
2642 snprintf(fname
, 255, font
->name
);
2643 changeFontProp(fname
, "o", 3);
2644 newfont
= WMCreateNormalFont(scrPtr
, fname
);
2651 WMGetFontOfSize(WMScreen
*scrPtr
, WMFont
*font
, short size
)
2654 if(!scrPtr
|| !font
|| size
<1)
2661 WMFreezeText(WMText
*tPtr
)
2665 tPtr
->frozen
= True
;
2669 WMThawText(WMText
*tPtr
)
2673 tPtr
->frozen
= False
;
2678 WMSetTextDefaultAlignment(WMText
*tPtr
, WMAlignment alignment
)
2681 if(tPtr
->monoFont
) return;
2683 tPtr
->dAlignment
= alignment
;
2684 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2690 WMSetTextBackgroundColor(WMText
*tPtr
, WMColor
*color
)
2698 tPtr
->bg
= WMWhiteColor(tPtr
->view
->screen
);
2700 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->bg
);
2701 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2705 WMSetTextDefaultColor(WMText
*tPtr
, WMColor
*color
)
2711 tPtr
->dColor
= color
;
2713 tPtr
->dColor
= WMBlackColor(tPtr
->view
->screen
);
2717 WMSetTextDefaultFont(WMText
*tPtr
, WMFont
*font
)
2725 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2729 WMSetTextUseFixedPitchFont(Text
*tPtr
, Bool fixed
)
2734 tPtr
->dFont
= WMCreateFontSet(tPtr
->view
->screen
,
2735 "lucidasanstypewriter-12");
2737 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2738 tPtr
->fixedPitch
= fixed
;
2742 WMSetTextParser(WMText
*tPtr
, WMParseAction
*parser
)
2745 if(tPtr
->monoFont
) return;
2746 tPtr
->parser
= parser
;
2751 WMGetTextParserActions(WMText
*tPtr
)
2753 WMParserActions null
;
2754 if(!tPtr
) return null
;
2760 WMGetTextAll(WMText
*tPtr
)
2768 if(!tPtr
) return NULL
;
2770 para
= tPtr
->paragraphs
;
2772 chunk
= para
->chunks
;
2774 if(chunk
->type
== ctText
) {
2775 if(chunk
->text
) length
+= chunk
->chars
;
2777 printf("getting image \n");
2779 chunk
= chunk
->next
;
2782 if(tPtr
->ignoreNewLine
) break;
2783 length
+= 4; // newlines
2787 text
= wmalloc(length
+1);
2789 para
= tPtr
->paragraphs
;
2791 chunk
= para
->chunks
;
2793 if(chunk
->type
== ctText
) {
2795 snprintf(&text
[where
], chunk
->chars
+1, "%s", chunk
->text
);
2796 where
+= chunk
->chars
;
2799 printf("writing image \n");
2801 chunk
= chunk
->next
;
2803 if(tPtr
->ignoreNewLine
) break;
2804 snprintf(&text
[where
++], 2, "\n");
2806 } text
[where
] = '\0';
2812 WMSetTextWriter(WMText
*tPtr
, WMParseAction
*writer
)
2818 tPtr
->writer
= writer
;