Logo Search packages:      
Sourcecode: whitedune version File versions  Download package

swt.c

/*
 * swt.c - stephen's window toolkit
 *
 * Copyright (C) 1999 Stephen F. White
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

/* 
   Define if your XmCreateFileSelectionDialog can do 
   shell like regular expressions like *.[wx][rm]l
   Rather impossible to test via configure 8-(
*/

#define MOTIF_FILE_REGEX 1

#include <Xm/Xm.h>
#include <Xm/BulletinB.h>
#include <Xm/CascadeB.h>
#include <Xm/DragDrop.h>
#include <Xm/DrawingA.h>
#include <Xm/FileSB.h>
#include <Xm/Label.h>
#include <Xm/MainW.h>
#include <Xm/MessageB.h>
#include <Xm/Protocols.h>
#include <Xm/PushB.h>
#include <Xm/RowColumn.h>
#include <Xm/ScrollBar.h>
#include <Xm/ScrolledW.h>
#include <Xm/Separator.h>
#include <Xm/Text.h>
#include <Xm/TextF.h>
#include <Xm/ToggleB.h>
#include <X11/keysym.h>
#include <X11/IntrinsicP.h>
#include <X11/cursorfont.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>

#include <GL/gl.h>
#include <GL/glx.h>

#include "config.h"
#include "swt.h"
#include "swtxinput.h"
#include "mycsnprintf.h"
#include "copy.xbm"
#include "copy_mask.xbm"
#include "link.xbm"
#include "link_mask.xbm"
#include "error.xbm"
#include "error_mask.xbm"
#include "cursor_none.xbm"
#include "stereoview.h"

#include "swt_structs.h"

#undef MOTIF_DRAGNDROP

#define MAX_PREFLINE          1024

XtAppContext      TheAppContext = NULL;

static Display           *TheDisplay = NULL;
static XVisualInfo     *TheVisual = NULL;
static Colormap         TheColormap;
static SFont             *TheDefaultFont = NULL;
static int        WindowCapacity;
static SWND        *WindowsToUpdate;
static int        NumWindowsToUpdate = 0;
static int        InDragNDrop = FALSE;
#ifdef MOTIF_DRAGNDROP
static XEvent           TheLastButtonPress;
static Widget           TheDragContext;
static Widget           TheCopyIcon;
static Widget           TheLinkIcon;
static Widget           TheMoveIcon;
#else /* !MOTIF_DRAGNDROP */
static Widget           TheDragWindow = 0;
static Widget           TheDragPopup = 0;
static Pixmap           TheDragPixmap;
static Pixmap           TheDragMask;
static int        TheDragPixmapOffsetX;
static int        TheDragPixmapOffsetY;
static Cursor           TheCopyCursor;
static Cursor           TheLinkCursor;
static Cursor           TheMoveCursor;
static Cursor           TheErrorCursor;
#endif /* !MOTIF_DRAGNDROP */
static int        _running = FALSE;
static int        _dialogDepth = 0;
static int        _dialogRC = 0;
static int        _keycodeMap[65536];
static int        _invKeycodeMap[65536];
static Pixmap           TheCopyPixmap;
static Pixmap           TheLinkPixmap;
static Pixmap           TheMovePixmap;
static Atom       WM_DELETE_WINDOW;
static int        acceleratorsEnabled = TRUE;

static Widget     createContainer(const char *title, int x, int y, int width,
                        int height, Widget parent);
static void initWidget(Widget widget, SWND wnd);
static void expose(Widget widget, XtPointer data,
                         XtPointer event_data);
static void update();
static void input(Widget widget, XtPointer data,
                        XtPointer event_data);
static void enterEvent(Widget widget, XtPointer data,
                        XEvent *event, Boolean *continue_to_dispatch);
static void acceleratorHandler(Widget w, XtPointer data, XEvent *event,
                           Boolean *cont);
static void resize(Widget widget, XtPointer data,
                         XtPointer event_data);
static void scrollCallback(Widget widget, XtPointer data,
                         XtPointer event_data);
static void buttonCallback(Widget widget, XtPointer data,
                         XtPointer event_data);
static void menuArmCallback(Widget widget, XtPointer data,
                        XtPointer event_data);
static void menuDisarmCallback(Widget widget, XtPointer data,
                           XtPointer event_data);
static void menuActivateCallback(Widget widget, XtPointer data,
                         XtPointer event_data);
static void textActivateCallback(Widget widget, XtPointer data,
                         XtPointer event_data);
static void textFocusCallback(Widget widget, XtPointer data,
                         XtPointer event_data);
static void textLosingFocusCallback(Widget widget, XtPointer data,
                              XtPointer event_data);
static void destroyCallback(Widget widget, XtPointer data,
                        XtPointer event_data);
static void destroyMenuCallback(Widget widget, XtPointer data,
                            XtPointer event_data);
static int  doFileDialog(const char *title, SWND wnd, char *filters,
                       char *buf, int len, int flags, char* ext);
static void onDeleteWindow(Widget widget, XtPointer data,
                         XtPointer event_data);
static void onDeleteDialog(Widget widget, XtPointer data,
                       XtPointer event_data);
static void timerProc(XtPointer closure, XtIntervalId *id);
static void dlgBtnCallback(Widget widget, XtPointer data,
                     XtPointer event_data);
#ifdef MOTIF_DRAGNDROP
static void dragProc(Widget widget, XtPointer data,
                   XtPointer event_data);
static void dropProc(Widget widget, XtPointer data,
                   XtPointer event_data);
static Boolean    convertProc(Widget widget, Atom *selection, Atom *target,
                      Atom *typeRtn, XtPointer *valueRtn,
                      unsigned long *lengthRtn, int *formatRtn,
                      unsigned long *max_lengthRtn,
                      XtPointer data, XtRequestId *request_id);
static void transferProc(Widget widget, XtPointer data,
                       Atom *selection, Atom *type, XtPointer value,
                       unsigned long *length, int *format);
static void dragFinish(Widget widget, XtPointer data,
                     XtPointer event_data);
static Widget     createDragIcon(Widget widget, Pixmap pixmap, Pixmap mask,
                         int width, int height);
static void createSourceCursor(Pixmap pixmap, int xPos, int yPos,
                           int width, int height, Widget widget,
                           Pixmap *pixmapOut, Pixmap *maskOut);
static int  opToMop(int op);
#else
static void doDragOver(SWindow *wnd, int x, int y, int modifiers);
static void doDrop(SWindow *wnd, int x, int y, int modifiers);
static void exposeDragDropWindow(Widget widget, XtPointer data,
                             XtPointer event_data);
static void updateCursor(Window window, int operation);
#endif
static const char *parseMenu(const char *s1, char *s2, char *mnemonic);
static int  getModifiers(int state);
static int  queryModifiers();
static int  messageBox(Widget parent, const char *text, const char *title,
                     int type, int icon);
static int  fileExists(const char *filename);
static int  dirExists(const char *filename);
static SMENU      findMenu(Widget widget, int id);
extern void insertMenuItem(Widget menu, SWND wnd, short pos,
                         const char *text, int id);
static void resetRadioButtons(Widget widget);
static void callCommandCallback(Widget widget, int id);
static void callHighlightCallback(Widget widget, int id);
static SWND findMainWindow(Widget widget);
static void appendPair(STABLE table, const char *key, const char *value);

static SWindow         *newSWindow();
static SMenu           *newSMenu();
static SBitmap         *newSBitmap();
static SCursor         *newSCursor();
static STimer            *newSTimer(int timeout, TimerCallback cb, void *data);
static SDContext       *newSDContext();

static const char *translations = "             \
      <BtnDown>:  DrawingAreaInput()      \n\
      <BtnUp>:    DrawingAreaInput()      \n\
      <KeyDown>:  DrawingAreaInput()      \n\
      <KeyUp>:    DrawingAreaInput()      \n\
      <Motion>:   DrawingAreaInput()      \n";

static Pixmap cursorNonePixmap, cursorNoneMask;
static XColor cursorNoneColor;

static int truecolor_mode=0;

static int start_aqua_X11=0;

#define USE_SW_COLOR_SHIFT \
    static struct swColorShift c; \
    static int initalized=0; \
    if (initalized==0) { \
        swSetupTrueColor(&c); \
        initalized=1; \
    }


void set_start_aqua_X11(void)
{
    start_aqua_X11=1;
}

unsigned long swX11ColorBits(int color,int bits)
{
    unsigned long ret = color;
    if (bits < 8)
       ret = color >> 8 - bits;
    return ret;
}

unsigned long swX11Color8Bits(int color,int bits)
{
    unsigned long ret = color;
    if (bits < 8)
       ret = color << 8 - bits;
    return ret;
}

int swGetColorMaskShift(unsigned long mask) 
{
    int shift = 0; 
    while ((mask & 1) == 0) { 
       shift++; 
       mask >>= 1;
    }
    return shift;
}

int swGetColorMaskBits(unsigned long mask) 
{
    int bits = 0; 
    while ((mask & 1) == 0) { 
       mask >>= 1;
    }
    while ((mask & 1) == 1) { 
       bits++; 
       mask >>= 1;
    }
    return bits;
}

static void swSetupTrueColor(struct swColorShift* colorshift)
{
    colorshift->rshift = swGetColorMaskShift(TheVisual->red_mask);
    colorshift->rbits  = swGetColorMaskBits(TheVisual->red_mask);

    colorshift->gshift = swGetColorMaskShift(TheVisual->green_mask);
    colorshift->gbits  = swGetColorMaskBits(TheVisual->green_mask);

    colorshift->bshift = swGetColorMaskShift(TheVisual->blue_mask);
    colorshift->bbits  = swGetColorMaskBits(TheVisual->blue_mask);
}

unsigned long swRGBToX11Color(int red, int green, int blue)
{
    USE_SW_COLOR_SHIFT
    return
      ((swX11ColorBits(red,c.rbits)   << c.rshift) & TheVisual->red_mask) | 
      ((swX11ColorBits(green,c.gbits) << c.gshift) & TheVisual->green_mask) | 
      ((swX11ColorBits(blue,c.bbits)  << c.bshift) & TheVisual->blue_mask);
}

int swX11ColorToRed8Bit(unsigned long color_pixel)
{
    USE_SW_COLOR_SHIFT
    return swX11Color8Bits((color_pixel & TheVisual->red_mask) >> c.rshift, 
                           c.rbits);
}

int swX11ColorToGreen8Bit(unsigned long color_pixel)
{
    USE_SW_COLOR_SHIFT
    return swX11Color8Bits((color_pixel & TheVisual->green_mask) >> c.gshift, 
                           c.gbits);
}

int swX11ColorToBlue8Bit(unsigned long color_pixel)
{
    USE_SW_COLOR_SHIFT
    return swX11Color8Bits((color_pixel & TheVisual->blue_mask) >> c.bshift, 
                           c.bbits);
}

int swX11ColorToSwColor(unsigned long color_pixel)
{
    return SW_RGB(swX11ColorToRed8Bit(color_pixel), 
                  swX11ColorToGreen8Bit(color_pixel), 
                  swX11ColorToBlue8Bit(color_pixel));
}

unsigned long swColorToX11Color(int color)
{
    int red, green, blue;
    red = (color & 0x000000ff);
    green = (color & 0x0000ff00) >> 8;
    blue = (color & 0x00ff0000) >> 16;

    return swRGBToX11Color(red,green,blue);
}

/* swInitialize gives back a flag if stereo is available */

