diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 35d3d69..3782e71 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,141 +1,142 @@ include_directories(${GPGME_INCLUDES}) ########### next target ############### set(libbasket_SRCS aboutdata.cpp archive.cpp backgroundmanager.cpp backup.cpp basketfactory.cpp basketlistview.cpp basketproperties.cpp basketscene.cpp basketscenemodel.cpp basketstatusbar.cpp basketview.cpp bnpview.cpp colorpicker.cpp common.cpp debugwindow.cpp decoratedbasket.cpp diskerrordialog.cpp file_metadata.cpp filter.cpp focusedwidgets.cpp formatimporter.cpp global.cpp gitwrapper.cpp htmlexporter.cpp history.cpp kcolorcombo2.cpp kgpgme.cpp linklabel.cpp newbasketdialog.cpp note.cpp notecontent.cpp notedrag.cpp noteedit.cpp noteitem.cpp notefactory.cpp noteselection.cpp password.cpp regiongrabber.cpp settings.cpp settings_versionsync.cpp + simplecontent.cpp softwareimporters.cpp systemtray.cpp tag.cpp tagsedit.cpp transparentwidget.cpp tools.cpp variouswidgets.cpp xmlwork.cpp ) set(PIMO_CPP "") # One of the generated files ki18n_wrap_ui(basket_FORM_HDRS passwordlayout.ui basketproperties.ui settings_versionsync.ui) qt5_add_dbus_adaptor(libbasket_SRCS org.kde.basket.BNPView.xml bnpview.h BNPView) qt5_add_resources(basket_RESOURCES ../basket.qrc) add_library(LibBasket SHARED ${libbasket_SRCS} ${basket_FORM_HDRS} ${basket_RESOURCES}) target_link_libraries(LibBasket ${PHONON_LIBRARY} ${GPGME_VANILLA_LIBRARIES} KF5::Archive KF5::ConfigWidgets KF5::CoreAddons KF5::Crash KF5::DBusAddons KF5::FileMetaData KF5::GlobalAccel KF5::GuiAddons KF5::I18n KF5::IconThemes KF5::KCMUtils KF5::KIOWidgets KF5::Notifications KF5::Parts KF5::TextWidgets KF5::WindowSystem KF5::XmlGui ) set_target_properties(LibBasket PROPERTIES VERSION ${Qt5Core_VERSION} SOVERSION ${Qt5Core_VERSION_MAJOR} ) install(TARGETS LibBasket DESTINATION ${LIB_INSTALL_DIR}) # Add unit tests after all variables have been set. # If we save target_link_libraries to a variable, we can reuse it too if (BUILD_TESTING) add_subdirectory(tests) endif () ########### next target ############### set(basket_SRCS main.cpp mainwindow.cpp application.cpp) add_executable(basket ${basket_SRCS}) target_link_libraries(basket LibBasket) if (LIBGIT2_FOUND) target_link_libraries(LibBasket ${LIBGIT2_LIBRARIES}) target_link_libraries(basket ${LIBGIT2_LIBRARIES}) endif() install(TARGETS basket DESTINATION ${BIN_INSTALL_DIR}) ########### next target ############### set(kcm_basket_PART_SRCS kcm_basket.cpp) add_library(kcm_basket MODULE ${kcm_basket_PART_SRCS}) target_link_libraries(kcm_basket LibBasket) install(TARGETS kcm_basket DESTINATION ${PLUGIN_INSTALL_DIR}) set(DESKTOP_FILES basket_config_general.desktop basket_config_baskets.desktop basket_config_new_notes.desktop basket_config_notes_appearance.desktop basket_config_apps.desktop basket_config_version_sync.desktop ) install(FILES org.kde.basket.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) install(FILES org.kde.basket.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install(FILES ${DESKTOP_FILES} DESTINATION ${SERVICES_INSTALL_DIR}) install(FILES basketui.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/basket) diff --git a/src/basketscenemodel.cpp b/src/basketscenemodel.cpp index 77def81..5fefd29 100644 --- a/src/basketscenemodel.cpp +++ b/src/basketscenemodel.cpp @@ -1,190 +1,190 @@ /* * SPDX-FileCopyrightText: (C) 2020 by Ismael Asensio * SPDX-License-Identifier: GPL-2.0-or-later */ #include "basketscenemodel.h" #include "basketscene.h" #include BasketSceneModel::BasketSceneModel(QObject *parent) : QAbstractItemModel(parent) , m_root(new NoteItem()) { - qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); } BasketSceneModel::~BasketSceneModel() { delete m_root; } int BasketSceneModel::rowCount(const QModelIndex &parent) const { return itemAtIndex(parent)->childrenCount(); } int BasketSceneModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent) return 1; } QModelIndex BasketSceneModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } NoteItem *item = itemAtIndex(parent)->children().at(row); if (item) { return createIndex(row, column, item); } return QModelIndex(); } QModelIndex BasketSceneModel::parent(const QModelIndex& index) const { if (!index.isValid()) return QModelIndex(); NoteItem *parentItem = itemAtIndex(index)->parent(); return indexOfItem(parentItem); } QVariant BasketSceneModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(section) if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return QStringLiteral("BasketSceneModel"); return QVariant(); } QHash BasketSceneModel::roleNames() const { static const auto roles = QHash { { ContentRole, "content" }, { TypeRole, "type" }, { GeometryRole, "geometry" }, { EditionRole, "edition" } }.unite(QAbstractItemModel::roleNames()); return roles; } QVariant BasketSceneModel::data(const QModelIndex &index, int role) const { if (!checkIndex(index, CheckIndexOption::IndexIsValid)) { return QVariant(); } const NoteItem *item = itemAtIndex(index); switch (role) { case Qt::DisplayRole: return item->displayText(); case Qt::DecorationRole: return item->decoration(); case Qt::ToolTipRole: // To ease debugging return item->toolTipInfo(); case BasketSceneModel::ContentRole: return QVariant::fromValue(item->content()); case BasketSceneModel::TypeRole: return item->type(); case BasketSceneModel::GeometryRole: return item->bounds(); case BasketSceneModel::EditionRole: { QVariant info; - info.setValue(item->editInformation()); + info.setValue(item->editionDates()); return info; } } return QVariant(); } NoteItem *BasketSceneModel::itemAtIndex(const QModelIndex &index) const { if (!index.isValid()) { return m_root; } return static_cast(index.internalPointer()); } QModelIndex BasketSceneModel::indexOfItem(NoteItem *item) const { if (!item || item == m_root) { return QModelIndex(); } return createIndex(item->row(), 0, item); } bool BasketSceneModel::insertRows(int row, int count, const QModelIndex& parent) { if(!checkIndex(parent)) { return false; } beginInsertRows(parent, row, row + count - 1); for (int i = 0; i < count; i++) { itemAtIndex(parent)->insertAt(row + i, new NoteItem()); } endInsertRows(); return true; } bool BasketSceneModel::removeRows(int row, int count, const QModelIndex& parent) { if(!checkIndex(parent)) { return false; } beginRemoveRows(parent, row, row + count - 1); for (int i = 0; i < count; i++) { itemAtIndex(parent)->removeAt(row); } endRemoveRows(); return true; } bool BasketSceneModel::moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent, int destinationChild) { const bool isMoveDown = (destinationParent == sourceParent && destinationChild > sourceRow); if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count -1, destinationParent, (isMoveDown) ? destinationChild + 1 : destinationChild)) { return false; } for (int i = 0; i < count; i++) { const int oldRow = (isMoveDown) ? sourceRow : sourceRow + i; itemAtIndex(destinationParent)->insertAt(destinationChild + i, itemAtIndex(sourceParent)->takeAt(oldRow)); } endMoveRows(); return true; } void BasketSceneModel::clear() { beginResetModel(); qDeleteAll(m_root->children()); endResetModel(); } void BasketSceneModel::loadFromXML(const QDomElement &node) { beginResetModel(); qDeleteAll(m_root->children()); NoteItem::setBasketParent(qobject_cast(qobject_cast(this)->parent())); m_root->loadFromXMLNode(node); endResetModel(); } diff --git a/src/noteitem.cpp b/src/noteitem.cpp index a5a7a91..e8b7073 100644 --- a/src/noteitem.cpp +++ b/src/noteitem.cpp @@ -1,271 +1,242 @@ /* * SPDX-FileCopyrightText: (C) 2003 by Sébastien Laoût * SPDX-FileCopyrightText: (C) 2020 by Ismael Asensio * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "basketscenemodel.h" - -#include "notefactory.h" -#include "xmlwork.h" +#include "noteitem.h" #include +#include "basketscene.h" // TEMP: to get basket->fullPath() +#include "xmlwork.h" + namespace { QString iconNameForType(NoteType::Id type) { switch (type) { case NoteType::Group: return "package"; case NoteType::Text: return "text-x-plain"; case NoteType::Html: return "text-html"; case NoteType::Image: return "folder-pictures-symbolic"; case NoteType::Animation: return "folder-video-symbolic"; case NoteType::Sound: return "folder-music-symbolic"; case NoteType::File: return "application-x-zerosize"; case NoteType::Link: return "edit-link"; case NoteType::CrossReference: return "basket"; case NoteType::Launcher: return "run-build"; case NoteType::Color: return "color-profile"; case NoteType::Unknown: return "application-octet-stream"; } return QString(); } } BasketScene *NoteItem::s_basket; NoteItem::NoteItem() : m_parent(nullptr) - , m_helperNote(new Note(s_basket)) + , m_content(nullptr) { } NoteItem::~NoteItem() { - delete m_helperNote; + delete m_content; qDeleteAll(m_children); } int NoteItem::row() const { if (!m_parent) { return 0; } return m_parent->m_children.indexOf(const_cast(this)); } NoteItem *NoteItem::parent() const { return m_parent; } void NoteItem::setParent(NoteItem *parent) { m_parent = parent; } QVector NoteItem::children() const { return m_children; } int NoteItem::childrenCount() const { return m_children.count(); } void NoteItem::insertAt(int row, NoteItem *item) { if (!item) return; item->setParent(this); if (row >= 0 && row < m_children.count() ) { m_children.insert(row, item); } else { m_children.append(item); } } void NoteItem::removeAt(int row) { if (row < 0 || row >= m_children.count()) { return; } delete m_children[row]; m_children.remove(row); } NoteItem *NoteItem::takeAt(int row) { if (row < 0 || row >= m_children.count()) { return nullptr; } return m_children.takeAt(row); } -NotePtr NoteItem::note() const -{ - return m_helperNote; -} - void NoteItem::setBasketParent(BasketScene* basket) { s_basket = basket; } QString NoteItem::displayText() const { - if (!note()) { return QStringLiteral("NULL NOTE"); } if (type() == NoteType::Group) { return QStringLiteral("GROUP"); } + if (!content()) { return QStringLiteral(""); } return content()->toText(QString()); } QIcon NoteItem::decoration() const { - if (!note()) { return QIcon::fromTheme("data-error"); } - if (type() == NoteType::Group) { return QIcon::fromTheme("package"); } - if (!content()) { return QIcon::fromTheme("empty"); } - - return QIcon::fromTheme(iconNameForType(content()->type())); + return QIcon::fromTheme(iconNameForType(type())); } -NoteContent *NoteItem::content() const +SimpleContent *NoteItem::content() const { - if (!note()) { - return nullptr; - } - return note()->content(); + return m_content; } NoteType::Id NoteItem::type() const { - if (!note()) { - return NoteType::Unknown; - } - if (!note()->content()) { - return NoteType::Group; - } - return note()->content()->type(); + return content()->type(); } QRect NoteItem::bounds() const { - return QRect( - note()->x(), - note()->y(), - note()->width(), - note()->height() - ); + return m_bounds; } -NoteItem::EditInfo NoteItem::editInformation() const +NoteItem::EditionDates NoteItem::editionDates() const { - return EditInfo { - note()->addedDate(), - note()->lastModificationDate() - }; + return m_editInfo; } QString NoteItem::address() const { if (!m_parent) { return QStringLiteral("root"); } return QString::number(reinterpret_cast(this), 16); } QString NoteItem::toolTipInfo() const { QStringList toolTip; // fullAddress and position within parent (debug) toolTip << QStringLiteral("<%1> @<%2>[%3]") .arg(address()) .arg(m_parent->address()) .arg(row()); // type - toolTip << QStringLiteral("Type: %1").arg(content() ? content()->typeName() : "Group"); + toolTip << QStringLiteral("Type: %1").arg(NoteType::typeToName(type())); // geometry const QRect geometry = bounds(); toolTip << QStringLiteral("x:%1 y:%2 w:%3 h:%4") .arg(geometry.x()).arg(geometry.y()) .arg(geometry.width()).arg(geometry.height()); // edition information - const EditInfo info = editInformation(); - toolTip << QStringLiteral("created: %1\nmodified: %2") - .arg(info.created.toString()) - .arg(info.modified.toString()); + const EditionDates info = editionDates(); + toolTip << QStringLiteral("created: %1").arg(info.created.toString()) + << QStringLiteral("modified: %1").arg(info.modified.toString()); + + // tags + toolTip << QStringLiteral("Tags: %1").arg(m_tagIds.join(QStringLiteral(", "))); + + //attributes + const auto attributes = m_content->attributes(); + for (const QString &attrName : attributes.keys()) { + toolTip << QStringLiteral("%1: %2").arg(attrName) + .arg(attributes.value(attrName).toString()); + } return toolTip.join(QStringLiteral("\n")); } void NoteItem::loadFromXMLNode(const QDomElement& node) { for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); if (e.isNull()) { continue; } - NoteItem *noteItem = new NoteItem(); - noteItem->setParent(this); - m_children.append(noteItem); - - NotePtr note = noteItem->note(); // Helper Note object - - if (e.tagName() == "group") { - // Node is a group. Recursively load from this element - noteItem->loadFromXMLNode(e); - } else if (e.tagName() == "note" || e.tagName() == "item") { // "item" is to keep compatible with 0.6.0 Alpha 1 (required?) - // Load note content - NoteFactory::loadNode(XMLWork::getElement(e, "content"), - e.attribute("type"), - note, - true); //lazyload - } + NoteItem *child = new NoteItem(); + this->insertAt(-1, child); - // Load dates - if (e.hasAttribute("added")) { - note->setAddedDate(QDateTime::fromString(e.attribute("added"), Qt::ISODate)); - } - if (e.hasAttribute("lastModification")) { - note->setLastModificationDate(QDateTime::fromString(e.attribute("lastModification"), Qt::ISODate)); - } - // Free Note Properties: - if (note->isFree()) { - int x = e.attribute("x").toInt(); - int y = e.attribute("y").toInt(); - note->setX(x < 0 ? 0 : x); - note->setY(y < 0 ? 0 : y); - } - // Resizeable Note Properties: - if (note->hasResizer() || note->isColumn()) { - note->setGroupWidth(e.attribute("width", "200").toInt()); - } - // Group Properties: - if (note->isGroup() && !note->isColumn() && XMLWork::trueOrFalse(e.attribute("folded", "false"))) { - note->toggleFolded(); - } - // Tags: - if (note->content()) { - const QString tagsString = XMLWork::getElementText(e, QStringLiteral("tags"), QString()); - const QStringList tagsId = tagsString.split(';'); - for (const QString &tagId : tagsId) { - State *state = Tag::stateForId(tagId); - if (state) { - note->addState(state, /*orReplace=*/true); - } - } + child->loadPropertiesFromXMLNode(e); + if (child->type() == NoteType::Group) { + child->loadFromXMLNode(e); } } } + +void NoteItem::loadPropertiesFromXMLNode(const QDomElement& node) +{ + if (node.tagName() == "group") { + m_content = new SimpleContent(NoteType::Group); + m_content->loadFromXMLNode(node); + } + if (node.tagName() == "note" || node.tagName() == "item") { // "item" is to keep compatible with 0.6.0 Alpha 1 (required?) + const QDomElement content = XMLWork::getElement(node, "content"); + NoteType::Id type = NoteType::typeFromLowerName(node.attribute("type")); + m_content = new SimpleContent(type, s_basket->fullPath()); + m_content->loadFromXMLNode(content); + } + + // Load dates + if (node.hasAttribute("added")) { + m_editInfo.created = QDateTime::fromString(node.attribute("added"), Qt::ISODate); + } + if (node.hasAttribute("lastModification")) { + m_editInfo.modified = QDateTime::fromString(node.attribute("lastModification"), Qt::ISODate); + } + + m_bounds = QRect(node.attribute("x", "0").toInt(), + node.attribute("y", "0").toInt(), + node.attribute("width", "200").toInt(), + node.attribute("height", "200").toInt()); + + // Tags: + const QString tagsString = XMLWork::getElementText(node, QStringLiteral("tags"), QString()); + m_tagIds = tagsString.split(';'); +} diff --git a/src/noteitem.h b/src/noteitem.h index 3394e10..2804ae3 100644 --- a/src/noteitem.h +++ b/src/noteitem.h @@ -1,80 +1,76 @@ /* * SPDX-FileCopyrightText: 2020 by Ismael Asensio * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once -#include "note.h" -#include "notecontent.h" - #include +#include "simplecontent.h" -class QDomElement; -typedef Note * NotePtr; +class QDomElement; /** NoteItem: Container that stores a Note object within a tree model * Eventually implement the managing functionallity here and use directly the `NoteContent` object */ class NoteItem { public: - struct EditInfo + struct EditionDates { QDateTime created; QDateTime modified; }; public: explicit NoteItem(); ~NoteItem(); // Tree structure int row() const; NoteItem *parent() const; void setParent(NoteItem *parent); QVector children() const; int childrenCount() const; void insertAt(int row, NoteItem *item); void removeAt(int row); NoteItem *takeAt(int row); // Accesors for compatibility with current code - NotePtr note() const; static void setBasketParent(BasketScene *basket); // NoteItem property getters QString displayText() const; QIcon decoration() const; - NoteContent *content() const; NoteType::Id type() const; + SimpleContent *content() const; QRect bounds() const; - EditInfo editInformation() const; + EditionDates editionDates() const; - // Recursive loader from an XML node + // Loader from XML node (.basket xml format) void loadFromXMLNode(const QDomElement &node); + void loadPropertiesFromXMLNode(const QDomElement &node); // Useful methods to debug QString address() const; QString toolTipInfo() const; private: NoteItem *m_parent; QVector m_children; - NotePtr m_helperNote; // Dummy note to help with the code transition. - static BasketScene *s_basket; // Stored to set a parent to the notes (and avoid crashes) + static BasketScene *s_basket; // Stored to access some basket methods before porting -// NoteContent *m_content; -// QRect m_bounds; -// EditInfo m_editInfo; -// QVector m_tags; + SimpleContent *m_content; + QRect m_bounds; + EditionDates m_editInfo; + QStringList m_tagIds; }; -Q_DECLARE_METATYPE(NoteContent *) -Q_DECLARE_METATYPE(NoteItem::EditInfo) +Q_DECLARE_METATYPE(SimpleContent *) +Q_DECLARE_METATYPE(NoteItem::EditionDates) diff --git a/src/simplecontent.cpp b/src/simplecontent.cpp new file mode 100644 index 0000000..d6e9822 --- /dev/null +++ b/src/simplecontent.cpp @@ -0,0 +1,83 @@ +/* + * SPDX-FileCopyrightText: (C) 2020 by Ismael Asensio + * SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "simplecontent.h" + +#include + +#include "common.h" + + +SimpleContent::SimpleContent(NoteType::Id type, const QString &basketPath) + : m_type(type) + , m_basketPath(basketPath) +{ +} + +NoteType::Id SimpleContent::type() const +{ + return m_type; +} + +QVariantMap SimpleContent::attributes() const +{ + return m_attributes; +} + + +QString SimpleContent::toText(const QString& unused) const +{ + Q_UNUSED(unused) +/* + switch (m_type) { + case NoteType::Text: + case NoteType::Link: + return QString::fromUtf8(m_rawData); + default: + return m_filePath; + } + return QString(); + */ + return m_filePath; +} + +// Indicates special attributes in the XML for some types +QVariantMap SimpleContent::defaultAttributes() const +{ + switch (m_type) { + case NoteType::Group: return { + { QStringLiteral("folded"), false }, + }; + case NoteType::Link: return { + { QStringLiteral("title"), QString() }, + { QStringLiteral("icon"), QString() }, + { QStringLiteral("autoTitle"), true }, + { QStringLiteral("autoIcon"), true } + }; + default: return {}; + } + return {}; +} + +void SimpleContent::loadFromXMLNode(QDomElement node) +{ + // Get filename + m_filePath = node.text(); +/* + //Load data + const bool loadedOk = FileStorage::loadFromFile(m_basketPath + m_filePath, &m_rawData); + if (!loadedOk) { + m_rawData = node.text().toUtf8(); + } +*/ + // Load Attributes + m_attributes = defaultAttributes(); + for (const QString &attrName : m_attributes.keys()) { + if (node.hasAttribute(attrName)) { + const QString attrValue = node.attribute(attrName); + m_attributes.insert(attrName, attrValue); + } + } +} diff --git a/src/simplecontent.h b/src/simplecontent.h new file mode 100644 index 0000000..bce7a79 --- /dev/null +++ b/src/simplecontent.h @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2020 by Ismael Asensio + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#pragma once + +#include "notecontent.h" // To use NoteType + +class QDomElement; + + +class SimpleContent { +public: + SimpleContent (NoteType::Id type, const QString &basketPath = QString()); + + NoteType::Id type() const; + QVariantMap attributes() const; + QString toText(const QString &unused) const; + + void loadFromXMLNode(QDomElement node); + +private: + QVariantMap defaultAttributes() const; // TODO: Implement later as subclass override + +private: + NoteType::Id m_type = NoteType::Unknown; + QString m_basketPath; + QString m_filePath; + QByteArray m_rawData; + QVariantMap m_attributes; +};