#!/usr/bin/env python

##############################################################################
#                                IcePref 0.10
#
# This is (or will be) the best icewm configuration utility
# known to man.  It requires a recent version of python as well as Gtk ( > 
# 1.2.0) and PyGtk.  It should work passably well with versions of icewm after
# 0.9.36. This version is optomized for version 0.9.45.  You may place it
# anywhere in your path. You are free to copy,  change, and distribute this
# program under the terms of the GNU GPL.  You  can obtain the latest version
# of this script from via http at
# http://members.xoom.com/SaintChoj/icepref.html.  Please report all bugs  to
# Dave at SLQTN@cc.usu.edu.  Please include IcePref in the subject line.
##############################################################################

from gtk import *
from string import *

import sys
import user
import os
import glob

##############################################################################
# Constants in a Changing World
##############################################################################

# these define the types of configuration widgets

TOGGLE = 0	#done
RANGE = 1 	#done
FILE = 2		#done
PATH = 3		#done
COLOR = 4 	#done
FONT = 5		#done
ENTRY = 6 	#done
KEYSTROKE = 7	#done
MULTI = 8 	#done
THEME = 9 	#done
BITMASK = 10	#done
MOUSEBUTTON = 11

# these define the indexes for the data in DEFAULTS and self.settings

TYPE = 0
VALUE = 1
TITLE = 2
MIN = 3
MAX = 4
NUM = 3

# the standard spacing and border width, respectively

SP = 5
BD = 5

# finally, the file we're editing

CONFIG_FILE=user.home + '/.icewm/preferences'

# and the paths to all your themes

THEME_PATH = [	'/usr/local/lib/X11/icewm/themes/*',
			'/usr/X11R6/lib/X11/icewm/themes/*',
			user.home + '/.icewm/themes/*'	]
			
SAMPLE_TEXT = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'
ERROR_TEXT = 'Invalid Font'
 
##############################################################################
# Cofiguration Options Data
#
# In order to add a new item, it must be added to each of the following three
# data structures.  DEFAULTS is a dictionary containing information about the
# widget to be used, the parameter's default value (as a string), the title/label
# and the min and max for range type controls.  The keys are, of course, the
# name of the variables in the preferences file.
##############################################################################

