#include "BookmarksDialog.h"
#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>
// Qt toolkit headers.
#include <QDebug>
#include <QStandardItemModel>
-#include <QTreeView>
// Construct the class.
-BookmarksDialog::BookmarksDialog() : QDialog(nullptr)
+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"));
// Setup the UI.
bookmarksDialogUi.setupUi(this);
- // Get the list of bookmarks.
- std::list<BookmarkStruct> *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(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;
+ QPushButton *closeButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Close);
+
+ // 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()));
+
+ // 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
+{
+ // 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();
+
+ // 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(6);
+
+ // 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_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(QLatin1String("bookmarks"), QIcon::fromTheme(QLatin1String("bookmark-new"))),
+ 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);
+
+ // 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();
- // 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"));
+ // 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 (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 *idItemPointer = new QStandardItem(QString::number(bookmarkStruct.id));
- QStandardItem *nameItemPointer = new QStandardItem(bookmarkStruct.favoriteIcon, bookmarkStruct.bookmarkName);
- QStandardItem *urlItemPointer = new QStandardItem(bookmarkStruct.bookmarkUrl);
+ 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);
+
+ // Only allow dropping on the name if this is a folder.
+ nameItemPointer->setDropEnabled(bookmarkStruct.isFolder);
- // Populate the cookie standard item list.
- bookmarkItemList.append(idItemPointer);
+ // Disable dragging and dropping on the URL.
+ urlItemPointer->setDragEnabled(false);
+ urlItemPointer->setDropEnabled(false);
+
+ // Disable selecting the URL.
+ urlItemPointer->setSelectable(false);
+
+ // Populate the bookmark item list.
bookmarkItemList.append(nameItemPointer);
bookmarkItemList.append(urlItemPointer);
+ bookmarkItemList.append(databaseIdItemPointer);
+ bookmarkItemList.append(displayOrderItemPointer);
+ bookmarkItemList.append(isFolderItemPointer);
+ bookmarkItemList.append(folderIdItemPointer);
- // Add the cookie to the tree.
- treeModelPointer->appendRow(bookmarkItemList);
+ // Add the bookmark to the parent folder.
+ folderItemNamePointer->appendRow(bookmarkItemList);
+
+ // Populate subfolders if this item is a folder.
+ if (bookmarkStruct.isFolder)
+ populateSubfolders(nameItemPointer, bookmarkStruct.folderId);
}
+}
- // Auto resize the headers.
- treeViewPointer->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
+void BookmarksDialog::refreshBookmarks() const
+{
+ // Repopulate the bookmarks in this dialog
+ populateBookmarks();
- // Indicate that all the rows are the same height, which improves performance.
- treeViewPointer->setUniformRowHeights(true);
+ // Emit the bookmark updated signal to redraw the bookmarks in the menu and toolbar.
+ emit bookmarkUpdated();
+}
- // Set the tree model.
- treeViewPointer->setModel(treeModelPointer);
+void BookmarksDialog::selectSubfolderContents(const QModelIndex &parentModelIndex) const
+{
+ // Get the index model.
+ const QAbstractItemModel *modelIndexAbstractItemPointer = parentModelIndex.model();
- // Hide the database ID column.
- treeViewPointer->setColumnHidden(0, true);
+ // Get the number of items in the folder.
+ int numberOfChildrenInFolder = modelIndexAbstractItemPointer->rowCount(parentModelIndex);
- // Get handles for the buttons.
- QDialogButtonBox *dialogButtonBoxPointer = bookmarksDialogUi.dialogButtonBox;
+ // 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);
- // Connect the buttons.
- connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject()));
+ // Select the subfolder contents if it is a folder.
+ if (childModelIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toBool())
+ selectSubfolderContents(childModelIndex);
+ }
- // Monitor editing of data in the tree model.
- connect(treeModelPointer, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(updateBookmarkFromTree(QStandardItem*)));
+ // 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(websiteTitle, websiteUrl, websiteFavoriteIcon, parentFolderId);
+
+ // Update the displayed bookmarks when a new one is added.
+ connect(addBookmarkDialogPointer, SIGNAL(bookmarkAdded()), this, SLOT(refreshBookmarks()));
+
+ // Show the dialog.
+ 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();
+
+ // 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.
+ editFolderDialogPointer->show();
+
+ // 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)
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() == NAME_COLUMN) // The bookmark name was edited.
{
// Update the bookmark name.
BookmarksDatabase::updateBookmarkName(databaseId, modifiedStandardItem->text());
// Emit the bookmark updated signal.
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.
+ {
+ // 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.
+ 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.
+ {
+ // 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"));
+ }
+}