]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/commitdiff
Add bookmark folders.
authorSoren Stoutner <soren@stoutner.com>
Fri, 6 Oct 2023 22:56:13 +0000 (15:56 -0700)
committerSoren Stoutner <soren@stoutner.com>
Fri, 6 Oct 2023 22:56:13 +0000 (15:56 -0700)
29 files changed:
src/CMakeLists.txt
src/databases/BookmarksDatabase.cpp
src/databases/BookmarksDatabase.h
src/dialogs/AddBookmarkDialog.cpp
src/dialogs/AddBookmarkDialog.h
src/dialogs/AddFolderDialog.cpp [new file with mode: 0644]
src/dialogs/AddFolderDialog.h [new file with mode: 0644]
src/dialogs/BookmarksDialog.cpp
src/dialogs/BookmarksDialog.h
src/dialogs/CMakeLists.txt
src/dialogs/EditBookmarkDialog.cpp
src/dialogs/EditBookmarkDialog.h
src/dialogs/EditFolderDialog.cpp [new file with mode: 0644]
src/dialogs/EditFolderDialog.h [new file with mode: 0644]
src/helpers/CMakeLists.txt
src/helpers/FolderHelper.cpp [new file with mode: 0644]
src/helpers/FolderHelper.h [new file with mode: 0644]
src/main.cpp
src/structs/BookmarkStruct.h
src/ui.rcs/browserwindowui.rc
src/uis/AddBookmarkDialog.ui
src/uis/AddFolderDialog.ui [new file with mode: 0644]
src/uis/BookmarksDialog.ui
src/uis/EditBookmarkDialog.ui
src/uis/EditFolderDialog.ui [new file with mode: 0644]
src/widgets/DraggableTreeView.cpp
src/widgets/DraggableTreeView.h
src/windows/BrowserWindow.cpp
src/windows/BrowserWindow.h

index 3ecd3c8d5a1d6ac65212213b913beec65f8a63d5..16bf76c362d862ddde3aeb60e6eb335efd5c6e0b 100644 (file)
@@ -36,6 +36,7 @@ kconfig_add_kcfg_files(privacybrowser settings/Settings.kcfgc)
 # Use KDE Frameworks to handle internationalization of the following UI files.
 ki18n_wrap_ui(privacybrowser
     uis/AddBookmarkDialog.ui
+    uis/AddFolderDialog.ui
     uis/AddOrEditCookieDialog.ui
     uis/AddTabWidget.ui
     uis/BookmarksDialog.ui
@@ -43,6 +44,7 @@ ki18n_wrap_ui(privacybrowser
     uis/DomainSettingsDialog.ui
     uis/DurableCookiesDialog.ui
     uis/EditBookmarkDialog.ui
+    uis/EditFolderDialog.ui
     uis/SaveDialog.ui
     uis/SettingsGeneral.ui
     uis/SettingsPrivacy.ui
index fdcb0fdaee31af77131b0492045827c8e8f70bea..e786eb62accbe5dc04d3bfc20cdc75de65c0a374 100644 (file)
@@ -109,30 +109,8 @@ void BookmarksDatabase::addBookmark(const BookmarkStruct *bookmarkStructPointer)
     // Get a handle for the bookmarks database.
     QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
 
-    // Instantiate a count bookmarks query.  TODO:  This needs to be updated to only count the bookmarks in the current folder.
-    QSqlQuery countBookmarksQuery(bookmarksDatabase);
-
-    // Set the query to be forward only, which is more performant.
-    countBookmarksQuery.setForwardOnly(true);
-
-    // Prepare the count bookmarks query.
-    countBookmarksQuery.prepare("SELECT " + DISPLAY_ORDER + " FROM " + BOOKMARKS_TABLE);
-
-    // Execute the count bookmarks query.
-    countBookmarksQuery.exec();
-
-    // Move to the last row.
-    countBookmarksQuery.last();
-
-    // Initialize a bookmarks count variable.
-    int bookmarksCount = 0;
-
-    // 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;
-    }
+    // Get the folder item count.
+    int folderItemCount = getFolderItemCount(bookmarkStructPointer->parentFolderId);
 
     // Instantiate an add bookmark query.
     QSqlQuery addBookmarkQuery(bookmarksDatabase);
@@ -141,22 +119,58 @@ void BookmarksDatabase::addBookmark(const BookmarkStruct *bookmarkStructPointer)
     addBookmarkQuery.prepare("INSERT INTO " + BOOKMARKS_TABLE + " (" +
                               BOOKMARK_NAME + ", " +
                               BOOKMARK_URL + ", " +
-                              FAVORITE_ICON + ", " +
-                              DISPLAY_ORDER + ") " +
-                              "VALUES (:bookmark_name, :bookmark_url, :favorite_icon, :display_order)"
+                              PARENT_FOLDER_ID + ", " +
+                              DISPLAY_ORDER + ", " +
+                              FAVORITE_ICON + ") " +
+                              "VALUES (:bookmark_name, :bookmark_url, :parent_folder_id, :display_order, :favorite_icon)"
     );
 
     // Bind the query values.
-    addBookmarkQuery.bindValue(":bookmark_name", bookmarkStructPointer->bookmarkName);
-    addBookmarkQuery.bindValue(":bookmark_url", bookmarkStructPointer->bookmarkUrl);
+    addBookmarkQuery.bindValue(":bookmark_name", bookmarkStructPointer->name);
+    addBookmarkQuery.bindValue(":bookmark_url", bookmarkStructPointer->url);
+    addBookmarkQuery.bindValue(":parent_folder_id", bookmarkStructPointer->parentFolderId);
+    addBookmarkQuery.bindValue(":display_order", folderItemCount);
     addBookmarkQuery.bindValue(":favorite_icon", getFavoriteIconBase64String(bookmarkStructPointer->favoriteIcon));
-    addBookmarkQuery.bindValue(":display_order", bookmarksCount);
 
     // Execute the add bookmark query.
     addBookmarkQuery.exec();
 }
 
-void BookmarksDatabase::deleteBookmark(const int bookmarkId)
+void BookmarksDatabase::addFolder(const BookmarkStruct *bookmarkStructPointer)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+    // Get the folder item count.
+    int folderItemCount = getFolderItemCount(bookmarkStructPointer->parentFolderId);
+
+    // Instantiate an add folder query.
+    QSqlQuery addFolderQuery(bookmarksDatabase);
+
+    // Prepare the add folder query.
+    addFolderQuery.prepare("INSERT INTO " + BOOKMARKS_TABLE + " (" +
+                              BOOKMARK_NAME + ", " +
+                              PARENT_FOLDER_ID + ", " +
+                              DISPLAY_ORDER + ", " +
+                              IS_FOLDER + ", " +
+                              FOLDER_ID + ", " +
+                              FAVORITE_ICON + ") " +
+                              "VALUES (:bookmark_name, :parent_folder_id, :display_order, :is_folder, :folder_id, :favorite_icon)"
+    );
+
+    // Bind the query values.
+    addFolderQuery.bindValue(":bookmark_name", bookmarkStructPointer->name);
+    addFolderQuery.bindValue(":parent_folder_id", bookmarkStructPointer->parentFolderId);
+    addFolderQuery.bindValue(":display_order", folderItemCount);
+    addFolderQuery.bindValue(":is_folder", 1);
+    addFolderQuery.bindValue(":folder_id", generateFolderId());
+    addFolderQuery.bindValue(":favorite_icon", getFavoriteIconBase64String(bookmarkStructPointer->favoriteIcon));
+
+    // Execute the add folder query.
+    addFolderQuery.exec();
+}
+
+void BookmarksDatabase::deleteBookmark(const int databaseId)
 {
     // Get a handle for the bookmarks database.
     QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
@@ -168,55 +182,83 @@ void BookmarksDatabase::deleteBookmark(const int bookmarkId)
     deleteBookmarkQuery.prepare("DELETE FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " = :id");
 
     // Bind the query values.
-    deleteBookmarkQuery.bindValue(":id", bookmarkId);
+    deleteBookmarkQuery.bindValue(":id", databaseId);
 
     // 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.
+double BookmarksDatabase::generateFolderId()
+{
+    // Get the current time in epoch format (milliseconds).
+    double possibleFolderId = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
 
-    // Instantiate a bookmarks query.
-    QSqlQuery bookmarksQuery(bookmarksDatabase);
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
 
-    // Set the query to be forward only, which is more performant.
-    bookmarksQuery.setForwardOnly(true);
+    // Instantiate a existing folder query.
+    QSqlQuery existingFolderQuery(bookmarksDatabase);
 
-    // Prepare the bookmarks query.
-    bookmarksQuery.prepare("SELECT " + ID + ", " + DISPLAY_ORDER + " FROM " + BOOKMARKS_TABLE + " ORDER BY " + DISPLAY_ORDER + " ASC");
+    // Prepare the existing folder query.
+    existingFolderQuery.prepare("SELECT " + ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + FOLDER_ID + " = :possible_folder_id");
+
+    // Bind the query values.
+    existingFolderQuery.bindValue(":possible_folder_id", possibleFolderId);
 
     // Execute the query.
-    bookmarksQuery.exec();
+    existingFolderQuery.exec();
 
-    // Create a new display order int.
-    int newDisplayOrder = 0;
+    // Generate a new folder ID if this one is not unique.  The existing folder query will only be valid if there is at least one item.
+    if (existingFolderQuery.isValid())
+        possibleFolderId = generateFolderId();
 
-    // 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);
+    return possibleFolderId;
+}
 
-            // Prepare the update display order query.
-            updateDisplayOrderQuery.prepare("UPDATE " + BOOKMARKS_TABLE + " SET " + DISPLAY_ORDER + " = :display_order WHERE " + ID + " = :id");
+QList<QString>* BookmarksDatabase::getAllFolderUrls(const double folderId)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
 
-            // Bind the query values.
-            updateDisplayOrderQuery.bindValue(":display_order", newDisplayOrder);
-            updateDisplayOrderQuery.bindValue(":id", bookmarksQuery.value(ID).toInt());
+    // Instantiate a folder URLs query.
+    QSqlQuery folderUrlsQuery(bookmarksDatabase);
 
-            // Execute the query.
-            updateDisplayOrderQuery.exec();
-        }
+    // Set the query to be forward only, which is more performant.
+    folderUrlsQuery.setForwardOnly(true);
 
-        // Increment the new display order.
-        ++newDisplayOrder;
+    // Prepare the folder URLs query.
+    folderUrlsQuery.prepare("SELECT " + BOOKMARK_URL + ", " + IS_FOLDER + ", " + FOLDER_ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id");
+
+    // Bind the query values.
+    folderUrlsQuery.bindValue(":parent_folder_id", folderId);
+
+    // Execute the query.
+    folderUrlsQuery.exec();
+
+    // Create a folder URLs list.
+    QList<QString> *folderUrlsListPointer = new QList<QString>;
+
+    // Populate the folder URLs list.
+    while (folderUrlsQuery.next())
+    {
+        // Process the entry according to the type.
+        if (folderUrlsQuery.value(IS_FOLDER).toBool())  // This is a folder.
+        {
+            // Get the subfolder URLs to the list.
+            folderUrlsListPointer->append(*getAllFolderUrls(folderUrlsQuery.value(FOLDER_ID).toDouble()));
+        }
+        else  // This is a bookmark.
+        {
+            // Add the URL to the list.
+            folderUrlsListPointer->append(folderUrlsQuery.value(BOOKMARK_URL).toString());
+        }
     }
+
+    // Return the folder URLs list.
+    return folderUrlsListPointer;
 }
 
-BookmarkStruct *BookmarksDatabase::getBookmark(int bookmarkId)
+BookmarkStruct* BookmarksDatabase::getBookmark(const int databaseId)
 {
     // Get a handle for the bookmarks database.
     QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
@@ -231,7 +273,7 @@ BookmarkStruct *BookmarksDatabase::getBookmark(int bookmarkId)
     bookmarkQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " = :id");
 
     // Bind the query values.
-    bookmarkQuery.bindValue(":id", bookmarkId);
+    bookmarkQuery.bindValue(":id", databaseId);
 
     // Execute the query.
     bookmarkQuery.exec();
@@ -252,10 +294,13 @@ BookmarkStruct *BookmarksDatabase::getBookmark(int bookmarkId)
     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->databaseId = bookmarkQuery.value(ID).toInt();
+    bookmarkStructPointer->name = bookmarkQuery.value(BOOKMARK_NAME).toString();
+    bookmarkStructPointer->url = bookmarkQuery.value(BOOKMARK_URL).toString();
+    bookmarkStructPointer->parentFolderId = bookmarkQuery.value(PARENT_FOLDER_ID).toDouble();
     bookmarkStructPointer->displayOrder = bookmarkQuery.value(DISPLAY_ORDER).toInt();
+    bookmarkStructPointer->isFolder = bookmarkQuery.value(IS_FOLDER).toBool();
+    bookmarkStructPointer->folderId = bookmarkQuery.value(FOLDER_ID).toDouble();
     bookmarkStructPointer->favoriteIcon = QIcon(favoriteIconPixmap);
 
     // Return the bookmark struct pointer.
@@ -298,10 +343,13 @@ std::list<BookmarkStruct>* BookmarksDatabase::getBookmarks()
         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.databaseId = bookmarksQuery.value(ID).toInt();
+        bookmarkStruct.name = bookmarksQuery.value(BOOKMARK_NAME).toString();
+        bookmarkStruct.url = bookmarksQuery.value(BOOKMARK_URL).toString();
+        bookmarkStruct.parentFolderId = bookmarksQuery.value(PARENT_FOLDER_ID).toDouble();
         bookmarkStruct.displayOrder = bookmarksQuery.value(DISPLAY_ORDER).toInt();
+        bookmarkStruct.isFolder = bookmarksQuery.value(IS_FOLDER).toBool();
+        bookmarkStruct.folderId = bookmarksQuery.value(FOLDER_ID).toDouble();
         bookmarkStruct.favoriteIcon = QIcon(favoriteIconPixmap);
 
         // Add the bookmark to the list.
@@ -312,7 +360,7 @@ std::list<BookmarkStruct>* BookmarksDatabase::getBookmarks()
     return bookmarkListPointer;
 }
 
-QList<BookmarkStruct>* BookmarksDatabase::getBookmarksExcept(QList<int> *exceptDatabaseIdsListPointer)
+QList<BookmarkStruct>* BookmarksDatabase::getBookmarksInFolderExcept(const double folderId, QList<int> *exceptDatabaseIdsListPointer)
 {
     // Get a handle for the bookmarks database.
     QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
@@ -340,7 +388,10 @@ QList<BookmarkStruct>* BookmarksDatabase::getBookmarksExcept(QList<int> *exceptD
     }
 
     // Prepare the bookmarks query.
-    bookmarksQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " NOT IN (" + idsNotToGetString + ") ORDER BY " + DISPLAY_ORDER + " ASC");
+    bookmarksQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id AND " + ID + " NOT IN (" + idsNotToGetString + ") ORDER BY " + DISPLAY_ORDER + " ASC");
+
+    // Bind the query values.
+    bookmarksQuery.bindValue(":parent_folder_id", folderId);
 
     // Execute the query.
     bookmarksQuery.exec();
