1
0
mirror of https://github.com/gryf/wmaker.git synced 2026-01-07 22:34:18 +01:00
Files
wmaker/WINGs/wtext.c
dan 9aca0d5f6e - Check whether libXft is at least version 2.1.2 else refuse to compile.
- Fixed bug in icon chooser dialog that could cause a segmentation fault
  in some cases (Pascal Hofstee <caelian@gmail.com>)
- Fixed crash in asm code in wrlib, with new versions of gcc.
- Fixed bug in the x86_PseudoColor_32_to_8() function which incorrectly
  used the r, g, b fields in the conversion.
- Fixed x86 ASM code in wrlib to work on 64 bit architectures.
- Fixed the focus flicker seen with some apps (notably gtk2)
  (Alexey Spiridonov <snarkmaster@gmail.com>)
- Fixed all crashing bugs that were generated by wmaker starting with the
  WMState file missing.
- Added NetWM support (a modified version of the patch originaly written
  by Peter Zijlstra <a.p.zijlstra@chello.nl>)
- Applied patch to enhance the Virtual Desktop behaviour, and to integrate
  it with the NetWM code (Peter Zijlstra <a.p.zijlstra@chello.nl>)
- Applied a few xinerama and placement fixes (Peter Zijlstra
    <a.p.zijlstra@chello.nl>)
- Fixed memory leak in dock code.
- Fixed and enhanced the text wrapping in WINGs.
- Fixed the layout of some elements in WPrefs.app
- Added workaround for aplications that don't set the required hints on the
  client leader window, but they set them on normal windows (observer with
  KDE 3.3.0 mainly). This will allow these apps to get an appicon again.
  (they should be fixed still)
- Added workaround for applications that do not set a command with
  XSetCommand(), but instead they set the _NET_WM_PID property. This works
  with operating systems that offer a /proc interface similar to what linux
  has. (This also is to fix problems with KDE 3.3.0 apps, but not only them).
- Fixed bug with autostart and exit scripts not being executed if user
  GNUstep path was different from ~/GNUstep (when setting GNUSTEP_USER_ROOT)
- Added utf8 support in WINGs (removed old X core font code)
- Added utility to convert old font names to new font names in style files
2004-10-12 01:34:32 +00:00

4238 lines
113 KiB
C

