/* WPrefs.c- main window and other basic stuff
 *
 *  WPrefs - Window Maker Preferences Program
 *
 *  Copyright (c) 1998-2003 Alfredo K. Kojima
 *
 *  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; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
 *  USA.
 */


#include "WPrefs.h"
#include <assert.h>


extern Panel *InitWindowHandling(WMScreen *scr, WMWidget *parent);

extern Panel *InitKeyboardSettings(WMScreen *scr, WMWidget *parent);

extern Panel *InitMouseSettings(WMScreen *scr, WMWidget *parent);

extern Panel *InitKeyboardShortcuts(WMScreen *scr, WMWidget *parent);

extern Panel *InitWorkspace(WMScreen *scr, WMWidget *parent);

extern Panel *InitFocus(WMScreen *scr, WMWidget *parent);

extern Panel *InitPreferences(WMScreen *scr, WMWidget *parent);

extern Panel *InitFont(WMScreen *scr, WMWidget *parent);
extern Panel *InitFontSimple(WMScreen *scr, WMWidget *parent);

extern Panel *InitConfigurations(WMScreen *scr, WMWidget *parent);

extern Panel *InitPaths(WMScreen *scr, WMWidget *parent);

extern Panel *InitMenu(WMScreen *scr, WMWidget *parent);

extern Panel *InitExpert(WMScreen *scr, WMWidget *parent);

extern Panel *InitMenuPreferences(WMScreen *scr, WMWidget *parent);

extern Panel *InitIcons(WMScreen *scr, WMWidget *parent);

extern Panel *InitThemes(WMScreen *scr, WMWidget *parent);

extern Panel *InitAppearance(WMScreen *scr, WMWidget *parent);




#define ICON_TITLE_FONT "sans serif:pixelsize=9"
#define ICON_TITLE_VFONT "sans serif:pixelsize=9:weight=100"


#define MAX_SECTIONS 16


typedef struct _WPrefs {
    WMWindow *win;

    WMScrollView *scrollV;
    WMFrame *buttonF;
    WMButton *sectionB[MAX_SECTIONS];

    int sectionCount;

    WMButton *saveBtn;
    WMButton *closeBtn;
    WMButton *undoBtn;
    WMButton *undosBtn;

    WMButton *balloonBtn;

    WMFrame *banner;
    WMLabel *nameL;
    WMLabel *versionL;
    WMLabel *statusL;

    Panel *currentPanel;
} _WPrefs;


static _WPrefs WPrefs;

/* system wide defaults dictionary. Read-only */
static WMPropList *GlobalDB = NULL;
/* user defaults dictionary */
static WMPropList *WindowMakerDB = NULL;
static char *WindowMakerDBPath = NULL;


static Bool TIFFOK = False;


#define INITIALIZED_PANEL	(1<<0)




static void loadConfigurations(WMScreen *scr, WMWindow *mainw);

static void savePanelData(Panel *panel);

static void prepareForClose();

void
quit(WMWidget *w, void *data)
{
    prepareForClose();

    exit(0);
}


