/*
*  XmNap  A Motif napster client
*
*  Copyright (C) 2000 Mats Peterson
*
*  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.  If not, write to
*  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
*  Boston, MA 02111-1307, USA.
*
*  Please send any comments/bug reports to
*  [email protected]  (Mats Peterson)
*/

#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/MessageB.h>
#include <Xm/Protocols.h>
#include <Xm/Notebook.h>
#include <Xm/RowColumn.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/TextF.h>
#include <Xm/SelectioB.h>
#include <Xm/ToggleB.h>
#include <Xm/Protocols.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>

#include "main.h"
#include "connect.h"
#include "command.h"
#include "search.h"
#include "config.h"
#include "configwin.h"
#include "transfer.h"
#include "shared.h"
#include "hotlist.h"
#include "input.h"
#include "msgbox.h"
#include "fsbox.h"
#ifdef USE_SOUND
#include "sound.h"
#endif
#include "util.h"


static String tmpPassWord;
static int tmpLinkType, tmpRegistered;
static String tmpMinLink;

static SERVER *tmpServers = NULL;
static SHAREDDIR *tmpSharedDirs = NULL;
static HOTLIST *tmpHotList = NULL;
static AUTOCOMMAND *tmpAutoCommands = NULL;

static int tmpAutoReconnect;
static int tmpShowPath;
static int tmpTimeOut;
static int tmpReconnTimeOut;

#ifdef USE_SOUND
#define LAST_PAGE 8
static String curSoundDir = NULL;
#else
#define LAST_PAGE 7
#endif

static int configChanged;

static int serverChanged;
static int sharedDirChanged;
static int hotListChanged;
static int autoCmdChanged;

static Widget configWin = NULL;



static void DestroyConfigWin(void)
{
   if (configWin) {
       XtUnmapWidget(XtParent(configWin));
       XtDestroyWidget(XtParent(configWin));
       configWin = curFocus = NULL;
   }
}