extern int
swInitialize(int *argc, char **argv,int stereotype)
{
    XrmDatabase         db;
    char bogus_bits[1] = { 0 };
    XColor  fg, bg;
    Pixmap  copyMask, linkMask, errorPixmap, errorMask;
    int           i;
    int attrib,firstattrib;
    int stereoret=NO_STEREO;

    int legal_depth_list[] = { 32, 24, 16, 15, 8, 4, 1 };

    int           attribs[] = {
      GLX_DEPTH_SIZE,         32,
      GLX_RED_SIZE,           8,
        GLX_ALPHA_SIZE,         8,
      GLX_DOUBLEBUFFER, GL_TRUE,
#ifndef HAVE_NO_STEREO
      GLX_STEREO,       GL_TRUE,
#endif
      GLX_RGBA,         GL_TRUE,
      0
    };

    int           attribs1[] = {
      GLX_DEPTH_SIZE,         32,
      GLX_RED_SIZE,           8,
        GLX_ALPHA_SIZE,         8,
      GLX_DOUBLEBUFFER, GL_TRUE,
      GLX_RGBA,         GL_TRUE,
        /* placeholders for GLX_ACCUM_RED/BLUE/GREEN_SIZE */
        0,                      0,                       
        0,                      0,                       
        0,                      0,                       
      0
    };

    int           attribs2[] = {
      GLX_DEPTH_SIZE,         16,
      GLX_RED_SIZE,           8,
      GLX_RGBA,         GL_TRUE,
        /* placeholders for GLX_ACCUM_RED/BLUE/GREEN_SIZE */
        0,                      0,                       
        0,                      0,                       
        0,                      0,                       
      0
    };

    int           attribs3[] = {
      GLX_DEPTH_SIZE,         16,
      GLX_RGBA,         GL_TRUE,
      0
    };

    int           attribs4[] = {
      GLX_DEPTH_SIZE,         16,
      GLX_RGBA,         GL_FALSE,
      0
    };
    int errorflag = 0;

    XtToolkitInitialize ();
    TheAppContext = XtCreateApplicationContext();
#ifdef HAVE_X11STARTCOMMAND
    if (getenv("DISPLAY") == NULL) {
        if (start_aqua_X11) {
            fprintf(stderr,"$DISPLAY unset, using \":0\"\n");
            setenv("DISPLAY",":0",0);
        } else {
            fprintf(stderr,"$DISPLAY unset, unable to start X11 application\n");
            fprintf(stderr,"either use the \"-startX11aqua\" option\n");
            fprintf(stderr,"or ");
            fprintf(stderr,"start X11 and use e.g. ");
            fprintf(stderr,"for csh: \"setenv DISPLAY :0\" or for sh: \"export DISPLAY=:0\" \n");
          swCleanup();
            exit(11);               
        }
    }   
#endif

    TheDisplay = XtOpenDisplay ( TheAppContext, NULL, NULL, "ProgramClass",
                         NULL, 0, argc, argv);
    if (!TheDisplay) {
#ifdef HAVE_X11STARTCOMMAND
        if (start_aqua_X11) {
            int pid;
            pid=fork();
            if (pid==-1)
                perror("fork: ");
            else if (pid==0) {
                int i;
                char** startcommand;
                int numCommands=7;
                setsid();
                startcommand=(char**)malloc(sizeof(char*)*numCommands);
                startcommand[0]=HAVE_X11STARTCOMMAND;
                /* "panther" */
                startcommand[1]="/Applications/Utilities/X11.app/Contents/MacOS/X11";
                /* "jaguar" */
                startcommand[2]="/Applications/X11.app/Contents/MacOS/X11";
                startcommand[3]="open /Applications/Utilities/X11.app";
                startcommand[4]="open /Applications/X11.app";
                startcommand[5]="/usr/X11R6/bin/X";
                startcommand[6]="X";
                for (i=0;i<numCommands;i++) 
                    if ((i==0) || 
                        (strcmp(startcommand[i],HAVE_X11STARTCOMMAND) != 0)) { 
                        fprintf(stderr,"Security warning:");
                        fprintf(stderr," try to start on console:\n%s\n",
                                     startcommand[i]);
                        if (system(startcommand[i]) == 0)
                            exit(0);
                        else
                            perror(startcommand[i]); 
                   }
                exit(11);  
            } else {
                int i;
                /* wait for X11 startup */
                int seconds = 30;
                for (i = 0; i < seconds; i++) {
                    sleep(1); 
                    TheDisplay = XtOpenDisplay ( TheAppContext, NULL, NULL,
                                                 "ProgramClass", NULL, 0, 
                                                  argc, argv);
                    if (!TheDisplay)
                        errorflag = 1;
                    else
                        break;
                }
                if (errorflag == 1) {
                  fprintf(stderr, "can't open display: %s\n", getenv("DISPLAY"));
                  fprintf(stderr, "after trying to start X11.app and wait %d seconds\n",
                            seconds),
                    swCleanup();
                exit(11);
                }
            }
        } else
            errorflag = 1;
#else 
        errorflag = 1;
#endif 
    }
    if (errorflag == 1) {
        if (getenv("DISPLAY") == NULL)
          fprintf(stderr, "can't open display\n");
        else
           fprintf(stderr, "can't open display: %s\n", getenv("DISPLAY"));
        swCleanup();
        exit(11);
    }

    /* 
     *      Try different color depths from legal_depth_list 
     *      Try a few different attributes 
     *      e.g. try GLX_DOUBLEBUFFER attribute, which is not supported by
     *      all machines
     *      use best attributes first
     */

    truecolor_mode=1;
    TheVisual=NULL;
    /* check for stereo visual */
    if (stereotype==QUAD_BUFFER_STEREO)
       firstattrib=0;
    else
       firstattrib=1;
    for (attrib=firstattrib;attrib<5;attrib++)
       {
       int idepth;
       for (idepth=0;idepth<sizeof(legal_depth_list)/sizeof(int);idepth++)
          {
          int alpha;
          for (alpha=1;alpha>=0;alpha--)
             {
             int redsize;
             for (redsize=8;redsize>=4;redsize--)
                {
                int* attribs_pointer=attribs;
                if (attrib==1)
                   attribs_pointer=attribs1;
                if (attrib==2)
                   attribs_pointer=attribs2;
                if (attrib==3)
                   attribs_pointer=attribs3;
                if (attrib==4)
                   {
                   attribs_pointer=attribs4;
                   truecolor_mode=0;
                   }
                attribs_pointer[1]=legal_depth_list[idepth];
                if ((attrib==0) || (attrib==1))
                   {
                   attribs_pointer[3]=redsize;
                   if (alpha)
                      attribs_pointer[5]=redsize;
                   else
                      attribs_pointer[5]=0;
                   }
                if (((attrib==1) || (attrib==2)) && 
                    ((stereotype==RED_GREEN_ANAGLYPH_STEREO) || 
                     (stereotype==GREEN_RED_ANAGLYPH_STEREO)))
                   {
                   attribs1[10]=GLX_ACCUM_RED_SIZE;
                   attribs1[11]=redsize;
                   attribs1[12]=GLX_ACCUM_GREEN_SIZE;
                   attribs1[13]=redsize;
                   }
                if (((attrib==1) || (attrib==2)) && 
                    ((stereotype==RED_BLUE_ANAGLYPH_STEREO) || 
                     (stereotype==BLUE_RED_ANAGLYPH_STEREO)))
                   {
                   attribs2[6]=GLX_ACCUM_RED_SIZE;
                   attribs2[7]=redsize;
                   attribs2[8]=GLX_ACCUM_BLUE_SIZE;
                   attribs2[9]=redsize;
                   }
                TheVisual = glXChooseVisual(TheDisplay, 
                                            DefaultScreen(TheDisplay),
                                    attribs_pointer);
                if (TheVisual) 
                   {
                   if (attrib==0)
                      {
#ifdef HAVE_STEREOCOMMAND
                      system(HAVE_STEREOCOMMAND);             
#endif
#ifdef HAVE_NO_STEREO
                      stereoret=NO_STEREO;
#else
                      stereoret=QUAD_BUFFER_STEREO;
#endif
                      }
                   if (((attrib==1) || (attrib==2)) && 
                       ((stereotype==RED_GREEN_ANAGLYPH_STEREO) || 
                        (stereotype==GREEN_RED_ANAGLYPH_STEREO) || 
                        (stereotype==RED_BLUE_ANAGLYPH_STEREO) || 
                        (stereotype==BLUE_RED_ANAGLYPH_STEREO))) 
                      stereoret=stereotype;
                   break;
                   }
                }
             if (TheVisual) break;
             }
          if (TheVisual) break;
          }
        if (TheVisual) break;
     }
     if (!TheVisual) {
      fprintf(stderr, "couldn't find an appropriate visual for OpenGL, bailing\n");
        swCleanup();
      exit(1);
    }

    if (truecolor_mode)
       { 
       XStandardColormap *stdColorMap = NULL;
       int i, numCmaps = 0;
       truecolor_mode=0;
       
       if (XmuLookupStandardColormap(TheDisplay, TheVisual->screen, 
                                     TheVisual->visualid, TheVisual->depth, 
                                     XA_RGB_DEFAULT_MAP, False, True) !=0) 
          if (XGetRGBColormaps(TheDisplay, DefaultRootWindow(TheDisplay),
                                    &stdColorMap, &numCmaps, 
                                    XA_RGB_DEFAULT_MAP) != 0) 
             if (stdColorMap != NULL) {
                for (i = 0; i < numCmaps; i++)
                   if (stdColorMap[i].visualid == TheVisual->visualid) {
                      TheColormap = stdColorMap[i].colormap;
                      stdColorMap = NULL;
                      if ( (TheVisual->visual->class==TrueColor ) ||
                           (TheVisual->visual->class==DirectColor ) )
                          truecolor_mode=1;
                      break;
                   }
                XFree(stdColorMap);
             }
             
       if (truecolor_mode==0)
          fprintf(stderr,"no true/directcolor visual, %s\n",
                         "fall back to slow color modus 8-(");
       }
    if (truecolor_mode==0)
        TheColormap = XCreateColormap(TheDisplay,
                                      DefaultRootWindow(TheDisplay),
                              TheVisual->visual, AllocNone);

    TheDefaultFont = swFindFont("helvetica", SW_PLAIN, 12);
    WM_DELETE_WINDOW = XmInternAtom(TheDisplay, (String) "WM_DELETE_WINDOW",
                            False);

    db = XtDatabase(TheDisplay);

    XrmPutStringResource(&db, "*XmDrawingArea.translations", translations);
    XrmPutStringResource(&db, "Dune*background", "#C0C0C0");
    XrmPutStringResource(&db, "Dune*XmTextField.foreground", "Black");
    XrmPutStringResource(&db, "Dune*XmTextField.background", "White");
    XrmPutStringResource(&db, "Dune*XmTextField.borderWidth", "0");

    memset(_keycodeMap, 0, 65536);
    _keycodeMap[Button1] = SW_MOUSE1;
    _keycodeMap[Button2] = SW_MOUSE2;
    _keycodeMap[Button3] = SW_MOUSE3;
    _keycodeMap[XK_Up] = SW_UP;
    _keycodeMap[XK_Down] = SW_DOWN;
    _keycodeMap[XK_Left] = SW_KEY_LEFT;
    _keycodeMap[XK_Right] = SW_KEY_RIGHT;
    _keycodeMap[XK_Delete] = SW_DELETE;
    for (i = 0x40; i < 0x80; i++) {
      _keycodeMap[i] = i;
    }
    for (i = 0; i < 65536; i++) {
      _invKeycodeMap[_keycodeMap[i]] = i;
    }

    WindowCapacity = 16;
    WindowsToUpdate = malloc(WindowCapacity * sizeof(SWND));

    TheCopyPixmap = XCreateBitmapFromData(TheDisplay,
                                DefaultRootWindow(TheDisplay),
                                copy_bits, copy_width, copy_height);

    copyMask = XCreateBitmapFromData(TheDisplay,
                       DefaultRootWindow(TheDisplay),
                       copy_mask_bits, copy_mask_width, copy_mask_height);

    TheLinkPixmap = XCreateBitmapFromData(TheDisplay,
                                DefaultRootWindow(TheDisplay),
                              link_bits, link_width, link_height);

    linkMask = XCreateBitmapFromData(TheDisplay,
                       DefaultRootWindow(TheDisplay),
                       link_mask_bits, link_mask_width, link_mask_height);

    TheMovePixmap = XCreateBitmapFromData(TheDisplay,
                                 DefaultRootWindow(TheDisplay),
                                 bogus_bits, 1, 1);

    errorPixmap = XCreateBitmapFromData(TheDisplay,
                              DefaultRootWindow(TheDisplay),
                              error_bits, error_width, error_height);

    errorMask = XCreateBitmapFromData(TheDisplay,
                              DefaultRootWindow(TheDisplay),
                  error_mask_bits, error_mask_width, error_mask_height);

    cursorNonePixmap = XCreateBitmapFromData(TheDisplay, DefaultRootWindow(TheDisplay),
                                             cursor_none_bits, 
                                             cursor_none_width, cursor_none_height);
    cursorNoneMask = XCreateBitmapFromData(TheDisplay, DefaultRootWindow(TheDisplay),
                                             cursor_none_bits, 
                                             cursor_none_width, cursor_none_height);


    fg.red = fg.green = fg.blue = 0;
    bg.red = bg.green = bg.blue = 65535;
    cursorNoneColor.red = cursorNoneColor.green = cursorNoneColor.blue = 0;
    if (truecolor_mode) {
        fg.pixel=swRGBToX11Color(fg.red>>8,fg.green>>8,fg.blue>>8);
        bg.pixel=swRGBToX11Color(bg.red>>8,bg.green>>8,bg.blue>>8);
        cursorNoneColor.pixel=swRGBToX11Color(cursorNoneColor.red>>8,
                                              cursorNoneColor.green>>8,
                                              cursorNoneColor.blue>>8);
    } else { 
        XAllocColor(TheDisplay, TheColormap, &fg);
        XAllocColor(TheDisplay, TheColormap, &bg);
        XAllocColor(TheDisplay, TheColormap, &cursorNoneColor);
    }

#ifndef MOTIF_DRAGNDROP
    TheCopyCursor = XCreatePixmapCursor(TheDisplay, TheCopyPixmap, copyMask,
                              &fg, &bg, copy_hot_x, copy_hot_y);
    TheLinkCursor = XCreatePixmapCursor(TheDisplay, TheLinkPixmap, linkMask,
                              &fg, &bg, link_hot_x, link_hot_y);
    TheMoveCursor = None;
    TheErrorCursor = XCreatePixmapCursor(TheDisplay, errorPixmap, errorMask,
                               &fg, &bg, error_hot_x, error_hot_y);
#endif

    return stereoret;
}

extern int
swMainLoop(void)
{
    _running = TRUE;
    while (_running)
    {
      XtAppProcessEvent(TheAppContext, XtIMAll);
      if (XtAppPending(TheAppContext) == 0) {
          update();
      }
    }
    return 0;
}

extern void
swExit(void)
{
    _running = FALSE;
    XFreePixmap(TheDisplay, TheCopyPixmap);
    XFreePixmap(TheDisplay, TheLinkPixmap);
    XFreePixmap(TheDisplay, TheMovePixmap);

#ifndef MOTIF_DRAGNDROP
    XFreeCursor(TheDisplay, TheCopyCursor);
    XFreeCursor(TheDisplay, TheLinkCursor);
    XFreeCursor(TheDisplay, TheErrorCursor);
#endif
    
    swCleanup();
/*    XtDestroyApplicationContext(TheAppContext); */
}

CleanupCallback cleanup=NULL;

extern void     swSetCleanup(CleanupCallback cb)
{
    cleanup=cb;
}

extern void     swCleanup(void)
{
    if (cleanup!=NULL)
       cleanup();
}

extern void
swGetScreenSize(int *width, int *height)
{
    int                 x, y;
    unsigned int  w, h, border_width, depth;
    Window        root;

    XGetGeometry(TheDisplay, DefaultRootWindow(TheDisplay), &root, &x, &y,
             &w, &h, &border_width, &depth);
    *width = (int) w;
    *height = (int) h;
}

extern void
swSetDefaultIcon(int resource)
{
}

extern int
swModalLoop(void)
{
    int           minDepth = _dialogDepth;
    _dialogDepth++;
    while (_dialogDepth > minDepth)
    {
      XtAppProcessEvent(TheAppContext, XtIMAll);
      if (XtAppPending(TheAppContext) == 0) {
          update();
      }
    }
    return _dialogRC;
}

extern void
swExitModalLoop(int rc)
{
    _dialogDepth--;
    _dialogRC = rc;
}

extern void
swSetExposeCallback(SWND wnd, ExposeCallback exposeCallback)
{
    if (!wnd) return;

    wnd->exposeCallback = exposeCallback;
    if (exposeCallback) {
      XtVaSetValues(wnd->widget, XmNbackgroundPixmap, None, NULL);
    }
}

extern void
swSetMouseCallback(SWND wnd, MouseCallback mouseCallback)
{
    if (!wnd) return;

    wnd->mouseCallback = mouseCallback;
}

extern void
swSetKeyCallback(SWND wnd, KeyCallback keyCallback)
{
    if (!wnd) return;

    wnd->keyCallback = keyCallback;
}

extern void
swSetFocusCallback(SWND wnd, FocusCallback focusCallback)
{
    if (!wnd) return;

    wnd->focusCallback = focusCallback;
}

extern void
swSetResizeCallback(SWND wnd, ResizeCallback resizeCallback)
{
    if (!wnd) return;

    wnd->resizeCallback = resizeCallback;
}

extern void
swSetEnterCallback(SWND wnd, EnterCallback enterCallback)
{
    if (!wnd) return;

    wnd->enterCallback = enterCallback;
}

extern void
swSetScrollBarCallback(SWND wnd, ScrollBarCallback scrollBarCallback)
{
    if (!wnd) return;

    wnd->scrollBarCallback = scrollBarCallback;
}

extern void
swSetCommandCallback(SWND wnd, CommandCallback cb)
{
    if (!wnd) return;

    wnd->commandCallback = cb;
}

extern void
swSetHighlightCallback(SWND wnd, CommandCallback cb)
{
    if (!wnd) return;

    wnd->highlightCallback = cb;
}

extern void
swCallCommandCallback(SWND wnd, int id)
{
    callCommandCallback(wnd->widget, id);
}

extern void
swCallHighlightCallback(SWND wnd, int id)
{
    callHighlightCallback(wnd->widget, id);
}

extern void
swSetCloseCallback(SWND wnd, CloseCallback cb)
{
    wnd->closeCallback = cb;
}

extern void
swSetClientData(SWND wnd, void *data)
{
    if (!wnd) return;
    wnd->data = data;
}

static void deleteFromWindowsToUpdate(SWND wnd)
{
    int i;
    for (i = 0; i < NumWindowsToUpdate; i++)
      if (WindowsToUpdate[i] == wnd) {
          WindowsToUpdate[i] = NULL;
      }
}

extern void
swDestroyWindow(SWND wnd)
{
    if (!wnd) return;

    if (wnd->menu) XtDestroyWidget(wnd->menu->widget);

    if (wnd->shell) {
      XtDestroyWidget(wnd->shell);
    } else if (wnd->mainWnd) {
      XtDestroyWidget(wnd->mainWnd);
    } else if (wnd->widget) {
      XtDestroyWidget(wnd->widget);
    }

    wnd->highlightCallback = NULL;

    deleteFromWindowsToUpdate(wnd);
}

/*********************/
/* window attributes */
/*********************/

extern void
swGetPosition(SWND wnd, int *x, int *y)
{
    Position            px, py;

    if (!wnd) return;

    XtVaGetValues(wnd->shell ? wnd->shell : wnd->widget,
              XmNx, &px, XmNy, &py, NULL);
    *x = px;  *y = py;
}

extern void
swGetAbsPosition(SWND wnd, int *x, int *y)
{
    Position      wx, wy;

    if (!wnd) return;

    XtTranslateCoords(wnd->widget, 0, 0, &wx, &wy);
    *x = wx;
    *y = wy;
}

extern void
swSetPosition(SWND wnd, int x, int y)
{
    if (!wnd) return;

    XtMoveWidget(wnd->shell ? wnd->shell : wnd->widget, x, y);
}

extern void
swGetSize(SWND wnd, int *width, int *height)
{
    Dimension     w, h;

    if (!wnd) return;

    XtVaGetValues(wnd->widget, XmNwidth, &w, XmNheight, &h, NULL);
    *width = w;
    *height = h;
}

extern void
swGetTotalSize(SWND wnd, int *width, int *height)
{
    Dimension     w, h;

    if (!wnd) return;

    XtVaGetValues(wnd->shell, XmNwidth, &w, XmNheight, &h, NULL);
    *width = w;
    *height = h;
}

extern void
swSetSize(SWND wnd, int width, int height)
{
    Widget  widget;
    Dimension     borderWidth;

    if (!wnd) return;

    widget = wnd->shell ? wnd->shell : wnd->widget;
    XtVaGetValues(widget, XmNborderWidth, &borderWidth, NULL);
    XtResizeWidget(widget, (Dimension) width, (Dimension) height, borderWidth);
}

extern void
swMoveWindow(SWND wnd, int x, int y, int width, int height)
{
    Widget  widget;
    Dimension     borderWidth;

    if (!wnd) return;

    widget = wnd->shell ? wnd->shell : wnd->widget;
    XtVaGetValues(widget, XmNborderWidth, &borderWidth, NULL);
    XtConfigureWidget(widget, x, y,
                  (Dimension) width, (Dimension) height, borderWidth);
}

extern void
swGetTitle(SWND wnd, char *buf, int len)
{
    String   str;
    if (!wnd || !wnd->widget) return;

    str = XtName(wnd->widget);
    strncpy(buf, str, len);
}

extern void
swSetTitle(SWND wnd, const char *title)
{
    if (!wnd || !wnd->shell) return;

    XtVaSetValues(wnd->shell, XmNtitle, title, NULL);
}

extern void
swSetIcon(SWND wnd, SBITMAP bitmap, SBITMAP mask, int big)
{
    if (!bitmap || !mask || !wnd || !wnd->shell) return;

    if (big) {
      XtVaSetValues(wnd->shell, XmNiconPixmap, bitmap->pixmap,
                          XmNiconMask, mask->pixmap);
    }
}

extern SWND
swGetParent(SWND wnd)
{
    Widget  p;
    SWND    pwnd;

    if (!wnd) return NULL;
    
    if (wnd->mainWnd) return NULL;

    for (p = XtParent(wnd->widget); p != NULL; p = XtParent(p)) {
      XtVaGetValues(p, XmNuserData, &pwnd, NULL);
      if (pwnd != NULL) return pwnd;
    }
    return NULL;
}

extern int
swGetWindowColor(SWND wnd, int type)
{
    XColor        c;
    
    if (!wnd) return 0;

    switch (type) {
      case SW_COLOR_WINDOW_FG:
      case SW_COLOR_TEXT:
      XtVaGetValues(wnd->widget, XmNforeground, &c.pixel, NULL);
      break;
      case SW_COLOR_WINDOW_BG:
      case SW_COLOR_FACE:
      case SW_COLOR_HIGHLIGHT_TEXT:
      XtVaGetValues(wnd->widget, XmNbackground, &c.pixel, NULL);
      break;
      case SW_COLOR_TSHADOW:
      XtVaGetValues(wnd->widget, XmNtopShadowColor, &c.pixel, NULL);
      break;
      case SW_COLOR_BSHADOW:
      XtVaGetValues(wnd->widget, XmNbottomShadowColor, &c.pixel, NULL);
      break;
      case SW_COLOR_HIGHLIGHT:
/* XmNhighlightColor is actually the focus rect color (isn't motif wonderful) */
      XtVaGetValues(wnd->widget, XmNhighlightColor, &c.pixel, NULL);
      return 0x00FF3030;
      default:
      return 0;
    }

    if (truecolor_mode)
        return swX11ColorToSwColor(c.pixel);
    else {
        XQueryColor(TheDisplay, TheColormap, &c);
        return SW_RGB((c.red >> 8), (c.green >> 8), (c.blue >> 8));
    }
}

extern void
swShowWindow(SWND wnd)
{
    if (!wnd) return;

    if (wnd->mainWnd) {
      XtRealizeWidget(wnd->shell);
    } else if (wnd->shell) {
      XtPopup(wnd->shell, XtGrabNone);
    } else {
      XtManageChild(wnd->widget);
    }
}