@@ -364,10 +415,13 @@ QList<BookmarkStruct>* BookmarksDatabase::getBookmarksExcept(QList<int> *exceptD
         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.databaseId = bookmarksQuery.value(ID).toInt();
+        bookmarkStruct.name = bookmarksQuery.value(BOOKMARK_NAME).toString();
+        bookmarkStruct.url = bookmarksQuery.value(BOOKMARK_URL).toString();
+        bookmarkStruct.parentFolderId = bookmarksQuery.value(PARENT_FOLDER_ID).toDouble();
         bookmarkStruct.displayOrder = bookmarksQuery.value(DISPLAY_ORDER).toInt();
+        bookmarkStruct.isFolder = bookmarksQuery.value(IS_FOLDER).toBool();
+        bookmarkStruct.folderId = bookmarksQuery.value(FOLDER_ID).toDouble();
         bookmarkStruct.favoriteIcon = QIcon(favoriteIconPixmap);
 
         // Add the bookmark to the list.
@@ -405,34 +459,406 @@ QString BookmarksDatabase::getFavoriteIconBase64String(const QIcon &favoriteIcon
     return favoriteIconBase64String;
 }
 
+QList<BookmarkStruct>* BookmarksDatabase::getFolderContents(const double folderId)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+    // Instantiate a folder contents query.
+    QSqlQuery folderContentsQuery(bookmarksDatabase);
+
+    // Set the query to be forward only, which is more performant.
+    folderContentsQuery.setForwardOnly(true);
+
+    // Prepare the folder contents query.
+    folderContentsQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id ORDER BY " + DISPLAY_ORDER + " ASC");
+
+    // Bind the query values.
+    folderContentsQuery.bindValue(":parent_folder_id", folderId);
+
+    // Execute the query.
+    folderContentsQuery.exec();
+
+    // Create a folder contents list.
+    QList<BookmarkStruct> *folderContentsListPointer = new QList<BookmarkStruct>;
+
+    // Populate the folder contents list.
+    while (folderContentsQuery.next())
+    {
+        // Create a bookmark struct.
+        struct BookmarkStruct bookmarkStruct;
+
+        // Get the favorite icon base 64 byte array.
+        QByteArray favoriteIconByteArray = QByteArray::fromBase64(folderContentsQuery.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.databaseId = folderContentsQuery.value(ID).toInt();
+        bookmarkStruct.name = folderContentsQuery.value(BOOKMARK_NAME).toString();
+        bookmarkStruct.url = folderContentsQuery.value(BOOKMARK_URL).toString();
+        bookmarkStruct.parentFolderId = folderContentsQuery.value(PARENT_FOLDER_ID).toDouble();
+        bookmarkStruct.displayOrder = folderContentsQuery.value(DISPLAY_ORDER).toInt();
+        bookmarkStruct.isFolder = folderContentsQuery.value(IS_FOLDER).toBool();
+        bookmarkStruct.folderId = folderContentsQuery.value(FOLDER_ID).toDouble();
+        bookmarkStruct.favoriteIcon = QIcon(favoriteIconPixmap);
+
+        // Add the item to the list.
+        folderContentsListPointer->append(bookmarkStruct);
+    }
+
+    // Return the folder contents list.
+    return folderContentsListPointer;
+}
+
+QList<int>* BookmarksDatabase::getFolderContentsDatabaseIds(const double folderId)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+    // Instantiate a folder contents query.
+    QSqlQuery folderContentsQuery(bookmarksDatabase);
+
+    // Set the query to be forward only, which is more performant.
+    folderContentsQuery.setForwardOnly(true);
+
+    // Prepare the folder contents query.
+    folderContentsQuery.prepare("SELECT " + ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id");
+
+    // Bind the query values.
+    folderContentsQuery.bindValue(":parent_folder_id", folderId);
+
+    // Execute the query.
+    folderContentsQuery.exec();
+
+    // Create a folder contents database ID list.
+    QList<int> *folderContentsDatabaseIdsListPointer = new QList<int>;
+
+    // Populate the folder contents list.
+    while (folderContentsQuery.next())
+    {
+        // Add the database ID to the list.
+        folderContentsDatabaseIdsListPointer->append(folderContentsQuery.value(ID).toInt());
+    }
+
+    // Return the folder contents database ID list.
+    return folderContentsDatabaseIdsListPointer;
+}
+
+QList<int> *BookmarksDatabase::getFolderContentsDatabaseIdsRecursively(const double folderId)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+    // Instantiate a folder contents query.
+    QSqlQuery folderContentsQuery(bookmarksDatabase);
+
+    // Set the query to be forward only, which is more performant.
+    folderContentsQuery.setForwardOnly(true);
+
+    // Prepare the folder contents query.
+    folderContentsQuery.prepare("SELECT " + ID + ", " + IS_FOLDER + ", " + FOLDER_ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id");
+
+    // Bind the query values.
+    folderContentsQuery.bindValue(":parent_folder_id", folderId);
+
+    // Execute the query.
+    folderContentsQuery.exec();
+
+    // Create a folder contents database ID list.
+    QList<int> *folderContentsDatabaseIdsListPointer = new QList<int>;
+
+    // Populate the folder contents list.
+    while (folderContentsQuery.next())
+    {
+        // Add the database ID to the list.
+        folderContentsDatabaseIdsListPointer->append(folderContentsQuery.value(ID).toInt());
+
+        // Recursively get the contents if this is a subfolder.
+        if (folderContentsQuery.value(IS_FOLDER).toBool())
+            folderContentsDatabaseIdsListPointer->append(*getFolderContentsDatabaseIdsRecursively(folderContentsQuery.value(FOLDER_ID).toDouble()));
+    }
+
+    // Return the folder contents database ID list.
+    return folderContentsDatabaseIdsListPointer;
+}
+
+int BookmarksDatabase::getFolderDatabaseId(const double folderId)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+    // Instantiate a folder database ID query.
+    QSqlQuery folderDatabaseIdQuery(bookmarksDatabase);
+
+    // Set the query to be forward only, which is more performant.
+    folderDatabaseIdQuery.setForwardOnly(true);
+
+    // Prepare the folder database ID query.
+    folderDatabaseIdQuery.prepare("SELECT " + ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + FOLDER_ID + " = :folder_id");
+
+    // Bind the query values.
+    folderDatabaseIdQuery.bindValue(":folder_id", folderId);
+
+    // Execute the query.
+    folderDatabaseIdQuery.exec();
+
+    // Move to the first entry.
+    folderDatabaseIdQuery.first();
+
+    // Return the folder database ID.
+    return folderDatabaseIdQuery.value(ID).toInt();
+}
+
+double BookmarksDatabase::getFolderId(const int databaseId)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+    // Instantiate a folder ID query.
+    QSqlQuery folderIdQuery(bookmarksDatabase);
+
+    // Set the query to be forward only, which is more performant.
+    folderIdQuery.setForwardOnly(true);
+
+    // Prepare the folder ID query.
+    folderIdQuery.prepare("SELECT " + FOLDER_ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " = :database_id");
+
+    // Bind the query values.
+    folderIdQuery.bindValue(":database_id", databaseId);
+
+    // Execute the query.
+    folderIdQuery.exec();
+
+    // Move to the first entry.
+    folderIdQuery.first();
+
+    // Return the folder ID.
+    return folderIdQuery.value(FOLDER_ID).toDouble();
+}
+
+int BookmarksDatabase::getFolderItemCount(const double folderId)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+    // Instantiate a folder contents query.
+    QSqlQuery folderContentsQuery(bookmarksDatabase);
+
+    // Set the query to be forward only, which is more performant.
+    folderContentsQuery.setForwardOnly(true);
+
+    // Prepare the folder contents query.
+    folderContentsQuery.prepare("SELECT " + ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id");
+
+    // Bind the query values.
+    folderContentsQuery.bindValue(":parent_folder_id", folderId);
+
+    // Execute the query.
+    folderContentsQuery.exec();
+
+    // Move to the last row.
+    folderContentsQuery.last();
+
+    // Initialize an item count variable.
+    int itemCount = 0;
+
+    // Check to see if the query is valid (there is at least one item).
+    if (folderContentsQuery.isValid())
+    {
+        // Get the number of rows (which is zero based) and add one to calculate the number of bookmarks.
+        itemCount = folderContentsQuery.at() + 1;
+    }
+
+    // Return the item count.
+    return itemCount;
+}
+
+double BookmarksDatabase::getParentFolderId(const int databaseId)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+    // Instantiate a parent folder ID query.
+    QSqlQuery parentFolderIdQuery(bookmarksDatabase);
+
+    // Set the query to be forward only, which is more performant.
+    parentFolderIdQuery.setForwardOnly(true);
+
+    // Prepare the parent folder ID query.
+    parentFolderIdQuery.prepare("SELECT " + PARENT_FOLDER_ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " = :database_id");
+
+    // Bind the query values.
+    parentFolderIdQuery.bindValue(":database_id", databaseId);
+
+    // Execute the query.
+    parentFolderIdQuery.exec();
+
+    // Move to the first entry.
+    parentFolderIdQuery.first();
+
+    // Return the parent folder ID.
+    return parentFolderIdQuery.value(PARENT_FOLDER_ID).toDouble();
+}
+
+QList<BookmarkStruct>* BookmarksDatabase::getSubfolders(const double folderId)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+    // Instantiate a subfolders query.
+    QSqlQuery subfoldersQuery(bookmarksDatabase);
+
+    // Set the query to be forward only, which is more performant.
+    subfoldersQuery.setForwardOnly(true);
+
+    // Prepare the subfolders query.
+    subfoldersQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " WHERE " + IS_FOLDER + " = 1 AND " + PARENT_FOLDER_ID + " = :parent_folder_id ORDER BY " + DISPLAY_ORDER + " ASC");
+
+    // Bind the query values.
+    subfoldersQuery.bindValue(":parent_folder_id", folderId);
+
+    // Execute the query.
+    subfoldersQuery.exec();
+
+    // Create a subfolder list.
+    QList<BookmarkStruct> *subfoldersListPointer = new QList<BookmarkStruct>;
+
+    // Populate the subfolder list.
+    while (subfoldersQuery.next())
+    {
+        // Create a bookmark struct.
+        struct BookmarkStruct bookmarkStruct;
+
+        // Get the favorite icon base 64 byte array.
+        QByteArray favoriteIconByteArray = QByteArray::fromBase64(subfoldersQuery.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.databaseId = subfoldersQuery.value(ID).toInt();
+        bookmarkStruct.name = subfoldersQuery.value(BOOKMARK_NAME).toString();
+        bookmarkStruct.parentFolderId = subfoldersQuery.value(PARENT_FOLDER_ID).toDouble();
+        bookmarkStruct.displayOrder = subfoldersQuery.value(DISPLAY_ORDER).toInt();
+        bookmarkStruct.isFolder = subfoldersQuery.value(IS_FOLDER).toBool();
+        bookmarkStruct.folderId = subfoldersQuery.value(FOLDER_ID).toDouble();
+        bookmarkStruct.favoriteIcon = QIcon(favoriteIconPixmap);
+
+        // Add the subfolder to the list.
+        subfoldersListPointer->append(bookmarkStruct);
+    }
+
+    // Return the subfolders list.
+    return subfoldersListPointer;
+}
+
+bool BookmarksDatabase::isFolder(const int databaseId)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+    // Instantiate an is folder query.
+    QSqlQuery isFolderQuery(bookmarksDatabase);
+
+    // Set the query to be forward only, which is more performant.
+    isFolderQuery.setForwardOnly(true);
+
+    // Prepare the is folder query.
+    isFolderQuery.prepare("SELECT " + IS_FOLDER + " FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " = :id");
+
+    // Bind the query values.
+    isFolderQuery.bindValue(":id", databaseId);
+
+    // Execute the query.
+    isFolderQuery.exec();
+
+    // Move to the first entry.
+    isFolderQuery.first();
+
+    // Return the folder status.
+    return isFolderQuery.value(IS_FOLDER).toBool();
+}
+
 void BookmarksDatabase::updateBookmark(const BookmarkStruct *bookmarkStructPointer)
 {
     // Get a handle for the bookmarks database.
     QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
 
-    // Instantiate an update bookmark name.
+    // Instantiate an update bookmark query.
     QSqlQuery updateBookmarkQuery(bookmarksDatabase);
 
     // Prepare the update bookmark query.
     updateBookmarkQuery.prepare("UPDATE " + BOOKMARKS_TABLE + " SET " +
                                 BOOKMARK_NAME + " = :bookmark_name, " +
                                 BOOKMARK_URL + " = :bookmark_url, " +
+                                PARENT_FOLDER_ID + " = :parent_folder_id, " +
                                 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(":bookmark_name", bookmarkStructPointer->name);
+    updateBookmarkQuery.bindValue(":bookmark_url", bookmarkStructPointer->url);
+    updateBookmarkQuery.bindValue(":parent_folder_id", bookmarkStructPointer->parentFolderId);
     updateBookmarkQuery.bindValue(":display_order", bookmarkStructPointer->displayOrder);
     updateBookmarkQuery.bindValue(":favorite_icon", getFavoriteIconBase64String(bookmarkStructPointer->favoriteIcon));
-    updateBookmarkQuery.bindValue(":id", bookmarkStructPointer->id);
+    updateBookmarkQuery.bindValue(":id", bookmarkStructPointer->databaseId);
 
     // Execute the query.
     updateBookmarkQuery.exec();
 }
 
-void BookmarksDatabase::updateDisplayOrder(const int bookmarkId, const int displayOrder)
+void BookmarksDatabase::updateBookmarkName(const int databaseId, const QString &bookmarkName)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+    // Instantiate an update bookmark name query.
+    QSqlQuery updateBookmarkNameQuery(bookmarksDatabase);
+
+    // Prepare the update bookmark name query.
+    updateBookmarkNameQuery.prepare("UPDATE " + BOOKMARKS_TABLE +
+                                    " SET " + BOOKMARK_NAME + " = :bookmark_name " +
+                                    "WHERE " + ID + " = :id");
+
+    // Bind the query values.
+    updateBookmarkNameQuery.bindValue(":bookmark_name", bookmarkName);
+    updateBookmarkNameQuery.bindValue(":id", databaseId);
+
+    // Execute the query.
+    updateBookmarkNameQuery.exec();
+}
+
+void BookmarksDatabase::updateBookmarkUrl(const int databaseId, const QString &bookmarkUrl)
+{
+    // Get a handle for the bookmarks database.
+    QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+    // Instantiate an update bookmark URL query.
+    QSqlQuery updateBookmarkUrlQuery(bookmarksDatabase);
+
+    // Prepare the update bookmark URL query.
+    updateBookmarkUrlQuery.prepare("UPDATE " + BOOKMARKS_TABLE +
+                                   " SET " + BOOKMARK_URL + " = :bookmark_url " +
+                                   "WHERE " + ID + " = :id");
+
+    // Bind the query values.
+    updateBookmarkUrlQuery.bindValue(":bookmark_url", bookmarkUrl);
+    updateBookmarkUrlQuery.bindValue(":id", databaseId);
+
+    // Execute the query.
+    updateBookmarkUrlQuery.exec();
+}
+
+void BookmarksDatabase::updateDisplayOrder(const int databaseId, const int displayOrder)
 {
     // Get a handle for the bookmarks database.
     QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
@@ -447,50 +873,66 @@ void BookmarksDatabase::updateDisplayOrder(const int bookmarkId, const int displ
 
     // Bind the query values.
     updateBookmarkDisplayOrderQuery.bindValue(":display_order", displayOrder);
-    updateBookmarkDisplayOrderQuery.bindValue(":id", bookmarkId);
+    updateBookmarkDisplayOrderQuery.bindValue(":id", databaseId);
 
     // Execute the query.
     updateBookmarkDisplayOrderQuery.exec();
 }
 
-void BookmarksDatabase::updateBookmarkName(const int bookmarkId, const QString &bookmarkName)
+void BookmarksDatabase::updateFolderContentsDisplayOrder(const double folderId)
 {
     // Get a handle for the bookmarks database.
     QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
 
-    // Instantiate an update bookmark name query.
-    QSqlQuery updateBookmarkNameQuery(bookmarksDatabase);
+    // Instantiate a folder contents query.
+    QSqlQuery folderContentsQuery(bookmarksDatabase);
 
-    // Prepare the update bookmark name query.
-    updateBookmarkNameQuery.prepare("UPDATE " + BOOKMARKS_TABLE +
-                                    " SET " + BOOKMARK_NAME + " = :bookmark_name " +
-                                    "WHERE " + ID + " = :id");
+    // Set the query to be forward only, which is more performant.
+    folderContentsQuery.setForwardOnly(true);
+
+    // Prepare the folder contents query.
+    folderContentsQuery.prepare("SELECT " + ID + ", " + DISPLAY_ORDER + " FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id ORDER BY " + DISPLAY_ORDER + " ASC");
 
     // Bind the query values.
-    updateBookmarkNameQuery.bindValue(":bookmark_name", bookmarkName);
-    updateBookmarkNameQuery.bindValue(":id", bookmarkId);
+    folderContentsQuery.bindValue(":parent_folder_id", folderId);
 
     // Execute the query.
-    updateBookmarkNameQuery.exec();
+    folderContentsQuery.exec();
+
+    // Define a new display order int.
+    int newDisplayOrder = 0;
+
+    // Populate the subfolder list.
+    while (folderContentsQuery.next())
+    {
+        // Update the display order if it has changed.
+        if (folderContentsQuery.value(DISPLAY_ORDER).toInt() != newDisplayOrder)
+            updateDisplayOrder(folderContentsQuery.value(ID).toInt(), newDisplayOrder);
+
+        // Increment the new display order.
+        ++newDisplayOrder;
+    }
 }
 
-void BookmarksDatabase::updateBookmarkUrl(const int bookmarkId, const QString &bookmarkUrl)
+void BookmarksDatabase::updateParentFolderAndDisplayOrder(const int databaseId, const double parentFolderId, const int displayOrder)
 {
     // Get a handle for the bookmarks database.
     QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME);
 
-    // Instantiate an update bookmark URL query.
-    QSqlQuery updateBookmarkUrlQuery(bookmarksDatabase);
+    // Instantiate an update bookmark display order query.
+    QSqlQuery updateBookmarkDisplayOrderQuery(bookmarksDatabase);
 
-    // Prepare the update bookmark URL query.
-    updateBookmarkUrlQuery.prepare("UPDATE " + BOOKMARKS_TABLE +
-                                   " SET " + BOOKMARK_URL + " = :bookmark_url " +
-                                   "WHERE " + ID + " = :id");
+    // Prepare the update bookmark display order query.
+    updateBookmarkDisplayOrderQuery.prepare("UPDATE " + BOOKMARKS_TABLE +
+                                            " SET " + PARENT_FOLDER_ID + " = :parent_folder_id " +
+                                            ", " + DISPLAY_ORDER + " = :display_order " +
+                                            "WHERE " + ID + " = :id");
 
     // Bind the query values.
-    updateBookmarkUrlQuery.bindValue(":bookmark_url", bookmarkUrl);
-    updateBookmarkUrlQuery.bindValue(":id", bookmarkId);
+    updateBookmarkDisplayOrderQuery.bindValue(":parent_folder_id", parentFolderId);
+    updateBookmarkDisplayOrderQuery.bindValue(":display_order", displayOrder);
+    updateBookmarkDisplayOrderQuery.bindValue(":id", databaseId);
 
     // Execute the query.
-    updateBookmarkUrlQuery.exec();
+    updateBookmarkDisplayOrderQuery.exec();
 }
index 35f94bd23c7b2193aa81d7c915d0f01a9fadd6e6..1e881400bc9a5c59c3157e8b64cd7087efd31040 100644 (file)
@@ -35,14 +35,27 @@ public:
     // The public functions.
     static void addBookmark(const BookmarkStruct *bookmarkStructPointer);
     static void addDatabase();
-    static void deleteBookmark(const int bookmarkId);
-    static BookmarkStruct* getBookmark(int bookmarkId);
+    static void addFolder(const BookmarkStruct *bookmarkStructPointer);
+    static void deleteBookmark(const int databaseId);
+    static QList<QString>* getAllFolderUrls(const double folderId);
+    static BookmarkStruct* getBookmark(const int databaseId);
     static std::list<BookmarkStruct>* getBookmarks();
-    static QList<BookmarkStruct>* getBookmarksExcept(QList<int> *exceptDatabaseIdsListPointer);
+    static QList<BookmarkStruct>* getBookmarksInFolderExcept(const double folderId, QList<int> *exceptDatabaseIdsListPointer);
+    static QList<int>* getFolderContentsDatabaseIds(const double folderId);
+    static QList<int>* getFolderContentsDatabaseIdsRecursively(const double folderId);
+    static QList<BookmarkStruct>* getFolderContents(const double folderId);
+    static int getFolderDatabaseId(const double folderId);
+    static double getFolderId(const int databaseId);
+    static int getFolderItemCount(const double folderId);
+    static double getParentFolderId(const int databaseId);
+    static QList<BookmarkStruct>* getSubfolders(const double folderId);
+    static bool isFolder(const int databaseId);
     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);
+    static void updateBookmarkName(const int databaseId, const QString &bookmarkName);
+    static void updateBookmarkUrl(const int databaseId, const QString &bookmarkUrl);
+    static void updateDisplayOrder(const int databaseId, const int displayOrder);
+    static void updateFolderContentsDisplayOrder(const double folderId);
+    static void updateParentFolderAndDisplayOrder(const int databaseId, const double parentFolderId, const int displayOrder);
 
     // The public constants.
     static const QString CONNECTION_NAME;
@@ -61,6 +74,7 @@ private:
     static const int SCHEMA_VERSION;
 
     // The private functions.
+    static double generateFolderId();
     static QString getFavoriteIconBase64String(const QIcon &favoriteIcon);
 };
 #endif
index 222a69a2fd71c7c1815e0ae765193df7cdee0772..e69543e7ab51cb1e28a0e79b33df964af1d04d95 100644 (file)
@@ -30,7 +30,7 @@
 #include <QPushButton>
 
 // Construct the class.
