From c49ad9cac82c839e2504c2b2765f97de74989732 Mon Sep 17 00:00:00 2001 From: kojima Date: Tue, 20 Jun 2000 23:27:37 +0000 Subject: [PATCH] added text widget from nwanua --- WINGs/ChangeLog | 7 + WINGs/Makefile.am | 6 +- WINGs/WINGs.h | 128 +- WINGs/configuration.c | 25 +- WINGs/testtext.c | 441 +++++++ WINGs/upbtn.xpm | 57 + WINGs/wm.html | 37 + WINGs/wm.png | Bin 0 -> 4732 bytes WINGs/wruler.c | 643 ++++++++++ WINGs/wtext.c | 2803 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 4142 insertions(+), 5 deletions(-) create mode 100644 WINGs/testtext.c create mode 100644 WINGs/upbtn.xpm create mode 100644 WINGs/wm.html create mode 100644 WINGs/wm.png create mode 100644 WINGs/wruler.c create mode 100644 WINGs/wtext.c diff --git a/WINGs/ChangeLog b/WINGs/ChangeLog index d6f30202..7509bf1b 100644 --- a/WINGs/ChangeLog +++ b/WINGs/ChangeLog @@ -1,3 +1,10 @@ +changes since wmaker 0.62.1: +............................ +- added WRuler widget +- added WText widget (rtf multiline text widget) + Nwanua Elumeze +- added a new AUTO option for the UseMultiByte option + changes since wmaker 0.62.0: ............................ - added WMSetWidgetDefaultFont(), WMSetWidgetDefaultBoldFont() diff --git a/WINGs/Makefile.am b/WINGs/Makefile.am index 776c2c02..d2f476e1 100644 --- a/WINGs/Makefile.am +++ b/WINGs/Makefile.am @@ -13,7 +13,7 @@ include_HEADERS = WINGs.h WUtil.h WINGsP.h bin_SCRIPTS = get-wings-flags get-wutil-flags noinst_PROGRAMS = wtest wmquery wmfile fontl testmywidget testcolorpanel\ - connect puzzle + connect puzzle testtext lib_LIBRARIES = libWINGs.a libWUtil.a @@ -30,7 +30,7 @@ wtest_DEPENDENCIES = libWINGs.a connect_LDADD = libWUtil.a @LIBRARY_SEARCH_PATH@ @NETLIBS@ @LIBPL@ -EXTRA_DIST = logo.xpm BUGS +EXTRA_DIST = logo.xpm BUGS wm.png upbtn.xpm wm.html # wbutton.c libWINGs_a_SOURCES = \ @@ -65,11 +65,13 @@ libWINGs_a_SOURCES = \ wpixmap.c \ wpopupbutton.c \ wprogressindicator.c \ + wruler.c \ wscroller.c \ wscrollview.c \ wslider.c \ wsplitview.c \ wtabview.c \ + wtext.c \ wtextfield.c \ wwindow.c \ wview.c \ diff --git a/WINGs/WINGs.h b/WINGs/WINGs.h index 0afab32c..059e7e86 100644 --- a/WINGs/WINGs.h +++ b/WINGs/WINGs.h @@ -193,6 +193,17 @@ enum { }; +enum { + WRulerLeft=0, + WRulerBody, + WRulerFirst, + WRulerRight, + WRulerBoth, + WRulerDocLeft +} WMRulerMargins; + + + /* drag operations */ typedef enum { WDOperationNone, @@ -275,7 +286,9 @@ enum { WC_SplitView = 13, WC_TabView = 14, WC_ProgressIndicator = 15, - WC_MenuView = 16 + WC_MenuView = 16, + WC_Ruler = 17, + WC_Text = 18 }; /* All widgets must start with the following structure @@ -319,6 +332,9 @@ 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; + /* not widgets */ typedef struct W_TabViewItem WMTabViewItem; @@ -492,6 +508,23 @@ typedef struct WMSelectionProcs { +typedef void WMParseAction(WMWidget *self, void *clientData, short type); + +/* these are the things parsers (for text) do */ + +typedef struct _parserActions { + void *(*createParagraph) (short fmargin, short bmargin, + short rmargin, short *tabstops, short numTabs, WMAlignment a); + void (*insertParagraph) (WMText *tPtr, void *para, InsertType type); + void *(*createPChunk) (WMPixmap *pixmap, short script, ushort ul); + void *(*createTChunk) (char *text, short chars, WMFont *font, + WMColor *color, short script, ushort ul); + void (*insertChunk) (WMText *tPtr, void *chunk, InsertType type); +} WMParserActions; + + + + typedef struct W_DraggingInfo WMDraggingInfo; @@ -1348,6 +1381,99 @@ void WMSetSplitViewResizeSubviewsProc(WMSplitView *sPtr, int WMGetSplitViewDividerThickness(WMSplitView *sPtr); +/* ...................................................................... */ + + +WMRuler *WMCreateRuler(WMWidget *parent); + +void WMShowRulerTabs(WMRuler *rPtr, Bool Show); + +int WMGetRulerMargin(WMRuler *rPtr, int which); + +int WMGetReleasedRulerMargin(WMRuler *rPtr); + +int WMGetGrabbedRulerMargin(WMRuler *rPtr); + +int WMGetRulerOffset(WMRuler *rPtr); + +void WMSetRulerOffset(WMRuler *rPtr, int pixels); + +void WMSetRulerMargin(WMRuler *rPtr, int which, int pixels); + +void WMSetRulerAction(WMRuler *rPtr, WMAction *action, void *clientData); + +void WMSetRulerMoveAction(WMRuler *rPtr, WMAction *action, void *clientData); + + +/* ....................................................................... */ + +WMText *WMCreateText(WMWidget *parent); + +int WMGetTextParagraphs(WMText *tPtr); +int WMGetTextCurrentParagraph(WMText *tPtr); +void WMSetTextCurrentParagraph(WMText *tPtr, int current); +void WMRemoveTextParagraph(WMText *tPtr, int which); + +int WMGetTextChunks(WMText *tPtr); +int WMGetTextCurrentChunk(WMText *tPtr); +void WMRemoveTextChunk(WMText *tPtr, int which); +void WMSetTextCurrentChunk(WMText *tPtr, int current); + +void W_InsertText(WMText *tPtr, void *data, int position);/* position = -1||0*/ +#define WMAppendTextStream(t, d) W_InsertText(t, d, -1) +#define WMPrependTextStream(t, d) W_InsertText(t, d, 0) + + +void WMSetTextParser(WMText *tPtr, WMParseAction *parser); +WMParserActions WMGetTextParserActions(WMText *tPtr); +#if 0 +typedef enum { + WDPCreateParagraph=0, + WDPInsertParagraph=1, + WDPCreateText=2, + WDPCreateImage=3, + WDPInsertChunk +} WMDoParse; +void WMDoTextParse(WMText *tPtr, short action); +#endif + +char * WMGetTextAll(WMText *tPtr); +void WMSetTextWriter(WMText *tPtr, WMParseAction *writer); + +void WMSetTextHasHorizontalScroller(WMText *tPtr, Bool flag); +void WMSetTextHasVerticalScroller(WMText *tPtr, Bool flag); + +void WMRefreshText(WMText *tPtr, int vpos, int hpos); +void WMFreezeText(WMText *tPtr); +void WMThawText(WMText *tPtr); + +void WMSetTextMonoFont(WMText *tPtr, Bool mono); +Bool WMGetTextMonoFont(WMText *tPtr); +void WMSetTextEditable(WMText *tPtr, Bool editable); +Bool WMGetTextEditable(WMText *tPtr); +void WMForceTextFocus(WMText *tPtr); + +void WMSetTextAlignment(WMText *tPtr, WMAlignment alignment); +void WMIgnoreTextNewline(WMText *tPtr, Bool ignore); + + +Bool WMScrollText(WMText *tPtr, int amount); +Bool WMPageText(WMText *tPtr, Bool scrollUp); + + +void WMShowTextRuler(WMText *tPtr, Bool show); +Bool WMGetTextRulerShown(WMText *tPtr); +void WMShowTextRulerTabs(WMText *tPtr, Bool show); +void WMSetTextRulerMargin(WMText *tPtr, char which, short pixels); +short WMGetTextRulerMargin(WMText *tPtr, char which); + +void WMSetTextUseFixedPitchFont(WMText *tPtr, Bool fixed); +void WMSetTextDefaultColor(WMText *tPtr, WMColor *color); +void WMSetTextDefaultFont(WMText *tPtr, WMFont *font); +void WMSetTextDefaultAlignment(WMText *tPtr, WMAlignment alignment); +void WMSetTextBackgroundColor(WMText *tPtr, WMColor *color); + + /* ....................................................................... */ diff --git a/WINGs/configuration.c b/WINGs/configuration.c index 3df6d5bc..015f48c6 100644 --- a/WINGs/configuration.c +++ b/WINGs/configuration.c @@ -2,6 +2,8 @@ #include "WINGsP.h" +#include + #include @@ -52,6 +54,7 @@ W_ReadConfigurations(void) if (defaults) { char *buttonName; unsigned button; + char *str; WINGsConfiguration.systemFont = WMGetUDStringForKey(defaults, "SystemFont"); @@ -59,8 +62,26 @@ W_ReadConfigurations(void) WINGsConfiguration.boldSystemFont = WMGetUDStringForKey(defaults, "BoldSystemFont"); - WINGsConfiguration.useMultiByte = - WMGetUDBoolForKey(defaults, "MultiByteText"); + WINGsConfiguration.useMultiByte = False; + str = WMGetUDStringForKey(defaults, "MultiByteText"); + if (str) { + if (strcasecmp(str, "YES") == 0) { + WINGsConfiguration.useMultiByte = True; + } else if (strcasecmp(str, "AUTO") == 0) { + char *locale; + + /* if it's a multibyte language (japanese, chinese or korean) + * then set it to True */ + locale = setlocale(LC_CTYPE, NULL); + if (locale != NULL + && (strncmp(locale, "ja", 2) == 0 + || strncmp(locale, "zh", 2) == 0 + || strncmp(locale, "ko", 2) == 0)) { + + WINGsConfiguration.useMultiByte = True; + } + } + } WINGsConfiguration.doubleClickDelay = WMGetUDIntegerForKey(defaults, "DoubleClickTime"); diff --git a/WINGs/testtext.c b/WINGs/testtext.c new file mode 100644 index 00000000..86495a09 --- /dev/null +++ b/WINGs/testtext.c @@ -0,0 +1,441 @@ +#include +#include +#include +#include +#include + +#include "wtext.h" + + +void +wAbort() +{ + exit(0); +} + + +void Close(WMWidget *self, void *client) +{ + exit(0); +} + + + +/* =============== a rudimentary HTML parser ======================= */ + +/* due to the WMSetTextParser stuff, it should not +be too hard to add parsers. like dis :-] */ + + +/* + * A hack to speed up caseless_equal. Thanks to Quincey Koziol for + * developing it for the "chimera" folks so I could use it 7 years later ;-) + * Constraint: nothing but '\0' may map to 0 + */ +unsigned char map_table[256] = +{ +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, +17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, +33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, +49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, +97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, +111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, +93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, +108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, +122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, +136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, +150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, +164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, +178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, +192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, +206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, +220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, +234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, +248, 249, 250, 251, 252, 253, 254, 255}; + +#define TOLOWER(x) (map_table[(int)x]) +static int +mystrcasecmp(const unsigned char *s1, const unsigned char *s2) +{ + if (!*s1 || !*s2) return 0; + while (*s2 != '\0') { + if (TOLOWER (*s1) != TOLOWER (*s2)) /* true if *s1 == 0 ! */ + return 0; + s1++; + s2++; + } + return (*s1=='\0' ||!isalnum(*s1))?1:0; +} + + +typedef struct _currentFormat { + void *para; + WMBag *fonts; + WMBag *colors; + WMColor *ccolor; + WMFont *cfont; + WMParserActions actions; + //WMBag *aligns; // for tables... + /* the following are "nested" + i.e.: + 1 2 1 1 2 0 get it? */ + short i; + short b; + short u; + short fmargin; + short bmargin; + WMAlignment align:2; + short type:1; + short ul:3; /* how "nested"... up to 8 levels deep */ + short comment:1; /* ignore text till --> */ + short RESERVED:10; +} CFMT; +CFMT cfmt; + + + +#if 0 +getArg(char *t, short type, void *arg) +{ + short d=0; + while(*(++t) && !d) { + if(type==0) { + if(*t>='0' && *t<='9') { + sscanf(t, "%d", arg); + while(*t&& (*t<'0' || *t>'9')) + t++; + d=1; + } + } + } +} +#endif + +void parseToken(WMText *tPtr, char *token, short tk) +{ + short mode=0; /* 0 starts, 1 closes */ + WMScreen *scr = (W_VIEW(tPtr))->screen; + + while(*token && isspace(*(token))) token++; + if(*token == '/') { + token++; + mode = 1; + while(isspace(*(token))) token++; + } + + if(strlen(token)==1) { + /* nice and fast for small tokens... no need for too much brain + power here */ + switch(TOLOWER(*token)) { + case 'i': + if(!mode) { + cfmt.cfont = WMGetFontItalic(scr, cfmt.cfont); + WMPutInBag(cfmt.fonts, (void *)cfmt.cfont); + } else { /*dun wanna remove the baseFont eh? */ + int count = WMGetBagItemCount(cfmt.fonts); + if(count>1) + WMDeleteFromBag(cfmt.fonts, count-1); + cfmt.cfont = (WMFont *)WMGetFromBag(cfmt.fonts, + WMGetBagItemCount(cfmt.fonts)-1); + } break; + case 'b': + if(!mode) { + cfmt.cfont = WMGetFontBold(scr, cfmt.cfont); + WMPutInBag(cfmt.fonts, (void *)cfmt.cfont); + } else { /*dun wanna remove the baseFont eh? */ + int count = WMGetBagItemCount(cfmt.fonts); + if(count>1) + WMDeleteFromBag(cfmt.fonts, count-1); + cfmt.cfont = (WMFont *)WMGetFromBag(cfmt.fonts, + WMGetBagItemCount(cfmt.fonts)-1); + } break; + case 'p': + cfmt.para = (cfmt.actions.createParagraph) (cfmt.fmargin, cfmt.bmargin, + WMWidgetWidth(tPtr)-30, NULL, 0, cfmt.align); + (cfmt.actions.insertParagraph) (tPtr, cfmt.para, cfmt.type); + cfmt.para = (cfmt.actions.createParagraph) (cfmt.fmargin, cfmt.bmargin, + WMWidgetWidth(tPtr)-30, NULL, 0, cfmt.align); + (cfmt.actions.insertParagraph) (tPtr, cfmt.para, cfmt.type); + break; + case 'u': cfmt.u = !mode; break; + } + } else { /* the tag is, as far as I'm concerned, useless */ + if(mystrcasecmp(token, "br")) { + cfmt.para = (cfmt.actions.createParagraph) (cfmt.fmargin, cfmt.bmargin, + WMWidgetWidth(tPtr)-30, NULL, 0, cfmt.align); + (cfmt.actions.insertParagraph) (tPtr, cfmt.para, cfmt.type); + } + else if(mystrcasecmp(token, "ul")) { + if(mode) { + if(cfmt.ul>1) cfmt.ul--; + } else cfmt.ul++; + if(cfmt.ul) { + cfmt.bmargin = cfmt.ul*30; + cfmt.fmargin = cfmt.bmargin-10; + } else cfmt.fmargin = cfmt.bmargin = 0; + } else if(mystrcasecmp(token, "li")) { + cfmt.para = (cfmt.actions.createParagraph) (cfmt.fmargin, cfmt.bmargin, + WMWidgetWidth(tPtr)-30, NULL, 0, cfmt.align); + (cfmt.actions.insertParagraph) (tPtr, cfmt.para, cfmt.type); + } else if(mystrcasecmp(token, "align")) + ;//printf("align"); + else if(mystrcasecmp(token, "img")) { + if(!mode) { + char *mark=NULL; + WMPixmap *pixmap; + void *chunk; + token+=3; + while(isspace(*(token))) token++; + do { + switch(TOLOWER(*token)) { + case 's': + if(TOLOWER(*(1+token)) == 'r' && TOLOWER(*(2+token)) == 'c') { + mark = strchr(token, '='); + if(mark) { + char img[256], *iptr; + token = mark+1; + if(!token) return; + sscanf(token, "%s", img); + iptr = img; + if(*img == '\"') { img[strlen(img)-1] = 0; iptr++;} + pixmap = WMCreatePixmapFromFile(scr, iptr); + chunk = (cfmt.actions.createPChunk)(pixmap, 0, False); + (cfmt.actions.insertChunk) (tPtr, chunk, itAppend); + printf("[%s]\n", iptr); + } } break; } } while(*(token++)); + } + } else if(mystrcasecmp(token, "font")) { +#if 0 + if(mode) { + cfmt.cfont = (WMFont *)WMGetFromBag(cfmt.fonts, + WMGetBagItemCount(cfmt.fonts)-1); + } else + (WMColor *)WMGetFromBag(cfmt.colors, + WMGetBagItemCount(cfmt.colors)-1), +#endif + } + else if(mystrcasecmp(token, "center")) { +printf("center\n"); + if(mode) cfmt.align = WALeft; + else cfmt.align = WACenter; + cfmt.para = (cfmt.actions.createParagraph) (cfmt.fmargin, cfmt.bmargin, + WMWidgetWidth(tPtr)-30, NULL, 0, cfmt.align); + (cfmt.actions.insertParagraph) (tPtr, cfmt.para, cfmt.type); + } + } + + + + + //printf("parse token (%s)[%s]\n", mode?"close":"open", token); +#if 0 + i=0; + //while(*token && !isspace(*(token))) token++; +//printf("A:%d a:%d z%d Z%d\n", '1', 'a', 'Z', 'z'); + do { + if(!mm) { + if(c>=65 && c<=122) { major[i++] = c; + } else if(c==' ' || c=='='){ major[i] = 0; i=0; mm=1; + printf("\nmajor: [%s]", major);} + } else { + if(c!=' ') { + minor[i++] = c; + } else { minor[i] = 0; i=0; printf(" minor: [%s] ", minor);} + } + }while((c = *(++token))); +#endif + + + //printf("parse token (%s)[%s]\n", mode?"close":"open", token); +} + +void HTMLParser(WMWidget *w, void *clientData, short type) +{ + static short init=1; /* have we been here at least once before? */ + char *stream = (char *) clientData; + WMText *tPtr = (WMText *)w; + WMScreen *scr; + void *chunk; + char c; + #define MAX_TOKEN_SIZE 255 + char token[MAX_TOKEN_SIZE+1]; + #define MAX_TEXT_SIZE 1023 + char text[MAX_TEXT_SIZE+1]; + short mode=0; + short tk=0, txt=0; + short wasspace=0; + + if(!tPtr || !stream) + return; + + scr = (W_VIEW(tPtr))->screen; + cfmt.type = type; + if(init) { + cfmt.actions = WMGetTextParserActions(tPtr); + cfmt.fonts = WMCreateBag(4); /* there sould always be at least 1 font... */ + cfmt.cfont = WMSystemFontOfSize(scr, 12); + WMPutInBag(cfmt.fonts, (void *)cfmt.cfont); + cfmt.colors = WMCreateBag(4); + cfmt.ccolor = WMBlackColor(scr); + WMPutInBag(cfmt.colors, (void *)cfmt.ccolor); + cfmt.i = cfmt.b = cfmt.u = cfmt.ul = 0; + cfmt.align = WALeft; + cfmt.fmargin = cfmt.bmargin = 0; + cfmt.para = (cfmt.actions.createParagraph) (cfmt.fmargin, cfmt.bmargin, + WMWidgetWidth(tPtr)-30, NULL, 0, cfmt.align); + (cfmt.actions.insertParagraph) (tPtr, cfmt.para, cfmt.type); + init = 0; + } + +#if 1 + if(strlen(stream) == 1 && stream[0] == '\n') { + /* sometimes if the text entered is a single char AND is a newline, + the user prolly typed it */ + cfmt.para = (cfmt.actions.createParagraph) (cfmt.fmargin, cfmt.bmargin, + WMWidgetWidth(tPtr)-30, NULL, 0, cfmt.align); + (cfmt.actions.insertParagraph) (tPtr, cfmt.para, cfmt.type); + return; + } +#endif + + +/* +*/ + + while( (c=*(stream++))) { +//printf("%c", c); + if(c == '\n' || c =='\t') + //c = ' '; //continue; + continue; + if(c == ' ') { + if(wasspace) + continue; + wasspace = 1; + }else wasspace = 0; + + if(c == '<' && !mode) { + mode=1; + if(txt>0) { + text[txt] = 0; + chunk = (cfmt.actions.createTChunk)(text, txt, cfmt.cfont, + cfmt.ccolor, 0, (cfmt.u?1:0)); + (cfmt.actions.insertChunk) (tPtr, chunk, cfmt.type); +//printf("%s\n", text); + } + txt = 0; + } else if(c == '>' && mode) { + token[tk] = 0; + if(tk>0) parseToken(tPtr, token, tk); + mode=0; + tk=0; + } else { + if(mode) { + if(tk < MAX_TOKEN_SIZE) token[tk++] = c; + } else if(txt < MAX_TEXT_SIZE) text[txt++] = c; + } + } + + if(tk>0) { token[tk] = 0; parseToken(tPtr, token, tk);} + if(txt>0) { + text[txt] = 0; + //printf("%s\n", text); + chunk = (cfmt.actions.createTChunk)(text, txt, + (WMFont *)WMGetFromBag(cfmt.fonts, + WMGetBagItemCount(cfmt.fonts)-1), + (WMColor *)WMGetFromBag(cfmt.colors, + WMGetBagItemCount(cfmt.colors)-1), + 0, (cfmt.u?1:0)); + (cfmt.actions.insertChunk) (tPtr, chunk, cfmt.type); + } + +} + + +/* ================= the driver ================== */ + +WMWidget *win; +WMText *text; + +void NotificationObserver(void *self, WMNotification *notif) +{ + void *object = WMGetNotificationObject(notif); + unsigned short w = WMWidgetWidth(win); + unsigned short h = WMWidgetHeight(win); + + if (WMGetNotificationName(notif) == WMViewSizeDidChangeNotification) { + if (object == WMWidgetView(win)) { + WMResizeWidget(text, w-20, h-20); + } + } + +{static int i=0; +switch(i++) { +case 0: WMSetTextHasHorizontalScroller(text, False); break; +case 1: WMSetTextHasVerticalScroller(text, False); break; +case 2: WMSetTextHasVerticalScroller(text, True); break; +case 3: WMSetTextHasHorizontalScroller(text, True); break; +default: i=0; +} +} +} + +int +main(int argc, char **argv) +{ + Display *dpy; + WMScreen *scr; + + + WMInitializeApplication("WMText", &argc, argv); + dpy = XOpenDisplay(NULL); + if(!dpy) exit(1); + + scr = WMCreateSimpleApplicationScreen(dpy); + + win = WMCreateWindow(scr, "WMText Test"); + WMRealizeWidget(win); + WMResizeWidget(win, 500, 300); + //WMResizeWidget(win, 900, 600); + WMSetWindowTitle(win,"WMText Test"); + WMSetWindowCloseAction(win, Close, NULL); + + text = WMCreateText(win); + WMRealizeWidget(text); + WMResizeWidget(text, 480, 280); + WMMoveWidget(text, 10, 10); + WMSetTextUseFixedPitchFont(text, False); + WMSetTextMonoFont(text, False); + WMSetTextParser(text, HTMLParser); + WMSetTextEditable(text, True); + WMFreezeText(text); + if(1){ + FILE *f = fopen("./wm.html", "r"); + char data[1024]; + if(f) { + while(fgets(data, 1022,f)) + WMAppendTextStream(text, data); + fclose(f); + } else { + WMAppendTextStream(text, "can't open the wm.html file, but here's a text stream
that
needs parsing"); + } + } + + +//WMPrependTextStream(text, "this is prepended\n"); +//WMAppendTextStream(text, "blueplanet.rtf"); + + + WMThawText(text); + WMRefreshText(text, 0, 0); + +/* + WMAddNotificationObserver(NotificationObserver, win, + WMViewSizeDidChangeNotification, WMWidgetView(win)); +*/ + WMSetViewNotifySizeChanges(WMWidgetView(win), True); + WMMapSubwidgets(win); + WMMapWidget(win); + + WMScreenMainLoop(scr); +} + diff --git a/WINGs/upbtn.xpm b/WINGs/upbtn.xpm new file mode 100644 index 00000000..d7ab44cb --- /dev/null +++ b/WINGs/upbtn.xpm @@ -0,0 +1,57 @@ +/* XPM */ +static char * upbtn_xpm[] = { +"20 22 32 1", +" c None", +". c #000000", +"+ c #FFFFFF", +"@ c #4A485A", +"# c #524052", +"$ c #4A595A", +"% c #5A596A", +"& c #4A486A", +"* c #5A595A", +"= c #52556A", +"- c #52406A", +"; c #6A556A", +"> c #5A6D6A", +", c #5A597B", +"' c #5A6D7B", +") c #6A557B", +"! c #6A6D7B", +"~ c #6A697B", +"{ c #6A698B", +"] c #7B797B", +"^ c #C5C2C5", +"/ c #6A818B", +"( c #6A7D7B", +"_ c #7B698B", +": c #6A798B", +"< c #7B799C", +"[ c #7B798B", +"} c #7B8D94", +"| c #7B81A4", +"1 c #8B85A4", +"2 c #73899C", +"3 c #7B89A4", +" ", +" .+ ", +" .@#+ ", +" .$%$&+ ", +" .*=*-*-+ ", +" .%%%%%%$%+ ", +" .%=%;%=*=*-+ ", +" .>%>,''>,>%=%+ ", +" .%)>)!~>)>)>=*#+ ", +" .>~'~'{'{'~',>%$@+ ", +" ]+++^{!{!~!)>+++++ ", +" ./{/{({'~+ ", +" ._:_:_!~>+ ", +" ./+ ", +" .2 +
+