extern void
swHideWindow(SWND wnd)
{
    if (!wnd) return;
    
    if (wnd->mainWnd) {
      XtUnrealizeWidget(wnd->shell);
    } else if (wnd->shell) {
      XtPopdown(wnd->shell);
    } else {
      XtUnmanageChild(wnd->widget);
    }
}

extern void
swIconifyWindow(SWND wnd)
{
    if (!wnd) return;

    XtVaSetValues(wnd->mainWnd, XmNiconic, True, NULL);
    XtVaSetValues(wnd->widget, XmNiconic, True, NULL);
    XtVaSetValues(wnd->shell, XmNiconic, True, NULL);

    XSync(TheDisplay, False);
}

extern void
swDeIconifyWindow(SWND wnd)
{
    if (!wnd) return;

    XtVaSetValues(wnd->shell, XmNinitialState, NormalState, NULL);
    XtVaSetValues(wnd->shell, XmNiconic, False, NULL);
    XtVaSetValues(wnd->widget, XmNinitialState, NormalState, NULL);
    XtVaSetValues(wnd->widget, XmNiconic, False, NULL);
    XtVaSetValues(wnd->mainWnd, XmNinitialState, NormalState, NULL);
    XtVaSetValues(wnd->mainWnd, XmNiconic, False, NULL);

    XSync(TheDisplay, False);
}

extern void
swMaximizeWindow(SWND wnd)
{
}

extern void
swSetWindowBG(SWND wnd, int color)
{
    if (!wnd) return;

    if (truecolor_mode)
       XtVaSetValues(wnd->widget, XmNbackground, swColorToX11Color(color), NULL);
    else  {   
       XColor     c;

       c.red = (color & 0x000000ff) << 8;
       c.green = (color & 0x0000ff00);
       c.blue = (color & 0x00ff0000) >> 8;
       XAllocColor(TheDisplay, TheColormap, &c);
       XtVaSetValues(wnd->widget, XmNbackground, c.pixel, NULL);
    }
}



extern int
swIsVisible(SWND wnd)
{
    if (!wnd) return FALSE;

    return XtIsManaged(wnd->widget);
}

extern int
swIsMaximized(SWND wnd)
{
    return FALSE;
}

extern void
swEnableWindow(SWND wnd, int flag)
{
    if (!wnd) return;

    XtSetSensitive(wnd->widget, flag);
}

extern void
swInvalidateRect(SWND wnd, int x, int y, int width, int height)
{

    int           x2 = x + width - 1;
    int           y2 = y + height - 1;

    if (!wnd) return;

    if (x < 0) x = 0;
    if (y < 0) y = 0;

    if (wnd->invx1 < 0) {
      if (NumWindowsToUpdate >= WindowCapacity) {
          WindowCapacity *= 2;
          WindowsToUpdate = (SWND *) realloc(WindowsToUpdate,
                                     WindowCapacity * sizeof(SWND));
      }
      WindowsToUpdate[NumWindowsToUpdate++] = wnd;
    }

    /* extend the invalidation region to include the new window */
    if (wnd->invx1 < 0 || x < wnd->invx1) wnd->invx1 = x;
    if (wnd->invy1 < 0 || y < wnd->invy1) wnd->invy1 = y;
    if (x2 > wnd->invx2) wnd->invx2 = x2;
    if (y2 > wnd->invy2) wnd->invy2 = y2;
}

extern void
swInvalidateWindow(SWND wnd)
{
    Dimension     w, h;
    if (!wnd) return;

    XtVaGetValues(wnd->widget, XmNwidth, &w, XmNheight, &h, NULL);
    swInvalidateRect(wnd, 0, 0, w, h);
}

extern void
swSetFocus(SWND wnd)
{
    if (!wnd) return;

    XmProcessTraversal(wnd->widget, XmTRAVERSE_CURRENT);
}

extern void
swEnableAccelerators(int enable)
{
    acceleratorsEnabled = enable;
}

extern STIMER
swSetTimer(SWND wnd, int timeout, TimerCallback cb, void *data)
{
    STimer  *t = newSTimer(timeout, cb, data);

    t->id = XtAppAddTimeOut(TheAppContext, timeout, timerProc, t);
    return t;
}

extern void
swKillTimer(STIMER timer)
{
    if (!timer) return;

    XtRemoveTimeOut(timer->id);
    timer->id=0;
    free(timer);
}

/***************/
/* Main Window */
/***************/

extern SWND
swCreateMainWindow(const char *title, int x, int y, int width, int height)
{
    SWindow    *wnd = newSWindow();
    
    /* create a shell for the top-level window */

    wnd->shell = XtVaAppCreateShell( title, "ProgramClass",
                        applicationShellWidgetClass, TheDisplay,
                        XmNx, x,
                        XmNy, y,
                        XmNwidth, width,
                        XmNheight, height,
                        XmNdefaultFontList, TheDefaultFont->fontList,
                        XmNbuttonFontList, TheDefaultFont->fontList,
                        XmNlabelFontList, TheDefaultFont->fontList,
                        XmNtextFontList, TheDefaultFont->fontList,
                        XmNvisual, TheVisual->visual,
                        XmNdepth, TheVisual->depth,
                        XmNcolormap, TheColormap,
                        XmNdeleteResponse, XmDO_NOTHING,
/*                      XmNdeleteResponse, XmUNMAP, */
                        NULL);

    /* set up a delete response */
    XmAddWMProtocolCallback(wnd->shell, WM_DELETE_WINDOW, onDeleteWindow, wnd);

    /* create a main window, for menus & such */
    wnd->mainWnd = XmCreateMainWindow(wnd->shell, (String) title, NULL, 0);
    XtManageChild(wnd->mainWnd);

    /* create a composite to hold all the children */
    wnd->widget = createContainer(title, x, y, width, height, wnd->mainWnd);
    initWidget(wnd->mainWnd, wnd);

    XmMainWindowSetAreas(wnd->mainWnd, NULL, NULL, NULL, NULL, wnd->widget);

    XtAddCallback(wnd->widget, XmNexposeCallback, expose, wnd);
    XtAddCallback(wnd->widget, XmNinputCallback, input, wnd);
    XtAddCallback(wnd->widget, XmNresizeCallback, resize, wnd);

    return wnd;
}

/**********/
/* Canvas */
/**********/

extern SWND
swCreateCanvas(const char *title, int x, int y, int width, int height,
             SWND parent)
{
    SWindow *w;

    if (!parent || !parent->widget) return NULL;

    w = newSWindow();
    w->widget = createContainer(title, x, y, width, height, parent->widget);
    XtAddEventHandler(w->widget, EnterWindowMask, False,
                  enterEvent, (XtPointer) w);
    XtAddEventHandler(w->widget, LeaveWindowMask, False,
                  enterEvent, (XtPointer) w);
    XtAddCallback(w->widget, XmNexposeCallback, expose, w);
    XtAddCallback(w->widget, XmNinputCallback, input, w);
    XtAddCallback(w->widget, XmNresizeCallback, resize, w);
    initWidget(w->widget, w);

    return (SWND) w;
}

/*********/
/* Popup */
/*********/

extern SWND
swCreatePopup(const char *title, int x, int y, int width, int height,
            SWND parent)
{
    SWindow     *s;

    if (!parent || !parent->widget) return NULL;

    s = newSWindow();
    s->shell = XtVaCreatePopupShell(title, overrideShellWidgetClass,
                            parent->widget,
                            XmNx, x,
                            XmNy, y,
                            XmNwidth, width,
                            XmNheight, height,
                            XmNvisual, TheVisual->visual,
                            NULL);
    
    s->widget = createContainer(title, x, y, width, height, s->shell);
    initWidget(s->widget, s);

    return s;
}

/*************/
/* ScrollBar */
/*************/

extern SWND
swCreateScrollBar(int flags, int x, int y, int width, int height,
              int min, int max, int value, int visible,
              SWND parent)
{
    SWindow     *sb;
    int           orientation, showArrows;
    Widget  w;
    if (!parent || !parent->widget) return NULL;

    sb = newSWindow();
    orientation = (flags & SW_HORIZONTAL) ? XmHORIZONTAL : XmVERTICAL;
    showArrows = !(flags & SW_NO_ARROWS);

    w = XtVaCreateManagedWidget("scrollbar",
                        xmScrollBarWidgetClass,
                        parent->widget,
                        XmNx, x,
                        XmNy, y,
                        XmNwidth, width,
                        XmNheight, height,
                        XmNminimum, min,
                        XmNmaximum, max,
                        XmNsliderSize, visible,
                        XmNvalue, value,
                        XmNorientation, orientation,
                        XmNshowArrows, showArrows,
                        NULL);

    XtAddCallback(w, XmNvalueChangedCallback, scrollCallback, sb);
    XtAddCallback(w, XmNdragCallback, scrollCallback, sb);
    XtAddCallback(w, XmNincrementCallback, scrollCallback, sb);
    XtAddCallback(w, XmNdecrementCallback, scrollCallback, sb);
    XtAddCallback(w, XmNpageIncrementCallback, scrollCallback, sb);
    XtAddCallback(w, XmNpageDecrementCallback, scrollCallback, sb);
    initWidget(w, sb);

    sb->widget = w;

    return sb;
}

extern void
swScrollBarSetValue(SWND sb, int value)
{
    if (!sb || !sb->widget) return;

    XtVaSetValues(sb->widget, XmNvalue, value, NULL);
}

extern void
swScrollBarSetRange(SWND sb, int min, int max)
{
    if (!sb || sb->widget) return;

    XtVaSetValues(sb->widget, XmNminimum, min, XmNmaximum, max, NULL);
}

extern void
swScrollBarSetVisible(SWND sb, int visible)
{
    if (!sb || !sb->widget) return;

    XtVaSetValues(sb->widget, XmNsliderSize, visible, NULL);
}

/**********/
/* Button */
/**********/

extern SWND
swCreateButton(const char *title, int x, int y, int width, int height,
             SWND parent)
{
    SWindow    *b;
    char       *newTitle;
    char    mnemonic[2];
    
    if (!title || !parent || !parent->widget) return NULL;

    newTitle = malloc(strlen(title) + 1);
    parseMenu(title, newTitle, mnemonic);

    b = newSWindow();
    b->widget = XtVaCreateManagedWidget(newTitle, xmPushButtonWidgetClass,
                              parent->widget,
                              XmNx, x,
                              XmNy, y,
                              XmNwidth, width,
                              XmNheight, height,
                              NULL);

    XtAddCallback(b->widget, XmNactivateCallback, buttonCallback, b);
    initWidget(b->widget, b);

    free(newTitle);
    return b;
}

/************/
/* CheckBox */
/************/

extern SWND
swCreateCheckBox(const char *title, int x, int y, int width, int height,
             SWND parent)
{
    SWindow *b;
    char       *newTitle;
    char    mnemonic[2];

    if (!parent || !parent->widget) return NULL;

    newTitle = malloc(strlen(title) + 1);
    parseMenu(title, newTitle, mnemonic);

    b = newSWindow();
    b->widget = XtVaCreateManagedWidget(newTitle, xmToggleButtonWidgetClass,
                              parent->widget,
                              XmNx, x,
                              XmNy, y,
                              XmNwidth, width,
                              XmNheight, height,
                              XmNmarginWidth, 0,
                              XmNmarginHeight, 0,
                              XmNalignment, XmALIGNMENT_BEGINNING,
                              NULL);

    XtAddCallback(b->widget, XmNvalueChangedCallback, buttonCallback, b);
    initWidget(b->widget, b);
    free(newTitle);

    return b;
}

extern void
swSetCheck(SWND wnd, int check)
{
    if (!wnd) return;

    XtVaSetValues(wnd->widget, XmNset, check, NULL);
}

extern int
swGetCheck(SWND wnd)
{
    Boolean       check;
    if (!wnd) return FALSE;

    XtVaGetValues(wnd->widget, XmNset, &check, NULL);
    return check;
}

/***************/
/* RadioButton */
/***************/

extern SWND
swCreateRadioButton(const char *title, int x, int y, int width, int height,
                SWND parent)
{
    SWindow    *b;
    char       *newTitle;
    char    mnemonic[2];

    if (!parent || !parent->widget) return NULL;
    newTitle = malloc(strlen(title) + 1);
    parseMenu(title, newTitle, mnemonic);

    b = newSWindow();
    b->widget = XtVaCreateManagedWidget(newTitle, xmToggleButtonWidgetClass,
                              parent->widget,
                              XmNx, x,
                              XmNy, y,
                              XmNwidth, width,
                              XmNheight, height,
                              XmNindicatorType, XmONE_OF_MANY,
                              XmNalignment, XmALIGNMENT_BEGINNING,
                              NULL);

    XtAddCallback(b->widget, XmNvalueChangedCallback, buttonCallback, b);
    initWidget(b->widget, b);
    free(newTitle);

    return b;
}

/************/
/* TextEdit */
/************/

static void passwdInput(Widget widget, XtPointer client_data, XtPointer data)
{
    int  len = 0;
    char *passwd = (char *) client_data;
    XmTextVerifyCallbackStruct *cbs = (XmTextVerifyCallbackStruct *) data;

    if (cbs->reason == XmCR_ACTIVATE) {
              return;
    }

    if (cbs->startPos < cbs->currInsert) {/* backspace */
        cbs->endPos = strlen (passwd); /* delete from here to end */
        passwd[cbs->startPos] = 0;
        /* backspace--terminate */
        return;
    }

    if ((cbs->endPos + cbs->text->length + 1) > PASSWORD_LENGTH)
        return;

    strncat (passwd, cbs->text->ptr, cbs->text->length);
    passwd[cbs->endPos + cbs->text->length] = 0;

    for (len = 0; len < cbs->text->length; len++)
        cbs->text->ptr[len] = '*';
 }

extern SWND
swCreateTextEdit(int flags, int x, int y, int width, int height, SWND parent)
{
    SWindow    *t;
    int           editable;
    Arg           av[15];
    int           ac = 0;

    if (!parent || !parent->widget) return NULL;

    t = newSWindow();
    t->password_data[0]=0;
    editable = !(flags & SW_NON_EDITABLE);

    XtSetArg(av[ac], XmNx, x);  ac++;
    XtSetArg(av[ac], XmNy, y);  ac++;
    XtSetArg(av[ac], XmNwidth, width);  ac++;
    XtSetArg(av[ac], XmNheight, height);  ac++;
    XtSetArg(av[ac], XmNborderWidth, 0);  ac++;
    XtSetArg(av[ac], XmNhighlightThickness, 0);  ac++;
    XtSetArg(av[ac], XmNshadowThickness, 0);  ac++;
    XtSetArg(av[ac], XmNeditable, editable);  ac++;
    XtSetArg(av[ac], XmNmarginHeight, 0);  ac++;
    XtSetArg(av[ac], XmNmarginWidth, 0);  ac++;

    if (flags & SW_SINGLE_LINE) {
      t->widget = XtCreateManagedWidget("", xmTextFieldWidgetClass,
                                 parent->widget, av, ac);
    } else {
      XtSetArg(av[ac], XmNeditMode, XmMULTI_LINE_EDIT);  ac++;
      t->widget = XtCreateManagedWidget("", xmTextWidgetClass,
                                 parent->widget, av, ac);
    }


    if (flags & SW_PASSWORD) {
         mlock(t->password_data, PASSWORD_LENGTH);
         XtAddCallback(t->widget, XmNmodifyVerifyCallback, passwdInput,
                       t->password_data);
         XtAddCallback(t->widget, XmNactivateCallback, passwdInput, 
                       t->password_data);
    } else 
         XtAddCallback(t->widget, XmNactivateCallback, textActivateCallback, t);
    XtAddCallback(t->widget, XmNfocusCallback, textFocusCallback, t);
    XtAddCallback(t->widget, XmNlosingFocusCallback, textLosingFocusCallback, t);
    initWidget(t->widget, t);

    return t;
}

extern void
swSetText(SWND wnd, const char *text)
{
    if (!wnd || !text) return;

    XtVaSetValues(wnd->widget, XmNvalue, text, NULL);
}

extern void
swGetText(SWND wnd, char *text, int len)
{
    char          *str;

    if (!wnd || !text) return;

    if (strlen(wnd->password_data) > 0) {
        strncpy(text, wnd->password_data, len);
    } else {
        str = XmTextFieldGetString(wnd->widget);
        strncpy(text, str, len);
        XtFree(str);
    }
}

extern void     
swSetTextCleanupPasswd(SWND wnd) 
{
    int i;
    /* remove password data from memory */
    for (i = 0; i < strlen(wnd->password_data); i++) 
        wnd->password_data[i] = (char) 0;
    munlock(wnd->password_data, PASSWORD_LENGTH);
}

extern void
swTextEditSetSelection(SWND wnd, int start, int end)
{
    if (!wnd) return;

    XmTextFieldSetSelection(wnd->widget, start, end, CurrentTime);
}

/*********/
/* Label */
/*********/

extern SWND
swCreateLabel(const char *title, int x, int y, int width, int height,
            SWND parent)
{
    SWindow    *l;
    char       *newTitle;
    char    mnemonic[2];

    if (!parent || !parent->widget) return NULL;

    l = newSWindow();
    newTitle = malloc(strlen(title) + 1);
    parseMenu(title, newTitle, mnemonic);
    l->widget = XtVaCreateManagedWidget(newTitle, xmLabelWidgetClass,
                              parent->widget,
                              XmNx, x,
                              XmNy, y,
                              XmNwidth, width,
                              XmNheight, height,
                              XmNalignment, XmALIGNMENT_BEGINNING,
                              XmNmarginWidth, 0,
                              XmNmarginHeight, 0,
                              NULL);

    initWidget(l->widget, l);
    free(newTitle);
    return l;
}

extern void
swLabelSetText(SWND wnd, const char *text)
{
    XmString      s;

    if (!wnd) return;

    s = XmStringCreateLocalized((String) text);
    XtVaSetValues(wnd->widget, XmNlabelString, s, NULL);
    XmStringFree(s);
}

/*******************/
/* Scrolled Window */
/*******************/

