/***************************************************************************
 *   Copyright (C) 2005 by S�astien Laot                                 *
 *   slaout@linux62.org                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.             *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <tdeapplication.h>
#include <tdestyle.h>
#include <kiconloader.h>
#include <tqpainter.h>
#include <tqfont.h>
#include <tqdom.h>
#include <tqdir.h>
#include <tdeglobalsettings.h>
#include <tdelocale.h>

#include "tag.h"
#include "xmlwork.h"
#include "global.h"
#include "debugwindow.h"
#include "bnpview.h"
#include "tools.h"
#include "basket.h"

#include <iostream>

/** class State: */

State::State(const TQString &id, Tag *tag)
 : m_id(id), m_name(), m_emblem(), m_bold(false), m_italic(false), m_underline(false), m_strikeOut(false),
   m_textColor(), m_fontName(), m_fontSize(-1), m_backgroundColor(), m_textEquivalent(), m_onAllTextLines(false), m_parentTag(tag)
{
}

State::~State()
{
}

State* State::nextState(bool cycle /*= true*/)
{
	if (!parentTag())
		return 0;

	List states = parentTag()->states();
	// The tag contains only one state:
	if (states.count() == 1)
		return 0;
	// Find the next state:
	for (List::iterator it = states.begin(); it != states.end(); ++it)
		// Found the current state in the list:
		if (*it == this) {
			// Find the next state:
			State *next = *(++it);
			if (it == states.end())
				return (cycle ? states.first() : 0);
			return next;
		}
	// Should not happens:
	return 0;
}

TQString State::fullName()
{
	if (!parentTag() || parentTag()->states().count() == 1)
		return (name().isEmpty() && parentTag() ? parentTag()->name() : name());
	return TQString(i18n("%1: %2")).arg(parentTag()->name(), name());
}

TQFont State::font(TQFont base)
{
	if (bold())
		base.setBold(true);
	if (italic())
		base.setItalic(true);
	if (underline())
		base.setUnderline(true);
	if (strikeOut())
		base.setStrikeOut(true);
	if (!fontName().isEmpty())
		base.setFamily(fontName());
	if (fontSize() > 0)
		base.setPointSize(fontSize());
	return base;
}

TQString State::toCSS(const TQString &gradientFolderPath, const TQString &gradientFolderName, const TQFont &baseFont)
{
	TQString css;
	if (bold())
		css += " font-weight: bold;";
	if (italic())
		css += " font-style: italic;";
	if (underline() && strikeOut())
		css += " text-decoration: underline line-through;";
	else if (underline())
		css += " text-decoration: underline;";
	else if (strikeOut())
		css += " text-decoration: line-through;";
	if (textColor().isValid())
		css += " color: " + textColor().name() + ";";
	if (!fontName().isEmpty()) {
		TQString fontFamily = Tools::cssFontDefinition(fontName(), /*onlyFontFamily=*/true);
		css += " font-family: " + fontFamily + ";";
	}
	if (fontSize() > 0)
		css += " font-size: " + TQString::number(fontSize()) + "px;";
	if (backgroundColor().isValid()) {
		// Get the colors of the gradient and the border:
		TQColor topBgColor;
		TQColor bottomBgColor;
		Note::getGradientColors(backgroundColor(), &topBgColor, &bottomBgColor);
		// Produce the CSS code:
		TQString gradientFileName = Basket::saveGradientBackground(backgroundColor(), font(baseFont), gradientFolderPath);
		css += " background: " + bottomBgColor.name() + " url('" + gradientFolderName + gradientFileName + "') repeat-x;";
		css += " border-top: solid " + topBgColor.name() + " 1px;";
		css += " border-bottom: solid " + Tools::mixColor(topBgColor, bottomBgColor).name() + " 1px;";
	}

	if (css.isEmpty())
		return "";
	else
		return "   .tag_" + id() + " {" + css + " }\n";
}

void State::merge(const List &states, State *result, int *emblemsCount, bool *haveInvisibleTags, const TQColor &backgroundColor)
{
	*result            = State(); // Reset to default values.
	*emblemsCount      = 0;
	*haveInvisibleTags = false;

	for (List::const_iterator it = states.begin(); it != states.end(); ++it) {
		State *state = *it;
		bool isVisible = false;
		// For each propertie, if that properties have a value (is not default) is the current state of the list,
		// and if it haven't been set to the result state by a previous state, then it's visible and we assign the propertie to the result state.
		if (!state->emblem().isEmpty()) {
			++*emblemsCount;
			isVisible = true;
		}
		if (state->bold() && !result->bold()) {
			result->setBold(true);
			isVisible = true;
		}
		if (state->italic() && !result->italic()) {
			result->setItalic(true);
			isVisible = true;
		}
		if (state->underline() && !result->underline()) {
			result->setUnderline(true);
			isVisible = true;
		}
		if (state->strikeOut() && !result->strikeOut()) {
			result->setStrikeOut(true);
			isVisible = true;
		}
		if (state->textColor().isValid() && !result->textColor().isValid()) {
			result->setTextColor(state->textColor());
			isVisible = true;
		}
		if (!state->fontName().isEmpty() && result->fontName().isEmpty()) {
			result->setFontName(state->fontName());
			isVisible = true;
		}
		if (state->fontSize() > 0 && result->fontSize() <= 0) {
			result->setFontSize(state->fontSize());
			isVisible = true;
		}
		if (state->backgroundColor().isValid() && !result->backgroundColor().isValid() && state->backgroundColor() != backgroundColor) { // vv
			result->setBackgroundColor(state->backgroundColor()); // This is particular: if the note background color is the same as the basket one, don't use that.
			isVisible = true;
		}
		// If it's not visible, well, at least one tag is not visible: the note will display "..." at the tags arrow place to show that:
		if (!isVisible)
			*haveInvisibleTags = true;
	}
}

