//
// Theme.cc for pekwm
// Copyright (C) 2003-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 "ParseUtil.hh"
#include "Theme.hh"

#include "PScreen.hh"
#include "Config.hh"
#include "PWinObj.hh"
#include "PFont.hh"
#include "PTexture.hh"
#include "ColorHandler.hh"
#include "FontHandler.hh"
#include "ImageHandler.hh"
#include "TextureHandler.hh"
#include "Util.hh"

#include <iostream>
#include <cstdlib>

using std::cerr;
using std::endl;
using std::string;
using std::list;
using std::vector;
using std::map;

// Theme::PDecorButtonData

//! @brief Theme::PDecorButtonData constructor.
Theme::PDecorButtonData::PDecorButtonData(void) :
_left(false), _width(1), _height(1)
{
	for (uint i = 0; i < BUTTON_STATE_NO; ++i) {
		_texture[i] = NULL;
	}
}

//! @brief Theme::PDecorButtonData destructor.
Theme::PDecorButtonData::~PDecorButtonData(void)
{
	unload();
}

//! @brief Parses CfgParser::Entry op_section, loads and verifies data.
//! @param op_section CfgParser::Entry with button configuration.
//! @return True if a valid button was parsed.
bool
Theme::PDecorButtonData::load (CfgParser::Entry *op_section)
{
  if (*op_section == "LEFT")
    _left = true;
  else if (*op_section == "RIGHT")
    _left = false;
  else
    return false;

  op_section = op_section->get_section ();

  // Get actions.
  ActionEvent ae;
  CfgParser::Entry *op_it;
  for (op_it = op_section->get_section_next (); op_it; op_it = op_it->get_section_next ())
    {
      if (Config::instance ()->parseActionEvent (op_it, ae, BUTTONCLICK_OK, true))
        _ae_list.push_back (ae);
    }

  // Got some actions, consider it to be a valid button.
  if (_ae_list.size() > 0)
    {
      TextureHandler *th = TextureHandler::instance();
      CfgParser::Entry *op_value;

      op_value = op_section->find_entry ("FOCUSED");
      if (op_value)
        _texture[BUTTON_STATE_FOCUSED] = th->getTexture (op_value->get_value ());
      op_value = op_section->find_entry ("UNFOCUSED");
      if (op_value)
        _texture[BUTTON_STATE_UNFOCUSED] = th->getTexture (op_value->get_value ());
      op_value = op_section->find_entry ("PRESSED");
      if (op_value)
        _texture[BUTTON_STATE_PRESSED] = th->getTexture (op_value->get_value ());
      op_value = op_section->find_entry ("HOOVER");
      if (op_value)
        _texture[BUTTON_STATE_HOOVER] = th->getTexture (op_value->get_value ());

      check ();

      return true;
    }

  return false;
}

//! @brief Unloads data.
void
Theme::PDecorButtonData::unload(void)
{
	for (uint i = 0; i < BUTTON_STATE_NO; ++i) {
		TextureHandler::instance()->returnTexture(_texture[i]);
		_texture[i] = NULL;
	}
}

//! @brief Verifies and makes sure no NULL textures exists.
void
Theme::PDecorButtonData::check(void)
{
	for (uint i = 0; i < BUTTON_STATE_NO; ++i) {
		if (_texture[i] == NULL) {
			_texture[i] = TextureHandler::instance()->getTexture("EMPTY");
		}
	}

	_width = _texture[BUTTON_STATE_FOCUSED]->getWidth();
	_height = _texture[BUTTON_STATE_FOCUSED]->getHeight();
	if (_width == 0) {
		cerr << " *** WARNING: button texture width == 0" << endl;
		_width = 1;
	}
	if (_height == 0) {
		cerr << " *** WARNING: button texture height == 0" << endl;
		_height = 1;
	}
}

// Theme::PDecorData

map<FocusedState, string> Theme::PDecorData::_fs_map = map<FocusedState, string>();
map<BorderPosition, string> Theme::PDecorData::_border_map = map<BorderPosition, string>();

