//
// Config.cc for pekwm
// Copyright (C) 2002-2005 Claes Nasten <pekdon{@}pekdon{.}net>
//
// This program is licensed under the GNU GPL.
// See the LICENSE file for more information.
//

#include "../config.h"
#include "Config.hh"

#include "Util.hh"
#include "PScreen.hh" // for DPY in keyconfig code

#include <iostream>
#include <fstream>

#include <cstdlib>

extern "C" {
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
}

using std::cerr;
using std::endl;
using std::string;
using std::list;
using std::map;
using std::vector;
using std::pair;
using std::ifstream;
using std::ofstream;

Config* Config::_instance = NULL;

const int FRAME_MASK =
	FRAME_OK|FRAME_BORDER_OK|CLIENT_OK|WINDOWMENU_OK|
	KEYGRABBER_OK|BUTTONCLICK_OK;
const int ANY_MASK =
	KEYGRABBER_OK|FRAME_OK|FRAME_BORDER_OK|CLIENT_OK|ROOTCLICK_OK|
	BUTTONCLICK_OK|WINDOWMENU_OK|ROOTMENU_OK|SCREEN_EDGE_OK;

//! @brief Constructor for Config class
Config::Config(void) :
_moveresize_edgeattract(0), _moveresize_edgeresist(0),
_moveresize_woattract(0), _moveresize_woresist(0),
_moveresize_opaquemove(0), _moveresize_opaqueresize(0),
_screen_workspaces(4), _screen_pixmap_cache_size(20),_screen_edge_size(0),
_screen_doubleclicktime(250), _screen_showframelist(true),
_screen_place_new(true), _screen_focus_new(false),
_screen_focus_new_child (true),
_screen_placement_row(false),
_screen_placement_ltr(true), _screen_placement_ttb(true),
_screen_placement_offset_x(0), _screen_placement_offset_y(0),
_screen_client_unique_name(true),
_screen_client_unique_name_pre(" #"), _screen_client_unique_name_post(""),
_menu_select_mask(0), _menu_enter_mask(0), _menu_exec_mask(0),
#ifdef HARBOUR
_harbour_da_min_s(0), _harbour_da_max_s(0),
#ifdef HAVE_XINERAMA
_harbour_head_nr(0),
#endif // HAVE_XINERAMA
#endif // HARBOUR
_viewport_cols(1), _viewport_rows(1)
{
	if (_instance != NULL) {
		throw string("Config, trying to create multiple instances");
	}
	_instance = this;

	// fill parsing maps
	_action_map[""] = pair<ActionType, uint>(ACTION_NO, 0);
	_action_map["FOCUS"] = pair<ActionType, uint>(ACTION_FOCUS, ANY_MASK);
	_action_map["UNFOCUS"] = pair<ActionType, uint>(ACTION_UNFOCUS, ANY_MASK);

	_action_map["SET"] = pair<ActionType, uint>(ACTION_SET, ANY_MASK);
	_action_map["UNSET"] = pair<ActionType, uint>(ACTION_UNSET, ANY_MASK);
	_action_map["TOGGLE"] = pair<ActionType, uint>(ACTION_TOGGLE, ANY_MASK);

	_action_map["MAXFILL"] = pair<ActionType, uint>(ACTION_MAXFILL, FRAME_MASK);
	_action_map["GROWDIRECTION"] = pair<ActionType, uint>(ACTION_GROW_DIRECTION, FRAME_MASK);
	_action_map["CLOSE"] = pair<ActionType, uint>(ACTION_CLOSE, FRAME_MASK);
	_action_map["CLOSEFRAME"] = pair<ActionType, uint>(ACTION_CLOSE_FRAME, FRAME_MASK);
	_action_map["KILL"] = pair<ActionType, uint>(ACTION_KILL, FRAME_MASK);
	_action_map["RAISE"] = pair<ActionType, uint>(ACTION_RAISE, FRAME_MASK);
	_action_map["LOWER"] = pair<ActionType, uint>(ACTION_LOWER, FRAME_MASK);
	_action_map["ACTIVATEORRAISE"] = pair<ActionType, uint>(ACTION_ACTIVATE_OR_RAISE, FRAME_MASK);
	_action_map["ACTIVATECLIENTREL"] = pair<ActionType, uint>(ACTION_ACTIVATE_CLIENT_REL, FRAME_MASK);
	_action_map["MOVECLIENTREL"] = pair<ActionType, uint>(ACTION_MOVE_CLIENT_REL, FRAME_MASK);
	_action_map["ACTIVATECLIENT"] = pair<ActionType, uint>(ACTION_ACTIVATE_CLIENT, FRAME_MASK);
	_action_map["ACTIVATECLIENTNUM"] = pair<ActionType, uint>(ACTION_ACTIVATE_CLIENT_NUM, KEYGRABBER_OK);
	_action_map["RESIZE"] = pair<ActionType, uint>(ACTION_RESIZE, BUTTONCLICK_OK|CLIENT_OK|FRAME_OK|FRAME_BORDER_OK);
	_action_map["MOVE"] = pair<ActionType, uint>(ACTION_MOVE, FRAME_OK|FRAME_BORDER_OK|CLIENT_OK);
	_action_map["MOVERESIZE"] = pair<ActionType, uint>(ACTION_MOVE_RESIZE, KEYGRABBER_OK);
	_action_map["GROUPINGDRAG"] = pair<ActionType, uint>(ACTION_GROUPING_DRAG, FRAME_OK|CLIENT_OK);
	_action_map["WARPTOVIEWPORT"] = pair<ActionType, uint>(ACTION_WARP_TO_VIEWPORT, SCREEN_EDGE_OK);
	_action_map["WARPTOWORKSPACE"] = pair<ActionType, uint>(ACTION_WARP_TO_WORKSPACE, SCREEN_EDGE_OK);
	_action_map["MOVETOEDGE"] = pair<ActionType, uint>(ACTION_MOVE_TO_EDGE, KEYGRABBER_OK);
	_action_map["NEXTFRAME"] = pair<ActionType, uint>(ACTION_NEXT_FRAME, KEYGRABBER_OK|ROOTCLICK_OK|SCREEN_EDGE_OK);
	_action_map["PREVFRAME"] = pair<ActionType, uint>(ACTION_PREV_FRAME, KEYGRABBER_OK|ROOTCLICK_OK|SCREEN_EDGE_OK);
	_action_map["NEXTFRAMEMRU"] = pair<ActionType, uint>(ACTION_NEXT_FRAME_MRU, KEYGRABBER_OK|ROOTCLICK_OK|SCREEN_EDGE_OK);
	_action_map["PREVFRAMEMRU"] = pair<ActionType, uint>(ACTION_PREV_FRAME_MRU, KEYGRABBER_OK|ROOTCLICK_OK|SCREEN_EDGE_OK);
	_action_map["FOCUSDIRECTIONAL"] = pair<ActionType, uint>(ACTION_FOCUS_DIRECTIONAL, FRAME_MASK);
	_action_map["ATTACHMARKED"] = pair<ActionType, uint>(ACTION_ATTACH_MARKED, FRAME_MASK);
	_action_map["ATTACHCLIENTINNEXTFRAME"] = pair<ActionType, uint>(ACTION_ATTACH_CLIENT_IN_NEXT_FRAME, FRAME_MASK);
	_action_map["ATTACHCLIENTINPREVFRAME"] = pair<ActionType, uint>(ACTION_ATTACH_CLIENT_IN_PREV_FRAME, FRAME_MASK);
	_action_map["VIEWPORTMOVEXY"] = pair<ActionType, uint>(ACTION_VIEWPORT_MOVE_XY, ANY_MASK);
	_action_map["VIEWPORTMOVEDRAG"] = pair<ActionType, uint>(ACTION_VIEWPORT_MOVE_DRAG, ANY_MASK);
	_action_map["VIEWPORTMOVEDIRECTION"] = pair<ActionType, uint>(ACTION_VIEWPORT_MOVE_DIRECTION, ANY_MASK);
	_action_map["VIEWPORTSCROLL"] = pair<ActionType, uint>(ACTION_VIEWPORT_SCROLL, ANY_MASK);
	_action_map["VIEWPORTGOTO"] = pair<ActionType, uint>(ACTION_VIEWPORT_GOTO, ANY_MASK);
	_action_map["FINDCLIENT"] = pair<ActionType, uint>(ACTION_FIND_CLIENT, ANY_MASK);
	_action_map["DETACH"] = pair<ActionType, uint>(ACTION_DETACH, FRAME_MASK);
	_action_map["SENDTOWORKSPACE"] = pair<ActionType, uint>(ACTION_SEND_TO_WORKSPACE, ANY_MASK);
	_action_map["GOTOWORKSPACE"] = pair<ActionType, uint>(ACTION_GOTO_WORKSPACE, ANY_MASK );
	_action_map["EXEC"] = pair<ActionType, uint>(ACTION_EXEC, FRAME_MASK|ROOTMENU_OK|ROOTCLICK_OK|SCREEN_EDGE_OK);
	_action_map["RELOAD"] = pair<ActionType, uint>(ACTION_RELOAD, KEYGRABBER_OK|ROOTMENU_OK);
	_action_map["RESTART"] = pair<ActionType, uint>(ACTION_RESTART, KEYGRABBER_OK|ROOTMENU_OK);
	_action_map["RESTARTOTHER"] = pair<ActionType, uint>(ACTION_RESTART_OTHER, KEYGRABBER_OK|ROOTMENU_OK);
	_action_map["EXIT"] = pair<ActionType, uint>(ACTION_EXIT, KEYGRABBER_OK|ROOTMENU_OK);
	_action_map["SHOWCMDDIALOG"] = pair<ActionType, uint>(ACTION_SHOW_CMD_DIALOG, KEYGRABBER_OK|ROOTCLICK_OK|SCREEN_EDGE_OK|ROOTMENU_OK|WINDOWMENU_OK);
#ifdef MENUS
	_action_map["SHOWMENU"] = pair<ActionType, uint>(ACTION_SHOW_MENU, FRAME_MASK|ROOTCLICK_OK|SCREEN_EDGE_OK|ROOTMENU_OK|WINDOWMENU_OK);
	_action_map["HIDEALLMENUS"] = pair<ActionType, uint>(ACTION_HIDE_ALL_MENUS, FRAME_MASK|ROOTCLICK_OK|SCREEN_EDGE_OK);
	_action_map["SUBMENU"] = pair<ActionType, uint>(ACTION_MENU_SUB, ROOTMENU_OK|WINDOWMENU_OK);
	_action_map["DYNAMIC"] = pair<ActionType, uint>(ACTION_MENU_DYN, ROOTMENU_OK|WINDOWMENU_OK);
#endif // MENUS

	_placement_map[""] = PLACE_NO;
	_placement_map["SMART"] = PLACE_SMART;
	_placement_map["MOUSENOTUNDER"] = PLACE_MOUSE_NOT_UNDER;
	_placement_map["MOUSECENTERED"] = PLACE_MOUSE_CENTERED;
	_placement_map["MOUSETOPLEFT"] = PLACE_MOUSE_TOP_LEFT;
	_placement_map["CENTEREDONPARENT"] = PLACE_CENTERED_ON_PARENT;

	_edge_map[""] = NO_EDGE;
	_edge_map["TOPLEFT"] = TOP_LEFT;
	_edge_map["TOPEDGE"] = TOP_EDGE;
	_edge_map["TOPCENTEREDGE"] = TOP_CENTER_EDGE;
	_edge_map["TOPRIGHT"] = TOP_RIGHT;
	_edge_map["BOTTOMRIGHT"] = BOTTOM_RIGHT;
	_edge_map["BOTTOMEDGE"] = BOTTOM_EDGE;
	_edge_map["BOTTOMCENTEREDGE"] = BOTTOM_CENTER_EDGE;
	_edge_map["BOTTOMLEFT"] = BOTTOM_LEFT;
	_edge_map["LEFTEDGE"] = LEFT_EDGE;
	_edge_map["LEFTCENTEREDGE"] = LEFT_CENTER_EDGE;
	_edge_map["RIGHTEDGE"] = RIGHT_EDGE;
	_edge_map["RIGHTCENTEREDGE"] = RIGHT_CENTER_EDGE;
	_edge_map["CENTER"] = CENTER;

	_raise_map[""] = NO_RAISE;
	_raise_map["ALWAYSRAISE"] = ALWAYS_RAISE;
	_raise_map["ENDRAISE"] = END_RAISE;
	_raise_map["NEVERRAISE"] = NEVER_RAISE;

	_skip_map[""] = SKIP_NONE;
	_skip_map["MENUS"] = SKIP_MENUS;
	_skip_map["FOCUSTOGGLE"] = SKIP_FOCUS_TOGGLE;
	_skip_map["SNAP"] = SKIP_SNAP;

	_layer_map[""] = LAYER_NONE;
	_layer_map["DESKTOP"] = LAYER_DESKTOP;
	_layer_map["BELOW"] = LAYER_BELOW;
	_layer_map["NORMAL"] = LAYER_NORMAL;
	_layer_map["ONTOP"] = LAYER_ONTOP;
	_layer_map["HARBOUR"] = LAYER_DOCK;
	_layer_map["ABOVEHARBOUR"] = LAYER_ABOVE_DOCK;
	_layer_map["MENU"] = LAYER_MENU;

	_moveresize_map[""] = NO_MOVERESIZE_ACTION;
	_moveresize_map["MOVEHORIZONTAL"] = MOVE_HORIZONTAL;
	_moveresize_map["MOVEVERTICAL"] = MOVE_VERTICAL;
	_moveresize_map["RESIZEHORIZONTAL"] = RESIZE_HORIZONTAL;
	_moveresize_map["RESIZEVERTICAL"] = RESIZE_VERTICAL;
	_moveresize_map["MOVESNAP"] = MOVE_SNAP;
	_moveresize_map["CANCEL"] = MOVE_CANCEL;
	_moveresize_map["END"] = MOVE_END;

	_cmddialog_map[""] = CMD_D_NO_ACTION;
	_cmddialog_map["INSERT"] = CMD_D_INSERT;
	_cmddialog_map["ERASE"] = CMD_D_REMOVE;
	_cmddialog_map["CLEAR"] = CMD_D_CLEAR;
	_cmddialog_map["EXEC"] = CMD_D_EXEC;
	_cmddialog_map["CLOSE"] = CMD_D_CLOSE;
	_cmddialog_map["COMPLETE"] = CMD_D_COMPLETE;
	_cmddialog_map["CURSNEXT"] = CMD_D_CURS_NEXT;
	_cmddialog_map["CURSPREV"] = CMD_D_CURS_PREV;
	_cmddialog_map["CURSEND"] = CMD_D_CURS_END;
	_cmddialog_map["CURSBEGIN"] = CMD_D_CURS_BEGIN;
	_cmddialog_map["HISTNEXT"] = CMD_D_HIST_NEXT;
	_cmddialog_map["HISTPREV"] = CMD_D_HIST_PREV;

	_direction_map[""] = DIRECTION_NO;
	_direction_map["UP"] = DIRECTION_UP;
	_direction_map["DOWN"] = DIRECTION_DOWN;
	_direction_map["LEFT"] = DIRECTION_LEFT;
	_direction_map["RIGHT"] = DIRECTION_RIGHT;

	_workspace_change_map[""] = WORKSPACE_NO;
	_workspace_change_map["LEFT"] = WORKSPACE_LEFT;
	_workspace_change_map["PREV"] = WORKSPACE_PREV;
	_workspace_change_map["RIGHT"] = WORKSPACE_RIGHT;
	_workspace_change_map["NEXT"] = WORKSPACE_NEXT;
	_workspace_change_map["LAST"] = WORKSPACE_LAST;

	_borderpos_map[""] = BORDER_NO_POS;
	_borderpos_map["TOPLEFT"] = BORDER_TOP_LEFT;
	_borderpos_map["TOP"] = BORDER_TOP;
	_borderpos_map["TOPRIGHT"] = BORDER_TOP_RIGHT;
	_borderpos_map["LEFT"] = BORDER_LEFT;
	_borderpos_map["RIGHT"] = BORDER_RIGHT;
	_borderpos_map["BOTTOMLEFT"] = BORDER_BOTTOM_LEFT;
	_borderpos_map["BOTTOM"] = BORDER_BOTTOM;
	_borderpos_map["BOTTOMRIGHT"] = BORDER_BOTTOM_RIGHT;

	_mouse_event_map[""] = MOUSE_EVENT_NO;
	_mouse_event_map["BUTTONPRESS"] = MOUSE_EVENT_PRESS;
	_mouse_event_map["BUTTONRELEASE"] = MOUSE_EVENT_RELEASE;
	_mouse_event_map["DOUBLECLICK"] = MOUSE_EVENT_DOUBLE;
	_mouse_event_map["MOTION"] = MOUSE_EVENT_MOTION;
	_mouse_event_map["ENTER"] = MOUSE_EVENT_ENTER;
	_mouse_event_map["LEAVE"] = MOUSE_EVENT_LEAVE;
	_mouse_event_map["ENTERMOVING"] = MOUSE_EVENT_ENTER_MOVING;

	_mod_map[""] = 0;
	_mod_map["NONE"] = 0;
	_mod_map["SHIFT"] = ShiftMask;
	_mod_map["CTRL"] = ControlMask;
	_mod_map["MOD1"] = Mod1Mask;
	_mod_map["MOD2"] = Mod2Mask;
	_mod_map["MOD3"] = Mod3Mask;
	_mod_map["MOD4"] = Mod4Mask;
	_mod_map["MOD5"] = Mod5Mask;
	_mod_map["ANY"] = MOD_ANY;

	_action_state_map[""] = ACTION_STATE_NO;
	_action_state_map["MAXIMIZED"] = ACTION_STATE_MAXIMIZED;
	_action_state_map["FULLSCREEN"] = ACTION_STATE_FULLSCREEN;
	_action_state_map["SHADED"] = ACTION_STATE_SHADED;
	_action_state_map["STICKY"] = ACTION_STATE_STICKY;
	_action_state_map["ALWAYSONTOP"] = ACTION_STATE_ALWAYS_ONTOP;
	_action_state_map["ALWAYSBELOW"] = ACTION_STATE_ALWAYS_BELOW;
	_action_state_map["DECORBORDER"] = ACTION_STATE_DECOR_BORDER;
	_action_state_map["DECORTITLEBAR"] = ACTION_STATE_DECOR_TITLEBAR;
	_action_state_map["ICONIFIED"] = ACTION_STATE_ICONIFIED;
	_action_state_map["TAGGED"] = ACTION_STATE_TAGGED;
	_action_state_map["MARKED"] = ACTION_STATE_MARKED;
	_action_state_map["SKIP"] = ACTION_STATE_SKIP;
	_action_state_map["CFGDENY"] = ACTION_STATE_CFG_DENY;
	_action_state_map["TITLE"] = ACTION_STATE_TITLE;
	_action_state_map["GLOBALGROUPING"] = ACTION_STATE_GLOBAL_GROUPING;

	_cfg_deny_map["POSITION"] = CFG_DENY_POSITION;
	_cfg_deny_map["SIZE"] = CFG_DENY_SIZE;
	_cfg_deny_map["STACKING"] = CFG_DENY_STACKING;
	_cfg_deny_map["ACTIVEWINDOW"] = CFG_DENY_ACTIVE_WINDOW;
	_cfg_deny_map["MAXIMIZEDVERT"] = CFG_DENY_STATE_MAXIMIZED_VERT;
	_cfg_deny_map["MAXIMIZEDHORZ"] = CFG_DENY_STATE_MAXIMIZED_HORZ;
	_cfg_deny_map["HIDDEN"] = CFG_DENY_STATE_HIDDEN;
	_cfg_deny_map["FULLSCREEN"] = CFG_DENY_STATE_FULLSCREEN;
	_cfg_deny_map["ABOVE"] = CFG_DENY_STATE_ABOVE;
	_cfg_deny_map["BELOW"] = CFG_DENY_STATE_BELOW;

#ifdef MENUS
	_menu_type_map[""] = NO_MENU_TYPE;
	_menu_type_map["WINDOW"] = WINDOWMENU_TYPE;
	_menu_type_map["ROOT"] = ROOTMENU_TYPE;
	_menu_type_map["GOTO"] = GOTOMENU_TYPE;
	_menu_type_map["GOTOCLIENT"] = GOTOCLIENTMENU_TYPE;
	_menu_type_map["ICON"] = ICONMENU_TYPE;
	_menu_type_map["ATTACHCLIENT"] = ATTACH_CLIENT_TYPE;
	_menu_type_map["ATTACHFRAME"] = ATTACH_FRAME_TYPE;
	_menu_type_map["ATTACHCLIENTINFRAME"] = ATTACH_CLIENT_IN_FRAME_TYPE;
	_menu_type_map["ATTACHFRAMEINFRAME"] = ATTACH_FRAME_IN_FRAME_TYPE;
	_menu_type_map["DECOR"] = DECORMENU_TYPE;

	_menu_action_map[""] = ACTION_MENU_NEXT;
	_menu_action_map["NEXTITEM"] = ACTION_MENU_NEXT;
	_menu_action_map["PREVITEM"] = ACTION_MENU_PREV;
	_menu_action_map["SELECT"] = ACTION_MENU_SELECT;
	_menu_action_map["ENTERSUBMENU"] = ACTION_MENU_ENTER_SUBMENU;
	_menu_action_map["LEAVESUBMENU"] = ACTION_MENU_LEAVE_SUBMENU;
	_menu_action_map["CLOSE"] = ACTION_CLOSE;
#endif // MENUS

#ifdef HARBOUR
	_harbour_placement_map[""] = NO_HARBOUR_PLACEMENT;
	_harbour_placement_map["TOP"] = TOP;
	_harbour_placement_map["LEFT"] = LEFT;
	_harbour_placement_map["RIGHT"] = RIGHT;
	_harbour_placement_map["BOTTOM"] = BOTTOM;

	_harbour_orientation_map[""] = NO_ORIENTATION;
	_harbour_orientation_map["TOPTOBOTTOM"] = TOP_TO_BOTTOM;
	_harbour_orientation_map["LEFTTORIGHT"] = TOP_TO_BOTTOM;
	_harbour_orientation_map["BOTTOMTOTOP"] = BOTTOM_TO_TOP;
	_harbour_orientation_map["RIGHTTOLEFT"] = BOTTOM_TO_TOP;
#endif // HARBOUR

	// fill the mouse action map
	_mouse_action_map[MOUSE_ACTION_LIST_TITLE_FRAME] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_TITLE_OTHER] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_CHILD_FRAME] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_CHILD_OTHER] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_ROOT] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_MENU] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_OTHER] = new list<ActionEvent>;

	_mouse_action_map[MOUSE_ACTION_LIST_EDGE_T] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_EDGE_B] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_EDGE_L] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_EDGE_R] = new list<ActionEvent>;

	_mouse_action_map[MOUSE_ACTION_LIST_BORDER_TL] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_BORDER_T] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_BORDER_TR] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_BORDER_L] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_BORDER_R] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_BORDER_BL] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_BORDER_B] = new list<ActionEvent>;
	_mouse_action_map[MOUSE_ACTION_LIST_BORDER_BR] = new list<ActionEvent>;
}

