diff options
Diffstat (limited to 'konqueror/keditbookmarks/commands.cpp')
-rw-r--r-- | konqueror/keditbookmarks/commands.cpp | 745 |
1 files changed, 745 insertions, 0 deletions
diff --git a/konqueror/keditbookmarks/commands.cpp b/konqueror/keditbookmarks/commands.cpp new file mode 100644 index 000000000..e545df54e --- /dev/null +++ b/konqueror/keditbookmarks/commands.cpp @@ -0,0 +1,745 @@ +// -*- indent-tabs-mode:nil -*- +// vim: set ts=4 sts=4 sw=4 et: +/* This file is part of the KDE project + Copyright (C) 2000 David Faure <[email protected]> + Copyright (C) 2002-2003 Alexander Kellett <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 as published by the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "commands.h" + +#include "kinsertionsort.h" + +#include "toplevel.h" +#include "listview.h" + +#include <assert.h> +#include <qvaluevector.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <kbookmarkdrag.h> +#include <kbookmarkmanager.h> + +#include <kurldrag.h> +#include <kdesktopfile.h> + +QString KEBMacroCommand::affectedBookmarks() const +{ + QPtrListIterator<KCommand> it(m_commands); + QString affectBook; + if(it.current()) + affectBook = dynamic_cast<IKEBCommand *>(it.current())->affectedBookmarks(); + ++it; + for ( ; it.current() ; ++it ) + affectBook = KBookmark::commonParent( affectBook, dynamic_cast<IKEBCommand *>(it.current())->affectedBookmarks()); + return affectBook; +} + +QString DeleteManyCommand::prevOrParentAddress(QString addr) +{ + QString prev = KBookmark::previousAddress( addr ); + if( CurrentMgr::bookmarkAt(prev).hasParent()) + return prev; + else + return KBookmark::parentAddress( addr ); +} + +QString DeleteManyCommand::preOrderNextAddress(QString addr) +{ + QString rootAdr = CurrentMgr::self()->mgr()->root().address(); + while(addr != rootAdr) + { + QString next = KBookmark::nextAddress(addr); + if(CurrentMgr::bookmarkAt( next ).hasParent() ) + return next; + addr = KBookmark::parentAddress( addr ); + } + return QString::null; +} + +bool DeleteManyCommand::isConsecutive(const QValueList<QString> & addresses) +{ + QValueList<QString>::const_iterator it, end; + it = addresses.begin(); + end = addresses.end(); + QString addr = *(addresses.begin()); + for( ; it != end; ++it) + { + if( *it != addr ) + return false; + addr = KBookmark::nextAddress(addr); + } + return true; +} + + +DeleteManyCommand::DeleteManyCommand(const QString &name, const QValueList<QString> & addresses) + : KEBMacroCommand(name) +{ + QValueList<QString>::const_iterator it, begin; + begin = addresses.begin(); + it = addresses.end(); + while(begin != it) + { + --it; + DeleteCommand * dcmd = new DeleteCommand(*it); + addCommand(dcmd); + } + + // Set m_currentAddress + if( addresses.count() == 1) + { + // First try next bookmark + if( CurrentMgr::bookmarkAt( KBookmark::nextAddress( *begin ) ).hasParent() ) + m_currentAddress = *begin; + else + { + m_currentAddress = preOrderNextAddress( KBookmark::parentAddress( *begin ) ); + if(m_currentAddress == QString::null) + m_currentAddress = prevOrParentAddress( *begin ); + } + } + else // multi selection + { + // Check if all bookmarks are consecutive + if(isConsecutive(addresses)) // Mark next bookmark after all selected + { // That's a little work... + QValueList<QString>::const_iterator last = addresses.end(); + --last; + if( CurrentMgr::bookmarkAt( KBookmark::nextAddress(*last) ).hasParent() ) + m_currentAddress = *begin; + else + { + m_currentAddress = preOrderNextAddress( KBookmark::parentAddress( *begin ) ); + if( m_currentAddress == QString::null) + m_currentAddress = prevOrParentAddress( *begin ); + } + } + else // not consecutive, select the common parent (This could be more clever) + { + QValueList<QString>::const_iterator jt, end; + end = addresses.end(); + m_currentAddress = *begin; + for( jt = addresses.begin(); jt != end; ++jt) + m_currentAddress = KBookmark::commonParent(m_currentAddress, *jt); + } + } +} + +QString DeleteManyCommand::currentAddress() const +{ + return m_currentAddress; +} + + +QString CreateCommand::name() const { + if (m_separator) { + return i18n("Insert Separator"); + } else if (m_group) { + return i18n("Create Folder"); + } else if (!m_originalBookmark.isNull()) { + return i18n("Copy %1").arg(m_mytext); + } else { + return i18n("Create Bookmark"); + } +} + +void CreateCommand::execute() { + QString parentAddress = KBookmark::parentAddress(m_to); + KBookmarkGroup parentGroup = + CurrentMgr::bookmarkAt(parentAddress).toGroup(); + + QString previousSibling = KBookmark::previousAddress(m_to); + + // kdDebug() << "CreateCommand::execute previousSibling=" + // << previousSibling << endl; + KBookmark prev = (previousSibling.isEmpty()) + ? KBookmark(QDomElement()) + : CurrentMgr::bookmarkAt(previousSibling); + + KBookmark bk = KBookmark(QDomElement()); + + if (m_separator) { + bk = parentGroup.createNewSeparator(); + + } else if (m_group) { + Q_ASSERT(!m_text.isEmpty()); + bk = parentGroup.createNewFolder(CurrentMgr::self()->mgr(), + m_text, false); + bk.internalElement().setAttribute("folded", (m_open ? "no" : "yes")); + if (!m_iconPath.isEmpty()) { + bk.internalElement().setAttribute("icon", m_iconPath); + } + + } else if (!m_originalBookmark.isNull()) { + // umm.. moveItem needs bk to be a child already! + bk = m_originalBookmark; + + } else { + bk = parentGroup.addBookmark(CurrentMgr::self()->mgr(), + m_text, m_url, + m_iconPath, false); + } + + // move to right position + parentGroup.moveItem(bk, prev); + if (!(name().isEmpty()) && !parentAddress.isEmpty() ) { + // open the parent (useful if it was empty) - only for manual commands + Q_ASSERT( parentGroup.internalElement().tagName() != "xbel" ); + parentGroup.internalElement().setAttribute("folded", "no"); + } + + Q_ASSERT(bk.address() == m_to); +} + +QString CreateCommand::finalAddress() const { + Q_ASSERT( !m_to.isEmpty() ); + return m_to; +} + +void CreateCommand::unexecute() { + // kdDebug() << "CreateCommand::unexecute deleting " << m_to << endl; + + KBookmark bk = CurrentMgr::bookmarkAt(m_to); + Q_ASSERT(!bk.isNull() && !bk.parentGroup().isNull()); + + ListView::self()->invalidate(bk.address()); + + bk.parentGroup().deleteBookmark(bk); +} + +QString CreateCommand::affectedBookmarks() const +{ + return KBookmark::parentAddress(m_to); +} + +QString CreateCommand::currentAddress() const +{ + QString bk = KBookmark::previousAddress( m_to ); + if(CurrentMgr::bookmarkAt( bk).hasParent()) + return bk; + else + return KBookmark::parentAddress( m_to ); +} + +/* -------------------------------------- */ + +QString EditCommand::name() const { + return i18n("%1 Change").arg(m_mytext); +} + +void EditCommand::execute() { + KBookmark bk = CurrentMgr::bookmarkAt(m_address); + Q_ASSERT(!bk.isNull()); + + m_reverseEditions.clear(); + + QValueList<Edition>::Iterator it = m_editions.begin(); + + for ( ; it != m_editions.end() ; ++it) { + // backup current value + m_reverseEditions.append( Edition((*it).attr, + bk.internalElement().attribute((*it).attr))); + // set new value + bk.internalElement().setAttribute((*it).attr, (*it).value); + } +} + +void EditCommand::unexecute() { + // code reuse + EditCommand cmd(m_address, m_reverseEditions); + cmd.execute(); + // get the editions back from it, + // in case they changed + // (hmm, shouldn't happen - TODO CHECK!) + m_editions = cmd.m_reverseEditions; +} + +QString EditCommand::affectedBookmarks() const +{ + return KBookmark::parentAddress(m_address); +} + +void EditCommand::modify(const QString & a, const QString & v) +{ + QValueList<Edition>::Iterator it = m_editions.begin(); + QValueList<Edition>::Iterator end = m_editions.end(); + for ( ; it != end; ++it) + { + if( (*it).attr == a) + (*it).value = v; + } +} + +/* -------------------------------------- */ + +QString NodeEditCommand::name() const { + // TODO - make dynamic + return i18n("Renaming"); +} + +QString NodeEditCommand::getNodeText(KBookmark bk, const QStringList &nodehier) { + QDomNode subnode = bk.internalElement(); + for (QStringList::ConstIterator it = nodehier.begin(); + it != nodehier.end(); ++it) + { + subnode = subnode.namedItem((*it)); + if (subnode.isNull()) + return QString::null; + } + return (subnode.firstChild().isNull()) + ? QString::null + : subnode.firstChild().toText().data(); +} + +QString NodeEditCommand::setNodeText(KBookmark bk, const QStringList &nodehier, + const QString newValue) { + QDomNode subnode = bk.internalElement(); + for (QStringList::ConstIterator it = nodehier.begin(); + it != nodehier.end(); ++it) + { + subnode = subnode.namedItem((*it)); + if (subnode.isNull()) { + subnode = bk.internalElement().ownerDocument().createElement((*it)); + bk.internalElement().appendChild(subnode); + } + } + + if (subnode.firstChild().isNull()) { + QDomText domtext = subnode.ownerDocument().createTextNode(""); + subnode.appendChild(domtext); + } + + QDomText domtext = subnode.firstChild().toText(); + + QString oldText = domtext.data(); + domtext.setData(newValue); + return oldText; +} + +void NodeEditCommand::execute() { + // DUPLICATED HEAVILY FROM KIO/BOOKMARKS + KBookmark bk = CurrentMgr::bookmarkAt(m_address); + Q_ASSERT(!bk.isNull()); + m_oldText = setNodeText(bk, QStringList() << m_nodename, m_newText); +} + +void NodeEditCommand::unexecute() { + // reuse code + NodeEditCommand cmd(m_address, m_oldText, m_nodename); + cmd.execute(); + // get the old text back from it, in case they changed + // (hmm, shouldn't happen) + // AK - DUP'ed from above??? + m_newText = cmd.m_oldText; +} + +void NodeEditCommand::modify(const QString & newText) +{ + m_newText = newText; +} + +QString NodeEditCommand::affectedBookmarks() const +{ + return KBookmark::parentAddress(m_address); +} + +/* -------------------------------------- */ + +void DeleteCommand::execute() { + // kdDebug() << "DeleteCommand::execute " << m_from << endl; + + KBookmark bk = CurrentMgr::bookmarkAt(m_from); + Q_ASSERT(!bk.isNull()); + + if (m_contentOnly) { + QDomElement groupRoot = bk.internalElement(); + + QDomNode n = groupRoot.firstChild(); + while (!n.isNull()) { + QDomElement e = n.toElement(); + if (!e.isNull()) { + // kdDebug() << e.tagName() << endl; + } + QDomNode next = n.nextSibling(); + groupRoot.removeChild(n); + n = next; + } + return; + } + + // TODO - bug - unparsed xml is lost after undo, + // we must store it all therefore + if (!m_cmd) { + if (bk.isGroup()) { + m_cmd = new CreateCommand( + m_from, bk.fullText(), bk.icon(), + bk.internalElement().attribute("folded") == "no"); + m_subCmd = deleteAll(bk.toGroup()); + m_subCmd->execute(); + + } else { + m_cmd = (bk.isSeparator()) + ? new CreateCommand(m_from) + : new CreateCommand(m_from, bk.fullText(), + bk.icon(), bk.url()); + } + } + + m_cmd->unexecute(); +} + +void DeleteCommand::unexecute() { + // kdDebug() << "DeleteCommand::unexecute " << m_from << endl; + + if (m_contentOnly) { + // TODO - recover saved metadata + return; + } + + m_cmd->execute(); + + if (m_subCmd) { + m_subCmd->unexecute(); + } +} + +QString DeleteCommand::affectedBookmarks() const +{ + return KBookmark::parentAddress(m_from); +} + +KEBMacroCommand* DeleteCommand::deleteAll(const KBookmarkGroup & parentGroup) { + KEBMacroCommand *cmd = new KEBMacroCommand(QString::null); + QStringList lstToDelete; + // we need to delete from the end, to avoid index shifting + for (KBookmark bk = parentGroup.first(); + !bk.isNull(); bk = parentGroup.next(bk)) + lstToDelete.prepend(bk.address()); + for (QStringList::Iterator it = lstToDelete.begin(); + it != lstToDelete.end(); ++it) + cmd->addCommand(new DeleteCommand((*it))); + return cmd; +} + +/* -------------------------------------- */ + +QString MoveCommand::name() const { + return i18n("Move %1").arg(m_mytext); +} + +void MoveCommand::execute() { + // kdDebug() << "MoveCommand::execute moving from=" << m_from + // << " to=" << m_to << endl; + + KBookmark bk = CurrentMgr::bookmarkAt(m_from); + Q_ASSERT(!bk.isNull()); + + // look for m_from in the QDom tree + KBookmark oldParent = + CurrentMgr::bookmarkAt(KBookmark::parentAddress(m_from)); + bool wasFirstChild = (KBookmark::positionInParent(m_from) == 0); + + KBookmark oldPreviousSibling = wasFirstChild + ? KBookmark(QDomElement()) + : CurrentMgr::bookmarkAt( + KBookmark::previousAddress(m_from)); + + // look for m_to in the QDom tree + QString parentAddress = KBookmark::parentAddress(m_to); + + KBookmark newParent = CurrentMgr::bookmarkAt(parentAddress); + Q_ASSERT(!newParent.isNull()); + Q_ASSERT(newParent.isGroup()); + + bool isFirstChild = (KBookmark::positionInParent(m_to) == 0); + + if (isFirstChild) { + newParent.toGroup().moveItem(bk, QDomElement()); + + } else { + QString afterAddress = KBookmark::previousAddress(m_to); + + // kdDebug() << "MoveCommand::execute afterAddress=" + // << afterAddress << endl; + KBookmark afterNow = CurrentMgr::bookmarkAt(afterAddress); + Q_ASSERT(!afterNow.isNull()); + + bool movedOkay = newParent.toGroup().moveItem(bk, afterNow); + Q_ASSERT(movedOkay); + + // kdDebug() << "MoveCommand::execute after moving in the dom tree" + // ": item=" << bk.address() << endl; + } + + // because we moved stuff around, the from/to + // addresses can have changed, update + m_to = bk.address(); + m_from = (wasFirstChild) + ? (oldParent.address() + "/0") + : KBookmark::nextAddress(oldPreviousSibling.address()); + // kdDebug() << "MoveCommand::execute : new addresses from=" + // << m_from << " to=" << m_to << endl; +} + +QString MoveCommand::finalAddress() const { + Q_ASSERT( !m_to.isEmpty() ); + return m_to; +} + +void MoveCommand::unexecute() { + // let's not duplicate code. + MoveCommand undoCmd(m_to, m_from); + undoCmd.execute(); + // get the addresses back from that command, in case they changed + m_from = undoCmd.m_to; + m_to = undoCmd.m_from; +} + +QString MoveCommand::affectedBookmarks() const +{ + return KBookmark::commonParent(KBookmark::parentAddress(m_from), KBookmark::parentAddress(m_to)); +} + +/* -------------------------------------- */ + +class SortItem { + public: + SortItem(const KBookmark & bk) : m_bk(bk) { ; } + + bool operator == (const SortItem & s) { + return (m_bk.internalElement() == s.m_bk.internalElement()); } + + bool isNull() const { + return m_bk.isNull(); } + + SortItem previousSibling() const { + return m_bk.parentGroup().previous(m_bk); } + + SortItem nextSibling() const { + return m_bk.parentGroup().next(m_bk); } + + const KBookmark& bookmark() const { + return m_bk; } + + private: + KBookmark m_bk; +}; + +class SortByName { + public: + static QString key(const SortItem &item) { + return (item.bookmark().isGroup() ? "a" : "b") + + (item.bookmark().fullText().lower()); + } +}; + +/* -------------------------------------- */ + +void SortCommand::execute() { + if (m_commands.isEmpty()) { + KBookmarkGroup grp = CurrentMgr::bookmarkAt(m_groupAddress).toGroup(); + Q_ASSERT(!grp.isNull()); + SortItem firstChild(grp.first()); + // this will call moveAfter, which will add + // the subcommands for moving the items + kInsertionSort<SortItem, SortByName, QString, SortCommand> + (firstChild, (*this)); + + } else { + // don't execute for second time on addCommand(cmd) + KEBMacroCommand::execute(); + } +} + +void SortCommand::moveAfter(const SortItem &moveMe, + const SortItem &afterMe) { + QString destAddress = + afterMe.isNull() + // move as first child + ? KBookmark::parentAddress(moveMe.bookmark().address()) + "/0" + // move after "afterMe" + : KBookmark::nextAddress(afterMe.bookmark().address()); + + MoveCommand *cmd = new MoveCommand(moveMe.bookmark().address(), + destAddress); + cmd->execute(); + this->addCommand(cmd); +} + +void SortCommand::unexecute() { + KEBMacroCommand::unexecute(); +} + +QString SortCommand::affectedBookmarks() const +{ + return m_groupAddress; +} + +/* -------------------------------------- */ + +KEBMacroCommand* CmdGen::setAsToolbar(const KBookmark &bk) { + KEBMacroCommand *mcmd = new KEBMacroCommand(i18n("Set as Bookmark Toolbar")); + + KBookmarkGroup oldToolbar = CurrentMgr::self()->mgr()->toolbar(); + if (!oldToolbar.isNull()) { + QValueList<EditCommand::Edition> lst; + lst.append(EditCommand::Edition("toolbar", "no")); + lst.append(EditCommand::Edition("icon", "")); + EditCommand *cmd1 = new EditCommand(oldToolbar.address(), lst); + mcmd->addCommand(cmd1); + } + + QValueList<EditCommand::Edition> lst; + lst.append(EditCommand::Edition("toolbar", "yes")); + lst.append(EditCommand::Edition("icon", "bookmark_toolbar")); + // TODO - see below + EditCommand *cmd2 = new EditCommand(bk.address(), lst); + mcmd->addCommand(cmd2); + + return mcmd; +} + +bool CmdGen::shownInToolbar(const KBookmark &bk) { + return (bk.internalElement().attribute("showintoolbar") == "yes"); +} + +KEBMacroCommand* CmdGen::setShownInToolbar(const QValueList<KBookmark> &bks, bool show) { + QString i18n_name = i18n("%1 in Bookmark Toolbar").arg(show ? i18n("Show") + : i18n("Hide")); + KEBMacroCommand *mcmd = new KEBMacroCommand(i18n_name); + + QValueList<KBookmark>::ConstIterator it, end; + end = bks.end(); + for(it = bks.begin(); it != end; ++it) + { + QValueList<EditCommand::Edition> lst; + lst.append(EditCommand::Edition("showintoolbar", show ? "yes" : "no")); + EditCommand *cmd = new EditCommand((*it).address(), lst); + mcmd->addCommand(cmd); + } + return mcmd; +} + +KEBMacroCommand* CmdGen::insertMimeSource( + const QString &cmdName, QMimeSource *_data, const QString &addr +) { + QMimeSource *data = _data; + bool modified = false; + const char *format = 0; + for (int i = 0; format = data->format(i), format; i++) { + // qt docs don't say if encodedData(blah) where + // blah is not a stored mimetype should return null + // or not. so, we search. sucky... + if (strcmp(format, "GALEON_BOOKMARK") == 0) { + modified = true; + QStoredDrag *mydrag = new QStoredDrag("application/x-xbel"); + mydrag->setEncodedData(data->encodedData("GALEON_BOOKMARK")); + data = mydrag; + break; + } else if( strcmp(format, "application/x-xbel" )==0) { + /* nothing we created a kbbookmarks drag when we copy element (slotcopy/slotpaste)*/ + break; + } else if (strcmp(format, "text/uri-list") == 0) { + KURL::List uris; + if (!KURLDrag::decode(data, uris)) + continue; // break out of format loop + KURL::List::ConstIterator uit = uris.begin(); + KURL::List::ConstIterator uEnd = uris.end(); + QValueList<KBookmark> urlBks; + for ( ; uit != uEnd ; ++uit ) { + if (!(*uit).url().endsWith(".desktop")) { + urlBks << KBookmark::standaloneBookmark((*uit).prettyURL(), (*uit)); + continue; + } + KDesktopFile df((*uit).path(), true); + QString title = df.readName(); + KURL url(df.readURL()); + if (title.isNull()) + title = url.prettyURL(); + urlBks << KBookmark::standaloneBookmark(title, url, df.readIcon()); + } + KBookmarkDrag *mydrag = KBookmarkDrag::newDrag(urlBks, 0); + modified = true; + data = mydrag; + } + } + if (!KBookmarkDrag::canDecode(data)) + { + if (modified) // Shouldn't happen + delete data; + return 0; + } + KEBMacroCommand *mcmd = new KEBMacroCommand(cmdName); + QString currentAddress = addr; + QValueList<KBookmark> bookmarks = KBookmarkDrag::decode(data); + for (QValueListConstIterator<KBookmark> it = bookmarks.begin(); + it != bookmarks.end(); ++it) { + CreateCommand *cmd = new CreateCommand(currentAddress, (*it)); + cmd->execute(); + mcmd->addCommand(cmd); + currentAddress = KBookmark::nextAddress(currentAddress); + } + if (modified) + delete data; + return mcmd; +} + +KEBMacroCommand* CmdGen::itemsMoved(const QValueVector<KEBListViewItem *> & items, + const QString &newAddress, bool copy) { + KEBMacroCommand *mcmd = new KEBMacroCommand(copy ? i18n("Copy Items") + : i18n("Move Items")); + + QValueList<KBookmark> list = ListView::self()->itemsToBookmarks( items ); + QValueList<KBookmark>::const_iterator it, end; + it = list.begin(); + end = list.end(); + + QString bkInsertAddr = newAddress; + for (; it != end; ++it) { + if (copy) { + CreateCommand *cmd; + cmd = new CreateCommand( + bkInsertAddr, + (*it).internalElement() + .cloneNode(true).toElement(), + (*it).text()); + + cmd->execute(); + mcmd->addCommand(cmd); + + bkInsertAddr = cmd->finalAddress(); + + } else /* if (move) */ { + QString oldAddress = (*it).address(); + if (bkInsertAddr.startsWith(oldAddress)) //FIXME uses internal representation of address + continue; + + MoveCommand *cmd = new MoveCommand(oldAddress, bkInsertAddr, + (*it).text()); + cmd->execute(); + mcmd->addCommand(cmd); + + bkInsertAddr = cmd->finalAddress(); + } + + bkInsertAddr = KBookmark::nextAddress(bkInsertAddr); + } + + return mcmd; +} |