]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blobdiff - src/dialogs/BookmarksDialog.cpp
Add bookmark folders.
[PrivacyBrowserPC.git] / src / dialogs / BookmarksDialog.cpp
index 4dc66ee873a04b892564b5e35b0f3a901c5551b9..be150cc9e4f5d36ef3ec4d5ba4a885529a325503 100644 (file)
@@ -22,7 +22,9 @@
 #include "ui_BookmarksDialog.h"
 #include "databases/BookmarksDatabase.h"
 #include "dialogs/AddBookmarkDialog.h"
+#include "dialogs/AddFolderDialog.h"
 #include "dialogs/EditBookmarkDialog.h"
+#include "dialogs/EditFolderDialog.h"
 
 // KDE Frameworks headers.
 #include <KLocalizedString>
@@ -32,7 +34,8 @@
 #include <QStandardItemModel>
 
 // Construct the class.
-BookmarksDialog::BookmarksDialog(QIcon currentWebsiteFavorieIcon) : QDialog(nullptr), websiteFavoriteIcon(currentWebsiteFavorieIcon)
+BookmarksDialog::BookmarksDialog(QString currentWebsiteTitle, QString currentWebsiteUrl, QIcon currentWebsiteFavorieIcon) :
+                                 QDialog(nullptr), websiteFavoriteIcon(currentWebsiteFavorieIcon), websiteTitle(currentWebsiteTitle), websiteUrl(currentWebsiteUrl)
 {
     // Set the dialog window title.
     setWindowTitle(i18nc("The bookmarks dialog window title", "Bookmarks"));
@@ -71,13 +74,14 @@ BookmarksDialog::BookmarksDialog(QIcon currentWebsiteFavorieIcon) : QDialog(null
     treeSelectionModelPointer = draggableTreeViewPointer->selectionModel();
 
     // Listen for selection changes.
-    connect(treeSelectionModelPointer, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateUi()));
+    connect(treeSelectionModelPointer, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateSelection()));
 
     // Repopulate the bookmarks when they are moved.
     connect(draggableTreeViewPointer, SIGNAL(bookmarksMoved()), this, SLOT(refreshBookmarks()));
 
     // Get handles for the buttons.
     QPushButton *addBookmarkButtonPointer = bookmarksDialogUi.addBookmarkButton;
+    QPushButton *addFolderButtonPointer = bookmarksDialogUi.addFolderButton;
     editButtonPointer = bookmarksDialogUi.editButton;
     deleteItemsButtonPointer = bookmarksDialogUi.deleteItemsButton;
     QDialogButtonBox *dialogButtonBoxPointer = bookmarksDialogUi.dialogButtonBox;
@@ -85,6 +89,7 @@ BookmarksDialog::BookmarksDialog(QIcon currentWebsiteFavorieIcon) : QDialog(null
 
     // Connect the buttons.
     connect(addBookmarkButtonPointer, SIGNAL(clicked()), this, SLOT(showAddBookmarkDialog()));
+    connect(addFolderButtonPointer, SIGNAL(clicked()), this, SLOT(showAddFolderDialog()));
     connect(editButtonPointer, SIGNAL(clicked()), this, SLOT(showEditDialog()));
     connect(deleteItemsButtonPointer, SIGNAL(clicked()), this, SLOT(deleteItems()));
     connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject()));