//! @brief Theme::PDecorData constructor.
Theme::PDecorData::PDecorData(void) :
_title_height(0), _title_width_min(0), _title_width_max(100),
_title_width_symetric(true)
{
	// init static data
	if (!_fs_map.size()) {
		_fs_map[FOCUSED_STATE_FOCUSED] = "FOCUSED";
		_fs_map[FOCUSED_STATE_UNFOCUSED] = "UNFOCUSED";
		_fs_map[FOCUSED_STATE_FOCUSED_SELECTED] = "FOCUSEDSELECTED";
		_fs_map[FOCUSED_STATE_UNFOCUSED_SELECTED] = "UNFOCUSEDSELECTED";
	}
	if (!_border_map.size()) {
		_border_map[BORDER_TOP_LEFT] = "TOPLEFT";
		_border_map[BORDER_TOP] = "TOP";
		_border_map[BORDER_TOP_RIGHT] = "TOPRIGHT";
		_border_map[BORDER_LEFT] = "LEFT";
		_border_map[BORDER_RIGHT] = "RIGHT";
		_border_map[BORDER_BOTTOM_LEFT] = "BOTTOMLEFT";
		_border_map[BORDER_BOTTOM] = "BOTTOM";
		_border_map[BORDER_BOTTOM_RIGHT] = "BOTTOMRIGHT";
	}

	// init arrays
	for (uint i = 0; i < DIRECTION_NO; ++i) {
		_pad[i] = 0;
	}
	for (uint i = 0; i < FOCUSED_STATE_NO; ++i) {
		_texture_tab[i] = NULL;
		_font[i] = NULL;
		_font_color[i] = NULL;
	}
	for (uint i = 0; i < FOCUSED_STATE_FOCUSED_SELECTED; ++i) {
		_texture_main[i] = NULL;
		_texture_separator[i] = NULL;
	}

	for (uint i = 0; i < FOCUSED_STATE_FOCUSED_SELECTED; ++i) {
		for (uint j = 0; j < BORDER_NO_POS; ++j) {
			_texture_border[i][j] = NULL;
		}
	}
}

//! @brief Theme::PDecorData destructor.
Theme::PDecorData::~PDecorData(void)
{
	unload();
}

//! @brief Parses CfgParser::Entry op_section, loads and verifies data.
//! @param op_section CfgParser::Entry with pdecor configuration.
//! @return True if a valid pdecor was parsed.
bool
Theme::PDecorData::load (CfgParser::Entry *op_section)
{
  CfgParser::Entry *op_value;

  _name = op_section->get_value ();
  if (!_name.size ())
    {
      cerr << " *** WARNING: no name identifying decor" << endl;
      return false;
    }

  op_section = op_section->get_section ();

  CfgParser::Entry *op_sub, *op_sub_2;
  op_sub = op_section->find_section ("TITLE");
  if (!op_sub)
    {
      cerr << " *** WARNING: no title section in decor: " << _name << endl;
      return false;
    }
  op_sub = op_sub->get_section ();

  TextureHandler *th = TextureHandler::instance(); // convenience

  vector<string> tok;
  list<CfgParserKey*> o_key_list;
  string o_value_pad, o_value_focused, o_value_unfocused;

  o_key_list.push_back (new CfgParserKeyInt ("HEIGHT", _title_height, 10, 0));
  o_key_list.push_back (new CfgParserKeyInt ("WIDTHMIN", _title_width_min, 0));
  o_key_list.push_back (new CfgParserKeyInt ("WIDTHMAX", _title_width_max,
                                             100, 0, 100));
  o_key_list.push_back (new CfgParserKeyBool ("WIDTHSYMETRIC",
                                              _title_width_symetric));
  o_key_list.push_back (new CfgParserKeyString ("PAD", o_value_pad,
                                                "0 0 0 0", 7));
  o_key_list.push_back (new CfgParserKeyString ("FOCUSED", o_value_focused,
                                                "Empty", th->getLengthMin ()));
  o_key_list.push_back (new CfgParserKeyString ("UNFOCUSED", o_value_unfocused,
                                                "Empty", th->getLengthMin ()));

  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*>());

  // Handle parsed data.
  _texture_main[FOCUSED_STATE_FOCUSED] = th->getTexture(o_value_focused);
  _texture_main[FOCUSED_STATE_UNFOCUSED] = th->getTexture(o_value_unfocused);
  if (Util::splitString(o_value_pad, tok, " \t", 4) == 4)
    {
      for (uint i = 0; i < DIRECTION_NO; ++i)
        _pad[i] = strtol (tok[i].c_str (), NULL, 10);
    }

  op_sub_2 = op_sub->find_section ("TAB");
  if (op_sub_2)
    {
      op_sub_2 = op_sub_2->get_section ();
      for (uint i = 0; i < FOCUSED_STATE_NO; ++i)
        {
          op_value = op_sub_2->find_entry (_fs_map[FocusedState (i)]);
          if (op_value)
            _texture_tab[i] = th->getTexture (op_value->get_value ());
        }
    }

  op_sub_2 = op_sub->find_section ("SEPARATOR");
  if (op_sub_2)
    {
      op_sub_2 = op_sub_2->get_section ();
      list<CfgParserKey*> o_key_list;
      string o_value_focused, o_value_unfocused;

      o_key_list.push_back (new CfgParserKeyString ("FOCUSED", o_value_focused,
                                                    "Empty", th->getLengthMin ()));
      o_key_list.push_back (new CfgParserKeyString ("UNFOCUSED", o_value_unfocused,
                                                    "Empty", th->getLengthMin ()));

      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*>());

      // Handle parsed data.
      _texture_separator[FOCUSED_STATE_FOCUSED] =
        th->getTexture(o_value_focused);
      _texture_separator[FOCUSED_STATE_UNFOCUSED] =
        th->getTexture(o_value_unfocused);
    }


  op_sub_2 = op_sub->find_section ("FONT");
  if (op_sub_2)
    {
      op_sub_2 = op_sub_2->get_section ();
      for (uint i = 0; i < FOCUSED_STATE_NO; ++i)
        {
          op_value = op_sub_2->find_entry (_fs_map[FocusedState(i)]);
          if (op_value)
            _font[i] = FontHandler::instance()->getFont(op_value->get_value ());
        }
    }
  else
    cerr << " *** WARNING: no font section in decor: " << _name << endl;

  op_sub_2 = op_sub->find_section ("FONTCOLOR");
  if (op_sub_2)
    {
      op_sub_2 = op_sub_2->get_section ();
      for (uint i = 0; i < FOCUSED_STATE_NO; ++i)
        {
          op_value = op_sub_2->find_entry (_fs_map[FocusedState(i)]);
          if (op_value)
            _font_color[i] = FontHandler::instance()->getColor(op_value->get_value ());
        }
    }

  loadButtons (op_sub->find_section ("BUTTONS"));
  loadBorder (op_sub->find_section ("BORDER"));

  check();

  return true;
}

