From f43aa47d6814ed787fdfdf1746f1a5f68cdc76d9 Mon Sep 17 00:00:00 2001 From: "Carlos R. Mafra" Date: Wed, 24 Aug 2011 19:18:06 +0200 Subject: [PATCH] Revert "WINGs: Remove wruler and wtext" This reverts commit f4890b17e6d13e9c5171c16cc473f060718cb99d. It turns out that I needed some functions from wtext.c to develop a WINGs front-end to my comic book collection MySQL database. Conflicts: WINGs/Makefile.am WINGs/WINGs/WINGs.h --- WINGs/Makefile.am | 2 + WINGs/WINGs/WINGs.h | 213 ++- WINGs/wruler.c | 532 +++++++ WINGs/wtext.c | 3958 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 4686 insertions(+), 19 deletions(-) create mode 100644 WINGs/wruler.c create mode 100644 WINGs/wtext.c diff --git a/WINGs/Makefile.am b/WINGs/Makefile.am index f04bac04..7a102ecd 100644 --- a/WINGs/Makefile.am +++ b/WINGs/Makefile.am @@ -50,11 +50,13 @@ libWINGs_la_SOURCES = \ wpanel.c \ wpixmap.c \ wpopupbutton.c \ + wruler.c \ wscroller.c \ wscrollview.c \ wslider.c \ wsplitview.c \ wtabview.c \ + wtext.c \ wtextfield.c \ wview.c \ wwindow.c diff --git a/WINGs/WINGs/WINGs.h b/WINGs/WINGs/WINGs.h index 42362dc4..1e82e40d 100644 --- a/WINGs/WINGs/WINGs.h +++ b/WINGs/WINGs/WINGs.h @@ -287,24 +287,26 @@ enum { typedef int W_Class; enum { - WC_Window, - WC_Frame, - WC_Label, - WC_Button, - WC_TextField, - WC_Scroller, - WC_ScrollView, - WC_List, - WC_Browser, - WC_PopUpButton, - WC_ColorWell, - WC_Slider, - WC_SplitView, - WC_TabView, - WC_ProgressIndicator, - WC_MenuView, - WC_Text, - WC_Box + WC_Window = 0, + WC_Frame = 1, + WC_Label = 2, + WC_Button = 3, + WC_TextField = 4, + WC_Scroller = 5, + WC_ScrollView = 6, + WC_List = 7, + WC_Browser = 8, + WC_PopUpButton = 9, + WC_ColorWell = 10, + WC_Slider = 11, + WC_Matrix = 12, /* not ready */ + WC_SplitView = 13, + WC_TabView = 14, + WC_ProgressIndicator = 15, + WC_MenuView = 16, + WC_Ruler = 17, + WC_Text = 18, + WC_Box = 19 }; /* All widgets must start with the following structure @@ -348,6 +350,7 @@ typedef struct W_Slider WMSlider; typedef struct W_Matrix WMMatrix; /* not ready */ typedef struct W_SplitView WMSplitView; typedef struct W_TabView WMTabView; +typedef struct W_Ruler WMRuler; typedef struct W_Text WMText; typedef struct W_Box WMBox; @@ -433,6 +436,18 @@ typedef enum WMFontStyle { } WMFontStyle; +/* WMRuler: */ +typedef struct { + WMArray *tabs; /* a growable array of tabstops */ + unsigned short left; /* left margin marker */ + unsigned short right; /* right margin marker */ + unsigned short first; /* indentation marker for first line only */ + unsigned short body; /* body indentation marker */ + unsigned short retainCount; +} WMRulerMargins; +/* All indentation and tab markers are _relative_ to the left margin marker */ + + typedef void WMEventProc(XEvent *event, void *clientData); typedef void WMEventHook(XEvent *event); @@ -1509,8 +1524,168 @@ void WMSetSplitViewResizeSubviewsProc(WMSplitView *sPtr, int WMGetSplitViewDividerThickness(WMSplitView *sPtr); -/* ---[ WINGs/wtabview.c ]------------------------------------------------ */ +/* ...................................................................... */ + +WMRuler* WMCreateRuler (WMWidget *parent); + +WMRulerMargins* WMGetRulerMargins(WMRuler *rPtr); + +void WMSetRulerMargins(WMRuler *rPtr, WMRulerMargins margins); + +Bool WMIsMarginEqualToMargin(WMRulerMargins *aMargin, WMRulerMargins *anotherMargin); + +int WMGetGrabbedRulerMargin(WMRuler *rPtr); + +int WMGetReleasedRulerMargin(WMRuler *rPtr); + +int WMGetRulerOffset(WMRuler *rPtr); + +void WMSetRulerOffset(WMRuler *rPtr, int pixels); + +void WMSetRulerMoveAction(WMRuler *rPtr, WMAction *action, void *clientData); + +void WMSetRulerReleaseAction(WMRuler *rPtr, WMAction *action, void *clientData); + +/* ....................................................................... */ + + +#define WMCreateText(parent) WMCreateTextForDocumentType \ + ((parent), (NULL), (NULL)) + +WMText* WMCreateTextForDocumentType(WMWidget *parent, WMAction *parser, + WMAction *writer); + +void WMSetTextDelegate(WMText *tPtr, WMTextDelegate *delegate); + +void WMFreezeText(WMText *tPtr); + +#define WMRefreshText(tPtr) WMThawText((tPtr)) + +void WMThawText(WMText *tPtr); + +int WMScrollText(WMText *tPtr, int amount); + +int WMPageText(WMText *tPtr, Bool direction); + +void WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave); + +void WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave); + +void WMSetTextHasRuler(WMText *tPtr, Bool shouldhave); + +void WMShowTextRuler(WMText *tPtr, Bool show); + +int WMGetTextRulerShown(WMText *tPtr); + +void WMSetTextEditable(WMText *tPtr, Bool editable); + +int WMGetTextEditable(WMText *tPtr); + +void WMSetTextUsesMonoFont(WMText *tPtr, Bool mono); + +int WMGetTextUsesMonoFont(WMText *tPtr); + +void WMSetTextIndentNewLines(WMText *tPtr, Bool indent); + +void WMSetTextIgnoresNewline(WMText *tPtr, Bool ignore); + +int WMGetTextIgnoresNewline(WMText *tPtr); + +void WMSetTextDefaultFont(WMText *tPtr, WMFont *font); + +WMFont* WMGetTextDefaultFont(WMText *tPtr); +void WMSetTextDefaultColor(WMText *tPtr, WMColor *color); + +WMColor* WMGetTextDefaultColor(WMText *tPtr); + +void WMSetTextRelief(WMText *tPtr, WMReliefType relief); + +void WMSetTextForegroundColor(WMText *tPtr, WMColor *color); + +void WMSetTextBackgroundColor(WMText *tPtr, WMColor *color); + +void WMSetTextBackgroundPixmap(WMText *tPtr, WMPixmap *pixmap); + +void WMPrependTextStream(WMText *tPtr, char *text); + +void WMAppendTextStream(WMText *tPtr, char *text); + +#define WMClearText(tPtr) WMAppendTextStream \ + ((tPtr), (NULL)) + +/* free the text */ +char* WMGetTextStream(WMText *tPtr); + +/* free the text */ +char* WMGetTextSelectedStream(WMText *tPtr); + +/* destroy the array */ +WMArray* WMGetTextObjects(WMText *tPtr); + +/* destroy the array */ +WMArray* WMGetTextSelectedObjects(WMText *tPtr); + +void WMSetTextSelectionColor(WMText *tPtr, WMColor *color); + +WMColor* WMGetTextSelectionColor(WMText *tPtr); + +void WMSetTextSelectionFont(WMText *tPtr, WMFont *font); + +WMFont* WMGetTextSelectionFont(WMText *tPtr); + +void WMSetTextSelectionUnderlined(WMText *tPtr, int underlined); + +int WMGetTextSelectionUnderlined(WMText *tPtr); + +void WMSetTextAlignment(WMText *tPtr, WMAlignment alignment); + +Bool WMFindInTextStream(WMText *tPtr, char *needle, Bool direction, + Bool caseSensitive); + +Bool WMReplaceTextSelection(WMText *tPtr, char *replacement); + + +/* parser related stuff... use only if implementing a new parser */ + +void* WMCreateTextBlockWithObject(WMText *tPtr, WMWidget *w, char *description, + WMColor *color, unsigned short first, + unsigned short extraInfo); + +void* WMCreateTextBlockWithPixmap(WMText *tPtr, WMPixmap *p, char *description, + WMColor *color, unsigned short first, + unsigned short extraInfo); + +void* WMCreateTextBlockWithText(WMText *tPtr, char *text, WMFont *font, + WMColor *color, unsigned short first, + unsigned short length); + +void WMSetTextBlockProperties(WMText *tPtr, void *vtb, unsigned int first, + unsigned int kanji, unsigned int underlined, + int script, WMRulerMargins *margins); + +/* do NOT free the margins */ +void WMGetTextBlockProperties(WMText *tPtr, void *vtb, unsigned int *first, + unsigned int *kanji, unsigned int *underlined, + int *script, WMRulerMargins *margins); + +int WMGetTextInsertType(WMText *tPtr); + +/*int WMGetTextBlocks(WMText *tPtr); + +void WMSetCurrentTextBlock(WMText *tPtr, int current); + +int WMGetCurrentTextBlock(WMText *tPtr);*/ + +void WMPrependTextBlock(WMText *tPtr, void *vtb); + +void WMAppendTextBlock(WMText *tPtr, void *vtb); + +void* WMRemoveTextBlock(WMText *tPtr); + +void WMDestroyTextBlock(WMText *tPtr, void *vtb); + +/* ---[ WINGs/wtabview.c ]------------------------------------------------ */ WMTabView* WMCreateTabView(WMWidget *parent); diff --git a/WINGs/wruler.c b/WINGs/wruler.c new file mode 100644 index 00000000..f8d4af03 --- /dev/null +++ b/WINGs/wruler.c @@ -0,0 +1,532 @@ +/* + * WINGs WMRuler: nifty ruler widget for WINGs :-) + * + * Copyright (c) 1999-2000 Nwanua Elumeze + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "WINGsP.h" +#include "wconfig.h" + +#define MIN_DOC_WIDTH 10 + +typedef struct W_Ruler { + W_Class widgetClass; + W_View *view; + W_View *pview; /* the parent's view (for drawing the line) */ + + WMAction *moveAction; /* what to when while moving */ + WMAction *releaseAction; /* what to do when released */ + void *clientData; + + WMColor *fg; + GC fgGC, bgGC; + WMFont *font; + WMRulerMargins margins; + int offset; + int motion; /* the position of the _moving_ marker(s) */ + int end; /* the last tick on the baseline (restrict markers to it) */ + + Pixmap drawBuffer; + + struct { + unsigned int whichMarker:3; + /* 0, 1, 2, 3, 4, 5, 6 */ + /* none, left, right, first, body, tabstop, first & body */ + + unsigned int buttonPressed:1; + unsigned int redraw:1; + unsigned int RESERVED:27; + } flags; +} Ruler; + +/* Marker for left margin + + |\ + | \ + |__\ + | + | + + */ +static void drawLeftMarker(Ruler * rPtr) +{ + XPoint points[4]; + int xpos = (rPtr->flags.whichMarker == 1 ? rPtr->motion : rPtr->margins.left); + + XDrawLine(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC, xpos, 8, xpos, 22); + points[0].x = xpos; + points[0].y = 1; + points[1].x = points[0].x + 6; + points[1].y = 8; + points[2].x = points[0].x + 6; + points[2].y = 9; + points[3].x = points[0].x; + points[3].y = 9; + XFillPolygon(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC, + points, 4, Convex, CoordModeOrigin); +} + +/* Marker for right margin + + /| + / | +/__| + | + | + + */ +static void drawRightMarker(Ruler * rPtr) +{ + XPoint points[4]; + int xpos = (rPtr->flags.whichMarker == 2 ? rPtr->motion : rPtr->margins.right); + + XDrawLine(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC, xpos, 8, xpos, 22); + points[0].x = xpos + 1; + points[0].y = 0; + points[1].x = points[0].x - 6; + points[1].y = 7; + points[2].x = points[0].x - 6; + points[2].y = 9; + points[3].x = points[0].x; + points[3].y = 9; + XFillPolygon(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC, + points, 4, Convex, CoordModeOrigin); +} + +/* Marker for first line only + _____ + |___| + | + + */ +static void drawFirstMarker(Ruler * rPtr) +{ + int xpos = ((rPtr->flags.whichMarker == 3 || rPtr->flags.whichMarker == 6) ? + rPtr->motion : rPtr->margins.first); + + XFillRectangle(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC, xpos - 5, 10, 11, 5); + XDrawLine(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC, xpos, 12, xpos, 22); +} + +/* Marker for rest of body + _____ + \ / + \./ + */ +static void drawBodyMarker(Ruler * rPtr) +{ + XPoint points[4]; + int xpos = ((rPtr->flags.whichMarker == 4 || rPtr->flags.whichMarker == 6) ? + rPtr->motion : rPtr->margins.body); + + points[0].x = xpos - 5; + points[0].y = 16; + points[1].x = points[0].x + 11; + points[1].y = 16; + points[2].x = points[0].x + 5; + points[2].y = 22; + XFillPolygon(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC, + points, 3, Convex, CoordModeOrigin); +} + +static void createDrawBuffer(Ruler * rPtr) +{ + if (!rPtr->view->flags.realized) + return; + + if (rPtr->drawBuffer) + XFreePixmap(rPtr->view->screen->display, rPtr->drawBuffer); + + rPtr->drawBuffer = XCreatePixmap(rPtr->view->screen->display, + rPtr->view->window, rPtr->view->size.width, 40, + rPtr->view->screen->depth); + XFillRectangle(rPtr->view->screen->display, rPtr->drawBuffer, + rPtr->bgGC, 0, 0, rPtr->view->size.width, 40); +} + +static void drawRulerOnPixmap(Ruler * rPtr) +{ + int i, j, w, m; + char c[3]; + int marks[9] = { 11, 3, 5, 3, 7, 3, 5, 3 }; + + if (!rPtr->drawBuffer || !rPtr->view->flags.realized) + return; + + XFillRectangle(rPtr->view->screen->display, rPtr->drawBuffer, + rPtr->bgGC, 0, 0, rPtr->view->size.width, 40); + + WMDrawString(rPtr->view->screen, rPtr->drawBuffer, rPtr->fg, + rPtr->font, rPtr->margins.left + 2, 26, _("0 inches"), 10); + + /* marker ticks */ + i = j = m = 0; + w = rPtr->view->size.width - rPtr->margins.left; + while (m < w) { + XDrawLine(rPtr->view->screen->display, rPtr->drawBuffer, + rPtr->fgGC, rPtr->margins.left + m, 23, rPtr->margins.left + m, marks[i % 8] + 23); + if (i != 0 && i % 8 == 0) { + if (j < 10) + snprintf(c, 3, "%d", ++j); + else + snprintf(c, 3, "%2d", ++j); + WMDrawString(rPtr->view->screen, rPtr->drawBuffer, rPtr->fg, + rPtr->font, rPtr->margins.left + 2 + m, 26, c, 2); + } + m = (++i) * 10; + } + + rPtr->end = rPtr->margins.left + m - 10; + if (rPtr->margins.right > rPtr->end) + rPtr->margins.right = rPtr->end; + /* base line */ + XDrawLine(rPtr->view->screen->display, rPtr->drawBuffer, rPtr->fgGC, + rPtr->margins.left, 22, rPtr->margins.left + m - 10, 22); + + drawLeftMarker(rPtr); + drawRightMarker(rPtr); + drawFirstMarker(rPtr); + drawBodyMarker(rPtr); + + rPtr->flags.redraw = False; +} + +static void paintRuler(Ruler * rPtr) +{ + if (!rPtr->drawBuffer || !rPtr->view->flags.realized) + return; + + if (rPtr->flags.redraw) + drawRulerOnPixmap(rPtr); + XCopyArea(rPtr->view->screen->display, rPtr->drawBuffer, + rPtr->view->window, rPtr->bgGC, 0, 0, rPtr->view->size.width, 40, 0, 0); +} + +static Bool verifyMarkerMove(Ruler * rPtr, int x) +{ + if (rPtr->flags.whichMarker < 1 || rPtr->flags.whichMarker > 6) + return False; + + switch (rPtr->flags.whichMarker) { + case 1: + if (x > rPtr->margins.right - 10 || x < rPtr->offset || + rPtr->margins.body + x > rPtr->margins.right - MIN_DOC_WIDTH || + rPtr->margins.first + x > rPtr->margins.right - MIN_DOC_WIDTH) + return False; + break; + + case 2: + if (x < rPtr->margins.first + MIN_DOC_WIDTH || x < rPtr->margins.body + MIN_DOC_WIDTH || x < rPtr->margins.left + MIN_DOC_WIDTH || x > rPtr->end) /*rPtr->view->size.width) */ + return False; + break; + + case 3: + if (x >= rPtr->margins.right - MIN_DOC_WIDTH || x < rPtr->margins.left) + return False; + break; + + case 4: + if (x >= rPtr->margins.right - MIN_DOC_WIDTH || x < rPtr->margins.left) + return False; + break; + + case 6: + if (x >= rPtr->margins.right - MIN_DOC_WIDTH || x < rPtr->margins.left) + return False; + break; + + default: + return False; + } + + rPtr->motion = x; + return True; +} + +static int whichMarker(Ruler * rPtr, int x, int y) +{ + if (x < rPtr->offset || y > 22) + return 0; + + if (rPtr->margins.left - x >= -6 && y <= 9 && (rPtr->margins.left - x <= 0) && y >= 4) { + rPtr->motion = rPtr->margins.left; + return 1; + } + if (rPtr->margins.right - x >= -1 && y <= 11 && rPtr->margins.right - x <= 5 && y >= 4) { + rPtr->motion = rPtr->margins.right; + return 2; + } +#if 0 + /* both first and body? */ + if (rPtr->margins.first - x <= 4 && rPtr->margins.first - x >= -5 + && rPtr->margins.body - x <= 4 && rPtr->margins.body - x >= -5 && y >= 15 && y <= 17) { + rPtr->motion = rPtr->margins.first; + return 6; + } +#endif + + if (rPtr->margins.first - x <= 4 && y <= 15 && rPtr->margins.first - x >= -5 && y >= 10) { + rPtr->motion = rPtr->margins.first; + return 3; + } + if (rPtr->margins.body - x <= 4 && y <= 21 && rPtr->margins.body - x >= -5 && y >= 17) { + rPtr->motion = rPtr->margins.body; + return 4; + } + /* do tabs (5) */ + + return 0; +} + +static void rulerDidResize(W_ViewDelegate * self, WMView * view) +{ + Ruler *rPtr = (Ruler *) view->self; + + createDrawBuffer(rPtr); + rPtr->flags.redraw = True; + paintRuler(rPtr); + +} + +static void handleEvents(XEvent * event, void *data) +{ + Ruler *rPtr = (Ruler *) data; + + switch (event->type) { + case Expose: + rulerDidResize(rPtr->view->delegate, rPtr->view); + break; + + case MotionNotify: + if (rPtr->flags.buttonPressed && (event->xmotion.state & Button1Mask)) { + if (verifyMarkerMove(rPtr, event->xmotion.x)) { + GC gc = WMColorGC(WMDarkGrayColor(rPtr->view->screen)); + + if (rPtr->moveAction) + (rPtr->moveAction) (rPtr, rPtr->clientData); + rPtr->flags.redraw = True; + paintRuler(rPtr); + XSetLineAttributes(rPtr->view->screen->display, gc, 1, + LineSolid, CapNotLast, JoinMiter); + XDrawLine(rPtr->pview->screen->display, + rPtr->pview->window, + gc, rPtr->motion + 1, 40, + rPtr->motion + 1, rPtr->pview->size.height - 5); + } + } + break; + + case ButtonPress: + if (event->xbutton.button != Button1) + return; + rPtr->flags.buttonPressed = True; + rPtr->flags.whichMarker = whichMarker(rPtr, event->xmotion.x, event->xmotion.y); + break; + + case ButtonRelease: + if (event->xbutton.button != Button1) + return; + rPtr->flags.buttonPressed = False; + switch (rPtr->flags.whichMarker) { + case 1:{ + int change = rPtr->margins.left - rPtr->motion; + + rPtr->margins.first -= change; + rPtr->margins.body -= change; + rPtr->margins.left = rPtr->motion; + rPtr->flags.redraw = True; + paintRuler(rPtr); + break; + } + case 2: + rPtr->margins.right = rPtr->motion; + break; + case 3: + rPtr->margins.first = rPtr->motion; + break; + case 4: + rPtr->margins.body = rPtr->motion; + break; + case 6: + rPtr->margins.first = rPtr->margins.body = rPtr->motion; + break; + } + if (rPtr->releaseAction) + (rPtr->releaseAction) (rPtr, rPtr->clientData); + break; + } +} + +W_ViewDelegate _RulerViewDelegate = { + NULL, + NULL, + rulerDidResize, + NULL, + NULL +}; + +WMRuler *WMCreateRuler(WMWidget * parent) +{ + Ruler *rPtr = wmalloc(sizeof(Ruler)); + unsigned int w = WMWidgetWidth(parent); + + rPtr->widgetClass = WC_Ruler; + + rPtr->view = W_CreateView(W_VIEW(parent)); + + if (!rPtr->view) { + wfree(rPtr); + return NULL; + } + + rPtr->view->self = rPtr; + + rPtr->drawBuffer = (Pixmap) NULL; + + W_ResizeView(rPtr->view, w, 40); + + WMCreateEventHandler(rPtr->view, ExposureMask | StructureNotifyMask + | EnterWindowMask | LeaveWindowMask | FocusChangeMask + | ButtonReleaseMask | ButtonPressMask | KeyReleaseMask + | KeyPressMask | Button1MotionMask, handleEvents, rPtr); + + rPtr->view->delegate = &_RulerViewDelegate; + + rPtr->fg = WMBlackColor(rPtr->view->screen); + rPtr->fgGC = WMColorGC(rPtr->fg); + rPtr->bgGC = WMColorGC(WMGrayColor(rPtr->view->screen)); + rPtr->font = WMSystemFontOfSize(rPtr->view->screen, 8); + + rPtr->offset = 22; + rPtr->margins.left = 22; + rPtr->margins.body = 22; + rPtr->margins.first = 42; + rPtr->margins.right = (w < 502 ? w : 502); + rPtr->margins.tabs = NULL; + + rPtr->flags.whichMarker = 0; /* none */ + rPtr->flags.buttonPressed = False; + rPtr->flags.redraw = True; + + rPtr->moveAction = NULL; + rPtr->releaseAction = NULL; + + rPtr->pview = W_VIEW(parent); + + return rPtr; +} + +void WMSetRulerMargins(WMRuler * rPtr, WMRulerMargins margins) +{ + if (!rPtr) + return; + rPtr->margins.left = margins.left + rPtr->offset; + rPtr->margins.right = margins.right + rPtr->offset; + rPtr->margins.first = margins.first + rPtr->offset; + rPtr->margins.body = margins.body + rPtr->offset; + rPtr->margins.tabs = margins.tabs; /*for loop */ + rPtr->flags.redraw = True; + paintRuler(rPtr); + +} + +WMRulerMargins *WMGetRulerMargins(WMRuler * rPtr) +{ + WMRulerMargins *margins = wmalloc(sizeof(WMRulerMargins)); + + if (!rPtr) { + margins->first = margins->body = margins->left = 0; + margins->right = 100; + return margins; + } + + margins->left = rPtr->margins.left - rPtr->offset; + margins->right = rPtr->margins.right - rPtr->offset; + margins->first = rPtr->margins.first - rPtr->offset; + margins->body = rPtr->margins.body - rPtr->offset; + /*for */ + margins->tabs = rPtr->margins.tabs; + + return margins; +} + +Bool WMIsMarginEqualToMargin(WMRulerMargins * aMargin, WMRulerMargins * anotherMargin) +{ + if (aMargin == anotherMargin) + return True; + else if (!aMargin || !anotherMargin) + return False; + if (aMargin->left != anotherMargin->left) + return False; + if (aMargin->first != anotherMargin->first) + return False; + if (aMargin->body != anotherMargin->body) + return False; + if (aMargin->right != anotherMargin->right) + return False; + + return True; +} + +void WMSetRulerOffset(WMRuler * rPtr, int pixels) +{ + if (!rPtr || pixels < 0 || pixels + MIN_DOC_WIDTH >= rPtr->view->size.width) + return; + rPtr->offset = pixels; + /*rulerDidResize(rPtr, rPtr->view); */ +} + +int WMGetRulerOffset(WMRuler * rPtr) +{ + if (!rPtr) + return 0; /* what value should return if no ruler? -1 or 0? */ + return rPtr->offset; +} + +void WMSetRulerReleaseAction(WMRuler * rPtr, WMAction * action, void *clientData) +{ + if (!rPtr) + return; + + rPtr->releaseAction = action; + rPtr->clientData = clientData; +} + +void WMSetRulerMoveAction(WMRuler * rPtr, WMAction * action, void *clientData) +{ + if (!rPtr) + return; + + rPtr->moveAction = action; + rPtr->clientData = clientData; +} + +/* _which_ one was released */ +int WMGetReleasedRulerMargin(WMRuler * rPtr) +{ + if (!rPtr) + return 0; + return rPtr->flags.whichMarker; +} + +/* _which_ one is being grabbed */ +int WMGetGrabbedRulerMargin(WMRuler * rPtr) +{ + if (!rPtr) + return 0; + return rPtr->flags.whichMarker; +} diff --git a/WINGs/wtext.c b/WINGs/wtext.c new file mode 100644 index 00000000..ece4b398 --- /dev/null +++ b/WINGs/wtext.c @@ -0,0 +1,3958 @@ + +/* 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, + 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)); + 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); + + 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; + +} -- 2.11.4.GIT