+GNU Window Maker
+X11 Window Manager

+http://windowmaker.org
+ftp.windowmaker.org

+

+ +Window Maker +is the GNU window manager +for the X Window System. It was +designed to emulate the look and feel of part of the NEXTSTEP(tm) GUI. It's +supposed to be relatively fast and small, feature rich, easy to configure and +easy to use, with a simple and elegant appearance borrowed from NEXTSTEP(tm). +

+Window Maker was designed keeping integration with GNUstep in +mind and is the +"official" window manager for it. It is also part of the GNU project +(www.gnu.org) + +

+What is GNUstep?

+GNUstep is a complete object-oriented development system, based on the +OpenStep specification released by NeXT(tm) (now Apple(tm)) and Sun(tm). It will provide everything one needs to produce cross-platform, object-oriented, graphical (and non-graphical) applications; providing among other things, base system libraries, a high-level GUI application framework that uses a Display PostScript(tm)-like imaging model (DGS), objects for accessing relational databases, distributed objects and a graphical development environment, with tools like interface modeller, a project management system +(project center) and other tools. +

+The GNUstep development system will be used to create a user environment, +with everything needed for a complete graphical user interface, such as a +file viewer, text editors and other applications. Note that the user +environment (or "desktop environment") is only a small part of the whole +GNUstep project and therefore it does not "compete" with other projects like +KDE or GNOME, simply because they are completely different things. +