//! @brief Unloads data.
void
Theme::PDecorData::unload(void)
{
	TextureHandler *th = TextureHandler::instance();

	for (uint i = 0; i < FOCUSED_STATE_NO; ++i) {
		th->returnTexture(_texture_tab[i]);
		FontHandler::instance()->returnFont(_font[i]);
		FontHandler::instance()->returnColor(_font_color[i]);

		_texture_tab[i] = NULL;
		_font[i] = NULL;
		_font_color[i] = NULL;
	}

	for (uint i = 0; i < FOCUSED_STATE_FOCUSED_SELECTED; ++i) {
		th->returnTexture(_texture_main[i]);
		th->returnTexture(_texture_separator[i]);
		_texture_main[i] = NULL;
		_texture_separator[i] = NULL;

		for (uint j = 0; j < BORDER_NO_POS; ++j) {
			th->returnTexture(_texture_border[i][j]);
			_texture_border[i][j] = NULL;
		}
	}

	list<Theme::PDecorButtonData*>::iterator it(_button_list.begin());
	for (; it != _button_list.end(); ++it) {
		delete *it;
	}
	_button_list.clear();
}

//! @brief Checks data properties, prints warning and tries to fix.
void
Theme::PDecorData::check(void)
{
	// check values
	if (_title_width_max > 100) {
		cerr << " *** WARNING: " << _name << " WIDTHMAX > 100" << endl;
		_title_width_max = 100;
	}

	checkTextures();
	checkFonts();
	checkBorder();
	checkColors();
}