static void
save(WMWidget *w, void *data)
{
    int i;
    WMPropList *p1, *p2;
    WMPropList *keyList;
    WMPropList *key;
    char *msg = "Reconfigure";
    XEvent ev;


    /*    puts("gathering data");*/
    for (i=0; i<WPrefs.sectionCount; i++) {
        PanelRec *rec = WMGetHangedData(WPrefs.sectionB[i]);
        if ((rec->callbacks.flags & INITIALIZED_PANEL))
            savePanelData((Panel*)rec);
    }
    /*    puts("compressing data");*/
    /* compare the user dictionary with the global and remove redundant data */
    keyList = WMGetPLDictionaryKeys(GlobalDB);
    /*    puts(WMGetPropListDescription(WindowMakerDB, False));*/
    for (i=0; i<WMGetPropListItemCount(keyList); i++) {
        key = WMGetFromPLArray(keyList, i);

        /* We don't have this value anyway, so no problem.
         * Probably  a new option */
        p1 = WMGetFromPLDictionary(WindowMakerDB, key);
        if (!p1)
            continue;
        /* The global doesn't have it, so no problem either. */
        p2 = WMGetFromPLDictionary(GlobalDB, key);
        if (!p2)
            continue;
        /* If both values are the same, don't save. */
        if (WMIsPropListEqualTo(p1, p2))
            WMRemoveFromPLDictionary(WindowMakerDB, key);
    }
    /*    puts(WMGetPropListDescription(WindowMakerDB, False));*/
    WMReleasePropList(keyList);
    /*    puts("storing data");*/

    WMWritePropListToFile(WindowMakerDB, WindowMakerDBPath, True);


    memset(&ev, 0, sizeof(XEvent));

    ev.xclient.type = ClientMessage;
    ev.xclient.message_type = XInternAtom(WMScreenDisplay(WMWidgetScreen(w)),
                                          "_WINDOWMAKER_COMMAND", False);
    ev.xclient.window = DefaultRootWindow(WMScreenDisplay(WMWidgetScreen(w)));
    ev.xclient.format = 8;

    for (i = 0; i <= strlen(msg); i++) {
        ev.xclient.data.b[i] = msg[i];
    }
    XSendEvent(WMScreenDisplay(WMWidgetScreen(w)),
               DefaultRootWindow(WMScreenDisplay(WMWidgetScreen(w))),
               False, SubstructureRedirectMask, &ev);
    XFlush(WMScreenDisplay(WMWidgetScreen(w)));
}



static void
undo(WMWidget *w, void *data)
{
    PanelRec *rec = (PanelRec*)WPrefs.currentPanel;

    if (!rec)
        return;

    if (rec->callbacks.undoChanges
        && (rec->callbacks.flags & INITIALIZED_PANEL)) {
        (*rec->callbacks.undoChanges)(WPrefs.currentPanel);
    }
}


static void
undoAll(WMWidget *w, void *data)
{
    int i;

    for (i=0; i<WPrefs.sectionCount; i++) {
        PanelRec *rec = WMGetHangedData(WPrefs.sectionB[i]);

        if (rec->callbacks.undoChanges
            && (rec->callbacks.flags & INITIALIZED_PANEL))
            (*rec->callbacks.undoChanges)((Panel*)rec);
    }
}



static void
prepareForClose()
{
    int i;

    for (i=0; i<WPrefs.sectionCount; i++) {
        PanelRec *rec = WMGetHangedData(WPrefs.sectionB[i]);

        if (rec->callbacks.prepareForClose
            && (rec->callbacks.flags & INITIALIZED_PANEL))
            (*rec->callbacks.prepareForClose)((Panel*)rec);
    }
}


void
toggleBalloons(WMWidget *w, void *data)
{
    WMUserDefaults *udb = WMGetStandardUserDefaults();
    Bool flag;

    flag = WMGetButtonSelected(WPrefs.balloonBtn);

    WMSetBalloonEnabled(WMWidgetScreen(WPrefs.win), flag);

    WMSetUDBoolForKey(udb, flag, "BalloonHelp");
}