static void ConfigCB(Widget w, XtPointer clientData,
       XmPushButtonCallbackStruct *cbs)
{
   SERVER *srv, *tmpSrv, *prevSrv = NULL;
   SHAREDDIR *dir, *tmpDir, *prevDir = NULL;
   SHARED *shared;
   HOTLIST *hot, *tmpHot, *prevHot = NULL;
   AUTOCOMMAND *cmd, *tmpCmd, *prevCmd = NULL;
   String p, tmp;
   Widget entry;
   int i;
   String userConfigEntryName[4] = {"*userConfig*userName",
                                    "*userConfig*passWord",
                                    "*userConfig*eMail",
                                    "*userConfig*dataPort"};
   String searchConfigEntryName[3] = {"*searchConfig*minBitrate",
                                      "*searchConfig*minFreq",
                                      "*searchConfig*maxFiles"};
   String *userInfoField[3] = {&userInfo.userName, &userInfo.passWord,
                               &userInfo.eMail};
   String *defSearchField[4] = {&defSearch.minBitrate, &defSearch.minFreq,
                                &defSearch.maxFiles, &defSearch.minLink};
#ifdef USE_SOUND
   String soundConfigEntryName[NUM_SOUNDS] =
                               {"*soundConfig*chanMsgSound",
                                "*soundConfig*privMsgSound",
                                "*soundConfig*joinSound",
                                "*soundConfig*partSound"};
#endif

   switch (cbs->reason) {
       case XmCR_OK:
           for (i = 0; i < 4; i++) {
               if (i == 1)
                   continue;
               entry = XtNameToWidget(w, userConfigEntryName[i]);
               tmp = XmTextFieldGetString(entry);
               if (i == 3) {
                   if (userInfo.dataPort != atoi(tmp)) {
                       userInfo.dataPort = atoi(tmp);
                       SetDataPort(userInfo.dataPort, 1);
                       configChanged = 1;
                   }
               } else {
                   if (strcmp(*userInfoField[i], tmp)) {
                       XtFree(*userInfoField[i]);
                       *userInfoField[i] = XtNewString(tmp);
                       configChanged = 1;
                   }
               }
               XtFree(tmp);
           }

           for (i = 0; i < 3; i++) {
               entry = XtNameToWidget(w, searchConfigEntryName[i]);
               tmp = XmTextFieldGetString(entry);
               if (strcmp(*defSearchField[i], tmp)) {
                   XtFree(*defSearchField[i]);
                   *defSearchField[i] = XtNewString(tmp);
                   configChanged = 1;
               }
               XtFree(tmp);
           }
#ifdef USE_SOUND
           for (i = 0; i < NUM_SOUNDS; i++) {
               entry = XtNameToWidget(w, soundConfigEntryName[i]);
               tmp = XmTextFieldGetString(entry);
               if (strcmp(sound[i], tmp)) {
                   XtFree(sound[i]);
                   sound[i] = XtNewString(tmp);
                   configChanged = 1;
               }
               XtFree(tmp);
           }
#endif
           entry = XtNameToWidget(w, "*miscConfig*timeOut");
           tmp = XmTextFieldGetString(entry);
           if (timeOut != atoi(tmp)) {
               timeOut = atoi(tmp);
               configChanged = 1;
           }
           XtFree(tmp);

           entry = XtNameToWidget(w, "*miscConfig*reconnTimeOut");
           tmp = XmTextFieldGetString(entry);
           if (reconnTimeOut != atoi(tmp)) {
               reconnTimeOut = atoi(tmp);
               configChanged = 1;
           }
           XtFree(tmp);

           entry = XtNameToWidget(w, "*miscConfig*initDlDir");
           tmp = XmTextFieldGetString(entry);
           if (strcmp(initDlDir, tmp)) {
               XtFree(initDlDir);
               initDlDir = XtNewString(tmp);
               configChanged = 1;
           }
           XtFree(tmp);

           if (configChanged) {
               SimpleMsg("Saving configuration");
               WriteConfig();
               SimpleMsgRemove();
           }
           break;
       case XmCR_CANCEL:
       case XmCR_PROTOCOLS:
           XtFree(userInfo.passWord);
           userInfo.passWord = XtNewString(tmpPassWord);
           userInfo.linkType = tmpLinkType;
           userInfo.registered = tmpRegistered;

           XtFree(defSearch.minLink);
           defSearch.minLink = XtNewString(tmpMinLink);

           if (serverChanged) {
               while (servers) {
                   XtFree(servers->name);
                   p = (String)servers;
                   servers = servers->next;
                   XtFree(p);
               }

               for (tmpSrv = tmpServers; tmpSrv; tmpSrv = tmpSrv->next) {
                   srv = XtNew(SERVER);
                   srv->name = XtNewString(tmpSrv->name);
                   srv->port = tmpSrv->port;
                   srv->meta = tmpSrv->meta;
                   srv->next = NULL;
                   if (! prevSrv)
                       servers = srv;
                   else
                       prevSrv->next = srv;
                   prevSrv = srv;
               }
           }

           if (sharedDirChanged) {
               while (sharedDirs) {
                   tmp = XtMalloc(PATH_MAX + 2);
                   sprintf(tmp, "%s*", sharedDirs->dirName);
                   for (shared = sharedFiles; shared; shared = shared->next) {
                       if (! fnmatch(tmp, shared->fileName, 0))
                           RemoveSharedFile(shared->fileName);
                   }
                   XtFree(tmp);
                   XtFree(sharedDirs->dirName);
                   p = (String)sharedDirs;
                   sharedDirs = sharedDirs->next;
                   XtFree(p);
               }

               for (tmpDir = tmpSharedDirs; tmpDir; tmpDir = tmpDir->next) {
                   dir = XtNew(SHAREDDIR);
                   dir->dirName = XtNewString(tmpDir->dirName);
                   dir->next = NULL;
                   if (! prevDir)
                       sharedDirs = dir;
                   else
                       prevDir->next = dir;
                   prevDir = dir;
                   (void)AddSharedFiles2(dir->dirName);
               }
           }

           if (hotListChanged) {
               while (hotList)
                   RemoveHotList(hotList->nick);

               for (tmpHot = tmpHotList; tmpHot; tmpHot = tmpHot->next) {
                   hot = XtNew(HOTLIST);
                   hot->nick = XtNewString(tmpHot->nick);
                   hot->next = NULL;
                   if (! prevHot)
                       hotList = hot;
                   else
                       prevHot->next = hot;
                   prevHot = hot;
                   AddHotList(hot->nick, 1);
               }
           }

           if (autoCmdChanged) {
               while (autoCommands) {
                   XtFree(autoCommands->data);
                   p = (String)autoCommands;
                   autoCommands = autoCommands->next;
                   XtFree(p);
               }

               for (tmpCmd = tmpAutoCommands; tmpCmd; tmpCmd = tmpCmd->next) {
                   cmd = XtNew(AUTOCOMMAND);
                   cmd->data = XtNewString(tmpCmd->data);
                   cmd->next = NULL;
                   if (! prevCmd)
                       autoCommands = cmd;
                   else
                       prevCmd->next = cmd;
                   prevCmd = cmd;
               }
           }

           autoReconnect = tmpAutoReconnect;
           showPath = tmpShowPath;
           timeOut = tmpTimeOut;
           reconnTimeOut = tmpReconnTimeOut;
   }

   XtFree(tmpPassWord);
   XtFree(tmpMinLink);

   while (tmpServers) {
       XtFree(tmpServers->name);
       p = (String)tmpServers;
       tmpServers = tmpServers->next;
       XtFree(p);
   }

   while (tmpSharedDirs) {
       XtFree(tmpSharedDirs->dirName);
       p = (String)tmpSharedDirs;
       tmpSharedDirs = tmpSharedDirs->next;
       XtFree(p);
   }

   while (tmpHotList) {
       XtFree(tmpHotList->nick);
       p = (String)tmpHotList;
       tmpHotList = tmpHotList->next;
       XtFree(p);
   }

   while (tmpAutoCommands) {
       XtFree(tmpAutoCommands->data);
       p = (String)tmpAutoCommands;
       tmpAutoCommands = tmpAutoCommands->next;
       XtFree(p);
   }

   DestroyConfigWin();
}


static void ConfigEntryFocusCB(Widget w, XtPointer clientData,
       XmAnyCallbackStruct *cbs) {
   XtVaSetValues(w, XmNcursorPositionVisible, True, NULL);
}


static void ConfigEntryLosingFocusCB(Widget w, XtPointer clientData,
       XmTextVerifyCallbackStruct *cbs) {
   XtVaSetValues(w, XmNcursorPositionVisible, False, NULL);
}


static Widget ConfigForm(Widget parent, int page, String formName)
{
   Widget form, title, rowCol;
   Arg args[20];
   int n;

   form = XtVaCreateManagedWidget(formName,
           xmFormWidgetClass, parent,
           XmNmarginWidth, 15,
           XmNmarginHeight, 15,
           XmNnotebookChildType, XmPAGE,
           XmNpageNumber, page,
           NULL);

   n = 0;
   XtSetArg(args[n], XmNmarginHeight, 6); n++;
   XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
   XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
   title = XmCreateLabel(form, "title", args, n);
   XtManageChild(title);

   rowCol = XtVaCreateManagedWidget("rowCol",
           xmRowColumnWidgetClass, form,
           XmNtopAttachment, XmATTACH_WIDGET,
           XmNtopWidget, title,
           XmNmarginWidth, 0,
           XmNtopOffset, 15,
           XmNbottomAttachment, XmATTACH_FORM,
           XmNleftAttachment, XmATTACH_FORM,
           XmNrightAttachment, XmATTACH_FORM,
           NULL);

   return form;
}