DEFAULTS = {	'ClickToFocus': [TOGGLE, '1', 'Focus windows by clicking'],
			'RaiseOnFocus': [TOGGLE, '1', 'Raise windows when focused'],
			'FocusOnClickClient': [TOGGLE, '1', 'Focus window when client area clicked'],
			'RaiseOnClickClient': [TOGGLE, '1', 'Raise window when client area clicked'],
			'RaiseOnClickTitleBar': [TOGGLE, '1', 'Raise window when title bar is clicked'],
			'RaiseOnClickButton': [TOGGLE, '1', 'Raise when frame button is clicked'],
			'RaiseOnClickFrame': [TOGGLE, '1', 'Raise when frame border is clicked'],
			'PassFirstClickToClient': [TOGGLE, '1', 'Pass focusing click on client area to client'],
			'FocusOnMap': [TOGGLE, '1', 'Focus normal window when initially mapped'],
			'FocusOnMapTransient': [TOGGLE, '1', 'Focus dialog window when initally mapped'],
			'FocusOnMapTransientActive': [TOGGLE, '1', 'Focus dialog window when initially mapped only if parent frame focused'],
			'PointerColormap': [TOGGLE, '1', 'Colormap follows pointer'],
			'LimitSize': [TOGGLE, '1', 'Limit initial size of windows to screen'],
			'LimitPosition': [TOGGLE, '1', 'Limit initial position of windows to screen'],
			'SizeMaximized': [TOGGLE, '0', 'Maximized windows can be resized'],
			'ShowMoveSizeStatus': [TOGGLE, '1', 'Show position status window during move/resize'],
			'MinimizeToDesktop': [TOGGLE, '0', 'Display mini-icons on desktop for minimized windows'],
			'StrongPointerFocus': [TOGGLE, '0', 'Always maintain focus under mouse window (makes some keyboard support non-functional)'],
			'OpaqueMove': [TOGGLE, '1', 'Opaque window move'],
			'OpaqueResize': [TOGGLE, '1', 'Opaque window resize'],
			'ManualPlacement': [TOGGLE, '0', 'Windows initially placed manually by user'],
			'MenuMouseTracking': [TOGGLE, '0', 'Menus track mouse even with no mouse buttons held'],
			'AutoRaise': [TOGGLE, '0', 'Auto raise windows after delay'],
			'DelayPointerFocus': [TOGGLE, '0', 'Delay pointer focusing when mouse moves'],
			'Win95Keys': [TOGGLE, '0', 'Support win95 keyboard keys'],
			'ModMetaIsCtrlAlt': [TOGGLE, '1', 'Treat Penguin/Meta/Win modifer as Ctrl+Alt'],
			'UseMouseWheel': [TOGGLE, '0', 'Support mouse wheel'],
			'ShowPopupsAbovePointer': [TOGGLE, '1', 'Show popup menus above mouse pointer'],
			'QuickSwitch': [TOGGLE, '1', 'Alt+Tab window switching'],
			'QuickSwitchToMinimized': [TOGGLE, '1', 'Alt+Tab to minimized windows'],
			'QuickSwitchToHidden': [TOGGLE, '0', 'Alt-Tab to hidden windows'],
			'QuickSwitchToAllWorkspaces': [TOGGLE, '0', 'Alt+Tab to windows on other workspaces'],
			'GrabRootWindow': [TOGGLE, '1', 'Manage root window'],
			'SnapMove': [TOGGLE, '1', 'Snap to nearest screen edge/window when moving windows'],
			'EdgeSwitch': [TOGGLE, '0', 'Workspace switches by moving mouse to left/right screen edge'],
			'DesktopBackgroundCenter': [TOGGLE, '0', 'Display desktop background centered and not tiled'],
			'AutoReloadMenus':[TOGGLE, '1', 'Reload menu files automatically'],
			'ShowMenuButtonIcon':[TOGGLE, '1', 'Show application icon over menu button'],
			'AutoDetectGNOME': [TOGGLE, '1', 'Automatically disable some functions when running under GNOME'],
			'ShowTaskBar': [TOGGLE, '1', 'Show task bar'],
			'TaskBarAtTop': [TOGGLE, '0', 'Task bar at top of the screen'],
			'TaskBarAutoHide': [TOGGLE, '0', 'Auto hide task bar after delay'],
			'TaskBarShowClock': [TOGGLE, '1', 'Show clock on task bar'],
			'TaskBarClockLeds': [TOGGLE, '1', 'Task bar clock uses nice pixmapped LCD display'],
			'TaskBarShowMailboxStatus': [TOGGLE, '1', 'Show mailbox status on the task bar'],
			'TaskBarMailboxStatusBeepOnNewMail': [TOGGLE, '0', 'Beep when new mail arrives'],
			'TaskBarMailboxStatusCountMessages': [TOGGLE, '0', 'Count messages in mailbox'],
			'TaskBarShowWorkspaces': [TOGGLE, '1', 'Show workspace switching buttons on task bar'],
			'TaskBarShowWindows':[TOGGLE, '1', 'Show windows on the taskbar'],
			'TaskBarShowAllWindows': [TOGGLE, '0', 'Show windows from all workspaces on task bar'],
			'TaskBarShowStartMenu': [TOGGLE, '1', 'Show "Start" menu on task bar'],
			'TaskBarShowWindowListMenu': [TOGGLE, '1', 'Show "window list" menu on task bar'],
			'TaskBarShowCPUStatus': [TOGGLE, '1', 'Show CPU status on task bar'],
			'TaskBarShowNetStatus': [TOGGLE, '1', 'Show ppp status on task bar'],
			'TaskBarDoubleHeight': [TOGGLE, '0', 'Use double height task bar'],
			'WarpPointer': [TOGGLE, '0', 'Move mouse when doing focusing in pointer focus mode'],
			'BorderSizeX': [RANGE, '6', 'Horizontal window border', 0, 128],
			'BorderSizeY': [RANGE, '6', 'Veritical window border', 0, 128],
			'DlgBorderSizeX': [RANGE, '2', 'Horizontal dialog window border', 0, 128],
			'DlgBorderSizeY': [RANGE, '2', 'Vertical dialog window border', 0, 128],
			'TitleBarHeight': [RANGE, '20', 'Title bar height', 0, 128],
			'CornerSizeX': [RANGE, '24', 'Resize corner width', 0, 64],
			'CornerSizeY': [RANGE, '24', 'Resize corner height', 0, 64],
			'ClickMotionDistance': [RANGE, '4', 'Pointer motion distance before click gets interpreted as drag', 0, 32],
			'ClickMotionDelay': [RANGE, '200', 'Delay before click gets interpreted as drag', 0, 2000],
			'MultiClickTime': [RANGE, '400', 'Multiple click time', 0, 5000],
			'ToolTipDelay': [RANGE, '1000', 'Delay before tooltip window is displayed', 0, 5000],
			'AutoHideDelay': [RANGE, '300', 'Delay before task bar is automatically hidden', 0, 5000],
			'AutoRaiseDelay': [RANGE, '400', 'Delay before windows are auto raised', 0, 5000],
			'EdgeResistance': [RANGE, '32', 'Resistance in pixels when trying to move windows off the screen', 0, 10000],
			'PointerFocusDelay': [RANGE, '200', 'Delay for pointer focus switching', 0, 1000],
			'SnapDistance': [RANGE, '8', 'Distance in pixels before windows snap together', 0, 128],
			'EdgeSwitchDelay': [RANGE, '600', 'Screen edge workspace switching delay', 0, 5000],
			'ScrollBarStartDelay': [RANGE, '500', 'Initial scrollbar autoscroll delay', 0, 5000],
			'ScrollBarDelay': [RANGE, '300', 'Scroll bar delay', 0, 5000],
			'AutoScrollStartDelay': [RANGE, '1000', 'Autoscroll start delay', 0, 5000],
			'AutoScrollDelay': [RANGE, '60', 'Auto scroll delay', 0, 5000],
			'UseRootButtons': [BITMASK, '0', 'Bitmask of root window button click to use in window manager', 0, 255],
			'ButtonRaiseMask': [BITMASK, '1', 'Bitmask of buttons that raise the window when pressed', 0, 255],
			'DesktopWinMenuButton': [MOUSEBUTTON, '1', 'Mouse-button clicked on desktop to show window list menu', 0, 5],
			'DesktopWinListButton': [MOUSEBUTTON, '2', 'Mouse-button clicked on desktop to show window list', 0, 5],
			'DesktopMenuButton': [MOUSEBUTTON, '3', 'Mouse-button clicked on desktop to show menu', 0, 5],
			'TitleBarMaximizeButton': [MOUSEBUTTON, '1', 'Mouse-button clicked on titlebar to maximize window', 0, 8],
			'TitleBarRollupButton': [MOUSEBUTTON, '2', 'Mouse-button clicked on titlebar to roll up window', 0, 8],
			'MailCheckDelay': [RANGE, '30', 'Delay between new-mail checks in seconds', 0, 3600], 
			'TitleButtonsLeft': [ENTRY, '"s"', 'Titlebar buttons from left to right (x=close, m=max, i=min, h=hide, r=rollup, s=sysmenu)'],
			'TitleButtonsRight': [ENTRY, '"xmi"', 'Titlebar buttons from right to left (x=close, m=max, i=min, h=hide, r=rollup, s=sysmenu)'],
			'IconPath': [PATH, '""', 'Icon search path (colon separated)'],
			'MailBoxPath': [FILE, '""', 'Mailbox path ($MAIL is used if no value is specified)'],
			'MailCommand': [FILE, '""', 'Command to run on mailbox'],
			'NewMailCommand': [FILE, '""', 'Command to run when new mail arrives'],
			'LockCommand': [FILE, '"xlock"', 'Command to lock display or show screensaver'],
			'ClockCommand': [FILE, '"xclock"', 'Command to run on clock'],
			'RunCommand': [FILE, '""', 'Command to select and run a program'],
			'OpenCommand': [FILE, '""', 'Open command'],
			'TerminalCommand': [FILE, '"xterm"', 'Terminal emulator (must accept -e option)'],
			'LogoutCommand': [FILE, '""', 'Command to start logout'],
			'LogoutCancelCommand': [FILE, '""', 'Command to cancel logout'],
			'NetworkStatusDevice': [ENTRY, '"ppp0"', 'Network device for which to show status'],
			'TimeFormat': [ENTRY, '"%H:%M:%S"', 'Clock time format (strftime format string)'],
			'DateFormat': [ENTRY, '"%B %A %Y/%m/%d %H:%M:%S %Z"', 'Clock date format for tooltip (strftime format string)'],
			'Theme': [THEME, '"nice/default.theme"', 'Theme (theme_directory/default.theme)'],
			'ThemeAuthor': [ENTRY, '""', 'Theme author'],
			'ThemeDescription': [ENTRY, '""', 'Theme description'],
			'TitleFontName': [FONT, '"-b&h-lucida-bold-r-*-*-*-120-*-*-*-*-*-*"', 'Title bar font'],
			'MenuFontName': [FONT, '"-b&h-lucida-bold-r-*-*-*-120-*-*-*-*-*-*"', 'Menu font'],
			'StatusFontName': [FONT, '"-b&h-lucidatypewriter-bold-r-*-*-*-120-*-*-*-*-*-*"', 'Status font'],
			'QuickSwitchFontName': [FONT, '"-b&h-lucidatypewriter-bold-r-*-*-*-120-*-*-*-*-*-*"', 'Quick switch font'],
			'NormalButtonFontName': [FONT, '"-b&h-lucida-medium-r-*-*-*-120-*-*-*-*-*-*"', 'Normal button font'],
			'ActiveButtonFontName': [FONT, '"-b&h-lucida-bold-r-*-*-*-120-*-*-*-*-*-*"', 'Active button font'],
			'NormalTaskBarFontName': [FONT, '"-b&h-lucida-medium-r-*-*-*-120-*-*-*-*-*-*"', 'Normal taskbar font'],
			'ActiveTaskBarFontName': [FONT, '"-b&h-lucida-bold-r-*-*-*-120-*-*-*-*-*-*"', 'Active taskbar font'],
			'MinimizedWindowFontName': [FONT, '"-b&h-lucida-medium-r-*-*-*-120-*-*-*-*-*-*"', 'Minimized window font'],
			'ListBoxFontName': [FONT, '"-b&h-lucida-medium-r-*-*-*-120-*-*-*-*-*-*"', 'List box font'],
			'ToolTipFontName': [FONT, '"-b&h-lucida-medium-r-*-*-*-120-*-*-*-*-*-*"', 'Tooltip font'],
			'ClockFontName': [FONT, '"-b&h-lucidatypewriter-medium-r-*-*-*-140-*-*-*-*-*-*"', 'Clock font'],
			'LabelFontName': [FONT, '"-b&h-lucida-medium-r-*-*-*-140-*-*-*-*-*-*"', 'Label font'],
			'ColorDialog': [COLOR, '"rgb:C0/C0/C0"', 'Dialog color'],
			'ColorActiveBorder': [COLOR, '"rgb:C0/C0/C0"', 'Active border color'],
			'ColorNormalBorder': [COLOR,'"rgb:C0/C0/C0"', 'Normal border color'],
			'ColorNormalTitleButton': [COLOR, '"rgb:C0/C0/C0"', 'Normal title button color'],
			'ColorNormalTitleButtonText': [COLOR, '"rgb:00/00/00"', 'Normal title button text color'],
			'ColorNormalButton': [COLOR, '"rgb:C0/C0/C0"', 'Normal button color'],
			'ColorNormalButtonText': [COLOR, '"rgb:00/00/00"', 'Normal button text color'],
			'ColorActiveButton': [COLOR, '"rgb:E0/E0/E0"', 'Active button color'],
			'ColorActiveButtonText': [COLOR, '"rgb:00/00/00"', 'Active button text color'],
			'ColorActiveTitleBar': [COLOR, '"rgb:00/00/A0"', 'Active title bar color'],
			'ColorNormalTitleBar': [COLOR, '"rgb:80/80/80"', 'Normal title bar color'],
			'ColorActiveTitleBarText': [COLOR, '"rgb:FF/FF/FF"', 'Active title bar text color'],
			'ColorNormalTitleBarText': [COLOR, '"rgb:00/00/00"', 'Normal title bar text color'],
			'ColorNormalMinimizedWindow': [COLOR, '"rgb:C0/C0/C0"', 'Normal minimized window color'],
			'ColorNormalMinimizedWindowText': [COLOR, '"rgb:00/00/00"', 'Normal minimized winodw text color'],
			'ColorActiveMinimizedWindow': [COLOR, '"rgb:E0/E0/E0"', 'Active minimized window color'],
			'ColorActiveMinimizedWindowText': [COLOR, '"rgb:00/00/00"', 'Active minimized window text color'],
			'ColorNormalMenu': [COLOR, '"rgb:C0/C0/C0"', 'Normal menu color'],
			'ColorActiveMenuItem': [COLOR, '"rgb:A0/A0/A0"', 'Active menu item color'],
			'ColorActiveMenuItemText': [COLOR, '"rgb:00/00/00"', 'Active menu item text color'],
			'ColorNormalMenuItemText': [COLOR, '"rgb:00/00/00"', 'Normal menu item text color'],
			'ColorDisabledMenuItemText': [COLOR, '"rgb:80/80/80"', 'Disabled menu item text color'],
			'ColorMoveSizeStatus': [COLOR, '"rgb:C0/C0/C0"', 'Move/size status color'],
			'ColorMoveSizeStatusText': [COLOR, '"rgb:00/00/00"', 'Move/size status text color'],
			'ColorQuickSwitch': [COLOR, '"rgb:C0/C0/C0"', 'Quick switch color'],
			'ColorQuickSwitchText': [COLOR, '"rgb:00/00/00"', 'Quick switch text color'],
			'ColorDefaultTaskBar': [COLOR, '"rgb:C0/C0/C0"', 'Default taskbar color'],
			'ColorNormalTaskBarApp': [COLOR, '"rgb:C0/C0/C0"', 'Normal taskbar color'],
			'ColorNormalTaskBarAppText': [COLOR, '"rgb:00/00/00"', 'Normal taskbar app text color'],
			'ColorActiveTaskBarApp': [COLOR, '"rgb:E0/E0/E0"', 'Active taskbar app color'],
			'ColorActiveTaskBarAppText': [COLOR, '"rgb:00/00/00"', 'Active taskbar app text color'],
			'ColorMinimizedTaskBarApp': [COLOR, '"rgb:A0/A0/A0"', 'Minimized taskbar app color'],
			'ColorMinimizedTaskBarAppText': [COLOR, '"rgb:00/00/00"', 'Minimized taskbar app text'],
			'ColorInvisibleTaskBarApp': [COLOR, '"rgb:80/80/80"', 'Invisible taskbar app color'],
			'ColorInvisibleTaskBarAppText': [COLOR, '"rgb:00/00/00"', 'Invisible taskbar app text'],
			'ColorScrollBar': [COLOR, '"rgb:A0/A0/A0"', 'Scroll bar color'],
			'ColorScrollBarArrow': [COLOR, '"rgb:C0/C0/C0"', 'Scroll bar arrow color'],
			'ColorScrollBarSlider': [COLOR, '"rgb:C0/C0/C0"', 'Scroll bar slider color'],
			'ColorListBox': [COLOR, '"rgb:C0/C0/C0"', 'List box color'],
			'ColorListBoxText': [COLOR, '"rgb:00/00/00"', 'List box text color'],
			'ColorListBoxSelection': [COLOR, '"rgb:80/80/80"', 'List box selection color'],
			'ColorListBoxSelectionText': [COLOR, '"rgb:00/00/00"', 'List box selection text color'],
			'ColorToolTip': [COLOR, '"rgb:E0/E0/00"', 'Tooltip color'],
			'ColorToolTipText': [COLOR, '"rgb:00/00/00"', 'Tooltip text color'],
			'ColorClock': [COLOR, '"rgb:00/00/00"', 'Clock color'],
			'ColorClockText': [COLOR, '"rgb:00/FF/00"', 'Clock text color'],
			'ColorLabel': [COLOR, '"rgb:C0/C0/C0"', 'Label color'],
			'ColorLabelText': [COLOR, '"rgb:00/00/00"', 'Label text color'],
			'ColorInput': [COLOR, '"rgb:FF/FF/FF"', 'Input color'],
			'ColorInputText': [COLOR, '"rgb:00/00/00"', 'Input text color'],
			'ColorInputSelection': [COLOR, '"rgb:80/80/80"', 'Input selection color'],
			'ColorInputSelectionText': [COLOR, '"rgb:00/00/00"', 'Input selection text color'],
			'DesktopBackgroundColor': [COLOR, '"rgb:00/50/60"', 'Desktop background color'],
			'DesktopBackgroundImage': [FILE, '""', 'Desktop background image'],
			'ColorCPUStatusUser': [COLOR, '"rgb:00/FF/00"', 'User CPU usage color'],
			'ColorCPUStatusSystem': [COLOR, '"rgb:FF/00/00"', 'System CPU usage color'],
			'ColorCPUStatusNice': [COLOR, '"rgb:00/00/FF"', 'Nice CPU usage color'],
			'ColorCPUStatusIdle': [COLOR, '"rgb:00/00/00"', 'CPU Idle color'],			
			'KeyWinRaise': [KEYSTROKE, '"Alt+F1"', '"Raise window" shortcut'],
			'KeyWinOccupyAll': [KEYSTROKE, '"Alt+F2"', '"Occupy all" shortcut'],
			'KeyWinLower': [KEYSTROKE, '"Alt+F3"', '"Lower window" shortcut'],
			'KeyWinClose': [KEYSTROKE, '"Alt+F4"', '"Close window" shortcut'],
			'KeyWinRestore': [KEYSTROKE, '"Alt+F5"', '"Restory window" shortcut'],
			'KeyWinPrev': [KEYSTROKE, '"Alt+Shift+F6"', '"Previous window" shortcut'],
			'KeyWinNext': [KEYSTROKE, '"Alt+F6"', '"Next window" shortcut'],
			'KeyWinMove': [KEYSTROKE, '"Alt+F7"', '"Move window" shortcut'],
			'KeyWinSize': [KEYSTROKE, '"Alt+F8"', '"Size window" shortcut'],
			'KeyWinMinimize': [KEYSTROKE, '"Alt+F9"', '"Minimize window" shortcut'],
			'KeyWinMaximize': [KEYSTROKE, '"Alt+F10"', '"Maximize window" shortcut'],
			'KeyWinMaximizeVert': [KEYSTROKE, '"Alt+Shift+F10"', '"Maximize window vertically" shortcut'],
			'KeyWinHide': [KEYSTROKE, '"Alt+F11"', '"Hide window" shortcut'],
			'KeyWinRollup': [KEYSTROKE, '"Alt+F12"', '"Rollup window" shortcut'],
			'KeyWinMenu': [KEYSTROKE, '"Alt+Space"', '"Window menu" shortcut'],
			'KeySysSwitchNext': [KEYSTROKE, '"Alt+Tab"', '"Next item" shortcut'],
			'KeySysSwitchLast': [KEYSTROKE, '"Alt+Shift+Tab"', '"Last item" shortcut'],
			'KeySysWinNext': [KEYSTROKE, '"Alt+Esc"', '"Next sys window" shortcut'],
			'KeySysWinPrev': [KEYSTROKE, '"Alt+Shift+Esc"', '"Previous sys window" shortcut'],
			'KeySysWinMenu': [KEYSTROKE, '"Shift+Esc"', '"Window menu" shortcut'],
			'KeySysDialog': [KEYSTROKE, '"Alt+Ctrl+Del"', '"Logout / screenlock dialog" shortcut'],
			'KeySysMenu': [KEYSTROKE, '"Ctrl+Esc"', '"Program menu" shortcut'],
			'KeySysRun': [KEYSTROKE, '"Alt+Ctrl+R"', '"Run" shortcut'],
			'KeySysWindowList': [KEYSTROKE, '"Alt+Ctrl+Esc"', '"Window list" shortcut'],
			'KeySysAddressBar': [KEYSTROKE, '"Alt+Ctrl+Space"', '"Address bar" shortcut'],
			'KeySysWorkspacePrev': [KEYSTROKE, '"Alt+Ctrl+Left"', '"Previous workspace" shortcut'],
			'KeySysWorkspaceNext': [KEYSTROKE, '"Alt+Ctrl+Right"', '"Next workspace" shortcut'],
			'KeySysWorkspacePrevTakeWin': [KEYSTROKE, '"Alt+Ctrl+Shift+Left"', '"Take window to previous workspace" shortcut'],
			'KeySysWorkspaceNextTakeWin': [KEYSTROKE, '"Alt+Ctrl+Shift+Right"', '"Take window to next workspace" shortcut'],
			'KeySysWorkspace1': [KEYSTROKE, '"Alt+Ctrl+1"', '"Workspace 1" shortcut'],
			'KeySysWorkspace2': [KEYSTROKE, '"Alt+Ctrl+2"', '"Workspace 2" shortcut'],
			'KeySysWorkspace3': [KEYSTROKE, '"Alt+Ctrl+3"', '"Workspace 3" shortcut'],
			'KeySysWorkspace4': [KEYSTROKE, '"Alt+Ctrl+4"', '"Workspace 4" shortcut'],
			'KeySysWorkspace5': [KEYSTROKE, '"Alt+Ctrl+5"', '"Workspace 5" shortcut'],
			'KeySysWorkspace6': [KEYSTROKE, '"Alt+Ctrl+6"', '"Workspace 6" shortcut'],
			'KeySysWorkspace7': [KEYSTROKE, '"Alt+Ctrl+7"', '"Workspace 7" shortcut'],
			'KeySysWorkspace8': [KEYSTROKE, '"Alt+Ctrl+8"', '"Workspace 8" shortcut'],
			'KeySysWorkspace9': [KEYSTROKE, '"Alt+Ctrl+9"', '"Workspace 9" shortcut'],
			'KeySysWorkspace10': [KEYSTROKE, '"Alt+Ctrl+0"', '"Workspace 10" shortcut'],
			'KeySysWorkspace1TakeWin': [KEYSTROKE, '"Alt+Ctrl+Shift+1"', '"Take window to workspace 1" shortcut'],
			'KeySysWorkspace2TakeWin': [KEYSTROKE, '"Alt+Ctrl+Shift+2"', '"Take window to workspace 2" shortcut'],
			'KeySysWorkspace3TakeWin': [KEYSTROKE, '"Alt+Ctrl+Shift+3"', '"Take window to workspace 3" shortcut'],
			'KeySysWorkspace4TakeWin': [KEYSTROKE, '"Alt+Ctrl+Shift+4"', '"Take window to workspace 4" shortcut'],
			'KeySysWorkspace5TakeWin': [KEYSTROKE, '"Alt+Ctrl+Shift+5"', '"Take window to workspace 5" shortcut'],
			'KeySysWorkspace6TakeWin': [KEYSTROKE, '"Alt+Ctrl+Shift+6"', '"Take window to workspace 6" shortcut'],
			'KeySysWorkspace7TakeWin': [KEYSTROKE, '"Alt+Ctrl+Shift+7"', '"Take window to workspace 7" shortcut'],
			'KeySysWorkspace8TakeWin': [KEYSTROKE, '"Alt+Ctrl+Shift+8"', '"Take window to workspace 8" shortcut'],
			'KeySysWorkspace9TakeWin': [KEYSTROKE, '"Alt+Ctrl+Shift+9"', '"Take window to workspace 9" shortcut'],
			'KeySysWorkspace10TakeWin': [KEYSTROKE, '"Alt+Ctrl+Shift+0"', '"Take window to workspace 10" shortcut'],
			'WorkspaceNames': [MULTI, '" 1 ", " 2 ", " 3 ", " 4 "', 'Names of the Workspaces', 10]
		}
		