static void
createMainWindow(WMScreen *scr)
{
    WMScroller *scroller;
    WMFont *font;
    char buffer[128];

    WPrefs.win = WMCreateWindow(scr, "wprefs");
    WMResizeWidget(WPrefs.win, 520, 390);
    WMSetWindowTitle(WPrefs.win, _("Window Maker Preferences"));
    WMSetWindowCloseAction(WPrefs.win, quit, NULL);
    WMSetWindowMaxSize(WPrefs.win, 520, 390);
    WMSetWindowMinSize(WPrefs.win, 520, 390);
    WMSetWindowMiniwindowTitle(WPrefs.win, "Preferences");

    WPrefs.scrollV = WMCreateScrollView(WPrefs.win);
    WMResizeWidget(WPrefs.scrollV, 500, 87);
    WMMoveWidget(WPrefs.scrollV, 10, 10);
    WMSetScrollViewRelief(WPrefs.scrollV, WRSunken);
    WMSetScrollViewHasHorizontalScroller(WPrefs.scrollV, True);
    WMSetScrollViewHasVerticalScroller(WPrefs.scrollV, False);
    scroller = WMGetScrollViewHorizontalScroller(WPrefs.scrollV);
    WMSetScrollerArrowsPosition(scroller, WSANone);

    WPrefs.buttonF = WMCreateFrame(WPrefs.win);
    WMSetFrameRelief(WPrefs.buttonF, WRFlat);

    WMSetScrollViewContentView(WPrefs.scrollV, WMWidgetView(WPrefs.buttonF));

    WPrefs.undosBtn = WMCreateCommandButton(WPrefs.win);
    WMResizeWidget(WPrefs.undosBtn, 90, 28);
    WMMoveWidget(WPrefs.undosBtn, 135, 350);
    WMSetButtonText(WPrefs.undosBtn, _("Revert Page"));
    WMSetButtonAction(WPrefs.undosBtn, undo, NULL);

    WPrefs.undoBtn = WMCreateCommandButton(WPrefs.win);
    WMResizeWidget(WPrefs.undoBtn, 90, 28);
    WMMoveWidget(WPrefs.undoBtn, 235, 350);
    WMSetButtonText(WPrefs.undoBtn, _("Revert All"));
    WMSetButtonAction(WPrefs.undoBtn, undoAll, NULL);

    WPrefs.saveBtn = WMCreateCommandButton(WPrefs.win);
    WMResizeWidget(WPrefs.saveBtn, 80, 28);
    WMMoveWidget(WPrefs.saveBtn, 335, 350);
    WMSetButtonText(WPrefs.saveBtn, _("Save"));
    WMSetButtonAction(WPrefs.saveBtn, save, NULL);

    WPrefs.closeBtn = WMCreateCommandButton(WPrefs.win);
    WMResizeWidget(WPrefs.closeBtn, 80, 28);
    WMMoveWidget(WPrefs.closeBtn, 425, 350);
    WMSetButtonText(WPrefs.closeBtn, _("Close"));
    WMSetButtonAction(WPrefs.closeBtn, quit, NULL);


    WPrefs.balloonBtn = WMCreateSwitchButton(WPrefs.win);
    WMResizeWidget(WPrefs.balloonBtn, 200, 28);
    WMMoveWidget(WPrefs.balloonBtn, 15, 350);
    WMSetButtonText(WPrefs.balloonBtn, _("Balloon Help"));
    WMSetButtonAction(WPrefs.balloonBtn, toggleBalloons, NULL);
    {
        WMUserDefaults *udb = WMGetStandardUserDefaults();
        Bool flag = WMGetUDBoolForKey(udb, "BalloonHelp");

        WMSetButtonSelected(WPrefs.balloonBtn, flag);
        WMSetBalloonEnabled(scr, flag);
    }

    /* banner */
    WPrefs.banner = WMCreateFrame(WPrefs.win);
    WMResizeWidget(WPrefs.banner, FRAME_WIDTH, FRAME_HEIGHT);
    WMMoveWidget(WPrefs.banner, FRAME_LEFT, FRAME_TOP);
    WMSetFrameRelief(WPrefs.banner, WRFlat);

    font = WMCreateFont(scr, "Lucida Sans,URW Gothic L,Times New Roman,serif"
                        ":bold:pixelsize=26:antialias=true");
    WPrefs.nameL = WMCreateLabel(WPrefs.banner);
    WMSetLabelTextAlignment(WPrefs.nameL, WACenter);
    WMResizeWidget(WPrefs.nameL, FRAME_WIDTH-20, 60);
    WMMoveWidget(WPrefs.nameL, 10, 50);
    WMSetLabelFont(WPrefs.nameL, font);
    WMSetLabelText(WPrefs.nameL, _("Window Maker Preferences"));
    WMReleaseFont(font);

    WPrefs.versionL = WMCreateLabel(WPrefs.banner);
    WMResizeWidget(WPrefs.versionL, FRAME_WIDTH-20, 20);
    WMMoveWidget(WPrefs.versionL, 10, 120);
    WMSetLabelTextAlignment(WPrefs.versionL, WACenter);
    sprintf(buffer, _("Version %s"), VERSION);
    WMSetLabelText(WPrefs.versionL, buffer);

    WPrefs.statusL = WMCreateLabel(WPrefs.banner);
    WMResizeWidget(WPrefs.statusL, FRAME_WIDTH-20, 60);
    WMMoveWidget(WPrefs.statusL, 10, 150);
    WMSetLabelTextAlignment(WPrefs.statusL, WACenter);
    WMSetLabelText(WPrefs.statusL, _("Starting..."));



    WMMapSubwidgets(WPrefs.win);

    WMUnmapWidget(WPrefs.undosBtn);
    WMUnmapWidget(WPrefs.undoBtn);
    WMUnmapWidget(WPrefs.saveBtn);
}