static Widget ConfigList(Widget parent, int page, String listName,
       XtCallbackProc callback)
{
   Widget list;
   XmString xms;
   Arg args[20];
   int n;
   String tmp;

   n = 0;
   XtSetArg(args[n], XmNresizePolicy, XmRESIZE_NONE); n++;
   XtSetArg(args[n], XmNnotebookChildType, XmPAGE); n++;
   XtSetArg(args[n], XmNpageNumber, page); n++;
   list = XmCreateSelectionBox(parent, listName, args, n);
   XtManageChild(list);

   XtAddCallback (list, XmNokCallback,
           (XtCallbackProc)callback, NULL);

   XtVaGetValues(list, XmNapplyLabelString, &xms, NULL);
   XmStringGetLtoR(xms, XmFONTLIST_DEFAULT_TAG, &tmp);
   if (strlen(tmp)) {
       XtManageChild(XtNameToWidget(list, "Apply"));
       XtAddCallback (list, XmNapplyCallback,
           (XtCallbackProc)callback, NULL);
   }
   XtFree(tmp);
   XmStringFree(xms);

   XtAddCallback (list, XmNcancelCallback,
           (XtCallbackProc)callback, NULL);

   XtUnmanageChild(XtNameToWidget(list, "Help"));
   XtUnmanageChild(XtNameToWidget(list, "Text"));
   XtUnmanageChild(XtNameToWidget(list, "Selection"));

   return list;
}


static void UserConfigPasswordCB(Widget w, XtPointer clientData,
       XtPointer callData)
{
   char *new;
   int len;
   XmTextVerifyCallbackStruct *cbs =
       (XmTextVerifyCallbackStruct *)callData;

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

   if (cbs->startPos < cbs->endPos)   {     /* don't allow delete */
       cbs->doit = False;
       return;
   }

   if (cbs->text->length > 1) {
       cbs->doit = False;        /* don't allow "paste" operations */
       return;                   /* make the user *type* the password! */
   }

   new = XtMalloc(cbs->endPos + 2);  /* new char + NULL terminator */
   if (userInfo.passWord) {
       strcpy(new, userInfo.passWord);
       XtFree(userInfo.passWord);
   } else
       new[0] = NULL;
   userInfo.passWord = new;
   strncat(userInfo.passWord, cbs->text->ptr, cbs->text->length);
   userInfo.passWord[cbs->endPos + cbs->text->length] = 0;

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

   configChanged = 1;
}


static void UserConfigLinkCB(Widget w, XtPointer clientData,
       XtPointer cbs)
{
   userInfo.linkType = (int)clientData;
   configChanged = 1;
}


static void UserConfigRegCB(Widget w, XtPointer clientData,
       XmToggleButtonCallbackStruct *cbs)
{
   userInfo.registered = (cbs->set == XmSET) ? 1 : 0;
   configChanged = 1;
}


static void UserConfig(Widget parent)
{
   String values[6];
   char dataPort[10];
   Widget pageForm, rowCol, form, label, entry = NULL;
   Widget linkMenu, regToggle;
   Arg args[20];
   XmString toggleString;
   XmString optLabel[11];
   int i, j, n;
   Dimension labelWidth, labelMaxWidth = 0;
   char labelName[20];
   String entryName[] = {"userName", "passWord", "eMail", "dataPort"};
   String starPassWord;

   pageForm = ConfigForm(parent, 1, "userConfig");
   rowCol = XtNameToWidget(pageForm, "rowCol");

   starPassWord = XtMalloc(strlen(userInfo.passWord) + 2);
   for (i = 0; i < strlen(userInfo.passWord); i++)
       starPassWord[i] = '*';
   starPassWord[i] = '\0';

   values[0] = userInfo.userName;
   values[1] = starPassWord;
   values[2] = userInfo.eMail;
   sprintf(dataPort, "%d", userInfo.dataPort);
   values[3] = dataPort;

   for (i = 0; i < 6; i++) {
       form = XtVaCreateWidget("form", xmFormWidgetClass,
               rowCol, NULL);

       sprintf(labelName, "label_%d", i);

       n = 0;
       XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
       XtSetArg(args[n], XmNmarginHeight, 6); n++;
       XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
       XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
       XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
       label = XmCreateLabel(form, labelName, args, n);
       XtManageChild(label);

       XtVaGetValues(label, XmNwidth, &labelWidth, NULL);
       if (labelWidth > labelMaxWidth)
           labelMaxWidth = labelWidth;

       if (i == 4) {
           for (j = 0; j < 11; j++)
               optLabel[j] = XmStringCreateLocalized (linkStr[j]);

           linkMenu = XmVaCreateSimpleOptionMenu (form, "linkMenu",
                   NULL, 0, userInfo.linkType, UserConfigLinkCB,
                   XmVaPUSHBUTTON, optLabel[0], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[1], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[2], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[3], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[4], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[5], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[6], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[7], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[8], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[9], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[10], NULL, NULL, NULL,
                   XmNtopAttachment, XmATTACH_FORM,
                   XmNbottomAttachment, XmATTACH_FORM,
                   XmNleftAttachment, XmATTACH_WIDGET,
                   XmNleftWidget, label,
                   XmNleftOffset, 20,
                   XmNmarginWidth, 0,
                   XmNmarginHeight, 0,
                   NULL);

           for (j = 0; j < 11; j++)
               XmStringFree(optLabel[j]);

           XtUnmanageChild(XtNameToWidget(linkMenu, "OptionLabel"));
           XtManageChild (linkMenu);
       } else if (i == 5) {
           toggleString = XmStringCreateLocalized(" ");
           regToggle = XtVaCreateManagedWidget("regToggle",
                   xmToggleButtonWidgetClass, form,
                   XmNlabelString, toggleString,
                   XmNindicatorOn, XmINDICATOR_CROSS_BOX,
                   XmNset, userInfo.registered ? XmSET : XmUNSET,
                   XmNtopAttachment, XmATTACH_FORM,
                   XmNbottomAttachment, XmATTACH_FORM,
                   XmNleftAttachment, XmATTACH_WIDGET,
                   XmNleftWidget, label,
                   XmNleftOffset, 20,
                   NULL);
           XmStringFree(toggleString);
           XtAddCallback(regToggle, XmNvalueChangedCallback,
                   (XtCallbackProc)UserConfigRegCB, NULL);
       } else {
           n = 0;
           XtSetArg(args[n], XmNcursorPositionVisible, False); n++;
           XtSetArg(args[n], XmNmarginWidth, 5); n++;
           XtSetArg(args[n], XmNmarginHeight, 2); n++;
           XtSetArg(args[n], XmNcolumns, 30); n++;
           XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
           XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
           XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
           XtSetArg(args[n], XmNleftWidget, label); n++;
           XtSetArg(args[n], XmNleftOffset, 20); n++;
           entry = XmCreateTextField(form, entryName[i], args, n);
           XtManageChild(entry);
           XmTextFieldSetString(entry, values[i]);

           XtAddCallback(entry, XmNfocusCallback,
                   (XtCallbackProc)ConfigEntryFocusCB, NULL);
           XtAddCallback(entry, XmNlosingFocusCallback,
                   (XtCallbackProc)ConfigEntryLosingFocusCB, NULL);

           if (i == 1) {
               XtAddCallback(entry, XmNmodifyVerifyCallback,
                       (XtCallbackProc)UserConfigPasswordCB, NULL);

           }
       }
       XtManageChild(form);
   }

   for (i = 0; i < 6; i++) {
       sprintf(labelName, "form.label_%d", i);
       XtVaSetValues(XtNameToWidget(rowCol, labelName),
               XmNwidth, labelMaxWidth, NULL);
   }

   XtFree(starPassWord);
}