//! @brief Loads border data.
void
Theme::PDecorData::loadBorder (CfgParser::Entry *op_section)
{
  if (!op_section)
    return;
  op_section = op_section->get_section ();

  TextureHandler *th = TextureHandler::instance(); // convenience

  CfgParser::Entry *op_sub, *op_value;

  op_sub = op_section->find_section ("FOCUSED");
  if (op_sub)
    {
      op_sub = op_sub->get_section ();
      for (uint i = 0; i < BORDER_NO_POS; ++i)
        {
          op_value = op_sub->find_entry (_border_map[BorderPosition (i)]);
          if (op_value)
            {
              _texture_border[FOCUSED_STATE_FOCUSED][i] =
                th->getTexture (op_value->get_value ());
            }
        }
    }

  op_sub = op_section->find_section ("UNFOCUSED");
  if (op_sub)
    {
      op_sub = op_sub->get_section ();
      for (uint i = 0; i < BORDER_NO_POS; ++i)
        {
          op_value = op_sub->find_entry (_border_map[BorderPosition (i)]);
          if (op_value)
            {
              _texture_border[FOCUSED_STATE_UNFOCUSED][i] =
                th->getTexture (op_value->get_value ());
            }
        }
    }
}

//! @brief Loads button data.
void
Theme::PDecorData::loadButtons (CfgParser::Entry *op_section)
{
  if (!op_section)
    return;
  op_section = op_section->get_section ();

  while ((op_section = op_section->get_section_next ()) != NULL)
    {
      Theme::PDecorButtonData *btn = new Theme::PDecorButtonData ();
      if (btn->load (op_section))
        _button_list.push_back(btn);
      else
        delete btn;
    }
}

//! @brief Checks for NULL textures, prints warning and sets empty texture
void
Theme::PDecorData::checkTextures(void)
{
	for (uint i = 0; i < FOCUSED_STATE_NO; ++i) {
		if (_texture_tab[i] == NULL) {
			cerr << " *** WARNING: " << _name << " missing tab texture state "
					 << _fs_map[FocusedState(i)] << endl;
			_texture_tab[i] = TextureHandler::instance()->getTexture("EMPTY");
		}
	}
	for (uint i = 0; i < FOCUSED_STATE_FOCUSED_SELECTED; ++i) {
		if (_texture_main[i] == NULL) {
			cerr << " *** WARNING: " << _name << " missing main texture state "
					 << _fs_map[FocusedState(i)] << endl;
			_texture_main[i] = TextureHandler::instance()->getTexture("EMPTY");
		}
		if (_texture_separator[i] == NULL) {
			cerr << " *** WARNING: " << _name << " missing tab texture state "
					 << _fs_map[FocusedState(i)] << endl;
			_texture_separator[i] = TextureHandler::instance()->getTexture("EMPTY");
		}
	}
}

//! @brief Checks for NULL fonts, prints warning and sets empty font
void
Theme::PDecorData::checkFonts(void)
{
	// the only font that's "obligatory" is the standard focused font,
	// others are only used if availible so we only check the focused font.
	if (_font[FOCUSED_STATE_FOCUSED] == NULL) {
		cerr << " *** WARNING: " << _name << " missing font state "
				 << _fs_map[FOCUSED_STATE_FOCUSED] << endl;
		_font[FOCUSED_STATE_FOCUSED] = FontHandler::instance()->getFont("");
	}
}

//! @brief Checks for NULL border PTextures.
void
Theme::PDecorData::checkBorder(void)
{
	for (uint state = FOCUSED_STATE_FOCUSED; state < FOCUSED_STATE_FOCUSED_SELECTED; ++state) {
		for (uint i = 0; i < BORDER_NO_POS; ++i) {
			if (_texture_border[state][i] == NULL) {
				cerr << " *** WARNING: " << _name << " missing border texture "
						 << _border_map[BorderPosition(i)] << " "
						 << _fs_map[FocusedState(state)] << endl;
				_texture_border[state][i] =
					TextureHandler::instance()->getTexture("EMPTY");
			}
		}
	}
}

//! @brief Checks for NULL colors, prints warning and sets empty color
void
Theme::PDecorData::checkColors(void)
{
	for (uint i = 0; i < FOCUSED_STATE_NO; ++i) {
		if (_font_color[i] == NULL) {
			cerr << " *** WARNING: " << _name << " missing font color state "
					 << _fs_map[FocusedState(i)] << endl;
			_font_color[i] = FontHandler::instance()->getColor("#000000");
		}
	}
}

// Theme::PMenuData

//! @brief PMenuData constructor
Theme::PMenuData::PMenuData(void)
{
  for (uint i = 0; i <= OBJECT_STATE_NO; ++i) {
    _font[i] = NULL;
    _color[i] = NULL;
    _tex_menu[i] = NULL;
    _tex_item[i] = NULL;
    _tex_arrow[i] = NULL;
  }
  for (uint i = 0; i < OBJECT_STATE_NO; ++i) {
    _tex_sep[i] = NULL;
  }
  for (uint i = 0; i < DIRECTION_NO; ++i) {
    _pad[i] = 0;
  }
}

