1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-19 20:38:08 +01:00
Files
wmaker/WINGs/wtext.c
dan ddde229665 - fixed speckles (white dots) on dithered images (bug still present on the
PowerPC architecture, because on LinuxPPC char is unsigned by default, not
  signed like on the other platforms).
  Bug fixed by Philip Derrin <philipd@student.unsw.edu.au>
- miscelaneous bug fixes

We would like people with cvs access experimenting the white 'speckles' on
images to test if they still have the problem.
2001-05-10 00:16:28 +00:00

4321 lines
116 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* WINGs WMText: multi-line/font/color/graphic text widget, by Nwanua. */
#include "WINGsP.h"
#include <ctype.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#define DO_BLINK 0
/* TODO:
* - verify what happens with XK_return in insertTextInt...
* - selection code... selects can be funny if it crosses over. use rect?
* - also inspect behaviour for WACenter and WARight
* - what if a widget grabs the click... howto say: "pressed me"?
* note that WMCreateEventHandler takes one data, but need widget & tPtr
* - FIX: graphix blocks MUST be skipped if monoFont even though they exist!
* - check if support for Horizontal Scroll is complete
* - Tabs now are simply replaced by 4 spaces...
* - redo blink code to reduce paint event... use pixmap buffer...
* - add paragraph support (full) and '\n' code in getStream..
*/
/* a Section is a section of a TextBlock that describes what parts
of a TextBlock has been laid out on which "line"...
o this greatly aids redraw, scroll and selection.
o this is created during layoutLine, but may be later modified.
o there may be many Sections per TextBlock, hence the array */
typedef struct {
unsigned int x, y; /* where to draw it from */
unsigned short w, h; /* its width and height */
unsigned short begin; /* where the layout begins */
unsigned short end ; /* where it ends */
unsigned short max_d; /* a quick hack for layOut if(laidOut) */
unsigned short last:1; /* is it the last section on a "line"? */
unsigned int _y:31; /* the "line" it and other textblocks are on */
} Section;
/* a TextBlock is a node in a doubly-linked list of TextBlocks containing:
o text for the block, color and font
o or a pointer to the pixmap
o OR a pointer to the widget and the (text) description for its graphic
*/
typedef struct _TextBlock {
struct _TextBlock *next; /* next text block in linked list */
struct _TextBlock *prior; /* prior text block in linked list */
char *text; /* pointer to text (could be kanji) */
/* or to the object's description */
union {
WMFont *font; /* the font */
WMWidget *widget; /* the embedded widget */
WMPixmap *pixmap; /* the pixmap */
} d; /* description */
unsigned short used; /* number of chars in this block */
unsigned short allocated; /* size of allocation (in chars) */
WMColor *color; /* the color */
Section *sections; /* the region for layouts (a growable array) */
/* an _array_! of size _nsections_ */
unsigned short s_begin; /* where the selection begins */
unsigned short s_end; /* where it ends */
unsigned int first:1; /* first TextBlock in paragraph */
unsigned int blank:1; /* ie. blank paragraph */
unsigned int kanji:1; /* is of 16-bit characters or not */
unsigned int graphic:1; /* graphic or text: text=0 */
unsigned int object:1; /* embedded object or pixmap */
unsigned int underlined:1; /* underlined or not */
unsigned int selected:1; /* selected or not */
unsigned int nsections:8; /* over how many "lines" a TextBlock wraps */
int script:8; /* script in points: negative for subscript */
unsigned int marginN:8; /* which of the margins in the tPtr to use */
unsigned int nClicks:2; /* single, double, triple clicks */
unsigned int RESERVED:7;
} TextBlock;
/* I'm lazy: visible.h vs. visible.size.height :-) */
typedef struct {
int y, x, h, w;
} myRect;
typedef struct W_Text {
W_Class widgetClass; /* the class number of this widget */
W_View *view; /* the view referring to this instance */
WMRuler *ruler; /* the ruler widget to manipulate paragraphs */
WMScroller *vS; /* the vertical scroller */
unsigned int vpos; /* the current vertical position */
unsigned int prevVpos; /* the previous vertical position */
WMScroller *hS; /* the horizontal scroller */
unsigned int hpos; /* the current horizontal position */
unsigned int prevHpos; /* the previous horizontal position */
WMFont *dFont; /* the default font */
WMColor *dColor; /* the default color */
WMPixmap *dBulletPix; /* the default pixmap for bullets */
GC bgGC; /* the background GC to draw with */
GC fgGC; /* the foreground GC to draw with */
GC stippledGC; /* the GC to overlay selected graphics with */
Pixmap db; /* the buffer on which to draw */
WMPixmap *bgPixmap; /* the background pixmap */
myRect visible; /* the actual rectangle that can be drawn into */
myRect cursor; /* the position and (height) of cursor */
myRect sel; /* the selection rectangle */
WMPoint clicked; /* where in the _document_ was clicked */
unsigned short tpos; /* the position in the currentTextBlock */
unsigned short docWidth; /* the width of the entire document */
unsigned int docHeight; /* the height of the entire document */
TextBlock *firstTextBlock;
TextBlock *lastTextBlock;
TextBlock *currentTextBlock;
WMArray *gfxItems; /* a nice array of graphic items */
#if DO_BLINK
WMHandlerID timerID; /* for nice twinky-winky */
#endif
WMAction *parser;
WMAction *writer;
WMTextDelegate *delegate;
Time lastClickTime;
WMRulerMargins *margins; /* an array of margins */
unsigned int nMargins:7; /* the total number of margins in use */
struct {
unsigned int monoFont:1; /* whether to ignore formats and graphic */
unsigned int focused:1; /* whether this instance has input focus */
unsigned int editable:1; /* "silly user, you can't edit me" */
unsigned int ownsSelection:1; /* "I ownz the current selection!" */
unsigned int pointerGrabbed:1;/* "heh, gib me pointer" */
unsigned int extendSelection:1; /* shift-drag to select more regions */
unsigned int rulerShown:1; /* whether the ruler is shown or not */
unsigned int frozen:1; /* whether screen updates are to be made */
unsigned int cursorShown:1; /* whether to show the cursor */
unsigned int acceptsGraphic:1;/* accept graphic when dropped */
unsigned int horizOnDemand:1;/* if a large image should appear*/
unsigned int needsLayOut:1; /* in case of Append/Deletes */
unsigned int ignoreNewLine:1;/* turn it into a ' ' in streams > 1 */
unsigned int indentNewLine:1;/* add " " for a newline typed */
unsigned int laidOut:1; /* have the TextBlocks all been laid out */
unsigned int waitingForSelection:1; /* I don't wanna wait in vain... */
unsigned int prepend:1; /* prepend=1, append=0 (for parsers) */
WMAlignment alignment:2; /* the alignment for text */
WMReliefType relief:3; /* the relief to display with */
unsigned int isOverGraphic:2;/* the mouse is over a graphic */
unsigned int first:1; /* for plain text parsing, newline? */
/* unsigned int RESERVED:1; */
} flags;
} Text;
#define NOTIFY(T,C,N,A) {\
WMNotification *notif = WMCreateNotification(N,T,A);\
if ((T)->delegate && (T)->delegate->C)\
(*(T)->delegate->C)((T)->delegate,notif);\
WMPostNotification(notif);\
WMReleaseNotification(notif);}
#define TYPETEXT 0
#if 0
/* just to print blocks of text not terminated by \0 */
static void
output(char *ptr, int len)
{
char *s;
s = wmalloc(len+1);
memcpy(s, ptr, len);
s[len] = 0;
/* printf(" s is [%s] (%d)\n", s, strlen(s)); */
printf("[%s]\n", s);
wfree(s);
}
#endif
#if DO_BLINK
#define CURSOR_BLINK_ON_DELAY 600
#define CURSOR_BLINK_OFF_DELAY 400
#endif
#define STIPPLE_WIDTH 8
#define STIPPLE_HEIGHT 8
static unsigned char STIPPLE_BITS[] = {
0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa
};
static char *default_bullet[] = {
"6 6 4 1",
" c None s None", ". c black",
"X c white", "o c #808080",
" ... ",
".XX.. ",
".XX..o",
".....o",
" ...oo",
" ooo "};
static void handleEvents(XEvent *event, void *data);
static void layOutDocument(Text *tPtr);
static void updateScrollers(Text *tPtr);
static int
getMarginNumber(Text *tPtr, WMRulerMargins *margins)
{
unsigned int i=0;
for(i=0; i < tPtr->nMargins; i++) {
if(WMIsMarginEqualToMargin(&tPtr->margins[i], margins))
return i;
}
return -1;
}
static int
newMargin(Text *tPtr, WMRulerMargins *margins)
{
int n;
if (!margins) {
tPtr->margins[0].retainCount++;
return 0;
}
n = getMarginNumber(tPtr, margins);
if (n == -1) {
if(tPtr->nMargins >= 127) {
n = tPtr->nMargins-1;
return n;
}
tPtr->margins = wrealloc(tPtr->margins,
(++tPtr->nMargins)*sizeof(WMRulerMargins));
n = tPtr->nMargins-1;
tPtr->margins[n].left = margins->left;
tPtr->margins[n].first = margins->first;
tPtr->margins[n].body = margins->body;
tPtr->margins[n].right = margins->right;
/* for each tab... */
tPtr->margins[n].retainCount = 1;
} else {
tPtr->margins[n].retainCount++;
}
return n;
}
static Bool
sectionWasSelected(Text *tPtr, TextBlock *tb, XRectangle *rect, int s)
{
unsigned short i, w, lw, selected = False, extend = False;
myRect sel;
/* if selection rectangle completely encloses the section */
if ((tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
&& (tb->sections[s]._y + tb->sections[s].h
<= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
sel.x = 0;
sel.w = tPtr->visible.w;
selected = extend = True;
/* or if it starts on a line and then goes further down */
} else if ((tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
&& (tb->sections[s]._y + tb->sections[s].h
<= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)
&& (tb->sections[s]._y + tb->sections[s].h
>= tPtr->visible.y + tPtr->sel.y) ) {
sel.x = WMAX(tPtr->sel.x, tPtr->clicked.x);
sel.w = tPtr->visible.w;
selected = extend = True;
/* or if it begins before a line, but ends on it */
} else if ((tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
&& (tb->sections[s]._y + tb->sections[s].h
>= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)
&& (tb->sections[s]._y
<= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
if (1||tPtr->sel.x + tPtr->sel.w > tPtr->clicked.x)
sel.w = tPtr->sel.x + tPtr->sel.w;
else
sel.w = tPtr->sel.x;
sel.x = 0;
selected = True;
/* or if the selection rectangle lies entirely within a line */
} else if ((tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
&& (tPtr->sel.w >= 2)
&& (tb->sections[s]._y + tb->sections[s].h
>= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
sel.x = tPtr->sel.x;
sel.w = tPtr->sel.w;
selected = True;
}
if (selected) {
selected = False;
/* if not within (modified) selection rectangle */
if ( tb->sections[s].x > sel.x + sel.w
|| tb->sections[s].x + tb->sections[s].w < sel.x)
return False;
if (tb->graphic) {
if ( tb->sections[s].x + tb->sections[s].w <= sel.x + sel.w
&& tb->sections[s].x >= sel.x) {
rect->width = tb->sections[s].w;
rect->x = tb->sections[s].x;
selected = True;
}
} else {
i = tb->sections[s].begin;
lw = 0;
if (0&& tb->sections[s].x >= sel.x) {
tb->s_begin = tb->sections[s].begin;
goto _selEnd;
}
while (++i <= tb->sections[s].end) {
w = WMWidthOfString(tb->d.font, &(tb->text[i-1]), 1);
lw += w;
if (lw + tb->sections[s].x >= sel.x
|| i == tb->sections[s].end ) {
lw -= w;
i--;
tb->s_begin = (tb->selected? WMIN(tb->s_begin, i) : i);
break;
}
}
if (i > tb->sections[s].end) {
printf("WasSelected: (i > tb->sections[s].end) \n");
return False;
}
_selEnd: rect->x = tb->sections[s].x + lw;
lw = 0;
while(++i <= tb->sections[s].end) {
w = WMWidthOfString(tb->d.font, &(tb->text[i-1]), 1);
lw += w;
if (lw + rect->x >= sel.x + sel.w
|| i == tb->sections[s].end ) {
if (i != tb->sections[s].end) {
lw -= w;
i--;
}
rect->width = lw;
if (tb->sections[s].last && sel.x + sel.w
>= tb->sections[s].x + tb->sections[s].w
&& extend ) {
rect->width += (tPtr->visible.w - rect->x - lw);
}
tb->s_end = (tb->selected? WMAX(tb->s_end, i) : i);
selected = True;
break;
}
}
}
}
if (selected) {
rect->y = tb->sections[s]._y - tPtr->vpos;
rect->height = tb->sections[s].h;
if(tb->graphic) { printf("DEBUG: graphic s%d h%d\n", s,tb->sections[s].h);}
}
return selected;
}
static void
setSelectionProperty(WMText *tPtr, WMFont *font, WMColor *color, int underlined)
{
TextBlock *tb;
int isFont=False;
tb = tPtr->firstTextBlock;
if (!tb || !tPtr->flags.ownsSelection)
return;
if(font && (!color || underlined==-1))
isFont = True;
while (tb) {
if (tPtr->flags.monoFont || tb->selected) {
if (tPtr->flags.monoFont || (tb->s_end - tb->s_begin == tb->used)
|| tb->graphic) {
if(isFont) {
if(!tb->graphic) {
WMReleaseFont(tb->d.font);
tb->d.font = WMRetainFont(font);
}
} else if(underlined !=-1) {
tb->underlined = underlined;
} else {
WMReleaseColor(tb->color);
tb->color = WMRetainColor(color);
}
} else if (tb->s_end <= tb->used && tb->s_begin < tb->s_end) {
TextBlock *midtb, *otb = tb;
if(underlined != -1) {
midtb = (TextBlock *) WMCreateTextBlockWithText(tPtr,
&(tb->text[tb->s_begin]), tb->d.font, tb->color,
False, (tb->s_end - tb->s_begin));
} else {
midtb = (TextBlock *) WMCreateTextBlockWithText(tPtr,
&(tb->text[tb->s_begin]),
(isFont?font:tb->d.font),
(isFont?tb->color:color),
False, (tb->s_end - tb->s_begin));
}
if (midtb) {
if(underlined != -1) {
midtb->underlined = underlined;
} else {
midtb->underlined = otb->underlined;
}
midtb->selected = !True;
midtb->s_begin = 0;
midtb->s_end = midtb->used;
tPtr->currentTextBlock = tb;
WMAppendTextBlock(tPtr, midtb);
tb = tPtr->currentTextBlock;
}
if (otb->used - otb->s_end > 0) {
TextBlock *ntb;
ntb = (TextBlock *)
WMCreateTextBlockWithText(tPtr,
&(otb->text[otb->s_end]), otb->d.font, otb->color,
False, otb->used - otb->s_end);
if (ntb) {
ntb->underlined = otb->underlined;
ntb->selected = False;
WMAppendTextBlock(tPtr, ntb);
tb = tPtr->currentTextBlock;
}
}
if (midtb) {
tPtr->currentTextBlock = midtb;
}
otb->selected = False;
otb->used = otb->s_begin;
}
}
tb = tb->next;
}
tPtr->flags.needsLayOut = True;
WMThawText(tPtr);
/* in case the size changed... */
if(isFont && tPtr->currentTextBlock) {
TextBlock *tb = tPtr->currentTextBlock;
printf("%d %d %d\n", tPtr->sel.y, tPtr->sel.h, tPtr->sel.w);
tPtr->sel.y = 3 + tb->sections[0]._y;
tPtr->sel.h = tb->sections[tb->nsections-1]._y - tb->sections[0]._y;
tPtr->sel.w = tb->sections[tb->nsections-1].w;
if(tb->sections[tb->nsections-1]._y != tb->sections[0]._y) {
tPtr->sel.x = 0;
}
printf("%d %d %d\n\n\n", tPtr->sel.y, tPtr->sel.h, tPtr->sel.w);
}
}
static Bool
removeSelection(Text *tPtr)
{
TextBlock *tb = NULL;
Bool first = False;
if (!(tb = tPtr->firstTextBlock))
return False;
while (tb) {
if (tb->selected) {
if(!first && !tb->graphic) {
WMReleaseFont(tPtr->dFont);
tPtr->dFont = WMRetainFont(tb->d.font);
first = True;
}
if ( (tb->s_end - tb->s_begin == tb->used) || tb->graphic) {
tPtr->currentTextBlock = tb;
if(tb->next) {
tPtr->tpos = 0;
} else if(tb->prior) {
if(tb->prior->graphic)
tPtr->tpos = 1;
else
tPtr->tpos = tb->prior->used;
} else tPtr->tpos = 0;
WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
tb = tPtr->currentTextBlock;
continue;
} else if (tb->s_end <= tb->used) {
memmove(&(tb->text[tb->s_begin]),
&(tb->text[tb->s_end]), tb->used - tb->s_end);
tb->used -= (tb->s_end - tb->s_begin);
tb->selected = False;
tPtr->tpos = tb->s_begin;
}
}
tb = tb->next;
}
return True;
}
static TextBlock *
getFirstNonGraphicBlockFor(TextBlock *tb, short dir)
{
TextBlock *hold = tb;
if (!tb)
return NULL;
while (tb) {
if (!tb->graphic)
break;
tb = (dir? tb->next : tb->prior);
}
if(!tb) {
tb = hold;
while (tb) {
if (!tb->graphic)
break;
tb = (dir? tb->prior : tb->next);
}
}
if(!tb)
return NULL;
return tb;
}
static Bool
updateStartForCurrentTextBlock(Text *tPtr, int x, int y, int *dir,
TextBlock *tb)
{
if (tPtr->flags.monoFont && tb->graphic) {
tb = getFirstNonGraphicBlockFor(tb, *dir);
if(!tb)
return 0;
if (tb->graphic) {
tPtr->currentTextBlock =
(dir? tPtr->lastTextBlock : tPtr->firstTextBlock);
tPtr->tpos = 0;
return 0;
}
}
if(!tb->sections) {
layOutDocument(tPtr);
return 0;
}
*dir = !(y <= tb->sections[0].y);
if(*dir) {
if ( ( y <= tb->sections[0]._y + tb->sections[0].h )
&& (y >= tb->sections[0]._y ) ) {
/* if it's on the same line */
if(x < tb->sections[0].x)
*dir = 0;
}
} else {
if ( ( y <= tb->sections[tb->nsections-1]._y
+ tb->sections[tb->nsections-1].h )
&& (y >= tb->sections[tb->nsections-1]._y ) ) {
/* if it's on the same line */
if(x > tb->sections[tb->nsections-1].x)
*dir = 1;
}
}
return 1;
}
static void
paintText(Text *tPtr)
{
TextBlock *tb;
WMFont *font;
GC gc, greyGC;
char *text;
int len, y, c, s, done=False, prev_y=-23, dir /* 1 = down */;
WMScreen *scr = tPtr->view->screen;
Display *dpy = tPtr->view->screen->display;
Window win = tPtr->view->window;
WMColor *color;
if (!tPtr->view->flags.realized || !tPtr->db || tPtr->flags.frozen)
return;
XFillRectangle(dpy, tPtr->db, tPtr->bgGC,
0, 0, tPtr->visible.w, tPtr->visible.h);
if (tPtr->bgPixmap) {
WMDrawPixmap(tPtr->bgPixmap, tPtr->db,
(tPtr->visible.w-tPtr->visible.x-tPtr->bgPixmap->width)/2,
(tPtr->visible.h-tPtr->visible.y-tPtr->bgPixmap->height)/2);
}
if (! (tb = tPtr->currentTextBlock)) {
if (! (tb = tPtr->firstTextBlock)) {
goto _copy_area;
}
}
if (tPtr->flags.ownsSelection) {
color = WMGrayColor(scr);
greyGC = WMColorGC(color);
WMReleaseColor(color);
}
done = False;
/* first, which direction? Don't waste time looking all over,
since the parts to be drawn will most likely be near what
was previously drawn */
if(!updateStartForCurrentTextBlock(tPtr, 0, tPtr->vpos, &dir, tb))
goto _copy_area;
while(tb) {
if (tb->graphic && tPtr->flags.monoFont)
goto _getSibling;
if(dir) {
if(tPtr->vpos <= tb->sections[tb->nsections-1]._y
+ tb->sections[tb->nsections-1].h)
break;
} else {
if(tPtr->vpos >= tb->sections[tb->nsections-1]._y
+ tb->sections[tb->nsections-1].h)
break;
}
_getSibling:
if(dir) {
if(tb->next)
tb = tb->next;
else break;
} else {
if(tb->prior)
tb = tb->prior;
else break;
}
}
/* first, place all text that can be viewed */
while (!done && tb) {
if (tb->graphic) {
tb = tb->next;
continue;
}
tb->selected = False;
for(s=0; s<tb->nsections && !done; s++) {
if (tb->sections[s]._y > tPtr->vpos + tPtr->visible.h) {
done = True;
break;
}
if ( tb->sections[s].y + tb->sections[s].h < tPtr->vpos)
continue;
if (tPtr->flags.monoFont) {
font = tPtr->dFont;
gc = tPtr->fgGC;
} else {
font = tb->d.font;
gc = WMColorGC(tb->color);
}
if (tPtr->flags.ownsSelection) {
XRectangle rect;
if ( sectionWasSelected(tPtr, tb, &rect, s)) {
tb->selected = True;
XFillRectangle(dpy, tPtr->db, greyGC,
rect.x, rect.y, rect.width, rect.height);
}
}
prev_y = tb->sections[s]._y;
len = tb->sections[s].end - tb->sections[s].begin;
text = &(tb->text[tb->sections[s].begin]);
y = tb->sections[s].y - tPtr->vpos;
WMDrawString(scr, tPtr->db, gc, font,
tb->sections[s].x - tPtr->hpos, y, text, len);
if (!tPtr->flags.monoFont && tb->underlined) {
XDrawLine(dpy, tPtr->db, gc,
tb->sections[s].x - tPtr->hpos,
y + font->y + 1,
tb->sections[s].x + tb->sections[s].w - tPtr->hpos,
y + font->y + 1);
}
}
tb = (!done? tb->next : NULL);
}
/* now , show all graphic items that can be viewed */
c = WMGetArrayItemCount(tPtr->gfxItems);
if (c > 0 && !tPtr->flags.monoFont) {
int j, h;
for(j=0; j<c; j++) {
tb = (TextBlock *) WMGetFromArray(tPtr->gfxItems, j);
/* if it's not viewable, and mapped, unmap it */
if (tb->sections[0]._y + tb->sections[0].h <= tPtr->vpos
|| tb->sections[0]._y >= tPtr->vpos + tPtr->visible.h ) {
if(tb->object) {
if ((W_VIEW(tb->d.widget))->flags.mapped) {
WMUnmapWidget(tb->d.widget);
}
}
} else {
/* if it's viewable, and not mapped, map it */
if(tb->object) {
W_View *view = W_VIEW(tb->d.widget);
if (!view->flags.realized)
WMRealizeWidget(tb->d.widget);
if(!view->flags.mapped) {
XMapWindow(view->screen->display, view->window);
XFlush(view->screen->display);
view->flags.mapped = 1;
}
}
if(tb->object) {
WMMoveWidget(tb->d.widget,
tb->sections[0].x + tPtr->visible.x - tPtr->hpos,
tb->sections[0].y + tPtr->visible.y - tPtr->vpos);
h = WMWidgetHeight(tb->d.widget) + 1;
} else {
WMDrawPixmap(tb->d.pixmap, tPtr->db,
tb->sections[0].x - tPtr->hpos,
tb->sections[0].y - tPtr->vpos);
h = tb->d.pixmap->height + 1;
}
if (tPtr->flags.ownsSelection) {
XRectangle rect;
if ( sectionWasSelected(tPtr, tb, &rect, 0)) {
Drawable d = (0&&tb->object?
(WMWidgetView(tb->d.widget))->window : tPtr->db);
tb->selected = True;
XFillRectangle(dpy, d, tPtr->stippledGC,
/*XFillRectangle(dpy, tPtr->db, tPtr->stippledGC,*/
rect.x, rect.y, rect.width, rect.height);
}
}
if (!tPtr->flags.monoFont && tb->underlined) {
XDrawLine(dpy, tPtr->db, WMColorGC(tb->color),
tb->sections[0].x - tPtr->hpos,
tb->sections[0].y + h - tPtr->vpos,
tb->sections[0].x + tb->sections[0].w - tPtr->hpos,
tb->sections[0].y + h - tPtr->vpos);
}
}
}
}
_copy_area:
if (tPtr->flags.editable && tPtr->flags.cursorShown
&& tPtr->cursor.x != -23 && tPtr->flags.focused) {
int y = tPtr->cursor.y - tPtr->vpos;
XDrawLine(dpy, tPtr->db, tPtr->fgGC,
tPtr->cursor.x, y,
tPtr->cursor.x, y + tPtr->cursor.h);
}
XCopyArea(dpy, tPtr->db, win, tPtr->bgGC, 0, 0,
tPtr->visible.w, tPtr->visible.h,
tPtr->visible.x, tPtr->visible.y);
W_DrawRelief(scr, win, 0, 0,
tPtr->view->size.width, tPtr->view->size.height,
tPtr->flags.relief);
if (tPtr->ruler && tPtr->flags.rulerShown)
XDrawLine(dpy, win, tPtr->fgGC,
2, 42, tPtr->view->size.width-4, 42);
}
static void
mouseOverObject(Text *tPtr, int x, int y)
{
TextBlock *tb;
Bool result = False;
x -= tPtr->visible.x;
x += tPtr->hpos;
y -= tPtr->visible.y;
y += tPtr->vpos;
if(tPtr->flags.ownsSelection) {
if(tPtr->sel.x <= x
&& tPtr->sel.y <= y
&& tPtr->sel.x + tPtr->sel.w >= x
&& tPtr->sel.y + tPtr->sel.h >= y) {
tPtr->flags.isOverGraphic = 1;
result = True;
}
}
if(!result) {
int j, c = WMGetArrayItemCount(tPtr->gfxItems);
if (c<1)
tPtr->flags.isOverGraphic = 0;
for(j=0; j<c; j++) {
tb = (TextBlock *) WMGetFromArray(tPtr->gfxItems, j);
if(!tb || !tb->sections) {
tPtr->flags.isOverGraphic = 0;
return;
}
if(!tb->object) {
if(tb->sections[0].x <= x
&& tb->sections[0].y <= y
&& tb->sections[0].x + tb->sections[0].w >= x
&& tb->sections[0].y + tb->d.pixmap->height >= y ) {
tPtr->flags.isOverGraphic = 3;
result = True;
break;
}
}
}
}
if(!result)
tPtr->flags.isOverGraphic = 0;
tPtr->view->attribs.cursor = (result?
tPtr->view->screen->defaultCursor
: tPtr->view->screen->textCursor);
{
XSetWindowAttributes attribs;
attribs.cursor = tPtr->view->attribs.cursor;
XChangeWindowAttributes(tPtr->view->screen->display,
tPtr->view->window, CWCursor,
&attribs);
}
}
#if DO_BLINK
static void
blinkCursor(void *data)
{
Text *tPtr = (Text*)data;
if (tPtr->flags.cursorShown) {
tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY,
blinkCursor, data);
} else {
tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
blinkCursor, data);
}
paintText(tPtr);
tPtr->flags.cursorShown = !tPtr->flags.cursorShown;
}
#endif
static void
updateCursorPosition(Text *tPtr)
{
TextBlock *tb = NULL;
int x, y, h, s;
if(tPtr->flags.needsLayOut)
layOutDocument(tPtr);
if (! (tb = tPtr->currentTextBlock)) {
if (! (tb = tPtr->firstTextBlock)) {
WMFont *font = tPtr->dFont;
tPtr->tpos = 0;
tPtr->cursor.h = font->height + abs(font->height-font->y);
tPtr->cursor.y = 2;
tPtr->cursor.x = 2;
return;
}
}
if(tb->blank) {
tPtr->tpos = 0;
y = tb->sections[0].y;
h = tb->sections[0].h;
x = tb->sections[0].x;
} else if(tb->graphic) {
y = tb->sections[0].y;
h = tb->sections[0].h;
x = tb->sections[0].x;
if(tPtr->tpos == 1)
x += tb->sections[0].w;
} else {
if(tPtr->tpos > tb->used)
tPtr->tpos = tb->used;
for(s=0; s<tb->nsections-1; s++) {
if(tPtr->tpos >= tb->sections[s].begin
&& tPtr->tpos <= tb->sections[s].end)
break;
}
y = tb->sections[s]._y;
h = tb->sections[s].h;
x = tb->sections[s].x + WMWidthOfString(
(tPtr->flags.monoFont?tPtr->dFont:tb->d.font),
&tb->text[tb->sections[s].begin],
tPtr->tpos - tb->sections[s].begin);
}
tPtr->cursor.y = y;
tPtr->cursor.h = h;
tPtr->cursor.x = x;
/* scroll the bars if the cursor is not visible */
if(tPtr->flags.editable && tPtr->cursor.x != -23) {
if(tPtr->cursor.y+tPtr->cursor.h
> tPtr->vpos+tPtr->visible.y+tPtr->visible.h) {
tPtr->vpos +=
(tPtr->cursor.y+tPtr->cursor.h+10
- (tPtr->vpos+tPtr->visible.y+tPtr->visible.h));
} else if(tPtr->cursor.y < tPtr->vpos+tPtr->visible.y) {
tPtr->vpos -= (tPtr->vpos+tPtr->visible.y-tPtr->cursor.y);
}
}
updateScrollers(tPtr);
}
static void
cursorToTextPosition(Text *tPtr, int x, int y)
{
TextBlock *tb = NULL;
int done=False, s, pos, len, _w, _y, dir=1; /* 1 == "down" */
char *text;
if(tPtr->flags.needsLayOut)
layOutDocument(tPtr);
y += (tPtr->vpos - tPtr->visible.y);
if (y<0)
y = 0;
x -= (tPtr->visible.x - 2);
if (x<0)
x=0;
/* clicked is relative to document, not window... */
tPtr->clicked.x = x;
tPtr->clicked.y = y;
if (! (tb = tPtr->currentTextBlock)) {
if (! (tb = tPtr->firstTextBlock)) {
WMFont *font = tPtr->dFont;
tPtr->tpos = 0;
tPtr->cursor.h = font->height + abs(font->height-font->y);
tPtr->cursor.y = 2;
tPtr->cursor.x = 2;
return;
}
}
/* first, which direction? Most likely, newly clicked
position will be close to previous */
if(!updateStartForCurrentTextBlock(tPtr, x, y, &dir, tb))
return;
s = (dir? 0 : tb->nsections-1);
if ( y >= tb->sections[s]._y
&& y <= tb->sections[s]._y + tb->sections[s].h) {
goto _doneV;
}
/* get the first (or last) section of the TextBlock that
lies about the vertical click point */
done = False;
while (!done && tb) {
if (tPtr->flags.monoFont && tb->graphic) {
if( (dir?tb->next:tb->prior))
tb = (dir?tb->next:tb->prior);
continue;
}
s = (dir? 0 : tb->nsections-1);
while (!done && (dir? (s<tb->nsections) : (s>=0) )) {
if ( (dir? (y <= tb->sections[s]._y + tb->sections[s].h) :
( y >= tb->sections[s]._y ) ) ) {
done = True;
} else {
dir? s++ : s--;
}
}
if (!done) {
if ( (dir? tb->next : tb->prior)) {
tb = (dir ? tb->next : tb->prior);
} else {
pos = tb->used;
break; /* goto _doneH; */
}
}
}
if (s<0 || s>=tb->nsections) {
s = (dir? tb->nsections-1 : 0);
}
_doneV:
/* we have the line, which TextBlock on that line is it? */
pos = (dir?0:tb->sections[s].begin);
if (tPtr->flags.monoFont && tb->graphic) {
TextBlock *hold = tb;
tb = getFirstNonGraphicBlockFor(hold, dir);
if(!tb) {
tPtr->tpos = 0;
tb = hold;
s = 0;
goto _doNothing;
}
}
if(tb->blank)
_w = 0;
_y = tb->sections[s]._y;
while (tb) {
if (tPtr->flags.monoFont && tb->graphic) {
tb = (dir ? tb->next : tb->prior);
continue;
}
if (dir) {
if (tb->graphic) {
if(tb->object)
_w = WMWidgetWidth(tb->d.widget)-5;
else
_w = tb->d.pixmap->width-5;
if (tb->sections[0].x + _w >= x)
break;
} else {
text = &(tb->text[tb->sections[s].begin]);
len = tb->sections[s].end - tb->sections[s].begin;
_w = WMWidthOfString(tb->d.font, text, len);
if (tb->sections[s].x + _w >= x)
break;
}
} else {
if (tb->sections[s].x <= x)
break;
}
if ((dir? tb->next : tb->prior)) {
TextBlock *nxt = (dir? tb->next : tb->prior);
if (tPtr->flags.monoFont && nxt->graphic) {
nxt = getFirstNonGraphicBlockFor(nxt, dir);
if (!nxt) {
pos = (dir?0:tb->sections[s].begin);
tPtr->cursor.x = tb->sections[s].x;
goto _doneH;
}
}
if (_y != nxt->sections[dir?0:nxt->nsections-1]._y) {
/* this must be the last/first on this line. stop */
pos = (dir? tb->sections[s].end : 0);
tPtr->cursor.x = tb->sections[s].x;
if (!tb->blank) {
if (tb->graphic) {
if(tb->object)
tPtr->cursor.x += WMWidgetWidth(tb->d.widget);
else
tPtr->cursor.x += tb->d.pixmap->width;
} else if (pos > tb->sections[s].begin) {
tPtr->cursor.x +=
WMWidthOfString(tb->d.font,
&(tb->text[tb->sections[s].begin]),
pos - tb->sections[s].begin);
}
}
goto _doneH;
}
}
if ( (dir? tb->next : tb->prior)) {
tb = (dir ? tb->next : tb->prior);
} else {
done = True;
break;
}
if (tb)
s = (dir? 0 : tb->nsections-1);
}
/* we have said TextBlock, now where within it? */
if (tb) {
if(tb->graphic) {
int gw = (tb->object ?
WMWidgetWidth(tb->d.widget) : tb->d.pixmap->width);
tPtr->cursor.x = tb->sections[0].x;
if(x > tPtr->cursor.x + gw/2) {
pos = 1;
tPtr->cursor.x += gw;
} else {
printf("first %d\n", tb->first);
if(tb->prior) {
if(tb->prior->graphic) pos = 1;
else pos = tb->prior->used;
tb = tb->prior;
} else pos = 0;
}
s = 0;
goto _doneH;
} else {
WMFont *f = tb->d.font;
len = tb->sections[s].end - tb->sections[s].begin;
text = &(tb->text[tb->sections[s].begin]);
_w = x - tb->sections[s].x;
pos = 0;
while (pos<len && WMWidthOfString(f, text, pos+1) < _w)
pos++;
tPtr->cursor.x = tb->sections[s].x +
(pos? WMWidthOfString(f, text, pos) : 0);
pos += tb->sections[s].begin;
}
}
_doneH:
if(tb->graphic) {
tPtr->tpos = (pos<=1)? pos : 0;
} else {
tPtr->tpos = (pos<tb->used)? pos : tb->used;
}
_doNothing:
if (!tb)
printf("...for this app will surely crash :-)\n");
tPtr->currentTextBlock = tb;
tPtr->cursor.h = tb->sections[s].h;
tPtr->cursor.y = tb->sections[s]._y;
/* scroll the bars if the cursor is not visible */
if(tPtr->flags.editable && tPtr->cursor.x != -23) {
if(tPtr->cursor.y+tPtr->cursor.h
> tPtr->vpos+tPtr->visible.y+tPtr->visible.h) {
tPtr->vpos +=
(tPtr->cursor.y+tPtr->cursor.h+10
- (tPtr->vpos+tPtr->visible.y+tPtr->visible.h));
updateScrollers(tPtr);
} else if(tPtr->cursor.y < tPtr->vpos+tPtr->visible.y) {
tPtr->vpos -= (tPtr->vpos+tPtr->visible.y-tPtr->cursor.y);
updateScrollers(tPtr);
}
}
}
static void
updateScrollers(Text *tPtr)
{
if (tPtr->flags.frozen)
return;
if (tPtr->vS) {
if (tPtr->docHeight <= tPtr->visible.h) {
WMSetScrollerParameters(tPtr->vS, 0, 1);
tPtr->vpos = 0;
} else {
float hmax = (float)(tPtr->docHeight);
WMSetScrollerParameters(tPtr->vS,
((float)tPtr->vpos)/(hmax - (float)tPtr->visible.h),
(float)tPtr->visible.h/hmax);
}
} else tPtr->vpos = 0;
if (tPtr->hS) {
if (tPtr->docWidth <= tPtr->visible.w) {
WMSetScrollerParameters(tPtr->hS, 0, 1);
tPtr->hpos = 0;
} else {
float wmax = (float)(tPtr->docWidth);
WMSetScrollerParameters(tPtr->hS,
((float)tPtr->hpos)/(wmax - (float)tPtr->visible.w),
(float)tPtr->visible.w/wmax);
}
} else tPtr->hpos = 0;
}
static void
scrollersCallBack(WMWidget *w, void *self)
{
Text *tPtr = (Text *)self;
Bool scroll = False;
int which;
if (!tPtr->view->flags.realized || tPtr->flags.frozen)
return;
if (w == tPtr->vS) {
int height;
height = tPtr->visible.h;
which = WMGetScrollerHitPart(tPtr->vS);
switch(which) {
case WSDecrementLine:
if (tPtr->vpos > 0) {
if (tPtr->vpos>16) tPtr->vpos-=16;
else tPtr->vpos=0;
scroll=True;
}
break;
case WSIncrementLine: {
int limit = tPtr->docHeight - height;
if (tPtr->vpos < limit) {
if (tPtr->vpos<limit-16) tPtr->vpos+=16;
else tPtr->vpos=limit;
scroll = True;
}
}
break;
case WSDecrementPage:
if(((int)tPtr->vpos - (int)height) >= 0)
tPtr->vpos -= height;
else
tPtr->vpos = 0;
scroll = True;
break;
case WSIncrementPage:
tPtr->vpos += height;
if (tPtr->vpos > (tPtr->docHeight - height))
tPtr->vpos = tPtr->docHeight - height;
scroll = True;
break;
case WSKnob:
tPtr->vpos = WMGetScrollerValue(tPtr->vS)
* (float)(tPtr->docHeight - height);
scroll = True;
break;
case WSKnobSlot:
case WSNoPart:
break;
}
scroll = (tPtr->vpos != tPtr->prevVpos);
tPtr->prevVpos = tPtr->vpos;
}
if (w == tPtr->hS) {
int width = tPtr->visible.w;
which = WMGetScrollerHitPart(tPtr->hS);
switch(which) {
case WSDecrementLine:
if (tPtr->hpos > 0) {
if (tPtr->hpos>16) tPtr->hpos-=16;
else tPtr->hpos=0;
scroll=True;
}break;
case WSIncrementLine: {
int limit = tPtr->docWidth - width;
if (tPtr->hpos < limit) {
if (tPtr->hpos<limit-16) tPtr->hpos+=16;
else tPtr->hpos=limit;
scroll = True;
}
}break;
case WSDecrementPage:
if(((int)tPtr->hpos - (int)width) >= 0)
tPtr->hpos -= width;
else
tPtr->hpos = 0;
scroll = True;
break;
case WSIncrementPage:
tPtr->hpos += width;
if (tPtr->hpos > (tPtr->docWidth - width))
tPtr->hpos = tPtr->docWidth - width;
scroll = True;
break;
case WSKnob:
tPtr->hpos = WMGetScrollerValue(tPtr->hS)
* (float)(tPtr->docWidth - width);
scroll = True;
break;
case WSKnobSlot:
case WSNoPart:
break;
}
scroll = (tPtr->hpos != tPtr->prevHpos);
tPtr->prevHpos = tPtr->hpos;
}
if (scroll) {
updateScrollers(tPtr);
paintText(tPtr);
}
}
typedef struct {
TextBlock *tb;
unsigned short begin, end; /* what part of the text block */
} myLineItems;
static int
layOutLine(Text *tPtr, myLineItems *items, int nitems, int x, int y)
{
int i, j=0, lw = 0, line_height=0, max_d=0, len, n;
WMFont *font;
char *text;
TextBlock *tb, *tbsame=NULL;
if(!items || nitems == 0)
return 0;
for(i=0; i<nitems; i++) {
tb = items[i].tb;
if (tb->graphic) {
if (!tPtr->flags.monoFont) {
if(tb->object) {
WMWidget *wdt = tb->d.widget;
line_height = WMAX(line_height, WMWidgetHeight(wdt));
if (tPtr->flags.alignment != WALeft)
lw += WMWidgetWidth(wdt);
} else {
line_height = WMAX(line_height,
tb->d.pixmap->height + max_d);
if (tPtr->flags.alignment != WALeft)
lw += tb->d.pixmap->width;
}
}
} else {
font = (tPtr->flags.monoFont)?tPtr->dFont : tb->d.font;
max_d = WMAX(max_d, abs(font->height-font->y));
line_height = WMAX(line_height, font->height + max_d);
text = &(tb->text[items[i].begin]);
len = items[i].end - items[i].begin;
if (tPtr->flags.alignment != WALeft)
lw += WMWidthOfString(font, text, len);
}
}
if (tPtr->flags.alignment == WARight) {
j = tPtr->visible.w - lw;
} else if (tPtr->flags.alignment == WACenter) {
j = (int) ((float)(tPtr->visible.w - lw))/2.0;
}
for(i=0; i<nitems; i++) {
tb = items[i].tb;
if (tbsame == tb) { /* extend it, since it's on same line */
tb->sections[tb->nsections-1].end = items[i].end;
n = tb->nsections-1;
} else {
tb->sections = wrealloc(tb->sections,
(++tb->nsections)*sizeof(Section));
n = tb->nsections-1;
tb->sections[n]._y = y + max_d;
tb->sections[n].max_d = max_d;
tb->sections[n].x = x+j;
tb->sections[n].h = line_height;
tb->sections[n].begin = items[i].begin;
tb->sections[n].end = items[i].end;
}
tb->sections[n].last = (i+1 == nitems);
if (tb->graphic) {
if (!tPtr->flags.monoFont) {
if(tb->object) {
WMWidget *wdt = tb->d.widget;
tb->sections[n].y = max_d + y
+ line_height - WMWidgetHeight(wdt);
tb->sections[n].w = WMWidgetWidth(wdt);
} else {
tb->sections[n].y = y + line_height
+ max_d - tb->d.pixmap->height;
tb->sections[n].w = tb->d.pixmap->width;
}
x += tb->sections[n].w;
}
} else {
font = (tPtr->flags.monoFont)? tPtr->dFont : tb->d.font;
len = items[i].end - items[i].begin;
text = &(tb->text[items[i].begin]);
tb->sections[n].y = y+line_height-font->y;
tb->sections[n].w =
WMWidthOfString(font,
&(tb->text[tb->sections[n].begin]),
tb->sections[n].end - tb->sections[n].begin);
x += WMWidthOfString(font, text, len);
}
tbsame = tb;
}
return line_height;
}
static void
layOutDocument(Text *tPtr)
{
TextBlock *tb;
myLineItems *items = NULL;
unsigned int itemsSize=0, nitems=0, begin, end;
WMFont *font;
unsigned int x, y=0, lw = 0, width=0, bmargin;
char *start=NULL, *mark=NULL;
if ( tPtr->flags.frozen || (!(tb = tPtr->firstTextBlock)) )
return;
assert(tPtr->visible.w > 20);
tPtr->docWidth = tPtr->visible.w;
x = tPtr->margins[tb->marginN].first;
bmargin = tPtr->margins[tb->marginN].body;
/* only partial layOut needed: re-Lay only affected textblocks */
if (tPtr->flags.laidOut) {
tb = tPtr->currentTextBlock;
/* search backwards for textblocks on same line */
while (tb->prior) {
if (!tb->sections || tb->nsections<1) {
tb = tPtr->firstTextBlock;
tPtr->flags.laidOut = False;
y = 0;
goto _layOut;
}
if(!tb->prior->sections || tb->prior->nsections<1) {
tb = tPtr->firstTextBlock;
tPtr->flags.laidOut = False;
y = 0;
goto _layOut;
}
if (tb->sections[0]._y !=
tb->prior->sections[tb->prior->nsections-1]._y) {
break;
}
tb = tb->prior;
}
if(tb->prior && tb->prior->sections && tb->prior->nsections>0) {
y = tb->prior->sections[tb->prior->nsections-1]._y +
tb->prior->sections[tb->prior->nsections-1].h -
tb->prior->sections[tb->prior->nsections-1].max_d;
} else {
y = 0;
}
}
_layOut:
while (tb) {
if (tb->sections && tb->nsections>0) {
wfree(tb->sections);
tb->sections = NULL;
tb->nsections = 0;
}
if (tb->first && tb->blank && tb->next && !tb->next->first) {
TextBlock *next = tb->next;
tPtr->currentTextBlock = tb;
WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
tb = next;
tb->first = True;
continue;
}
if (tb->first && tb != tPtr->firstTextBlock) {
y += layOutLine(tPtr, items, nitems, x, y);
x = tPtr->margins[tb->marginN].first;
bmargin = tPtr->margins[tb->marginN].body;
nitems = 0;
lw = 0;
}
if (tb->graphic) {
if (!tPtr->flags.monoFont) {
if(tb->object)
width = WMWidgetWidth(tb->d.widget);
else
width = tb->d.pixmap->width;
if (width > tPtr->docWidth)
tPtr->docWidth = width;
lw += width;
if (lw >= tPtr->visible.w - x ) {
y += layOutLine(tPtr, items, nitems, x, y);
nitems = 0;
x = bmargin;
lw = width;
}
if(nitems + 1> itemsSize) {
items = wrealloc(items,
(++itemsSize)*sizeof(myLineItems));
}
items[nitems].tb = tb;
items[nitems].begin = 0;
items[nitems].end = 0;
nitems++;
}
} else if ((start = tb->text)) {
begin = end = 0;
font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
while (start) {
mark = strchr(start, ' ');
if (mark) {
end += (int)(mark-start)+1;
start = mark+1;
} else {
end += strlen(start);
start = mark;
}
if (end > tb->used)
end = tb->used;
if (end-begin > 0) {
width = WMWidthOfString(font,
&tb->text[begin], end-begin);
/* if it won't fit, char wrap it */
if (width >= tPtr->visible.w) {
char *t = &tb->text[begin];
int l=end-begin, i=0;
do {
width = WMWidthOfString(font, t, ++i);
} while (width < tPtr->visible.w && i < l);
if(i>2) i--;
end = begin+i;
start = &tb->text[end];
}
lw += width;
}
if (lw >= tPtr->visible.w - x) {
y += layOutLine(tPtr, items, nitems, x, y);
lw = width;
x = bmargin;
nitems = 0;
}
if(nitems + 1 > itemsSize) {
items = wrealloc(items,
(++itemsSize)*sizeof(myLineItems));
}
items[nitems].tb = tb;
items[nitems].begin = begin;
items[nitems].end = end;
nitems++;
begin = end;
}
}
/* not yet fully ready. but is already VERY FAST for a 3Mbyte file ;-) */
if(0&&tPtr->flags.laidOut
&& tb->next && tb->next->sections && tb->next->nsections>0
&& (tPtr->vpos + tPtr->visible.h
< tb->next->sections[0]._y)) {
if(tPtr->lastTextBlock->sections
&& tPtr->lastTextBlock->nsections > 0 ) {
TextBlock *ltb = tPtr->lastTextBlock;
int ly = ltb->sections[ltb->nsections-1]._y;
int lh = ltb->sections[ltb->nsections-1].h;
int ss, sd;
lh += 1 + tPtr->visible.y + ltb->sections[ltb->nsections-1].max_d;
printf("it's %d\n", tPtr->visible.y + ltb->sections[ltb->nsections-1].max_d);
y += layOutLine(tPtr, items, nitems, x, y);
ss= ly+lh-y;
sd = tPtr->docHeight-y;
printf("dif %d-%d: %d\n", ss, sd, ss-sd);
y += tb->next->sections[0]._y-y;
nitems = 0;
printf("nitems%d\n", nitems);
if(ss-sd!=0)
y = tPtr->docHeight+ss-sd;
break;
} else {
tPtr->flags.laidOut = False;
}
}
tb = tb->next;
}
if (nitems > 0)
y += layOutLine(tPtr, items, nitems, x, y);
if (tPtr->docHeight != y+10) {
tPtr->docHeight = y+10;
updateScrollers(tPtr);
}
if(tPtr->docWidth > tPtr->visible.w && !tPtr->hS) {
XEvent event;
tPtr->flags.horizOnDemand = True;
WMSetTextHasHorizontalScroller((WMText*)tPtr, True);
event.type = Expose;
handleEvents(&event, (void *)tPtr);
} else if(tPtr->docWidth <= tPtr->visible.w
&& tPtr->hS && tPtr->flags.horizOnDemand ) {
tPtr->flags.horizOnDemand = False;
WMSetTextHasHorizontalScroller((WMText*)tPtr, False);
}
tPtr->flags.laidOut = True;
if(items && itemsSize > 0)
wfree(items);
}
static void
textDidResize(W_ViewDelegate *self, WMView *view)
{
Text *tPtr = (Text *)view->self;
unsigned short w = tPtr->view->size.width;
unsigned short h = tPtr->view->size.height;
unsigned short rh = 0, vw = 0, rel;
rel = (tPtr->flags.relief == WRFlat);
if (tPtr->ruler && tPtr->flags.rulerShown) {
WMMoveWidget(tPtr->ruler, 2, 2);
WMResizeWidget(tPtr->ruler, w - 4, 40);
rh = 40;
}
if (tPtr->vS) {
WMMoveWidget(tPtr->vS, 1 - (rel?1:0), rh + 1 - (rel?1:0));
WMResizeWidget(tPtr->vS, 20, h - rh - 2 + (rel?2:0));
vw = 20;
WMSetRulerOffset(tPtr->ruler,22);
} else WMSetRulerOffset(tPtr->ruler, 2);
if (tPtr->hS) {
if (tPtr->vS) {
WMMoveWidget(tPtr->hS, vw, h - 21);
WMResizeWidget(tPtr->hS, w - vw - 1, 20);
} else {
WMMoveWidget(tPtr->hS, vw+1, h - 21);
WMResizeWidget(tPtr->hS, w - vw - 2, 20);
}
}
tPtr->visible.x = (tPtr->vS)?24:4;
tPtr->visible.y = (tPtr->ruler && tPtr->flags.rulerShown)?43:3;
tPtr->visible.w = tPtr->view->size.width - tPtr->visible.x - 8;
tPtr->visible.h = tPtr->view->size.height - tPtr->visible.y;
tPtr->visible.h -= (tPtr->hS)?20:0;
tPtr->margins[0].right = tPtr->visible.w;
if (tPtr->view->flags.realized) {
if (tPtr->db) {
XFreePixmap(tPtr->view->screen->display, tPtr->db);
tPtr->db = (Pixmap) NULL;
}
if (tPtr->visible.w < 40)
tPtr->visible.w = 40;
if (tPtr->visible.h < 20)
tPtr->visible.h = 20;
if(!tPtr->db) {
tPtr->db = XCreatePixmap(tPtr->view->screen->display,
tPtr->view->window, tPtr->visible.w,
tPtr->visible.h, tPtr->view->screen->depth);
}
}
WMThawText(tPtr);
}
W_ViewDelegate _TextViewDelegate =
{
NULL,
NULL,
textDidResize,
NULL,
};
#define TEXT_BUFFER_INCR 8
#define reqBlockSize(requested) (requested + TEXT_BUFFER_INCR)
static void
clearText(Text *tPtr)
{
tPtr->vpos = tPtr->hpos = 0;
tPtr->docHeight = tPtr->docWidth = 0;
tPtr->cursor.x = -23;
if (!tPtr->firstTextBlock)
return;
while (tPtr->currentTextBlock)
WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
tPtr->firstTextBlock = NULL;
tPtr->currentTextBlock = NULL;
tPtr->lastTextBlock = NULL;
WMEmptyArray(tPtr->gfxItems);
}
/* possibly remove a single character from the currentTextBlock,
or if there's a selection, remove it...
note that Delete and Backspace are treated differently */
static void
deleteTextInteractively(Text *tPtr, KeySym ksym)
{
TextBlock *tb;
Bool back = (Bool) (ksym == XK_BackSpace);
Bool done = 1, wasFirst = 0;
if (!tPtr->flags.editable)
return;
if ( !(tb = tPtr->currentTextBlock) )
return;
if (tPtr->flags.ownsSelection) {
if(removeSelection(tPtr))
layOutDocument(tPtr);
return;
}
wasFirst = tb->first;
if (back && tPtr->tpos < 1) {
if (tb->prior) {
if(tb->prior->blank) {
tPtr->currentTextBlock = tb->prior;
WMRemoveTextBlock(tPtr);
tPtr->currentTextBlock = tb;
tb->first = True;
layOutDocument(tPtr);
return;
} else {
if(tb->blank) {
TextBlock *prior = tb->prior;
tPtr->currentTextBlock = tb;
WMRemoveTextBlock(tPtr);
tb = prior;
} else {
tb = tb->prior;
}
if(tb->graphic)
tPtr->tpos = 1;
else
tPtr->tpos = tb->used;
tPtr->currentTextBlock = tb;
done = 1;
if(wasFirst) {
if(tb->next)
tb->next->first = False;
layOutDocument(tPtr);
return;
}
}
}
}
if ( (tb->used > 0) && ((back?tPtr->tpos > 0:1))
&& (tPtr->tpos <= tb->used) && !tb->graphic) {
if (back)
tPtr->tpos--;
memmove(&(tb->text[tPtr->tpos]),
&(tb->text[tPtr->tpos + 1]), tb->used - tPtr->tpos);
tb->used--;
done = 0;
}
/* if there are no characters left to back over in the textblock,
but it still has characters to the right of the cursor: */
if ( (back? (tPtr->tpos == 0 && !done) : ( tPtr->tpos >= tb->used))
|| tb->graphic) {
/* no more chars, and it's marked as blank? */
if(tb->blank) {
TextBlock *sibling = (back? tb->prior : tb->next);
if(tb->used == 0 || tb->graphic)
WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
if (sibling) {
tPtr->currentTextBlock = sibling;
if(tb->graphic)
tPtr->tpos = (back? 1 : 0);
else
tPtr->tpos = (back? sibling->used : 0);
}
/* no more chars, so mark it as blank */
} else if(tb->used == 0) {
tb->blank = 1;
} else if(tb->graphic) {
Bool hasNext = (Bool)(tb->next);
WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
if(hasNext) {
tPtr->tpos = 0;
} else if(tPtr->currentTextBlock) {
tPtr->tpos = (tPtr->currentTextBlock->graphic?
1 : tPtr->currentTextBlock->used);
}
} else printf("DEBUG: unaccounted for... catch this!\n");
}
layOutDocument(tPtr);
}
static void
insertTextInteractively(Text *tPtr, char *text, int len)
{
TextBlock *tb;
char *newline = NULL;
if (!tPtr->flags.editable) {
return;
}
if (len < 1 || !text)
return;
if(tPtr->flags.ignoreNewLine && *text == '\n' && len == 1)
return;
if (tPtr->flags.ownsSelection)
removeSelection(tPtr);
if (tPtr->flags.ignoreNewLine) {
int i;
for(i=0; i<len; i++) {
if (text[i] == '\n')
text[i] = ' ';
}
}
tb = tPtr->currentTextBlock;
if (!tb || tb->graphic) {
tPtr->tpos = 0;
WMAppendTextStream(tPtr, text);
layOutDocument(tPtr);
return;
}
if ((newline = strchr(text, '\n'))) {
int nlen = (int)(newline-text);
int s = tb->used - tPtr->tpos;
if (!tb->blank && nlen>0) {
char *save;
if (s > 0) {
save = wmalloc(s);
memcpy(save, &tb->text[tPtr->tpos], s);
tb->used -= (tb->used - tPtr->tpos);
}
insertTextInteractively(tPtr, text, nlen);
newline++;
WMAppendTextStream(tPtr, newline);
if (s>0) {
insertTextInteractively(tPtr, save, s);
wfree(save);
}
} else {
if (tPtr->tpos>0 && tPtr->tpos < tb->used
&& !tb->graphic && tb->text) {
unsigned short savePos = tPtr->tpos;
void *ntb = WMCreateTextBlockWithText(
tPtr, &tb->text[tPtr->tpos],
tb->d.font, tb->color, True, tb->used - tPtr->tpos);
if(tb->sections[0].end == tPtr->tpos)
WMAppendTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
NULL, tb->d.font, tb->color, True, 0));
tb->used = savePos;
WMAppendTextBlock(tPtr, ntb);
tPtr->tpos = 0;
} else if (tPtr->tpos == tb->used) {
if(tPtr->flags.indentNewLine) {
WMAppendTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
" ", tb->d.font, tb->color, True, 4));
tPtr->tpos = 4;
} else {
WMAppendTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
NULL, tb->d.font, tb->color, True, 0));
tPtr->tpos = 0;
}
} else if (tPtr->tpos == 0) {
if(tPtr->flags.indentNewLine) {
WMPrependTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
" ", tb->d.font, tb->color, True, 4));
} else {
WMPrependTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
NULL, tb->d.font, tb->color, True, 0));
}
tPtr->tpos = 0;
if(tPtr->currentTextBlock->next)
tPtr->currentTextBlock = tPtr->currentTextBlock->next;
}
}
} else {
if (tb->used + len >= tb->allocated) {
tb->allocated = reqBlockSize(tb->used+len);
tb->text = wrealloc(tb->text, tb->allocated);
}
if (tb->blank) {
memcpy(tb->text, text, len);
tb->used = len;
tPtr->tpos = len;
tb->text[tb->used] = 0;
tb->blank = False;
} else {
memmove(&(tb->text[tPtr->tpos+len]), &tb->text[tPtr->tpos],
tb->used-tPtr->tpos+1);
memmove(&tb->text[tPtr->tpos], text, len);
tb->used += len;
tPtr->tpos += len;
tb->text[tb->used] = 0;
}
}
layOutDocument(tPtr);
}
static void
selectRegion(Text *tPtr, int x, int y)
{
if (x < 0 || y < 0)
return;
y += (tPtr->flags.rulerShown? 40: 0);
y += tPtr->vpos;
if (y>10)
y -= 10; /* the original offset */
x -= tPtr->visible.x-2;
if (x<0)
x=0;
tPtr->sel.x = WMAX(0, WMIN(tPtr->clicked.x, x));
tPtr->sel.w = abs(tPtr->clicked.x - x);
tPtr->sel.y = WMAX(0, WMIN(tPtr->clicked.y, y));
tPtr->sel.h = abs(tPtr->clicked.y - y);
tPtr->flags.ownsSelection = True;
paintText(tPtr);
}
static void
releaseSelection(Text *tPtr)
{
TextBlock *tb = tPtr->firstTextBlock;
while(tb) {
tb->selected = False;
tb = tb->next;
}
tPtr->flags.ownsSelection = False;
WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY,
CurrentTime);
paintText(tPtr);
}
WMData*
requestHandler(WMView *view, Atom selection, Atom target, void *cdata,
Atom *type)
{
Text *tPtr = view->self;
Display *dpy = tPtr->view->screen->display;
Atom _TARGETS;
Atom TEXT = XInternAtom(dpy, "TEXT", False);
Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
WMData *data = NULL;
if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
char *text = WMGetTextSelectedStream(tPtr);
if (text) {
data = WMCreateDataWithBytes(text, strlen(text));
WMSetDataFormat(data, TYPETEXT);
}
*type = target;
return data;
} else printf("didn't get it\n");
_TARGETS = XInternAtom(dpy, "TARGETS", False);
if (target == _TARGETS) {
Atom *ptr;
ptr = wmalloc(4 * sizeof(Atom));
ptr[0] = _TARGETS;
ptr[1] = XA_STRING;
ptr[2] = TEXT;
ptr[3] = COMPOUND_TEXT;
data = WMCreateDataWithBytes(ptr, 4*4);
WMSetDataFormat(data, 32);
*type = target;
return data;
}
return NULL;
}
static void
lostHandler(WMView *view, Atom selection, void *cdata)
{
releaseSelection((WMText *)view->self);
}
static WMSelectionProcs selectionHandler = {
requestHandler, lostHandler, NULL
};
static void
ownershipObserver(void *observerData, WMNotification *notification)
{
if (observerData != WMGetNotificationClientData(notification))
lostHandler(WMWidgetView(observerData), XA_PRIMARY, NULL);
}
static void
autoSelectText(Text *tPtr, int clicks)
{
int x, start;
TextBlock *tb;
char *mark = NULL, behind, ahead;
if(!(tb = tPtr->currentTextBlock))
return;
if(clicks == 2) {
switch(tb->text[tPtr->tpos]) {
case ' ': return;
/*
case '<': case '>': behind = '<'; ahead = '>'; break;
case '{': case '}': behind = '{'; ahead = '}'; break;
case '[': case ']': behind = '['; ahead = ']'; break;
*/
default: behind = ahead = ' ';
}
tPtr->sel.y = tPtr->cursor.y+5;
tPtr->sel.h = 6;/*tPtr->cursor.h-10;*/
if(tb->graphic) {
tPtr->sel.x = tb->sections[0].x;
tPtr->sel.w = tb->sections[0].w;
} else {
WMFont *font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
start = tPtr->tpos;
while(start > 0 && tb->text[start-1] != behind)
start--;
x = tPtr->cursor.x;
if(tPtr->tpos > start){
x -= WMWidthOfString(font, &tb->text[start],
tPtr->tpos - start);
}
tPtr->sel.x = (x<0?0:x)+1;
if((mark = strchr(&tb->text[start], ahead))) {
tPtr->sel.w = WMWidthOfString(font, &tb->text[start],
(int)(mark - &tb->text[start]));
} else if(tb->used > start) {
tPtr->sel.w = WMWidthOfString(font, &tb->text[start],
tb->used - start);
}
}
} else if(clicks == 3) {
TextBlock *cur = tb;
while(tb && !tb->first) {
tb = tb->prior;
}
tPtr->sel.y = tb->sections[0]._y;
tb = cur;
while(tb->next && !tb->next->first) {
tb = tb->next;
}
tPtr->sel.h = tb->sections[tb->nsections-1]._y
+ 5 - tPtr->sel.y;
tPtr->sel.x = 0;
tPtr->sel.w = tPtr->docWidth;
tPtr->clicked.x = 0; /* only for now, fix sel. code */
}
if (!tPtr->flags.ownsSelection) {
WMCreateSelectionHandler(tPtr->view,
XA_PRIMARY, tPtr->lastClickTime, &selectionHandler, NULL);
tPtr->flags.ownsSelection = True;
}
paintText(tPtr);
}
static void
fontChanged(void *observerData, WMNotification *notification)
{
WMText *tPtr = (WMText *) observerData;
WMFont *font = (WMFont *)WMGetNotificationClientData(notification);
printf("fontChanged\n");
if(!tPtr || !font)
return;
if (tPtr->flags.ownsSelection)
WMSetTextSelectionFont(tPtr, font);
}
static void
handleTextKeyPress(Text *tPtr, XEvent *event)
{
char buffer[64];
KeySym ksym;
int control_pressed = False;
TextBlock *tb = NULL;
if (((XKeyEvent *) event)->state & ControlMask)
control_pressed = True;
buffer[XLookupString(&event->xkey, buffer, 63, &ksym, NULL)] = 0;
switch(ksym) {
case XK_Home:
if((tPtr->currentTextBlock = tPtr->firstTextBlock))
tPtr->tpos = 0;
updateCursorPosition(tPtr);
paintText(tPtr);
break;
case XK_End:
if((tPtr->currentTextBlock = tPtr->lastTextBlock)) {
if(tPtr->currentTextBlock->graphic)
tPtr->tpos = 1;
else
tPtr->tpos = tPtr->currentTextBlock->used;
}
updateCursorPosition(tPtr);
paintText(tPtr);
break;
case XK_Left:
if(!(tb = tPtr->currentTextBlock))
break;
if(tb->graphic)
goto L_imaGFX;
if(tPtr->tpos==0) {
L_imaGFX: if(tb->prior) {
tPtr->currentTextBlock = tb->prior;
if(tPtr->currentTextBlock->graphic)
tPtr->tpos = 1;
else
tPtr->tpos = tPtr->currentTextBlock->used;
if(!tb->first && tPtr->tpos > 0)
tPtr->tpos--;
} else tPtr->tpos = 0;
} else tPtr->tpos--;
updateCursorPosition(tPtr);
paintText(tPtr);
break;
case XK_Right:
if(!(tb = tPtr->currentTextBlock))
break;
if(tb->graphic)
goto R_imaGFX;
if(tPtr->tpos == tb->used) {
R_imaGFX: if(tb->next) {
tPtr->currentTextBlock = tb->next;
tPtr->tpos = 0;
if(!tb->next->first && tb->next->used>0)
tPtr->tpos++;
} else {
if(tb->graphic)
tPtr->tpos = 1;
else
tPtr->tpos = tb->used;
}
} else tPtr->tpos++;
updateCursorPosition(tPtr);
paintText(tPtr);
break;
case XK_Down:
cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
tPtr->clicked.y + tPtr->cursor.h - tPtr->vpos);
paintText(tPtr);
break;
case XK_Up:
cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
tPtr->visible.y + tPtr->cursor.y - tPtr->vpos - 3);
paintText(tPtr);
break;
case XK_BackSpace:
case XK_Delete:
#ifdef XK_KP_Delete
case XK_KP_Delete:
#endif
deleteTextInteractively(tPtr, ksym);
updateCursorPosition(tPtr);
paintText(tPtr);
break;
case XK_Control_R :
case XK_Control_L :
control_pressed = True;
break;
case XK_Tab:
insertTextInteractively(tPtr, " ", 4);
updateCursorPosition(tPtr);
paintText(tPtr);
break;
case XK_Return:
*buffer = '\n';
default:
if (*buffer != 0 && !control_pressed) {
insertTextInteractively(tPtr, buffer, strlen(buffer));
updateCursorPosition(tPtr);
paintText(tPtr);
} else if (control_pressed && ksym==XK_r) {
Bool i = !tPtr->flags.rulerShown;
WMShowTextRuler(tPtr, i);
tPtr->flags.rulerShown = i;
}
else if (control_pressed && *buffer == '')
XBell(tPtr->view->screen->display, 0);
else
WMRelayToNextResponder(tPtr->view, event);
}
if (!control_pressed && tPtr->flags.ownsSelection)
releaseSelection(tPtr);
}
static void
pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
void *cdata, WMData *data)
{
Text *tPtr = (Text *)view->self;
char *text;
tPtr->flags.waitingForSelection = 0;
if (data) {
text = (char*)WMDataBytes(data);
if (tPtr->parser) {
(tPtr->parser) (tPtr, (void *) text);
layOutDocument(tPtr);
} else insertTextInteractively(tPtr, text, strlen(text));
updateCursorPosition(tPtr);
paintText(tPtr);
} else {
int n;
text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
if (text) {
text[n] = 0;
if (tPtr->parser) {
(tPtr->parser) (tPtr, (void *) text);
layOutDocument(tPtr);
} else insertTextInteractively(tPtr, text, n);
updateCursorPosition(tPtr);
paintText(tPtr);
XFree(text);
}
}
}
static void
handleActionEvents(XEvent *event, void *data)
{
Text *tPtr = (Text *)data;
Display *dpy = event->xany.display;
KeySym ksym;
switch (event->type) {
case KeyPress:
ksym = XLookupKeysym((XKeyEvent*)event, 0);
if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
tPtr->flags.extendSelection = True;
return;
}
if (tPtr->flags.focused) {
XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
GrabModeAsync, GrabModeAsync, None,
tPtr->view->screen->invisibleCursor, CurrentTime);
tPtr->flags.pointerGrabbed = True;
handleTextKeyPress(tPtr, event);
} break;
case KeyRelease:
ksym = XLookupKeysym((XKeyEvent*)event, 0);
if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
tPtr->flags.extendSelection = False;
return;
/* end modify flag so selection can be extended */
}
break;
case MotionNotify:
if (tPtr->flags.pointerGrabbed) {
tPtr->flags.pointerGrabbed = False;
XUngrabPointer(dpy, CurrentTime);
}
if(tPtr->flags.waitingForSelection)
break;
if ((event->xmotion.state & Button1Mask)) {
TextBlock *tb = tPtr->currentTextBlock;
if(tb && tPtr->flags.isOverGraphic &&
tb->graphic && !tb->object) {
WMSize offs;
WMPixmap *pixmap = tb->d.pixmap;
char *types[2] = {"application/X-image", NULL};
offs.width = 2;
offs.height = 2;
WMDragImageFromView(tPtr->view, pixmap, types,
wmkpoint(event->xmotion.x_root, event->xmotion.y_root),
offs, event, True);
} else {
if (!tPtr->flags.ownsSelection) {
WMCreateSelectionHandler(tPtr->view,
XA_PRIMARY, event->xbutton.time,
&selectionHandler, NULL);
tPtr->flags.ownsSelection = True;
}
}
selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
break;
}
mouseOverObject(tPtr, event->xmotion.x, event->xmotion.y);
break;
case ButtonPress:
if (tPtr->flags.pointerGrabbed) {
tPtr->flags.pointerGrabbed = False;
XUngrabPointer(dpy, CurrentTime);
break;
}
if (tPtr->flags.waitingForSelection)
break;
if (tPtr->flags.extendSelection && tPtr->flags.ownsSelection) {
selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
return;
}
if (tPtr->flags.ownsSelection)
releaseSelection(tPtr);
if (event->xbutton.button == Button1) {
if(WMIsDoubleClick(event)) {
TextBlock *tb = tPtr->currentTextBlock;
tPtr->lastClickTime = event->xbutton.time;
if(tb && tb->graphic && !tb->object) {
if(tPtr->delegate && tPtr->delegate->didDoubleClickOnPicture) {
char *desc;
desc = wmalloc(tb->used+1);
memcpy(desc, tb->text, tb->used);
desc[tb->used] = 0;
(*tPtr->delegate->didDoubleClickOnPicture)(tPtr->delegate, desc);
wfree(desc);
}
} else {
autoSelectText(tPtr, 2);
}
break;
} else if(event->xbutton.time - tPtr->lastClickTime
< WINGsConfiguration.doubleClickDelay) {
tPtr->lastClickTime = event->xbutton.time;
autoSelectText(tPtr, 3);
break;
}
if (!tPtr->flags.focused) {
WMSetFocusToWidget(tPtr);
tPtr->flags.focused = True;
}
tPtr->lastClickTime = event->xbutton.time;
cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
paintText(tPtr);
}
if (event->xbutton.button
== WINGsConfiguration.mouseWheelDown) {
WMScrollText(tPtr, 16);
break;
}
if (event->xbutton.button
== WINGsConfiguration.mouseWheelUp) {
WMScrollText(tPtr, -16);
break;
}
if (event->xbutton.button == Button2) {
char *text = NULL;
int n;
if (!tPtr->flags.editable) {
XBell(dpy, 0);
break;
}
if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
event->xbutton.time, pasteText, NULL)) {
text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
tPtr->flags.waitingForSelection = 0;
if (text) {
text[n] = 0;
if (tPtr->parser) {
(tPtr->parser) (tPtr, (void *) text);
layOutDocument(tPtr);
}
else
insertTextInteractively(tPtr, text, n);
XFree(text);
#if 0
NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
(void*)WMInsertTextEvent);
#endif
updateCursorPosition(tPtr);
paintText(tPtr);
} else {
tPtr->flags.waitingForSelection = True;
}
}
break;
}
case ButtonRelease:
if (tPtr->flags.pointerGrabbed) {
tPtr->flags.pointerGrabbed = False;
XUngrabPointer(dpy, CurrentTime);
break;
}
if (tPtr->flags.waitingForSelection)
break;
}
}
static void
handleEvents(XEvent *event, void *data)
{
Text *tPtr = (Text *)data;
switch(event->type) {
case Expose:
if (event->xexpose.count!=0)
break;
if(tPtr->hS) {
if (!(W_VIEW(tPtr->hS))->flags.realized)
WMRealizeWidget(tPtr->hS);
}
if(tPtr->vS) {
if (!(W_VIEW(tPtr->vS))->flags.realized)
WMRealizeWidget(tPtr->vS);
}
if(tPtr->ruler) {
if (!(W_VIEW(tPtr->ruler))->flags.realized)
WMRealizeWidget(tPtr->ruler);
}
if(!tPtr->db)
textDidResize(tPtr->view->delegate, tPtr->view);
paintText(tPtr);
break;
case FocusIn:
if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))
!= tPtr->view)
return;
tPtr->flags.focused = True;
#if DO_BLINK
if (tPtr->flags.editable && !tPtr->timerID) {
tPtr->timerID = WMAddTimerHandler(12+0*CURSOR_BLINK_ON_DELAY,
blinkCursor, tPtr);
}
#endif
break;
case FocusOut:
tPtr->flags.focused = False;
paintText(tPtr);
#if DO_BLINK
if (tPtr->timerID) {
WMDeleteTimerHandler(tPtr->timerID);
tPtr->timerID = NULL;
}
#endif
break;
case DestroyNotify:
clearText(tPtr);
if(tPtr->db)
XFreePixmap(tPtr->view->screen->display, tPtr->db);
if(tPtr->gfxItems)
WMEmptyArray(tPtr->gfxItems);
#if DO_BLINK
if (tPtr->timerID)
WMDeleteTimerHandler(tPtr->timerID);
#endif
WMReleaseFont(tPtr->dFont);
WMReleaseColor(tPtr->dColor);
WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
WMRemoveNotificationObserver(tPtr);
wfree(tPtr);
break;
}
}
static void
insertPlainText(Text *tPtr, char *text)
{
char *start, *mark;
void *tb = NULL;
start = text;
while (start) {
mark = strchr(start, '\n');
if (mark) {
tb = WMCreateTextBlockWithText(tPtr,
start, tPtr->dFont,
tPtr->dColor, tPtr->flags.first, (int)(mark-start));
start = mark+1;
tPtr->flags.first = True;
} else {
if (start && strlen(start)) {
tb = WMCreateTextBlockWithText(tPtr, start, tPtr->dFont,
tPtr->dColor, tPtr->flags.first, strlen(start));
} else tb = NULL;
tPtr->flags.first = False;
start = mark;
}
if (tPtr->flags.prepend)
WMPrependTextBlock(tPtr, tb);
else
WMAppendTextBlock(tPtr, tb);
}
}
static void
rulerMoveCallBack(WMWidget *w, void *self)
{
Text *tPtr = (Text *)self;
if (!tPtr)
return;
if (W_CLASS(tPtr) != WC_Text)
return;
paintText(tPtr);
}
static void
rulerReleaseCallBack(WMWidget *w, void *self)
{
Text *tPtr = (Text *)self;
if (!tPtr)
return;
if (W_CLASS(tPtr) != WC_Text)
return;
WMThawText(tPtr);
return;
}
static unsigned
draggingSourceOperation(WMView *self, Bool local)
{
return WDOperationCopy;
}
static WMData*
fetchDragData(WMView *self, char *type)
{
TextBlock *tb = ((WMText *)self->self)->currentTextBlock;
char *desc;
WMData *data;
if (!tb)
return NULL;
printf("type is [%s]\n", type);
desc = wmalloc(tb->used+1);
memcpy(desc, tb->text, tb->used);
desc[tb->used] = 0;
data = WMCreateDataWithBytes(desc, strlen(desc)+1);
wfree(desc);
return data;
}
static WMDragSourceProcs _DragSourceProcs = {
draggingSourceOperation,
NULL,
NULL,
fetchDragData
};
static unsigned
draggingEntered(WMView *self, WMDraggingInfo *info)
{
printf("draggingEntered\n");
return WDOperationCopy;
}
static unsigned
draggingUpdated(WMView *self, WMDraggingInfo *info)
{
return WDOperationCopy;
}
static void
draggingExited(WMView *self, WMDraggingInfo *info)
{
printf("draggingExited\n");
}
static Bool
prepareForDragOperation(WMView *self, WMDraggingInfo *info)
{
printf("prepareForDragOperation\n");
return True;
}
char *badbadbad;
static void
receivedData(WMView *view, Atom selection, Atom target, Time timestamp,
void *cdata, WMData *data)
{
badbadbad = wstrdup((char *)WMDataBytes(data));
}
/* when it's done in WINGs, remove this */
Bool
requestDroppedData(WMView *view, WMDraggingInfo *info, char *type)
{
WMScreen *scr = W_VIEW_SCREEN(view);
if (!WMRequestSelection(scr->dragInfo.destView,
scr->xdndSelectionAtom,
XInternAtom(scr->display, type, False),
scr->dragInfo.timestamp,
receivedData, &scr->dragInfo)) {
wwarning("could not request data for dropped data");
}
{
XEvent ev;
ev.type = ClientMessage;
ev.xclient.message_type = scr->xdndFinishedAtom;
ev.xclient.format = 32;
ev.xclient.window = info->destinationWindow;
ev.xclient.data.l[0] = 0;
ev.xclient.data.l[1] = 0;
ev.xclient.data.l[2] = 0;
ev.xclient.data.l[3] = 0;
ev.xclient.data.l[4] = 0;
XSendEvent(scr->display, info->sourceWindow, False, 0, &ev);
XFlush(scr->display);
}
return True;
}
static Bool
performDragOperation(WMView *self, WMDraggingInfo *info)
{
WMColor *color;
WMText *tPtr = (WMText *)self->self;
if (!tPtr)
return True;
requestDroppedData(tPtr->view, info, "application/X-color");
color = WMCreateNamedColor(W_VIEW_SCREEN(self), badbadbad, True);
if(color) {
WMSetTextSelectionColor(tPtr, color);
WMReleaseColor(color);
}
return True;
}
static void
concludeDragOperation(WMView *self, WMDraggingInfo *info)
{
printf("concludeDragOperation\n");
}
static WMDragDestinationProcs _DragDestinationProcs = {
draggingEntered,
draggingUpdated,
draggingExited,
prepareForDragOperation,
performDragOperation,
concludeDragOperation
};
char *
getStream(WMText *tPtr, int sel, int array)
{
TextBlock *tb = NULL;
char *text = NULL;
unsigned long where = 0;
if (!tPtr)
return NULL;
if (!(tb = tPtr->firstTextBlock))
return NULL;
if (tPtr->writer) {
(tPtr->writer) (tPtr, (void *) text);
return text;
}
tb = tPtr->firstTextBlock;
while (tb) {
if (!tb->graphic || (tb->graphic && !tPtr->flags.monoFont)) {
if (!sel || (tb->graphic && tb->selected)) {
if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank)
&& tb != tPtr->firstTextBlock) {
text = wrealloc(text, where+1);
text[where++] = '\n';
}
if(tb->blank)
goto _gSnext;
if(tb->graphic && array) {
text = wrealloc(text, where+4);
text[where++] = 0xFA;
text[where++] = (tb->used>>8)&0x0ff;
text[where++] = tb->used&0x0ff;
text[where++] = tb->allocated; /* extra info */
}
text = wrealloc(text, where+tb->used);
memcpy(&text[where], tb->text, tb->used);
where += tb->used;
} else if (sel && tb->selected) {
if (!tPtr->flags.ignoreNewLine && tb->blank) {
text = wrealloc(text, where+1);
text[where++] = '\n';
}
if(tb->blank)
goto _gSnext;
text = wrealloc(text, where+(tb->s_end - tb->s_begin));
memcpy(&text[where], &tb->text[tb->s_begin],
tb->s_end - tb->s_begin);
where += tb->s_end - tb->s_begin;
}
}
_gSnext:tb = tb->next;
}
/* +1 for the end of string, let's be nice */
text = wrealloc(text, where+1);
text[where] = 0;
return text;
}
static void
releaseStreamObjects(void *data)
{
if(data)
wfree(data);
}
WMArray *
getStreamObjects(WMText *tPtr, int sel)
{
WMArray *array = WMCreateArrayWithDestructor(4, releaseStreamObjects);
WMData *data;
char *stream;
unsigned short len;
char *start, *fa, *desc;
stream = getStream(tPtr, sel, 1);
if(!stream)
return NULL;
start = stream;
while (start) {
fa = strchr(start, 0xFA);
if (fa) {
if((int)(fa - start)>0) {
desc = start;
desc[(int)(fa - start)] = 0;
data = WMCreateDataWithBytes((void *)desc, (int)(fa - start));
WMSetDataFormat(data, TYPETEXT);
WMAddToArray(array, (void *) data);
}
len = *(fa+1)*0xff + *(fa+2);
data = WMCreateDataWithBytes((void *)(fa+4), len);
WMSetDataFormat(data, *(fa+3));
WMAddToArray(array, (void *) data);
start = fa + len + 4;
} else {
if (start && strlen(start)) {
data = WMCreateDataWithBytes((void *)start, strlen(start));
WMSetDataFormat(data, TYPETEXT);
WMAddToArray(array, (void *) data);
}
start = fa;
}
}
wfree(stream);
return array;
}
WMText *
WMCreateTextForDocumentType(WMWidget *parent, WMAction *parser, WMAction *writer)
{
Text *tPtr;
Display *dpy;
WMScreen *scr;
XGCValues gcv;
tPtr = wmalloc(sizeof(Text));
memset(tPtr, 0, sizeof(Text));
tPtr->widgetClass = WC_Text;
tPtr->view = W_CreateView(W_VIEW(parent));
if (!tPtr->view) {
perror("could not create text's view\n");
wfree(tPtr);
return NULL;
}
dpy = tPtr->view->screen->display;
scr = tPtr->view->screen;
tPtr->view->self = tPtr;
tPtr->view->attribs.cursor = scr->textCursor;
tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
W_ResizeView(tPtr->view, 250, 200);
tPtr->dColor = WMWhiteColor(scr);
tPtr->bgGC = WMColorGC(tPtr->dColor);
W_SetViewBackgroundColor(tPtr->view, tPtr->dColor);
WMReleaseColor(tPtr->dColor);
tPtr->dColor = WMBlackColor(scr);
tPtr->fgGC = WMColorGC(tPtr->dColor);
gcv.graphics_exposures = False;
gcv.foreground = W_PIXEL(scr->gray);
gcv.background = W_PIXEL(scr->darkGray);
gcv.fill_style = FillStippled;
gcv.stipple = XCreateBitmapFromData(dpy, W_DRAWABLE(scr),
STIPPLE_BITS, STIPPLE_WIDTH, STIPPLE_HEIGHT);
tPtr->stippledGC = XCreateGC(dpy, W_DRAWABLE(scr),
GCForeground|GCBackground|GCStipple
|GCFillStyle|GCGraphicsExposures, &gcv);
tPtr->ruler = NULL;
tPtr->vS = NULL;
tPtr->hS = NULL;
tPtr->dFont = WMRetainFont(WMSystemFontOfSize(scr, 12));
tPtr->view->delegate = &_TextViewDelegate;
tPtr->delegate = NULL;
#if DO_BLINK
tPtr->timerID = NULL;
#endif
WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
|EnterWindowMask|LeaveWindowMask|FocusChangeMask,
handleEvents, tPtr);
WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
|KeyReleaseMask|KeyPressMask|Button1MotionMask,
handleActionEvents, tPtr);
WMAddNotificationObserver(ownershipObserver, tPtr,
"_lostOwnership", tPtr);
WMSetViewDragSourceProcs(tPtr->view, &_DragSourceProcs);
WMSetViewDragDestinationProcs(tPtr->view, &_DragDestinationProcs);
{
char *types[3] = {"application/X-color", "application/X-image", NULL};
WMRegisterViewForDraggedTypes(tPtr->view, types);
}
WMAddNotificationObserver(fontChanged, tPtr,
"WMFontPanelDidChangeNotification", tPtr);
tPtr->firstTextBlock = NULL;
tPtr->lastTextBlock = NULL;
tPtr->currentTextBlock = NULL;
tPtr->tpos = 0;
tPtr->gfxItems = WMCreateArray(4);
tPtr->parser = parser;
tPtr->writer = writer;
tPtr->sel.x = tPtr->sel.y = 2;
tPtr->sel.w = tPtr->sel.h = 0;
tPtr->clicked.x = tPtr->clicked.y = 2;
tPtr->visible.x = tPtr->visible.y = 2;
tPtr->visible.h = tPtr->view->size.height;
tPtr->visible.w = tPtr->view->size.width - 4;
tPtr->cursor.x = -23;
tPtr->docWidth = 0;
tPtr->docHeight = 0;
tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
default_bullet);
tPtr->db = (Pixmap) NULL;
tPtr->bgPixmap = NULL;
tPtr->margins = WMGetRulerMargins(NULL);
tPtr->margins->right = tPtr->visible.w;
tPtr->nMargins = 1;
tPtr->flags.rulerShown = False;
tPtr->flags.monoFont = False;
tPtr->flags.focused = False;
tPtr->flags.editable = True;
tPtr->flags.ownsSelection = False;
tPtr->flags.pointerGrabbed = False;
tPtr->flags.extendSelection = False;
tPtr->flags.frozen = False;
tPtr->flags.cursorShown = True;
tPtr->flags.acceptsGraphic = False;
tPtr->flags.horizOnDemand = False;
tPtr->flags.needsLayOut = False;
tPtr->flags.ignoreNewLine = False;
tPtr->flags.indentNewLine = False;
tPtr->flags.laidOut = False;
tPtr->flags.ownsSelection = False;
tPtr->flags.waitingForSelection = False;
tPtr->flags.prepend = False;
tPtr->flags.isOverGraphic = False;
tPtr->flags.relief = WRSunken;
tPtr->flags.isOverGraphic = 0;
tPtr->flags.alignment = WALeft;
tPtr->flags.first = True;
return tPtr;
}
void
WMPrependTextStream(WMText *tPtr, char *text)
{
CHECK_CLASS(tPtr, WC_Text);
if(!text) {
if (tPtr->flags.ownsSelection)
releaseSelection(tPtr);
clearText(tPtr);
updateScrollers(tPtr);
return;
}
tPtr->flags.prepend = True;
if (text && tPtr->parser)
(tPtr->parser) (tPtr, (void *) text);
else
insertPlainText(tPtr, text);
tPtr->flags.needsLayOut = True;
tPtr->tpos = 0;
if(!tPtr->flags.frozen) {
layOutDocument(tPtr);
}
}
void
WMAppendTextStream(WMText *tPtr, char *text)
{
CHECK_CLASS(tPtr, WC_Text);
if(!text) {
if (tPtr->flags.ownsSelection)
releaseSelection(tPtr);
clearText(tPtr);
updateScrollers(tPtr);
return;
}
tPtr->flags.prepend = False;
if (text && tPtr->parser)
(tPtr->parser) (tPtr, (void *) text);
else
insertPlainText(tPtr, text);
tPtr->flags.needsLayOut = True;
if(tPtr->currentTextBlock) {
if(tPtr->currentTextBlock->graphic)
tPtr->tpos = 1;
else
tPtr->tpos = tPtr->currentTextBlock->used;
}
if(!tPtr->flags.frozen) {
layOutDocument(tPtr);
}
}
char *
WMGetTextStream(WMText *tPtr)
{
CHECK_CLASS(tPtr, WC_Text);
return getStream(tPtr, 0, 0);
}
char *
WMGetTextSelectedStream(WMText *tPtr)
{
CHECK_CLASS(tPtr, WC_Text);
return getStream(tPtr, 1, 0);
}
WMArray *
WMGetTextObjects(WMText *tPtr)
{
CHECK_CLASS(tPtr, WC_Text);
return getStreamObjects(tPtr, 0);
}
WMArray *
WMGetTextSelectedObjects(WMText *tPtr)
{
CHECK_CLASS(tPtr, WC_Text);
return getStreamObjects(tPtr, 1);
}
void
WMSetTextDelegate(WMText *tPtr, WMTextDelegate *delegate)
{
CHECK_CLASS(tPtr, WC_Text);
tPtr->delegate = delegate;
}
void *
WMCreateTextBlockWithObject(WMText *tPtr, WMWidget *w,
char *description, WMColor *color,
unsigned short first, unsigned short extraInfo)
{
TextBlock *tb;
if (!w || !description || !color)
return NULL;
tb = wmalloc(sizeof(TextBlock));
if (!tb)
return NULL;
tb->text = wstrdup(description);
tb->used = strlen(description);
tb->blank = False;
tb->d.widget = w;
tb->color = WMRetainColor(color);
tb->marginN = newMargin(tPtr, NULL);
tb->allocated = extraInfo;
tb->first = first;
tb->kanji = False;
tb->graphic = True;
tb->object = True;
tb->underlined = False;
tb->selected = False;
tb->script = 0;
tb->sections = NULL;
tb->nsections = 0;
tb->prior = NULL;
tb->next = NULL;
return tb;
}
void *
WMCreateTextBlockWithPixmap(WMText *tPtr, WMPixmap *p,
char *description, WMColor *color,
unsigned short first, unsigned short extraInfo)
{
TextBlock *tb;
if (!p || !description || !color)
return NULL;
tb = wmalloc(sizeof(TextBlock));
if (!tb)
return NULL;
tb->text = wstrdup(description);
tb->used = strlen(description);
tb->blank = False;
tb->d.pixmap = WMRetainPixmap(p);
tb->color = WMRetainColor(color);
tb->marginN = newMargin(tPtr, NULL);
tb->allocated = extraInfo;
tb->first = first;
tb->kanji = False;
tb->graphic = True;
tb->object = False;
tb->underlined = False;
tb->selected = False;
tb->script = 0;
tb->sections = NULL;
tb->nsections = 0;
tb->prior = NULL;
tb->next = NULL;
return tb;
}
void*
WMCreateTextBlockWithText(WMText *tPtr, char *text, WMFont *font, WMColor *color,
unsigned short first, unsigned short len)
{
TextBlock *tb;
if (!font || !color)
return NULL;
tb = wmalloc(sizeof(TextBlock));
if (!tb)
return NULL;
tb->allocated = reqBlockSize(len);
tb->text = (char *)wmalloc(tb->allocated);
memset(tb->text, 0, tb->allocated);
if (len < 1|| !text || (*text == '\n' && len==1 )) {
*tb->text = ' ';
tb->used = 1;
tb->blank = True;
} else {
memcpy(tb->text, text, len);
tb->used = len;
tb->blank = False;
}
tb->text[tb->used] = 0;
tb->d.font = WMRetainFont(font);
tb->color = WMRetainColor(color);
tb->marginN = newMargin(tPtr, NULL);
tb->first = first;
tb->kanji = False;
tb->graphic = False;
tb->underlined = False;
tb->selected = False;
tb->script = 0;
tb->sections = NULL;
tb->nsections = 0;
tb->prior = NULL;
tb->next = NULL;
return tb;
}
void
WMSetTextBlockProperties(WMText *tPtr, void *vtb, unsigned int first,
unsigned int kanji, unsigned int underlined, int script,
WMRulerMargins *margins)
{
TextBlock *tb = (TextBlock *) vtb;
if (!tb)
return;
tb->first = first;
tb->kanji = kanji;
tb->underlined = underlined;
tb->script = script;
tb->marginN = newMargin(tPtr, margins);
}
void
WMGetTextBlockProperties(WMText *tPtr, void *vtb, unsigned int *first,
unsigned int *kanji, unsigned int *underlined, int *script,
WMRulerMargins *margins)
{
TextBlock *tb = (TextBlock *) vtb;
if (!tb)
return;
if (first) *first = tb->first;
if (kanji) *kanji = tb->kanji;
if (underlined) *underlined = tb->underlined;
if (script) *script = tb->script;
if (margins) margins = &tPtr->margins[tb->marginN];
}
void
WMPrependTextBlock(WMText *tPtr, void *vtb)
{
TextBlock *tb = (TextBlock *)vtb;
if (!tPtr || !tb)
return;
if (tb->graphic) {
if(tb->object) {
WMWidget *w = tb->d.widget;
if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
(W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
(W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
}
}
WMAddToArray(tPtr->gfxItems, (void *)tb);
tPtr->tpos = 1;
} else {
tPtr->tpos = tb->used;
}
if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
tb->next = tb->prior = NULL;
tb->first = True;
tPtr->lastTextBlock = tPtr->firstTextBlock
= tPtr->currentTextBlock = tb;
return;
}
if(!tb->first) {
tb->marginN = tPtr->currentTextBlock->marginN;
}
tb->next = tPtr->currentTextBlock;
tb->prior = tPtr->currentTextBlock->prior;
if (tPtr->currentTextBlock->prior)
tPtr->currentTextBlock->prior->next = tb;
tPtr->currentTextBlock->prior = tb;
if (!tb->prior)
tPtr->firstTextBlock = tb;
tPtr->currentTextBlock = tb;
}
void
WMAppendTextBlock(WMText *tPtr, void *vtb)
{
TextBlock *tb = (TextBlock *)vtb;
if (!tPtr || !tb)
return;
if (tb->graphic) {
if(tb->object) {
WMWidget *w = tb->d.widget;
if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
(W_VIEW(w))->attribs.cursor =
tPtr->view->screen->defaultCursor;
(W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
}
}
WMAddToArray(tPtr->gfxItems, (void *)tb);
tPtr->tpos = 1;
} else {
tPtr->tpos = tb->used;
}
if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
tb->next = tb->prior = NULL;
tb->first = True;
tPtr->lastTextBlock = tPtr->firstTextBlock
= tPtr->currentTextBlock = tb;
return;
}
if(!tb->first) {
tb->marginN = tPtr->currentTextBlock->marginN;
}
tb->next = tPtr->currentTextBlock->next;
tb->prior = tPtr->currentTextBlock;
if (tPtr->currentTextBlock->next)
tPtr->currentTextBlock->next->prior = tb;
tPtr->currentTextBlock->next = tb;
if (!tb->next)
tPtr->lastTextBlock = tb;
tPtr->currentTextBlock = tb;
}
void*
WMRemoveTextBlock(WMText *tPtr)
{
TextBlock *tb = NULL;
if (!tPtr || !tPtr->firstTextBlock || !tPtr->lastTextBlock
|| !tPtr->currentTextBlock) {
return NULL;
}
tb = tPtr->currentTextBlock;
if (tb->graphic) {
WMRemoveFromArray(tPtr->gfxItems, (void *)tb);
if(tb->object) {
WMUnmapWidget(tb->d.widget);
}
}
if (tPtr->currentTextBlock == tPtr->firstTextBlock) {
if (tPtr->currentTextBlock->next)
tPtr->currentTextBlock->next->prior = NULL;
tPtr->firstTextBlock = tPtr->currentTextBlock->next;
tPtr->currentTextBlock = tPtr->firstTextBlock;
} else if (tPtr->currentTextBlock == tPtr->lastTextBlock) {
tPtr->currentTextBlock->prior->next = NULL;
tPtr->lastTextBlock = tPtr->currentTextBlock->prior;
tPtr->currentTextBlock = tPtr->lastTextBlock;
} else {
tPtr->currentTextBlock->prior->next = tPtr->currentTextBlock->next;
tPtr->currentTextBlock->next->prior = tPtr->currentTextBlock->prior;
tPtr->currentTextBlock = tPtr->currentTextBlock->next;
}
return (void *)tb;
}
#if 0
static void
destroyWidget(WMWidget *widget)
{
WMDestroyWidget(widget);
// -- never do this -- wfree(widget);
}
#endif
void
WMDestroyTextBlock(WMText *tPtr, void *vtb)
{
TextBlock *tb = (TextBlock *)vtb;
if (!tPtr || !tb)
return;
if (tb->graphic) {
if(tb->object) {
/* naturally, there's a danger to destroying widgets whose action
* brings us here: ie. press a button to destroy it...
* need to find a safer way. till then... this stays commented out */
/* 5 months later... destroy it 10 seconds after now which should
* be enough time for the widget's action to be completed... :-) */
// This is a bad assumption. Just destroy the widget here.
// if the caller needs it, it can protect it with W_RetainView()
//WMAddTimerHandler(10000, destroyWidget, (void *)tb->d.widget);
WMDestroyWidget(tb->d.widget);
//tb->d.widget = NULL;
} else {
WMReleasePixmap(tb->d.pixmap);
//tb->d.pixmap = NULL;
}
} else {
WMReleaseFont(tb->d.font);
}
WMReleaseColor(tb->color);
// isn't this going to memleak if nsections==0? if (tb->sections && tb->nsections > 0)
if (tb->sections)
wfree(tb->sections);
wfree(tb->text);
wfree(tb);
//tb = NULL;
}
void
WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
{
if (!tPtr)
return;
if (color)
tPtr->fgGC = WMColorGC(color);
else {
WMColor *color = WMBlackColor(tPtr->view->screen);
tPtr->fgGC = WMColorGC(color);
WMReleaseColor(color);
}
paintText(tPtr);
}
void
WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
{
if (!tPtr)
return;
if (color) {
tPtr->bgGC = WMColorGC(color);
W_SetViewBackgroundColor(tPtr->view, color);
} else {
tPtr->bgGC = WMColorGC(WMWhiteColor(tPtr->view->screen));
W_SetViewBackgroundColor(tPtr->view,
WMWhiteColor(tPtr->view->screen));
}
paintText(tPtr);
}
void WMSetTextBackgroundPixmap(WMText *tPtr, WMPixmap *pixmap)
{
if (!tPtr)
return;
if (tPtr->bgPixmap)
WMReleasePixmap(tPtr->bgPixmap);
if (pixmap)
tPtr->bgPixmap = WMRetainPixmap(pixmap);
else
tPtr->bgPixmap = NULL;
}
void
WMSetTextRelief(WMText *tPtr, WMReliefType relief)
{
if (!tPtr)
return;
tPtr->flags.relief = relief;
textDidResize(tPtr->view->delegate, tPtr->view);
}
void
WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
{
if (!tPtr)
return;
if (shouldhave && !tPtr->hS) {
tPtr->hS = WMCreateScroller(tPtr);
(W_VIEW(tPtr->hS))->attribs.cursor = tPtr->view->screen->defaultCursor;
(W_VIEW(tPtr->hS))->attribFlags |= CWOverrideRedirect | CWCursor;
WMSetScrollerArrowsPosition(tPtr->hS, WSAMinEnd);
WMSetScrollerAction(tPtr->hS, scrollersCallBack, tPtr);
WMMapWidget(tPtr->hS);
} else if (!shouldhave && tPtr->hS) {
WMUnmapWidget(tPtr->hS);
WMDestroyWidget(tPtr->hS);
tPtr->hS = NULL;
}
tPtr->hpos = 0;
tPtr->prevHpos = 0;
textDidResize(tPtr->view->delegate, tPtr->view);
}
void
WMSetTextHasRuler(WMText *tPtr, Bool shouldhave)
{
if (!tPtr)
return;
if(shouldhave && !tPtr->ruler) {
tPtr->ruler = WMCreateRuler(tPtr);
(W_VIEW(tPtr->ruler))->attribs.cursor =
tPtr->view->screen->defaultCursor;
(W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
WMSetRulerReleaseAction(tPtr->ruler, rulerReleaseCallBack, tPtr);
WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
} else if(!shouldhave && tPtr->ruler) {
WMShowTextRuler(tPtr, False);
WMDestroyWidget(tPtr->ruler);
tPtr->ruler = NULL;
}
textDidResize(tPtr->view->delegate, tPtr->view);
}
void
WMShowTextRuler(WMText *tPtr, Bool show)
{
if(!tPtr)
return;
if(!tPtr->ruler)
return;
if(tPtr->flags.monoFont)
show = False;
tPtr->flags.rulerShown = show;
if(show) {
WMMapWidget(tPtr->ruler);
} else {
WMUnmapWidget(tPtr->ruler);
}
textDidResize(tPtr->view->delegate, tPtr->view);
}
Bool
WMGetTextRulerShown(WMText *tPtr)
{
if(!tPtr)
return 0;
if(!tPtr->ruler)
return 0;
return tPtr->flags.rulerShown;
}
void
WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
{
if (!tPtr)
return;
if (shouldhave && !tPtr->vS) {
tPtr->vS = WMCreateScroller(tPtr);
(W_VIEW(tPtr->vS))->attribs.cursor = tPtr->view->screen->defaultCursor;
(W_VIEW(tPtr->vS))->attribFlags |= CWOverrideRedirect | CWCursor;
WMSetScrollerArrowsPosition(tPtr->vS, WSAMaxEnd);
WMSetScrollerAction(tPtr->vS, scrollersCallBack, tPtr);
WMMapWidget(tPtr->vS);
} else if (!shouldhave && tPtr->vS) {
WMUnmapWidget(tPtr->vS);
WMDestroyWidget(tPtr->vS);
tPtr->vS = NULL;
}
tPtr->vpos = 0;
tPtr->prevVpos = 0;
textDidResize(tPtr->view->delegate, tPtr->view);
}
Bool
WMScrollText(WMText *tPtr, int amount)
{
Bool scroll=False;
if (!tPtr)
return False;
if (amount == 0 || !tPtr->view->flags.realized)
return False;
if (amount < 0) {
if (tPtr->vpos > 0) {
if (tPtr->vpos > abs(amount)) tPtr->vpos += amount;
else tPtr->vpos=0;
scroll=True;
}
} else {
int limit = tPtr->docHeight - tPtr->visible.h;
if (tPtr->vpos < limit) {
if (tPtr->vpos < limit-amount) tPtr->vpos += amount;
else tPtr->vpos = limit;
scroll = True;
}
}
if (scroll && tPtr->vpos != tPtr->prevVpos) {
updateScrollers(tPtr);
paintText(tPtr);
}
tPtr->prevVpos = tPtr->vpos;
return scroll;
}
Bool
WMPageText(WMText *tPtr, Bool direction)
{
if (!tPtr) return False;
if (!tPtr->view->flags.realized) return False;
return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
}
void
WMSetTextEditable(WMText *tPtr, Bool editable)
{
if (!tPtr)
return;
tPtr->flags.editable = editable;
}
int
WMGetTextEditable(WMText *tPtr)
{
if (!tPtr)
return 0;
return tPtr->flags.editable;
}
void
WMSetTextIndentNewLines(WMText *tPtr, Bool indent)
{
if (!tPtr)
return;
tPtr->flags.indentNewLine = indent;
}
void
WMSetTextIgnoresNewline(WMText *tPtr, Bool ignore)
{
if (!tPtr)
return;
tPtr->flags.ignoreNewLine = ignore;
}
Bool
WMGetTextIgnoresNewline(WMText *tPtr)
{
if (!tPtr)
return True;
return tPtr->flags.ignoreNewLine;
}
void
WMSetTextUsesMonoFont(WMText *tPtr, Bool mono)
{
if (!tPtr)
return;
if (mono) {
if(tPtr->flags.rulerShown)
WMShowTextRuler(tPtr, False);
if(tPtr->flags.alignment != WALeft)
tPtr->flags.alignment = WALeft;
}
tPtr->flags.monoFont = mono;
textDidResize(tPtr->view->delegate, tPtr->view);
}
Bool
WMGetTextUsesMonoFont(WMText *tPtr)
{
if (!tPtr)
return True;
return tPtr->flags.monoFont;
}
void
WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
{
if (!tPtr)
return;
WMReleaseFont(tPtr->dFont);
if (font)
tPtr->dFont = WMRetainFont(font);
else
tPtr->dFont = WMRetainFont(WMSystemFontOfSize(tPtr->view->screen, 12));
}
WMFont *
WMGetTextDefaultFont(WMText *tPtr)
{
if (!tPtr)
return NULL;
else
return WMRetainFont(tPtr->dFont);
}
void
WMSetTextDefaultColor(WMText *tPtr, WMColor *color)
{
if (!tPtr)
return;
WMReleaseColor(tPtr->dColor);
if (color)
tPtr->dColor = WMRetainColor(color);
else
tPtr->dColor = WMBlackColor(tPtr->view->screen);
}
WMColor *
WMGetTextDefaultColor(WMText *tPtr)
{
if (!tPtr)
return NULL;
else
return WMRetainColor(tPtr->dColor);
}
void
WMSetTextAlignment(WMText *tPtr, WMAlignment alignment)
{
if (!tPtr)
return;
if(tPtr->flags.monoFont)
tPtr->flags.alignment = WALeft;
else
tPtr->flags.alignment = alignment;
WMThawText(tPtr);
}
int
WMGetTextInsertType(WMText *tPtr)
{
if (!tPtr)
return 0;
return tPtr->flags.prepend;
}
void
WMSetTextSelectionColor(WMText *tPtr, WMColor *color)
{
if (!tPtr || !color)
return;
setSelectionProperty(tPtr, NULL, color, -1);
}
WMColor *
WMGetTextSelectionColor(WMText *tPtr)
{
TextBlock *tb;
if (!tPtr)
return NULL;
tb = tPtr->currentTextBlock;
if (!tb || !tPtr->flags.ownsSelection)
return NULL;
if(!tb->selected)
return NULL;
return tb->color;
}
void
WMSetTextSelectionFont(WMText *tPtr, WMFont *font)
{
if (!tPtr || !font)
return;
setSelectionProperty(tPtr, font, NULL, -1) ;
}
WMFont *
WMGetTextSelectionFont(WMText *tPtr)
{
TextBlock *tb;
if (!tPtr)
return NULL;
tb = tPtr->currentTextBlock;
if (!tb || !tPtr->flags.ownsSelection)
return NULL;
if(!tb->selected)
return NULL;
if(tb->graphic) {
tb = getFirstNonGraphicBlockFor(tb, 1);
if(!tb)
return NULL;
}
return (tb->selected ? tb->d.font : NULL);
}
void
WMSetTextSelectionUnderlined(WMText *tPtr, int underlined)
{
if (!tPtr || (underlined!=0 && underlined !=1))
return;
setSelectionProperty(tPtr, NULL, NULL, underlined);
}
int
WMGetTextSelectionUnderlined(WMText *tPtr)
{
TextBlock *tb;
if (!tPtr)
return 0;
tb = tPtr->currentTextBlock;
if (!tb || !tPtr->flags.ownsSelection)
return 0;
if(!tb->selected)
return 0;
return tb->underlined;
}
void
WMFreezeText(WMText *tPtr)
{
if (!tPtr)
return;
tPtr->flags.frozen = True;
}
void
WMThawText(WMText *tPtr)
{
if (!tPtr)
return;
tPtr->flags.frozen = False;
if(tPtr->flags.monoFont) {
int j, c = WMGetArrayItemCount(tPtr->gfxItems);
TextBlock *tb;
/* make sure to unmap widgets no matter where they are */
/* they'll be later remapped if needed by paintText */
for(j=0; j<c; j++) {
if ((tb = (TextBlock *) WMGetFromArray(tPtr->gfxItems, j))) {
if (tb->object && ((W_VIEW(tb->d.widget))->flags.mapped))
WMUnmapWidget(tb->d.widget);
}
}
}
tPtr->flags.laidOut = False;
layOutDocument(tPtr);
updateScrollers(tPtr);
paintText(tPtr);
tPtr->flags.needsLayOut = False;
}
/* find first occurence of a string */
static char *
mystrstr(char *haystack, char *needle, unsigned short len, char *end,
Bool caseSensitive)
{
char *ptr;
if(!haystack || !needle || !end)
return NULL;
for (ptr = haystack; ptr < end; ptr++) {
if(caseSensitive) {
if (*ptr == *needle && !strncmp(ptr, needle, len))
return ptr;
} else {
if (tolower(*ptr) == tolower(*needle) &&
!strncasecmp(ptr, needle, len))
return ptr;
}
}
return NULL;
}
/* find last occurence of a string */
static char *
mystrrstr(char *haystack, char *needle, unsigned short len, char *end,
Bool caseSensitive)
{
char *ptr;
if(!haystack || !needle || !end)
return NULL;
for (ptr = haystack-2; ptr > end; ptr--) {
if(caseSensitive) {
if (*ptr == *needle && !strncmp(ptr, needle, len))
return ptr;
} else {
if (tolower(*ptr) == tolower(*needle) &&
!strncasecmp(ptr, needle, len))
return ptr;
}
}
return NULL;
}
Bool
WMFindInTextStream(WMText *tPtr, char *needle, Bool direction,
Bool caseSensitive)
{
TextBlock *tb;
char *mark=NULL;
unsigned short pos;
if (!tPtr || !needle)
return False;
#if 0
if (! (tb = tPtr->currentTextBlock)) {
if (! (tb = ( (direction > 0) ?
tPtr->firstTextBlock : tPtr->lastTextBlock) ) ){
return False;
}
} else {
/* if(tb != ((direction>0) ?tPtr->firstTextBlock : tPtr->lastTextBlock))
tb = (direction>0) ? tb->next : tb->prior; */
if(tb != tPtr->lastTextBlock)
tb = tb->prior;
}
#endif
tb = tPtr->currentTextBlock;
pos = tPtr->tpos;
while(tb) {
if (!tb->graphic) {
if(direction > 0) {
if(pos+1 < tb->used)
pos++;
if(tb->used - pos> 0 && pos > 0) {
mark = mystrstr(&tb->text[pos], needle,
strlen(needle), &tb->text[tb->used], caseSensitive);
} else {
tb = tb->next;
pos = 0;
continue;
}
} else {
if(pos-1 > 0)
pos--;
if(pos > 0) {
mark = mystrrstr(&tb->text[pos], needle,
strlen(needle), tb->text, caseSensitive);
} else {
tb = tb->prior;
if(!tb)
return False;
pos = tb->used;
continue;
}
}
if(mark) {
WMFont *font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
tPtr->tpos = (int)(mark - tb->text);
tPtr->currentTextBlock = tb;
updateCursorPosition(tPtr);
tPtr->sel.y = tPtr->cursor.y+5;
tPtr->sel.h = tPtr->cursor.h-10;
tPtr->sel.x = tPtr->cursor.x +1;
tPtr->sel.w = WMIN(WMWidthOfString(font,
&tb->text[tPtr->tpos], strlen(needle)),
tPtr->docWidth - tPtr->sel.x);
tPtr->flags.ownsSelection = True;
paintText(tPtr);
return True;
}
}
tb = (direction>0) ? tb->next : tb->prior;
if(tb) {
pos = (direction>0) ? 0 : tb->used;
}
}
return False;
}
Bool
WMReplaceTextSelection(WMText *tPtr, char *replacement)
{
if (!tPtr)
return False;
if (!tPtr->flags.ownsSelection)
return False;
removeSelection(tPtr);
if(replacement) {
insertTextInteractively(tPtr, replacement, strlen(replacement));
updateCursorPosition(tPtr);
paintText(tPtr);
}
return True;
}