//! @brief Destructor for Config class
Config::~Config(void)
{
	_instance = NULL;

	map<MouseActionListName, list<ActionEvent>* >::iterator it;
	for (it = _mouse_action_map.begin(); it != _mouse_action_map.end(); ++it) {
		delete it->second;
	}
}

//! @brief Tries to load config_file, ~/.pekwm/config, SYSCONFDIR/config
void
Config::load(const std::string &config_file)
{
  CfgParser cfg;
  bool success = false;
  string file_success;

  _config_file = config_file;

  // Try loading command line specified file.
  if (_config_file.size ())
    {
      success = cfg.parse (config_file, CfgParserSource::SOURCE_FILE);
      if (success)
        file_success = config_file;
    }

  // Try loading ~/.pekwm/config
  if (!success)
    {
      _config_file = string(getenv("HOME")) + string("/.pekwm/config");
      success = cfg.parse (_config_file, CfgParserSource::SOURCE_FILE);
      if (success)
        file_success = _config_file;
    }

  // Copy cfg files to ~/.pekwm and then try loading global config
  if (!success)
    {
      copyConfigFiles();

      _config_file = string(SYSCONFDIR "/config");
      success = cfg.parse (_config_file, CfgParserSource::SOURCE_FILE);
      if (success)
        file_success = _config_file;
    }

  if (!success)
    {
      cerr << " *** WARNING: unable to load configuration files!" << endl;
      return;
    }

  // Update PEKWM_CONFIG_FILE environment if needed ( to reflect active file )
  char *cfg_env = getenv("PEKWM_CONFIG_FILE");
  if (!cfg_env || (strcmp(cfg_env, file_success.c_str()) != 0))
    setenv("PEKWM_CONFIG_FILE", file_success.c_str(), 1);

  string o_file_mouse; // temporary filepath for mouseconfig

  CfgParser::Entry *op_section, *op_value, *op_sub, *op_sub_2;

  // Get other config files dests.
  op_section = cfg.get_entry_root ()->find_section ("FILES");
  if (op_section)
    {
      op_section = op_section->get_section ();

      list<CfgParserKey*> o_key_list;

      o_key_list.push_back (new CfgParserKeyPath ("KEYS", _files_keys,
                                                  SYSCONFDIR "/keys"));
      o_key_list.push_back (new CfgParserKeyPath ("MOUSE", o_file_mouse,
                                                  SYSCONFDIR "/mouse"));
      o_key_list.push_back (new CfgParserKeyPath ("MENU", _files_menu,
                                                  SYSCONFDIR "/menu"));
      o_key_list.push_back (new CfgParserKeyPath ("START", _files_start,
                                                  SYSCONFDIR "/start"));
      o_key_list.push_back (new CfgParserKeyPath ("AUTOPROPS", _files_autoprops,
                                                  SYSCONFDIR "/autoproperties"));
      o_key_list.push_back (new CfgParserKeyPath ("THEME", _files_theme,
                                                  DATADIR "/pekwm/themes/default/theme"));

      op_section->parse_key_values (o_key_list.begin (), o_key_list.end ());

      for_each (o_key_list.begin (), o_key_list.end (),
                Util::Free<CfgParserKey*>());
    }

  // Parse moving / resizing options.
  op_section = cfg.get_entry_root ()->find_section ("MOVERESIZE");
  if (op_section)
    {
      op_section = op_section->get_section ();

      list<CfgParserKey*> o_key_list;
      o_key_list.push_back (new CfgParserKeyInt ("EDGEATTRACT",
                                                 _moveresize_edgeattract, 0, 0));
      o_key_list.push_back (new CfgParserKeyInt ("EDGERESIST",
                                                 _moveresize_edgeresist, 0, 0));
      o_key_list.push_back (new CfgParserKeyInt ("WINDOWATTRACT",
                                                 _moveresize_woattract, 0, 0));
      o_key_list.push_back (new CfgParserKeyInt ("WINDOWRESIST",
                                                 _moveresize_woresist, 0, 0));
      o_key_list.push_back (new CfgParserKeyBool ("OPAQUEMOVE",
                                                  _moveresize_opaquemove));
      o_key_list.push_back (new CfgParserKeyBool ("OPAQUERESIZE",
                                                  _moveresize_opaqueresize));

      op_section->parse_key_values (o_key_list.begin (), o_key_list.end ());

      for_each (o_key_list.begin (), o_key_list.end (),
                Util::Free<CfgParserKey*>());
    }

  // Screen, important stuff such as number of workspaces
  op_section = cfg.get_entry_root ()->find_section ("SCREEN");
  if (op_section)
    {
      op_section = op_section->get_section ();

      list<CfgParserKey*> o_key_list;
      o_key_list.push_back (new CfgParserKeyInt ("WORKSPACES",
                                                 _screen_workspaces, 4, 1));
      o_key_list.push_back (new CfgParserKeyInt ("PIXMAPCACHESIZE",
                                                 _screen_pixmap_cache_size));
      o_key_list.push_back (new CfgParserKeyInt ("EDGESIZE", _screen_edge_size, 0, 0));
      o_key_list.push_back (new CfgParserKeyInt ("DOUBLECLICKTIME",
                                                 _screen_doubleclicktime, 250, 0));
      o_key_list.push_back (new CfgParserKeyString ("TRIMTITLE",
                                                    _screen_trim_title));
      o_key_list.push_back (new CfgParserKeyBool ("SHOWFRAMELIST",
                                                  _screen_showframelist));
      o_key_list.push_back (new CfgParserKeyBool ("SHOWSTATUSWINDOW",
                                                  _screen_show_status_window));

      o_key_list.push_back (new CfgParserKeyBool ("PLACENEW", _screen_place_new));
      o_key_list.push_back (new CfgParserKeyBool ("FOCUSNEW", _screen_focus_new));
      o_key_list.push_back (new CfgParserKeyBool ("FOCUSNEWCHILD", _screen_focus_new_child, true));

      op_section->parse_key_values (o_key_list.begin (), o_key_list.end ());

      for_each (o_key_list.begin (), o_key_list.end (),
                Util::Free<CfgParserKey*>());

      op_sub = op_section->find_section ("VIEWPORTS");
      if (op_sub)
        {
          op_sub = op_sub->get_section ();

          list<CfgParserKey*> o_key_list;
          o_key_list.push_back (new CfgParserKeyInt ("COLUMNS", _viewport_cols,
                                                     1, 1));
          o_key_list.push_back (new CfgParserKeyInt ("ROWS", _viewport_rows,
                                                     1, 1));

          op_sub->parse_key_values (o_key_list.begin (), o_key_list.end ());

          for_each (o_key_list.begin (), o_key_list.end (),
                    Util::Free<CfgParserKey*>());
        }

      op_sub = op_section->find_section ("PLACEMENT");
      if (op_sub)
        {
          op_sub = op_sub->get_section ();

          op_value = op_sub->find_entry ("MODEL");
          if (op_value)
            {
              _screen_placementmodels.clear ();

              vector<string> models;
              if (Util::splitString (op_value->get_value (), models, " \t"))
                {
                   vector<string>::iterator it( models.begin());
                   for (; it != models.end(); ++it)
                     _screen_placementmodels.push_back(ParseUtil::getValue<PlacementModel> (*it, _placement_map));
                }
            }

          op_sub_2 = op_sub->find_section ("SMART");
          if (op_sub_2)
            {
              op_sub_2 = op_sub_2->get_section ();

              list<CfgParserKey*> o_key_list;
              o_key_list.push_back (new CfgParserKeyBool ("ROW",
                                                          _screen_placement_row));
              o_key_list.push_back (new CfgParserKeyBool ("LEFTTORIGHT",
                                                          _screen_placement_ltr));
              o_key_list.push_back (new CfgParserKeyBool ("TOPTOBOTTOM",
                                                          _screen_placement_ttb));
              o_key_list.push_back (new CfgParserKeyInt ("OFFSETX",
                                                          _screen_placement_offset_x, 0, 0));
              o_key_list.push_back (new CfgParserKeyInt ("OFFSETY",
                                                          _screen_placement_offset_y, 0, 0));

              // Do the parsing
              op_sub_2->parse_key_values (o_key_list.begin (),
                                          o_key_list.end ());

              for_each (o_key_list.begin (), o_key_list.end (),
                        Util::Free<CfgParserKey*>());
            }
        }

      if (!_screen_placementmodels.size())
        _screen_placementmodels.push_back(PLACE_MOUSE_CENTERED);

      op_sub = op_section->find_section ("UNIQUENAMES");
      if (op_sub)
        {
          op_sub = op_sub->get_section ();

          list<CfgParserKey*> o_key_list;
          o_key_list.push_back (new CfgParserKeyBool ("SETUNIQUE",
                                                  _screen_client_unique_name));
          o_key_list.push_back (new CfgParserKeyString ("PRE",
                                              _screen_client_unique_name_pre));
          o_key_list.push_back (new CfgParserKeyString ("POST",
                                             _screen_client_unique_name_post));

          op_sub->parse_key_values (o_key_list.begin (), o_key_list.end ());

          for_each (o_key_list.begin (), o_key_list.end (),
                    Util::Free<CfgParserKey*>());
        }
    }

  op_section = cfg.get_entry_root ()->find_section ("MENU");
  if (op_section)
    {
      op_section = op_section->get_section ();

      list<CfgParserKey*> o_key_list;
      string o_value_select, o_value_enter, o_value_exec;

      o_key_list.push_back (new CfgParserKeyString ("SELECT", o_value_select,
                                                    "MOTION", 0));
      o_key_list.push_back (new CfgParserKeyString ("ENTER", o_value_enter,
                                                    "BUTTONPRESS", 0));
      o_key_list.push_back (new CfgParserKeyString ("EXEC", o_value_exec,
                                                    "BUTTONRELEASE", 0));

      op_section->parse_key_values (o_key_list.begin (), o_key_list. end());

      _menu_select_mask = getMenuMask (o_value_select);
      _menu_enter_mask = getMenuMask (o_value_enter);
      _menu_exec_mask = getMenuMask (o_value_exec);

     for_each (o_key_list.begin (), o_key_list.end (),
               Util::Free<CfgParserKey*>());
    }

#ifdef HARBOUR
  op_section = cfg.get_entry_root ()->find_section ("HARBOUR");
  if (op_section)
    {
      op_section = op_section->get_section ();

      list<CfgParserKey*> o_key_list;
      string o_value_placement, o_value_orientation;

      o_key_list.push_back (new CfgParserKeyBool ("ONTOP", _harbour_ontop));
      o_key_list.push_back (new CfgParserKeyBool ("MAXIMIZEOVER",
                                                  _harbour_maximize_over));
#ifdef HAVE_XINERAMA
      o_key_list.push_back (new CfgParserKeyInt ("HEAD", _harbour_head_nr,
                                                 0, 0));
#endif // HAVE_XINERAMA
      o_key_list.push_back (new CfgParserKeyString ("PLACEMENT", o_value_placement,
                                                    "RIGHT", 0));
      o_key_list.push_back (new CfgParserKeyString ("ORIENTATION",
                                                    o_value_orientation,
                                                    "TOPTOBOTTOM", 0));

      op_section->parse_key_values (o_key_list.begin (), o_key_list.end ());

      for_each (o_key_list.begin (), o_key_list.end (),
                Util::Free<CfgParserKey*>());

     _harbour_placement =
       ParseUtil::getValue<HarbourPlacement>(o_value_placement,
                                             _harbour_placement_map);
     _harbour_orientation =
       ParseUtil::getValue<Orientation>(o_value_orientation,
                                        _harbour_orientation_map);
     if (_harbour_placement == NO_HARBOUR_PLACEMENT)
       _harbour_placement = TOP;
     if (_harbour_orientation == NO_ORIENTATION)
       _harbour_orientation = TOP_TO_BOTTOM;

     op_sub = op_section->find_section ("DOCKAPP");
     if (op_sub)
       {
         op_sub = op_sub->get_section ();

         list<CfgParserKey*> o_key_list;
         o_key_list.push_back (new CfgParserKeyInt ("SIDEMIN",
                                                    _harbour_da_min_s, 64, 0));
         o_key_list.push_back (new CfgParserKeyInt ("SIDEMAX",
                                                     _harbour_da_max_s, 64, 0));

         op_sub->parse_key_values (o_key_list.begin (), o_key_list.end ());

         for_each (o_key_list.begin (), o_key_list.end (),
                   Util::Free<CfgParserKey*>());
       }
    }
#endif // HARBOUR

  // Load the mouse configuration
  loadMouseConfig(o_file_mouse);
}