-AddBookmarkDialog::AddBookmarkDialog(const QString &bookmarkName, const QString &bookmarkUrl, const QIcon &favoriteIcon) : QDialog(nullptr)
+AddBookmarkDialog::AddBookmarkDialog(const QString &bookmarkName, const QString &bookmarkUrl, const QIcon &favoriteIcon, const double parentFolderId) : QDialog(nullptr)
 {
     // Set the window title.
     setWindowTitle(i18nc("The add bookmark dialog window title.", "Add Bookmark"));
@@ -38,7 +38,7 @@ AddBookmarkDialog::AddBookmarkDialog(const QString &bookmarkName, const QString
     // Set the window modality.
     setWindowModality(Qt::WindowModality::ApplicationModal);
 
-    // Instantiate the bookmarks dialog UI.
+    // Instantiate the add bookmark dialog UI.
     Ui::AddBookmarkDialog addBookmarkDialogUi;
 
     // Setup the UI.
@@ -47,36 +47,83 @@ AddBookmarkDialog::AddBookmarkDialog(const QString &bookmarkName, const QString
     // Get handles for the widgets.
     defaultFavoriteIconRadioButtonPointer = addBookmarkDialogUi.defaultFavoriteIconRadioButton;
     customFavoriteIconRadioButtonPointer = addBookmarkDialogUi.customFavoriteIconRadioButton;
-    bookmarkNamePointer = addBookmarkDialogUi.bookmarkNameLineEdit;
-    bookmarkUrlPointer = addBookmarkDialogUi.bookmarkUrlLineEdit;
+    parentFolderTreeWidgetPointer = addBookmarkDialogUi.parentFolderTreeWidget;
+    bookmarkNameLineEditPointer = addBookmarkDialogUi.bookmarkNameLineEdit;
+    bookmarkUrlLineEditPointer = addBookmarkDialogUi.bookmarkUrlLineEdit;
     QPushButton *browseButtonPointer = addBookmarkDialogUi.browseButton;
     QDialogButtonBox *dialogButtonBoxPointer = addBookmarkDialogUi.dialogButtonBox;
 
     // Set the default favorite icon.
     defaultFavoriteIconRadioButtonPointer->setIcon(favoriteIcon);
 
+    // Instantiate a folder helper.
+    folderHelperPointer = new FolderHelper();
+
+    // Set the parent folder tree widget column count.
+    parentFolderTreeWidgetPointer->setColumnCount(2);
+
+    // Hide the second column.
+    parentFolderTreeWidgetPointer->hideColumn(folderHelperPointer->FOLDER_ID_COLUMN);
+
+    // Set the column header.
+    parentFolderTreeWidgetPointer->setHeaderLabel(i18nc("The folder tree widget header", "Select Parent Folder"));
+
+    // Create a bookmarks tree widget item.
+    QTreeWidgetItem *bookmarksTreeWidgetItemPointer = new QTreeWidgetItem();
+
+    // Populate the bookmarks tree widget item.
+    bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_NAME_COLUMN, i18nc("The bookmarks root tree widget name", "Bookmarks"));
+    bookmarksTreeWidgetItemPointer->setIcon(folderHelperPointer->FOLDER_NAME_COLUMN, QIcon::fromTheme("bookmarks"));
+    bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_ID_COLUMN, QLatin1String("0"));
+
+    // Add the bookmarks tree widget item to the root of the tree.
+    parentFolderTreeWidgetPointer->addTopLevelItem(bookmarksTreeWidgetItemPointer);
+
+    // Select the root bookmarks folder if it is the initial parent folder.
+    if (parentFolderId == 0)
+        bookmarksTreeWidgetItemPointer->setSelected(true);
+
+    // Populate the subfolders.
+    folderHelperPointer->populateSubfolders(bookmarksTreeWidgetItemPointer, parentFolderId);
+
+    // Open all the folders.
+    parentFolderTreeWidgetPointer->expandAll();
+
     // Populate the line edits.
-    bookmarkNamePointer->setText(bookmarkName);
-    bookmarkUrlPointer->setText(bookmarkUrl);
+    bookmarkNameLineEditPointer->setText(bookmarkName);
+    bookmarkUrlLineEditPointer->setText(bookmarkUrl);
 
     // Scroll to the beginning of the line edits.
-    bookmarkNamePointer->setCursorPosition(0);
-    bookmarkUrlPointer->setCursorPosition(0);
+    bookmarkNameLineEditPointer->setCursorPosition(0);
+    bookmarkUrlLineEditPointer->setCursorPosition(0);
 
     // Add buttons to the dialog button box.
-    QPushButton *addBookmarkButtonPointer = dialogButtonBoxPointer->addButton(i18nc("The add bookmark button", "Add"), QDialogButtonBox::AcceptRole);
+    addButtonPointer = dialogButtonBoxPointer->addButton(i18nc("The add bookmark button", "Add"), QDialogButtonBox::AcceptRole);
 
     // Set the button icons.
-    addBookmarkButtonPointer->setIcon(QIcon::fromTheme("list-add"));
+    addButtonPointer->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()));
+
+    // Update the UI when the line edits change.
+    connect(bookmarkNameLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi()));
+    connect(bookmarkUrlLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi()));
+
+    // Set the initial UI status.
+    updateUi();
 }
 
 void AddBookmarkDialog::addBookmark()
 {
+    // Get the selected folders list.
+    QList<QTreeWidgetItem*> selectedFoldersList = parentFolderTreeWidgetPointer->selectedItems();
+
+    // Get the selected folder.
+    QTreeWidgetItem *selectedFolderPointer = selectedFoldersList.first();
+
     // Get the favorite icon.
     QIcon favoriteIcon = defaultFavoriteIconRadioButtonPointer->isChecked() ? defaultFavoriteIconRadioButtonPointer->icon() : customFavoriteIconRadioButtonPointer->icon();
 
@@ -84,8 +131,9 @@ void AddBookmarkDialog::addBookmark()
     BookmarkStruct *bookmarkStructPointer = new BookmarkStruct;
 
     // Populate the bookmark struct.
-    bookmarkStructPointer->bookmarkName = bookmarkNamePointer->text();
-    bookmarkStructPointer->bookmarkUrl = bookmarkUrlPointer->text();
+    bookmarkStructPointer->name = bookmarkNameLineEditPointer->text();
+    bookmarkStructPointer->url = bookmarkUrlLineEditPointer->text();
+    bookmarkStructPointer->parentFolderId = selectedFolderPointer->text(folderHelperPointer->FOLDER_ID_COLUMN).toDouble();
     bookmarkStructPointer->favoriteIcon = favoriteIcon;
 
     // Add the bookmark.
@@ -114,3 +162,18 @@ void AddBookmarkDialog::browse()
         customFavoriteIconRadioButtonPointer->setChecked(true);
     }
 }
+
+void AddBookmarkDialog::updateUi()
+{
+    // Determine if both line edits are populated.
+    if (bookmarkNameLineEditPointer->text().isEmpty() || bookmarkUrlLineEditPointer->text().isEmpty())  // At least one of the line edits is empty.
+    {
+        // Disable the add button.
+        addButtonPointer->setEnabled(false);
+    }
+    else  // Both of the line edits are populated.
+    {
+        // Enable the add button.
+        addButtonPointer->setEnabled(true);
+    }
+}
index c054876690e168a34b8e83095987c64e20dc9217..18c3ca144d273c68f10cc857f13049b6954d118f 100644 (file)
 #ifndef ADDBOOKMARKDIALOG_H
 #define ADDBOOKMARKDIALOG_H
 
+// Application headers.
+#include "helpers/FolderHelper.h"
+
 // Qt toolkit headers.
 #include <QDialog>
 #include <QLineEdit>
 #include <QRadioButton>
+#include <QTreeWidget>
 
 class AddBookmarkDialog : public QDialog
 {
@@ -32,7 +36,7 @@ class AddBookmarkDialog : public QDialog
 
 public:
     // The primary constructor.
-    explicit AddBookmarkDialog(const QString &bookmarkName, const QString &bookmarkUrl, const QIcon &favoriteIcon);
+    explicit AddBookmarkDialog(const QString &bookmarkName, const QString &bookmarkUrl, const QIcon &favoriteIcon, const double parentFolderId = 0);
 
 signals:
     // The signals.
@@ -42,12 +46,18 @@ private Q_SLOTS:
     // The private slots.
     void addBookmark();
     void browse();
+    void updateUi();
 
 private:
+    // The private variables.
+    FolderHelper *folderHelperPointer;
+
     // The private widgets.
-    QLineEdit *bookmarkNamePointer;
-    QLineEdit *bookmarkUrlPointer;
+    QPushButton *addButtonPointer;
+    QLineEdit *bookmarkNameLineEditPointer;
+    QLineEdit *bookmarkUrlLineEditPointer;
     QRadioButton *customFavoriteIconRadioButtonPointer;
     QRadioButton *defaultFavoriteIconRadioButtonPointer;
+    QTreeWidget *parentFolderTreeWidgetPointer;
 };
 #endif
diff --git a/src/dialogs/AddFolderDialog.cpp b/src/dialogs/AddFolderDialog.cpp
new file mode 100644 (file)
index 0000000..37b8845
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2023 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+// Application headers.
+#include "AddFolderDialog.h"
+#include "ui_AddFolderDialog.h"
+#include "databases/BookmarksDatabase.h"
+#include "helpers/FolderHelper.h"
+#include "structs/BookmarkStruct.h"
+
+// Qt toolkit headers.
+#include <QFileDialog>
+
+// Construct the class.
+AddFolderDialog::AddFolderDialog(const QIcon &currentWebsiteFavoriteIcon, const double parentFolderId) : QDialog(nullptr)
+{
+    // Set the window title.
+    setWindowTitle(i18nc("The add folder dialog window title.", "Add Folder"));
+
+    // Set the window modality.
+    setWindowModality(Qt::WindowModality::ApplicationModal);
+
+    // Instantiate the add folder dialog UI.
+    Ui::AddFolderDialog addFolderDialogUi;
+
+    // Setup the UI.
+    addFolderDialogUi.setupUi(this);
+
+    // Get handles for the widgets.
+    defaultFolderIconRadioButtonPointer = addFolderDialogUi.defaultFolderIconRadioButton;
+    currentWebsiteFavoriteIconRadioButtonPointer = addFolderDialogUi.currentWebsiteFavoriteIconRadioButton;
+    customFolderIconRadioButtonPointer = addFolderDialogUi.customFolderIconRadioButton;
+    parentFolderTreeWidgetPointer = addFolderDialogUi.parentFolderTreeWidget;
+    folderNameLineEditPointer = addFolderDialogUi.folderNameLineEdit;
+    QPushButton *browseButtonPointer = addFolderDialogUi.browseButton;
+    QDialogButtonBox *dialogButtonBoxPointer = addFolderDialogUi.dialogButtonBox;
+
+    // Set the default favorite icon.
+    currentWebsiteFavoriteIconRadioButtonPointer->setIcon(currentWebsiteFavoriteIcon);
+
+    // Instantiate a folder helper.
+    folderHelperPointer = new FolderHelper();
+
+    // Set the parent folder tree widget column count.
+    parentFolderTreeWidgetPointer->setColumnCount(2);
+
+    // Hide the second column.
+    parentFolderTreeWidgetPointer->hideColumn(folderHelperPointer->FOLDER_ID_COLUMN);
+
+    // Set the column header.
+    parentFolderTreeWidgetPointer->setHeaderLabel(i18nc("The folder tree widget header", "Select Parent Folder"));
+
+    // Create a bookmarks tree widget item.
+    QTreeWidgetItem *bookmarksTreeWidgetItemPointer = new QTreeWidgetItem();
+
+    // Populate the bookmarks tree widget item.
+    bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_NAME_COLUMN, i18nc("The bookmarks root tree widget name", "Bookmarks"));
+    bookmarksTreeWidgetItemPointer->setIcon(folderHelperPointer->FOLDER_NAME_COLUMN, QIcon::fromTheme("bookmarks"));
+    bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_ID_COLUMN, QLatin1String("0"));
+
+    // Add the bookmarks tree widget item to the root of the tree.
+    parentFolderTreeWidgetPointer->addTopLevelItem(bookmarksTreeWidgetItemPointer);
+
+    // Select the root bookmarks folder if it is the initial parent folder.
+    if (parentFolderId == 0)
+        bookmarksTreeWidgetItemPointer->setSelected(true);
+
+    // Populate the subfolders.
+    folderHelperPointer->populateSubfolders(bookmarksTreeWidgetItemPointer, parentFolderId);
+
+    // Open all the folders.
+    parentFolderTreeWidgetPointer->expandAll();
+
+    // Focus the folder name line edit.
+    folderNameLineEditPointer->setFocus();
+
+    // Add buttons to the dialog button box.
+    addButtonPointer = dialogButtonBoxPointer->addButton(i18nc("The add folder button", "Add"), QDialogButtonBox::AcceptRole);
+
+    // Set the button icons.
+    addButtonPointer->setIcon(QIcon::fromTheme("list-add"));
+
+    // Connect the buttons.
+    connect(browseButtonPointer, SIGNAL(clicked()), this, SLOT(browse()));
+    connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(addFolder()));
+    connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject()));
+
+    // Update the UI when the folder name changes.
+    connect(folderNameLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi(const QString&)));
+
+    // Set the initial UI status.
+    updateUi(folderNameLineEditPointer->text());
+}
+
+void AddFolderDialog::addFolder()
+{
+    // Get the parent folder ID.
+    QList<QTreeWidgetItem*> selectedFoldersList = parentFolderTreeWidgetPointer->selectedItems();
+
+    // Get the selected folder.
+    QTreeWidgetItem *selectedFolderPointer = selectedFoldersList.first();
+
+    // Create a favorite icon.
+    QIcon favoriteIcon;
+
+    // Get the favorite icon.
+    if (defaultFolderIconRadioButtonPointer->isChecked())  // The default folder icon is checked.
+        favoriteIcon = defaultFolderIconRadioButtonPointer->icon();
+    else if (currentWebsiteFavoriteIconRadioButtonPointer->isChecked())  // The current website favorite icon is checked.
+        favoriteIcon = currentWebsiteFavoriteIconRadioButtonPointer->icon();
+    else  // The custom folder icon is checked.
+        favoriteIcon = customFolderIconRadioButtonPointer->icon();
+
+    // Create a bookmark struct.
+    BookmarkStruct *bookmarkStructPointer = new BookmarkStruct;
+
+    // Populate the bookmark struct.
+    bookmarkStructPointer->name = folderNameLineEditPointer->text();
+    bookmarkStructPointer->parentFolderId = selectedFolderPointer->text(folderHelperPointer->FOLDER_ID_COLUMN).toDouble();
+    bookmarkStructPointer->favoriteIcon = favoriteIcon;
+
+    // Add the folder.
+    BookmarksDatabase::addFolder(bookmarkStructPointer);
+
+    // Update the list of bookmarks in the menu and toolbar.
+    emit folderAdded();
+
+    // Close the dialog.
+    close();
+}
+
+void AddFolderDialog::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.
+        customFolderIconRadioButtonPointer->setIcon(QIcon(imageFileString));
+
+        // Check the custom favorite icon radio button.
+        customFolderIconRadioButtonPointer->setChecked(true);
+    }
+}
+
+void AddFolderDialog::updateUi(const QString &newFolderName)
+{
+    // Set the status of the add button based on the
+    addButtonPointer->setEnabled(!newFolderName.isEmpty());
+}
diff --git a/src/dialogs/AddFolderDialog.h b/src/dialogs/AddFolderDialog.h
new file mode 100644 (file)
index 0000000..2dc05df
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2023 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ADDFOLDERDIALOG_H
+#define ADDFOLDERDIALOG_H
+
+// Application headers.
+#include "helpers/FolderHelper.h"
+
+// Qt toolkit headers.
+#include <QDialog>
+#include <QLineEdit>
+#include <QRadioButton>
+#include <QTreeWidget>
+
+class AddFolderDialog : public QDialog
+{
+    // Include the Q_OBJECT macro.
+    Q_OBJECT
+
+public:
+    // The primary constructor.
+    explicit AddFolderDialog(const QIcon &currentWebsiteFavoriteIcon, const double parentFolderId = 0);
+
+signals:
+    // The signals.
+    void folderAdded() const;
+
+private Q_SLOTS:
+    // The private slots.
+    void addFolder();
+    void browse();
+    void updateUi(const QString &newFolderName);
+
+private:
+    // The private variables.
+    FolderHelper *folderHelperPointer;
+
+    // The private widgets.
+    QPushButton *addButtonPointer;
+    QRadioButton *currentWebsiteFavoriteIconRadioButtonPointer;
+    QRadioButton *customFolderIconRadioButtonPointer;
+    QRadioButton *defaultFolderIconRadioButtonPointer;
+    QLineEdit *folderNameLineEditPointer;
+    QTreeWidget *parentFolderTreeWidgetPointer;
+};
+#endif
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.
     {
index e81d0b41cf008f38ce07e398a67eae0b69c5b2a9..78a48963da485afde9e6fc259e5cdccd50753e53 100644 (file)
@@ -36,13 +36,15 @@ class BookmarksDialog : public QDialog
 
 public:
     // The primary constructor.
-    explicit BookmarksDialog(QIcon currentWebsiteFavoriteIcon);
+    explicit BookmarksDialog(QString currentWebsiteTitle, QString currentWebsiteUrl, QIcon currentWebsiteFavoriteIcon);
 
     // The public constants.
-    static const int BOOKMARK_NAME_COLUMN = 0;
-    static const int BOOKMARK_URL_COLUMN = 1;
+    static const int NAME_COLUMN = 0;
+    static const int URL_COLUMN = 1;
     static const int DATABASE_ID_COLUMN = 2;
-    static const int DISPLAY_ORDER = 3;
+    static const int DISPLAY_ORDER_COLUMN = 3;
+    static const int IS_FOLDER_COLUMN = 4;
+    static const int FOLDER_ID_COLUMN = 5;
 
 signals:
     // The signals.
@@ -53,14 +55,12 @@ private Q_SLOTS:
     void deleteItems() const;
     void refreshBookmarks() const;
     void showAddBookmarkDialog() const;
+    void showAddFolderDialog() const;
     void showEditDialog();
     void updateBookmarkFromTree(QStandardItem *modifiedStandardItem);
-    void updateUi() const;
+    void updateSelection() const;
 
 private:
-    // The private functions.
-    void populateBookmarks() const;
-
     // The private variables.
     QPushButton *deleteItemsButtonPointer;
     QPushButton *editButtonPointer;
@@ -68,5 +68,13 @@ private:
     QItemSelectionModel *treeSelectionModelPointer;
     DraggableTreeView *draggableTreeViewPointer;
     QIcon websiteFavoriteIcon;
+    QString websiteTitle;
+    QString websiteUrl;
+
+    // The private functions.
+    void populateBookmarks() const;
+    void populateSubfolders(QStandardItem *folderItemNamePointer, const double folderId) const;
+    void selectSubfolderContents(const QModelIndex &parentModelIndex) const;
+    void updateUi() const;
 };
 #endif
index a1cc4d0646da136c23c283394d83b82ec033decc..4b72f5c6c382a72138abd69c839f099720b202b0 100644 (file)
 # List the sources to include in the executable.
 target_sources(privacybrowser PRIVATE
     AddBookmarkDialog.cpp
+    AddFolderDialog.cpp
     AddOrEditCookieDialog.cpp
     BookmarksDialog.cpp
     CookiesDialog.cpp
     DomainSettingsDialog.cpp
     DurableCookiesDialog.cpp
     EditBookmarkDialog.cpp
+    EditFolderDialog.cpp
     SaveDialog.cpp
 )
index 35582f18b3811907ee3e04585e41027e4e6e80a1..6dd3f712471247b9e4bd0f33b02b723184fb3976 100644 (file)
@@ -26,7 +26,7 @@
 #include <QFileDialog>
 
 // Construct the class.
