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>
28 void wgdbFree(void *ptr
)
29 { if(!ptr
) printf("err... cannot ");
30 printf("gdbFree [%p]\n", ptr
);
35 typedef enum {ctText
=0, ctImage
=1} ChunkType
;
36 typedef enum { dtDelete
=0, dtBackSpace
} DeleteType
;
37 typedef enum {wrWord
=0, wrChar
=1, wrNone
=2} Wrapping
;
39 /* Why singly-linked and not say doubly-linked?
40 99% of the time (draw, append), the "prior"
41 member would have been a useless memory and CPU overhead,
42 and deletes _are_ relatively infrequent.
43 When the "prior" member needs to be used, the overhead of
44 doing things the hard way will be incurred... but seldomly. */
47 /* a Chunk is a singly-linked list of chunks containing:
48 o text with a given format
51 typedef struct _Chunk
{
52 char *text
; /* the text in the chunk */
53 WMPixmap
*pixmap
; /* OR the pixmap it holds */
54 short chars
; /* the number of characters in this chunk */
55 short mallocedSize
; /* the number of characters that can be held */
57 WMFont
*font
; /* the chunk's font */
58 WMColor
*color
; /* the chunk's color */
59 short ul
:1; /* underlined or not */
60 ChunkType type
:1; /* a "Text" or "Image" chunk */
61 short script
:4; /* script in points: negative for subscript */
67 struct _Chunk
*next
;/*the next member in this list */
73 /* a Paragraph is a singly-linked list of paragraphs containing:
74 o a list of chunks in that paragraph
75 o the formats for that paragraph
76 o its (draw) position relative to the entire document */
77 typedef struct _Paragraph
{
78 Chunk
*chunks
; /* the list of text and/or image chunks */
79 short fmargin
; /* the start position of the first line */
80 short bmargin
; /* the start positions of the rest of the lines */
81 short rmargin
; /* the end position of the entire paragraph */
82 short numTabs
; /* the number of tabstops */
83 short *tabstops
; /* an array of tabstops */
85 Pixmap drawbuffer
; /* the pixmap onto which the (entire)
86 paragraph will be drawn */
87 WMPixmap
*bulletPix
;/* the pixmap to use for bulleting */
88 int top
; /* the top of the paragraph relative to document */
89 int bottom
; /* the bottom of the paragraph relative to document */
90 int width
; /* the width of the paragraph */
91 int height
; /* the height of the paragraph */
92 WMAlignment align
:2;/* justification of this paragraph */
95 struct _Paragraph
*next
; /* the next member in this list */
99 static char *default_bullet
[] = {
101 " c None s None", ". c black",
102 "X c white", "o c #808080",
110 /* this is really a shrunk down version of the original
111 "broken" icon... I did not draw it, I simply shrunk it */
112 static char * unk_xpm
[] = {
114 " c None", ". c #0B080C", "+ c #13A015", "@ c #5151B8",
115 "# c #992719", "$ c #5B1C20", "% c #1DF51D", "& c #D1500D", "* c #2F304A",
116 "= c #0C6A0C", "- c #F2F1DE", "; c #D59131", "> c #B2B083", ", c #DD731A",
117 "' c #CC3113", ") c #828238", "! c #6A6A94",
118 "......!@@@@@@@....$$....",
119 "...@!@@@@@@@**...$#'....",
120 "..!!@@@@@@@@.......#....",
121 "..!@@@@@@@@@*.......$...",
122 ".!@@@#,,#*@@*..*>.*.#...",
123 "*@@@@#'',,@@@...---!....",
124 "!@@@@@*.#;*@@..!--->....",
125 "@@@@@@@@#,.@@..!----@...",
126 "!@@@@@@*#;'$...!----@...",
127 "*@@@@@@..'&;;#.)----)...",
128 ".@@@@@@..$..&'.>----)...",
129 ".@@@@@@**---,'>-----!...",
130 ".@@@@@@**---,'>-----@...",
131 "..@@@@@@@---;;;,;---....",
132 "..*@@@@*@--->#',;,-*.)..",
133 "........)---->)@;#!..>..",
134 ".....)----------;$..>)..",
135 "=%%%*.*!-------);..)-*..",
136 "=%%%%+...*)>!@*$,.>--...",
137 "*+++++++.......*$@-->...",
138 "............**@)!)>->...",
139 "........................",
140 "........................",
141 "........................"};
143 typedef struct W_Text
{
144 W_Class widgetClass
; /* the class number of this widget */
145 W_View
*view
; /* the view referring to this instance */
146 WMColor
*bg
; /* the background color to use when drawing */
148 WMRuler
*ruler
; /* the ruler subwiget to maipulate paragraphs */
150 WMScroller
*hscroller
; /* the horizontal scroller */
151 short hpos
; /* the current horizontal position */
152 short prevHpos
; /* the previous horizontal position */
154 WMScroller
*vscroller
; /* the vertical scroller */
155 int vpos
; /* the current vertical position */
156 int prevVpos
; /* the previous vertical position */
158 int visibleW
; /* the actual horizontal space available */
159 int visibleH
; /* the actual vertical space available */
161 Paragraph
*paragraphs
; /* the linked list of the paragraphs in the doc. */
162 int docWidth
; /* the width of the entire document */
163 int docHeight
; /* the height of the entire document */
165 WMFont
*dFont
; /* the default font */
166 WMColor
*dColor
; /* the default color */
167 WMPixmap
*dBulletPix
; /* the default pixmap for bullets */
168 WMPixmap
*dUnknownImg
; /* the pixmap for (missing/broken) images */
170 WMRect sRect
; /* the selected area */
171 Paragraph
*currentPara
; /* the current paragraph, in which actions occur */
172 Chunk
*currentChunk
; /* the current chunk, about which actions occur */
173 short tpos
; /* the cursor position (text position) */
174 WMParseAction
*parser
; /* what action to use to parse input text */
175 WMParseAction
*writer
; /* what action to use to write text */
176 WMParserActions funcs
; /* the "things" that parsers/writers might do */
177 XPoint clicked
; /* the position of the last mouse click */
178 XPoint cursor
; /* where the cursor is "placed" */
179 short clheight
; /* the height of the "line" clicked on */
180 short clwidth
; /* the width of the "line" clicked on */
182 WMReliefType relief
:2; /* the relief to display with */
183 Wrapping wrapping
:2; /* the type of wrapping to use in drawing */
184 WMAlignment dAlignment
:2;/* default justification */
185 ushort monoFont
:1; /* whether to ignore "rich" commands */
186 ushort fixedPitch
:1; /* assume each char in dFont is the same size */
187 ushort editable
:1; /* whether to accept user changes or not*/
188 ushort rulerShown
:1; /* whether the ruler is shown or not */
189 ushort cursorShown
:1; /* whether the cursor is currently being shown */
190 ushort frozen
:1; /* whether screen updates are to be made */
191 ushort focused
:1; /* whether this instance has input focus */
192 ushort pointerGrabbed
:1;/* whether this instance has the pointer */
193 ushort buttonHeld
:1; /* the user is still holding down the button */
194 ushort ignoreNewLine
:1; /* whether to ignore the newline character */
195 ushort waitingForSelection
:1; /* whether there is a pending paste event */
196 ushort ownsSelection
:1; /* whether it ownz the current selection */
197 ushort findingClickPoint
:1;/* whether the search for a clickpoint is on */
198 ushort foundClickPoint
:1;/* whether the clickpoint has been found */
199 ushort hasVscroller
:1; /* whether to enable the vertical scroller */
200 ushort hasHscroller
:1; /* whether to enable the horizontal scroller */
206 /* --------- static functions that are "private". don't touch :-) --------- */
209 /* max "characters per chunk that will be drawn at a time" */
210 #define MAX_WORD_LENGTH 100
213 #define MIN_DOC_WIDTH 200
214 typedef struct _LocalMargins
{
215 short left
, right
, first
, body
;
218 typedef struct _MyTextItems
{
219 char text
[MAX_WORD_LENGTH
+1];
223 Chunk
*chunk
;/* used for "click" events */
224 short start
; /* ditto... where in the chunk we start (ie. wrapped chunk) */
231 chunkSelectionRect(Text
*tPtr
, Paragraph
*para
, MyTextItems item
,
232 short y
, short j
, short lh
)
235 short type
=0; /* 0:none 1:partial: 2:all */
236 short rh
=(tPtr
->rulerShown
)?45:5;
238 WMFont
*font
= (tPtr
->monoFont
|| item
.chunk
->type
!= ctText
)?
239 tPtr
->dFont
:item
.chunk
->font
;
242 if(y
+para
->top
+rh
> tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
243 || y
+para
->top
+rh
+lh
< tPtr
->sRect
.pos
.y
)
246 if(item
.chunk
->type
== ctText
)
247 w
= WMWidthOfString(font
, item
.text
, item
.chars
);
248 else w
= item
.chunk
->pixmap
->width
;
250 if(y
+para
->top
+rh
>= tPtr
->sRect
.pos
.y
&& (y
+para
->top
+rh
+lh
251 <= tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
))
252 //&& item.x+j >= tPtr->sRect.pos.x+tPtr->sRect.size.width))
258 if(item
.x
+j
>= tPtr
->sRect
.pos
.x
&&
259 item
.x
+j
+w
< tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
)
262 if(type
== 1 && y
+para
->top
+rh
+lh
<=
263 tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
)
268 if(type
== 1 && item
.chunk
->type
== ctText
) { /* partial coverage */
269 lm
= 2+WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
270 /* even I am still confused, so don't ask please */
271 if( (item
.x
+j
+lm
>= tPtr
->sRect
.pos
.x
&&
272 item
.x
+j
+lm
<= tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
)
273 || (item
.x
+j
+lm
>= tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
274 && y
+para
->top
+rh
+lh
<=
275 tPtr
->sRect
.pos
.y
+tPtr
->sRect
.size
.height
)
276 || (tPtr
->sRect
.pos
.y
< y
+para
->top
+rh
277 && tPtr
->sRect
.pos
.x
+tPtr
->sRect
.size
.width
>
280 rect
.pos
.x
= item
.x
+j
;
281 item
.chunk
->selected
= True
;
282 if(item
.chunk
->chars
> 6) {
283 item
.chunk
->sStart
= 3;
284 item
.chunk
->sEnd
= item
.chunk
->chars
;
286 item
.chunk
->sStart
= 0;
287 item
.chunk
->sEnd
= item
.chunk
->chars
;
290 } else if(type
== 2) {
291 rect
.pos
.x
= item
.x
+j
;
292 item
.chunk
->selected
= True
;
293 if(item
.chunk
->type
== ctText
) {
294 item
.chunk
->sStart
= 0;
295 item
.chunk
->sStart
= item
.chunk
->chars
;
296 rect
.size
.width
= WMWidthOfString(font
,
297 item
.text
, item
.chars
);
299 rect
.size
.width
= item
.chunk
->pixmap
->width
;
304 rect
.size
.height
= lh
;
309 myDrawText(Text
*tPtr
, Paragraph
*para
, MyTextItems
*items
,
310 short nitems
, short pwidth
, int y
, short draw
, short spacepos
)
312 short i
, ul_thick
, u
, j
=0; /* j = justification */
313 short line_width
= 0, line_height
=0, mx_descent
=0;
314 WMScreen
*screen
= tPtr
->view
->screen
;
318 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return 0;
319 for(i
=0; i
<nitems
; i
++) {
320 if(items
[i
].type
== ctText
) {
321 font
= (tPtr
->monoFont
)?tPtr
->dFont
:items
[i
].chunk
->font
;
322 mx_descent
= WMIN(mx_descent
, -font
->y
);
323 line_height
= WMAX(line_height
, font
->height
);
324 //printf("chunk.x %d xpoint.x %d\n",
325 // items[i].x, tPtr->clicked.x);
327 line_width
+= WMWidthOfString(font
,
328 items
[i
].text
, items
[i
].chars
);
330 mx_descent
= WMIN(mx_descent
, -(items
[i
].pix
->height
-3));
331 /* replace -3 wif descent... */
332 line_height
= WMAX(line_height
, items
[i
].pix
->height
);
333 if(para
->align
== WARight
|| para
->align
== WACenter
) {
334 line_width
+= items
[i
].pix
->width
;
337 if(para
->align
== WARight
) {
338 j
= pwidth
- line_width
;
339 } else if (para
->align
== WACenter
) {
340 j
= (short) ((float)(pwidth
- line_width
))/2.0;
343 if(tPtr
->findingClickPoint
&& (y
+line_height
>= tPtr
->clicked
.y
)) {
344 tPtr
->foundClickPoint
= True
;
345 tPtr
->currentChunk
= items
[0].chunk
; /* just first on this "line" */
346 tPtr
->tpos
= items
[0].start
; /* where to "start" counting from */
347 tPtr
->clicked
.x
= j
+items
[0].x
;
348 tPtr
->clicked
.y
= y
+line_height
+mx_descent
;
349 tPtr
->clheight
= line_height
; /* to draw the cursor */
350 tPtr
->clwidth
= line_width
; /* where to stop searching */
352 } if(!draw
) return line_height
;
354 for(i
=0; i
<nitems
; i
++) {
357 if(tPtr
->ownsSelection
) {
358 WMRect rect
= chunkSelectionRect(tPtr
, para
,
359 items
[i
], y
, j
, line_height
);
360 if(rect
.pos
.x
!= -23) { /* has been selected */
361 XFillRectangle(tPtr
->view
->screen
->display
, para
->drawbuffer
,
362 WMColorGC(WMGrayColor(tPtr
->view
->screen
)),
363 rect
.pos
.x
, rect
.pos
.y
, rect
.size
.width
, rect
.size
.height
);
367 if(items
[i
].type
== ctText
) {
368 gc
= WMColorGC(items
[i
].chunk
->color
);
369 font
= (tPtr
->monoFont
)?tPtr
->dFont
:items
[i
].chunk
->font
;
370 WMDrawString(screen
, para
->drawbuffer
, gc
, font
,
371 items
[i
].x
+j
, y
- font
->y
- mx_descent
,
372 items
[i
].text
, items
[i
].chars
);
373 if(items
[i
].chunk
->ul
&& !tPtr
->monoFont
) {
374 ul_thick
= (short) ((float)font
->height
)/12.0;
375 if (ul_thick
< 1) ul_thick
= 1;
376 for(u
=0; u
<ul_thick
; u
++) {
377 XDrawLine(screen
->display
, para
->drawbuffer
, gc
, items
[i
].x
+j
,
378 y
+ 1 + u
- mx_descent
,
379 items
[i
].x
+ j
+ WMWidthOfString(font
,
380 items
[i
].text
, items
[i
].chars
), y
+ 1 + u
- mx_descent
);
384 WMDrawPixmap(items
[i
].pix
, para
->drawbuffer
, items
[i
].x
+j
,
385 y
+ 3 - mx_descent
- items
[i
].pix
->height
);
391 drawPChunkPart(Text
*tPtr
, Chunk
*chunk
, LocalMargins m
, Paragraph
*para
,
392 MyTextItems
*items
, short *nitems
, short *Lmargin
, XPoint
*where
, short draw
)
398 chunk
->pixmap
= WMRetainPixmap(tPtr
->dUnknownImg
);
400 p_width
= m
.right
- WMIN(m
.first
, m
.body
) - WMGetRulerOffset(tPtr
->ruler
);
401 if(p_width
< MIN_DOC_WIDTH
) // need WMRuler to take care of this...
403 if(where
->x
+ chunk
->pixmap
->width
<= p_width
- *Lmargin
) {
404 /* it can fit on rest of line */
405 items
[*nitems
].pix
= chunk
->pixmap
;
406 items
[*nitems
].type
= ctImage
;
407 items
[*nitems
].chars
= 0;
408 items
[*nitems
].x
= *Lmargin
+where
->x
;
409 items
[*nitems
].chunk
= chunk
;
410 items
[*nitems
].start
= 0;
412 if(*nitems
>= MAX_CHUNX
) {
413 items
[*nitems
].chars
= 0;
414 items
[*nitems
].x
= *Lmargin
+where
->x
;
415 items
[*nitems
].chunk
= chunk
;
416 items
[*nitems
].start
= 0;
417 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
418 p_width
-*Lmargin
, where
->y
, draw
, 0);
419 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
424 where
->x
+= chunk
->pixmap
->width
;
426 } else if(chunk
->pixmap
->width
<= p_width
- *Lmargin
) {
427 /* it can fit on an entire line, flush the myDrawText then wrap it */
428 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
429 p_width
-*Lmargin
, where
->y
, draw
, 0);
431 *Lmargin
= WMAX(0, m
.body
- m
.first
);
433 drawPChunkPart(tPtr
, chunk
, m
, para
, items
, nitems
,
434 Lmargin
, where
, draw
);
439 *Lmargin
= WMAX(0, m
.body
- m
.first
);
440 items
[*nitems
].pix
= chunk
->pixmap
;
441 items
[*nitems
].type
= ctImage
;
442 items
[*nitems
].chars
= 0;
443 items
[*nitems
].x
= *Lmargin
+where
->x
;
444 items
[*nitems
].chunk
= chunk
;
445 items
[*nitems
].start
= 0;
446 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
447 p_width
-*Lmargin
, where
->y
, draw
, 0);
450 /* scale image to fit, call self again */
451 /* deprecated - the management */
457 drawTChunkPart(Text
*tPtr
, Chunk
*chunk
, char *bufr
, LocalMargins m
,
458 Paragraph
*para
, MyTextItems
*items
, short *nitems
, short len
, short start
,
459 short *Lmargin
, XPoint
*where
, short draw
, short spacepos
)
461 short t_chunk_width
, p_width
, chars
;
462 WMFont
*font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
463 /* if(doc->clickstart.yes && doc->clickstart.done) return; */
466 p_width
= m
.right
- WMIN(m
.first
, m
.body
);
467 if(p_width
< MIN_DOC_WIDTH
) // need WMRuler to take care of this...
471 t_chunk_width
= WMWidthOfString(font
, bufr
, len
);
472 if((where
->x
+ t_chunk_width
<= p_width
- *Lmargin
)
473 || (tPtr
->wrapping
== wrNone
)) {
474 /* if it can fit on rest of line, append to line */
475 chars
= WMIN(len
, MAX_WORD_LENGTH
);
476 snprintf(items
[*nitems
].text
, chars
+1, "%s", bufr
);
477 items
[*nitems
].chars
= chars
;
478 items
[*nitems
].x
= *Lmargin
+where
->x
;
479 items
[*nitems
].type
= ctText
;
480 items
[*nitems
].chunk
= chunk
;
481 items
[*nitems
].start
= start
;
483 if(*nitems
>= MAX_CHUNX
) {
484 chars
= WMIN(len
, MAX_WORD_LENGTH
);
485 snprintf(items
[*nitems
].text
, chars
+1, "%s", bufr
);
486 items
[*nitems
].chars
= chars
;
487 items
[*nitems
].x
= *Lmargin
+where
->x
;
488 items
[*nitems
].type
= ctText
;
489 items
[*nitems
].chunk
= chunk
;
490 items
[*nitems
].start
= start
;
491 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
+1,
492 p_width
-*Lmargin
, where
->y
, draw
, spacepos
);
493 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
498 where
->x
+= t_chunk_width
;
500 } else if(t_chunk_width
<= p_width
- *Lmargin
) {
501 /* it can fit on an entire line, flush and wrap it to a new line */
502 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
,
503 p_width
-*Lmargin
, where
->y
, draw
, spacepos
);
504 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
506 *Lmargin
= WMAX(0, m
.body
- m
.first
);
508 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
, items
, nitems
,
509 len
, start
, Lmargin
, where
, draw
, spacepos
);
511 /* otherwise, chop line, call ourself recursively until it's all gone */
512 short J
=0; /* bufr */
513 short j
=0; /* local tmp buffer */
515 short diff
= p_width
- *Lmargin
- where
->x
;
519 where
->y
+= myDrawText(tPtr
, para
, items
, *nitems
,
520 p_width
-*Lmargin
, where
->y
, draw
, spacepos
);
521 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return;
523 *Lmargin
= WMAX(0, m
.body
- m
.first
);
525 diff
= p_width
- *Lmargin
- where
->x
;
528 for(J
=0; J
<len
; J
++) {
530 if(WMWidthOfString(font
, tmp
, j
+1) > diff
) {
531 drawTChunkPart(tPtr
, chunk
, tmp
, m
, para
, items
, nitems
,
532 j
, start
+_start
, Lmargin
, where
, draw
, spacepos
);
537 /* and there's always that last chunk, get it too */
538 drawTChunkPart(tPtr
, chunk
, tmp
, m
, para
, items
, nitems
,
539 j
, start
+_start
, Lmargin
, where
, draw
, spacepos
);
543 /* this function does what it's called :-)
544 o It is also used for calculating extents of para,
545 (returns height) so watch out for (Bool) draw
546 o Also used to determine where mouse was clicked */
548 putParagraphOnPixmap(Text
*tPtr
, Paragraph
*para
, Bool draw
)
550 char bufr
[MAX_WORD_LENGTH
+1]; /* a single word + '\0' */
551 MyTextItems items
[MAX_CHUNX
+1];
552 short lmargin
, spacepos
, i
, s
, nitems
, start
;
557 if(!tPtr
->view
->flags
.realized
|| !para
) return 0;
559 where
.x
= 0, where
.y
=0, nitems
= 0;
560 m
.left
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
561 m
.right
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) - m
.left
;
562 m
.first
= para
->fmargin
, m
.body
= para
->bmargin
;
565 W_Screen
*screen
= tPtr
->view
->screen
;
567 XFreePixmap(screen
->display
, para
->drawbuffer
);
568 if(para
->width
<2*tPtr
->dFont
->height
) para
->width
= 2*tPtr
->dFont
->height
;
569 if(para
->height
<tPtr
->dFont
->height
) para
->height
= tPtr
->dFont
->height
;
570 para
->drawbuffer
= XCreatePixmap(screen
->display
,
571 tPtr
->view
->window
, para
->width
, para
->height
, screen
->depth
);
572 XFillRectangle(screen
->display
, para
->drawbuffer
,
573 WMColorGC(tPtr
->bg
), 0, 0, para
->width
, para
->height
);
577 //if(para->align != tPtr->dAlignment)
578 // para->align = tPtr->dAlignment;
580 /* draw the bullet if appropriate */
581 if(m
.body
>m
.first
&& !tPtr
->monoFont
) {
582 lmargin
= m
.body
- m
.first
;
585 WMDrawPixmap(para
->bulletPix
, para
->drawbuffer
, lmargin
-10, 5);
587 WMDrawPixmap(tPtr
->dBulletPix
, para
->drawbuffer
, lmargin
-10, 5);
589 /* NeXT sez next tab, I say the m.body - m.first margin */
591 lmargin
= WMAX(0, m
.first
- m
.body
);
594 if(tPtr
->findingClickPoint
&& !para
->chunks
) {
595 tPtr
->currentChunk
= NULL
;
596 tPtr
->foundClickPoint
= True
;
598 tPtr
->clicked
.x
= lmargin
;
600 tPtr
->clheight
= para
->height
;
605 chunk
= para
->chunks
;
608 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return 0;
610 if(chunk
->type
== ctImage
&& !tPtr
->monoFont
) {
611 drawPChunkPart(tPtr
, chunk
, m
, para
, items
, &nitems
,
612 &lmargin
, &where
, draw
);
613 } else if(chunk
->text
&& chunk
->type
== ctText
) {
614 if(tPtr
->wrapping
== wrNone
) {
615 drawTChunkPart(tPtr
, chunk
, chunk
->text
, m
, para
, items
, &nitems
,
616 chunk
->chars
, 0, &lmargin
, &where
, draw
, spacepos
);
617 } else if(tPtr
->wrapping
== wrWord
) {
618 spacepos
=0, i
=0, start
=0;
619 while(spacepos
< chunk
->chars
) {
620 bufr
[i
] = chunk
->text
[spacepos
];
621 if( bufr
[i
] == ' ' || i
>= MAX_WORD_LENGTH
) {
622 if(bufr
[i
] == ' ') s
=1; else s
=0;
623 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
,
624 items
, &nitems
, i
+s
, start
, &lmargin
, &where
,
627 if(i
> MAX_WORD_LENGTH
-1)
633 /* catch that last onery one. */
634 drawTChunkPart(tPtr
, chunk
, bufr
, m
, para
,
635 items
, &nitems
, i
, start
, &lmargin
, &where
, draw
, spacepos
);
636 } } chunk
= chunk
->next
;
638 /* we might have a few leftover items that need drawing */
640 where
.y
+= myDrawText(tPtr
, para
, items
,
641 nitems
, m
.right
-m
.left
-lmargin
, where
.y
, draw
, spacepos
);
642 if(tPtr
->findingClickPoint
&& tPtr
->foundClickPoint
) return 0;
648 calcParaExtents(Text
*tPtr
, Paragraph
*para
)
653 para
->width
= tPtr
->visibleW
;
656 para
->rmargin
= tPtr
->visibleW
;
658 para
->width
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) -
659 WMIN(para
->fmargin
, para
->bmargin
)
660 - WMGetRulerOffset(tPtr
->ruler
);
664 para
->height
= tPtr
->dFont
->height
;
666 para
->height
= putParagraphOnPixmap(tPtr
, para
, False
);
668 if(para
->height
<tPtr
->dFont
->height
)
669 para
->height
= tPtr
->dFont
->height
;
670 para
->bottom
= para
->top
+ para
->height
;
675 /* rather than bother with redrawing _all_ the pixmaps, simply
676 rearrange (i.e., push down or pull up) paragraphs after this one */
678 affectNextParas(Text
*tPtr
, Paragraph
*para
, int move_y
)
683 if(!para
|| move_y
==0) return;
685 old_y
= para
->bottom
;
686 calcParaExtents(tPtr
, para
);
687 old_y
-= para
->bottom
;
688 if(old_y
== 0) return;
690 }if(move_y
== 0) return;
695 next
->bottom
= next
->top
+ next
->height
;
696 next
= next
->next
; // I know, I know
697 }tPtr
->docHeight
+= move_y
;
700 tPtr
->vpos
+= move_y
;
701 if(tPtr
->vpos
< 0) tPtr
->vpos
= 0;
702 if(tPtr
->vpos
> tPtr
->docHeight
- tPtr
->visibleH
)
703 tPtr
->vpos
= tPtr
->docHeight
- tPtr
->visibleH
;
710 calcDocExtents(Text
*tPtr
)
715 tPtr
->docWidth
= tPtr
->visibleW
;
717 tPtr
->docWidth
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
) -
718 WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
721 para
= tPtr
->paragraphs
;
724 para
->top
= tPtr
->docHeight
;
725 tPtr
->docHeight
+= calcParaExtents(tPtr
, para
);
726 para
->bottom
= tPtr
->docHeight
;
729 } else { /* default to this if no paragraphs */
730 tPtr
->docHeight
= tPtr
->dFont
->height
;
733 if(tPtr
->editable
) /* add space at bottom to enter new stuff */
734 tPtr
->docHeight
+= tPtr
->dFont
->height
;
739 /* If any part of a paragraph is viewable, the entire
740 paragraph is drawn on an otherwise empty (XFreePixmap) pixmap.
741 The actual viewable parts of the paragraph(s) are then pieced
742 together via paintText:
744 -------------------------------------------
745 || this is a paragraph in this document||
746 ||========================================||
747 || | only part of it is visible though. ||
748 || |-------------------------------------||
749 ||[.| This is another paragraph ||
750 || | which I'll make relatively long ||
751 || | just for the sake of writing a long ||
752 || | paragraph with a picture: ^_^ ||
753 || |-------------------------------------||
754 ||==| Of the three paragraphs, only ||
755 ||/\| the preceding was totally copied to ||
756 ||\/| totally copied to the window, even ||
757 ==========================================||
758 though they are all on pixmaps.
759 -------------------------------------------
760 This paragraph exists only in
761 memory and so has a NULL pixmap.
762 -------------------------------------------
765 simple, right? Performance: the best of both worlds...
766 o fast scrolling: no need to rewrite what's already
767 on the screen, simply XCopy it.
768 o fast typing: only change current para, then simply
769 affect other (i.e., subsequent) paragraphs.
770 o If no part of para is on screen, gdbFree pixmap; else draw on
771 individual pixmap per para then piece several paras together
772 o Keep track of who to XCopy to window (see paintText) */
774 drawDocumentPartsOnPixmap(Text
*tPtr
, Bool all
)
778 para
= tPtr
->paragraphs
;
780 /* the 32 reduces jitter on the human eye by preparing paragraphs
781 in anticipation of when the _moving_ scrollbar reaches them */
782 if(para
->bottom
+ 32 < tPtr
->vpos
||
783 para
->top
> tPtr
->visibleH
+ tPtr
->vpos
+ 32 ) {
784 if(para
->drawbuffer
) {
785 XFreePixmap(tPtr
->view
->screen
->display
, para
->drawbuffer
);
786 para
->drawbuffer
= (Pixmap
) NULL
;
789 if(!para
->drawbuffer
|| all
)
790 putParagraphOnPixmap(tPtr
, para
, True
);
798 /* this function blindly copies the "visible" parts of a pragraph
799 unto the view, (top-down approach). It starts drawing from
800 the top of the view (which paragraph to draw is determined by
801 drawDocumentPartsOnPixmap); it stops at the bottom of the view. */
803 paintText(Text
*tPtr
)
805 short lmargin
, para_lmargin
;
806 int from
=5, to
=5, height
;
808 short vS
=0, hS
=0, rh
=0;
810 if(!tPtr
->view
->flags
.realized
) return;
812 if(tPtr
->rulerShown
) rh
= 40;
815 if(tPtr
->hasVscroller
) vS
= 21;
816 if(tPtr
->hasHscroller
) hS
= 21;
818 //XClearWindow(tPtr->view->screen->display, tPtr->view->window);
820 lmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
821 if(tPtr
->paragraphs
) {
822 para
= tPtr
->paragraphs
;
824 if(para
->drawbuffer
) {
825 from
= (para
->top
<=tPtr
->vpos
)?tPtr
->vpos
-para
->top
:0;
826 height
= para
->height
- from
;
827 if(from
>=0 && height
>0 ) {
828 para_lmargin
= WMIN(para
->fmargin
, para
->bmargin
);
829 if(lmargin
-vS
<WMIN(para
->fmargin
, para
->bmargin
)) {
831 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
832 lmargin
, to
, 2+para_lmargin
, height
, False
);
834 XFillRectangle(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
835 WMColorGC(tPtr
->dColor
), lmargin
, to
, 2+para_lmargin
, height
);
838 XCopyArea(tPtr
->view
->screen
->display
, para
->drawbuffer
,
839 tPtr
->view
->window
, WMColorGC(tPtr
->bg
), 0, from
,
840 para
->width
-4, height
, lmargin
+para_lmargin
+2, to
);
841 if( (to
+=height
) > tPtr
->visibleH
+rh
)
849 /* clear any left over space (esp. during para deletes/ ruler changes) */
850 if(tPtr
->docHeight
< tPtr
->visibleH
&& tPtr
->visibleH
+rh
+5-to
>0) {
851 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
, vS
, to
,
852 tPtr
->view
->size
.width
-vS
, tPtr
->visibleH
+rh
+hS
+5-to
, False
);
856 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
857 vS
+1, rh
+5, lmargin
-vS
, tPtr
->visibleH
+rh
+5-vS
, False
);
860 // from the "selection" days...
861 W_DrawRelief(tPtr
->view
->screen
, WMWidgetXID(tPtr
),
862 tPtr
->sRect
.pos
.x
, tPtr
->sRect
.pos
.y
,
863 tPtr
->sRect
.size
.width
, tPtr
->sRect
.size
.height
, tPtr
->relief
);
866 W_DrawRelief(tPtr
->view
->screen
, WMWidgetXID(tPtr
), 0, rh
,
867 tPtr
->visibleW
+vS
, tPtr
->visibleH
+hS
, tPtr
->relief
);
869 if(tPtr
->editable
&& tPtr
->clheight
> 0) {
870 int top
= tPtr
->cursor
.y
-tPtr
->vpos
;
871 int bot
= top
+tPtr
->clheight
;
874 if(bot
>tPtr
->visibleH
+hS
-2) bot
= tPtr
->visibleH
+hS
-2;
876 //do something about italic text...
877 XDrawLine(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
878 WMColorGC(tPtr
->dColor
), lmargin
+tPtr
->cursor
.x
, top
,
879 lmargin
+tPtr
->cursor
.x
, bot
);
886 /* called anytime either the ruler, vscroller or hscroller is hidden/shown,
887 or when the widget is resized by some user action */
889 resizeText(W_ViewDelegate
*self
, WMView
*view
)
891 Text
*tPtr
= (Text
*)view
->self
;
894 if(!tPtr
->monoFont
&& tPtr
->rulerShown
)
897 W_ResizeView(view
, view
->size
.width
, view
->size
.height
);
898 WMResizeWidget(tPtr
->ruler
, view
->size
.width
, 40);
901 if(tPtr
->hasVscroller
) {
902 WMMoveWidget(tPtr
->vscroller
, 1, 1+rh
);
903 WMResizeWidget(tPtr
->vscroller
, 20, view
->size
.height
-rh
-2);
904 tPtr
->visibleW
= view
->size
.width
-21;
906 if(tPtr
->hasHscroller
) {
907 WMMoveWidget(tPtr
->hscroller
, 20, view
->size
.height
-21);
908 WMResizeWidget(tPtr
->hscroller
, view
->size
.width
-21, 20);
909 tPtr
->visibleH
= view
->size
.height
-21-rh
;
910 } else tPtr
->visibleH
= view
->size
.height
-rh
;
912 tPtr
->visibleW
= view
->size
.width
;
913 if(tPtr
->hasHscroller
) {
914 WMMoveWidget(tPtr
->hscroller
, 1, view
->size
.height
-21);
915 WMResizeWidget(tPtr
->hscroller
, view
->size
.width
-2, 20);
916 tPtr
->visibleH
= view
->size
.height
-21-rh
;
917 } else tPtr
->visibleH
= view
->size
.width
-2-rh
;
919 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
922 W_ViewDelegate _TextViewDelegate
=
932 /* a plain text parser */
933 /* this gives useful hints on how to make a more
934 interesting parser for say HTML, RTF */
936 defaultParser(Text
*tPtr
, void *data
, short type
)
938 char *start
, *mark
, *text
= (char *) data
;
940 Paragraph
*para
= NULL
;
944 mark
= strchr(start
, '\n');
946 /* there is a newline, indicating the need for a new paragraph */
947 /* attach the chunk to the current paragraph */
948 if((short)(mark
-start
) > 1) {
949 /* ignore chunks with just a single newline but still make a
951 chunk
= (tPtr
->funcs
.createTChunk
) (start
, (short)(mark
-start
),
952 tPtr
->dFont
, tPtr
->dColor
, 0, False
);
953 (tPtr
->funcs
.insertChunk
) (tPtr
, chunk
, type
);
955 /* _then_ create a new paragraph for the _next_ chunk */
956 para
= (tPtr
->funcs
.createParagraph
) (0, 0, tPtr
->visibleW
,
958 (tPtr
->funcs
.insertParagraph
) (tPtr
, para
, type
);
961 /* just attach the chunk to the current paragraph */
962 if(strlen(start
) > 0) {
963 chunk
= (tPtr
->funcs
.createTChunk
) (start
, strlen(start
),
964 tPtr
->dFont
, tPtr
->dColor
, 0, False
);
965 (tPtr
->funcs
.insertChunk
) (tPtr
, chunk
, type
);
972 updateScrollers(Text
*tPtr
)
974 if(tPtr
->hasVscroller
) {
975 if(tPtr
->docHeight
< tPtr
->visibleH
) {
976 WMSetScrollerParameters(tPtr
->vscroller
, 0, 1);
979 float vmax
= (float)(tPtr
->docHeight
);
980 WMSetScrollerParameters(tPtr
->vscroller
,
981 ((float)tPtr
->vpos
)/(vmax
- (float)tPtr
->visibleH
),
982 (float)tPtr
->visibleH
/vmax
);
986 if(tPtr
->hasHscroller
)
991 scrollersCallBack(WMWidget
*w
, void *self
)
993 Text
*tPtr
= (Text
*)self
;
997 if(!tPtr
->view
->flags
.realized
) return;
999 if(w
== tPtr
->vscroller
) {
1002 vmax
= (float)(tPtr
->docHeight
);
1003 height
= tPtr
->visibleH
;
1005 height
-= 7; /* the top border (5) + bottom (2) */
1007 switch(WMGetScrollerHitPart(tPtr
->vscroller
)) {
1008 case WSDecrementLine
:
1009 if(tPtr
->vpos
> 0) {
1010 if(tPtr
->vpos
>16) tPtr
->vpos
-=16;
1014 case WSIncrementLine
: {
1015 int limit
= tPtr
->docHeight
- height
;
1016 if(tPtr
->vpos
< limit
) {
1017 if(tPtr
->vpos
<limit
-16) tPtr
->vpos
+=16;
1018 else tPtr
->vpos
=limit
;
1021 case WSDecrementPage
:
1022 tPtr
->vpos
-= height
;
1028 printf("dimple needs to jump to mouse location ;-/\n");
1030 case WSIncrementPage
:
1031 tPtr
->vpos
+= height
;
1032 if(tPtr
->vpos
> (tPtr
->docHeight
- height
))
1033 tPtr
->vpos
= tPtr
->docHeight
- height
;
1036 printf("dimple needs to jump to mouse location ;-/\n");
1041 tPtr
->vpos
= WMGetScrollerValue(tPtr
->vscroller
)
1042 * (float)(tPtr
->docHeight
- height
);
1049 float vmax
= (float)(tPtr
->docHeight
);
1050 ((float)tPtr
->vpos
)/(vmax
- (float)tPtr
->visibleH
),
1051 (float)tPtr
->visibleH
/vmax
);
1052 dimple
=where mouse is
.
1056 scroll
= (tPtr
->vpos
!= tPtr
->prevVpos
);
1057 tPtr
->prevVpos
= tPtr
->vpos
;
1060 if(w
== tPtr
->hscroller
)
1063 //need scrollv || scrollh
1067 if(tPtr->rulerShown)
1068 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47,
1069 tPtr->view->size.width-24, tPtr->view->size.height-49, True);
1071 XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2,
1072 tPtr->view->size.width-24, tPtr->view->size.height-4, True);
1075 updateScrollers(tPtr
);
1076 drawDocumentPartsOnPixmap(tPtr
, False
);
1083 W_InsertText(WMText
*tPtr
, void *data
, int position
)
1087 Paragraph
*para
= tPtr
->paragraphs
, *ptmp
;
1088 Chunk
*chunk
, *ctmp
;
1091 chunk
= para
->chunks
;
1093 if(chunk
->type
== ctText
&& chunk
->text
)
1094 wgdbFree(chunk
->text
);
1095 else if(chunk
->pixmap
)
1096 WMReleasePixmap(chunk
->pixmap
);
1098 chunk
= chunk
->next
;
1103 if(ptmp
->drawbuffer
)
1104 XFreePixmap(tPtr
->view
->screen
->display
, ptmp
->drawbuffer
);
1107 tPtr
->paragraphs
= NULL
;
1108 tPtr
->currentPara
= NULL
;
1109 tPtr
->currentChunk
= NULL
;
1111 WMRefreshText(tPtr
, 0, 0);
1116 (tPtr
->parser
)(tPtr
, data
, position
>= 0 ? 1 : 0);
1118 defaultParser(tPtr
, data
, position
>= 0 ? 1 : 0);
1122 cursorToTextPosition(Text
*tPtr
, int x
, int y
)
1124 Paragraph
*para
= NULL
;
1125 Chunk
*chunk
= NULL
;
1128 short orig_x
, orig_y
;
1130 if(x
<(tPtr
->hasVscroller
?21:1)) {
1131 y
-= tPtr
->clheight
;
1132 x
= tPtr
->view
->size
.width
; //tPtr->visibleW;
1133 } else if(x
>tPtr
->clwidth
&& x
<tPtr
->clicked
.x
) {
1134 //x = (tPtr->hasVscroller)?21:1;
1135 //y += tPtr->clheight;
1141 if(y
<0 || y
>tPtr
->view
->size
.height
-3) return;
1143 tPtr
->clicked
.x
= orig_x
;
1144 tPtr
->clicked
.y
= y
;
1145 tPtr
->clicked
.y
+= tPtr
->vpos
;
1146 tPtr
->clicked
.y
-= tPtr
->rulerShown
?40:0;
1147 para
= tPtr
->paragraphs
;
1150 if( tPtr
->clicked
.y
>= para
->top
-4 &&
1151 tPtr
->clicked
.y
< para
->bottom
+4) break;
1153 } if(!(tPtr
->currentPara
= para
)) return;
1155 tPtr
->clicked
.y
-= para
->top
;
1156 if(tPtr
->clicked
.y
<0) tPtr
->clicked
.y
=0;
1157 if(tPtr
->hasVscroller
) x
-= 21;
1160 tPtr
->findingClickPoint
= True
;
1161 tPtr
->foundClickPoint
= False
;
1162 /* also affects tPtr->currentChunk, tPtr->clicked.x and y,
1163 tPtr->clheight and ->width */
1164 putParagraphOnPixmap(tPtr
, para
, False
);
1165 tPtr
->findingClickPoint
= False
;
1166 tPtr
->clicked
.y
+= para
->top
;
1168 if(tPtr
->currentChunk
) {
1169 short _width
=0, start
=tPtr
->tpos
, done
=False
, w
=0;
1170 chunk
= tPtr
->currentChunk
;
1171 while(!done
&& chunk
&& line_width
<tPtr
->clwidth
) {
1172 if(chunk
->type
== ctText
) {
1173 font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
1174 for (w
=start
; w
<chunk
->chars
; w
++) {
1175 _width
= WMWidthOfString(font
, &chunk
->text
[w
], 1);
1176 line_width
+= _width
;
1177 if(line_width
+tPtr
->clicked
.x
>= x
) {
1178 line_width
-= _width
;
1184 if(0&&chunk
->next
) {
1185 if(chunk
->next
->type
== ctImage
) {
1186 if(x
+10 < line_width
+chunk
->next
->pixmap
->width
) {
1191 _width
= chunk
->pixmap
->width
;
1192 line_width
+= _width
;
1193 if(line_width
+tPtr
->clicked
.x
>= x
) {
1194 line_width
-= _width
;
1200 chunk
= chunk
->next
;
1204 tPtr
->currentChunk
= chunk
;
1207 short vS
= (tPtr
->hasVscroller
)?32:12;
1208 if(para
->align
== WARight
) {
1209 tPtr
->clicked
.x
= tPtr
->view
->size
.width
-vS
;
1210 } else if (para
->align
== WACenter
) {
1211 tPtr
->clicked
.x
= -(vS
/2)+(tPtr
->view
->size
.width
-vS
)/2;
1213 tPtr
->clicked
.x
= 2;
1216 tPtr
->cursor
.x
= tPtr
->clicked
.x
+2+line_width
;
1217 tPtr
->cursor
.y
= tPtr
->clicked
.y
;
1218 tPtr
->clicked
.y
= orig_y
;
1219 tPtr
->clicked
.x
= orig_x
;
1220 putParagraphOnPixmap(tPtr
, para
, True
);
1225 deleteTextInteractively(Text
*tPtr
, DeleteType type
)
1229 short pos
,w
=0,h
=0, doprev
=False
, doprevpara
=False
;
1231 int current
= WMGetTextCurrentChunk(tPtr
);
1233 if(!(para
= tPtr
->currentPara
)) return;
1234 if(!(chunk
= tPtr
->currentChunk
)) return;
1235 font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
1236 doprev
= (tPtr
->tpos
< 2);
1239 case dtDelete
: /* delete _after_ cursor ... implement later */
1240 case dtBackSpace
: /* delete _before_ cursor */
1241 if(chunk
->chars
> 1) {
1243 printf("here %d\n", pos
);
1245 w
= WMWidthOfString(font
, &chunk
->text
[pos
], 1);
1246 memmove(&(chunk
->text
[pos
]),
1247 &(chunk
->text
[pos
+1]), chunk
->chars
-pos
+1);
1251 WMRemoveTextChunk(tPtr
, current
);
1257 WMSetTextCurrentChunk(tPtr
, current
-1);
1258 if(!tPtr
->currentChunk
) {
1259 printf("PREV PARA\n");
1261 tPtr
->tpos
= tPtr
->currentChunk
->chars
;
1264 int currentp
= WMGetTextCurrentParagraph(tPtr
);
1267 para
->chunks
= NULL
;
1268 WMRemoveTextParagraph(tPtr
, currentp
);
1269 WMSetTextCurrentParagraph(tPtr
, currentp
-1);
1270 WMSetTextCurrentChunk(tPtr
, -1);
1271 para
= tPtr
->currentPara
;
1273 if(!tPtr
->currentChunk
|| !para
->chunks
) {
1274 para
->chunks
= chunk
;
1275 tPtr
->currentChunk
= chunk
;
1277 tPtr
->currentChunk
->next
= chunk
;
1280 if(1) { //if(1||(para && !doprevpara)) {
1281 affectNextParas(tPtr
, para
, -23);
1282 putParagraphOnPixmap(tPtr
, para
, True
);
1283 drawDocumentPartsOnPixmap(tPtr
, False
);
1284 updateScrollers(tPtr
);
1286 //cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y);
1287 } else WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1291 /* give us nice chunk sizes (multiples of 16) */
1293 reqBlockSize(short requested
)
1295 return requested
+16-(requested
%16);
1299 insertTextInteractively(Text
*tPtr
, char *text
)
1301 Paragraph
*para
=NULL
;
1302 Chunk
*chunk
=NULL
, *newchunk
=NULL
;
1303 int height
= -23; /* should only be changed upon newline */
1307 if(!tPtr
->editable
) return;
1308 if(*text
== '\n' && tPtr
->ignoreNewLine
)
1311 para
= tPtr
->currentPara
;
1312 chunk
= tPtr
->currentChunk
;
1313 font
= (tPtr
->monoFont
|| !chunk
)?tPtr
->dFont
:chunk
->font
;
1317 if(chunk
) { /* there's a chunk (or part of it) to detach from old */
1318 int current
= WMGetTextCurrentChunk(tPtr
);
1319 if(tPtr
->tpos
<=0) { /* at start of chunk */
1320 if(current
<1) { /* the first chunk... make old para blank */
1321 newchunk
= para
->chunks
;
1322 para
->chunks
= NULL
;
1323 putParagraphOnPixmap(tPtr
, para
, True
);
1324 } else { /* not first chunk... */
1325 printf("cut me out \n");
1327 } else if(tPtr
->tpos
< chunk
->chars
&& chunk
->type
== ctText
) {
1328 /* not at start of chunk */
1329 char text
[chunk
->chars
-tPtr
->tpos
+1];
1332 text
[i
] = chunk
->text
[tPtr
->tpos
+i
];
1333 } while(++i
< chunk
->chars
-tPtr
->tpos
);
1335 newchunk
= (tPtr
->funcs
.createTChunk
) (text
, i
, chunk
->font
,
1336 chunk
->color
, chunk
->script
, chunk
->ul
);
1337 newchunk
->next
= chunk
->next
;
1339 /* might want to demalloc for LARGE cuts */
1340 //calcParaExtents(tPtr, para);
1341 para
->height
= putParagraphOnPixmap(tPtr
, para
, True
);
1342 //putParagraphOnPixmap(tPtr, para, True);
1343 } else if(tPtr
->tpos
>= chunk
->chars
) {
1345 WMSetTextCurrentChunk(tPtr
, current
-1);
1346 prev
= tPtr
->currentChunk
;
1348 newchunk
= prev
->next
;
1350 putParagraphOnPixmap(tPtr
, para
, True
);
1352 } else newchunk
= NULL
;
1354 if(para
) /* the preceeding one */
1355 new_top
= para
->bottom
;
1357 WMAppendTextStream(tPtr
, "\n");
1358 para
= tPtr
->currentPara
;
1360 para
->chunks
= newchunk
;
1361 tPtr
->currentChunk
= newchunk
;
1363 para
->top
= new_top
;
1364 calcParaExtents(tPtr
, para
);
1365 height
= para
->height
;
1368 WMAppendTextStream(tPtr
, text
);
1369 para
= tPtr
->currentPara
;
1370 } else if(!para
->chunks
|| !chunk
) {
1371 //WMPrependTextStream(tPtr, text);
1372 WMAppendTextStream(tPtr
, text
);
1373 } else if(chunk
->type
== ctImage
) {
1374 WMPrependTextStream(tPtr
, text
);
1376 printf("\n\nprepe\n\n");
1378 if(tPtr
->tpos
> chunk
->chars
) {
1379 printf("\n\nmore\n\n");
1380 tPtr
->tpos
= chunk
->chars
;
1383 if(chunk
->chars
+1 >= chunk
->mallocedSize
) {
1384 chunk
->mallocedSize
= reqBlockSize(chunk
->chars
+1);
1385 chunk
->text
= wrealloc(chunk
->text
, chunk
->mallocedSize
);
1388 memmove(&(chunk
->text
[tPtr
->tpos
+1]), &chunk
->text
[tPtr
->tpos
],
1389 chunk
->chars
-tPtr
->tpos
+1);
1390 w
= WMWidthOfString(font
, text
, 1);
1391 memmove(&chunk
->text
[tPtr
->tpos
], text
, 1);
1394 //doc->clickstart.cursor.x +=
1395 //WMWidthOfString(chunk->fmt->font, text,len);
1401 affectNextParas(tPtr
, para
, height
);
1402 putParagraphOnPixmap(tPtr
, para
, True
);
1403 drawDocumentPartsOnPixmap(tPtr
, False
);
1404 updateScrollers(tPtr
);
1406 //cursorToTextPosition(tPtr, tPtr->clicked.x+w, tPtr->clicked.y);
1407 //check for "sneppah tahw" with blank paras...
1414 selectRegion(Text
*tPtr
, int x
, int y
)
1416 tPtr
->sRect
.pos
.x
= WMIN(tPtr
->clicked
.x
, x
);
1417 tPtr
->sRect
.size
.width
= abs(tPtr
->clicked
.x
-x
);
1418 tPtr
->sRect
.pos
.y
= WMIN(tPtr
->clicked
.y
, y
);
1419 if(tPtr
->sRect
.pos
.y
<0) tPtr
->sRect
.pos
.y
=0;
1420 tPtr
->sRect
.size
.height
= abs(tPtr
->clicked
.y
-y
);
1423 while(y>tPtr->visibleH && tPtr->vpos < tPtr->docHeight-tPtr->visibleH) {
1424 WMRefreshText(tPtr, tPtr->vpos+16, tPtr->hpos);
1427 //printf("%d %d \n", y, tPtr->vpos);
1429 //foreach para in selection...
1430 drawDocumentPartsOnPixmap(tPtr
, True
);
1438 #define WM_EMACSKEYMASK ControlMask
1439 #define WM_EMACSKEY_LEFT XK_b
1440 #define WM_EMACSKEY_RIGHT XK_f
1441 #define WM_EMACSKEY_HOME XK_a
1442 #define WM_EMACSKEY_END XK_e
1443 #define WM_EMACSKEY_BS XK_h
1444 #define WM_EMACSKEY_DEL XK_d
1447 handleTextKeyPress(Text
*tPtr
, XEvent
*event
)
1451 int control_pressed
= False
;
1453 if(!tPtr
->editable
) return;
1455 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
)
1456 control_pressed
= True
;
1457 buffer
[XLookupString(&event
->xkey
, buffer
, 1, &ksym
, NULL
)] = '\0';
1463 if(tPtr
->currentChunk
) {
1465 Chunk
*chunk
= tPtr
->currentChunk
;
1466 if(chunk
->type
== ctText
) {
1467 WMFont
*font
= (tPtr
->monoFont
)?tPtr
->dFont
:chunk
->font
;
1468 if(ksym
==XK_Right
) {
1469 short pos
= (tPtr
->tpos
<chunk
->chars
)?tPtr
->tpos
+1:
1471 w
= WMWidthOfString(font
,&chunk
->text
[pos
],1);
1473 short pos
= (tPtr
->tpos
>0)?tPtr
->tpos
-1:0;
1474 w
= WMWidthOfString(font
,&chunk
->text
[pos
],1);
1476 } else { w
= chunk
->pixmap
->width
; }
1477 if(ksym
==XK_Right
) w
= -w
;
1478 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
-w
, tPtr
->clicked
.y
);
1480 if(ksym
==XK_Right
) ksym
= XK_Down
;
1488 noCChunk
: { short h
= tPtr
->clheight
-2;
1489 if(ksym
==XK_Down
) h
= -h
;
1490 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
, tPtr
->clicked
.y
-h
);
1494 deleteTextInteractively(tPtr
, dtBackSpace
);
1499 deleteTextInteractively(tPtr
, dtDelete
);
1505 if(buffer
[0] != '\0' && (buffer
[0] == '\n' || !iscntrl(buffer
[0])))
1506 insertTextInteractively(tPtr
, buffer
);
1507 else if(control_pressed
&& ksym
==XK_r
)
1508 {Bool i
= !tPtr
->rulerShown
; WMShowTextRuler(tPtr
, i
);
1509 tPtr
->rulerShown
= i
; }
1517 pasteText(WMView
*view
, Atom selection
, Atom target
, Time timestamp
,
1518 void *cdata
, WMData
*data
)
1520 Text
*tPtr
= (Text
*)view
->self
;
1524 tPtr
->waitingForSelection
= False
;
1526 str
= (char*)WMDataBytes(data
);
1527 if(tPtr
->tpos
<1) WMPrependTextStream(tPtr
, str
);
1528 else WMAppendTextStream(tPtr
, str
);
1529 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1532 str
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1535 if(tPtr
->tpos
<1) WMPrependTextStream(tPtr
, str
);
1536 else WMAppendTextStream(tPtr
, str
);
1538 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1545 releaseSelection(Text
*tPtr
)
1547 Paragraph
*para
= tPtr
->paragraphs
;
1550 chunk
= para
->chunks
;
1552 chunk
->selected
= False
;
1553 chunk
= chunk
->next
;
1557 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
1558 tPtr
->ownsSelection
= False
;
1559 drawDocumentPartsOnPixmap(tPtr
, True
);
1565 requestHandler(WMView
*view
, Atom selection
, Atom target
,
1566 void *cdata
, Atom
*type
)
1568 Text
*tPtr
= view
->self
;
1570 Display
*dpy
= tPtr
->view
->screen
->display
;
1572 Atom TEXT
= XInternAtom(dpy
, "TEXT", False
);
1573 Atom COMPOUND_TEXT
= XInternAtom(dpy
, "COMPOUND_TEXT", False
);
1574 WMData
*data
= NULL
;
1577 if(!tPtr
->ownsSelection
|| !tPtr
->paragraphs
) return NULL
;
1578 //printf("got here\n");
1580 if (target
== XA_STRING
|| target
== TEXT
|| target
== COMPOUND_TEXT
) {
1581 //for bleh in selection...
1583 Paragraph
*para
= tPtr
->paragraphs
;
1584 Chunk
*chunk
= NULL
;
1585 char pixmap
[] = "[pixmap]";
1590 chunk
= para
->chunks
;
1593 if(chunk
->selected
&& chunk
->type
== ctText
) {
1594 len
= chunk
->chars
; //chunk->sEnd - chunk->sStart;
1598 memcpy(s
, &chunk
->text
[0*chunk
->sStart
], len
);
1601 data
= WMCreateDataWithBytes(s
, strlen(s
));
1604 printf("append: %c %d\n", *s
, strlen(s
));
1605 WMAppendDataBytes(data
, s
, strlen(s
));
1610 printf("len is %d [%d %d] %d \n", len
, chunk
->sStart
, chunk
->sEnd
,
1613 chunk
= chunk
->next
;
1619 WMSetDataFormat(data
, 8);
1626 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
1627 if (target
== _TARGETS
) {
1628 Atom
*ptr
= wmalloc(4 * sizeof(Atom
));
1632 ptr
[3] = COMPOUND_TEXT
;
1634 data
= WMCreateDataWithBytes(ptr
, 4*4);
1635 WMSetDataFormat(data
, 32);
1648 lostHandler(WMView
*view
, Atom selection
, void *cdata
)
1650 WMText
*tPtr
= (WMText
*)view
->self
;
1651 releaseSelection(tPtr
);
1654 static WMSelectionProcs selectionHandler
= {
1655 requestHandler
, lostHandler
, NULL
};
1658 _notification(void *observerData
, WMNotification
*notification
)
1660 WMText
*to
= (WMText
*)observerData
;
1661 WMText
*tw
= (WMText
*)WMGetNotificationClientData(notification
);
1662 if (to
!= tw
) lostHandler(to
->view
, XA_PRIMARY
, NULL
);
1666 handleTextEvents(XEvent
*event
, void *data
)
1668 Text
*tPtr
= (Text
*)data
;
1669 Display
*dpy
= event
->xany
.display
;
1671 if(tPtr
->waitingForSelection
) return;
1673 switch (event
->type
) {
1675 if(!tPtr
->editable
|| tPtr
->buttonHeld
) {
1679 if(tPtr
->ownsSelection
) releaseSelection(tPtr
);
1680 //if (tPtr->waitingForSelection) return;
1683 XGrabPointer(dpy
, W_VIEW(tPtr
)->window
, False
,
1684 PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
1685 GrabModeAsync
, GrabModeAsync
, None
,
1686 W_VIEW(tPtr
)->screen
->invisibleCursor
, CurrentTime
);
1687 tPtr
->pointerGrabbed
= True
;
1689 handleTextKeyPress(tPtr
, event
);
1693 if(tPtr
->pointerGrabbed
) {
1694 tPtr
->pointerGrabbed
= False
;
1695 XUngrabPointer(dpy
, CurrentTime
);
1697 if((event
->xmotion
.state
& Button1Mask
)) {
1698 selectRegion(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
1699 if(!tPtr
->ownsSelection
) {
1700 WMCreateSelectionHandler(tPtr
->view
, XA_PRIMARY
,
1701 event
->xbutton
.time
, &selectionHandler
, NULL
);
1702 tPtr
->ownsSelection
= True
;
1707 if(event
->xbutton
.button
== Button1
) {
1708 if(tPtr
->ownsSelection
) releaseSelection(tPtr
);
1709 cursorToTextPosition(tPtr
, event
->xmotion
.x
, event
->xmotion
.y
);
1710 if (tPtr
->pointerGrabbed
) {
1711 tPtr
->pointerGrabbed
= False
;
1712 XUngrabPointer(dpy
, CurrentTime
);
1716 if(!tPtr
->focused
) {
1717 WMSetFocusToWidget(tPtr
);
1718 tPtr
->focused
= True
;
1721 if(event
->xbutton
.button
== 4)
1722 WMScrollText(tPtr
, -16);
1723 else if(event
->xbutton
.button
== 5)
1724 WMScrollText(tPtr
, 16);
1729 tPtr
->buttonHeld
= False
;
1730 if (tPtr
->pointerGrabbed
) {
1731 tPtr
->pointerGrabbed
= False
;
1732 XUngrabPointer(dpy
, CurrentTime
);
1735 if(event
->xbutton
.button
== 4 || event
->xbutton
.button
== 5)
1737 if(event
->xbutton
.button
== Button2
&& tPtr
->editable
) {
1740 if(!WMRequestSelection(tPtr
->view
, XA_PRIMARY
, XA_STRING
,
1741 event
->xbutton
.time
, pasteText
, NULL
)) {
1742 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1745 WMAppendTextStream(tPtr
, text
);
1747 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
1748 } else tPtr
->waitingForSelection
= True
;
1757 handleNonTextEvents(XEvent
*event
, void *data
)
1759 Text
*tPtr
= (Text
*)data
;
1761 switch(event
->type
) {
1763 if(!event
->xexpose
.count
&& tPtr
->view
->flags
.realized
)
1768 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
1770 tPtr
->focused
= True
;
1771 //cursor...paintText(tPtr);
1775 tPtr
->focused
= False
;
1776 //cursor...paintText(tPtr);
1781 //for(...)WMRemoveTextParagraph(tPtr, para);
1786 //printf("handleNonTextEvents\n");
1792 rulerCallBack(WMWidget
*w
, void *self
)
1794 Text
*tPtr
= (Text
*)self
;
1797 if(tPtr
->currentPara
) {
1798 Paragraph
*para
= tPtr
->currentPara
;
1799 para
->fmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerFirst
);
1800 para
->bmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerBody
);
1801 para
->rmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
);
1802 affectNextParas(tPtr
, para
, -23);
1803 putParagraphOnPixmap(tPtr
, para
, True
);
1806 which
= WMGetReleasedRulerMargin(tPtr
->ruler
);
1807 if(which
!= WRulerDocLeft
&& which
!= WRulerRight
1808 /* && Selection.para.count > 0 */ ) {
1810 "//for(i=0; i<Selection.para.count; i++) {"
1812 "//calcParaExtents(tPtr, para);}\n");
1814 WMRefreshText(tPtr
, 0, 0);
1821 rulerMoveCallBack(WMWidget
*w
, void *self
)
1823 Text
*tPtr
= (Text
*)self
;
1824 short rmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerRight
);
1827 if(WMGetGrabbedRulerMargin(tPtr
->ruler
) == WRulerLeft
) {
1828 short lmargin
= WMGetRulerMargin(tPtr
->ruler
, WRulerDocLeft
);
1829 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
1830 22, 42, lmargin
-21, tPtr
->visibleH
, True
);
1831 } else if(WMGetGrabbedRulerMargin(tPtr
->ruler
) == WRulerRight
&&
1832 tPtr
->docWidth
+11 < rmargin
) {
1833 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
1834 rmargin
-3, 42, 10, tPtr
->visibleH
, True
);
1841 /* ------------- non-static functions that are "friends" ------------- */
1842 /* ------------- called as (tPtr->funcs.foo)(bars...) ------------- */
1844 /* create a new paragraph. Don't do anything with it just yet */
1847 createParagraph(short fmargin
, short bmargin
, short rmargin
,
1848 short *tabstops
, short numTabs
, WMAlignment alignment
)
1850 Paragraph
*para
= wmalloc(sizeof(Paragraph
));
1851 if(!para
) return NULL
;
1853 para
->chunks
= NULL
;
1857 para
->fmargin
= (fmargin
>=0)?fmargin
:0;
1858 para
->bmargin
= (bmargin
>=0)?bmargin
:0;
1859 if(rmargin
-bmargin
>= 100 && rmargin
-fmargin
>= 100)
1860 para
->rmargin
= rmargin
;
1862 para
->rmargin
= 100;
1863 para
->tabstops
= tabstops
;
1864 para
->numTabs
= (tabstops
)?numTabs
:0;
1866 para
->drawbuffer
= (Pixmap
)NULL
;
1867 para
->bulletPix
= NULL
;
1868 para
->top
= para
->bottom
= 0;
1869 para
->width
= para
->height
= 0;
1871 para
->align
= alignment
;
1876 /* insert the new paragraph in the tPtr, either right before
1877 or after the currentPara. It's the responsibility of the
1878 calling code to set what currentPara is. via WMSetTextCurrentParagraph.
1879 If currentPara is not set, set it as the first in the document.
1880 This function then sets currentPara as _this_ paragraph.
1881 NOTE: this means careless parser implementors might lose previous
1882 paragraphs... but this keeps stuff small and non-buggy :-) */
1884 insertParagraph(WMText
*tPtr
, void *v
, InsertType type
)
1885 //insertParagraph(WMText *tPtr, Paragraph *para, InsertType type)
1888 Paragraph
*para
= (Paragraph
*)v
;
1889 if(!para
|| !tPtr
) return;
1891 if(!tPtr
->currentPara
) {
1892 tPtr
->paragraphs
= para
;
1894 tmp
= tPtr
->paragraphs
;
1895 if(type
== itAppend
) {
1896 while(tmp
->next
&& tmp
!= tPtr
->currentPara
)
1899 para
->next
= tmp
->next
;
1901 } else { /* must be prepend */
1902 /* this "prior" member is that "doing things the hard way"
1903 I spoke of. See? it's not too bad afterall... */
1904 Paragraph
*prior
= NULL
;
1905 while(tmp
->next
&& tmp
!= tPtr
->currentPara
) {
1909 /* if this is the first */
1910 if(tmp
== tPtr
->paragraphs
) {
1912 tPtr
->paragraphs
= para
;
1917 tPtr
->currentPara
= para
;
1921 /* create a new chunk to contain exactly ONE pixmap */
1924 createPChunk(WMPixmap
*pixmap
, short script
, ushort ul
)
1928 chunk
= wmalloc(sizeof(Chunk
));
1934 chunk
->pixmap
= NULL
; /* if it's NULL, we'll draw the "broken" pixmap... */
1935 else chunk
->pixmap
= WMRetainPixmap(pixmap
);
1937 chunk
->mallocedSize
= 0;
1938 chunk
->type
= ctImage
;
1940 chunk
->color
= NULL
;
1941 chunk
->script
= script
;
1943 chunk
->selected
= False
;
1949 /* create a new chunk to contain some text with the given format */
1952 createTChunk(char *text
, short chars
, WMFont
*font
,
1953 WMColor
*color
, short script
, ushort ul
)
1957 if(!text
|| chars
<0 || !font
|| !color
) return NULL
;
1958 chunk
= wmalloc(sizeof(Chunk
));
1959 if(!chunk
) return NULL
;
1961 chunk
->mallocedSize
= reqBlockSize(chars
);
1962 chunk
->text
= (char *)wmalloc(chunk
->mallocedSize
);
1963 memcpy(chunk
->text
, text
, chars
);
1964 chunk
->pixmap
= NULL
;
1965 chunk
->chars
= chars
;
1966 chunk
->type
= ctText
;
1967 chunk
->font
= WMRetainFont(font
);
1968 chunk
->color
= WMRetainColor(color
);
1969 chunk
->script
= script
;
1971 chunk
->selected
= False
;
1977 /* insert the new chunk in the paragraph, either right before
1978 or after the currentChunk. It's the responsibility of the
1979 calling code to set what currentChunk is via WMSetTextCurrentChunk.
1980 If currentChunk is not set, set it as the first in the existing
1981 paragraph... if not even that, you lose... try again.
1982 This function then sets currentChunk as _this_ chunk.
1983 NOTE: this means careless parser implementors might lose previous
1984 paragraphs/chunks... but this keeps stuff small and non-buggy :-) */
1986 insertChunk(WMText
*tPtr
, void *v
, InsertType type
)
1989 Chunk
*chunk
= (Chunk
*)v
;
1991 if(!tPtr
|| !chunk
) return;
1993 if(!tPtr
->paragraphs
) { /* i.e., first chunk via insertTextInteractively */
1994 Paragraph
*para
= (tPtr
->funcs
.createParagraph
) (0, 0, tPtr
->visibleW
,
1996 (tPtr
->funcs
.insertParagraph
) (tPtr
, para
, itAppend
);
1999 if(!tPtr
->currentPara
)
2001 if(!tPtr
->currentChunk
) { /* there is a current chunk */
2002 tPtr
->currentPara
->chunks
= chunk
;
2003 } else if(!tPtr
->currentPara
->chunks
) {
2004 /* but it's not of this paragraph */
2005 tPtr
->currentPara
->chunks
= chunk
;
2007 tmp
= tPtr
->currentPara
->chunks
;
2009 if(type
== itAppend
) {
2010 while(tmp
->next
&& tmp
!= tPtr
->currentChunk
)
2013 chunk
->next
= tmp
->next
;
2016 } else { /* must be prepend */
2017 /* this "prior" member is that "doing things the hard way"
2018 I spoke of. See? it's not too bad afterall... */
2019 Chunk
*prior
= NULL
;
2020 while(tmp
->next
&& tmp
!= tPtr
->currentChunk
) {
2024 /* if this is the first */
2025 if(tmp
== tPtr
->currentPara
->chunks
) {
2027 tPtr
->currentPara
->chunks
= chunk
;
2029 prior
->next
= chunk
;
2032 tPtr
->currentChunk
= chunk
;
2033 tPtr
->tpos
= chunk
->chars
;
2037 /* ------------- non-static functions (i.e., APIs) ------------- */
2038 /* ------------- called as WMVerbText[Subject] ------------- */
2040 #define DEFAULT_TEXT_WIDTH 250
2041 #define DEFAULT_TEXT_HEIGHT 200
2046 WMCreateText(WMWidget
*parent
)
2048 Text
*tPtr
= wmalloc(sizeof(Text
));
2050 perror("could not create text widget\n");
2053 memset(tPtr
, 0, sizeof(Text
));
2054 tPtr
->widgetClass
= WC_Text
;
2055 tPtr
->view
= W_CreateView(W_VIEW(parent
));
2057 perror("could not create text's view\n");
2061 tPtr
->view
->self
= tPtr
;
2062 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
2063 tPtr
->view
->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2064 W_ResizeView(tPtr
->view
, DEFAULT_TEXT_WIDTH
, DEFAULT_TEXT_HEIGHT
);
2065 tPtr
->bg
= tPtr
->view
->screen
->white
;
2066 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->bg
);
2069 tPtr
->ruler
= WMCreateRuler(tPtr
);
2070 (W_VIEW(tPtr
->ruler
))->attribs
.cursor
= tPtr
->view
->screen
->defaultCursor
;
2071 (W_VIEW(tPtr
->ruler
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2072 WMMoveWidget(tPtr
->ruler
, 0, 0);
2073 WMResizeWidget(tPtr
->ruler
, W_VIEW(parent
)->size
.width
, 40);
2074 WMShowRulerTabs(tPtr
->ruler
, True
);
2075 WMSetRulerAction(tPtr
->ruler
, rulerCallBack
, tPtr
);
2076 WMSetRulerMoveAction(tPtr
->ruler
, rulerMoveCallBack
, tPtr
);
2080 tPtr
->vscroller
= WMCreateScroller(tPtr
);
2081 (W_VIEW(tPtr
->vscroller
))->attribs
.cursor
=
2082 tPtr
->view
->screen
->defaultCursor
;
2083 (W_VIEW(tPtr
->vscroller
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2084 WMMoveWidget(tPtr
->vscroller
, 1, 1);
2085 WMResizeWidget(tPtr
->vscroller
, 20, tPtr
->view
->size
.height
- 2);
2086 WMSetScrollerArrowsPosition(tPtr
->vscroller
, WSAMaxEnd
);
2087 WMSetScrollerAction(tPtr
->vscroller
, scrollersCallBack
, tPtr
);
2091 tPtr
->hscroller
= WMCreateScroller(tPtr
);
2092 (W_VIEW(tPtr
->hscroller
))->attribs
.cursor
=
2093 tPtr
->view
->screen
->defaultCursor
;
2094 (W_VIEW(tPtr
->hscroller
))->attribFlags
|= CWOverrideRedirect
| CWCursor
;
2095 WMMoveWidget(tPtr
->hscroller
, 1, tPtr
->view
->size
.height
-21);
2096 WMResizeWidget(tPtr
->hscroller
, tPtr
->view
->size
.width
- 2, 20);
2097 WMSetScrollerArrowsPosition(tPtr
->hscroller
, WSAMaxEnd
);
2098 WMSetScrollerAction(tPtr
->hscroller
, scrollersCallBack
, tPtr
);
2100 tPtr
->visibleW
= tPtr
->view
->size
.width
;
2101 tPtr
->visibleH
= tPtr
->view
->size
.height
;
2103 tPtr
->paragraphs
= NULL
;
2105 tPtr
->docHeight
= 0;
2106 tPtr
->dBulletPix
= WMCreatePixmapFromXPMData(tPtr
->view
->screen
,
2108 tPtr
->dUnknownImg
= WMCreatePixmapFromXPMData(tPtr
->view
->screen
,
2111 tPtr
->sRect
.pos
.x
= tPtr
->sRect
.pos
.y
= 0;
2112 tPtr
->sRect
.size
.width
= tPtr
->sRect
.size
.height
= 0;
2113 tPtr
->currentPara
= NULL
;
2114 tPtr
->currentChunk
= NULL
;
2117 tPtr
->parser
= NULL
;
2118 tPtr
->writer
= NULL
;
2119 tPtr
->funcs
.createParagraph
= createParagraph
;
2120 tPtr
->funcs
.insertParagraph
= insertParagraph
;
2121 tPtr
->funcs
.createPChunk
= createPChunk
;
2122 tPtr
->funcs
.createTChunk
= createTChunk
;
2123 tPtr
->funcs
.insertChunk
= insertChunk
;
2125 tPtr
->clicked
.x
= tPtr
->clicked
.y
= -23;
2126 tPtr
->cursor
.x
= tPtr
->cursor
.y
= -23;
2128 tPtr
->relief
= WRSunken
;
2129 tPtr
->wrapping
= wrWord
;
2130 tPtr
->editable
= False
;
2131 tPtr
->cursorShown
= False
;
2132 tPtr
->frozen
= False
;
2133 tPtr
->focused
= False
;
2134 tPtr
->pointerGrabbed
= False
;
2135 tPtr
->buttonHeld
= False
;
2136 tPtr
->ignoreNewLine
= False
;
2137 tPtr
->waitingForSelection
= False
;
2138 tPtr
->findingClickPoint
= False
;
2139 tPtr
->foundClickPoint
= False
;
2140 tPtr
->ownsSelection
= False
;
2144 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2145 tPtr
->dColor
= WMBlackColor(tPtr
->view
->screen
);
2147 tPtr
->view
->delegate
= &_TextViewDelegate
;
2148 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
2149 |EnterWindowMask
|LeaveWindowMask
|FocusChangeMask
,
2150 handleNonTextEvents
, tPtr
);
2151 WMCreateEventHandler(tPtr
->view
, ButtonReleaseMask
|ButtonPressMask
2152 |KeyReleaseMask
|KeyPressMask
|Button1MotionMask
,
2153 handleTextEvents
, tPtr
);
2155 WMAddNotificationObserver(_notification
, tPtr
, "_lostOwnership", tPtr
);
2157 WMSetTextMonoFont(tPtr
, True
);
2158 WMShowTextRuler(tPtr
, False
);
2159 WMSetTextHasHorizontalScroller(tPtr
, False
);
2160 WMSetTextHasVerticalScroller(tPtr
, True
);
2161 //printf("the sizeof chunk is %d\n", sizeof(Chunk));
2162 //printf("the sizeof para is %d\n", sizeof(Paragraph));
2163 //printf("the sizeof text is %d\n", sizeof(Text));
2168 //WRetainPixmap(tPtr->dBulletPix);
2171 WMRemoveTextParagraph(WMText
*tPtr
, int which
)
2173 Paragraph
*prior
, *removed
;
2174 if(!tPtr
|| which
<0) return;
2176 WMSetTextCurrentParagraph(tPtr
, which
);
2177 removed
= tPtr
->currentPara
;
2178 if(!removed
) return;
2179 if(removed
->chunks
)printf("WMRemoveTextChunks\n");
2180 if(removed
== tPtr
->paragraphs
|| which
==0) {
2181 tPtr
->paragraphs
= removed
->next
;
2183 WMSetTextCurrentParagraph(tPtr
, which
-1);
2184 prior
= tPtr
->currentPara
;
2186 prior
->next
= removed
->next
;
2195 /* set what is known as the currentPara in the tPtr. */
2196 /* negative number means: "gib me last chunk" */
2198 WMSetTextCurrentParagraph(WMText
*tPtr
, int current
)
2203 if(!tPtr
|| current
<0) return;
2205 tPtr
->currentPara
= tPtr
->paragraphs
;
2208 tmp
= tPtr
->paragraphs
;
2209 while(tmp
->next
&& ((current
==-23)?1:i
++<current
)) {
2210 //while(tmp && i++<current) {
2212 } tPtr
->currentPara
= tmp
;
2213 //? want to do this?if(tmp) tPtr->currentChunk = tmp
2218 WMGetTextParagraphs(WMText
*tPtr
)
2223 tmp
= tPtr
->paragraphs
;
2233 WMGetTextCurrentParagraph(WMText
*tPtr
)
2238 if(!tPtr
) return current
;
2239 if(!tPtr
->currentPara
) return current
;
2240 if(!tPtr
->paragraphs
) return current
;
2241 tmp
= tPtr
->paragraphs
;
2244 if(tmp
== tPtr
->currentPara
)
2250 /* set what is known as the currentChunk within the currently
2251 selected currentPara (or the first paragraph in the document). */
2253 WMSetTextCurrentChunk(WMText
*tPtr
, int current
)
2259 tPtr
->currentChunk
= NULL
;
2260 if(!tPtr
->currentPara
) {
2261 tPtr
->currentPara
= tPtr
->paragraphs
;
2262 if(!tPtr
->currentPara
)
2267 tPtr
->currentChunk
= tPtr
->currentPara
->chunks
;
2270 tmp
= tPtr
->currentPara
->chunks
;
2272 while(tmp
->next
&& ((current
<0)?1:i
++<current
))
2274 } tPtr
->currentChunk
= tmp
;
2279 WMRemoveTextChunk(WMText
*tPtr
, int which
)
2281 Chunk
*prior
, *removed
;
2283 if(!tPtr
|| which
<0) return;
2284 para
= tPtr
->currentPara
;
2287 WMSetTextCurrentChunk(tPtr
, which
);
2288 removed
= tPtr
->currentChunk
;
2289 if(!removed
) return;
2290 if(removed
== tPtr
->currentPara
->chunks
|| which
==0) {
2291 para
->chunks
= removed
->next
;
2293 WMSetTextCurrentChunk(tPtr
, which
-1);
2294 prior
= tPtr
->currentChunk
;
2296 prior
->next
= removed
->next
;
2298 if(removed
->type
== ctText
) {
2299 wgdbFree(removed
->text
);
2300 WMReleaseFont(removed
->font
);
2301 WMReleaseColor(removed
->color
);
2303 WMReleasePixmap(removed
->pixmap
);
2310 WMGetTextCurrentChunk(WMText
*tPtr
)
2316 if(!tPtr
->currentChunk
) return 0;
2317 if(!tPtr
->currentPara
) {
2318 tPtr
->currentPara
= tPtr
->paragraphs
;
2319 if(!tPtr
->currentPara
)
2323 tmp
= tPtr
->currentPara
->chunks
;
2325 if(tmp
== tPtr
->currentChunk
)
2334 WMGetTextChunks(WMText
*tPtr
)
2338 if(!tPtr
|| !tPtr
->currentPara
) return 0;
2339 tmp
= tPtr
->currentPara
->chunks
;
2347 WMShowTextRuler(WMText
*tPtr
, Bool show
)
2350 if(tPtr
->monoFont
) show
= False
;
2352 tPtr
->rulerShown
= show
;
2353 if(show
) WMMapWidget(tPtr
->ruler
);
2354 else WMUnmapWidget(tPtr
->ruler
);
2355 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2359 WMGetTextRulerShown(WMText
*tPtr
)
2361 if(!tPtr
) return False
;
2362 return tPtr
->rulerShown
;
2366 WMSetTextRulerMargin(WMText
*tPtr
, char which
, short pixels
)
2369 if(tPtr
->monoFont
) return;
2370 WMSetRulerMargin(tPtr
->ruler
, which
, pixels
);
2371 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2375 WMGetTextRulerMargin(WMText
*tPtr
, char which
)
2380 return WMGetRulerMargin(tPtr
->ruler
, which
);
2385 WMShowTextRulerTabs(WMText
*tPtr
, Bool show
)
2388 if(tPtr
->monoFont
) return;
2389 WMShowRulerTabs(tPtr
->ruler
, show
);
2393 WMSetTextMonoFont(WMText
*tPtr
, Bool mono
)
2396 if(mono
&& tPtr
->rulerShown
)
2397 WMShowTextRuler(tPtr
, False
);
2399 tPtr
->monoFont
= mono
;
2403 WMGetTextMonoFont(WMText
*tPtr
)
2405 if(!tPtr
) return True
;
2406 return tPtr
->monoFont
;
2410 WMForceTextFocus(WMText
*tPtr
)
2414 if(tPtr
->clicked
.x
== -23 || tPtr
->clicked
.y
== 23)
2415 cursorToTextPosition(tPtr
, 100, 100); /* anyplace */
2417 cursorToTextPosition(tPtr
, tPtr
->clicked
.x
, tPtr
->clicked
.y
);
2422 WMSetTextEditable(WMText
*tPtr
, Bool editable
)
2425 tPtr
->editable
= editable
;
2430 WMGetTextEditable(WMText
*tPtr
)
2433 return tPtr
->editable
;
2438 WMScrollText(WMText
*tPtr
, int amount
)
2441 if(amount
== 0 || !tPtr
) return;
2442 if(!tPtr
->view
->flags
.realized
) return;
2445 if(tPtr
->vpos
> 0) {
2446 if(tPtr
->vpos
> amount
) tPtr
->vpos
+= amount
;
2450 int limit
= tPtr
->docHeight
- tPtr
->visibleH
;
2451 if(tPtr
->vpos
< limit
) {
2452 if(tPtr
->vpos
< limit
-amount
) tPtr
->vpos
+= amount
;
2453 else tPtr
->vpos
= limit
;
2457 if(scroll
&& tPtr
->vpos
!= tPtr
->prevVpos
) {
2458 updateScrollers(tPtr
);
2459 drawDocumentPartsOnPixmap(tPtr
, False
);
2462 tPtr
->prevVpos
= tPtr
->vpos
;
2467 WMPageText(WMText
*tPtr
, Bool scrollUp
)
2470 if(!tPtr
->view
->flags
.realized
) return;
2472 return WMScrollText(tPtr
, scrollUp
2473 ? tPtr
->visibleH
:-tPtr
->visibleH
);
2477 WMIgnoreTextNewline(WMText
*tPtr
, Bool ignore
)
2480 tPtr
->ignoreNewLine
= ignore
;
2485 WMSetTextHasHorizontalScroller(WMText
*tPtr
, Bool flag
)
2491 rh
= tPtr
->rulerShown
?40:0;
2492 tPtr
->hasHscroller
= flag
;
2494 WMMapWidget(tPtr
->hscroller
);
2495 tPtr
->visibleH
= tPtr
->view
->size
.height
-rh
-22;
2497 WMUnmapWidget(tPtr
->hscroller
);
2498 tPtr
->visibleH
= tPtr
->view
->size
.height
-rh
;
2500 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2506 WMSetTextHasVerticalScroller(WMText
*tPtr
, Bool flag
)
2509 tPtr
->hasVscroller
= flag
;
2511 WMMapWidget(tPtr
->vscroller
);
2512 tPtr
->visibleW
= tPtr
->view
->size
.width
-22;
2513 WMSetRulerOffset(tPtr
->ruler
, 22); /* scrollbar width + 2 */
2515 WMUnmapWidget(tPtr
->vscroller
);
2516 tPtr
->visibleW
= tPtr
->view
->size
.width
;
2517 WMSetRulerOffset(tPtr
->ruler
, 2);
2519 resizeText(tPtr
->view
->delegate
, tPtr
->view
);
2526 WMRefreshText(WMText
*tPtr
, int vpos
, int hpos
)
2532 if(tPtr
->frozen
|| !tPtr
->view
->flags
.realized
)
2536 XClearArea(tPtr
->view
->screen
->display
, tPtr
->view
->window
,
2537 22, (tPtr
->rulerShown
)?45:5,
2538 tPtr
->visibleW
, tPtr
->visibleH
, True
);
2540 calcDocExtents(tPtr
);
2542 printf("vpos:%d tPtr->docHeight%d tPtr->visibleH%d \n",
2543 vpos, tPtr->docHeight, tPtr->visibleH);
2546 // tPtr->vpos = vpos;
2548 if(vpos < 0 || tPtr->docHeight < tPtr->visibleH)
2550 else if(vpos-tPtr->visibleH>tPtr->docHeight)
2551 tPtr->vpos = vpos-tPtr->docHeight-tPtr->visibleH-tPtr->docHeight;
2553 tPtr->vpos = tPtr->docHeight-tPtr->visibleH;
2557 if(hpos
< 0 || hpos
> tPtr
->docWidth
)
2562 drawDocumentPartsOnPixmap(tPtr
, True
);
2563 updateScrollers(tPtr
);
2567 /* would be nice to have in WINGs proper... */
2569 changeFontProp(char *fname
, char *newprop
, short which
)
2571 char before
[128], prop
[128], after
[128];
2583 if(part
==which
) bptr
= prop
;
2584 else if(part
==which
+1) bptr
= after
;
2591 snprintf(fname
, 255, "%s-%s%s", before
, newprop
, after
);
2594 /* TODO: put in wfont? */
2596 WMGetFontPlain(WMScreen
*scrPtr
, WMFont
*font
)
2606 WMGetFontBold(WMScreen
*scrPtr
, WMFont
*font
)
2608 WMFont
*newfont
=NULL
;
2610 if(!scrPtr
|| !font
)
2612 snprintf(fname
, 255, font
->name
);
2613 changeFontProp(fname
, "bold", 2);
2614 newfont
= WMCreateNormalFont(scrPtr
, fname
);
2621 WMGetFontItalic(WMScreen
*scrPtr
, WMFont
*font
)
2623 WMFont
*newfont
=NULL
;
2625 if(!scrPtr
|| !font
)
2627 snprintf(fname
, 255, font
->name
);
2628 changeFontProp(fname
, "o", 3);
2629 newfont
= WMCreateNormalFont(scrPtr
, fname
);
2636 WMGetFontOfSize(WMScreen
*scrPtr
, WMFont
*font
, short size
)
2639 if(!scrPtr
|| !font
|| size
<1)
2646 WMFreezeText(WMText
*tPtr
)
2650 tPtr
->frozen
= True
;
2654 WMThawText(WMText
*tPtr
)
2658 tPtr
->frozen
= False
;
2663 WMSetTextDefaultAlignment(WMText
*tPtr
, WMAlignment alignment
)
2666 if(tPtr
->monoFont
) return;
2668 tPtr
->dAlignment
= alignment
;
2669 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2675 WMSetTextBackgroundColor(WMText
*tPtr
, WMColor
*color
)
2683 tPtr
->bg
= WMWhiteColor(tPtr
->view
->screen
);
2685 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->bg
);
2686 WMRefreshText(tPtr
, tPtr
->vpos
, tPtr
->hpos
);
2690 WMSetTextDefaultColor(WMText
*tPtr
, WMColor
*color
)
2696 tPtr
->dColor
= color
;
2698 tPtr
->dColor
= WMBlackColor(tPtr
->view
->screen
);
2702 WMSetTextDefaultFont(WMText
*tPtr
, WMFont
*font
)
2710 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2714 WMSetTextUseFixedPitchFont(Text
*tPtr
, Bool fixed
)
2719 tPtr
->dFont
= WMCreateFontSet(tPtr
->view
->screen
,
2720 "lucidasanstypewriter-12");
2722 tPtr
->dFont
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
2723 tPtr
->fixedPitch
= fixed
;
2727 WMSetTextParser(WMText
*tPtr
, WMParseAction
*parser
)
2730 if(tPtr
->monoFont
) return;
2731 tPtr
->parser
= parser
;
2736 WMGetTextParserActions(WMText
*tPtr
)
2738 WMParserActions null
;
2739 if(!tPtr
) return null
;
2745 WMGetTextAll(WMText
*tPtr
)
2753 if(!tPtr
) return NULL
;
2755 para
= tPtr
->paragraphs
;
2757 chunk
= para
->chunks
;
2759 if(chunk
->type
== ctText
) {
2760 if(chunk
->text
) length
+= chunk
->chars
;
2762 printf("getting image \n");
2764 chunk
= chunk
->next
;
2767 if(tPtr
->ignoreNewLine
) break;
2768 length
+= 4; // newlines
2772 text
= wmalloc(length
+1);
2774 para
= tPtr
->paragraphs
;
2776 chunk
= para
->chunks
;
2778 if(chunk
->type
== ctText
) {
2780 snprintf(&text
[where
], chunk
->chars
+1, "%s", chunk
->text
);
2781 where
+= chunk
->chars
;
2784 printf("writing image \n");
2786 chunk
= chunk
->next
;
2788 if(tPtr
->ignoreNewLine
) break;
2789 snprintf(&text
[where
++], 2, "\n");
2791 } text
[where
] = '\0';
2797 WMSetTextWriter(WMText
*tPtr
, WMParseAction
*writer
)
2803 tPtr
->writer
= writer
;