static void
showPanel(Panel *panel)
{
    PanelRec *rec = (PanelRec*)panel;

    if (!(rec->callbacks.flags & INITIALIZED_PANEL)) {
        (*rec->callbacks.createWidgets)(panel);
        rec->callbacks.flags |= INITIALIZED_PANEL;
    }

    WMSetWindowTitle(WPrefs.win, rec->sectionName);

    if (rec->callbacks.showPanel)
        (*rec->callbacks.showPanel)(panel);

    WMMapWidget(rec->box);
}



static void
hidePanel(Panel *panel)
{
    PanelRec *rec = (PanelRec*)panel;

    WMUnmapWidget(rec->box);

    if (rec->callbacks.hidePanel)
        (*rec->callbacks.hidePanel)(panel);
}


static void
savePanelData(Panel *panel)
{
    PanelRec *rec = (PanelRec*)panel;

    if (rec->callbacks.updateDomain) {
        (*rec->callbacks.updateDomain)(panel);
    }
}


static void
changeSection(WMWidget *self, void *data)
{
    if (WPrefs.currentPanel == data)
        return;

    if (WPrefs.currentPanel == NULL) {
        WMDestroyWidget(WPrefs.nameL);
        WMDestroyWidget(WPrefs.versionL);
        WMDestroyWidget(WPrefs.statusL);

        WMSetFrameRelief(WPrefs.banner, WRGroove);

        /*	WMMapWidget(WPrefs.undosBtn);
         WMMapWidget(WPrefs.undoBtn);
         */
        WMMapWidget(WPrefs.saveBtn);
    }

    showPanel(data);

    if (WPrefs.currentPanel)
        hidePanel(WPrefs.currentPanel);
    WPrefs.currentPanel = data;
}



char*
LocateImage(char *name)
{
    char *path;
    char *tmp = wmalloc(strlen(name)+8);

    if (TIFFOK) {
        sprintf(tmp, "%s.tiff", name);
        path = WMPathForResourceOfType(tmp, "tiff");
    } else {
        sprintf(tmp, "%s.xpm", name);
        path = WMPathForResourceOfType(tmp, "xpm");
    }
    wfree(tmp);
    if (!path) {
        wwarning(_("could not locate image file %s\n"), name);
    }

    return path;
}