-EditBookmarkDialog::EditBookmarkDialog(const int bookmarkId, QIcon &currentWebsiteFavoriteIcon) : QDialog(nullptr), bookmarkDatabaseId(bookmarkId)
+EditBookmarkDialog::EditBookmarkDialog(const int databaseId, QIcon &currentWebsiteFavoriteIcon) : QDialog(nullptr), bookmarkDatabaseId(databaseId)
 {
     // Set the window title.
     setWindowTitle(i18nc("The edit bookmark dialog window title.", "Edit Bookmark"));
@@ -34,7 +34,7 @@ EditBookmarkDialog::EditBookmarkDialog(const int bookmarkId, QIcon &currentWebsi
     // Set the window modality.
     setWindowModality(Qt::WindowModality::ApplicationModal);
 
-    // Instantiate the bookmarks dialog UI.
+    // Instantiate the edit bookmark dialog UI.
     Ui::EditBookmarkDialog editBookmarkDialogUi;
 
     // Setup the UI.
@@ -42,39 +42,82 @@ EditBookmarkDialog::EditBookmarkDialog(const int bookmarkId, QIcon &currentWebsi
 
     // Get handles for the widgets.
     currentFavoriteIconRadioButtonPointer = editBookmarkDialogUi.currentFavoriteIconRadioButton;
-    currentWebsiteFavoritIconRadioButtonPointer = editBookmarkDialogUi.currentWebsiteFavoriteIconRadioButton;
+    currentWebsiteFavoriteIconRadioButtonPointer = editBookmarkDialogUi.currentWebsiteFavoriteIconRadioButton;
     customFavoriteIconRadioButtonPointer = editBookmarkDialogUi.customFavoriteIconRadioButton;
-    bookmarkNamePointer = editBookmarkDialogUi.bookmarkNameLineEdit;
-    bookmarkUrlPointer = editBookmarkDialogUi.bookmarkUrlLineEdit;
+    parentFolderTreeWidgetPointer = editBookmarkDialogUi.parentFolderTreeWidget;
+    bookmarkNameLineEditPointer = editBookmarkDialogUi.bookmarkNameLineEdit;
+    bookmarkUrlLineEditPointer = editBookmarkDialogUi.bookmarkUrlLineEdit;
     QPushButton *browseButtonPointer = editBookmarkDialogUi.browseButton;
     QDialogButtonBox *dialogButtonBoxPointer = editBookmarkDialogUi.dialogButtonBox;
+    saveButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Save);
 
     // Get the bookmark struct.
-    bookmarkStructPointer = BookmarksDatabase::getBookmark(bookmarkId);
+    bookmarkStructPointer = BookmarksDatabase::getBookmark(databaseId);
 
     // Set the favorite icons.
     currentFavoriteIconRadioButtonPointer->setIcon(bookmarkStructPointer->favoriteIcon);
-    currentWebsiteFavoritIconRadioButtonPointer->setIcon(currentWebsiteFavoriteIcon);
+    currentWebsiteFavoriteIconRadioButtonPointer->setIcon(currentWebsiteFavoriteIcon);
+
+    // Instantiate a folder helper.
+    folderHelperPointer = new FolderHelper();
+
+    // Set the parent folder tree widget column count.
+    parentFolderTreeWidgetPointer->setColumnCount(2);
+
+    // Hide the second column.
+    parentFolderTreeWidgetPointer->hideColumn(folderHelperPointer->FOLDER_ID_COLUMN);
+
+    // Set the column header.
+    parentFolderTreeWidgetPointer->setHeaderLabel(i18nc("The folder tree widget header", "Select Parent Folder"));
+
+    // Create a bookmarks tree widget item.
+    QTreeWidgetItem *bookmarksTreeWidgetItemPointer = new QTreeWidgetItem();
+
+    // Populate the bookmarks tree widget item.
+    bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_NAME_COLUMN, i18nc("The bookmarks root tree widget name", "Bookmarks"));
+    bookmarksTreeWidgetItemPointer->setIcon(folderHelperPointer->FOLDER_NAME_COLUMN, QIcon::fromTheme("bookmarks"));
+    bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_ID_COLUMN, QLatin1String("0"));
+
+    // Add the bookmarks tree widget item to the root of the tree.
+    parentFolderTreeWidgetPointer->addTopLevelItem(bookmarksTreeWidgetItemPointer);
+
+    // Select the root bookmarks folder if it is the initial parent folder.
+    if (bookmarkStructPointer->parentFolderId == 0)
+        bookmarksTreeWidgetItemPointer->setSelected(true);
+
+    // Populate the subfolders.
+    folderHelperPointer->populateSubfolders(bookmarksTreeWidgetItemPointer, bookmarkStructPointer->parentFolderId);
+
+    // Open all the folders.
+    parentFolderTreeWidgetPointer->expandAll();
 
     // Populate the line edits.
-    bookmarkNamePointer->setText(bookmarkStructPointer->bookmarkName);
-    bookmarkUrlPointer->setText(bookmarkStructPointer->bookmarkUrl);
+    bookmarkNameLineEditPointer->setText(bookmarkStructPointer->name);
+    bookmarkUrlLineEditPointer->setText(bookmarkStructPointer->url);
 
     // Scroll to the beginning of the line edits.
-    bookmarkNamePointer->setCursorPosition(0);
-    bookmarkUrlPointer->setCursorPosition(0);
+    bookmarkNameLineEditPointer->setCursorPosition(0);
+    bookmarkUrlLineEditPointer->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()));
+
+    // Update the UI when the line edits change.
+    connect(bookmarkNameLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi()));
+    connect(bookmarkUrlLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi()));
+
+    // Set the initial UI status.
+    updateUi();
 }
 
 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 (*)"));
