home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-385-Vol-1of3.iso
/
x
/
xtici.zip
/
xtici
/
controller.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-08-28
|
20KB
|
728 lines
/*
* Code and supporting documentation (c) Copyright 1990 1991 Tektronix, Inc.
* All Rights Reserved
*
* This file is a component of an X Window System client which uses the Xcms
* Color Management System. TekColor is a trademark of Tektronix, Inc. The
* TekColor Editor is the subject of U.S. and foreign patents pending. The
* term "TekHVC" designates a particular color space that is the subject of
* U.S. Patent No. 4,985,853 (equivalent foreign patents pending).
* Permission is hereby granted to use, copy, modify, sell, and otherwise
* distribute this software and its documentation for the X Window System
* environment, for any purpose and without fee, provided that:
*
* 1. The code and documentation are only used to implement a
* TekColor Editor in an X Window System environment; and
* 2. This copyright and permission notice is reproduced in all copies
* of the code and in supporting documentation.
*
* Permission is granted to modify this code as required to allow it to
* be compiled on any host computer, provided that the functionality of
* the TekColor Editor is not modified in any way. A description of any
* modifications must be sent to Tektronix, Inc. Contact
* Tektronix Inc., P.O. Box 1000, Mail Station 60-850,
* Network Displays Division Engineering, Wilsonville, OR 97070.
*
* Tektronix makes no representation about the suitability of this software
* for any purpose. It is provided "as is" and with all faults.
*
* TEKTRONIX DISCLAIMS ALL WARRANTIES APPLICABLE TO THIS SOFTWARE,
* INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE. IN NO EVENT SHALL TEKTRONIX BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR THE PERFORMANCE OF THIS SOFTWARE.
*
* NAME
* controller - control interface appearance and behaviour
*
* DESCRIPTION
* Controller provides a middle layer between the widgets
* and the underlying data (resources.c) that is being managed.
* Much of the work of this package is partitioned into other
* packages that deal with a specific portion of the interface -
* such as leaf.c, huebar.c - or a specific task - such as
* layout.c and message.c.
*/
#ifndef LINT
static char *copy_notice = "Copyright 1991 Tektronix, Inc.";
#ifdef RCS_ID
static char *rcsid= "$Header: controller.c,v 1.2 91/08/28 10:01:23 adamsc Exp $";
#endif /* RCS_ID */
#endif /* LINT */
/*
* EXTERNAL INCLUDES
*/
/*
* INTERNAL INCLUDES
*/
#include "xtici.h"
#include "CMSColor.h"
#include <X11/Xatom.h>
#include "patch.h"
#include "widgets/xticiSD.h"
#include "widgets/ColorS.h"
#include "widgets/Hvcleaf.h"
/*
* EXTERNS
*/
/*
* GLOBALS
*/
#ifdef DEC
# define GLOBAL global
#else
# define GLOBAL
#endif
GLOBAL int reqVisual;
/* special event processing mode */
GLOBAL int grabState = GrabNone;
/* controls how the huebar is displayed */
GLOBAL int huebarState = HuebarEmpty;
/* and the leaf */
GLOBAL int leafState = LeafEmpty;
/* ends of ranges, or color cells which could be displayed */
GLOBAL Pixel clist[MaxList] = {0, 0};
GLOBAL int currentPatch = 0;
/* cached local clipboard color */
GLOBAL XcmsColor clipColor;
/* remembers if text widget is being programatically altered */
GLOBAL int textProgramInput = 0;
/*
* LOCAL DEFINES
*/
extern void ClearMapEdited();
extern void NotifyTarget();
/*
* LOCAL TYPEDEFS
*/
/*
* LOCALS VARIABLES
*/
#ifdef DEBUG
# define STATIC
#else
# define STATIC static
#endif
/*
* Variables allow the warnings about colors
* avoided after the first time.
*/
STATIC Boolean ifEditable = False;
STATIC Boolean bwEditable = False;
STATIC char *overwriteCellHelp = "\
This action imports the color value from the selected color cell.\n\
The existing color in that cell has been edited. If you continue this\n\
action, those changes will be lost. You may want to save the existing\n\
color cell values to a file, then select Import Pixel again.";
/************************************************************************
* *
* PRIVATE ROUTINES *
* *
************************************************************************/
/*
* Find the lowest InputOutput window enclosing the event.
* The window is sought in this order:
* 1) lowest window at x,y with CMS_Status property (if requested for export)
* 2) lowest window at x,y if no CMS_Status property exists for any enclosing
* window.
* 3) a child of the root window.
* 4) the root window itself.
* Documentation use to say:
* "It is a piece of cake to go even further down the subwindow tree,
* but that is probably not the right thing to do."
* I am not sure why bobtl wrote that, so I am fixing a bug by looking
* down the entire subtree for the lowest window, or the lowest window
* with CMS_Status property set if the cmsflag is set during export operations.
* The x and y are the event coordinates in the returned window.
*/
STATIC void FindWindow(e, cmsflag, which, x, y)
XButtonEvent *e;
Bool cmsflag; /* true if looking for window with CMS_Status property */
Window *which; /* return */
int *x, *y; /* return */
{
Window root = DefaultRootWindow(dpy);
Window child = None;
Window cmswindow = None;
Window parent, target;
XWindowAttributes wa;
STATIC Atom cmsatom;
Atom type = None;
int format;
unsigned long nitems, after;
unsigned char *data;
if (!cmsatom)
cmsatom = XInternAtom(dpy, CMS_Status , False);
/*
* Start at the top (root). Locate the real (lowest) window the
* pointer was in based on the x,y. If cmsflag is set, find the
* lowest window with CMS_Status property containing x,y.
*
* First start by translating to the root.
* Then progress down until no more children containing pointer.
* Do not traverse down any InputOnly windows since the window we return
* will be used in graphics calls.
*/
*x = e->x;
*y = e->y;
parent = e->window;
target = root;
do {
XTranslateCoordinates(dpy, parent, target, *x, *y, x, y, &child);
if(cmsflag){
/* check if this window has the magic CMS_Status property */
XGetWindowProperty(dpy, target, cmsatom, 0L, 0L, False,
AnyPropertyType, &type, &format, &nitems, &after, &data);
if (type != None)
cmswindow = target;
}
parent = target;
target = child;
if (child != None)
XGetWindowAttributes(dpy, child, &wa);
} while (child != None && wa.class != InputOnly);
if( !cmsflag || cmswindow == None)
*which = parent; /* lowest window with x,y */
else
*which = cmswindow; /* lowest window with x,y and CMS_Status prop */
}
/*
* Return the pixel value at x,y in window w;
* Return -1 if the visual of the window is not PseudoColor.
* TODO: should allow editing of other color types
*
* The pixel value is found by extracting an image of size 1x1.
* This minimizes data transfer and also avoids problems when
* the window is partially off-screen.
*/
STATIC int GetCell(w, x, y, cell)
Window w;
int x, y;
XcmsColor *cell;
{
XWindowAttributes wa;
XImage *xi;
Pixel spot;
XGetWindowAttributes(dpy, w, &wa);
xi = XGetImage(dpy, w, x, y, 1, 1, AllPlanes, ZPixmap);
spot = XGetPixel(xi, 0, 0);
XDestroyImage(xi);
cell->pixel = spot;
if (XcmsQueryColor(dpy, wa.colormap, cell, XcmsTekHVCFormat)
== XcmsFailure) {
#ifdef XDEBUG
fprintf(stderr, "Warning: XcmsQueryColor failed in GetCell\n");
#endif
}
return(spot);
}
/*
* Handle an error by setting a semaphore.
*/
/* STATIC int HandleError(edpy, e)
* Display *edpy;
* XErrorEvent *e;
* {
* xerror = e->error_code;
* return(NULL); /* return satisfies declared type for error handler */
/* }
*/
/************************************************************************
* *
* PUBLIC ROUTINES *
* *
************************************************************************/
/* ARGSUSED */
void AllowFloat(w, gestureCode, call)
Widget w;
int gestureCode;
TextCallbackStruct *call;
{
int len, i;
char *ptr = NULL;
if (call->reason != CR_MODIFYING_TEXT_VALUE)
return;
/*
* This routine is called when any change is made to the text,
* even when that change is made by the program. I don't care
* what changes the program is making, I just wan't to react to
* user input.
*/
if (textProgramInput)
return;
if (gesture == GestureNone)
BeginGesture(gestureCode);
len = strlen(call->text);
ptr = call->text;
if (len <= 0)
return;
for (i = 0; i < len; i++, ptr++) {
if ( !isdigit(*ptr) && (*ptr != '.') )
call->doit = False;
}
}
/* ARGSUSED */
void AllowInt(w, gestureCode, call)
Widget w;
int gestureCode;
TextCallbackStruct *call;
{
int len, i;
char *ptr = NULL;
if (call->reason != CR_MODIFYING_TEXT_VALUE)
return;
/*
* This routine is called when any change is made to the text,
* even when that change is made by the program. I don't care
* what changes the program is making, I just wan't to react to
* user input.
*/
if (textProgramInput)
return;
if (gesture == GestureNone)
BeginGesture(gestureCode);
len = strlen(call->text);
ptr = call->text;
if (len <= 0)
return;
for (i = 0; i < len; i++, ptr++) {
if (!isdigit(*ptr))
call->doit = False;
}
}
/* ARGSUSED */
void ChangedIndex(w, which, call)
Widget w;
int which;
ScaleCallbackStruct *call;
{
switch (call->reason) {
case CR_DISARM:
EndIndex(which);
call->value = currentIndex;
return;
case CR_ARM:
BeginGesture(GestureMouse);
return;
case CR_SINGLE_SELECT:
BeginGesture(GestureNone);
break;
case CR_DRAG:
case CR_VALUE_CHANGED:
break;
}
NewIndex ((int)call->value, which);
call->value = currentIndex;
}
/*
* Application has grabbed the pointer; respond to mouse buttons.
* Return non-zero if this event is "used" by the grab.
* Any mouse button is used.
*/
int HandleGrab(e)
XButtonEvent *e; /* not necessarily a button event; check before using */
{
int tmp;
Window target;
int x, y;
int newPixel; /* Should be Pixel, but GetCell can return negative */
XcmsColor newCell;
Bool cmsflag = False; /* False for imports, True for exports */
void NewCell();
if ( (e->type != ButtonPress) && (e->type != ButtonRelease) )
return(0);
if (grabState & GrabUp) {
if (e->type != ButtonPress)
return(1);
grabState |= GrabDown;
grabState &= ~GrabUp;
} else { /* grabState is down */
if (e->type != ButtonRelease)
return(1);
/* based on grab state, process the pick */
if (grabState & GrabCell || grabState & GrabMap)
cmsflag = True; /* Find export window with CMS_Status prop */
FindWindow(e, cmsflag, &target, &x, &y);
/* done with grab, release now,
following routines may require interaction */
tmp = grabState;
grabState = GrabNone;
XUngrabPointer(dpy, (Time)e->time);
newCell.format = XcmsTekHVCFormat;
/*
* Grab a single color cell, importing its value from the window
* colormap, changing index and color to the new values.
*/
if (tmp & GrabPixel) {
Pixel tmpPixel = currentIndex;
newPixel = GetCell(target, x, y, &newCell);
if (newPixel < 0)
return(1);
/*
* This undo is rather tricky because first we will need to
* restore the changed values of the new cell, then restore
* the currently edited cell.
*/
currentIndex = tmpPixel;
NewCell(&newCell, MenuWidget);
/*
* Grab the image of a window, complete with its supporting
* visual type and colormap. Change complete colormap,
* along with the index.
*/
} else {
/*
* The target returned by FindWindow may not be the main client
* window if the click is in the wm wrapper windows.
* Insure our target is the client.
* If Null is returned, then no window manager is running and we will
* go with the original target window (root or child of root).
*
* The above documentation is old. Currently the above call
* to FindWindow() returns the lowest window with the
* CMS_Status property, or the lowest window if none has the
* CMS_Status property. We ignore WM_State property.
*
* Exporting a cell (GrabCell) or a map (GrabMap).
*/
if (tmp & GrabCell) {
newPixel = currentIndex;
ClearIndexEdited(currentIndex);
} else {
newPixel = -1;
ClearMapEdited(mapSize);
}
NotifyTarget(target, e, newPixel);
}
} /* end else grab state is down */
return(1);
}
/*
* Return true if the index should not be modified.
* This involves checking if the index is Black/White Pixel
* or one of the editor interface colors.
* TODO: add help strings
*/
Boolean IndexProtected(pix)
Pixel pix;
{
if (!ifEditable && IsIfIndex(pix)) {
if (Warning("This color cell is used by the color editor",
(char *)NULL) == MessageContinue) {
ifEditable = True;
} else {
return(True);
}
}
if (!bwEditable && IsBWIndex(pix)) {
if (Warning("This color cell is a default color (black or white)",
(char *)NULL) == MessageContinue)
bwEditable = True;
else
return(True);
}
if (IsLeafIndex(pix)) {
if (Warning("This color cell is used to fill the leaf.\n\
Fill will be removed to edit this cell.", (char *)NULL) == MessageContinue) {
NewLeafState(leafState & ~LeafFilled, NoWidget);
} else {
return(True);
}
} else if (IsHuebarIndex(pix)) {
if (Warning("This color cell is used to fill the Hue bar.\n\
Hues will be removed to edit this cell.", (char *)NULL) == MessageContinue) {
NewHuebarState(HuebarEmpty, NoWidget);
} else {
return(True);
}
}
return(False);
}
/*
* Update all cached values to reflect a new color cell values.
* Then update all interface components.
*/
void NewCell(cell, from)
XcmsColor *cell;
int from;
{
XcmsColor *hvc = cell;
/* TODO: should also happen in local version */
/* allow user to back out of overwrite of an edited cell */
if (EditedIndex(cell->pixel) && Warning(
"Color value in cell will be lost", overwriteCellHelp))
return;
NewIndex ((int)cell->pixel, from);
EditHvcCells(cell, 1, DECREASE_CHROMA, False);
NewColor(hvc, from, UnknownChange);
/* cell may already have been marked; clear it */
ClearIndexEdited(cell->pixel);
/* set checkpoint value */
CacheCell((int)cell->pixel);
}
/*
* Replace all the cells in the passed range with the values in the
* passed map.
*/
void NewCellRange(oldmap, from, to)
Colormap oldmap;
Pixel from, to;
{
int i;
XcmsColor color;
for (i = from; i <= to; i++) {
if (!EditedIndex(i))
continue;
if (Warning("Current color values will be lost", overwriteCellHelp))
return;
}
CopyColormap(oldmap, from, to, True);
/* if the current cell is in the range, reset the interface */
if ( (currentIndex >= from) && (currentIndex <= to)) {
ConstrainColor(currentIndex);
bcopy((char *)¤tMap[currentIndex], (char *)&color, sizeof(XcmsColor));
NewColor(&color, NoWidget, UnknownChange);
}
/* clear editing mark from all cells of range */
for (i = from; i <= to; i++)
ClearIndexEdited(i);
}
/*
* Update the interface to reflect a new color.
* The color is always passed as an HVC specification.
*/
void NewColor(new, from, what)
XcmsColor *new;
int from;
int what;
{
/* Update the HVC interface */
if ((what & HueChange) &&
(new->spec.TekHVC.H != currentHvc.spec.TekHVC.H)) {
currentHvc.spec.TekHVC.H = new->spec.TekHVC.H;
if (from != HuebarWidget)
UpdateHueWidget();
}
/* only update the hue bar if we absolutely must */
if ( (currentHvc.spec.TekHVC.V != new->spec.TekHVC.V) || (currentHvc.spec.TekHVC.C != new->spec.TekHVC.C) ) {
Pixel dbase;
int dlen;
currentHvc.spec.TekHVC.V = new->spec.TekHVC.V;
currentHvc.spec.TekHVC.C = new->spec.TekHVC.C;
if (huebarState == HuebarDynamic)
/* in this state, widget is ok; just set the colormap */
HuebarRangeUpdate (&dbase, &dlen);
}
/* this changes all three values at once, so do regardless of changes */
if (from != LeafWidget)
UpdateLeafWidget();
/*
* Update the hvc coordinate widget if it is showing.
*/
if (HvcShowing && (from != HvcWidget) )
UpdateShowWidget(HvcWidget);
/* update other possible coordinate systems */
/* this involves server round trips, so do only at end of gesture */
if (gesture == GestureNone) {
if (RgbShowing && (from != RgbWidget) )
UpdateShowWidget(RgbWidget);
if (UvyShowing && (from != UvyWidget) )
UpdateShowWidget(UvyWidget);
}
/* RGB and HVC coordinates are always showing, update them */
if (from != HvcWidget)
UpdateShowWidget(HvcWidget);
if ( (gesture == GestureNone) && (from != RgbWidget) )
UpdateShowWidget(RgbWidget);
}
/*
* Store a new color (specified by HVC) in the colormap.
* Validate and constraint the color to be usable.
* Update the interface to reflect the new color.
*/
/* ARGSUSED */
void NewHvc(new, from, what)
XcmsColor *new;
int from;
int what;
{
XcmsColor color;
/* constrain values to be legal; chroma is clamped by library */
while (new->spec.TekHVC.H >= 360.0)
new->spec.TekHVC.H -= 360.0;
while (new->spec.TekHVC.H < 0.0)
new->spec.TekHVC.H += 360.0;
if (new->spec.TekHVC.V < 0.0)
new->spec.TekHVC.V = 0.0;
else if (new->spec.TekHVC.V > 100.0)
new->spec.TekHVC.V = 100.0;
if (new->spec.TekHVC.C < 0.0)
new->spec.TekHVC.C = 0.0;
/*
* Always constrain chroma to <= LeafMaxChroma so that it remains visible
* on the leaf widget.
*/
else if (new->spec.TekHVC.C > LeafMaxChroma)
new->spec.TekHVC.C = LeafMaxChroma;
color.format = XcmsTekHVCFormat;
color.pixel = new->pixel;
color.spec.TekHVC.H = new->spec.TekHVC.H;
color.spec.TekHVC.V = new->spec.TekHVC.V;
color.spec.TekHVC.C = new->spec.TekHVC.C;
EditHvcCells(&color, 1, DECREASE_CHROMA, True);
new->format = color.format;
new->pixel = color.pixel;
new->spec.TekHVC.H = color.spec.TekHVC.H;
new->spec.TekHVC.V = color.spec.TekHVC.V;
new->spec.TekHVC.C = color.spec.TekHVC.C;
if ( (new->spec.TekHVC.H == currentHvc.spec.TekHVC.H)
&& (new->spec.TekHVC.V == currentHvc.spec.TekHVC.V)
&& (new->spec.TekHVC.C == currentHvc.spec.TekHVC.C) )
return;
NewIndex ((int)new->pixel, NoWidget);
}
/*
* Update the interface to correspond to a new index.
*/
void NewIndex (new, where)
int new;
int where;
{
Arg arg;
XcmsColor color;
if (!gesture && IndexProtected((Pixel)new)) {
return;
}
currentIndex = (Pixel)new;
clist[currentPatch] = currentIndex;
currentHvc.pixel = currentIndex;
/*
* Update the all interface pieces that indicate index.
* Where may indicate that some pieces are already kosher.
*/
if (where != ColormapWidget) {
/* UpdateMapWidget(); */
XtSetArg(arg, XtNvalue, new);
XtSetValues(colormap, &arg, 1);
}
/* always force the current patch to redraw */
UpdatePatchWidget(where);
/* Assume that a change of index will mean a change of color; drive that */
ConstrainColor((Pixel)new);
bcopy((char *)¤tMap[new], (char *)&color, sizeof(XcmsColor));
NewColor(&color, where, UnknownChange);
}
/*
* Update user interface components to reflect a new map size
*/
/* void NewSize(new)
* int new;
* {
* Arg args[4];
* int n = 0;
*
* mapSize = new;
* XtSetArg(args[n], XtNmaximum, mapSize - 1); n++;
* XtSetArg(args[n], XtNlength, mapSize - 1); n++;
* /* shows 32 colors in the zoom bar */
/* XtSetArg(args[n], XtNzoom, new / 32); n++;
/*
* If currentIndex >= new, reset the value to avoid a toolkit warning.
* This is just temporary, the real value will be set later.
*/
/* if (currentIndex >= new) {
* XtSetArg(args[n], XtNvalue, mapSize - 1); n++;
* }
* XtSetValues(colormap, args, n);
* }
*/