ede/pekwm/Config.cc

1883 lines
66 KiB
C++

//
// Config.cc for pekwm
// Copyright © 2002-2009 Claes Nästén <me@pekdon.net>
//
// This program is licensed under the GNU GPL.
// See the LICENSE file for more information.
//
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H
#include "Config.hh"
#include "Compat.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::wstring;
using std::list;
using std::map;
using std::vector;
using std::pair;
using std::ifstream;
using std::ofstream;
using std::strtol;
using std::getenv;
Config* Config::_instance = 0;
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;
/**
* Parse width and height limits.
*/
bool
SizeLimits::parse(const std::string &minimum, const std::string &maximum)
{
return (parseLimit(minimum, _limits[WIDTH_MIN], _limits[HEIGHT_MIN])
&& parseLimit(maximum, _limits[WIDTH_MAX], _limits[HEIGHT_MAX]));
}
/**
* Parse single limit.
*/
bool
SizeLimits::parseLimit(const std::string &limit, unsigned int &min, unsigned int &max)
{
bool status = false;
vector<string> tokens;
if ((Util::splitString(limit, tokens, "x", 2, true)) == 2) {
min = strtol(tokens[0].c_str(), 0, 10);
max = strtol(tokens[1].c_str(), 0, 10);
status = true;
} else {
min = 0;
max = 0;
}
return status;
}
//! @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_workspaces_per_row(0), _screen_workspace_name_default(L"Workspace"),
_screen_edge_indent(false),
_screen_doubleclicktime(250), _screen_fullscreen_above(true),
_screen_fullscreen_detect(true),
_screen_showframelist(true),
_screen_show_status_window(true), _screen_show_status_window_on_root(false),
_screen_show_client_id(false),
_screen_show_workspace_indicator(500), _screen_workspace_indicator_scale(16),
#ifdef OPACITY
_screen_workspace_indicator_opacity(EWMH_OPAQUE_WINDOW),
#endif // OPACITY
_screen_place_new(true), _screen_focus_new(false),
_screen_focus_new_child(true), _screen_honour_randr(true),
_screen_honour_aspectratio(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(""),
_screen_report_all_clients(false),
_menu_select_mask(0), _menu_enter_mask(0), _menu_exec_mask(0),
_menu_display_icons(true),
#ifdef OPACITY
_menu_focus_opacity(EWMH_OPAQUE_WINDOW),
_menu_unfocus_opacity(EWMH_OPAQUE_WINDOW),
#endif // OPACITY
_cmd_dialog_history_unique(true), _cmd_dialog_history_size(1024),
_cmd_dialog_history_file("~/.pekwm/history"), _cmd_dialog_history_save_interval(16),
_harbour_da_min_s(0), _harbour_da_max_s(0),
_harbour_ontop(true), _harbour_maximize_over(false),
_harbour_placement(TOP), _harbour_orientation(TOP_TO_BOTTOM), _harbour_head_nr(0)
#ifdef OPACITY
,_harbour_opacity(EWMH_OPAQUE_WINDOW)
#endif // OPACITY
{
if (_instance) {
throw string("Config, trying to create multiple instances");
}
_instance = this;
for (uint i = 0; i <= SCREEN_EDGE_NO; ++i) {
_screen_edge_sizes.push_back(0);
}
// 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["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["FindClient"] = pair<ActionType, uint>(ACTION_FIND_CLIENT, ANY_MASK);
_action_map["GotoClientID"] = pair<ActionType, uint>(ACTION_GOTO_CLIENT_ID, 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);
_action_map["ShowSearchDialog"] = pair<ActionType, uint>(ACTION_SHOW_SEARCH_DIALOG, KEYGRABBER_OK|ROOTCLICK_OK|SCREEN_EDGE_OK|ROOTMENU_OK|WINDOWMENU_OK);
_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);
_action_map["SendKey"] = pair<ActionType, uint>(ACTION_SEND_KEY, ANY_MASK);
#ifdef OPACITY
_action_map["SetOpacity"] = pair<ActionType, uint>(ACTION_SET_OPACITY, FRAME_MASK);
#endif // OPACITY
_action_access_mask_map[""] = ACTION_ACCESS_NO;
_action_access_mask_map["MOVE"] = ACTION_ACCESS_MOVE;
_action_access_mask_map["RESIZE"] = ACTION_ACCESS_RESIZE;
_action_access_mask_map["ICONIFY"] = ACTION_ACCESS_MINIMIZE;
_action_access_mask_map["SHADE"] = ACTION_ACCESS_SHADE;
_action_access_mask_map["STICK"] = ACTION_ACCESS_STICK;
_action_access_mask_map["MAXIMIZEHORIZONTAL"] = ACTION_ACCESS_MAXIMIZE_HORZ;
_action_access_mask_map["MAXIMIZEVERTICAL"] = ACTION_ACCESS_MAXIMIZE_VERT;
_action_access_mask_map["FULLSCREEN"] = ACTION_ACCESS_FULLSCREEN;
_action_access_mask_map["SETWORKSPACE"] = ACTION_ACCESS_CHANGE_DESKTOP;
_action_access_mask_map["CLOSE"] = ACTION_ACCESS_CLOSE;
_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;
_skip_map["PAGER"] = SKIP_PAGER;
_skip_map["TASKBAR"] = SKIP_TASKBAR;
_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;
_inputdialog_map[""] = INPUT_NO_ACTION;
_inputdialog_map["INSERT"] = INPUT_INSERT;
_inputdialog_map["ERASE"] = INPUT_REMOVE;
_inputdialog_map["CLEAR"] = INPUT_CLEAR;
_inputdialog_map["CLEARFROMCURSOR"] = INPUT_CLEARFROMCURSOR;
_inputdialog_map["EXEC"] = INPUT_EXEC;
_inputdialog_map["CLOSE"] = INPUT_CLOSE;
_inputdialog_map["COMPLETE"] = INPUT_COMPLETE;
_inputdialog_map["COMPLETEABORT"] = INPUT_COMPLETE_ABORT;
_inputdialog_map["CURSNEXT"] = INPUT_CURS_NEXT;
_inputdialog_map["CURSPREV"] = INPUT_CURS_PREV;
_inputdialog_map["CURSEND"] = INPUT_CURS_END;
_inputdialog_map["CURSBEGIN"] = INPUT_CURS_BEGIN;
_inputdialog_map["HISTNEXT"] = INPUT_HIST_NEXT;
_inputdialog_map["HISTPREV"] = INPUT_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["PREVV"] = WORKSPACE_PREV_V;
_workspace_change_map["UP"] = WORKSPACE_UP;
_workspace_change_map["NEXTV"] = WORKSPACE_NEXT_V;
_workspace_change_map["DOWN"] = WORKSPACE_DOWN;
_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;
_mouse_event_map["MOTIONPRESSED"] = MOUSE_EVENT_MOTION_PRESSED;
_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;
#ifdef OPACITY
_action_state_map["Opaque"] = ACTION_STATE_OPAQUE;
#endif // OPACITY
_action_state_map["Title"] = ACTION_STATE_TITLE;
_action_state_map["HarbourHidden"] = ACTION_STATE_HARBOUR_HIDDEN;
_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;
_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;
_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;
// 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 = 0;
map<MouseActionListName, list<ActionEvent>* >::iterator it;
for (it = _mouse_action_map.begin(); it != _mouse_action_map.end(); ++it) {
delete it->second;
}
}
/**
* Returns an array of NULL-terminated desktop names in UTF-8.
*
* @param names *names will be set to an array of desktop names or 0. The caller has to delete [] *names
* @param length *length will be set to the complete length of array *names points to or 0.
*/
void
Config::getDesktopNamesUTF8(uchar **names, uint *length) const
{
if (! _screen_workspace_names.size()) {
*names = 0;
*length = 0;
return;
}
// Convert strings to UTF-8 and calculate total length
string utf8_names;
vector<wstring>::const_iterator it(_screen_workspace_names.begin());
for (; it != _screen_workspace_names.end(); ++it) {
string utf8_name(Util::to_utf8_str(*it));
utf8_names.append(utf8_name.c_str(), utf8_name.size() + 1);
}
*names = new uchar[utf8_names.size()];
::memcpy(*names, utf8_names.c_str(), utf8_names.size());
*length = utf8_names.size();
}
/**
* Sets the desktop names.
*
* @param names names is expected to point to an array of NULL-terminated utf8-strings.
* @param length The length of the array "names".
*/
void
Config::setDesktopNamesUTF8(char *names, ulong length)
{
_screen_workspace_names.clear();
if (! names || ! length) {
return;
}
for (ulong i = 0; i < length;) {
_screen_workspace_names.push_back(Util::from_utf8_str(names));
i += strlen(names) + 1;
names += strlen(names) + 1;
}
}
//! @brief Tries to load config_file, ~/.pekwm/config, SYSCONFDIR/config
bool
Config::load(const std::string &config_file)
{
if (! Util::requireReload(_cfg_state, config_file)) {
return false;
}
CfgParser cfg;
_config_file = config_file;
bool success = tryHardLoadConfig(cfg, _config_file);
// Make sure config is reloaded next time as content is dynamically
// generated from the configuration file.
if (! success || cfg.is_dynamic_content()) {
_cfg_state.clear();
} else {
_cfg_state = cfg.get_file_list();
}
if (! success) {
cerr << " *** WARNING: unable to load configuration files!" << endl;
return false;
}
// Update PEKWM_CONFIG_FILE environment if needed (to reflect active file)
char *cfg_env = getenv("PEKWM_CONFIG_FILE");
if (! cfg_env || (strcmp(cfg_env, _config_file.c_str()) != 0)) {
setenv("PEKWM_CONFIG_FILE", _config_file.c_str(), 1);
}
string o_file_mouse; // temporary filepath for mouseconfig
CfgParser::Entry *section;
// Get other config files dests.
section = cfg.get_entry_root()->find_section("FILES");
if (section) {
loadFiles(section);
}
// Parse moving / resizing options.
section = cfg.get_entry_root()->find_section("MOVERESIZE");
if (section) {
loadMoveResize(section);
}
// Screen, important stuff such as number of workspaces
section = cfg.get_entry_root()->find_section("SCREEN");
if (section) {
loadScreen(section);
}
section = cfg.get_entry_root()->find_section("MENU");
if (section) {
loadMenu(section);
}
section = cfg.get_entry_root()->find_section("CMDDIALOG");
if (section) {
loadCmdDialog(section);
}
section = cfg.get_entry_root()->find_section("HARBOUR");
if (section) {
loadHarbour(section);
}
return true;
}
//! @brief Loads file section of configuration
//! @param section Pointer to FILES section.
void
Config::loadFiles(CfgParser::Entry *section)
{
if (! section) {
return;
}
list<CfgParserKey*> key_list;
key_list.push_back(new CfgParserKeyPath("KEYS", _files_keys, SYSCONFDIR "/keys"));
key_list.push_back(new CfgParserKeyPath("MOUSE", _files_mouse, SYSCONFDIR "/mouse"));
key_list.push_back(new CfgParserKeyPath("MENU", _files_menu, SYSCONFDIR "/menu"));
key_list.push_back(new CfgParserKeyPath("START", _files_start, SYSCONFDIR "/start"));
key_list.push_back(new CfgParserKeyPath("AUTOPROPS", _files_autoprops, SYSCONFDIR "/autoproperties"));
key_list.push_back(new CfgParserKeyPath("THEME", _files_theme, DATADIR "/pekwm/themes/default/theme"));
key_list.push_back(new CfgParserKeyPath("ICONS", _files_icon_path, DATADIR "/pekwm/icons"));
// Parse
section->parse_key_values(key_list.begin(), key_list.end());
// Free up resources
for_each(key_list.begin(), key_list.end(), Util::Free<CfgParserKey*>());
}
//! @brief Loads MOVERESIZE section of main configuration
//! @param section Pointer to MOVERESIZE section.
void
Config::loadMoveResize(CfgParser::Entry *section)
{
if (! section) {
return;
}
list<CfgParserKey*> key_list;
key_list.push_back(new CfgParserKeyNumeric<int>("EDGEATTRACT", _moveresize_edgeattract, 0, 0));
key_list.push_back(new CfgParserKeyNumeric<int>("EDGERESIST", _moveresize_edgeresist, 0, 0));
key_list.push_back(new CfgParserKeyNumeric<int>("WINDOWATTRACT",_moveresize_woattract, 0, 0));
key_list.push_back(new CfgParserKeyNumeric<int>("WINDOWRESIST", _moveresize_woresist, 0, 0));
key_list.push_back(new CfgParserKeyBool("OPAQUEMOVE", _moveresize_opaquemove));
key_list.push_back(new CfgParserKeyBool("OPAQUERESIZE", _moveresize_opaqueresize));
// Parse data
section->parse_key_values(key_list.begin(), key_list.end());
// Free up resources
for_each(key_list.begin(), key_list.end(), Util::Free<CfgParserKey*>());
}
//! @brief Loads SCREEN section of main configuration
//! @param section Pointer to SCREEN section.
void
Config::loadScreen(CfgParser::Entry *section)
{
if (! section) {
return;
}
// Parse data
string edge_size, workspace_names, trim_title;
CfgParser::Entry *value;
list<CfgParserKey*> key_list;
key_list.push_back(new CfgParserKeyNumeric<int>("WORKSPACES", _screen_workspaces, 4, 1));
key_list.push_back(new CfgParserKeyNumeric<int>("PIXMAPCACHESIZE", _screen_pixmap_cache_size));
key_list.push_back(new CfgParserKeyNumeric<int>("WORKSPACESPERROW", _screen_workspaces_per_row, 0, 0));
key_list.push_back(new CfgParserKeyString("WORKSPACENAMES", workspace_names));
key_list.push_back(new CfgParserKeyString("EDGESIZE", edge_size));
key_list.push_back(new CfgParserKeyBool("EDGEINDENT", _screen_edge_indent));
key_list.push_back(new CfgParserKeyNumeric<int>("DOUBLECLICKTIME", _screen_doubleclicktime, 250, 0));
key_list.push_back(new CfgParserKeyString("TRIMTITLE", trim_title));
key_list.push_back(new CfgParserKeyBool("FULLSCREENABOVE", _screen_fullscreen_above, true));
key_list.push_back(new CfgParserKeyBool("FULLSCREENDETECT", _screen_fullscreen_detect, true));
key_list.push_back(new CfgParserKeyBool("SHOWFRAMELIST", _screen_showframelist));
key_list.push_back(new CfgParserKeyBool("SHOWSTATUSWINDOW", _screen_show_status_window));
key_list.push_back(new CfgParserKeyBool("SHOWSTATUSWINDOWCENTEREDONROOT", _screen_show_status_window_on_root, false));
key_list.push_back(new CfgParserKeyBool("SHOWCLIENTID", _screen_show_client_id));
key_list.push_back(new CfgParserKeyNumeric<int>("SHOWWORKSPACEINDICATOR",
_screen_show_workspace_indicator, 500, 0));
key_list.push_back(new CfgParserKeyNumeric<int>("WORKSPACEINDICATORSCALE",
_screen_workspace_indicator_scale, 16, 2));
#ifdef OPACITY
key_list.push_back(new CfgParserKeyNumeric<uint>("WORKSPACEINDICATOROPACITY",
_screen_workspace_indicator_opacity, 100, 0, 100));
#endif // OPACITY
key_list.push_back(new CfgParserKeyBool("PLACENEW", _screen_place_new));
key_list.push_back(new CfgParserKeyBool("FOCUSNEW", _screen_focus_new));
key_list.push_back(new CfgParserKeyBool("FOCUSNEWCHILD", _screen_focus_new_child, true));
key_list.push_back(new CfgParserKeyBool("HONOURRANDR", _screen_honour_randr, true));
key_list.push_back(new CfgParserKeyBool("HONOURASPECTRATIO", _screen_honour_aspectratio, true));
key_list.push_back(new CfgParserKeyBool("REPORTALLCLIENTS", _screen_report_all_clients, false));
// Parse data
section->parse_key_values(key_list.begin(), key_list.end());
// Free up resources
for_each(key_list.begin(), key_list.end(), Util::Free<CfgParserKey*>());
key_list.clear();
// Convert input data
_screen_trim_title = Util::to_wide_str(trim_title);
// Convert opacity from percent to absolute value
CONV_OPACITY(_screen_workspace_indicator_opacity);
int edge_size_all = 0;
_screen_edge_sizes.clear();
if (edge_size.size()) {
vector<string> sizes;
if (Util::splitString(edge_size, sizes, " \t", 4) == 4) {
for (vector<string>::iterator it(sizes.begin()); it != sizes.end(); ++it) {
_screen_edge_sizes.push_back(strtol(it->c_str(), 0, 10));
}
} else {
edge_size_all = strtol(edge_size.c_str(), 0, 10);
}
}
for (uint i = 0; i < SCREEN_EDGE_NO; ++i) {
_screen_edge_sizes.push_back(edge_size_all);
}
// Add SCREEN_EDGE_NO to the list for safety
_screen_edge_sizes.push_back(0);
// Workspace names
_screen_workspace_names.clear();
vector<string> vs;
if (Util::splitString(workspace_names, vs, ";", 0, true)) {
vector<string>::iterator vs_it(vs.begin());
for (; vs_it != vs.end(); ++vs_it) {
_screen_workspace_names.push_back(Util::to_wide_str(*vs_it));
}
}
CfgParser::Entry *sub = section->find_section("PLACEMENT");
if (sub) {
value = sub->find_entry("MODEL");
if (value) {
_screen_placementmodels.clear();
vector<string> models;
if (Util::splitString(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));
}
}
CfgParser::Entry *sub_2 = sub->find_section("SMART");
if (sub_2) {
key_list.push_back(new CfgParserKeyBool("ROW", _screen_placement_row));
key_list.push_back(new CfgParserKeyBool("LEFTTORIGHT", _screen_placement_ltr));
key_list.push_back(new CfgParserKeyBool("TOPTOBOTTOM", _screen_placement_ttb));
key_list.push_back(new CfgParserKeyNumeric<int>("OFFSETX", _screen_placement_offset_x, 0, 0));
key_list.push_back(new CfgParserKeyNumeric<int>("OFFSETY", _screen_placement_offset_y, 0, 0));
// Do the parsing
sub_2->parse_key_values(key_list.begin(), key_list.end());
// Freeup resources
for_each(key_list.begin(), key_list.end(), Util::Free<CfgParserKey*>());
key_list.clear();
}
}
// Fallback value
if (! _screen_placementmodels.size()) {
_screen_placementmodels.push_back(PLACE_MOUSE_CENTERED);
}
sub = section->find_section("UNIQUENAMES");
if (sub) {
key_list.push_back(new CfgParserKeyBool("SETUNIQUE", _screen_client_unique_name));
key_list.push_back(new CfgParserKeyString("PRE", _screen_client_unique_name_pre));
key_list.push_back(new CfgParserKeyString("POST", _screen_client_unique_name_post));
// Parse data
sub->parse_key_values(key_list.begin(), key_list.end());
// Free up resources
for_each(key_list.begin(), key_list.end(), Util::Free<CfgParserKey*>());
key_list.clear();
}
}
//! @brief Loads the MENU section of the main configuration
//! @param section Pointer to MENU section
void
Config::loadMenu(CfgParser::Entry *section)
{
if (! section) {
return;
}
list<CfgParserKey*> key_list;
string value_select, value_enter, value_exec;
key_list.push_back(new CfgParserKeyString("SELECT", value_select, "MOTION", 0));
key_list.push_back(new CfgParserKeyString("ENTER", value_enter, "BUTTONPRESS", 0));
key_list.push_back(new CfgParserKeyString("EXEC", value_exec, "BUTTONRELEASE", 0));
key_list.push_back(new CfgParserKeyBool("DISPLAYICONS", _menu_display_icons, true));
#ifdef OPACITY
key_list.push_back(new CfgParserKeyNumeric<uint>("FOCUSOPACITY", _menu_focus_opacity, 100, 0, 100));
key_list.push_back(new CfgParserKeyNumeric<uint>("UNFOCUSOPACITY", _menu_unfocus_opacity, 100, 0, 100));
#endif // OPACITY
// Parse data
section->parse_key_values(key_list.begin(), key_list.end());
_menu_select_mask = getMenuMask(value_select);
_menu_enter_mask = getMenuMask(value_enter);
_menu_exec_mask = getMenuMask(value_exec);
// Free up resources
for_each(key_list.begin(), key_list.end(), Util::Free<CfgParserKey*>());
key_list.clear();
// Parse icon size limits
CfgParser::iterator it(section->begin());
for (; it != section->end(); ++it) {
if (*(*it) == "ICONS") {
loadMenuIcons((*it)->get_section());
}
}
// Convert opacity from percent to absolute value
CONV_OPACITY(_menu_focus_opacity);
CONV_OPACITY(_menu_unfocus_opacity);
}
/**
* Load Icon size limits for menu.
*/
void
Config::loadMenuIcons(CfgParser::Entry *section)
{
if (! section || ! section->get_value().size()) {
return;
}
list<CfgParserKey*> key_list;
string minimum, maximum;
key_list.push_back(new CfgParserKeyString("MINIMUM", minimum, "16x16", 3));
key_list.push_back(new CfgParserKeyString("MAXIMUM", maximum, "16x16", 3));
// Parse data
section->parse_key_values(key_list.begin(), key_list.end());
for_each(key_list.begin(), key_list.end(), Util::Free<CfgParserKey*>());
SizeLimits limits;
if (limits.parse(minimum, maximum)) {
_menu_icon_limits[section->get_value()] = limits;
}
}
/**
* Load configuration from CmdDialog section.
*/
void
Config::loadCmdDialog(CfgParser::Entry *section)
{
if (! section) {
return;
}
list<CfgParserKey*> key_list;
key_list.push_back(new CfgParserKeyBool("HISTORYUNIQUE", _cmd_dialog_history_unique));
key_list.push_back(new CfgParserKeyNumeric<int>("HISTORYSIZE", _cmd_dialog_history_size, 1024, 1));
key_list.push_back(new CfgParserKeyPath("HISTORYFILE", _cmd_dialog_history_file, "~/.pekwm/history"));
key_list.push_back(new CfgParserKeyNumeric<int>("HISTORYSAVEINTERVAL", _cmd_dialog_history_save_interval, 16, 0));
section->parse_key_values(key_list.begin(), key_list.end());
for_each(key_list.begin(), key_list.end(), Util::Free<CfgParserKey*>());
}
//! @brief Loads the HARBOUR section of the main configuration
void
Config::loadHarbour(CfgParser::Entry *section)
{
if (! section) {
return;
}
list<CfgParserKey*> key_list;
string value_placement, value_orientation;
key_list.push_back(new CfgParserKeyBool("ONTOP", _harbour_ontop, true));
key_list.push_back(new CfgParserKeyBool("MAXIMIZEOVER", _harbour_maximize_over, false));
key_list.push_back(new CfgParserKeyNumeric<int>("HEAD", _harbour_head_nr, 0, 0));
key_list.push_back(new CfgParserKeyString("PLACEMENT", value_placement, "RIGHT", 0));
key_list.push_back(new CfgParserKeyString("ORIENTATION", value_orientation, "TOPTOBOTTOM", 0));
#ifdef OPACITY
key_list.push_back(new CfgParserKeyNumeric<uint>("OPACITY", _harbour_opacity, 100, 0, 100));
#endif // OPACITY
// Parse data
section->parse_key_values(key_list.begin(), key_list.end());
// Free up resources
for_each(key_list.begin(), key_list.end(), Util::Free<CfgParserKey*>());
key_list.clear();
// Convert opacity from percent to absolute value
CONV_OPACITY(_harbour_opacity);
_harbour_placement = ParseUtil::getValue<HarbourPlacement>(value_placement, _harbour_placement_map);
_harbour_orientation = ParseUtil::getValue<Orientation>(value_orientation, _harbour_orientation_map);
if (_harbour_placement == NO_HARBOUR_PLACEMENT) {
_harbour_placement = RIGHT;
}
if (_harbour_orientation == NO_ORIENTATION) {
_harbour_orientation = TOP_TO_BOTTOM;
}
CfgParser::Entry *sub = section->find_section("DOCKAPP");
if (sub) {
key_list.push_back(new CfgParserKeyNumeric<int>("SIDEMIN", _harbour_da_min_s, 64, 0));
key_list.push_back(new CfgParserKeyNumeric<int>("SIDEMAX", _harbour_da_max_s, 64, 0));
// Parse data
sub->parse_key_values(key_list.begin(), key_list.end());
// Free up resources
for_each(key_list.begin(), key_list.end(), Util::Free<CfgParserKey*>());
key_list.clear();
}
}
//! @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;
}
ActionAccessMask
Config::getActionAccessMask(const std::string &name)
{
ActionAccessMask mask = ParseUtil::getValue<ActionAccessMask>(name, _action_access_mask_map);
return mask;
}
//! @brief
bool
Config::parseKey(const std::string &key_string, uint &mod, uint &key)
{
// used for parsing
vector<string> tok;
vector<string>::iterator it;
uint num;
// chop the string up separating mods and the end key/button
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, 0, 10);
} else if (strcasecmp(tok[num].c_str(), "ANY") == 0) {
// Do no matching, anything goes.
key = 0;
} else {
KeySym keysym = XStringToKeysym(tok[num].c_str());
// XStringToKeysym() may fail. Perhaps we have luck after some
// simple transformations. First we convert the string to lowercase
// and try again. Then we try with only the first character in
// uppercase and at last we try a complete uppercase string. If all
// fails, we print a warning and return false.
if (keysym == NoSymbol) {
string str = tok[num];
Util::to_lower(str);
keysym = XStringToKeysym(str.c_str());
if (keysym == NoSymbol) {
str[0] = ::toupper(str[0]);
keysym = XStringToKeysym(str.c_str());
if (keysym == NoSymbol) {
Util::to_upper(str);
keysym = XStringToKeysym(str.c_str());
if (keysym == NoSymbol) {
cerr << " *** WARNING: Couldn't find keysym for " << tok[num] << endl;
return false;
}
}
}
}
key = XKeysymToKeycode(PScreen::instance()->getDpy(), keysym);
}
// 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
vector<string> tok;
vector<string>::iterator it;
// chop the string up separating mods and the end key/button
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;
}
//! @brief Parse a single action and fills action.
//! @param action_string String representation of action.
//! @param action Action structure to fill in.
//! @param mask Mask action is valid for.
//! @return true on success, else false
bool
Config::parseAction(const std::string &action_string, Action &action, uint mask)
{
vector<string> tok;
// chop the string up separating the action and parameters
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:
case ACTION_SHOW_CMD_DIALOG:
case ACTION_SHOW_SEARCH_DIALOG:
case ACTION_SEND_KEY:
case ACTION_MENU_DYN:
action.setParamS(tok[1]);
break;
case ACTION_ACTIVATE_CLIENT_REL:
case ACTION_MOVE_CLIENT_REL:
case ACTION_GOTO_CLIENT_ID:
action.setParamI(0, strtol(tok[1].c_str(), 0, 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(), 0, 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, parseWorkspaceNumber(tok[1]));
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:
if ((Util::splitString(tok[1], tok, " \t", 2)) == 2) {
action.setParamI(0, ParseUtil::getValue<Raise>(tok[tok.size() - 2],
_raise_map));
action.setParamI(1, Util::isTrue(tok[tok.size() - 1]));
} else {
action.setParamI(0, ParseUtil::getValue<Raise>(tok[1],
_raise_map));
action.setParamI(1, false);
}
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, 1 + ParseUtil::getValue<BorderPosition>(tok[1], _borderpos_map));
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;
case ACTION_SHOW_MENU:
if ((Util::splitString(tok[1], tok, " \t", 2)) == 2) {
Util::to_upper(tok[tok.size() - 2]);
action.setParamS(tok[tok.size() - 2]);
action.setParamI(0, Util::isTrue(tok[tok.size() - 1]));
} else {
Util::to_upper(tok[1]);
action.setParamS(tok[1]);
action.setParamI(0, false); // Default to non-sticky
}
break;
#ifdef OPACITY
case ACTION_SET_OPACITY:
if ((Util::splitString(tok[1], tok, " \t", 2)) == 2) {
action.setParamI(0, std::atoi(tok[tok.size() - 2].c_str()));
action.setParamI(1, std::atoi(tok[tok.size() - 1].c_str()));
} else {
action.setParamI(0, std::atoi(tok[1].c_str()));
action.setParamI(1, std::atoi(tok[1].c_str()));
}
break;
#endif // OPACITY
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;
}
bool
Config::parseActionAccessMask(const std::string &action_mask, uint &mask)
{
mask = ACTION_ACCESS_NO;
vector<string> tok;
if (Util::splitString(action_mask, tok, " \t")) {
vector<string>::iterator it(tok.begin());
for (; it != tok.end(); ++it) {
mask |= getActionAccessMask(*it);
}
}
return true;
}
//! @brief
bool
Config::parseActionState(Action &action, const std::string &as_action)
{
vector<string> tok;
// chop the string up separating the action and parameters
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
string directions;
switch (action.getParamI(0)) {
case ACTION_STATE_MAXIMIZED:
// Using copy of token here to silence valgrind checks.
directions = tok[1];
Util::splitString(directions, 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)
{
vector<string> tok;
vector<string>::iterator it;
Action action;
// reset the action event
ae.action_list.clear();
// chop the string up separating the actions
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.clear();
}
}
return true;
}
return false;
}
//! @brief
bool
Config::parseActionEvent(CfgParser::Entry *section, ActionEvent &ae, uint mask, bool button)
{
CfgParser::Entry *value = section->find_entry("ACTIONS");
if (! value && section->get_section()) {
value = section->get_section()->find_entry("ACTIONS");
}
if (! value) {
return false;
}
string str_button = section->get_value();
if (! str_button.size()) {
if ((ae.type == MOUSE_EVENT_ENTER) || (ae.type == MOUSE_EVENT_LEAVE)) {
str_button = "1";
} else {
return false;
}
}
bool ok;
if (button) {
ok = parseButton(str_button, ae.mod, ae.sym);
} else {
ok = parseKey(str_button, ae.mod, ae.sym);
}
if (ok) {
return parseActions(value->get_value(), ae, mask);
}
return false;
}
//! @brief
bool
Config::parseMoveResizeAction(const std::string &action_string, Action &action)
{
vector<string> tok;
// Chop the string up separating the actions.
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(), 0, 10));
break;
default:
// Do nothing.
break;
}
}
return true;
}
}
return false;
}
//! @brief
bool
Config::parseMoveResizeActions(const std::string &action_string, ActionEvent& ae)
{
vector<string> tok;
vector<string>::iterator it;
Action action;
// reset the action event
ae.action_list.clear();
// chop the string up separating the actions
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.clear();
}
}
return true;
}
return false;
}
//! @brief Parses MoveResize Event.
bool
Config::parseMoveResizeEvent(CfgParser::Entry *section, ActionEvent& ae)
{
CfgParser::Entry *value;
if (! section->get_value().size ()) {
return false;
}
if (parseKey(section->get_value(), ae.mod, ae.sym)) {
value = section->get_section()->find_entry("ACTIONS");
if (value) {
return parseMoveResizeActions(value->get_value(), ae);
}
}
return false;
}
//! @brief
bool
Config::parseInputDialogAction(const std::string &val, Action &action)
{
action.setAction(ParseUtil::getValue<InputDialogAction>(val, _inputdialog_map));
return (action.getAction() != INPUT_NO_ACTION);
}
//! @brief
bool
Config::parseInputDialogActions(const std::string &actions, ActionEvent &ae)
{
vector<string> tok;
vector<string>::iterator it;
Action action;
// reset the action event
ae.action_list.clear();
// chop the string up separating the actions
if (Util::splitString(actions, tok, ";")) {
for (it = tok.begin(); it != tok.end(); ++it) {
if (parseInputDialogAction(*it, action)) {
ae.action_list.push_back(action);
action.clear();
}
}
return true;
}
return false;
}
//! @brief Parses InputDialog Event.
bool
Config::parseInputDialogEvent(CfgParser::Entry *section, ActionEvent &ae)
{
CfgParser::Entry *value;
if (! section->get_value().size()) {
return false;
}
if (parseKey(section->get_value(), ae.mod, ae.sym)) {
value = section->get_section()->find_entry("ACTIONS");
if (value) {
return parseInputDialogActions(value->get_value(), ae);
}
}
return false;
}
/**
* Get mask for handling menu events.
*/
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_NO) {
mask_return |= val;
}
}
return mask_return;
}
//! @brief
bool
Config::parseMenuAction(const std::string &action_string, Action &action)
{
vector<string> tok;
// chop the string up separating the actions
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)
{
vector<string> tok;
vector<string>::iterator it;
Action action;
// reset the action event
ae.action_list.clear();
// chop the string up separating the actions
if (Util::splitString(actions, tok, ";")) {
for (it = tok.begin(); it != tok.end(); ++it) {
if (parseMenuAction(*it, action)) {
ae.action_list.push_back(action);
action.clear();
}
}
return true;
}
return false;
}
//! @brief Parses MenuEvent.
bool
Config::parseMenuEvent(CfgParser::Entry *section, ActionEvent& ae)
{
CfgParser::Entry *value;
if (! section->get_value().size()) {
return false;
}
if (parseKey(section->get_value(), ae.mod, ae.sym)) {
value = section->get_section()->find_entry("ACTIONS");
if (value) {
return parseMenuActions(value->get_value(), ae);
}
}
return false;
}
//! @brief
uint
Config::getMouseButton(const std::string &button)
{
uint btn;
if (button.size() == 1) { // it's a button
btn = unsigned(strtol(button.c_str(), 0, 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;
}
/**
* Load main configuration file, priority as follows:
*
* 1. Load command line specified file.
* 2. Load ~/.pekwm/config
* 3. Copy configuration and load ~/.pekwm/config
* 4. Load system configuration
*/
bool
Config::tryHardLoadConfig(CfgParser &cfg, std::string &file)
{
bool success = false;
// Try loading command line specified file.
if (file.size()) {
success = cfg.parse(file, CfgParserSource::SOURCE_FILE, true);
}
// Try loading ~/.pekwm/config
if (! success) {
file = string(getenv("HOME")) + string("/.pekwm/config");
success = cfg.parse(file, CfgParserSource::SOURCE_FILE, true);
// Copy cfg files to ~/.pekwm and try loading ~/.pekwm/config again.
if (! success) {
copyConfigFiles();
success = cfg.parse(file, CfgParserSource::SOURCE_FILE, true);
}
}
// Try loading system configuration files.
if (! success) {
file = string(SYSCONFDIR "/config");
success = cfg.parse(file, CfgParserSource::SOURCE_FILE, true);
}
return success;
}
//! @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");
string themes_dir = cfg_dir + string("/themes");
bool cp_config, cp_keys, cp_mouse, cp_menu;
bool cp_autoprops, cp_start, cp_vars;
bool make_themes = false;
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 already 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 << " already exists and isn't a directory" << endl
<< "Can't copy config files !" << endl;
return;
}
// we already 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 apparently 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;
if (stat(themes_dir.c_str(), &stat_buf)) {
make_themes = true;
}
} else { // we didn't have a ~/.pekwm directory already, 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;
make_themes = true;
}
if (cp_config) {
Util::copyTextFile(SYSCONFDIR "/config", cfg_file);
}
if (cp_keys) {
Util::copyTextFile(SYSCONFDIR "/keys", keys_file);
}
if (cp_mouse) {
Util::copyTextFile(SYSCONFDIR "/mouse", mouse_file);
}
if (cp_menu) {
Util::copyTextFile(SYSCONFDIR "/menu", menu_file);
}
if (cp_autoprops) {
Util::copyTextFile(SYSCONFDIR "/autoproperties", autoprops_file);
}
if (cp_start) {
Util::copyTextFile(SYSCONFDIR "/start", start_file);
}
if (cp_vars) {
Util::copyTextFile(SYSCONFDIR "/vars", vars_file);
}
if (make_themes) {
mkdir(themes_dir.c_str(), 0700);
}
}
/**
* Parses mouse configuration file.
*/
bool
Config::loadMouseConfig(const std::string &mouse_file)
{
if (! Util::requireReload(_mouse_state, mouse_file)) {
return false;
}
CfgParser mouse_cfg;
if (! mouse_cfg.parse(mouse_file, CfgParserSource::SOURCE_FILE, true)
&& ! mouse_cfg.parse(SYSCONFDIR "/mouse", CfgParserSource::SOURCE_FILE, true)) {
_mouse_state.clear();
return false;
}
// Clear state if load failed or dynamic content.
if (mouse_cfg.is_dynamic_content()) {
_mouse_state.clear();
} else {
_mouse_state = mouse_cfg.get_file_list();
}
// 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 *section;
section = mouse_cfg.get_entry_root()->find_section("FRAMETITLE");
if (section) {
parseButtons(section, _mouse_action_map[MOUSE_ACTION_LIST_TITLE_FRAME], FRAME_OK);
}
section = mouse_cfg.get_entry_root()->find_section("OTHERTITLE");
if (section) {
parseButtons(section, _mouse_action_map[MOUSE_ACTION_LIST_TITLE_OTHER], FRAME_OK);
}
section = mouse_cfg.get_entry_root()->find_section("CLIENT");
if (section) {
parseButtons(section, _mouse_action_map[MOUSE_ACTION_LIST_CHILD_FRAME], CLIENT_OK);
}
section = mouse_cfg.get_entry_root()->find_section("ROOT");
if (section) {
parseButtons(section, _mouse_action_map[MOUSE_ACTION_LIST_ROOT], ROOTCLICK_OK);
}
section = mouse_cfg.get_entry_root()->find_section("MENU");
if (section) {
parseButtons(section, _mouse_action_map[MOUSE_ACTION_LIST_MENU], FRAME_OK);
}
section = mouse_cfg.get_entry_root()->find_section("OTHER");
if (section) {
parseButtons(section, _mouse_action_map[MOUSE_ACTION_LIST_OTHER], FRAME_OK);
}
section = mouse_cfg.get_entry_root()->find_section("SCREENEDGE");
if (section) {
CfgParser::iterator edge_it(section->begin());
for (; edge_it != section->end(); ++edge_it) {
uint pos = ParseUtil::getValue<DirectionType>((*edge_it)->get_name(), _direction_map);
if (pos != SCREEN_EDGE_NO) {
parseButtons((*edge_it)->get_section(), getEdgeListFromPosition(pos), SCREEN_EDGE_OK);
}
}
}
section = mouse_cfg.get_entry_root()->find_section("BORDER");
if (section) {
CfgParser::iterator border_it(section->begin());
for (; border_it != section->end(); ++border_it) {
uint pos = ParseUtil::getValue<BorderPosition>((*border_it)->get_name(), _borderpos_map);
if (pos != BORDER_NO_POS) {
parseButtons((*border_it)->get_section(), getBorderListFromPosition(pos), FRAME_BORDER_OK);
}
}
}
return true;
}
//! @brief Parses mouse config section, like FRAME
void
Config::parseButtons(CfgParser::Entry *section, std::list<ActionEvent>* mouse_list, ActionOk action_ok)
{
if (! section || ! mouse_list) {
return;
}
ActionEvent ae;
CfgParser::Entry *value;
CfgParser::iterator it(section->begin());
for (; it != section->end(); ++it) {
if (! (*it)->get_section()) {
continue;
}
ae.type = ParseUtil::getValue<MouseEventType>((*it)->get_name(), _mouse_event_map);
if (ae.type == MOUSE_EVENT_NO) {
continue;
}
if (ae.type == MOUSE_EVENT_MOTION) {
value = (*it)->get_section()->find_entry("THRESHOLD");
if (value) {
ae.threshold = strtol(value->get_value().c_str(), 0, 10);
} else {
ae.threshold = 0;
}
}
if (parseActionEvent((*it), ae, action_ok, true)) {
mouse_list->push_back(ae);
}
}
}
// frame border configuration
list<ActionEvent>*
Config::getBorderListFromPosition(uint pos)
{
list<ActionEvent> *ret = 0;
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 = 0;
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;
}
//! @brief Parses workspace number
int
Config::parseWorkspaceNumber(const std::string &workspace)
{
// Get workspace looking for relative numbers
uint num = ParseUtil::getValue<WorkspaceChangeType>(workspace, _workspace_change_map);
if (num == WORKSPACE_NO) {
// Workspace isn't relative, check for 2x2 and ordinary specification
vector<string> tok;
if (Util::splitString(workspace, tok, "x", 2, true) == 2) {
uint row = strtol(tok[0].c_str(), 0, 10) - 1;
uint col = strtol(tok[1].c_str(), 0, 10) - 1;
num = _screen_workspaces_per_row * row + col;
} else {
num = strtol(workspace.c_str(), 0, 10) - 1;
}
}
// Fallback to 0 if something went wrong
if (num < 0) {
num = 0;
}
return num;
}
#ifdef OPACITY
//! @brief Parses a string which contains two opacity values
bool
Config::parseOpacity(const std::string value, uint &focused, uint &unfocused)
{
std::vector<string> tokens;
switch ((Util::splitString(value, tokens, " ,", 2))) {
case 2:
focused = std::atoi(tokens.at(0).c_str());
unfocused = std::atoi(tokens.at(1).c_str());
break;
case 1:
focused = unfocused = std::atoi(tokens.at(0).c_str());
break;
default:
return false;
}
CONV_OPACITY(focused);
CONV_OPACITY(unfocused);
return true;
}
#endif // OPACITY