//! @brief
ActionType
Config::getAction(const std::string &name, uint mask)
{
	pair<ActionType, uint> val(ParseUtil::getValue<pair<ActionType, uint> >(name, _action_map));

	if ((val.first != ACTION_NO) && (val.second&mask)) {
		return val.first;
	}
	return ACTION_NO;
}

//! @brief
bool
Config::parseKey(const std::string &key_string, uint &mod, uint &key)
{
	// used for parsing
	static vector<string> tok;
	static vector<string>::iterator it;

	static uint num;

	// chop the string up separating mods and the end key/button
	tok.clear();
	if (Util::splitString(key_string, tok, " \t")) {
		num = tok.size() - 1;
		if ((tok[num].size() > 1) && (tok[num][0] == '#')) {
			key = strtol(tok[num].c_str() + 1, NULL, 10);
		} else {
			key = XKeysymToKeycode(PScreen::instance()->getDpy(),
														 XStringToKeysym(tok[num].c_str()));
			if (strcasecmp(tok[num].c_str(), "ANY") == 0) {
				key = 0; // FIXME: for now, but there's no XK_Any?
			}
		}

		// if the last token isn't an key/button, the action isn't valid
		if ((key != 0) || (strcasecmp(tok[num].c_str(), "ANY") == 0)) {
			tok.pop_back(); // remove the key/button

			// add the modifier
			mod = 0;
			for (it = tok.begin(); it != tok.end(); ++it) {
				mod |= getMod(*it);
			}

			return true;
		}
	}

	return false;
}