@@ -101,16 +106,32 @@ BookmarksDialog::BookmarksDialog(QIcon currentWebsiteFavorieIcon) : QDialog(null
 
 void BookmarksDialog::deleteItems() const
 {
+    // Create a parent folder ID standard C++ list (which can be sorted and from which duplicates can be removed).
+    std::list<double> parentFolderIdList;
+
     // Get the list of selected model indexes.
     QList<QModelIndex> selectedModelIndexList = treeSelectionModelPointer->selectedRows(DATABASE_ID_COLUMN);
 
     // Delete each of the bookmarks.
     for (const QModelIndex &modelIndex : selectedModelIndexList)
     {
+        // Add the parent folder ID to the list.
+        parentFolderIdList.push_back(modelIndex.parent().siblingAtColumn(FOLDER_ID_COLUMN).data().toDouble());
+
         // Delete the bookmark.
         BookmarksDatabase::deleteBookmark(modelIndex.data().toInt());
     }
 
+    // Sort the parent folder ID.
+    parentFolderIdList.sort();
+
+    // Remove duplicates from the parent folder ID list.
+    parentFolderIdList.unique();
+
+    // Update the folder contents display order for each folder with a deleted bookmark.
+    for (const double parentFolderId : parentFolderIdList)
+        BookmarksDatabase::updateFolderContentsDisplayOrder(parentFolderId);
+
     // Repopulate the bookmarks in this dialog
     populateBookmarks();
 
@@ -124,38 +145,90 @@ void BookmarksDialog::populateBookmarks() const
     treeModelPointer->clear();
 
     // Set the column count.
-    treeModelPointer->setColumnCount(4);
+    treeModelPointer->setColumnCount(6);
 
-    // 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"));
+    // Set the tree header data.
+    treeModelPointer->setHeaderData(NAME_COLUMN, Qt::Horizontal, i18nc("The bookmark Name header.", "Name"));
+    treeModelPointer->setHeaderData(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);
+    draggableTreeViewPointer->setColumnHidden(DISPLAY_ORDER_COLUMN, true);
+    draggableTreeViewPointer->setColumnHidden(IS_FOLDER_COLUMN, true);
+    draggableTreeViewPointer->setColumnHidden(FOLDER_ID_COLUMN, true);
+
+    // Create a bookmarks root item list.
+    QList<QStandardItem*> bookmarksRootItemList;
+
+    // Create the root items.
+    QStandardItem *rootItemNamePointer = new QStandardItem(QIcon::fromTheme("bookmarks"), i18nc("The bookmarks root tree widget name", "Bookmarks"));
+    QStandardItem *rootItemUrlPointer = new QStandardItem(QLatin1String(""));
+    QStandardItem *rootItemDatabaseIdPonter = new QStandardItem(QLatin1String("-1"));  // The root item doesn't have a database ID.
+    QStandardItem *rootItemDisplayOrderPointer = new QStandardItem(QLatin1String("-1"));  // The root item doesn't have a display order.
+    QStandardItem *rootItemIsFolderPointer = new QStandardItem(QLatin1String("1"));
+    QStandardItem *rootItemFolderIdPointer = new QStandardItem(QLatin1String("0"));
+
+    // Enable dropping on the root name column.
+    rootItemNamePointer->setDropEnabled(true);
+
+    // Disable dropping on the URL.
+    rootItemUrlPointer->setDropEnabled(false);
+
+    // Disable dragging of the bookmarks root item.
+    rootItemNamePointer->setDragEnabled(false);
+    rootItemUrlPointer->setDragEnabled(false);
+
+    // Disable selecting the URL.
+    rootItemUrlPointer->setSelectable(false);
 
-    // Get the list of bookmarks.
-    std::list<BookmarkStruct> *bookmarksListPointer = BookmarksDatabase::getBookmarks();
+    // Populate the bookmarks root item list.
+    bookmarksRootItemList.append(rootItemNamePointer);
+    bookmarksRootItemList.append(rootItemUrlPointer);
+    bookmarksRootItemList.append(rootItemDatabaseIdPonter);
+    bookmarksRootItemList.append(rootItemDisplayOrderPointer);
+    bookmarksRootItemList.append(rootItemIsFolderPointer);
+    bookmarksRootItemList.append(rootItemFolderIdPointer);
+
+    // Add the bookmarks root item to the tree.
+    treeModelPointer->appendRow(bookmarksRootItemList);
+
+    // Populate the subfolders, starting with the root folder ID (`0`).
+    populateSubfolders(rootItemNamePointer, 0);
+
+    // Expand all the folder.
+    draggableTreeViewPointer->expandAll();
+
+    // Update the UI.
+    updateUi();
+}
+
+void BookmarksDialog::populateSubfolders(QStandardItem *folderItemNamePointer, const double folderId) const
+{
+    // Get the folder contents.
+    QList<BookmarkStruct> *folderContentsListPointer = BookmarksDatabase::getFolderContents(folderId);
 
     // Populate the bookmarks tree view.
-    for (const BookmarkStruct &bookmarkStruct : *bookmarksListPointer)
+    for (const BookmarkStruct &bookmarkStruct : *folderContentsListPointer)
     {
-        // Create a list for the bookmark items.
+        // Create a bookmark item list.
         QList<QStandardItem*> bookmarkItemList;
 
         // Create the bookmark items.
-        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));
-
+        QStandardItem *nameItemPointer = new QStandardItem(bookmarkStruct.favoriteIcon, bookmarkStruct.name);
+        QStandardItem *urlItemPointer = new QStandardItem(bookmarkStruct.url);
+        QStandardItem *databaseIdItemPointer = new QStandardItem(QString::number(bookmarkStruct.databaseId));
+        QStandardItem *displayOrderItemPointer = new QStandardItem(QString::number(bookmarkStruct.displayOrder));
+        QStandardItem *isFolderItemPointer = new QStandardItem(QString::number(bookmarkStruct.isFolder));
+        QStandardItem *folderIdItemPointer = new QStandardItem(QString::number(bookmarkStruct.folderId, 'f', 0));  // Format the folder ID as a floating point with no trailing zeros.
+
+        // Enable dragging and dropping of the name column.
         nameItemPointer->setDragEnabled(true);
-        nameItemPointer->setDropEnabled(true);
 
-        // Disable dragging the URL.
-        urlItemPointer->setDragEnabled(false);
+        // Only allow dropping on the name if this is a folder.
+        nameItemPointer->setDropEnabled(bookmarkStruct.isFolder);
 
-        // Disable dropping on the URL.  For some reason this doesn't work.
+        // Disable dragging and dropping on the URL.
+        urlItemPointer->setDragEnabled(false);
         urlItemPointer->setDropEnabled(false);
 
         // Disable selecting the URL.
@@ -164,15 +237,18 @@ void BookmarksDialog::populateBookmarks() const
         // Populate the bookmark item list.
         bookmarkItemList.append(nameItemPointer);
         bookmarkItemList.append(urlItemPointer);
-        bookmarkItemList.append(idItemPointer);
-        bookmarkItemList.append(displayOrderPointer);
+        bookmarkItemList.append(databaseIdItemPointer);
+        bookmarkItemList.append(displayOrderItemPointer);
+        bookmarkItemList.append(isFolderItemPointer);
+        bookmarkItemList.append(folderIdItemPointer);
 
-        // Add the bookmark to the tree.
-        treeModelPointer->appendRow(bookmarkItemList);
-    }
+        // Add the bookmark to the parent folder.
+        folderItemNamePointer->appendRow(bookmarkItemList);
 