+    QString imageFileString = QFileDialog::getOpenFileName(this, i18nc("The browse for favorite icon dialog header", "Favorite Icon Image"), QDir::homePath(),
+                              i18nc("The browse for image files filter", "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())
@@ -89,31 +132,49 @@ void EditBookmarkDialog::browse()
 
 void EditBookmarkDialog::save()
 {
+    // Get the selected folders list.
+    QList<QTreeWidgetItem*> selectedFoldersList = parentFolderTreeWidgetPointer->selectedItems();
+
+    // Get the selected folder.
+    QTreeWidgetItem *selectedFolderPointer = selectedFoldersList.first();
+
+    // Get the parent folder ID.
+    double parentFolderId = selectedFolderPointer->text(folderHelperPointer->FOLDER_ID_COLUMN).toDouble();
+
+    // Get the original display order.
+    int displayOrder = bookmarkStructPointer->displayOrder;
+
+    // Get the new display order if the parent folder has changed.
+    if (parentFolderId != bookmarkStructPointer->parentFolderId)
+        displayOrder = BookmarksDatabase::getFolderItemCount(parentFolderId);
+
     // 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 if (currentWebsiteFavoriteIconRadioButtonPointer->isChecked())  // The current website favorite icon is checked.
+        favoriteIcon = currentWebsiteFavoriteIconRadioButtonPointer->icon();
     else  // The custom favorite icon is checked.
         favoriteIcon = customFavoriteIconRadioButtonPointer->icon();
 
-    qDebug() << "Favorite icon:  " << favoriteIcon;
-
     // Create a bookmark struct.
-    BookmarkStruct *bookmarkStructPointer = new BookmarkStruct;
+    BookmarkStruct *updatedBookmarkStructPointer = new BookmarkStruct;
 
     // Populate the bookmark struct.
-    bookmarkStructPointer->id = bookmarkDatabaseId;
-    bookmarkStructPointer->bookmarkName = bookmarkNamePointer->text();
-    bookmarkStructPointer->bookmarkUrl = bookmarkUrlPointer->text();
-    bookmarkStructPointer->displayOrder = bookmarkStructPointer->displayOrder;
-    bookmarkStructPointer->favoriteIcon = favoriteIcon;
+    updatedBookmarkStructPointer->databaseId = bookmarkDatabaseId;
+    updatedBookmarkStructPointer->name = bookmarkNameLineEditPointer->text();
+    updatedBookmarkStructPointer->url = bookmarkUrlLineEditPointer->text();
+    updatedBookmarkStructPointer->parentFolderId = parentFolderId;
+    updatedBookmarkStructPointer->displayOrder = displayOrder;
+    updatedBookmarkStructPointer->favoriteIcon = favoriteIcon;
 
     // Update the bookmark.
-    BookmarksDatabase::updateBookmark(bookmarkStructPointer);
+    BookmarksDatabase::updateBookmark(updatedBookmarkStructPointer);
+
+    // Update the display order of all the items in the previous folder.
+    BookmarksDatabase::updateFolderContentsDisplayOrder(bookmarkStructPointer->parentFolderId);
 
     // Emit the bookmark saved signal.
     emit bookmarkSaved();
@@ -121,3 +182,18 @@ void EditBookmarkDialog::save()
     // Close the dialog.
     close();
 }
+
+void EditBookmarkDialog::updateUi()
+{
+    // Determine if both line edits are populated.
+    if (bookmarkNameLineEditPointer->text().isEmpty() || bookmarkUrlLineEditPointer->text().isEmpty())  // At least one of the line edits is empty.
+    {
+        // Disable the save button.
+        saveButtonPointer->setEnabled(false);
+    }
+    else  // Both of the line edits are populated.
+    {
+        // Enable the save button.
+        saveButtonPointer->setEnabled(true);
+    }
+}
index c0655811d98b55e2ce40dd631cc4ce7861684f9c..bbba43e3cbd33ea1ba09e9070939bbfa27afb0e1 100644 (file)
@@ -21,6 +21,7 @@
 #define EDITBOOKMARKDIALOG_H
 
 // Application headers.
+#include "helpers/FolderHelper.h"
 #include "structs/BookmarkStruct.h"
 
 // Qt toolkit headers.
@@ -35,7 +36,7 @@ class EditBookmarkDialog : public QDialog
 
 public:
     // The primary constructor.
-    explicit EditBookmarkDialog(const int bookmarkId, QIcon &currentWebsiteFavoriteIcon);
+    explicit EditBookmarkDialog(const int databaseId, QIcon &currentWebsiteFavoriteIcon);
 
 signals:
     // The signals.
@@ -45,15 +46,21 @@ private Q_SLOTS:
     // The private slots.
     void browse();
     void save();
+    void updateUi();
 
 private:
+    // The private variables.
+    FolderHelper *folderHelperPointer;
+
     // The private widgets.
     int bookmarkDatabaseId;
-    QLineEdit *bookmarkNamePointer;
+    QLineEdit *bookmarkNameLineEditPointer;
     BookmarkStruct *bookmarkStructPointer;
-    QLineEdit *bookmarkUrlPointer;
+    QLineEdit *bookmarkUrlLineEditPointer;
     QRadioButton *currentFavoriteIconRadioButtonPointer;
-    QRadioButton *currentWebsiteFavoritIconRadioButtonPointer;
+    QRadioButton *currentWebsiteFavoriteIconRadioButtonPointer;
     QRadioButton *customFavoriteIconRadioButtonPointer;
+    QTreeWidget *parentFolderTreeWidgetPointer;
+    QPushButton *saveButtonPointer;
 };
 #endif
diff --git a/src/dialogs/EditFolderDialog.cpp b/src/dialogs/EditFolderDialog.cpp
new file mode 100644 (file)
index 0000000..4d332ce
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2023 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+// Application headers.
+#include "EditFolderDialog.h"
+#include "ui_EditFolderDialog.h"
+#include "databases/BookmarksDatabase.h"
+
+// Qt toolkit headers.
+#include <QFileDialog>
+
+// Construct the class.
+EditFolderDialog::EditFolderDialog(const int databaseId, QIcon &currentWebsiteFavoriteIcon) : QDialog(nullptr), folderDatabaseId(databaseId)
+{
+    // Set the window title.
+    setWindowTitle(i18nc("The edit folder dialog window title.", "Edit Folder"));
+
+    // Set the window modality.
+    setWindowModality(Qt::WindowModality::ApplicationModal);
+
+    // Instantiate the edit folder dialog UI.
+    Ui::EditFolderDialog editFolderDialogUi;
+
+    // Setup the UI.
+    editFolderDialogUi.setupUi(this);
+
+    // Get handles for the widgets.
+    currentFolderIconRadioButtonPointer = editFolderDialogUi.currentFolderIconRadioButton;
+    currentWebsiteFavoriteIconRadioButtonPointer = editFolderDialogUi.currentWebsiteFavoriteIconRadioButton;
+    customFolderIconRadioButtonPointer = editFolderDialogUi.customFolderIconRadioButton;
+    parentFolderTreeWidgetPointer = editFolderDialogUi.parentFolderTreeWidget;
+    folderNameLineEditPointer = editFolderDialogUi.folderNameLineEdit;
+    QPushButton *browseButtonPointer = editFolderDialogUi.browseButton;
+    QDialogButtonBox *dialogButtonBoxPointer = editFolderDialogUi.dialogButtonBox;
+    saveButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Save);
+
+    // Get the bookmark struct.
+    bookmarkStructPointer = BookmarksDatabase::getBookmark(databaseId);
+
+    // Set the folder icons.
+    currentFolderIconRadioButtonPointer->setIcon(bookmarkStructPointer->favoriteIcon);
+    currentWebsiteFavoriteIconRadioButtonPointer->setIcon(currentWebsiteFavoriteIcon);
+
+    // Instantiate a folder helper.
+    folderHelperPointer = new FolderHelper();
+
+    // Set the parent folder tree widget column count.
+    parentFolderTreeWidgetPointer->setColumnCount(2);
+
+    // Hide the second column.
+    parentFolderTreeWidgetPointer->hideColumn(folderHelperPointer->FOLDER_ID_COLUMN);
+
+    // Set the column header.
+    parentFolderTreeWidgetPointer->setHeaderLabel(i18nc("The folder tree widget header", "Select Parent Folder"));
+
+    // Create a bookmarks tree widget item.
+    QTreeWidgetItem *bookmarksTreeWidgetItemPointer = new QTreeWidgetItem();
+
+    // Populate the bookmarks tree widget item.
+    bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_NAME_COLUMN, i18nc("The bookmarks root tree widget name", "Bookmarks"));
+    bookmarksTreeWidgetItemPointer->setIcon(folderHelperPointer->FOLDER_NAME_COLUMN, QIcon::fromTheme("bookmarks"));
+    bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_ID_COLUMN, QLatin1String("0"));
+
+    // Add the bookmarks tree widget item to the root of the tree.
+    parentFolderTreeWidgetPointer->addTopLevelItem(bookmarksTreeWidgetItemPointer);
+
+    // Select the root bookmarks folder if it is the initial parent folder.
+    if (bookmarkStructPointer->parentFolderId == 0)
+        bookmarksTreeWidgetItemPointer->setSelected(true);
+
+    // Populate the subfolders, except for the one being edited.
+    folderHelperPointer->populateSubfoldersExcept(databaseId, bookmarksTreeWidgetItemPointer, bookmarkStructPointer->parentFolderId);
+
+    // Open all the folders.
+    parentFolderTreeWidgetPointer->expandAll();
+
+    // Populate the line edits.
+    folderNameLineEditPointer->setText(bookmarkStructPointer->name);
+
+    // Scroll to the beginning of the line edits.
+    folderNameLineEditPointer->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()));
+
+    // Update the UI when the line edit changes.
+    connect(folderNameLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi()));
+
+    // Set the initial UI status.
+    updateUi();
+}
+
+void EditFolderDialog::browse()
+{
+    // Get an image file string from the user.
+    QString imageFileString = QFileDialog::getOpenFileName(this, i18nc("The browse for folder icon dialog header", "Folder Icon Image"), QDir::homePath(),
+                              i18nc("The browse for image files filter", "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 folder icon.
+        customFolderIconRadioButtonPointer->setIcon(QIcon(imageFileString));
+
+        // Check the custom folder icon radio button.
+        customFolderIconRadioButtonPointer->setChecked(true);
+    }
+}
+
+void EditFolderDialog::save()
+{
+    // Get the selected folders list.
+    QList<QTreeWidgetItem*> selectedFoldersList = parentFolderTreeWidgetPointer->selectedItems();
+
+    // Get the selected folder.
+    QTreeWidgetItem *selectedFolderPointer = selectedFoldersList.first();
+
+    // Get the parent folder ID.
+    double parentFolderId = selectedFolderPointer->text(folderHelperPointer->FOLDER_ID_COLUMN).toDouble();
+
+    // Get the original display order.
+    int displayOrder = bookmarkStructPointer->displayOrder;
+
+    // Get the new display order if the parent folder has changed.
+    if (parentFolderId != bookmarkStructPointer->parentFolderId)
+        displayOrder = BookmarksDatabase::getFolderItemCount(parentFolderId);
+
+    // Create a favorite icon.
+    QIcon favoriteIcon;
+
+    // Get the favorite icon.
+    if (currentFolderIconRadioButtonPointer->isChecked())  // The current folder icon is checked.
+        favoriteIcon = currentFolderIconRadioButtonPointer->icon();
+    else if (currentWebsiteFavoriteIconRadioButtonPointer->isChecked())  // The current website favorite icon is checked.
+        favoriteIcon = currentWebsiteFavoriteIconRadioButtonPointer->icon();
+    else  // The custom favorite icon is checked.
+        favoriteIcon = customFolderIconRadioButtonPointer->icon();
+
+    // Create a bookmark struct.
+    BookmarkStruct *updatedBookmarkStructPointer = new BookmarkStruct;
+
+    // Populate the bookmark struct.
+    updatedBookmarkStructPointer->databaseId = folderDatabaseId;
+    updatedBookmarkStructPointer->name = folderNameLineEditPointer->text();
+    updatedBookmarkStructPointer->parentFolderId = parentFolderId;
+    updatedBookmarkStructPointer->displayOrder = displayOrder;
+    updatedBookmarkStructPointer->favoriteIcon = favoriteIcon;
+
+    // Update the folder.
+    BookmarksDatabase::updateBookmark(updatedBookmarkStructPointer);
+
+    // Update the display order of all the items in the previous folder.
+    BookmarksDatabase::updateFolderContentsDisplayOrder(bookmarkStructPointer->parentFolderId);
+
+    // Emit the folder saved signal.
+    emit folderSaved();
+
+    // Close the dialog.
+    close();
+}
+
+void EditFolderDialog::updateUi()
+{
+    // Set the status of the save button.
+    saveButtonPointer->setEnabled(!folderNameLineEditPointer->text().isEmpty());
+}
diff --git a/src/dialogs/EditFolderDialog.h b/src/dialogs/EditFolderDialog.h
new file mode 100644 (file)
index 0000000..ac203d8
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2023 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EDITFOLDERDIALOG_H
+#define EDITFOLDERDIALOG_H
+
+// Application headers.
+#include "helpers/FolderHelper.h"
+#include "structs/BookmarkStruct.h"
+
+// Qt toolkit headers.
+#include <QDialog>
+#include <QRadioButton>
+
+class EditFolderDialog : public QDialog
+{
+    // Include the Q_OBJECT macro.
+    Q_OBJECT
+
+public:
+    // The primary constructor.
+    explicit EditFolderDialog(const int databaseId, QIcon &currentWebsiteFavoriteIcon);
+
+signals:
+    // The signals.
+    void folderSaved() const;
+
+private Q_SLOTS:
+    // The private slots.
+    void browse();
+    void save();
+    void updateUi();
+
+private:
+    // The private variables.
+    FolderHelper *folderHelperPointer;
+
+    // The private widgets.
+    BookmarkStruct *bookmarkStructPointer;
+    QRadioButton *currentFolderIconRadioButtonPointer;
+    QRadioButton *currentWebsiteFavoriteIconRadioButtonPointer;
+    QRadioButton *customFolderIconRadioButtonPointer;
+    int folderDatabaseId;
+    QLineEdit *folderNameLineEditPointer;
+    QTreeWidget *parentFolderTreeWidgetPointer;
+    QPushButton *saveButtonPointer;
+};
+#endif
index a1a19de6e12f9274e322885f1a72f0d190273c2c..921e0c078290ca5df6ee973847025535ccf9631e 100644 (file)
@@ -18,6 +18,7 @@
 
 # List the sources to include in the executable.
 target_sources(privacybrowser PRIVATE
+    FolderHelper.cpp
     SearchEngineHelper.cpp
     UserAgentHelper.cpp
 )
diff --git a/src/helpers/FolderHelper.cpp b/src/helpers/FolderHelper.cpp
new file mode 100644 (file)
index 0000000..01a45da
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2023 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+// Application headers.
+#include "FolderHelper.h"
+#include "databases/BookmarksDatabase.h"
+#include "structs/BookmarkStruct.h"
+
+// Construct the class.
+FolderHelper::FolderHelper() {}
+
+void FolderHelper::populateSubfolders(QTreeWidgetItem *treeWidgetItemPointer, const double initialParentFolderId)
+{
+    // Get the list of subfolders.
+    QList<BookmarkStruct> *subfoldersList = BookmarksDatabase::getSubfolders(treeWidgetItemPointer->text(FOLDER_ID_COLUMN).toDouble());
+
+    // Populate each subfolder.
+    for (BookmarkStruct bookmarkStruct : *subfoldersList)
+    {
+        // Create a tree widget item.
+        QTreeWidgetItem *subfolderWidgetItemPointer = new QTreeWidgetItem();
+
+        // Populate the tree widget item.
+        subfolderWidgetItemPointer->setText(FOLDER_NAME_COLUMN, bookmarkStruct.name);
+        subfolderWidgetItemPointer->setIcon(FOLDER_NAME_COLUMN, bookmarkStruct.favoriteIcon);
+        subfolderWidgetItemPointer->setText(FOLDER_ID_COLUMN, QString::number(bookmarkStruct.folderId, 'f', 0));  // Format the folder ID as a floating point with no trailing zeros.
+
+        // Add the subfolder to the tree widget item.
+        treeWidgetItemPointer->addChild(subfolderWidgetItemPointer);
+
+        // Select the folder if it is the initial parent folder.
+        if (bookmarkStruct.folderId == initialParentFolderId)
+            subfolderWidgetItemPointer->setSelected(true);
+
+        // Add any subfolders.
+        populateSubfolders(subfolderWidgetItemPointer, initialParentFolderId);
+    }
+}
+
+void FolderHelper::populateSubfoldersExcept(const double exceptSubfolderDatabaseId, QTreeWidgetItem *treeWidgetItemPointer, const double initialParentFolderId)
+{
+    // Get the list of subfolders.
+    QList<BookmarkStruct> *subfoldersList = BookmarksDatabase::getSubfolders(treeWidgetItemPointer->text(FOLDER_ID_COLUMN).toDouble());
+
+    // Populate each subfolder.
+    for (BookmarkStruct bookmarkStruct : *subfoldersList)
+    {
+        // Only populate the subfolder if it is not excepted.
+        if (bookmarkStruct.databaseId != exceptSubfolderDatabaseId)
+        {
+            // Create a tree widget item.
+            QTreeWidgetItem *subfolderWidgetItemPointer = new QTreeWidgetItem();
+
+            // Populate the tree widget item.
+            subfolderWidgetItemPointer->setText(FOLDER_NAME_COLUMN, bookmarkStruct.name);
+            subfolderWidgetItemPointer->setIcon(FOLDER_NAME_COLUMN, bookmarkStruct.favoriteIcon);
+            subfolderWidgetItemPointer->setText(FOLDER_ID_COLUMN, QString::number(bookmarkStruct.folderId, 'f', 0));  // Format the folder ID as a floating point with no trailing zeros.
+
+            // Add the subfolder to the tree widget item.
+            treeWidgetItemPointer->addChild(subfolderWidgetItemPointer);
+
+            // Select the folder if it is the initial parent folder.
+            if (bookmarkStruct.folderId == initialParentFolderId)
+                subfolderWidgetItemPointer->setSelected(true);
+
+            // Add any subfolders.
+            populateSubfoldersExcept(exceptSubfolderDatabaseId, subfolderWidgetItemPointer, initialParentFolderId);
+        }
+    }
+}
diff --git a/src/helpers/FolderHelper.h b/src/helpers/FolderHelper.h
new file mode 100644 (file)
index 0000000..9248576
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FOLDERHELPER_H
+#define FOLDERHELPER_H
+
+// Qt toolkit headers.
+#include <QTreeWidgetItem>
+
+class FolderHelper
+{
+public:
+    // The default constructor.
+    explicit FolderHelper();
+
+    // The public constants.
+    const int FOLDER_NAME_COLUMN = 0;
+    const int FOLDER_ID_COLUMN = 1;
+
+    // The public functions.
+    void populateSubfolders(QTreeWidgetItem *treeWidgetItemPointer, const double initialParentFolderId);
+    void populateSubfoldersExcept(const double exceptSubfolderDatabaseId, QTreeWidgetItem *treeWidgetItemPointer, const double initialParentFolderId);
+};
+#endif
index c39eb883cb04bf8c282d1a38fa113c5a72633cf7..726d3fb67b97e08ca7b7d8b706f15f9487a48ccb 100644 (file)
@@ -26,8 +26,8 @@
 // KDE Frameworks headers.
 #include <KAboutData>
 #include <KCrash>
-#include <KLocalizedString>
 #include <KDBusService>
+#include <KLocalizedString>
 
 // Qt headers.
 #include <QApplication>
index 98ac69a04ee9d3a1c2a70966858268c280bd0bd6..d7e0a868ac39bdb05b2a13de428848cef9845616 100644 (file)
 
 struct BookmarkStruct
 {
-    int id;
-    QString bookmarkName;
-    QString bookmarkUrl;
+    int databaseId;
+    QString name;
+    QString url;
+    double parentFolderId;
     int displayOrder;
+    bool isFolder;
+    double folderId;
     QIcon favoriteIcon;
 };
 #endif
index c0e5a196febaec95eeb999728fe8e947ef196bc7..1d613226c466cbf7db36c38e87d92a7fcbacb1f2 100644 (file)
 
         <!-- Bookmarks. -->
         <Menu name="bookmarks">
+            <Action name="add_folder" />
+
+            <Separator />
+
             <Action name="view_bookmarks_toolbar" />
 
             <Separator />
index 01f143ce84f4d861ae7834487036aaf17b3a03a4..0fdaf33e20062c489f48096a1585dc59993837d3 100644 (file)
                                     </property>
                                 </widget>
                             </item>
+
+                            <!-- Spacer. -->
+                            <item>
+                                <spacer>
+                                    <property name="orientation">
+                                        <enum>Qt::Vertical</enum>
+                                    </property>
+                                </spacer>
+                            </item>
                         </layout>
                     </item>
 
-                    <!-- Name and URL column. -->
+                    <!-- Parent folder, name and URL column. -->
                     <item>
                         <layout class="QVBoxLayout">
                             <!-- First row. -->
                                     <property name="alignment">
                                         <enum>Qt::AlignLeft</enum>
                                     </property>
+
+                                    <!-- Parent folder.  -->
+                                    <item>
+                                        <widget class="QTreeWidget" name="parentFolderTreeWidget">
+                                            <property name="minimumSize">
+                                                <size>
+                                                    <width>1000</width>
+                                                    <height>700</height>
+                                                </size>
+                                            </property>
+                                        </widget>
+                                    </item>
+                                </layout>
+                            </item>
+
+                            <!-- Second row. -->
+                            <item>
+                                <layout class="QHBoxLayout">
+                                    <property name="alignment">
+                                        <enum>Qt::AlignLeft</enum>
+                                    </property>
+
                                     <!-- Bookmark name.  -->
                                     <item>
                                         <widget class="QLabel">
                                 </layout>
                             </item>
 
-                            <!-- Second row. -->
+                            <!-- Third row. -->
                             <item>
                                 <layout class="QHBoxLayout">
                                     <property name="alignment">
                                         <enum>Qt::AlignLeft</enum>
                                     </property>
+
                                     <!-- Bookmark URL. -->
                                     <item>
                                         <widget class="QLabel">
                                     </item>
 
                                     <item>
-                                        <widget class="QLineEdit" name="bookmarkUrlLineEdit">
-                                            <property name="sizePolicy">
-                                                <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
-                                                    <horstretch>0</horstretch>
-                                                    <verstretch>0</verstretch>
-                                                </sizepolicy>
-                                            </property>
-
-                                            <property name="minimumSize">
-                                                <size>
-                                                    <width>700</width>
-                                                    <height>0</height>
-                                                </size>
-                                            </property>
-                                        </widget>
+                                        <widget class="QLineEdit" name="bookmarkUrlLineEdit" />
                                     </item>
                                 </layout>
                             </item>
                 </layout>
             </item>
 
-            <!-- Spacer. -->
-            <item>
-                <spacer>
-                    <property name="orientation">
-                        <enum>Qt::Vertical</enum>
-                    </property>
-                </spacer>
-            </item>
-
             <!-- Buttons. -->
             <item>
                 <layout class="QHBoxLayout">
diff --git a/src/uis/AddFolderDialog.ui b/src/uis/AddFolderDialog.ui
new file mode 100644 (file)
index 0000000..752059e
--- /dev/null
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Copyright 2023 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>. -->
+
+<ui version="4.0">
+    <class>AddFolderDialog</class>
+
+    <widget class="QWidget">
+        <layout class="QVBoxLayout">
+            <!-- Dialog body. -->
+            <item>
+                <layout class="QHBoxLayout">
+                    <!-- Favorite icon column. -->
+                    <item>
+                        <layout class="QVBoxLayout">
+                            <property name="rightMargin">
+                                <number>10</number>
+                            </property>
+
+                            <!-- First row, default folder icon. -->
+                            <item>
+                                <widget class="QRadioButton" name="defaultFolderIconRadioButton">
+                                    <property name="text">
+                                        <string>Default folder icon</string>
+                                    </property>
+
+                                    <property name="checked">
+                                        <bool>true</bool>
+                                    </property>
+
+                                    <property name="icon">
+                                        <iconset theme="folder"/>
+                                    </property>
+
+                                    <property name="iconSize">
+                                        <size>
+                                            <height>32</height>
+                                            <width>32</width>
+                                        </size>
+                                    </property>
+                                </widget>
+                            </item>
+
+                            <!-- Second row, current website favorite icon. -->
+                            <item>
+                                <widget class="QRadioButton" name="currentWebsiteFavoriteIconRadioButton">
+                                    <property name="text">
+                                        <string>Current website favorite icon</string>
+                                    </property>
+
+                                    <property name="iconSize">
+                                        <size>
+                                            <height>32</height>
+                                            <width>32</width>
+                                        </size>
+                                    </property>
+                                </widget>
+                            </item>
+
+                            <!-- Third row, custom folder icon. -->
+                            <item>
+                                <widget class="QRadioButton" name="customFolderIconRadioButton">
+                                    <property name="text">
+                                        <string>Custom folder icon</string>
+                                    </property>
+
+                                    <property name="icon">
+                                        <iconset theme="folder-symbolic"/>
+                                    </property>
+
+                                    <property name="iconSize">
+                                        <size>
+                                            <height>32</height>
+                                            <width>32</width>
+                                        </size>
+                                    </property>
+                                </widget>
+                            </item>
+
+                            <!-- Spacer. -->
+                            <item>
+                                <spacer>
+                                    <property name="orientation">
+                                        <enum>Qt::Vertical</enum>
+                                    </property>
+                                </spacer>
+                            </item>
+                        </layout>
+                    </item>
+
+                    <!-- Parent folder and name column. -->
+                    <item>
+                        <layout class="QVBoxLayout">
+                            <!-- First row. -->
+                            <item>
+                                <layout class="QHBoxLayout">
+                                    <property name="alignment">
+                                        <enum>Qt::AlignLeft</enum>
+                                    </property>
+
+                                    <!-- Parent folder.  -->
+                                    <item>
+                                        <widget class="QTreeWidget" name="parentFolderTreeWidget">
+                                            <property name="minimumSize">
+                                                <size>
+                                                    <width>1000</width>
+                                                    <height>700</height>
+                                                </size>
+                                            </property>
+                                        </widget>
+                                    </item>
+                                </layout>
+                            </item>
+
+                            <!-- Second row. -->
+                            <item>
+                                <layout class="QHBoxLayout">
+                                    <property name="alignment">
+                                        <enum>Qt::AlignLeft</enum>
+                                    </property>
+
+                                    <!-- Folder name. -->
+                                    <item>
+                                        <widget class="QLabel">
+                                            <property name="toolTip">
+                                                <string>The folder name.</string>
+                                            </property>
+
+                                            <property name="text">
+                                                <string>Folder name</string>
+                                            </property>
+                                        </widget>
+                                    </item>
+
+                                    <item>
+                                        <widget class="QLineEdit" name="folderNameLineEdit" />
+                                    </item>
+                                </layout>
+                            </item>
+                        </layout>
+                    </item>
+                </layout>
+            </item>
+
+            <!-- Buttons. -->
+            <item>
+                <layout class="QHBoxLayout">
+                    <!-- Browse button. -->
+                    <item>
+                        <widget class="QPushButton" name="browseButton">
+                            <property name="text">
+                                <string>Browse</string>
+                            </property>
+
+                            <property name="icon">
+                                <iconset theme="insert-image" />
+                            </property>
+                        </widget>
+                    </item>
+
+                    <!-- Cancel button - dialog button box. -->
+                    <item>
+                        <widget class="QDialogButtonBox" name="dialogButtonBox">
+                            <property name="standardButtons">
+                                <set>QDialogButtonBox::Cancel</set>
+                            </property>
+                        </widget>
+                    </item>
+                </layout>
+            </item>
+        </layout>
+    </widget>
+</ui>
index b9355bbfb61077c2e9a3d05ff2c61b9d5e8b6f74..cce8b2a12319b13d641d436ccf0aeb6cff079113 100644 (file)
                         </widget>
                     </item>
 
+                    <!-- Add folder button. -->
+                    <item>
+                        <widget class="QPushButton" name="addFolderButton">
+                            <property name="text">
+                                <string>Add folder</string>
+                            </property>
+
+                            <property name="icon">
+                                <iconset theme="folder-add" />
+                            </property>
+                        </widget>
+                    </item>
+
                     <!-- Edit button. -->
                     <item>
                         <widget class="QPushButton" name="editButton">
index 9dab0e8fd00732a9af68dee829f47145b26f4e94..68a84132710cd445f8705d1d7a4bffd8ceff7771 100644 (file)
                                     </property>
                                 </widget>
                             </item>
+
+                            <!-- Spacer. -->
+                            <item>
+                                <spacer>
+                                    <property name="orientation">
+                                        <enum>Qt::Vertical</enum>
+                                    </property>
+                                </spacer>
+                            </item>
                         </layout>
                     </item>
 
-                    <!-- Name and URL column. -->
+                    <!-- Parent folder, name and URL column. -->
                     <item>
                         <layout class="QVBoxLayout">
                             <!-- First row. -->
                                     <property name="alignment">
                                         <enum>Qt::AlignLeft</enum>
                                     </property>
+
+                                    <!-- Parent folder.  -->
+                                    <item>
+                                        <widget class="QTreeWidget" name="parentFolderTreeWidget">
+                                            <property name="minimumSize">
+                                                <size>
+                                                    <width>1000</width>
+                                                    <height>700</height>
+                                                </size>
+                                            </property>
+                                        </widget>
+                                    </item>
+                                </layout>
+                            </item>
+
+                            <!-- Second row. -->
+                            <item>
+                                <layout class="QHBoxLayout">
+                                    <property name="alignment">
+                                        <enum>Qt::AlignLeft</enum>
+                                    </property>
+
                                     <!-- Bookmark name.  -->
                                     <item>
                                         <widget class="QLabel">
                                     <property name="alignment">
                                         <enum>Qt::AlignLeft</enum>
                                     </property>
+
                                     <!-- Bookmark URL. -->
                                     <item>
                                         <widget class="QLabel">
                 </layout>
             </item>
 
-            <!-- Spacer. -->
-            <item>
-                <spacer>
-                    <property name="orientation">
-                        <enum>Qt::Vertical</enum>
-                    </property>
-                </spacer>
-            </item>
-
             <!-- Buttons. -->
             <item>
                 <layout class="QHBoxLayout">
diff --git a/src/uis/EditFolderDialog.ui b/src/uis/EditFolderDialog.ui
new file mode 100644 (file)
index 0000000..545ed3c
--- /dev/null
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Copyright 2023 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>. -->
+
+<ui version="4.0">
+    <class>EditFolderDialog</class>
+
+    <widget class="QWidget">
+        <layout class="QVBoxLayout">
+            <!-- Dialog body. -->
+            <item>
+                <layout class="QHBoxLayout">
+                    <!-- Favorite icon column. -->
+                    <item>
+                        <layout class="QVBoxLayout">
+                            <property name="rightMargin">
+                                <number>10</number>
+                            </property>
+
+                            <!-- First row, current folder icon. -->
+                            <item>
+                                <widget class="QRadioButton" name="currentFolderIconRadioButton">
+                                    <property name="text">
+                                        <string>Default folder icon</string>
+                                    </property>
+
+                                    <property name="checked">
+                                        <bool>true</bool>
+                                    </property>
+
+                                    <property name="iconSize">
+                                        <size>
+                                            <height>32</height>
+                                            <width>32</width>
+                                        </size>
+                                    </property>
+                                </widget>
+                            </item>
+
+                            <!-- Second row, current website favorite icon. -->
+                            <item>
+                                <widget class="QRadioButton" name="currentWebsiteFavoriteIconRadioButton">
+                                    <property name="text">
+                                        <string>Current website favorite icon</string>
+                                    </property>
+
+                                    <property name="iconSize">
+                                        <size>
+                                            <height>32</height>
+                                            <width>32</width>
+                                        </size>
+                                    </property>
+                                </widget>
+                            </item>
+
+                            <!-- Third row, custom folder icon. -->
+                            <item>
+                                <widget class="QRadioButton" name="customFolderIconRadioButton">
+                                    <property name="text">
+                                        <string>Custom folder icon</string>
+                                    </property>
+
+                                    <property name="icon">
+                                        <iconset theme="folder-symbolic"/>
+                                    </property>
+
+                                    <property name="iconSize">
+                                        <size>
+                                            <height>32</height>
+                                            <width>32</width>
+                                        </size>
+                                    </property>
+                                </widget>
+                            </item>
+
+                            <!-- Spacer. -->
+                            <item>
+                                <spacer>
+                                    <property name="orientation">
+                                        <enum>Qt::Vertical</enum>
+                                    </property>
+                                </spacer>
+                            </item>
+                        </layout>
+                    </item>
+
+                    <!-- Parent folder and name column. -->
+                    <item>
+                        <layout class="QVBoxLayout">
+                            <!-- First row. -->
+                            <item>
+                                <layout class="QHBoxLayout">
+                                    <property name="alignment">
+                                        <enum>Qt::AlignLeft</enum>
+                                    </property>
+
+                                    <!-- Parent folder.  -->
+                                    <item>
+                                        <widget class="QTreeWidget" name="parentFolderTreeWidget">
+                                            <property name="minimumSize">
+                                                <size>
+                                                    <width>1000</width>
+                                                    <height>700</height>
+                                                </size>
+                                            </property>
+                                        </widget>
+                                    </item>
+                                </layout>
+                            </item>
+
+                            <!-- Second row. -->
+                            <item>
+                                <layout class="QHBoxLayout">
+                                    <property name="alignment">
+                                        <enum>Qt::AlignLeft</enum>
+                                    </property>
+
+                                    <!-- Folder name. -->
+                                    <item>
+                                        <widget class="QLabel">
+                                            <property name="toolTip">
+                                                <string>The folder name.</string>
+                                            </property>
+
+                                            <property name="text">
+                                                <string>Folder name</string>
+                                            </property>
+                                        </widget>
+                                    </item>
+
+                                    <item>
+                                        <widget class="QLineEdit" name="folderNameLineEdit" />
+                                    </item>
+                                </layout>
+                            </item>
+                        </layout>
+                    </item>
+                </layout>
+            </item>
+
+            <!-- Buttons. -->
+            <item>
+                <layout class="QHBoxLayout">
+                    <!-- Browse button. -->
+                    <item>
+                        <widget class="QPushButton" name="browseButton">
+                            <property name="text">
+                                <string>Browse</string>
+                            </property>
+
+                            <property name="icon">
+                                <iconset theme="insert-image" />
+                            </property>
+                        </widget>
+                    </item>
+
+                    <!-- Cancel button - dialog button box. -->
+                    <item>
+                        <widget class="QDialogButtonBox" name="dialogButtonBox">
+                            <property name="standardButtons">
+                                <set>QDialogButtonBox::Save | QDialogButtonBox::Cancel</set>
+                            </property>
+                        </widget>
+                    </item>
+                </layout>
+            </item>
+        </layout>
+    </widget>
+</ui>
index 1fa07a56498c74bf94d8a69c02bc83ec815ee26e..6471b08b4bf0f6ffba130efce540e9215421017f 100644 (file)
@@ -36,52 +36,105 @@ void DraggableTreeView::dropEvent(QDropEvent *dropEvent)
     // Get the list of currently selected items that are moving.
     QList<QModelIndex> indexesMovingList = selectedIndexes();
 
-    // Create a list of database IDs that are moving.
-    QList<int> *databaseIdsMovingListPointer = new QList<int>;
+    // Create a list of selected database IDs.
+    QList<int> *selectedDatabaseIdsListPointer = new QList<int>;
 
-    // Populate the list of moving database IDs.
+    // Populate the list of selected 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());
+        if (modelIndex.column() == BookmarksDialog::NAME_COLUMN)
+            selectedDatabaseIdsListPointer->append(modelIndex.siblingAtColumn(BookmarksDialog::DATABASE_ID_COLUMN).data().toInt());
     }
 
