1 /***************************************************************************
2  *   Copyright (C) 2005 by Sébastien Laoût                                 *
3  *   slaout@linux62.org                                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20 
21 #include "tag.h"
22 
23 #include <KIconLoader>
24 #include <KActionCollection>
25 #include <KLocalizedString>
26 
27 #include <QtCore/QDir>
28 #include <QtCore/QList>
29 #include <QtCore/QTextStream>
30 #include <QtGui/QFont>
31 #include <QtXml/QDomDocument>
32 #include <QLocale>
33 
34 #include "xmlwork.h"
35 #include "global.h"
36 #include "debugwindow.h"
37 #include "bnpview.h"
38 #include "tools.h"
39 #include "basketscene.h"
40 #include "gitwrapper.h"
41 
42 
43 /** class State: */
44 
State(const QString & id,Tag * tag)45 State::State(const QString &id, Tag *tag)
46         : m_id(id), m_name(), m_emblem(), m_bold(false), m_italic(false), m_underline(false),
47         m_strikeOut(false), m_textColor(), m_fontName(), m_fontSize(-1), m_backgroundColor(),
48         m_textEquivalent(), m_onAllTextLines(false), m_allowCrossReferences(true), m_parentTag(tag)
49 {
50 }
51 
~State()52 State::~State()
53 {
54 }
55 
nextState(bool cycle)56 State* State::nextState(bool cycle /*= true*/)
57 {
58     if (!parentTag())
59         return 0;
60 
61     List states = parentTag()->states();
62     // The tag contains only one state:
63     if (states.count() == 1)
64         return 0;
65     // Find the next state:
66     for (List::iterator it = states.begin(); it != states.end(); ++it)
67         // Found the current state in the list:
68         if (*it == this) {
69             // Find the next state:
70             State *next = *(++it);
71             if (it == states.end())
72                 return (cycle ? states.first() : 0);
73             return next;
74         }
75     // Should not happens:
76     Q_ASSERT(false);
77     return 0;
78 }
79 
fullName()80 QString State::fullName()
81 {
82     if (!parentTag() || parentTag()->states().count() == 1)
83         return (name().isEmpty() && parentTag() ? parentTag()->name() : name());
84     return QString(i18n("%1: %2", parentTag()->name(), name()));
85 }
86 
font(QFont base)87 QFont State::font(QFont base)
88 {
89     if (bold())
90         base.setBold(true);
91     if (italic())
92         base.setItalic(true);
93     if (underline())
94         base.setUnderline(true);
95     if (strikeOut())
96         base.setStrikeOut(true);
97     if (!fontName().isEmpty())
98         base.setFamily(fontName());
99     if (fontSize() > 0)
100         base.setPointSize(fontSize());
101     return base;
102 }
103 
toCSS(const QString & gradientFolderPath,const QString & gradientFolderName,const QFont & baseFont)104 QString State::toCSS(const QString &gradientFolderPath, const QString &gradientFolderName, const QFont &baseFont)
105 {
106     QString css;
107     if (bold())
108         css += " font-weight: bold;";
109     if (italic())
110         css += " font-style: italic;";
111     if (underline() && strikeOut())
112         css += " text-decoration: underline line-through;";
113     else if (underline())
114         css += " text-decoration: underline;";
115     else if (strikeOut())
116         css += " text-decoration: line-through;";
117     if (textColor().isValid())
118         css += " color: " + textColor().name() + ";";
119     if (!fontName().isEmpty()) {
120         QString fontFamily = Tools::cssFontDefinition(fontName(), /*onlyFontFamily=*/true);
121         css += " font-family: " + fontFamily + ";";
122     }
123     if (fontSize() > 0)
124         css += " font-size: " + QString::number(fontSize()) + "px;";
125     if (backgroundColor().isValid()) {
126         // Get the colors of the gradient and the border:
127         QColor topBgColor;
128         QColor bottomBgColor;
129         Note::getGradientColors(backgroundColor(), &topBgColor, &bottomBgColor);
130         // Produce the CSS code:
131         QString gradientFileName = BasketScene::saveGradientBackground(backgroundColor(), font(baseFont), gradientFolderPath);
132         css += " background: " + bottomBgColor.name() + " url('" + gradientFolderName + gradientFileName + "') repeat-x;";
133         css += " border-top: solid " + topBgColor.name() + " 1px;";
134         css += " border-bottom: solid " + Tools::mixColor(topBgColor, bottomBgColor).name() + " 1px;";
135     }
136 
137     if (css.isEmpty())
138         return "";
139     else
140         return "   .tag_" + id() + " {" + css + " }\n";
141 }
142 
merge(const List & states,State * result,int * emblemsCount,bool * haveInvisibleTags,const QColor & backgroundColor)143 void State::merge(const List &states, State *result, int *emblemsCount, bool *haveInvisibleTags, const QColor &backgroundColor)
144 {
145     *result            = State(); // Reset to default values.
146     *emblemsCount      = 0;
147     *haveInvisibleTags = false;
148 
149     for (List::const_iterator it = states.begin(); it != states.end(); ++it) {
150         State *state = *it;
151         bool isVisible = false;
152         // For each propertie, if that properties have a value (is not default) is the current state of the list,
153         // 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.
154         if (!state->emblem().isEmpty()) {
155             ++*emblemsCount;
156             isVisible = true;
157         }
158         if (state->bold() && !result->bold()) {
159             result->setBold(true);
160             isVisible = true;
161         }
162         if (state->italic() && !result->italic()) {
163             result->setItalic(true);
164             isVisible = true;
165         }
166         if (state->underline() && !result->underline()) {
167             result->setUnderline(true);
168             isVisible = true;
169         }
170         if (state->strikeOut() && !result->strikeOut()) {
171             result->setStrikeOut(true);
172             isVisible = true;
173         }
174         if (state->textColor().isValid() && !result->textColor().isValid()) {
175             result->setTextColor(state->textColor());
176             isVisible = true;
177         }
178         if (!state->fontName().isEmpty() && result->fontName().isEmpty()) {
179             result->setFontName(state->fontName());
180             isVisible = true;
181         }
182         if (state->fontSize() > 0 && result->fontSize() <= 0) {
183             result->setFontSize(state->fontSize());
184             isVisible = true;
185         }
186         if (state->backgroundColor().isValid() && !result->backgroundColor().isValid() && state->backgroundColor() != backgroundColor) { // vv
187             result->setBackgroundColor(state->backgroundColor()); // This is particular: if the note background color is the same as the basket one, don't use that.
188             isVisible = true;
189         }
190         // 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:
191         if (!isVisible)
192             *haveInvisibleTags = true;
193     }
194 }
195 
copyTo(State * other)196 void State::copyTo(State *other)
197 {
198     other->m_id              = m_id;
199     other->m_name            = m_name;
200     other->m_emblem          = m_emblem;
201     other->m_bold            = m_bold;
202     other->m_italic          = m_italic;
203     other->m_underline       = m_underline;
204     other->m_strikeOut       = m_strikeOut;
205     other->m_textColor       = m_textColor;
206     other->m_fontName        = m_fontName;
207     other->m_fontSize        = m_fontSize;
208     other->m_backgroundColor = m_backgroundColor;
209     other->m_textEquivalent  = m_textEquivalent;
210     other->m_onAllTextLines  = m_onAllTextLines; // TODO
211     other->m_allowCrossReferences = m_allowCrossReferences;
212     //TODO: other->m_parentTag;
213 }
214 
215 /** class Tag: */
216 
217 Tag::List Tag::all = Tag::List();
218 
219 long Tag::nextStateUid = 1;
220 
getNextStateUid()221 long Tag::getNextStateUid()
222 {
223     return nextStateUid++; // Return the next Uid and THEN increment the Uid
224 }
225 
Tag()226 Tag::Tag()
227 {
228     static int tagNumber = 0;
229     ++tagNumber;
230     QString sAction = "tag_shortcut_number_" + QString::number(tagNumber);
231 
232     KActionCollection *ac = Global::bnpView->actionCollection();
233     m_action = ac->addAction(sAction, Global::bnpView,
234                              SLOT(activatedTagShortcut()));
235     m_action->setText("FAKE TEXT");
236     m_action->setIcon(QIcon::fromTheme("FAKE ICON"));
237 
238     ac->setShortcutsConfigurable(m_action, false); // We do it in the tag properties dialog
239 
240     m_inheritedBySiblings = false;
241 }
242 
~Tag()243 Tag::~Tag()
244 {
245     delete m_action;
246 }
247 
setName(const QString & name)248 void Tag::setName(const QString &name)
249 {
250     m_name = name;
251     m_action->setText("TAG SHORTCUT: " + name); // TODO: i18n  (for debug purpose only by now).
252 }
253 
stateForId(const QString & id)254 State* Tag::stateForId(const QString &id)
255 {
256     for (List::iterator it = all.begin(); it != all.end(); ++it)
257         for (State::List::iterator it2 = (*it)->states().begin(); it2 != (*it)->states().end(); ++it2)
258             if ((*it2)->id() == id)
259                 return *it2;
260     return 0;
261 }
262 
tagForKAction(QAction * action)263 Tag* Tag::tagForKAction(QAction *action)
264 {
265     for (List::iterator it = all.begin(); it != all.end(); ++it)
266         if ((*it)->m_action == action)
267             return *it;
268     return 0;
269 }
270 
loadTags(const QString & path)271 QMap<QString, QString> Tag::loadTags(const QString &path/* = QString()*//*, bool merge = false*/)
272 {
273     QMap<QString, QString> mergedStates;
274 
275     bool merge = !path.isEmpty();
276     QString fullPath = (merge ? path : Global::savesFolder() + "tags.xml");
277     QString doctype  = "basketTags";
278 
279     QDir dir;
280     if (!dir.exists(fullPath)) {
281         if (merge)
282             return mergedStates;
283         DEBUG_WIN << "Tags file does not exist: Creating it...";
284         createDefaultTagsSet(fullPath);
285     }
286 
287     QScopedPointer<QDomDocument> document(XMLWork::openFile(doctype, fullPath));
288     if (!document) {
289         DEBUG_WIN << "<font color=red>FAILED to read the tags file</font>";
290         return mergedStates;
291     }
292 
293     QDomElement docElem = document->documentElement();
294     if (!merge)
295         nextStateUid = docElem.attribute("nextStateUid", QString::number(nextStateUid)).toLong();
296 
297     QDomNode node = docElem.firstChild();
298     while (!node.isNull()) {
299         QDomElement element = node.toElement();
300         if ((!element.isNull()) && element.tagName() == "tag") {
301             Tag *tag = new Tag();
302             // Load properties:
303             QString name      = XMLWork::getElementText(element, "name");
304             QString shortcut  = XMLWork::getElementText(element, "shortcut");
305             QString inherited = XMLWork::getElementText(element, "inherited", "false");
306             tag->setName(name);
307             tag->setShortcut(QKeySequence(shortcut));
308             tag->setInheritedBySiblings(XMLWork::trueOrFalse(inherited));
309             // Load states:
310             QDomNode subNode = element.firstChild();
311             while (!subNode.isNull()) {
312                 QDomElement subElement = subNode.toElement();
313                 if ((!subElement.isNull()) && subElement.tagName() == "state") {
314                     State *state = new State(subElement.attribute("id"), tag);
315                     state->setName(XMLWork::getElementText(subElement, "name"));
316                     state->setEmblem(XMLWork::getElementText(subElement, "emblem"));
317                     QDomElement textElement = XMLWork::getElement(subElement, "text");
318                     state->setBold(XMLWork::trueOrFalse(textElement.attribute("bold",      "false")));
319                     state->setItalic(XMLWork::trueOrFalse(textElement.attribute("italic",    "false")));
320                     state->setUnderline(XMLWork::trueOrFalse(textElement.attribute("underline", "false")));
321                     state->setStrikeOut(XMLWork::trueOrFalse(textElement.attribute("strikeOut", "false")));
322                     QString textColor = textElement.attribute("color", "");
323                     state->setTextColor(textColor.isEmpty() ? QColor() : QColor(textColor));
324                     QDomElement fontElement = XMLWork::getElement(subElement, "font");
325                     state->setFontName(fontElement.attribute("name", ""));
326                     QString fontSize = fontElement.attribute("size", "");
327                     state->setFontSize(fontSize.isEmpty() ? -1 : fontSize.toInt());
328                     QString backgroundColor = XMLWork::getElementText(subElement, "backgroundColor", "");
329                     state->setBackgroundColor(backgroundColor.isEmpty() ? QColor() : QColor(backgroundColor));
330                     QDomElement textEquivalentElement = XMLWork::getElement(subElement, "textEquivalent");
331                     state->setTextEquivalent(textEquivalentElement.attribute("string", ""));
332                     state->setOnAllTextLines(XMLWork::trueOrFalse(textEquivalentElement.attribute("onAllTextLines", "false")));
333                     QString allowXRef = XMLWork::getElementText(subElement, "allowCrossReferences", "true");
334                     state->setAllowCrossReferences(XMLWork::trueOrFalse(allowXRef));
335                     tag->appendState(state);
336                 }
337                 subNode = subNode.nextSibling();
338             }
339             // If the Tag is Valid:
340             if (tag->countStates() > 0) {
341                 // Rename Things if Needed:
342                 State *firstState = tag->states().first();
343                 if (tag->countStates() == 1 && firstState->name().isEmpty())
344                     firstState->setName(tag->name());
345                 if (tag->name().isEmpty())
346                     tag->setName(firstState->name());
347                 // Add or Merge the Tag:
348                 if (!merge) {
349                     all.append(tag);
350                 } else {
351                     Tag *similarTag = tagSimilarTo(tag);
352                     // Tag does not exists, add it:
353                     if (similarTag == 0) {
354                         // We are merging the new states, so we should choose new and unique (on that computer) ids for those states:
355                         for (State::List::iterator it = tag->states().begin(); it != tag->states().end(); ++it) {
356                             State *state = *it;
357                             QString uid    = state->id();
358                             QString newUid = "tag_state_" + QString::number(getNextStateUid());
359                             state->setId(newUid);
360                             mergedStates[uid] = newUid;
361                         }
362                         // TODO: if shortcut is already assigned to a previous note, do not import it, keep the user settings!
363                         all.append(tag);
364                         // Tag already exists, rename to theire ids:
365                     } else {
366                         State::List::iterator it2 = similarTag->states().begin();
367                         for (State::List::iterator it = tag->states().begin(); it != tag->states().end(); ++it, ++it2) {
368                             State *state        = *it;
369                             State *similarState = *it2;
370                             QString uid    = state->id();
371                             QString newUid = similarState->id();
372                             if (uid != newUid)
373                                 mergedStates[uid] = newUid;
374                         }
375                         delete tag; // Already exists, not to be merged. Delete the shortcut and all.
376                     }
377                 }
378             }
379         }
380         node = node.nextSibling();
381     }
382 
383     return mergedStates;
384 }
385 
tagSimilarTo(Tag * tagToTest)386 Tag* Tag::tagSimilarTo(Tag *tagToTest)
387 {
388     // Tags are considered similar if they have the same name, the same number of states, in the same order, and the same look.
389     // Keyboard shortcut, text equivalent and onEveryLines are user settings, and thus not considered during the comparison.
390     // Default tags (To Do, Important, Idea...) do not take into account the name of the tag and states during the comparison.
391     // Default tags are equal only if they have the same number of states, in the same order, and the same look.
392     // This is because default tag names are translated differently in every countries, but they are essentialy the same!
393     // User tags begins with "tag_state_" followed by a number. Default tags are the other ones.
394 
395     // Browse all tags:
396     for (List::iterator it = all.begin(); it != all.end(); ++it) {
397         Tag *tag = *it;
398         bool same = true;
399         bool sameName;
400         bool defaultTag = true;
401         // We test only name and look. Shorcut and whenever it is inherited by sibling new notes are user settings only!
402         sameName = tag->name() == tagToTest->name();
403         if (tag->countStates() != tagToTest->countStates())
404             continue; // Tag is different!
405         // We found a tag with same name, check if every states/look are same too:
406         State::List::iterator itTest = tagToTest->states().begin();
407         for (State::List::iterator it2 = (*it)->states().begin(); it2 != (*it)->states().end(); ++it2, ++itTest) {
408             State *state       = *it2;
409             State *stateToTest = *itTest;
410             if (state->id().startsWith(QLatin1String("tag_state_")) || stateToTest->id().startsWith(QLatin1String("tag_state_"))) {
411                 defaultTag = false;
412             }
413             if (state->name()            != stateToTest->name())            {
414                 sameName = false;
415             }
416             if (state->emblem()          != stateToTest->emblem())          {
417                 same = false; break;
418             }
419             if (state->bold()            != stateToTest->bold())            {
420                 same = false; break;
421             }
422             if (state->italic()          != stateToTest->italic())          {
423                 same = false; break;
424             }
425             if (state->underline()       != stateToTest->underline())       {
426                 same = false; break;
427             }
428             if (state->strikeOut()       != stateToTest->strikeOut())       {
429                 same = false; break;
430             }
431             if (state->textColor()       != stateToTest->textColor())       {
432                 same = false; break;
433             }
434             if (state->fontName()        != stateToTest->fontName())        {
435                 same = false; break;
436             }
437             if (state->fontSize()        != stateToTest->fontSize())        {
438                 same = false; break;
439             }
440             if (state->backgroundColor() != stateToTest->backgroundColor()) {
441                 same = false; break;
442             }
443             // Text equivalent (as well as onAllTextLines) is also a user setting!
444         }
445         // We found an existing tag that is "exactly" the same:
446         if (same && (sameName || defaultTag))
447             return tag;
448     }
449 
450     // Not found:
451     return 0;
452 }
453 
saveTags()454 void Tag::saveTags()
455 {
456     DEBUG_WIN << "Saving tags...";
457     saveTagsTo(all, Global::savesFolder() + "tags.xml");
458 
459     GitWrapper::commitTagsXml();
460 }
461 
saveTagsTo(QList<Tag * > & list,const QString & fullPath)462 void Tag::saveTagsTo(QList<Tag*> &list, const QString &fullPath)
463 {
464     // Create Document:
465     QDomDocument document(/*doctype=*/"basketTags");
466     QDomElement root = document.createElement("basketTags");
467     root.setAttribute("nextStateUid", static_cast<long long int>(nextStateUid));
468     document.appendChild(root);
469 
470     // Save all tags:
471     for (List::iterator it = list.begin(); it != list.end(); ++it) {
472         Tag *tag = *it;
473         // Create tag node:
474         QDomElement tagNode = document.createElement("tag");
475         root.appendChild(tagNode);
476         // Save tag properties:
477         XMLWork::addElement(document, tagNode, "name",      tag->name());
478         XMLWork::addElement(document, tagNode, "shortcut", tag->shortcut().toString());
479         XMLWork::addElement(document, tagNode, "inherited", XMLWork::trueOrFalse(tag->inheritedBySiblings()));
480         // Save all states:
481         for (State::List::iterator it2 = (*it)->states().begin(); it2 != (*it)->states().end(); ++it2) {
482             State *state = *it2;
483             // Create state node:
484             QDomElement stateNode = document.createElement("state");
485             tagNode.appendChild(stateNode);
486             // Save state properties:
487             stateNode.setAttribute("id", state->id());
488             XMLWork::addElement(document, stateNode, "name",   state->name());
489             XMLWork::addElement(document, stateNode, "emblem", state->emblem());
490             QDomElement textNode = document.createElement("text");
491             stateNode.appendChild(textNode);
492             QString textColor = (state->textColor().isValid() ? state->textColor().name() : "");
493             textNode.setAttribute("bold",      XMLWork::trueOrFalse(state->bold()));
494             textNode.setAttribute("italic",    XMLWork::trueOrFalse(state->italic()));
495             textNode.setAttribute("underline", XMLWork::trueOrFalse(state->underline()));
496             textNode.setAttribute("strikeOut", XMLWork::trueOrFalse(state->strikeOut()));
497             textNode.setAttribute("color",     textColor);
498             QDomElement fontNode = document.createElement("font");
499             stateNode.appendChild(fontNode);
500             fontNode.setAttribute("name", state->fontName());
501             fontNode.setAttribute("size", state->fontSize());
502             QString backgroundColor = (state->backgroundColor().isValid() ? state->backgroundColor().name() : "");
503             XMLWork::addElement(document, stateNode, "backgroundColor", backgroundColor);
504             QDomElement textEquivalentNode = document.createElement("textEquivalent");
505             stateNode.appendChild(textEquivalentNode);
506             textEquivalentNode.setAttribute("string",         state->textEquivalent());
507             textEquivalentNode.setAttribute("onAllTextLines", XMLWork::trueOrFalse(state->onAllTextLines()));
508             XMLWork::addElement(document, stateNode, "allowCrossReferences", XMLWork::trueOrFalse(state->allowCrossReferences()));
509         }
510     }
511 
512     // Write to Disk:
513     if (!BasketScene::safelySaveToFile(fullPath, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" + document.toString()))
514         DEBUG_WIN << "<font color=red>FAILED to save tags</font>!";
515 }
516 
copyTo(Tag * other)517 void Tag::copyTo(Tag *other)
518 {
519     other->m_name = m_name;
520     other->m_action->setShortcut(m_action->shortcut());
521     other->m_inheritedBySiblings =  m_inheritedBySiblings;
522 }
523 
createDefaultTagsSet(const QString & fullPath)524 void Tag::createDefaultTagsSet(const QString &fullPath)
525 {
526     QString xml = QString(
527                       "<!DOCTYPE basketTags>\n"
528                       "<basketTags>\n"
529                       "  <tag>\n"
530                       "    <name>%1</name>\n" // "To Do"
531                       "    <shortcut>Ctrl+1</shortcut>\n"
532                       "    <inherited>true</inherited>\n"
533                       "    <state id=\"todo_unchecked\">\n"
534                       "      <name>%2</name>\n" // "Unchecked"
535                       "      <emblem>tag_checkbox</emblem>\n"
536                       "      <text bold=\"false\" italic=\"false\" underline=\"false\" strikeOut=\"false\" color=\"\" />\n"
537                       "      <font name=\"\" size=\"\" />\n"
538                       "      <backgroundColor></backgroundColor>\n"
539                       "      <textEquivalent string=\"[ ]\" onAllTextLines=\"false\" />\n"
540                       "    </state>\n"
541                       "    <state id=\"todo_done\">\n"
542                       "      <name>%3</name>\n" // "Done"
543                       "      <emblem>tag_checkbox_checked</emblem>\n"
544                       "      <text bold=\"false\" italic=\"false\" underline=\"false\" strikeOut=\"true\" color=\"\" />\n"
545                       "      <font name=\"\" size=\"\" />\n"
546                       "      <backgroundColor></backgroundColor>\n"
547                       "      <textEquivalent string=\"[x]\" onAllTextLines=\"false\" />\n"
548                       "    </state>\n"
549                       "  </tag>\n"
550                       "\n"
551                       "  <tag>\n"
552                       "    <name>%4</name>\n" // "Progress"
553                       "    <shortcut>Ctrl+2</shortcut>\n"
554                       "    <inherited>true</inherited>\n"
555                       "    <state id=\"progress_000\">\n"
556                       "      <name>%5</name>\n" // "0 %"
557                       "      <emblem>tag_progress_000</emblem>\n"
558                       "      <textEquivalent string=\"[    ]\" />\n"
559                       "    </state>\n"
560                       "    <state id=\"progress_025\">\n"
561                       "      <name>%6</name>\n" // "25 %"
562                       "      <emblem>tag_progress_025</emblem>\n"
563                       "      <textEquivalent string=\"[=   ]\" />\n"
564                       "    </state>\n"
565                       "    <state id=\"progress_050\">\n"
566                       "      <name>%7</name>\n" // "50 %"
567                       "      <emblem>tag_progress_050</emblem>\n"
568                       "      <textEquivalent string=\"[==  ]\" />\n"
569                       "    </state>\n"
570                       "    <state id=\"progress_075\">\n"
571                       "      <name>%8</name>\n" // "75 %"
572                       "      <emblem>tag_progress_075</emblem>\n"
573                       "      <textEquivalent string=\"[=== ]\" />\n"
574                       "    </state>\n"
575                       "    <state id=\"progress_100\">\n"
576                       "      <name>%9</name>\n" // "100 %"
577                       "      <emblem>tag_progress_100</emblem>\n"
578                       "      <textEquivalent string=\"[====]\" />\n"
579                       "    </state>\n"
580                       "  </tag>\n"
581                       "\n")
582                   .arg(i18n("To Do"),     i18n("Unchecked"),      i18n("Done"))           // %1 %2 %3
583                   .arg(i18n("Progress"),  i18n("0 %"),            i18n("25 %"))           // %4 %5 %6
584                   .arg(i18n("50 %"),      i18n("75 %"),           i18n("100 %"))          // %7 %8 %9
585                   + QString(
586                       "  <tag>\n"
587                       "    <name>%1</name>\n" // "Priority"
588                       "    <shortcut>Ctrl+3</shortcut>\n"
589                       "    <inherited>true</inherited>\n"
590                       "    <state id=\"priority_low\">\n"
591                       "      <name>%2</name>\n" // "Low"
592                       "      <emblem>tag_priority_low</emblem>\n"
593                       "      <textEquivalent string=\"{1}\" />\n"
594                       "    </state>\n"
595                       "    <state id=\"priority_medium\">\n"
596                       "      <name>%3</name>\n" // "Medium
597                       "      <emblem>tag_priority_medium</emblem>\n"
598                       "      <textEquivalent string=\"{2}\" />\n"
599                       "    </state>\n"
600                       "    <state id=\"priority_high\">\n"
601                       "      <name>%4</name>\n" // "High"
602                       "      <emblem>tag_priority_high</emblem>\n"
603                       "      <textEquivalent string=\"{3}\" />\n"
604                       "    </state>\n"
605                       "  </tag>\n"
606                       "\n"
607                       "  <tag>\n"
608                       "    <name>%5</name>\n" // "Preference"
609                       "    <shortcut>Ctrl+4</shortcut>\n"
610                       "    <inherited>true</inherited>\n"
611                       "    <state id=\"preference_bad\">\n"
612                       "      <name>%6</name>\n" // "Bad"
613                       "      <emblem>tag_preference_bad</emblem>\n"
614                       "      <textEquivalent string=\"(*  )\" />\n"
615                       "    </state>\n"
616                       "    <state id=\"preference_good\">\n"
617                       "      <name>%7</name>\n" // "Good"
618                       "      <emblem>tag_preference_good</emblem>\n"
619                       "      <textEquivalent string=\"(** )\" />\n"
620                       "    </state>\n"
621                       "    <state id=\"preference_excellent\">\n"
622                       "      <name>%8</name>\n" // "Excellent"
623                       "      <emblem>tag_preference_excellent</emblem>\n"
624                       "      <textEquivalent string=\"(***)\" />\n"
625                       "    </state>\n"
626                       "  </tag>\n"
627                       "\n"
628                       "  <tag>\n"
629                       "    <name>%9</name>\n" // "Highlight"
630                       "    <shortcut>Ctrl+5</shortcut>\n"
631                       "    <state id=\"highlight\">\n"
632                       "      <backgroundColor>#ffffcc</backgroundColor>\n"
633                       "      <textEquivalent string=\"=>\" />\n"
634                       "    </state>\n"
635                       "  </tag>\n"
636                       "\n")
637                   .arg(i18n("Priority"),  i18n("Low"),            i18n("Medium"))         // %1 %2 %3
638                   .arg(i18n("High"),      i18n("Preference"),     i18n("Bad"))            // %4 %5 %6
639                   .arg(i18n("Good"),      i18n("Excellent"),      i18n("Highlight"))      // %7 %8 %9
640                   + QString(
641                       "  <tag>\n"
642                       "    <name>%1</name>\n" // "Important"
643                       "    <shortcut>Ctrl+6</shortcut>\n"
644                       "    <state id=\"important\">\n"
645                       "      <emblem>tag_important</emblem>\n"
646                       "      <backgroundColor>#ffcccc</backgroundColor>\n"
647                       "      <textEquivalent string=\"!!\" />\n"
648                       "    </state>\n"
649                       "  </tag>\n"
650                       "\n"
651                       "  <tag>\n"
652                       "    <name>%2</name>\n" // "Very Important"
653                       "    <shortcut>Ctrl+7</shortcut>\n"
654                       "    <state id=\"very_important\">\n"
655                       "      <emblem>tag_important</emblem>\n"
656                       "      <text color=\"#ffffff\" />\n"
657                       "      <backgroundColor>#ff0000</backgroundColor>\n"
658                       "      <textEquivalent string=\"/!\\\" />\n"
659                       "    </state>\n"
660                       "  </tag>\n"
661                       "\n"
662                       "  <tag>\n"
663                       "    <name>%3</name>\n" // "Information"
664                       "    <shortcut>Ctrl+8</shortcut>\n"
665                       "    <state id=\"information\">\n"
666                       "      <emblem>dialog-information</emblem>\n"
667                       "      <textEquivalent string=\"(i)\" />\n"
668                       "    </state>\n"
669                       "  </tag>\n"
670                       "\n"
671                       "  <tag>\n"
672                       "    <name>%4</name>\n" // "Idea"
673                       "    <shortcut>Ctrl+9</shortcut>\n"
674                       "    <state id=\"idea\">\n"
675                       "      <emblem>ktip</emblem>\n"
676                       "      <textEquivalent string=\"%5\" />\n" // I.
677                       "    </state>\n"
678                       "  </tag>""\n"
679                       "\n"
680                       "  <tag>\n"
681                       "    <name>%6</name>\n" // "Title"
682                       "    <shortcut>Ctrl+0</shortcut>\n"
683                       "    <state id=\"title\">\n"
684                       "      <text bold=\"true\" />\n"
685                       "      <textEquivalent string=\"##\" />\n"
686                       "    </state>\n"
687                       "  </tag>\n"
688                       "\n"
689                       "  <tag>\n"
690                       "    <name>%7</name>\n" // "Code"
691                       "    <state id=\"code\">\n"
692                       "      <font name=\"monospace\" />\n"
693                       "      <textEquivalent string=\"|\" onAllTextLines=\"true\" />\n"
694                       "      <allowCrossReferences>false</allowCrossReferences>\n"
695                       "    </state>\n"
696                       "  </tag>\n"
697                       "\n"
698                       "  <tag>\n"
699                       "    <state id=\"work\">\n"
700                       "      <name>%8</name>\n" // "Work"
701                       "      <text color=\"#ff8000\" />\n"
702                       "      <textEquivalent string=\"%9\" />\n" // W.
703                       "    </state>\n"
704                       "  </tag>""\n"
705                       "\n")
706                   .arg(i18n("Important"), i18n("Very Important"),              i18n("Information"))                   // %1 %2 %3
707                   .arg(i18n("Idea"),      i18nc("The initial of 'Idea'", "I."), i18n("Title"))                         // %4 %5 %6
708                   .arg(i18n("Code"),      i18n("Work"),                        i18nc("The initial of 'Work'", "W."))   // %7 %8 %9
709                   + QString(
710                       "  <tag>\n"
711                       "    <state id=\"personal\">\n"
712                       "      <name>%1</name>\n" // "Personal"
713                       "      <text color=\"#008000\" />\n"
714                       "      <textEquivalent string=\"%2\" />\n" // P.
715                       "    </state>\n"
716                       "  </tag>\n"
717                       "\n"
718                       "  <tag>\n"
719                       "    <state id=\"funny\">\n"
720                       "      <name>%3</name>\n" // "Funny"
721                       "      <emblem>tag_fun</emblem>\n"
722                       "    </state>\n"
723                       "  </tag>\n"
724                       "</basketTags>\n"
725                       "")
726                   .arg(i18n("Personal"), i18nc("The initial of 'Personal'", "P."), i18n("Funny"));   // %1 %2 %3
727 
728     // Write to Disk:
729     QFile file(fullPath);
730     if (file.open(QIODevice::WriteOnly)) {
731         QTextStream stream(&file);
732         stream.setCodec("UTF-8");
733         stream << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
734         stream << xml;
735         file.close();
736     } else
737         DEBUG_WIN << "<font color=red>FAILED to create the tags file</font>!";
738 }
739 
740 // StateAction
StateAction(State * state,const QKeySequence & shortcut,QWidget * parent,bool withTagName)741 StateAction::StateAction(State *state, const QKeySequence &shortcut, QWidget* parent, bool withTagName)
742         : KToggleAction(parent)
743         , m_state(state)
744 {
745     setText(m_state->name());
746 
747     if (withTagName && m_state->parentTag())
748         setText(m_state->parentTag()->name());
749 
750     setIcon(KIconLoader::global()->loadIcon(m_state->emblem(),
751                                             KIconLoader::Small,
752                                             16,
753                                             KIconLoader::DefaultState,
754                                             QStringList(),
755                                             /*path_store=*/0L,
756                                             /*canReturnNull=*/true
757                                            )
758            );
759 
760     setShortcut(shortcut);
761 }
762 
~StateAction()763 StateAction::~StateAction()
764 {
765     // pass
766 }
767