# This list defines the order in which options will be output to the
# '.icewm/preferences' file.

ORDER = [ 	'ClickToFocus',
			'RaiseOnFocus',
			'FocusOnClickClient',
			'RaiseOnClickClient',
			'RaiseOnClickTitleBar',
			'RaiseOnClickButton',
			'RaiseOnClickFrame',
			'PassFirstClickToClient',
			'FocusOnMap',
			'FocusOnMapTransient',
			'FocusOnMapTransientActive',
			'PointerColormap',
			'LimitSize',
			'LimitPosition',
			'SizeMaximized',
			'ShowMoveSizeStatus',
			'MinimizeToDesktop',
			'StrongPointerFocus',
			'OpaqueMove',
			'OpaqueResize',
			'ManualPlacement',
			'MenuMouseTracking',
			'AutoRaise',
			'DelayPointerFocus',
			'Win95Keys',
			'ModMetaIsCtrlAlt',
			'UseMouseWheel',
			'ShowPopupsAbovePointer',
			'QuickSwitch',
			'QuickSwitchToMinimized',
			'QuickSwitchToHidden',
			'QuickSwitchToAllWorkspaces',
			'GrabRootWindow',
			'SnapMove',
			'EdgeSwitch',
			'DesktopBackgroundCenter',
			'AutoReloadMenus',
			'ShowMenuButtonIcon',
			'AutoDetectGNOME',
			'ShowTaskBar',
			'TaskBarAtTop',
			'TaskBarAutoHide',
			'TaskBarShowClock',
			'TaskBarClockLeds',
			'TaskBarShowMailboxStatus',
			'TaskBarMailboxStatusBeepOnNewMail',
			'TaskBarMailboxStatusCountMessages',
			'TaskBarShowWorkspaces',
			'TaskBarShowWindows',
			'TaskBarShowAllWindows',
			'TaskBarShowStartMenu',
			'TaskBarShowWindowListMenu',
			'TaskBarShowCPUStatus',
			'TaskBarShowNetStatus',
			'TaskBarDoubleHeight',
			'WarpPointer',
			'BorderSizeX',
			'BorderSizeY',
			'DlgBorderSizeX',
			'DlgBorderSizeY',
			'TitleBarHeight',
			'CornerSizeX',
			'CornerSizeY',
			'ClickMotionDistance',
			'ClickMotionDelay',
			'MultiClickTime',
			'ToolTipDelay',
			'AutoHideDelay',
			'AutoRaiseDelay',
			'EdgeResistance',
			'PointerFocusDelay',
			'SnapDistance',
			'EdgeSwitchDelay',
			'ScrollBarStartDelay',
			'ScrollBarDelay',
			'AutoScrollStartDelay',
			'AutoScrollDelay',
			'UseRootButtons',
			'ButtonRaiseMask',
			'DesktopWinMenuButton',
			'DesktopWinListButton',
			'DesktopMenuButton',
			'TitleBarMaximizeButton',
			'TitleBarRollupButton',
			'MailCheckDelay',
			'TitleButtonsLeft',
			'TitleButtonsRight',
			'IconPath',
			'MailBoxPath',
			'MailCommand',
			'NewMailCommand',
			'LockCommand',
			'ClockCommand',
			'RunCommand',
			'OpenCommand',
			'TerminalCommand',
			'LogoutCommand',
			'LogoutCancelCommand',
			'NetworkStatusDevice',
			'TimeFormat',
			'DateFormat',
			'Theme',
			'ThemeAuthor',
			'ThemeDescription',
			'TitleFontName',
			'MenuFontName',
			'StatusFontName',
			'QuickSwitchFontName',
			'NormalButtonFontName',
			'ActiveButtonFontName',
			'NormalTaskBarFontName',
			'ActiveTaskBarFontName',
			'MinimizedWindowFontName',
			'ListBoxFontName',
			'ToolTipFontName',
			'ClockFontName',
			'LabelFontName',
			'ColorDialog',
			'ColorActiveBorder',
			'ColorNormalBorder',
			'ColorNormalTitleButton',
			'ColorNormalTitleButtonText',
			'ColorNormalButton',
			'ColorNormalButtonText',
			'ColorActiveButton',
			'ColorActiveButtonText',
			'ColorActiveTitleBar',
			'ColorNormalTitleBar',
			'ColorActiveTitleBarText',
			'ColorNormalTitleBarText',
			'ColorNormalMinimizedWindow',
			'ColorNormalMinimizedWindowText',
			'ColorActiveMinimizedWindow',
			'ColorActiveMinimizedWindowText',
			'ColorNormalMenu',
			'ColorActiveMenuItem',
			'ColorActiveMenuItemText',
			'ColorNormalMenuItemText',
			'ColorDisabledMenuItemText',
			'ColorMoveSizeStatus',
			'ColorMoveSizeStatusText',
			'ColorQuickSwitch',
			'ColorQuickSwitchText',
			'ColorDefaultTaskBar',
			'ColorNormalTaskBarApp',
			'ColorNormalTaskBarAppText',
			'ColorActiveTaskBarApp',
			'ColorActiveTaskBarAppText',
			'ColorMinimizedTaskBarApp',
			'ColorMinimizedTaskBarAppText',
			'ColorInvisibleTaskBarApp',
			'ColorInvisibleTaskBarAppText',
			'ColorScrollBar',
			'ColorScrollBarArrow',
			'ColorScrollBarSlider',
			'ColorListBox',
			'ColorListBoxText',
			'ColorListBoxSelection',
			'ColorListBoxSelectionText',
			'ColorToolTip',
			'ColorToolTipText',
			'ColorClock',
			'ColorClockText',
			'ColorLabel',
			'ColorLabelText',
			'ColorInput',
			'ColorInputText',
			'ColorInputSelection',
			'ColorInputSelectionText',
			'DesktopBackgroundColor',
			'DesktopBackgroundImage',
			'ColorCPUStatusUser',
			'ColorCPUStatusSystem',
			'ColorCPUStatusNice',
			'ColorCPUStatusIdle',
			'KeyWinRaise',
			'KeyWinOccupyAll',
			'KeyWinLower',
			'KeyWinClose',
			'KeyWinRestore',
			'KeyWinPrev',
			'KeyWinNext',
			'KeyWinMove',
			'KeyWinSize',
			'KeyWinMinimize',
			'KeyWinMaximize',
			'KeyWinMaximizeVert',
			'KeyWinHide',
			'KeyWinRollup',
			'KeyWinMenu',
			'KeySysSwitchNext',
			'KeySysSwitchLast',
			'KeySysWinNext',
			'KeySysWinPrev',
			'KeySysWinMenu',
			'KeySysDialog',
			'KeySysMenu',
			'KeySysRun',
			'KeySysWindowList',
			'KeySysAddressBar',
			'KeySysWorkspacePrev',
			'KeySysWorkspaceNext',
			'KeySysWorkspacePrevTakeWin',
			'KeySysWorkspaceNextTakeWin',
			'KeySysWorkspace1',
			'KeySysWorkspace2',
			'KeySysWorkspace3',
			'KeySysWorkspace4',
			'KeySysWorkspace5',
			'KeySysWorkspace6',
			'KeySysWorkspace7',
			'KeySysWorkspace8',
			'KeySysWorkspace9',
			'KeySysWorkspace10',
			'KeySysWorkspace1TakeWin',
			'KeySysWorkspace2TakeWin',
			'KeySysWorkspace3TakeWin',
			'KeySysWorkspace4TakeWin',
			'KeySysWorkspace5TakeWin',
			'KeySysWorkspace6TakeWin',
			'KeySysWorkspace7TakeWin',
			'KeySysWorkspace8TakeWin',
			'KeySysWorkspace9TakeWin',
			'KeySysWorkspace10TakeWin',
			'WorkspaceNames'
	]