void State::copyTo(State *other)
{
	other->m_id              = m_id;
	other->m_name            = m_name;
	other->m_emblem          = m_emblem;
	other->m_bold            = m_bold;
	other->m_italic          = m_italic;
	other->m_underline       = m_underline;
	other->m_strikeOut       = m_strikeOut;
	other->m_textColor       = m_textColor;
	other->m_fontName        = m_fontName;
	other->m_fontSize        = m_fontSize;
	other->m_backgroundColor = m_backgroundColor;
	other->m_textEquivalent  = m_textEquivalent;
	other->m_onAllTextLines  = m_onAllTextLines; // TODO
	//TODO: other->m_parentTag;
}

/** class Tag: */

Tag::List Tag::all = Tag::List();

long Tag::nextStateUid = 1;

long Tag::getNextStateUid()
{
	return nextStateUid++; // Return the next Uid and THEN increment the Uid
}

Tag::Tag()
{
	static int tagNumber = 0;
	++tagNumber;
	TQString sAction = "tag_shortcut_number_" + TQString::number(tagNumber);
	m_action = new TDEAction("FAKE TEXT", "FAKE ICON", TDEShortcut(), Global::bnpView, TQ_SLOT(activatedTagShortcut()), Global::bnpView->actionCollection(), sAction.utf8());
	m_action->setShortcutConfigurable(false); // We do it in the tag properties dialog

	m_inheritedBySiblings = false;
}

Tag::~Tag()
{
	delete m_action;
}

void Tag::setName(const TQString &name)
{
	m_name = name;
	m_action->setText("TAG SHORTCUT: " + name); // TODO: i18n  (for debug purpose only by now).
}

State* Tag::stateForId(const TQString &id)
{
	for (List::iterator it = all.begin(); it != all.end(); ++it)
		for (State::List::iterator it2 = (*it)->states().begin(); it2 != (*it)->states().end(); ++it2)
			if ((*it2)->id() == id)
				return *it2;
	return 0;
}

Tag* Tag::tagForTDEAction(TDEAction *action)
{
	for (List::iterator it = all.begin(); it != all.end(); ++it)
		if ((*it)->m_action == action)
			return *it;
	return 0;
}

TQMap<TQString, TQString> Tag::loadTags(const TQString &path/* = TQString()*//*, bool merge = false*/)
{
	TQMap<TQString, TQString> mergedStates;

	bool merge = !path.isEmpty();
	TQString fullPath = (merge ? path : Global::savesFolder() + "tags.xml");
	TQString doctype  = "basketTags";

	TQDir dir;
	if (!dir.exists(fullPath)) {
		if (merge)
			return mergedStates;
		DEBUG_WIN << "Tags file does not exist: Creating it...";
		createDefaultTagsSet(fullPath);
	}

	TQDomDocument *document = XMLWork::openFile(doctype, fullPath);
	if (!document) {
		DEBUG_WIN << "<font color=red>FAILED to read the tags file</font>";
		return mergedStates;
	}

	TQDomElement docElem = document->documentElement();
	if (!merge)
		nextStateUid = docElem.attribute("nextStateUid", TQString::number(nextStateUid)).toLong();

	TQDomNode node = docElem.firstChild();
	while (!node.isNull()) {
		TQDomElement element = node.toElement();
		if ( (!element.isNull()) && element.tagName() == "tag" ) {
			Tag *tag = new Tag();
			// Load properties:
			TQString name      = XMLWork::getElementText(element, "name");
			TQString shortcut  = XMLWork::getElementText(element, "shortcut");
			TQString inherited = XMLWork::getElementText(element, "inherited", "false");
			tag->setName(name);
			tag->setShortcut(TDEShortcut(shortcut));
			tag->setInheritedBySiblings(XMLWork::trueOrFalse(inherited));
			// Load states:
			TQDomNode subNode = element.firstChild();
			while (!subNode.isNull()) {
				TQDomElement subElement = subNode.toElement();
				if ( (!subElement.isNull()) && subElement.tagName() == "state" ) {
					State *state = new State(subElement.attribute("id"), tag);
					state->setName(   XMLWork::getElementText(subElement, "name")   );
					state->setEmblem( XMLWork::getElementText(subElement, "emblem") );
					TQDomElement textElement = XMLWork::getElement(subElement, "text");
					state->setBold(      XMLWork::trueOrFalse(textElement.attribute("bold",      "false")) );
					state->setItalic(    XMLWork::trueOrFalse(textElement.attribute("italic",    "false")) );
					state->setUnderline( XMLWork::trueOrFalse(textElement.attribute("underline", "false")) );
					state->setStrikeOut( XMLWork::trueOrFalse(textElement.attribute("strikeOut", "false")) );
					TQString textColor = textElement.attribute("color", "");
					state->setTextColor(textColor.isEmpty() ? TQColor() : TQColor(textColor));
					TQDomElement fontElement = XMLWork::getElement(subElement, "font");
					state->setFontName(fontElement.attribute("name", ""));
					TQString fontSize = fontElement.attribute("size", "");
					state->setFontSize(fontSize.isEmpty() ? -1 : fontSize.toInt());
					TQString backgroundColor = XMLWork::getElementText(subElement, "backgroundColor", "");
					state->setBackgroundColor(backgroundColor.isEmpty() ? TQColor() : TQColor(backgroundColor));
					TQDomElement textEquivalentElement = XMLWork::getElement(subElement, "textEquivalent");
					state->setTextEquivalent( textEquivalentElement.attribute("string", "") );
					state->setOnAllTextLines( XMLWork::trueOrFalse(textEquivalentElement.attribute("onAllTextLines", "false")) );
					tag->appendState(state);
				}
				subNode = subNode.nextSibling();
			}
			// If the Tag is Valid:
			if (tag->countStates() > 0) {
				// Rename Things if Needed:
				State *firstState = tag->states().first();
				if (tag->countStates() == 1 && firstState->name().isEmpty())
					firstState->setName(tag->name());
				if (tag->name().isEmpty())
					tag->setName(firstState->name());
				// Add or Merge the Tag:
				if (!merge) {
					all.append(tag);
				} else {
					Tag *similarTag = tagSimilarTo(tag);
					// Tag does not exists, add it:
					if (similarTag == 0) {
						// We are merging the new states, so we should choose new and unique (on that computer) ids for those states:
						for (State::List::iterator it = tag->states().begin(); it != tag->states().end(); ++it) {
							State *state = *it;
							TQString uid    = state->id();
							TQString newUid = "tag_state_" + TQString::number(getNextStateUid());
							state->setId(newUid);
							mergedStates[uid] = newUid;
						}
						// TODO: if shortcut is already assigned to a previous note, do not import it, keep the user settings!
						all.append(tag);
					// Tag already exists, rename to theire ids:
					} else {
						State::List::iterator it2 = similarTag->states().begin();
						for (State::List::iterator it = tag->states().begin(); it != tag->states().end(); ++it, ++it2) {
							State *state        = *it;
							State *similarState = *it2;
							TQString uid    = state->id();
							TQString newUid = similarState->id();
							if (uid != newUid)
								mergedStates[uid] = newUid;
						}
						delete tag; // Already exists, not to be merged. Delete the shortcut and all.
					}
				}
			}
		}
		node = node.nextSibling();
	}

	return mergedStates;
}

