/*************************************************************************** * 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. * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include "tag.h" #include "xmlwork.h" #include "global.h" #include "debugwindow.h" #include "bnpview.h" #include "tools.h" #include "basket.h" #include /** 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 (!tqparentTag()) return 0; List states = tqparentTag()->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 (!tqparentTag() || tqparentTag()->states().count() == 1) return (name().isEmpty() && tqparentTag() ? tqparentTag()->name() : name()); return TQString(i18n("%1: %2")).tqarg(tqparentTag()->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 Tag::loadTags(const TQString &path/* = TQString()*//*, bool merge = false*/) { TQMap 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 << "FAILED to read the tags file"; 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 &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, "\n" + document.toString())) DEBUG_WIN << "FAILED to save tags!"; } 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( "\n" "\n" " \n" " %1\n" // "To Do" " Ctrl+1\n" " true\n" " \n" " %2\n" // "Unchecked" " tag_checkbox\n" " \n" " \n" " \n" " \n" " \n" " \n" " %3\n" // "Done" " tag_checkbox_checked\n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " %4\n" // "Progress" " Ctrl+2\n" " true\n" " \n" " %5\n" // "0 %" " tag_progress_000\n" " \n" " \n" " \n" " %6\n" // "25 %" " tag_progress_025\n" " \n" " \n" " \n" " %7\n" // "50 %" " tag_progress_050\n" " \n" " \n" " \n" " %8\n" // "75 %" " tag_progress_075\n" " \n" " \n" " \n" " %9\n" // "100 %" " tag_progress_100\n" " \n" " \n" " \n" "\n") .tqarg( i18n("To Do"), i18n("Unchecked"), i18n("Done") ) // %1 %2 %3 .tqarg( i18n("Progress"), i18n("0 %"), i18n("25 %") ) // %4 %5 %6 .tqarg( i18n("50 %"), i18n("75 %"), i18n("100 %") ) // %7 %8 %9 + TQString( " \n" " %1\n" // "Priority" " Ctrl+3\n" " true\n" " \n" " %2\n" // "Low" " tag_priority_low\n" " \n" " \n" " \n" " %3\n" // "Medium " tag_priority_medium\n" " \n" " \n" " \n" " %4\n" // "High" " tag_priority_high\n" " \n" " \n" " \n" "\n" " \n" " %5\n" // "Preference" " Ctrl+4\n" " true\n" " \n" " %6\n" // "Bad" " tag_preference_bad\n" " \n" " \n" " \n" " %7\n" // "Good" " tag_preference_good\n" " \n" " \n" " \n" " %8\n" // "Excellent" " tag_preference_excelent\n" // "excelent": typo error, but we should keep compatibility with old versions. " \n" " \n" " \n" "\n" " \n" " %9\n" // "Highlight" " Ctrl+5\n" " \n" " #ffffcc\n" " \" />\n" " \n" " \n" "\n") .tqarg( i18n("Priority"), i18n("Low"), i18n("Medium") ) // %1 %2 %3 .tqarg( i18n("High"), i18n("Preference"), i18n("Bad") ) // %4 %5 %6 .tqarg( i18n("Good"), i18n("Excellent"), i18n("Highlight") ) // %7 %8 %9 + TQString( " \n" " %1\n" // "Important" " Ctrl+6\n" " \n" " tag_important\n" " #ffcccc\n" " \n" " \n" " \n" "\n" " \n" " %2\n" // "Very Important" " Ctrl+7\n" " \n" " tag_important\n" " \n" " #ff0000\n" " \n" " \n" " \n" "\n" " \n" " %3\n" // "Information" " Ctrl+8\n" " \n" " messagebox_info\n" " \n" " \n" " \n" "\n" " \n" " %4\n" // "Idea" " Ctrl+9\n" " \n" " ktip\n" " \n" // I. " \n" " ""\n" "\n" " \n" " %6\n" // "Title" " Ctrl+0\n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " %7\n" // "Code" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " %8\n" // "Work" " \n" " \n" // W. " \n" " ""\n" "\n") .tqarg( i18n("Important"), i18n("Very Important"), i18n("Information") ) // %1 %2 %3 .tqarg( i18n("Idea"), i18n("The initial of 'Idea'", "I."), i18n("Title") ) // %4 %5 %6 .tqarg( i18n("Code"), i18n("Work"), i18n("The initial of 'Work'", "W.") ) // %7 %8 %9 + TQString( " \n" " \n" " %1\n" // "Personal" " \n" " \n" // P. " \n" " \n" "\n" " \n" " \n" " %3\n" // "Funny" " tag_fun\n" " \n" " \n" "\n" "") .tqarg( 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 << "\n"; stream << xml; file.close(); } else DEBUG_WIN << "FAILED to create the tags file!"; } #include #include #include #include #include #include #include #include #include /** 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->tqstyle().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(KGlobalSettings::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(KGlobalSettings::highlightedTextColor()); painter->setFont(font); painter->setClipping(false); painter->drawText(x + 5 + w, y, 3000, h, AlignLeft | AlignVCenter | DontClip | ShowPrefix, m_shortcut); } } TQSize IndentedMenuItem::tqsizeHint() { int iconSize = KIcon::SizeSmall; int iconMargin = StateMenuItem::iconMargin(); TQSize textSize = TQFontMetrics(KGlobalSettings::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->tqparentTag() ? m_state->tqparentTag()->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->tqstyle().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(KGlobalSettings::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(KGlobalSettings::highlightedTextColor()); painter->setFont(font); painter->setClipping(false); painter->drawText(x + 5 + w, y, 3000, h, AlignLeft | AlignVCenter | DontClip | ShowPrefix, m_shortcut); } } TQSize StateMenuItem::tqsizeHint() { 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(KGlobalSettings::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->tqstyle().tqpixelMetric(TQStyle::PM_IndicatorWidth, 0); int height = kapp->tqstyle().tqpixelMetric(TQStyle::PM_IndicatorHeight, 0); TQRect rect(0, 0, width, height); TQColor menuBackgroundColor = (dynamic_cast(&(kapp->tqstyle())) == NULL ? TQColor(cg.background()) : cg.background().light(103)); // Enabled, Not hovering TQPixmap pixmap(width, height); pixmap.fill(menuBackgroundColor); // In case the tqpixelMetric() 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->tqstyle().tqdrawPrimitive(TQStyle::PE_Indicator, &painter, rect, cg, style); painter.end(); // Enabled, Hovering TQPixmap pixmapHover(width, height); pixmapHover.fill(menuBackgroundColor); // In case the tqpixelMetric() haven't returned a bigger rectangle than what drawPrimitive() draws painter.begin(&pixmapHover); style |= TQStyle::Style_MouseOver; cg.setColor(TQColorGroup::Background, KGlobalSettings::highlightColor()); kapp->tqstyle().tqdrawPrimitive(TQStyle::PE_Indicator, &painter, rect, cg, style); painter.end(); // Disabled TQPixmap pixmapDisabled(width, height); pixmapDisabled.fill(menuBackgroundColor); // In case the tqpixelMetric() 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->tqstyle().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->tqstyle().tqpixelMetric(TQStyle::PM_ExclusiveIndicatorWidth, 0); int height = kapp->tqstyle().tqpixelMetric(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->tqstyle().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->tqstyle().tqdrawControl(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 tqmask 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 tqmask. * The heuristic tqmask 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->tqstyle().tqdrawPrimitive(TQStyle::PE_ExclusiveIndicator, &painter, rect, cg, style); style |= TQStyle::Style_MouseOver; cg.setColor(TQColorGroup::Background, KGlobalSettings::highlightColor()); kapp->tqstyle().tqdrawControl(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; }