extern SWND
swCreateScrolledWindow(int x, int y, int width, int height, SWND parent)
{
    SWindow *w;

    if (!parent || !parent->widget) return NULL;

    w = newSWindow();
    w->widget = XtVaCreateManagedWidget("swScrolledWindow",
                              xmScrolledWindowWidgetClass,
                              parent->widget,
                              XmNx, x, XmNy, y,
                              XmNwidth, width,
                              XmNheight, height,
                              XmNscrollBarDisplayPolicy, XmAS_NEEDED,
                              XmNscrollingPolicy, XmAUTOMATIC,
                              NULL);

    initWidget(w->widget, w);
    return (SWND) w;
}

extern void
swScrolledWindowSetChild(SWND wnd, SWND child)
{
    Widget  clipWindow;

    if (!wnd || !child) return;

    XtVaSetValues(wnd->widget, XmNworkWindow, child->widget, NULL);
    XtVaGetValues(wnd->widget, XmNclipWindow, &clipWindow, NULL);
    XtVaSetValues(clipWindow, XmNbackground, 0, NULL);
}

extern void
swGetScrollPosition(SWND wnd, int *x, int *y)
{
    Widget        hsb, vsb;

    if (!wnd) return;

    XtVaGetValues(wnd->widget, XmNhorizontalScrollBar,      &hsb, 
                         XmNverticalScrollBar,  &vsb,
                         NULL);
    
    if (x) XtVaGetValues(hsb, XmNvalue, x, NULL);
    if (y) XtVaGetValues(vsb, XmNvalue, y, NULL);
}

extern void
swSetScrollPosition(SWND wnd, int x, int y)
{
    Widget        hsb, vsb, workWindow;

    if (!wnd) return;

    XtVaGetValues(wnd->widget, XmNhorizontalScrollBar,      &hsb, 
                         XmNverticalScrollBar,  &vsb,
                         XmNworkWindow,         &workWindow,
                         NULL);
    
    XtVaSetValues(hsb, XmNvalue, x, NULL);
    XtVaSetValues(vsb, XmNvalue, y, NULL);
    XtMoveWidget(workWindow, -x, -y);
}

extern void
swGetScrollViewportSize(SWND wnd, int *w, int *h)
{
    Widget        clipWindow;
    Dimension           width, height;

    if (!wnd || !w || !h) return;

    XtVaGetValues(wnd->widget, XmNclipWindow, &clipWindow, NULL);
    XtVaGetValues(clipWindow, XmNwidth, &width, XmNheight, &height, NULL);
    *w = width;
    *h = height;
}

extern void
swSetScrollSizes(SWND wnd, int width, int height)
{
    Widget        workWindow;

    if (!wnd) return;

    if (width <= 0) width = 1;
    if (height <= 0) height = 1;

    XtVaGetValues(wnd->widget, XmNworkWindow, &workWindow, NULL);
    XtVaSetValues(workWindow, XmNwidth, width, XmNheight, height, NULL);
}

/********************/
/* Drawing Contexts */
/********************/

extern SDC
swCreateDC(SWND wnd)
{
    SDContext  *dc;
    char    dash_list[2] = { 5, 5};
    
    if (!wnd || !wnd->widget) return NULL;

    dc = newSDContext();

    dc->gc = XCreateGC(TheDisplay, XtWindow(wnd->widget), 0, NULL);
    dc->drawable = XtWindow(wnd->widget);
    XSetDashes(TheDisplay, dc->gc, 0, dash_list, 2);
    XSetFont(TheDisplay, dc->gc, TheDefaultFont->fs->fid);

    return dc;
}

extern SDC
swCreateBitmapDC(SDC dc, int width, int height)
{
    SDContext  *cdc;
    char    dash_list[2] = { 5, 5};
    
    if (!dc || !dc->drawable) return NULL;

    cdc = newSDContext();

    cdc->drawable = XCreatePixmap(TheDisplay,
                        dc->drawable, width, height,
                        TheVisual->depth);
    cdc->gc = XCreateGC(TheDisplay, cdc->drawable, 0, NULL);
    cdc->isBitmapDC = 1;
    XSetDashes(TheDisplay, cdc->gc, 0, dash_list, 2);
    XSetFont(TheDisplay, cdc->gc, TheDefaultFont->fs->fid);

    return cdc;
}

extern void
swDestroyDC(SDC dc)
{
    if (!dc) return;

    if (dc->isBitmapDC) XFreePixmap(TheDisplay, dc->drawable);
    XFreeGC(TheDisplay, dc->gc);
    free(dc);
}

/*************/
/* GLContext */
/*************/

extern SGLC
swCreateGLContext(SDC dc)
{
    SGLContext    *c;

    if (!dc || !dc->drawable) return NULL;

    c = (SGLContext *) malloc(sizeof(SGLContext));
    c->glxContext = glXCreateContext(TheDisplay, TheVisual, NULL, GL_TRUE);
    return c;
}

extern void
swDestroyGLContext(SGLC context)
{
    if (!context) return;

    glXDestroyContext(TheDisplay, context->glxContext);
    free(context);
}

extern void
swMakeCurrent(SDC dc, SGLC context)
{
    if (!dc || !dc->drawable || !context || !context->glxContext) return;

    glXMakeCurrent(TheDisplay, dc->drawable, context->glxContext);
}

extern void
swSwapBuffers(SDC dc, SGLC context)
{
    if (!dc || !dc->drawable) return;

    glXSwapBuffers(TheDisplay, dc->drawable);
    if (!glXIsDirect(TheDisplay, context->glxContext)) glFinish();
}

extern int
swLoadGLFont(const char *name, int style, float *kernX, float *kernY)
{
     XFontStruct *fontInfo;
     Font id;
     unsigned int first, last;
     int base;

/* you may want to insert something like the following instead of FONT24
 * 
 *  "-Misc-Fixed-Medium-R-Normal--20-200-75-75-C-100-ISO8859-1"
 *  "-Sony-Fixed-Medium-R-Normal--24-170-100-100-C-120-ISO8859-1"
 *  "-adobe-helvetica-medium-r-normal--17-120-100-100-p-88-iso8859-1"
 * if in doubt, use something like 
 *    xlsfonts -ll '*x2?'
 */
     fontInfo = NULL;
#ifdef FONT24
     fontInfo = XLoadQueryFont(TheDisplay,FONT24);
#endif
     if (fontInfo == NULL) {
         fprintf(stderr,"no font found\n");
         return 0;
     }
     id = fontInfo->fid;
     first = fontInfo->min_char_or_byte2;
     last = fontInfo->max_char_or_byte2; 
     base = glGenLists(last+1);
     if (base == 0) {
        fprintf(stderr,"out of display lists\n");
        return 0;
     }
     glXUseXFont(id, first, last-first+1,base+first);
     return base;
}

/**********************/
/* Drawing Attributes */
/**********************/

extern void
swSetFGColor(SDC dc, int color)
{
    if (truecolor_mode) {
        XSetForeground(TheDisplay, dc->gc, swColorToX11Color(color));  
    } else {
        XColor    c;

        c.red = (color & 0x000000ff) << 8;
        c.green = (color & 0x0000ff00);
        c.blue = (color & 0x00ff0000) >> 8;
        XAllocColor(TheDisplay, TheColormap, &c);
        XSetForeground(TheDisplay, dc->gc, c.pixel);
    }
}

extern void
swSetBGColor(SDC dc, int color)
{
    if (truecolor_mode) 
        XSetBackground(TheDisplay, dc->gc, swColorToX11Color(color));
    else {
        XColor    c;

        c.red = (color & 0x000000ff) << 8;
        c.green = (color & 0x0000ff00);
        c.blue = (color & 0x00ff0000) >> 8;
        XAllocColor(TheDisplay, TheColormap, &c);
        XSetBackground(TheDisplay, dc->gc, c.pixel);
    }
}

extern void
swSetFont(SDC dc, SFONT font)
{
    if (!dc || !font) return;

    XSetFont(TheDisplay, dc->gc, font->fs->fid);
    dc->font = font;
}

extern int
swGetFontAscent(SFONT font)
{
    if (!font) return 0;
    return font->fs->ascent;
}

extern int
swGetFontDescent(SFONT font)
{
    if (!font) return 0;
    return font->fs->descent;
}

extern int
swGetFontHeight(SFONT font)
{
    if (!font) return 0;
    return font->fs->ascent + font->fs->descent;
}

extern void
swSetLineStyle(SDC dc, int style)
{
    XGCValues     values;
    if (!dc) return;

    XGetGCValues(TheDisplay, dc->gc, GCLineStyle, &values);

    if (style == SW_SOLID) {
      values.line_style = LineSolid;
    } else if (style == SW_DASH) {
      char  dash_list[2] = { 5, 5};
      values.line_style = LineOnOffDash;
      XSetDashes(TheDisplay, dc->gc, 0, dash_list, 2);
    } else if (style == SW_DOT) {
      char  dash_list[2] = { 1, 1};
      values.line_style = LineOnOffDash;
      XSetDashes(TheDisplay, dc->gc, 0, dash_list, 2);
    }
    XChangeGC(TheDisplay, dc->gc, GCLineStyle, &values);
}

extern void
swSetLineWidth(SDC dc, int width)
{
    XGCValues     values;
    if (!dc) return;

    XGetGCValues(TheDisplay, dc->gc, GCLineWidth, &values);
    values.line_width = width;
    XChangeGC(TheDisplay, dc->gc, GCLineWidth, &values);
}

extern void
swSetCapStyle(SDC dc, int style)
{
    XGCValues     values;
    if (!dc) return;

    XGetGCValues(TheDisplay, dc->gc, GCCapStyle, &values);

    if (style == SW_CAP_ROUND) {
      values.cap_style = CapRound;
    } else if (style == SW_CAP_SQUARE) {
      values.cap_style = CapProjecting;
    } else if (style == SW_CAP_FLAT) {
      values.cap_style = CapButt;
    }
    XChangeGC(TheDisplay, dc->gc, GCCapStyle, &values);
}

extern void
swSetJoinStyle(SDC dc, int style)
{
}

extern void
swSetClipRect(SDC dc, int x, int y, int width, int height)
{
    XRectangle          rect;

    if (!dc) return;

    rect.x = x;  rect.y = y;  rect.width = width;  rect.height = height;
    XSetClipRectangles(TheDisplay, dc->gc, 0, 0, &rect, 1, Unsorted);
}

/***********/
/* Drawing */
/***********/

extern void
swDrawLine(SDC dc, int x1, int y1, int x2, int y2)
{
    if (!dc || !dc->gc) return;

    XDrawLine(TheDisplay, dc->drawable, dc->gc, x1, y1, x2, y2);
}

extern void
swDrawPoint(SDC dc, int x, int y)
{
    if (!dc) return;

    XDrawPoint(TheDisplay, dc->drawable, dc->gc, x, y);
}

extern void
swDrawRect(SDC dc, int x, int y, int width, int height)
{
    if (!dc) return;

    XDrawRectangle(TheDisplay, dc->drawable, dc->gc, x, y, width, height);
}

extern void
swDrawArc(SDC dc, int x, int y, int width, int height,
        float startAngle, float endAngle)
{
    if (!dc) return;

    XDrawArc(TheDisplay, dc->drawable, dc->gc, x, y, width, height,
               (int) (startAngle * 64.0),
               (int) ((endAngle - startAngle) * 64.0));
}

extern void
swFillRect(SDC dc, int x, int y, int width, int height)
{
    if (!dc) return;

    XFillRectangle(TheDisplay, dc->drawable, dc->gc, x, y, width, height);
}

extern void
swFillPatternRect(SDC dc, int x, int y, int width, int height, SBITMAP bitmap)
{
    XGCValues     values;
    if (!dc) return;

    values.tile = bitmap->pixmap;
    values.fill_style = FillTiled;
    XChangeGC(TheDisplay, dc->gc, GCFillStyle | GCTile, &values);
    XFillRectangle(TheDisplay, dc->drawable, dc->gc, x, y, width, height);
    values.fill_style = FillSolid;
    XChangeGC(TheDisplay, dc->gc, GCFillStyle, &values);
}

extern void
swDrawText(SDC dc, int x, int y, const char *str)
{
    if (!dc || !str) return;

    XDrawString(TheDisplay, dc->drawable, dc->gc, x, y, str, strlen(str));
}

extern void
swDrawTextTruncated(SDC dc, int x, int y, int width, int height,
                const char *str)
{
    int                 len;
    XRectangle          rect;
    XFontStruct          *fs;

    if (!dc || !str || width <= 0 || height <= 0) return;

    fs = dc->font->fs;

    len = strlen(str);
    rect.x = x;  rect.y = y;  rect.width = width;  rect.height = height;
    XSetClipRectangles(TheDisplay, dc->gc, 0, 0, &rect, 1, Unsorted);
    if (XTextWidth(fs, str, len) > width) {
      char         *buf = malloc(len + 4);
      strcpy(buf, str);
      strcpy(buf + len, "...");
      while(len > 1 && XTextWidth(fs, buf, len + 3) > width) {
          len--;
          strcpy(buf + len, "...");
      }
      XDrawString(TheDisplay, dc->drawable, dc->gc, x, y + fs->ascent,
                buf, len + 3);
      free(buf);
    } else {
      XDrawString(TheDisplay, dc->drawable, dc->gc, x, y + fs->ascent,
                str, len);
    }
    XSetClipMask(TheDisplay, dc->gc, None);
}

extern void
swDrawBitmap(SDC dc, SBITMAP bitmap, int sx, int sy, int dx, int dy,
           int width, int height)
{
    if (!dc || !bitmap) return;

    XCopyArea(TheDisplay, bitmap->pixmap, dc->drawable, dc->gc,
            sx, sy, width, height, dx, dy);
}

extern void
swDrawMaskedBitmap(SDC dc, SBITMAP bitmap, SBITMAP mask, int sx, int sy,
               int dx, int dy, int width, int height)
{
    if (!dc || !bitmap) return;

    XSetClipMask(TheDisplay, dc->gc, mask->pixmap);
    XSetClipOrigin(TheDisplay, dc->gc, dx - sx, dy - sy);
    XCopyArea(TheDisplay, bitmap->pixmap, dc->drawable, dc->gc,
            sx, sy, width, height, dx, dy);
    XSetClipMask(TheDisplay, dc->gc, None);
}

extern void
swCopyRect(SDC src, SDC dest, int sx, int sy, int dx, int dy,
           int width, int height)
{
    if (!src || !dest) return;

    XCopyArea(TheDisplay, src->drawable, dest->drawable, dest->gc,
            sx, sy, width, height, dx, dy);
}

/********/
/* Font */
/********/

extern SFONT
swFindFont(const char *family, int style, int pointSize)
{
    char          fontspec[1024];
    char          fam[768];
    const char           *weight, *slant;
    SFont          *font;
    XFontStruct          *fs;
    XmFontListEntry     entry;

    strncpy(fam, family, 767);

    weight = (style & SW_BOLD) ? "bold" : "medium";
    slant = (style & SW_ITALIC) ? "i" : "r";

    sprintf(fontspec, "-*-%s-%s-%s-*-*-%d-*-*-*-*-*-*-*",
          fam, weight, slant, pointSize);

    fs = XLoadQueryFont(TheDisplay, fontspec);
    if (fs) {
      font = (SFont *) malloc(sizeof(SFont));
      entry = XmFontListEntryCreate("XmFONTLIST_DEFAULT_TAG", XmFONT_IS_FONT,
                              fs);
      font->fontList = XmFontListAppendEntry (NULL, entry);
      XmFontListEntryFree(&entry);
      font->fs = fs;
      return font;
    } else {
      return NULL;
    }
}

extern void
swDeleteFont(SFONT font)
{
    XmFontListFree(font->fontList);
    XFreeFont(TheDisplay, font->fs);
    free(font);
}

extern SFONT
swGetDefaultFont()
{
    return TheDefaultFont;
}

extern int
swGetStringWidth(SFONT font, const char *str)
{
    if (!font || !str) return 0;
    return XTextWidth(font->fs, str, strlen(str));
}

/********/
/* Menu */
/********/

extern SMENU
swCreateMenuBar(SWND wnd)
{
    SMenu      *menu;
    Arg           av[5];
    int           ac = 0;

    if (!wnd || !wnd->mainWnd) return NULL;

    menu = newSMenu();
    XtSetArg(av[ac], XmNmarginWidth, 0);  ac++;
    XtSetArg(av[ac], XmNmarginHeight, 0);  ac++;
    menu->widget = XmCreateMenuBar(wnd->mainWnd, (String) "", av, ac);
    XmMainWindowSetAreas(wnd->mainWnd, menu->widget, NULL, NULL, NULL,
                   wnd->widget);
    XtAddCallback(menu->widget, XmNdestroyCallback, destroyMenuCallback, menu);
    XtManageChild(menu->widget);
    wnd->menu = menu;
    menu->wnd = wnd;
    return menu;
}

extern void
swMenuSetFlags(SMENU menu, int id, int mask, int flags)
{
    if (!menu) return;
    menu = findMenu(menu->widget, id);
    if (!menu) return;
    menu->flags = (menu->flags & ~mask) | flags;
    XtVaSetValues(menu->widget,
              XmNsensitive, menu->flags & SW_MENU_DISABLED ? False : True,
              XmNset, menu->flags & SW_MENU_CHECKED ? True : False,
              XmNindicatorType, menu->flags & SW_MENU_RADIO_ITEM ? XmONE_OF_MANY : XmN_OF_MANY,
              NULL);
}

extern SMENU
swAppendMenu(SMENU parent, const char *text)
{
    SMenu      *menu;
    Widget  cascade;
    Arg           av[10];
    int           ac = 0;
    char       *newText = malloc(strlen(text));
    char    mnemonic[2];
    
    parseMenu(text, newText, mnemonic);
    mnemonic[1] = '\0';

    if (!parent) return NULL;

    menu = newSMenu();
    XtSetArg(av[ac], XmNvisual, TheVisual->visual);  ac++;

    menu->widget = XmCreatePulldownMenu(parent->widget, newText, av, ac);
    menu->wnd = parent->wnd;
    XtAddCallback(menu->widget, XmNdestroyCallback, destroyMenuCallback, menu);

    ac = 0;
    XtSetArg(av[ac], XmNsubMenuId, menu->widget);  ac++;
    if (mnemonic[0]) {
      XtSetArg(av[ac], XmNmnemonic, XStringToKeysym(mnemonic));  ac++;
    }
    cascade = XmCreateCascadeButton(parent->widget, (String) newText, av, ac);

    XtManageChild(cascade);
    free(newText);

    return menu;
}