# This list controls how the various configuration options are distributed
# on the tabs of the note book.  It is a list of two item sublists.  Each of
# these sublists contains the label of a tab and a list of the configuration
# options associated with that tab.  
TABS = [
		['Focus & Behavior',
			[
			'ClickToFocus',
			'RaiseOnFocus',
			'FocusOnClickClient',
			'RaiseOnClickClient',
			'RaiseOnClickTitleBar',
			'RaiseOnClickButton',
			'RaiseOnClickFrame',
			'PassFirstClickToClient',
			'FocusOnMap',
			'FocusOnMapTransient',
			'FocusOnMapTransientActive',
			'PointerColormap',
			'LimitSize',
			'LimitPosition',
			'SizeMaximized',
			'ShowMoveSizeStatus',
			'MinimizeToDesktop',
			'StrongPointerFocus',
			'OpaqueMove',
			'OpaqueResize',
			'ManualPlacement',
			'MenuMouseTracking',
			'AutoRaise',
			'DelayPointerFocus',
			'ShowPopupsAbovePointer',
			'QuickSwitch',
			'QuickSwitchToMinimized',
			'QuickSwitchToHidden',
			'QuickSwitchToAllWorkspaces',
			'GrabRootWindow',
			'SnapMove',
			'EdgeSwitch',
			'UseMouseWheel',
			'WarpPointer'
			]],
		['Menus',
			[
			'AutoReloadMenus',
			'ShowMenuButtonIcon'
			]],
		['Taskbar',
			[
			'ShowTaskBar',
			'TaskBarAtTop',
			'TaskBarAutoHide',
			'TaskBarShowClock',
			'TaskBarClockLeds',
			'TaskBarShowMailboxStatus',
			'TaskBarMailboxStatusBeepOnNewMail',
			'TaskBarMailboxStatusCountMessages',
			'TaskBarShowWorkspaces',
			'TaskBarShowWindows',
			'TaskBarShowAllWindows',
			'TaskBarShowStartMenu',
			'TaskBarShowWindowListMenu',
			'TaskBarShowCPUStatus',
			'TaskBarShowNetStatus',
			'NetworkStatusDevice',
			'TaskBarDoubleHeight',
			'AutoDetectGNOME',
			]],
		['Borders',
			[
			'BorderSizeX',
			'BorderSizeY',
			'DlgBorderSizeX',
			'DlgBorderSizeY',
			'TitleBarHeight',
			'CornerSizeX',
			'CornerSizeY'
			]],
		['Timings',
			[
			
			'ClickMotionDelay',
			'MultiClickTime',
			'ToolTipDelay',
			'AutoHideDelay',
			'AutoRaiseDelay',			
			'PointerFocusDelay',
			'EdgeSwitchDelay',
			'ScrollBarDelay',
			'ScrollBarStartDelay',
			'AutoScrollDelay',
			'AutoScrollStartDelay',
			'MailCheckDelay'
			]],
		['Thresholds',
			[
			'ClickMotionDistance',
			'EdgeResistance',
			'SnapDistance'
			]],
		['Mouse Buttons',
			[
			'UseRootButtons',
			'ButtonRaiseMask',
			'DesktopWinMenuButton',
			'DesktopWinListButton',
			'DesktopMenuButton',
			'TitleBarMaximizeButton',
			'TitleBarRollupButton'
			]],
		['Title Buttons',
			[
			'TitleButtonsLeft',
			'TitleButtonsRight'
			]],
		['Paths',
			[
			'IconPath',
			'MailBoxPath'
			]],
		['Commands',
			[
			'MailCommand',
			'NewMailCommand',
			'LockCommand',
			'ClockCommand',
			'RunCommand',
			'OpenCommand',
			'TerminalCommand',
			'LogoutCommand',
			'LogoutCancelCommand'
			]],
		['Formats',
			[
			'TimeFormat',
			'DateFormat'
			]],
		['Theme',
			[
			'Theme',
			'ThemeAuthor',
			'ThemeDescription'
			]],
		['Fonts',
			[
			'TitleFontName',
			'MenuFontName',
			'StatusFontName',
			'QuickSwitchFontName',
			'NormalButtonFontName',
			'ActiveButtonFontName',
			'NormalTaskBarFontName',
			'ActiveTaskBarFontName',
			'MinimizedWindowFontName',
			'ListBoxFontName',
			'ToolTipFontName',
			'ClockFontName',
			'LabelFontName'
			]],
		['Colors',
			[
			'ColorDialog',
			'ColorActiveBorder',
			'ColorNormalBorder',
			'ColorNormalTitleButton',
			'ColorNormalTitleButtonText',
			'ColorNormalButton',
			'ColorNormalButtonText',
			'ColorActiveButton',
			'ColorActiveButtonText',
			'ColorActiveTitleBar',
			'ColorNormalTitleBar',
			'ColorActiveTitleBarText',
			'ColorNormalTitleBarText',
			'ColorNormalMinimizedWindow',
			'ColorNormalMinimizedWindowText',
			'ColorActiveMinimizedWindow',
			'ColorActiveMinimizedWindowText',
			'ColorNormalMenu',
			'ColorActiveMenuItem',
			'ColorActiveMenuItemText',
			'ColorNormalMenuItemText',
			'ColorDisabledMenuItemText',
			'ColorMoveSizeStatus',
			'ColorMoveSizeStatusText',
			'ColorQuickSwitch',
			'ColorQuickSwitchText',
			'ColorDefaultTaskBar',
			'ColorNormalTaskBarApp',
			'ColorNormalTaskBarAppText',
			'ColorActiveTaskBarApp',
			'ColorActiveTaskBarAppText',
			'ColorMinimizedTaskBarApp',
			'ColorMinimizedTaskBarAppText',
			'ColorInvisibleTaskBarApp',
			'ColorInvisibleTaskBarAppText',
			'ColorScrollBar',
			'ColorScrollBarArrow',
			'ColorScrollBarSlider',
			'ColorListBox',
			'ColorListBoxText',
			'ColorListBoxSelection',
			'ColorListBoxSelectionText',
			'ColorToolTip',
			'ColorToolTipText',
			'ColorClock',
			'ColorClockText',
			'ColorLabel',
			'ColorLabelText',
			'ColorInput',
			'ColorInputText',
			'ColorInputSelection',
			'ColorInputSelectionText',
			'ColorCPUStatusUser',
			'ColorCPUStatusSystem',
			'ColorCPUStatusNice',
			'ColorCPUStatusIdle'
			]],
		['Background',
			[
			'DesktopBackgroundCenter',
			'DesktopBackgroundColor',
			'DesktopBackgroundImage'
			]],
		['Keybindings',
			[
			'Win95Keys',
			'ModMetaIsCtrlAlt',
			'KeyWinRaise',
			'KeyWinOccupyAll',
			'KeyWinLower',
			'KeyWinClose',
			'KeyWinRestore',
			'KeyWinPrev',
			'KeyWinNext',
			'KeyWinMove',
			'KeyWinSize',
			'KeyWinMinimize',
			'KeyWinMaximize',
			'KeyWinMaximizeVert',
			'KeyWinHide',
			'KeyWinRollup',
			'KeyWinMenu',
			'KeySysSwitchNext',
			'KeySysSwitchLast',
			'KeySysWinNext',
			'KeySysWinPrev',
			'KeySysWinMenu',
			'KeySysDialog',
			'KeySysMenu',
			'KeySysRun',
			'KeySysWindowList',
			'KeySysAddressBar',
			'KeySysWorkspacePrev',
			'KeySysWorkspaceNext',
			'KeySysWorkspacePrevTakeWin',
			'KeySysWorkspaceNextTakeWin',
			'KeySysWorkspace1',
			'KeySysWorkspace2',
			'KeySysWorkspace3',
			'KeySysWorkspace4',
			'KeySysWorkspace5',
			'KeySysWorkspace6',
			'KeySysWorkspace7',
			'KeySysWorkspace8',
			'KeySysWorkspace9',
			'KeySysWorkspace10',
			'KeySysWorkspace1TakeWin',
			'KeySysWorkspace2TakeWin',
			'KeySysWorkspace3TakeWin',
			'KeySysWorkspace4TakeWin',
			'KeySysWorkspace5TakeWin',
			'KeySysWorkspace6TakeWin',
			'KeySysWorkspace7TakeWin',
			'KeySysWorkspace8TakeWin',
			'KeySysWorkspace9TakeWin',
			'KeySysWorkspace10TakeWin'
			]],
		['Workspaces',
			[
			'WorkspaceNames'
			]]
	]
	