//! @brief PMenuData destructor
Theme::PMenuData::~PMenuData(void)
{
  unload();
}

//! @brief Parses CfgParser::Entry op_section, loads and verifies data.
//! @param op_section CfgParser::Entry with pmenu configuration.
void
Theme::PMenuData::load(CfgParser::Entry *op_section)
{
  op_section = op_section->get_section ();

  CfgParser::Entry *op_value;
  op_value = op_section->find_entry ("PAD");
  if (op_value)
    {
      vector<string> tok;
      if (Util::splitString (op_value->get_value (), tok, " \t", 4) == 4)
        {
          for (int i = 0; i < DIRECTION_NO; ++i)
            _pad[i] = strtol (tok[i].c_str(), NULL, 10);
        }
    }

  op_value = op_section->find_section ("FOCUSED");
  if (op_value)
    loadState (op_value, OBJECT_STATE_FOCUSED);
  op_value = op_section->find_section ("UNFOCUSED");
  if (op_value)
    loadState (op_value, OBJECT_STATE_UNFOCUSED);
  op_value = op_section->find_section ("SELECTED");
  if (op_value)
    loadState (op_value, OBJECT_STATE_SELECTED);

  check();
}

//! @brief Unloads data.
void
Theme::PMenuData::unload(void)
{
	for (uint i = 0; i <= OBJECT_STATE_NO; ++i) {
		FontHandler::instance()->returnFont(_font[i]);
		FontHandler::instance()->returnColor(_color[i]);
		TextureHandler::instance()->returnTexture(_tex_menu[i]);
		TextureHandler::instance()->returnTexture(_tex_item[i]);
		TextureHandler::instance()->returnTexture(_tex_arrow[i]);

		_font[i] = NULL;
		_color[i] = NULL;
		_tex_menu[i] = NULL;
		_tex_item[i] = NULL;
		_tex_arrow[i] = NULL;
	}

	for (uint i = 0; i < OBJECT_STATE_NO; ++i) {
		TextureHandler::instance()->returnTexture(_tex_sep[i]);
		_tex_sep[i] = NULL;
	}
}

//! @brief Check data properties, prints warning and tries to fix.
void
Theme::PMenuData::check(void)
{
  for (uint i = 0; i <= OBJECT_STATE_NO; ++i) {
    if (_font[i] == NULL) {
      _font[i] = FontHandler::instance()->getFont("");
    }
    if (_color[i] == NULL) {
      _color[i] = FontHandler::instance()->getColor("#000000");
    }
    if (_tex_menu[i] == NULL) {
      _tex_menu[i] = TextureHandler::instance()->getTexture("EMPTY");
    }
    if (_tex_item[i] == NULL) {
      _tex_item[i] = TextureHandler::instance()->getTexture("EMPTY");
    }
    if (_tex_arrow[i] == NULL) {
      _tex_arrow[i] = TextureHandler::instance()->getTexture("EMPTY");
    }
  }

  for (uint i = 0; i < OBJECT_STATE_NO; ++i) {
    if (_tex_sep[i] == NULL) {
      _tex_sep[i] = TextureHandler::instance()->getTexture("EMPTY");
    }
  }
}

//! @brief
void
Theme::PMenuData::loadState (CfgParser::Entry *op_section, ObjectState state)
{
  op_section = op_section->get_section ();

  list<CfgParserKey*> o_key_list;
  string o_value_font, o_value_background, o_value_item;
  string o_value_text, o_value_arrow, o_value_separator;

  o_key_list.push_back (new CfgParserKeyString ("FONT", o_value_font));
  o_key_list.push_back (new CfgParserKeyString ("BACKGROUND", o_value_background,
                                                "Solid #ffffff"));
  o_key_list.push_back (new CfgParserKeyString ("ITEM", o_value_item,
                                                "Solid #ffffff"));
  o_key_list.push_back (new CfgParserKeyString ("TEXT", o_value_text,
                                                "Solid #000000"));
  o_key_list.push_back (new CfgParserKeyString ("ARROW", o_value_arrow,
                                                "Solid #000000"));
  if (state < OBJECT_STATE_SELECTED)
    {
      o_key_list.push_back (new CfgParserKeyString ("SEPARATOR",
                                                    o_value_separator,
                                                    "Solid #000000"));
    }

  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*>());

  TextureHandler *th = TextureHandler::instance ();

  // Handle parsed data.
  _font[state] = FontHandler::instance ()->getFont (o_value_font);
  _tex_menu[state] = th->getTexture (o_value_background);
  _tex_item[state] = th->getTexture (o_value_item);
  _color[state] = FontHandler::instance ()->getColor (o_value_text);
  _tex_arrow[state] = th->getTexture (o_value_arrow);
  if (state < OBJECT_STATE_SELECTED)
    _tex_sep[state] = th->getTexture(o_value_separator);
}