extern void
swAppendMenuItem(SMENU parent, const char *text, int id)
{
    if (!parent || !text) return;

    insertMenuItem(parent->widget, parent->wnd, XmLAST_POSITION, text, id);
}

extern void
swInsertMenuItem(SMENU menu, int beforeId, const char *text, int id)
{
    short   pos;
    
    if (!menu || !text) return;
    menu = findMenu(menu->widget, beforeId);
    if (!menu) return;
    XtVaGetValues(menu->widget, XmNpositionIndex, &pos, NULL);

    insertMenuItem(XtParent(menu->widget), menu->wnd, pos, text, id);
}

extern void
swDeleteMenuItem(SMENU menu, int id)
{
    if (!menu) return;
    menu = findMenu(menu->widget, id);
    if (!menu) return;
    XtDestroyWidget(menu->widget);
}

extern void
swAppendSeparator(SMENU parent)
{
    SMenu      *m;
    
    if (!parent) return;
    
    m = newSMenu();
    m->widget = XtVaCreateManagedWidget("---", xmSeparatorWidgetClass,
                              parent->widget,
                              NULL);
    m->wnd = parent->wnd;
    XtAddCallback(m->widget, XmNdestroyCallback, destroyMenuCallback, m);
}

/**********/
/* Bitmap */
/**********/

extern SBITMAP
swCreateBitmap(SWND wnd, int width, int height)
{
    SBitmap     *bitmap;
    if (!wnd) return NULL;

    bitmap = newSBitmap();
    bitmap->pixmap = XCreatePixmap(TheDisplay,
                           DefaultRootWindow(TheDisplay), width, height,
                           TheVisual->depth);
    return bitmap;
}

extern SBITMAP
swCreateMask(SBITMAP bitmap, int width, int height, int bgcolor)
{
    XImage     *src, *dst;
    GC            gc;
    Pixel   pixel;
    SBITMAP mask;
    int           x, y;
    XColor  bg;

    if (!bitmap) return NULL;

    bg.red = (bgcolor & 0x000000ff) << 8;
    bg.green = (bgcolor & 0x0000ff00);
    bg.blue = (bgcolor & 0x00ff0000) >> 8;
    if (truecolor_mode)
       bg.pixel = swColorToX11Color(bgcolor);
    else
       XAllocColor(TheDisplay, TheColormap, &bg);

    mask = newSBitmap();
    mask->pixmap = XCreatePixmap(TheDisplay, bitmap->pixmap,
                         width, height, 1);
    gc = XCreateGC(TheDisplay, mask->pixmap, 0, NULL);

    src = XGetImage(TheDisplay, bitmap->pixmap, 0, 0, width, height,
                AllPlanes, ZPixmap);

/*
    dst = XCreateImage(TheDisplay, TheVisual->visual, 1,
                   XYBitmap, 0, malloc(width * height / 8), width, height, 32, 0);
 */
    dst = XCreateImage(TheDisplay, TheVisual->visual, 1,
                   XYBitmap, 0, malloc(width * height), width, height, 32, 0);

    for (y = 0; y < height; y++) { 
      for (x = 0; x < width; x++) {
          pixel = XGetPixel(src, x, y);
          XPutPixel(dst, x, y, pixel == bg.pixel);
      }
    }
    XPutImage(TheDisplay, mask->pixmap, gc, dst, 0, 0, 0, 0, width, height);
    XDestroyImage(src);
    XDestroyImage(dst);
    XFreeGC(TheDisplay, gc);
    return mask;
}

extern void
swDestroyBitmap(SBITMAP bitmap)
{
    if (!bitmap) return;

    XFreePixmap(TheDisplay, bitmap->pixmap);
    free(bitmap);
}

extern void
swDrawMappedPixels(SBITMAP bitmap, int srcx, int srcy, int dstx, int dsty, 
               int width, int height, const cbyte *data, int numColors,
               const int *palette)
{
    XImage     *image;
    XColor      colors[256];
    int           i;
    char       *d;
    GC            gc;

    if (!bitmap || !data || !palette) return;

    gc = XCreateGC(TheDisplay, bitmap->pixmap, 0, NULL);

    for (i = 0; i < numColors; i++) {
      cbyte       r = SW_RED(palette[i]);
      cbyte       g = SW_GREEN(palette[i]);
      cbyte       b = SW_BLUE(palette[i]);

      colors[i].pixel = i;
      colors[i].red = r << 8;
      colors[i].green = g << 8;
      colors[i].blue = b << 8;
      colors[i].flags = DoRed | DoGreen | DoBlue;
        
       if (truecolor_mode) {
            colors[i].pixel=swColorToX11Color(palette[i]);
        } else 
          if (!XAllocColor(TheDisplay, TheColormap, &colors[i])) {
             fprintf(stderr, "couldn't get color %d\n", i);
      }
    }

    switch (TheVisual->depth) {
      case 8:
      {
      char        *newData = malloc(width * height);
      for (i = 0; i < width * height; i++) {
        newData[i] = (char) colors[(int) data[i]].pixel;
      }
      d = newData;
      break;
      }
      case 12:
      case 15:
      case 16:
      {
      short       *newData = (short *) malloc(width * height * 2);
      for (i = 0; i < width * height; i++) {
        newData[i] = (short) colors[(int) data[i]].pixel;
      }
      d = (char *) newData;
      break;
      }
      case 18:
      case 21:
      case 24:
      case 32:
      {
      int         *newData = (int *) malloc(width * height * 4);
      for (i = 0; i < width * height; i++) {
        newData[i] = (int) colors[(int) data[i]].pixel;
      }
      d = (char *) newData;
      break;
      }
      default:
        fprintf(stderr,"unsupported Visual: depth %d\n",TheVisual->depth);
      XFreeGC(TheDisplay, gc);
      return;
    }
      
    image = XCreateImage(TheDisplay, TheVisual->visual, TheVisual->depth,
                   ZPixmap, 0, d, width, height, 8, 0);

    XPutImage(TheDisplay, bitmap->pixmap, gc, image, srcx, srcy,
            dstx, dsty, width, height);
    XDestroyImage(image);
    XFreeGC(TheDisplay, gc);
}

/**********/
/* cursor */
/**********/

extern SCURSOR
swLoadCursor(int id)
{
    SCursor     *cursor = newSCursor();
   

    switch (id) {
      case SW_CURSOR_ARROW:
      cursor->cursor = XCreateFontCursor(TheDisplay, XC_top_left_arrow);
      break;
      case SW_CURSOR_DBL_ARROW_VERT:
      cursor->cursor = XCreateFontCursor(TheDisplay, XC_sb_v_double_arrow);
      break;
      case SW_CURSOR_DBL_ARROW_HORZ:
      cursor->cursor = XCreateFontCursor(TheDisplay, XC_sb_h_double_arrow);
      break;
      case SW_CURSOR_CROSS:
      cursor->cursor = XCreateFontCursor(TheDisplay, XC_cross);
      break;
      case SW_CURSOR_CIRCLE:
      cursor->cursor = XCreateFontCursor(TheDisplay, XC_circle);
      break;
      case SW_CURSOR_NONE:
      cursor->cursor = XCreatePixmapCursor(TheDisplay, 
              cursorNonePixmap, cursorNonePixmap, 
              &cursorNoneColor, &cursorNoneColor, 0, 0);
      break;
      default:
      cursor->cursor = None;  /* FIXME: load cursor from resource here */
      break;
    }
    return cursor;
}

extern void
swSetCursor(SWND wnd, SCURSOR cursor)
{
    if (!wnd || !wnd->widget || !XtWindow(wnd->widget) || !cursor) return;

    XDefineCursor(TheDisplay, XtWindow(wnd->widget), cursor->cursor);
}

extern void
swDestroyCursor(SCURSOR cursor)
{
    XFreeCursor(TheDisplay, cursor->cursor);
    free(cursor);
}

/************/
/* combobox */
/************/

extern SWND
swCreateComboBox(const char *title, int x, int y, int width, int height,
             SWND parent)
{
    SWindow    *w;
    Arg           av[15];
    int           ac = 0;
    Widget  pulldown;

    if (!parent) return NULL;

    w = newSWindow();
    XtSetArg(av[ac], XmNvisual, TheVisual->visual);  ac++;
    pulldown = XmCreatePulldownMenu(parent->widget, (String) "", av, ac);

    ac = 0;
    XtSetArg(av[ac], XmNx, x);  ac++;
    XtSetArg(av[ac], XmNy, y);  ac++;
    XtSetArg(av[ac], XmNwidth, width);  ac++;
    XtSetArg(av[ac], XmNheight, height);  ac++;
    XtSetArg(av[ac], XmNmarginWidth, 0);  ac++;
    XtSetArg(av[ac], XmNmarginHeight, 0);  ac++;
    XtSetArg(av[ac], XmNsubMenuId, pulldown); ac++;
    XtSetArg(av[ac], XmNorientation, XmVERTICAL); ac++;

    w->widget = XmCreateOptionMenu(parent->widget, (String) title, av, ac);
    initWidget(w->widget, w);
    XtManageChild(w->widget);
    return w;
}

extern void
swComboBoxDeleteAll(SWND list)
{
    Widget  pulldown;
    WidgetList    children;
    int           i, numChildren;

    if (!list) return;

    XtVaGetValues(list->widget, XmNsubMenuId, &pulldown, NULL);
    XtVaGetValues(pulldown, XmNchildren, &children,
                      XmNnumChildren, &numChildren, NULL);
    for (i = 0; i < numChildren; i++) {
      XtUnmanageChild(children[i]);
      XtDestroyWidget(children[i]);
    }
}

extern void
swComboBoxAppendItem(SWND list, const char *str)
{
    Widget  pulldown;
    XmString      s;

    if (!list) return;

    XtVaGetValues(list->widget, XmNsubMenuId, &pulldown, NULL);
    s = XmStringCreateLocalized((String) str);
    XtVaCreateManagedWidget(str, xmPushButtonWidgetClass,
                      pulldown,
                      XmNlabelString, s,
                      NULL);
    XmStringFree(s);
}

extern void
swComboBoxSetSelection(SWND list, int selection)
{
    Widget  pulldown;
    WidgetList    children;
    int           numChildren;

    if (!list) return;

    XtVaGetValues(list->widget, XmNsubMenuId, &pulldown, NULL);
    XtVaGetValues(pulldown, XmNchildren, &children,
                      XmNnumChildren, &numChildren, NULL);
    if (selection >= 0 && selection < numChildren) {
      XtVaSetValues(list->widget, XmNmenuHistory, children[selection], NULL);
    }
}

extern int
swComboBoxGetSelection(SWND list)
{
    Widget  pulldown;
    WidgetList    children;
    int           i, numChildren;
    Widget  selected;

    if (!list) return -1;

    XtVaGetValues(list->widget, XmNmenuHistory, &selected, NULL);
    XtVaGetValues(list->widget, XmNsubMenuId, &pulldown, NULL);
    XtVaGetValues(pulldown, XmNchildren, &children, NULL);
    XtVaGetValues(pulldown, XmNnumChildren, &numChildren, NULL);
    for (i = 0; i < numChildren; i++) {
      if (children[i] == selected) return i;
    }
    return -1;
}

/***********/
/* dialogs */
/***********/

extern SWND
swCreateDialog(const char *title, int x, int y, int width, int height,
             SWND parent)
{
    SWindow        *w;
    Arg                 av[10];
    int                 ac = 0;
    XmString            str;
    Widget        p;

    if (!parent) return NULL;

    p = parent->shell ? parent->shell : parent->widget;

    w = newSWindow();
    XtSetArg(av[ac], XmNx, x);  ac++;
    XtSetArg(av[ac], XmNy, y);  ac++;
    XtSetArg(av[ac], XmNwidth, width);  ac++;
    XtSetArg(av[ac], XmNheight, height);  ac++;
    XtSetArg(av[ac], XmNautoUnmanage, False);  ac++;
    XtSetArg(av[ac], XmNvisual, TheVisual->visual); ac++;
    str = XmStringCreateLocalized((String) title);
    XtSetArg(av[ac], XmNdialogTitle, str);  ac++;
    XtSetArg(av[ac], XmNdeleteResponse, XmDO_NOTHING);  ac++;

    w->widget = XmCreateBulletinBoardDialog(p, (String) title, av, ac);
    XmStringFree(str);
    /* set up a delete response */
    XmAddWMProtocolCallback(XtParent(w->widget), WM_DELETE_WINDOW,
                      onDeleteDialog, w);
    initWidget(w->widget, w);
    return w;
}

extern int
swDialog(SWND dialog)
{
    int           rc;

    if (!dialog) return 0;

    XtVaSetValues(dialog->widget, XmNdialogStyle,
              XmDIALOG_PRIMARY_APPLICATION_MODAL, NULL);
    XtManageChild(dialog->widget);
    rc = swModalLoop();
    XtUnmanageChild(dialog->widget);
    return rc;
}

extern void
swEndDialog(int rc)
{
    swExitModalLoop(rc);
}

extern SWND
swGetDialogItem(SWND dialog, int item)
{
    int           i, numChildren;
    WidgetList    children;

    if (!dialog) return NULL;

    XtVaGetValues(dialog->widget, XmNnumChildren, &numChildren,
                          XmNchildren, &children,
                          NULL);
    for (i = 0; i < numChildren; i++) {
      SWND        wnd;

      XtVaGetValues(children[i], XmNuserData, &wnd, NULL);
      if (wnd && wnd->data == (void *) item) {
          return wnd;
      }
    }

    return NULL;
}

extern void
swSetDefaultButton(SWND dialog, SWND button)
{
    if (!dialog || !button) return;

    XtVaSetValues(dialog->widget, XmNdefaultButton, button->widget, NULL);
}

extern void
swSetCancelButton(SWND dialog, SWND button)
{
    if (!dialog || !button) return;

    XtVaSetValues(dialog->widget, XmNcancelButton, button->widget, NULL);
}

void swChAppleDir(void)
{
#ifdef MACOSX
/*
 *  MacOSX programs started from Desktop use "/" as default path,
 *  so change directory to $HOME/Desktop first
 */
    char *home;
    char *path;
    char *desktop = "/Desktop";
    home = getenv("HOME");
    if (home != NULL) {
        path = (char *) malloc(strlen(home)+strlen(desktop)+1);
        strcpy(path, home);
        strcat(path, desktop);        
        chdir(path);
        free(path);
    }
#endif
}

extern int
swOpenDirDialog(SWND wnd, const char *title, char *filters,
             char *buf, int len)
{
    return doFileDialog(title, wnd, filters, buf, len, FILE_MUST_EXIST, NULL);
}


extern int
swOpenFileDialog(SWND wnd, const char *title, char *filters,
             char *buf, int len)
{
    return doFileDialog(title, wnd, filters, buf, len, NOT_A_DIRECTORY, NULL);
}

extern int
swSaveFileDialog(SWND wnd, const char *title, char *filters,
             char *buf, int len, char* ext)
{
    return doFileDialog(title, wnd, filters, buf, len, OVERWRITE_PROMPT, ext);
}

/***************/
/* message box */
/***************/

extern int
swMessageBox(SWND parent, const char *text, const char *title, int type,
           int icon)
{
    Widget        top;

    if (!parent) return 0;

    top = parent->shell ? parent->shell : parent->widget;
    return messageBox(top, text, title, type, icon);
}

/********************/
/* debugging output */
/********************/


extern void
swDebugf(const char *fmt, ...)
{
    va_list ap;
    char message[1024];

    va_start(ap, fmt);
    myvsnprintf(message, 1024, fmt, ap);
    fprintf(stderr, "%s", message);
}

extern double
swGetCurrentTime(void)
{
    struct timeval      now;

    gettimeofday(&now, NULL);
    return (double) now.tv_sec + (double) now.tv_usec / 1000000.0;
}

extern void
swGetTempFile(char *buf, char* name, char* ext, int len)
{
    static int tempCounter = 0;
 
    snprintf(buf,len,"%s_%d_%d%s",name,getpid(),++tempCounter,ext);
}

extern void
swGetTempDir(char *buf, int len)
{
    strncpy(buf, "/tmp/", len);
}

extern void
swGetTempPath(char *buf, char* name, char* ext,int len)
{
    swGetTempDir(buf,len);
    swGetTempFile(buf+strlen(buf),name,ext,len-strlen(buf));
}

extern int 
swIsXML(const char *filename)
{
    int i;

    if (strlen(filename)==0)
       return(0);
    for (i=strlen(filename)-1;(i>0)&&(filename[i]!='.');i--);
    return !strcmp(&filename[i],".xml");
}

extern char*
swKillFileQuotes(char* filename)
{
    return filename;
}
      
extern char*
swGetEnv(const char* variablename)
{
    return getenv(variablename);
}

extern char*
swGetToNullDevice(void)
{
    return " > /dev/null";
}


extern int
swMkdirCommand(SWND wnd,const char* dir)
{
    char* string;
    char* mkdir="mkdir -p ";
    int ret;

    string=(char*)malloc(strlen(mkdir)+strlen(dir)+1);
    if (string==NULL) return 1;
    strcpy(string,mkdir);
    strcat(string,dir);
    ret=system(string);
    free(string);
    return ret;
}    

extern int
swCopyCommand(SWND wnd,const char* file,const char* dir)
{
    char* string;
    char* cp="cp ";
    int ret;

    string=(char*)malloc(strlen(cp)+strlen(file)+1+strlen(dir)+1);
    if (string==NULL) return 1;
    strcpy(string,cp);
    strcat(string,file);
    strcat(string," ");
    strcat(string,dir);
    ret=system(string);
    free(string);
    return ret;
}    