static WMPixmap*
makeTitledIcon(WMScreen *scr, WMPixmap *icon, char *title1, char *title2)
{
    return WMRetainPixmap(icon);

#if 0
    static GC gc = NULL;
    static XFontStruct *hfont = NULL;
    static XFontStruct *vfont = NULL;
    WMPixmap *tmp;
    Pixmap pix, mask;
    Display *dpy = WMScreenDisplay(scr);
    WMColor *black = WMBlackColor(scr);
    GC fgc;
    WMSize size = WMGetPixmapSize(icon);


    tmp = WMCreatePixmap(scr, 60, 60, WMScreenDepth(scr), True);

    pix = WMGetPixmapXID(tmp);
    mask = WMGetPixmapMaskXID(tmp);

    if (gc == NULL) {
        gc = XCreateGC(dpy, mask, 0, NULL);

        hfont = XLoadQueryFont(dpy, ICON_TITLE_FONT);
        vfont = XLoadQueryFont(dpy, ICON_TITLE_VFONT);
    }

    if (hfont == NULL) {
        return WMRetainPixmap(icon);
    }

    XSetForeground(dpy, gc, 0);
    XFillRectangle(dpy, mask, gc, 0, 0, 60, 60);

    fgc = WMColorGC(black);

    XSetForeground(dpy, gc, 1);

    XCopyArea(dpy, WMGetPixmapXID(icon), pix, fgc, 0, 0,
              size.width, size.height, 12, 12);

    if (WMGetPixmapMaskXID(icon) != None)
        XCopyPlane(dpy, WMGetPixmapMaskXID(icon), mask, gc, 0, 0,
                   size.width, size.height, 12, 12, 1);
    else
        XFillRectangle(dpy, mask, gc, 12, 12, 48, 48);


    if (title1) {
        XSetFont(dpy, fgc, vfont->fid);
        XSetFont(dpy, gc, vfont->fid);

        XDrawString(dpy, pix, fgc, 0, vfont->ascent,
                    title1, strlen(title1));

        XDrawString(dpy, mask, gc, 0, vfont->ascent,
                    title1, strlen(title1));
    }

    if (title2) {
        XSetFont(dpy, fgc, hfont->fid);
        XSetFont(dpy, gc, hfont->fid);

        XDrawString(dpy, pix, fgc, (title1 ? 12 : 0), hfont->ascent,
                    title2, strlen(title2));

        XDrawString(dpy, mask, gc, (title1 ? 12 : 0), hfont->ascent,
                    title2, strlen(title2));
    }

    return tmp;
#endif
}


void
SetButtonAlphaImage(WMScreen *scr, WMButton *bPtr, char *file,
                    char *title1, char *title2)
{
    WMPixmap *icon;
    WMPixmap *icon2;
    RColor color;
    char *iconPath;

    iconPath = LocateImage(file);

    color.red = 0xae;
    color.green = 0xaa;
    color.blue = 0xae;
    color.alpha = 0;
    if (iconPath) {
        icon = WMCreateBlendedPixmapFromFile(scr, iconPath, &color);
        if (!icon)
            wwarning(_("could not load icon file %s"), iconPath);
    } else {
        icon = NULL;
    }

    if (icon) {
        icon2 = makeTitledIcon(scr, icon, title1, title2);
        if (icon)
            WMReleasePixmap(icon);
    } else {
        icon2 = NULL;
    }

    WMSetButtonImage(bPtr, icon2);

    if (icon2)
        WMReleasePixmap(icon2);

    color.red = 0xff;
    color.green = 0xff;
    color.blue = 0xff;
    color.alpha = 0;
    if (iconPath) {
        icon = WMCreateBlendedPixmapFromFile(scr, iconPath, &color);
        if (!icon)
            wwarning(_("could not load icon file %s"), iconPath);
    } else {
        icon = NULL;
    }

    WMSetButtonAltImage(bPtr, icon);

    if (icon)
        WMReleasePixmap(icon);

    if (iconPath)
        wfree(iconPath);
}


void
AddSection(Panel *panel, char *iconFile)
{
    WMButton *bPtr;

    assert(WPrefs.sectionCount < MAX_SECTIONS);


    bPtr = WMCreateCustomButton(WPrefs.buttonF,	WBBStateLightMask
                                |WBBStateChangeMask);
    WMResizeWidget(bPtr, 64, 64);
    WMMoveWidget(bPtr, WPrefs.sectionCount*64, 0);
    WMSetButtonImagePosition(bPtr, WIPImageOnly);
    WMSetButtonAction(bPtr, changeSection, panel);
    WMHangData(bPtr, panel);

    WMSetBalloonTextForView(((PanelRec*)panel)->description,
                            WMWidgetView(bPtr));

    {
        char *t1, *t2;

        t1 = wstrdup(((PanelRec*)panel)->sectionName);
        t2 = strchr(t1, ' ');
        if (t2) {
            *t2 = 0;
            t2++;
        }
        SetButtonAlphaImage(WMWidgetScreen(bPtr), bPtr, iconFile,
                            t1, t2);
        wfree(t1);
    }
    WMMapWidget(bPtr);

    WPrefs.sectionB[WPrefs.sectionCount] = bPtr;

    if (WPrefs.sectionCount > 0) {
        WMGroupButtons(WPrefs.sectionB[0], bPtr);
    }

    WPrefs.sectionCount++;

    WMResizeWidget(WPrefs.buttonF, WPrefs.sectionCount*64, 64);
}