bool
Config::parseButton(const std::string &button_string, uint &mod, uint &button)
{
	// used for parsing
	static vector<string> tok;
	static vector<string>::iterator it;

	// chop the string up separating mods and the end key/button
	tok.clear();
	if (Util::splitString(button_string, tok, " \t")) {
		// if the last token isn't an key/button, the action isn't valid
		button = getMouseButton(tok[tok.size() - 1]);
		if (button != BUTTON_NO) {
			tok.pop_back(); // remove the key/button

			// add the modifier
			mod = 0;
			uint tmp_mod;

			for (it = tok.begin(); it != tok.end(); ++it) {
				tmp_mod = getMod(*it);
				if (tmp_mod == MOD_ANY) {
					mod = MOD_ANY;
					break;
				} else {
					mod |= tmp_mod;
				}
			}

			return true;
		}
	}

	return false;
}

bool
Config::parseAction(const std::string &action_string, Action &action, uint mask)
{
	static vector<string> tok;

	// chop the string up separating the action and parameters
	tok.clear();
	if (Util::splitString(action_string, tok, " \t", 2)) {
		action.setAction(getAction(tok[0], mask));
		if (action.getAction() != ACTION_NO) {
			if (tok.size() == 2) { // we got enough tok for a parameter
				switch (action.getAction()) {
				case ACTION_EXEC:
				case ACTION_RESTART_OTHER:
				case ACTION_FIND_CLIENT:
#ifdef MENUS
				case ACTION_MENU_DYN:
#endif // MENUS
					action.setParamS(tok[1]);
					break;
				case ACTION_ACTIVATE_CLIENT_REL:
				case ACTION_MOVE_CLIENT_REL:
					action.setParamI(0, strtol(tok[1].c_str(), NULL, 10));
					break;
				case ACTION_SET:
				case ACTION_UNSET:
				case ACTION_TOGGLE:
					parseActionState(action, tok[1]);
					break;
				case ACTION_MAXFILL:
					if ((Util::splitString(tok[1], tok, " \t", 2)) == 2) {
						action.setParamI(0, Util::isTrue(tok[tok.size() - 2]));
						action.setParamI(1, Util::isTrue(tok[tok.size() - 1]));
					} else {
						cerr << "*** WARNING: Missing argument to MaxFill." << endl;
					}
					break;
				case ACTION_GROW_DIRECTION:
					action.setParamI(0, ParseUtil::getValue<DirectionType>(tok[1], _direction_map));
					break;
				case ACTION_ACTIVATE_CLIENT_NUM:
					action.setParamI(0, strtol(tok[1].c_str(), NULL, 10) - 1);
					if (action.getParamI(0) < 0) {
						cerr << "*** WARNING: Negative number to ActivateClientNum." << endl;
						action.setParamI(0, 0);
					}
					break;
				case ACTION_WARP_TO_WORKSPACE:
				case ACTION_SEND_TO_WORKSPACE:
				case ACTION_GOTO_WORKSPACE:
					action.setParamI(0, ParseUtil::getValue<WorkspaceChangeType>(tok[1], _workspace_change_map));
					if (static_cast<uint>(action.getParamI(0)) == WORKSPACE_NO) {
						action.setParamI(0, strtol(tok[1].c_str(), NULL, 10) - 1);
						if (action.getParamI(0) < 0) {
							action.setParamI(0, 0);
						}
					}
					break;
				case ACTION_GROUPING_DRAG:
					action.setParamI(0, Util::isTrue(tok[1]));
					break;
				case ACTION_MOVE_TO_EDGE:
					action.setParamI(0, ParseUtil::getValue<OrientationType>(tok[1], _edge_map));
					break;
				case ACTION_NEXT_FRAME:
				case ACTION_NEXT_FRAME_MRU:
				case ACTION_PREV_FRAME:
				case ACTION_PREV_FRAME_MRU:
					action.setParamI(0, ParseUtil::getValue<Raise>(tok[1], _raise_map));
					break;
				case ACTION_FOCUS_DIRECTIONAL:
					if ((Util::splitString(tok[1], tok, " \t", 2)) == 2) {
						action.setParamI(0, ParseUtil::getValue<DirectionType>(tok[tok.size() - 2], _direction_map));
						action.setParamI(1, Util::isTrue(tok[tok.size() - 1])); // raise?
					} else {
						action.setParamI(0, ParseUtil::getValue<DirectionType>(tok[1], _direction_map));
						action.setParamI(1, true); // default to raise
					}
					break;
				case ACTION_RESIZE:
					action.setParamI(0, ParseUtil::getValue<BorderPosition>(tok[1], _borderpos_map));
					break;
				case ACTION_WARP_TO_VIEWPORT:
				case ACTION_VIEWPORT_MOVE_DIRECTION:
					action.setParamI(0, ParseUtil::getValue<DirectionType>(tok[1], _direction_map));
					break;
				case ACTION_VIEWPORT_SCROLL:
				case ACTION_VIEWPORT_MOVE_XY:
					if ((Util::splitString(tok[1], tok, " \t", 2)) == 2) {
						action.setParamI(0, strtol(tok[tok.size() - 2].c_str(), NULL, 10));
						action.setParamI(1, strtol(tok[tok.size() - 1].c_str(), NULL, 10));
					}
					break;
				case ACTION_VIEWPORT_GOTO:
					if ((Util::splitString(tok[1], tok, " \t", 2)) == 2) {
						action.setParamI(0, strtol(tok[tok.size() - 2].c_str(), NULL, 10) - 1);
						action.setParamI(1, strtol(tok[tok.size() - 1].c_str(), NULL, 10) - 1);
					}
					break;
				case ACTION_RAISE:
				case ACTION_LOWER:
					if ((Util::splitString(tok[1], tok, " \t", 1)) == 1) {
						action.setParamI(0, Util::isTrue(tok[tok.size() - 1]));
					} else {
						action.setParamI(0, false);
					}
					break;
#ifdef MENUS
				case ACTION_SHOW_MENU:
					if ((Util::splitString(tok[1], tok, " \t", 2)) == 2) {
						action.setParamI(0, ParseUtil::getValue<MenuType>(tok[tok.size() - 2], _menu_type_map));
						action.setParamI(1, Util::isTrue(tok[tok.size() - 1]));
					} else {
						action.setParamI(0, ParseUtil::getValue<MenuType>(tok[1], _menu_type_map));
						action.setParamI(1, false); // Default to non-sticky
					}
					break;
#endif // MENUS
				default:
					// do nothing
					break;
				}
			} else {
				switch (action.getAction()) {
				case ACTION_MAXFILL:
					action.setParamI(0, 1);
					action.setParamI(1, 1);
					break;
				default:
					// do nothing
					break;
				}
			}

			return true;
		}
	}

	return false;
}