-    // Update the UI.
-    updateUi();
+        // Populate subfolders if this item is a folder.
+        if (bookmarkStruct.isFolder)
+            populateSubfolders(nameItemPointer, bookmarkStruct.folderId);
+    }
 }
 
 void BookmarksDialog::refreshBookmarks() const
@@ -184,10 +260,68 @@ void BookmarksDialog::refreshBookmarks() const
     emit bookmarkUpdated();
 }
 
+void BookmarksDialog::selectSubfolderContents(const QModelIndex &parentModelIndex) const
+{
+    // Get the index model.
+    const QAbstractItemModel *modelIndexAbstractItemPointer = parentModelIndex.model();
+
+    // Get the number of items in the folder.
+    int numberOfChildrenInFolder = modelIndexAbstractItemPointer->rowCount(parentModelIndex);
+
+    // Select any child items.
+    if (numberOfChildrenInFolder > 0)
+    {
+        // Select the contents of any subfolders.
+        for (int i = 0; i < numberOfChildrenInFolder; ++i)
+        {
+            // Get the child model index.
+            QModelIndex childModelIndex = modelIndexAbstractItemPointer->index(i, 0, parentModelIndex);
+
+            // Select the subfolder contents if it is a folder.
+            if (childModelIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toBool())
+                selectSubfolderContents(childModelIndex);
+        }
+
+        // Get the first and last child model indexes.
+        QModelIndex firstChildModelIndex = modelIndexAbstractItemPointer->index(0, 0, parentModelIndex);
+        QModelIndex lastChildModelIndex = modelIndexAbstractItemPointer->index((numberOfChildrenInFolder - 1), 0, parentModelIndex);
+
+        // Create an item selection that includes all the child items.
+        QItemSelection folderChildItemsSelection = QItemSelection(firstChildModelIndex, lastChildModelIndex);
+
+        // Get the current selection.
+        QItemSelection currentSelection = treeSelectionModelPointer->selection();
+
+        // Combine the current selection and the folder child items selection.
+        currentSelection.merge(folderChildItemsSelection, QItemSelectionModel::SelectCurrent);
+
+        // Selected the updated list of items.
+        treeSelectionModelPointer->select(currentSelection, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
+    }
+}
+
 void BookmarksDialog::showAddBookmarkDialog() const
 {
+    // Return the most recently selected index.
+    QModelIndex currentIndex = treeSelectionModelPointer->currentIndex();
+
+    // Instantiate a parent folder ID.
+    double parentFolderId;
+
+    // Get the parent folder ID.
+    if (currentIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toInt() == 1)  // The current index is a folder.
+    {
+        // Store the parent folder ID.
+        parentFolderId = currentIndex.siblingAtColumn(FOLDER_ID_COLUMN).data().toDouble();
+    }
+    else  // The current index is not a folder.
+    {
+        // Store the parent folder ID of the folder that contains the bookmark.
+        parentFolderId = currentIndex.parent().siblingAtColumn(FOLDER_ID_COLUMN).data().toDouble();
+    }
+
     // Instantiate an add bookmark dialog.
-    AddBookmarkDialog *addBookmarkDialogPointer = new AddBookmarkDialog(QLatin1String(""), QLatin1String(""), websiteFavoriteIcon);
+    AddBookmarkDialog *addBookmarkDialogPointer = new AddBookmarkDialog(websiteTitle, websiteUrl, websiteFavoriteIcon, parentFolderId);
 
     // Update the displayed bookmarks when a new one is added.
     connect(addBookmarkDialogPointer, SIGNAL(bookmarkAdded()), this, SLOT(refreshBookmarks()));
@@ -196,19 +330,64 @@ void BookmarksDialog::showAddBookmarkDialog() const
     addBookmarkDialogPointer->show();
 }
 
+void BookmarksDialog::showAddFolderDialog() const
+{
+    // Get the most recently selected index.
+    QModelIndex currentIndex = treeSelectionModelPointer->currentIndex();
+
+    // Instantiate a parent folder ID.
+    double parentFolderId;
+
+    // Get the parent folder ID.
+    if (currentIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toInt() == 1)  // The current index is a folder.
+    {
+        // Store the parent folder ID.
+        parentFolderId = currentIndex.siblingAtColumn(FOLDER_ID_COLUMN).data().toDouble();
+    }
+    else  // The current index is not a folder.
+    {
+        // Store the parent folder ID of the folder that contains the bookmark.
+        parentFolderId = currentIndex.parent().siblingAtColumn(FOLDER_ID_COLUMN).data().toDouble();
+    }
+
+    // Instantiate an add folder dialog.
+    AddFolderDialog *addFolderDialogPointer = new AddFolderDialog(websiteFavoriteIcon, parentFolderId);
+
+    // Update the displayed bookmarks when a folder is added.
+    connect(addFolderDialogPointer, SIGNAL(folderAdded()), this, SLOT(refreshBookmarks()));
+
+    // Show the dialog.
+    addFolderDialogPointer->show();
+}
+
 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);
