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 */
19 #include <X11/keysym.h>
20 #include <X11/Xatom.h>
26 void wgdbFree(void *ptr
)
27 { if(!ptr
) printf("err... cannot ");
28 printf("gdbFree [%p]\n", ptr
);
33 typedef enum {ctText
=0, ctImage
=1} ChunkType
;
34 typedef enum { dtDelete
=0, dtBackSpace
} DeleteType
;
35 typedef enum {wrWord
=0, wrChar
=1, wrNone
=2} Wrapping
;
37 /* Why singly-linked and not say doubly-linked?
38 99% of the time (draw, append), the "prior"
39 member would have been a useless memory and CPU overhead,
40 and deletes _are_ relatively infrequent.
41 When the "prior" member needs to be used, the overhead of
42 doing things the hard way will be incurred... but seldomly. */
45 /* a Chunk is a singly-linked list of chunks containing:
46 o text with a given format
49 typedef struct _Chunk
{
50 char *text
; /* the text in the chunk */
51 WMPixmap
*pixmap
; /* OR the pixmap it holds */
52 short chars
; /* the number of characters in this chunk */
53 short mallocedSize
; /* the number of characters that can be held */
55 WMFont
*font
; /* the chunk's font */
56 WMColor
*color
; /* the chunk's color */
57 short ul
:1; /* underlined or not */
58 ChunkType type
:1; /* a "Text" or "Image" chunk */
59 short script
:4; /* script in points: negative for subscript */
65 struct _Chunk
*next
;/*the next member in this list */
71 /* a Paragraph is a singly-linked list of paragraphs containing:
72 o a list of chunks in that paragraph
73 o the formats for that paragraph
74 o its (draw) position relative to the entire document */
75 typedef struct _Paragraph
{
76 Chunk
*chunks
; /* the list of text and/or image chunks */
77 short fmargin
; /* the start position of the first line */
78 short bmargin
; /* the start positions of the rest of the lines */
79 short rmargin
; /* the end position of the entire paragraph */
80 short numTabs
; /* the number of tabstops */
81 short *tabstops
; /* an array of tabstops */
83 Pixmap drawbuffer
; /* the pixmap onto which the (entire)
84 paragraph will be drawn */
85 WMPixmap
*bulletPix
;/* the pixmap to use for bulleting */
86 int top
; /* the top of the paragraph relative to document */
87 int bottom
; /* the bottom of the paragraph relative to document */
88 int width
; /* the width of the paragraph */
89 int height
; /* the height of the paragraph */
90 WMAlignment align
:2;/* justification of this paragraph */
93 struct _Paragraph
*next
; /* the next member in this list */
97 static char *default_bullet
[] = {
99 " c None s None", ". c black",
100 "X c white", "o c #808080",
108 /* this is really a shrunk down version of the original
109 "broken" icon... I did not draw it, I simply shrunk it */
110 static char * unk_xpm
[] = {
112 " c None", ". c #0B080C", "+ c #13A015", "@ c #5151B8",
113 "# c #992719", "$ c #5B1C20", "% c #1DF51D", "& c #D1500D", "* c #2F304A",
114 "= c #0C6A0C", "- c #F2F1DE", "; c #D59131", "> c #B2B083", ", c #DD731A",
115 "' c #CC3113", ") c #828238", "! c #6A6A94",
116 "......!@@@@@@@....$$....",
117 "...@!@@@@@@@**...$#'....",
118 "..!!@@@@@@@@.......#....",
119 "..!@@@@@@@@@*.......$...",
120 ".!@@@#,,#*@@*..*>.*.#...",
121 "*@@@@#'',,@@@...---!....",
122 "!@@@@@*.#;*@@..!--->....",
123 "@@@@@@@@#,.@@..!----@...",
124 "!@@@@@@*#;'$...!----@...",
125 "*@@@@@@..'&;;#.)----)...",
126 ".@@@@@@..$..&'.>----)...",
127 ".@@@@@@**---,'>-----!...",
128 ".@@@@@@**---,'>-----@...",
129 "..@@@@@@@---;;;,;---....",
130 "..*@@@@*@--->#',;,-*.)..",
131 "........)---->)@;#!..>..",
132 ".....)----------;$..>)..",
133 "=%%%*.*!-------);..)-*..",
134 "=%%%%+...*)>!@*$,.>--...",
135 "*+++++++.......*$@-->...",
136 "............**@)!)>->...",
137 "........................",
138 "........................",
139 "........................"};
141 typedef struct W_Text
{
142 W_Class widgetClass
; /* the class number of this widget */
143 W_View
*view
; /* the view referring to this instance */
144 WMColor
*bg
; /* the background color to use when drawing */
146 WMRuler
*ruler
; /* the ruler subwiget to maipulate paragraphs */
148 WMScroller
*hscroller
; /* the horizontal scroller */
149 short hpos
; /* the current horizontal position */
150 short prevHpos
; /* the previous horizontal position */
152 WMScroller
*vscroller
; /* the vertical scroller */
153 int vpos
; /* the current vertical position */
154 int prevVpos
; /* the previous vertical position */
156 int visibleW
; /* the actual horizontal space available */
157 int visibleH
; /* the actual vertical space available */
159 Paragraph
*paragraphs
; /* the linked list of the paragraphs in the doc. */
160 int docWidth
; /* the width of the entire document */
161 int docHeight
; /* the height of the entire document */
163 WMFont
*dFont
; /* the default font */
164 WMColor
*dColor
; /* the default color */
165 WMPixmap
*dBulletPix
; /* the default pixmap for bullets */
166 WMPixmap
*dUnknownImg
; /* the pixmap for (missing/broken) images */
168 WMRect sRect
; /* the selected area */
169 Paragraph
*currentPara
; /* the current paragraph, in which actions occur */
170 Chunk
*currentChunk
; /* the current chunk, about which actions occur */
171 short tpos
; /* the cursor position (text position) */
172 WMParseAction
*parser
; /* what action to use to parse input text */
173 WMParseAction
*writer
; /* what action to use to write text */
174 WMParserActions funcs
; /* the "things" that parsers/writers might do */
175 XPoint clicked
; /* the position of the last mouse click */
176 XPoint cursor
; /* where the cursor is "placed" */
177 short clheight
; /* the height of the "line" clicked on */
178 short clwidth
; /* the width of the "line" clicked on */
180 WMReliefType relief
:2; /* the relief to display with */
181 Wrapping wrapping
:2; /* the type of wrapping to use in drawing */
182 WMAlignment dAlignment
:2;/* default justification */
183 ushort monoFont
:1; /* whether to ignore "rich" commands */
184 ushort fixedPitch
:1; /* assume each char in dFont is the same size */
185 ushort editable
:1; /* whether to accept user changes or not*/
186 ushort rulerShown
:1; /* whether the ruler is shown or not */
187 ushort cursorShown
:1; /* whether the cursor is currently being shown */
188 ushort frozen
:1; /* whether screen updates are to be made */
189 ushort focused
:1; /* whether this instance has input focus */
190 ushort pointerGrabbed
:1;/* whether this instance has the pointer */
191 ushort buttonHeld
:1; /* the user is still holding down the button */
192 ushort ignoreNewLine
:1; /* whether to ignore the newline character */
193 ushort waitingForSelection
:1; /* whether there is a pending paste event */
194 ushort ownsSelection
:1; /* whether it ownz the current selection */
195 ushort findingClickPoint
:1;/* whether the search for a clickpoint is on */
196 ushort foundClickPoint
:1;/* whether the clickpoint has been found */
197 ushort hasVscroller
:1; /* whether to enable the vertical scroller */
198 ushort hasHscroller
:1; /* whether to enable the horizontal scroller */
204 /* --------- static functions that are "private". don't touch :-) --------- */
207 /* max "characters per chunk that will be drawn at a time" */
208 #define MAX_WORD_LENGTH 100
211 #define MIN_DOC_WIDTH 200
212 typedef struct _LocalMargins
{
213 short left
, right
, first
, body
;
216 typedef struct _MyTextItems
{
217 char text
[MAX_WORD_LENGTH
+1];
221 Chunk
*chunk
;/* used for "click" events */
222 short start
; /* ditto... where in the chunk we start (ie. wrapped chunk) */
229 chunkSelectionRect(Text
*tPtr
, Paragraph
*para
, MyTextItems item
,
230 short y
, short j
, short lh
)
233 short type
=0; /* 0:none 1:partial: 2:all */
234 short rh
=(tPtr
->rulerShown
)?45:5;
236 WMFont
*font
= (tPtr
->monoFont
|| item
.chunk
->type
!= ctText
)?
237 tPtr
->dFont
:item
.chunk
->font
;
240 if(y
+para
->top
+rh
> tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
241 || y
+para
->top
+rh
+lh
< tPtr
->sRect
.pos
.y
)
244 if(item
.chunk
->type
== ctText
)
245 w
= WMWidthOfString(font
, item
.text
, item
.chars
);
246 else w
= item
.chunk
->pixmap
->width
;
248 if(y
+para
->top
+rh
>= tPtr
->sRect
.pos
.y
&& (y
+para
->top
+rh
+lh
249 <= tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
))
250 //&& item.x+j >= tPtr->sRect.pos.x+tPtr->sRect.size.width))
256 if(item
.x
+j
>= tPtr
->sRect
.pos
.x
&&
257 item
.x
+j
+w
< tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
)
260 if(type
== 1 && y
+para
->top
+rh
+lh
<=
261 tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
)
266 if(type
== 1 && item
.chunk
->type
== ctText
) { /* partial coverage */
267 lm
= 2+WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
268 /* even I am still confused, so don't ask please */
269 if( (item
.x
+j
+lm
>= tPtr
->sRect
.pos
.x
&&
270 item
.x
+j
+lm
<= tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
)
271 || (item
.x
+j
+lm
>= tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
272 && y
+para
->top
+rh
+lh
<=
273 tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
)
274 || (tPtr
->sRect
.pos
.y
< y
+para
->top
+rh
275 && tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
>
278 rect
.pos
.x
= item
.x
+j
;
279 item
.chunk
->selected
= True
;
280 if(item
.chunk
->chars
> 6) {
281 item
.chunk
->sStart
= 3;
282 item
.chunk
->sEnd
= item
.chunk
->chars
;
284 item
.chunk
->sStart
= 0;
285 item
.chunk
->sEnd
= item
.chunk
->chars
;
288 } else if(type
== 2) {
289 rect
.pos
.x
= item
.x
+j
;
290 item
.chunk
->selected
= True
;
291 if(item
.chunk
->type
== ctText
) {
292 item
.chunk
->sStart
= 0;
293 item
.chunk
->sStart
= item
.chunk
->chars
;
294 rect
.size
.width
= WMWidthOfString(font
,
295 item
.text
, item
.chars
);
297 rect
.size
.width
= item
.chunk
->pixmap
->width
;
302 rect
.size
.height
= lh
;
307 myDrawText(Text
*tPtr
, Paragraph
*para
, MyTextItems
*items
,
308 short nitems
, short pwidth
, int y
, short draw
, short spacepos
)
310 short i
, ul_thick
, u
, j
=0; /* j = justification */
311 short line_width
= 0, line_height
=0, mx_descent
=0;
312 WMScreen
*screen
= tPtr
->view
->screen
;
316 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return 0;
317 for(i
=0; i
<nitems
; i
++) {
318 if(items
[i
].type
== ctText
) {
319 font
= (tPtr
->monoFont
)?tPtr
->dFont
:items
[i
].chunk
->font
;
320 mx_descent
= WMIN(mx_descent
, -font
->y
);
321 line_height
= WMAX(line_height
, font
->height
);
322 //printf("chunk.x %d xpoint.x %d\n",
323 // items[i].x, tPtr->clicked.x);
325 line_width
+= WMWidthOfString(font
,
326 items
[i
].text
, items
[i
].chars
);
328 mx_descent
= WMIN(mx_descent
, -(items
[i
].pix
->height
-3));
329 /* replace -3 wif descent... */
330 line_height
= WMAX(line_height
, items
[i
].pix
->height
);
331 if(para
->align
== WARight
|| para
->align
== WACenter
) {
332 line_width
+= items
[i
].pix
->width
;
335 if(para
->align
== WARight
) {
336 j
= pwidth
- line_width
;
337 } else if (para
->align
== WACenter
) {
338 j
= (short) ((float)(pwidth
- line_width
))/2.0;
341 if(tPtr
->findingClickPoint
&& (y
+line_height
>= tPtr
->clicked
.y
)) {
342 tPtr
->foundClickPoint
= True
;
343 tPtr
->currentChunk
= items
[0].chunk
; /* just first on this "line" */
344 tPtr
->tpos
= items
[0].start
; /* where to "start" counting from */
345 tPtr
->clicked
.x
= j
+items
[0].x
;
346 tPtr
->clicked
.y
= y
+line_height
+mx_descent
;
347 tPtr
->clheight
= line_height
; /* to draw the cursor */
348 tPtr
->clwidth
= line_width
; /* where to stop searching */
350 } if(!draw
) return line_height
;
352 for(i
=0; i
<nitems
; i
++) {
355 if(tPtr
->ownsSelection
) {
356 WMRect rect
= chunkSelectionRect(tPtr
, para
,
357 items
[i
], y
, j
, line_height
);
358 if(rect
.pos
.x
!= -23) { /* has been selected */
359 XFillRectangle(tPtr
->view
->screen
->display
, para
->drawbuffer
,
360 WMColorGC(WMGrayColor(tPtr
->view
->screen
)),
361 rect
.pos
.x
, rect
.pos
.y
, rect
.size
.width
, rect
.size
.height
);
365 if(items
[i
].type
== ctText
) {
366 gc
= WMColorGC(items
[i
].chunk
->color
);
367 font
= (tPtr
->monoFont
)?tPtr
->dFont
:items
[i
].chunk
->font
;
368 WMDrawString(screen
, para
->drawbuffer
, gc
, font
,
369 items
[i
].x
+j
, y
- font
->y
- mx_descent
,
370 items
[i
].text
, items
[i
].chars
);
371 if(items
[i
].chunk
->ul
&& !tPtr
->monoFont
) {
372 ul_thick
= (short) ((float)font
->height
)/12.0;
373 if (ul_thick
< 1) ul_thick
= 1;
374 for(u
=0; u
<ul_thick
; u
++) {
375 XDrawLine(screen
->display
, para
->drawbuffer
, gc
, items
[i
].x
+j
,
376 y
+ 1 + u
- mx_descent
,
377 items
[i
].x
+ j
+ WMWidthOfString(font
,
378 items
[i
].text
, items
[i
].chars
), y
+ 1 + u
- mx_descent
);
382 WMDrawPixmap(items
[i
].pix
, para
->drawbuffer
, items
[i
].x
+j
,
383 y
+ 3 - mx_descent
- items
[i
].pix
->height
);
389 drawPChunkPart(Text
*tPtr
, Chunk
*chunk
, LocalMargins m
, Paragraph
*para
,
390 MyTextItems
*items
, short *nitems
, short *Lmargin
, XPoint
*where
, short draw
)
396 chunk
->pixmap
= WMRetainPixmap(tPtr
->dUnknownImg
);
398 p_width
= m
.right
- WMIN(m
.first
, m
.body
) - WMGetRulerOffset(tPtr
->ruler
);
399 if(p_width
< MIN_DOC_WIDTH
) // need WMRuler to take care of this...
401 if(where
->x
+ chunk
->pixmap
->width
<= p_width
- *Lmargin
) {
402 /* it can fit on rest of line */
403 items
[*nitems
].pix
= chunk
->pixmap
;
404 items
[*nitems
].type
= ctImage
;
405 items
[*nitems
].chars
= 0;
406 items
[*nitems
].x
= *Lmargin
+where
->x
;
407 items
[*nitems
].chunk
= chunk
;
408 items
[*nitems
].start
= 0;
410 if(*nitems
>= MAX_CHUNX
) {
411 items
[*nitems
].chars
= 0;
412 items
[*nitems
].x
= *Lmargin
+where
->x
;
413 items
[*nitems
].chunk
= chunk
;
414 items
[*nitems
].start
= 0;
415 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
416 p_width
-*Lmargin
, where
->y
, draw
, 0);
417 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
422 where
->x
+= chunk
->pixmap
->width
;
424 } else if(chunk
->pixmap
->width
<= p_width
- *Lmargin
) {
425 /* it can fit on an entire line, flush the myDrawText then wrap it */
426 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
427 p_width
-*Lmargin
, where
->y
, draw
, 0);
429 *Lmargin
= WMAX(0, m
.body
- m
.first
);
431 drawPChunkPart(tPtr
, chunk
, m
, para
, items
, nitems
,
432 Lmargin
, where
, draw
);
437 *Lmargin
= WMAX(0, m
.body
- m
.first
);
438 items
[*nitems
].pix
= chunk
->pixmap
;
439 items
[*nitems
].type
= ctImage
;
440 items
[*nitems
].chars
= 0;
441 items
[*nitems
].x
= *Lmargin
+where
->x
;
442 items
[*nitems
].chunk
= chunk
;
443 items
[*nitems
].start
= 0;
444 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
445 p_width
-*Lmargin
, where
->y
, draw
, 0);
448 /* scale image to fit, call self again */
449 /* deprecated - the management */
455 drawTChunkPart(Text
*tPtr
, Chunk
*chunk
, char *bufr
, LocalMargins m
,
456 Paragraph
*para
, MyTextItems
*items
, short *nitems
, short len
, short start
,
457 short *Lmargin
, XPoint
*where
, short draw
, short spacepos
)
459 short t_chunk_width
, p_width
, chars
;
460 WMFont
*font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
461 /* if(doc->clickstart.yes && doc->clickstart.done) return; */
464 p_width
= m
.right
- WMIN(m
.first
, m
.body
);
465 if(p_width
< MIN_DOC_WIDTH
) // need WMRuler to take care of this...
469 t_chunk_width
= WMWidthOfString(font
, bufr
, len
);
470 if((where
->x
+ t_chunk_width
<= p_width
- *Lmargin
)
471 || (tPtr
->wrapping
== wrNone
)) {
472 /* if it can fit on rest of line, append to line */
473 chars
= WMIN(len
, MAX_WORD_LENGTH
);
474 snprintf(items
[*nitems
].text
, chars
+1, "%s", bufr
);
475 items
[*nitems
].chars
= chars
;
476 items
[*nitems
].x
= *Lmargin
+where
->x
;
477 items
[*nitems
].type
= ctText
;
478 items
[*nitems
].chunk
= chunk
;
479 items
[*nitems
].start
= start
;
481 if(*nitems
>= MAX_CHUNX
) {
482 chars
= WMIN(len
, MAX_WORD_LENGTH
);
483 snprintf(items
[*nitems
].text
, chars
+1, "%s", bufr
);
484 items
[*nitems
].chars
= chars
;
485 items
[*nitems
].x
= *Lmargin
+where
->x
;
486 items
[*nitems
].type
= ctText
;
487 items
[*nitems
].chunk
= chunk
;
488 items
[*nitems
].start
= start
;
489 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
490 p_width
-*Lmargin
, where
->y
, draw
, spacepos
);
491 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
496 where
->x
+= t_chunk_width
;
498 } else if(t_chunk_width
<= p_width
- *Lmargin
) {
499 /* it can fit on an entire line, flush and wrap it to a new line */
500 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
,
501 p_width
-*Lmargin
, where
->y
, draw
, spacepos
);
502 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
504 *Lmargin
= WMAX(0, m
.body
- m
.first
);
506 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
, items
, nitems
,
507 len
, start
, Lmargin
, where
, draw
, spacepos
);
509 /* otherwise, chop line, call ourself recursively until it's all gone */
510 short J
=0; /* bufr */
511 short j
=0; /* local tmp buffer */
513 short diff
= p_width
- *Lmargin
- where
->x
;
517 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
,
518 p_width
-*Lmargin
, where
->y
, draw
, spacepos
);
519 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
521 *Lmargin
= WMAX(0, m
.body
- m
.first
);
523 diff
= p_width
- *Lmargin
- where
->x
;
526 for(J
=0; J
<len
; J
++) {
528 if(WMWidthOfString(font
, tmp
, j
+1) > diff
) {
529 drawTChunkPart(tPtr
, chunk
, tmp
, m
, para
, items
, nitems
,
530 j
, start
+_start
, Lmargin
, where
, draw
, spacepos
);
535 /* and there's always that last chunk, get it too */
536 drawTChunkPart(tPtr
, chunk
, tmp
, m
, para
, items
, nitems
,
537 j
, start
+_start
, Lmargin
, where
, draw
, spacepos
);
541 /* this function does what it's called :-)
542 o It is also used for calculating extents of para,
543 (returns height) so watch out for (Bool) draw
544 o Also used to determine where mouse was clicked */
546 putParagraphOnPixmap(Text
*tPtr
, Paragraph
*para
, Bool draw
)
548 char bufr
[MAX_WORD_LENGTH
+1]; /* a single word + '\0' */
549 MyTextItems items
[MAX_CHUNX
+1];
550 short lmargin
, spacepos
, i
, s
, nitems
, start
;
555 if(!tPtr
->view
->flags
.realized
|| !para
) return 0;
557 where
.x
= 0, where
.y
=0, nitems
= 0;
558 m
.left
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
559 m
.right
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) - m
.left
;
560 m
.first
= para
->fmargin
, m
.body
= para
->bmargin
;
563 W_Screen
*screen
= tPtr
->view
->screen
;
565 XFreePixmap(screen
->display
, para
->drawbuffer
);
566 if(para
->width
<2*tPtr
->dFont
->height
) para
->width
= 2*tPtr
->dFont
->height
;
567 if(para
->height
<tPtr
->dFont
->height
) para
->height
= tPtr
->dFont
->height
;
568 para
->drawbuffer
= XCreatePixmap(screen
->display
,
569 tPtr
->view
->window
, para
->width
, para
->height
, screen
->depth
);
570 XFillRectangle(screen
->display
, para
->drawbuffer
,
571 WMColorGC(tPtr
->bg
), 0, 0, para
->width
, para
->height
);
575 //if(para->align != tPtr->dAlignment)
576 // para->align = tPtr->dAlignment;
578 /* draw the bullet if appropriate */
579 if(m
.body
>m
.first
&& !tPtr
->monoFont
) {
580 lmargin
= m
.body
- m
.first
;
583 WMDrawPixmap(para
->bulletPix
, para
->drawbuffer
, lmargin
-10, 5);
585 WMDrawPixmap(tPtr
->dBulletPix
, para
->drawbuffer
, lmargin
-10, 5);
587 /* NeXT sez next tab, I say the m.body - m.first margin */
589 lmargin
= WMAX(0, m
.first
- m
.body
);
592 if(tPtr
->findingClickPoint
&& !para
->chunks
) {
593 tPtr
->currentChunk
= NULL
;
594 tPtr
->foundClickPoint
= True
;
596 tPtr
->clicked
.x
= lmargin
;
598 tPtr
->clheight
= para
->height
;
603 chunk
= para
->chunks
;
606 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return 0;
608 if(chunk
->type
== ctImage
&& !tPtr
->monoFont
) {
609 drawPChunkPart(tPtr
, chunk
, m
, para
, items
, &nitems
,
610 &lmargin
, &where
, draw
);
611 } else if(chunk
->text
&& chunk
->type
== ctText
) {
612 if(tPtr
->wrapping
== wrNone
) {
613 drawTChunkPart(tPtr
, chunk
, chunk
->text
, m
, para
, items
, &nitems
,
614 chunk
->chars
, 0, &lmargin
, &where
, draw
, spacepos
);
615 } else if(tPtr
->wrapping
== wrWord
) {
616 spacepos
=0, i
=0, start
=0;
617 while(spacepos
< chunk
->chars
) {
618 bufr
[i
] = chunk
->text
[spacepos
];
619 if( bufr
[i
] == ' ' || i
>= MAX_WORD_LENGTH
) {
620 if(bufr
[i
] == ' ') s
=1; else s
=0;
621 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
,
622 items
, &nitems
, i
+s
, start
, &lmargin
, &where
,
625 if(i
> MAX_WORD_LENGTH
-1)
631 /* catch that last onery one. */
632 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
,
633 items
, &nitems
, i
, start
, &lmargin
, &where
, draw
, spacepos
);
634 } } chunk
= chunk
->next
;
636 /* we might have a few leftover items that need drawing */
638 where
.y
+= myDrawText(tPtr
, para
, items
,
639 nitems
, m
.right
-m
.left
-lmargin
, where
.y
, draw
, spacepos
);
640 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return 0;
646 calcParaExtents(Text
*tPtr
, Paragraph
*para
)
651 para
->width
= tPtr
->visibleW
;
654 para
->rmargin
= tPtr
->visibleW
;
656 para
->width
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) -
657 WMIN(para
->fmargin
, para
->bmargin
)
658 - WMGetRulerOffset(tPtr
->ruler
);
662 para
->height
= tPtr
->dFont
->height
;
664 para
->height
= putParagraphOnPixmap(tPtr
, para
, False
);
666 if(para
->height
<tPtr
->dFont
->height
)
667 para
->height
= tPtr
->dFont
->height
;
668 para
->bottom
= para
->top
+ para
->height
;
673 /* rather than bother with redrawing _all_ the pixmaps, simply
674 rearrange (i.e., push down or pull up) paragraphs after this one */
676 affectNextParas(Text
*tPtr
, Paragraph
*para
, int move_y
)
681 if(!para
|| move_y
==0) return;
683 old_y
= para
->bottom
;
684 calcParaExtents(tPtr
, para
);
685 old_y
-= para
->bottom
;
686 if(old_y
== 0) return;
688 }if(move_y
== 0) return;
693 next
->bottom
= next
->top
+ next
->height
;
694 next
= next
->next
; // I know, I know
695 }tPtr
->docHeight
+= move_y
;
698 tPtr
->vpos
+= move_y
;
699 if(tPtr
->vpos
< 0) tPtr
->vpos
= 0;
700 if(tPtr
->vpos
> tPtr
->docHeight
- tPtr
->visibleH
)
701 tPtr
->vpos
= tPtr
->docHeight
- tPtr
->visibleH
;
708 calcDocExtents(Text
*tPtr
)
713 tPtr
->docWidth
= tPtr
->visibleW
;
715 tPtr
->docWidth
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) -
716 WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
719 para
= tPtr
->paragraphs
;
722 para
->top
= tPtr
->docHeight
;
723 tPtr
->docHeight
+= calcParaExtents(tPtr
, para
);
724 para
->bottom
= tPtr
->docHeight
;
727 } else { /* default to this if no paragraphs */
728 tPtr
->docHeight
= tPtr
->dFont
->height
;
731 if(tPtr
->editable
) /* add space at bottom to enter new stuff */
732 tPtr
->docHeight
+= tPtr
->dFont
->height
;
737 /* If any part of a paragraph is viewable, the entire
738 paragraph is drawn on an otherwise empty (XFreePixmap) pixmap.
739 The actual viewable parts of the paragraph(s) are then pieced
740 together via paintText:
742 -------------------------------------------
743 || this is a paragraph in this document||
744 ||========================================||
745 || | only part of it is visible though. ||
746 || |-------------------------------------||
747 ||[.| This is another paragraph ||
748 || | which I'll make relatively long ||
749 || | just for the sake of writing a long ||
750 || | paragraph with a picture: ^_^ ||
751 || |-------------------------------------||
752 ||==| Of the three paragraphs, only ||
753 ||/\| the preceding was totally copied to ||
754 ||\/| totally copied to the window, even ||
755 ==========================================||
756 though they are all on pixmaps.
757 -------------------------------------------
758 This paragraph exists only in
759 memory and so has a NULL pixmap.
760 -------------------------------------------
763 simple, right? Performance: the best of both worlds...
764 o fast scrolling: no need to rewrite what's already
765 on the screen, simply XCopy it.
766 o fast typing: only change current para, then simply
767 affect other (i.e., subsequent) paragraphs.
768 o If no part of para is on screen, gdbFree pixmap; else draw on
769 individual pixmap per para then piece several paras together
770 o Keep track of who to XCopy to window (see paintText) */
772 drawDocumentPartsOnPixmap(Text
*tPtr
, Bool all
)
776 para
= tPtr
->paragraphs
;
778 /* the 32 reduces jitter on the human eye by preparing paragraphs
779 in anticipation of when the _moving_ scrollbar reaches them */
780 if(para
->bottom
+ 32 < tPtr
->vpos
||
781 para
->top
> tPtr
->visibleH
+ tPtr
->vpos
+ 32 ) {
782 if(para
->drawbuffer
) {
783 XFreePixmap(tPtr
->view
->screen
->display
, para
->drawbuffer
);
784 para
->drawbuffer
= (Pixmap
) NULL
;
787 if(!para
->drawbuffer
|| all
)
788 putParagraphOnPixmap(tPtr
, para
, True
);
796 /* this function blindly copies the "visible" parts of a pragraph
797 unto the view, (top-down approach). It starts drawing from
798 the top of the view (which paragraph to draw is determined by
799 drawDocumentPartsOnPixmap); it stops at the bottom of the view. */
801 paintText(Text
*tPtr
)
803 short lmargin
, para_lmargin
;
804 int from
=5, to
=5, height
;
806 short vS
=0, hS
=0, rh
=0;
808 if(!tPtr
->view
->flags
.realized
) return;
810 if(tPtr
->rulerShown
) rh
= 40;
813 if(tPtr
->hasVscroller
) vS
= 21;
814 if(tPtr
->hasHscroller
) hS
= 21;
816 //XClearWindow(tPtr->view->screen->display, tPtr->view->window);
818 lmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
819 if(tPtr
->paragraphs
) {
820 para
= tPtr
->paragraphs
;
822 if(para
->drawbuffer
) {
823 from
= (para
->top
<=tPtr
->vpos
)?tPtr
->vpos
-para
->top
:0;
824 height
= para
->height
- from
;
825 if(from
>=0 && height
>0 ) {
826 para_lmargin
= WMIN(para
->fmargin
, para
->bmargin
);
827 if(lmargin
-vS
<WMIN(para
->fmargin
, para
->bmargin
)) {
829 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
830 lmargin
, to
, 2+para_lmargin
, height
, False
);
832 XFillRectangle(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
833 WMColorGC(tPtr
->dColor
), lmargin
, to
, 2+para_lmargin
, height
);
836 XCopyArea(tPtr
->view
->screen
->display
, para
->drawbuffer
,
837 tPtr
->view
->window
, WMColorGC(tPtr
->bg
), 0, from
,
838 para
->width
-4, height
, lmargin
+para_lmargin
+2, to
);
839 if( (to
+=height
) > tPtr
->visibleH
+rh
)
847 /* clear any left over space (esp. during para deletes/ ruler changes) */
848 if(tPtr
->docHeight
< tPtr
->visibleH
&& tPtr
->visibleH
+rh
+5-to
>0) {
849 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
, vS
, to
,
850 tPtr
->view
->size
.width
-vS
, tPtr
->visibleH
+rh
+hS
+5-to
, False
);
854 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
855 vS
+1, rh
+5, lmargin
-vS
, tPtr
->visibleH
+rh
+5-vS
, False
);
858 // from the "selection" days...
859 W_DrawRelief(tPtr
->view
->screen
, WMWidgetXID(tPtr
),
860 tPtr
->sRect
.pos
.x
, tPtr
->sRect
.pos
.y
,
861 tPtr
->sRect
.size
.width
, tPtr
->sRect
.size
.height
, tPtr
->relief
);
864 W_DrawRelief(tPtr
->view
->screen
, WMWidgetXID(tPtr
), 0, rh
,
865 tPtr
->visibleW
+vS
, tPtr
->visibleH
+hS
, tPtr
->relief
);
867 if(tPtr
->editable
&& tPtr
->clheight
> 0) {
868 int top
= tPtr
->cursor
.y
-tPtr
->vpos
;
869 int bot
= top
+tPtr
->clheight
;
872 if(bot
>tPtr
->visibleH
+hS
-2) bot
= tPtr
->visibleH
+hS
-2;
874 //do something about italic text...
875 XDrawLine(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
876 WMColorGC(tPtr
->dColor
), lmargin
+tPtr
->cursor
.x
, top
,
877 lmargin
+tPtr
->cursor
.x
, bot
);
884 /* called anytime either the ruler, vscroller or hscroller is hidden/shown,
885 or when the widget is resized by some user action */
887 resizeText(W_ViewDelegate
*self
, WMView
*view
)
889 Text
*tPtr
= (Text
*)view
->self
;
892 if(!tPtr
->monoFont
&& tPtr
->rulerShown
)
895 W_ResizeView(view
, view
->size
.width
, view
->size
.height
);
896 WMResizeWidget(tPtr
->ruler
, view
->size
.width
, 40);
899 if(tPtr
->hasVscroller
) {
900 WMMoveWidget(tPtr
->vscroller
, 1, 1+rh
);
901 WMResizeWidget(tPtr
->vscroller
, 20, view
->size
.height
-rh
-2);
902 tPtr
->visibleW
= view
->size
.width
-21;
904 if(tPtr
->hasHscroller
) {
905 WMMoveWidget(tPtr
->hscroller
, 20, view
->size
.height
-21);
906 WMResizeWidget(tPtr
->hscroller
, view
->size
.width
-21, 20);
907 tPtr
->visibleH
= view
->size
.height
-21-rh
;
908 } else tPtr
->visibleH
= view
->size
.height
-rh
;
910 tPtr
->visibleW
= view
->size
.width
;
911 if(tPtr
->hasHscroller
) {
912 WMMoveWidget(tPtr
->hscroller
, 1, view
->size
.height
-21);
913 WMResizeWidget(tPtr
->hscroller
, view
->size
.width
-2, 20);
914 tPtr
->visibleH
= view
->size
.height
-21-rh
;
915 } else tPtr
->visibleH
= view
->size
.width
-2-rh
;
917 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
920 W_ViewDelegate _TextViewDelegate
=
930 /* a plain text parser */
931 /* this gives useful hints on how to make a more
932 interesting parser for say HTML, RTF */
934 defaultParser(Text
*tPtr
, void *data
, short type
)
936 char *start
, *mark
, *text
= (char *) data
;
938 Paragraph
*para
= NULL
;
942 mark
= strchr(start
, '\n');
944 /* there is a newline, indicating the need for a new paragraph */
945 /* attach the chunk to the current paragraph */
946 if((short)(mark
-start
) > 1) {
947 /* ignore chunks with just a single newline but still make a
949 chunk
= (tPtr
->funcs
.createTChunk
) (start
, (short)(mark
-start
),
950 tPtr
->dFont
, tPtr
->dColor
, 0, False
);
951 (tPtr
->funcs
.insertChunk
) (tPtr
, chunk
, type
);
953 /* _then_ create a new paragraph for the _next_ chunk */
954 para
= (tPtr
->funcs
.createParagraph
) (0, 0, tPtr
->visibleW
,
956 (tPtr
->funcs
.insertParagraph
) (tPtr
, para
, type
);
959 /* just attach the chunk to the current paragraph */
960 if(strlen(start
) > 0) {
961 chunk
= (tPtr
->funcs
.createTChunk
) (start
, strlen(start
),
962 tPtr
->dFont
, tPtr
->dColor
, 0, False
);
963 (tPtr
->funcs
.insertChunk
) (tPtr
, chunk
, type
);
970 updateScrollers(Text
*tPtr
)
972 if(tPtr
->hasVscroller
) {
973 if(tPtr
->docHeight
< tPtr
->visibleH
) {
974 WMSetScrollerParameters(tPtr
->vscroller
, 0, 1);
977 float vmax
= (float)(tPtr
->docHeight
);
978 WMSetScrollerParameters(tPtr
->vscroller
,
979 ((float)tPtr
->vpos
)/(vmax
- (float)tPtr
->visibleH
),
980 (float)tPtr
->visibleH
/vmax
);
984 if(tPtr
->hasHscroller
)
989 scrollersCallBack(WMWidget
*w
, void *self
)
991 Text
*tPtr
= (Text
*)self
;
995 if(!tPtr
->view
->flags
.realized
) return;
997 if(w
== tPtr
->vscroller
) {
1000 vmax
= (float)(tPtr
->docHeight
);
1001 height
= tPtr
->visibleH
;
1003 height
-= 7; /* the top border (5) + bottom (2) */
1005 switch(WMGetScrollerHitPart(tPtr
->vscroller
)) {
1006 case WSDecrementLine
:
1007 if(tPtr
->vpos
> 0) {
1008 if(tPtr
->vpos
>16) tPtr
->vpos
-=16;
1012 case WSIncrementLine
: {
1013 int limit
= tPtr
->docHeight
- height
;
1014 if(tPtr
->vpos
< limit
) {
1015 if(tPtr
->vpos
<limit
-16) tPtr
->vpos
+=16;
1016 else tPtr
->vpos
=limit
;
1019 case WSDecrementPage
:
1020 tPtr
->vpos
-= height
;
1026 printf("dimple needs to jump to mouse location ;-/\n");
1028 case WSIncrementPage
:
1029 tPtr
->vpos
+= height
;
1030 if(tPtr
->vpos
> (tPtr
->docHeight
- height
))
1031 tPtr
->vpos
= tPtr
->docHeight
- height
;
1034 printf("dimple needs to jump to mouse location ;-/\n");
1039 tPtr
->vpos
= WMGetScrollerValue(tPtr
->vscroller
)
1040 * (float)(tPtr
->docHeight
- height
);
1047 float vmax
= (float)(tPtr
->docHeight
);
1048 ((float)tPtr
->vpos
)/(vmax
- (float)tPtr
->visibleH
),
1049 (float)tPtr
->visibleH
/vmax
);
1050 dimple
=where mouse is
.
1054 scroll
= (tPtr
->vpos
!= tPtr
->prevVpos
);
1055 tPtr
->prevVpos
= tPtr
->vpos
;
1058 if(w
== tPtr
->hscroller
)
1061 //need scrollv || scrollh
1065 if(tPtr->rulerShown)
1066 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
1067 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
1069 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
1070 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
1073 updateScrollers(tPtr
);
1074 drawDocumentPartsOnPixmap(tPtr
, False
);
1081 W_InsertText(WMText
*tPtr
, void *data
, int position
)
1085 Paragraph
*para
= tPtr
->paragraphs
, *ptmp
;
1086 Chunk
*chunk
, *ctmp
;
1089 chunk
= para
->chunks
;
1091 if(chunk
->type
== ctText
&& chunk
->text
)
1092 wgdbFree(chunk
->text
);
1093 else if(chunk
->pixmap
)
1094 WMReleasePixmap(chunk
->pixmap
);
1096 chunk
= chunk
->next
;
1101 if(ptmp
->drawbuffer
)
1102 XFreePixmap(tPtr
->view
->screen
->display
, ptmp
->drawbuffer
);
1105 tPtr
->paragraphs
= NULL
;
1106 tPtr
->currentPara
= NULL
;
1107 tPtr
->currentChunk
= NULL
;
1109 WMRefreshText(tPtr
, 0, 0);
1114 (tPtr
->parser
)(tPtr
, data
, position
>= 0 ? 1 : 0);
1116 defaultParser(tPtr
, data
, position
>= 0 ? 1 : 0);
1120 cursorToTextPosition(Text
*tPtr
, int x
, int y
)
1122 Paragraph
*para
= NULL
;
1123 Chunk
*chunk
= NULL
;
1126 short orig_x
, orig_y
;
1128 if(x
<(tPtr
->hasVscroller
?21:1)) {
1129 y
-= tPtr
->clheight
;
1130 x
= tPtr
->view
->size
.width
; //tPtr->visibleW;
1131 } else if(x
>tPtr
->clwidth
&& x
<tPtr
->clicked
.x
) {
1132 //x = (tPtr->hasVscroller)?21:1;
1133 //y += tPtr->clheight;
1139 if(y
<0 || y
>tPtr
->view
->size
.height
-3) return;
1141 tPtr
->clicked
.x
= orig_x
;
1142 tPtr
->clicked
.y
= y
;
1143 tPtr
->clicked
.y
+= tPtr
->vpos
;
1144 tPtr
->clicked
.y
-= tPtr
->rulerShown
?40:0;
1145 para
= tPtr
->paragraphs
;
1148 if( tPtr
->clicked
.y
>= para
->top
-4 &&
1149 tPtr
->clicked
.y
< para
->bottom
+4) break;
1151 } if(!(tPtr
->currentPara
= para
)) return;
1153 tPtr
->clicked
.y
-= para
->top
;
1154 if(tPtr
->clicked
.y
<0) tPtr
->clicked
.y
=0;
1155 if(tPtr
->hasVscroller
) x
-= 21;
1158 tPtr
->findingClickPoint
= True
;
1159 tPtr
->foundClickPoint
= False
;
1160 /* also affects tPtr->currentChunk, tPtr->clicked.x and y,
1161 tPtr->clheight and ->width */
1162 putParagraphOnPixmap(tPtr
, para
, False
);
1163 tPtr
->findingClickPoint
= False
;
1164 tPtr
->clicked
.y
+= para
->top
;
1166 if(tPtr
->currentChunk
) {
1167 short _width
=0, start
=tPtr
->tpos
, done
=False
, w
=0;
1168 chunk
= tPtr
->currentChunk
;
1169 while(!done
&& chunk
&& line_width
<tPtr
->clwidth
) {
1170 if(chunk
->type
== ctText
) {
1171 font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
1172 for (w
=start
; w
<chunk
->chars
; w
++) {
1173 _width
= WMWidthOfString(font
, &chunk
->text
[w
], 1);
1174 line_width
+= _width
;
1175 if(line_width
+tPtr
->clicked
.x
>= x
) {
1176 line_width
-= _width
;
1182 if(0&&chunk
->next
) {
1183 if(chunk
->next
->type
== ctImage
) {
1184 if(x
+10 < line_width
+chunk
->next
->pixmap
->width
) {
1189 _width
= chunk
->pixmap
->width
;
1190 line_width
+= _width
;
1191 if(line_width
+tPtr
->clicked
.x
>= x
) {
1192 line_width
-= _width
;
1198 chunk
= chunk
->next
;
1202 tPtr
->currentChunk
= chunk
;
1205 short vS
= (tPtr
->hasVscroller
)?32:12;
1206 if(para
->align
== WARight
) {
1207 tPtr
->clicked
.x
= tPtr
->view
->size
.width
-vS
;
1208 } else if (para
->align
== WACenter
) {
1209 tPtr
->clicked
.x
= -(vS
/2)+(tPtr
->view
->size
.width
-vS
)/2;
1211 tPtr
->clicked
.x
= 2;
1214 tPtr
->cursor
.x
= tPtr
->clicked
.x
+2+line_width
;
1215 tPtr
->cursor
.y
= tPtr
->clicked
.y
;
1216 tPtr
->clicked
.y
= orig_y
;
1217 tPtr
->clicked
.x
= orig_x
;
1218 putParagraphOnPixmap(tPtr
, para
, True
);
1223 deleteTextInteractively(Text
*tPtr
, DeleteType type
)
1227 short pos
,w
=0,h
=0, doprev
=False
, doprevpara
=False
;
1229 int current
= WMGetTextCurrentChunk(tPtr
);
1231 if(!(para
= tPtr
->currentPara
)) return;
1232 if(!(chunk
= tPtr
->currentChunk
)) return;
1233 font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
1234 doprev
= (tPtr
->tpos
< 2);
1237 case dtDelete
: /* delete _after_ cursor ... implement later */
1238 case dtBackSpace
: /* delete _before_ cursor */
1239 if(chunk
->chars
> 1) {
1241 printf("here %d\n", pos
);
1243 w
= WMWidthOfString(font
, &chunk
->text
[pos
], 1);
1244 memmove(&(chunk
->text
[pos
]),
1245 &(chunk
->text
[pos
+1]), chunk
->chars
-pos
+1);
1249 WMRemoveTextChunk(tPtr
, current
);
1255 WMSetTextCurrentChunk(tPtr
, current
-1);
1256 if(!tPtr
->currentChunk
) {
1257 printf("PREV PARA\n");
1259 tPtr
->tpos
= tPtr
->currentChunk
->chars
;
1262 int currentp
= WMGetTextCurrentParagraph(tPtr
);
1265 para
->chunks
= NULL
;
1266 WMRemoveTextParagraph(tPtr
, currentp
);
1267 WMSetTextCurrentParagraph(tPtr
, currentp
-1);
1268 WMSetTextCurrentChunk(tPtr
, -1);
1269 para
= tPtr
->currentPara
;
1271 if(!tPtr
->currentChunk
|| !para
->chunks
) {
1272 para
->chunks
= chunk
;
1273 tPtr
->currentChunk
= chunk
;
1275 tPtr
->currentChunk
->next
= chunk
;
1278 if(1) { //if(1||(para && !doprevpara)) {
1279 affectNextParas(tPtr
, para
, -23);
1280 putParagraphOnPixmap(tPtr
, para
, True
);
1281 drawDocumentPartsOnPixmap(tPtr
, False
);
1282 updateScrollers(tPtr
);
1284 //cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1285 } else WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1289 /* give us nice chunk sizes (multiples of 16) */
1291 reqBlockSize(short requested
)
1293 return requested
+16-(requested
%16);
1297 insertTextInteractively(Text
*tPtr
, char *text
)
1299 Paragraph
*para
=NULL
;
1300 Chunk
*chunk
=NULL
, *newchunk
=NULL
;
1301 int height
= -23; /* should only be changed upon newline */
1305 if(!tPtr
->editable
) return;
1306 if(*text
== '\n' && tPtr
->ignoreNewLine
)
1309 para
= tPtr
->currentPara
;
1310 chunk
= tPtr
->currentChunk
;
1311 font
= (tPtr
->monoFont
|| !chunk
)?tPtr
->dFont
:chunk
->font
;
1315 if(chunk
) { /* there's a chunk (or part of it) to detach from old */
1316 int current
= WMGetTextCurrentChunk(tPtr
);
1317 if(tPtr
->tpos
<=0) { /* at start of chunk */
1318 if(current
<1) { /* the first chunk... make old para blank */
1319 newchunk
= para
->chunks
;
1320 para
->chunks
= NULL
;
1321 putParagraphOnPixmap(tPtr
, para
, True
);
1322 } else { /* not first chunk... */
1323 printf("cut me out \n");
1325 } else if(tPtr
->tpos
< chunk
->chars
&& chunk
->type
== ctText
) {
1326 /* not at start of chunk */
1327 char text
[chunk
->chars
-tPtr
->tpos
+1];
1330 text
[i
] = chunk
->text
[tPtr
->tpos
+i
];
1331 } while(++i
< chunk
->chars
-tPtr
->tpos
);
1333 newchunk
= (tPtr
->funcs
.createTChunk
) (text
, i
, chunk
->font
,
1334 chunk
->color
, chunk
->script
, chunk
->ul
);
1335 newchunk
->next
= chunk
->next
;
1337 /* might want to demalloc for LARGE cuts */
1338 //calcParaExtents(tPtr, para);
1339 para
->height
= putParagraphOnPixmap(tPtr
, para
, True
);
1340 //putParagraphOnPixmap(tPtr, para, True);
1341 } else if(tPtr
->tpos
>= chunk
->chars
) {
1343 WMSetTextCurrentChunk(tPtr
, current
-1);
1344 prev
= tPtr
->currentChunk
;
1346 newchunk
= prev
->next
;
1348 putParagraphOnPixmap(tPtr
, para
, True
);
1350 } else newchunk
= NULL
;
1352 if(para
) /* the preceeding one */
1353 new_top
= para
->bottom
;
1355 WMAppendTextStream(tPtr
, "\n");
1356 para
= tPtr
->currentPara
;
1358 para
->chunks
= newchunk
;
1359 tPtr
->currentChunk
= newchunk
;
1361 para
->top
= new_top
;
1362 calcParaExtents(tPtr
, para
);
1363 height
= para
->height
;
1366 WMAppendTextStream(tPtr
, text
);
1367 para
= tPtr
->currentPara
;
1368 } else if(!para
->chunks
|| !chunk
) {
1369 //WMPrependTextStream(tPtr, text);
1370 WMAppendTextStream(tPtr
, text
);
1371 } else if(chunk
->type
== ctImage
) {
1372 WMPrependTextStream(tPtr
, text
);
1374 printf("\n\nprepe\n\n");
1376 if(tPtr
->tpos
> chunk
->chars
) {
1377 printf("\n\nmore\n\n");
1378 tPtr
->tpos
= chunk
->chars
;
1381 if(chunk
->chars
+1 >= chunk
->mallocedSize
) {
1382 chunk
->mallocedSize
= reqBlockSize(chunk
->chars
+1);
1383 chunk
->text
= wrealloc(chunk
->text
, chunk
->mallocedSize
);
1386 memmove(&(chunk
->text
[tPtr
->tpos
+1]), &chunk
->text
[tPtr
->tpos
],
1387 chunk
->chars
-tPtr
->tpos
+1);
1388 w
= WMWidthOfString(font
, text
, 1);
1389 memmove(&chunk
->text
[tPtr
->tpos
], text
, 1);
1392 //doc->clickstart.cursor.x +=
1393 //WMWidthOfString(chunk->fmt->font, text,len);
1399 affectNextParas(tPtr
, para
, height
);
1400 putParagraphOnPixmap(tPtr
, para
, True
);
1401 drawDocumentPartsOnPixmap(tPtr
, False
);
1402 updateScrollers(tPtr
);
1404 //cursorToTextPosition(tPtr, tPtr->clicked.x+w, tPtr->clicked.y);
1405 //check for "sneppah tahw" with blank paras...
1412 selectRegion(Text
*tPtr
, int x
, int y
)
1414 tPtr
->sRect
.pos
.x
= WMIN(tPtr
->clicked
.x
, x
);
1415 tPtr
->sRect
.size
.width
= abs(tPtr
->clicked
.x
-x
);
1416 tPtr
->sRect
.pos
.y
= WMIN(tPtr
->clicked
.y
, y
);
1417 if(tPtr
->sRect
.pos
.y
<0) tPtr
->sRect
.pos
.y
=0;
1418 tPtr
->sRect
.size
.height
= abs(tPtr
->clicked
.y
-y
);
1421 while(y>tPtr->visibleH && tPtr->vpos < tPtr->docHeight-tPtr->visibleH) {
1422 WMRefreshText(tPtr, tPtr->vpos+16, tPtr->hpos);
1425 //printf("%d %d \n", y, tPtr->vpos);
1427 //foreach para in selection...
1428 drawDocumentPartsOnPixmap(tPtr
, True
);
1436 #define WM_EMACSKEYMASK ControlMask
1437 #define WM_EMACSKEY_LEFT XK_b
1438 #define WM_EMACSKEY_RIGHT XK_f
1439 #define WM_EMACSKEY_HOME XK_a
1440 #define WM_EMACSKEY_END XK_e
1441 #define WM_EMACSKEY_BS XK_h
1442 #define WM_EMACSKEY_DEL XK_d
1445 handleTextKeyPress(Text
*tPtr
, XEvent
*event
)
1449 int control_pressed
= False
;
1451 if(!tPtr
->editable
) return;
1453 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
)
1454 control_pressed
= True
;
1455 buffer
[XLookupString(&event
->xkey
, buffer
, 1, &ksym
, NULL
)] = '\0';
1461 if(tPtr
->currentChunk
) {
1463 Chunk
*chunk
= tPtr
->currentChunk
;
1464 if(chunk
->type
== ctText
) {
1465 WMFont
*font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
1466 if(ksym
==XK_Right
) {
1467 short pos
= (tPtr
->tpos
<chunk
->chars
)?tPtr
->tpos
+1:
1469 w
= WMWidthOfString(font
,&chunk
->text
[pos
],1);
1471 short pos
= (tPtr
->tpos
>0)?tPtr
->tpos
-1:0;
1472 w
= WMWidthOfString(font
,&chunk
->text
[pos
],1);
1474 } else { w
= chunk
->pixmap
->width
; }
1475 if(ksym
==XK_Right
) w
= -w
;
1476 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
-w
, tPtr
->clicked
.y
);
1478 if(ksym
==XK_Right
) ksym
= XK_Down
;
1486 noCChunk
: { short h
= tPtr
->clheight
-2;
1487 if(ksym
==XK_Down
) h
= -h
;
1488 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
, tPtr
->clicked
.y
-h
);
1492 deleteTextInteractively(tPtr
, dtBackSpace
);
1497 deleteTextInteractively(tPtr
, dtDelete
);
1503 if(buffer
[0] != '\0' && (buffer
[0] == '\n' || !iscntrl(buffer
[0])))
1504 insertTextInteractively(tPtr
, buffer
);
1505 else if(control_pressed
&& ksym
==XK_r
)
1506 {Bool i
= !tPtr
->rulerShown
; WMShowTextRuler(tPtr
, i
);
1507 tPtr
->rulerShown
= i
; }
1515 pasteText(WMView
*view
, Atom selection
, Atom target
, Time timestamp
,
1516 void *cdata
, WMData
*data
)
1518 Text
*tPtr
= (Text
*)view
->self
;
1522 tPtr
->waitingForSelection
= False
;
1524 str
= (char*)WMDataBytes(data
);
1525 if(tPtr
->tpos
<1) WMPrependTextStream(tPtr
, str
);
1526 else WMAppendTextStream(tPtr
, str
);
1527 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1530 str
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1533 if(tPtr
->tpos
<1) WMPrependTextStream(tPtr
, str
);
1534 else WMAppendTextStream(tPtr
, str
);
1536 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1543 releaseSelection(Text
*tPtr
)
1545 Paragraph
*para
= tPtr
->paragraphs
;
1548 chunk
= para
->chunks
;
1550 chunk
->selected
= False
;
1551 chunk
= chunk
->next
;
1555 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
1556 tPtr
->ownsSelection
= False
;
1557 drawDocumentPartsOnPixmap(tPtr
, True
);
1563 requestHandler(WMView
*view
, Atom selection
, Atom target
,
1564 void *cdata
, Atom
*type
)
1566 Text
*tPtr
= view
->self
;
1568 Display
*dpy
= tPtr
->view
->screen
->display
;
1570 Atom TEXT
= XInternAtom(dpy
, "TEXT", False
);
1571 Atom COMPOUND_TEXT
= XInternAtom(dpy
, "COMPOUND_TEXT", False
);
1572 WMData
*data
= NULL
;
1575 if(!tPtr
->ownsSelection
|| !tPtr
->paragraphs
) return NULL
;
1576 //printf("got here\n");
1578 if (target
== XA_STRING
|| target
== TEXT
|| target
== COMPOUND_TEXT
) {
1579 //for bleh in selection...
1581 Paragraph
*para
= tPtr
->paragraphs
;
1582 Chunk
*chunk
= NULL
;
1583 char pixmap
[] = "[pixmap]";
1588 chunk
= para
->chunks
;
1591 if(chunk
->selected
&& chunk
->type
== ctText
) {
1592 len
= chunk
->chars
; //chunk->sEnd - chunk->sStart;
1596 memcpy(s
, &chunk
->text
[0*chunk
->sStart
], len
);
1599 data
= WMCreateDataWithBytes(s
, strlen(s
));
1602 printf("append: %c %d\n", *s
, strlen(s
));
1603 WMAppendDataBytes(data
, s
, strlen(s
));
1608 printf("len is %d [%d %d] %d \n", len
, chunk
->sStart
, chunk
->sEnd
,
1611 chunk
= chunk
->next
;
1617 WMSetDataFormat(data
, 8);
1624 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
1625 if (target
== _TARGETS
) {
1626 Atom
*ptr
= wmalloc(4 * sizeof(Atom
));
1630 ptr
[3] = COMPOUND_TEXT
;
1632 data
= WMCreateDataWithBytes(ptr
, 4*4);
1633 WMSetDataFormat(data
, 32);
1646 lostHandler(WMView
*view
, Atom selection
, void *cdata
)
1648 WMText
*tPtr
= (WMText
*)view
->self
;
1649 releaseSelection(tPtr
);
1652 static WMSelectionProcs selectionHandler
= {
1653 requestHandler
, lostHandler
, NULL
};
1656 _notification(void *observerData
, WMNotification
*notification
)
1658 WMText
*to
= (WMText
*)observerData
;
1659 WMText
*tw
= (WMText
*)WMGetNotificationClientData(notification
);
1660 if (to
!= tw
) lostHandler(to
->view
, XA_PRIMARY
, NULL
);
1664 handleTextEvents(XEvent
*event
, void *data
)
1666 Text
*tPtr
= (Text
*)data
;
1667 Display
*dpy
= event
->xany
.display
;
1669 if(tPtr
->waitingForSelection
) return;
1671 switch (event
->type
) {
1673 if(!tPtr
->editable
|| tPtr
->buttonHeld
) {
1677 if(tPtr
->ownsSelection
) releaseSelection(tPtr
);
1678 //if (tPtr->waitingForSelection) return;
1681 XGrabPointer(dpy
, W_VIEW(tPtr
)->window
, False
,
1682 PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
1683 GrabModeAsync
, GrabModeAsync
, None
,
1684 W_VIEW(tPtr
)->screen
->invisibleCursor
, CurrentTime
);
1685 tPtr
->pointerGrabbed
= True
;
1687 handleTextKeyPress(tPtr
, event
);
1691 if(tPtr
->pointerGrabbed
) {
1692 tPtr
->pointerGrabbed
= False
;
1693 XUngrabPointer(dpy
, CurrentTime
);
1695 if((event
->xmotion
.state
& Button1Mask
)) {
1696 selectRegion(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
1697 if(!tPtr
->ownsSelection
) {
1698 WMCreateSelectionHandler(tPtr
->view
, XA_PRIMARY
,
1699 event
->xbutton
.time
, &selectionHandler
, NULL
);
1700 tPtr
->ownsSelection
= True
;
1705 if(event
->xbutton
.button
== Button1
) {
1706 if(tPtr
->ownsSelection
) releaseSelection(tPtr
);
1707 cursorToTextPosition(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
1708 if (tPtr
->pointerGrabbed
) {
1709 tPtr
->pointerGrabbed
= False
;
1710 XUngrabPointer(dpy
, CurrentTime
);
1714 if(!tPtr
->focused
) {
1715 WMSetFocusToWidget(tPtr
);
1716 tPtr
->focused
= True
;
1719 if(event
->xbutton
.button
== 4)
1720 WMScrollText(tPtr
, -16);
1721 else if(event
->xbutton
.button
== 5)
1722 WMScrollText(tPtr
, 16);
1727 tPtr
->buttonHeld
= False
;
1728 if (tPtr
->pointerGrabbed
) {
1729 tPtr
->pointerGrabbed
= False
;
1730 XUngrabPointer(dpy
, CurrentTime
);
1733 if(event
->xbutton
.button
== 4 || event
->xbutton
.button
== 5)
1735 if(event
->xbutton
.button
== Button2
&& tPtr
->editable
) {
1738 if(!WMRequestSelection(tPtr
->view
, XA_PRIMARY
, XA_STRING
,
1739 event
->xbutton
.time
, pasteText
, NULL
)) {
1740 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1743 WMAppendTextStream(tPtr
, text
);
1745 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1746 } else tPtr
->waitingForSelection
= True
;
1755 handleNonTextEvents(XEvent
*event
, void *data
)
1757 Text
*tPtr
= (Text
*)data
;
1759 switch(event
->type
) {
1761 if(!event
->xexpose
.count
&& tPtr
->view
->flags
.realized
)
1766 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
1768 tPtr
->focused
= True
;
1769 //cursor...paintText(tPtr);
1773 tPtr
->focused
= False
;
1774 //cursor...paintText(tPtr);
1779 //for(...)WMRemoveTextParagraph(tPtr, para);
1784 //printf("handleNonTextEvents\n");
1790 rulerCallBack(WMWidget
*w
, void *self
)
1792 Text
*tPtr
= (Text
*)self
;
1795 if(tPtr
->currentPara
) {
1796 Paragraph
*para
= tPtr
->currentPara
;
1797 para
->fmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerFirst
);
1798 para
->bmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerBody
);
1799 para
->rmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
);
1800 affectNextParas(tPtr
, para
, -23);
1801 putParagraphOnPixmap(tPtr
, para
, True
);
1804 which
= WMGetReleasedRulerMargin(tPtr
->ruler
);
1805 if(which
!= WRulerDocLeft
&& which
!= WRulerRight
1806 /* && Selection.para.count > 0 */ ) {
1808 "//for(i=0; i<Selection.para.count; i++) {"
1810 "//calcParaExtents(tPtr, para);}\n");
1812 WMRefreshText(tPtr
, 0, 0);
1819 rulerMoveCallBack(WMWidget
*w
, void *self
)
1821 Text
*tPtr
= (Text
*)self
;
1822 short rmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
);
1825 if(WMGetGrabbedRulerMargin(tPtr
->ruler
) == WRulerLeft
) {
1826 short lmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
1827 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
1828 22, 42, lmargin
-21, tPtr
->visibleH
, True
);
1829 } else if(WMGetGrabbedRulerMargin(tPtr
->ruler
) == WRulerRight
&&
1830 tPtr
->docWidth
+11 < rmargin
) {
1831 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
1832 rmargin
-3, 42, 10, tPtr
->visibleH
, True
);
1839 /* ------------- non-static functions that are "friends" ------------- */
1840 /* ------------- called as (tPtr->funcs.foo)(bars...) ------------- */
1842 /* create a new paragraph. Don't do anything with it just yet */
1845 createParagraph(short fmargin
, short bmargin
, short rmargin
,
1846 short *tabstops
, short numTabs
, WMAlignment alignment
)
1848 Paragraph
*para
= wmalloc(sizeof(Paragraph
));
1849 if(!para
) return NULL
;
1851 para
->chunks
= NULL
;
1855 para
->fmargin
= (fmargin
>=0)?fmargin
:0;
1856 para
->bmargin
= (bmargin
>=0)?bmargin
:0;
1857 if(rmargin
-bmargin
>= 100 && rmargin
-fmargin
>= 100)
1858 para
->rmargin
= rmargin
;
1860 para
->rmargin
= 100;
1861 para
->tabstops
= tabstops
;
1862 para
->numTabs
= (tabstops
)?numTabs
:0;
1864 para
->drawbuffer
= (Pixmap
)NULL
;
1865 para
->bulletPix
= NULL
;
1866 para
->top
= para
->bottom
= 0;
1867 para
->width
= para
->height
= 0;
1869 para
->align
= alignment
;
1874 /* insert the new paragraph in the tPtr, either right before
1875 or after the currentPara. It's the responsibility of the
1876 calling code to set what currentPara is. via WMSetTextCurrentParagraph.
1877 If currentPara is not set, set it as the first in the document.
1878 This function then sets currentPara as _this_ paragraph.
1879 NOTE: this means careless parser implementors might lose previous
1880 paragraphs... but this keeps stuff small and non-buggy :-) */
1882 insertParagraph(WMText
*tPtr
, void *v
, InsertType type
)
1883 //insertParagraph(WMText *tPtr, Paragraph *para, InsertType type)
1886 Paragraph
*para
= (Paragraph
*)v
;
1887 if(!para
|| !tPtr
) return;
1889 if(!tPtr
->currentPara
) {
1890 tPtr
->paragraphs
= para
;
1892 tmp
= tPtr
->paragraphs
;
1893 if(type
== itAppend
) {
1894 while(tmp
->next
&& tmp
!= tPtr
->currentPara
)
1897 para
->next
= tmp
->next
;
1899 } else { /* must be prepend */
1900 /* this "prior" member is that "doing things the hard way"
1901 I spoke of. See? it's not too bad afterall... */
1902 Paragraph
*prior
= NULL
;
1903 while(tmp
->next
&& tmp
!= tPtr
->currentPara
) {
1907 /* if this is the first */
1908 if(tmp
== tPtr
->paragraphs
) {
1910 tPtr
->paragraphs
= para
;
1915 tPtr
->currentPara
= para
;
1919 /* create a new chunk to contain exactly ONE pixmap */
1922 createPChunk(WMPixmap
*pixmap
, short script
, ushort ul
)
1926 chunk
= wmalloc(sizeof(Chunk
));
1932 chunk
->pixmap
= NULL
; /* if it's NULL, we'll draw the "broken" pixmap... */
1933 else chunk
->pixmap
= WMRetainPixmap(pixmap
);
1935 chunk
->mallocedSize
= 0;
1936 chunk
->type
= ctImage
;
1938 chunk
->color
= NULL
;
1939 chunk
->script
= script
;
1941 chunk
->selected
= False
;
1947 /* create a new chunk to contain some text with the given format */
1950 createTChunk(char *text
, short chars
, WMFont
*font
,
1951 WMColor
*color
, short script
, ushort ul
)
1955 if(!text
|| chars
<0 || !font
|| !color
) return NULL
;
1956 chunk
= wmalloc(sizeof(Chunk
));
1957 if(!chunk
) return NULL
;
1959 chunk
->mallocedSize
= reqBlockSize(chars
);
1960 chunk
->text
= (char *)wmalloc(chunk
->mallocedSize
);
1961 memcpy(chunk
->text
, text
, chars
);
1962 chunk
->pixmap
= NULL
;
1963 chunk
->chars
= chars
;
1964 chunk
->type
= ctText
;
1965 chunk
->font
= WMRetainFont(font
);
1966 chunk
->color
= WMRetainColor(color
);
1967 chunk
->script
= script
;
1969 chunk
->selected
= False
;
1975 /* insert the new chunk in the paragraph, either right before
1976 or after the currentChunk. It's the responsibility of the
1977 calling code to set what currentChunk is via WMSetTextCurrentChunk.
1978 If currentChunk is not set, set it as the first in the existing
1979 paragraph... if not even that, you lose... try again.
1980 This function then sets currentChunk as _this_ chunk.
1981 NOTE: this means careless parser implementors might lose previous
1982 paragraphs/chunks... but this keeps stuff small and non-buggy :-) */
1984 insertChunk(WMText
*tPtr
, void *v
, InsertType type
)
1987 Chunk
*chunk
= (Chunk
*)v
;
1989 if(!tPtr
|| !chunk
) return;
1991 if(!tPtr
->paragraphs
) { /* i.e., first chunk via insertTextInteractively */
1992 Paragraph
*para
= (tPtr
->funcs
.createParagraph
) (0, 0, tPtr
->visibleW
,
1994 (tPtr
->funcs
.insertParagraph
) (tPtr
, para
, itAppend
);
1997 if(!tPtr
->currentPara
)
1999 if(!tPtr
->currentChunk
) { /* there is a current chunk */
2000 tPtr
->currentPara
->chunks
= chunk
;
2001 } else if(!tPtr
->currentPara
->chunks
) {
2002 /* but it's not of this paragraph */
2003 tPtr
->currentPara
->chunks
= chunk
;
2005 tmp
= tPtr
->currentPara
->chunks
;
2007 if(type
== itAppend
) {
2008 while(tmp
->next
&& tmp
!= tPtr
->currentChunk
)
2011 chunk
->next
= tmp
->next
;
2014 } else { /* must be prepend */
2015 /* this "prior" member is that "doing things the hard way"
2016 I spoke of. See? it's not too bad afterall... */
2017 Chunk
*prior
= NULL
;
2018 while(tmp
->next
&& tmp
!= tPtr
->currentChunk
) {
2022 /* if this is the first */
2023 if(tmp
== tPtr
->currentPara
->chunks
) {
2025 tPtr
->currentPara
->chunks
= chunk
;
2027 prior
->next
= chunk
;
2030 tPtr
->currentChunk
= chunk
;
2031 tPtr
->tpos
= chunk
->chars
;
2035 /* ------------- non-static functions (i.e., APIs) ------------- */
2036 /* ------------- called as WMVerbText[Subject] ------------- */
2038 #define DEFAULT_TEXT_WIDTH 250
2039 #define DEFAULT_TEXT_HEIGHT 200
2044 WMCreateText(WMWidget
*parent
)
2046 Text
*tPtr
= wmalloc(sizeof(Text
));
2048 perror("could not create text widget\n");
2051 memset(tPtr
, 0, sizeof(Text
));
2052 tPtr
->widgetClass
= WC_Text
;
2053 tPtr
->view
= W_CreateView(W_VIEW(parent
));
2055 perror("could not create text's view\n");
2059 tPtr
->view
->self
= tPtr
;
2060 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
2061 tPtr
->view
->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2062 W_ResizeView(tPtr
->view
, DEFAULT_TEXT_WIDTH
, DEFAULT_TEXT_HEIGHT
);
2063 tPtr
->bg
= tPtr
->view
->screen
->white
;
2064 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->bg
);
2067 tPtr
->ruler
= WMCreateRuler(tPtr
);
2068 (W_VIEW(tPtr
->ruler
))->attribs
.cursor
= tPtr
->view
->screen
->defaultCursor
;
2069 (W_VIEW(tPtr
->ruler
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2070 WMMoveWidget(tPtr
->ruler
, 0, 0);
2071 WMResizeWidget(tPtr
->ruler
, W_VIEW(parent
)->size
.width
, 40);
2072 WMShowRulerTabs(tPtr
->ruler
, True
);
2073 WMSetRulerAction(tPtr
->ruler
, rulerCallBack
, tPtr
);
2074 WMSetRulerMoveAction(tPtr
->ruler
, rulerMoveCallBack
, tPtr
);
2078 tPtr
->vscroller
= WMCreateScroller(tPtr
);
2079 (W_VIEW(tPtr
->vscroller
))->attribs
.cursor
=
2080 tPtr
->view
->screen
->defaultCursor
;
2081 (W_VIEW(tPtr
->vscroller
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2082 WMMoveWidget(tPtr
->vscroller
, 1, 1);
2083 WMResizeWidget(tPtr
->vscroller
, 20, tPtr
->view
->size
.height
- 2);
2084 WMSetScrollerArrowsPosition(tPtr
->vscroller
, WSAMaxEnd
);
2085 WMSetScrollerAction(tPtr
->vscroller
, scrollersCallBack
, tPtr
);
2089 tPtr
->hscroller
= WMCreateScroller(tPtr
);
2090 (W_VIEW(tPtr
->hscroller
))->attribs
.cursor
=
2091 tPtr
->view
->screen
->defaultCursor
;
2092 (W_VIEW(tPtr
->hscroller
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2093 WMMoveWidget(tPtr
->hscroller
, 1, tPtr
->view
->size
.height
-21);
2094 WMResizeWidget(tPtr
->hscroller
, tPtr
->view
->size
.width
- 2, 20);
2095 WMSetScrollerArrowsPosition(tPtr
->hscroller
, WSAMaxEnd
);
2096 WMSetScrollerAction(tPtr
->hscroller
, scrollersCallBack
, tPtr
);
2098 tPtr
->visibleW
= tPtr
->view
->size
.width
;
2099 tPtr
->visibleH
= tPtr
->view
->size
.height
;
2101 tPtr
->paragraphs
= NULL
;
2103 tPtr
->docHeight
= 0;
2104 tPtr
->dBulletPix
= WMCreatePixmapFromXPMData(tPtr
->view
->screen
,
2106 tPtr
->dUnknownImg
= WMCreatePixmapFromXPMData(tPtr
->view
->screen
,
2109 tPtr
->sRect
.pos
.x
= tPtr
->sRect
.pos
.y
= 0;
2110 tPtr
->sRect
.size
.width
= tPtr
->sRect
.size
.height
= 0;
2111 tPtr
->currentPara
= NULL
;
2112 tPtr
->currentChunk
= NULL
;
2115 tPtr
->parser
= NULL
;
2116 tPtr
->writer
= NULL
;
2117 tPtr
->funcs
.createParagraph
= createParagraph
;
2118 tPtr
->funcs
.insertParagraph
= insertParagraph
;
2119 tPtr
->funcs
.createPChunk
= createPChunk
;
2120 tPtr
->funcs
.createTChunk
= createTChunk
;
2121 tPtr
->funcs
.insertChunk
= insertChunk
;
2123 tPtr
->clicked
.x
= tPtr
->clicked
.y
= -23;
2124 tPtr
->cursor
.x
= tPtr
->cursor
.y
= -23;
2126 tPtr
->relief
= WRSunken
;
2127 tPtr
->wrapping
= wrWord
;
2128 tPtr
->editable
= False
;
2129 tPtr
->cursorShown
= False
;
2130 tPtr
->frozen
= False
;
2131 tPtr
->focused
= False
;
2132 tPtr
->pointerGrabbed
= False
;
2133 tPtr
->buttonHeld
= False
;
2134 tPtr
->ignoreNewLine
= False
;
2135 tPtr
->waitingForSelection
= False
;
2136 tPtr
->findingClickPoint
= False
;
2137 tPtr
->foundClickPoint
= False
;
2138 tPtr
->ownsSelection
= False
;
2142 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2143 tPtr
->dColor
= WMBlackColor(tPtr
->view
->screen
);
2145 tPtr
->view
->delegate
= &_TextViewDelegate
;
2146 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
2147 |EnterWindowMask
|LeaveWindowMask
|FocusChangeMask
,
2148 handleNonTextEvents
, tPtr
);
2149 WMCreateEventHandler(tPtr
->view
, ButtonReleaseMask
|ButtonPressMask
2150 |KeyReleaseMask
|KeyPressMask
|Button1MotionMask
,
2151 handleTextEvents
, tPtr
);
2153 WMAddNotificationObserver(_notification
, tPtr
, "_lostOwnership", tPtr
);
2155 WMSetTextMonoFont(tPtr
, True
);
2156 WMShowTextRuler(tPtr
, False
);
2157 WMSetTextHasHorizontalScroller(tPtr
, False
);
2158 WMSetTextHasVerticalScroller(tPtr
, True
);
2159 //printf("the sizeof chunk is %d\n", sizeof(Chunk));
2160 //printf("the sizeof para is %d\n", sizeof(Paragraph));
2161 //printf("the sizeof text is %d\n", sizeof(Text));
2166 //WRetainPixmap(tPtr->dBulletPix);
2169 WMRemoveTextParagraph(WMText
*tPtr
, int which
)
2171 Paragraph
*prior
, *removed
;
2172 if(!tPtr
|| which
<0) return;
2174 WMSetTextCurrentParagraph(tPtr
, which
);
2175 removed
= tPtr
->currentPara
;
2176 if(!removed
) return;
2177 if(removed
->chunks
)printf("WMRemoveTextChunks\n");
2178 if(removed
== tPtr
->paragraphs
|| which
==0) {
2179 tPtr
->paragraphs
= removed
->next
;
2181 WMSetTextCurrentParagraph(tPtr
, which
-1);
2182 prior
= tPtr
->currentPara
;
2184 prior
->next
= removed
->next
;
2193 /* set what is known as the currentPara in the tPtr. */
2194 /* negative number means: "gib me last chunk" */
2196 WMSetTextCurrentParagraph(WMText
*tPtr
, int current
)
2201 if(!tPtr
|| current
<0) return;
2203 tPtr
->currentPara
= tPtr
->paragraphs
;
2206 tmp
= tPtr
->paragraphs
;
2207 while(tmp
->next
&& ((current
==-23)?1:i
++<current
)) {
2208 //while(tmp && i++<current) {
2210 } tPtr
->currentPara
= tmp
;
2211 //? want to do this?if(tmp) tPtr->currentChunk = tmp
2216 WMGetTextParagraphs(WMText
*tPtr
)
2221 tmp
= tPtr
->paragraphs
;
2231 WMGetTextCurrentParagraph(WMText
*tPtr
)
2236 if(!tPtr
) return current
;
2237 if(!tPtr
->currentPara
) return current
;
2238 if(!tPtr
->paragraphs
) return current
;
2239 tmp
= tPtr
->paragraphs
;
2242 if(tmp
== tPtr
->currentPara
)
2248 /* set what is known as the currentChunk within the currently
2249 selected currentPara (or the first paragraph in the document). */
2251 WMSetTextCurrentChunk(WMText
*tPtr
, int current
)
2257 tPtr
->currentChunk
= NULL
;
2258 if(!tPtr
->currentPara
) {
2259 tPtr
->currentPara
= tPtr
->paragraphs
;
2260 if(!tPtr
->currentPara
)
2265 tPtr
->currentChunk
= tPtr
->currentPara
->chunks
;
2268 tmp
= tPtr
->currentPara
->chunks
;
2270 while(tmp
->next
&& ((current
<0)?1:i
++<current
))
2272 } tPtr
->currentChunk
= tmp
;
2277 WMRemoveTextChunk(WMText
*tPtr
, int which
)
2279 Chunk
*prior
, *removed
;
2281 if(!tPtr
|| which
<0) return;
2282 para
= tPtr
->currentPara
;
2285 WMSetTextCurrentChunk(tPtr
, which
);
2286 removed
= tPtr
->currentChunk
;
2287 if(!removed
) return;
2288 if(removed
== tPtr
->currentPara
->chunks
|| which
==0) {
2289 para
->chunks
= removed
->next
;
2291 WMSetTextCurrentChunk(tPtr
, which
-1);
2292 prior
= tPtr
->currentChunk
;
2294 prior
->next
= removed
->next
;
2296 if(removed
->type
== ctText
) {
2297 wgdbFree(removed
->text
);
2298 WMReleaseFont(removed
->font
);
2299 WMReleaseColor(removed
->color
);
2301 WMReleasePixmap(removed
->pixmap
);
2308 WMGetTextCurrentChunk(WMText
*tPtr
)
2314 if(!tPtr
->currentChunk
) return 0;
2315 if(!tPtr
->currentPara
) {
2316 tPtr
->currentPara
= tPtr
->paragraphs
;
2317 if(!tPtr
->currentPara
)
2321 tmp
= tPtr
->currentPara
->chunks
;
2323 if(tmp
== tPtr
->currentChunk
)
2332 WMGetTextChunks(WMText
*tPtr
)
2336 if(!tPtr
|| !tPtr
->currentPara
) return 0;
2337 tmp
= tPtr
->currentPara
->chunks
;
2345 WMShowTextRuler(WMText
*tPtr
, Bool show
)
2348 if(tPtr
->monoFont
) show
= False
;
2350 tPtr
->rulerShown
= show
;
2351 if(show
) WMMapWidget(tPtr
->ruler
);
2352 else WMUnmapWidget(tPtr
->ruler
);
2353 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2357 WMGetTextRulerShown(WMText
*tPtr
)
2359 if(!tPtr
) return False
;
2360 return tPtr
->rulerShown
;
2364 WMSetTextRulerMargin(WMText
*tPtr
, char which
, short pixels
)
2367 if(tPtr
->monoFont
) return;
2368 WMSetRulerMargin(tPtr
->ruler
, which
, pixels
);
2369 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2373 WMGetTextRulerMargin(WMText
*tPtr
, char which
)
2378 return WMGetRulerMargin(tPtr
->ruler
, which
);
2383 WMShowTextRulerTabs(WMText
*tPtr
, Bool show
)
2386 if(tPtr
->monoFont
) return;
2387 WMShowRulerTabs(tPtr
->ruler
, show
);
2391 WMSetTextMonoFont(WMText
*tPtr
, Bool mono
)
2394 if(mono
&& tPtr
->rulerShown
)
2395 WMShowTextRuler(tPtr
, False
);
2397 tPtr
->monoFont
= mono
;
2401 WMGetTextMonoFont(WMText
*tPtr
)
2403 if(!tPtr
) return True
;
2404 return tPtr
->monoFont
;
2408 WMForceTextFocus(WMText
*tPtr
)
2412 if(tPtr
->clicked
.x
== -23 || tPtr
->clicked
.y
== 23)
2413 cursorToTextPosition(tPtr
, 100, 100); /* anyplace */
2415 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
, tPtr
->clicked
.y
);
2420 WMSetTextEditable(WMText
*tPtr
, Bool editable
)
2423 tPtr
->editable
= editable
;
2428 WMGetTextEditable(WMText
*tPtr
)
2431 return tPtr
->editable
;
2436 WMScrollText(WMText
*tPtr
, int amount
)
2439 if(amount
== 0 || !tPtr
) return;
2440 if(!tPtr
->view
->flags
.realized
) return;
2443 if(tPtr
->vpos
> 0) {
2444 if(tPtr
->vpos
> amount
) tPtr
->vpos
+= amount
;
2448 int limit
= tPtr
->docHeight
- tPtr
->visibleH
;
2449 if(tPtr
->vpos
< limit
) {
2450 if(tPtr
->vpos
< limit
-amount
) tPtr
->vpos
+= amount
;
2451 else tPtr
->vpos
= limit
;
2455 if(scroll
&& tPtr
->vpos
!= tPtr
->prevVpos
) {
2456 updateScrollers(tPtr
);
2457 drawDocumentPartsOnPixmap(tPtr
, False
);
2460 tPtr
->prevVpos
= tPtr
->vpos
;
2465 WMPageText(WMText
*tPtr
, Bool scrollUp
)
2468 if(!tPtr
->view
->flags
.realized
) return;
2470 return WMScrollText(tPtr
, scrollUp
2471 ? tPtr
->visibleH
:-tPtr
->visibleH
);
2475 WMIgnoreTextNewline(WMText
*tPtr
, Bool ignore
)
2478 tPtr
->ignoreNewLine
= ignore
;
2483 WMSetTextHasHorizontalScroller(WMText
*tPtr
, Bool flag
)
2489 rh
= tPtr
->rulerShown
?40:0;
2490 tPtr
->hasHscroller
= flag
;
2492 WMMapWidget(tPtr
->hscroller
);
2493 tPtr
->visibleH
= tPtr
->view
->size
.height
-rh
-22;
2495 WMUnmapWidget(tPtr
->hscroller
);
2496 tPtr
->visibleH
= tPtr
->view
->size
.height
-rh
;
2498 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2504 WMSetTextHasVerticalScroller(WMText
*tPtr
, Bool flag
)
2507 tPtr
->hasVscroller
= flag
;
2509 WMMapWidget(tPtr
->vscroller
);
2510 tPtr
->visibleW
= tPtr
->view
->size
.width
-22;
2511 WMSetRulerOffset(tPtr
->ruler
, 22); /* scrollbar width + 2 */
2513 WMUnmapWidget(tPtr
->vscroller
);
2514 tPtr
->visibleW
= tPtr
->view
->size
.width
;
2515 WMSetRulerOffset(tPtr
->ruler
, 2);
2517 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2524 WMRefreshText(WMText
*tPtr
, int vpos
, int hpos
)
2530 if(tPtr
->frozen
|| !tPtr
->view
->flags
.realized
)
2534 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
2535 22, (tPtr
->rulerShown
)?45:5,
2536 tPtr
->visibleW
, tPtr
->visibleH
, True
);
2538 calcDocExtents(tPtr
);
2540 printf("vpos:%d tPtr->docHeight%d tPtr->visibleH%d \n",
2541 vpos, tPtr->docHeight, tPtr->visibleH);
2544 // tPtr->vpos = vpos;
2546 if(vpos < 0 || tPtr->docHeight < tPtr->visibleH)
2548 else if(vpos-tPtr->visibleH>tPtr->docHeight)
2549 tPtr->vpos = vpos-tPtr->docHeight-tPtr->visibleH-tPtr->docHeight;
2551 tPtr->vpos = tPtr->docHeight-tPtr->visibleH;
2555 if(hpos
< 0 || hpos
> tPtr
->docWidth
)
2560 drawDocumentPartsOnPixmap(tPtr
, True
);
2561 updateScrollers(tPtr
);
2565 /* would be nice to have in WINGs proper... */
2567 changeFontProp(char *fname
, char *newprop
, short which
)
2569 char before
[128], prop
[128], after
[128];
2581 if(part
==which
) bptr
= prop
;
2582 else if(part
==which
+1) bptr
= after
;
2589 snprintf(fname
, 255, "%s-%s%s", before
, newprop
, after
);
2592 /* TODO: put in wfont? */
2594 WMGetFontPlain(WMScreen
*scrPtr
, WMFont
*font
)
2604 WMGetFontBold(WMScreen
*scrPtr
, WMFont
*font
)
2606 WMFont
*newfont
=NULL
;
2608 if(!scrPtr
|| !font
)
2610 snprintf(fname
, 255, font
->name
);
2611 changeFontProp(fname
, "bold", 2);
2612 newfont
= WMCreateNormalFont(scrPtr
, fname
);
2619 WMGetFontItalic(WMScreen
*scrPtr
, WMFont
*font
)
2621 WMFont
*newfont
=NULL
;
2623 if(!scrPtr
|| !font
)
2625 snprintf(fname
, 255, font
->name
);
2626 changeFontProp(fname
, "o", 3);
2627 newfont
= WMCreateNormalFont(scrPtr
, fname
);
2634 WMGetFontOfSize(WMScreen
*scrPtr
, WMFont
*font
, short size
)
2637 if(!scrPtr
|| !font
|| size
<1)
2644 WMFreezeText(WMText
*tPtr
)
2648 tPtr
->frozen
= True
;
2652 WMThawText(WMText
*tPtr
)
2656 tPtr
->frozen
= False
;
2661 WMSetTextDefaultAlignment(WMText
*tPtr
, WMAlignment alignment
)
2664 if(tPtr
->monoFont
) return;
2666 tPtr
->dAlignment
= alignment
;
2667 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2673 WMSetTextBackgroundColor(WMText
*tPtr
, WMColor
*color
)
2681 tPtr
->bg
= WMWhiteColor(tPtr
->view
->screen
);
2683 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->bg
);
2684 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2688 WMSetTextDefaultColor(WMText
*tPtr
, WMColor
*color
)
2694 tPtr
->dColor
= color
;
2696 tPtr
->dColor
= WMBlackColor(tPtr
->view
->screen
);
2700 WMSetTextDefaultFont(WMText
*tPtr
, WMFont
*font
)
2708 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2712 WMSetTextUseFixedPitchFont(Text
*tPtr
, Bool fixed
)
2717 tPtr
->dFont
= WMCreateFontSet(tPtr
->view
->screen
,
2718 "lucidasanstypewriter-12");
2720 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2721 tPtr
->fixedPitch
= fixed
;
2725 WMSetTextParser(WMText
*tPtr
, WMParseAction
*parser
)
2728 if(tPtr
->monoFont
) return;
2729 tPtr
->parser
= parser
;
2734 WMGetTextParserActions(WMText
*tPtr
)
2736 WMParserActions null
;
2737 if(!tPtr
) return null
;
2743 WMGetTextAll(WMText
*tPtr
)
2751 if(!tPtr
) return NULL
;
2753 para
= tPtr
->paragraphs
;
2755 chunk
= para
->chunks
;
2757 if(chunk
->type
== ctText
) {
2758 if(chunk
->text
) length
+= chunk
->chars
;
2760 printf("getting image \n");
2762 chunk
= chunk
->next
;
2765 if(tPtr
->ignoreNewLine
) break;
2766 length
+= 4; // newlines
2770 text
= wmalloc(length
+1);
2772 para
= tPtr
->paragraphs
;
2774 chunk
= para
->chunks
;
2776 if(chunk
->type
== ctText
) {
2778 snprintf(&text
[where
], chunk
->chars
+1, "%s", chunk
->text
);
2779 where
+= chunk
->chars
;
2782 printf("writing image \n");
2784 chunk
= chunk
->next
;
2786 if(tPtr
->ignoreNewLine
) break;
2787 snprintf(&text
[where
++], 2, "\n");
2789 } text
[where
] = '\0';
2795 WMSetTextWriter(WMText
*tPtr
, WMParseAction
*writer
)
2801 tPtr
->writer
= writer
;