//! @brief
bool
Config::parseActionState(Action &action, const std::string &as_action)
{
	static vector<string> tok;

	// chop the string up separating the action and parameters
	tok.clear();
	if (Util::splitString(as_action, tok, " \t", 2)) {
		action.setParamI(0, ParseUtil::getValue<ActionStateType>(tok[0], _action_state_map));
		if (action.getParamI(0) != ACTION_STATE_NO) {
			if (tok.size() == 2) { // we got enough tok for a parameter
				switch (action.getParamI(0)) {
				case ACTION_STATE_MAXIMIZED:
					Util::splitString(tok[1], tok, " \t", 2);
					if (tok.size() == 4) {
						action.setParamI(1, Util::isTrue(tok[2]));
						action.setParamI(2, Util::isTrue(tok[3]));
					} else {
						cerr << "*** WARNING: Missing argument to Maximized." << endl;
					}
					break;
				case ACTION_STATE_TAGGED:
					action.setParamI(1, Util::isTrue(tok[1]));
					break;
				case ACTION_STATE_SKIP:
					action.setParamI(1, getSkip(tok[1]));
					break;
				case ACTION_STATE_CFG_DENY:
					action.setParamI(1, getCfgDeny(tok[1]));
					break;
				case ACTION_STATE_DECOR:
				case ACTION_STATE_TITLE:
					action.setParamS(tok[1]);
					break;
				};
			} else {
				switch (action.getParamI(0)) {
				case ACTION_STATE_MAXIMIZED:
					action.setParamI(1, 1);
					action.setParamI(2, 1);
					break;
				default:
					// do nothing
					break;
				}
			}

			return true;
		}
	}

	return false;
}