+    // Get a list of root selected database IDs.
+    QList<int> *rootSelectedDatabaseIdsListPointer = getRootSelectedDatabaseIds(selectedDatabaseIdsListPointer);
+
     // 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();
+    // Create a previous parent folder ID standard C++ list (which can be sorted and from which duplicates can be removed).
+    std::list<double> previousParentFolderIdList;
 
     // Process the move according to the drop position.
     switch (dropPosition)
     {
         case QAbstractItemView::OnItem:
-            // TODO  Implement for moving into a folder.
+        {
+            // Get the new parent folder ID.
+            double newParentFolderId = dropModelIndex.siblingAtColumn(BookmarksDialog::FOLDER_ID_COLUMN).data().toDouble();
+
+            // Get a list of all the items in the target folder except those selected.
+            QList<BookmarkStruct> *itemsInFolderExceptSelectedListPointer = BookmarksDatabase::getBookmarksInFolderExcept(newParentFolderId, selectedDatabaseIdsListPointer);
+
+            // Initialize a new display order tracker.
+            int newDisplayOrder = 0;
+
+            // Move all the items to the top of the target folder.
+            for (const int databaseId : *rootSelectedDatabaseIdsListPointer)
+            {
+                // Get the item's current parent folder ID.
+                double currentParentFolderId = BookmarksDatabase::getParentFolderId(databaseId);
+
+                // Add the parent folder ID to the list of previous parent folders if the item is from a different folder.
+                if (currentParentFolderId != newParentFolderId)
+                    previousParentFolderIdList.push_back(currentParentFolderId);
+
+                // Update the parent folder and display order for each bookmark.
+                BookmarksDatabase::updateParentFolderAndDisplayOrder(databaseId, newParentFolderId, newDisplayOrder);
+
+                // Increment the new display order.
+                ++newDisplayOrder;
+            }
+
+            // Update the display order of the existing items in the folder.
+            for (const BookmarkStruct &bookmarkStruct : *itemsInFolderExceptSelectedListPointer)
+            {
+                // Set the bookmark's display order if it has changed.
+                if (bookmarkStruct.displayOrder != newDisplayOrder)
+                    BookmarksDatabase::updateDisplayOrder(bookmarkStruct.databaseId, newDisplayOrder);
+
+                // Increment the new display order.
+                ++newDisplayOrder;
+            }
             break;
+        }
 
         case QAbstractItemView::AboveItem:
         {
-            // Get a list of all the bookmarks except those selected.
-            QList<BookmarkStruct> *bookmarksExceptSelectedListPointer = BookmarksDatabase::getBookmarksExcept(databaseIdsMovingListPointer);
+            // Get the drop display order.
+            int dropDisplayOrder = dropModelIndex.siblingAtColumn(BookmarksDialog::DISPLAY_ORDER_COLUMN).data().toInt();
+
+            // Get the drop parent folder ID.
+            double dropParentFolderId = dropModelIndex.parent().siblingAtColumn(BookmarksDialog::FOLDER_ID_COLUMN).data().toInt();
+
+            // Get a list of all the items in the target folder except those selected.
+            QList<BookmarkStruct> *itemsInFolderExceptSelectedListPointer = BookmarksDatabase::getBookmarksInFolderExcept(dropParentFolderId, selectedDatabaseIdsListPointer);
 
             // Initialize a new display order tracker.
             int newDisplayOrder = 0;
 
-            // Move the bookmarks.
-            for (const BookmarkStruct &bookmarkStruct : *bookmarksExceptSelectedListPointer)
+            // Process all the items in the target folder, moving in the new ones.
+            for (const BookmarkStruct &bookmarkStruct : *itemsInFolderExceptSelectedListPointer)
             {
                 // 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)
+                    for (const int databaseId : *rootSelectedDatabaseIdsListPointer)
                     {
-                        // Update the display order for each bookmark.
-                        BookmarksDatabase::updateDisplayOrder(databaseId, newDisplayOrder);
+                        // Get the item's current parent folder ID.
+                        double currentParentFolderId = BookmarksDatabase::getParentFolderId(databaseId);
+
+                        // Add the parent folder ID to the list of previous parent folders if the item is from a different folder.
+                        if (currentParentFolderId != dropParentFolderId)
+                            previousParentFolderIdList.push_back(currentParentFolderId);
+
+                        // Update the parent folder and display order for each bookmark.
+                        BookmarksDatabase::updateParentFolderAndDisplayOrder(databaseId, dropParentFolderId, newDisplayOrder);
 
                         // Increment the new display order.
                         ++newDisplayOrder;
@@ -90,29 +143,34 @@ void DraggableTreeView::dropEvent(QDropEvent *dropEvent)
 
                 // Set the bookmark's display order if it has changed.
                 if (bookmarkStruct.displayOrder != newDisplayOrder)
-                    BookmarksDatabase::updateDisplayOrder(bookmarkStruct.id, newDisplayOrder);
+                    BookmarksDatabase::updateDisplayOrder(bookmarkStruct.databaseId, newDisplayOrder);
 
                 // Increment the new display order.
                 ++newDisplayOrder;
             }
-
             break;
         }
 
         case QAbstractItemView::BelowItem:
         {
-            // Get a list of all the bookmarks except those selected.
-            QList<BookmarkStruct> *bookmarksExceptSelectedListPointer = BookmarksDatabase::getBookmarksExcept(databaseIdsMovingListPointer);
+            // Get the drop display order.
+            int dropDisplayOrder = dropModelIndex.siblingAtColumn(BookmarksDialog::DISPLAY_ORDER_COLUMN).data().toInt();
+
+            // Get the drop parent folder ID.
+            double dropParentFolderId = dropModelIndex.parent().siblingAtColumn(BookmarksDialog::FOLDER_ID_COLUMN).data().toInt();
+
+            // Get a list of all the items in the target folder except those selected.
+            QList<BookmarkStruct> *itemsInFolderExceptSelectedListPointer = BookmarksDatabase::getBookmarksInFolderExcept(dropParentFolderId, selectedDatabaseIdsListPointer);
 
             // Initialize a new display order tracker.
             int newDisplayOrder = 0;
 
-            // Move the bookmarks.
-            for (const BookmarkStruct &bookmarkStruct : *bookmarksExceptSelectedListPointer)
+            // Process all the items in the target folder, moving in the new ones.
+            for (const BookmarkStruct &bookmarkStruct : *itemsInFolderExceptSelectedListPointer)
             {
                 // Set the bookmark's display order if it has changed.
                 if (bookmarkStruct.displayOrder != newDisplayOrder)
-                    BookmarksDatabase::updateDisplayOrder(bookmarkStruct.id, newDisplayOrder);
+                    BookmarksDatabase::updateDisplayOrder(bookmarkStruct.databaseId, newDisplayOrder);
 
                 // Increment the new display order.
                 ++newDisplayOrder;
@@ -121,51 +179,106 @@ void DraggableTreeView::dropEvent(QDropEvent *dropEvent)
                 if (bookmarkStruct.displayOrder == dropDisplayOrder)
                 {
                     // Add all of the bookmarks being moved to this point.
-                    for (const int databaseId : *databaseIdsMovingListPointer)
+                    for (const int databaseId : *rootSelectedDatabaseIdsListPointer)
                     {
-                        // Update the display order for each bookmark.
-                        BookmarksDatabase::updateDisplayOrder(databaseId, newDisplayOrder);
+                        // Get the item's current parent folder ID.
+                        double currentParentFolderId = BookmarksDatabase::getParentFolderId(databaseId);
+
+                        // Add the parent folder ID to the list of previous parent folders if the item is from a different folder.
+                        if (currentParentFolderId != dropParentFolderId)
+                            previousParentFolderIdList.push_back(currentParentFolderId);
+
+                        // Update the parent folder and display order for each bookmark.
+                        BookmarksDatabase::updateParentFolderAndDisplayOrder(databaseId, dropParentFolderId, newDisplayOrder);
 
                         // Increment the new display order.
                         ++newDisplayOrder;
                     }
                 }
             }
-
             break;
         }
 
         case QAbstractItemView::OnViewport:
+        {
+            // Get the drop parent folder ID.
+            double dropParentFolderId = 0;
 
-            // Get a list of all the bookmarks except those selected.
-            QList<BookmarkStruct> *bookmarksExceptSelectedListPointer = BookmarksDatabase::getBookmarksExcept(databaseIdsMovingListPointer);
+            // Get a list of all the items in the root folder except those selected.
+            QList<BookmarkStruct> *itemsInFolderExceptSelectedListPointer = BookmarksDatabase::getBookmarksInFolderExcept(dropParentFolderId, selectedDatabaseIdsListPointer);
 
             // Initialize a new display order tracker.
             int newDisplayOrder = 0;
 
-            // Move the bookmarks.
-            for (const BookmarkStruct &bookmarkStruct : *bookmarksExceptSelectedListPointer)
+            // Update the display order of the existing items in the folder.
+            for (const BookmarkStruct &bookmarkStruct : *itemsInFolderExceptSelectedListPointer)
             {
                 // Set the bookmark's display order if it has changed.
                 if (bookmarkStruct.displayOrder != newDisplayOrder)
-                    BookmarksDatabase::updateDisplayOrder(bookmarkStruct.id, newDisplayOrder);
+                    BookmarksDatabase::updateDisplayOrder(bookmarkStruct.databaseId, 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)
+            for (const int databaseId : *rootSelectedDatabaseIdsListPointer)
             {
-                // Update the display order for each bookmark.
-                BookmarksDatabase::updateDisplayOrder(databaseId, newDisplayOrder);
+                // Get the item's current parent folder ID.
+                double currentParentFolderId = BookmarksDatabase::getParentFolderId(databaseId);
+
+                // Add the parent folder ID to the list of previous parent folders if the item is from a different folder.
+                if (currentParentFolderId != dropParentFolderId)
+                    previousParentFolderIdList.push_back(currentParentFolderId);
+
+                // Update the parent folder and display order for each bookmark.
+                BookmarksDatabase::updateParentFolderAndDisplayOrder(databaseId, dropParentFolderId, newDisplayOrder);
 
                 // Increment the new display order.
                 ++newDisplayOrder;
             }
             break;
+        }
     }
 
+    // Sort the previous parent folder ID list.
+    previousParentFolderIdList.sort();
+
+    // Remove duplicates from the parent folder ID list.
+    previousParentFolderIdList.unique();
+
+    // Update the folder contents display order for each previous parent folder.
+    for (const double parentFolderId : previousParentFolderIdList)
+        BookmarksDatabase::updateFolderContentsDisplayOrder(parentFolderId);
+
     // Emit the bookmarks moved signal.
     emit bookmarksMoved();
 }
+
+QList<int>* DraggableTreeView::getRootSelectedDatabaseIds(QList<int> *selectedDatabaseIdsPointer) const
+{
+    // Create a list of the database IDs of the contents of each selected folder.
+    QList<int> selectedFoldersContentsDatabaseIds;
+
+    // Populate the list of the database IDs of the contents of each selected folder.
+    for (const int databaseId : *selectedDatabaseIdsPointer)
+    {
+        // If this is not the root item and is a folder, get the database IDs of the contents.
+        if ((databaseId != -1) && BookmarksDatabase::isFolder(databaseId))
+            selectedFoldersContentsDatabaseIds.append(*BookmarksDatabase::getFolderContentsDatabaseIds(BookmarksDatabase::getFolderId(databaseId)));
+    }
+
+    // Create a root selected database IDs list.
+    QList<int>* rootSelectedDatabaseIdsListPointer = new QList<int>;
+
+    // Populate the root selected database IDs list.
+    for (const int databaseId : *selectedDatabaseIdsPointer)
+    {
+        // Add the database ID to the root selected database IDs list if it isn't the root item and it isn't contained in the selected folder contents database IDs list.
+        if ((databaseId != -1) && !selectedFoldersContentsDatabaseIds.contains(databaseId))
+            rootSelectedDatabaseIdsListPointer->append(databaseId);
+    }
+
+    // Return the root selected database IDs list.
+    return rootSelectedDatabaseIdsListPointer;
+}
index f520ed360d94c0f9255a739199026827bb8b0465..983ff1c2924c5875dac86a72c0a50f996359efe3 100644 (file)
@@ -38,5 +38,8 @@ signals:
 
 protected:
     void dropEvent(QDropEvent *event) override;
+
+private:
+    QList<int>* getRootSelectedDatabaseIds(QList<int> *selectedDatabaseIdsPointer) const;
 };
 #endif
index 0fdfe26375043dd813985d38200ebf64e36c3570..f85726dd9e080cdbf4f95b8b69a0aef7a8416e74 100644 (file)
 #include "ui_SettingsSpellCheck.h"
 #include "databases/BookmarksDatabase.h"
 #include "dialogs/AddBookmarkDialog.h"
+#include "dialogs/AddFolderDialog.h"
 #include "dialogs/BookmarksDialog.h"
 #include "dialogs/CookiesDialog.h"
 #include "dialogs/DomainSettingsDialog.h"
 #include "dialogs/EditBookmarkDialog.h"
+#include "dialogs/EditFolderDialog.h"
 #include "helpers/SearchEngineHelper.h"
 #include "helpers/UserAgentHelper.h"
 #include "structs/BookmarkStruct.h"
@@ -45,6 +47,7 @@
 #include <QInputDialog>
 #include <QNetworkCookie>
 #include <QMenuBar>
+#include <QMessageBox>
 #include <QShortcut>
 #include <QStatusBar>
 #include <QWebEngineFindTextResult>
@@ -110,6 +113,7 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     searchEngineBingActionPointer = actionCollectionPointer->addAction(QLatin1String("search_engine_bing"));
     searchEngineYahooActionPointer = actionCollectionPointer->addAction(QLatin1String("search_engine_yahoo"));
     searchEngineCustomActionPointer = actionCollectionPointer->addAction(QLatin1String("search_engine_custom"));
+    QAction *addFolderPointer = actionCollectionPointer->addAction(QLatin1String("add_folder"));
     viewBookmarksToolBarActionPointer = actionCollectionPointer->addAction(QLatin1String("view_bookmarks_toolbar"));
     QAction *domainSettingsActionPointer = actionCollectionPointer->addAction(QLatin1String("domain_settings"));
     cookiesActionPointer = actionCollectionPointer->addAction(QLatin1String("cookies"));
@@ -189,6 +193,7 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     searchEngineGoogleActionPointer->setText(i18nc("Search engine", "Google"));
     searchEngineBingActionPointer->setText(i18nc("Search engine", "Bing"));
     searchEngineYahooActionPointer->setText(i18nc("Search engine", "Yahoo"));
+    addFolderPointer->setText(i18nc("Add folder", "Add Folder"));
     viewBookmarksToolBarActionPointer->setText(i18nc("View bookmarks toolbar", "View Bookmarks Toolbar"));
     domainSettingsActionPointer->setText(i18nc("Domain Settings action", "Domain Settings"));
     cookiesActionPointer->setText(i18nc("The Cookies action, which also displays the number of cookies", "Cookies - %1", 0));
@@ -223,6 +228,7 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     searchEngineYahooActionPointer->setIcon(QIcon::fromTheme(QLatin1String("im-yahoo"), QIcon::fromTheme(QLatin1String("edit-find"))));
     searchEngineCustomActionPointer->setIcon(QIcon::fromTheme(QLatin1String("edit-find")));
     zoomFactorActionPointer->setIcon(QIcon::fromTheme(QLatin1String("zoom-fit-best")));
+    addFolderPointer->setIcon(QIcon::fromTheme(QLatin1String("folder-add")));
     viewBookmarksToolBarActionPointer->setIcon(QIcon::fromTheme(QLatin1String("bookmarks")));
     domainSettingsActionPointer->setIcon(QIcon::fromTheme(QLatin1String("settings-configure"), QIcon::fromTheme(QLatin1String("preferences-desktop"))));
     cookiesActionPointer->setIcon(QIcon::fromTheme(QLatin1String("preferences-web-browser-cookies"), QIcon::fromTheme(QLatin1String("appointment-new"))));
@@ -259,6 +265,7 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     QKeySequence ctrlShiftYKeySequence = QKeySequence(i18nc("The Yahoo search engine key sequence.", "Ctrl+Shift+Y"));
     QKeySequence ctrlShiftCKeySequence = QKeySequence(i18nc("The custom search engine key sequence.", "Ctrl+Shift+C"));
     QKeySequence ctrlAltShiftBKeySequence = QKeySequence(i18nc("The edit bookmarks key sequence.", "Ctrl+Alt+Shift+B"));
+    QKeySequence altFKeySequence = QKeySequence(i18nc("The add folder key sequence.", "Alt+F"));
     QKeySequence ctrlAltBKeySequence = QKeySequence(i18nc("The view bookmarks toolbar key sequence.", "Ctrl+Alt+B"));
     QKeySequence ctrlShiftDKeySequence = QKeySequence(i18nc("The domain settings key sequence.", "Ctrl+Shift+D"));
     QKeySequence ctrlSemicolonKeySequence = QKeySequence(i18nc("The cookies dialog key sequence.", "Ctrl+;"));
@@ -293,6 +300,7 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     actionCollectionPointer->setDefaultShortcut(searchEngineYahooActionPointer, ctrlShiftYKeySequence);
     actionCollectionPointer->setDefaultShortcut(searchEngineCustomActionPointer, ctrlShiftCKeySequence);
     actionCollectionPointer->setDefaultShortcut(editBookmarksActionPointer, ctrlAltShiftBKeySequence);
+    actionCollectionPointer->setDefaultShortcut(addFolderPointer, altFKeySequence);
     actionCollectionPointer->setDefaultShortcut(viewBookmarksToolBarActionPointer, ctrlAltBKeySequence);
     actionCollectionPointer->setDefaultShortcut(domainSettingsActionPointer, ctrlShiftDKeySequence);
     actionCollectionPointer->setDefaultShortcut(cookiesActionPointer, ctrlSemicolonKeySequence);
@@ -305,6 +313,7 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     connect(viewSourceActionPointer, SIGNAL(triggered()), this, SLOT(toggleViewSource()));
     connect(viewSourceInNewTabActionPointer, SIGNAL(triggered()), this, SLOT(toggleViewSourceInNewTab()));
     connect(zoomFactorActionPointer, SIGNAL(triggered()), this, SLOT(getZoomFactorFromUser()));