+For more information on the GNUstep project, visit: +http://www.gnustep.org and http://gnustep.current.nu + diff --git a/WINGs/wm.png b/WINGs/wm.png new file mode 100644 index 0000000000000000000000000000000000000000..b475b3e0e3fccebe24d7454c9386ecc9f95507f6 GIT binary patch literal 4732 zcmWmD`6JVh@s+#9W#CzNg4F_gy1bQf-cO$a|tFswr|-!!SDD zGZgWz^?rLx(z_K(I^OHu+dC4zpYQVzc>MNs_jIFM$BY7df%gCaf{2U79;a}G=Oc$t zyO?-7n~s(EPq@)X8^b*?nAJJn={ValI3g*Ixj0g=*ch+7L)T50yf}G0#noi7#J4rh zKFQeNgD~^qwjHDK_B0EpR#%I@#_%|%*;sYR`f_{O4ywC_;g89pAH%tGhum9=Jx6;I z4HOhkWjn73Q%(h2zN_AMAjr(}e zuJV6g;+My?Yy}bdF2-~_&((=z$rk#}OiMMDEi=`TUAj99vRn=#5YMw+W*QUMX3N~{ zXqSR5ma9XS%#Hiv9Tq1zAMO^^W;iaQ-)lNVu?=d(N2 zPJg;RVSW8aud(r!a{sV!7Ar1ytlZz-m~yu*;kUb~tr7Hr2zreVt?x?6mne(%@Zb#} z_cP_ZQ|XTH&M{jvoF3MNo-#3OI_KL`=(h5z;a2_rOX2jILiZ!Ky8o63mD=g`oeQot zHmJ3;`|l95u<+=;d-on@Fg}_Xuht*13SU1--_|45t>ITs_CO(0Zmy(@Karg8M0H~`7 z7!KG3jDi34|Ca!m4PX;X_7Ly~i!sWE5u<{Jp)zQ@SG`B)^^vpc=Bcxzoi`r+LUBGr z_UvjLtI`i{ix}%_8n2&ufKO<)AE&t=266! zd5z?$Wm&iuA#2bTrkG_KZ5>ZY7|)+?YKS2%L>B$QL2gNY?spxFjyaY?&yPOyalBxmK$E)bpNMD@Bq1PW>;!_+wkCJcrm1l;S>^tm!=KphwhRzssJK*j$S#@c2QS zMGrnhP1;nMeR7Oy|KvieUQF<|P-3wHF4dqeTsw6~ZOr-aPFH_%>r+OM0Qbe#uvAi9 z`NAGjTg%3=^QJl%i>H=G`C)tWt4tQCsm;x|A9&RTbnbLC*xK{p^v7GoC&0Y#&zP0h z`D;u(r$BO>S%wOt5AcGI!QUJ6L^}g#8o3WOt{%C%tM93i!~R)sFMW6FshI_r?qQCEDrXAb z%)E2a;#KzatA8gu{9g&J%xb_#%FNKehLj89E8VpX$|=^*k1@qe#hv*|!!HyreB-LY zejbO8FHB0@e8bK}BaeFE)`p~R7k^RSF^TwmCmZc$LwB; z0Whe6pjXHxKmsnj{9V}&I&ACBINvRNcJXdu1FOY26qM0Q^~rzhv@1(`#%eZJQ+0a8asWV&W9RY1G$%zVixWaaof}z%BQNsK>CPI% z9Yh)$1Pb^*ri74lZ+qtNa@f(3FO=o;0L}tcf7L>aOWfdW-Hkn;$7-cDygk^}8=U1sEiK01&uW_KVfFJHTSfsE* zaq_WgBgRW5G#pf7Uj7hwfGviTae(h*u8sr)-@;-!|H+0CJw`h)C10QQ-^xPt<&X5- z{;vclw4jTIbv2m~LO~`$Gxax6ihleG-~6||qUGUj7x`tqy)2{=Yt9nGw!2!{fd42S z(jN`Pkm6EIk;N>rWo%6QvjrZ)m1qlMY@BEoD^K{AQ;i;tihBH85RjZlt|XQiv`#Sm2iHt zQ02?Oqb~#Ea-Gq@y$kAtHXL?Q=W-1422hNm2z+sLknpn>+OFYbfYK_yH>ZHK+-6$g zS^K?rBe{7)ZG!a&ZVGn${V4g}Qae(4J{G{`>opYs=QdBqDxF<9#L@jqJnAFS*(ED0 zz0nD1?vB-jTHIg|(~q%L<`=iI#x?Q5ooaf0kxV^0s5dj14)uoV6L>` z2L*gCM1BJ+pv~^74@CG5#~_EN-IOWpSeYyq`AG)Ld|-MqkG<_tU8qFeje1f!Jyw|@ zb!qLbhr9wM%2fbZnofb4EY5*s8W_`$o4COPp-Kh@dV~yE3)>a*iFSE+C|zSnka^IGhIG@LgL{bNn2iT~((V-* zmX>S)G}7RTXzo-+OiB0n(eQomx#)pPe~h-YG%X?yYako3I`xM_tVN90XZ0dGiD@DO zv{5O(Sn^AxpxDt4RFu-46(0ng$)257et0>q#^3@XZ`V_TaejZCg}hWzK1f`Oj52?~ zJMCwIY5+wbUSD5FxFo@wFNP|br4&=j=d}LjA+P~rn9#2Qq*K8y*Pp@uUL9174>$?u z(~48;8JbBI?lFgyvXol*;5#-)xq@FzpKU~Lr%THFff5(vBB{l%CaP`%`1aSw!XGb6 zERW;xhUs&J*~eTG`x)NZqxb_%?a{S$O@asDL~xL$#Njqfamg)JuZLm9dJk7e%4eZk#mm2;6{*J?@T?45FDE|bAd|%E2Us8maTq8Ow#O14DTPd;ug)pTXPDVf`~~xcmYpAg$Ije0Qcot zv`55Pamg?&Aa1`>ywnc|0V{cjfDq#6#NA{mESh~t(ty9iBN8|nm*ooMH}dpYn00y1 zRS2gGAt1hDp_sChm-IlaxIzDl+(tnwE!&$jxER1j!sFs7@MkpGPZm~P&ShA_E;WT+ zyNUY%p`LwDIt>ua)Nv(h*b9736*GrMv;CV8+JXYMGsq+!(wC1QLEtedqV}7D8zbHG z8QefX{KUckl>2|$Z^!^ppOFFNuO}>d2iLlCt0ay%=KkBf+}R+19rlULcj{CMfjSJ= z<6*y{iA}MYbe_|A72=u{li!-Av$g_esqWcQ3`>LTRrRI-PhqGaceOBjQlgo5VWm;2EFWbZHB zPfRHVJ4e+|1fhe(c3N?}5AXp?8N!c)bAG0Yq@ZI{KwW)Ui&(*A**=jSL*t+TaBofC z5oaa%r9E&%ITB05{K6)b@?iI+@Z+(TM|(3i<;ab1Sua);y%?ai6rL5h4fV(!K`vM3~tvC`NcZW^unKG}8BhhpiZxWqs@3&`s#=h1{gc-E3f|Cz_0D0G zlU*E>1Zw{T`lmcDraZgZHJz~;2CaN;03GYnyr56b*5ChAuescow!b|b>-`J04K+v+ z_EwW!nnMo9mSer|wB0<5th5HYJi$7_nwbSEk3E1sY`bv zN2@mt&sCBHi-xy+!m6Kwcaj@#u1_gXl3Kqlw}a#6?4Ub}R01LfkB`AF9)NlS&{W&o zl{S2(yX`srPW_Miq@x>M>7*{|nhE7?*S0sE@J>ZArW+fGf&x($6g;+}OC?a@FL5V| zD#TTyz!)Kgiq8Rru`nUYNccSf`b*GBuj|eRx)5)K;kHn;5!Bi(^sr}7 zqzcoILZr1`I9IqS7Q!=oyz6>5DSfCnLJxcoqM;AL6}oyu*_=KSOsMUxm}mZ)jEA?X=dn$sefmzMJJZ0CGj77;G0qvEiktE)Ba%gVO4HJ>H6fl||Y6q<3+b zx(}t{Jz|CZ;n0=#E|0W6q@#8MrJwat)GP1JW@CR}>zA--*f*WQM$nb;-j1`vY|gKn i6oQX8deI1)_cle?iI9b2OuZO)PfVB<_b5St?f(HACCu*t literal 0 HcmV?d00001 diff --git a/WINGs/wruler.c b/WINGs/wruler.c new file mode 100644 index 00000000..19165804 --- /dev/null +++ b/WINGs/wruler.c @@ -0,0 +1,643 @@ + +/* WMRuler: nifty ruler widget for WINGs (OK, for WMText ;-) */ +/* Copyleft (>) 1999, 2000 Nwanua Elumeze */ + + +#include +#include +#include + +#include "wruler.h" + +#define DEFAULT_X_OFFSET 22 +#define MIN_DOC_WIDTH 200 +#define DEFAULT_RULER_WIDTH 240 +#define DEFAULT_RULER_HEIGHT 40 + + +/* a pixel is defined here as 1/8 of an "inch" */ +/* an "inch" is not the British unit inch... just so you know :-P */ + +typedef struct W_Ruler { + W_Class widgetClass; + W_View *view; + + W_View *pview; + + void *clientData; + WMAction *action, *moveaction; + + struct { + int left, body, first, right, dleft; + } margins; + int which_marker; + + int end; + int pressed; + int offset; + + struct { + unsigned int showtabs:1; + unsigned int buttonPressed:1; + } flags; + +} Ruler; + + +static void +resizeRuler(W_ViewDelegate *self, WMView *view) +{ + Ruler *rPtr = (Ruler *)view->self; + + CHECK_CLASS(rPtr, WC_Ruler); + + W_ResizeView(rPtr->view, view->size.width, + DEFAULT_RULER_HEIGHT); /* more like DEFACTO_RULER_HEIGHT ;-) */ + + rPtr->end = view->size.width; + rPtr->margins.right = view->size.width; +} + + +W_ViewDelegate _RulerViewDelegate = +{ + NULL, + NULL, + resizeRuler, + NULL, + NULL +}; + +static void +paintRuler(Ruler *rPtr) +{ + GC gc; + XPoint points[6]; + WMFont *font; + int i, j, xpos; + char c[3]; + int actual_x; + + CHECK_CLASS(rPtr, WC_Ruler); + + if(!rPtr->view->flags.mapped) + return; + + gc = WMColorGC(WMBlackColor(rPtr->view->screen)); + font = WMSystemFontOfSize(rPtr->view->screen, 8); + + + WMDrawString(rPtr->view->screen, rPtr->view->window, gc, + font, rPtr->margins.dleft+2, 25, "0 inches", 10); + + /* marker ticks */ + j=0; + for(i=80; iview->size.width; i+=80) { + if(j<10) + snprintf(c,3,"%d",++j); + else + snprintf(c,3,"%2d",++j); + WMDrawString(rPtr->view->screen, rPtr->view->window, gc, + font, rPtr->margins.dleft+2+i, 25, c, 2); + } + + if(rPtr->flags.showtabs){ + points[0].y = 9; + points[1].y = 15; + points[2].y = 20; + } + + for(i=0; iview->size.width; i+=40) { + XDrawLine(rPtr->view->screen->display, rPtr->view->window, + gc, rPtr->margins.dleft+i, 21, rPtr->margins.dleft+i, 31); + + if(rPtr->flags.showtabs){ + points[0].x = rPtr->margins.dleft+i+40; + points[1].x = points[0].x+6; + points[2].x = points[0].x; + XFillPolygon (rPtr->view->screen->display, rPtr->view->window, + WMColorGC(WMDarkGrayColor(rPtr->view->screen)), + points, 3, Convex, CoordModeOrigin); + + XDrawLine(rPtr->view->screen->display, rPtr->view->window, + WMColorGC(WMDarkGrayColor(rPtr->view->screen)), + rPtr->margins.dleft+i, 18, rPtr->margins.dleft+i, 21); + } + } + + for(i=0; iview->size.width; i+=20) + XDrawLine(rPtr->view->screen->display, rPtr->view->window, + gc, rPtr->margins.dleft+i, 21, rPtr->margins.dleft+i, 27); + + for(i=0; iview->size.width-20; i+=10) + XDrawLine(rPtr->view->screen->display, rPtr->view->window, + gc, rPtr->margins.dleft+i, 21, rPtr->margins.dleft+i, 24); + + /* guide the end marker in future drawings till next resize */ + rPtr->end = i+12; + + /* base line */ + XDrawLine(rPtr->view->screen->display, rPtr->view->window, + gc, rPtr->margins.dleft, 21, rPtr->margins.left+i-10, 21); + + + /* Marker for right margin + + /| + / | + /__| + | + | */ + + xpos = rPtr->margins.right; + XDrawLine(rPtr->view->screen->display, rPtr->view->window, + WMColorGC(WMDarkGrayColor(rPtr->view->screen)), xpos, 10, xpos, 20); + + points[0].x = xpos+1; + points[0].y = 2; + points[1].x = points[0].x-6; + points[1].y = 9; + points[2].x = points[0].x-6; + points[2].y = 11; + points[3].x = points[0].x; + points[3].y = 11; + XFillPolygon (rPtr->view->screen->display, rPtr->view->window, + gc, points, 4, Convex, CoordModeOrigin); + + + /* Marker for left margin + + |\ + | \ + |__\ + | + | */ + + xpos = rPtr->margins.left; + XDrawLine(rPtr->view->screen->display, rPtr->view->window, + WMColorGC(WMDarkGrayColor(rPtr->view->screen)), xpos, 10, xpos, 20); + + points[0].x = xpos; + points[0].y = 3; + points[1].x = points[0].x+6; + points[1].y = 10; + points[2].x = points[0].x+6; + points[2].y = 11; + points[3].x = points[0].x; + points[3].y = 11; + XFillPolygon (rPtr->view->screen->display, rPtr->view->window, + gc, points, 4, Convex, CoordModeOrigin); + +/* + points[3].x = + + /* Marker for first line only + _____ + |___| + | + + */ + + xpos = rPtr->margins.first + rPtr->margins.dleft; + XFillRectangle(rPtr->view->screen->display, rPtr->view->window, + gc, xpos-5, 7, 11, 5); + + XDrawLine(rPtr->view->screen->display, rPtr->view->window, + gc, xpos, 10, xpos, 20); + + + /* Marker for rest of body + _____ + \ / + \./ */ + + + xpos = rPtr->margins.body + rPtr->margins.dleft; + points[0].x = xpos-5; + points[0].y = 14; + points[1].x = points[0].x+11; + points[1].y = 14; + points[2].x = points[0].x+5; + points[2].y = 20; + XFillPolygon (rPtr->view->screen->display, rPtr->view->window, + gc, points, 3, Convex, CoordModeOrigin); + + + if(!rPtr->flags.buttonPressed) + return; + + /* actual_x is used as a shortcut is all... */ + switch(rPtr->which_marker){ + case WRulerLeft: actual_x = rPtr->margins.left; break; + case WRulerBody: + actual_x = rPtr->margins.body + rPtr->margins.dleft; + break; + case WRulerFirst: + actual_x = rPtr->margins.first + rPtr->margins.dleft; + break; + case WRulerRight: actual_x = rPtr->margins.right; break; + default: return; + } + + + gc = WMColorGC(WMDarkGrayColor(rPtr->view->screen)), + + XSetLineAttributes(rPtr->view->screen->display, gc, 1, + LineOnOffDash, CapNotLast, JoinMiter); + + XDrawLine(rPtr->pview->screen->display, rPtr->pview->window, + gc, actual_x+1, 45, actual_x+1, rPtr->pview->size.height-5); + + XSetLineAttributes(rPtr->view->screen->display, gc, 1, + LineSolid, CapNotLast, JoinMiter); + + +} + +static int +whichMarker(Ruler *rPtr, int x, int y) +{ + CHECK_CLASS(rPtr, WC_Ruler); + + if(y>22) + return; + + if(x<0) + return; + + if( rPtr->margins.dleft-x >= -6 && y <= 9 && + rPtr->margins.dleft-x <=0 && y>=4) + return WRulerLeft; + + if( rPtr->margins.right-x >= -1 && y <= 11 && + rPtr->margins.right-x <=5 && y>=4) + return WRulerRight; + + x -= rPtr->margins.dleft; + if(x<0) + return; + + if( rPtr->margins.first-x <= 4 && y<=12 && + rPtr->margins.first-x >= -5 && y>=5) + return WRulerFirst; + + + + if( rPtr->margins.body-x <= 4 && y<=19 && + rPtr->margins.body-x >= -5 && y>=15) + return WRulerBody; + + + /* 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>=13 && y<=14) + return WRulerBoth; + + return -1; +} + +static Bool +verifyMarkerMove(Ruler *rPtr, int x) +{ + if(!rPtr) + return; + + switch(rPtr->which_marker){ + case WRulerLeft: + if(x < rPtr->margins.right-MIN_DOC_WIDTH && + rPtr->margins.body + x <= rPtr->margins.right-MIN_DOC_WIDTH && + rPtr->margins.first + x <= rPtr->margins.right-MIN_DOC_WIDTH) + rPtr->margins.left = x; + else return False; + break; + + + case WRulerBody: + if(x < rPtr->margins.right-MIN_DOC_WIDTH && x >= rPtr->margins.left) + rPtr->margins.body = x - rPtr->margins.dleft; + else return False; + break; + + case WRulerFirst: + if(x < rPtr->margins.right-MIN_DOC_WIDTH && x >= rPtr->margins.left) + rPtr->margins.first = x - rPtr->margins.dleft; + else return False; + break; + + case WRulerRight: + 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->margins.right = x; + else return False; + break; + + case WRulerBoth: + if( x < rPtr->margins.right-MIN_DOC_WIDTH && x >= rPtr->margins.left) { + rPtr->margins.first = x - rPtr->margins.dleft; + rPtr->margins.body = x - rPtr->margins.dleft; + } else return False; + break; + + default : return False; + } + + return True; +} + + +static void +moveMarker(Ruler *rPtr, int x) +{ + + CHECK_CLASS(rPtr, WC_Ruler); + + if(x < rPtr->offset || x > rPtr->end) + return; + +/* restrictive ticks */ +/* + if(x%10-2 != 0) + return; +*/ + if(!verifyMarkerMove(rPtr, x)) + return; + + /* clear around the motion area */ +#if 0 + XClearWindow(rPtr->view->screen->display, rPtr->view->window); +#else + + XClearArea(rPtr->view->screen->display, rPtr->view->window, + x-10, 3, 20, rPtr->view->size.height-4, False); + +#endif + +#if 0 + { //while(1) { + if (QLength(rPtr->view->screen->display) > 0 ) { + XEvent * event; + while(QLength(rPtr->view->screen->display) > 0 ) { + XNextEvent(rPtr->view->screen->display, event); + if(event->type == MotionNotify || + event->type == ButtonRelease) + break; + } + } + } +#endif + + + + rPtr->pressed = x; + + if(rPtr->moveaction) + (rPtr->moveaction)(rPtr, rPtr->clientData); + + paintRuler(rPtr); +} + +static void +handleEvents(XEvent *event, void *data) +{ + Ruler *rPtr = (Ruler*)data; + + CHECK_CLASS(rPtr, WC_Ruler); + + + switch (event->type) { + + case Expose: + if(event->xexpose.count) + break; + if(rPtr->view->flags.realized) + paintRuler(rPtr); + break; + + case ButtonPress: + if(event->xbutton.button != Button1) + return; + + rPtr->flags.buttonPressed = True; + rPtr->which_marker = + whichMarker(rPtr, event->xmotion.x, + event->xmotion.y); + +#if 0 + /* clear around the clicked area */ + if(rPtr->which_marker>=0 && rPtr->which_marker <= 4) { + + XClearArea(rPtr->view->screen->display, rPtr->view->window, + WMGetRulerMargin(rPtr, rPtr->which_marker)-10, 2, 20, 21, True); + + paintRuler(rPtr); + } +#endif + paintRuler(rPtr); + break; + + case ButtonRelease: + if(event->xbutton.button != Button1) + return; + + rPtr->flags.buttonPressed = False; + rPtr->pressed = event->xmotion.x; + if(rPtr->which_marker == 0) { + rPtr->margins.dleft = rPtr->margins.left; + + /* clear entire (moved) ruler */ + XClearArea(rPtr->view->screen->display, + rPtr->view->window, 0, 0, rPtr->view->size.width, + rPtr->view->size.height, True); + } + + if(rPtr->action) + (rPtr->action)(rPtr, rPtr->clientData); + + paintRuler(rPtr); + break; + + case MotionNotify: + if(rPtr->flags.buttonPressed && + (event->xmotion.state & Button1Mask)) { + moveMarker(rPtr, event->xmotion.x); + } + break; + + } +} + + +WMRuler * +WMCreateRuler(WMWidget *parent) +{ + Ruler *rPtr = wmalloc(sizeof(Ruler)); + + memset(rPtr, 0, sizeof(Ruler)); + + rPtr->widgetClass = WC_Ruler; + + rPtr->view = W_CreateView(W_VIEW(parent)); + if (!rPtr->view) { + free(rPtr); + return NULL; + } + rPtr->view->self = rPtr; + + W_ResizeView(rPtr->view, DEFAULT_RULER_WIDTH, DEFAULT_RULER_HEIGHT); + + WMCreateEventHandler(rPtr->view, ExposureMask|StructureNotifyMask + |EnterWindowMask|LeaveWindowMask|FocusChangeMask + |ButtonReleaseMask|ButtonPressMask|KeyReleaseMask + |KeyPressMask|Button1MotionMask, handleEvents, rPtr); + + rPtr->view->delegate = &_RulerViewDelegate; + + rPtr->which_marker = WRulerLeft; /* none */ + rPtr->pressed = 0; + + + rPtr->offset = DEFAULT_X_OFFSET; + rPtr->margins.left = DEFAULT_X_OFFSET; + rPtr->margins.dleft = DEFAULT_X_OFFSET; + rPtr->margins.body = 0; /* relative */ + rPtr->margins.first = 0; /* relative */ + rPtr->margins.right = 300+DEFAULT_X_OFFSET; + rPtr->end = 320+DEFAULT_X_OFFSET; + + rPtr->flags.showtabs = False; + rPtr->flags.buttonPressed = False; + + rPtr->pview = W_VIEW(parent); + + return rPtr; +} + + +void +WMShowRulerTabs(WMRuler *rPtr, Bool show) +{ + if(!rPtr) + return; + CHECK_CLASS(rPtr, WC_Ruler); + rPtr->flags.showtabs = show; +} + +void +WMSetRulerMargin(WMRuler *rPtr, int which, int pixels) +{ + if(!rPtr) + return; + + + if(pixelsoffset) + pixels = rPtr->offset; + + rPtr->which_marker = which; + if(!verifyMarkerMove(rPtr, pixels)) + return; + + rPtr->pressed = pixels; + if(which == WRulerLeft) + rPtr->margins.dleft = rPtr->margins.left; + + if(!rPtr->view->flags.realized) + return; + + XClearArea(rPtr->view->screen->display, + rPtr->view->window, 0, 0, rPtr->view->size.width, + rPtr->view->size.height, True); + + paintRuler(rPtr); +} + +int +WMGetRulerMargin(WMRuler *rPtr, int which) +{ + if(!rPtr) + return; + + CHECK_CLASS(rPtr, WC_Ruler); + + switch(which) { + case WRulerLeft: return rPtr->margins.left; break; + case WRulerBody: return rPtr->margins.body; break; + case WRulerFirst: return rPtr->margins.first; break; + case WRulerRight: return rPtr->margins.right; break; + case WRulerBoth: return rPtr->margins.body; break; + case WRulerDocLeft: return rPtr->margins.dleft; break; + default: return rPtr->margins.dleft; + } +} + +/* _which_ one was released */ +int +WMGetReleasedRulerMargin(WMRuler *rPtr) +{ + if(!rPtr) + return WRulerLeft; + CHECK_CLASS(rPtr, WC_Ruler); + if(rPtr->which_marker != -1) + return rPtr->which_marker; + else + return WRulerLeft; +} + +/* _which_ one is being grabbed */ +int +WMGetGrabbedRulerMargin(WMRuler *rPtr) +{ + if(!rPtr) + return WRulerLeft; + CHECK_CLASS(rPtr, WC_Ruler); + if(rPtr->which_marker != -1) + return rPtr->which_marker; + else + return WRulerLeft; +} + + + +void +WMSetRulerOffset(WMRuler *rPtr, int pixels) +{ + if(!rPtr || pixels<0) + return; + CHECK_CLASS(rPtr, WC_Ruler); + rPtr->offset = pixels; +} + +int +WMGetRulerOffset(WMRuler *rPtr) +{ + if(!rPtr) + return; + CHECK_CLASS(rPtr, WC_Ruler); + return rPtr->offset; +} + + +void +WMSetRulerAction(WMRuler *rPtr, WMAction *action, void *clientData) +{ + if(!rPtr) + return; + + CHECK_CLASS(rPtr, WC_Ruler); + rPtr->action = action; + rPtr->clientData = clientData; +} + +void +WMSetRulerMoveAction(WMRuler *rPtr, WMAction *moveaction, void *clientData) +{ + if(!rPtr) + return; + + CHECK_CLASS(rPtr, WC_Ruler); + rPtr->moveaction = moveaction; + rPtr->clientData = clientData; +} + diff --git a/WINGs/wtext.c b/WINGs/wtext.c new file mode 100644 index 00000000..68ffea74 --- /dev/null +++ b/WINGs/wtext.c @@ -0,0 +1,2803 @@ +/* WMText: multi-line/font/color text widget for WINGs */ +/* Copyleft (>) 1999, 2000 Nwanua Elumeze */ + + +/* .( * . + .* . ) . + + . . POOF .* . + '* . ( .) ' + jgs ` ( . * */ + + +/* if monoFont, ignore pixmaps, colors, fonts, script, underline */ + + +#include +#include +#include +#include +#include +#include + +#include "wruler.h" +#include "wtext.h" + +void wgdbFree(void *ptr) +{ if(!ptr) printf("err... cannot "); +printf("gdbFree [%p]\n", ptr); +wfree(ptr); +} + + +typedef enum {ctText=0, ctImage=1} ChunkType; +typedef enum { dtDelete=0, dtBackSpace } DeleteType; +typedef enum {wrWord=0, wrChar=1, wrNone=2} Wrapping; + +/* Why singly-linked and not say doubly-linked? + 99% of the time (draw, append), the "prior" + member would have been a useless memory and CPU overhead, + and deletes _are_ relatively infrequent. + When the "prior" member needs to be used, the overhead of + doing things the hard way will be incurred... but seldomly. */ + + +/* a Chunk is a singly-linked list of chunks containing: + o text with a given format + o or an image + o but NOT both */ +typedef struct _Chunk { + char *text; /* the text in the chunk */ + WMPixmap *pixmap; /* OR the pixmap it holds */ + short chars; /* the number of characters in this chunk */ + short mallocedSize; /* the number of characters that can be held */ + + WMFont *font; /* the chunk's font */ + WMColor *color; /* the chunk's color */ + short ul:1; /* underlined or not */ + ChunkType type:1; /* a "Text" or "Image" chunk */ + short script:4; /* script in points: negative for subscript */ +//hrmm selec... + ushort selected; + ushort sStart; + ushort sEnd; + ushort RESERVED:10; + struct _Chunk *next;/*the next member in this list */ + +}Chunk; + + + +/* a Paragraph is a singly-linked list of paragraphs containing: + o a list of chunks in that paragraph + o the formats for that paragraph + o its (draw) position relative to the entire document */ +typedef struct _Paragraph { + Chunk *chunks; /* the list of text and/or image chunks */ + short fmargin; /* the start position of the first line */ + short bmargin; /* the start positions of the rest of the lines */ + short rmargin; /* the end position of the entire paragraph */ + short numTabs; /* the number of tabstops */ + short *tabstops; /* an array of tabstops */ + + Pixmap drawbuffer; /* the pixmap onto which the (entire) + paragraph will be drawn */ + WMPixmap *bulletPix;/* the pixmap to use for bulleting */ + int top; /* the top of the paragraph relative to document */ + int bottom; /* the bottom of the paragraph relative to document */ + int width; /* the width of the paragraph */ + int height; /* the height of the paragraph */ + WMAlignment align:2;/* justification of this paragraph */ + ushort RESERVED:14; + + struct _Paragraph *next; /* the next member in this list */ +} Paragraph; + + +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 "}; + +/* this is really a shrunk down version of the original +"broken" icon... I did not draw it, I simply shrunk it */ +static char * unk_xpm[] = { +"24 24 17 1", +" c None", ". c #0B080C", "+ c #13A015", "@ c #5151B8", +"# c #992719", "$ c #5B1C20", "% c #1DF51D", "& c #D1500D", "* c #2F304A", +"= c #0C6A0C", "- c #F2F1DE", "; c #D59131", "> c #B2B083", ", c #DD731A", +"' c #CC3113", ") c #828238", "! c #6A6A94", +"......!@@@@@@@....$$....", +"...@!@@@@@@@**...$#'....", +"..!!@@@@@@@@.......#....", +"..!@@@@@@@@@*.......$...", +".!@@@#,,#*@@*..*>.*.#...", +"*@@@@#'',,@@@...---!....", +"!@@@@@*.#;*@@..!--->....", +"@@@@@@@@#,.@@..!----@...", +"!@@@@@@*#;'$...!----@...", +"*@@@@@@..'&;;#.)----)...", +".@@@@@@..$..&'.>----)...", +".@@@@@@**---,'>-----!...", +".@@@@@@**---,'>-----@...", +"..@@@@@@@---;;;,;---....", +"..*@@@@*@--->#',;,-*.)..", +"........)---->)@;#!..>..", +".....)----------;$..>)..", +"=%%%*.*!-------);..)-*..", +"=%%%%+...*)>!@*$,.>--...", +"*+++++++.......*$@-->...", +"............**@)!)>->...", +"........................", +"........................", +"........................"}; + +typedef struct W_Text { + W_Class widgetClass; /* the class number of this widget */ + W_View *view; /* the view referring to this instance */ + WMColor *bg; /* the background color to use when drawing */ + + WMRuler *ruler; /* the ruler subwiget to maipulate paragraphs */ + + WMScroller *hscroller; /* the horizontal scroller */ + short hpos; /* the current horizontal position */ + short prevHpos; /* the previous horizontal position */ + + WMScroller *vscroller; /* the vertical scroller */ + int vpos; /* the current vertical position */ + int prevVpos; /* the previous vertical position */ + + int visibleW; /* the actual horizontal space available */ + int visibleH; /* the actual vertical space available */ + + Paragraph *paragraphs; /* the linked list of the paragraphs in the doc. */ + int docWidth; /* the width of the entire document */ + int docHeight; /* the height of the entire document */ + + WMFont *dFont; /* the default font */ + WMColor *dColor; /* the default color */ + WMPixmap *dBulletPix; /* the default pixmap for bullets */ + WMPixmap *dUnknownImg; /* the pixmap for (missing/broken) images */ + + WMRect sRect; /* the selected area */ + Paragraph *currentPara; /* the current paragraph, in which actions occur */ + Chunk *currentChunk; /* the current chunk, about which actions occur */ + short tpos; /* the cursor position (text position) */ + WMParseAction *parser; /* what action to use to parse input text */ + WMParseAction *writer; /* what action to use to write text */ + WMParserActions funcs; /* the "things" that parsers/writers might do */ + XPoint clicked; /* the position of the last mouse click */ + XPoint cursor; /* where the cursor is "placed" */ + short clheight; /* the height of the "line" clicked on */ + short clwidth; /* the width of the "line" clicked on */ + + WMReliefType relief:2; /* the relief to display with */ + Wrapping wrapping:2; /* the type of wrapping to use in drawing */ + WMAlignment dAlignment:2;/* default justification */ + ushort monoFont:1; /* whether to ignore "rich" commands */ + ushort fixedPitch:1; /* assume each char in dFont is the same size */ + ushort editable:1; /* whether to accept user changes or not*/ + ushort rulerShown:1; /* whether the ruler is shown or not */ + ushort cursorShown:1; /* whether the cursor is currently being shown */ + ushort frozen:1; /* whether screen updates are to be made */ + ushort focused:1; /* whether this instance has input focus */ + ushort pointerGrabbed:1;/* whether this instance has the pointer */ + ushort buttonHeld:1; /* the user is still holding down the button */ + ushort ignoreNewLine:1; /* whether to ignore the newline character */ + ushort waitingForSelection:1; /* whether there is a pending paste event */ + ushort ownsSelection:1; /* whether it ownz the current selection */ + ushort findingClickPoint:1;/* whether the search for a clickpoint is on */ + ushort foundClickPoint:1;/* whether the clickpoint has been found */ + ushort hasVscroller:1; /* whether to enable the vertical scroller */ + ushort hasHscroller:1; /* whether to enable the horizontal scroller */ + ushort RESERVED:10; +} Text; + + + +/* --------- static functions that are "private". don't touch :-) --------- */ + + +/* max "characters per chunk that will be drawn at a time" */ +#define MAX_WORD_LENGTH 100 +/* max on a line */ +#define MAX_CHUNX 64 +#define MIN_DOC_WIDTH 200 +typedef struct _LocalMargins { + short left, right, first, body; + } LocalMargins; + +typedef struct _MyTextItems { + char text[MAX_WORD_LENGTH+1]; + WMPixmap *pix; + short chars; + short x; + Chunk *chunk;/* used for "click" events */ + short start; /* ditto... where in the chunk we start (ie. wrapped chunk) */ + ushort type:1; + ushort RESERVED:15; +} MyTextItems; + + +static WMRect +chunkSelectionRect(Text *tPtr, Paragraph *para, MyTextItems item, + short y, short j, short lh) +{ + WMRect rect; + short type=0; /* 0:none 1:partial: 2:all */ + short rh=(tPtr->rulerShown)?45:5; + short w, lm; + WMFont *font = (tPtr->monoFont || item.chunk->type != ctText)? + tPtr->dFont:item.chunk->font; + + rect.pos.x = -23; + if(y+para->top+rh > tPtr->sRect.pos.y+tPtr->sRect.size.height + || y+para->top+rh+lh < tPtr->sRect.pos.y) + return rect; + + if(item.chunk->type == ctText) + w = WMWidthOfString(font, item.text, item.chars); + else w = item.chunk->pixmap->width; + + if(y+para->top+rh >= tPtr->sRect.pos.y && (y+para->top+rh+lh + <= tPtr->sRect.pos.y+tPtr->sRect.size.height)) + //&& item.x+j >= tPtr->sRect.pos.x+tPtr->sRect.size.width)) + type = 2; + else type = 1; + + +#if 0 + if(item.x+j >= tPtr->sRect.pos.x && + item.x+j+w < tPtr->sRect.pos.x+tPtr->sRect.size.width) + type = 2; + + if(type == 1 && y+para->top+rh+lh <= + tPtr->sRect.pos.y+tPtr->sRect.size.height) + type = 2; +#endif + + + if(type == 1 && item.chunk->type == ctText) { /* partial coverage */ + lm = 2+WMGetRulerMargin(tPtr->ruler, WRulerDocLeft); + /* even I am still confused, so don't ask please */ + if( (item.x+j+lm >= tPtr->sRect.pos.x && + item.x+j+lm <= tPtr->sRect.pos.x+tPtr->sRect.size.width) + || (item.x+j+lm >= tPtr->sRect.pos.x+tPtr->sRect.size.width + && y+para->top+rh+lh <= + tPtr->sRect.pos.y+tPtr->sRect.size.height) + || (tPtr->sRect.pos.y < y+para->top+rh + && tPtr->sRect.pos.x+tPtr->sRect.size.width > + item.x+j+lm) ){ + rect.size.width = w; + rect.pos.x = item.x+j; + item.chunk->selected = True; + if(item.chunk->chars > 6) { + item.chunk->sStart = 3; + item.chunk->sEnd = item.chunk->chars; + } else { + item.chunk->sStart = 0; + item.chunk->sEnd = item.chunk->chars; + } + } + } else if(type == 2) { + rect.pos.x = item.x+j; + item.chunk->selected = True; + if(item.chunk->type == ctText) { + item.chunk->sStart = 0; + item.chunk->sStart = item.chunk->chars; + rect.size.width = WMWidthOfString(font, + item.text, item.chars); + } else { + rect.size.width = item.chunk->pixmap->width; + } + } + + rect.pos.y = y; + rect.size.height = lh; + return rect; +} + +static int +myDrawText(Text *tPtr, Paragraph *para, MyTextItems *items, + short nitems, short pwidth, int y, short draw, short spacepos) +{ + short i, ul_thick, u, j=0; /* j = justification */ + short line_width = 0, line_height=0, mx_descent=0; + WMScreen *screen = tPtr->view->screen; + GC gc; + WMFont *font; + + if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0; + for(i=0; imonoFont)?tPtr->dFont:items[i].chunk->font; + mx_descent = WMIN(mx_descent, -font->y); + line_height = WMAX(line_height, font->height); + //printf("chunk.x %d xpoint.x %d\n", + // items[i].x, tPtr->clicked.x); + + line_width += WMWidthOfString(font, + items[i].text, items[i].chars); + } else { + mx_descent = WMIN(mx_descent, -(items[i].pix->height-3)); +/* replace -3 wif descent... */ + line_height = WMAX(line_height, items[i].pix->height); + if(para->align == WARight || para->align == WACenter) { + line_width += items[i].pix->width; + } } } + + if(para->align == WARight) { + j = pwidth - line_width; + } else if (para->align == WACenter) { + j = (short) ((float)(pwidth - line_width))/2.0; + } + + if(tPtr->findingClickPoint && (y+line_height >= tPtr->clicked.y)) { + tPtr->foundClickPoint = True; + tPtr->currentChunk = items[0].chunk; /* just first on this "line" */ + tPtr->tpos = items[0].start; /* where to "start" counting from */ + tPtr->clicked.x = j+items[0].x; + tPtr->clicked.y = y+line_height+mx_descent; + tPtr->clheight = line_height; /* to draw the cursor */ + tPtr->clwidth = line_width; /* where to stop searching */ + return 0; + } if(!draw) return line_height; + + for(i=0; iownsSelection) { + WMRect rect = chunkSelectionRect(tPtr, para, + items[i], y, j, line_height); + if(rect.pos.x != -23) { /* has been selected */ + XFillRectangle(tPtr->view->screen->display, para->drawbuffer, + WMColorGC(WMGrayColor(tPtr->view->screen)), + rect.pos.x, rect.pos.y, rect.size.width, rect.size.height); + } + } + + if(items[i].type == ctText) { + gc = WMColorGC(items[i].chunk->color); + font = (tPtr->monoFont)?tPtr->dFont:items[i].chunk->font; + WMDrawString(screen, para->drawbuffer, gc, font, + items[i].x+j, y - font->y - mx_descent, + items[i].text, items[i].chars); + if(items[i].chunk->ul && !tPtr->monoFont) { + ul_thick = (short) ((float)font->height)/12.0; + if (ul_thick < 1) ul_thick = 1; + for(u=0; udisplay, para->drawbuffer, gc, items[i].x+j, + y + 1 + u - mx_descent, + items[i].x + j + WMWidthOfString(font, + items[i].text, items[i].chars), y + 1 + u - mx_descent); + + } + } } else { + WMDrawPixmap(items[i].pix, para->drawbuffer, items[i].x+j, + y + 3 - mx_descent - items[i].pix->height); + } } + return line_height; +} + +static void +drawPChunkPart(Text *tPtr, Chunk *chunk, LocalMargins m, Paragraph *para, + MyTextItems *items, short *nitems, short *Lmargin, XPoint *where, short draw) +{ + short p_width; + + if(!chunk) return; + if(!chunk->pixmap) + chunk->pixmap = WMRetainPixmap(tPtr->dUnknownImg); + + p_width = m.right - WMIN(m.first, m.body) - WMGetRulerOffset(tPtr->ruler); + if(p_width < MIN_DOC_WIDTH) // need WMRuler to take care of this... + return; + if(where->x + chunk->pixmap->width <= p_width - *Lmargin) { + /* it can fit on rest of line */ + items[*nitems].pix = chunk->pixmap; + items[*nitems].type = ctImage; + items[*nitems].chars = 0; + items[*nitems].x = *Lmargin+where->x; + items[*nitems].chunk = chunk; + items[*nitems].start = 0; + + if(*nitems >= MAX_CHUNX) { + items[*nitems].chars = 0; + items[*nitems].x = *Lmargin+where->x; + items[*nitems].chunk = chunk; + items[*nitems].start = 0; + where->y += myDrawText(tPtr, para, items, *nitems+1, + p_width-*Lmargin, where->y, draw, 0); + if(tPtr->findingClickPoint && tPtr->foundClickPoint) return; + *nitems = 0; + where->x = 0; + } else { + (*nitems)++; + where->x += chunk->pixmap->width; + } + } else if(chunk->pixmap->width <= p_width - *Lmargin) { + /* it can fit on an entire line, flush the myDrawText then wrap it */ + where->y += myDrawText(tPtr, para, items, *nitems+1, + p_width-*Lmargin, where->y, draw, 0); + *nitems = 0; + *Lmargin = WMAX(0, m.body - m.first); + where->x = 0; + drawPChunkPart(tPtr, chunk, m, para, items, nitems, + Lmargin, where, draw); + } else { +#if 1 + *nitems = 0; + where->x = 0; + *Lmargin = WMAX(0, m.body - m.first); + items[*nitems].pix = chunk->pixmap; + items[*nitems].type = ctImage; + items[*nitems].chars = 0; + items[*nitems].x = *Lmargin+where->x; + items[*nitems].chunk = chunk; + items[*nitems].start = 0; + where->y += myDrawText(tPtr, para, items, *nitems+1, + p_width-*Lmargin, where->y, draw, 0); +#endif + + /* scale image to fit, call self again */ + /* deprecated - the management */ + } + +} + +static void +drawTChunkPart(Text *tPtr, Chunk *chunk, char *bufr, LocalMargins m, + Paragraph *para, MyTextItems *items, short *nitems, short len, short start, + short *Lmargin, XPoint *where, short draw, short spacepos) +{ + short t_chunk_width, p_width, chars; + WMFont *font = (tPtr->monoFont)?tPtr->dFont:chunk->font; +/* if(doc->clickstart.yes && doc->clickstart.done) return; */ + + if(len==0) return; + p_width = m.right - WMIN(m.first, m.body); + if(p_width < MIN_DOC_WIDTH) // need WMRuler to take care of this... + return; + + + t_chunk_width = WMWidthOfString(font, bufr, len); + if((where->x + t_chunk_width <= p_width - *Lmargin) + || (tPtr->wrapping == wrNone)) { + /* if it can fit on rest of line, append to line */ + chars = WMIN(len, MAX_WORD_LENGTH); + snprintf(items[*nitems].text, chars+1, "%s", bufr); + items[*nitems].chars = chars; + items[*nitems].x = *Lmargin+where->x; + items[*nitems].type = ctText; + items[*nitems].chunk = chunk; + items[*nitems].start = start; + + if(*nitems >= MAX_CHUNX) { + chars = WMIN(len, MAX_WORD_LENGTH); + snprintf(items[*nitems].text, chars+1, "%s", bufr); + items[*nitems].chars = chars; + items[*nitems].x = *Lmargin+where->x; + items[*nitems].type = ctText; + items[*nitems].chunk = chunk; + items[*nitems].start = start; + where->y += myDrawText(tPtr, para, items, *nitems+1, + p_width-*Lmargin, where->y, draw, spacepos); + if(tPtr->findingClickPoint && tPtr->foundClickPoint) return; + *nitems = 0; + where->x = 0; + } else { + (*nitems)++; + where->x += t_chunk_width; + } + } else if(t_chunk_width <= p_width - *Lmargin) { + /* it can fit on an entire line, flush and wrap it to a new line */ + where->y += myDrawText(tPtr, para, items, *nitems, + p_width-*Lmargin, where->y, draw, spacepos); + if(tPtr->findingClickPoint && tPtr->foundClickPoint) return; + *nitems = 0; + *Lmargin = WMAX(0, m.body - m.first); + where->x = 0; + drawTChunkPart(tPtr, chunk, bufr, m, para, items, nitems, + len, start, Lmargin, where, draw, spacepos); + } else { + /* otherwise, chop line, call ourself recursively until it's all gone */ + short J=0; /* bufr */ + short j=0; /* local tmp buffer */ + char tmp[len]; + short diff = p_width - *Lmargin - where->x; + short _start=0; + + if(diff < 20) { + where->y += myDrawText(tPtr, para, items, *nitems, + p_width-*Lmargin, where->y, draw, spacepos); + if(tPtr->findingClickPoint && tPtr->foundClickPoint) return; + *nitems = 0; + *Lmargin = WMAX(0, m.body - m.first); + where->x = 0; + diff = p_width - *Lmargin - where->x; + } + + for(J=0; J diff) { + drawTChunkPart(tPtr, chunk, tmp, m, para, items, nitems, + j, start+_start, Lmargin, where, draw, spacepos); + _start = J; + J--; j=0; + } else j++; + } + /* and there's always that last chunk, get it too */ + drawTChunkPart(tPtr, chunk, tmp, m, para, items, nitems, + j, start+_start, Lmargin, where, draw, spacepos); + } +} + +/* this function does what it's called :-) + o It is also used for calculating extents of para, + (returns height) so watch out for (Bool) draw + o Also used to determine where mouse was clicked */ +static int +putParagraphOnPixmap(Text *tPtr, Paragraph *para, Bool draw) +{ + char bufr[MAX_WORD_LENGTH+1]; /* a single word + '\0' */ + MyTextItems items[MAX_CHUNX+1]; + short lmargin, spacepos, i, s, nitems, start; + LocalMargins m; + XPoint where; + Chunk *chunk; + + if(!tPtr->view->flags.realized || !para) return 0; + + where.x = 0, where.y =0, nitems = 0; + m.left = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft); + m.right = WMGetRulerMargin(tPtr->ruler, WRulerRight) - m.left; + m.first = para->fmargin, m.body = para->bmargin; + + if(draw) { + W_Screen *screen = tPtr->view->screen; + if(para->drawbuffer) + XFreePixmap(screen->display, para->drawbuffer); + if(para->width<2*tPtr->dFont->height) para->width = 2*tPtr->dFont->height; + if(para->heightdFont->height) para->height = tPtr->dFont->height; + para->drawbuffer = XCreatePixmap(screen->display, + tPtr->view->window, para->width, para->height, screen->depth); + XFillRectangle(screen->display, para->drawbuffer, + WMColorGC(tPtr->bg), 0, 0, para->width, para->height); + + } + + //if(para->align != tPtr->dAlignment) + // para->align = tPtr->dAlignment; + + /* draw the bullet if appropriate */ + if(m.body>m.first && !tPtr->monoFont) { + lmargin = m.body - m.first; + if(draw) { + if(para->bulletPix) + WMDrawPixmap(para->bulletPix, para->drawbuffer, lmargin-10, 5); + else + WMDrawPixmap(tPtr->dBulletPix, para->drawbuffer, lmargin-10, 5); + } + /* NeXT sez next tab, I say the m.body - m.first margin */ + } else { + lmargin = WMAX(0, m.first - m.body); + } + + if(tPtr->findingClickPoint && !para->chunks) { + tPtr->currentChunk = NULL; + tPtr->foundClickPoint = True; + tPtr->tpos = 0; + tPtr->clicked.x = lmargin; + tPtr->clicked.y = 5; + tPtr->clheight = para->height; + tPtr->clwidth = 0; + return 0; + } + + chunk = para->chunks; + while(chunk) { + + if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0; + + if(chunk->type == ctImage && !tPtr->monoFont ) { + drawPChunkPart(tPtr, chunk, m, para, items, &nitems, + &lmargin, &where, draw); + } else if(chunk->text && chunk->type == ctText) { + if(tPtr->wrapping == wrNone) { + drawTChunkPart(tPtr, chunk, chunk->text, m, para, items, &nitems, + chunk->chars, 0, &lmargin, &where, draw, spacepos); + } else if(tPtr->wrapping == wrWord) { + spacepos=0, i=0, start=0; + while(spacepos < chunk->chars) { + bufr[i] = chunk->text[spacepos]; + if( bufr[i] == ' ' || i >= MAX_WORD_LENGTH ) { + if(bufr[i] == ' ') s=1; else s=0; + drawTChunkPart(tPtr, chunk, bufr, m, para, + items, &nitems, i+s, start, &lmargin, &where, + draw, spacepos); + start = spacepos+s; + if(i > MAX_WORD_LENGTH-1) + spacepos--; + i=0; + } else i++; + spacepos++; + } + /* catch that last onery one. */ + drawTChunkPart(tPtr, chunk, bufr, m, para, + items, &nitems, i, start, &lmargin, &where, draw, spacepos); + } } chunk = chunk->next; + } + /* we might have a few leftover items that need drawing */ + if(nitems >0) { + where.y += myDrawText(tPtr, para, items, + nitems, m.right-m.left-lmargin, where.y, draw, spacepos); + if(tPtr->findingClickPoint && tPtr->foundClickPoint) return 0; + } + return where.y; +} + +static int +calcParaExtents(Text *tPtr, Paragraph *para) +{ + if(!para) return 0; + + if(tPtr->monoFont) { + para->width = tPtr->visibleW; + para->fmargin = 0; + para->bmargin = 0; + para->rmargin = tPtr->visibleW; + } else { + para->width = WMGetRulerMargin(tPtr->ruler, WRulerRight) - + WMIN(para->fmargin, para->bmargin) + - WMGetRulerOffset(tPtr->ruler); + } + + if(!para->chunks) + para->height = tPtr->dFont->height; + else + para->height = putParagraphOnPixmap(tPtr, para, False); + + if(para->heightdFont->height) + para->height = tPtr->dFont->height; + para->bottom = para->top + para->height; + return para->height; +} + + +/* rather than bother with redrawing _all_ the pixmaps, simply + rearrange (i.e., push down or pull up) paragraphs after this one */ +static void +affectNextParas(Text *tPtr, Paragraph *para, int move_y) +{ + Paragraph *next; + int old_y = 0; + + if(!para || move_y==0) return; + if(move_y == -23) { + old_y = para->bottom; + calcParaExtents(tPtr, para); + old_y -= para->bottom; + if(old_y == 0) return; + move_y = -old_y; + }if(move_y == 0) return; + + next = para->next; + while(next) { + next->top += move_y; + next->bottom = next->top + next->height; + next = next->next; // I know, I know + }tPtr->docHeight += move_y; + +#if 0 + tPtr->vpos += move_y; + if(tPtr->vpos < 0) tPtr->vpos = 0; + if(tPtr->vpos > tPtr->docHeight - tPtr->visibleH) + tPtr->vpos = tPtr->docHeight - tPtr->visibleH; +#endif + +} + + +static void +calcDocExtents(Text *tPtr) +{ + Paragraph *para; + + if(tPtr->monoFont) { + tPtr->docWidth = tPtr->visibleW; + } else { + tPtr->docWidth = WMGetRulerMargin(tPtr->ruler, WRulerRight) - + WMGetRulerMargin(tPtr->ruler, WRulerDocLeft); + } + tPtr->docHeight = 0; + para = tPtr->paragraphs; + if(para) { + while(para) { + para->top = tPtr->docHeight; + tPtr->docHeight += calcParaExtents(tPtr, para); + para->bottom = tPtr->docHeight; + para = para->next; + } + } else { /* default to this if no paragraphs */ + tPtr->docHeight = tPtr->dFont->height; + } +#if 0 + if(tPtr->editable) /* add space at bottom to enter new stuff */ + tPtr->docHeight += tPtr->dFont->height; +#endif +} + + +/* If any part of a paragraph is viewable, the entire +paragraph is drawn on an otherwise empty (XFreePixmap) pixmap. +The actual viewable parts of the paragraph(s) are then pieced +together via paintText: + + ------------------------------------------- + || this is a paragraph in this document|| + ||========================================|| + || | only part of it is visible though. || + || |-------------------------------------|| + ||[.| This is another paragraph || + || | which I'll make relatively long || + || | just for the sake of writing a long || + || | paragraph with a picture: ^_^ || + || |-------------------------------------|| + ||==| Of the three paragraphs, only || + ||/\| the preceding was totally copied to || + ||\/| totally copied to the window, even || + ==========================================|| + though they are all on pixmaps. + ------------------------------------------- + This paragraph exists only in + memory and so has a NULL pixmap. + ------------------------------------------- + + +simple, right? Performance: the best of both worlds... + o fast scrolling: no need to rewrite what's already + on the screen, simply XCopy it. + o fast typing: only change current para, then simply + affect other (i.e., subsequent) paragraphs. + o If no part of para is on screen, gdbFree pixmap; else draw on + individual pixmap per para then piece several paras together + o Keep track of who to XCopy to window (see paintText) */ +static void +drawDocumentPartsOnPixmap(Text *tPtr, Bool all) +{ + Paragraph *para; + + para = tPtr->paragraphs; + while(para) { + /* the 32 reduces jitter on the human eye by preparing paragraphs + in anticipation of when the _moving_ scrollbar reaches them */ + if(para->bottom + 32 < tPtr->vpos || + para->top > tPtr->visibleH + tPtr->vpos + 32 ) { + if(para->drawbuffer) { + XFreePixmap(tPtr->view->screen->display, para->drawbuffer); + para->drawbuffer = (Pixmap) NULL; + } + } else { + if(!para->drawbuffer || all) + putParagraphOnPixmap(tPtr, para, True); + } + para = para->next; + } +} + + + +/* this function blindly copies the "visible" parts of a pragraph + unto the view, (top-down approach). It starts drawing from + the top of the view (which paragraph to draw is determined by + drawDocumentPartsOnPixmap); it stops at the bottom of the view. */ +static void +paintText(Text *tPtr) +{ + short lmargin, para_lmargin; + int from=5, to=5, height; + Paragraph *para; + short vS=0, hS=0, rh=0; + + if(!tPtr->view->flags.realized) return; + + if(tPtr->rulerShown) rh = 40; + to += rh; + + if(tPtr->hasVscroller) vS = 21; + if(tPtr->hasHscroller) hS = 21; + +//XClearWindow(tPtr->view->screen->display, tPtr->view->window); + + lmargin = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft); + if(tPtr->paragraphs) { + para = tPtr->paragraphs; + while(para) { + if(para->drawbuffer) { + from = (para->top<=tPtr->vpos)?tPtr->vpos-para->top:0; + height = para->height - from; + if(from>=0 && height>0 ) { + para_lmargin = WMIN(para->fmargin, para->bmargin); + if(lmargin-vSfmargin, para->bmargin)) { +#if 0 + XClearArea(tPtr->view->screen->display, tPtr->view->window, + lmargin, to, 2+para_lmargin, height, False); +#else + XFillRectangle(tPtr->view->screen->display, tPtr->view->window, + WMColorGC(tPtr->dColor), lmargin, to, 2+para_lmargin, height); +#endif + } + XCopyArea(tPtr->view->screen->display, para->drawbuffer, + tPtr->view->window, WMColorGC(tPtr->bg), 0, from, + para->width-4, height, lmargin+para_lmargin+2, to); + if( (to+=height) > tPtr->visibleH+rh) + break; + } } + para = para->next; + } } + + +#if 0 + /* clear any left over space (esp. during para deletes/ ruler changes) */ + if(tPtr->docHeight < tPtr->visibleH && tPtr->visibleH+rh+5-to>0) { + XClearArea(tPtr->view->screen->display, tPtr->view->window, vS, to, + tPtr->view->size.width-vS, tPtr->visibleH+rh+hS+5-to, False); + } + + if(lmargin>vS) + XClearArea(tPtr->view->screen->display, tPtr->view->window, + vS+1, rh+5, lmargin-vS, tPtr->visibleH+rh+5-vS, False); + + +// from the "selection" days... + W_DrawRelief(tPtr->view->screen, WMWidgetXID(tPtr), + tPtr->sRect.pos.x, tPtr->sRect.pos.y, + tPtr->sRect.size.width, tPtr->sRect.size.height, tPtr->relief); +#endif + + W_DrawRelief(tPtr->view->screen, WMWidgetXID(tPtr), 0, rh, + tPtr->visibleW+vS, tPtr->visibleH+hS, tPtr->relief); + + if(tPtr->editable && tPtr->clheight > 0) { + int top = tPtr->cursor.y-tPtr->vpos; + int bot = top+tPtr->clheight; + if(bot>5) { + if(top<5) top=5; + if(bot>tPtr->visibleH+hS-2) bot = tPtr->visibleH+hS-2; + if(bot-top>1) { + //do something about italic text... + XDrawLine(tPtr->view->screen->display, tPtr->view->window, + WMColorGC(tPtr->dColor), lmargin+tPtr->cursor.x, top, + lmargin+tPtr->cursor.x, bot); + } } } + + +} + + +/* called anytime either the ruler, vscroller or hscroller is hidden/shown, + or when the widget is resized by some user action */ +static void +resizeText(W_ViewDelegate *self, WMView *view) +{ + Text *tPtr = (Text *)view->self; + short rh=0; + + if(!tPtr->monoFont && tPtr->rulerShown) + rh = 40; + + W_ResizeView(view, view->size.width, view->size.height); + WMResizeWidget(tPtr->ruler, view->size.width, 40); + + + if(tPtr->hasVscroller) { + WMMoveWidget(tPtr->vscroller, 1, 1+rh); + WMResizeWidget(tPtr->vscroller, 20, view->size.height-rh-2); + tPtr->visibleW = view->size.width-21; + + if(tPtr->hasHscroller) { + WMMoveWidget(tPtr->hscroller, 20, view->size.height-21); + WMResizeWidget(tPtr->hscroller, view->size.width-21, 20); + tPtr->visibleH = view->size.height-21-rh; + } else tPtr->visibleH = view->size.height-rh; + } else { + tPtr->visibleW = view->size.width; + if(tPtr->hasHscroller) { + WMMoveWidget(tPtr->hscroller, 1, view->size.height-21); + WMResizeWidget(tPtr->hscroller, view->size.width-2, 20); + tPtr->visibleH = view->size.height-21-rh; + } else tPtr->visibleH = view->size.width-2-rh; + } + WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos); +} + +W_ViewDelegate _TextViewDelegate = +{ + NULL, + NULL, + resizeText, + NULL, +}; + + + +/* a plain text parser */ +/* this gives useful hints on how to make a more + interesting parser for say HTML, RTF */ +static void +defaultParser(Text *tPtr, void *data, short type) +{ + char *start, *mark, *text = (char *) data; + Chunk *chunk = NULL; + Paragraph *para = NULL; + + start = text; + while(start) { + mark = strchr(start, '\n'); + if(mark) { + /* there is a newline, indicating the need for a new paragraph */ + /* attach the chunk to the current paragraph */ + if((short)(mark-start) > 1) { + /* ignore chunks with just a single newline but still make a + blank paragraph */ + chunk = (tPtr->funcs.createTChunk) (start, (short)(mark-start), + tPtr->dFont, tPtr->dColor, 0, False); + (tPtr->funcs.insertChunk) (tPtr, chunk, type); + } + /* _then_ create a new paragraph for the _next_ chunk */ + para = (tPtr->funcs.createParagraph) (0, 0, tPtr->visibleW, + NULL, 0, WALeft); + (tPtr->funcs.insertParagraph) (tPtr, para, type); + start = mark+1; + } else { + /* just attach the chunk to the current paragraph */ + if(strlen(start) > 0) { + chunk = (tPtr->funcs.createTChunk) (start, strlen(start), + tPtr->dFont, tPtr->dColor, 0, False); + (tPtr->funcs.insertChunk) (tPtr, chunk, type); + } start = mark; + } } + +} + +static void +updateScrollers(Text *tPtr) +{ + if(tPtr->hasVscroller) { + if(tPtr->docHeight < tPtr->visibleH) { + WMSetScrollerParameters(tPtr->vscroller, 0, 1); + tPtr->vpos = 0; + } else { + float vmax = (float)(tPtr->docHeight); + WMSetScrollerParameters(tPtr->vscroller, + ((float)tPtr->vpos)/(vmax - (float)tPtr->visibleH), + (float)tPtr->visibleH/vmax); + } + } + + if(tPtr->hasHscroller) + ; +} + +static void +scrollersCallBack(WMWidget *w, void *self) +{ + Text *tPtr = (Text *)self; + Bool scroll = False; + Bool dimple = False; + + if(!tPtr->view->flags.realized) return; + + if(w == tPtr->vscroller) { + float vmax; + int height; + vmax = (float)(tPtr->docHeight); + height = tPtr->visibleH; + if(height>7) + height -= 7; /* the top border (5) + bottom (2) */ + + switch(WMGetScrollerHitPart(tPtr->vscroller)) { + case WSDecrementLine: + if(tPtr->vpos > 0) { + if(tPtr->vpos>16) tPtr->vpos-=16; + else tPtr->vpos=0; + scroll=True; + }break; + case WSIncrementLine: { + int limit = tPtr->docHeight - height; + if(tPtr->vpos < limit) { + if(tPtr->vposvpos+=16; + else tPtr->vpos=limit; + scroll = True; + }}break; + case WSDecrementPage: + tPtr->vpos -= height; + + if(tPtr->vpos < 0) + tPtr->vpos = 0; + dimple = True; + scroll = True; + printf("dimple needs to jump to mouse location ;-/\n"); + break; + case WSIncrementPage: + tPtr->vpos += height; + if(tPtr->vpos > (tPtr->docHeight - height)) + tPtr->vpos = tPtr->docHeight - height; + dimple = True; + scroll = True; + printf("dimple needs to jump to mouse location ;-/\n"); + break; + + + case WSKnob: + tPtr->vpos = WMGetScrollerValue(tPtr->vscroller) + * (float)(tPtr->docHeight - height); + scroll = True; + break; + +#if 0 + case WSKnobSlot: + case WSNoPart: +float vmax = (float)(tPtr->docHeight); +((float)tPtr->vpos)/(vmax - (float)tPtr->visibleH), +(float)tPtr->visibleH/vmax); +dimple =where mouse is. +#endif + break; + } + scroll = (tPtr->vpos != tPtr->prevVpos); + tPtr->prevVpos = tPtr->vpos; + } + + if(w == tPtr->hscroller) + ; + +//need scrollv || scrollh + if(scroll) { +/* + if(0&&dimple) { + if(tPtr->rulerShown) + XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 47, + tPtr->view->size.width-24, tPtr->view->size.height-49, True); + else + XClearArea(tPtr->view->screen->display, tPtr->view->window, 22, 2, + tPtr->view->size.width-24, tPtr->view->size.height-4, True); + } +*/ + updateScrollers(tPtr); + drawDocumentPartsOnPixmap(tPtr, False); + paintText(tPtr); + } +} + + +void +W_InsertText(WMText *tPtr, void *data, int position) +{ + if(!tPtr) return; + if(!data) { + Paragraph *para = tPtr->paragraphs, *ptmp; + Chunk *chunk, *ctmp; + WMFreezeText(tPtr); + while(para) { + chunk = para->chunks; + while(chunk) { + if(chunk->type == ctText && chunk->text) + wgdbFree(chunk->text); + else if(chunk->pixmap) + WMReleasePixmap(chunk->pixmap); + ctmp = chunk; + chunk = chunk->next; + wgdbFree(ctmp); + } + ptmp = para; + para = para->next; + if(ptmp->drawbuffer) + XFreePixmap(tPtr->view->screen->display, ptmp->drawbuffer); + wgdbFree(ptmp); + } + tPtr->paragraphs = NULL; + tPtr->currentPara = NULL; + tPtr->currentChunk = NULL; + WMThawText(tPtr); + WMRefreshText(tPtr, 0, 0); + return; + } + + if(tPtr->parser) + (tPtr->parser)(tPtr, data, position >= 0 ? 1 : 0); + else + defaultParser(tPtr, data, position >= 0 ? 1 : 0); +} + +static void +cursorToTextPosition(Text *tPtr, int x, int y) +{ + Paragraph *para = NULL; + Chunk *chunk = NULL; + WMFont *font; + short line_width=0; + short orig_x, orig_y; + + if(x<(tPtr->hasVscroller?21:1)) { + y -= tPtr->clheight; + x = tPtr->view->size.width; //tPtr->visibleW; + } else if(x>tPtr->clwidth && xclicked.x) { + //x = (tPtr->hasVscroller)?21:1; + //y += tPtr->clheight; + } + + if(x<0) x=0; + orig_x = x; + + if(y<0 || y>tPtr->view->size.height-3) return; + orig_y = y; + tPtr->clicked.x = orig_x; + tPtr->clicked.y = y; + tPtr->clicked.y += tPtr->vpos; + tPtr->clicked.y -= tPtr->rulerShown?40:0; + para = tPtr->paragraphs; + if(!para) return; + while(para->next) { + if( tPtr->clicked.y>= para->top-4 && + tPtr->clicked.y < para->bottom+4) break; + para = para->next; + } if(!(tPtr->currentPara = para)) return; + + tPtr->clicked.y -= para->top; + if(tPtr->clicked.y<0) tPtr->clicked.y=0; + if(tPtr->hasVscroller) x -= 21; + if(x<0) x=0; + + tPtr->findingClickPoint = True; + tPtr->foundClickPoint = False; + /* also affects tPtr->currentChunk, tPtr->clicked.x and y, + tPtr->clheight and ->width */ + putParagraphOnPixmap(tPtr, para, False); + tPtr->findingClickPoint = False; + tPtr->clicked.y += para->top; + + if(tPtr->currentChunk) { + short _width=0, start=tPtr->tpos, done=False, w=0; + chunk = tPtr->currentChunk; + while(!done && chunk && line_widthclwidth) { + if(chunk->type == ctText) { + font = (tPtr->monoFont)?tPtr->dFont:chunk->font; + for (w=start; wchars; w++) { + _width = WMWidthOfString(font, &chunk->text[w], 1); + line_width += _width; + if(line_width+tPtr->clicked.x >= x) { + line_width -= _width; + done = True; +printf("break\n"); + break; + } } + + if(0&&chunk->next) { + if(chunk->next->type == ctImage) { + if(x+10 < line_width+chunk->next->pixmap->width) { +printf("true\n"); + done = True; + } } } + } else { + _width = chunk->pixmap->width; + line_width += _width; + if(line_width+tPtr->clicked.x >= x) { + line_width -= _width; + tPtr->tpos = 0; + done = True; + } } + + if(!done) { + chunk = chunk->next; + start = w = 0; + } else { + tPtr->tpos = w; + tPtr->currentChunk = chunk; + break; + } } } else { + short vS = (tPtr->hasVscroller)?32:12; + if(para->align == WARight) { + tPtr->clicked.x = tPtr->view->size.width-vS; + } else if (para->align == WACenter) { + tPtr->clicked.x = -(vS/2)+(tPtr->view->size.width-vS)/2; + } else { + tPtr->clicked.x = 2; + } } + + tPtr->cursor.x = tPtr->clicked.x+2+line_width; + tPtr->cursor.y = tPtr->clicked.y; + tPtr->clicked.y = orig_y; + tPtr->clicked.x = orig_x; + putParagraphOnPixmap(tPtr, para, True); + paintText(tPtr); +} + +static void +deleteTextInteractively(Text *tPtr, DeleteType type) +{ + Paragraph *para; + Chunk *chunk; + short pos,w=0,h=0, doprev=False, doprevpara=False; + WMFont *font; + int current = WMGetTextCurrentChunk(tPtr); + + if(!(para = tPtr->currentPara)) return; + if(!(chunk = tPtr->currentChunk)) return; + font = (tPtr->monoFont)?tPtr->dFont:chunk->font; + doprev = (tPtr->tpos < 2); + + switch(type) { + case dtDelete: /* delete _after_ cursor ... implement later */ + case dtBackSpace: /* delete _before_ cursor */ + if(chunk->chars > 1) { + pos = tPtr->tpos-1; +printf("here %d\n", pos); + if(pos>0) { + w = WMWidthOfString(font, &chunk->text[pos], 1); + memmove(&(chunk->text[pos]), + &(chunk->text[pos+1]), chunk->chars-pos+1); + tPtr->tpos--; + chunk->chars--; + } } else { + WMRemoveTextChunk(tPtr, current); + doprev = True; + } + + if(doprev) { + if(current > 0) { + WMSetTextCurrentChunk(tPtr, current-1); + if(!tPtr->currentChunk) { + printf("PREV PARA\n"); + } else { + tPtr->tpos = tPtr->currentChunk->chars; + } + } else if(0){ + int currentp = WMGetTextCurrentParagraph(tPtr); + doprevpara = True; + if(currentp > 1) { + para->chunks = NULL; + WMRemoveTextParagraph(tPtr, currentp); + WMSetTextCurrentParagraph(tPtr, currentp-1); + WMSetTextCurrentChunk(tPtr, -1); + para = tPtr->currentPara; + if(para) { + if(!tPtr->currentChunk || !para->chunks) { + para->chunks = chunk; + tPtr->currentChunk = chunk; + } else + tPtr->currentChunk->next = chunk; + } } } } } + + if(1) { //if(1||(para && !doprevpara)) { + affectNextParas(tPtr, para, -23); + putParagraphOnPixmap(tPtr, para, True); + drawDocumentPartsOnPixmap(tPtr, False); + updateScrollers(tPtr); +paintText(tPtr); + //cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y); + } else WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos); +} + + +/* give us nice chunk sizes (multiples of 16) */ +static short +reqBlockSize(short requested) +{ + return requested+16-(requested%16); +} + +static void +insertTextInteractively(Text *tPtr, char *text) +{ + Paragraph *para=NULL; + Chunk *chunk=NULL, *newchunk=NULL; + int height = -23; /* should only be changed upon newline */ + short w=0,h=0; + WMFont *font; + + if(!tPtr->editable) return; + if(*text == '\n' && tPtr->ignoreNewLine) + return; + + para = tPtr->currentPara; + chunk = tPtr->currentChunk; + font = (tPtr->monoFont || !chunk)?tPtr->dFont:chunk->font; + + if(*text == '\n') { + int new_top=0; + if(chunk) { /* there's a chunk (or part of it) to detach from old */ + int current = WMGetTextCurrentChunk(tPtr); + if(tPtr->tpos <=0) { /* at start of chunk */ + if(current<1) { /* the first chunk... make old para blank */ + newchunk = para->chunks; + para->chunks = NULL; + putParagraphOnPixmap(tPtr, para, True); + } else { /* not first chunk... */ + printf("cut me out \n"); + } + } else if(tPtr->tpos < chunk->chars && chunk->type == ctText) { + /* not at start of chunk */ + char text[chunk->chars-tPtr->tpos+1]; + int i=0; + do { + text[i] = chunk->text[tPtr->tpos+i]; + } while(++i < chunk->chars-tPtr->tpos); + chunk->chars -= i; + newchunk = (tPtr->funcs.createTChunk) (text, i, chunk->font, + chunk->color, chunk->script, chunk->ul); + newchunk->next = chunk->next; + chunk->next = NULL; + /* might want to demalloc for LARGE cuts */ + //calcParaExtents(tPtr, para); + para->height = putParagraphOnPixmap(tPtr, para, True); + //putParagraphOnPixmap(tPtr, para, True); + } else if(tPtr->tpos >= chunk->chars) { + Chunk *prev; + WMSetTextCurrentChunk(tPtr, current-1); + prev = tPtr->currentChunk; + if(!prev) return; + newchunk = prev->next; + prev->next = NULL; + putParagraphOnPixmap(tPtr, para, True); + } + } else newchunk = NULL; + + if(para) /* the preceeding one */ + new_top = para->bottom; + + WMAppendTextStream(tPtr, "\n"); + para = tPtr->currentPara; + if(!para) return; + para->chunks = newchunk; + tPtr->currentChunk = newchunk; + tPtr->tpos = 0; + para->top = new_top; + calcParaExtents(tPtr, para); + height = para->height; + } else { + if(!para) { + WMAppendTextStream(tPtr, text); + para = tPtr->currentPara; + } else if(!para->chunks || !chunk) { + //WMPrependTextStream(tPtr, text); + WMAppendTextStream(tPtr, text); + } else if(chunk->type == ctImage) { + WMPrependTextStream(tPtr, text); + +printf("\n\nprepe\n\n"); + } else { + if(tPtr->tpos > chunk->chars) { +printf("\n\nmore\n\n"); + tPtr->tpos = chunk->chars; + } + + if(chunk->chars+1 >= chunk->mallocedSize) { + chunk->mallocedSize = reqBlockSize(chunk->chars+1); + chunk->text = wrealloc(chunk->text, chunk->mallocedSize); + } + + memmove(&(chunk->text[tPtr->tpos+1]), &chunk->text[tPtr->tpos], + chunk->chars-tPtr->tpos+1); + w = WMWidthOfString(font, text, 1); + memmove(&chunk->text[tPtr->tpos], text, 1); + chunk->chars++; + tPtr->tpos++; +//doc->clickstart.cursor.x += +//WMWidthOfString(chunk->fmt->font, text,len); + } + } + + + if(para) { + affectNextParas(tPtr, para, height); + putParagraphOnPixmap(tPtr, para, True); + drawDocumentPartsOnPixmap(tPtr, False); + updateScrollers(tPtr); +paintText(tPtr); + //cursorToTextPosition(tPtr, tPtr->clicked.x+w, tPtr->clicked.y); +//check for "sneppah tahw" with blank paras... +//paintText(tPtr); + } + +} + +static void +selectRegion(Text *tPtr, int x, int y) +{ + tPtr->sRect.pos.x = WMIN(tPtr->clicked.x, x); + tPtr->sRect.size.width = abs(tPtr->clicked.x-x); + tPtr->sRect.pos.y = WMIN(tPtr->clicked.y, y); + if(tPtr->sRect.pos.y<0) tPtr->sRect.pos.y=0; + tPtr->sRect.size.height = abs(tPtr->clicked.y-y); + +/* + while(y>tPtr->visibleH && tPtr->vpos < tPtr->docHeight-tPtr->visibleH) { + WMRefreshText(tPtr, tPtr->vpos+16, tPtr->hpos); + } +*/ +//printf("%d %d \n", y, tPtr->vpos); + +//foreach para in selection... + drawDocumentPartsOnPixmap(tPtr, True); + paintText(tPtr); +} + + + + + +#define WM_EMACSKEYMASK ControlMask +#define WM_EMACSKEY_LEFT XK_b +#define WM_EMACSKEY_RIGHT XK_f +#define WM_EMACSKEY_HOME XK_a +#define WM_EMACSKEY_END XK_e +#define WM_EMACSKEY_BS XK_h +#define WM_EMACSKEY_DEL XK_d + +static void +handleTextKeyPress(Text *tPtr, XEvent *event) +{ + char buffer[2]; + KeySym ksym; + int control_pressed = False; + + if(!tPtr->editable) return; + + if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK) + control_pressed = True; + buffer[XLookupString(&event->xkey, buffer, 1, &ksym, NULL)] = '\0'; + + switch(ksym) { + + case XK_Right: + case XK_Left: + if(tPtr->currentChunk) { + short w; + Chunk *chunk = tPtr->currentChunk; + if(chunk->type == ctText) { + WMFont *font = (tPtr->monoFont)?tPtr->dFont:chunk->font; + if(ksym==XK_Right) { + short pos = (tPtr->tposchars)?tPtr->tpos+1: + chunk->chars; + w = WMWidthOfString(font,&chunk->text[pos],1); + } else { + short pos = (tPtr->tpos>0)?tPtr->tpos-1:0; + w = WMWidthOfString(font,&chunk->text[pos],1); + } + } else { w = chunk->pixmap->width; } + if(ksym==XK_Right) w = -w; + cursorToTextPosition(tPtr, tPtr->clicked.x-w, tPtr->clicked.y); + } else { + if(ksym==XK_Right) ksym = XK_Down; + else ksym = XK_Up; + goto noCChunk; + } + break; + + case XK_Down: + case XK_Up: +noCChunk: { short h = tPtr->clheight-2; + if(ksym==XK_Down) h = -h; + cursorToTextPosition(tPtr, tPtr->clicked.x, tPtr->clicked.y-h); + } break; + + case XK_BackSpace: + deleteTextInteractively(tPtr, dtBackSpace); + break; + + case XK_Delete: + case XK_KP_Delete: + deleteTextInteractively(tPtr, dtDelete); + break; + + case XK_Return: + buffer[0] = '\n'; + default: + if(buffer[0] != '\0' && (buffer[0] == '\n' || !iscntrl(buffer[0]))) + insertTextInteractively(tPtr, buffer); + else if(control_pressed && ksym==XK_r) + {Bool i = !tPtr->rulerShown; WMShowTextRuler(tPtr, i); + tPtr->rulerShown = i; } + } + +} + + + +static void +pasteText(WMView *view, Atom selection, Atom target, Time timestamp, + void *cdata, WMData *data) +{ + Text *tPtr = (Text *)view->self; + char *str; + + + tPtr->waitingForSelection = False; + if(data) { + str = (char*)WMDataBytes(data); + if(tPtr->tpos<1) WMPrependTextStream(tPtr, str); + else WMAppendTextStream(tPtr, str); + WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos); + } else { + int n; + str = XFetchBuffer(tPtr->view->screen->display, &n, 0); + if(str) { + str[n] = 0; + if(tPtr->tpos<1) WMPrependTextStream(tPtr, str); + else WMAppendTextStream(tPtr, str); + XFree(str); + WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos); + } + } +} + + +static void +releaseSelection(Text *tPtr) +{ + Paragraph *para = tPtr->paragraphs; + Chunk *chunk; + while(para) { + chunk = para->chunks; + while(chunk) { + chunk->selected = False; + chunk = chunk->next; + } + para = para->next; + } + WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime); + tPtr->ownsSelection = False; + drawDocumentPartsOnPixmap(tPtr, True); + paintText(tPtr); +} + + +static WMData* +requestHandler(WMView *view, Atom selection, Atom target, + void *cdata, Atom *type) +{ + Text *tPtr = view->self; + int count; + 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(!tPtr->ownsSelection || !tPtr->paragraphs) return NULL; +//printf("got here\n"); + + if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) { +//for bleh in selection... + char *s = NULL; + Paragraph *para = tPtr->paragraphs; + Chunk *chunk = NULL; + char pixmap[] = "[pixmap]"; + Bool first=True; + short len; + + while(para) { + chunk = para->chunks; + while(chunk) { + + if(chunk->selected && chunk->type == ctText) { + len = chunk->chars; //chunk->sEnd - chunk->sStart; + if(len>0) { + s = wmalloc(len+1); + if(s) { + memcpy(s, &chunk->text[0*chunk->sStart], len); + s[len] = 0; + if(first) { + data = WMCreateDataWithBytes(s, strlen(s)); + first = False; + } else { +printf("append: %c %d\n", *s, strlen(s)); + WMAppendDataBytes(data, s, strlen(s)); + } + //gdbFree(s); + } } } +#if 0 +printf("len is %d [%d %d] %d \n", len, chunk->sStart, chunk->sEnd, +chunk->chars); +#endif + chunk = chunk->next; + } + para = para->next; + } + + if(data) { + WMSetDataFormat(data, 8); + *type = target; + } + return data; + } + +#if 0 + _TARGETS = XInternAtom(dpy, "TARGETS", False); + if (target == _TARGETS) { + Atom *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; + } +#endif + + return NULL; + +} + + +static void +lostHandler(WMView *view, Atom selection, void *cdata) +{ + WMText *tPtr = (WMText *)view->self; + releaseSelection(tPtr); +} + +static WMSelectionProcs selectionHandler = { + requestHandler, lostHandler, NULL }; + +static void +_notification(void *observerData, WMNotification *notification) +{ + WMText *to = (WMText *)observerData; + WMText *tw = (WMText *)WMGetNotificationClientData(notification); + if (to != tw) lostHandler(to->view, XA_PRIMARY, NULL); +} + +static void +handleTextEvents(XEvent *event, void *data) +{ + Text *tPtr = (Text *)data; + Display *dpy = event->xany.display; + + if(tPtr->waitingForSelection) return; + + switch (event->type) { + case KeyPress: + if(!tPtr->editable || tPtr->buttonHeld) { + XBell(dpy, 0); + return; + } + if(tPtr->ownsSelection) releaseSelection(tPtr); + //if (tPtr->waitingForSelection) return; + if(tPtr->focused) { +#if 0 + XGrabPointer(dpy, W_VIEW(tPtr)->window, False, + PointerMotionMask|ButtonPressMask|ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, None, + W_VIEW(tPtr)->screen->invisibleCursor, CurrentTime); + tPtr->pointerGrabbed = True; +#endif + handleTextKeyPress(tPtr, event); + } break; + + case MotionNotify: + if(tPtr->pointerGrabbed) { + tPtr->pointerGrabbed = False; + XUngrabPointer(dpy, CurrentTime); + } + if((event->xmotion.state & Button1Mask)) { + selectRegion(tPtr, event->xmotion.x, event->xmotion.y); + if(!tPtr->ownsSelection) { + WMCreateSelectionHandler(tPtr->view, XA_PRIMARY, + event->xbutton.time, &selectionHandler, NULL); + tPtr->ownsSelection = True; + } + break; + } + case ButtonPress: + if(event->xbutton.button == Button1) { + if(tPtr->ownsSelection) releaseSelection(tPtr); + cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y); + if (tPtr->pointerGrabbed) { + tPtr->pointerGrabbed = False; + XUngrabPointer(dpy, CurrentTime); + break; + } + } + if(!tPtr->focused) { + WMSetFocusToWidget(tPtr); + tPtr->focused = True; + break; + } + if(event->xbutton.button == 4) + WMScrollText(tPtr, -16); + else if(event->xbutton.button == 5) + WMScrollText(tPtr, 16); + + break; + + case ButtonRelease: + tPtr->buttonHeld = False; + if (tPtr->pointerGrabbed) { + tPtr->pointerGrabbed = False; + XUngrabPointer(dpy, CurrentTime); + break; + } + if(event->xbutton.button == 4 || event->xbutton.button == 5) + break; + if(event->xbutton.button == Button2 && tPtr->editable) { + char *text = NULL; + int n; + if(!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING, + event->xbutton.time, pasteText, NULL)) { + text = XFetchBuffer(tPtr->view->screen->display, &n, 0); + if(text) { + text[n] = 0; + WMAppendTextStream(tPtr, text); + XFree(text); + WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos); + } else tPtr->waitingForSelection = True; + } } break; + + } + +} + + +static void +handleNonTextEvents(XEvent *event, void *data) +{ + Text *tPtr = (Text *)data; + + switch(event->type) { + case Expose: + if(!event->xexpose.count && tPtr->view->flags.realized) + paintText(tPtr); + break; + + case FocusIn: + if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view) + return; + tPtr->focused = True; + //cursor...paintText(tPtr); + break; + + case FocusOut: + tPtr->focused = False; + //cursor...paintText(tPtr); + break; + + case DestroyNotify: + printf("destroy"); + //for(...)WMRemoveTextParagraph(tPtr, para); + break; + + } + +//printf("handleNonTextEvents\n"); +} + + + +static void +rulerCallBack(WMWidget *w, void *self) +{ + Text *tPtr = (Text *)self; + short which; + + if(tPtr->currentPara) { + Paragraph *para = tPtr->currentPara; + para->fmargin = WMGetRulerMargin(tPtr->ruler, WRulerFirst); + para->bmargin = WMGetRulerMargin(tPtr->ruler, WRulerBody); + para->rmargin = WMGetRulerMargin(tPtr->ruler, WRulerRight); + affectNextParas(tPtr, para, -23); + putParagraphOnPixmap(tPtr, para, True); + } +#if 0 + which = WMGetReleasedRulerMargin(tPtr->ruler); + if(which != WRulerDocLeft && which != WRulerRight + /* && Selection.para.count > 0 */ ) { + printf("" + "//for(i=0; iruler, WRulerRight); + + + if(WMGetGrabbedRulerMargin(tPtr->ruler) == WRulerLeft) { + short lmargin = WMGetRulerMargin(tPtr->ruler, WRulerDocLeft); + XClearArea(tPtr->view->screen->display, tPtr->view->window, + 22, 42, lmargin-21, tPtr->visibleH, True); + } else if(WMGetGrabbedRulerMargin(tPtr->ruler) == WRulerRight && + tPtr->docWidth+11 < rmargin) { + XClearArea(tPtr->view->screen->display, tPtr->view->window, + rmargin-3, 42, 10, tPtr->visibleH, True); + } + paintText(tPtr); +} + + + +/* ------------- non-static functions that are "friends" ------------- */ +/* ------------- called as (tPtr->funcs.foo)(bars...) ------------- */ + +/* create a new paragraph. Don't do anything with it just yet */ +//Paragraph * +void * +createParagraph(short fmargin, short bmargin, short rmargin, + short *tabstops, short numTabs, WMAlignment alignment) +{ + Paragraph *para = wmalloc(sizeof(Paragraph)); + if(!para) return NULL; + + para->chunks = NULL; + para->next = NULL; + + + para->fmargin = (fmargin>=0)?fmargin:0; + para->bmargin = (bmargin>=0)?bmargin:0; + if(rmargin-bmargin >= 100 && rmargin-fmargin >= 100) + para->rmargin = rmargin; + else + para->rmargin = 100; + para->tabstops = tabstops; + para->numTabs = (tabstops)?numTabs:0; + + para->drawbuffer = (Pixmap)NULL; + para->bulletPix = NULL; + para->top = para->bottom = 0; + para->width = para->height = 0; + + para->align = alignment; + + return para; +} + +/* insert the new paragraph in the tPtr, either right before + or after the currentPara. It's the responsibility of the + calling code to set what currentPara is. via WMSetTextCurrentParagraph. + If currentPara is not set, set it as the first in the document. + This function then sets currentPara as _this_ paragraph. + NOTE: this means careless parser implementors might lose previous + paragraphs... but this keeps stuff small and non-buggy :-) */ +void +insertParagraph(WMText *tPtr, void *v, InsertType type) +//insertParagraph(WMText *tPtr, Paragraph *para, InsertType type) +{ + Paragraph *tmp; + Paragraph *para = (Paragraph *)v; + if(!para || !tPtr) return; + + if(!tPtr->currentPara) { + tPtr->paragraphs = para; + } else { + tmp = tPtr->paragraphs; + if(type == itAppend) { + while(tmp->next && tmp != tPtr->currentPara) + tmp = tmp->next; + + para->next = tmp->next; + tmp->next = para; + } else { /* must be prepend */ + /* this "prior" member is that "doing things the hard way" + I spoke of. See? it's not too bad afterall... */ + Paragraph *prior = NULL; + while(tmp->next && tmp != tPtr->currentPara) { + prior = tmp; + tmp = tmp->next; + } + /* if this is the first */ + if(tmp == tPtr->paragraphs) { + para->next = tmp; + tPtr->paragraphs = para; + } else { + prior->next = para; + para->next = tmp; + } } } + tPtr->currentPara = para; + +} + +/* create a new chunk to contain exactly ONE pixmap */ +void * +//Chunk * +createPChunk(WMPixmap *pixmap, short script, ushort ul) +{ + Chunk *chunk; + + chunk = wmalloc(sizeof(Chunk)); + if(!chunk) + return NULL; + + chunk->text = NULL; + if(!pixmap) + chunk->pixmap = NULL; /* if it's NULL, we'll draw the "broken" pixmap... */ + else chunk->pixmap = WMRetainPixmap(pixmap); + chunk->chars = 0; + chunk->mallocedSize = 0; + chunk->type = ctImage; + chunk->font = NULL; + chunk->color = NULL; + chunk->script = script; + chunk->ul = ul; + chunk->selected = False; + chunk->next = NULL; + return chunk; +} + + +/* create a new chunk to contain some text with the given format */ +void * +//Chunk * +createTChunk(char *text, short chars, WMFont *font, + WMColor *color, short script, ushort ul) +{ + Chunk *chunk; + + if(!text || chars<0 || !font || !color) return NULL; + chunk = wmalloc(sizeof(Chunk)); + if(!chunk) return NULL; + + chunk->mallocedSize = reqBlockSize(chars); + chunk->text = (char *)wmalloc(chunk->mallocedSize); + memcpy(chunk->text, text, chars); + chunk->pixmap = NULL; + chunk->chars = chars; + chunk->type = ctText; + chunk->font = WMRetainFont(font); + chunk->color = WMRetainColor(color); + chunk->script = script; + chunk->ul = ul; + chunk->selected = False; + chunk->next = NULL; + + return chunk; +} + +/* insert the new chunk in the paragraph, either right before + or after the currentChunk. It's the responsibility of the + calling code to set what currentChunk is via WMSetTextCurrentChunk. + If currentChunk is not set, set it as the first in the existing + paragraph... if not even that, you lose... try again. + This function then sets currentChunk as _this_ chunk. + NOTE: this means careless parser implementors might lose previous + paragraphs/chunks... but this keeps stuff small and non-buggy :-) */ +void +insertChunk(WMText *tPtr, void *v, InsertType type) +{ + Chunk *tmp; + Chunk *chunk = (Chunk *)v; + + if(!tPtr || !chunk) return; + + if(!tPtr->paragraphs) { /* i.e., first chunk via insertTextInteractively */ + Paragraph *para = (tPtr->funcs.createParagraph) (0, 0, tPtr->visibleW, + NULL, 0, WALeft); + (tPtr->funcs.insertParagraph) (tPtr, para, itAppend); + } + + if(!tPtr->currentPara) + return; + if(!tPtr->currentChunk) { /* there is a current chunk */ + tPtr->currentPara->chunks = chunk; + } else if(!tPtr->currentPara->chunks) { + /* but it's not of this paragraph */ + tPtr->currentPara->chunks = chunk; + } else { + tmp = tPtr->currentPara->chunks; + + if(type == itAppend) { + while(tmp->next && tmp != tPtr->currentChunk) + tmp = tmp->next; + + chunk->next = tmp->next; + tmp->next = chunk; + + } else { /* must be prepend */ + /* this "prior" member is that "doing things the hard way" + I spoke of. See? it's not too bad afterall... */ + Chunk *prior = NULL; + while(tmp->next && tmp != tPtr->currentChunk) { + prior = tmp; + tmp = tmp->next; + } + /* if this is the first */ + if(tmp == tPtr->currentPara->chunks) { + chunk->next = tmp; + tPtr->currentPara->chunks = chunk; + } else { + prior->next = chunk; + chunk->next = tmp; + } } } + tPtr->currentChunk = chunk; + tPtr->tpos = chunk->chars; +} + + +/* ------------- non-static functions (i.e., APIs) ------------- */ +/* ------------- called as WMVerbText[Subject] ------------- */ + +#define DEFAULT_TEXT_WIDTH 250 +#define DEFAULT_TEXT_HEIGHT 200 + + + +WMText* +WMCreateText(WMWidget *parent) +{ + Text *tPtr = wmalloc(sizeof(Text)); + if(!tPtr) { + perror("could not create text widget\n"); + return NULL; + } + memset(tPtr, 0, sizeof(Text)); + tPtr->widgetClass = WC_Text; + tPtr->view = W_CreateView(W_VIEW(parent)); + if (!tPtr->view) { + perror("could not create text's view\n"); + wgdbFree(tPtr); + return NULL; + } + tPtr->view->self = tPtr; + tPtr->view->attribs.cursor = tPtr->view->screen->textCursor; + tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor; + W_ResizeView(tPtr->view, DEFAULT_TEXT_WIDTH, DEFAULT_TEXT_HEIGHT); + tPtr->bg = tPtr->view->screen->white; + W_SetViewBackgroundColor(tPtr->view, tPtr->bg); + + + tPtr->ruler = WMCreateRuler(tPtr); + (W_VIEW(tPtr->ruler))->attribs.cursor = tPtr->view->screen->defaultCursor; + (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor; + WMMoveWidget(tPtr->ruler, 0, 0); + WMResizeWidget(tPtr->ruler, W_VIEW(parent)->size.width, 40); + WMShowRulerTabs(tPtr->ruler, True); + WMSetRulerAction(tPtr->ruler, rulerCallBack, tPtr); + WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr); + + tPtr->vpos = 0; + tPtr->prevVpos = 0; + tPtr->vscroller = WMCreateScroller(tPtr); + (W_VIEW(tPtr->vscroller))->attribs.cursor = + tPtr->view->screen->defaultCursor; + (W_VIEW(tPtr->vscroller))->attribFlags |= CWOverrideRedirect | CWCursor; + WMMoveWidget(tPtr->vscroller, 1, 1); + WMResizeWidget(tPtr->vscroller, 20, tPtr->view->size.height - 2); + WMSetScrollerArrowsPosition(tPtr->vscroller, WSAMaxEnd); + WMSetScrollerAction(tPtr->vscroller, scrollersCallBack, tPtr); + + tPtr->hpos = 0; + tPtr->prevHpos = 0; + tPtr->hscroller = WMCreateScroller(tPtr); + (W_VIEW(tPtr->hscroller))->attribs.cursor = + tPtr->view->screen->defaultCursor; + (W_VIEW(tPtr->hscroller))->attribFlags |= CWOverrideRedirect | CWCursor; + WMMoveWidget(tPtr->hscroller, 1, tPtr->view->size.height-21); + WMResizeWidget(tPtr->hscroller, tPtr->view->size.width - 2, 20); + WMSetScrollerArrowsPosition(tPtr->hscroller, WSAMaxEnd); + WMSetScrollerAction(tPtr->hscroller, scrollersCallBack, tPtr); + + tPtr->visibleW = tPtr->view->size.width; + tPtr->visibleH = tPtr->view->size.height; + + tPtr->paragraphs = NULL; + tPtr->docWidth = 0; + tPtr->docHeight = 0; + tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen, + default_bullet); + tPtr->dUnknownImg = WMCreatePixmapFromXPMData(tPtr->view->screen, + unk_xpm); + + tPtr->sRect.pos.x = tPtr->sRect.pos.y = 0; + tPtr->sRect.size.width = tPtr->sRect.size.height = 0; + tPtr->currentPara = NULL; + tPtr->currentChunk = NULL; + tPtr->tpos = 0; + + tPtr->parser = NULL; + tPtr->writer = NULL; + tPtr->funcs.createParagraph = createParagraph; + tPtr->funcs.insertParagraph = insertParagraph; + tPtr->funcs.createPChunk = createPChunk; + tPtr->funcs.createTChunk = createTChunk; + tPtr->funcs.insertChunk = insertChunk; + + tPtr->clicked.x = tPtr->clicked.y = -23; + tPtr->cursor.x = tPtr->cursor.y = -23; + + tPtr->relief = WRSunken; + tPtr->wrapping = wrWord; + tPtr->editable = False; + tPtr->cursorShown = False; + tPtr->frozen = False; + tPtr->focused = False; + tPtr->pointerGrabbed = False; + tPtr->buttonHeld = False; + tPtr->ignoreNewLine = False; + tPtr->waitingForSelection = False; + tPtr->findingClickPoint = False; + tPtr->foundClickPoint = False; + tPtr->ownsSelection = False; + tPtr->clheight = 0; + tPtr->clwidth = 0; + + tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont); + tPtr->dColor = WMBlackColor(tPtr->view->screen); + + tPtr->view->delegate = &_TextViewDelegate; + WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask + |EnterWindowMask|LeaveWindowMask|FocusChangeMask, + handleNonTextEvents, tPtr); + WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask + |KeyReleaseMask|KeyPressMask|Button1MotionMask, + handleTextEvents, tPtr); + + WMAddNotificationObserver(_notification, tPtr, "_lostOwnership", tPtr); + + WMSetTextMonoFont(tPtr, True); + WMShowTextRuler(tPtr, False); + WMSetTextHasHorizontalScroller(tPtr, False); + WMSetTextHasVerticalScroller(tPtr, True); +//printf("the sizeof chunk is %d\n", sizeof(Chunk)); +//printf("the sizeof para is %d\n", sizeof(Paragraph)); +//printf("the sizeof text is %d\n", sizeof(Text)); + return tPtr; +} + +//WMSetTextBullet() +//WRetainPixmap(tPtr->dBulletPix); + +void +WMRemoveTextParagraph(WMText *tPtr, int which) +{ + Paragraph *prior, *removed; + if(!tPtr || which<0) return; + + WMSetTextCurrentParagraph(tPtr, which); + removed = tPtr->currentPara; + if(!removed) return; + if(removed->chunks)printf("WMRemoveTextChunks\n"); + if(removed == tPtr->paragraphs || which==0) { + tPtr->paragraphs = removed->next; + } else { + WMSetTextCurrentParagraph(tPtr, which-1); + prior = tPtr->currentPara; + if(!prior) return; + prior->next = removed->next; + } + wgdbFree(removed); +// removeChunks + removed = NULL; +} + + + +/* set what is known as the currentPara in the tPtr. */ +/* negative number means: "gib me last chunk" */ +void +WMSetTextCurrentParagraph(WMText *tPtr, int current) +{ + Paragraph *tmp; + int i=0; + + if(!tPtr || current<0) return; + if(current == 0) { + tPtr->currentPara = tPtr->paragraphs; + return; + } + tmp = tPtr->paragraphs; + while(tmp->next && ((current==-23)?1:i++next; + } tPtr->currentPara = tmp; + //? want to do this?if(tmp) tPtr->currentChunk = tmp +} + + +int +WMGetTextParagraphs(WMText *tPtr) +{ + int current=0; + Paragraph *tmp; + if(!tPtr) return 0; + tmp = tPtr->paragraphs; + while(tmp) { + tmp = tmp->next; + current++; + } return current; +} + + + +int +WMGetTextCurrentParagraph(WMText *tPtr) +{ + int current=-1; + Paragraph *tmp; + + if(!tPtr) return current; + if(!tPtr->currentPara) return current; + if(!tPtr->paragraphs) return current; + tmp = tPtr->paragraphs; + while(tmp) { + current++; + if(tmp == tPtr->currentPara) + break; + tmp = tmp->next; + } return current; +} + +/* set what is known as the currentChunk within the currently +selected currentPara (or the first paragraph in the document). */ +void +WMSetTextCurrentChunk(WMText *tPtr, int current) +{ + Chunk *tmp; + int i=0; + + if(!tPtr) return; + tPtr->currentChunk = NULL; + if(!tPtr->currentPara) { + tPtr->currentPara = tPtr->paragraphs; + if(!tPtr->currentPara) + return; + } + + if(current == 0) { + tPtr->currentChunk = tPtr->currentPara->chunks; + return; + } + tmp = tPtr->currentPara->chunks; + if(tmp) { + while(tmp->next && ((current<0)?1:i++next; + } tPtr->currentChunk = tmp; +} + + +void +WMRemoveTextChunk(WMText *tPtr, int which) +{ + Chunk *prior, *removed; + Paragraph *para; + if(!tPtr || which<0) return; + para = tPtr->currentPara; + if(!para) return; + + WMSetTextCurrentChunk(tPtr, which); + removed = tPtr->currentChunk; + if(!removed) return; + if(removed == tPtr->currentPara->chunks || which==0) { + para->chunks = removed->next; + } else { + WMSetTextCurrentChunk(tPtr, which-1); + prior = tPtr->currentChunk; + if(!prior) return; + prior->next = removed->next; + } + if(removed->type == ctText) { + wgdbFree(removed->text); + WMReleaseFont(removed->font); + WMReleaseColor(removed->color); + } else { + WMReleasePixmap(removed->pixmap); + } + wgdbFree(removed); + removed = NULL; +} + +int +WMGetTextCurrentChunk(WMText *tPtr) +{ + int current=0; + Chunk *tmp; + + if(!tPtr) return 0; + if(!tPtr->currentChunk) return 0; + if(!tPtr->currentPara) { + tPtr->currentPara = tPtr->paragraphs; + if(!tPtr->currentPara) + return 0; + } + + tmp = tPtr->currentPara->chunks; + while(tmp) { + if(tmp == tPtr->currentChunk) + break; + tmp = tmp->next; + current++; + } + return current; +} + +int +WMGetTextChunks(WMText *tPtr) +{ + short current=0; + Chunk *tmp; + if(!tPtr || !tPtr->currentPara) return 0; + tmp = tPtr->currentPara->chunks; + while(tmp) { + tmp = tmp->next; + current++; + } return current; +} + +void +WMShowTextRuler(WMText *tPtr, Bool show) +{ + if(!tPtr) return; + if(tPtr->monoFont) show = False; + + tPtr->rulerShown = show; + if(show) WMMapWidget(tPtr->ruler); + else WMUnmapWidget(tPtr->ruler); + resizeText(tPtr->view->delegate, tPtr->view); +} + +Bool +WMGetTextRulerShown(WMText *tPtr) +{ + if(!tPtr) return False; + return tPtr->rulerShown; +} + +void +WMSetTextRulerMargin(WMText *tPtr, char which, short pixels) +{ + if(!tPtr) return; + if(tPtr->monoFont) return; + WMSetRulerMargin(tPtr->ruler, which, pixels); + WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos); +} + +short +WMGetTextRulerMargin(WMText *tPtr, char which) +{ + if(!tPtr) return 0; + if(tPtr->monoFont) + return 0; + return WMGetRulerMargin(tPtr->ruler, which); +} + + +void +WMShowTextRulerTabs(WMText *tPtr, Bool show) +{ + if(!tPtr) return; + if(tPtr->monoFont) return; + WMShowRulerTabs(tPtr->ruler, show); +} + +void +WMSetTextMonoFont(WMText *tPtr, Bool mono) +{ + if(!tPtr) return; + if(mono && tPtr->rulerShown) + WMShowTextRuler(tPtr, False); + + tPtr->monoFont = mono; +} + +Bool +WMGetTextMonoFont(WMText *tPtr) +{ + if(!tPtr) return True; + return tPtr->monoFont; +} + +void +WMForceTextFocus(WMText *tPtr) +{ + if(!tPtr) return; + + if(tPtr->clicked.x == -23 || tPtr->clicked.y == 23) + cursorToTextPosition(tPtr, 100, 100); /* anyplace */ + else + cursorToTextPosition(tPtr, tPtr->clicked.x, tPtr->clicked.y); +} + + +void +WMSetTextEditable(WMText *tPtr, Bool editable) +{ + if(!tPtr) return; + tPtr->editable = editable; +} + + +Bool +WMGetTextEditable(WMText *tPtr) +{ + if(!tPtr) return 0; + return tPtr->editable; +} + + +Bool +WMScrollText(WMText *tPtr, int amount) +{ + Bool scroll=False; + if(amount == 0 || !tPtr) return; + if(!tPtr->view->flags.realized) return; + + if(amount < 0) { + if(tPtr->vpos > 0) { + if(tPtr->vpos > amount) tPtr->vpos += amount; + else tPtr->vpos=0; + scroll=True; + } } else { + int limit = tPtr->docHeight - tPtr->visibleH; + 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); + drawDocumentPartsOnPixmap(tPtr, False); + paintText(tPtr); + } + tPtr->prevVpos = tPtr->vpos; + return scroll; +} + +Bool +WMPageText(WMText *tPtr, Bool scrollUp) +{ + if(!tPtr) return; + if(!tPtr->view->flags.realized) return; + + return WMScrollText(tPtr, scrollUp + ? tPtr->visibleH:-tPtr->visibleH); +} + +void +WMIgnoreTextNewline(WMText *tPtr, Bool ignore) +{ + if(!tPtr) return; + tPtr->ignoreNewLine = ignore; +} + + +void +WMSetTextHasHorizontalScroller(WMText *tPtr, Bool flag) +{ + if(tPtr) { + short rh; + if(tPtr->monoFont) + return; + rh = tPtr->rulerShown?40:0; + tPtr->hasHscroller = flag; + if(flag) { + WMMapWidget(tPtr->hscroller); + tPtr->visibleH = tPtr->view->size.height-rh-22; + } else { + WMUnmapWidget(tPtr->hscroller); + tPtr->visibleH = tPtr->view->size.height-rh; + } + resizeText(tPtr->view->delegate, tPtr->view); + } +} + + +void +WMSetTextHasVerticalScroller(WMText *tPtr, Bool flag) +{ + if(tPtr) { + tPtr->hasVscroller = flag; + if(flag) { + WMMapWidget(tPtr->vscroller); + tPtr->visibleW = tPtr->view->size.width-22; + WMSetRulerOffset(tPtr->ruler, 22); /* scrollbar width + 2 */ + } else { + WMUnmapWidget(tPtr->vscroller); + tPtr->visibleW = tPtr->view->size.width; + WMSetRulerOffset(tPtr->ruler, 2); + } + resizeText(tPtr->view->delegate, tPtr->view); + } +} + + + +void +WMRefreshText(WMText *tPtr, int vpos, int hpos) +{ + + if(!tPtr) + return; + + if(tPtr->frozen || !tPtr->view->flags.realized) + return; + + + XClearArea(tPtr->view->screen->display, tPtr->view->window, + 22, (tPtr->rulerShown)?45:5, + tPtr->visibleW, tPtr->visibleH, True); + + calcDocExtents(tPtr); +/* +printf("vpos:%d tPtr->docHeight%d tPtr->visibleH%d \n", +vpos, tPtr->docHeight, tPtr->visibleH); +*/ + + // tPtr->vpos = vpos; +/* + if(vpos < 0 || tPtr->docHeight < tPtr->visibleH) + tPtr->vpos = 0; + else if(vpos-tPtr->visibleH>tPtr->docHeight) + tPtr->vpos = vpos-tPtr->docHeight-tPtr->visibleH-tPtr->docHeight; + else + tPtr->vpos = tPtr->docHeight-tPtr->visibleH; +*/ + + + if(hpos < 0 || hpos > tPtr->docWidth) + tPtr->hpos = 0; + else + tPtr->hpos = hpos; + + drawDocumentPartsOnPixmap(tPtr, True); + updateScrollers(tPtr); + paintText(tPtr); +} + +/* would be nice to have in WINGs proper... */ +static void +changeFontProp(char *fname, char *newprop, short which) +{ + char before[128], prop[128], after[128]; + char *ptr, *bptr; + int part=0; + + if(!fname || !prop) + return; + + ptr = fname; + bptr = before; + while (*ptr) { + if(*ptr == '-') { + *bptr = 0; + if(part==which) bptr = prop; + else if(part==which+1) bptr = after; + *bptr++ = *ptr; + part++; + } else { + *bptr++ = *ptr; + } ptr++; + }*bptr = 0; + snprintf(fname, 255, "%s-%s%s", before, newprop, after); +} + +/* TODO: put in wfont? */ +WMFont * +WMGetFontPlain(WMScreen *scrPtr, WMFont *font) +{ + WMFont *nfont=NULL; + if(!scrPtr|| !font) + return NULL; + return font; + +} + +WMFont * +WMGetFontBold(WMScreen *scrPtr, WMFont *font) +{ + WMFont *newfont=NULL; + char fname[256]; + if(!scrPtr || !font) + return NULL; + snprintf(fname, 255, font->name); + changeFontProp(fname, "bold", 2); + newfont = WMCreateNormalFont(scrPtr, fname); + if(!newfont) + newfont = font; + return newfont; +} + +WMFont * +WMGetFontItalic(WMScreen *scrPtr, WMFont *font) +{ + WMFont *newfont=NULL; + char fname[256]; + if(!scrPtr || !font) + return NULL; + snprintf(fname, 255, font->name); + changeFontProp(fname, "o", 3); + newfont = WMCreateNormalFont(scrPtr, fname); + if(!newfont) + newfont = font; + return newfont; +} + +WMFont * +WMGetFontOfSize(WMScreen *scrPtr, WMFont *font, short size) +{ + WMFont *nfont=NULL; + if(!scrPtr || !font || size<1) + return NULL; + return font; +} +/* */ + +void +WMFreezeText(WMText *tPtr) +{ + if(!tPtr) + return; + tPtr->frozen = True; +} + +void +WMThawText(WMText *tPtr) +{ + if(!tPtr) + return; + tPtr->frozen = False; +} + + +void +WMSetTextDefaultAlignment(WMText *tPtr, WMAlignment alignment) +{ + if(!tPtr) return; + if(tPtr->monoFont) return; + + tPtr->dAlignment = alignment; + WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos); +} + + + +void +WMSetTextBackgroundColor(WMText *tPtr, WMColor *color) +{ + if(!tPtr) + return; + + if(color) + tPtr->bg = color; + else + tPtr->bg = WMWhiteColor(tPtr->view->screen); + + W_SetViewBackgroundColor(tPtr->view, tPtr->bg); + WMRefreshText(tPtr, tPtr->vpos, tPtr->hpos); +} + +void +WMSetTextDefaultColor(WMText *tPtr, WMColor *color) +{ + if(!tPtr) + return; + + if(color) + tPtr->dColor = color; + else + tPtr->dColor = WMBlackColor(tPtr->view->screen); +} + +void +WMSetTextDefaultFont(WMText *tPtr, WMFont *font) +{ + if(!tPtr) + return; + + if(font) + tPtr->dFont = font; + else + tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont); +} + +void +WMSetTextUseFixedPitchFont(Text *tPtr, Bool fixed) +{ + if(!tPtr) + return; + if(fixed) + tPtr->dFont = WMCreateFontSet(tPtr->view->screen, + "lucidasanstypewriter-12"); + else + tPtr->dFont = WMRetainFont(tPtr->view->screen->normalFont); + tPtr->fixedPitch = fixed; +} + +void +WMSetTextParser(WMText *tPtr, WMParseAction *parser) +{ + if(!tPtr) return; + if(tPtr->monoFont) return; + tPtr->parser = parser; +} + + +WMParserActions +WMGetTextParserActions(WMText *tPtr) +{ + WMParserActions null; + if(!tPtr) return null; + return tPtr->funcs; +} + + +char * +WMGetTextAll(WMText *tPtr) +{ + char *text; + int length=0; + int where=0; + Paragraph *para; + Chunk *chunk; + + if(!tPtr) return NULL; + + para = tPtr->paragraphs; + while(para) { + chunk = para->chunks; + while(chunk) { + if(chunk->type == ctText) { + if(chunk->text) length += chunk->chars; + } else { + printf("getting image \n"); + } + chunk = chunk->next; + } + + if(tPtr->ignoreNewLine) break; + length += 4; // newlines + para = para->next; + } + + text = wmalloc(length+1); + + para = tPtr->paragraphs; + while(para) { + chunk = para->chunks; + while(chunk) { + if(chunk->type == ctText) { + if(chunk->text) { + snprintf(&text[where], chunk->chars+1, "%s", chunk->text); + where += chunk->chars; + } + } else { + printf("writing image \n"); + } + chunk = chunk->next; + } + if(tPtr->ignoreNewLine) break; + snprintf(&text[where++], 2, "\n"); + para = para->next; + } text[where] = '\0'; + + return text; +} + +void +WMSetTextWriter(WMText *tPtr, WMParseAction *writer) +{ + if(!tPtr) + return; + if(tPtr->monoFont) + return; + tPtr->writer = writer; +} +