//! @brief
bool
Config::parseActions(const std::string &action_string, ActionEvent &ae, uint mask)
{
	static vector<string> tok;
	static vector<string>::iterator it;
	static Action action;

	// reset the action event
	ae.action_list.clear();

	// chop the string up separating the actions
	tok.clear();
	if (Util::splitString(action_string, tok, ";")) {
		for (it = tok.begin(); it != tok.end(); ++it) {
			if (parseAction(*it, action, mask)) {
				ae.action_list.push_back(action);
				action.setParamS("");
			}
		}

		return true;
	}

	return false;
}

//! @brief
bool
Config::parseActionEvent(CfgParser::Entry *op_section, ActionEvent &ae,
                         uint mask, bool button)
{
  CfgParser::Entry *op_value;

  string o_button = op_section->get_value ();
  if (!o_button.size())
    {
      if ((ae.type == MOUSE_EVENT_ENTER) || (ae.type == MOUSE_EVENT_LEAVE))
        o_button = "1";
      else
        return false;
    }

  bool ok;
  if (button)
    ok = parseButton (o_button, ae.mod, ae.sym);
  else
    ok = parseKey (o_button, ae.mod, ae.sym);

  op_value = op_section->get_section ()->find_entry ("ACTIONS");
  if (ok && op_value)
    return parseActions (op_value->get_value (), ae, mask);
  return false;
}

//! @brief
bool
Config::parseMoveResizeAction(const std::string &action_string, Action &action)
{
  static vector<string> tok;

  // Chop the string up separating the actions.
  tok.clear();
  if (Util::splitString(action_string, tok, " \t", 2))
    {
      action.setAction(ParseUtil::getValue<MoveResizeActionType>(tok[0],
                                                             _moveresize_map));
      if (action.getAction() != NO_MOVERESIZE_ACTION)
        {
          if (tok.size() == 2) { // we got enough tok for a paremeter
            switch (action.getAction())
              {
              case MOVE_HORIZONTAL:
              case MOVE_VERTICAL:
              case RESIZE_HORIZONTAL:
              case RESIZE_VERTICAL:
              case MOVE_SNAP:
                action.setParamI(0, strtol(tok[1].c_str(), NULL, 10));
                break;
              default:
                // Do nothing.
                break;
              }
            }

          return true;
        }
    }

  return false;
}