Tag* Tag::tagSimilarTo(Tag *tagToTest)
{
	// Tags are considered similar if they have the same name, the same number of states, in the same order, and the same look.
	// Keyboard shortcut, text equivalent and onEveryLines are user settings, and thus not considered during the comparision.
	// Default tags (To Do, Important, Idea...) do not take into account the name of the tag and states during the comparision.
	// Default tags are equal only if they have the same number of states, in the same order, and the same look.
	// This is because default tag names are translated differently in every countries, but they are essentialy the same!
	// User tags begins with "tag_state_" followed by a number. Default tags are the other ones.

	// Browse all tags:
	for (List::iterator it = all.begin(); it != all.end(); ++it) {
		Tag *tag = *it;
		bool same = true;
		bool sameName;
		bool defaultTag = true;
		// We test only name and look. Shorcut and whenever it is inherited by sibling new notes are user settings only!
		sameName = tag->name() == tagToTest->name();
		if (tag->countStates() != tagToTest->countStates())
			continue; // Tag is different!
		// We found a tag with same name, check if every states/look are same too:
		State::List::iterator itTest = tagToTest->states().begin();
		for (State::List::iterator it2 = (*it)->states().begin(); it2 != (*it)->states().end(); ++it2, ++itTest) {
			State *state       = *it2;
			State *stateToTest = *itTest;
			if (state->id().startsWith("tag_state_") || stateToTest->id().startsWith("tag_state_")) { defaultTag = false; }
			if (state->name()            != stateToTest->name())            { sameName = false;    }
			if (state->emblem()          != stateToTest->emblem())          { same = false; break; }
			if (state->bold()            != stateToTest->bold())            { same = false; break; }
			if (state->italic()          != stateToTest->italic())          { same = false; break; }
			if (state->underline()       != stateToTest->underline())       { same = false; break; }
			if (state->strikeOut()       != stateToTest->strikeOut())       { same = false; break; }
			if (state->textColor()       != stateToTest->textColor())       { same = false; break; }
			if (state->fontName()        != stateToTest->fontName())        { same = false; break; }
			if (state->fontSize()        != stateToTest->fontSize())        { same = false; break; }
			if (state->backgroundColor() != stateToTest->backgroundColor()) { same = false; break; }
			// Text equivalent (as well as onAllTextLines) is also a user setting!
		}
		// We found an existing tag that is "exactly" the same:
		if (same && (sameName || defaultTag))
			return tag;
	}

	// Not found:
	return 0;
}

void Tag::saveTags()
{
	DEBUG_WIN << "Saving tags...";
	saveTagsTo(all, Global::savesFolder() + "tags.xml");
}