static void SearchConfigLinkCB(Widget w, XtPointer clientData,
       XtPointer cbs)
{
   XtFree(defSearch.minLink);
   defSearch.minLink = XtMalloc(5);
   sprintf(defSearch.minLink, "%d", (int)clientData);
   configChanged = 1;
}


static void SearchConfig(Widget parent)
{
   String values[4];
   Widget pageForm, rowCol, form, label, entry = NULL, linkMenu;
   Arg args[20];
   XmString optLabel[11];
   int i, j, n;
   Dimension labelWidth, labelMaxWidth = 0;
   char labelName[20];
   String entryName[] = {"minBitrate", "minFreq", "maxFiles"};

   pageForm = ConfigForm(parent, 2, "searchConfig");
   rowCol = XtNameToWidget(pageForm, "rowCol");

   values[0] = defSearch.minBitrate;
   values[1] = defSearch.minFreq;
   values[2] = defSearch.maxFiles;

   for (i = 0; i < 4; i++) {
       form = XtVaCreateWidget("form", xmFormWidgetClass,
               rowCol, NULL);

       sprintf(labelName, "label_%d", i);

       n = 0;
       XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
       XtSetArg(args[n], XmNmarginHeight, 6); n++;
       XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
       XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
       XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
       label = XmCreateLabel(form, labelName, args, n);
       XtManageChild(label);

       XtVaGetValues(label, XmNwidth, &labelWidth, NULL);
       if (labelWidth > labelMaxWidth)
           labelMaxWidth = labelWidth;

       if (i == 3) {
           for (j = 0; j < 11; j++)
               optLabel[j] = XmStringCreateLocalized (linkStr[j]);

           linkMenu = XmVaCreateSimpleOptionMenu (form, "linkMenu",
                   NULL, 0, atoi(defSearch.minLink), SearchConfigLinkCB,
                   XmVaPUSHBUTTON, optLabel[0], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[1], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[2], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[3], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[4], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[5], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[6], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[7], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[8], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[9], NULL, NULL, NULL,
                   XmVaPUSHBUTTON, optLabel[10], NULL, NULL, NULL,
                   XmNtopAttachment, XmATTACH_FORM,
                   XmNbottomAttachment, XmATTACH_FORM,
                   XmNleftAttachment, XmATTACH_WIDGET,
                   XmNleftWidget, label,
                   XmNleftOffset, 20,
                   XmNmarginWidth, 0,
                   XmNmarginHeight, 0,
                   NULL);

           for (j = 0; j < 11; j++)
               XmStringFree(optLabel[j]);

           XtUnmanageChild(XtNameToWidget(linkMenu, "OptionLabel"));
           XtManageChild (linkMenu);
       } else {
           n = 0;
           XtSetArg(args[n], XmNcursorPositionVisible, False); n++;
           XtSetArg(args[n], XmNmarginWidth, 5); n++;
           XtSetArg(args[n], XmNmarginHeight, 2); n++;
           XtSetArg(args[n], XmNcolumns, 10); n++;
           XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
           XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
           XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
           XtSetArg(args[n], XmNleftWidget, label); n++;
           XtSetArg(args[n], XmNleftOffset, 20); n++;
           entry = XmCreateTextField(form, entryName[i], args, n);
           XtManageChild(entry);
           XmTextFieldSetString(entry, values[i]);

           XtAddCallback(entry, XmNfocusCallback,
                   (XtCallbackProc)ConfigEntryFocusCB, NULL);
           XtAddCallback(entry, XmNlosingFocusCallback,
                   (XtCallbackProc)ConfigEntryLosingFocusCB, NULL);
       }
       XtManageChild(form);
   }

   for (i = 0; i < 4; i++) {
       sprintf(labelName, "form.label_%d", i);
       XtVaSetValues(XtNameToWidget(rowCol, labelName),
               XmNwidth, labelMaxWidth, NULL);
   }
}


static void UpdateServerList(Widget list)
{
   String tmp = XtMalloc(256);
   int numServers, i;
   XmString *str;
   SERVER *srv;
   String srvType[2] = {"Normal", "Meta"};

   for (numServers = 0, srv = servers; srv;
        numServers++, srv = srv->next);

   str = (XmString*)XtMalloc(numServers * sizeof(XmString));
   for (i = 0, srv = servers; i < numServers; i++, srv = srv->next) {
       sprintf(tmp, "%-30.29s%-10d%s",
               srv->name, srv->port, srvType[srv->meta]);
       str[i] = XmStringCreateLocalized(tmp);
   }

   XtVaSetValues(list,
           XmNlistItems, str,
           XmNlistItemCount, numServers,
           NULL);

   for (i = 0; i < numServers; i++)
       XmStringFree (str[i]);
   XtFree((String)str);
   XtFree(tmp);
}