//! @brief
bool
Config::parseMoveResizeActions(const std::string &action_string, ActionEvent& ae)
{
	static vector<string> tok;
	static vector<string>::iterator it;
	static Action action;

	// reset the action event
	ae.action_list.clear();

	// chop the string up separating the actions
	tok.clear();
	if (Util::splitString(action_string, tok, ";")) {
		for (it = tok.begin(); it != tok.end(); ++it) {
			if (parseMoveResizeAction(*it, action)) {
				ae.action_list.push_back(action);
				action.setParamS("");
			}
		}

		return true;
	}

	return false;
}

//! @brief Parses MoveResize Event.
bool
Config::parseMoveResizeEvent(CfgParser::Entry *op_section, ActionEvent& ae)
{
  CfgParser::Entry *op_value;

  if (!op_section->get_value ().size ())
    return false;

  if (parseKey (op_section->get_value (), ae.mod, ae.sym))
    {
      op_value = op_section->get_section ()->find_entry ("ACTIONS");
      if (op_value)
        return parseMoveResizeActions (op_value->get_value (), ae);
    }

  return false;
}

//! @brief
bool
Config::parseCmdDialogAction(const std::string &val, Action &action)
{
	action.setAction(ParseUtil::getValue<CmdDialogAction>(val, _cmddialog_map));
	return (action.getAction() != CMD_D_NO_ACTION);
}

//! @brief
bool
Config::parseCmdDialogActions(const std::string &actions, ActionEvent &ae)
{
	static vector<string> tok;
	static vector<string>::iterator it;
	static Action action;

	// reset the action event
	ae.action_list.clear();

	// chop the string up separating the actions
	tok.clear();
	if (Util::splitString(actions, tok, ";")) {
		for (it = tok.begin(); it != tok.end(); ++it) {
			if (parseCmdDialogAction(*it, action)) {
				ae.action_list.push_back(action);
			}
		}

		return true;
	}

	return false;

}

//! @brief Parses CmdDialog Event.
bool
Config::parseCmdDialogEvent (CfgParser::Entry *op_section, ActionEvent &ae)
{
  CfgParser::Entry *op_value;

  if (!op_section->get_value ().size ())
    return false;

  if (parseKey (op_section->get_value (), ae.mod, ae.sym))
    {
      op_value = op_section->get_section ()->find_entry ("ACTIONS");
      if (op_value)
        return parseCmdDialogActions(op_value->get_value (), ae);
    }

  return false;
}

//! @brief
uint
Config::getMenuMask (const std::string &mask)
{
  uint mask_return = 0, val;

  vector<string> tok;
  Util::splitString (mask, tok, " \t");

  vector<string>::iterator it (tok.begin ());
  for (; it != tok.end(); ++it)
    {
      val = ParseUtil::getValue<MouseEventType> (*it, _mouse_event_map);
      if (val < MOUSE_EVENT_ENTER)
        mask_return |= val;
    }
  return mask_return;
}

#ifdef MENUS
//! @brief
bool
Config::parseMenuAction(const std::string &action_string, Action &action)
{
	static vector<string> tok;

	// chop the string up separating the actions
	tok.clear();
	if (Util::splitString(action_string, tok, " \t", 2)) {
		action.setAction(ParseUtil::getValue<ActionType>(tok[0], _menu_action_map));
		if (action.getAction() != ACTION_NO) {
			return true;
		}
	}

	return false;
}

//! @brief
bool
Config::parseMenuActions(const std::string &actions, ActionEvent &ae)
{
	static vector<string> tok;
	static vector<string>::iterator it;
	static Action action;

	// reset the action event
	ae.action_list.clear();

	// chop the string up separating the actions
	tok.clear();
	if (Util::splitString(actions, tok, ";")) {
		for (it = tok.begin(); it != tok.end(); ++it) {
			if (parseMenuAction(*it, action)) {
				ae.action_list.push_back(action);
				action.setParamS("");
			}
		}

		return true;
	}

	return false;
}

//! @brief Parses MenuEvent.
bool
Config::parseMenuEvent (CfgParser::Entry *op_section, ActionEvent& ae)
{
  CfgParser::Entry *op_value;

  if (!op_section->get_value ().size ())
    return false;

  if (parseKey (op_section->get_value (), ae.mod, ae.sym))
    {
      op_value = op_section->get_section ()->find_entry ("ACTIONS");
      if (op_value)
        return parseMenuActions (op_value->get_value (), ae);
    }

  return false;
}
#endif // MENUS

//! @brief
uint
Config::getMouseButton(const std::string &button)
{
	uint btn;

	if (button.size() == 1) { // it's a button
		btn = unsigned(strtol(button.c_str(), NULL, 10));
	} else if (strcasecmp(button.c_str(), "ANY") == 0) { // any button
		btn = BUTTON_ANY;
	} else {
		btn = BUTTON_NO;
	}

	if (btn > BUTTON_NO) {
		btn = BUTTON_NO;
	}

	return btn;
}

//! @brief Populates the ~/.pekwm/ dir with config files
void
Config::copyConfigFiles(void)
{
	string cfg_dir = getenv("HOME") + string("/.pekwm");

	string cfg_file = cfg_dir + string("/config");
	string keys_file = cfg_dir + string("/keys");
	string mouse_file = cfg_dir + string("/mouse");
	string menu_file = cfg_dir + string("/menu");
	string autoprops_file = cfg_dir + string("/autoproperties");
	string start_file = cfg_dir + string("/start");
	string vars_file = cfg_dir + string("/vars");

	bool cp_config, cp_keys, cp_mouse, cp_menu;
	bool cp_autoprops, cp_start, cp_vars;
	cp_config = cp_keys = cp_mouse = cp_menu = false;
	cp_autoprops = cp_start = cp_vars = false;

	struct stat stat_buf;
	// check and see if we allready have a ~/.pekwm/ directory
	if (stat(cfg_dir.c_str(), &stat_buf) == 0) {
		// is it a dir or file?
		if (!S_ISDIR(stat_buf.st_mode)) {
			cerr << cfg_dir << " allready exists and isn't a directory" << endl
					 << "Can't copy config files !" << endl;
			return;
		}

		// we allready have a directory, see if it's writeable and executable
		bool cfg_dir_ok = false;

		if (getuid() == stat_buf.st_uid) {
			if ((stat_buf.st_mode&S_IWUSR) && (stat_buf.st_mode&(S_IXUSR))) {
				cfg_dir_ok = true;
			}
		}
		if (! cfg_dir_ok) {
			if (getgid() == stat_buf.st_gid) {
				if ((stat_buf.st_mode&S_IWGRP) && (stat_buf.st_mode&(S_IXGRP))) {
					cfg_dir_ok = true;
				}
			}
		}

		if (!cfg_dir_ok) {
			if (!(stat_buf.st_mode&S_IWOTH) || !(stat_buf.st_mode&(S_IXOTH))) {
				cerr << "You don't have the rights to add files to the: " << cfg_dir
						 << " directory! Therefor I can't copy the config files!" << endl;
				return;
			}
		}

		// we apperently could write and exec that dir, now see if we have any
		// files in it
		if (stat(cfg_file.c_str(), &stat_buf))
			cp_config = true;
		if (stat(keys_file.c_str(), &stat_buf))
			cp_keys = true;
		if (stat(mouse_file.c_str(), &stat_buf))
			cp_mouse = true;
		if (stat(menu_file.c_str(), &stat_buf))
			cp_menu = true;
		if (stat(autoprops_file.c_str(), &stat_buf))
			cp_autoprops = true;
		if (stat(start_file.c_str(), &stat_buf))
			cp_start = true;
		if (stat(vars_file.c_str(), &stat_buf))
			cp_vars = true;

	} else { // we didn't have a ~/.pekwm directory allready, lets create one
		if (mkdir(cfg_dir.c_str(), 0700)) {
			cerr << "Can't create " << cfg_dir << " directory!" << endl;
			cerr << "Can't copy config files !" << endl;
			return;
		}

		cp_config = cp_keys = cp_mouse = cp_menu = true;
		cp_autoprops = cp_start = cp_vars = true;
	}

	if (cp_config)
		copyTextFile(SYSCONFDIR "/config", cfg_file);
	if (cp_keys)
		copyTextFile(SYSCONFDIR "/keys", keys_file);
	if (cp_mouse)
		copyTextFile(SYSCONFDIR "/mouse", mouse_file);
	if (cp_menu)
		copyTextFile(SYSCONFDIR "/menu", menu_file);
	if (cp_autoprops)
		copyTextFile(SYSCONFDIR "/autoproperties", autoprops_file);
	if (cp_start)
		copyTextFile(SYSCONFDIR "/start", start_file);
	if (cp_vars)
		copyTextFile(SYSCONFDIR "/vars", vars_file);
}