void Tag::saveTagsTo(TQValueList<Tag*> &list, const TQString &fullPath)
{
	// Create Document:
	TQDomDocument document(/*doctype=*/"basketTags");
	TQDomElement root = document.createElement("basketTags");
	root.setAttribute("nextStateUid", nextStateUid);
	document.appendChild(root);

	// Save all tags:
	for (List::iterator it = list.begin(); it != list.end(); ++it) {
		Tag *tag = *it;
		// Create tag node:
		TQDomElement tagNode = document.createElement("tag");
		root.appendChild(tagNode);
		// Save tag properties:
		XMLWork::addElement( document, tagNode, "name",      tag->name()                                      );
		XMLWork::addElement( document, tagNode, "shortcut",  tag->shortcut().toStringInternal()               );
		XMLWork::addElement( document, tagNode, "inherited", XMLWork::trueOrFalse(tag->inheritedBySiblings()) );
		// Save all states:
		for (State::List::iterator it2 = (*it)->states().begin(); it2 != (*it)->states().end(); ++it2) {
			State *state = *it2;
			// Create state node:
			TQDomElement stateNode = document.createElement("state");
			tagNode.appendChild(stateNode);
			// Save state properties:
			stateNode.setAttribute("id", state->id());
			XMLWork::addElement( document, stateNode, "name",   state->name()   );
			XMLWork::addElement( document, stateNode, "emblem", state->emblem() );
			TQDomElement textNode = document.createElement("text");
			stateNode.appendChild(textNode);
			TQString textColor = (state->textColor().isValid() ? state->textColor().name() : "");
			textNode.setAttribute( "bold",      XMLWork::trueOrFalse(state->bold())      );
			textNode.setAttribute( "italic",    XMLWork::trueOrFalse(state->italic())    );
			textNode.setAttribute( "underline", XMLWork::trueOrFalse(state->underline()) );
			textNode.setAttribute( "strikeOut", XMLWork::trueOrFalse(state->strikeOut()) );
			textNode.setAttribute( "color",     textColor                                );
			TQDomElement fontNode = document.createElement("font");
			stateNode.appendChild(fontNode);
			fontNode.setAttribute( "name", state->fontName() );
			fontNode.setAttribute( "size", state->fontSize() );
			TQString backgroundColor = (state->backgroundColor().isValid() ? state->backgroundColor().name() : "");
			XMLWork::addElement( document, stateNode, "backgroundColor", backgroundColor );
			TQDomElement textEquivalentNode = document.createElement("textEquivalent");
			stateNode.appendChild(textEquivalentNode);
			textEquivalentNode.setAttribute( "string",         state->textEquivalent()                       );
			textEquivalentNode.setAttribute( "onAllTextLines", XMLWork::trueOrFalse(state->onAllTextLines()) );
		}
	}

	// Write to Disk:
	if (!Basket::safelySaveToFile(fullPath, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" + document.toString()))
		DEBUG_WIN << "<font color=red>FAILED to save tags</font>!";
}

void Tag::copyTo(Tag *other)
{
	other->m_name = m_name;
	other->m_action->setShortcut(m_action->shortcut());
	other->m_inheritedBySiblings =  m_inheritedBySiblings;
}

void Tag::createDefaultTagsSet(const TQString &fullPath)
{
	TQString xml = TQString(
		"<!DOCTYPE basketTags>\n"
		"<basketTags>\n"
		"  <tag>\n"
		"    <name>%1</name>\n" // "To Do"
		"    <shortcut>Ctrl+1</shortcut>\n"
		"    <inherited>true</inherited>\n"
		"    <state id=\"todo_unchecked\">\n"
		"      <name>%2</name>\n" // "Unchecked"
		"      <emblem>tag_checkbox</emblem>\n"
		"      <text bold=\"false\" italic=\"false\" underline=\"false\" strikeOut=\"false\" color=\"\" />\n"
		"      <font name=\"\" size=\"\" />\n"
		"      <backgroundColor></backgroundColor>\n"
		"      <textEquivalent string=\"[ ]\" onAllTextLines=\"false\" />\n"
		"    </state>\n"
		"    <state id=\"todo_done\">\n"
		"      <name>%3</name>\n" // "Done"
		"      <emblem>tag_checkbox_checked</emblem>\n"
		"      <text bold=\"false\" italic=\"false\" underline=\"false\" strikeOut=\"true\" color=\"\" />\n"
		"      <font name=\"\" size=\"\" />\n"
		"      <backgroundColor></backgroundColor>\n"
		"      <textEquivalent string=\"[x]\" onAllTextLines=\"false\" />\n"
		"    </state>\n"
		"  </tag>\n"
		"\n"
		"  <tag>\n"
		"    <name>%4</name>\n" // "Progress"
		"    <shortcut>Ctrl+2</shortcut>\n"
		"    <inherited>true</inherited>\n"
		"    <state id=\"progress_000\">\n"
		"      <name>%5</name>\n" // "0 %"
		"      <emblem>tag_progress_000</emblem>\n"
		"      <textEquivalent string=\"[    ]\" />\n"
		"    </state>\n"
		"    <state id=\"progress_025\">\n"
		"      <name>%6</name>\n" // "25 %"
		"      <emblem>tag_progress_025</emblem>\n"
		"      <textEquivalent string=\"[=   ]\" />\n"
		"    </state>\n"
		"    <state id=\"progress_050\">\n"
		"      <name>%7</name>\n" // "50 %"
		"      <emblem>tag_progress_050</emblem>\n"
		"      <textEquivalent string=\"[==  ]\" />\n"
		"    </state>\n"
		"    <state id=\"progress_075\">\n"
		"      <name>%8</name>\n" // "75 %"
		"      <emblem>tag_progress_075</emblem>\n"
		"      <textEquivalent string=\"[=== ]\" />\n"
		"    </state>\n"
		"    <state id=\"progress_100\">\n"
		"      <name>%9</name>\n" // "100 %"
		"      <emblem>tag_progress_100</emblem>\n"
		"      <textEquivalent string=\"[====]\" />\n"
		"    </state>\n"
		"  </tag>\n"
		"\n")
			.arg( i18n("To Do"),     i18n("Unchecked"),      i18n("Done")        )  // %1 %2 %3
			.arg( i18n("Progress"),  i18n("0 %"),            i18n("25 %")        )  // %4 %5 %6
			.arg( i18n("50 %"),      i18n("75 %"),           i18n("100 %")       )  // %7 %8 %9
	+ TQString(
		"  <tag>\n"
		"    <name>%1</name>\n" // "Priority"
		"    <shortcut>Ctrl+3</shortcut>\n"
		"    <inherited>true</inherited>\n"
		"    <state id=\"priority_low\">\n"
		"      <name>%2</name>\n" // "Low"
		"      <emblem>tag_priority_low</emblem>\n"
		"      <textEquivalent string=\"{1}\" />\n"
		"    </state>\n"
		"    <state id=\"priority_medium\">\n"
		"      <name>%3</name>\n" // "Medium
		"      <emblem>tag_priority_medium</emblem>\n"
		"      <textEquivalent string=\"{2}\" />\n"
		"    </state>\n"
		"    <state id=\"priority_high\">\n"
		"      <name>%4</name>\n" // "High"
		"      <emblem>tag_priority_high</emblem>\n"
		"      <textEquivalent string=\"{3}\" />\n"
		"    </state>\n"
		"  </tag>\n"
		"\n"
		"  <tag>\n"
		"    <name>%5</name>\n" // "Preference"
		"    <shortcut>Ctrl+4</shortcut>\n"
		"    <inherited>true</inherited>\n"
		"    <state id=\"preference_bad\">\n"
		"      <name>%6</name>\n" // "Bad"
		"      <emblem>tag_preference_bad</emblem>\n"
		"      <textEquivalent string=\"(*  )\" />\n"
		"    </state>\n"
		"    <state id=\"preference_good\">\n"
		"      <name>%7</name>\n" // "Good"
		"      <emblem>tag_preference_good</emblem>\n"
		"      <textEquivalent string=\"(** )\" />\n"
		"    </state>\n"
		"    <state id=\"preference_excelent\">\n"
		"      <name>%8</name>\n" // "Excellent"
		"      <emblem>tag_preference_excelent</emblem>\n" // "excelent": typo error, but we should keep compatibility with old versions.
		"      <textEquivalent string=\"(***)\" />\n"
		"    </state>\n"
		"  </tag>\n"
		"\n"
		"  <tag>\n"
		"    <name>%9</name>\n" // "Highlight"
		"    <shortcut>Ctrl+5</shortcut>\n"
		"    <state id=\"highlight\">\n"
		"      <backgroundColor>#ffffcc</backgroundColor>\n"
		"      <textEquivalent string=\"=>\" />\n"
		"    </state>\n"
		"  </tag>\n"
		"\n")
			.arg( i18n("Priority"),  i18n("Low"),            i18n("Medium")      )  // %1 %2 %3
			.arg( i18n("High"),      i18n("Preference"),     i18n("Bad")         )  // %4 %5 %6
			.arg( i18n("Good"),      i18n("Excellent"),      i18n("Highlight")   )  // %7 %8 %9
	+ TQString(
		"  <tag>\n"
		"    <name>%1</name>\n" // "Important"
		"    <shortcut>Ctrl+6</shortcut>\n"
		"    <state id=\"important\">\n"
		"      <emblem>tag_important</emblem>\n"
		"      <backgroundColor>#ffcccc</backgroundColor>\n"
		"      <textEquivalent string=\"!!\" />\n"
		"    </state>\n"
		"  </tag>\n"
		"\n"
		"  <tag>\n"
		"    <name>%2</name>\n" // "Very Important"
		"    <shortcut>Ctrl+7</shortcut>\n"
		"    <state id=\"very_important\">\n"
		"      <emblem>tag_important</emblem>\n"
		"      <text color=\"#ffffff\" />\n"
		"      <backgroundColor>#ff0000</backgroundColor>\n"
		"      <textEquivalent string=\"/!\\\" />\n"
		"    </state>\n"
		"  </tag>\n"
		"\n"
		"  <tag>\n"
		"    <name>%3</name>\n" // "Information"
		"    <shortcut>Ctrl+8</shortcut>\n"
		"    <state id=\"information\">\n"
		"      <emblem>messagebox_info</emblem>\n"
		"      <textEquivalent string=\"(i)\" />\n"
		"    </state>\n"
		"  </tag>\n"
		"\n"
		"  <tag>\n"
		"    <name>%4</name>\n" // "Idea"
		"    <shortcut>Ctrl+9</shortcut>\n"
		"    <state id=\"idea\">\n"
		"      <emblem>ktip</emblem>\n"
		"      <textEquivalent string=\"%5\" />\n" // I.
		"    </state>\n"
		"  </tag>""\n"
		"\n"
		"  <tag>\n"
		"    <name>%6</name>\n" // "Title"
		"    <shortcut>Ctrl+0</shortcut>\n"
		"    <state id=\"title\">\n"
		"      <text bold=\"true\" />\n"
		"      <textEquivalent string=\"##\" />\n"
		"    </state>\n"
		"  </tag>\n"
		"\n"
		"  <tag>\n"
		"    <name>%7</name>\n" // "Code"
		"    <state id=\"code\">\n"
		"      <font name=\"monospace\" />\n"
		"      <textEquivalent string=\"|\" onAllTextLines=\"true\" />\n"
		"    </state>\n"
		"  </tag>\n"
		"\n"
		"  <tag>\n"
		"    <state id=\"work\">\n"
		"      <name>%8</name>\n" // "Work"
		"      <text color=\"#ff8000\" />\n"
		"      <textEquivalent string=\"%9\" />\n" // W.
		"    </state>\n"
		"  </tag>""\n"
		"\n")
			.arg( i18n("Important"), i18n("Very Important"),              i18n("Information")                 ) // %1 %2 %3
			.arg( i18n("Idea"),      i18n("The initial of 'Idea'", "I."), i18n("Title")                       ) // %4 %5 %6
			.arg( i18n("Code"),      i18n("Work"),                        i18n("The initial of 'Work'", "W.") ) // %7 %8 %9
	+ TQString(
		"  <tag>\n"
		"    <state id=\"personal\">\n"
		"      <name>%1</name>\n" // "Personal"
		"      <text color=\"#008000\" />\n"
		"      <textEquivalent string=\"%2\" />\n" // P.
		"    </state>\n"
		"  </tag>\n"
		"\n"
		"  <tag>\n"
		"    <state id=\"funny\">\n"
		"      <name>%3</name>\n" // "Funny"
		"      <emblem>tag_fun</emblem>\n"
		"    </state>\n"
		"  </tag>\n"
		"</basketTags>\n"
		"")
			.arg( i18n("Personal"), i18n("The initial of 'Personal'", "P."), i18n("Funny") ); // %1 %2 %3

	// Write to Disk:
	TQFile file(fullPath);
	if (file.open(IO_WriteOnly)) {
		TQTextStream stream(&file);
		stream.setEncoding(TQTextStream::UnicodeUTF8);
		stream << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
		stream << xml;
		file.close();
	} else
		DEBUG_WIN << "<font color=red>FAILED to create the tags file</font>!";
}

#include <tdeapplication.h>
#include <tqrect.h>
#include <tqstyle.h>
#include <tqcheckbox.h>
#include <tqbitmap.h>
#include <tdeglobalsettings.h>
#include <tqimage.h>
#include <tqradiobutton.h>
#include <kiconeffect.h>

/** class IndentedMenuItem: */

IndentedMenuItem::IndentedMenuItem(const TQString &text, const TQString &icon, const TQString &shortcut)
 : m_text(text), m_icon(icon), m_shortcut(shortcut)
{
}

IndentedMenuItem::~IndentedMenuItem()
{
}

void IndentedMenuItem::paint(TQPainter *painter, const TQColorGroup &cg, bool active, bool enabled, int x, int y, int w, int h)
{
	TQPen  pen  = painter->pen();
	TQFont font = painter->font();

	int iconSize   = TDEIcon::SizeSmall;
	int iconMargin = StateMenuItem::iconMargin();

	/* When an item is disabled, it often have a 3D sunken look.
	 * This is done by calling this paint routine two times, with different pen color and offset.
	 * A disabled item is first painted in the rect (x+1, y+1, w, h) and with pen of cg.light() color,
	 * It is then drawn a second time in the rect (x, y, w, h).
	 * But we don't want to draw the icon two times! So, we try to detect if we are in the "etched-text draw" state and then don't draw the icon.
	 * This doesn't work for every styles but it's already better than nothing (styles when it doesn't work are seldomly used, if used).
	 */
	bool drawingEtchedText = !enabled && !active && painter->pen().color() != cg.mid()/*== cg.foreground()*/;
	if (drawingEtchedText) {
		TQString styleName = kapp->style().name();
		if (styleName == "plastik" || styleName == "lipstik")
			painter->setPen(cg.light());
		drawingEtchedText = !enabled && !active && painter->pen().color() != cg.foreground();
	} else
		drawingEtchedText = !enabled && !active && painter->pen().color() == cg.light();
	if (!m_icon.isEmpty() && !drawingEtchedText) {
		TQPixmap icon = kapp->iconLoader()->loadIcon(m_icon, TDEIcon::Small, iconSize,
		                                            (enabled ? (active ? TDEIcon::ActiveState : TDEIcon::DefaultState) : TDEIcon::DisabledState),
		                                            /*path_store=*/0L, /*canReturnNull=*/true);
		painter->drawPixmap(x, y + (h-iconSize)/2, icon);
	}
	/* Pen and font are already set to the good ones, so we can directly draw the text.
	 * BUT, for the half of styles provided with KDE, the pen is not set for the Active state (when hovered by mouse of selected by keyboard).
	 * So, I set the pen myself.
	 * But it's certainly a bug in those styles because some other styles eg. just draw a 3D sunken rect when an item is selected
	 * and keep the background to white, drawing a white text over it is... very bad. But I can't see what can be done.
	 */
	if (active && enabled)
		painter->setPen(TDEGlobalSettings::highlightedTextColor());
	painter->drawText(x + iconSize + iconMargin, y, w - iconSize - iconMargin, h, AlignLeft | AlignVCenter | DontClip | ShowPrefix, m_text/*painter->pen().color().name()*/);

	if (!m_shortcut.isEmpty()) {
		painter->setPen(pen);
		if (active && enabled)
			painter->setPen(TDEGlobalSettings::highlightedTextColor());
		painter->setFont(font);
		painter->setClipping(false);
		painter->drawText(x + 5 + w, y, 3000, h, AlignLeft | AlignVCenter | DontClip | ShowPrefix, m_shortcut);
	}
}

TQSize IndentedMenuItem::sizeHint()
{
	int iconSize   = TDEIcon::SizeSmall;
	int iconMargin = StateMenuItem::iconMargin();
	TQSize textSize = TQFontMetrics(TDEGlobalSettings::menuFont()).size( AlignLeft | AlignVCenter | ShowPrefix | DontClip,  m_text );
	return TQSize(iconSize + iconMargin + textSize.width(), textSize.height());
}

/** class StateMenuItem: */

StateMenuItem::StateMenuItem(State *state, const TQString &shortcut, bool withTagName)
 : m_state(state), m_shortcut(shortcut)
{
	m_name = (withTagName && m_state->parentTag() ? m_state->parentTag()->name() : m_state->name());
}

StateMenuItem::~StateMenuItem()
{
}

void StateMenuItem::paint(TQPainter *painter, const TQColorGroup &cg, bool active, bool enabled, int x, int y, int w, int h)
{
	TQPen  pen  = painter->pen();
	TQFont font = painter->font();

	int iconSize   = 16; // We use 16 instead of TDEIcon::SizeSmall (the size of icons in menus) because tags will always be 16*16 icons

	if (!active && m_state->backgroundColor().isValid())
		painter->fillRect(x/*-1*/, y/*-1*/, w/*+2*/, h/*+2*/, m_state->backgroundColor());
	/* When an item is disabled, it often have a 3D sunken look.
	 * This is done by calling this paint routine two times, with different pen color and offset.
	 * A disabled item is first painted in the rect (x+1, y+1, w, h) and with pen of cg.light() color,
	 * It is then drawn a second time in the rect (x, y, w, h).
	 * But we don't want to draw the icon two times! So, we try to detect if we are in the "etched-text draw" state and then don't draw the icon.
	 * This doesn't work for every styles but it's already better than nothing (styles when it doesn't work are seldomly used, if used).
	 */
	bool drawingEtchedText = !enabled && !active && painter->pen().color() != cg.mid()/*== cg.foreground()*/;
	if (drawingEtchedText) {
		TQString styleName = kapp->style().name();
		if (styleName == "plastik" || styleName == "lipstik")
			painter->setPen(cg.light());
		drawingEtchedText = !enabled && !active && painter->pen().color() != cg.foreground();
	} else
		drawingEtchedText = !enabled && !active && painter->pen().color() == cg.light();
	if (!m_state->emblem().isEmpty() && !drawingEtchedText) {
		TQPixmap icon = kapp->iconLoader()->loadIcon(m_state->emblem(), TDEIcon::Small, iconSize,
		                                            (enabled ? (active ? TDEIcon::ActiveState : TDEIcon::DefaultState) : TDEIcon::DisabledState),
		                                            /*path_store=*/0L, /*canReturnNull=*/true);
		painter->drawPixmap(x, y + (h-iconSize)/2, icon);
	}
	if (enabled && !active && m_state->textColor().isValid())
		painter->setPen(m_state->textColor());
	/* Pen and font are already set to the good ones, so we can directly draw the text.
	 * BUT, for the half of styles provided with KDE, the pen is not set for the Active state (when hovered by mouse of selected by keyboard).
	 * So, I set the pen myself.
	 * But it's certainly a bug in those styles because some other styles eg. just draw a 3D sunken rect when an item is selected
	 * and keep the background to white, drawing a white text over it is... very bad. But I can't see what can be done.
	 */
	if (active && enabled)
		painter->setPen(TDEGlobalSettings::highlightedTextColor());
	painter->setFont( m_state->font(painter->font()) );
	painter->drawText(x + iconSize + iconMargin(), y, w - iconSize - iconMargin(), h, AlignLeft | AlignVCenter | DontClip | ShowPrefix, m_name);

	if (!m_shortcut.isEmpty()) {
		painter->setPen(pen);
		if (active && enabled)
			painter->setPen(TDEGlobalSettings::highlightedTextColor());
		painter->setFont(font);
		painter->setClipping(false);
		painter->drawText(x + 5 + w, y, 3000, h, AlignLeft | AlignVCenter | DontClip | ShowPrefix, m_shortcut);
	}
}

TQSize StateMenuItem::sizeHint()
{
	int iconSize   = 16; // We use 16 instead of TDEIcon::SizeSmall (the size of icons in menus) because tags will always be 16*16 icons
	TQFont theFont = m_state->font(TDEGlobalSettings::menuFont());
	TQSize textSize = TQFontMetrics(theFont).size( AlignLeft | AlignVCenter | ShowPrefix | DontClip,  m_name );
	return TQSize(iconSize + iconMargin() + textSize.width(), textSize.height());
}

TQIconSet StateMenuItem::checkBoxIconSet(bool checked, TQColorGroup cg)
{
	int width  = kapp->style().pixelMetric(TQStyle::PM_IndicatorWidth,  0);
	int height = kapp->style().pixelMetric(TQStyle::PM_IndicatorHeight, 0);
	TQRect rect(0, 0, width, height);

	TQColor menuBackgroundColor = (dynamic_cast<TDEStyle*>(&(kapp->style())) == NULL ? TQColor(cg.background()) : cg.background().light(103));

	// Enabled, Not hovering
	TQPixmap  pixmap(width, height);
	pixmap.fill(menuBackgroundColor); // In case the pixelMetric() haven't returned a bigger rectangle than what drawPrimitive() draws
	TQPainter painter(&pixmap);
	int style = TQStyle::Style_Enabled | TQStyle::Style_Active | (checked ? TQStyle::Style_On : TQStyle::Style_Off);
	TQColor background = cg.color(TQColorGroup::Background);
	kapp->style().drawPrimitive(TQStyle::PE_Indicator, &painter, rect, cg, style);
	painter.end();

	// Enabled, Hovering
	TQPixmap  pixmapHover(width, height);
	pixmapHover.fill(menuBackgroundColor); // In case the pixelMetric() haven't returned a bigger rectangle than what drawPrimitive() draws
	painter.begin(&pixmapHover);
	style |= TQStyle::Style_MouseOver;
	cg.setColor(TQColorGroup::Background, TDEGlobalSettings::highlightColor());
	kapp->style().drawPrimitive(TQStyle::PE_Indicator, &painter, rect, cg, style);
	painter.end();

	// Disabled
	TQPixmap  pixmapDisabled(width, height);
	pixmapDisabled.fill(menuBackgroundColor); // In case the pixelMetric() haven't returned a bigger rectangle than what drawPrimitive() draws
	painter.begin(&pixmapDisabled);
	style = /*TQStyle::Style_Enabled | */TQStyle::Style_Active | (checked ? TQStyle::Style_On : TQStyle::Style_Off);
	cg.setColor(TQColorGroup::Background, background);
	kapp->style().drawPrimitive(TQStyle::PE_Indicator, &painter, rect, cg, style);
	painter.end();

	TQIconSet iconSet(pixmap);
	iconSet.setPixmap(pixmapHover,         TQIconSet::Automatic, TQIconSet::Active);
	iconSet.setPixmap(pixmapDisabled,      TQIconSet::Automatic, TQIconSet::Disabled);
	return iconSet;
}

TQIconSet StateMenuItem::radioButtonIconSet(bool checked, TQColorGroup cg)
{
	int width  = kapp->style().pixelMetric(TQStyle::PM_ExclusiveIndicatorWidth,  0);
	int height = kapp->style().pixelMetric(TQStyle::PM_ExclusiveIndicatorHeight, 0);
	TQRect rect(0, 0, width, height);

	int style = TQStyle::Style_Default | TQStyle::Style_Enabled | (checked ? TQStyle::Style_On : TQStyle::Style_Off);

	TQPixmap pixmap(width, height);
	pixmap.fill(TQt::red);
	TQPainter painter(&pixmap);
	/* We can't use that line of code (like for checkboxes):
	 * //kapp->style().drawPrimitive(TQStyle::PE_ExclusiveIndicator, &painter, rect, cg, style);
	 * because Plastik (and derived styles) don't care of the TQStyle::Style_On flag and will ALWAYS draw an unchecked radiobutton.
	 * So, we use another method:
	 */
	TQRadioButton rb(0);
	rb.setChecked(checked);
	kapp->style().drawControl(TQStyle::CE_RadioButton, &painter, &rb, rect, cg, style);
	painter.end();
	/* Some styles like Plastik (and derived ones) have TQStyle::PE_ExclusiveIndicator drawing a radiobutton disc, as wanted,
	 * and leave pixels ouside it untouched, BUT TQStyle::PE_ExclusiveIndicatorMask is a fully black square.
	 * So, we can't apply the mask to make the radiobutton circle transparent outside.
	 * We're using an hack by filling the pixmap in TQt::red, drawing the radiobutton and then creating an heuristic mask.
	 * The heuristic mask is created using the 4 edge pixels (that are red) and by making transparent every pixels that are of this color:
	 */
	pixmap.setMask(pixmap.createHeuristicMask());

	TQPixmap pixmapHover(width, height);
	pixmapHover.fill(TQt::red);
	painter.begin(&pixmapHover);
	//kapp->style().drawPrimitive(TQStyle::PE_ExclusiveIndicator, &painter, rect, cg, style);
	style |= TQStyle::Style_MouseOver;
	cg.setColor(TQColorGroup::Background, TDEGlobalSettings::highlightColor());
	kapp->style().drawControl(TQStyle::CE_RadioButton, &painter, &rb, rect, cg, style);
	painter.end();
	pixmapHover.setMask(pixmapHover.createHeuristicMask());

	TQIconSet iconSet(pixmap);
	iconSet.setPixmap(pixmapHover, TQIconSet::Automatic, TQIconSet::Active);
	return iconSet;
}