static void SrvListCB(Widget w, XtPointer clientData,
       XmSelectionBoxCallbackStruct *cbs)
{
   Widget list, shell;
   int i, *pos;
   SERVER *newSrv, *srv, *prevSrv = NULL;
   String srvString, srvName, portString, metaString;
   XmPushButtonCallbackStruct cbs2;

   switch (cbs->reason) {
       case XmCR_OK:
           list = XtNameToWidget(w, "ItemsListSW.ItemsList");
           XtVaGetValues(list, XmNselectedPositions, &pos, NULL);
           if (! pos)
               return;
           cbs2.reason = XmCR_OK;
           for (shell = w; ! XtIsShell(shell); shell = XtParent(shell));
           XtCallCallbacks(XtNameToWidget(shell, "configWin"),
                   XmNokCallback, (XtPointer)&cbs2);
           for (i = 0, srv = servers; i < ((*pos) - 1);
                    i++, srv = srv->next);
           if (srvConn)
               Disconnect("");
           Connect(srv->name, srv->port, srv->meta, 1);
           return;
       case XmCR_APPLY:
           srvString = GetInput("Server (name:port:meta)", "", 30);
           if (! strlen(srvString))
               return;

           if (! SplitServer(srvString, &srvName, &portString,
                   &metaString)) {
               ErrMsg("Invalid server format");
               return;
           }

           newSrv = XtNew(SERVER);
           newSrv->name = XtNewString(srvName);
           newSrv->port = atoi(portString);
           newSrv->meta = atoi(metaString);
           newSrv->next = NULL;

           if (! servers)
               servers = newSrv;
           else {
               for (srv = servers; srv->next; srv = srv->next);
               srv->next = newSrv;
           }
           break;
       case XmCR_CANCEL:
           list = XtNameToWidget(w, "ItemsListSW.ItemsList");
           XtVaGetValues(list, XmNselectedPositions, &pos, NULL);
           if (! pos)
               return;
           for (i = 0, srv = servers; i < ((*pos) - 1);
                i++, srv = srv->next) {
               prevSrv = srv;
           }

           if (prevSrv) {
               prevSrv->next = srv->next;
           } else {
               servers = servers->next;
           }

           XtFree(srv->name);
           XtFree((char*)srv);
   }

   if (servers) {
       if (curServer.name)
           XtFree(curServer.name);
       curServer.name = XtNewString(servers->name);
       curServer.port = servers->port;
       curServer.meta = servers->meta;
   }

   UpdateServerList(w);
   serverChanged = 1;
   configChanged = 1;
}


static void ServerConfig(Widget parent)
{
   Widget serverList;

   serverList = ConfigList(parent, 3, "serverConfig",
           (XtCallbackProc)SrvListCB);

   if (! servers)
       return;
   UpdateServerList(serverList);
}


static void UpdateSharedDirList(Widget list)
{
   int numDirs, i;
   XmString *str;
   SHAREDDIR *dir;

   for (numDirs = 0, dir = sharedDirs; dir;
        numDirs++, dir = dir->next);

   str = (XmString*)XtMalloc(numDirs * sizeof(XmString));
   for (i = 0, dir = sharedDirs; i < numDirs; i++, dir = dir->next)
       str[i] = XmStringCreateLocalized(dir->dirName);

   XtVaSetValues(list,
           XmNlistItems, str,
           XmNlistItemCount, numDirs,
           NULL);

   for (i = 0; i < numDirs; i++)
       XmStringFree (str[i]);
   XtFree((String)str);
}


static void SharedDirListCB(Widget w, XtPointer clientData,
       XmSelectionBoxCallbackStruct *cbs)
{
   Widget list;
   int i, *pos;
   SHAREDDIR *dir;

   switch (cbs->reason) {
       case XmCR_OK:
           if (! AddSharedFiles())
               return;
           break;
       case XmCR_CANCEL:
           list = XtNameToWidget(w, "ItemsListSW.ItemsList");
           XtVaGetValues(list, XmNselectedPositions, &pos, NULL);
           if (! pos)
               return;
           for (i = 0, dir = sharedDirs; i < ((*pos) - 1);
                i++, dir = dir->next);
           if (! RemoveSharedFiles(dir->dirName))
               return;
   }

   UpdateSharedDirList(w);
   sharedDirChanged = 1;
   configChanged = 1;
}


static void SharedDirConfig(Widget parent)
{
   Widget sharedDirList;

   sharedDirList = ConfigList(parent, 4, "sharedConfig",
           (XtCallbackProc)SharedDirListCB);

   if (! sharedDirs)
       return;
   UpdateSharedDirList(sharedDirList);
}


static void UpdateHotList(Widget list)
{
   int numHot, i;
   XmString *str;
   HOTLIST *hot;

   for (numHot = 0, hot = hotList; hot;
        numHot++, hot = hot->next);

   str = (XmString*)XtMalloc(numHot * sizeof(XmString));
   for (i = 0, hot = hotList; i < numHot; i++, hot = hot->next)
       str[i] = XmStringCreateLocalized(hot->nick);

   XtVaSetValues(list,
           XmNlistItems, str,
           XmNlistItemCount, numHot,
           NULL);

   for (i = 0; i < numHot; i++)
       XmStringFree (str[i]);
   XtFree((String)str);
}


static void HotListCB(Widget w, XtPointer clientData,
       XmSelectionBoxCallbackStruct *cbs)
{
   Widget list;
   int *pos;
   String nick;

   switch (cbs->reason) {
       case XmCR_OK:
           nick = GetInput("User", "", 30);
           if (! strlen(nick))
               return;
           AddHotList(nick, 1);
           break;
       case XmCR_CANCEL:
           list = XtNameToWidget(w, "ItemsListSW.ItemsList");
           XtVaGetValues(list, XmNselectedPositions, &pos, NULL);
           if (! pos)
               return;
           RemoveHotList2((*pos) - 1);
   }

   UpdateHotList(w);
   hotListChanged = 1;
   configChanged = 1;
}


