You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
basket/src/tag.cpp

935 lines
38 KiB

/***************************************************************************
* Copyright (C) 2005 by S<EFBFBD>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. *
***************************************************************************/
#include <kapplication.h>
#include <kstyle.h>
#include <kiconloader.h>
#include <tqpainter.h>
#include <tqfont.h>
#include <tqdom.h>
#include <tqdir.h>
#include <kglobalsettings.h>
#include <klocale.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 KAction("FAKE TEXT", "FAKE ICON", KShortcut(), TQT_TQOBJECT(Global::bnpView), TQT_SLOT(activatedTagShortcut()), Global::bnpView->actionCollection(), sAction);
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::tagForKAction(KAction *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(KShortcut(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 <kapplication.h>
#include <tqrect.h>
#include <tqstyle.h>
#include <tqcheckbox.h>
#include <tqbitmap.h>
#include <kglobalsettings.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 = KIcon::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, KIcon::Small, iconSize,
(enabled ? (active ? KIcon::ActiveState : KIcon::DefaultState) : KIcon::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 = KIcon::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 KIcon::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(), KIcon::Small, iconSize,
(enabled ? (active ? KIcon::ActiveState : KIcon::DefaultState) : KIcon::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 KIcon::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<KStyle*>(&(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().tqdrawPrimitive(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().tqdrawPrimitive(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().tqdrawPrimitive(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().tqdrawPrimitive(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().tqdrawPrimitive(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;
}