// Theme::TextDialogData

//! @brief TextDialogData constructor.
Theme::TextDialogData::TextDialogData(void) :
_font(NULL), _color(NULL), _tex(NULL)
{
  for (uint i = 0; i < DIRECTION_NO; ++i) {
    _pad[i] = 0;
  }
}

//! @brief TextDialogData destructor.
Theme::TextDialogData::~TextDialogData(void)
{
  unload();
}

//! @brief Parses CfgParser::Entry op_section, loads and verifies data.
//! @param op_section CfgParser::Entry with textdialog configuration.
void
Theme::TextDialogData::load (CfgParser::Entry *op_section)
{
  op_section = op_section->get_section ();

  list<CfgParserKey*> o_key_list;
  string o_value_font, o_value_text, o_value_texture, o_value_pad;

  o_key_list.push_back (new CfgParserKeyString ("FONT", o_value_font));
  o_key_list.push_back (new CfgParserKeyString ("TEXT", o_value_text,
                                                "#000000"));
  o_key_list.push_back (new CfgParserKeyString ("TEXTURE", o_value_texture,
                                                "Solid #ffffff"));
  o_key_list.push_back (new CfgParserKeyString ("PAD", o_value_pad,
                                                "0 0 0 0", 7));

  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*>());

  // Handle parsed data.
  _font = FontHandler::instance ()->getFont (o_value_font);
  _color = FontHandler::instance ()->getColor (o_value_text);
  _tex = TextureHandler::instance ()->getTexture (o_value_texture);

  vector<string> tok;
  if (Util::splitString (o_value_pad, tok, " \t", 4) == 4)
    {
      for (uint i = 0; i < DIRECTION_NO; ++i)
        _pad[i] = strtol(tok[i].c_str(), NULL, 10);
    }

  check();
}

//! @brief Unloads data.
void
Theme::TextDialogData::unload(void)
{
	FontHandler::instance()->returnFont(_font);
	FontHandler::instance()->returnColor(_color);
	TextureHandler::instance()->returnTexture(_tex);

	_font = NULL;
	_tex = NULL;
	_color = NULL;
}

//! @brief Check data properties, prints warning and tries to fix.
//! @todo print warnings
void
Theme::TextDialogData::check(void)
{
	if (_font == NULL) {
		_font = FontHandler::instance()->getFont("");
	}
	if (_color == NULL) {
		_color = FontHandler::instance()->getColor("#000000");
	}
	if (_tex == NULL) {
		_tex = TextureHandler::instance()->getTexture("EMPTY");
	}
}

// Theme

//! @brief Theme constructor
Theme::Theme(PScreen *scr) :
_scr(scr), _image_handler(NULL),
_is_loaded(false), _invert_gc(None)
{
	// image handler
	_image_handler = new ImageHandler();

	// window gc's
	XGCValues gv;
	gv.function = GXinvert;
	gv.subwindow_mode = IncludeInferiors;
	gv.line_width = 1;
	_invert_gc = XCreateGC(_scr->getDpy(), _scr->getRoot(),
												 GCFunction|GCSubwindowMode|GCLineWidth, &gv);


	_scr->grabServer();

	load(Config::instance()->getThemeFile());

	_scr->ungrabServer(true);
}

//! @brief Theme destructor
Theme::~Theme(void)
{
	unload(); // should clean things up

	XFreeGC(_scr->getDpy(), _invert_gc);

	delete _image_handler;
}

