mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-21 13:28:05 +01:00
2821 lines
85 KiB
C
2821 lines
85 KiB
C
/* WMText: multi-line/font/color text widget for WINGs */
|
|
/* Copyleft (>) 1999, 2000 Nwanua Elumeze <nwanua@colorado.edu> */
|
|
|
|
|
|
/* .( * .
|
|
.* . ) .
|
|
|
|
. . POOF .* .
|
|
'* . ( .) '
|
|
jgs ` ( . * */
|
|
|
|
|
|
/* if monoFont, ignore pixmaps, colors, fonts, script, underline */
|
|
|
|
|
|
#include <WMaker.h>
|
|
#include <WINGsP.h>
|
|
#include <X11/keysym.h>
|
|
#include <X11/Xatom.h>
|
|
#include <ctype.h>
|
|
|
|
|
|
typedef enum {
|
|
ctText=0,
|
|
ctImage=1
|
|
} ChunkType;
|
|
|
|
typedef enum {
|
|
dtDelete=0,
|
|
dtBackSpace
|
|
} DeleteType;
|
|
|
|
typedef enum {
|
|
wrWord=0,
|
|
wrChar=1,
|
|
wrNone=2
|
|
} Wrapping;
|
|
|
|
|
|
void wgdbFree(void *ptr)
|
|
{
|
|
if(!ptr) printf("err... cannot ");
|
|
printf("gdbFree [%p]\n", ptr);
|
|
wfree(ptr);
|
|
}
|
|
|
|
|
|
/* Why single-linked and not say double-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 single-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:3; /* 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; i<nitems; i++) {
|
|
if(items[i].type == ctText) {
|
|
font = (tPtr->monoFont)?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; i<nitems; i++) {
|
|
|
|
//account for vpos
|
|
if(tPtr->ownsSelection) {
|
|
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; u<ul_thick; u++) {
|
|
XDrawLine(screen->display, 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<len; J++) {
|
|
tmp[j] = bufr[J];
|
|
if(WMWidthOfString(font, tmp, j+1) > 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->height<tPtr->dFont->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->height<tPtr->dFont->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-vS<WMIN(para->fmargin, 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->vpos<limit-16) tPtr->vpos+=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, Bool prepend)
|
|
{
|
|
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, prepend);
|
|
else
|
|
defaultParser(tPtr, data, prepend);
|
|
}
|
|
|
|
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 && x<tPtr->clicked.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_width<tPtr->clwidth) {
|
|
if(chunk->type == ctText) {
|
|
font = (tPtr->monoFont)?tPtr->dFont:chunk->font;
|
|
for (w=start; w<chunk->chars; 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->tpos<chunk->chars)?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; i<Selection.para.count; i++) {"
|
|
"affect"
|
|
"//calcParaExtents(tPtr, para);}\n");
|
|
} else {
|
|
WMRefreshText(tPtr, 0, 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
static void
|
|
rulerMoveCallBack(WMWidget *w, void *self)
|
|
{
|
|
Text *tPtr = (Text *)self;
|
|
short rmargin = WMGetRulerMargin(tPtr->ruler, 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, Bool prepend)
|
|
//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(!prepend) {
|
|
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, Bool prepend)
|
|
{
|
|
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, False);
|
|
}
|
|
|
|
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(!prepend) {
|
|
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++<current)) {
|
|
//while(tmp && i++<current) {
|
|
tmp = tmp->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++<current))
|
|
tmp = tmp->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;
|
|
}
|
|
|