extern int             
swShellCommand(SWND wnd, const char* file, const char* command, 
               const char* directory)
{
    char* string;
    char* cd="cd ";
    int ret;

    string=(char*)malloc(strlen(cd)+strlen(directory)+1+strlen(command)+1);
    if (string==NULL) return 1;
    strcpy(string,cd);
    strcat(string,directory);
    strcat(string,";");
    strcat(string,command);
    ret=system(string);
    free(string);
    return ret;
}

extern void 
swDeleteCallbacks(SWND wnd)
{
    if (wnd==NULL) return;
    wnd->exposeCallback = NULL;
    wnd->mouseCallback = NULL;
    wnd->keyCallback = NULL;
    wnd->focusCallback = NULL;
    wnd->resizeCallback = NULL;
    wnd->enterCallback = NULL;
    wnd->scrollBarCallback = NULL;
    wnd->commandCallback = NULL;
    wnd->highlightCallback = NULL;
    wnd->closeCallback = NULL;
    wnd->dropCallback = NULL;
}

/**************************/
/* persistent preferences */
/**************************/

extern STABLE
swLoadPreferences(const char *company, const char *appName)
{
    char    line[MAX_PREFLINE], *c, *key, *value;
    const char *home = getenv("HOME");
    FILE       *f;
    STable     *table;
    int           len;

    /* company is (intentionally) ignored */

    if (!appName) return NULL;
    
    len = strlen(home) + strlen(appName) + 5;

    table = (STable *) malloc(sizeof(STable));
    table->size = 0;
    table->capacity = 8;
    table->filename = (char *) malloc(len);
    snprintf(table->filename, len, "%s/.%src", home, appName);

    table->data = (Pair *) malloc(table->capacity * sizeof(Pair));

    f = fopen(table->filename, "r");

    if (!f) return table;

    while (fgets(line, MAX_PREFLINE, f))
    {
      if (line[0] == '#') continue;
      c = line;
      while(isspace(*c)) c++;
      if (!*c) continue;
      key = c;
      while(*c && !isspace(*c)) c++;
      *c = '\0';
      c++;
      while(isspace(*c)) c++;
      value = c;
      while(*c && *c != '\n') c++;
      if (*c == '\n') *c = '\0';
      appendPair(table, key, value);
    }
    fclose(f);

    return table;
}

extern void
swSavePreferences(STABLE table)
{
    int         f;
    int           i;

    f = open(table->filename, O_WRONLY | O_CREAT | O_TRUNC
                 ,S_IRUSR | S_IWUSR);
    if (f==-1) return;

    for (i = 0; i < table->size; i++) {
      mywritestr(f,table->data[i].key);
      mywritestr(f," ");
      mywritestr(f,table->data[i].value);
      mywritestr(f,"\n");
    }
    close(f);
}

extern void
swDeletePreferences(STABLE table)
{
    int           i;

    for (i = 0; i < table->size; i++) {
      free(table->data[i].key);
      free(table->data[i].value);
    }
    free(table->filename);
    free(table->data);
    free(table);
}

extern const char *
swGetPreference(STABLE table, const char *key, const char *defaultValue)
{
    int           i;

    for (i = 0; i < table->size; i++) {
      if (!strcmp(table->data[i].key, key)) {
          return table->data[i].value;
      }
    }
    return defaultValue;
}

extern void
swSetPreference(STABLE table, const char *key, const char *value)
{
    int           i;

    for (i = 0; i < table->size; i++) {
      if (!strcmp(table->data[i].key, key)) {
          free(table->data[i].value);
          table->data[i].value = strdup(value);
          return;
      }
    }
    appendPair(table, key, value);
}

extern int
swGetIntPreference(STABLE table, const char *key, int defaultValue)
{
    int           i;

    for (i = 0; i < table->size; i++) {
      if (!strcmp(table->data[i].key, key)) {
          return atoi(table->data[i].value);
      }
    }
    return defaultValue;
}

extern void
swSetIntPreference(STABLE table, const char *key, int value)
{
    char    str[20];
    sprintf(str, "%d", value);
    swSetPreference(table, key, str);
}