+    connect(addFolderPointer, SIGNAL(triggered()), this, SLOT(showAddFolderDialog()));
     connect(viewBookmarksToolBarActionPointer, SIGNAL(triggered()), this, SLOT(toggleViewBookmarksToolBar()));
     connect(cookiesActionPointer, SIGNAL(triggered()), this, SLOT(showCookiesDialog()));
     connect(domainSettingsActionPointer, SIGNAL(triggered()), this, SLOT(showDomainSettingsDialog()));
@@ -509,8 +518,10 @@ BrowserWindow::BrowserWindow(bool firstWindow, QString *initialUrlStringPointer)
     bookmarksMenuPointer->addSeparator();
 
     // Initialize the current bookmarks lists.
-    bookmarksMenuCurrentActionList = QList<QAction*>();
-    bookmarksToolBarCurrentActionList = QList<QAction*>();
+    bookmarksMenuActionList = QList<QPair<QMenu *, QAction *> *>();
+    bookmarksMenuSubmenuList = QList<QPair<QMenu *, QMenu *> *>();
+    bookmarksToolBarActionList = QList<QAction*>();
+    bookmarksToolBarSubfolderActionList = QList<QPair<QMenu *, QAction * > *>();
 
     // Set the bookmarks toolbar context menu policy.
     bookmarksToolBarPointer->setContextMenuPolicy(Qt::CustomContextMenu);
@@ -606,7 +617,7 @@ void BrowserWindow::decrementZoom()
 void BrowserWindow::editBookmarks() const
 {
     // Instantiate an edit bookmarks dialog.
-    BookmarksDialog *bookmarksDialogPointer = new BookmarksDialog(tabWidgetPointer->getCurrentTabFavoritIcon());
+    BookmarksDialog *bookmarksDialogPointer = new BookmarksDialog(tabWidgetPointer->getCurrentTabTitle(), tabWidgetPointer->getCurrentTabUrl(), tabWidgetPointer->getCurrentTabFavoritIcon());
 
     // Update the displayed bookmarks when edited.
     connect(bookmarksDialogPointer, SIGNAL(bookmarkUpdated()), this, SLOT(populateBookmarks()));
@@ -797,71 +808,366 @@ void BrowserWindow::newWindow() const
 void BrowserWindow::populateBookmarks()
 {
     // Remove all the current menu bookmarks.
-    for (QAction *bookmarkAction : bookmarksMenuCurrentActionList)
+    for (QPair<QMenu *, QAction *> *bookmarkPairPointer : bookmarksMenuActionList)
     {
         // Remove the bookmark.
-        bookmarksMenuPointer->removeAction(bookmarkAction);
+        bookmarkPairPointer->first->removeAction(bookmarkPairPointer->second);
+    }
+
+    // Remove all the current menu subfolders.
+    for (QPair<QMenu *, QMenu *> *submenuPairPointer : bookmarksMenuSubmenuList)
+    {
+        // Remove the submenu from the parent menu.
+        submenuPairPointer->first->removeAction(submenuPairPointer->second->menuAction());
+    }
+
+    // Remove all the current toolbar subfolders.
+    for (QPair<QMenu *, QAction *> *subfolderPairPointer : bookmarksToolBarSubfolderActionList)
+    {
+        // Remove the action from the subfolder.
+        subfolderPairPointer->first->removeAction(subfolderPairPointer->second);
     }
 
     // Remove all the current toolbar bookmarks.
-    for (QAction *bookmarkAction : bookmarksToolBarCurrentActionList)
+    for (QAction *bookmarkAction : bookmarksToolBarActionList)
     {
         // Remove the bookmark.
         bookmarksToolBarPointer->removeAction(bookmarkAction);
     }
 
     // Clear the current bookmark lists.
-    bookmarksMenuCurrentActionList.clear();
-    bookmarksToolBarCurrentActionList.clear();
+    bookmarksMenuActionList.clear();
+    bookmarksMenuSubmenuList.clear();
+    bookmarksToolBarActionList.clear();
+    bookmarksToolBarSubfolderActionList.clear();
 
-    // Get a list of the bookmarks.
-    std::list<BookmarkStruct> *bookmarkListPointer = BookmarksDatabase::getBookmarks();
+    // Populate the bookmarks subfolders, beginning with the root folder (`0`);
+    populateBookmarksMenuSubfolders(0, bookmarksMenuPointer);
 
-    // Populate the bookmarks menu.
-    for (BookmarkStruct bookmarkStruct : *bookmarkListPointer)
+    // Populate the bookmarks toolbar.
+    populateBookmarksToolBar();
+
+    // Get a handle for the bookmark toolbar layout.
+    QLayout *bookmarksToolBarLayoutPointer = bookmarksToolBarPointer->layout();
+
+    // Get the count of the bookmarks.
+    int bookmarkCount = bookmarksToolBarLayoutPointer->count();
+
+    // Set the layout of each bookmark to be left aligned.
+    for(int i = 0; i < bookmarkCount; ++i)
+        bookmarksToolBarLayoutPointer->itemAt(i)->setAlignment(Qt::AlignLeft);
+}
+
+void BrowserWindow::populateBookmarksMenuSubfolders(const double folderId, QMenu *menuPointer)
+{
+    // Get the folder contents.
+    QList<BookmarkStruct> *folderContentsListPointer = BookmarksDatabase::getFolderContents(folderId);
+
+    // Populate the bookmarks menu and toolbar.
+    for (BookmarkStruct bookmarkStruct : *folderContentsListPointer)
     {
-        // Get the bookmark URL.
-        QString bookmarkUrl = bookmarkStruct.bookmarkUrl;
+        // Process the item according to the type.
+        if (bookmarkStruct.isFolder)  // This item is a folder.
+        {
+            // Add a submenu to the menu.
+            QMenu *submenuPointer = menuPointer->addMenu(bookmarkStruct.favoriteIcon, bookmarkStruct.name);
+
+            // Add the submenu to the beginning of the list of menus to be deleted on repopulate.
+            bookmarksMenuSubmenuList.prepend(new QPair<QMenu *, QMenu *>(menuPointer, submenuPointer));
+
+            // Populate any subfolders.
+            populateBookmarksMenuSubfolders(bookmarkStruct.folderId, submenuPointer);
+        }
+        else  // This item is a bookmark.
+        {
+            // Add the bookmark to the menu.
+            QAction *menuBookmarkActionPointer = menuPointer->addAction(bookmarkStruct.favoriteIcon, bookmarkStruct.name, [=]
+                {
+                    // Remove the focus from the URL line edit.
+                    urlLineEditPointer->clearFocus();
+
+                    // Load the URL.
+                    tabWidgetPointer->loadUrlFromLineEdit(bookmarkStruct.url);
+                }
+            );
+
+            // Add the actions to the beginning of the list of bookmarks to be deleted on repopulate.
+            bookmarksMenuActionList.prepend(new QPair<QMenu *, QAction *>(menuPointer, menuBookmarkActionPointer));
+        }
+    }
+}
+
+void BrowserWindow::populateBookmarksToolBar()
+{
+    // Get the root folder contents (which has a folder ID of `0`).
+    QList<BookmarkStruct> *folderContentsListPointer = BookmarksDatabase::getFolderContents(0);
+
+    // Populate the bookmarks toolbar.
+    for (BookmarkStruct bookmarkStruct : *folderContentsListPointer)
+    {
+        // Process the item according to the type.
+        if (bookmarkStruct.isFolder)  // This item is a folder.
+        {
+            // Add the subfolder action.
+            QAction *toolBarSubfolderActionPointer = bookmarksToolBarPointer->addAction(bookmarkStruct.favoriteIcon, bookmarkStruct.name);
+
+            // Add the bookmark database ID to the toolbar action.
+            toolBarSubfolderActionPointer->setData(bookmarkStruct.databaseId);
+
+            // Add the action to the beginning of the list of actions to be deleted on repopulate.
+            bookmarksToolBarActionList.prepend(toolBarSubfolderActionPointer);
+
+            // Create a subfolder menu.
+            QMenu *subfolderMenuPointer = new QMenu();
+
+            // Add the menu to the action.
+            toolBarSubfolderActionPointer->setMenu(subfolderMenuPointer);
+
+            // Add the submenu to the toolbar menu list.
+            bookmarksToolBarMenuList.append(new QPair<QMenu *, const double>(subfolderMenuPointer, bookmarkStruct.folderId));
+
+            // Set the popup mode for the menu.
+            dynamic_cast<QToolButton *>(bookmarksToolBarPointer->widgetForAction(toolBarSubfolderActionPointer))->setPopupMode(QToolButton::InstantPopup);
+
+            // Populate the subfolder.
+            populateBookmarksToolBarSubfolders(bookmarkStruct.folderId, subfolderMenuPointer);
+        }
+        else  // This item is a bookmark.
+        {
+            // Add the bookmark to the toolbar.
+            QAction *toolBarBookmarkActionPointer = bookmarksToolBarPointer->addAction(bookmarkStruct.favoriteIcon, bookmarkStruct.name, [=]
+                {
+                    // Remove the focus from the URL line edit.
+                    urlLineEditPointer->clearFocus();
+
+                    // Load the URL.
+                    tabWidgetPointer->loadUrlFromLineEdit(bookmarkStruct.url);
+                }
+            );
+
+            // Add the bookmark database ID to the toolbar action.
+            toolBarBookmarkActionPointer->setData(bookmarkStruct.databaseId);
+
+            // Add the actions to the beginning of the current bookmarks lists.
+            bookmarksToolBarActionList.prepend(toolBarBookmarkActionPointer);
+        }
+    }
+
+    // Add the extra items to the toolbar folder menus.  The first item in the pair is the menu pointer.  The second is the folder ID.
+    for (QPair<QMenu *, const double> *menuAndFolderIdPairPointer : bookmarksToolBarMenuList)
+    {
+        // Add a separator.
+        menuAndFolderIdPairPointer->first->addSeparator();
+
+        // Add the open folder in new tabs action to the menu.
+        menuAndFolderIdPairPointer->first->addAction(QIcon::fromTheme(QLatin1String("tab-new")), i18nc("The open folder in new tabs action", "Open Folder in New Tabs"), [=]
+            {
+                // Get all the folder URLs.
+                QList<QString> *folderUrlsListPointer = BookmarksDatabase::getAllFolderUrls(menuAndFolderIdPairPointer->second);
+
+                // Open the URLs in new tabs.  `true` removes the URL line edit focus, `false` does not load a background tab.
+                for (QString url : *folderUrlsListPointer)
+                    tabWidgetPointer->addTab(true, false, url);
+            }
+        );
+
+        // Add the open folder in background tabs action to the menu.
+        menuAndFolderIdPairPointer->first->addAction(QIcon::fromTheme(QLatin1String("tab-new")), i18nc("The open folder in background tabs action", "Open Folder in Background Tabs"), [=]
+            {
+                // Get all the folder URLs.
+                QList<QString> *folderUrlsListPointer = BookmarksDatabase::getAllFolderUrls(menuAndFolderIdPairPointer->second);
+
+                // Open the URLs in new tabs.  `true` removes the URL line edit focus, `true` loads a background tab.
+                for (QString url : *folderUrlsListPointer)
+                    tabWidgetPointer->addTab(true, true, url);
+            }
+        );
+
+        // Add the open folder in new window action to the menu.
+        menuAndFolderIdPairPointer->first->addAction(QIcon::fromTheme(QLatin1String("window-new")), i18nc("The open folder in new window action", "Open Folder in New Window"), [=]
+            {
+                // Get all the folder URLs.
+                QList<QString> *folderUrlsListPointer = BookmarksDatabase::getAllFolderUrls(menuAndFolderIdPairPointer->second);
+
+                // Create a new browser window.
+                BrowserWindow *browserWindowPointer = new BrowserWindow(false, &folderUrlsListPointer->first());
+
+                // Get a count of the folder URLs.
+                const int folderUrls = folderUrlsListPointer->count();
 
-        // Add the bookmark to the menu.
-        QAction *menuBookmarkActionPointer = bookmarksMenuPointer->addAction(bookmarkStruct.favoriteIcon, bookmarkStruct.bookmarkName, [=]
+                // Load all the other URLs.  `true` removes the URL line edit focus, `true` loads a background tab.
+                for (int i = 1; i < folderUrls; ++i)
+                    browserWindowPointer->tabWidgetPointer->addTab(true, true, folderUrlsListPointer->value(i));
+
+                // Show the new browser window.
+                browserWindowPointer->show();
+            }
+        );
+
+        // Add a separator.
+        menuAndFolderIdPairPointer->first->addSeparator();
+
+        // Add the add bookmark action to the menu.
+        menuAndFolderIdPairPointer->first->addAction(QIcon::fromTheme(QLatin1String("bookmark-new")), i18nc("The add bookmark action", "Add Bookmark"), [=]
             {
-                // Remove the focus from the URL line edit.
-                urlLineEditPointer->clearFocus();
+                // Instantiate an add bookmark dialog.
+                AddBookmarkDialog *addBookmarkDialogPointer = new AddBookmarkDialog(tabWidgetPointer->getCurrentTabTitle(), tabWidgetPointer->getCurrentTabUrl(),
+                                                                                    tabWidgetPointer->getCurrentTabFavoritIcon(), menuAndFolderIdPairPointer->second);
+
+                // Update the displayed bookmarks when a new one is added.
+                connect(addBookmarkDialogPointer, SIGNAL(bookmarkAdded()), this, SLOT(populateBookmarks()));
 
-                // Load the URL.
-                tabWidgetPointer->loadUrlFromLineEdit(bookmarkUrl);
+                // Show the dialog.
+                addBookmarkDialogPointer->show();
             }
         );
 
-        // Add the bookmark to the toolbar.
-        QAction *toolBarBookmarkActionPointer = bookmarksToolBarPointer->addAction(bookmarkStruct.favoriteIcon, bookmarkStruct.bookmarkName, [=]
+        // Add the add folder action to the menu.
+        menuAndFolderIdPairPointer->first->addAction(QIcon::fromTheme(QLatin1String("folder-add")), i18nc("The add folder action", "Add Folder"), [=]
             {
-                // Remove the focus from the URL line edit.
-                urlLineEditPointer->clearFocus();
+                // Instantiate an add folder dialog.
+                AddFolderDialog *addFolderDialogPointer = new AddFolderDialog(tabWidgetPointer->getCurrentTabFavoritIcon(), menuAndFolderIdPairPointer->second);
+
+                // Update the displayed bookmarks when a folder is added.
+                connect(addFolderDialogPointer, SIGNAL(folderAdded()), this, SLOT(populateBookmarks()));
+
+                // Show the dialog.
+                addFolderDialogPointer->show();
+            }
+        );
+
+        // Add a separator.
+        menuAndFolderIdPairPointer->first->addSeparator();
+
+        // Add the edit folder action to the menu.
+        menuAndFolderIdPairPointer->first->addAction(QIcon::fromTheme(QLatin1String("edit-entry")), i18nc("The edit folder action", "Edit Folder"), [=]
+            {
+                // Get the current tab favorite icon.
+                QIcon currentTabFavoriteIcon = tabWidgetPointer->getCurrentTabFavoritIcon();
 
-                // Load the URL.
-                tabWidgetPointer->loadUrlFromLineEdit(bookmarkUrl);
+                // Instantiate an edit folder dialog.
+                QDialog *editFolderDialogPointer = new EditFolderDialog(BookmarksDatabase::getFolderDatabaseId(menuAndFolderIdPairPointer->second), currentTabFavoriteIcon);
+
+                // Show the dialog.
+                editFolderDialogPointer->show();
+
+                // Process bookmark events.
+                connect(editFolderDialogPointer, SIGNAL(folderSaved()), this, SLOT(populateBookmarks()));
             }
         );
 
-        // Add the bookmark database ID to the toolbar action.
-        toolBarBookmarkActionPointer->setData(bookmarkStruct.id);
+        // Add the delete folder action to the menu.
+        menuAndFolderIdPairPointer->first->addAction(QIcon::fromTheme(QLatin1String("delete")), i18nc("Delete folder context menu entry", "Delete Folder"), [=]
+            {
+                // Get the folder database ID.
+                int folderDatabaseId = BookmarksDatabase::getFolderDatabaseId(menuAndFolderIdPairPointer->second);
+
+                // Create an items to delete list.
+                QList<int>* itemsToDeleteListPointer = new QList<int>;
+
+                // Add the folder to the list of items to delete.
+                itemsToDeleteListPointer->append(folderDatabaseId);
+
+                // Add the folder contents to the list of items to delete.
+                itemsToDeleteListPointer->append(*BookmarksDatabase::getFolderContentsDatabaseIdsRecursively(menuAndFolderIdPairPointer->second));
+
+                // Instantiate a delete dialog message box.
+                QMessageBox deleteDialogMessageBox;
+
+                // Set the icon.
+                deleteDialogMessageBox.setIcon(QMessageBox::Warning);
+
+                // Set the window title.
+                deleteDialogMessageBox.setWindowTitle(i18nc("Delete bookmarks dialog title", "Delete Bookmarks"));
 
-        // Add the actions to the current bookmarks lists.
-        bookmarksMenuCurrentActionList.append(menuBookmarkActionPointer);
-        bookmarksToolBarCurrentActionList.append(toolBarBookmarkActionPointer);
+                // Set the text.
+                deleteDialogMessageBox.setText(i18ncp("Delete bookmarks dialog main message", "Delete %1 bookmark item?", "Delete %1 bookmark items?", itemsToDeleteListPointer->count()));
+
+                // Set the informative text.
+                deleteDialogMessageBox.setInformativeText(i18nc("Delete bookmarks dialog secondary message", "This cannot be undone."));
+
+                // Set the standard buttons.
+                deleteDialogMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+
+                // Set the default button.
+                deleteDialogMessageBox.setDefaultButton(QMessageBox::No);
+
+                // Display the dialog and capture the return value.
+                int returnValue = deleteDialogMessageBox.exec();
+
+                // Delete the domain if instructed.
+                if (returnValue == QMessageBox::Yes)
+                {
+                    // Get the parent folder ID.
+                    double parentFolderId = BookmarksDatabase::getParentFolderId(folderDatabaseId);
+
+                    // Delete the folder and its contents.
+                    for (const int databaseId : *itemsToDeleteListPointer)
+                        BookmarksDatabase::deleteBookmark(databaseId);
+
+                    // Update the display order of the bookmarks in the parent folder.
+                    BookmarksDatabase::updateFolderContentsDisplayOrder(parentFolderId);
+
+                    // Repopulate the bookmarks.
+                    populateBookmarks();
+                }
+            }
+        );
     }
+}
 
-    // Get a handle for the bookmark toolbar layout.
-    QLayout *bookmarksToolBarLayoutPointer = bookmarksToolBarPointer->layout();
+void BrowserWindow::populateBookmarksToolBarSubfolders(const double folderId, QMenu *menuPointer)
+{
+    // Get the folder contents.
+    QList<BookmarkStruct> *folderContentsListPointer = BookmarksDatabase::getFolderContents(folderId);
 
-    // Get the count of the bookmarks.
-    int bookmarkCount = bookmarksToolBarLayoutPointer->count();
+    // Populate the bookmarks folder.
+    for (BookmarkStruct bookmarkStruct : *folderContentsListPointer)
+    {
+        // Get the bookmark URL.
+        QString bookmarkUrl = bookmarkStruct.url;
 
-    // Set the layout of each bookmark to be left aligned.
-    for(int i = 0; i < bookmarkCount; ++i)
-        bookmarksToolBarLayoutPointer->itemAt(i)->setAlignment(Qt::AlignLeft);
+        // Process the item according to the type.
+        if (bookmarkStruct.isFolder)  // This item is a folder.
+        {
+            // Add the subfolder action.
+            QAction *toolBarSubfolderActionPointer = menuPointer->addAction(bookmarkStruct.favoriteIcon, bookmarkStruct.name);
+
+            // Add the action to the beginning of the list of actions to be deleted on repopulate.
+            bookmarksToolBarSubfolderActionList.prepend(new QPair<QMenu *, QAction *>(menuPointer, toolBarSubfolderActionPointer));
+
+            // Create a subfolder menu.
+            QMenu *subfolderMenuPointer = new QMenu();
+
+            // Add the submenu to the action.
+            toolBarSubfolderActionPointer->setMenu(subfolderMenuPointer);
+
+            // Add the submenu to the toolbar menu list.
+            bookmarksToolBarMenuList.append(new QPair<QMenu *, const double>(subfolderMenuPointer, bookmarkStruct.folderId));
+
+            // Populate the subfolder menu.
+            populateBookmarksToolBarSubfolders(bookmarkStruct.folderId, subfolderMenuPointer);
+        }
+        else  // This item is a bookmark.
+        {
+            // Add the bookmark to the folder.
+            QAction *toolBarBookmarkActionPointer = menuPointer->addAction(bookmarkStruct.favoriteIcon, bookmarkStruct.name, [=]
+                {
+                    // Remove the focus from the URL line edit.
+                    urlLineEditPointer->clearFocus();
+
+                    // Load the URL.
+                    tabWidgetPointer->loadUrlFromLineEdit(bookmarkUrl);
+                }
+            );
+
+            // Add the bookmark database ID to the toolbar action.
+            toolBarBookmarkActionPointer->setData(bookmarkStruct.databaseId);
+
+            // Add the action to the beginning of the list of actions to be deleted on repopulate.
+            bookmarksToolBarSubfolderActionList.prepend(new QPair<QMenu *, QAction *>(menuPointer, toolBarBookmarkActionPointer));
+        }
+    }
 }
 
 void BrowserWindow::refresh() const
@@ -894,103 +1200,285 @@ void BrowserWindow::showAddBookmarkDialog() const
     addBookmarkDialogPointer->show();
 }
 
+void BrowserWindow::showAddFolderDialog() const
+{
+    // Instantiate an add folder dialog.
+    AddFolderDialog *addFolderDialogPointer = new AddFolderDialog(tabWidgetPointer->getCurrentTabFavoritIcon());
+
+    // Update the displayed bookmarks when a folder is added.
+    connect(addFolderDialogPointer, SIGNAL(folderAdded()), this, SLOT(populateBookmarks()));
+
+    // Show the dialog.
+    addFolderDialogPointer->show();
+}
+
 void BrowserWindow::showBookmarkContextMenu(const QPoint &point)
 {
     // Get the bookmark action.
     QAction *bookmarkActionPointer = bookmarksToolBarPointer->actionAt(point);
 
-    // Check to see if an bookmark was clicked.
-    if (bookmarkActionPointer)  // A bookmark was clicked.
+    // Check to see if an action was clicked.
+    if (bookmarkActionPointer)  // An action was clicked.
     {
         // Create a bookmark context menu.
         QMenu *bookmarkContextMenuPointer = new QMenu();
 
-        // Get the bookmark ID from the action.
-        int bookmarkId = bookmarkActionPointer->data().toInt();
-
-        // Add the open in new tab action to the menu.
-        bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("tab-new")), i18nc("Open bookmark in new tab context menu entry", "Open in New Tab"), [=]
-            {
-                // Get the bookmark.
-                BookmarkStruct *bookmarkStructPointer = BookmarksDatabase::getBookmark(bookmarkId);
-
-                // Open the bookmark in a new tab.  `true` removes the URL line edit focus, `false` does not load a background tab.
-                tabWidgetPointer->addTab(true, false, bookmarkStructPointer->bookmarkUrl);
-            }
-        );
+        // Get the database ID from the action.
+        int databaseId = bookmarkActionPointer->data().toInt();
 
-        // Add the open in background tab action to the menu.
-        bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("tab-new")), i18nc("Open bookmark in background tab context menu entry", "Open in Background Tab"), [=]
-            {
-                // Get the bookmark.
-                BookmarkStruct *bookmarkStructPointer = BookmarksDatabase::getBookmark(bookmarkId);
-
-                // Open the bookmark in a new tab.  `true` removes the URL line edit focus, `true` loads a background tab.
-                tabWidgetPointer->addTab(true, true, bookmarkStructPointer->bookmarkUrl);
-            }
-        );
-
-        // Add the open in new window action to the menu.
-        bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("window-new")), i18nc("Open bookmark in new window context menu entry", "Open in New Window"), [=]
-            {
-                // Get the bookmark.
-                BookmarkStruct *bookmarkStructPointer = BookmarksDatabase::getBookmark(bookmarkId);
-
-                // Create a new browser window.
-                BrowserWindow *browserWindowPointer = new BrowserWindow(false, &bookmarkStructPointer->bookmarkUrl);
+        // Create the menu according to the type.
+        if (BookmarksDatabase::isFolder(databaseId))  // A folder was clicked.
+        {
+            // Get the folder ID.
+            double folderId = BookmarksDatabase::getFolderId(databaseId);
+
+            // Add the open folder in new tabs action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("tab-new")), i18nc("The open folder in new tabs action", "Open Folder in New Tabs"), [=]
+                {
+                    // Get all the folder URLs.
+                    QList<QString> *folderUrlsListPointer = BookmarksDatabase::getAllFolderUrls(folderId);
+
+                    // Open the URLs in new tabs.  `true` removes the URL line edit focus, `false` does not load a background tab.
+                    for (QString url : *folderUrlsListPointer)
+                        tabWidgetPointer->addTab(true, false, url);
+                }
+            );
+
+            // Add the open folder in background tabs action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("tab-new")), i18nc("The open folder in background tabs action", "Open Folder in Background Tabs"), [=]
+                {
+                    // Get all the folder URLs.
+                    QList<QString> *folderUrlsListPointer = BookmarksDatabase::getAllFolderUrls(folderId);
+
+                    // Open the URLs in new tabs.  `true` removes the URL line edit focus, `true` loads a background tab.
+                    for (QString url : *folderUrlsListPointer)
+                        tabWidgetPointer->addTab(true, true, url);
+                }
+            );
+
+            // Add the open folder in new window action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("window-new")), i18nc("The open folder in new window action", "Open Folder in New Window"), [=]
+                {
+                    // Get all the folder URLs.
+                    QList<QString> *folderUrlsListPointer = BookmarksDatabase::getAllFolderUrls(folderId);
+
+                    // Create a new browser window.
+                    BrowserWindow *browserWindowPointer = new BrowserWindow(false, &folderUrlsListPointer->first());
+
+                    // Get a count of the folder URLs.
+                    const int folderUrls = folderUrlsListPointer->count();
 
-                // Show the new browser window.
-                browserWindowPointer->show();
-            }
-        );
+                    // Load all the other URLs.  `true` removes the URL line edit focus, `true` loads a background tab.
+                    for (int i = 1; i < folderUrls; ++i)
+                        browserWindowPointer->tabWidgetPointer->addTab(true, true, folderUrlsListPointer->value(i));
+
+                    // Show the new browser window.
+                    browserWindowPointer->show();
+                }
+            );
+
+            // Add a separator.
+            bookmarkContextMenuPointer->addSeparator();
+
+            // Add the add bookmark action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("bookmark-new")), i18nc("The add bookmark action", "Add Bookmark"), [=]
+                {
+                    // Instantiate an add bookmark dialog.
+                    AddBookmarkDialog *addBookmarkDialogPointer = new AddBookmarkDialog(tabWidgetPointer->getCurrentTabTitle(), tabWidgetPointer->getCurrentTabUrl(),
+                                                                                        tabWidgetPointer->getCurrentTabFavoritIcon(), folderId);
+
+                    // Update the displayed bookmarks when a new one is added.
+                    connect(addBookmarkDialogPointer, SIGNAL(bookmarkAdded()), this, SLOT(populateBookmarks()));
+
+                    // Show the dialog.
+                    addBookmarkDialogPointer->show();
+                }
+            );
+
+            // Add the add folder action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("folder-add")), i18nc("The add folder action", "Add Folder"), [=]
+                {
+                    // Instantiate an add folder dialog.
+                    AddFolderDialog *addFolderDialogPointer = new AddFolderDialog(tabWidgetPointer->getCurrentTabFavoritIcon(), folderId);
+
+                    // Update the displayed bookmarks when a folder is added.
+                    connect(addFolderDialogPointer, SIGNAL(folderAdded()), this, SLOT(populateBookmarks()));
+
+                    // Show the dialog.
+                    addFolderDialogPointer->show();
+                }
+            );
+
+            // Add a separator.
+            bookmarkContextMenuPointer->addSeparator();
+
+            // Add the edit folder action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("edit-entry")), i18nc("The edit folder action", "Edit Folder"), [=]
+                {
+                    // Get the current tab favorite icon.
+                    QIcon currentTabFavoriteIcon = tabWidgetPointer->getCurrentTabFavoritIcon();
+
+                    // Instantiate an edit folder dialog.
+                    QDialog *editFolderDialogPointer = new EditFolderDialog(BookmarksDatabase::getFolderDatabaseId(folderId), currentTabFavoriteIcon);
+
+                    // Show the dialog.
+                    editFolderDialogPointer->show();
+
+                    // Process bookmark events.
+                    connect(editFolderDialogPointer, SIGNAL(folderSaved()), this, SLOT(populateBookmarks()));
+                }
+            );
+
+            // Add the delete folder action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("delete")), i18nc("Delete folder context menu entry", "Delete Folder"), [=]
+                {
+                    // Get the folder database ID.
+                    int folderDatabaseId = BookmarksDatabase::getFolderDatabaseId(folderId);
+
+                    // Create an items to delete list.
+                    QList<int>* itemsToDeleteListPointer = new QList<int>;
+
+                    // Add the folder to the list of items to delete.
+                    itemsToDeleteListPointer->append(folderDatabaseId);
+
+                    // Add the folder contents to the list of items to delete.
+                    itemsToDeleteListPointer->append(*BookmarksDatabase::getFolderContentsDatabaseIdsRecursively(folderId));
 
-        // Add a separator.
-        bookmarkContextMenuPointer->addSeparator();
+                    // Instantiate a delete dialog message box.
+                    QMessageBox deleteDialogMessageBox;
+
+                    // Set the icon.
+                    deleteDialogMessageBox.setIcon(QMessageBox::Warning);
 
-        // Add the edit action to the menu.
-        bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("edit-entry")), i18nc("Edit bookmark context menu entry", "Edit"), [=]
-            {
-                // Get the current tab favorite icon.
-                QIcon currentTabFavoriteIcon = tabWidgetPointer->getCurrentTabFavoritIcon();
+                    // Set the window title.
+                    deleteDialogMessageBox.setWindowTitle(i18nc("Delete bookmarks dialog title", "Delete Bookmarks"));
 
-                // Instantiate an edit bookmark dialog.
-                QDialog *editBookmarkDialogPointer = new EditBookmarkDialog(bookmarkId, currentTabFavoriteIcon);
+                    // Set the text.
+                    deleteDialogMessageBox.setText(i18ncp("Delete bookmarks dialog main message", "Delete %1 bookmark item?", "Delete %1 bookmark items?", itemsToDeleteListPointer->count()));
 
-                // Show the dialog.
-                editBookmarkDialogPointer->show();
+                    // Set the informative text.
+                    deleteDialogMessageBox.setInformativeText(i18nc("Delete bookmarks dialog secondary message", "This cannot be undone."));
 
-                // Process bookmark events.
-                connect(editBookmarkDialogPointer, SIGNAL(bookmarkSaved()), this, SLOT(populateBookmarks()));
-            }
-        );
+                    // Set the standard buttons.
+                    deleteDialogMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
 
