Add (and install) svg for the new krunner interface.
[kdebase/uwolfer.git] / apps / keditbookmarks / commands.cpp
blob403cfdac2d14f99aea861df29138f84e0a3416af
1 // -*- indent-tabs-mode:nil -*-
2 // vim: set ts=4 sts=4 sw=4 et:
3 /* This file is part of the KDE project
4 Copyright (C) 2000 David Faure <faure@kde.org>
5 Copyright (C) 2002-2003 Alexander Kellett <lypanov@kde.org>
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public
9 License version 2 or at your option version 3 as published by
10 the Free Software Foundation.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
23 #include "commands.h"
24 #include "bookmarkmodel.h"
26 #include "kinsertionsort.h"
28 #include "toplevel.h"
30 #include <assert.h>
32 #include <kdebug.h>
33 #include <klocale.h>
34 #include <kbookmarkmanager.h>
35 #include <kdesktopfile.h>
37 #include "treeitem_p.h" // TODO REMOVEME
39 class KBookmarkModelInsertSentry
41 public:
42 KBookmarkModelInsertSentry(const KBookmark& parent, int first, int last)
44 QModelIndex mParent = CurrentMgr::self()->model()->indexForBookmark(parent);
45 CurrentMgr::self()->model()->beginInsertRows( mParent, first, last);
47 // TODO REMOVEME use of internal class TreeItem!
48 mt = static_cast<TreeItem *>(mParent.internalPointer());
49 mf = first;
50 ml = last;
52 ~KBookmarkModelInsertSentry()
54 mt->insertChildren(mf, ml);
55 CurrentMgr::self()->model()->endInsertRows();
57 private:
58 TreeItem * mt;
59 int mf, ml;
61 class KBookmarkModelRemoveSentry
63 public:
64 KBookmarkModelRemoveSentry(const KBookmark& parent, int first, int last)
66 QModelIndex mParent = CurrentMgr::self()->model()->indexForBookmark(parent);
68 CurrentMgr::self()->model()->beginRemoveRows( mParent, first, last);
70 mt = static_cast<TreeItem *>(mParent.internalPointer());
71 mf = first;
72 ml = last;
74 ~KBookmarkModelRemoveSentry()
76 mt->deleteChildren(mf, ml);
77 CurrentMgr::self()->model()->endRemoveRows();
79 private:
80 TreeItem * mt;
81 int mf, ml;
84 QString KEBMacroCommand::affectedBookmarks() const
86 const QList<K3Command *> commandList = commands();
87 QList<K3Command*>::const_iterator it = commandList.constBegin();
88 if ( it == commandList.constEnd() )
89 return QString();
90 // Need to use dynamic_cast here due to going cross-hierarchy, but it should never return 0.
91 QString affectBook = dynamic_cast<IKEBCommand *>(*it)->affectedBookmarks();
92 ++it;
93 for( ; it != commandList.constEnd() ; ++it )
95 affectBook = KBookmark::commonParent( affectBook, dynamic_cast<IKEBCommand *>(*it)->affectedBookmarks());
97 return affectBook;
100 DeleteManyCommand::DeleteManyCommand(const QString &name, const QList<KBookmark> & bookmarks)
101 : KEBMacroCommand(name)
103 QList<KBookmark>::const_iterator it, begin;
104 begin = bookmarks.constBegin();
105 it = bookmarks.constEnd();
106 while(begin != it)
108 --it;
109 DeleteCommand * dcmd = new DeleteCommand( (*it).address() );
110 addCommand(dcmd);
115 QString CreateCommand::name() const {
116 if (m_separator) {
117 return i18n("Insert Separator");
118 } else if (m_group) {
119 return i18n("Create Folder");
120 } else if (!m_originalBookmark.isNull()) {
121 return i18n("Copy %1", m_mytext);
122 } else {
123 return i18n("Create Bookmark");
127 void CreateCommand::execute()
129 QString parentAddress = KBookmark::parentAddress(m_to);
130 KBookmarkGroup parentGroup =
131 CurrentMgr::bookmarkAt(parentAddress).toGroup();
133 QString previousSibling = KBookmark::previousAddress(m_to);
135 // kDebug() << "CreateCommand::execute previousSibling="
136 // << previousSibling << endl;
137 KBookmark prev = (previousSibling.isEmpty())
138 ? KBookmark(QDomElement())
139 : CurrentMgr::bookmarkAt(previousSibling);
141 KBookmark bk = KBookmark(QDomElement());
142 // TODO use m_to.positionInParent()
143 KBookmarkModelInsertSentry guard(parentGroup, KBookmark::positionInParent(m_to), KBookmark::positionInParent(m_to));
145 if (m_separator) {
146 bk = parentGroup.createNewSeparator();
148 } else if (m_group) {
149 Q_ASSERT(!m_text.isEmpty());
150 bk = parentGroup.createNewFolder(m_text);
151 bk.internalElement().setAttribute("folded", (m_open ? "no" : "yes"));
152 if (!m_iconPath.isEmpty()) {
153 bk.setIcon(m_iconPath);
155 } else if(!m_originalBookmark.isNull()) {
156 QDomElement element = m_originalBookmark.internalElement().cloneNode().toElement();
157 bk = KBookmark(element);
158 parentGroup.addBookmark(bk);
159 } else {
160 bk = parentGroup.addBookmark(m_text, m_url, m_iconPath);
163 // move to right position
164 parentGroup.moveBookmark(bk, prev);
165 if (!(name().isEmpty()) && !parentAddress.isEmpty() ) {
166 // open the parent (useful if it was empty) - only for manual commands
167 Q_ASSERT( parentGroup.internalElement().tagName() != "xbel" );
168 parentGroup.internalElement().setAttribute("folded", "no");
171 Q_ASSERT(bk.address() == m_to);
174 QString CreateCommand::finalAddress() const {
175 Q_ASSERT( !m_to.isEmpty() );
176 return m_to;
179 void CreateCommand::unexecute() {
180 KBookmark bk = CurrentMgr::bookmarkAt(m_to);
181 Q_ASSERT(!bk.isNull() && !bk.parentGroup().isNull());
183 // TODO use bk.positionInParent()
184 KBookmarkModelRemoveSentry(bk.parentGroup(), KBookmark::positionInParent(bk.address()), KBookmark::positionInParent(bk.address()));
185 bk.parentGroup().deleteBookmark(bk);
188 QString CreateCommand::affectedBookmarks() const
190 return KBookmark::parentAddress(m_to);
193 /* -------------------------------------- */
195 EditCommand::EditCommand(const QString & address, int col, const QString & newValue)
196 : K3Command(), mAddress(address), mCol(col)
198 if(mCol == 1)
200 KUrl u(newValue);
201 mNewValue = u.url( KUrl::LeaveTrailingSlash );
203 else
204 mNewValue = newValue;
207 QString EditCommand::name() const
209 if(mCol==-1)
210 return i18n("%1 Change", i18n("Icon"));
211 else if(mCol==0)
212 return i18n("%1 Change", i18n("Title") );
213 else if(mCol==1)
214 return i18n("%1 Change", i18n("URL"));
215 else if(mCol==2)
216 return i18n("%1 Change", i18n("Comment"));
217 //Never reached
218 return QString("");
221 void EditCommand::execute()
223 KBookmark bk = CurrentMgr::bookmarkAt(mAddress);
224 if(mCol==-2)
226 mOldValue = bk.internalElement().attribute("toolbar");
227 bk.internalElement().setAttribute("toolbar", mNewValue);
229 else if(mCol==-1)
231 mOldValue = bk.icon();
232 bk.setIcon(mNewValue);
234 else if(mCol==0)
236 mOldValue = bk.fullText();
237 bk.setFullText(mNewValue);
239 else if(mCol==1)
241 mOldValue = bk.url().prettyUrl();
242 bk.setUrl(KUrl(mNewValue));
244 else if(mCol==2)
246 mOldValue = getNodeText(bk, QStringList()<<"desc");
247 setNodeText(bk, QStringList()<<"desc", mNewValue);
249 CurrentMgr::self()->model()->emitDataChanged(bk);
252 void EditCommand::unexecute()
255 KBookmark bk = CurrentMgr::bookmarkAt(mAddress);
256 if(mCol==-2)
258 bk.internalElement().setAttribute("toolbar", mOldValue);
260 else if(mCol==-1)
262 bk.setIcon(mOldValue);
264 else if(mCol==0)
266 bk.setFullText(mOldValue);
268 else if(mCol==1)
270 bk.setUrl(KUrl(mOldValue));
272 else if(mCol==2)
274 setNodeText(bk, QStringList()<<"desc", mOldValue);
276 CurrentMgr::self()->model()->emitDataChanged(bk);
279 void EditCommand::modify(const QString &newValue)
281 if(mCol == 1)
283 KUrl u(newValue);
284 mNewValue = u.url( KUrl::LeaveTrailingSlash );
286 else
287 mNewValue = newValue;
290 QString EditCommand::getNodeText(const KBookmark& bk, const QStringList &nodehier)
292 QDomNode subnode = bk.internalElement();
293 for (QStringList::ConstIterator it = nodehier.begin();
294 it != nodehier.end(); ++it)
296 subnode = subnode.namedItem((*it));
297 if (subnode.isNull())
298 return QString();
300 return (subnode.firstChild().isNull())
301 ? QString()
302 : subnode.firstChild().toText().data();
305 QString EditCommand::setNodeText(const KBookmark& bk, const QStringList &nodehier,
306 const QString& newValue)
308 QDomNode subnode = bk.internalElement();
309 for (QStringList::ConstIterator it = nodehier.begin();
310 it != nodehier.end(); ++it)
312 QDomNode parent = subnode;
313 subnode = subnode.namedItem((*it));
314 if (subnode.isNull()) {
315 subnode = bk.internalElement().ownerDocument().createElement((*it));
316 parent.appendChild(subnode);
320 if (subnode.firstChild().isNull()) {
321 QDomText domtext = subnode.ownerDocument().createTextNode("");
322 subnode.appendChild(domtext);
325 QDomText domtext = subnode.firstChild().toText();
327 QString oldText = domtext.data();
328 domtext.setData(newValue);
329 return oldText;
332 /* -------------------------------------- */
334 void DeleteCommand::execute() {
335 KBookmark bk = CurrentMgr::bookmarkAt(m_from);
336 Q_ASSERT(!bk.isNull());
338 if (m_contentOnly) {
339 QDomElement groupRoot = bk.internalElement();
341 QDomNode n = groupRoot.firstChild();
342 while (!n.isNull()) {
343 QDomElement e = n.toElement();
344 if (!e.isNull()) {
345 // kDebug() << e.tagName();
347 QDomNode next = n.nextSibling();
348 groupRoot.removeChild(n);
349 n = next;
351 return;
354 // TODO - bug - unparsed xml is lost after undo,
355 // we must store it all therefore
357 //FIXME this removes the comments, that's bad!
358 if (!m_cmd) {
359 if (bk.isGroup()) {
360 m_cmd = new CreateCommand(
361 m_from, bk.fullText(), bk.icon(),
362 bk.internalElement().attribute("folded") == "no");
363 m_subCmd = deleteAll(bk.toGroup());
364 m_subCmd->execute();
366 } else {
367 m_cmd = (bk.isSeparator())
368 ? new CreateCommand(m_from)
369 : new CreateCommand(m_from, bk.fullText(),
370 bk.icon(), bk.url());
373 m_cmd->unexecute();
376 void DeleteCommand::unexecute() {
377 // kDebug() << "DeleteCommand::unexecute " << m_from;
379 if (m_contentOnly) {
380 // TODO - recover saved metadata
381 return;
384 m_cmd->execute();
386 if (m_subCmd) {
387 m_subCmd->unexecute();
391 QString DeleteCommand::affectedBookmarks() const
393 return KBookmark::parentAddress(m_from);
396 KEBMacroCommand* DeleteCommand::deleteAll(const KBookmarkGroup & parentGroup) {
397 KEBMacroCommand *cmd = new KEBMacroCommand(QString());
398 QStringList lstToDelete;
399 // we need to delete from the end, to avoid index shifting
400 for (KBookmark bk = parentGroup.first();
401 !bk.isNull(); bk = parentGroup.next(bk))
402 lstToDelete.prepend(bk.address());
403 for (QStringList::Iterator it = lstToDelete.begin();
404 it != lstToDelete.end(); ++it)
405 cmd->addCommand(new DeleteCommand((*it)));
406 return cmd;
409 /* -------------------------------------- */
411 QString MoveCommand::name() const {
412 return i18n("Move %1", m_mytext);
415 void MoveCommand::execute() {
416 // kDebug() << "MoveCommand::execute moving from=" << m_from
417 // << " to=" << m_to << endl;
420 KBookmark fromBk = CurrentMgr::self()->mgr()->findByAddress( m_from );
422 m_cc = new CreateCommand(m_to, fromBk, "");
423 m_cc->execute();
425 m_dc = new DeleteCommand( fromBk.address() );
426 m_dc->execute();
429 QString MoveCommand::finalAddress() const {
430 Q_ASSERT( !m_to.isEmpty() );
431 return m_to;
434 void MoveCommand::unexecute() {
436 m_dc->unexecute();
437 m_cc->unexecute();
440 QString MoveCommand::affectedBookmarks() const
442 return KBookmark::commonParent(KBookmark::parentAddress(m_from), KBookmark::parentAddress(m_to));
445 /* -------------------------------------- */
447 class SortItem {
448 public:
449 SortItem(const KBookmark & bk) : m_bk(bk) {}
451 bool operator == (const SortItem & s) {
452 return (m_bk.internalElement() == s.m_bk.internalElement()); }
454 bool isNull() const {
455 return m_bk.isNull(); }
457 SortItem previousSibling() const {
458 return m_bk.parentGroup().previous(m_bk); }
460 SortItem nextSibling() const {
461 return m_bk.parentGroup().next(m_bk); }
463 const KBookmark& bookmark() const {
464 return m_bk; }
466 private:
467 KBookmark m_bk;
470 class SortByName {
471 public:
472 static QString key(const SortItem &item) {
473 return (item.bookmark().isGroup() ? "a" : "b")
474 + (item.bookmark().fullText().toLower());
478 /* -------------------------------------- */
480 void SortCommand::execute() {
481 if (commands().isEmpty()) {
482 KBookmarkGroup grp = CurrentMgr::bookmarkAt(m_groupAddress).toGroup();
483 Q_ASSERT(!grp.isNull());
484 SortItem firstChild(grp.first());
485 // this will call moveAfter, which will add
486 // the subcommands for moving the items
487 kInsertionSort<SortItem, SortByName, QString, SortCommand>
488 (firstChild, (*this));
490 } else {
491 // don't execute for second time on addCommand(cmd)
492 KEBMacroCommand::execute();
496 void SortCommand::moveAfter(const SortItem &moveMe,
497 const SortItem &afterMe) {
498 QString destAddress =
499 afterMe.isNull()
500 // move as first child
501 ? KBookmark::parentAddress(moveMe.bookmark().address()) + "/0"
502 // move after "afterMe"
503 : KBookmark::nextAddress(afterMe.bookmark().address());
505 MoveCommand *cmd = new MoveCommand(moveMe.bookmark().address(),
506 destAddress);
507 cmd->execute();
508 this->addCommand(cmd);
511 void SortCommand::unexecute() {
512 KEBMacroCommand::unexecute();
515 QString SortCommand::affectedBookmarks() const
517 return m_groupAddress;
520 /* -------------------------------------- */
522 KEBMacroCommand* CmdGen::setAsToolbar(const KBookmark &bk)
524 KEBMacroCommand *mcmd = new KEBMacroCommand(i18n("Set as Bookmark Toolbar"));
526 KBookmarkGroup oldToolbar = CurrentMgr::self()->mgr()->toolbar();
527 if (!oldToolbar.isNull())
529 mcmd->addCommand( new EditCommand(oldToolbar.address(), -2, "no")); //toolbar
530 mcmd->addCommand( new EditCommand(oldToolbar.address(), -1, "")); //icon
533 mcmd->addCommand( new EditCommand(bk.address(), -2, "yes"));
534 mcmd->addCommand( new EditCommand(bk.address(), -1, "bookmark-toolbar"));
536 return mcmd;
539 KEBMacroCommand* CmdGen::insertMimeSource(const QString &cmdName, const QMimeData *data, const QString &addr)
541 KEBMacroCommand *mcmd = new KEBMacroCommand(cmdName);
542 QString currentAddress = addr;
543 KBookmark::List bookmarks = KBookmark::List::fromMimeData(data);
544 KBookmark::List::const_iterator it, end;
545 end = bookmarks.constEnd();
547 for (it = bookmarks.constBegin(); it != end; ++it)
549 CreateCommand *cmd = new CreateCommand(currentAddress, (*it));
550 cmd->execute();
551 mcmd->addCommand(cmd);
552 currentAddress = KBookmark::nextAddress(currentAddress);
554 return mcmd;
558 //FIXME copy=true needed? what is the difference with insertMimeSource
559 KEBMacroCommand* CmdGen::itemsMoved(const QList<KBookmark> & items,
560 const QString &newAddress, bool copy) {
561 KEBMacroCommand *mcmd = new KEBMacroCommand(copy ? i18n("Copy Items")
562 : i18n("Move Items"));
564 QList<KBookmark>::const_iterator it, end;
565 it = items.begin();
566 end = items.end();
568 QString bkInsertAddr = newAddress;
569 for (; it != end; ++it) {
570 if (copy) {
571 CreateCommand *cmd;
572 cmd = new CreateCommand(
573 bkInsertAddr,
574 KBookmark((*it).internalElement()
575 .cloneNode(true).toElement()),
576 (*it).text());
578 cmd->execute();
579 mcmd->addCommand(cmd);
581 bkInsertAddr = cmd->finalAddress();
583 } else /* if (move) */ {
584 QString oldAddress = (*it).address();
585 if (bkInsertAddr.startsWith(oldAddress))
586 continue; // trying to insert a parent into one of its childs, ignore :)
588 MoveCommand *cmd = new MoveCommand(oldAddress, bkInsertAddr,
589 (*it).text());
590 cmd->execute();
591 mcmd->addCommand(cmd);
593 bkInsertAddr = cmd->finalAddress();
596 bkInsertAddr = KBookmark::nextAddress(bkInsertAddr);
599 return mcmd;