+    // Check to see if the selected item is a folder.
+    if (currentIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toInt() == 1)  // The selected item is a folder.
+    {
+        // Instantiate an edit folder dialog.
+        QDialog *editFolderDialogPointer = new EditFolderDialog(currentIndex.siblingAtColumn(DATABASE_ID_COLUMN).data().toInt(), websiteFavoriteIcon);
 
-    // Show the dialog.
-    editBookmarkDialogPointer->show();
+        // Show the dialog.
+        editFolderDialogPointer->show();
 
-    // Process bookmark events.
-    connect(editBookmarkDialogPointer, SIGNAL(bookmarkSaved()), this, SLOT(refreshBookmarks()));
+        // Update the bookmarks UI.
+        connect(editFolderDialogPointer, SIGNAL(folderSaved()), this, SLOT(refreshBookmarks()));
+    }
+    else  // The selected item is a bookmark.
+    {
+        // Instantiate an edit bookmark dialog.
+        QDialog *editBookmarkDialogPointer = new EditBookmarkDialog(currentIndex.siblingAtColumn(DATABASE_ID_COLUMN).data().toInt(), websiteFavoriteIcon);
+
+        // Show the dialog.
+        editBookmarkDialogPointer->show();
+
+        // Update the bookmarks UI.
+        connect(editBookmarkDialogPointer, SIGNAL(bookmarkSaved()), this, SLOT(refreshBookmarks()));
+    }
 }
 
 void BookmarksDialog::updateBookmarkFromTree(QStandardItem *modifiedStandardItem)