static void HotListConfig(Widget parent)
{
   Widget hotList;

   hotList = ConfigList(parent, 5, "hotlistConfig",
           (XtCallbackProc)HotListCB);

   if (! hotList)
       return;
   UpdateHotList(hotList);
}


static void UpdateAutoCmdList(Widget list)
{
   int numCmds, i;
   XmString *str;
   AUTOCOMMAND *cmd;

   for (numCmds = 0, cmd = autoCommands; cmd;
        numCmds++, cmd = cmd->next);

   str = (XmString*)XtMalloc(numCmds * sizeof(XmString));
   for (i = 0, cmd = autoCommands; i < numCmds; i++, cmd = cmd->next)
       str[i] = XmStringCreateLocalized(cmd->data);

   XtVaSetValues(list,
           XmNlistItems, str,
           XmNlistItemCount, numCmds,
           NULL);

   for (i = 0; i < numCmds; i++)
       XmStringFree (str[i]);
   XtFree((String)str);
}


static void AutoCmdListCB(Widget w, XtPointer clientData,
       XmSelectionBoxCallbackStruct *cbs)
{
   Widget list;
   int i, *pos;
   AUTOCOMMAND *newCmd, *cmd, *prevCmd = NULL;
   String cmdString;

   switch (cbs->reason) {
       case XmCR_OK:
           cmdString = GetInput("Command", "", 40);
           if (! strlen(cmdString))
                   return;
           newCmd = XtNew(AUTOCOMMAND);
           newCmd->data = XtNewString(cmdString);
           newCmd->next = NULL;
           if (! autoCommands)
               autoCommands = newCmd;
           else {
               for (cmd = autoCommands; cmd->next; cmd = cmd->next);
               cmd->next = newCmd;
           }
           break;
       case XmCR_CANCEL:
           list = XtNameToWidget(w, "ItemsListSW.ItemsList");
           XtVaGetValues(list, XmNselectedPositions, &pos, NULL);
           if (! pos)
               return;
           for (i = 0, cmd = autoCommands; i < ((*pos) - 1);
                i++, cmd = cmd->next) {
               prevCmd = cmd;
           }
           if (prevCmd)
               prevCmd->next = cmd->next;
           else
               autoCommands = autoCommands->next;
           XtFree(cmd->data);
           XtFree((char*)cmd);
   }

   UpdateAutoCmdList(w);
   autoCmdChanged = 1;
   configChanged = 1;
}


static void AutoCmdConfig(Widget parent)
{
   Widget autoCmdList;

   autoCmdList = ConfigList(parent, 6, "autoCmdConfig",
           (XtCallbackProc)AutoCmdListCB);

   if (! autoCommands)
       return;
   UpdateAutoCmdList(autoCmdList);
}


#ifdef USE_SOUND
static void SoundBtnCB(Widget w, XtPointer clientData,
       XmPushButtonCallbackStruct *cbs)
{
   Widget entry = (Widget)clientData;
   String tmp, dir, tail, name;

   if (! curSoundDir)
       curSoundDir = XtNewString(getenv("HOME"));

   tmp = XmTextFieldGetString(entry);
   if ((tail = strrchr(tmp, '/'))) {
       dir = GetDir(tmp);
       tail += 1;
   } else {
       dir = XtNewString(curSoundDir);
       tail = "";
   }

   name = OpenFile(dir, tail);
   if (! *name)
       goto exit;

   if (CheckWav(name) == -1)
       goto exit;

   XmTextFieldSetString(entry, name);
   XtFree(curSoundDir);
   curSoundDir = GetDir(name);

exit:
   XtFree(tmp);
   XtFree(dir);
}


static void SoundConfig(Widget parent)
{
   Widget pageForm, rowCol, form, label, entry = NULL, btn;
   Arg args[20];
   int i, n;
   Dimension labelWidth, labelMaxWidth = 0;
   char labelName[20], btnName[20];
   String entryName[] = {"chanMsgSound", "privMsgSound",
                         "joinSound", "partSound"};

   pageForm = ConfigForm(parent, 7, "soundConfig");
   rowCol = XtNameToWidget(pageForm, "rowCol");

   for (i = 0; i < NUM_SOUNDS; i++) {
       form = XtVaCreateWidget("form", xmFormWidgetClass,
               rowCol, NULL);

       sprintf(labelName, "label_%d", i);

       n = 0;
       XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
       XtSetArg(args[n], XmNmarginHeight, 6); n++;
       XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
       XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
       XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
       label = XmCreateLabel(form, labelName, args, n);
       XtManageChild(label);

       XtVaGetValues(label, XmNwidth, &labelWidth, NULL);
       if (labelWidth > labelMaxWidth)
           labelMaxWidth = labelWidth;

       sprintf(btnName, "button_%d", i);

       btn = XtVaCreateManagedWidget(btnName,
               xmPushButtonWidgetClass, form,
               XmNtopAttachment, XmATTACH_FORM,
               XmNbottomAttachment, XmATTACH_FORM,
               XmNrightAttachment, XmATTACH_FORM,
               NULL);

       n = 0;
       XtSetArg(args[n], XmNcursorPositionVisible, False); n++;
       XtSetArg(args[n], XmNmarginWidth, 5); n++;
       XtSetArg(args[n], XmNmarginHeight, 2); n++;
       XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
       XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
       XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
       XtSetArg(args[n], XmNleftWidget, label); n++;
       XtSetArg(args[n], XmNleftOffset, 20); n++;
       XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
       XtSetArg(args[n], XmNrightWidget, btn); n++;
       XtSetArg(args[n], XmNrightOffset, 10); n++;
       XtSetArg(args[n], XmNcolumns, 25); n++;
       entry = XmCreateTextField(form, entryName[i], args, n);

       XtAddCallback(btn, XmNactivateCallback,
               (XtCallbackProc)SoundBtnCB, (XtPointer)entry);

       XtAddCallback(entry, XmNfocusCallback,
               (XtCallbackProc)ConfigEntryFocusCB, NULL);
       XtAddCallback(entry, XmNlosingFocusCallback,
               (XtCallbackProc)ConfigEntryLosingFocusCB, NULL);
       XtManageChild(entry);
       XmTextFieldSetString(entry, sound[i]);
       XtManageChild(form);
   }

   for (i = 0; i < NUM_SOUNDS; i++) {
       sprintf(labelName, "form.label_%d", i);
       XtVaSetValues(XtNameToWidget(rowCol, labelName),
               XmNwidth, labelMaxWidth, NULL);
   }
}
#endif


