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>
23 void wgdbFree(void *ptr
)
25 if(!ptr
) printf("err... cannot ");
26 printf("gdbFree [%p]\n", ptr
);
31 typedef enum {ctText
=0, ctImage
=1} ChunkType
;
32 typedef enum { dtDelete
=0, dtBackSpace
} DeleteType
;
33 typedef enum {wrWord
=0, wrChar
=1, wrNone
=2} Wrapping
;
35 /* Why singly-linked and not say doubly-linked?
36 99% of the time (draw, append), the "prior"
37 member would have been a useless memory and CPU overhead,
38 and deletes _are_ relatively infrequent.
39 When the "prior" member needs to be used, the overhead of
40 doing things the hard way will be incurred... but seldomly. */
43 /* a Chunk is a singly-linked list of chunks containing:
44 o text with a given format
47 typedef struct _Chunk
{
48 char *text
; /* the text in the chunk */
49 WMPixmap
*pixmap
; /* OR the pixmap it holds */
50 short chars
; /* the number of characters in this chunk */
51 short mallocedSize
; /* the number of characters that can be held */
53 WMFont
*font
; /* the chunk's font */
54 WMColor
*color
; /* the chunk's color */
55 short ul
:1; /* underlined or not */
56 ChunkType type
:1; /* a "Text" or "Image" chunk */
57 short script
:4; /* script in points: negative for subscript */
63 struct _Chunk
*next
;/*the next member in this list */
69 /* a Paragraph is a singly-linked list of paragraphs containing:
70 o a list of chunks in that paragraph
71 o the formats for that paragraph
72 o its (draw) position relative to the entire document */
73 typedef struct _Paragraph
{
74 Chunk
*chunks
; /* the list of text and/or image chunks */
75 short fmargin
; /* the start position of the first line */
76 short bmargin
; /* the start positions of the rest of the lines */
77 short rmargin
; /* the end position of the entire paragraph */
78 short numTabs
; /* the number of tabstops */
79 short *tabstops
; /* an array of tabstops */
81 Pixmap drawbuffer
; /* the pixmap onto which the (entire)
82 paragraph will be drawn */
83 WMPixmap
*bulletPix
;/* the pixmap to use for bulleting */
84 int top
; /* the top of the paragraph relative to document */
85 int bottom
; /* the bottom of the paragraph relative to document */
86 int width
; /* the width of the paragraph */
87 int height
; /* the height of the paragraph */
88 WMAlignment align
:2;/* justification of this paragraph */
91 struct _Paragraph
*next
; /* the next member in this list */
95 static char *default_bullet
[] = {
97 " c None s None", ". c black",
98 "X c white", "o c #808080",
106 /* this is really a shrunk down version of the original
107 "broken" icon... I did not draw it, I simply shrunk it */
108 static char * unk_xpm
[] = {
110 " c None", ". c #0B080C", "+ c #13A015", "@ c #5151B8",
111 "# c #992719", "$ c #5B1C20", "% c #1DF51D", "& c #D1500D", "* c #2F304A",
112 "= c #0C6A0C", "- c #F2F1DE", "; c #D59131", "> c #B2B083", ", c #DD731A",
113 "' c #CC3113", ") c #828238", "! c #6A6A94",
114 "......!@@@@@@@....$$....",
115 "...@!@@@@@@@**...$#'....",
116 "..!!@@@@@@@@.......#....",
117 "..!@@@@@@@@@*.......$...",
118 ".!@@@#,,#*@@*..*>.*.#...",
119 "*@@@@#'',,@@@...---!....",
120 "!@@@@@*.#;*@@..!--->....",
121 "@@@@@@@@#,.@@..!----@...",
122 "!@@@@@@*#;'$...!----@...",
123 "*@@@@@@..'&;;#.)----)...",
124 ".@@@@@@..$..&'.>----)...",
125 ".@@@@@@**---,'>-----!...",
126 ".@@@@@@**---,'>-----@...",
127 "..@@@@@@@---;;;,;---....",
128 "..*@@@@*@--->#',;,-*.)..",
129 "........)---->)@;#!..>..",
130 ".....)----------;$..>)..",
131 "=%%%*.*!-------);..)-*..",
132 "=%%%%+...*)>!@*$,.>--...",
133 "*+++++++.......*$@-->...",
134 "............**@)!)>->...",
135 "........................",
136 "........................",
137 "........................"};
139 typedef struct W_Text
{
140 W_Class widgetClass
; /* the class number of this widget */
141 W_View
*view
; /* the view referring to this instance */
142 WMColor
*bg
; /* the background color to use when drawing */
144 WMRuler
*ruler
; /* the ruler subwiget to maipulate paragraphs */
146 WMScroller
*hscroller
; /* the horizontal scroller */
147 short hpos
; /* the current horizontal position */
148 short prevHpos
; /* the previous horizontal position */
150 WMScroller
*vscroller
; /* the vertical scroller */
151 int vpos
; /* the current vertical position */
152 int prevVpos
; /* the previous vertical position */
154 int visibleW
; /* the actual horizontal space available */
155 int visibleH
; /* the actual vertical space available */
157 Paragraph
*paragraphs
; /* the linked list of the paragraphs in the doc. */
158 int docWidth
; /* the width of the entire document */
159 int docHeight
; /* the height of the entire document */
161 WMFont
*dFont
; /* the default font */
162 WMColor
*dColor
; /* the default color */
163 WMPixmap
*dBulletPix
; /* the default pixmap for bullets */
164 WMPixmap
*dUnknownImg
; /* the pixmap for (missing/broken) images */
166 WMRect sRect
; /* the selected area */
167 Paragraph
*currentPara
; /* the current paragraph, in which actions occur */
168 Chunk
*currentChunk
; /* the current chunk, about which actions occur */
169 short tpos
; /* the cursor position (text position) */
170 WMParseAction
*parser
; /* what action to use to parse input text */
171 WMParseAction
*writer
; /* what action to use to write text */
172 WMParserActions funcs
; /* the "things" that parsers/writers might do */
173 XPoint clicked
; /* the position of the last mouse click */
174 XPoint cursor
; /* where the cursor is "placed" */
175 short clheight
; /* the height of the "line" clicked on */
176 short clwidth
; /* the width of the "line" clicked on */
178 WMReliefType relief
:2; /* the relief to display with */
179 Wrapping wrapping
:2; /* the type of wrapping to use in drawing */
180 WMAlignment dAlignment
:2;/* default justification */
181 ushort monoFont
:1; /* whether to ignore "rich" commands */
182 ushort fixedPitch
:1; /* assume each char in dFont is the same size */
183 ushort editable
:1; /* whether to accept user changes or not*/
184 ushort rulerShown
:1; /* whether the ruler is shown or not */
185 ushort cursorShown
:1; /* whether the cursor is currently being shown */
186 ushort frozen
:1; /* whether screen updates are to be made */
187 ushort focused
:1; /* whether this instance has input focus */
188 ushort pointerGrabbed
:1;/* whether this instance has the pointer */
189 ushort buttonHeld
:1; /* the user is still holding down the button */
190 ushort ignoreNewLine
:1; /* whether to ignore the newline character */
191 ushort waitingForSelection
:1; /* whether there is a pending paste event */
192 ushort ownsSelection
:1; /* whether it ownz the current selection */
193 ushort findingClickPoint
:1;/* whether the search for a clickpoint is on */
194 ushort foundClickPoint
:1;/* whether the clickpoint has been found */
195 ushort hasVscroller
:1; /* whether to enable the vertical scroller */
196 ushort hasHscroller
:1; /* whether to enable the horizontal scroller */
202 /* --------- static functions that are "private". don't touch :-) --------- */
205 /* max "characters per chunk that will be drawn at a time" */
206 #define MAX_WORD_LENGTH 100
209 #define MIN_DOC_WIDTH 200
210 typedef struct _LocalMargins
{
211 short left
, right
, first
, body
;
214 typedef struct _MyTextItems
{
215 char text
[MAX_WORD_LENGTH
+1];
219 Chunk
*chunk
;/* used for "click" events */
220 short start
; /* ditto... where in the chunk we start (ie. wrapped chunk) */
227 chunkSelectionRect(Text
*tPtr
, Paragraph
*para
, MyTextItems item
,
228 short y
, short j
, short lh
)
231 short type
=0; /* 0:none 1:partial: 2:all */
232 short rh
=(tPtr
->rulerShown
)?45:5;
234 WMFont
*font
= (tPtr
->monoFont
|| item
.chunk
->type
!= ctText
)?
235 tPtr
->dFont
:item
.chunk
->font
;
238 if(y
+para
->top
+rh
> tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
239 || y
+para
->top
+rh
+lh
< tPtr
->sRect
.pos
.y
)
242 if(item
.chunk
->type
== ctText
)
243 w
= WMWidthOfString(font
, item
.text
, item
.chars
);
244 else w
= item
.chunk
->pixmap
->width
;
246 if(y
+para
->top
+rh
>= tPtr
->sRect
.pos
.y
&& (y
+para
->top
+rh
+lh
247 <= tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
))
248 //&& item.x+j >= tPtr->sRect.pos.x+tPtr->sRect.size.width))
254 if(item
.x
+j
>= tPtr
->sRect
.pos
.x
&&
255 item
.x
+j
+w
< tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
)
258 if(type
== 1 && y
+para
->top
+rh
+lh
<=
259 tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
)
264 if(type
== 1 && item
.chunk
->type
== ctText
) { /* partial coverage */
265 lm
= 2+WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
266 /* even I am still confused, so don't ask please */
267 if( (item
.x
+j
+lm
>= tPtr
->sRect
.pos
.x
&&
268 item
.x
+j
+lm
<= tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
)
269 || (item
.x
+j
+lm
>= tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
270 && y
+para
->top
+rh
+lh
<=
271 tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
)
272 || (tPtr
->sRect
.pos
.y
< y
+para
->top
+rh
273 && tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
>
276 rect
.pos
.x
= item
.x
+j
;
277 item
.chunk
->selected
= True
;
278 if(item
.chunk
->chars
> 6) {
279 item
.chunk
->sStart
= 3;
280 item
.chunk
->sEnd
= item
.chunk
->chars
;
282 item
.chunk
->sStart
= 0;
283 item
.chunk
->sEnd
= item
.chunk
->chars
;
286 } else if(type
== 2) {
287 rect
.pos
.x
= item
.x
+j
;
288 item
.chunk
->selected
= True
;
289 if(item
.chunk
->type
== ctText
) {
290 item
.chunk
->sStart
= 0;
291 item
.chunk
->sStart
= item
.chunk
->chars
;
292 rect
.size
.width
= WMWidthOfString(font
,
293 item
.text
, item
.chars
);
295 rect
.size
.width
= item
.chunk
->pixmap
->width
;
300 rect
.size
.height
= lh
;
305 myDrawText(Text
*tPtr
, Paragraph
*para
, MyTextItems
*items
,
306 short nitems
, short pwidth
, int y
, short draw
, short spacepos
)
308 short i
, ul_thick
, u
, j
=0; /* j = justification */
309 short line_width
= 0, line_height
=0, mx_descent
=0;
310 WMScreen
*screen
= tPtr
->view
->screen
;
314 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return 0;
315 for(i
=0; i
<nitems
; i
++) {
316 if(items
[i
].type
== ctText
) {
317 font
= (tPtr
->monoFont
)?tPtr
->dFont
:items
[i
].chunk
->font
;
318 mx_descent
= WMIN(mx_descent
, -font
->y
);
319 line_height
= WMAX(line_height
, font
->height
);
320 //printf("chunk.x %d xpoint.x %d\n",
321 // items[i].x, tPtr->clicked.x);
323 line_width
+= WMWidthOfString(font
,
324 items
[i
].text
, items
[i
].chars
);
326 mx_descent
= WMIN(mx_descent
, -(items
[i
].pix
->height
-3));
327 /* replace -3 wif descent... */
328 line_height
= WMAX(line_height
, items
[i
].pix
->height
);
329 if(para
->align
== WARight
|| para
->align
== WACenter
) {
330 line_width
+= items
[i
].pix
->width
;
333 if(para
->align
== WARight
) {
334 j
= pwidth
- line_width
;
335 } else if (para
->align
== WACenter
) {
336 j
= (short) ((float)(pwidth
- line_width
))/2.0;
339 if(tPtr
->findingClickPoint
&& (y
+line_height
>= tPtr
->clicked
.y
)) {
340 tPtr
->foundClickPoint
= True
;
341 tPtr
->currentChunk
= items
[0].chunk
; /* just first on this "line" */
342 tPtr
->tpos
= items
[0].start
; /* where to "start" counting from */
343 tPtr
->clicked
.x
= j
+items
[0].x
;
344 tPtr
->clicked
.y
= y
+line_height
+mx_descent
;
345 tPtr
->clheight
= line_height
; /* to draw the cursor */
346 tPtr
->clwidth
= line_width
; /* where to stop searching */
348 } if(!draw
) return line_height
;
350 for(i
=0; i
<nitems
; i
++) {
353 if(tPtr
->ownsSelection
) {
354 WMRect rect
= chunkSelectionRect(tPtr
, para
,
355 items
[i
], y
, j
, line_height
);
356 if(rect
.pos
.x
!= -23) { /* has been selected */
357 XFillRectangle(tPtr
->view
->screen
->display
, para
->drawbuffer
,
358 WMColorGC(WMGrayColor(tPtr
->view
->screen
)),
359 rect
.pos
.x
, rect
.pos
.y
, rect
.size
.width
, rect
.size
.height
);
363 if(items
[i
].type
== ctText
) {
364 gc
= WMColorGC(items
[i
].chunk
->color
);
365 font
= (tPtr
->monoFont
)?tPtr
->dFont
:items
[i
].chunk
->font
;
366 WMDrawString(screen
, para
->drawbuffer
, gc
, font
,
367 items
[i
].x
+j
, y
- font
->y
- mx_descent
,
368 items
[i
].text
, items
[i
].chars
);
369 if(items
[i
].chunk
->ul
&& !tPtr
->monoFont
) {
370 ul_thick
= (short) ((float)font
->height
)/12.0;
371 if (ul_thick
< 1) ul_thick
= 1;
372 for(u
=0; u
<ul_thick
; u
++) {
373 XDrawLine(screen
->display
, para
->drawbuffer
, gc
, items
[i
].x
+j
,
374 y
+ 1 + u
- mx_descent
,
375 items
[i
].x
+ j
+ WMWidthOfString(font
,
376 items
[i
].text
, items
[i
].chars
), y
+ 1 + u
- mx_descent
);
380 WMDrawPixmap(items
[i
].pix
, para
->drawbuffer
, items
[i
].x
+j
,
381 y
+ 3 - mx_descent
- items
[i
].pix
->height
);
387 drawPChunkPart(Text
*tPtr
, Chunk
*chunk
, LocalMargins m
, Paragraph
*para
,
388 MyTextItems
*items
, short *nitems
, short *Lmargin
, XPoint
*where
, short draw
)
394 chunk
->pixmap
= WMRetainPixmap(tPtr
->dUnknownImg
);
396 p_width
= m
.right
- WMIN(m
.first
, m
.body
) - WMGetRulerOffset(tPtr
->ruler
);
397 if(p_width
< MIN_DOC_WIDTH
) // need WMRuler to take care of this...
399 if(where
->x
+ chunk
->pixmap
->width
<= p_width
- *Lmargin
) {
400 /* it can fit on rest of line */
401 items
[*nitems
].pix
= chunk
->pixmap
;
402 items
[*nitems
].type
= ctImage
;
403 items
[*nitems
].chars
= 0;
404 items
[*nitems
].x
= *Lmargin
+where
->x
;
405 items
[*nitems
].chunk
= chunk
;
406 items
[*nitems
].start
= 0;
408 if(*nitems
>= MAX_CHUNX
) {
409 items
[*nitems
].chars
= 0;
410 items
[*nitems
].x
= *Lmargin
+where
->x
;
411 items
[*nitems
].chunk
= chunk
;
412 items
[*nitems
].start
= 0;
413 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
414 p_width
-*Lmargin
, where
->y
, draw
, 0);
415 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
420 where
->x
+= chunk
->pixmap
->width
;
422 } else if(chunk
->pixmap
->width
<= p_width
- *Lmargin
) {
423 /* it can fit on an entire line, flush the myDrawText then wrap it */
424 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
425 p_width
-*Lmargin
, where
->y
, draw
, 0);
427 *Lmargin
= WMAX(0, m
.body
- m
.first
);
429 drawPChunkPart(tPtr
, chunk
, m
, para
, items
, nitems
,
430 Lmargin
, where
, draw
);
435 *Lmargin
= WMAX(0, m
.body
- m
.first
);
436 items
[*nitems
].pix
= chunk
->pixmap
;
437 items
[*nitems
].type
= ctImage
;
438 items
[*nitems
].chars
= 0;
439 items
[*nitems
].x
= *Lmargin
+where
->x
;
440 items
[*nitems
].chunk
= chunk
;
441 items
[*nitems
].start
= 0;
442 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
443 p_width
-*Lmargin
, where
->y
, draw
, 0);
446 /* scale image to fit, call self again */
447 /* deprecated - the management */
453 drawTChunkPart(Text
*tPtr
, Chunk
*chunk
, char *bufr
, LocalMargins m
,
454 Paragraph
*para
, MyTextItems
*items
, short *nitems
, short len
, short start
,
455 short *Lmargin
, XPoint
*where
, short draw
, short spacepos
)
457 short t_chunk_width
, p_width
, chars
;
458 WMFont
*font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
459 /* if(doc->clickstart.yes && doc->clickstart.done) return; */
462 p_width
= m
.right
- WMIN(m
.first
, m
.body
);
463 if(p_width
< MIN_DOC_WIDTH
) // need WMRuler to take care of this...
467 t_chunk_width
= WMWidthOfString(font
, bufr
, len
);
468 if((where
->x
+ t_chunk_width
<= p_width
- *Lmargin
)
469 || (tPtr
->wrapping
== wrNone
)) {
470 /* if it can fit on rest of line, append to line */
471 chars
= WMIN(len
, MAX_WORD_LENGTH
);
472 snprintf(items
[*nitems
].text
, chars
+1, "%s", bufr
);
473 items
[*nitems
].chars
= chars
;
474 items
[*nitems
].x
= *Lmargin
+where
->x
;
475 items
[*nitems
].type
= ctText
;
476 items
[*nitems
].chunk
= chunk
;
477 items
[*nitems
].start
= start
;
479 if(*nitems
>= MAX_CHUNX
) {
480 chars
= WMIN(len
, MAX_WORD_LENGTH
);
481 snprintf(items
[*nitems
].text
, chars
+1, "%s", bufr
);
482 items
[*nitems
].chars
= chars
;
483 items
[*nitems
].x
= *Lmargin
+where
->x
;
484 items
[*nitems
].type
= ctText
;
485 items
[*nitems
].chunk
= chunk
;
486 items
[*nitems
].start
= start
;
487 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
488 p_width
-*Lmargin
, where
->y
, draw
, spacepos
);
489 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
494 where
->x
+= t_chunk_width
;
496 } else if(t_chunk_width
<= p_width
- *Lmargin
) {
497 /* it can fit on an entire line, flush and wrap it to a new line */
498 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
,
499 p_width
-*Lmargin
, where
->y
, draw
, spacepos
);
500 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
502 *Lmargin
= WMAX(0, m
.body
- m
.first
);
504 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
, items
, nitems
,
505 len
, start
, Lmargin
, where
, draw
, spacepos
);
507 /* otherwise, chop line, call ourself recursively until it's all gone */
508 short J
=0; /* bufr */
509 short j
=0; /* local tmp buffer */
511 short diff
= p_width
- *Lmargin
- where
->x
;
515 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
,
516 p_width
-*Lmargin
, where
->y
, draw
, spacepos
);
517 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
519 *Lmargin
= WMAX(0, m
.body
- m
.first
);
521 diff
= p_width
- *Lmargin
- where
->x
;
524 for(J
=0; J
<len
; J
++) {
526 if(WMWidthOfString(font
, tmp
, j
+1) > diff
) {
527 drawTChunkPart(tPtr
, chunk
, tmp
, m
, para
, items
, nitems
,
528 j
, start
+_start
, Lmargin
, where
, draw
, spacepos
);
533 /* and there's always that last chunk, get it too */
534 drawTChunkPart(tPtr
, chunk
, tmp
, m
, para
, items
, nitems
,
535 j
, start
+_start
, Lmargin
, where
, draw
, spacepos
);
539 /* this function does what it's called :-)
540 o It is also used for calculating extents of para,
541 (returns height) so watch out for (Bool) draw
542 o Also used to determine where mouse was clicked */
544 putParagraphOnPixmap(Text
*tPtr
, Paragraph
*para
, Bool draw
)
546 char bufr
[MAX_WORD_LENGTH
+1]; /* a single word + '\0' */
547 MyTextItems items
[MAX_CHUNX
+1];
548 short lmargin
, spacepos
, i
, s
, nitems
, start
;
553 if(!tPtr
->view
->flags
.realized
|| !para
) return 0;
555 where
.x
= 0, where
.y
=0, nitems
= 0;
556 m
.left
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
557 m
.right
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) - m
.left
;
558 m
.first
= para
->fmargin
, m
.body
= para
->bmargin
;
561 W_Screen
*screen
= tPtr
->view
->screen
;
563 XFreePixmap(screen
->display
, para
->drawbuffer
);
564 if(para
->width
<2*tPtr
->dFont
->height
) para
->width
= 2*tPtr
->dFont
->height
;
565 if(para
->height
<tPtr
->dFont
->height
) para
->height
= tPtr
->dFont
->height
;
566 para
->drawbuffer
= XCreatePixmap(screen
->display
,
567 tPtr
->view
->window
, para
->width
, para
->height
, screen
->depth
);
568 XFillRectangle(screen
->display
, para
->drawbuffer
,
569 WMColorGC(tPtr
->bg
), 0, 0, para
->width
, para
->height
);
573 //if(para->align != tPtr->dAlignment)
574 // para->align = tPtr->dAlignment;
576 /* draw the bullet if appropriate */
577 if(m
.body
>m
.first
&& !tPtr
->monoFont
) {
578 lmargin
= m
.body
- m
.first
;
581 WMDrawPixmap(para
->bulletPix
, para
->drawbuffer
, lmargin
-10, 5);
583 WMDrawPixmap(tPtr
->dBulletPix
, para
->drawbuffer
, lmargin
-10, 5);
585 /* NeXT sez next tab, I say the m.body - m.first margin */
587 lmargin
= WMAX(0, m
.first
- m
.body
);
590 if(tPtr
->findingClickPoint
&& !para
->chunks
) {
591 tPtr
->currentChunk
= NULL
;
592 tPtr
->foundClickPoint
= True
;
594 tPtr
->clicked
.x
= lmargin
;
596 tPtr
->clheight
= para
->height
;
601 chunk
= para
->chunks
;
604 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return 0;
606 if(chunk
->type
== ctImage
&& !tPtr
->monoFont
) {
607 drawPChunkPart(tPtr
, chunk
, m
, para
, items
, &nitems
,
608 &lmargin
, &where
, draw
);
609 } else if(chunk
->text
&& chunk
->type
== ctText
) {
610 if(tPtr
->wrapping
== wrNone
) {
611 drawTChunkPart(tPtr
, chunk
, chunk
->text
, m
, para
, items
, &nitems
,
612 chunk
->chars
, 0, &lmargin
, &where
, draw
, spacepos
);
613 } else if(tPtr
->wrapping
== wrWord
) {
614 spacepos
=0, i
=0, start
=0;
615 while(spacepos
< chunk
->chars
) {
616 bufr
[i
] = chunk
->text
[spacepos
];
617 if( bufr
[i
] == ' ' || i
>= MAX_WORD_LENGTH
) {
618 if(bufr
[i
] == ' ') s
=1; else s
=0;
619 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
,
620 items
, &nitems
, i
+s
, start
, &lmargin
, &where
,
623 if(i
> MAX_WORD_LENGTH
-1)
629 /* catch that last onery one. */
630 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
,
631 items
, &nitems
, i
, start
, &lmargin
, &where
, draw
, spacepos
);
632 } } chunk
= chunk
->next
;
634 /* we might have a few leftover items that need drawing */
636 where
.y
+= myDrawText(tPtr
, para
, items
,
637 nitems
, m
.right
-m
.left
-lmargin
, where
.y
, draw
, spacepos
);
638 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return 0;
644 calcParaExtents(Text
*tPtr
, Paragraph
*para
)
649 para
->width
= tPtr
->visibleW
;
652 para
->rmargin
= tPtr
->visibleW
;
654 para
->width
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) -
655 WMIN(para
->fmargin
, para
->bmargin
)
656 - WMGetRulerOffset(tPtr
->ruler
);
660 para
->height
= tPtr
->dFont
->height
;
662 para
->height
= putParagraphOnPixmap(tPtr
, para
, False
);
664 if(para
->height
<tPtr
->dFont
->height
)
665 para
->height
= tPtr
->dFont
->height
;
666 para
->bottom
= para
->top
+ para
->height
;
671 /* rather than bother with redrawing _all_ the pixmaps, simply
672 rearrange (i.e., push down or pull up) paragraphs after this one */
674 affectNextParas(Text
*tPtr
, Paragraph
*para
, int move_y
)
679 if(!para
|| move_y
==0) return;
681 old_y
= para
->bottom
;
682 calcParaExtents(tPtr
, para
);
683 old_y
-= para
->bottom
;
684 if(old_y
== 0) return;
686 }if(move_y
== 0) return;
691 next
->bottom
= next
->top
+ next
->height
;
692 next
= next
->next
; // I know, I know
693 }tPtr
->docHeight
+= move_y
;
696 tPtr
->vpos
+= move_y
;
697 if(tPtr
->vpos
< 0) tPtr
->vpos
= 0;
698 if(tPtr
->vpos
> tPtr
->docHeight
- tPtr
->visibleH
)
699 tPtr
->vpos
= tPtr
->docHeight
- tPtr
->visibleH
;
706 calcDocExtents(Text
*tPtr
)
711 tPtr
->docWidth
= tPtr
->visibleW
;
713 tPtr
->docWidth
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) -
714 WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
717 para
= tPtr
->paragraphs
;
720 para
->top
= tPtr
->docHeight
;
721 tPtr
->docHeight
+= calcParaExtents(tPtr
, para
);
722 para
->bottom
= tPtr
->docHeight
;
725 } else { /* default to this if no paragraphs */
726 tPtr
->docHeight
= tPtr
->dFont
->height
;
729 if(tPtr
->editable
) /* add space at bottom to enter new stuff */
730 tPtr
->docHeight
+= tPtr
->dFont
->height
;
735 /* If any part of a paragraph is viewable, the entire
736 paragraph is drawn on an otherwise empty (XFreePixmap) pixmap.
737 The actual viewable parts of the paragraph(s) are then pieced
738 together via paintText:
740 -------------------------------------------
741 || this is a paragraph in this document||
742 ||========================================||
743 || | only part of it is visible though. ||
744 || |-------------------------------------||
745 ||[.| This is another paragraph ||
746 || | which I'll make relatively long ||
747 || | just for the sake of writing a long ||
748 || | paragraph with a picture: ^_^ ||
749 || |-------------------------------------||
750 ||==| Of the three paragraphs, only ||
751 ||/\| the preceding was totally copied to ||
752 ||\/| totally copied to the window, even ||
753 ==========================================||
754 though they are all on pixmaps.
755 -------------------------------------------
756 This paragraph exists only in
757 memory and so has a NULL pixmap.
758 -------------------------------------------
761 simple, right? Performance: the best of both worlds...
762 o fast scrolling: no need to rewrite what's already
763 on the screen, simply XCopy it.
764 o fast typing: only change current para, then simply
765 affect other (i.e., subsequent) paragraphs.
766 o If no part of para is on screen, gdbFree pixmap; else draw on
767 individual pixmap per para then piece several paras together
768 o Keep track of who to XCopy to window (see paintText) */
770 drawDocumentPartsOnPixmap(Text
*tPtr
, Bool all
)
774 para
= tPtr
->paragraphs
;
776 /* the 32 reduces jitter on the human eye by preparing paragraphs
777 in anticipation of when the _moving_ scrollbar reaches them */
778 if(para
->bottom
+ 32 < tPtr
->vpos
||
779 para
->top
> tPtr
->visibleH
+ tPtr
->vpos
+ 32 ) {
780 if(para
->drawbuffer
) {
781 XFreePixmap(tPtr
->view
->screen
->display
, para
->drawbuffer
);
782 para
->drawbuffer
= (Pixmap
) NULL
;
785 if(!para
->drawbuffer
|| all
)
786 putParagraphOnPixmap(tPtr
, para
, True
);
794 /* this function blindly copies the "visible" parts of a pragraph
795 unto the view, (top-down approach). It starts drawing from
796 the top of the view (which paragraph to draw is determined by
797 drawDocumentPartsOnPixmap); it stops at the bottom of the view. */
799 paintText(Text
*tPtr
)
801 short lmargin
, para_lmargin
;
802 int from
=5, to
=5, height
;
804 short vS
=0, hS
=0, rh
=0;
806 if(!tPtr
->view
->flags
.realized
) return;
808 if(tPtr
->rulerShown
) rh
= 40;
811 if(tPtr
->hasVscroller
) vS
= 21;
812 if(tPtr
->hasHscroller
) hS
= 21;
814 //XClearWindow(tPtr->view->screen->display, tPtr->view->window);
816 lmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
817 if(tPtr
->paragraphs
) {
818 para
= tPtr
->paragraphs
;
820 if(para
->drawbuffer
) {
821 from
= (para
->top
<=tPtr
->vpos
)?tPtr
->vpos
-para
->top
:0;
822 height
= para
->height
- from
;
823 if(from
>=0 && height
>0 ) {
824 para_lmargin
= WMIN(para
->fmargin
, para
->bmargin
);
825 if(lmargin
-vS
<WMIN(para
->fmargin
, para
->bmargin
)) {
827 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
828 lmargin
, to
, 2+para_lmargin
, height
, False
);
830 XFillRectangle(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
831 WMColorGC(tPtr
->dColor
), lmargin
, to
, 2+para_lmargin
, height
);
834 XCopyArea(tPtr
->view
->screen
->display
, para
->drawbuffer
,
835 tPtr
->view
->window
, WMColorGC(tPtr
->bg
), 0, from
,
836 para
->width
-4, height
, lmargin
+para_lmargin
+2, to
);
837 if( (to
+=height
) > tPtr
->visibleH
+rh
)
845 /* clear any left over space (esp. during para deletes/ ruler changes) */
846 if(tPtr
->docHeight
< tPtr
->visibleH
&& tPtr
->visibleH
+rh
+5-to
>0) {
847 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
, vS
, to
,
848 tPtr
->view
->size
.width
-vS
, tPtr
->visibleH
+rh
+hS
+5-to
, False
);
852 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
853 vS
+1, rh
+5, lmargin
-vS
, tPtr
->visibleH
+rh
+5-vS
, False
);
856 // from the "selection" days...
857 W_DrawRelief(tPtr
->view
->screen
, WMWidgetXID(tPtr
),
858 tPtr
->sRect
.pos
.x
, tPtr
->sRect
.pos
.y
,
859 tPtr
->sRect
.size
.width
, tPtr
->sRect
.size
.height
, tPtr
->relief
);
862 W_DrawRelief(tPtr
->view
->screen
, WMWidgetXID(tPtr
), 0, rh
,
863 tPtr
->visibleW
+vS
, tPtr
->visibleH
+hS
, tPtr
->relief
);
865 if(tPtr
->editable
&& tPtr
->clheight
> 0) {
866 int top
= tPtr
->cursor
.y
-tPtr
->vpos
;
867 int bot
= top
+tPtr
->clheight
;
870 if(bot
>tPtr
->visibleH
+hS
-2) bot
= tPtr
->visibleH
+hS
-2;
872 //do something about italic text...
873 XDrawLine(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
874 WMColorGC(tPtr
->dColor
), lmargin
+tPtr
->cursor
.x
, top
,
875 lmargin
+tPtr
->cursor
.x
, bot
);
882 /* called anytime either the ruler, vscroller or hscroller is hidden/shown,
883 or when the widget is resized by some user action */
885 resizeText(W_ViewDelegate
*self
, WMView
*view
)
887 Text
*tPtr
= (Text
*)view
->self
;
890 if(!tPtr
->monoFont
&& tPtr
->rulerShown
)
893 W_ResizeView(view
, view
->size
.width
, view
->size
.height
);
894 WMResizeWidget(tPtr
->ruler
, view
->size
.width
, 40);
897 if(tPtr
->hasVscroller
) {
898 WMMoveWidget(tPtr
->vscroller
, 1, 1+rh
);
899 WMResizeWidget(tPtr
->vscroller
, 20, view
->size
.height
-rh
-2);
900 tPtr
->visibleW
= view
->size
.width
-21;
902 if(tPtr
->hasHscroller
) {
903 WMMoveWidget(tPtr
->hscroller
, 20, view
->size
.height
-21);
904 WMResizeWidget(tPtr
->hscroller
, view
->size
.width
-21, 20);
905 tPtr
->visibleH
= view
->size
.height
-21-rh
;
906 } else tPtr
->visibleH
= view
->size
.height
-rh
;
908 tPtr
->visibleW
= view
->size
.width
;
909 if(tPtr
->hasHscroller
) {
910 WMMoveWidget(tPtr
->hscroller
, 1, view
->size
.height
-21);
911 WMResizeWidget(tPtr
->hscroller
, view
->size
.width
-2, 20);
912 tPtr
->visibleH
= view
->size
.height
-21-rh
;
913 } else tPtr
->visibleH
= view
->size
.width
-2-rh
;
915 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
918 W_ViewDelegate _TextViewDelegate
=
928 /* a plain text parser */
929 /* this gives useful hints on how to make a more
930 interesting parser for say HTML, RTF */
932 defaultParser(Text
*tPtr
, void *data
, short type
)
934 char *start
, *mark
, *text
= (char *) data
;
936 Paragraph
*para
= NULL
;
940 mark
= strchr(start
, '\n');
942 /* there is a newline, indicating the need for a new paragraph */
943 /* attach the chunk to the current paragraph */
944 if((short)(mark
-start
) > 1) {
945 /* ignore chunks with just a single newline but still make a
947 chunk
= (tPtr
->funcs
.createTChunk
) (start
, (short)(mark
-start
),
948 tPtr
->dFont
, tPtr
->dColor
, 0, False
);
949 (tPtr
->funcs
.insertChunk
) (tPtr
, chunk
, type
);
951 /* _then_ create a new paragraph for the _next_ chunk */
952 para
= (tPtr
->funcs
.createParagraph
) (0, 0, tPtr
->visibleW
,
954 (tPtr
->funcs
.insertParagraph
) (tPtr
, para
, type
);
957 /* just attach the chunk to the current paragraph */
958 if(strlen(start
) > 0) {
959 chunk
= (tPtr
->funcs
.createTChunk
) (start
, strlen(start
),
960 tPtr
->dFont
, tPtr
->dColor
, 0, False
);
961 (tPtr
->funcs
.insertChunk
) (tPtr
, chunk
, type
);
968 updateScrollers(Text
*tPtr
)
970 if(tPtr
->hasVscroller
) {
971 if(tPtr
->docHeight
< tPtr
->visibleH
) {
972 WMSetScrollerParameters(tPtr
->vscroller
, 0, 1);
975 float vmax
= (float)(tPtr
->docHeight
);
976 WMSetScrollerParameters(tPtr
->vscroller
,
977 ((float)tPtr
->vpos
)/(vmax
- (float)tPtr
->visibleH
),
978 (float)tPtr
->visibleH
/vmax
);
982 if(tPtr
->hasHscroller
)
987 scrollersCallBack(WMWidget
*w
, void *self
)
989 Text
*tPtr
= (Text
*)self
;
993 if(!tPtr
->view
->flags
.realized
) return;
995 if(w
== tPtr
->vscroller
) {
998 vmax
= (float)(tPtr
->docHeight
);
999 height
= tPtr
->visibleH
;
1001 height
-= 7; /* the top border (5) + bottom (2) */
1003 switch(WMGetScrollerHitPart(tPtr
->vscroller
)) {
1004 case WSDecrementLine
:
1005 if(tPtr
->vpos
> 0) {
1006 if(tPtr
->vpos
>16) tPtr
->vpos
-=16;
1010 case WSIncrementLine
: {
1011 int limit
= tPtr
->docHeight
- height
;
1012 if(tPtr
->vpos
< limit
) {
1013 if(tPtr
->vpos
<limit
-16) tPtr
->vpos
+=16;
1014 else tPtr
->vpos
=limit
;
1017 case WSDecrementPage
:
1018 tPtr
->vpos
-= height
;
1024 printf("dimple needs to jump to mouse location ;-/\n");
1026 case WSIncrementPage
:
1027 tPtr
->vpos
+= height
;
1028 if(tPtr
->vpos
> (tPtr
->docHeight
- height
))
1029 tPtr
->vpos
= tPtr
->docHeight
- height
;
1032 printf("dimple needs to jump to mouse location ;-/\n");
1037 tPtr
->vpos
= WMGetScrollerValue(tPtr
->vscroller
)
1038 * (float)(tPtr
->docHeight
- height
);
1045 float vmax
= (float)(tPtr
->docHeight
);
1046 ((float)tPtr
->vpos
)/(vmax
- (float)tPtr
->visibleH
),
1047 (float)tPtr
->visibleH
/vmax
);
1048 dimple
=where mouse is
.
1052 scroll
= (tPtr
->vpos
!= tPtr
->prevVpos
);
1053 tPtr
->prevVpos
= tPtr
->vpos
;
1056 if(w
== tPtr
->hscroller
)
1059 //need scrollv || scrollh
1063 if(tPtr->rulerShown)
1064 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
1065 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
1067 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
1068 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
1071 updateScrollers(tPtr
);
1072 drawDocumentPartsOnPixmap(tPtr
, False
);
1079 W_InsertText(WMText
*tPtr
, void *data
, int position
)
1083 Paragraph
*para
= tPtr
->paragraphs
, *ptmp
;
1084 Chunk
*chunk
, *ctmp
;
1087 chunk
= para
->chunks
;
1089 if(chunk
->type
== ctText
&& chunk
->text
)
1090 wgdbFree(chunk
->text
);
1091 else if(chunk
->pixmap
)
1092 WMReleasePixmap(chunk
->pixmap
);
1094 chunk
= chunk
->next
;
1099 if(ptmp
->drawbuffer
)
1100 XFreePixmap(tPtr
->view
->screen
->display
, ptmp
->drawbuffer
);
1103 tPtr
->paragraphs
= NULL
;
1104 tPtr
->currentPara
= NULL
;
1105 tPtr
->currentChunk
= NULL
;
1107 WMRefreshText(tPtr
, 0, 0);
1112 (tPtr
->parser
)(tPtr
, data
, position
>= 0 ? 1 : 0);
1114 defaultParser(tPtr
, data
, position
>= 0 ? 1 : 0);
1118 cursorToTextPosition(Text
*tPtr
, int x
, int y
)
1120 Paragraph
*para
= NULL
;
1121 Chunk
*chunk
= NULL
;
1124 short orig_x
, orig_y
;
1126 if(x
<(tPtr
->hasVscroller
?21:1)) {
1127 y
-= tPtr
->clheight
;
1128 x
= tPtr
->view
->size
.width
; //tPtr->visibleW;
1129 } else if(x
>tPtr
->clwidth
&& x
<tPtr
->clicked
.x
) {
1130 //x = (tPtr->hasVscroller)?21:1;
1131 //y += tPtr->clheight;
1137 if(y
<0 || y
>tPtr
->view
->size
.height
-3) return;
1139 tPtr
->clicked
.x
= orig_x
;
1140 tPtr
->clicked
.y
= y
;
1141 tPtr
->clicked
.y
+= tPtr
->vpos
;
1142 tPtr
->clicked
.y
-= tPtr
->rulerShown
?40:0;
1143 para
= tPtr
->paragraphs
;
1146 if( tPtr
->clicked
.y
>= para
->top
-4 &&
1147 tPtr
->clicked
.y
< para
->bottom
+4) break;
1149 } if(!(tPtr
->currentPara
= para
)) return;
1151 tPtr
->clicked
.y
-= para
->top
;
1152 if(tPtr
->clicked
.y
<0) tPtr
->clicked
.y
=0;
1153 if(tPtr
->hasVscroller
) x
-= 21;
1156 tPtr
->findingClickPoint
= True
;
1157 tPtr
->foundClickPoint
= False
;
1158 /* also affects tPtr->currentChunk, tPtr->clicked.x and y,
1159 tPtr->clheight and ->width */
1160 putParagraphOnPixmap(tPtr
, para
, False
);
1161 tPtr
->findingClickPoint
= False
;
1162 tPtr
->clicked
.y
+= para
->top
;
1164 if(tPtr
->currentChunk
) {
1165 short _width
=0, start
=tPtr
->tpos
, done
=False
, w
=0;
1166 chunk
= tPtr
->currentChunk
;
1167 while(!done
&& chunk
&& line_width
<tPtr
->clwidth
) {
1168 if(chunk
->type
== ctText
) {
1169 font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
1170 for (w
=start
; w
<chunk
->chars
; w
++) {
1171 _width
= WMWidthOfString(font
, &chunk
->text
[w
], 1);
1172 line_width
+= _width
;
1173 if(line_width
+tPtr
->clicked
.x
>= x
) {
1174 line_width
-= _width
;
1180 if(0&&chunk
->next
) {
1181 if(chunk
->next
->type
== ctImage
) {
1182 if(x
+10 < line_width
+chunk
->next
->pixmap
->width
) {
1187 _width
= chunk
->pixmap
->width
;
1188 line_width
+= _width
;
1189 if(line_width
+tPtr
->clicked
.x
>= x
) {
1190 line_width
-= _width
;
1196 chunk
= chunk
->next
;
1200 tPtr
->currentChunk
= chunk
;
1203 short vS
= (tPtr
->hasVscroller
)?32:12;
1204 if(para
->align
== WARight
) {
1205 tPtr
->clicked
.x
= tPtr
->view
->size
.width
-vS
;
1206 } else if (para
->align
== WACenter
) {
1207 tPtr
->clicked
.x
= -(vS
/2)+(tPtr
->view
->size
.width
-vS
)/2;
1209 tPtr
->clicked
.x
= 2;
1212 tPtr
->cursor
.x
= tPtr
->clicked
.x
+2+line_width
;
1213 tPtr
->cursor
.y
= tPtr
->clicked
.y
;
1214 tPtr
->clicked
.y
= orig_y
;
1215 tPtr
->clicked
.x
= orig_x
;
1216 putParagraphOnPixmap(tPtr
, para
, True
);
1221 deleteTextInteractively(Text
*tPtr
, DeleteType type
)
1225 short pos
,w
=0,h
=0, doprev
=False
, doprevpara
=False
;
1227 int current
= WMGetTextCurrentChunk(tPtr
);
1229 if(!(para
= tPtr
->currentPara
)) return;
1230 if(!(chunk
= tPtr
->currentChunk
)) return;
1231 font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
1232 doprev
= (tPtr
->tpos
< 2);
1235 case dtDelete
: /* delete _after_ cursor ... implement later */
1236 case dtBackSpace
: /* delete _before_ cursor */
1237 if(chunk
->chars
> 1) {
1239 printf("here %d\n", pos
);
1241 w
= WMWidthOfString(font
, &chunk
->text
[pos
], 1);
1242 memmove(&(chunk
->text
[pos
]),
1243 &(chunk
->text
[pos
+1]), chunk
->chars
-pos
+1);
1247 WMRemoveTextChunk(tPtr
, current
);
1253 WMSetTextCurrentChunk(tPtr
, current
-1);
1254 if(!tPtr
->currentChunk
) {
1255 printf("PREV PARA\n");
1257 tPtr
->tpos
= tPtr
->currentChunk
->chars
;
1260 int currentp
= WMGetTextCurrentParagraph(tPtr
);
1263 para
->chunks
= NULL
;
1264 WMRemoveTextParagraph(tPtr
, currentp
);
1265 WMSetTextCurrentParagraph(tPtr
, currentp
-1);
1266 WMSetTextCurrentChunk(tPtr
, -1);
1267 para
= tPtr
->currentPara
;
1269 if(!tPtr
->currentChunk
|| !para
->chunks
) {
1270 para
->chunks
= chunk
;
1271 tPtr
->currentChunk
= chunk
;
1273 tPtr
->currentChunk
->next
= chunk
;
1276 if(1) { //if(1||(para && !doprevpara)) {
1277 affectNextParas(tPtr
, para
, -23);
1278 putParagraphOnPixmap(tPtr
, para
, True
);
1279 drawDocumentPartsOnPixmap(tPtr
, False
);
1280 updateScrollers(tPtr
);
1282 //cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1283 } else WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1287 /* give us nice chunk sizes (multiples of 16) */
1289 reqBlockSize(short requested
)
1291 return requested
+16-(requested
%16);
1295 insertTextInteractively(Text
*tPtr
, char *text
)
1297 Paragraph
*para
=NULL
;
1298 Chunk
*chunk
=NULL
, *newchunk
=NULL
;
1299 int height
= -23; /* should only be changed upon newline */
1303 if(!tPtr
->editable
) return;
1304 if(*text
== '\n' && tPtr
->ignoreNewLine
)
1307 para
= tPtr
->currentPara
;
1308 chunk
= tPtr
->currentChunk
;
1309 font
= (tPtr
->monoFont
|| !chunk
)?tPtr
->dFont
:chunk
->font
;
1313 if(chunk
) { /* there's a chunk (or part of it) to detach from old */
1314 int current
= WMGetTextCurrentChunk(tPtr
);
1315 if(tPtr
->tpos
<=0) { /* at start of chunk */
1316 if(current
<1) { /* the first chunk... make old para blank */
1317 newchunk
= para
->chunks
;
1318 para
->chunks
= NULL
;
1319 putParagraphOnPixmap(tPtr
, para
, True
);
1320 } else { /* not first chunk... */
1321 printf("cut me out \n");
1323 } else if(tPtr
->tpos
< chunk
->chars
&& chunk
->type
== ctText
) {
1324 /* not at start of chunk */
1325 char text
[chunk
->chars
-tPtr
->tpos
+1];
1328 text
[i
] = chunk
->text
[tPtr
->tpos
+i
];
1329 } while(++i
< chunk
->chars
-tPtr
->tpos
);
1331 newchunk
= (tPtr
->funcs
.createTChunk
) (text
, i
, chunk
->font
,
1332 chunk
->color
, chunk
->script
, chunk
->ul
);
1333 newchunk
->next
= chunk
->next
;
1335 /* might want to demalloc for LARGE cuts */
1336 //calcParaExtents(tPtr, para);
1337 para
->height
= putParagraphOnPixmap(tPtr
, para
, True
);
1338 //putParagraphOnPixmap(tPtr, para, True);
1339 } else if(tPtr
->tpos
>= chunk
->chars
) {
1341 WMSetTextCurrentChunk(tPtr
, current
-1);
1342 prev
= tPtr
->currentChunk
;
1344 newchunk
= prev
->next
;
1346 putParagraphOnPixmap(tPtr
, para
, True
);
1348 } else newchunk
= NULL
;
1350 if(para
) /* the preceeding one */
1351 new_top
= para
->bottom
;
1353 WMAppendTextStream(tPtr
, "\n");
1354 para
= tPtr
->currentPara
;
1356 para
->chunks
= newchunk
;
1357 tPtr
->currentChunk
= newchunk
;
1359 para
->top
= new_top
;
1360 calcParaExtents(tPtr
, para
);
1361 height
= para
->height
;
1364 WMAppendTextStream(tPtr
, text
);
1365 para
= tPtr
->currentPara
;
1366 } else if(!para
->chunks
|| !chunk
) {
1367 //WMPrependTextStream(tPtr, text);
1368 WMAppendTextStream(tPtr
, text
);
1369 } else if(chunk
->type
== ctImage
) {
1370 WMPrependTextStream(tPtr
, text
);
1372 printf("\n\nprepe\n\n");
1374 if(tPtr
->tpos
> chunk
->chars
) {
1375 printf("\n\nmore\n\n");
1376 tPtr
->tpos
= chunk
->chars
;
1379 if(chunk
->chars
+1 >= chunk
->mallocedSize
) {
1380 chunk
->mallocedSize
= reqBlockSize(chunk
->chars
+1);
1381 chunk
->text
= wrealloc(chunk
->text
, chunk
->mallocedSize
);
1384 memmove(&(chunk
->text
[tPtr
->tpos
+1]), &chunk
->text
[tPtr
->tpos
],
1385 chunk
->chars
-tPtr
->tpos
+1);
1386 w
= WMWidthOfString(font
, text
, 1);
1387 memmove(&chunk
->text
[tPtr
->tpos
], text
, 1);
1390 //doc->clickstart.cursor.x +=
1391 //WMWidthOfString(chunk->fmt->font, text,len);
1397 affectNextParas(tPtr
, para
, height
);
1398 putParagraphOnPixmap(tPtr
, para
, True
);
1399 drawDocumentPartsOnPixmap(tPtr
, False
);
1400 updateScrollers(tPtr
);
1402 //cursorToTextPosition(tPtr, tPtr->clicked.x+w, tPtr->clicked.y);
1403 //check for "sneppah tahw" with blank paras...
1410 selectRegion(Text
*tPtr
, int x
, int y
)
1412 tPtr
->sRect
.pos
.x
= WMIN(tPtr
->clicked
.x
, x
);
1413 tPtr
->sRect
.size
.width
= abs(tPtr
->clicked
.x
-x
);
1414 tPtr
->sRect
.pos
.y
= WMIN(tPtr
->clicked
.y
, y
);
1415 if(tPtr
->sRect
.pos
.y
<0) tPtr
->sRect
.pos
.y
=0;
1416 tPtr
->sRect
.size
.height
= abs(tPtr
->clicked
.y
-y
);
1419 while(y>tPtr->visibleH && tPtr->vpos < tPtr->docHeight-tPtr->visibleH) {
1420 WMRefreshText(tPtr, tPtr->vpos+16, tPtr->hpos);
1423 //printf("%d %d \n", y, tPtr->vpos);
1425 //foreach para in selection...
1426 drawDocumentPartsOnPixmap(tPtr
, True
);
1434 #define WM_EMACSKEYMASK ControlMask
1435 #define WM_EMACSKEY_LEFT XK_b
1436 #define WM_EMACSKEY_RIGHT XK_f
1437 #define WM_EMACSKEY_HOME XK_a
1438 #define WM_EMACSKEY_END XK_e
1439 #define WM_EMACSKEY_BS XK_h
1440 #define WM_EMACSKEY_DEL XK_d
1443 handleTextKeyPress(Text
*tPtr
, XEvent
*event
)
1447 int control_pressed
= False
;
1449 if(!tPtr
->editable
) return;
1451 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
)
1452 control_pressed
= True
;
1453 buffer
[XLookupString(&event
->xkey
, buffer
, 1, &ksym
, NULL
)] = '\0';
1459 if(tPtr
->currentChunk
) {
1461 Chunk
*chunk
= tPtr
->currentChunk
;
1462 if(chunk
->type
== ctText
) {
1463 WMFont
*font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
1464 if(ksym
==XK_Right
) {
1465 short pos
= (tPtr
->tpos
<chunk
->chars
)?tPtr
->tpos
+1:
1467 w
= WMWidthOfString(font
,&chunk
->text
[pos
],1);
1469 short pos
= (tPtr
->tpos
>0)?tPtr
->tpos
-1:0;
1470 w
= WMWidthOfString(font
,&chunk
->text
[pos
],1);
1472 } else { w
= chunk
->pixmap
->width
; }
1473 if(ksym
==XK_Right
) w
= -w
;
1474 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
-w
, tPtr
->clicked
.y
);
1476 if(ksym
==XK_Right
) ksym
= XK_Down
;
1484 noCChunk
: { short h
= tPtr
->clheight
-2;
1485 if(ksym
==XK_Down
) h
= -h
;
1486 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
, tPtr
->clicked
.y
-h
);
1490 deleteTextInteractively(tPtr
, dtBackSpace
);
1495 deleteTextInteractively(tPtr
, dtDelete
);
1501 if(buffer
[0] != '\0' && (buffer
[0] == '\n' || !iscntrl(buffer
[0])))
1502 insertTextInteractively(tPtr
, buffer
);
1503 else if(control_pressed
&& ksym
==XK_r
)
1504 {Bool i
= !tPtr
->rulerShown
; WMShowTextRuler(tPtr
, i
);
1505 tPtr
->rulerShown
= i
; }
1513 pasteText(WMView
*view
, Atom selection
, Atom target
, Time timestamp
,
1514 void *cdata
, WMData
*data
)
1516 Text
*tPtr
= (Text
*)view
->self
;
1520 tPtr
->waitingForSelection
= False
;
1522 str
= (char*)WMDataBytes(data
);
1523 if(tPtr
->tpos
<1) WMPrependTextStream(tPtr
, str
);
1524 else WMAppendTextStream(tPtr
, str
);
1525 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1528 str
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1531 if(tPtr
->tpos
<1) WMPrependTextStream(tPtr
, str
);
1532 else WMAppendTextStream(tPtr
, str
);
1534 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1541 releaseSelection(Text
*tPtr
)
1543 Paragraph
*para
= tPtr
->paragraphs
;
1546 chunk
= para
->chunks
;
1548 chunk
->selected
= False
;
1549 chunk
= chunk
->next
;
1553 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
1554 tPtr
->ownsSelection
= False
;
1555 drawDocumentPartsOnPixmap(tPtr
, True
);
1561 requestHandler(WMView
*view
, Atom selection
, Atom target
,
1562 void *cdata
, Atom
*type
)
1564 Text
*tPtr
= view
->self
;
1566 Display
*dpy
= tPtr
->view
->screen
->display
;
1568 Atom TEXT
= XInternAtom(dpy
, "TEXT", False
);
1569 Atom COMPOUND_TEXT
= XInternAtom(dpy
, "COMPOUND_TEXT", False
);
1570 WMData
*data
= NULL
;
1573 if(!tPtr
->ownsSelection
|| !tPtr
->paragraphs
) return NULL
;
1574 //printf("got here\n");
1576 if (target
== XA_STRING
|| target
== TEXT
|| target
== COMPOUND_TEXT
) {
1577 //for bleh in selection...
1579 Paragraph
*para
= tPtr
->paragraphs
;
1580 Chunk
*chunk
= NULL
;
1581 char pixmap
[] = "[pixmap]";
1586 chunk
= para
->chunks
;
1589 if(chunk
->selected
&& chunk
->type
== ctText
) {
1590 len
= chunk
->chars
; //chunk->sEnd - chunk->sStart;
1594 memcpy(s
, &chunk
->text
[0*chunk
->sStart
], len
);
1597 data
= WMCreateDataWithBytes(s
, strlen(s
));
1600 printf("append: %c %d\n", *s
, strlen(s
));
1601 WMAppendDataBytes(data
, s
, strlen(s
));
1606 printf("len is %d [%d %d] %d \n", len
, chunk
->sStart
, chunk
->sEnd
,
1609 chunk
= chunk
->next
;
1615 WMSetDataFormat(data
, 8);
1622 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
1623 if (target
== _TARGETS
) {
1624 Atom
*ptr
= wmalloc(4 * sizeof(Atom
));
1628 ptr
[3] = COMPOUND_TEXT
;
1630 data
= WMCreateDataWithBytes(ptr
, 4*4);
1631 WMSetDataFormat(data
, 32);
1644 lostHandler(WMView
*view
, Atom selection
, void *cdata
)
1646 WMText
*tPtr
= (WMText
*)view
->self
;
1647 releaseSelection(tPtr
);
1650 static WMSelectionProcs selectionHandler
= {
1651 requestHandler
, lostHandler
, NULL
};
1654 _notification(void *observerData
, WMNotification
*notification
)
1656 WMText
*to
= (WMText
*)observerData
;
1657 WMText
*tw
= (WMText
*)WMGetNotificationClientData(notification
);
1658 if (to
!= tw
) lostHandler(to
->view
, XA_PRIMARY
, NULL
);
1662 handleTextEvents(XEvent
*event
, void *data
)
1664 Text
*tPtr
= (Text
*)data
;
1665 Display
*dpy
= event
->xany
.display
;
1667 if(tPtr
->waitingForSelection
) return;
1669 switch (event
->type
) {
1671 if(!tPtr
->editable
|| tPtr
->buttonHeld
) {
1675 if(tPtr
->ownsSelection
) releaseSelection(tPtr
);
1676 //if (tPtr->waitingForSelection) return;
1679 XGrabPointer(dpy
, W_VIEW(tPtr
)->window
, False
,
1680 PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
1681 GrabModeAsync
, GrabModeAsync
, None
,
1682 W_VIEW(tPtr
)->screen
->invisibleCursor
, CurrentTime
);
1683 tPtr
->pointerGrabbed
= True
;
1685 handleTextKeyPress(tPtr
, event
);
1689 if(tPtr
->pointerGrabbed
) {
1690 tPtr
->pointerGrabbed
= False
;
1691 XUngrabPointer(dpy
, CurrentTime
);
1693 if((event
->xmotion
.state
& Button1Mask
)) {
1694 selectRegion(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
1695 if(!tPtr
->ownsSelection
) {
1696 WMCreateSelectionHandler(tPtr
->view
, XA_PRIMARY
,
1697 event
->xbutton
.time
, &selectionHandler
, NULL
);
1698 tPtr
->ownsSelection
= True
;
1703 if(event
->xbutton
.button
== Button1
) {
1704 if(tPtr
->ownsSelection
) releaseSelection(tPtr
);
1705 cursorToTextPosition(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
1706 if (tPtr
->pointerGrabbed
) {
1707 tPtr
->pointerGrabbed
= False
;
1708 XUngrabPointer(dpy
, CurrentTime
);
1712 if(!tPtr
->focused
) {
1713 WMSetFocusToWidget(tPtr
);
1714 tPtr
->focused
= True
;
1717 if(event
->xbutton
.button
== 4)
1718 WMScrollText(tPtr
, -16);
1719 else if(event
->xbutton
.button
== 5)
1720 WMScrollText(tPtr
, 16);
1725 tPtr
->buttonHeld
= False
;
1726 if (tPtr
->pointerGrabbed
) {
1727 tPtr
->pointerGrabbed
= False
;
1728 XUngrabPointer(dpy
, CurrentTime
);
1731 if(event
->xbutton
.button
== 4 || event
->xbutton
.button
== 5)
1733 if(event
->xbutton
.button
== Button2
&& tPtr
->editable
) {
1736 if(!WMRequestSelection(tPtr
->view
, XA_PRIMARY
, XA_STRING
,
1737 event
->xbutton
.time
, pasteText
, NULL
)) {
1738 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1741 WMAppendTextStream(tPtr
, text
);
1743 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1744 } else tPtr
->waitingForSelection
= True
;
1753 handleNonTextEvents(XEvent
*event
, void *data
)
1755 Text
*tPtr
= (Text
*)data
;
1757 switch(event
->type
) {
1759 if(!event
->xexpose
.count
&& tPtr
->view
->flags
.realized
)
1764 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
1766 tPtr
->focused
= True
;
1767 //cursor...paintText(tPtr);
1771 tPtr
->focused
= False
;
1772 //cursor...paintText(tPtr);
1777 //for(...)WMRemoveTextParagraph(tPtr, para);
1782 //printf("handleNonTextEvents\n");
1788 rulerCallBack(WMWidget
*w
, void *self
)
1790 Text
*tPtr
= (Text
*)self
;
1793 if(tPtr
->currentPara
) {
1794 Paragraph
*para
= tPtr
->currentPara
;
1795 para
->fmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerFirst
);
1796 para
->bmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerBody
);
1797 para
->rmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
);
1798 affectNextParas(tPtr
, para
, -23);
1799 putParagraphOnPixmap(tPtr
, para
, True
);
1802 which
= WMGetReleasedRulerMargin(tPtr
->ruler
);
1803 if(which
!= WRulerDocLeft
&& which
!= WRulerRight
1804 /* && Selection.para.count > 0 */ ) {
1806 "//for(i=0; i<Selection.para.count; i++) {"
1808 "//calcParaExtents(tPtr, para);}\n");
1810 WMRefreshText(tPtr
, 0, 0);
1817 rulerMoveCallBack(WMWidget
*w
, void *self
)
1819 Text
*tPtr
= (Text
*)self
;
1820 short rmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
);
1823 if(WMGetGrabbedRulerMargin(tPtr
->ruler
) == WRulerLeft
) {
1824 short lmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
1825 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
1826 22, 42, lmargin
-21, tPtr
->visibleH
, True
);
1827 } else if(WMGetGrabbedRulerMargin(tPtr
->ruler
) == WRulerRight
&&
1828 tPtr
->docWidth
+11 < rmargin
) {
1829 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
1830 rmargin
-3, 42, 10, tPtr
->visibleH
, True
);
1837 /* ------------- non-static functions that are "friends" ------------- */
1838 /* ------------- called as (tPtr->funcs.foo)(bars...) ------------- */
1840 /* create a new paragraph. Don't do anything with it just yet */
1843 createParagraph(short fmargin
, short bmargin
, short rmargin
,
1844 short *tabstops
, short numTabs
, WMAlignment alignment
)
1846 Paragraph
*para
= wmalloc(sizeof(Paragraph
));
1847 if(!para
) return NULL
;
1849 para
->chunks
= NULL
;
1853 para
->fmargin
= (fmargin
>=0)?fmargin
:0;
1854 para
->bmargin
= (bmargin
>=0)?bmargin
:0;
1855 if(rmargin
-bmargin
>= 100 && rmargin
-fmargin
>= 100)
1856 para
->rmargin
= rmargin
;
1858 para
->rmargin
= 100;
1859 para
->tabstops
= tabstops
;
1860 para
->numTabs
= (tabstops
)?numTabs
:0;
1862 para
->drawbuffer
= (Pixmap
)NULL
;
1863 para
->bulletPix
= NULL
;
1864 para
->top
= para
->bottom
= 0;
1865 para
->width
= para
->height
= 0;
1867 para
->align
= alignment
;
1872 /* insert the new paragraph in the tPtr, either right before
1873 or after the currentPara. It's the responsibility of the
1874 calling code to set what currentPara is. via WMSetTextCurrentParagraph.
1875 If currentPara is not set, set it as the first in the document.
1876 This function then sets currentPara as _this_ paragraph.
1877 NOTE: this means careless parser implementors might lose previous
1878 paragraphs... but this keeps stuff small and non-buggy :-) */
1880 insertParagraph(WMText
*tPtr
, void *v
, InsertType type
)
1881 //insertParagraph(WMText *tPtr, Paragraph *para, InsertType type)
1884 Paragraph
*para
= (Paragraph
*)v
;
1885 if(!para
|| !tPtr
) return;
1887 if(!tPtr
->currentPara
) {
1888 tPtr
->paragraphs
= para
;
1890 tmp
= tPtr
->paragraphs
;
1891 if(type
== itAppend
) {
1892 while(tmp
->next
&& tmp
!= tPtr
->currentPara
)
1895 para
->next
= tmp
->next
;
1897 } else { /* must be prepend */
1898 /* this "prior" member is that "doing things the hard way"
1899 I spoke of. See? it's not too bad afterall... */
1900 Paragraph
*prior
= NULL
;
1901 while(tmp
->next
&& tmp
!= tPtr
->currentPara
) {
1905 /* if this is the first */
1906 if(tmp
== tPtr
->paragraphs
) {
1908 tPtr
->paragraphs
= para
;
1913 tPtr
->currentPara
= para
;
1917 /* create a new chunk to contain exactly ONE pixmap */
1920 createPChunk(WMPixmap
*pixmap
, short script
, ushort ul
)
1924 chunk
= wmalloc(sizeof(Chunk
));
1930 chunk
->pixmap
= NULL
; /* if it's NULL, we'll draw the "broken" pixmap... */
1931 else chunk
->pixmap
= WMRetainPixmap(pixmap
);
1933 chunk
->mallocedSize
= 0;
1934 chunk
->type
= ctImage
;
1936 chunk
->color
= NULL
;
1937 chunk
->script
= script
;
1939 chunk
->selected
= False
;
1945 /* create a new chunk to contain some text with the given format */
1948 createTChunk(char *text
, short chars
, WMFont
*font
,
1949 WMColor
*color
, short script
, ushort ul
)
1953 if(!text
|| chars
<0 || !font
|| !color
) return NULL
;
1954 chunk
= wmalloc(sizeof(Chunk
));
1955 if(!chunk
) return NULL
;
1957 chunk
->mallocedSize
= reqBlockSize(chars
);
1958 chunk
->text
= (char *)wmalloc(chunk
->mallocedSize
);
1959 memcpy(chunk
->text
, text
, chars
);
1960 chunk
->pixmap
= NULL
;
1961 chunk
->chars
= chars
;
1962 chunk
->type
= ctText
;
1963 chunk
->font
= WMRetainFont(font
);
1964 chunk
->color
= WMRetainColor(color
);
1965 chunk
->script
= script
;
1967 chunk
->selected
= False
;
1973 /* insert the new chunk in the paragraph, either right before
1974 or after the currentChunk. It's the responsibility of the
1975 calling code to set what currentChunk is via WMSetTextCurrentChunk.
1976 If currentChunk is not set, set it as the first in the existing
1977 paragraph... if not even that, you lose... try again.
1978 This function then sets currentChunk as _this_ chunk.
1979 NOTE: this means careless parser implementors might lose previous
1980 paragraphs/chunks... but this keeps stuff small and non-buggy :-) */
1982 insertChunk(WMText
*tPtr
, void *v
, InsertType type
)
1985 Chunk
*chunk
= (Chunk
*)v
;
1987 if(!tPtr
|| !chunk
) return;
1989 if(!tPtr
->paragraphs
) { /* i.e., first chunk via insertTextInteractively */
1990 Paragraph
*para
= (tPtr
->funcs
.createParagraph
) (0, 0, tPtr
->visibleW
,
1992 (tPtr
->funcs
.insertParagraph
) (tPtr
, para
, itAppend
);
1995 if(!tPtr
->currentPara
)
1997 if(!tPtr
->currentChunk
) { /* there is a current chunk */
1998 tPtr
->currentPara
->chunks
= chunk
;
1999 } else if(!tPtr
->currentPara
->chunks
) {
2000 /* but it's not of this paragraph */
2001 tPtr
->currentPara
->chunks
= chunk
;
2003 tmp
= tPtr
->currentPara
->chunks
;
2005 if(type
== itAppend
) {
2006 while(tmp
->next
&& tmp
!= tPtr
->currentChunk
)
2009 chunk
->next
= tmp
->next
;
2012 } else { /* must be prepend */
2013 /* this "prior" member is that "doing things the hard way"
2014 I spoke of. See? it's not too bad afterall... */
2015 Chunk
*prior
= NULL
;
2016 while(tmp
->next
&& tmp
!= tPtr
->currentChunk
) {
2020 /* if this is the first */
2021 if(tmp
== tPtr
->currentPara
->chunks
) {
2023 tPtr
->currentPara
->chunks
= chunk
;
2025 prior
->next
= chunk
;
2028 tPtr
->currentChunk
= chunk
;
2029 tPtr
->tpos
= chunk
->chars
;
2033 /* ------------- non-static functions (i.e., APIs) ------------- */
2034 /* ------------- called as WMVerbText[Subject] ------------- */
2036 #define DEFAULT_TEXT_WIDTH 250
2037 #define DEFAULT_TEXT_HEIGHT 200
2042 WMCreateText(WMWidget
*parent
)
2044 Text
*tPtr
= wmalloc(sizeof(Text
));
2046 perror("could not create text widget\n");
2049 memset(tPtr
, 0, sizeof(Text
));
2050 tPtr
->widgetClass
= WC_Text
;
2051 tPtr
->view
= W_CreateView(W_VIEW(parent
));
2053 perror("could not create text's view\n");
2057 tPtr
->view
->self
= tPtr
;
2058 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
2059 tPtr
->view
->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2060 W_ResizeView(tPtr
->view
, DEFAULT_TEXT_WIDTH
, DEFAULT_TEXT_HEIGHT
);
2061 tPtr
->bg
= tPtr
->view
->screen
->white
;
2062 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->bg
);
2065 tPtr
->ruler
= WMCreateRuler(tPtr
);
2066 (W_VIEW(tPtr
->ruler
))->attribs
.cursor
= tPtr
->view
->screen
->defaultCursor
;
2067 (W_VIEW(tPtr
->ruler
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2068 WMMoveWidget(tPtr
->ruler
, 0, 0);
2069 WMResizeWidget(tPtr
->ruler
, W_VIEW(parent
)->size
.width
, 40);
2070 WMShowRulerTabs(tPtr
->ruler
, True
);
2071 WMSetRulerAction(tPtr
->ruler
, rulerCallBack
, tPtr
);
2072 WMSetRulerMoveAction(tPtr
->ruler
, rulerMoveCallBack
, tPtr
);
2076 tPtr
->vscroller
= WMCreateScroller(tPtr
);
2077 (W_VIEW(tPtr
->vscroller
))->attribs
.cursor
=
2078 tPtr
->view
->screen
->defaultCursor
;
2079 (W_VIEW(tPtr
->vscroller
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2080 WMMoveWidget(tPtr
->vscroller
, 1, 1);
2081 WMResizeWidget(tPtr
->vscroller
, 20, tPtr
->view
->size
.height
- 2);
2082 WMSetScrollerArrowsPosition(tPtr
->vscroller
, WSAMaxEnd
);
2083 WMSetScrollerAction(tPtr
->vscroller
, scrollersCallBack
, tPtr
);
2087 tPtr
->hscroller
= WMCreateScroller(tPtr
);
2088 (W_VIEW(tPtr
->hscroller
))->attribs
.cursor
=
2089 tPtr
->view
->screen
->defaultCursor
;
2090 (W_VIEW(tPtr
->hscroller
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2091 WMMoveWidget(tPtr
->hscroller
, 1, tPtr
->view
->size
.height
-21);
2092 WMResizeWidget(tPtr
->hscroller
, tPtr
->view
->size
.width
- 2, 20);
2093 WMSetScrollerArrowsPosition(tPtr
->hscroller
, WSAMaxEnd
);
2094 WMSetScrollerAction(tPtr
->hscroller
, scrollersCallBack
, tPtr
);
2096 tPtr
->visibleW
= tPtr
->view
->size
.width
;
2097 tPtr
->visibleH
= tPtr
->view
->size
.height
;
2099 tPtr
->paragraphs
= NULL
;
2101 tPtr
->docHeight
= 0;
2102 tPtr
->dBulletPix
= WMCreatePixmapFromXPMData(tPtr
->view
->screen
,
2104 tPtr
->dUnknownImg
= WMCreatePixmapFromXPMData(tPtr
->view
->screen
,
2107 tPtr
->sRect
.pos
.x
= tPtr
->sRect
.pos
.y
= 0;
2108 tPtr
->sRect
.size
.width
= tPtr
->sRect
.size
.height
= 0;
2109 tPtr
->currentPara
= NULL
;
2110 tPtr
->currentChunk
= NULL
;
2113 tPtr
->parser
= NULL
;
2114 tPtr
->writer
= NULL
;
2115 tPtr
->funcs
.createParagraph
= createParagraph
;
2116 tPtr
->funcs
.insertParagraph
= insertParagraph
;
2117 tPtr
->funcs
.createPChunk
= createPChunk
;
2118 tPtr
->funcs
.createTChunk
= createTChunk
;
2119 tPtr
->funcs
.insertChunk
= insertChunk
;
2121 tPtr
->clicked
.x
= tPtr
->clicked
.y
= -23;
2122 tPtr
->cursor
.x
= tPtr
->cursor
.y
= -23;
2124 tPtr
->relief
= WRSunken
;
2125 tPtr
->wrapping
= wrWord
;
2126 tPtr
->editable
= False
;
2127 tPtr
->cursorShown
= False
;
2128 tPtr
->frozen
= False
;
2129 tPtr
->focused
= False
;
2130 tPtr
->pointerGrabbed
= False
;
2131 tPtr
->buttonHeld
= False
;
2132 tPtr
->ignoreNewLine
= False
;
2133 tPtr
->waitingForSelection
= False
;
2134 tPtr
->findingClickPoint
= False
;
2135 tPtr
->foundClickPoint
= False
;
2136 tPtr
->ownsSelection
= False
;
2140 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2141 tPtr
->dColor
= WMBlackColor(tPtr
->view
->screen
);
2143 tPtr
->view
->delegate
= &_TextViewDelegate
;
2144 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
2145 |EnterWindowMask
|LeaveWindowMask
|FocusChangeMask
,
2146 handleNonTextEvents
, tPtr
);
2147 WMCreateEventHandler(tPtr
->view
, ButtonReleaseMask
|ButtonPressMask
2148 |KeyReleaseMask
|KeyPressMask
|Button1MotionMask
,
2149 handleTextEvents
, tPtr
);
2151 WMAddNotificationObserver(_notification
, tPtr
, "_lostOwnership", tPtr
);
2153 WMSetTextMonoFont(tPtr
, True
);
2154 WMShowTextRuler(tPtr
, False
);
2155 WMSetTextHasHorizontalScroller(tPtr
, False
);
2156 WMSetTextHasVerticalScroller(tPtr
, True
);
2157 //printf("the sizeof chunk is %d\n", sizeof(Chunk));
2158 //printf("the sizeof para is %d\n", sizeof(Paragraph));
2159 //printf("the sizeof text is %d\n", sizeof(Text));
2164 //WRetainPixmap(tPtr->dBulletPix);
2167 WMRemoveTextParagraph(WMText
*tPtr
, int which
)
2169 Paragraph
*prior
, *removed
;
2170 if(!tPtr
|| which
<0) return;
2172 WMSetTextCurrentParagraph(tPtr
, which
);
2173 removed
= tPtr
->currentPara
;
2174 if(!removed
) return;
2175 if(removed
->chunks
)printf("WMRemoveTextChunks\n");
2176 if(removed
== tPtr
->paragraphs
|| which
==0) {
2177 tPtr
->paragraphs
= removed
->next
;
2179 WMSetTextCurrentParagraph(tPtr
, which
-1);
2180 prior
= tPtr
->currentPara
;
2182 prior
->next
= removed
->next
;
2191 /* set what is known as the currentPara in the tPtr. */
2192 /* negative number means: "gib me last chunk" */
2194 WMSetTextCurrentParagraph(WMText
*tPtr
, int current
)
2199 if(!tPtr
|| current
<0) return;
2201 tPtr
->currentPara
= tPtr
->paragraphs
;
2204 tmp
= tPtr
->paragraphs
;
2205 while(tmp
->next
&& ((current
==-23)?1:i
++<current
)) {
2206 //while(tmp && i++<current) {
2208 } tPtr
->currentPara
= tmp
;
2209 //? want to do this?if(tmp) tPtr->currentChunk = tmp
2214 WMGetTextParagraphs(WMText
*tPtr
)
2219 tmp
= tPtr
->paragraphs
;
2229 WMGetTextCurrentParagraph(WMText
*tPtr
)
2234 if(!tPtr
) return current
;
2235 if(!tPtr
->currentPara
) return current
;
2236 if(!tPtr
->paragraphs
) return current
;
2237 tmp
= tPtr
->paragraphs
;
2240 if(tmp
== tPtr
->currentPara
)
2246 /* set what is known as the currentChunk within the currently
2247 selected currentPara (or the first paragraph in the document). */
2249 WMSetTextCurrentChunk(WMText
*tPtr
, int current
)
2255 tPtr
->currentChunk
= NULL
;
2256 if(!tPtr
->currentPara
) {
2257 tPtr
->currentPara
= tPtr
->paragraphs
;
2258 if(!tPtr
->currentPara
)
2263 tPtr
->currentChunk
= tPtr
->currentPara
->chunks
;
2266 tmp
= tPtr
->currentPara
->chunks
;
2268 while(tmp
->next
&& ((current
<0)?1:i
++<current
))
2270 } tPtr
->currentChunk
= tmp
;
2275 WMRemoveTextChunk(WMText
*tPtr
, int which
)
2277 Chunk
*prior
, *removed
;
2279 if(!tPtr
|| which
<0) return;
2280 para
= tPtr
->currentPara
;
2283 WMSetTextCurrentChunk(tPtr
, which
);
2284 removed
= tPtr
->currentChunk
;
2285 if(!removed
) return;
2286 if(removed
== tPtr
->currentPara
->chunks
|| which
==0) {
2287 para
->chunks
= removed
->next
;
2289 WMSetTextCurrentChunk(tPtr
, which
-1);
2290 prior
= tPtr
->currentChunk
;
2292 prior
->next
= removed
->next
;
2294 if(removed
->type
== ctText
) {
2295 wgdbFree(removed
->text
);
2296 WMReleaseFont(removed
->font
);
2297 WMReleaseColor(removed
->color
);
2299 WMReleasePixmap(removed
->pixmap
);
2306 WMGetTextCurrentChunk(WMText
*tPtr
)
2312 if(!tPtr
->currentChunk
) return 0;
2313 if(!tPtr
->currentPara
) {
2314 tPtr
->currentPara
= tPtr
->paragraphs
;
2315 if(!tPtr
->currentPara
)
2319 tmp
= tPtr
->currentPara
->chunks
;
2321 if(tmp
== tPtr
->currentChunk
)
2330 WMGetTextChunks(WMText
*tPtr
)
2334 if(!tPtr
|| !tPtr
->currentPara
) return 0;
2335 tmp
= tPtr
->currentPara
->chunks
;
2343 WMShowTextRuler(WMText
*tPtr
, Bool show
)
2346 if(tPtr
->monoFont
) show
= False
;
2348 tPtr
->rulerShown
= show
;
2349 if(show
) WMMapWidget(tPtr
->ruler
);
2350 else WMUnmapWidget(tPtr
->ruler
);
2351 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2355 WMGetTextRulerShown(WMText
*tPtr
)
2357 if(!tPtr
) return False
;
2358 return tPtr
->rulerShown
;
2362 WMSetTextRulerMargin(WMText
*tPtr
, char which
, short pixels
)
2365 if(tPtr
->monoFont
) return;
2366 WMSetRulerMargin(tPtr
->ruler
, which
, pixels
);
2367 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2371 WMGetTextRulerMargin(WMText
*tPtr
, char which
)
2376 return WMGetRulerMargin(tPtr
->ruler
, which
);
2381 WMShowTextRulerTabs(WMText
*tPtr
, Bool show
)
2384 if(tPtr
->monoFont
) return;
2385 WMShowRulerTabs(tPtr
->ruler
, show
);
2389 WMSetTextMonoFont(WMText
*tPtr
, Bool mono
)
2392 if(mono
&& tPtr
->rulerShown
)
2393 WMShowTextRuler(tPtr
, False
);
2395 tPtr
->monoFont
= mono
;
2399 WMGetTextMonoFont(WMText
*tPtr
)
2401 if(!tPtr
) return True
;
2402 return tPtr
->monoFont
;
2406 WMForceTextFocus(WMText
*tPtr
)
2410 if(tPtr
->clicked
.x
== -23 || tPtr
->clicked
.y
== 23)
2411 cursorToTextPosition(tPtr
, 100, 100); /* anyplace */
2413 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
, tPtr
->clicked
.y
);
2418 WMSetTextEditable(WMText
*tPtr
, Bool editable
)
2421 tPtr
->editable
= editable
;
2426 WMGetTextEditable(WMText
*tPtr
)
2429 return tPtr
->editable
;
2434 WMScrollText(WMText
*tPtr
, int amount
)
2437 if(amount
== 0 || !tPtr
) return;
2438 if(!tPtr
->view
->flags
.realized
) return;
2441 if(tPtr
->vpos
> 0) {
2442 if(tPtr
->vpos
> amount
) tPtr
->vpos
+= amount
;
2446 int limit
= tPtr
->docHeight
- tPtr
->visibleH
;
2447 if(tPtr
->vpos
< limit
) {
2448 if(tPtr
->vpos
< limit
-amount
) tPtr
->vpos
+= amount
;
2449 else tPtr
->vpos
= limit
;
2453 if(scroll
&& tPtr
->vpos
!= tPtr
->prevVpos
) {
2454 updateScrollers(tPtr
);
2455 drawDocumentPartsOnPixmap(tPtr
, False
);
2458 tPtr
->prevVpos
= tPtr
->vpos
;
2463 WMPageText(WMText
*tPtr
, Bool scrollUp
)
2466 if(!tPtr
->view
->flags
.realized
) return;
2468 return WMScrollText(tPtr
, scrollUp
2469 ? tPtr
->visibleH
:-tPtr
->visibleH
);
2473 WMIgnoreTextNewline(WMText
*tPtr
, Bool ignore
)
2476 tPtr
->ignoreNewLine
= ignore
;
2481 WMSetTextHasHorizontalScroller(WMText
*tPtr
, Bool flag
)
2487 rh
= tPtr
->rulerShown
?40:0;
2488 tPtr
->hasHscroller
= flag
;
2490 WMMapWidget(tPtr
->hscroller
);
2491 tPtr
->visibleH
= tPtr
->view
->size
.height
-rh
-22;
2493 WMUnmapWidget(tPtr
->hscroller
);
2494 tPtr
->visibleH
= tPtr
->view
->size
.height
-rh
;
2496 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2502 WMSetTextHasVerticalScroller(WMText
*tPtr
, Bool flag
)
2505 tPtr
->hasVscroller
= flag
;
2507 WMMapWidget(tPtr
->vscroller
);
2508 tPtr
->visibleW
= tPtr
->view
->size
.width
-22;
2509 WMSetRulerOffset(tPtr
->ruler
, 22); /* scrollbar width + 2 */
2511 WMUnmapWidget(tPtr
->vscroller
);
2512 tPtr
->visibleW
= tPtr
->view
->size
.width
;
2513 WMSetRulerOffset(tPtr
->ruler
, 2);
2515 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2522 WMRefreshText(WMText
*tPtr
, int vpos
, int hpos
)
2528 if(tPtr
->frozen
|| !tPtr
->view
->flags
.realized
)
2532 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
2533 22, (tPtr
->rulerShown
)?45:5,
2534 tPtr
->visibleW
, tPtr
->visibleH
, True
);
2536 calcDocExtents(tPtr
);
2538 printf("vpos:%d tPtr->docHeight%d tPtr->visibleH%d \n",
2539 vpos, tPtr->docHeight, tPtr->visibleH);
2542 // tPtr->vpos = vpos;
2544 if(vpos < 0 || tPtr->docHeight < tPtr->visibleH)
2546 else if(vpos-tPtr->visibleH>tPtr->docHeight)
2547 tPtr->vpos = vpos-tPtr->docHeight-tPtr->visibleH-tPtr->docHeight;
2549 tPtr->vpos = tPtr->docHeight-tPtr->visibleH;
2553 if(hpos
< 0 || hpos
> tPtr
->docWidth
)
2558 drawDocumentPartsOnPixmap(tPtr
, True
);
2559 updateScrollers(tPtr
);
2563 /* would be nice to have in WINGs proper... */
2565 changeFontProp(char *fname
, char *newprop
, short which
)
2567 char before
[128], prop
[128], after
[128];
2579 if(part
==which
) bptr
= prop
;
2580 else if(part
==which
+1) bptr
= after
;
2587 snprintf(fname
, 255, "%s-%s%s", before
, newprop
, after
);
2590 /* TODO: put in wfont? */
2592 WMGetFontPlain(WMScreen
*scrPtr
, WMFont
*font
)
2602 WMGetFontBold(WMScreen
*scrPtr
, WMFont
*font
)
2604 WMFont
*newfont
=NULL
;
2606 if(!scrPtr
|| !font
)
2608 snprintf(fname
, 255, font
->name
);
2609 changeFontProp(fname
, "bold", 2);
2610 newfont
= WMCreateNormalFont(scrPtr
, fname
);
2617 WMGetFontItalic(WMScreen
*scrPtr
, WMFont
*font
)
2619 WMFont
*newfont
=NULL
;
2621 if(!scrPtr
|| !font
)
2623 snprintf(fname
, 255, font
->name
);
2624 changeFontProp(fname
, "o", 3);
2625 newfont
= WMCreateNormalFont(scrPtr
, fname
);
2632 WMGetFontOfSize(WMScreen
*scrPtr
, WMFont
*font
, short size
)
2635 if(!scrPtr
|| !font
|| size
<1)
2642 WMFreezeText(WMText
*tPtr
)
2646 tPtr
->frozen
= True
;
2650 WMThawText(WMText
*tPtr
)
2654 tPtr
->frozen
= False
;
2659 WMSetTextDefaultAlignment(WMText
*tPtr
, WMAlignment alignment
)
2662 if(tPtr
->monoFont
) return;
2664 tPtr
->dAlignment
= alignment
;
2665 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2671 WMSetTextBackgroundColor(WMText
*tPtr
, WMColor
*color
)
2679 tPtr
->bg
= WMWhiteColor(tPtr
->view
->screen
);
2681 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->bg
);
2682 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2686 WMSetTextDefaultColor(WMText
*tPtr
, WMColor
*color
)
2692 tPtr
->dColor
= color
;
2694 tPtr
->dColor
= WMBlackColor(tPtr
->view
->screen
);
2698 WMSetTextDefaultFont(WMText
*tPtr
, WMFont
*font
)
2706 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2710 WMSetTextUseFixedPitchFont(Text
*tPtr
, Bool fixed
)
2715 tPtr
->dFont
= WMCreateFontSet(tPtr
->view
->screen
,
2716 "lucidasanstypewriter-12");
2718 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2719 tPtr
->fixedPitch
= fixed
;
2723 WMSetTextParser(WMText
*tPtr
, WMParseAction
*parser
)
2726 if(tPtr
->monoFont
) return;
2727 tPtr
->parser
= parser
;
2732 WMGetTextParserActions(WMText
*tPtr
)
2734 WMParserActions null
;
2735 if(!tPtr
) return null
;
2741 WMGetTextAll(WMText
*tPtr
)
2749 if(!tPtr
) return NULL
;
2751 para
= tPtr
->paragraphs
;
2753 chunk
= para
->chunks
;
2755 if(chunk
->type
== ctText
) {
2756 if(chunk
->text
) length
+= chunk
->chars
;
2758 printf("getting image \n");
2760 chunk
= chunk
->next
;
2763 if(tPtr
->ignoreNewLine
) break;
2764 length
+= 4; // newlines
2768 text
= wmalloc(length
+1);
2770 para
= tPtr
->paragraphs
;
2772 chunk
= para
->chunks
;
2774 if(chunk
->type
== ctText
) {
2776 snprintf(&text
[where
], chunk
->chars
+1, "%s", chunk
->text
);
2777 where
+= chunk
->chars
;
2780 printf("writing image \n");
2782 chunk
= chunk
->next
;
2784 if(tPtr
->ignoreNewLine
) break;
2785 snprintf(&text
[where
++], 2, "\n");
2787 } text
[where
] = '\0';
2793 WMSetTextWriter(WMText
*tPtr
, WMParseAction
*writer
)
2799 tPtr
->writer
= writer
;