Rabbit Remote Control 0.1.0-bate8
Loading...
Searching...
No Matches
FrmBookmark.cpp
1// Author: Kang Lin <kl222@126.com>
2
3#include <QMenu>
4#include <QLabel>
5#include <QPushButton>
6#include <QVBoxLayout>
7#include <QHBoxLayout>
8#include <QHeaderView>
9#include <QMessageBox>
10#include <QInputDialog>
11#include <QFileDialog>
12#include <QLoggingCategory>
13
14#include "RabbitCommonDir.h"
15#include "FrmBookmark.h"
16#include "ui_FrmBookmark.h"
17
18static Q_LOGGING_CATEGORY(log, "WebBrowser.Bookmark")
19CFrmBookmark::CFrmBookmark(CParameterWebBrowser *pPara, QWidget *parent)
20 : QWidget(parent)
21 , ui(new Ui::CFrmBookmark)
22 , m_pDatabase(nullptr)
23 , m_pPara(pPara)
24 , m_pTreeView(nullptr)
25 , m_pSearchEdit(nullptr)
26 , m_pToolBar(nullptr)
27 , m_pModel(new QStandardItemModel(this))
28{
29 ui->setupUi(this);
30 setWindowTitle(tr("Bookmark"));
31 setAcceptDrops(true);
32
33 setupUI();
34
35 if(m_pPara) {
36 m_pDatabase = &pPara->m_BookmarkDatabase;
37 resize(m_pPara->GetWindowSize());
38 }
39
40 loadBookmarks();
41}
42
43CFrmBookmark::~CFrmBookmark()
44{
45 delete ui;
46}
47
48void CFrmBookmark::setupUI()
49{
50 bool check = false;
51 QVBoxLayout *pMainLayout = new QVBoxLayout(this);
52 if(!pMainLayout) return;
53
54 // 工具栏
55 setupToolBar();
56 pMainLayout->addWidget(m_pToolBar);
57
58 // 搜索框
59 QHBoxLayout *pSearchLayout = new QHBoxLayout;
60 if(pSearchLayout) {
61 pSearchLayout->addWidget(new QLabel(tr("Search:")));
62
63 m_pSearchEdit = new QLineEdit(this);
64 if(m_pSearchEdit) {
65 m_pSearchEdit->setPlaceholderText(tr("Input keyword to search boolmark ......"));
66 m_pSearchEdit->setClearButtonEnabled(true);
67 check = connect(m_pSearchEdit, &QLineEdit::textChanged,
68 this, &CFrmBookmark::onSearchTextChanged);
69 Q_ASSERT(check);
70 pSearchLayout->addWidget(m_pSearchEdit);
71 }
72 pMainLayout->addLayout(pSearchLayout);
73 }
74
75 // 书签树
76 setupTreeView();
77 pMainLayout->addWidget(m_pTreeView);
78
79 // 按钮
80 QHBoxLayout *pButtonLayout = new QHBoxLayout;
81 if(pButtonLayout) {
82 pButtonLayout->addStretch();
83
84 QPushButton *closeButton = new QPushButton(tr("Close"), this);
85 connect(closeButton, &QPushButton::clicked, this, &CFrmBookmark::close);
86 pButtonLayout->addWidget(closeButton);
87
88 pMainLayout->addLayout(pButtonLayout);
89 }
90 setLayout(pMainLayout);
91}
92
93void CFrmBookmark::setupToolBar()
94{
95 bool check = false;
96 m_pToolBar = new QToolBar(this);
97 if(!m_pToolBar) return;
98
99 // 添加书签
100 QAction *addAction = m_pToolBar->addAction(QIcon::fromTheme("add"), tr("Add bookmark"));
101 if(addAction) {
102 check = connect(addAction, &QAction::triggered, this, &CFrmBookmark::onAddBookmark);
103 Q_ASSERT(check);
104 }
105
106 // 添加文件夹
107 QAction *addFolderAction = m_pToolBar->addAction(QIcon::fromTheme("folder-new"), tr("Add folder"));
108 if(addFolderAction) {
109 check = connect(addFolderAction, &QAction::triggered, this, &CFrmBookmark::onAddFolder);
110 Q_ASSERT(check);
111 }
112
113 m_pToolBar->addSeparator();
114
115 // 编辑
116 QAction *editAction = m_pToolBar->addAction(QIcon::fromTheme("edit"), tr("Edit"));
117 if(editAction) {
118 check = connect(editAction, &QAction::triggered, this, &CFrmBookmark::onEditBookmark);
119 Q_ASSERT(check);
120 }
121
122 // 删除
123 QAction *pDeleteAction = m_pToolBar->addAction(QIcon::fromTheme("edit-delete"), tr("Delete"));
124 if(pDeleteAction) {
125 check = connect(pDeleteAction, &QAction::triggered, this, &CFrmBookmark::onDeleteBookmark);
126 Q_ASSERT(check);
127 }
128
129 /*
130 // 设为最爱
131 QAction *favoriteAction = m_pToolBar->addAction(QIcon::fromTheme("favorites"), tr("Favorite"));
132 if(favoriteAction) {
133 check = connect(favoriteAction, &QAction::triggered, this, &CFrmBookmark::onSetFavorite);
134 Q_ASSERT(check);
135 }//*/
136
137 m_pToolBar->addSeparator();
138
139 // 导入
140 QAction *importAction = m_pToolBar->addAction(QIcon::fromTheme("import"), tr("Import"));
141 if(importAction) {
142 check = connect(importAction, &QAction::triggered, this, &CFrmBookmark::onImportBookmarks);
143 Q_ASSERT(check);
144 }
145
146 // 导出
147 QAction *exportAction = m_pToolBar->addAction(QIcon::fromTheme("export"), tr("Export"));
148 if(exportAction) {
149 check = connect(exportAction, &QAction::triggered, this, &CFrmBookmark::onExportBookmarks);
150 Q_ASSERT(check);
151 }
152
153 m_pToolBar->addSeparator();
154
155 // 刷新
156 QAction *refreshAction = m_pToolBar->addAction(QIcon::fromTheme("view-refresh"), tr("Refresh"));
157 if(refreshAction) {
158 check = connect(refreshAction, &QAction::triggered, this, &CFrmBookmark::refresh);
159 Q_ASSERT(check);
160 }
161}
162
163void CFrmBookmark::setupTreeView()
164{
165 bool check = false;
166 m_pTreeView = new QTreeView(this);
167 if(!m_pTreeView) return;
168 m_pTreeView->setHeaderHidden(true);
169 m_pTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
170 m_pTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
171
172 // 连接信号
173 check = connect(m_pTreeView, &QTreeView::doubleClicked,
174 this, &CFrmBookmark::onTreeViewDoubleClicked);
175 Q_ASSERT(check);
176 check = connect(m_pTreeView, &QTreeView::customContextMenuRequested,
177 this, &CFrmBookmark::onCustomContextMenu);
178 Q_ASSERT(check);
179
180 if(m_pModel) {
181 m_pTreeView->setModel(m_pModel);
182 // 设置列
183 m_pModel->setColumnCount(1);
184 }
185 /*
186 if(m_pTreeView->header())
187 m_pTreeView->header()->setStretchLastSection(true); // 最后一列自适应//*/
188}
189
190void CFrmBookmark::loadBookmarks()
191{
192 if(!m_pDatabase || !m_pModel || !m_pTreeView) return;
193 m_pModel->clear();
194 m_folderItems.clear();
195
196 // 设置表头
197 m_pModel->setHorizontalHeaderLabels(QStringList() << tr("Title"));
198
199 // 加载文件夹结构
200 QList<BookmarkItem> folders = m_pDatabase->getAllFolders();
201
202 // 创建根节点
203 QStandardItem *rootItem = m_pModel->invisibleRootItem();
204
205 int nCurrent = m_pPara->GetBookmarkCurrentFolder();
206 QStandardItem* pCurrentItem = nullptr;
207
208 // 先添加顶级文件夹
209 for (const auto &folder : folders) {
210 if (folder.folderId == 0) { // 顶级文件夹
211 QStandardItem *pFolderItem = new QStandardItem(folder.getIcon(), folder.title);
212 if(!pFolderItem) continue;
213 pFolderItem->setData(folder.id, ID);
214 pFolderItem->setData(BookmarkType_Folder, Type);
215 rootItem->appendRow(pFolderItem);
216 m_folderItems[folder.id] = pFolderItem;
217 if(folder.id == nCurrent)
218 pCurrentItem = pFolderItem;
219 continue;
220 }
221
222 auto it = m_folderItems.find(folder.folderId);
223 if(m_folderItems.end() == it) {
224 qWarning(log) << "The parent of folder is not find:" << folder.folderId;
225 continue;
226 }
227 QStandardItem *pFolderItem = new QStandardItem(folder.getIcon(), folder.title);
228 if(!pFolderItem) continue;
229 pFolderItem->setData(folder.id, ID);
230 pFolderItem->setData(BookmarkType_Folder, Type);
231 (*it)->appendRow(pFolderItem);
232 m_folderItems[folder.id] = pFolderItem;
233 if(folder.id == nCurrent)
234 pCurrentItem = pFolderItem;
235 }
236
237 // Set current index
238 if(pCurrentItem) {
239 auto index = m_pModel->indexFromItem(pCurrentItem);
240 m_pTreeView->setCurrentIndex(index);
241 }
242
243 // 添加书签到对应的文件夹
244 QList<BookmarkItem> bookmarks = m_pDatabase->getAllBookmarks(-1);
245 for (const auto &bookmark : bookmarks) {
246 QStandardItem *pParentItem = nullptr;
247
248 if (bookmark.folderId > 0 && m_folderItems.contains(bookmark.folderId)) {
249 pParentItem = m_folderItems[bookmark.folderId];
250 } else {
251 pParentItem = rootItem;
252 }
253
254 QStandardItem *bookmarkItem = new QStandardItem(bookmark.getIcon(), bookmark.title);
255 bookmarkItem->setData(bookmark.id, ID);
256 bookmarkItem->setData(BookmarkType_Bookmark, Type);
257 bookmarkItem->setData(bookmark.url, Url);
258
259 if(pParentItem)
260 pParentItem->appendRow(bookmarkItem);
261 }
262
263 // 展开所有节点
264 m_pTreeView->expandAll();
265}
266
267void CFrmBookmark::onAddBookmark()
268{
269 bool ok;
270 QString url = QInputDialog::getText(this, tr("Add bookmark"),
271 tr("Url:"), QLineEdit::Normal,
272 "http://", &ok);
273 if (!ok || url.isEmpty()) return;
274
275 QString title = QInputDialog::getText(this, tr("Add bookmark"),
276 tr("Title:"), QLineEdit::Normal,
277 "", &ok);
278 if (!ok) return;
279
280 BookmarkItem item(BookmarkType_Bookmark);
281 item.url = url;
282 item.title = title.isEmpty() ? url : title;
283 item.createdTime = QDateTime::currentDateTime();
284 item.lastVisitTime = item.createdTime;
285 item.modifiedTime = item.createdTime;
286
287 // 获取选中的文件夹
288 QModelIndex currentIndex = m_pTreeView->currentIndex();
289 if (currentIndex.isValid()) {
290 int type = currentIndex.data(Type).toInt();
291 if (BookmarkType_Folder == type) {
292 item.folderId = currentIndex.data(Qt::UserRole).toInt();
293 }
294 }
295
296 if (m_pDatabase->addBookmark(item)) {
297 refresh();
298 }
299}
300
301void CFrmBookmark::onAddFolder()
302{
303 bool ok;
304 QString name = QInputDialog::getText(this, tr("Add folder"),
305 tr("Folder name:"), QLineEdit::Normal,
306 "", &ok);
307 if (!ok || name.isEmpty()) return;
308
309 int parentId = 0;
310 QModelIndex currentIndex = m_pTreeView->currentIndex();
311 if (currentIndex.isValid()) {
312 int type = currentIndex.data(Type).toInt();
313 if (BookmarkType_Folder == type) {
314 parentId = currentIndex.data(Qt::UserRole).toInt();
315 }
316 }
317
318 if (m_pDatabase->addFolder(name, parentId)) {
319 refresh();
320 }
321}
322
323void CFrmBookmark::onEditBookmark()
324{
325 QModelIndex index = m_pTreeView->currentIndex();
326 if (!index.isValid()) return;
327
328 int type = index.data(Type).toInt();
329 int id = index.data(Qt::UserRole).toInt();
330
331 if (BookmarkType_Bookmark == type) {
332 BookmarkItem item = m_pDatabase->getBookmark(id);
333 if (item.id == 0) return;
334
335 bool ok;
336 QString title = QInputDialog::getText(this, tr("Edit bookmark"),
337 tr("Title:"), QLineEdit::Normal,
338 item.title, &ok);
339 if (!ok) return;
340
341 QString url = QInputDialog::getText(this, tr("Add bookmark"),
342 tr("Url:"), QLineEdit::Normal,
343 item.url, &ok);
344 if (!ok) return;
345
346 item.title = title;
347 item.url = url;
348
349 if (m_pDatabase->updateBookmark(item)) {
350 refresh();
351 }
352 } else if (BookmarkType_Folder == type) {
353 QString oldName = index.data(Qt::DisplayRole).toString();
354
355 bool ok;
356 QString newName = QInputDialog::getText(this, tr("Rename folder"),
357 tr("Folder name:"), QLineEdit::Normal,
358 oldName, &ok);
359 if (!ok || newName.isEmpty()) return;
360
361 if (m_pDatabase->renameFolder(id, newName)) {
362 refresh();
363 }
364 }
365}
366
367void CFrmBookmark::onDeleteBookmark()
368{
369 QModelIndex index = m_pTreeView->currentIndex();
370 if (!index.isValid()) return;
371
372 int type = index.data(Type).toInt();
373 QString name = index.data(Qt::DisplayRole).toString();
374 int id = index.data(Qt::UserRole).toInt();
375 if(1 == id && BookmarkType_Folder == type) {
376 QMessageBox::warning(this, tr("Warning"), tr("The folder \"%1\" is not delete").arg(name));
377 return;
378 }
379
380 QString message;
381 if (BookmarkType_Bookmark == type) {
382 message = tr("Are you sure you want to delete the bookmark \"%1\"?").arg(name);
383 } else if (BookmarkType_Folder == type) {
384 message = tr("Are you sure you want to delete the folder \"%1\"?").arg(name);
385 } else {
386 return;
387 }
388
389 QMessageBox::StandardButton reply = QMessageBox::question(
390 this, tr("Confirm deletion"), message,
391 QMessageBox::Yes | QMessageBox::No,
392 QMessageBox::No
393 );
394
395 if (reply == QMessageBox::Yes) {
396 if (BookmarkType_Bookmark == type) {
397 m_pDatabase->deleteBookmark(id);
398 } else if (BookmarkType_Folder == type) {
399 m_pDatabase->deleteFolder(id);
400 }
401 refresh();
402 }
403}
404
405void CFrmBookmark::onSetFavorite()
406{
407 QModelIndex index = m_pTreeView->currentIndex();
408 if (!index.isValid()) return;
409
410 int type = index.data(Type).toInt();
411 if (type != BookmarkType_Bookmark) return;
412
413 int id = index.data(ID).toInt();
414 BookmarkItem item = m_pDatabase->getBookmark(id);
415 if (item.id == 0) return;
416
417 if (m_pDatabase->updateBookmark(item)) {
418 refresh();
419 }
420}
421
422void CFrmBookmark::onImportBookmarks()
423{
424 QString filename = QFileDialog::getOpenFileName(
425 this, tr("Import bookmarks"),
426 RabbitCommon::CDir::Instance()->GetDirUserDocument(),
427 tr("JSON (*.json);; All files (*.*)"));
428
429 if (filename.isEmpty()) return;
430
431 QFileInfo fi(filename);
432 if(0 == fi.suffix().compare("json", Qt::CaseInsensitive)) {
433 if (m_pDatabase->ImportFromJsonFile(filename)) {
434 QMessageBox::information(this, tr("Import bookmarks"),
435 tr("Successfully imported bookmarks from file: %1").arg(filename));
436 refresh();
437 } else {
438 QMessageBox::critical(this, tr("Import bookmarks"),
439 tr("Failed to import bookmark from file: %1").arg(filename) + "\n\n"
440 + tr("Error: ") + m_pDatabase->GetError());
441 }
442 return;
443 }
444
445 QMessageBox::warning(this, tr("Import bookmarks"),
446 tr("Invalid file: %1").arg(filename) + "\n\n"
447 + tr("Please use html file"));
448}
449
450void CFrmBookmark::onExportBookmarks()
451{
452 QString filename = QFileDialog::getSaveFileName(
453 this, tr("Export bookmarks"),
454 RabbitCommon::CDir::Instance()->GetDirUserDocument(),
455 tr("JSON (*.json);; All files (*.*)"));
456
457 if (filename.isEmpty()) return;
458
459 QFileInfo fi(filename);
460 if(0 == fi.suffix().compare("json", Qt::CaseInsensitive)) {
461 if (m_pDatabase->ExportToJsonFile(filename)) {
462 QMessageBox::information(this, tr("Export bookmarks"),
463 tr("Bookmarks successfully exported to file: %1").arg(filename));
464 } else {
465 QMessageBox::critical(this, tr("Export bookmarks"),
466 tr("Failed to export bookmark to file: %1").arg(filename) + "\n\n"
467 + tr("Error: ") + m_pDatabase->GetError());
468 }
469 return;
470 }
471
472 QMessageBox::warning(this, tr("Export bookmarks"),
473 tr("Invalid file: %1").arg(filename) + "\n\n"
474 + tr("Please use html file"));
475}
476
477void CFrmBookmark::onSearchTextChanged(const QString &text)
478{
479 if (text.isEmpty()) {
480 loadBookmarks();
481 return;
482 }
483
484 if(!m_pModel) return;
485
486 m_pModel->clear();
487 m_folderItems.clear();
488
489 QList<BookmarkItem> bookmarks = m_pDatabase->searchBookmarks(text);
490 QStandardItem *pRootItem = m_pModel->invisibleRootItem();
491 if(!pRootItem) return;
492
493 foreach (const auto &bookmark, bookmarks) {
494 QStandardItem *item = new QStandardItem(
495 bookmark.getIcon(), bookmark.title);
496 item->setData(bookmark.id, ID);
497 item->setData(BookmarkType_Bookmark, Type);
498 item->setData(bookmark.url, Url);
499
500 item->setToolTip(bookmark.url);
501
502 pRootItem->appendRow(item);
503 }
504}
505
506void CFrmBookmark::onTreeViewDoubleClicked(const QModelIndex &index)
507{
508 if(!m_pDatabase) return;
509 int type = index.data(Type).toInt();
510 if (BookmarkType_Bookmark == type) {
511 QString url = index.data(Url).toString();
512 int id = index.data(ID).toInt();
513 if (!url.isEmpty()) {
514 BookmarkItem item = m_pDatabase->getBookmark(id);
515 item.lastVisitTime = QDateTime::currentDateTime();
516 m_pDatabase->updateBookmark(item);
517 emit openUrlRequested(url);
518 close(); // 关闭对话框
519 }
520 }
521}
522
523void CFrmBookmark::onCustomContextMenu(const QPoint &pos)
524{
525 if(!m_pTreeView) return;
526 QModelIndex index = m_pTreeView->indexAt(pos);
527 if (!index.isValid()) return;
528
529 QMenu menu(this);
530
531 int id = index.data(ID).toInt();
532 int type = index.data(Type).toInt();
533
534 if (BookmarkType_Bookmark == type) {
535 menu.addAction(QIcon::fromTheme("document-open"), tr("Open"), this, [this, index]() {
536 onTreeViewDoubleClicked(index);
537 });
538
539 menu.addAction(QIcon::fromTheme("edit"), tr("Edit"), this, &CFrmBookmark::onEditBookmark);
540 menu.addSeparator();
541
542 QAction *favoriteAction = menu.addAction(QIcon::fromTheme("favorites"), tr("Favorite"));
543 connect(favoriteAction, &QAction::triggered, this, &CFrmBookmark::onSetFavorite);
544
545 menu.addAction(QIcon::fromTheme("edit-delete"), tr("Delete"), this, &CFrmBookmark::onDeleteBookmark);
546 } else if (BookmarkType_Folder == type) {
547 menu.addAction(QIcon::fromTheme("Add"), tr("Add bookmark"), this, &CFrmBookmark::onAddBookmark);
548 menu.addAction(QIcon::fromTheme("folder-new"), tr("Add folder"), this, &CFrmBookmark::onAddFolder);
549 menu.addSeparator();
550 menu.addAction(QIcon::fromTheme("edit"), tr("Edit"), this, &CFrmBookmark::onEditBookmark);
551 if(1 != id)
552 menu.addAction(QIcon::fromTheme("edit-delete"), tr("Delete"), this, &CFrmBookmark::onDeleteBookmark);
553 }
554
555 menu.exec(m_pTreeView->viewport()->mapToGlobal(pos));
556}
557
558void CFrmBookmark::refresh()
559{
560 loadBookmarks();
561}