############################################################################
# Classes used in the general IcePref user interface
#
# These are placed at the beginning so that they may be inherited from by
# all other classes
############################################################################
		
# This class defines the standard style of labels used in many other classes
# of IcePref

class Label(GtkLabel):
	def __init__(self, title):
		GtkLabel.__init__(self, title)
		self.set_line_wrap(TRUE)
		self.set_alignment(JUSTIFY_LEFT, 0)
		self.set_padding(2,2)
		
# This is a somewhat generic class for dialogs
 
class Dialog(GtkWindow):
	def __init__(self, titlebar, title):
		GtkWindow.__init__(self, title=titlebar)
		self.set_modal(TRUE)
		self.init_widgets(title)
		self.init_buttons()
		self.show()
	
	def init_widgets(self, title):
		vbox = GtkVBox(spacing = SP)
		vbox.set_border_width(BD)
		self.add(vbox)
		vbox.show()

		label = Label(title)
		vbox.pack_start(label)
		label.show()

		self.bbox = GtkHButtonBox()
		vbox.pack_start(self.bbox)
		self.bbox.show()

	def init_buttons(self):
		self.ok_button = GtkButton('Ok')
		self.ok_button.grab_focus()
		self.ok_button.connect('clicked', self.button_cb, 1)
		self.bbox.pack_start(self.ok_button)
		self.ok_button.show()
		
	def button_cb(self, object=None, data=None):
		ret = data
		self.hide()
		self.destroy()

# This derivative of Dialog is used to confirm or cancel choices
		
class ConfirmDialog(Dialog):
	def init_buttons(self):
		self.ok_button = GtkButton('Ok')
		self.ok_button.connect('clicked', button_cb, 1)
		self.bbox.pack_start(self.ok_button)
		self.ok_button.show()
		
		self.cancel_button = GtkButton('Cancel')
		self.cancel_button.connect('clicked', button_cb, 0)
		self.bbox.pack_start(self.cancel_button)
		self.cancel_button.show()

##############################################################################
# The configuration option classes
#
# These classes are designed to implement the configuration user interface in
# generic way.  All of them (at least all of them that are directly used
# by the Application class) have the methods set_value and get_value, the
# function of which should be self evident.  To facilitate interaction with
# the text configuration file, set_value always takes a string argument and
# get_value always returns a string value.
##############################################################################