//! @brief Copies a single text file.
void
Config::copyTextFile(const std::string &from, const std::string &to)
{
	if ((from.length() == 0) || (to.length() == 0)) {
		return;
	}

	ifstream stream_from(from.c_str());
	if (stream_from.good() == false) {
		cerr << __FILE__ << "@" << __LINE__ << ": "
				 << "Can't copy: " << from << " to: " << to << endl
				 << "Shutting down" << endl;
		exit(1);
	}

	ofstream stream_to(to.c_str());
	if (stream_to.good() == false) {
		cerr << __FILE__ << "@" << __LINE__ << ": "
				 << "Can't copy: " << from << " to: " << to << endl;
	}

	stream_to << stream_from.rdbuf();
}

//! @brief Parses the Section for mouse actions.
void
Config::loadMouseConfig(const std::string &file)
{
  if (!file.size())
    return;

  CfgParser mouse_cfg;

  bool success = mouse_cfg.parse (file, CfgParserSource::SOURCE_FILE);
  if (!success)
    success = mouse_cfg.parse (string(SYSCONFDIR "/mouse"),
                               CfgParserSource::SOURCE_FILE);

  if (!success)
    return;

  // Make sure old actions get unloaded.
  map<MouseActionListName, list<ActionEvent>* >::iterator it;
  for (it = _mouse_action_map.begin (); it != _mouse_action_map.end (); ++it)
    it->second->clear ();

  CfgParser::Entry *op_section, *op_sub;

  op_section = mouse_cfg.get_entry_root ()->find_section ("FRAMETITLE");
  if (op_section)
    {
      parseButtons (op_section,
                    _mouse_action_map[MOUSE_ACTION_LIST_TITLE_FRAME], FRAME_OK);
    }
  op_section = mouse_cfg.get_entry_root ()->find_section ("OTHERTITLE");
  if (op_section)
    {
      parseButtons(op_section,
                   _mouse_action_map[MOUSE_ACTION_LIST_TITLE_OTHER], FRAME_OK);
    }
  op_section = mouse_cfg.get_entry_root ()->find_section ("CLIENT");
  if (op_section)
    {
      parseButtons (op_section,
                    _mouse_action_map[MOUSE_ACTION_LIST_CHILD_FRAME], CLIENT_OK);
    }
  op_section = mouse_cfg.get_entry_root ()->find_section ("ROOT");
  if (op_section)
    {
      parseButtons(op_section,
                   _mouse_action_map[MOUSE_ACTION_LIST_ROOT], ROOTCLICK_OK);
    }
  op_section = mouse_cfg.get_entry_root ()->find_section ("MENU");
  if (op_section)
    {
      parseButtons (op_section,
                    _mouse_action_map[MOUSE_ACTION_LIST_MENU], FRAME_OK);
    }
  op_section = mouse_cfg.get_entry_root ()->find_section ("OTHER");
  if (op_section)
    {
      parseButtons (op_section,
                    _mouse_action_map[MOUSE_ACTION_LIST_OTHER], FRAME_OK);
    }
  op_section = mouse_cfg.get_entry_root ()->find_section ("SCREENEDGE");
  if (op_section)
    {
      op_section = op_section->get_section ();
      uint pos;
      for (op_sub = op_section->get_section_next (); op_sub != NULL; op_sub = op_sub->get_section_next ())
        {
          pos = ParseUtil::getValue<DirectionType>(op_sub->get_name (),
                                                   _direction_map);

          if (pos != SCREEN_EDGE_NO)
            parseButtons (op_sub, getEdgeListFromPosition (pos), SCREEN_EDGE_OK);
	}
    }
  op_section = mouse_cfg.get_entry_root ()->find_section ("BORDER");
  if (op_section)
    {
      op_section = op_section->get_section ();
      uint pos;
      for (op_sub = op_section->get_section_next (); op_sub; op_sub = op_sub->get_section_next ())
        {
          pos = ParseUtil::getValue<BorderPosition>(op_sub->get_name(),
                                                    _borderpos_map);
          if (pos != BORDER_NO_POS)
            parseButtons(op_sub, getBorderListFromPosition (pos), FRAME_BORDER_OK);
        }
    }
}

//! @brief Parses mouse config section, like FRAME
void
Config::parseButtons(CfgParser::Entry *op_section,
                     std::list<ActionEvent>* mouse_list, ActionOk action_ok)
{
  if (!op_section || !mouse_list)
    return;
  op_section = op_section->get_section ();

  ActionEvent ae;
  CfgParser::Entry *op_value;

  while ((op_section = op_section->get_section_next ()) != NULL)
    {
      ae.type = ParseUtil::getValue<MouseEventType>(op_section->get_name (),
                                                    _mouse_event_map);

      if (ae.type == MOUSE_EVENT_NO)
        continue;

      if (ae.type == MOUSE_EVENT_MOTION)
        {
          op_value = op_section->get_section ()->find_entry ("THRESHOLD");
          if (op_value)
            ae.threshold = strtol (op_value->get_value ().c_str (), NULL, 10);
          else
            ae.threshold = 0;
        }

        if (parseActionEvent (op_section, ae, action_ok, true))
          mouse_list->push_back(ae);
    }
}

// frame border configuration

list<ActionEvent>*
Config::getBorderListFromPosition(uint pos)
{
	list<ActionEvent> *ret = NULL;

	switch (pos) {
	case BORDER_TOP_LEFT:
		ret = _mouse_action_map[MOUSE_ACTION_LIST_BORDER_TL];
		break;
	case BORDER_TOP:
		ret = _mouse_action_map[MOUSE_ACTION_LIST_BORDER_T];
		break;
	case BORDER_TOP_RIGHT:
		ret = _mouse_action_map[MOUSE_ACTION_LIST_BORDER_TR];
		break;
	case BORDER_LEFT:
		ret = _mouse_action_map[MOUSE_ACTION_LIST_BORDER_L];
		break;
	case BORDER_RIGHT:
		ret = _mouse_action_map[MOUSE_ACTION_LIST_BORDER_R];
		break;
	case BORDER_BOTTOM_LEFT:
		ret = _mouse_action_map[MOUSE_ACTION_LIST_BORDER_BL];
		break;
	case BORDER_BOTTOM:
		ret = _mouse_action_map[MOUSE_ACTION_LIST_BORDER_B];
		break;
	case BORDER_BOTTOM_RIGHT:
		ret = _mouse_action_map[MOUSE_ACTION_LIST_BORDER_BR];
		break;
	}

	return ret;
}

list<ActionEvent>*
Config::getEdgeListFromPosition(uint pos)
{
	list<ActionEvent> *ret = NULL;

	switch (pos) {
	case SCREEN_EDGE_TOP:
		ret = _mouse_action_map[MOUSE_ACTION_LIST_EDGE_T];
		break;
	case SCREEN_EDGE_BOTTOM:
		ret = _mouse_action_map[MOUSE_ACTION_LIST_EDGE_B];
		break;
	case SCREEN_EDGE_LEFT:
		ret = _mouse_action_map[MOUSE_ACTION_LIST_EDGE_L];
		break;
	case SCREEN_EDGE_RIGHT:
		ret = _mouse_action_map[MOUSE_ACTION_LIST_EDGE_R];
		break;
	};

	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1