void
Initialize(WMScreen *scr)
{
    char **list;
    int i;
    char *path;

    list = RSupportedFileFormats();
    for (i=0; list[i]!=NULL; i++) {
        if (strcmp(list[i], "TIFF")==0) {
            TIFFOK = True;
            break;
        }
    }

    if (TIFFOK)
        path = WMPathForResourceOfType("WPrefs.tiff", NULL);
    else
        path = WMPathForResourceOfType("WPrefs.xpm", NULL);
    if (path) {
        RImage *tmp;

        tmp = RLoadImage(WMScreenRContext(scr), path, 0);
        if (!tmp) {
            wwarning(_("could not load image file %s:%s"), path,
                     RMessageForError(RErrorCode));
        } else {
            WMSetApplicationIconImage(scr, tmp);
            RReleaseImage(tmp);
        }
        wfree(path);
    }

    memset(&WPrefs, 0, sizeof(_WPrefs));
    createMainWindow(scr);

    WMRealizeWidget(WPrefs.win);
    
    WMSetWindowMiniwindowImage(WPrefs.win, WMGetApplicationIconImage(scr));
    
    WMMapWidget(WPrefs.win);
    XFlush(WMScreenDisplay(scr));
    WMSetLabelText(WPrefs.statusL, _("Loading Window Maker configuration files..."));
    XFlush(WMScreenDisplay(scr));
    loadConfigurations(scr, WPrefs.win);

    WMSetLabelText(WPrefs.statusL, _("Initializing configuration panels..."));

    InitFocus(scr, WPrefs.banner);
    InitWindowHandling(scr, WPrefs.banner);

    InitMenuPreferences(scr, WPrefs.banner);
    InitIcons(scr, WPrefs.banner);
    InitPreferences(scr, WPrefs.banner);

    InitPaths(scr, WPrefs.banner);
    InitWorkspace(scr, WPrefs.banner);
    InitConfigurations(scr, WPrefs.banner);

    InitMenu(scr, WPrefs.banner);

#ifdef not_yet_fully_implemented
    InitKeyboardSettings(scr, WPrefs.banner);
#endif
    InitKeyboardShortcuts(scr, WPrefs.banner);
    InitMouseSettings(scr, WPrefs.banner);

    InitAppearance(scr, WPrefs.banner);

    InitFontSimple(scr, WPrefs.banner);

#ifdef not_yet_fully_implemented
    InitThemes(scr, WPrefs.banner);
#endif
    InitExpert(scr, WPrefs.banner);

    WMRealizeWidget(WPrefs.scrollV);

    WMSetLabelText(WPrefs.statusL, "");
}


WMWindow*
GetWindow(Panel *panel)
{
    return WPrefs.win;
}