# The Entry class describes the text entries.
# title = the text of the label
# value = the intial value
		
class Entry(GtkVBox):
	def __init__(self, title='', value=''):
		GtkVBox.__init__(self, spacing=SP)
		self.set_border_width(BD)
		self.init_widgets(title)
		self.set_value(value)
		self.show()
		
	def init_widgets(self, title):
		label = Label(title)
		label.show()
		self.pack_start(label, FALSE, FALSE)
		
		self.entry = GtkEntry()
		self.entry.show()
		self.pack_start(self.entry, FALSE, FALSE)
		
	def set_value(self, value):
		value = value[1 : len(value) - 1]
		self.entry.set_text(value)
		
	def get_value(self):
		value = self.entry.get_text()
		value = '"' + value + '"'
		return value
		
# The Toggled class describes the check buttons (for boolean options).
# title = text of the label
# value = initial value
		
class Toggled(GtkHBox):
	def __init__(self, title='', value=''):
		GtkHBox.__init__(self, spacing=SP)
		self.set_border_width(BD)
		self.init_widgets(title)
		self.set_value(value)
		self.show()
		
	def init_widgets(self, title):
		self.button = GtkCheckButton()
		self.button.show()
		self.pack_start(self.button, FALSE, FALSE, 0)
		
		label = Label(title)
		label.show()
		self.pack_start(label, FALSE, FALSE, 0)
		
	def set_value(self, value):
		self.button.set_active(eval(value))
		
	def get_value(self):
		value = self.button.get_active()
		return str(value)

# The Range class describes the range widget.  Currently, it includes both
# a GtkAdjustment widget and a spinbutton.  This could, of course, be changed
# if it is found to be unsatisfactory.
# title = text of the label
# min = bottom of the allowed range
# max = top of the allowed range
# value = initial value
		
class Range(GtkVBox):
	def __init__(self, title='', min=0, max=100, value=0):
		GtkVBox.__init__(self, spacing=SP)
		self.set_border_width(BD)
		self.init_widgets(title, min, max)
		self.set_value(value)
		self.show()
		
	def init_widgets(self, title, min, max):
		hbox = GtkHBox(FALSE, SP)
		hbox.set_border_width(BD)
		hbox.show()
		self.pack_start(hbox, FALSE, FALSE, 0)
		label = Label(title)
		label.show()
		hbox.pack_start(label, FALSE, FALSE, 0)
		adj = GtkAdjustment(lower=min, upper=max, step_incr=1)
		self.spin = GtkSpinButton(adj)
		self.spin.set_digits(0)
		self.spin.show()
		hbox.pack_end(self.spin, FALSE, FALSE, 0)
		scale = GtkHScale(adj)
		scale.set_draw_value(FALSE)
		scale.show()
		self.pack_start(scale, FALSE, FALSE, 0)
		
	def set_value(self, value):
		self.spin.set_value(eval(value))
		
	def get_value(self):
		ret = self.spin.get_value_as_int()
		return str(ret)

# The Keystroke class is currently an alias for the Entry class.  I hope to
# eventually add something more intuitive, but this will be complicated!  If
# you're in the mood . . . :-)
		
class Keystroke(GtkVBox):
	def __init__(self, title, value):
		GtkVBox.__init__(self)
		self.set_border_width(BD)
		self.init_constants()
		self.init_widgets(title)
		self.set_value(value)
		self.show()
		
	def init_constants(self):
		self.mods=[
					['Alt', 'Alt+'],
					['Ctrl', 'Ctrl+'],
					['Shift', 'Shift+']
					
				]
		
	def init_widgets(self, title):
		label = Label(title)
		self.pack_start(label)
		label.show()
		
		hbox = GtkHBox()
		self.pack_start(hbox)
		hbox.show()
		
		self.mod_buttons = []
		
		for item in self.mods:
			button = GtkToggleButton(item[0])
			hbox.pack_start(button)
			self.mod_buttons.append(button)
			button.show()
			
		self.entry = GtkEntry()
		hbox.pack_start(self.entry)
		self.entry.show()
		
	def set_value(self, value):
		value = replace(value, '"', '')
		for i in range(len(self.mods)):
			if count(value, self.mods[i][1]) == 1:
				self.mod_buttons[i].set_active(TRUE)
				value = replace(value, self.mods[i][1], '')
			else:
				self.mod_buttons[i].set_active(FALSE)
				value = replace(value, self.mods[i][1], '')
				
		self.entry.set_text(value)
		
	def get_value(self):
		value = ''
		
		for i in range(len(self.mods)):
			if self.mod_buttons[i].get_active():
				value = value + self.mods[i][1]
		
		entry_text = self.entry.get_text()
		value = '"' + value + entry_text + '"'
		
		return value

# The ButtonEntry class is a relatively generic class for metawidgets that
# contain both a text entry and a button to call up a dialog box.  Color,
# Font, and File all inherit from this class.  Path inherits from File.
# title = label text
# value = initial value

class ButtonEntry(GtkVBox):
	def __init__(self, title='', value=''):
		GtkVBox.__init__(self, FALSE, SP)
		self.set_border_width(BD)
		self.init_widgets(title)
		self.set_value(value)
		self.show()
		
	def init_widgets(self, title):
		label = Label(title)
		label.show()
		self.pack_start(label, FALSE, FALSE, 0)
		
		hbox = GtkHBox(FALSE, SP)
		hbox.show()
		self.pack_start(hbox, FALSE, FALSE, 0)
		
		self.entry = GtkEntry()
		self.entry.show()
		hbox.pack_start(self.entry, TRUE, TRUE, 0)
		
		button = GtkButton('...')
		button.connect('clicked', self.select)
		button.show()
		hbox.pack_start(button, FALSE, FALSE, 0)
		
	def set_value(self, value):
		value = value[1 : len(value) - 1]
		self.entry.set_text(value)
		
	def get_value(self):
		value = '"' + self.entry.get_text() + '"'
		return value
		
	def cancel(self, data=None):
		self.win.hide()
		self.win.destroy()
		
# The Color class is used for configuring (obviously) options which require
# the rgb value for a color.  It is a derivative of the ButtonEntry class and
# therefore accepts the same options.	
				
class Color(ButtonEntry):
	def select(self, data=None):
		value = self.entry.get_text()
		self.win = GtkColorSelectionDialog(name='Select Color')
		self.win.colorsel.set_opacity(FALSE)
		self.win.colorsel.set_update_policy(UPDATE_CONTINUOUS)
		self.win.set_position(WIN_POS_MOUSE)
		self.win.ok_button.connect('clicked', self.ok)
		self.win.cancel_button.connect('clicked', self.cancel)
		self.win.help_button.destroy()
		if value != '':
			r = atoi(value[4:6], 16) / 255.0
			g = atoi(value[7:9], 16) / 255.0
			b = atoi(value[10:12], 16) / 255.0
			self.win.colorsel.set_color((r, g, b))
		self.win.set_modal(TRUE)
		self.win.show()
		
	def ok(self, data=None):
		raw_values = self.win.colorsel.get_color()
		r,g,b = raw_values
		color = [r, g, b]
		for i in range(0,3):
			color[i] = hex(int(color[i] * 255.0))[2:]
			if len(color[i]) == 1:
				color[i] = '0' + color[i]
				
		r,g,b = color[0], color[1], color[2]
			
		value = 'rgb:' + r + '/' + g + '/' + b
		self.entry.set_text(value)
		self.win.hide()
		self.win.destroy()

# Font class is used (wonder of wonders) to select a font and returns an X
# font descriptor, which coincidentally is just what icewm demands.  It is
# a derivative of the ButtonEntry superclass and takes the same options.
		
class Font(ButtonEntry):

	def init_widgets(self, title):
		label = Label(title)
		label.show()
		self.pack_start(label, FALSE, FALSE, 0)
		
		self.sample = GtkText()
		self.sample.set_editable(FALSE)
		self.sample.set_usize(50,50)
		self.pack_start(self.sample)
		self.sample.show()
		
		hbox = GtkHBox(FALSE, SP)
		hbox.show()
		self.pack_start(hbox, FALSE, FALSE, 0)
		
		self.entry = GtkEntry()
		self.entry.connect('changed', self.update)
		self.entry.show()
		hbox.pack_start(self.entry, TRUE, TRUE, 0)
		
		button = GtkButton('...')
		button.connect('clicked', self.select)
		button.show()
		hbox.pack_start(button, FALSE, FALSE, 0)
		
	def set_value(self, value):
		value = value[1 : len(value) - 1]
		self.entry.set_text(value)
		self.set_sample(value)
		
	def set_sample(self, value):
		length = self.sample.get_length()
		if length > 0:
			self.sample.delete_text(0, length)		
		# The structure catches errors which are caused by invalid
		# font specifications.
		try:
			font = load_font(value)
			self.sample.insert(fg=None, bg=None, font=font, string=SAMPLE_TEXT)
		# If the font is invalid, it should trigger this little wonder
		except RuntimeError:
			self.sample.insert(fg=None, bg=None, font=None, string=ERROR_TEXT)

	def select(self, data=None):
		self.win = GtkFontSelectionDialog('Select Font')
		self.win.ok_button.connect('clicked', self.ok)
		self.win.cancel_button.connect('clicked', self.cancel)
		value = self.entry.get_text()
		if value !='':
			self.win.fontsel.set_font_name(value)
		self.win.set_modal(TRUE)
		self.win.show()
		
	def ok(self, data=None):
		value = self.win.fontsel.get_font_name()
		self.win.hide()
		self.win.destroy()
		self.entry.set_text(value)
		self.set_sample(value)
		
	def update(self, widget, data=None):
		value = self.entry.get_text()
		self.set_sample(value)
		
