From f18185adbdce9891be0cbd2197838441aaa5ed3e Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Thu, 31 Aug 2023 17:16:13 -0700 Subject: [PATCH] Add dragging and dropping of bookmarks. --- src/CMakeLists.txt | 1 + src/databases/BookmarksDatabase.cpp | 306 +++++++++++++++++++++++++--- src/databases/BookmarksDatabase.h | 12 +- src/dialogs/AddBookmarkDialog.cpp | 55 +++-- src/dialogs/AddBookmarkDialog.h | 12 +- src/dialogs/BookmarksDialog.cpp | 197 +++++++++++++++--- src/dialogs/BookmarksDialog.h | 27 ++- src/dialogs/CMakeLists.txt | 1 + src/dialogs/EditBookmarkDialog.cpp | 123 +++++++++++ src/dialogs/EditBookmarkDialog.h | 59 ++++++ src/structs/BookmarkStruct.h | 1 + src/uis/AddBookmarkDialog.ui | 292 ++++++++++++++------------ src/uis/BookmarksDialog.ui | 49 ++++- src/uis/EditBookmarkDialog.ui | 202 ++++++++++++++++++ src/widgets/CMakeLists.txt | 5 +- src/widgets/DraggableTreeView.cpp | 171 ++++++++++++++++ src/widgets/DraggableTreeView.h | 42 ++++ src/windows/BrowserWindow.cpp | 36 +++- src/windows/BrowserWindow.h | 3 +- 19 files changed, 1369 insertions(+), 225 deletions(-) create mode 100644 src/dialogs/EditBookmarkDialog.cpp create mode 100644 src/dialogs/EditBookmarkDialog.h create mode 100644 src/uis/EditBookmarkDialog.ui create mode 100644 src/widgets/DraggableTreeView.cpp create mode 100644 src/widgets/DraggableTreeView.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f96399a..3ecd3c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -42,6 +42,7 @@ ki18n_wrap_ui(privacybrowser uis/CookiesDialog.ui uis/DomainSettingsDialog.ui uis/DurableCookiesDialog.ui + uis/EditBookmarkDialog.ui uis/SaveDialog.ui uis/SettingsGeneral.ui uis/SettingsPrivacy.ui diff --git a/src/databases/BookmarksDatabase.cpp b/src/databases/BookmarksDatabase.cpp index c290682..fdcb0fd 100644 --- a/src/databases/BookmarksDatabase.cpp +++ b/src/databases/BookmarksDatabase.cpp @@ -104,31 +104,35 @@ void BookmarksDatabase::addDatabase() } }; -void BookmarksDatabase::addBookmark(const QString &bookmarkName, const QString &bookmarkUrl, const QIcon &favoriteIcon) +void BookmarksDatabase::addBookmark(const BookmarkStruct *bookmarkStructPointer) { // Get a handle for the bookmarks database. QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); - // Get a favorite icon pixmap. - QPixmap favoriteIconPixmap = favoriteIcon.pixmap(32, 32); + // Instantiate a count bookmarks query. TODO: This needs to be updated to only count the bookmarks in the current folder. + QSqlQuery countBookmarksQuery(bookmarksDatabase); - // Create a favorite icon byte array. - QByteArray favoriteIconByteArray; + // Set the query to be forward only, which is more performant. + countBookmarksQuery.setForwardOnly(true); - // Create a favorite icon buffer. - QBuffer favoriteIconBuffer(&favoriteIconByteArray); + // Prepare the count bookmarks query. + countBookmarksQuery.prepare("SELECT " + DISPLAY_ORDER + " FROM " + BOOKMARKS_TABLE); - // Open the buffer. - favoriteIconBuffer.open(QIODevice::WriteOnly); + // Execute the count bookmarks query. + countBookmarksQuery.exec(); - // Convert the favorite icon pixmap into a byte array in PNG format. - favoriteIconPixmap.save(&favoriteIconBuffer, "PNG"); + // Move to the last row. + countBookmarksQuery.last(); - // Close the buffer. - favoriteIconBuffer.close(); + // Initialize a bookmarks count variable. + int bookmarksCount = 0; - // Convert the favorite icon byte array to a base 64 string. - QString favoriteIconBase64String = favoriteIconByteArray.toBase64(); + // Check to see if the query is valid (there is at least one bookmark). + if (countBookmarksQuery.isValid()) + { + // Get the number of rows (which is zero based) and add one to calculate the number of bookmarks. + bookmarksCount = countBookmarksQuery.at() + 1; + } // Instantiate an add bookmark query. QSqlQuery addBookmarkQuery(bookmarksDatabase); @@ -137,19 +141,127 @@ void BookmarksDatabase::addBookmark(const QString &bookmarkName, const QString & addBookmarkQuery.prepare("INSERT INTO " + BOOKMARKS_TABLE + " (" + BOOKMARK_NAME + ", " + BOOKMARK_URL + ", " + - FAVORITE_ICON + ") " - "VALUES (:bookmark_name, :bookmark_url, :favorite_icon)" + FAVORITE_ICON + ", " + + DISPLAY_ORDER + ") " + + "VALUES (:bookmark_name, :bookmark_url, :favorite_icon, :display_order)" ); - // Bind the values. - addBookmarkQuery.bindValue(":bookmark_name", bookmarkName); - addBookmarkQuery.bindValue(":bookmark_url", bookmarkUrl); - addBookmarkQuery.bindValue(":favorite_icon", favoriteIconBase64String); + // Bind the query values. + addBookmarkQuery.bindValue(":bookmark_name", bookmarkStructPointer->bookmarkName); + addBookmarkQuery.bindValue(":bookmark_url", bookmarkStructPointer->bookmarkUrl); + addBookmarkQuery.bindValue(":favorite_icon", getFavoriteIconBase64String(bookmarkStructPointer->favoriteIcon)); + addBookmarkQuery.bindValue(":display_order", bookmarksCount); - // Execute the query. + // Execute the add bookmark query. addBookmarkQuery.exec(); } +void BookmarksDatabase::deleteBookmark(const int bookmarkId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a delete bookmark query. + QSqlQuery deleteBookmarkQuery(bookmarksDatabase); + + // Prepare the delete bookmark query. + deleteBookmarkQuery.prepare("DELETE FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " = :id"); + + // Bind the query values. + deleteBookmarkQuery.bindValue(":id", bookmarkId); + + // Execute the query. + deleteBookmarkQuery.exec(); + + // Reset the display order for the other items in the folder. TODO: make this folder aware. + // TODO: Perhaps, for performance reasons, this shouldn't run each time a bookmarks is deleted, but batched at the end. + + // Instantiate a bookmarks query. + QSqlQuery bookmarksQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + bookmarksQuery.setForwardOnly(true); + + // Prepare the bookmarks query. + bookmarksQuery.prepare("SELECT " + ID + ", " + DISPLAY_ORDER + " FROM " + BOOKMARKS_TABLE + " ORDER BY " + DISPLAY_ORDER + " ASC"); + + // Execute the query. + bookmarksQuery.exec(); + + // Create a new display order int. + int newDisplayOrder = 0; + + // Update the display order for each bookmark. + while (bookmarksQuery.next()) + { + // Check if the new display order is different than the current display order. + if (bookmarksQuery.value(DISPLAY_ORDER).toInt() != newDisplayOrder) + { + // Instantiate an update display order query. + QSqlQuery updateDisplayOrderQuery(bookmarksDatabase); + + // Prepare the update display order query. + updateDisplayOrderQuery.prepare("UPDATE " + BOOKMARKS_TABLE + " SET " + DISPLAY_ORDER + " = :display_order WHERE " + ID + " = :id"); + + // Bind the query values. + updateDisplayOrderQuery.bindValue(":display_order", newDisplayOrder); + updateDisplayOrderQuery.bindValue(":id", bookmarksQuery.value(ID).toInt()); + + // Execute the query. + updateDisplayOrderQuery.exec(); + } + + // Increment the new display order. + ++newDisplayOrder; + } +} + +BookmarkStruct *BookmarksDatabase::getBookmark(int bookmarkId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a bookmark query. + QSqlQuery bookmarkQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + bookmarkQuery.setForwardOnly(true); + + // Prepare the bookmark query. + bookmarkQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " = :id"); + + // Bind the query values. + bookmarkQuery.bindValue(":id", bookmarkId); + + // Execute the query. + bookmarkQuery.exec(); + + // Move to the first entry. + bookmarkQuery.first(); + + // Create a bookmark struct. + struct BookmarkStruct *bookmarkStructPointer = new BookmarkStruct(); + + // Get the favorite icon base 64 byte array. + QByteArray favoriteIconByteArray = QByteArray::fromBase64(bookmarkQuery.value(FAVORITE_ICON).toByteArray()); + + // Create a favorite icon pixmap. + QPixmap favoriteIconPixmap; + + // Load the pixmap from byte array. + favoriteIconPixmap.loadFromData(favoriteIconByteArray); + + // Populate the bookmark struct. + bookmarkStructPointer->id = bookmarkQuery.value(ID).toInt(); + bookmarkStructPointer->bookmarkName = bookmarkQuery.value(BOOKMARK_NAME).toString(); + bookmarkStructPointer->bookmarkUrl = bookmarkQuery.value(BOOKMARK_URL).toString(); + bookmarkStructPointer->displayOrder = bookmarkQuery.value(DISPLAY_ORDER).toInt(); + bookmarkStructPointer->favoriteIcon = QIcon(favoriteIconPixmap); + + // Return the bookmark struct pointer. + return bookmarkStructPointer; +} + std::list* BookmarksDatabase::getBookmarks() { // Get a handle for the bookmarks database. @@ -162,7 +274,7 @@ std::list* BookmarksDatabase::getBookmarks() bookmarksQuery.setForwardOnly(true); // Prepare the bookmarks query. - bookmarksQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE); + bookmarksQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " ORDER BY " + DISPLAY_ORDER + " ASC"); // Execute the query. bookmarksQuery.exec(); @@ -176,7 +288,7 @@ std::list* BookmarksDatabase::getBookmarks() // Create a bookmark struct. struct BookmarkStruct bookmarkStruct; - // Get the favorite icon base 64 bute array. + // Get the favorite icon base 64 byte array. QByteArray favoriteIconByteArray = QByteArray::fromBase64(bookmarksQuery.value(FAVORITE_ICON).toByteArray()); // Create a favorite icon pixmap. @@ -189,6 +301,7 @@ std::list* BookmarksDatabase::getBookmarks() bookmarkStruct.id = bookmarksQuery.value(ID).toInt(); bookmarkStruct.bookmarkName = bookmarksQuery.value(BOOKMARK_NAME).toString(); bookmarkStruct.bookmarkUrl = bookmarksQuery.value(BOOKMARK_URL).toString(); + bookmarkStruct.displayOrder = bookmarksQuery.value(DISPLAY_ORDER).toInt(); bookmarkStruct.favoriteIcon = QIcon(favoriteIconPixmap); // Add the bookmark to the list. @@ -199,6 +312,147 @@ std::list* BookmarksDatabase::getBookmarks() return bookmarkListPointer; } +QList* BookmarksDatabase::getBookmarksExcept(QList *exceptDatabaseIdsListPointer) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a bookmarks query. + QSqlQuery bookmarksQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + bookmarksQuery.setForwardOnly(true); + + // Create an IDs not to get string. + QString idsNotToGetString; + + for (const int databaseId : *exceptDatabaseIdsListPointer) + { + // Check to see if there the string already has at least one number. + if (!idsNotToGetString.isEmpty()) + { + // This is not the first number, so add a `,`. + idsNotToGetString.append(QLatin1Char(',')); + } + + // Append the database ID. + idsNotToGetString.append(QString::number(databaseId)); + } + + // Prepare the bookmarks query. + bookmarksQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " NOT IN (" + idsNotToGetString + ") ORDER BY " + DISPLAY_ORDER + " ASC"); + + // Execute the query. + bookmarksQuery.exec(); + + // Create a bookmark list. + QList *bookmarkListPointer = new QList; + + // Populate the bookmark list. + while (bookmarksQuery.next()) + { + // Create a bookmark struct. + struct BookmarkStruct bookmarkStruct; + + // Get the favorite icon base 64 byte array. + QByteArray favoriteIconByteArray = QByteArray::fromBase64(bookmarksQuery.value(FAVORITE_ICON).toByteArray()); + + // Create a favorite icon pixmap. + QPixmap favoriteIconPixmap; + + // Load the pixmap from byte array. + favoriteIconPixmap.loadFromData(favoriteIconByteArray); + + // Populate the bookmark struct. + bookmarkStruct.id = bookmarksQuery.value(ID).toInt(); + bookmarkStruct.bookmarkName = bookmarksQuery.value(BOOKMARK_NAME).toString(); + bookmarkStruct.bookmarkUrl = bookmarksQuery.value(BOOKMARK_URL).toString(); + bookmarkStruct.displayOrder = bookmarksQuery.value(DISPLAY_ORDER).toInt(); + bookmarkStruct.favoriteIcon = QIcon(favoriteIconPixmap); + + // Add the bookmark to the list. + bookmarkListPointer->push_back(bookmarkStruct); + } + + // Return the bookmark list. + return bookmarkListPointer; +} + +QString BookmarksDatabase::getFavoriteIconBase64String(const QIcon &favoriteIcon) +{ + // Get a favorite icon pixmap. + QPixmap favoriteIconPixmap = favoriteIcon.pixmap(32, 32); + + // Create a favorite icon byte array. + QByteArray favoriteIconByteArray; + + // Create a favorite icon buffer. + QBuffer favoriteIconBuffer(&favoriteIconByteArray); + + // Open the buffer. + favoriteIconBuffer.open(QIODevice::WriteOnly); + + // Convert the favorite icon pixmap into a byte array in PNG format. + favoriteIconPixmap.save(&favoriteIconBuffer, "PNG"); + + // Close the buffer. + favoriteIconBuffer.close(); + + // Convert the favorite icon byte array to a base 64 string. + QString favoriteIconBase64String = favoriteIconByteArray.toBase64(); + + // Return the favorite icon base 64 string. + return favoriteIconBase64String; +} + +void BookmarksDatabase::updateBookmark(const BookmarkStruct *bookmarkStructPointer) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate an update bookmark name. + QSqlQuery updateBookmarkQuery(bookmarksDatabase); + + // Prepare the update bookmark query. + updateBookmarkQuery.prepare("UPDATE " + BOOKMARKS_TABLE + " SET " + + BOOKMARK_NAME + " = :bookmark_name, " + + BOOKMARK_URL + " = :bookmark_url, " + + DISPLAY_ORDER + " = :display_order, " + + FAVORITE_ICON + "= :favorite_icon " + + "WHERE " + ID + " = :id"); + + // Bind the query values. + updateBookmarkQuery.bindValue(":bookmark_name", bookmarkStructPointer->bookmarkName); + updateBookmarkQuery.bindValue(":bookmark_url", bookmarkStructPointer->bookmarkUrl); + updateBookmarkQuery.bindValue(":display_order", bookmarkStructPointer->displayOrder); + updateBookmarkQuery.bindValue(":favorite_icon", getFavoriteIconBase64String(bookmarkStructPointer->favoriteIcon)); + updateBookmarkQuery.bindValue(":id", bookmarkStructPointer->id); + + // Execute the query. + updateBookmarkQuery.exec(); +} + +void BookmarksDatabase::updateDisplayOrder(const int bookmarkId, const int displayOrder) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate an update bookmark display order query. + QSqlQuery updateBookmarkDisplayOrderQuery(bookmarksDatabase); + + // Prepare the update bookmark display order query. + updateBookmarkDisplayOrderQuery.prepare("UPDATE " + BOOKMARKS_TABLE + + " SET " + DISPLAY_ORDER + " = :display_order " + + "WHERE " + ID + " = :id"); + + // Bind the query values. + updateBookmarkDisplayOrderQuery.bindValue(":display_order", displayOrder); + updateBookmarkDisplayOrderQuery.bindValue(":id", bookmarkId); + + // Execute the query. + updateBookmarkDisplayOrderQuery.exec(); +} + void BookmarksDatabase::updateBookmarkName(const int bookmarkId, const QString &bookmarkName) { // Get a handle for the bookmarks database. @@ -212,7 +466,7 @@ void BookmarksDatabase::updateBookmarkName(const int bookmarkId, const QString & " SET " + BOOKMARK_NAME + " = :bookmark_name " + "WHERE " + ID + " = :id"); - // Bind the values. + // Bind the query values. updateBookmarkNameQuery.bindValue(":bookmark_name", bookmarkName); updateBookmarkNameQuery.bindValue(":id", bookmarkId); @@ -233,7 +487,7 @@ void BookmarksDatabase::updateBookmarkUrl(const int bookmarkId, const QString &b " SET " + BOOKMARK_URL + " = :bookmark_url " + "WHERE " + ID + " = :id"); - // Bind the values. + // Bind the query values. updateBookmarkUrlQuery.bindValue(":bookmark_url", bookmarkUrl); updateBookmarkUrlQuery.bindValue(":id", bookmarkId); diff --git a/src/databases/BookmarksDatabase.h b/src/databases/BookmarksDatabase.h index d0f743a..35f94bd 100644 --- a/src/databases/BookmarksDatabase.h +++ b/src/databases/BookmarksDatabase.h @@ -33,9 +33,14 @@ public: BookmarksDatabase(); // The public functions. - static void addBookmark(const QString &bookmarkName, const QString &bookmarkUrl, const QIcon &favoriteIcon); + static void addBookmark(const BookmarkStruct *bookmarkStructPointer); static void addDatabase(); + static void deleteBookmark(const int bookmarkId); + static BookmarkStruct* getBookmark(int bookmarkId); static std::list* getBookmarks(); + static QList* getBookmarksExcept(QList *exceptDatabaseIdsListPointer); + static void updateBookmark(const BookmarkStruct *bookmarkStructPointer); + static void updateDisplayOrder(const int bookmarkId, const int displayOrder); static void updateBookmarkName(const int bookmarkId, const QString &bookmarkName); static void updateBookmarkUrl(const int bookmarkId, const QString &bookmarkUrl); @@ -51,8 +56,11 @@ public: static const QString IS_FOLDER; static const QString PARENT_FOLDER_ID; - private: +private: // The private static constants. static const int SCHEMA_VERSION; + + // The private functions. + static QString getFavoriteIconBase64String(const QIcon &favoriteIcon); }; #endif diff --git a/src/dialogs/AddBookmarkDialog.cpp b/src/dialogs/AddBookmarkDialog.cpp index cc0d132..222a69a 100644 --- a/src/dialogs/AddBookmarkDialog.cpp +++ b/src/dialogs/AddBookmarkDialog.cpp @@ -26,10 +26,11 @@ #include // Qt toolkit headers. +#include #include // Construct the class. -AddBookmarkDialog::AddBookmarkDialog(const QString &bookmarkName, const QString &bookmarkUrl, const QIcon &favoriteIcon) : QDialog(nullptr), icon(favoriteIcon) +AddBookmarkDialog::AddBookmarkDialog(const QString &bookmarkName, const QString &bookmarkUrl, const QIcon &favoriteIcon) : QDialog(nullptr) { // Set the window title. setWindowTitle(i18nc("The add bookmark dialog window title.", "Add Bookmark")); @@ -44,28 +45,21 @@ AddBookmarkDialog::AddBookmarkDialog(const QString &bookmarkName, const QString addBookmarkDialogUi.setupUi(this); // Get handles for the widgets. - QGraphicsView *favoriteIconGraphicsViewPointer = addBookmarkDialogUi.favoriteIconGraphicsView; + defaultFavoriteIconRadioButtonPointer = addBookmarkDialogUi.defaultFavoriteIconRadioButton; + customFavoriteIconRadioButtonPointer = addBookmarkDialogUi.customFavoriteIconRadioButton; bookmarkNamePointer = addBookmarkDialogUi.bookmarkNameLineEdit; bookmarkUrlPointer = addBookmarkDialogUi.bookmarkUrlLineEdit; + QPushButton *browseButtonPointer = addBookmarkDialogUi.browseButton; QDialogButtonBox *dialogButtonBoxPointer = addBookmarkDialogUi.dialogButtonBox; - // Create a graphics scene. - QGraphicsScene *favoriteIconGraphicsScenePointer = new QGraphicsScene(this); - - // Set the graphics scene. - favoriteIconGraphicsViewPointer->setScene(favoriteIconGraphicsScenePointer); - - // Set the background of the graphics view to be the same as the window - favoriteIconGraphicsViewPointer->setBackgroundRole(QPalette::Window); - - // Add the MIME type icon to the scene. - favoriteIconGraphicsScenePointer->addPixmap(favoriteIcon.pixmap(32, 32)); + // Set the default favorite icon. + defaultFavoriteIconRadioButtonPointer->setIcon(favoriteIcon); // Populate the line edits. bookmarkNamePointer->setText(bookmarkName); bookmarkUrlPointer->setText(bookmarkUrl); - // Scroll the the beginning of the line edits. + // Scroll to the beginning of the line edits. bookmarkNamePointer->setCursorPosition(0); bookmarkUrlPointer->setCursorPosition(0); @@ -76,16 +70,47 @@ AddBookmarkDialog::AddBookmarkDialog(const QString &bookmarkName, const QString addBookmarkButtonPointer->setIcon(QIcon::fromTheme("list-add")); // Connect the buttons. + connect(browseButtonPointer, SIGNAL(clicked()), this, SLOT(browse())); connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(addBookmark())); connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); } void AddBookmarkDialog::addBookmark() { + // Get the favorite icon. + QIcon favoriteIcon = defaultFavoriteIconRadioButtonPointer->isChecked() ? defaultFavoriteIconRadioButtonPointer->icon() : customFavoriteIconRadioButtonPointer->icon(); + + // Create a bookmark struct. + BookmarkStruct *bookmarkStructPointer = new BookmarkStruct; + + // Populate the bookmark struct. + bookmarkStructPointer->bookmarkName = bookmarkNamePointer->text(); + bookmarkStructPointer->bookmarkUrl = bookmarkUrlPointer->text(); + bookmarkStructPointer->favoriteIcon = favoriteIcon; + // Add the bookmark. - BookmarksDatabase::addBookmark(bookmarkNamePointer->text(), bookmarkUrlPointer->text(), icon); + BookmarksDatabase::addBookmark(bookmarkStructPointer); + + // Update the list of bookmarks in the menu and toolbar. + emit bookmarkAdded(); // Close the dialog. close(); } +void AddBookmarkDialog::browse() +{ + // Get an image file string from the user. + QString imageFileString = QFileDialog::getOpenFileName(this, tr("Favorite Icon Image"), QDir::homePath(), + tr("Image Files — *.bmp, *.gif, *.jpg, *.jpeg, *.png, *.svg (*.bmp *.gif *.jpg *.jpeg *.png *.svg);;All Files (*)")); + + // Check to see if an image file string was returned. This will be empty if the user selected cancel. + if (!imageFileString.isEmpty()) + { + // Set the custom favorite icon. + customFavoriteIconRadioButtonPointer->setIcon(QIcon(imageFileString)); + + // Check the custom favorite icon radio button. + customFavoriteIconRadioButtonPointer->setChecked(true); + } +} diff --git a/src/dialogs/AddBookmarkDialog.h b/src/dialogs/AddBookmarkDialog.h index 2cee8b9..c054876 100644 --- a/src/dialogs/AddBookmarkDialog.h +++ b/src/dialogs/AddBookmarkDialog.h @@ -22,8 +22,8 @@ // Qt toolkit headers. #include -#include #include +#include class AddBookmarkDialog : public QDialog { @@ -34,16 +34,20 @@ public: // The primary constructor. explicit AddBookmarkDialog(const QString &bookmarkName, const QString &bookmarkUrl, const QIcon &favoriteIcon); +signals: + // The signals. + void bookmarkAdded() const; + private Q_SLOTS: // The private slots. void addBookmark(); + void browse(); private: // The private widgets. QLineEdit *bookmarkNamePointer; QLineEdit *bookmarkUrlPointer; - - // The private variables. - const QIcon icon; + QRadioButton *customFavoriteIconRadioButtonPointer; + QRadioButton *defaultFavoriteIconRadioButtonPointer; }; #endif diff --git a/src/dialogs/BookmarksDialog.cpp b/src/dialogs/BookmarksDialog.cpp index 509fade..4dc66ee 100644 --- a/src/dialogs/BookmarksDialog.cpp +++ b/src/dialogs/BookmarksDialog.cpp @@ -21,6 +21,8 @@ #include "BookmarksDialog.h" #include "ui_BookmarksDialog.h" #include "databases/BookmarksDatabase.h" +#include "dialogs/AddBookmarkDialog.h" +#include "dialogs/EditBookmarkDialog.h" // KDE Frameworks headers. #include @@ -28,10 +30,9 @@ // Qt toolkit headers. #include #include -#include // Construct the class. -BookmarksDialog::BookmarksDialog() : QDialog(nullptr) +BookmarksDialog::BookmarksDialog(QIcon currentWebsiteFavorieIcon) : QDialog(nullptr), websiteFavoriteIcon(currentWebsiteFavorieIcon) { // Set the dialog window title. setWindowTitle(i18nc("The bookmarks dialog window title", "Bookmarks")); @@ -45,62 +46,169 @@ BookmarksDialog::BookmarksDialog() : QDialog(nullptr) // Setup the UI. bookmarksDialogUi.setupUi(this); - // Get the list of bookmarks. - std::list *bookmarksListPointer = BookmarksDatabase::getBookmarks(); - - // Get a handle for the tree view. - QTreeView *treeViewPointer = bookmarksDialogUi.treeView; + // Get a handle for the draggable tree view. + draggableTreeViewPointer = bookmarksDialogUi.draggableTreeView; // Initialize the tree model. - QStandardItemModel *treeModelPointer = new QStandardItemModel(); + treeModelPointer = new QStandardItemModel(); + + // Auto resize the headers. + draggableTreeViewPointer->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + + // Indicate that all the rows are the same height, which improves performance. + draggableTreeViewPointer->setUniformRowHeights(true); + + // Set the selection mode to allow multiple rows to be selected at once. + draggableTreeViewPointer->setSelectionMode(QAbstractItemView::ExtendedSelection); + + // Allow dragging of bookmarks to reorder. + draggableTreeViewPointer->setDragDropMode(QAbstractItemView::InternalMove); + + // Set the tree model. + draggableTreeViewPointer->setModel(treeModelPointer); + + // Get a handle for the tree selection model. + treeSelectionModelPointer = draggableTreeViewPointer->selectionModel(); + + // Listen for selection changes. + connect(treeSelectionModelPointer, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateUi())); + + // Repopulate the bookmarks when they are moved. + connect(draggableTreeViewPointer, SIGNAL(bookmarksMoved()), this, SLOT(refreshBookmarks())); + + // Get handles for the buttons. + QPushButton *addBookmarkButtonPointer = bookmarksDialogUi.addBookmarkButton; + editButtonPointer = bookmarksDialogUi.editButton; + deleteItemsButtonPointer = bookmarksDialogUi.deleteItemsButton; + QDialogButtonBox *dialogButtonBoxPointer = bookmarksDialogUi.dialogButtonBox; + QPushButton *closeButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Close); + + // Connect the buttons. + connect(addBookmarkButtonPointer, SIGNAL(clicked()), this, SLOT(showAddBookmarkDialog())); + connect(editButtonPointer, SIGNAL(clicked()), this, SLOT(showEditDialog())); + connect(deleteItemsButtonPointer, SIGNAL(clicked()), this, SLOT(deleteItems())); + connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); + + // Set the close button to be the default. + closeButtonPointer->setDefault(true); + + // Monitor editing of data in the tree model. + connect(treeModelPointer, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(updateBookmarkFromTree(QStandardItem*))); + + // Populate the bookmarks. + populateBookmarks(); +} + +void BookmarksDialog::deleteItems() const +{ + // Get the list of selected model indexes. + QList selectedModelIndexList = treeSelectionModelPointer->selectedRows(DATABASE_ID_COLUMN); + + // Delete each of the bookmarks. + for (const QModelIndex &modelIndex : selectedModelIndexList) + { + // Delete the bookmark. + BookmarksDatabase::deleteBookmark(modelIndex.data().toInt()); + } + + // Repopulate the bookmarks in this dialog + populateBookmarks(); + + // Emit the bookmark updated signal to redraw the bookmarks in the menu and toolbar. + emit bookmarkUpdated(); +} + +void BookmarksDialog::populateBookmarks() const +{ + // Clear the current contents of the tree model. + treeModelPointer->clear(); // Set the column count. - treeModelPointer->setColumnCount(3); + treeModelPointer->setColumnCount(4); - // Set the tree header data. The first column is the database ID, which is not displayed. - treeModelPointer->setHeaderData(1, Qt::Horizontal, i18nc("The bookmark Name header.", "Name")); - treeModelPointer->setHeaderData(2, Qt::Horizontal, i18nc("The bookmark URL header.", "URL")); + // Set the tree header data. The last column is the database ID, which is not displayed. + treeModelPointer->setHeaderData(BOOKMARK_NAME_COLUMN, Qt::Horizontal, i18nc("The bookmark Name header.", "Name")); + treeModelPointer->setHeaderData(BOOKMARK_URL_COLUMN, Qt::Horizontal, i18nc("The bookmark URL header.", "URL")); + + // Hide the backend columns. + draggableTreeViewPointer->setColumnHidden(DATABASE_ID_COLUMN, true); + draggableTreeViewPointer->setColumnHidden(DISPLAY_ORDER, true); + + // Get the list of bookmarks. + std::list *bookmarksListPointer = BookmarksDatabase::getBookmarks(); // Populate the bookmarks tree view. - for (BookmarkStruct bookmarkStruct : *bookmarksListPointer) + for (const BookmarkStruct &bookmarkStruct : *bookmarksListPointer) { // Create a list for the bookmark items. QList bookmarkItemList; // Create the bookmark items. - QStandardItem *idItemPointer = new QStandardItem(QString::number(bookmarkStruct.id)); QStandardItem *nameItemPointer = new QStandardItem(bookmarkStruct.favoriteIcon, bookmarkStruct.bookmarkName); QStandardItem *urlItemPointer = new QStandardItem(bookmarkStruct.bookmarkUrl); + QStandardItem *idItemPointer = new QStandardItem(QString::number(bookmarkStruct.id)); + QStandardItem *displayOrderPointer = new QStandardItem(QString::number(bookmarkStruct.displayOrder)); - // Populate the cookie standard item list. - bookmarkItemList.append(idItemPointer); + nameItemPointer->setDragEnabled(true); + nameItemPointer->setDropEnabled(true); + + // Disable dragging the URL. + urlItemPointer->setDragEnabled(false); + + // Disable dropping on the URL. For some reason this doesn't work. + urlItemPointer->setDropEnabled(false); + + // Disable selecting the URL. + urlItemPointer->setSelectable(false); + + // Populate the bookmark item list. bookmarkItemList.append(nameItemPointer); bookmarkItemList.append(urlItemPointer); + bookmarkItemList.append(idItemPointer); + bookmarkItemList.append(displayOrderPointer); - // Add the cookie to the tree. + // Add the bookmark to the tree. treeModelPointer->appendRow(bookmarkItemList); } - // Auto resize the headers. - treeViewPointer->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + // Update the UI. + updateUi(); +} - // Indicate that all the rows are the same height, which improves performance. - treeViewPointer->setUniformRowHeights(true); +void BookmarksDialog::refreshBookmarks() const +{ + // Repopulate the bookmarks in this dialog + populateBookmarks(); - // Set the tree model. - treeViewPointer->setModel(treeModelPointer); + // Emit the bookmark updated signal to redraw the bookmarks in the menu and toolbar. + emit bookmarkUpdated(); +} - // Hide the database ID column. - treeViewPointer->setColumnHidden(0, true); +void BookmarksDialog::showAddBookmarkDialog() const +{ + // Instantiate an add bookmark dialog. + AddBookmarkDialog *addBookmarkDialogPointer = new AddBookmarkDialog(QLatin1String(""), QLatin1String(""), websiteFavoriteIcon); - // Get handles for the buttons. - QDialogButtonBox *dialogButtonBoxPointer = bookmarksDialogUi.dialogButtonBox; + // Update the displayed bookmarks when a new one is added. + connect(addBookmarkDialogPointer, SIGNAL(bookmarkAdded()), this, SLOT(refreshBookmarks())); - // Connect the buttons. - connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); + // Show the dialog. + addBookmarkDialogPointer->show(); +} - // Monitor editing of data in the tree model. - connect(treeModelPointer, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(updateBookmarkFromTree(QStandardItem*))); +void BookmarksDialog::showEditDialog() +{ + // Get the current model index. + QModelIndex currentIndex = treeSelectionModelPointer->currentIndex(); + + // Instantiate an edit bookmark dialog. + QDialog *editBookmarkDialogPointer = new EditBookmarkDialog(currentIndex.siblingAtColumn(DATABASE_ID_COLUMN).data().toInt(), websiteFavoriteIcon); + + // Show the dialog. + editBookmarkDialogPointer->show(); + + // Process bookmark events. + connect(editBookmarkDialogPointer, SIGNAL(bookmarkSaved()), this, SLOT(refreshBookmarks())); } void BookmarksDialog::updateBookmarkFromTree(QStandardItem *modifiedStandardItem) @@ -109,13 +217,13 @@ void BookmarksDialog::updateBookmarkFromTree(QStandardItem *modifiedStandardItem QModelIndex modifiedItemModelIndex = modifiedStandardItem->index(); // Get the model index of the database ID. - QModelIndex databaseIdModelIndex = modifiedItemModelIndex.siblingAtColumn(0); + QModelIndex databaseIdModelIndex = modifiedItemModelIndex.siblingAtColumn(DATABASE_ID_COLUMN); // Get the database ID. int databaseId = databaseIdModelIndex.data().toInt(); // Check to see if the bookmark name or the URL was edited. - if (modifiedStandardItem->column() == 1) // The bookmark name was edited. + if (modifiedStandardItem->column() == BOOKMARK_NAME_COLUMN) // The bookmark name was edited. { // Update the bookmark name. BookmarksDatabase::updateBookmarkName(databaseId, modifiedStandardItem->text()); @@ -129,3 +237,26 @@ void BookmarksDialog::updateBookmarkFromTree(QStandardItem *modifiedStandardItem // Emit the bookmark updated signal. emit bookmarkUpdated(); } + +void BookmarksDialog::updateUi() const +{ + // Set the status of the buttons. + if (treeSelectionModelPointer->hasSelection()) // A bookmark or folder is selected. + { + // Enabled the buttons. + editButtonPointer->setEnabled(true); + deleteItemsButtonPointer->setEnabled(true); + + // Update the delete items button text. + deleteItemsButtonPointer->setText(i18ncp("Delete items button populated text.", "Delete %1 item", "Delete %1 items", treeSelectionModelPointer->selectedRows().count())); + } + else // Nothing is selected. + { + // Disable the buttons. + editButtonPointer->setEnabled(false); + deleteItemsButtonPointer->setEnabled(false); + + // Update the delete items button text. + deleteItemsButtonPointer->setText(i18nc("Delete items button default text", "Delete items")); + } +} diff --git a/src/dialogs/BookmarksDialog.h b/src/dialogs/BookmarksDialog.h index 3e2713e..e81d0b4 100644 --- a/src/dialogs/BookmarksDialog.h +++ b/src/dialogs/BookmarksDialog.h @@ -22,9 +22,11 @@ // Application headers. #include "structs/BookmarkStruct.h" +#include "widgets/DraggableTreeView.h" // Qt toolkit headers. #include +#include #include class BookmarksDialog : public QDialog @@ -34,7 +36,13 @@ class BookmarksDialog : public QDialog public: // The primary constructor. - explicit BookmarksDialog(); + explicit BookmarksDialog(QIcon currentWebsiteFavoriteIcon); + + // The public constants. + static const int BOOKMARK_NAME_COLUMN = 0; + static const int BOOKMARK_URL_COLUMN = 1; + static const int DATABASE_ID_COLUMN = 2; + static const int DISPLAY_ORDER = 3; signals: // The signals. @@ -42,6 +50,23 @@ signals: private Q_SLOTS: // The private slots. + void deleteItems() const; + void refreshBookmarks() const; + void showAddBookmarkDialog() const; + void showEditDialog(); void updateBookmarkFromTree(QStandardItem *modifiedStandardItem); + void updateUi() const; + +private: + // The private functions. + void populateBookmarks() const; + + // The private variables. + QPushButton *deleteItemsButtonPointer; + QPushButton *editButtonPointer; + QStandardItemModel *treeModelPointer; + QItemSelectionModel *treeSelectionModelPointer; + DraggableTreeView *draggableTreeViewPointer; + QIcon websiteFavoriteIcon; }; #endif diff --git a/src/dialogs/CMakeLists.txt b/src/dialogs/CMakeLists.txt index b5d7cfd..a1cc4d0 100644 --- a/src/dialogs/CMakeLists.txt +++ b/src/dialogs/CMakeLists.txt @@ -24,5 +24,6 @@ target_sources(privacybrowser PRIVATE CookiesDialog.cpp DomainSettingsDialog.cpp DurableCookiesDialog.cpp + EditBookmarkDialog.cpp SaveDialog.cpp ) diff --git a/src/dialogs/EditBookmarkDialog.cpp b/src/dialogs/EditBookmarkDialog.cpp new file mode 100644 index 0000000..35582f1 --- /dev/null +++ b/src/dialogs/EditBookmarkDialog.cpp @@ -0,0 +1,123 @@ +/* + * Copyright 2023 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +// Application headers. +#include "EditBookmarkDialog.h" +#include "ui_EditBookmarkDialog.h" +#include "databases/BookmarksDatabase.h" + +// Qt toolkit headers. +#include + +// Construct the class. +EditBookmarkDialog::EditBookmarkDialog(const int bookmarkId, QIcon ¤tWebsiteFavoriteIcon) : QDialog(nullptr), bookmarkDatabaseId(bookmarkId) +{ + // Set the window title. + setWindowTitle(i18nc("The edit bookmark dialog window title.", "Edit Bookmark")); + + // Set the window modality. + setWindowModality(Qt::WindowModality::ApplicationModal); + + // Instantiate the bookmarks dialog UI. + Ui::EditBookmarkDialog editBookmarkDialogUi; + + // Setup the UI. + editBookmarkDialogUi.setupUi(this); + + // Get handles for the widgets. + currentFavoriteIconRadioButtonPointer = editBookmarkDialogUi.currentFavoriteIconRadioButton; + currentWebsiteFavoritIconRadioButtonPointer = editBookmarkDialogUi.currentWebsiteFavoriteIconRadioButton; + customFavoriteIconRadioButtonPointer = editBookmarkDialogUi.customFavoriteIconRadioButton; + bookmarkNamePointer = editBookmarkDialogUi.bookmarkNameLineEdit; + bookmarkUrlPointer = editBookmarkDialogUi.bookmarkUrlLineEdit; + QPushButton *browseButtonPointer = editBookmarkDialogUi.browseButton; + QDialogButtonBox *dialogButtonBoxPointer = editBookmarkDialogUi.dialogButtonBox; + + // Get the bookmark struct. + bookmarkStructPointer = BookmarksDatabase::getBookmark(bookmarkId); + + // Set the favorite icons. + currentFavoriteIconRadioButtonPointer->setIcon(bookmarkStructPointer->favoriteIcon); + currentWebsiteFavoritIconRadioButtonPointer->setIcon(currentWebsiteFavoriteIcon); + + // Populate the line edits. + bookmarkNamePointer->setText(bookmarkStructPointer->bookmarkName); + bookmarkUrlPointer->setText(bookmarkStructPointer->bookmarkUrl); + + // Scroll to the beginning of the line edits. + bookmarkNamePointer->setCursorPosition(0); + bookmarkUrlPointer->setCursorPosition(0); + + // Connect the buttons. + connect(browseButtonPointer, SIGNAL(clicked()), this, SLOT(browse())); + connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(save())); + connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); +} + +void EditBookmarkDialog::browse() +{ + // Get an image file string from the user. + QString imageFileString = QFileDialog::getOpenFileName(this, tr("Favorite Icon Image"), QDir::homePath(), + tr("Image Files — *.bmp, *.gif, *.jpg, *.jpeg, *.png, *.svg (*.bmp *.gif *.jpg *.jpeg *.png *.svg);;All Files (*)")); + + // Check to see if an image file string was returned. This will be empty if the user selected cancel. + if (!imageFileString.isEmpty()) + { + // Set the custom favorite icon. + customFavoriteIconRadioButtonPointer->setIcon(QIcon(imageFileString)); + + // Check the custom favorite icon radio button. + customFavoriteIconRadioButtonPointer->setChecked(true); + } +} + +void EditBookmarkDialog::save() +{ + // Create a favorite icon. + QIcon favoriteIcon; + + // Get the favorite icon. + if (currentFavoriteIconRadioButtonPointer->isChecked()) // The current favorite icon is checked. + favoriteIcon = currentFavoriteIconRadioButtonPointer->icon(); + else if (currentWebsiteFavoritIconRadioButtonPointer->isChecked()) // The current website favorite icon is checked. + favoriteIcon = currentWebsiteFavoritIconRadioButtonPointer->icon(); + else // The custom favorite icon is checked. + favoriteIcon = customFavoriteIconRadioButtonPointer->icon(); + + qDebug() << "Favorite icon: " << favoriteIcon; + + // Create a bookmark struct. + BookmarkStruct *bookmarkStructPointer = new BookmarkStruct; + + // Populate the bookmark struct. + bookmarkStructPointer->id = bookmarkDatabaseId; + bookmarkStructPointer->bookmarkName = bookmarkNamePointer->text(); + bookmarkStructPointer->bookmarkUrl = bookmarkUrlPointer->text(); + bookmarkStructPointer->displayOrder = bookmarkStructPointer->displayOrder; + bookmarkStructPointer->favoriteIcon = favoriteIcon; + + // Update the bookmark. + BookmarksDatabase::updateBookmark(bookmarkStructPointer); + + // Emit the bookmark saved signal. + emit bookmarkSaved(); + + // Close the dialog. + close(); +} diff --git a/src/dialogs/EditBookmarkDialog.h b/src/dialogs/EditBookmarkDialog.h new file mode 100644 index 0000000..c065581 --- /dev/null +++ b/src/dialogs/EditBookmarkDialog.h @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +#ifndef EDITBOOKMARKDIALOG_H +#define EDITBOOKMARKDIALOG_H + +// Application headers. +#include "structs/BookmarkStruct.h" + +// Qt toolkit headers. +#include +#include +#include + +class EditBookmarkDialog : public QDialog +{ + // Include the Q_OBJECT macro. + Q_OBJECT + +public: + // The primary constructor. + explicit EditBookmarkDialog(const int bookmarkId, QIcon ¤tWebsiteFavoriteIcon); + +signals: + // The signals. + void bookmarkSaved() const; + +private Q_SLOTS: + // The private slots. + void browse(); + void save(); + +private: + // The private widgets. + int bookmarkDatabaseId; + QLineEdit *bookmarkNamePointer; + BookmarkStruct *bookmarkStructPointer; + QLineEdit *bookmarkUrlPointer; + QRadioButton *currentFavoriteIconRadioButtonPointer; + QRadioButton *currentWebsiteFavoritIconRadioButtonPointer; + QRadioButton *customFavoriteIconRadioButtonPointer; +}; +#endif diff --git a/src/structs/BookmarkStruct.h b/src/structs/BookmarkStruct.h index 2819174..98ac69a 100644 --- a/src/structs/BookmarkStruct.h +++ b/src/structs/BookmarkStruct.h @@ -29,6 +29,7 @@ struct BookmarkStruct int id; QString bookmarkName; QString bookmarkUrl; + int displayOrder; QIcon favoriteIcon; }; #endif diff --git a/src/uis/AddBookmarkDialog.ui b/src/uis/AddBookmarkDialog.ui index b6dc2cf..01f143c 100644 --- a/src/uis/AddBookmarkDialog.ui +++ b/src/uis/AddBookmarkDialog.ui @@ -19,134 +19,168 @@ along with Privacy Browser PC. If not, see . --> - AddBookmarkDialog - - - - - - - - - Qt::AlignLeft - - - - - - - - 0 - 0 - - - - - - 32 - 32 - - - - - QFrame::NoFrame - - - - Qt::AlignCenter - - - - - - - - - Qt::RichText - - - - &nbsp; - - - - - - - - - The name of the bookmark. - - - - Bookmark name - - - - - - - + AddBookmarkDialog + + + + + + + + + + + 10 + + + + + + + Default favorite icon + + + + true + + + + + 32 + 32 + + + + + + + + + + Custom favorite icon + + + + + + + + + 32 + 32 + + + + + + + + + + + + + + + Qt::AlignLeft + + + + + + The name of the bookmark. + + + + Bookmark name + + + + + + + + + + + + + + + Qt::AlignLeft + + + + + + The URL of the bookmark. + + + + Bookmark URL + + + + + + + + + 0 + 0 + + + + + + 700 + 0 + + + + + + + + + + + + + + + + Qt::Vertical + + + + + + + + + + + + Browse + + + + + + + + + + + + + QDialogButtonBox::Cancel + + + + + - - - - - - - Qt::AlignLeft - - - - - - - The URL of the bookmark. - - - - Bookmark URL - - - - - - - - - 0 - 0 - - - - - - 700 - 0 - - - - - - - - - - - - Qt::Vertical - - - - - - - - - QDialogButtonBox::Cancel - - - - - + diff --git a/src/uis/BookmarksDialog.ui b/src/uis/BookmarksDialog.ui index 8510878..b9355bb 100644 --- a/src/uis/BookmarksDialog.ui +++ b/src/uis/BookmarksDialog.ui @@ -21,6 +21,14 @@ BookmarksDialog + + + DraggableTreeView + QTreeView +
widgets/DraggableTreeView.h
+
+
+ @@ -34,12 +42,51 @@ - + + + + + + Add bookmark + + + + + + + + + + + + + Edit + + + + + + + + + + + + + Delete items + + + + + + + + diff --git a/src/uis/EditBookmarkDialog.ui b/src/uis/EditBookmarkDialog.ui new file mode 100644 index 0000000..9dab0e8 --- /dev/null +++ b/src/uis/EditBookmarkDialog.ui @@ -0,0 +1,202 @@ + + + + + + EditBookmarkDialog + + + + + + + + + + + 10 + + + + + + + Current favorite icon + + + + true + + + + + 32 + 32 + + + + + + + + + + Current website favorite icon + + + + + 32 + 32 + + + + + + + + + + Custom favorite icon + + + + + + + + + 32 + 32 + + + + + + + + + + + + + + + Qt::AlignLeft + + + + + + The name of the bookmark. + + + + Bookmark name + + + + + + + + + + + + + + + Qt::AlignLeft + + + + + + The URL of the bookmark. + + + + Bookmark URL + + + + + + + + + 0 + 0 + + + + + + 700 + 0 + + + + + + + + + + + + + + + + Qt::Vertical + + + + + + + + + + + + Browse + + + + + + + + + + + + + QDialogButtonBox::Save | QDialogButtonBox::Cancel + + + + + + + + diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index a391b89..d237afb 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022-2023 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -18,6 +18,7 @@ # List the sources to include in the executable. target_sources(privacybrowser PRIVATE - TabWidget.cpp + DraggableTreeView.cpp PrivacyWebEngineView.cpp + TabWidget.cpp ) diff --git a/src/widgets/DraggableTreeView.cpp b/src/widgets/DraggableTreeView.cpp new file mode 100644 index 0000000..1fa07a5 --- /dev/null +++ b/src/widgets/DraggableTreeView.cpp @@ -0,0 +1,171 @@ +/* + * Copyright 2023 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +// Application headers. +#include "DraggableTreeView.h" +#include "databases/BookmarksDatabase.h" +#include "dialogs/BookmarksDialog.h" + +// Qt toolkit headers. +#include +#include +#include + +// Construct the class. +DraggableTreeView::DraggableTreeView(QWidget *parentWidget) : QTreeView(parentWidget) {} + +// Handle drop events. +void DraggableTreeView::dropEvent(QDropEvent *dropEvent) +{ + // Get the list of currently selected items that are moving. + QList indexesMovingList = selectedIndexes(); + + // Create a list of database IDs that are moving. + QList *databaseIdsMovingListPointer = new QList; + + // Populate the list of moving database IDs. + for (const QModelIndex &modelIndex : indexesMovingList) + { + // Only process model indexes from the bookmark name column (by default, there will be model indexes from all the visible columns in the list). + if (modelIndex.column() == BookmarksDialog::BOOKMARK_NAME_COLUMN) + databaseIdsMovingListPointer->append(modelIndex.siblingAtColumn(BookmarksDialog::DATABASE_ID_COLUMN).data().toInt()); + } + + // Get the drop position. + int dropPosition = dropIndicatorPosition(); + + // Get the drop model index. + QModelIndex dropModelIndex = indexAt(dropEvent->pos()); + + // Get the drop display order. + int dropDisplayOrder = dropModelIndex.siblingAtColumn(BookmarksDialog::DISPLAY_ORDER).data().toInt(); + + // Process the move according to the drop position. + switch (dropPosition) + { + case QAbstractItemView::OnItem: + // TODO Implement for moving into a folder. + break; + + case QAbstractItemView::AboveItem: + { + // Get a list of all the bookmarks except those selected. + QList *bookmarksExceptSelectedListPointer = BookmarksDatabase::getBookmarksExcept(databaseIdsMovingListPointer); + + // Initialize a new display order tracker. + int newDisplayOrder = 0; + + // Move the bookmarks. + for (const BookmarkStruct &bookmarkStruct : *bookmarksExceptSelectedListPointer) + { + // Check to see if this is the drop display order. + if (bookmarkStruct.displayOrder == dropDisplayOrder) + { + // Add all of the bookmarks being moved to this point. + for (const int databaseId : *databaseIdsMovingListPointer) + { + // Update the display order for each bookmark. + BookmarksDatabase::updateDisplayOrder(databaseId, newDisplayOrder); + + // Increment the new display order. + ++newDisplayOrder; + } + } + + // Set the bookmark's display order if it has changed. + if (bookmarkStruct.displayOrder != newDisplayOrder) + BookmarksDatabase::updateDisplayOrder(bookmarkStruct.id, newDisplayOrder); + + // Increment the new display order. + ++newDisplayOrder; + } + + break; + } + + case QAbstractItemView::BelowItem: + { + // Get a list of all the bookmarks except those selected. + QList *bookmarksExceptSelectedListPointer = BookmarksDatabase::getBookmarksExcept(databaseIdsMovingListPointer); + + // Initialize a new display order tracker. + int newDisplayOrder = 0; + + // Move the bookmarks. + for (const BookmarkStruct &bookmarkStruct : *bookmarksExceptSelectedListPointer) + { + // Set the bookmark's display order if it has changed. + if (bookmarkStruct.displayOrder != newDisplayOrder) + BookmarksDatabase::updateDisplayOrder(bookmarkStruct.id, newDisplayOrder); + + // Increment the new display order. + ++newDisplayOrder; + + // Check to see if this is the drop display order. + if (bookmarkStruct.displayOrder == dropDisplayOrder) + { + // Add all of the bookmarks being moved to this point. + for (const int databaseId : *databaseIdsMovingListPointer) + { + // Update the display order for each bookmark. + BookmarksDatabase::updateDisplayOrder(databaseId, newDisplayOrder); + + // Increment the new display order. + ++newDisplayOrder; + } + } + } + + break; + } + + case QAbstractItemView::OnViewport: + + // Get a list of all the bookmarks except those selected. + QList *bookmarksExceptSelectedListPointer = BookmarksDatabase::getBookmarksExcept(databaseIdsMovingListPointer); + + // Initialize a new display order tracker. + int newDisplayOrder = 0; + + // Move the bookmarks. + for (const BookmarkStruct &bookmarkStruct : *bookmarksExceptSelectedListPointer) + { + // Set the bookmark's display order if it has changed. + if (bookmarkStruct.displayOrder != newDisplayOrder) + BookmarksDatabase::updateDisplayOrder(bookmarkStruct.id, newDisplayOrder); + + // Increment the new display order. + ++newDisplayOrder; + } + + // Add all of the bookmarks being moved to the end of the list. + for (const int databaseId : *databaseIdsMovingListPointer) + { + // Update the display order for each bookmark. + BookmarksDatabase::updateDisplayOrder(databaseId, newDisplayOrder); + + // Increment the new display order. + ++newDisplayOrder; + } + break; + } + + // Emit the bookmarks moved signal. + emit bookmarksMoved(); +} diff --git a/src/widgets/DraggableTreeView.h b/src/widgets/DraggableTreeView.h new file mode 100644 index 0000000..f520ed3 --- /dev/null +++ b/src/widgets/DraggableTreeView.h @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +#ifndef DRAGGABLETREEVIEW_H +#define DRAGGABLETREEVIEW_H + +// Qt toolkit headers. +#include + +class DraggableTreeView : public QTreeView +{ + // Include the Q_OBJECT macro. + Q_OBJECT + +public: + // The default contructor. + explicit DraggableTreeView(QWidget *parentWidget = nullptr); + +signals: + // The signals. + void bookmarksMoved() const; + +protected: + void dropEvent(QDropEvent *event) override; +}; +#endif diff --git a/src/windows/BrowserWindow.cpp b/src/windows/BrowserWindow.cpp index c8c766d..dda65d9 100644 --- a/src/windows/BrowserWindow.cpp +++ b/src/windows/BrowserWindow.cpp @@ -73,7 +73,7 @@ BrowserWindow::BrowserWindow(bool firstWindow) : KXmlGuiWindow() QAction *backActionPointer = KStandardAction::back(this, SLOT(back()), actionCollectionPointer); QAction *forwardActionPointer = KStandardAction::forward(this, SLOT(forward()), actionCollectionPointer); KStandardAction::home(this, SLOT(home()), actionCollectionPointer); - KStandardAction::addBookmark(this, SLOT(addBookmark()), actionCollectionPointer); + KStandardAction::addBookmark(this, SLOT(showAddBookmarkDialog()), actionCollectionPointer); QAction *editBookmarksActionPointer = KStandardAction::editBookmarks(this, SLOT(editBookmarks()), actionCollectionPointer); KStandardAction::preferences(this, SLOT(showSettingsDialog()), actionCollectionPointer); KStandardAction::find(this, SLOT(showFindTextActions()), actionCollectionPointer); @@ -509,6 +509,12 @@ BrowserWindow::BrowserWindow(bool firstWindow) : KXmlGuiWindow() bookmarksMenuCurrentActionList = QList(); bookmarksToolBarCurrentActionList = QList(); + // Set the bookmarks toolbar context menu policy. + bookmarksToolBarPointer->setContextMenuPolicy(Qt::CustomContextMenu); + + // Show the custom bookmark context menu when requested. + connect(bookmarksToolBarPointer, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showBookmarkContextMenu(const QPoint&))); + // Populate the bookmarks. populateBookmarks(); @@ -525,15 +531,6 @@ BrowserWindow::BrowserWindow(bool firstWindow) : KXmlGuiWindow() tabWidgetPointer->loadInitialWebsite(); } -void BrowserWindow::addBookmark() const -{ - // Instantiate an add bookmark dialog. - AddBookmarkDialog *addBookmarkDialogPointer = new AddBookmarkDialog(tabWidgetPointer->getCurrentTabTitle(), tabWidgetPointer->getCurrentTabUrl(), tabWidgetPointer->getCurrentTabFavoritIcon()); - - // Show the dialog. - addBookmarkDialogPointer->show(); -} - void BrowserWindow::addOrEditDomainSettings() const { // Remove the focus from the URL line edit. @@ -598,7 +595,7 @@ void BrowserWindow::decrementZoom() void BrowserWindow::editBookmarks() const { // Instantiate an edit bookmarks dialog. - BookmarksDialog *bookmarksDialogPointer = new BookmarksDialog(); + BookmarksDialog *bookmarksDialogPointer = new BookmarksDialog(tabWidgetPointer->getCurrentTabFavoritIcon()); // Update the displayed bookmarks when edited. connect(bookmarksDialogPointer, SIGNAL(bookmarkUpdated()), this, SLOT(populateBookmarks())); @@ -868,6 +865,23 @@ void BrowserWindow::reloadAndBypassCache() const tabWidgetPointer->refresh(); } +void BrowserWindow::showAddBookmarkDialog() const +{ + // Instantiate an add bookmark dialog. + AddBookmarkDialog *addBookmarkDialogPointer = new AddBookmarkDialog(tabWidgetPointer->getCurrentTabTitle(), tabWidgetPointer->getCurrentTabUrl(), tabWidgetPointer->getCurrentTabFavoritIcon()); + + // Update the displayed bookmarks when a new one is added. + connect(addBookmarkDialogPointer, SIGNAL(bookmarkAdded()), this, SLOT(populateBookmarks())); + + // Show the dialog. + addBookmarkDialogPointer->show(); +} + +void BrowserWindow::showBookmarkContextMenu(const QPoint&) const +{ + qDebug() << "Show the bookmark context menu."; +} + void BrowserWindow::showCookiesDialog() { // Remove the focus from the URL line edit. diff --git a/src/windows/BrowserWindow.h b/src/windows/BrowserWindow.h index 49cb198..92cdc8e 100644 --- a/src/windows/BrowserWindow.h +++ b/src/windows/BrowserWindow.h @@ -51,7 +51,6 @@ public: private Q_SLOTS: // The private slots. - void addBookmark() const; void addOrEditDomainSettings() const; void back() const; void clearUrlLineEditFocus() const; @@ -71,6 +70,8 @@ private Q_SLOTS: void populateBookmarks(); void refresh() const; void reloadAndBypassCache() const; + void showAddBookmarkDialog() const; + void showBookmarkContextMenu(const QPoint&) const; void showCookiesDialog(); void showDownloadLocationBrowseDialog() const; void showDomainSettingsDialog() const; -- 2.43.0