419 lines
11 KiB
C
419 lines
11 KiB
C
/* This file is part of the Project Athena Zephyr Notification System.
|
|
* It is one of the source files comprising zwgc, the Zephyr WindowGram
|
|
* client.
|
|
*
|
|
* Created by: Marc Horowitz <marc@athena.mit.edu>
|
|
*
|
|
* $Id$
|
|
*
|
|
* Copyright (c) 1989 by the Massachusetts Institute of Technology.
|
|
* For copying and distribution information, see the file
|
|
* "mit-copyright.h".
|
|
*/
|
|
|
|
#if (!defined(lint) && !defined(SABER))
|
|
static const char rcsid_xmark_c[] = "$Id$";
|
|
#endif
|
|
|
|
#include <zephyr/mit-copyright.h>
|
|
|
|
#include <sysdep.h>
|
|
|
|
#ifndef X_DISPLAY_MISSING
|
|
|
|
#include <X11/X.h>
|
|
#include <X11/Xlib.h>
|
|
#include <zephyr/zephyr.h>
|
|
#include "X_gram.h"
|
|
#include "X_fonts.h"
|
|
#include "xmark.h"
|
|
#include "new_string.h"
|
|
|
|
int markblock[3] = { -1 , -1 , -1 };
|
|
int markchar[3] = { -1 , -1 , -1 };
|
|
int markpixel[3] = { -1 , -1 , -1 };
|
|
x_gram *markgram = NULL;
|
|
|
|
int oldblock[2] = { -1 , -1 };
|
|
int oldpixel[2] = { -1 , -1 };
|
|
x_gram *oldgram = NULL;
|
|
|
|
#define xmarkValid() \
|
|
((markgram) && \
|
|
(STARTBLOCK != -1) && (ENDBLOCK != -1) && \
|
|
(STARTCHAR != -1) && (ENDCHAR != -1) && \
|
|
(STARTPIXEL != -1) && (ENDPIXEL != -1))
|
|
|
|
void
|
|
xmarkSetBound(x_gram *gram,
|
|
int x,
|
|
int y,
|
|
int which)
|
|
{
|
|
int i, xofs, yofs;
|
|
XFontSet font;
|
|
xblock *xb;
|
|
int num_chars;
|
|
XRectangle *ink, *logical;
|
|
|
|
#ifdef MARK_DEBUG
|
|
#define RETURN \
|
|
if ((oldblock[which] != markblock[which]) || \
|
|
(oldpixel[which] != markpixel[which])) { \
|
|
printf("----- SetBound:\noldblock[%d]=%d, oldpixel[%d]=%d\nmarkblock[%d]=%d, markpixel[%d]=%d\n-----", \
|
|
which,oldblock[which],which,oldpixel[which], \
|
|
which,markblock[which],which,markpixel[which]); \
|
|
} \
|
|
return
|
|
#else
|
|
#define RETURN return
|
|
#endif
|
|
|
|
if (markgram != gram) {
|
|
xmarkClear();
|
|
markgram = gram;
|
|
} else if (which < XMARK_TEMP_BOUND) {
|
|
oldblock[which]=markblock[which];
|
|
oldpixel[which]=markpixel[which];
|
|
}
|
|
|
|
/* Start at the top, fastforward to first span not too high. */
|
|
for (i=0,xb=gram->blocks ;
|
|
(i<gram->numblocks) && (xb->y2 < y) ;
|
|
i++,xb++) ;
|
|
|
|
/* the point is after the end */
|
|
if (i==gram->numblocks) {
|
|
markblock[which]=i;
|
|
markchar[which]=0;
|
|
markpixel[which]=0;
|
|
RETURN;
|
|
}
|
|
|
|
/* is the point before the beginning of the line? */
|
|
if (x <= xb->x1) {
|
|
markblock[which]=i;
|
|
markchar[which]=0;
|
|
markpixel[which]=0;
|
|
RETURN;
|
|
}
|
|
|
|
/* is the point in the nether space between this line and the last? */
|
|
if (y < xb->y1) {
|
|
markblock[which]=i;
|
|
markchar[which]=0;
|
|
markpixel[which]=0;
|
|
RETURN;
|
|
}
|
|
|
|
for (yofs=xb->y1;(i<gram->numblocks) && (xb->y1 == yofs);i++,xb++) {
|
|
|
|
if (x <= xb->x2) {
|
|
markblock[which]=i;
|
|
|
|
xofs=xb->x1;
|
|
if ((x < xofs) || (y < xb->y1)) {
|
|
markchar[which]=0;
|
|
RETURN;
|
|
}
|
|
font = xb->font;
|
|
#ifdef X_HAVE_UTF8_STRING
|
|
Xutf8TextPerCharExtents(font, xb->wstr, xb->wlen,
|
|
NULL, NULL, -1, &num_chars, NULL, NULL);
|
|
#else
|
|
XwcTextPerCharExtents(font, (wchar_t *)xb->wstr, xb->wlen,
|
|
NULL, NULL, -1, &num_chars, NULL, NULL);
|
|
#endif
|
|
ink = malloc(num_chars * sizeof(XRectangle));
|
|
logical = malloc(num_chars * sizeof(XRectangle));
|
|
#ifdef X_HAVE_UTF8_STRING
|
|
Xutf8TextPerCharExtents(font, xb->wstr, xb->wlen,
|
|
ink, logical, num_chars, &num_chars, NULL, NULL);
|
|
#else
|
|
XwcTextPerCharExtents(font, (wchar_t *)xb->wstr, xb->wlen,
|
|
ink, logical, num_chars, &num_chars, NULL, NULL);
|
|
#endif
|
|
for (i=0;i<num_chars;i++) {
|
|
if (x <= xofs + logical[i].x + logical[i].width) {
|
|
markchar[which]=i;
|
|
markpixel[which]=xofs + logical[i].x - xb->x1;
|
|
free(ink);
|
|
free(logical);
|
|
RETURN;
|
|
}
|
|
}
|
|
free(ink);
|
|
free(logical);
|
|
}
|
|
}
|
|
|
|
/* The endpoint is after the end of the block if the loop ends */
|
|
markblock[which]=i;
|
|
markchar[which]=0;
|
|
markpixel[which]=0;
|
|
RETURN;
|
|
}
|
|
|
|
/* needs both bounds to be valid (!= -1) */
|
|
static int
|
|
xmarkNearest(int x,
|
|
int y)
|
|
{
|
|
int middle;
|
|
|
|
xmarkSetBound(markgram,x,y,XMARK_TEMP_BOUND);
|
|
middle=(ENDBLOCK+STARTBLOCK)/2;
|
|
|
|
if (markblock[XMARK_TEMP_BOUND] < middle)
|
|
return(XMARK_START_BOUND);
|
|
else if (markblock[XMARK_TEMP_BOUND] > middle)
|
|
return(XMARK_END_BOUND);
|
|
else {
|
|
middle=(ENDCHAR+STARTCHAR)/2;
|
|
if (markchar[XMARK_TEMP_BOUND] < middle)
|
|
return(XMARK_START_BOUND);
|
|
else
|
|
return(XMARK_END_BOUND);
|
|
}
|
|
}
|
|
|
|
static void
|
|
xmarkExpose(Display *dpy,
|
|
Window w,
|
|
x_gram *gram,
|
|
unsigned int b1,
|
|
unsigned int p1,
|
|
unsigned int b2,
|
|
unsigned int p2)
|
|
{
|
|
#define swap(x,y) temp=(x); (x)=(y); (y)=temp
|
|
unsigned int i,temp;
|
|
XEvent event;
|
|
#define expose (event.xexpose)
|
|
|
|
if (((int)b1==-1) || ((int)p1==-1) || ((int)b2==-1) || ((int)p2==-1))
|
|
return;
|
|
|
|
if ((b1 > b2) || ((b1 == b2) && (p1 > p2))) {
|
|
swap(b1,b2);
|
|
swap(p1,p2);
|
|
}
|
|
|
|
#if defined(_IBMR2) && !defined(__GNUC__) && defined(RS6000_OPT_BUG)
|
|
/* A version of the AIX 3.1 RS/6000 C compiler needs this to prevent
|
|
a core dump in the loop below. */
|
|
&b1;
|
|
#endif
|
|
|
|
expose.type=Expose;
|
|
expose.display=dpy;
|
|
expose.window=w;
|
|
|
|
for (i=b1;i<=b2;i++) {
|
|
if (b1==b2) {
|
|
expose.x=gram->blocks[i].x1+p1;
|
|
expose.y=gram->blocks[i].y1;
|
|
expose.width=p2-p1;
|
|
expose.height=gram->blocks[i].y2-gram->blocks[i].y1;
|
|
expose.count=0;
|
|
} else if (i==b1) {
|
|
expose.x=gram->blocks[i].x1+p1;
|
|
expose.y=gram->blocks[i].y1;
|
|
expose.width=gram->blocks[i].x2-p1;
|
|
expose.height=gram->blocks[i].y2-gram->blocks[i].y1;
|
|
expose.count=b2-i;
|
|
} else if (i==b2) {
|
|
expose.x=gram->blocks[i].x1;
|
|
expose.y=gram->blocks[i].y1;
|
|
expose.width=p2;
|
|
expose.height=gram->blocks[i].y2-gram->blocks[i].y1;
|
|
expose.count=b2-i;
|
|
} else {
|
|
expose.x=gram->blocks[i].x1;
|
|
expose.y=gram->blocks[i].y1;
|
|
expose.width=gram->blocks[i].x2-gram->blocks[i].x1;
|
|
expose.height=gram->blocks[i].y2-gram->blocks[i].y1;
|
|
expose.count=b2-i;
|
|
}
|
|
|
|
#ifdef MARK_DEBUG
|
|
if (expose.width && expose.height) {
|
|
printf("---- markExpose:\nb1=%d p1=%d b2=%d p2=%d\n",b1,p1,b2,p2);
|
|
printf("x=%d y=%d w=%d h=%d\n-----",
|
|
expose.x,expose.y,expose.width,expose.height);
|
|
}
|
|
#endif
|
|
if ((expose.width && expose.height) || (expose.count == 0))
|
|
XSendEvent(dpy,w,True,ExposureMask,&event);
|
|
}
|
|
}
|
|
|
|
/* Public functions: */
|
|
|
|
void
|
|
xmarkRedraw(Display *dpy,
|
|
Window w,
|
|
x_gram *gram,
|
|
int range)
|
|
{
|
|
#define ob1 ((unsigned int) oldblock[XMARK_START_BOUND])
|
|
#define ob2 ((unsigned int) oldblock[XMARK_END_BOUND])
|
|
#define nb1 ((unsigned int) markblock[XMARK_START_BOUND])
|
|
#define nb2 ((unsigned int) markblock[XMARK_END_BOUND])
|
|
#define op1 ((unsigned int) oldpixel[XMARK_START_BOUND])
|
|
#define op2 ((unsigned int) oldpixel[XMARK_END_BOUND])
|
|
#define np1 ((unsigned int) markpixel[XMARK_START_BOUND])
|
|
#define np2 ((unsigned int) markpixel[XMARK_END_BOUND])
|
|
|
|
if (range==XMARK_REDRAW_CURRENT) {
|
|
if (!markgram) return;
|
|
xmarkExpose(dpy,w,gram,nb1,np1,nb2,np2);
|
|
} else if (range==XMARK_REDRAW_OLD) {
|
|
if (!oldgram) return;
|
|
xmarkExpose(dpy,w,gram,ob1,op1,ob2,op2);
|
|
} else if (range==XMARK_REDRAW_START) {
|
|
if (!markgram) return;
|
|
xmarkExpose(dpy,w,gram,ob1,op1,nb1,np1);
|
|
} else if (range==XMARK_REDRAW_END) {
|
|
if (!markgram) return;
|
|
xmarkExpose(dpy,w,gram,ob2,op2,nb2,np2);
|
|
}
|
|
#ifdef DEBUG
|
|
else {
|
|
printf("xmarkRedraw: This shouldn't happen!\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* needs both bounds to be valid (!= -1) */
|
|
int
|
|
xmarkSecond(void)
|
|
{
|
|
if (STARTBLOCK > ENDBLOCK)
|
|
return(XMARK_START_BOUND);
|
|
else if (STARTBLOCK < ENDBLOCK)
|
|
return(XMARK_END_BOUND);
|
|
else {
|
|
if (STARTCHAR > ENDCHAR)
|
|
return(XMARK_START_BOUND);
|
|
else if (STARTCHAR < ENDCHAR)
|
|
return(XMARK_END_BOUND);
|
|
else
|
|
return(XMARK_END_BOUND);
|
|
}
|
|
}
|
|
|
|
void
|
|
xmarkClear(void)
|
|
{
|
|
oldblock[0]=markblock[0];
|
|
oldblock[1]=markblock[1];
|
|
oldpixel[0]=markpixel[0];
|
|
oldpixel[1]=markpixel[1];
|
|
oldgram=markgram;
|
|
|
|
markblock[0] = -1;
|
|
markblock[1] = -1;
|
|
markchar[0] = -1;
|
|
markchar[1] = -1;
|
|
markpixel[0] = -1;
|
|
markpixel[1] = -1;
|
|
markgram=NULL;
|
|
}
|
|
|
|
int
|
|
xmarkExtendFromFirst(x_gram *gram,
|
|
int x,
|
|
int y)
|
|
{
|
|
if (markgram != gram) {
|
|
xmarkClear();
|
|
markgram = gram;
|
|
}
|
|
|
|
if (STARTBLOCK == -1) {
|
|
xmarkStart(gram,x,y);
|
|
xmarkEnd(gram,x,y);
|
|
return(XMARK_REDRAW_CURRENT);
|
|
} else if (ENDBLOCK == -1) {
|
|
xmarkEnd(gram,x,y);
|
|
return(XMARK_REDRAW_CURRENT);
|
|
} else {
|
|
xmarkSetBound(gram,x,y,XMARK_END_BOUND);
|
|
return(XMARK_REDRAW_END);
|
|
}
|
|
}
|
|
|
|
int
|
|
xmarkExtendFromNearest(x_gram *gram,
|
|
int x,
|
|
int y)
|
|
{
|
|
int bound;
|
|
|
|
if (markgram != gram) {
|
|
xmarkClear();
|
|
markgram = gram;
|
|
}
|
|
|
|
if (STARTBLOCK == -1) {
|
|
xmarkStart(gram,x,y);
|
|
xmarkEnd(gram,x,y);
|
|
return(XMARK_REDRAW_CURRENT);
|
|
} else if (ENDBLOCK == -1) {
|
|
xmarkEnd(gram,x,y);
|
|
return(XMARK_REDRAW_CURRENT);
|
|
} else {
|
|
xmarkSetBound(gram,x,y,bound=xmarkNearest(x,y));
|
|
return(bound==XMARK_START_BOUND?XMARK_REDRAW_START:XMARK_REDRAW_END);
|
|
}
|
|
}
|
|
|
|
char *
|
|
xmarkGetText(void)
|
|
{
|
|
int i, idx, len;
|
|
int last_y = -1;
|
|
string temp;
|
|
string text_so_far = string_Copy("");
|
|
char *text = markgram->text;
|
|
int startblock,endblock,startchar,endchar;
|
|
|
|
if (xmarkValid()) {
|
|
if (xmarkSecond() == XMARK_END_BOUND) {
|
|
startblock=STARTBLOCK;
|
|
endblock=ENDBLOCK;
|
|
startchar=STARTCHAR;
|
|
endchar=ENDCHAR;
|
|
} else {
|
|
startblock=ENDBLOCK;
|
|
endblock=STARTBLOCK;
|
|
startchar=ENDCHAR;
|
|
endchar=STARTCHAR;
|
|
}
|
|
|
|
for (i=startblock; i<=endblock; i++) {
|
|
if (last_y != -1 && last_y != markgram->blocks[i].y)
|
|
text_so_far = string_Concat2(text_so_far, "\n");
|
|
idx = markgram->blocks[i].strindex;
|
|
len = markgram->blocks[i].strlen;
|
|
if (startblock == endblock)
|
|
temp = string_CreateFromData(text+idx+startchar,
|
|
endchar-startchar);
|
|
else if (i==startblock)
|
|
temp = string_CreateFromData(text+idx+startchar,len-startchar);
|
|
else if (i==endblock)
|
|
temp = string_CreateFromData(text+idx,endchar);
|
|
else
|
|
temp = string_CreateFromData(text+idx,len);
|
|
text_so_far = string_Concat2(text_so_far, temp);
|
|
free(temp);
|
|
last_y = markgram->blocks[i].y;
|
|
}
|
|
}
|
|
|
|
return(text_so_far);
|
|
}
|
|
|
|
#endif /* X_DISPLAY_MISSING */
|