//
// AutoProperties.cc for pekwm
// Copyright (C) 2003-2004 Claes Nasten <pekdon{@}pekdon{.}net>
//
// This program is licensed under the GNU GPL.
// See the LICENSE file for more information.
//
#include "../config.h"
#include "AutoProperties.hh"
#include "Config.hh"
#include "Util.hh"
#include <algorithm>
#include <vector>
#ifdef DEBUG
#include <iostream>
using std::cerr;
using std::endl;
#endif // DEBUG
using std::string;
using std::list;
using std::vector;
using std::find;
AutoProperties *AutoProperties::_instance = NULL;
//! @brief Constructor for AutoProperties class
AutoProperties::AutoProperties(void) :
#ifdef HARBOUR
_harbour_sort(false),
#endif // HARBOUR
_apply_on_start(true)
{
#ifdef DEBUG
if (_instance)
{
cerr << __FILE__ << "@" << __LINE__ << ": "
<< "AutoProperties(" << this << ")::AutoProperties()" << endl
<< " *** _instance allready set: " << _instance << endl;
}
#endif // DEBUG
_instance = this;
// fill parsing maps
_apply_on_map[""] = APPLY_ON_NONE;
_apply_on_map["START"] = APPLY_ON_START;
_apply_on_map["NEW"] = APPLY_ON_NEW;
_apply_on_map["RELOAD"] = APPLY_ON_RELOAD;
_apply_on_map["WORKSPACE"] = APPLY_ON_WORKSPACE;
_apply_on_map["TRANSIENT"] = APPLY_ON_TRANSIENT;
_apply_on_map["TRANSIENTONLY"] = APPLY_ON_TRANSIENT_ONLY;
// global properties
_property_map[""] = AP_NO_PROPERTY;
_property_map["WORKSPACE"] = AP_WORKSPACE;
_property_map["PROPERTY"] = AP_PROPERTY;
_property_map["STICKY"] = AP_STICKY;
_property_map["SHADED"] = AP_SHADED;
_property_map["MAXIMIZEDVERTICAL"] = AP_MAXIMIZED_VERTICAL;
_property_map["MAXIMIZEDHORIZONTAL"] = AP_MAXIMIZED_HORIZONTAL;
_property_map["ICONIFIED"] = AP_ICONIFIED;
_property_map["BORDER"] = AP_BORDER;
_property_map["TITLEBAR"] = AP_TITLEBAR;
_property_map["FRAMEGEOMETRY"] = AP_FRAME_GEOMETRY;
_property_map["CLIENTGEOMETRY"] = AP_CLIENT_GEOMETRY;
_property_map["LAYER"] = AP_LAYER;
_property_map["SKIP"] = AP_SKIP;
_property_map["FULLSCREEN"] = AP_FULLSCREEN;
_property_map["PLACENEW"] = AP_PLACE_NEW;
_property_map["FOCUSNEW"] = AP_FOCUS_NEW;
_property_map["FOCUSABLE"] = AP_FOCUSABLE;
_property_map["VIEWPORT"] = AP_VIEWPORT;
_property_map["CFGDENY"] = AP_CFG_DENY;
// group properties
_group_property_map[""] = AP_NO_PROPERTY;
_group_property_map["SIZE"] = AP_GROUP_SIZE;
_group_property_map["BEHIND"] = AP_GROUP_BEHIND;
_group_property_map["FOCUSEDFIRST"] = AP_GROUP_FOCUSED_FIRST;
_group_property_map["GLOBAL"] = AP_GROUP_GLOBAL;
_group_property_map["RAISE"] = AP_GROUP_RAISE;
}
//! @brief Destructor for AutoProperties class
AutoProperties::~AutoProperties(void)
{
unload();
_instance = NULL;
}
//! @brief Loads the autoprop config file.
void
AutoProperties::load(void)
{
// dealloc memory
unload();
CfgParser a_cfg;
if (!a_cfg.parse (Config::instance()->getAutoPropsFile()))
{
string cfg_file = SYSCONFDIR "/autoproperties";
if (!a_cfg.parse (cfg_file))
return;
}
// reset values
_apply_on_start = true;
vector<string> tokens;
vector<string>::iterator it;
list<uint> workspaces;
CfgParser::Entry *op_it (a_cfg.get_entry_root ());
while ((op_it = op_it->get_section_next ()) != NULL)
{
if (*op_it == "PROPERTY")
parseAutoProperty (op_it, NULL);
else if (*op_it == "TITLERULES")
parseTitleProperty (op_it);
else if (*op_it == "DECORRULES")
parseDecorProperty (op_it);
#ifdef HARBOUR
else if (*op_it == "HARBOUR")
parseDockAppProperty (op_it);
#endif // HARBOUR
else if (*op_it == "WORKSPACE") // Workspace section
{
op_it = op_it->get_section ();
CfgParser::Entry *op_value = op_it->find_entry ("WORKSPACE");
if (!op_value)
continue; // Need workspace numbers.
tokens.clear();
if (Util::splitString(op_value->get_value (), tokens, " \t"))
{
workspaces.clear();
for (it = tokens.begin(); it != tokens.end(); ++it)
workspaces.push_back(strtol(it->c_str(), NULL, 10) - 1);
// Get all properties on for these workspaces.
CfgParser::Entry *op_it_sub (op_it->get_section ());
while ((op_it_sub = op_it_sub->get_section_next ()) != NULL)
parseAutoProperty(op_it_sub, &workspaces);
}
}
}
}
//! @brief Frees allocated memory
void
AutoProperties::unload(void)
{
list<Property*>::iterator it;
// remove auto properties
for (it = _prop_list.begin(); it != _prop_list.end(); ++it) {
delete *it;
}
_prop_list.clear();
// remove title properties
for (it = _title_prop_list.begin(); it != _title_prop_list.end(); ++it) {
delete *it;
}
_title_prop_list.clear();
// remove decor properties
for (it = _decor_prop_list.begin(); it != _decor_prop_list.end(); ++it) {
delete *it;
}
_decor_prop_list.clear();
}
//! @brief Finds a property from the prop_list
Property*
AutoProperties::findProperty(const ClassHint* class_hint,
std::list<Property*>* prop_list, int ws, uint type)
{
// Allready remove apply on start
if (!_apply_on_start && (type == APPLY_ON_START))
return NULL;
list<Property*>::iterator it(prop_list->begin());
list<uint>::iterator w_it;
// start searching for a suitable property
for (bool ok = false; it != prop_list->end(); ++it, ok = false) {
// see if the type matches, if we have one
if ((type != 0) && !(*it)->isApplyOn(type))
continue;
if (matchAutoClass(*class_hint, *it)) {
// make sure it applies on the correct workspace
if ((*it)->getWsList().size()) {
w_it = find((*it)->getWsList().begin(), (*it)->getWsList().end(),
unsigned(ws));
if (w_it != (*it)->getWsList().end())
return *it;
} else {
return *it;
}
}
}
return NULL;
}
//! @brief Parses a cs and sets up a basic property
bool
AutoProperties::parseProperty(CfgParser::Entry *op_section, Property *prop)
{
vector<string> tokens;
if ((Util::splitString (op_section->get_value (), tokens, ",", 2)) != 2)
return false;
CfgParser::Entry *op_value;
if (!prop->getHintName().parse_match (tokens[0])
|| !prop->getHintClass().parse_match (tokens[1]))
{
return false;
}
// Get extra matching info.
op_value = op_section->find_entry ("TITLE");
if (op_value)
prop->getTitle().parse_match (op_value->get_value ());
op_value = op_section->find_entry ("ROLE");
if (op_value)
prop->getRole().parse_match (op_value->get_value ());
// Parse apply on mask.
op_value = op_section->find_entry ("APPLYON");
if (op_value)
{
tokens.clear();
if ((Util::splitString(op_value->get_value (), tokens, " \t", 5)))
{
vector<string>::iterator it;
for (it = tokens.begin(); it != tokens.end(); ++it)
prop->applyAdd((uint) ParseUtil::getValue<ApplyOn>(*it, _apply_on_map));
}
}
return true;
}
//! @brief Parses AutopProperty
void
AutoProperties::parseAutoProperty(CfgParser::Entry *op_section,
std::list<uint>* ws)
{
op_section = op_section->get_section ();
AutoProperty* property = new AutoProperty();
if (!parseProperty(op_section, property))
{
delete property;
return; // Not a valid property.
}
// copy workspaces, if any
if (ws)
property->getWsList().assign(ws->begin(), ws->end());
// see if we have a group section
CfgParser::Entry *op_it(op_section->find_section ("GROUP"));
if (op_it)
parseAutoGroup(op_it, property);
// start parsing of values
string name, value;
vector<string> tokens;
vector<string>::iterator it;
PropertyType property_type;
for (op_it = op_section->get_entry_next (); op_it; op_it = op_it->get_entry_next ())
{
property_type = ParseUtil::getValue<PropertyType>(op_it->get_name (),
_property_map);
switch (property_type) {
case AP_STICKY:
property->maskAdd(AP_STICKY);
property->sticky = Util::isTrue(op_it->get_value ());
break;
case AP_SHADED:
property->maskAdd(AP_SHADED);
property->shaded = Util::isTrue(op_it->get_value ());
break;
case AP_MAXIMIZED_VERTICAL:
property->maskAdd(AP_MAXIMIZED_VERTICAL);
property->maximized_vertical = Util::isTrue(op_it->get_value ());
break;
case AP_MAXIMIZED_HORIZONTAL:
property->maskAdd(AP_MAXIMIZED_HORIZONTAL);
property->maximized_horizontal = Util::isTrue(op_it->get_value ());
break;
case AP_ICONIFIED:
property->maskAdd(AP_ICONIFIED);
property->iconified = Util::isTrue(op_it->get_value ());
break;
case AP_BORDER:
property->maskAdd(AP_BORDER);
property->border = Util::isTrue(op_it->get_value ());
break;
case AP_TITLEBAR:
property->maskAdd(AP_TITLEBAR);
property->titlebar = Util::isTrue(op_it->get_value ());
break;
case AP_FRAME_GEOMETRY:
property->maskAdd(AP_FRAME_GEOMETRY);
property->frame_gm_mask =
XParseGeometry((char*) op_it->get_value ().c_str(),
&property->frame_gm.x, &property->frame_gm.y,
&property->frame_gm.width, &property->frame_gm.height);
break;
case AP_CLIENT_GEOMETRY:
property->maskAdd(AP_CLIENT_GEOMETRY);
property->client_gm_mask =
XParseGeometry((char*) op_it->get_value ().c_str(),
&property->client_gm.x, &property->client_gm.y,
&property->client_gm.width, &property->client_gm.height);
break;
case AP_LAYER:
property->maskAdd(AP_LAYER);
property->layer = Config::instance()->getLayer(op_it->get_value ());
break;
case AP_WORKSPACE:
property->maskAdd(AP_WORKSPACE);
property->workspace = unsigned(strtol(op_it->get_value ().c_str(), NULL, 10) - 1);
break;
case AP_SKIP:
property->maskAdd(AP_SKIP);
tokens.clear();
if ((Util::splitString(op_it->get_value (), tokens, " \t"))) {
for (it = tokens.begin(); it != tokens.end(); ++it)
property->skip |= Config::instance()->getSkip(*it);
}
break;
case AP_FULLSCREEN:
property->maskAdd(AP_FULLSCREEN);
property->fullscreen = Util::isTrue(op_it->get_value ());
break;
case AP_PLACE_NEW:
property->maskAdd(AP_PLACE_NEW);
property->place_new = Util::isTrue(op_it->get_value ());
break;
case AP_FOCUS_NEW:
property->maskAdd(AP_FOCUS_NEW);
property->focus_new = Util::isTrue(op_it->get_value ());
break;
case AP_FOCUSABLE:
property->maskAdd(AP_FOCUSABLE);
property->focusable = Util::isTrue(op_it->get_value ());
break;
case AP_CFG_DENY:
property->maskAdd(AP_CFG_DENY);
tokens.clear();
if ((Util::splitString(op_it->get_value (), tokens, " \t"))) {
for (it = tokens.begin(); it != tokens.end(); ++it) {
property->cfg_deny |= Config::instance()->getCfgDeny(*it);
}
}
break;
case AP_VIEWPORT:
tokens.clear();
if (Util::splitString(op_it->get_value (), tokens, " \t", 2) == 2) {
property->maskAdd(AP_VIEWPORT);
property->viewport_col = strtol(tokens[0].c_str(), NULL, 10) - 1;
property->viewport_row = strtol(tokens[1].c_str(), NULL, 10) - 1;
}
break;
default:
// do nothing
break;
}
}
_prop_list.push_back(property);
}
//! @brief Parses a Group section of the AutoProps
void
AutoProperties::parseAutoGroup(CfgParser::Entry *op_section,
AutoProperty* property)
{
op_section = op_section->get_section ();
if (op_section->get_value ().size ())
property->group_name = op_section->get_value ();
PropertyType property_type;
while ((op_section = op_section->get_entry_next ()) != NULL)
{
property_type = ParseUtil::getValue<PropertyType> (op_section->get_name (), _group_property_map);
switch (property_type)
{
case AP_GROUP_SIZE:
property->group_size = strtol (op_section->get_value ().c_str (),
NULL, 10);
break;
case AP_GROUP_BEHIND:
property->group_behind = Util::isTrue (op_section->get_value ());
break;
case AP_GROUP_FOCUSED_FIRST:
property->group_focused_first = Util::isTrue (op_section->get_value ());
break;
case AP_GROUP_GLOBAL:
property->group_global = Util::isTrue (op_section->get_value ());
break;
case AP_GROUP_RAISE:
property->group_raise = Util::isTrue (op_section->get_value ());
break;
default:
// do nothing
break;
}
}
}
//! @brief Parses a title property section
void
AutoProperties::parseTitleProperty(CfgParser::Entry *op_section)
{
op_section = op_section->get_section ();
bool ok;
TitleProperty *property;
CfgParser::Entry *op_sub;
while ((op_section = op_section->get_section_next ()) != NULL)
{
op_sub = op_section->get_section ();
ok = false;
property = new TitleProperty();
if (parseProperty (op_sub, property))
{
CfgParser::Entry *op_value = op_sub->find_entry ("RULE");
if (op_value)
ok = property->getTitleRule ().parse_ed_s (op_value->get_value ());
}
if (ok)
_title_prop_list.push_back(property);
else
delete property;
}
}
//! @brief
void
AutoProperties::parseDecorProperty(CfgParser::Entry *op_section)
{
op_section = op_section->get_section ();
bool ok;
DecorProperty *property;
CfgParser::Entry *op_sub;
while ((op_section = op_section->get_section_next ()) != NULL)
{
op_sub = op_section->get_section ();
ok = false;
property = new DecorProperty ();
if (parseProperty (op_sub, property))
{
CfgParser::Entry *op_value = op_sub->find_entry ("DECOR");
if (op_value)
{
property->applyAdd (APPLY_ON_START);
property->setName (op_value->get_value ());
}
}
if (ok)
_decor_prop_list.push_back(property);
else
delete property;
}
}
#ifdef HARBOUR
//! @brief
void
AutoProperties::parseDockAppProperty(CfgParser::Entry *op_section)
{
op_section = op_section->get_section ();
// Reset harbour sort, set to true if POSITION property found.
_harbour_sort = false;
bool ok;
DockAppProperty *property;
CfgParser::Entry *op_sub;
while ((op_section = op_section->get_section_next ()) != NULL)
{
op_sub = op_section->get_section ();
ok = false;
property = new DockAppProperty ();
if (parseProperty (op_sub, property))
{
CfgParser::Entry *op_value = op_sub->find_entry ("POSITION");
if (op_value)
{
_harbour_sort = true;
ok = true;
int position = strtol (op_value->get_value ().c_str(), NULL, 10);
property->setPosition (position);
}
}
if (ok)
_dock_app_prop_list.push_back(property);
else
delete property;
}
}
#endif // HARBOUR
//! @brief Searches the _prop_list for a property
AutoProperty*
AutoProperties::findAutoProperty(const ClassHint* class_hint,
int ws, uint type)
{
AutoProperty *property =
(AutoProperty*) findProperty(class_hint, &_prop_list, ws, type);
if (property)
return (AutoProperty*) property;
return NULL;
}
//! @brief Searches the _title_prop_list for a property
TitleProperty*
AutoProperties::findTitleProperty(const ClassHint* class_hint)
{
TitleProperty *property =
(TitleProperty*) findProperty(class_hint, &_title_prop_list, -1, 0);
return property;
}
//! @brief
DecorProperty*
AutoProperties::findDecorProperty(const ClassHint* class_hint)
{
DecorProperty *property =
(DecorProperty*) findProperty(class_hint, &_decor_prop_list, -1, 0);
return property;
}
#ifdef HARBOUR
DockAppProperty*
AutoProperties::findDockAppProperty(const ClassHint *class_hint)
{
DockAppProperty *property =
(DockAppProperty*) findProperty(class_hint, &_dock_app_prop_list, -1, 0);
return property;
}
#endif // HARBOUR
//! @brief Removes all ApplyOnStart actions as they consume memory
void
AutoProperties::removeApplyOnStart(void)
{
list<Property*>::iterator it(_prop_list.begin());
for (; it != _prop_list.end(); ++it) {
if ((*it)->isApplyOn(APPLY_ON_START)) {
(*it)->applyRemove(APPLY_ON_START);
if (!(*it)->getApplyOn()) {
delete *it;
it = _prop_list.erase(it);
--it; // compensate for the ++ in the loop
}
}
}
_apply_on_start = false;
}
//! @brief Tries to match a class hint against an autoproperty data entry
bool
AutoProperties::matchAutoClass(const ClassHint &hint, Property *prop)
{
bool ok = false;
if ((prop->getHintName() == hint.h_name)
&& (prop->getHintClass() == hint.h_class))
{
ok = true;
if (prop->getTitle ().is_match_ok ())
ok = (prop->getTitle () == hint.title);
if (ok && prop->getRole ().is_match_ok ())
ok = (prop->getRole () == hint.h_role);
}
return ok;
}
syntax highlighted by Code2HTML, v. 0.9.1