static void MiscConfigToggleCB(Widget w, XtPointer clientData,
       XmToggleButtonCallbackStruct *cbs)
{
   if (! (strcmp(XtName(w), "autoReconnectToggle")))
       autoReconnect = (cbs->set == XmSET) ? 1 : 0;
   else
       showPath = (cbs->set == XmSET) ? 1 : 0;
   configChanged = 1;
}


static void DirBtnCB(Widget w, XtPointer clientData,
       XmPushButtonCallbackStruct *cbs)
{
   Widget entry = (Widget)clientData;
   String tmp, dir;

   tmp = XmTextFieldGetString(entry);
   dir = ChooseDir(tmp);
   XtFree(tmp);
   if (! strlen(dir))
       return;
   XmTextFieldSetString(entry, dir);
}


static void MiscConfig(Widget parent)
{
   String values[5];
   char timeOutStr[5], reconnTimeOutStr[5];
   Widget pageForm, rowCol, form, label, entry = NULL;
   Widget toggle, dirBtn;
   Arg args[20];
   XmString toggleString;
   int i, n;
   Dimension labelWidth, labelMaxWidth = 0;
   char labelName[20];
   String toggleName[] = {"autoReconnectToggle", "showPathToggle"};
   String entryName[] = {"timeOut", "reconnTimeOut", "initDlDir"};

   pageForm = ConfigForm(parent, LAST_PAGE, "miscConfig");
   rowCol = XtNameToWidget(pageForm, "rowCol");

   sprintf(timeOutStr, "%d", timeOut);
   sprintf(reconnTimeOutStr, "%d", reconnTimeOut);
   values[2] = timeOutStr;
   values[3] = reconnTimeOutStr;
   values[4] = initDlDir;

   for (i = 0; i < 5; i++) {
       form = XtVaCreateWidget("form", xmFormWidgetClass,
               rowCol, NULL);

       sprintf(labelName, "label_%d", i);

       n = 0;
       XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
       XtSetArg(args[n], XmNmarginHeight, 6); n++;
       XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
       XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
       XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
       label = XmCreateLabel(form, labelName, args, n);
       XtManageChild(label);

       XtVaGetValues(label, XmNwidth, &labelWidth, NULL);
       if (labelWidth > labelMaxWidth)
           labelMaxWidth = labelWidth;

       if (i < 2) {
           toggleString = XmStringCreateLocalized(" ");
           toggle = XtVaCreateManagedWidget(toggleName[i],
                   xmToggleButtonWidgetClass, form,
                   XmNlabelString, toggleString,
                   XmNindicatorOn, XmINDICATOR_CROSS_BOX,
                   XmNtopAttachment, XmATTACH_FORM,
                   XmNbottomAttachment, XmATTACH_FORM,
                   XmNleftAttachment, XmATTACH_WIDGET,
                   XmNleftWidget, label,
                   XmNleftOffset, 20,
                   NULL);
           XmStringFree(toggleString);
           if (i == 0) {
               XtVaSetValues(toggle,
                       XmNset, autoReconnect ? XmSET : XmUNSET, NULL);
           } else {
               XtVaSetValues(toggle,
                       XmNset, showPath ? XmSET : XmUNSET, NULL);
           }
           XtAddCallback(toggle, XmNvalueChangedCallback,
                   (XtCallbackProc)MiscConfigToggleCB, NULL);
       } else {
           n = 0;
           XtSetArg(args[n], XmNcursorPositionVisible, False); n++;
           XtSetArg(args[n], XmNmarginWidth, 5); n++;
           XtSetArg(args[n], XmNmarginHeight, 2); n++;
           XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
           XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
           XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
           XtSetArg(args[n], XmNleftWidget, label); n++;
           XtSetArg(args[n], XmNleftOffset, 20); n++;

           if (i == 4) {
               dirBtn = XtVaCreateManagedWidget("dirBtn",
                       xmPushButtonWidgetClass, form,
                       XmNtopAttachment, XmATTACH_FORM,
                       XmNbottomAttachment, XmATTACH_FORM,
                       XmNrightAttachment, XmATTACH_FORM,
                       NULL);

               XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
               XtSetArg(args[n], XmNrightWidget, dirBtn); n++;
               XtSetArg(args[n], XmNrightOffset, 10); n++;
               XtSetArg(args[n], XmNcolumns, 20); n++;
               entry = XmCreateTextField(form, entryName[i - 2], args, n);

               XtAddCallback(dirBtn, XmNactivateCallback,
                       (XtCallbackProc)DirBtnCB, (XtPointer)entry);
           } else {
               XtSetArg(args[n], XmNcolumns, 5); n++;
               entry = XmCreateTextField(form, entryName[i - 2], args, n);
           }

           XtAddCallback(entry, XmNfocusCallback,
                   (XtCallbackProc)ConfigEntryFocusCB, NULL);
           XtAddCallback(entry, XmNlosingFocusCallback,
                   (XtCallbackProc)ConfigEntryLosingFocusCB, NULL);
           XtManageChild(entry);
           XmTextFieldSetString(entry, values[i]);
       }
       XtManageChild(form);
   }

   for (i = 0; i < 5; i++) {
       sprintf(labelName, "form.label_%d", i);
       XtVaSetValues(XtNameToWidget(rowCol, labelName),
               XmNwidth, labelMaxWidth, NULL);
   }
}