/* 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 */
WMColor *fgColor; /* The current foreground color */
WMColor *bgColor; /* The background color */
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;
WMArray *xdndSourceTypes;
WMArray *xdndDestinationTypes;
} 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;
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, WMColorGC(tPtr->bgColor), 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;
}
}
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;
color = tPtr->fgColor;
} else {
font = tb->d.font;
color = tb->color;
}
if (tPtr->flags.ownsSelection) {
XRectangle rect;
if (sectionWasSelected(tPtr, tb, &rect, s)) {
tb->selected = True;
XFillRectangle(dpy, tPtr->db, WMColorGC(scr->gray),
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, color, font,
tb->sections[s].x - tPtr->hpos, y, text, len);
if (!tPtr->flags.monoFont && tb->underlined) {
XDrawLine(dpy, tPtr->db, WMColorGC(color),
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, WMColorGC(tPtr->fgColor),
tPtr->cursor.x, y,
tPtr->cursor.x, y + tPtr->cursor.h);
}
XCopyArea(dpy, tPtr->db, win, WMColorGC(tPtr->bgColor), 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, WMColorGC(tPtr->fgColor),
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));*/
max_d = 2;
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=NULL;
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 == '\a') {
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)) {
if (WMIsDraggingFromView(tPtr->view)) {
WMDragImageFromView(tPtr->view, event);
break;
}
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) {
TextBlock *tb = tPtr->currentTextBlock;
if(WMIsDoubleClick(event)) {
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;
} else if (tb && tPtr->flags.isOverGraphic &&
tb->graphic && !tb->object && tb->d.pixmap) {
WMSetViewDragImage(tPtr->view, tb->d.pixmap);
WMDragImageFromView(tPtr->view, event);
break;
}
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;
if (WMIsDraggingFromView(tPtr->view))
WMDragImageFromView(tPtr->view, event);
}
}
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);
WMFreeArray(tPtr->xdndSourceTypes);
WMFreeArray(tPtr->xdndDestinationTypes);
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 WMArray*
dropDataTypes(WMView *self)
{
return ((Text*)self->self)->xdndSourceTypes;
}
static WMDragOperationType
wantedDropOperation(WMView *self)
{
return WDOperationCopy;
}
static Bool
acceptDropOperation(WMView *self, WMDragOperationType allowedOperation)
{
return (allowedOperation == WDOperationCopy);
}
static WMData*
fetchDragData(WMView *self, char *type)
{
TextBlock *tb = ((WMText *)self->self)->currentTextBlock;
char *desc;
WMData *data;
if (strcmp(type, "text/plain")) {
if (!tb)
return NULL;
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;
}
return NULL;
}
static WMDragSourceProcs _DragSourceProcs = {
dropDataTypes,
wantedDropOperation,
NULL,
acceptDropOperation,
NULL,
NULL,
fetchDragData
};
static WMArray*
requiredDataTypes(WMView *self, WMDragOperationType request, WMArray *sourceDataTypes)
{
return ((Text*)self->self)->xdndDestinationTypes;
}
static WMDragOperationType
allowedOperation(WMView *self, WMDragOperationType request, WMArray *sourceDataTypes)
{
return WDOperationCopy;
}
static void
performDragOperation(WMView *self, WMArray *dropData, WMArray *operations,
WMPoint* dropLocation)
{
WMText *tPtr = (WMText *)self->self;
WMData* data;
char* colorName;
WMColor *color;
if (tPtr) {
/* only one required type, implies only one drop data */
/* get application/X-color if any */
data = (WMData*)WMPopFromArray(dropData);
if (data != NULL) {
colorName = (char*)WMDataBytes(data);
color = WMCreateNamedColor(W_VIEW_SCREEN(self), colorName, True);
if(color) {
WMSetTextSelectionColor(tPtr, color);
WMReleaseColor(color);
}
}
}
}
static WMDragDestinationProcs _DragDestinationProcs = {
NULL,
requiredDataTypes,
allowedOperation,
NULL,
performDragOperation,
NULL
};
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;
}
#define XDND_TEXT_DATA_TYPE "text/plain"
#define XDND_COLOR_DATA_TYPE "application/X-color"
static WMArray*
getXdndSourceTypeArray()
{
WMArray *types = WMCreateArray(1);
WMAddToArray(types, XDND_TEXT_DATA_TYPE);
return types;
}
static WMArray*
getXdndDestinationTypeArray()
{
WMArray *types = WMCreateArray(1);
WMAddToArray(types, XDND_COLOR_DATA_TYPE);
return types;
}
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 = WMBlackColor(scr);
tPtr->fgColor = WMBlackColor(scr);
tPtr->bgColor = WMWhiteColor(scr);
W_SetViewBackgroundColor(tPtr->view, tPtr->bgColor);
gcv.graphics_exposures = False;
gcv.foreground = W_PIXEL(scr->gray);
gcv.background = W_PIXEL(scr->darkGray);
gcv.fill_style = FillStippled;
/* why not use scr->stipple here? */
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 = 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,
WMSelectionOwnerDidChangeNotification,
tPtr);
WMSetViewDragSourceProcs(tPtr->view, &_DragSourceProcs);
WMSetViewDragDestinationProcs(tPtr->view, &_DragDestinationProcs);
{
WMArray *types = WMCreateArray(2);
WMAddToArray(types, "application/X-color");
WMAddToArray(types, "application/X-image");
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;
tPtr->xdndSourceTypes = getXdndSourceTypeArray();
tPtr->xdndDestinationTypes = getXdndDestinationTypeArray();
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));
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));
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));
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 (!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 (!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->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 (!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);
} else {
WMReleasePixmap(tb->d.pixmap);
}
} 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);
}
void
WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
{
if (tPtr->fgColor)
WMReleaseColor(tPtr->fgColor);
tPtr->fgColor = WMRetainColor(color ? color : tPtr->view->screen->black);
paintText(tPtr);
}
void
WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
{
if (tPtr->bgColor)
WMReleaseColor(tPtr->bgColor);
tPtr->bgColor = WMRetainColor(color ? color : tPtr->view->screen->white);
W_SetViewBackgroundColor(tPtr->view, tPtr->bgColor);
paintText(tPtr);
}
void
WMSetTextBackgroundPixmap(WMText *tPtr, WMPixmap *pixmap)
{
if (tPtr->bgPixmap)
WMReleasePixmap(tPtr->bgPixmap);
if (pixmap)
tPtr->bgPixmap = WMRetainPixmap(pixmap);
else
tPtr->bgPixmap = NULL;
}
void
WMSetTextRelief(WMText *tPtr, WMReliefType relief)
{
tPtr->flags.relief = relief;
textDidResize(tPtr->view->delegate, tPtr->view);
}
void
WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
{
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(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->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->ruler)
return False;
return tPtr->flags.rulerShown;
}
void
WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
{
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 (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->view->flags.realized)
return False;
return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
}
void
WMSetTextEditable(WMText *tPtr, Bool editable)
{
tPtr->flags.editable = editable;
}
int
WMGetTextEditable(WMText *tPtr)
{
return tPtr->flags.editable;
}
void
WMSetTextIndentNewLines(WMText *tPtr, Bool indent)
{
tPtr->flags.indentNewLine = indent;
}
void
WMSetTextIgnoresNewline(WMText *tPtr, Bool ignore)
{
tPtr->flags.ignoreNewLine = ignore;
}
Bool
WMGetTextIgnoresNewline(WMText *tPtr)
{
return tPtr->flags.ignoreNewLine;
}
void
WMSetTextUsesMonoFont(WMText *tPtr, Bool mono)
{
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)
{
return tPtr->flags.monoFont;
}
void
WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
{
if (tPtr->dFont)
WMReleaseFont(tPtr->dFont);
if (font) {
tPtr->dFont = WMRetainFont(font);
} else {
tPtr->dFont = WMSystemFontOfSize(tPtr->view->screen, 12);
}
}
WMFont*
WMGetTextDefaultFont(WMText *tPtr)
{
return WMRetainFont(tPtr->dFont);
}
void
WMSetTextDefaultColor(WMText *tPtr, WMColor *color)
{
if (tPtr->dColor)
WMReleaseColor(tPtr->dColor);
if (color) {
tPtr->dColor = WMRetainColor(color);
} else {
tPtr->dColor = WMBlackColor(tPtr->view->screen);
}
}
WMColor*
WMGetTextDefaultColor(WMText *tPtr)
{
return tPtr->dColor;
}
void
WMSetTextAlignment(WMText *tPtr, WMAlignment alignment)
{
if(tPtr->flags.monoFont)
tPtr->flags.alignment = WALeft;
else
tPtr->flags.alignment = alignment;
WMThawText(tPtr);
}
int
WMGetTextInsertType(WMText *tPtr)
{
return tPtr->flags.prepend;
}
void
WMSetTextSelectionColor(WMText *tPtr, WMColor *color)
{
setSelectionProperty(tPtr, NULL, color, -1);
}
WMColor*
WMGetTextSelectionColor(WMText *tPtr)
{
TextBlock *tb;
tb = tPtr->currentTextBlock;
if (!tb || !tPtr->flags.ownsSelection)
return NULL;
if(!tb->selected)
return NULL;
return tb->color;
}
void
WMSetTextSelectionFont(WMText *tPtr, WMFont *font)
{
setSelectionProperty(tPtr, font, NULL, -1) ;
}
WMFont*
WMGetTextSelectionFont(WMText *tPtr)
{
TextBlock *tb;
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)
{
// check this
if (underlined!=0 && underlined!=1)
return;
setSelectionProperty(tPtr, NULL, NULL, underlined);
}
int
WMGetTextSelectionUnderlined(WMText *tPtr)
{
TextBlock *tb;
tb = tPtr->currentTextBlock;
if (!tb || !tPtr->flags.ownsSelection)
return 0;
if(!tb->selected)
return 0;
return tb->underlined;
}
void
WMFreezeText(WMText *tPtr)
{
tPtr->flags.frozen = True;
}
void
WMThawText(WMText *tPtr)
{
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 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->flags.ownsSelection)
return False;
removeSelection(tPtr);
if(replacement) {
insertTextInteractively(tPtr, replacement, strlen(replacement));
updateCursorPosition(tPtr);
paintText(tPtr);
}
return True;
}