@@ -223,7 +402,7 @@ void BookmarksDialog::updateBookmarkFromTree(QStandardItem *modifiedStandardItem
     int databaseId = databaseIdModelIndex.data().toInt();
 
     // Check to see if the bookmark name or the URL was edited.
-    if (modifiedStandardItem->column() == BOOKMARK_NAME_COLUMN)  // The bookmark name was edited.
+    if (modifiedStandardItem->column() == NAME_COLUMN)  // The bookmark name was edited.
     {
         // Update the bookmark name.
         BookmarksDatabase::updateBookmarkName(databaseId, modifiedStandardItem->text());
@@ -238,17 +417,62 @@ void BookmarksDialog::updateBookmarkFromTree(QStandardItem *modifiedStandardItem
     emit bookmarkUpdated();
 }
 
+void BookmarksDialog::updateSelection() const
+{
+    // Set the status of the buttons.
+    if (treeSelectionModelPointer->hasSelection())  // A bookmark or folder is selected.
+    {
+        // Get the list of selected model indexes.
+        QModelIndexList selectedRowsModelIndexList = treeSelectionModelPointer->selectedRows();
+
+        // Check to see if each selected item is a folder.
+        for(QModelIndex modelIndex : selectedRowsModelIndexList)
+        {
+            // If it is a folder, select all the children bookmarks.
+            if (modelIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toBool())
+                selectSubfolderContents(modelIndex);
+        }
+    }
+
+    // Update the UI.
+    updateUi();
+}
+
 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);
+        // Get the currently selected index.
+        QModelIndex currentSelectedIndex = treeSelectionModelPointer->currentIndex();
+
+        // Get the list of selected model indexes.
+        QModelIndexList selectedRowsModelIndexList = treeSelectionModelPointer->selectedRows();
+
+        // Get the number of selected rows.
+        int numberOfSelectedRows = selectedRowsModelIndexList.count();
+
+        // Enable the edit button if a folder or only one bookmark is selected.
+        editButtonPointer->setEnabled(currentSelectedIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toBool() || (numberOfSelectedRows == 1));
+
+        // Check if the root folder is selected.
+        if (treeSelectionModelPointer->isRowSelected(0))
+        {
+            // Disable the edit button.
+            editButtonPointer->setEnabled(false);
+
+            // Decrease the number of selected rows by 1.
+            --numberOfSelectedRows;
+        }
+
+        // Enabled the delete button if at least one real bookmark or folder is selected.
+        deleteItemsButtonPointer->setEnabled(numberOfSelectedRows > 0);
 
         // Update the delete items button text.
-        deleteItemsButtonPointer->setText(i18ncp("Delete items button populated text.", "Delete %1 item", "Delete %1 items", treeSelectionModelPointer->selectedRows().count()));
+        if (numberOfSelectedRows > 0)
+            deleteItemsButtonPointer->setText(i18ncp("Delete items button populated text.", "Delete %1 item", "Delete %1 items", numberOfSelectedRows));
+        else
+            deleteItemsButtonPointer->setText(i18nc("Delete items button default text", "Delete items"));
     }
     else  // Nothing is selected.
     {