/*****************/
/* mouse capture */
/*****************/
extern void
swSetCapture(SWND wnd)
{
    if (!wnd) return;

    XtGrabPointer(wnd->widget, False,
              PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
              GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
}

extern void
swReleaseCapture(SWND wnd)
{
    if (!wnd) return;

    XtUngrabPointer(wnd->widget, CurrentTime);
}

/****************/
/* accelerators */
/****************/

extern void
swAddAccelerator(SWND wnd, int key, int modifiers, int id)
{
    int                 state = 0;
    AccelTable           *table;
    Accelerator          *accel;

    if (!wnd) return;

    if (modifiers & SW_SHIFT) state |= ShiftMask;
    if (modifiers & SW_CONTROL) state |= ControlMask;
    if (modifiers & SW_ALT) state |= Mod1Mask;

    table = wnd->accelTable;
    if (!table) {
      table = (AccelTable *) malloc(sizeof(AccelTable));
      table->size = 0;
      table->capacity = 8;
      table->data = (Accelerator *) malloc(8 * sizeof(Accelerator));
      wnd->accelTable = table;
    }
    if (table->size == table->capacity) {
      table->capacity *= 2;
      table->data = (Accelerator *)
            realloc(table->data, table->capacity * sizeof(Accelerator));
    }
    accel = table->data + table->size;

/*  
 * quick and dirty bugfix from 
 *  http://sourceforge.net/tracker/index.php?func=detail&aid=205443&group_id=3356&atid=103356
 *  MUFTI
 */
    if (key<=256) 
       accel->key = _invKeycodeMap[tolower(key)]; 
    else 
       accel->key = _invKeycodeMap[key]; 

    accel->state = state;
    accel->id = id;
    table->size++;
}

/*****************/
/* drag und drop */
/*****************/

#ifdef MOTIF_DRAGNDROP

extern void
swRegisterDropTarget(SWND wnd, DropCallback dropCB)
{
    Arg                 av[10];
    int                 ac = 0;
    Atom          importTargets[1] = { XA_STRING };

    if (!wnd || !dropCB) return;

    wnd->dropCallback = dropCB;
    XtVaSetValues(XmGetXmDisplay(TheDisplay),
              XmNdragReceiverProtocolStyle,  XmDRAG_DYNAMIC,
              XmNdragInitiatorProtocolStyle, XmDRAG_DYNAMIC,
              NULL);
    XtSetArg(av[ac], XmNdropSiteOperations,
                 XmDROP_MOVE | XmDROP_COPY | XmDROP_LINK);  ac++;
    XtSetArg(av[ac], XmNimportTargets, importTargets);  ac++;
    XtSetArg(av[ac], XmNnumImportTargets, 1);  ac++;
    XtSetArg(av[ac], XmNdropProc, dropProc);  ac++;
    XtSetArg(av[ac], XmNdragProc, dragProc);  ac++;
    XmDropSiteRegister(wnd->widget, av, ac);
    XtVaSetValues(wnd->widget, XmNuserData, wnd, NULL);
}

extern void
swUnregisterDropTarget(SWND wnd)
{
    XmDropSiteUnregister(wnd->widget);
    wnd->dropCallback = NULL;
}

extern int
swDragDrop(SWND wnd, int operations, SBITMAP bitmap, SBITMAP mask,
         int x, int y, int width, int height)
{
    XEvent  event;
    Arg           av[15];
    int           ac = 0;
    Atom    exportTargets[1] = { XA_STRING };
    Widget  icon = NULL;

    if (!wnd || !bitmap || !mask) return 0;

    if (bitmap) {
      Pixmap            pixmap, mask;
      createSourceCursor(bitmap->pixmap, x, y, width, height,
                     wnd->widget, &pixmap, &mask);
      icon = createDragIcon(wnd->widget, pixmap, mask, width, height);
      XtSetArg(av[ac], XmNsourceCursorIcon, icon);  ac++;
    }

    TheCopyIcon = createDragIcon(wnd->widget, TheCopyPixmap, 0,
                         copy_width, copy_height);
    TheLinkIcon = createDragIcon(wnd->widget, TheLinkPixmap, 0,
                         link_width, link_height);
    TheMoveIcon = createDragIcon(wnd->widget, TheMovePixmap, 0, 1, 1);

    XtSetArg(av[ac], XmNcursorBackground, 0); ac++;
    XtSetArg(av[ac], XmNconvertProc, convertProc); ac++;
    XtSetArg(av[ac], XmNexportTargets, exportTargets); ac++;
    XtSetArg(av[ac], XmNnumExportTargets, 1);  ac++;
    XtSetArg(av[ac], XmNdragOperations, opToMop(operations));  ac++;
    XtSetArg(av[ac], XmNclientData, wnd);  ac++;
    TheDragContext = XmDragStart(wnd->widget, &TheLastButtonPress, av, ac);
    XtAddCallback(TheDragContext, XmNdragDropFinishCallback, dragFinish, wnd);

    InDragNDrop = TRUE;

    while (InDragNDrop)
    {
      XtAppProcessEvent(TheAppContext, XtIMAll);
      if (XtAppPending(TheAppContext) == 0) {
          update();
      }
    }
    XtDestroyWidget(TheCopyIcon);
    XtDestroyWidget(TheLinkIcon);
    XtDestroyWidget(TheMoveIcon);
    if (icon) XtDestroyWidget(icon);
    return 0;
}

#else /* !MOTIF_DRAGNDROP */

extern void
swRegisterDropTarget(SWND wnd, DropCallback dropCB)
{
    if (!wnd || !dropCB) return;

    wnd->dropCallback = dropCB;
}

extern void
swUnregisterDropTarget(SWND wnd)
{
    wnd->dropCallback = NULL;
}

extern int
swDragDrop(SWND wnd, int operations, SBITMAP bitmap, SBITMAP mask,
         int x, int y, int width, int height)
{
    int           rx, ry, wx, wy;
    unsigned      state;
    Window  root, child;

    if (!wnd || !bitmap || !mask) return 0;

    XQueryPointer(TheDisplay, XtWindow(wnd->widget), &root, &child, &rx, &ry,
              &wx, &wy, &state);
                   
    TheDragPopup = XtVaCreatePopupShell("swDragWindow",
                               overrideShellWidgetClass,
                               XtParent(wnd->widget),
                               XmNwidth, width,
                               XmNheight, height,
                               XmNborderWidth, 0,
                               XmNx, rx, XmNy, ry,
                               XmNvisual, TheVisual->visual,
                               NULL);
    
    TheDragWindow = createContainer("", 0, 0, width, height, TheDragPopup);

    XtAddCallback(TheDragWindow, XmNexposeCallback, exposeDragDropWindow, NULL);

    TheDragPixmapOffsetX = x;
    TheDragPixmapOffsetY = y;
    TheDragPixmap = bitmap->pixmap;
    TheDragMask = mask->pixmap;

    XtPopup(TheDragPopup, XtGrabNone);

    InDragNDrop = TRUE;

    while (InDragNDrop)
    {
      XtAppProcessEvent(TheAppContext, XtIMAll);
      if (XtAppPending(TheAppContext) == 0) {
          update();
      }
    }
    return 0;
}

#endif /* !MOTIF_DRAGNDROP */

/********************/
/* static functions */
/********************/

static Widget
createContainer(const char *title, int x, int y, int width, int height,
            Widget parent)
{
    return XtVaCreateManagedWidget(title, xmDrawingAreaWidgetClass,
                           parent,
                           XmNx, x, XmNy, y,
                           XmNwidth, width,
                           XmNheight, height,
                           XmNmarginWidth, 0,
                           XmNmarginHeight, 0,
                           XmNresizePolicy, XmRESIZE_NONE,  /* ignore resize requests */
                                                    /* from children */
                           XtNvisual, TheVisual->visual,
                           NULL);

}

static SWindow *
newSWindow()
{
    SWindow *wnd = malloc(sizeof(SWindow));
    wnd->widget = NULL;
    wnd->shell = NULL;
    wnd->mainWnd = NULL;
    wnd->menu = NULL;
    wnd->accelTable = NULL;
    wnd->data = NULL;
    wnd->exposeCallback = NULL;
    wnd->mouseCallback = NULL;
    wnd->keyCallback = NULL;
    wnd->focusCallback = NULL;
    wnd->resizeCallback = NULL;
    wnd->enterCallback = NULL;
    wnd->scrollBarCallback = NULL;
    wnd->commandCallback = NULL;
    wnd->highlightCallback = NULL;
    wnd->closeCallback = NULL;
    wnd->dropCallback = NULL;
    wnd->invx1 = wnd->invx2 = wnd->invy1 = wnd->invy2 = -1;

    return wnd;
}

static SMenu *
newSMenu()
{
    SMenu *menu = malloc(sizeof(SMenu));
    menu->widget = NULL;
    menu->wnd = NULL;
    menu->id = -1;
    menu->flags = 0;

    return menu;
}

static SDContext *
newSDContext()
{
    SDContext *dc = malloc(sizeof(SDContext));
    dc->gc = NULL;
    dc->drawable = 0;
    dc->isBitmapDC = 0;
    dc->font = TheDefaultFont;

    return dc;
}

static SBitmap *
newSBitmap()
{
    SBitmap *bitmap = (SBitmap *) malloc(sizeof(SBitmap));
    bitmap->pixmap = 0;

    return bitmap;
}

static SCursor *
newSCursor()
{
    SCursor *cursor = (SCursor *) malloc(sizeof(SCursor));
    cursor->cursor = 0;

    return cursor;
}

static STimer *
newSTimer(int timeout, TimerCallback cb, void *data)
{
    STimer *timer = (STimer *) malloc(sizeof(STimer));

    timer->timeout = timeout;
    timer->timerCallback = cb;
    timer->data = data;

    return timer;
}

static void
initWidget(Widget widget, SWND wnd)
{
    SWND    mainWnd = findMainWindow(widget);

    XtVaSetValues(widget, XmNuserData, wnd, NULL);
    XtAddCallback(widget, XmNdestroyCallback, destroyCallback, wnd);
    if (mainWnd && mainWnd->accelTable) {
      XtInsertEventHandler(widget, KeyPressMask,
                       False, acceleratorHandler, (XtPointer) mainWnd,
                       XtListHead);
    }
}

static void
resize(Widget widget, XtPointer data, XtPointer event_data)
{
    SWindow        *w = (SWindow *) data;
    Dimension           width, height;

    if (!w) return;

    XtVaGetValues(widget, XmNwidth, &width, XmNheight, &height, NULL);
    if (w->resizeCallback) {
      w->resizeCallback(w->data, width, height);
    }
}

static void
scrollCallback(Widget widget, XtPointer data, XtPointer event_data)
{
    SWindow  *sb = (SWindow *) data;
    XmScrollBarCallbackStruct *cbs = (XmScrollBarCallbackStruct *) event_data;
    int           type;

    if (!sb || !sb->scrollBarCallback || !cbs) return;

    switch (cbs->reason)
    {
      case XmCR_VALUE_CHANGED:
      case XmCR_DRAG:
      type = SW_SB_SCROLL;
      break;
      case XmCR_INCREMENT:
      type = SW_SB_LINE_DOWN;
      break;
      case XmCR_DECREMENT:
      type = SW_SB_LINE_UP;
      break;
      case XmCR_PAGE_INCREMENT:
      type = SW_SB_PAGE_DOWN;
      break;
      case XmCR_PAGE_DECREMENT:
      type = SW_SB_PAGE_UP;
      break;
      default:
      type = SW_SB_SCROLL;
      break;
    }

    (sb->scrollBarCallback)(sb->data, type, cbs->value);
}

static void
buttonCallback(Widget widget, XtPointer data, XtPointer event_data)
{
    SWindow        *b = (SWindow *) data;
    unsigned char type;

    if (!b) return;

    if (XtClass(widget) == xmToggleButtonWidgetClass) {
      XtVaGetValues(widget, XmNindicatorType, &type, NULL);
      if (type == XmONE_OF_MANY) {
          XtVaSetValues(widget, XmNset, True, NULL);
          resetRadioButtons(widget);
      }
    }
    callCommandCallback(b->widget, (int)b->data);
}

static void
textActivateCallback(Widget widget, XtPointer data, XtPointer event_data)
{
    SWindow *wnd = (SWindow *) data;
    if (!wnd) return;

    callCommandCallback(wnd->widget, (int)wnd->data);
}

static void
textFocusCallback(Widget widget, XtPointer data, XtPointer event_data)
{
    SWindow    *w = (SWindow *) data;
    if (!w || !w->focusCallback) return;

    w->focusCallback(w->data, TRUE);
}

static void
textLosingFocusCallback(Widget widget, XtPointer data, XtPointer event_data)
{
    SWindow    *w = (SWindow *) data;
    if (!w || !w->focusCallback) return;

    w->focusCallback(w->data, FALSE);
}

static void
destroyCallback(Widget widget, XtPointer data, XtPointer event_data)
{
    SWindow    *wnd = (SWindow *) data;

    if (wnd->accelTable) {
      free(wnd->accelTable->data);
      free(wnd->accelTable);
    }
    free(wnd);
}

static void
destroyMenuCallback(Widget widget, XtPointer data, XtPointer event_data)
{
   free((SMenu *) data);
}

static void
menuActivateCallback(Widget widget, XtPointer data, XtPointer event_data)
{
    SMenu *m = (SMenu *) data;
    if (m) {
      XtVaSetValues(m->widget,
                  XmNset, m->flags & SW_MENU_CHECKED ? True : False, NULL);
      
      callCommandCallback(m->wnd->widget, m->id);
    }
}

static void
menuArmCallback(Widget widget, XtPointer data, XtPointer event_data)
{
    SMenu *m = (SMenu *) data;
    if (m) {
      callHighlightCallback(m->wnd->widget, m->id);
    }
}

static void
menuDisarmCallback(Widget widget, XtPointer data, XtPointer event_data)
{
    SMenu *m = (SMenu *) data;
    if (m) {
      callHighlightCallback(m->wnd->widget, -1);
    }
}

/* 
   Make a shell like regular expressions like "*.[wx][rm][ll]"
   from a Windows like "*.wrl;*.xml" filetypelist
*/

char *mkfilter(char* filters)
{
    int           i,j;
    char*       filter;
    int         numpatterns;
    char**      patterns;
    int         maxlen;
    char*       ret;
    int         nextcharto;
    int     abortflag=FALSE;

   filter=strdup(filters);
   /* how much patterns ? */
   numpatterns=1;
   strtok(filter,";");
   while (strtok(NULL,";")!=NULL)
      numpatterns++;
   patterns=(char**) malloc((numpatterns+1)*sizeof(char*));
   free(filter);
   filter=strdup(filters);
   /* split string into pattern */
   patterns[i=0]=strtok(filter,";");
   while ((patterns[++i]=strtok(NULL,";"))!=NULL);
   /* how long can be returnvalue string ? */
   maxlen=0;
   for (i=0;i<numpatterns;i++)
      if (strlen(patterns[i])>maxlen)
         maxlen=strlen(patterns[i]);
   /* build return value */
   nextcharto=0;
   ret=(char*) malloc(numpatterns*(maxlen+2)+1);
   for (i=0;i<maxlen;i++) {
      /* collect characters to a [123] string */
      int samecharactersflag=TRUE;
      int nextcharforward=0;
      ret[nextcharto+nextcharforward++]='[';
      for (j=0;j<numpatterns;j++) {
         /* test if pattern ended */
         if (i>=strlen(patterns[j])) {
            abortflag=TRUE;
            break;
         }
         ret[nextcharto+nextcharforward++]=patterns[j][i];         
         if (j!=0)
            if (patterns[j][i]!=patterns[j-1][i])
               samecharactersflag=FALSE;
      }
      if (abortflag==TRUE) {
         ret[nextcharto++]='*';
         break;
      }
      ret[nextcharto+nextcharforward++]=']';
      /* if all characters are the same, use this character instead */
      if (samecharactersflag==TRUE) 
         ret[nextcharto++]=patterns[0][i];
      else
         nextcharto+=nextcharforward;
   }
   ret[nextcharto]=0;
   free(filter);
   free(patterns);
   return ret;
}

static int
doFileDialog(const char *title, SWND wnd, char *filters, char *buf,
           int len, int flags, char* ext)
{
    Widget  dlg;
    Arg           av[10];
    int           ac = 0;
    XmString      str, tstr;
    char       *b;
    int           rc;
    int           cont;

    if (!wnd || !buf || !filters) return 0;

    swChAppleDir();

    filters = strdup(filters + strlen(filters) + 1);
    if (strchr(filters, ';')) {
#ifdef MOTIF_FILE_REGEX
       filters=mkfilter(filters);
#else
       filters=strdup("*");
#endif
    } else {
       filters=strdup(filters);
    }
    str = XmStringCreateLocalized((String) filters);
    XtSetArg(av[ac], XmNpattern, str);  ac++;
    tstr = XmStringCreateLocalized((String) title);
    XtSetArg(av[ac], XmNdialogTitle, tstr);  ac++;
    XtSetArg(av[ac], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ac++;
    XtSetArg(av[ac], XmNdeleteResponse, XmDO_NOTHING);  ac++;
    XtSetArg(av[ac], XmNvisual, TheVisual->visual);  ac++;
    dlg = XmCreateFileSelectionDialog (wnd->shell ? wnd->shell : wnd->widget,
                               (String) title, av, ac);
    XtAddCallback(dlg, XmNokCallback, dlgBtnCallback, (XtPointer) 1);
    XtAddCallback(dlg, XmNcancelCallback, dlgBtnCallback, (XtPointer) 2);
    XmAddWMProtocolCallback(XtParent(dlg), WM_DELETE_WINDOW, dlgBtnCallback,
                      (XtPointer) 2);
    XtManageChild(dlg);
    XmStringFree(str);
    XmStringFree(tstr);
    do {
      cont = FALSE;
      rc = swModalLoop();
      if (rc == 1) {
          XtVaGetValues(dlg, XmNtextString, &str, NULL);
          XmStringGetLtoR(str, XmFONTLIST_DEFAULT_TAG, &b);
          strncpy(buf, b, len);
          XtFree(b);
          XmStringFree(str);

            /* attach default file extension, if no extension given */
            if (ext!=NULL) {
                char* path=strdup(buf);
                char* freepath=path;
                while (strstr(path,"/")!=NULL)
                    path=strstr(path,"/")+1; 
                if (strstr(path,".")==NULL)
                  if (strlen(buf)<strlen(ext))
                      strcat(buf,ext);
                  else if (strcasecmp(buf+strlen(buf)-strlen(ext),ext)!=0)
                    if ((strlen(buf)+strlen(ext))<len)
                          strcat(buf,ext);
                free(freepath);
            }

          if ( ((flags & (FILE_MUST_EXIST)) || (flags & (NOT_A_DIRECTORY)))
                 && !fileExists(buf)) {
            char  msg[1024];
            mysnprintf(msg, 1024, "%s:  %s\n", buf, strerror(errno));
            rc = messageBox(XtParent(dlg), msg, title, SW_MB_OK,
                        SW_MB_WARNING);
            cont = TRUE;
          }
          if (flags & OVERWRITE_PROMPT && fileExists(buf)) {
            char  msg[1024];
            mysnprintf(msg, 1024, "Replace existing file %s?", buf);
            rc = messageBox(XtParent(dlg), msg, title, SW_MB_YESNO,
                        SW_MB_WARNING);
            if (rc == IDYES) {
                rc = IDOK;
            } else if (rc == IDNO || rc == IDCANCEL) {
                rc = IDCANCEL;
                cont = TRUE;
            }
          }
          if (flags & NOT_A_DIRECTORY && dirExists(buf)) {
            char  msg[1024];
            mysnprintf(msg, 1024, 
                    "choose a %s file, not a directory: %s\n", filters, buf);
            rc = messageBox(XtParent(dlg), msg, title, SW_MB_OK,
                        SW_MB_WARNING);
            cont = TRUE;
          }
      }
    } while (cont);
    XtDestroyWidget(dlg);
    free(filters);
    return rc == 1;
}

#ifndef MOTIF_DRAGNDROP
static SWND
findDropTarget(SWND wnd)
{
    Widget  widget;

    if (wnd->dropCallback) return wnd;

    for (widget = wnd->widget; widget != NULL; widget = XtParent(widget))
    {
      XtVaGetValues(widget, XmNuserData, &wnd, NULL);
      if (wnd && wnd->dropCallback) return wnd;
    }
    return NULL;
}

static void
doDragOver(SWindow *wnd, int x, int y, int modifiers)
{
    SWindow        *target = findDropTarget(wnd);
    if (target != NULL) {
      int operation = target->dropCallback(target->data,
                                   SW_DRAG_OVER, x, y, modifiers);
      updateCursor(XtWindow(wnd->widget), operation);
    }
}

static void
doDrop(SWindow *wnd, int x, int y, int modifiers)
{
    SWindow        *target = findDropTarget(wnd);
    if (target != NULL) {
      target->dropCallback(target->data, SW_DROP, x, y, modifiers);
      InDragNDrop = FALSE;
      XUndefineCursor(TheDisplay, XtWindow(wnd->widget));
      XtDestroyWidget(TheDragPopup);
    }
}
#endif

static void
input(Widget widget, XtPointer data, XtPointer event_data)
{
    SWindow        *w = (SWindow *) data;
    XmDrawingAreaCallbackStruct     *cbs =
      (XmDrawingAreaCallbackStruct *) event_data;
    XEvent         *event = (XEvent *) cbs->event;

    if (!w) return;

    if (event->type == MotionNotify) {
      XMotionEvent *e = (XMotionEvent *) event;
#ifndef MOTIF_DRAGNDROP
      if (InDragNDrop) {
          XtMoveWidget(TheDragPopup, e->x_root - 24, e->y_root - 8);
          doDragOver(w, e->x, e->y, getModifiers(e->state));
      } else
#endif
      if (w->mouseCallback)
          (w->mouseCallback)(w->data, e->x, e->y, getModifiers(e->state));
    } else if (event->type == KeyPress) {
      XKeyEvent *e = (XKeyEvent *) event;
      KeySym            k = XKeycodeToKeysym(e->display, e->keycode, 0);
#ifndef MOTIF_DRAGNDROP
      if (InDragNDrop) {
          doDragOver(w, e->x, e->y, queryModifiers());
      } else
#endif
      if (w->keyCallback)
          (w->keyCallback)(w->data, _keycodeMap[k], TRUE, e->x, e->y, getModifiers(e->state));
    } else if (event->type == KeyRelease) {
      XKeyEvent *e = (XKeyEvent *) event;
      KeySym            k = XKeycodeToKeysym(e->display, e->keycode, 0);
#ifndef MOTIF_DRAGNDROP
      if (InDragNDrop) {
          doDragOver(w, e->x, e->y, queryModifiers());
      } else
#endif
      if (w->keyCallback)
          (w->keyCallback)(w->data, _keycodeMap[k], FALSE, e->x, e->y, 
                       getModifiers(e->state));
    } else if (event->type == ButtonPress) {
      XButtonEvent *e = (XButtonEvent *) event;
#ifdef MOTIF_DRAGNDROP
      TheLastButtonPress = *event;
#endif
      if (w->keyCallback)
          (w->keyCallback)(w->data, _keycodeMap[e->button], TRUE,
                       e->x, e->y, getModifiers(e->state));
    } else if (event->type == ButtonRelease) {
      XButtonEvent *e = (XButtonEvent *) event;
#ifndef MOTIF_DRAGNDROP
      if (InDragNDrop) {
          doDrop(w, e->x, e->y, getModifiers(e->state));
      } else
#endif
      if (w->keyCallback)
          (w->keyCallback)(w->data, _keycodeMap[e->button], FALSE,
                       e->x, e->y, getModifiers(e->state));
    }
}

static void
enterEvent(Widget w, XtPointer data, XEvent *event,
         Boolean *continue_to_dispatch)
{
    SWindow *wnd = (SWindow *) data;

    if (wnd->enterCallback)
      wnd->enterCallback(wnd->data, event->type == EnterNotify);
    *continue_to_dispatch = False;
}

static void
acceleratorHandler(Widget widget, XtPointer data, XEvent *event,
               Boolean *continue_to_dispatch)
{
    SWindow    *w, *mainWnd = (SWindow *) data;
    XKeyEvent  *e = (XKeyEvent *) event;
    int           mask = ShiftMask | ControlMask | Mod1Mask;
    KeySym  key = XKeycodeToKeysym(e->display, e->keycode, 0);

    if (XtClass(widget) == xmTextFieldWidgetClass && key == XK_Escape) {
      XtVaGetValues(widget, XmNuserData, &w, NULL);
        if (w && w->commandCallback) {
          w->commandCallback(w->data, FALSE);
          return;
      }
    }
    if (mainWnd->accelTable && acceleratorsEnabled) {
      int   i, n = mainWnd->accelTable->size;

      for (i = 0; i < n; i++) {
          Accelerator   *accel = mainWnd->accelTable->data + i;
          if (accel->key == key && (e->state & mask) == accel->state) {
            callCommandCallback(mainWnd->widget, accel->id);
            break;
          }
      }
    }
    *continue_to_dispatch = True;
}

static void
expose(Widget widget, XtPointer data, XtPointer event_data)
{
    SWindow        *wnd = (SWindow *) data;
    XmDrawingAreaCallbackStruct *cbs =
      (XmDrawingAreaCallbackStruct *) event_data;
    XExposeEvent *event = (XExposeEvent *) cbs->event;

    if (!wnd || !wnd->exposeCallback) return;

    swInvalidateRect(wnd, event->x, event->y, event->width, event->height);
}

static void
update()
{
    int           i;
    for (i = 0; i < NumWindowsToUpdate; i++) {
      SWND w = WindowsToUpdate[i];

      if (w && w->invx1 >= 0 && w->exposeCallback) {
          w->exposeCallback(w->data, w->invx1, w->invy1,
                        w->invx2 - w->invx1 + 1, 
                                w->invy2 - w->invy1 + 1);
          w->invx1 = w->invx2 = w->invy1 = w->invy2 = -1;
      }
    }
    NumWindowsToUpdate = 0;
}

void  swUpdate(void)
{
    int running = 1;
    do
    {
        running = 0;
      if (XtAppPending(TheAppContext) != 0) {
          XtAppProcessEvent(TheAppContext, XtIMAll);
            running = 1;
          update();
      }
    } while (running);
}


static void
onDeleteWindow(Widget widget, XtPointer data, XtPointer event_data)
{
    SWindow *wnd = (SWindow *) data;

    XtRemoveCallback(wnd->widget, XmNexposeCallback, expose, wnd);
    XtRemoveCallback(wnd->widget, XmNinputCallback, input, wnd);
    XtRemoveCallback(wnd->widget, XmNresizeCallback, resize, wnd);

    deleteFromWindowsToUpdate(wnd);
    if (wnd && wnd->closeCallback)
      wnd->closeCallback(wnd->data);
    else 
      swExit();
}

static void
onDeleteDialog(Widget widget, XtPointer data, XtPointer event_data)
{
    SWindow *wnd = (SWindow *) data;

    deleteFromWindowsToUpdate(wnd);
    swExitModalLoop(IDCANCEL);
}

static void
timerProc(XtPointer closure, XtIntervalId *id)
{
    STimer  *t = (STimer *) closure;

    if (t->timerCallback(t->data))
      t->id = XtAppAddTimeOut(TheAppContext, t->timeout, timerProc, t);
}

static void
dlgBtnCallback(Widget widget, XtPointer data, XtPointer event_data)
{
    swExitModalLoop((int) data);
}

#ifdef MOTIF_DRAGNDROP
static void
dragProc(Widget widget, XtPointer data, XtPointer event_data)
{
    XmDragProcCallbackStruct *cbs = (XmDragProcCallbackStruct *) event_data;
    SWindow    *wnd;
    int           op = XmDROP_NOOP;
    int           modifiers;
    Widget  icon;

    modifiers = queryModifiers();
                   
    XtVaGetValues(widget, XmNuserData, &wnd, NULL);
    if (wnd && wnd->dropCallback) switch (cbs->reason) {
      case XmCR_DROP_SITE_ENTER_MESSAGE:
      op = wnd->dropCallback(wnd->data, SW_DRAG_ENTER,
                         cbs->x, cbs->y, modifiers);
      break;
      case XmCR_OPERATION_CHANGED:
      /* FALL THRU ? */
      case XmCR_DROP_SITE_MOTION_MESSAGE:
      op = wnd->dropCallback(wnd->data, SW_DRAG_OVER,
                         cbs->x, cbs->y, modifiers);
      break;
      case XmCR_DROP_SITE_LEAVE_MESSAGE:
      op = wnd->dropCallback(wnd->data, SW_DRAG_LEAVE,
                         cbs->x, cbs->y, modifiers);
      break;
    }
    cbs->dropSiteStatus = op != 0 ? XmDROP_SITE_VALID : XmDROP_SITE_INVALID;
    cbs->operation = opToMop(op);
    if (cbs->operation == XmDROP_COPY) {
      icon = TheCopyIcon;
    } else if (cbs->operation == XmDROP_LINK) {
      icon = TheLinkIcon;
    } else if (cbs->operation == XmDROP_MOVE) {
      icon = TheMoveIcon;
    } else {
      icon = NULL;
    }
    XtVaSetValues(TheDragContext, XmNoperationCursorIcon, icon, NULL);
    cbs->animate = False;
}

static void
dropProc(Widget widget, XtPointer data, XtPointer event_data)
{
    XmDropProcCallbackStruct *cbs = (XmDropProcCallbackStruct *) event_data;
    XmDropTransferEntryRec  transferEntries[1];
    Arg           av[10];
    int           ac = 0;
    SWindow    *wnd;
    int           modifiers = queryModifiers();

    XtVaGetValues(widget, XmNuserData, &wnd, NULL);

    if (cbs->dropAction == XmDROP)
    {
      transferEntries[0].target = XA_STRING;
      transferEntries[0].client_data = (XtPointer) widget;

      XtSetArg(av[ac], XmNdropTransfers, transferEntries); ac++;
      XtSetArg(av[ac], XmNnumDropTransfers, 1); ac++;
      XtSetArg(av[ac], XmNtransferProc, transferProc); ac++;
      wnd->dropCallback(wnd->data, SW_DROP, cbs->x, cbs->y, modifiers);
    } else {
      XtSetArg(av[ac], XmNtransferStatus, XmTRANSFER_FAILURE); ac++;
      XtSetArg(av[ac], XmNnumDropTransfers, 0); ac++;
      cbs->operation = XmDROP_NOOP;
      cbs->dropSiteStatus = XmINVALID_DROP_SITE;
    }
    XmDropTransferStart(cbs->dragContext, av, ac);
}

static void
dragFinish(Widget widget, XtPointer data, XtPointer event_data)
{
    InDragNDrop = FALSE;
}

static Boolean
convertProc(Widget widget, Atom *selection, Atom *target, Atom *typeRtn,
          XtPointer *valueRtn, unsigned long *lengthRtn, int *formatRtn,
          unsigned long *max_lengthRtn, XtPointer data,
          XtRequestId *request_id)
{
    const char *tmpString = "blah";
    char    *passtext;
      
    if (*target != XA_STRING)
      return(False);
       
    passtext = XtMalloc(strlen(tmpString)+1);
    strcpy(passtext, tmpString);
 
    *typeRtn = XA_STRING;
    *valueRtn = (XtPointer) passtext;
    *lengthRtn = strlen(passtext);
    *formatRtn = 8;
    return(True);
}

static void
transferProc(Widget widget, XtPointer data, Atom *selection, Atom *type,
           XtPointer value, unsigned long *length, int *format)
{
    XtFree(value);
}

static Widget
createDragIcon(Widget widget, Pixmap pixmap, Pixmap mask,
             int width, int height)
{
    Arg           av[10];
    int           ac = 0;

    XtSetArg(av[ac], XmNwidth, width);  ac++;
    XtSetArg(av[ac], XmNheight, height);  ac++;
    XtSetArg(av[ac], XmNdepth, 1);  ac++;
    XtSetArg(av[ac], XmNpixmap, pixmap);  ac++;
    if (mask) XtSetArg(av[ac], XmNmask, mask);  ac++;
    return XmCreateDragIcon(widget, (String) "swDragIcon", av, ac);
}

/*
 * createSourceCursor()
 * 
 * given a multiplane (colour) pixmap, create a monochrome bitmap and mask.
 * uses a braindead algo for conversion to mono (luminance threshold).
 */

static void
createSourceCursor(Pixmap pixmap, int xPos, int yPos, int width, int height,
               Widget widget, Pixmap *pixmapOut, Pixmap *maskOut)
{
    Pixel   bg;
    XImage     *imageIn, *maskImage, *image;
    GC            gc;
    Window  window = XtWindow(widget);
    int           x, y;
    XColor  c;

    imageIn = XGetImage(TheDisplay, pixmap, xPos, yPos, width, height,
                  AllPlanes, ZPixmap);
    maskImage = XCreateImage(TheDisplay, TheVisual->visual, 1, XYPixmap,
                       0, malloc(width * height), width, height, 8, 0);
    image = XCreateImage(TheDisplay, TheVisual->visual, 1, XYPixmap,
                       0, malloc(width * height), width, height, 8, 0);
    *pixmapOut = XCreatePixmap(TheDisplay, window, width, height, 1);
    *maskOut = XCreatePixmap(TheDisplay, window, width, height, 1);
    XtVaGetValues(widget, XmNbackground, &bg, NULL);

    for (y = 0; y < height; y++) {
      for (x = 0; x < width; x++) {
            int red;
            int green;
            int blue;
          c.pixel = XGetPixel(imageIn, x, y);
            if (truecolor_mode)
                red = swX11ColorToRed8Bit(c.pixel) << 8;
                green = swX11ColorToGreen8Bit(c.pixel) << 8;
                blue = swX11ColorToBlue8Bit(c.pixel) << 8;
            } else {
              XQueryColor(TheDisplay, TheColormap, &c);
                red = c.red;
                green = c.green;
                blue = c.blue;
            }
          XPutPixel(image, x, y, red + green + blue < 32768 * 3);
          XPutPixel(maskImage, x, y, c.pixel != bg);
      }
    }
    gc = XCreateGC(TheDisplay, *pixmapOut, 0, NULL);
    XPutImage(TheDisplay, *pixmapOut, gc, image, 0, 0, 0, 0, width, height);
    XPutImage(TheDisplay, *maskOut, gc, maskImage, 0, 0, 0, 0, width, height);
    XFreeGC(TheDisplay, gc);
    XDestroyImage(imageIn);
    XDestroyImage(image);
    XDestroyImage(maskImage);
}

static int
opToMop(int op)
{
    int           mop = XmDROP_NOOP;

    if (op & SW_DRAG_MOVE)
      mop |= XmDROP_MOVE;
    if (op & SW_DRAG_COPY)
      mop |= XmDROP_COPY;
    if (op & SW_DRAG_LINK)
      mop |= XmDROP_LINK;

    return mop;
}

#else

static void
updateCursor(Window window, int operation)
{
    Cursor  cursor;

    switch (operation) {
      case SW_DRAG_MOVE:
      cursor = TheMoveCursor;
      break;
      case SW_DRAG_COPY:
      cursor = TheCopyCursor;
      break;
      case SW_DRAG_LINK:
      cursor = TheLinkCursor;
      break;
      default:
      cursor = TheErrorCursor;
      break;
    }
    if (cursor)
      XDefineCursor(TheDisplay, window, cursor);
    else
      XUndefineCursor(TheDisplay, window);
}

static void
exposeDragDropWindow(Widget widget, XtPointer data, XtPointer event_data)
{
    GC gc = XCreateGC(TheDisplay, XtWindow(widget), 0, NULL);
    Dimension     width, height;

    XtVaGetValues(widget, XmNwidth, &width, XmNheight, &height, NULL);
    XSetClipMask(TheDisplay, gc, TheDragMask);
    XSetClipOrigin(TheDisplay, gc, -TheDragPixmapOffsetX, -TheDragPixmapOffsetY);
    XCopyArea(TheDisplay, TheDragPixmap, XtWindow(widget),
            gc, TheDragPixmapOffsetX, TheDragPixmapOffsetY,
            width, height, 0, 0);
    XFreeGC(TheDisplay, gc);
}

#endif /* MOTIF_DRAGNDROP */

static const char *
parseMenu(const char *s1, char *s2, char *mnemonic)
{
    mnemonic[0] = '\0';
    while (*s1) {
      if (*s1 == '&') {
          mnemonic[0] = *(s1 + 1);
          mnemonic[1] = '\0';
          s1++;
      } else if (*s1 == '\t') {
          *s2 = '\0';
          return s1 + 1;
      } else {
          *s2++ = *s1++;
      }
    }
    *s2 = '\0';
    return NULL;
}

static int
getModifiers(int state)
{
    int           modifiers = 0;

    if (state & ShiftMask) {
      modifiers |= SW_SHIFT;
    }
    if (state & ControlMask) {
      modifiers |= SW_CONTROL;
    }
    if (state & Mod1Mask) {
      modifiers |= SW_ALT;
    }
    return modifiers;
}

static int
queryModifiers()
{
    int           x;
    Window  window;
    unsigned      state;

    XQueryPointer(TheDisplay, DefaultRootWindow(TheDisplay), &window, &window,
              &x, &x, &x, &x, &state);
    return getModifiers(state);
}

static SMENU
findMenu(Widget widget, int id)
{
    int           i, numChildren;
    WidgetList    children;
    SMENU   child;

    if (!widget) return NULL;

    XtVaGetValues(widget, XmNchildren, &children,
                    XmNnumChildren, &numChildren,
                    NULL);
    for (i = 0; i < numChildren; i++) {
      if (XtIsSubclass(children[i], xmToggleButtonWidgetClass)) {
          XtVaGetValues(children[i], XmNuserData, &child, NULL);
          if (child->id == id) return child;
      } else if (XtIsSubclass(children[i], xmCascadeButtonWidgetClass)) {
          Widget  subMenu = NULL;
          XtVaGetValues(children[i], XmNsubMenuId, &subMenu, NULL);
          child = findMenu(subMenu, id);
          if (child) return child;
      }
    }
    return NULL;
}

extern void
insertMenuItem(Widget menu, SWND wnd, short pos, const char *text, int id)
{
    SMenu      *m;
    char       *newText = malloc(strlen(text) + 1);
    char    mnemonic[2];
    const char *accelerator;
    
    accelerator = parseMenu(text, newText, mnemonic);
    mnemonic[1] = '\0';
    
    m = newSMenu();
    m->widget = XtVaCreateManagedWidget(newText, xmToggleButtonWidgetClass,
                              menu,
                              XmNpositionIndex, pos,
                              XmNindicatorOn, True,
                              XmNuserData, m,
                              NULL);
    m->wnd = wnd;
    m->id = id;
    if (mnemonic[0]) {
      XtVaSetValues(m->widget, XmNmnemonic, XStringToKeysym(mnemonic), NULL);
    }
    if (accelerator) {
      XmString    a = XmStringCreateLocalized((String) accelerator);
      XtVaSetValues(m->widget, XmNacceleratorText, a, NULL);
      XmStringFree(a);
    }

    XtAddCallback(m->widget, XmNarmCallback, menuArmCallback, m);
    XtAddCallback(m->widget, XmNdisarmCallback, menuDisarmCallback, m);
    XtAddCallback(m->widget, XmNvalueChangedCallback, menuActivateCallback, m);
    XtAddCallback(m->widget, XmNdestroyCallback, destroyMenuCallback, m);
    free(newText);
}

static void
resetRadioButtons(Widget widget)
{
    int                 i, numChildren;
    WidgetList          children;
    unsigned char type;

    if (!widget) return;

    XtVaGetValues(XtParent(widget), XmNnumChildren, &numChildren,
                            XmNchildren, &children,
                            NULL);

    for (i = 0; i < numChildren; i++) {
      if (children[i] != widget
          && XtClass(children[i]) == xmToggleButtonWidgetClass) {
          XtVaGetValues(children[i], XmNindicatorType, &type, NULL);
          if (type == XmONE_OF_MANY) {
            XtVaSetValues(children[i], XmNset, False, NULL);
          }
      }
    }
}

static void
callCommandCallback(Widget widget, int id)
{
    SWND    wnd;

    for (; widget != NULL; widget = XtParent(widget)) {
      XtVaGetValues(widget, XmNuserData, &wnd, NULL);
      if (wnd && wnd->commandCallback) {
          wnd->commandCallback(wnd->data, id);
          return;
      }
    }
}

static void
callHighlightCallback(Widget widget, int id)
{
    SWND    wnd;

    for (; widget != NULL; widget = XtParent(widget)) {
      XtVaGetValues(widget, XmNuserData, &wnd, NULL);
      if (wnd && wnd->highlightCallback) {
          wnd->highlightCallback(wnd->data, id);
          return;
      }
    }
}

static SWND
findMainWindow(Widget widget)
{
    SWND    wnd;

    for (; widget != NULL; widget = XtParent(widget)) {
      XtVaGetValues(widget, XmNuserData, &wnd, NULL);
      if (wnd && wnd->mainWnd) return wnd;
    }
    return NULL;
}

static void
appendPair(STABLE table, const char *key, const char *value)
{
    if (table->size == table->capacity) {
      table->capacity *= 2;
      table->data = (Pair *) realloc(table->data,
                               table->capacity * sizeof(Pair));
    }
    table->data[table->size].key = strdup(key);
    table->data[table->size].value = strdup(value);
    table->size++;
}

static int
messageBox(Widget parent, const char *text, const char *title, int type,
         int icon)
{
    int                 ac = 0;
    Arg                 av[10];
    XmString            textStr, titleStr;
    Widget        mbox, yes, no;
    int                 rc;
    unsigned char dialogType;

    if (!parent || !text || !title) return 0;

    XRaiseWindow(TheDisplay, XtWindow(parent));
    textStr = XmStringCreateLocalized((String) text);
    XtSetArg(av[ac], XmNmessageString, textStr);  ac++;
    titleStr = XmStringCreateLocalized((String) title);
    XtSetArg(av[ac], XmNdialogTitle, titleStr);  ac++;
    XtSetArg(av[ac], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL);  ac++;
    XtSetArg(av[ac], XmNautoUnmanage, False);  ac++;
    XtSetArg(av[ac], XmNvisual, TheVisual->visual);  ac++;
    switch(icon) {
      case SW_MB_NOICON:
      dialogType = XmDIALOG_MESSAGE;
      break;
      case SW_MB_ERROR:
      dialogType = XmDIALOG_ERROR;
      break;
      case SW_MB_INFORMATION:
      dialogType = XmDIALOG_INFORMATION;
      break;
      case SW_MB_QUESTION:
      dialogType = XmDIALOG_QUESTION;
      break;
      case SW_MB_WARNING:
      dialogType = XmDIALOG_WARNING;
      break;
    }
    XtSetArg(av[ac], XmNdialogType, dialogType);   ac++;
    mbox = XmCreateMessageDialog(parent, (String) title, av, ac);
    XmStringFree(textStr);
    XmStringFree(titleStr);
    XtUnmanageChild(XmMessageBoxGetChild(mbox, XmDIALOG_HELP_BUTTON));
    switch (type) {
      case SW_MB_OK:
      XtUnmanageChild(XmMessageBoxGetChild(mbox, XmDIALOG_CANCEL_BUTTON));
      break;
      case SW_MB_OKCANCEL:
      break;
      case SW_MB_YESNO:
      XtUnmanageChild(XmMessageBoxGetChild(mbox, XmDIALOG_CANCEL_BUTTON));
      /* FALL THRU */
      case SW_MB_YESNOCANCEL:
      XtUnmanageChild(XmMessageBoxGetChild(mbox, XmDIALOG_OK_BUTTON));
      yes = XtVaCreateManagedWidget("Yes", xmPushButtonWidgetClass,
                              mbox, NULL);
      XtAddCallback(yes, XmNactivateCallback, dlgBtnCallback,
                  (XtPointer) IDYES);
      no = XtVaCreateManagedWidget("No", xmPushButtonWidgetClass,
                             mbox, NULL);
      XtAddCallback(no, XmNactivateCallback, dlgBtnCallback,
                  (XtPointer) IDNO);
      break;
      
    }
    XtAddCallback(mbox, XmNokCallback, dlgBtnCallback, (XtPointer) IDOK);
    XtAddCallback(mbox, XmNcancelCallback, dlgBtnCallback,
              (XtPointer) IDCANCEL);
    XtManageChild(mbox);
    rc = swModalLoop();
    XtUnmanageChild(mbox);
    return rc;
}

static int
fileExists(const char *filename)
{
    struct stat   buf;
    return stat(filename, &buf) == 0;
}

static int
dirExists(const char *filename)
{
    struct stat   buf;
    if (stat(filename, &buf) == 0)
        if (buf.st_mode & S_IFDIR)
            return 1;
    return 0;
}

#ifdef HAVE_XINPUT

void swInitXinputDevice(struct swXinput* xinput,const char *device)
   {
   XExtensionVersion* version;
   XDeviceInfo *info;
   int number_of_devices;
   int i;

   xinput->xinput=NULL;
   version=XGetExtensionVersion(TheDisplay,"XInputExtension");
   if ((version==NULL) || (version==(void*)NoSuchExtension))
      {
      fprintf(stderr,"No XInputExtension (%s)\n",device);
      return;
      };
# ifdef DEBUG
   printf("X Input Extension Version %d.%d\n", 
           version->major_version,version->minor_version);
# endif
   XFree(version);
   info=XListInputDevices(TheDisplay,&number_of_devices);
   for (i=0;i<number_of_devices;++i)
      {
      int c;
      XAnyClassPtr xclass;
      xclass=info[i].inputclassinfo;
      if (strcmp(info[i].name,device)==0)
         {
         xinput->xinput=XOpenDevice(TheDisplay,info[i].id);
         for (c=0;c<info[i].num_classes;++c)
            {
            if (xclass->class==ButtonClass)
               {
               XButtonInfo *button_class=(XButtonInfo *)xclass;
               xinput->number_buttons=button_class->num_buttons;
               }
            if (xclass->class==ValuatorClass)
               {
               int i;
               XValuatorInfo *axes_class=(XValuatorInfo *)xclass;
               XAxisInfoPtr axes=axes_class->axes;
               xinput->number_axes=axes_class->num_axes;
               xinput->axes=malloc(axes_class->num_axes*sizeof(int));
               xinput->minvalue=malloc(axes_class->num_axes*sizeof(int));
               xinput->maxvalue=malloc(axes_class->num_axes*sizeof(int));
               for (i=0;i<xinput->number_axes;i++)
                   {
                   xinput->maxvalue[i]=axes->max_value;
                   xinput->minvalue[i]=axes->min_value;
                   axes++;
                   }
               }
            xclass=(XAnyClassPtr)((char*)xclass+xclass->length);
            }
         }
      }
   XFreeDeviceList(info); 
   if (xinput == NULL)
      {
      fprintf(stderr,"xinputdevice %s ",device);
      fprintf(stderr,"initialisation failed\n");
      return;
      }
   }

void swListXinputDevice(void)
   {
   XExtensionVersion* version;
   XDeviceInfo *info;
   int number_of_devices;
   int i;

   version=XGetExtensionVersion(TheDisplay,"XInputExtension");
   if ((version==NULL) || (version==(void*)NoSuchExtension))
      {
      fprintf(stderr,"No XInputExtension\n");
      return;
      };
   XFree(version);
   info=XListInputDevices(TheDisplay,&number_of_devices);
   for (i=0;i<number_of_devices;++i) {
      int c;
      XAnyClassPtr xclass;
      printf("\"%s\" ",info[i].name);
#ifdef HAVE_XINPUTLIST_LONG_LISTING
      if ((strcmp(info[i].name,"virtual_pointer")!=0) && 
          (strcmp(info[i].name,"keyboard")!=0))
         XOpenDevice(TheDisplay,info[i].id);
      xclass=info[i].inputclassinfo;
      for (c=0;c<info[i].num_classes;++c)
         {
         if (xclass->class==ValuatorClass)
            {
            XValuatorInfo *axes_class=(XValuatorInfo *)xclass;
            XAxisInfoPtr axes=axes_class->axes;
            printf("axes: %d ",axes_class->num_axes);
            }
         if (xclass->class==ButtonClass)
            {
            XButtonInfo *button_class=(XButtonInfo *)xclass;
            printf("buttons: %d ",button_class->num_buttons);
            }
            xclass=(XAnyClassPtr)((char*)xclass+xclass->length);
         }       
#endif
      printf("\n");
      }
   }

void swQueryXinputDevice(struct swXinput* xinput)
   {
   int c;
   XDeviceState* state;
   XInputClass* xclass;
   state=XQueryDeviceState(TheDisplay,xinput->xinput);
   if (state==XI_BadDevice)
      xinput->xinput=NULL;
   xclass=state->data;
   for (c=0;c<state->num_classes;++c)
      {
      if (xclass->class==ButtonClass)
         {
         XButtonState  *button_class=(XButtonState *)xclass;
         xinput->number_buttons=button_class->num_buttons;
         }
      if (xclass->class==ValuatorClass)
         {
         XValuatorState *axes_class=(XValuatorState *)xclass;
         int i;
         for (i=0;i<axes_class->num_valuators;i++)
            xinput->axes[i]=axes_class->valuators[i];
         }
      xclass=(XInputClass*)((char*)xclass+xclass->length);
      }
   }

void swCloseXinputDevice(struct swXinput* xinput)
   {
   XCloseDevice(TheDisplay,xinput->xinput);
   }
#endif

Generated by  Doxygen 1.6.0   Back to index