static void Backup(void)
{
   SERVER *srv, *tmpSrv, *prevSrv = NULL;
   SHAREDDIR *dir, *tmpDir, *prevDir = NULL;
   HOTLIST *hot, *tmpHot, *prevHot = NULL;
   AUTOCOMMAND *cmd, *tmpCmd, *prevCmd = NULL;

   tmpPassWord = XtNewString(userInfo.passWord);
   tmpLinkType = userInfo.linkType;
   tmpRegistered = userInfo.registered;

   tmpMinLink = XtNewString(defSearch.minLink);

   for (srv = servers; srv; srv = srv->next) {
       tmpSrv = XtNew(SERVER);
       tmpSrv->name = XtNewString(srv->name);
       tmpSrv->port = srv->port;
       tmpSrv->meta = srv->meta;
       tmpSrv->next = NULL;
       if (! prevSrv)
           tmpServers = tmpSrv;
       else
           prevSrv->next = tmpSrv;
       prevSrv = tmpSrv;
   }

   for (dir = sharedDirs; dir; dir = dir->next) {
       tmpDir = XtNew(SHAREDDIR);
       tmpDir->dirName = XtNewString(dir->dirName);
       tmpDir->next = NULL;
       if (! prevDir)
           tmpSharedDirs = tmpDir;
       else
           prevDir->next = tmpDir;
       prevDir = tmpDir;
   }

   for (hot = hotList; hot; hot = hot->next) {
       tmpHot = XtNew(HOTLIST);
       tmpHot->nick = XtNewString(hot->nick);
       tmpHot->next = NULL;
       if (! prevHot)
           tmpHotList = tmpHot;
       else
           prevHot->next = tmpHot;
       prevHot = tmpHot;
   }

   for (cmd = autoCommands; cmd; cmd = cmd->next) {
       tmpCmd = XtNew(AUTOCOMMAND);
       tmpCmd->data = XtNewString(cmd->data);
       tmpCmd->next = NULL;
       if (! prevCmd)
           tmpAutoCommands = tmpCmd;
       else
           prevCmd->next = tmpCmd;
       prevCmd = tmpCmd;
   }

   tmpAutoReconnect = autoReconnect;
   tmpShowPath = showPath;
   tmpTimeOut = timeOut;
   tmpReconnTimeOut = reconnTimeOut;
}


void ConfigWin(int pageNum)
{
   Widget shell, configNB;
   Arg args[20];
#ifdef USE_SOUND
   String pageName[] = {"userConfig", "searchConfig",
                        "serverConfig", "sharedConfig",
                        "hotlistConfig", "autoCmdConfig",
                        "soundConfig", "miscConfig", NULL};
#else
   String pageName[] = {"userConfig", "searchConfig",
                        "serverConfig", "sharedConfig",
                        "hotlistConfig", "autoCmdConfig",
                        "miscConfig", NULL};
#endif
   String tabName;
   int i, n;

   DestroyConfigWin();

   shell = XtVaCreatePopupShell("configWin_popup",
           topLevelShellWidgetClass, topLevel,
           XmNiconPixmap, napPix,
           NULL);

   n = 0;
   XtSetArg(args[n], XmNautoUnmanage, False); n++;
   XtSetArg(args[n], XmNdefaultPosition, False); n++;
   XtSetArg(args[n], XmNnoResize, True); n++;
   XtSetArg(args[n], XmNmessageAlignment, XmALIGNMENT_CENTER); n++;
   configWin = XmCreateMessageBox(shell, "configWin", args, n);
   XtUnmanageChild(XtNameToWidget(configWin, "Help"));

   n = 0;
   XtSetArg(args[n], XmNbindingType, XmNONE); n++;
   XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
   XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
   XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
   XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
   configNB = XmCreateNotebook(configWin, "configNB", args, n);
   XtManageChild(configNB);

   XtAddCallback(configWin, XmNokCallback,
           (XtCallbackProc)ConfigCB, NULL);
   XtAddCallback(configWin, XmNcancelCallback,
           (XtCallbackProc)ConfigCB, NULL);
   XmAddWMProtocolCallback(XtParent(configWin),
           XmInternAtom(XtDisplay(configWin), "WM_DELETE_WINDOW", False),
           (XtCallbackProc)ConfigCB, NULL);
   XtAddCallback(configWin, XmNfocusCallback,
           (XtCallbackProc)FocusCB, NULL);
   XmAddWMProtocolCallback(topLevel,
           XmInternAtom(XtDisplay(topLevel), "WM_DELETE_WINDOW", False),
           (XtCallbackProc)ConfigCB, NULL);

   for (i = 0; pageName[i]; i++) {
       tabName = XtMalloc(strlen(pageName[i]) + sizeof("Tab") + 1);
       sprintf(tabName, "%sTab", pageName[i]);
       (void)XtVaCreateManagedWidget(tabName,
               xmPushButtonWidgetClass, configNB,
               XmNnotebookChildType, XmMAJOR_TAB,
               XmNpageNumber, i + 1,
               NULL);
       XtFree(tabName);
   }

   Backup();

   UserConfig(configNB);
   SearchConfig(configNB);
   ServerConfig(configNB);
   SharedDirConfig(configNB);
   HotListConfig(configNB);
   AutoCmdConfig(configNB);
#ifdef USE_SOUND
   SoundConfig(configNB);
#endif
   MiscConfig(configNB);

   serverChanged = sharedDirChanged = hotListChanged =
       autoCmdChanged = configChanged = 0;

   XtVaSetValues(configNB, XmNinitialFocus,
           XtNameToWidget(configNB, pageName[pageNum - 1]), NULL);
   XtVaSetValues(configWin, XmNinitialFocus, configNB,
           NULL);

   XtManageChild(configWin);
   XtPopup(shell, XtGrabNone);

   XtVaSetValues(configNB, XmNcurrentPageNumber, pageNum, NULL);
}