//! @brief Loads the "ThemeFile", unloads any previous loaded theme.
void
Theme::load(const std::string &dir)
{
  if (_is_loaded)
    unload();

  CfgParser theme;

  _theme_dir = dir;
  if (_theme_dir.size ())
    {
      if (_theme_dir.at (_theme_dir.size() - 1) != '/')
        _theme_dir.append("/");
    }
  else
    {
      cerr << " *** WARNING: empty theme directory name, using default." << endl;
      _theme_dir = DATADIR "/pekwm/themes/default/";
    }

  string theme_file = _theme_dir + string("theme");
  if (!theme.parse (theme_file, CfgParserSource::SOURCE_FILE))
    {
       _theme_dir = DATADIR "/pekwm/themes/default/";
       theme_file = _theme_dir + string ("theme");
       if (!theme.parse (theme_file, CfgParserSource::SOURCE_FILE))
         {
           cerr << " *** WARNING: couldn't load " << _theme_dir
                << " or default theme." << endl;
         }
  }

  CfgParser::Entry *op_section, *op_value;

  // Set image basedir.
  _image_handler->setDir(_theme_dir);

  // Load decor data.
  op_section = theme.get_entry_root ()->find_section ("PDECOR");
  if (op_section)
    {
      // Get section data, don't need name or value.
      op_section = op_section->get_section ();

      //op_section = op_section->get_section ();
      while ((op_section = op_section->get_section_next ()) != NULL)
        {
          Theme::PDecorData *data = new Theme::PDecorData();
          if (data->load (op_section))
            _pdecordata_map[data->getName()] = data;
          else
            delete data;
       }
    }

  if (!getPDecorData ("DEFAULT"))
    {
      cerr << " *** WARNING: theme doesn't contain any DEFAULT decor." << endl;

      // Create DEFAULT decor, let check fill it up with empty but non-null data.
      Theme::PDecorData *data = new Theme::PDecorData();
      data->setName("DEFAULT");
      data->check();
      _pdecordata_map[data->getName()] = data;
    }

  // Load menu data.
  op_section = theme.get_entry_root ()->find_section ("MENU");
  if (op_section)
    _menu_data.load(op_section);
  else
    {
      cerr << " *** WARNING: missing menu section" << endl;
      _menu_data.check ();
    }

  // Load StatusWindow data.
  op_section = theme.get_entry_root ()->find_section ("STATUS");
  if (op_section)
    _status_data.load (op_section);
  else
    {
      cerr << " *** WARNING: missing status section" << endl;
      _status_data.check ();
    }

  // Load CmdDialog data.
  op_section = theme.get_entry_root ()->find_section ("CMDDIALOG");
  if (op_section)
    _cmd_d_data.load (op_section);
  else
    {
      cerr << " *** WARNING: missing cmddialog section" << endl;
      _cmd_d_data.check ();
    }

#ifdef HARBOUR
  // Load Harbour texture.
  op_section = theme.get_entry_root ()->find_section ("HARBOUR");
  if (op_section)
    {
      op_section = op_section->get_section ();

      op_value = op_section->find_entry ("TEXTURE");
      if (op_value)
        _harbour_texture = TextureHandler::instance ()->getTexture (op_value->get_value ());
    }
  else
    cerr << " *** WARNING: missing harbour section" << endl;

  if (!_harbour_texture)
    {
      cerr << " *** WARNING: missing harbour texture" << endl;
      _harbour_texture = TextureHandler::instance ()->getTexture("EMPTY");
    }
#endif // HARBOUR

  // Exec root command.
  op_section = theme.get_entry_root ()->find_section ("ROOT");
  if (op_section)
    {
      op_value = op_section->find_entry ("COMMAND");
      if (op_value)
        Util::forkExec (op_value->get_value ());
    }

  _is_loaded = true;
}

//! @brief Unloads all pixmaps, fonts, gc and colors allocated by the theme.
void
Theme::unload(void) {
	map<string, Theme::PDecorData*>::iterator it(_pdecordata_map.begin());
	for (; it != _pdecordata_map.end(); ++it) {
		delete it->second;
	}
	_pdecordata_map.clear();

#ifdef HARBOUR
	TextureHandler::instance()->returnTexture(_harbour_texture);
	_harbour_texture = NULL;
#endif // HARBOUR

	// unload menu
	_menu_data.unload();

	_status_data.unload();
	_cmd_d_data.unload();

	_is_loaded = false;
}


syntax highlighted by Code2HTML, v. 0.9.1