# The File class is used for options that require a file name and path.  It is
# a derivative of the Button Entry class and takes the same options.
		
class File(ButtonEntry):
		
	def select(self, data=None):
		self.win = GtkFileSelection()
		self.win.ok_button.connect('clicked', self.ok)
		self.win.cancel_button.connect('clicked', self.cancel)
		value = self.entry.get_text()
		if value != '""':
			self.win.set_filename(value)
		self.win.set_modal(TRUE)
		self.win.show()
		
	def ok(self, data=None):
		value = self.win.get_filename()
		self.win.hide()
		self.win.destroy()
		self.entry.set_text(value)

# The ThemeName class is a specialized class for configuring the theme option
# It is obviously a derivative of File and shares its lovely arguments.  It is
# now obsolete as the ThemeSel class has replaced it.
		
class ThemeName(File):

	def ok(self, data=None):
		value = self.win.get_filename()
		if len(value) - rfind(value, '.theme'):
			last_slash = rfind(value, '/')
			sec_last_slash = rfind(value[:last_slash], '/')
			value = value[sec_last_slash + 1 : ]
			self.win.hide()
			self.win.destroy()
			self.entry.set_text(value)
			

# The class Path is used to configure options that require a path but no file
# name.  It is a derivative of File and accepts the same options.
		
class Path(File):
		
	def ok(self, data=None):
		value = self.win.get_filename()
		value = value[: rfind(value, '/') + 1]
		self.win.hide()
		self.win.destroy()
		self.entry.set_text(value)
		
# The Multi class is used to configure options that consist of multiple
# strings in comma seperated lists.  Right now, this is limited to the desktop
# names.
# title = label text
# value = initial value
# num = number of text entries to be displayed
		
class Multi(GtkVBox):
	def __init__(self, title, value, num):
		GtkVBox.__init__(self, spacing = SP)
		self.num = num
		self.set_border_width(BD)
		self.init_widgets(title)
		self.set_value(value)
		self.show()
		
	def init_widgets(self, title):
		label = Label(title)
		label.show()
		self.pack_start(label)
		
		self.entries = []
		for i in range(0, self.num):
			entry = GtkEntry()
			entry.show()
			self.pack_start(entry)
			self.entries.append(entry)
			
	def divide(self, string):
		list = []
		while count(string, '"') > 0:
			f_quote = find(string, '"')
			l_quote = find(string[f_quote + 1 :], '"') + (len(string) - len(string[f_quote + 1:]))
			item = string[f_quote : l_quote + 1]
			string = string[l_quote + 1 :]
			list.append(item)			
		return list
		
	def set_value(self, value):
		for i in range(0, self.num):
			self.entries[i].set_text('')
		values = self.divide(value)
		if len(values) > self.num: values = values[:self.num]
		for i in range(0, len(values)):
			trimmed = values[i][1 : len(values[i]) - 1]
			self.entries[i].set_text(trimmed)
		
	def get_value(self):
		values = []
		for entry in self.entries:
			text = entry.get_text()
			if text != '':
				values.append(text)		
		value = ''	
		for item in values:
			value = value + '"' + item + '"' + ','		
		value = value[:len(value) - 1]
		return value

# The ThemeData class, when passed the whole path of a theme, sets its members
# to the values required by the ThemeSel class.

class ThemeData:
	def __init__(self, full_path):
		self.full_path = full_path
		self.init_vars(full_path)

	def init_vars(self, full_path):
		slash_1 = rfind(full_path, '/')
		slash_2 = rfind(full_path[:slash_1], '/')
		self.name = full_path[slash_2 + 1 : slash_1]
		self.theme_file = full_path[slash_1 + 1:]
		self.full_name = self.name + ' (' + self.theme_file + ')'
		self.path = self.name + '/' + self.theme_file

# This class creates a CList which can be used to select a theme.  It is quite
# specialized at this point.  Perhaps it could later be generalized to handle
# other options.  Currently, it does not reset itself when self.value is changed
# with the set_value method.  If this is desired, it could easily be implemented.
		
class ThemeSel(GtkVBox):
	def __init__(self, title='', value=''):
		GtkVBox.__init__(self)
		self.set_border_width(BD)
		self.set_spacing(SP)
		self.init_theme_list()
		self.set_value(value)
		self.init_widgets(title)
		self.show()
		
	def init_theme_list(self):
		self.theme_list = []
		for path in THEME_PATH:
			subdir_list = glob.glob(path)
			self.extract_theme_files(subdir_list)
			
	def extract_theme_files(self, subdir_list):
		for subdir in subdir_list:
			contents = glob.glob(subdir + '/*.theme')
			if contents != []:
				for file in contents:
					theme = ThemeData(file)
					self.theme_list.append(theme)
	
	def set_value(self, value):
		self.value = value[1 : len(value) - 1]
		if self.value == '':
			self.value = self.theme_list[0].path
			
	def get_value(self):
		value = '"' + self.value + '"'
		return value
	
	def init_widgets(self, title):
		label = Label(title)
		self.pack_start(label)
		label.show()
		
		swin = GtkScrolledWindow()
		swin.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
		swin.set_usize(150, 150)
		self.pack_start(swin)
		swin.show()
		
		clist = GtkCList(2, ['Theme', 'File'])
		clist.connect('select_row', self.clist_cb)
		swin.add(clist)
		clist.show()
		
		clist.freeze()
		row_count = 0
		for item in self.theme_list:
			row = [item.name, item.theme_file]
			clist.append(row)
			clist.set_row_data(row_count, item.path)
			if item.path == self.value:
				clist.select_row(row_count,0)
			row_count = row_count + 1
		clist.sort()
		clist.columns_autosize()
		clist.thaw()
		
	def clist_cb(self, widget, row, col, event):
		self.value = widget.get_row_data(row)

class OptionMenu(GtkVBox):
	def __init__(self, title='', value=''):
		GtkVBox.__init__(self)
		self.set_border_width(BD)
		self.init_widgets(title)
		self.set_value(value)
		self.show()
		
	def init_widgets(self, title):
		label = Label(title)
		self.pack_start(label)
		label.show()
		
		self.option_menu = GtkOptionMenu()
		self.option_menu.set_menu(self.create_menu())
		self.pack_start(self.option_menu)
		self.option_menu.show()
		
	def init_options(self):
		self.options = [['Null', '0']]
		
	def create_menu(self):
		self.init_options()
		menu = GtkMenu()
		group = None
		self.all_items = []			
		for option in self.options:
			menuitem = GtkRadioMenuItem(group, option[0])
			menuitem.connect('activate', self.menu_cb, option[1])
			group = menuitem
			menu.append(menuitem)
			self.all_items.append(menuitem)
			menuitem.show()
			
		return menu
		
	def menu_cb(self, widget, data):
		self.value = str(data)
			
	def set_value(self, value):
		if eval(value) > 7:
			value = str(7)
		if eval(value) < 0:
			value = str(0)
		self.value = value
		for i in range(len(self.options)):
			if value == self.options[i][1]:
				self.all_items[i].set_active(TRUE)
				self.option_menu.set_history(i)
			
	def get_value(self):
		return self.value

# This special class is used to configure options which use a bitmask to control
# the behavior of the three mouse buttons.

class BitMask(OptionMenu):
	def init_options(self):
		self.options = [['No mouse buttons', '0'],
					['First button only', '1'],
					['Second button only', '2'],
					['Third button only', '4'],
					['First and second buttons', '3'],
					['First and third buttons', '5'],
					['Second and third buttons', '6'],
					['All three buttons', '7']]


class MouseButton(OptionMenu):
	def init_options(self):
		self.options = [['No mouse button', '0'],
					['First mouse button', '1'],
					['Second mouse button', '2'],
					['Third mouse button', '3'],
					['Fourth mouse button', '4'],
					['Fifth mouse button', '5']]

##############################################################################		
# The Application class -- mother of all IcePref classes
#
# The Application class is the main class of this little utility.  It is
# confusing and I don't feel like commenting it right now.
##############################################################################
	
