X-Git-Url: https://repo.or.cz/w/wmaker-crm.git/blobdiff_plain/59fc927dc9f183802621138534fa6eaafe5593ba..688a56e8ab67b56550e2874d9d7423f0d435bfd9:/WINGs/wtext.c diff --git a/WINGs/wtext.c b/WINGs/wtext.c dissimilarity index 95% index cb360b1a..f032ca6d 100644 --- a/WINGs/wtext.c +++ b/WINGs/wtext.c @@ -1,4241 +1,3959 @@ - -/* WINGs WMText: multi-line/font/color/graphic text widget, by Nwanua. */ - - -#include "WINGsP.h" -#include -#include -#include - -#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 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; snsections && !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; jgfxItems, 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; jgfxItems, 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; snsections-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? (snsections) : (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 (poscursor.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 = (posused)? 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->vposvpos+=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->hposhpos+=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; igraphic) { - 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; isections[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 = (tb->next != NULL); - - 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; icurrentTextBlock; - 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); - - -} - -# if 0 -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); -} -#endif - - -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; jgfxItems, 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; - -} - - + +/* WINGs WMText: multi-line/font/color/graphic text widget, by Nwanua. */ + +#include "WINGsP.h" +#include +#include +#include + +#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 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 = (tb->next != NULL); + + 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); + +} + +# if 0 +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); +} +#endif + +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; + +}