static void
loadConfigurations(WMScreen *scr, WMWindow *mainw)
{
    WMPropList *db, *gdb;
    char *path;
    FILE *file;
    char buffer[1024];
    char mbuf[1024];
    int v1, v2, v3;

    path = wdefaultspathfordomain("WindowMaker");
    WindowMakerDBPath = path;

    db = WMReadPropListFromFile(path);
    if (db) {
        if (!WMIsPLDictionary(db)) {
            WMReleasePropList(db);
            db = NULL;
            sprintf(mbuf, _("Window Maker domain (%s) is corrupted!"), path);
            WMRunAlertPanel(scr, mainw, _("Error"), mbuf, _("OK"), NULL, NULL);
        }
    } else {
        sprintf(mbuf, _("Could not load Window Maker domain (%s) from defaults database."),
                path);
        WMRunAlertPanel(scr, mainw, _("Error"), mbuf, _("OK"), NULL, NULL);
    }

    path = getenv("WMAKER_BIN_NAME");
    if (!path)
        path = "wmaker";
    {
        char *command;

        command = wstrconcat(path, " --version");
        file = popen(command, "r");
        wfree(command);
    }
    if (!file || !fgets(buffer, 1023, file)) {
        wsyserror(_("could not extract version information from Window Maker"));
        wfatal(_("Make sure wmaker is in your search path."));

        WMRunAlertPanel(scr, mainw, _("Error"),
                        _("Could not extract version from Window Maker. Make sure it is correctly installed and is in your PATH environment variable."),
                        _("OK"), NULL, NULL);
        exit(1);
    }
    if (file)
        pclose(file);

    if (sscanf(buffer, "Window Maker %i.%i.%i",&v1,&v2,&v3)!=3
        && sscanf(buffer, "WindowMaker %i.%i.%i",&v1,&v2,&v3)!=3) {
        WMRunAlertPanel(scr, mainw, _("Error"),
                        _("Could not extract version from Window Maker. "
                          "Make sure it is correctly installed and the path "
                          "where it installed is in the PATH environment "
                          "variable."), _("OK"), NULL, NULL);
        exit(1);
    }
    if (v1 == 0 && (v2 < 18 || v3 < 0)) {
        sprintf(mbuf, _("WPrefs only supports Window Maker 0.18.0 or newer.\n"
                        "The version installed is %i.%i.%i\n"), v1, v2, v3);
        WMRunAlertPanel(scr, mainw, _("Error"), mbuf, _("OK"), NULL, NULL);
        exit(1);

    }
    if (v1 > 1 || (v1 == 1 && (v2 > 0))) {
        sprintf(mbuf, _("Window Maker %i.%i.%i, which is installed in your system, is not fully supported by this version of WPrefs."),
                v1, v2, v3);
        WMRunAlertPanel(scr, mainw, _("Warning"), mbuf, _("OK"), NULL, NULL);
    }

    {
        char *command;

        command = wstrconcat(path, " --global_defaults_path");
        file = popen(command, "r");
        wfree(command);
    }
    if (!file || !fgets(buffer, 1023, file)) {
        wsyserror(_("could not run \"%s --global_defaults_path\"."), path);
        exit(1);
    } else {
        char *ptr;
        ptr = strchr(buffer, '\n');
        if (ptr)
            *ptr = 0;
        strcat(buffer, "/WindowMaker");
    }

    if (file)
        pclose(file);

    gdb = WMReadPropListFromFile(buffer);

    if (gdb) {
        if (!WMIsPLDictionary(gdb)) {
            WMReleasePropList(gdb);
            gdb = NULL;
            sprintf(mbuf, _("Window Maker domain (%s) is corrupted!"), buffer);
            WMRunAlertPanel(scr, mainw, _("Error"), mbuf, _("OK"), NULL, NULL);
        }
    } else {
        sprintf(mbuf, _("Could not load global Window Maker domain (%s)."),
                buffer);
        WMRunAlertPanel(scr, mainw, _("Error"), mbuf, _("OK"), NULL, NULL);
    }

    if (!db) {
        db = WMCreatePLDictionary(NULL, NULL);
    }
    if (!gdb) {
        gdb = WMCreatePLDictionary(NULL, NULL);
    }

    GlobalDB = gdb;

    WindowMakerDB = db;
}


WMPropList*
GetObjectForKey(char *defaultName)
{
    WMPropList *object = NULL;
    WMPropList *key = WMCreatePLString(defaultName);

    object = WMGetFromPLDictionary(WindowMakerDB, key);
    if (!object)
        object = WMGetFromPLDictionary(GlobalDB, key);

    WMReleasePropList(key);

    return object;
}