-        // Add the copy URL action to the menu.
-        bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("edit-copy")), i18nc("Copy bookmark url context menu entry", "Copy URL"), [=]
-            {
-                // Get the bookmark.
-                BookmarkStruct *bookmarkStructPointer = BookmarksDatabase::getBookmark(bookmarkId);
+                    // Set the default button.
+                    deleteDialogMessageBox.setDefaultButton(QMessageBox::No);
 
-                // Get a handle for the clipboard.
-                QClipboard *clipboard = QGuiApplication::clipboard();
+                    // Display the dialog and capture the return value.
+                    int returnValue = deleteDialogMessageBox.exec();
 
-                // Place the URL on the keyboard.
-                clipboard->setText(bookmarkStructPointer->bookmarkUrl);
-            }
-        );
+                    // Delete the domain if instructed.
+                    if (returnValue == QMessageBox::Yes)
+                    {
+                        // Get the parent folder ID.
+                        double parentFolderId = BookmarksDatabase::getParentFolderId(folderDatabaseId);
 
-        // Add a separator.
-        bookmarkContextMenuPointer->addSeparator();
+                        // Delete the folder and its contents.
+                        for (const int databaseId : *itemsToDeleteListPointer)
+                            BookmarksDatabase::deleteBookmark(databaseId);
 
-        // Add the delete action to the menu.
-        bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("delete")), i18nc("Delete bookmark context menu entry", "Delete"), [=]
-            {
-                // Delete the bookmark.
-                BookmarksDatabase::deleteBookmark(bookmarkId);
+                        // Update the display order of the bookmarks in the parent folder.
+                        BookmarksDatabase::updateFolderContentsDisplayOrder(parentFolderId);
 
-                // Repopulate the bookmarks.
-                populateBookmarks();
-            }
-        );
+                        // Repopulate the bookmarks.
+                        populateBookmarks();
+                    }
+                }
+            );
+        }
+        else  // A bookmark was clicked.
+        {
+            // Add the open in new tab action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("tab-new")), i18nc("Open bookmark in new tab context menu entry", "Open in New Tab"), [=]
+                {
+                    // Get the bookmark.
+                    BookmarkStruct *bookmarkStructPointer = BookmarksDatabase::getBookmark(databaseId);
+
+                    // Open the bookmark in a new tab.  `true` removes the URL line edit focus, `false` does not load a background tab.
+                    tabWidgetPointer->addTab(true, false, bookmarkStructPointer->url);
+                }
+            );
+
+            // Add the open in background tab action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("tab-new")), i18nc("Open bookmark in background tab context menu entry", "Open in Background Tab"), [=]
+                {
+                    // Get the bookmark.
+                    BookmarkStruct *bookmarkStructPointer = BookmarksDatabase::getBookmark(databaseId);
+
+                    // Open the bookmark in a new tab.  `true` removes the URL line edit focus, `true` loads a background tab.
+                    tabWidgetPointer->addTab(true, true, bookmarkStructPointer->url);
+                }
+            );
+
+            // Add the open in new window action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("window-new")), i18nc("Open bookmark in new window context menu entry", "Open in New Window"), [=]
+                {
+                    // Get the bookmark.
+                    BookmarkStruct *bookmarkStructPointer = BookmarksDatabase::getBookmark(databaseId);
+
+                    // Create a new browser window and load the first URL.  `false` indicates it is not the first browser window.
+                    BrowserWindow *browserWindowPointer = new BrowserWindow(false, &bookmarkStructPointer->url);
+
+                    // Show the new browser window.
+                    browserWindowPointer->show();
+                }
+            );
+
+            // Add a separator.
+            bookmarkContextMenuPointer->addSeparator();
+
+            // Add the edit action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("edit-entry")), i18nc("Edit bookmark context menu entry", "Edit"), [=]
+                {
+                    // Get the current tab favorite icon.
+                    QIcon currentTabFavoriteIcon = tabWidgetPointer->getCurrentTabFavoritIcon();
+
+                    // Instantiate an edit bookmark dialog.
+                    QDialog *editBookmarkDialogPointer = new EditBookmarkDialog(databaseId, currentTabFavoriteIcon);
+
+                    // Show the dialog.
+                    editBookmarkDialogPointer->show();
+
+                    // Process bookmark events.
+                    connect(editBookmarkDialogPointer, SIGNAL(bookmarkSaved()), this, SLOT(populateBookmarks()));
+                }
+            );
+
+            // Add the copy URL action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("edit-copy")), i18nc("Copy bookmark URL context menu entry", "Copy URL"), [=]
+                {
+                    // Get the bookmark.
+                    BookmarkStruct *bookmarkStructPointer = BookmarksDatabase::getBookmark(databaseId);
+
+                    // Get a handle for the clipboard.
+                    QClipboard *clipboard = QGuiApplication::clipboard();
+
+                    // Place the URL on the keyboard.
+                    clipboard->setText(bookmarkStructPointer->url);
+                }
+            );
+
+            // Add a separator.
+            bookmarkContextMenuPointer->addSeparator();
+
+            // Add the delete action to the menu.
+            bookmarkContextMenuPointer->addAction(QIcon::fromTheme(QLatin1String("delete")), i18nc("Delete bookmark context menu entry", "Delete"), [=]
+                {
+                    // Get the parent folder ID.
+                    double parentFolderId = BookmarksDatabase::getParentFolderId(databaseId);
+
+                    // Delete the bookmark.
+                    BookmarksDatabase::deleteBookmark(databaseId);
+
+                    // Update the display order of the bookmarks in the parent folder.
+                    BookmarksDatabase::updateFolderContentsDisplayOrder(parentFolderId);
+
+                    // Repopulate the bookmarks.
+                    populateBookmarks();
+                }
+            );
+        }
 
         // Delete the menu from memory when it is closed.
         bookmarkContextMenuPointer->setAttribute(Qt::WA_DeleteOnClose);
index 73ac519565ed78c40720481a7cee956b7c6ae8d7..eb6f82e94e66b2abe1b4e6dc16ed5c9e12af58ce 100644 (file)
@@ -71,6 +71,7 @@ private Q_SLOTS:
     void refresh() const;
     void reloadAndBypassCache() const;
     void showAddBookmarkDialog() const;
+    void showAddFolderDialog() const;
     void showBookmarkContextMenu(const QPoint &point);
     void showCookiesDialog();
     void showDownloadLocationBrowseDialog() const;
@@ -106,10 +107,13 @@ private Q_SLOTS:
 
 private:
     // The private variables.
-    QList<QAction*> bookmarksMenuCurrentActionList;
+    QList<QPair<QMenu *, QAction *> *> bookmarksMenuActionList;
     QMenu *bookmarksMenuPointer;
-    QList<QAction*> bookmarksToolBarCurrentActionList;
+    QList<QPair<QMenu *, QMenu *> *> bookmarksMenuSubmenuList;
+    QList<QAction*> bookmarksToolBarActionList;
+    QList<QPair<QMenu *, const double> *> bookmarksToolBarMenuList;
     KToolBar *bookmarksToolBarPointer;
+    QList<QPair<QMenu *, QAction *> *> bookmarksToolBarSubfolderActionList;
     bool bookmarksToolBarIsVisible = false;
     bool bookmarksToolBarUninitialized = true;
     KConfigDialog *configDialogPointer;
@@ -171,5 +175,10 @@ private:
     QPushButton *zoomMinusButtonPointer;
     QAction *zoomOutActionPointer;
     QPushButton *zoomPlusButtonPointer;
+
+    // The private functions.
+    void populateBookmarksMenuSubfolders(const double folderId, QMenu *menuPointer);
+    void populateBookmarksToolBar();
+    void populateBookmarksToolBarSubfolders(const double folderId, QMenu *menuPointer);
 };
 #endif