class Application(GtkWindow):
	
	def __init__(self, argv):
		GtkWindow.__init__(self, title='IcePref')
		self.connect('destroy', mainquit)
		self.check_for_dir()
		self.init_settings()
		self.get_current_settings()
		
		self.vbox = GtkVBox(spacing = SP)
		self.vbox.set_border_width(BD)
		self.vbox.show()
		self.add(self.vbox)
		self.init_menu()
		self.widget_dict = {}
		self.init_notebook()
		self.init_buttons()
		self.show()
		
	def mainloop(self):
		mainloop()
	
	# Checks for ~/.icewm.  Creates it if it doesn't exist
		
	def check_for_dir(self):
		if not ('.icewm' in os.listdir(user.home)):
			os.mkdir(user.home + '/.icewm')
			win = Dialog('Attention', 'I have graciously made an ~/.icewm directory for you, as you did not have one')
	
	# Makes a copy of DEFAULTS in self.settings.  This still doesn't seem to
	# do what I want it to.
		
	def init_settings(self):
		self.settings = {}
		for key in DEFAULTS.keys():
			self.settings[key] = DEFAULTS[key][:]
	
	# Goes through each of the configuration options and forces the widgets
	# to conform to the data in self.settings
	 	
	def update_widgets(self, data=None):
		for widget in ORDER:
			self.widget_dict[widget].set_value(self.settings[widget][VALUE])
	
	# Takes a line from the configuration file and returns the name of the
	# configuration option (name) and its value (value) as a tuple.
			
	def trim(self, string):
	
		# cuts out everything after the first `#'
		if find(string, '#') != -1:
			string = string[:find(string, '#')]
		
		# checks to if the line contains `='	
		if find(string, '=') == -1:
			name, value = ('', '') # if not, set name and value to null
		else: # if so, sort the stuff out
			name, value = '', ''
			f_quote = find(string, '"')
			l_quote = rfind(string, '"')
			if f_quote != -1:
				name = string[:f_quote - 1]
				value = string[f_quote : l_quote+1]
			else:
				name = string[:find(string, '=')]
				value = string[find(string, '=') + 1: find(string, ' ')]			
		return name, value
	
	# reads the preferences file for the current settings
	
	def get_current_settings(self, data=None):
		try:
			f = open(CONFIG_FILE, 'r')
			line = ' '
			while line <>'':
				found = ''
				line = f.readline()
				name, value = self.trim(line)		
				if name and value:
					for key in self.settings.keys():
						if (name == key) and (len(key) > len(found)):
							found = key
					if found !='':
						self.settings[found][VALUE]=value
				elif name and not value:
					print '%s does not appear to be a valid option' % line[:-1]
			f.close()
		except IOError:
			win = Dialog('Warning!', "Argh!  I cannot open your '.icewm/preferences' file!")
	
	# writes the contents of self.settings to the preferences file
			
	def save_current_settings(self, *data):
		# this stuff is to make a backup file
		win = Dialog('Attention', "Backing up 'preferences' file . . .")
		try: 
			f = open(CONFIG_FILE, 'r')
			old_pref = f.read()
			f.close()
			
			backup = CONFIG_FILE + '~'
			f = open(backup, 'w')
			f.write(old_pref)
			f.close()
		except:
			win = Dialog('Attention', 'Oh!  I guess you had no preferences file to backup!')
		# this stuff saves the actual info
		try: 
			f = open(CONFIG_FILE, 'w')
			f.write('# This configuration file automatically generated by IcePref--your friendly pythonated config util.\n\n')
			for name in ORDER:
				string =''
				# this adds some descriptors depending upon the type of
				# option
				if self.settings[name][TYPE] == TOGGLE:
					string =' # 0 / 1'
				elif self.settings[name][TYPE] == RANGE:
					min = self.settings[name][MIN]
					max = self.settings[name][MAX]
					string = ' # ' + str(min) + '-' + str(max)
				# this sets up the comment descriptor, which is the same
				# as the label text.
				comment = '# ' + self.settings[name][TITLE] + '\n'
				if self.widget_dict.has_key(name):
					line = name + '=' + self.widget_dict[name].get_value() + string + '\n'
				else:
					print 'Warning!  No widget for option %s' % name
				f.write(comment)
				f.write(line)
				f.write('\n')
			f.close()
		except IOError:
			win = Dialog('Warning', "Argh! I can't write to your silly 'preferences' file.")

	def restart(self, widget=None, data=None):
		# restart icewm.  Note that this is experimental and I have no idea
		# if it will work on all systems.
		os.system('killall -HUP -q icewm')
		os.system('killall -HUP -q icewm-gnome')
	
	# this is the callback for the OK button
	
	def ok(self, data=None):
		# save and exit
		print 'OK'
		self.save_current_settings()
		mainquit()
		
	# callback for the `about' menu option
		
	def about_cb(self, *data):
		win = Dialog('About', 'This is IcePref 0.10 by David Mortensen--a pythonated configuration utility for IceWM.  Visit the IcePref website at http://members.xoom.com/SaintChoj/icepref.html.')
	
	# reloads the preferences file and sets all of the config widgets to
	# corresponding values.
			
	def set_file_settings(self, *data):
		self.get_current_settings()
		self.update_widgets()
		
	# returns self.settings to the default values--doesn't work quite right
			
	def set_default_settings(self, *data):
		self.init_settings()
		self.update_widgets()
	
	# creates the menubar
		
	def init_menu(self):
		menu_items = [
				['/_File', None, None, 0, '<Branch>'],
				['/File/tearoff1', None, None, 0, '<Tearoff>'],
				['/File/_Reload', '<control>O', self.set_file_settings, 0, ''],
				['/File/_Defaults', '<control>D', self.set_default_settings, 0, ''],
				['/File/_Save', '<control>S', self.save_current_settings, 0, ''],
				['/File/sep1', None, None, 0, '<Separator>'],
				['/File/_Restart IceWM', '<control>R', self.restart, 0, ''],
				['/File/sep2', None, None, 0, '<Separator>'],
				['/File/_Close', '<control>Q', mainquit, 0, ''],
				['/_Help', None, None, 0, '<LastBranch>'],
				['/Help/_About', None, self.about_cb, 0, '']
					]
					
		ag = GtkAccelGroup()
		itemf = GtkItemFactory(GtkMenuBar, '<main>', ag)
		self.add_accel_group(ag)
		itemf.create_items(menu_items)
		self.menubar = itemf.get_widget('<main>')
		self.vbox.pack_start(self.menubar, expand=FALSE)
		self.menubar.show()
		
	# creates the notebook and its friends.

	def init_notebook(self):
		notebook = GtkNotebook()
		notebook.set_tab_pos(POS_LEFT)
		notebook.set_scrollable(TRUE)
		notebook.show()
		self.vbox.pack_start(notebook)
		sep = GtkHSeparator()
		sep.show()
		self.vbox.pack_start(sep, expand = FALSE)
		for tab in TABS:
			label = GtkLabel(tab[0])
			widgets = tab[1]
			scroll = GtkScrolledWindow()
			scroll.set_usize(400, 300)
			scroll.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
			scroll.show()
			notebook.append_page(scroll, label)
			
			vbox = GtkVBox(spacing=SP)
			vbox.show()
			
			if len(widgets) > 4:
				rows = (int(len(widgets)/2) + 1)
				cols = 2
			else:
				rows = len(widgets)
				cols = 1
			if rows < 2: rows = 2
			table = GtkTable(	rows = rows,
							cols = cols,
							homogeneous=FALSE)
			table.show()
			vbox.pack_start(table, FALSE, FALSE)
			scroll.add_with_viewport(vbox)
			x, y = 0, 0
			for item in widgets:
				widget = self.widget_chooser(item)
				table.attach(widget, x,x+1, y,y+1)
				self.widget_dict[item] = widget
				if y == rows - 1 and cols == 2:
					y = 0
					x = x + 1
				else:
					y = y + 1
					
	# accepts a configuration option as its argument and returns the
	# appropriate widget.  If a new type of widget is added, it will have
	# to be added here as well.
					
	def widget_chooser(self, item):
		type = self.settings[item][TYPE]
		if type == TOGGLE:
			widget = Toggled(	self.settings[item][TITLE],
							self.settings[item][VALUE])
		elif type == RANGE:
			widget = Range(	self.settings[item][TITLE],
							self.settings[item][MIN],
							self.settings[item][MAX],
							self.settings[item][VALUE])
		elif type == FILE:
			widget = File(		self.settings[item][TITLE],
							self.settings[item][VALUE])
		elif type == PATH:
			widget = Path(		self.settings[item][TITLE],
							self.settings[item][VALUE])
		elif type == COLOR:
			widget = Color(	self.settings[item][TITLE],
							self.settings[item][VALUE])
		elif type == FONT:
			widget = Font(		self.settings[item][TITLE],
							self.settings[item][VALUE])
		elif type == ENTRY:
			widget = Entry(	self.settings[item][TITLE],
							self.settings[item][VALUE])
		elif type == KEYSTROKE:
			widget = Keystroke(	self.settings[item][TITLE],
							self.settings[item][VALUE])
		elif type == MULTI:
			widget = Multi(	self.settings[item][TITLE],
							self.settings[item][VALUE],
							self.settings[item][NUM])
		elif type == THEME:
			widget = ThemeSel(	self.settings[item][TITLE],
							self.settings[item][VALUE])
					
		elif type == BITMASK:
			widget = BitMask(	self.settings[item][TITLE],
							self.settings[item][VALUE])
							
		elif type == MOUSEBUTTON:
			widget = MouseButton( 	self.settings[item][TITLE],
								self.settings[item][VALUE])
		
		return widget
		
	# creates the buttons at the bottom of the window
	
	def init_buttons(self):
	
		buttons = [
					['Save', self.save_current_settings],
					['Defaults', self.set_default_settings],
					['Reload', self.set_file_settings],
					['Restart', self.restart],
					['Close', mainquit]
				]
				
		bbox = GtkHButtonBox()
		bbox.set_layout(BUTTONBOX_SPREAD)
		bbox.show()
		self.vbox.pack_start(bbox, FALSE, FALSE, 0)
		for item in buttons:
			button = GtkButton(item[0])
			button.connect('clicked', item[1])
			button.show()
			bbox.pack_start(button, TRUE, FALSE, 0)

# makes the whole show run

if __name__ == '__main__':		
	app = Application(sys.argv)
	app.mainloop()