void
SetObjectForKey(WMPropList *object, char *defaultName)
{
    WMPropList *key = WMCreatePLString(defaultName);

    WMPutInPLDictionary(WindowMakerDB, key, object);
    WMReleasePropList(key);
}


void
RemoveObjectForKey(char *defaultName)
{
    WMPropList *key = WMCreatePLString(defaultName);

    WMRemoveFromPLDictionary(WindowMakerDB, key);

    WMReleasePropList(key);
}


char*
GetStringForKey(char *defaultName)
{
    WMPropList *val;

    val = GetObjectForKey(defaultName);

    if (!val)
        return NULL;

    if (!WMIsPLString(val))
        return NULL;

    return WMGetFromPLString(val);
}



WMPropList*
GetArrayForKey(char *defaultName)
{
    WMPropList *val;

    val = GetObjectForKey(defaultName);

    if (!val)
        return NULL;

    if (!WMIsPLArray(val))
        return NULL;

    return val;
}


WMPropList*
GetDictionaryForKey(char *defaultName)
{
    WMPropList *val;

    val = GetObjectForKey(defaultName);

    if (!val)
        return NULL;

    if (!WMIsPLDictionary(val))
        return NULL;

    return val;
}


int
GetIntegerForKey(char *defaultName)
{
    WMPropList *val;
    char *str;
    int value;

    val = GetObjectForKey(defaultName);

    if (!val)
        return 0;

    if (!WMIsPLString(val))
        return 0;

    str = WMGetFromPLString(val);
    if (!str)
        return 0;

    if (sscanf(str, "%i", &value)!=1)
        return 0;

    return value;
}


Bool
GetBoolForKey(char *defaultName)
{
    int value;
    char *str;

    str = GetStringForKey(defaultName);

    if (!str)
        return False;

    if (sscanf(str, "%i", &value)==1 && value!=0)
        return True;

    if (strcasecmp(str, "YES")==0)
        return True;

    if (strcasecmp(str, "Y")==0)
        return True;

    return False;
}


void
SetIntegerForKey(int value, char *defaultName)
{
    WMPropList *object;
    char buffer[128];

    sprintf(buffer, "%i", value);
    object = WMCreatePLString(buffer);

    SetObjectForKey(object, defaultName);
    WMReleasePropList(object);
}



void
SetStringForKey(char *value, char *defaultName)
{
    WMPropList *object;

    object = WMCreatePLString(value);

    SetObjectForKey(object, defaultName);
    WMReleasePropList(object);
}


void
SetBoolForKey(Bool value, char *defaultName)
{
    static WMPropList *yes = NULL, *no = NULL;

    if (!yes) {
        yes = WMCreatePLString("YES");
        no = WMCreatePLString("NO");
    }

    SetObjectForKey(value ? yes : no, defaultName);
}


void
SetSpeedForKey(int speed, char *defaultName)
{
    char *str;

    switch (speed) {
    case 0:
        str = "ultraslow";
        break;
    case 1:
        str = "slow";
        break;
    case 2:
        str = "medium";
        break;
    case 3:
        str = "fast";
        break;
    case 4:
        str = "ultrafast";
        break;
    default:
        str = NULL;
    }

    if (str)
        SetStringForKey(str, defaultName);
}


int
GetSpeedForKey(char *defaultName)
{
    char *str;
    int i;

    str = GetStringForKey(defaultName);
    if (!str)
        return 2;

    if (strcasecmp(str, "ultraslow")==0)
        i = 0;
    else if (strcasecmp(str, "slow")==0)
        i = 1;
    else if (strcasecmp(str, "medium")==0)
        i = 2;
    else if (strcasecmp(str, "fast")==0)
        i = 3;
    else if (strcasecmp(str, "ultrafast")==0)
        i = 4;
    else {
        wwarning(_("bad speed value for option %s\n. Using default Medium"),
                 defaultName);
        i = 2;
    }
    return i;
}




syntax highlighted by Code2HTML, v. 0.9.1