8#include <QLoggingCategory>
18#include "FrmHistory.h"
19#include "RabbitCommonDir.h"
20#include "ui_FrmHistory.h"
22static Q_LOGGING_CATEGORY(log,
"WebBrowser.History")
27 , m_pModelHistory(
nullptr)
29 , m_pDateStart(
nullptr)
36 setWindowTitle(tr(
"History"));
38 QToolBar* pToolBar = ui->toolBar;
40 QDate curDate = QDate::currentDate();
41 m_pDateStart =
new QDateEdit(curDate.addDays(-7), pToolBar);
43 m_pDateStart->setToolTip(tr(
"Start date"));
44 m_pDateEnd =
new QDateEdit(curDate, pToolBar);
46 m_pDateEnd->setToolTip(tr(
"End date"));
47 QComboBox* pCB =
new QComboBox(pToolBar);
48 pCB->addItem(tr(
"One day"), 1);
49 pCB->addItem(tr(
"Two days"), 2);
50 pCB->addItem(tr(
"One Week"), 7);
51 pCB->addItem(tr(
"One month"), curDate.daysInMonth());
52 pCB->setCurrentIndex(2);
53 check = connect(pCB, SIGNAL(currentIndexChanged(
int)),
54 this, SLOT(slotComboxIndexChanged(
int)));
57 pToolBar->addWidget(pCB);
58 pToolBar->addWidget(m_pDateStart);
59 pToolBar->addWidget(m_pDateEnd);
61 QAction* pRefresh = pToolBar->addAction(
62 QIcon::fromTheme(
"view-refresh"), tr(
"Refresh"),
63 this, &CFrmHistory::slotRefresh);
65 pRefresh->setToolTip(pRefresh->text());
66 pRefresh->setStatusTip(pRefresh->text());
69 pToolBar->addSeparator();
70 QSpinBox* pSBLimit =
new QSpinBox(pToolBar);
72 pToolBar->addWidget(pSBLimit);
73 pSBLimit->setToolTip(tr(
"Limit"));
76 nMax = qMax(nMax, m_pPara->GetDatabaseViewLimit());
77 pSBLimit->setRange(-1, nMax);
79 pSBLimit->setValue(m_pPara->GetDatabaseViewLimit());
80 check = connect(pSBLimit, SIGNAL(valueChanged(
int)),
this, SLOT(slotLimit(
int)));
84 pToolBar->addSeparator();
86 QIcon::fromTheme(
"import"), tr(
"Import"),
this, SLOT(slotImport()));
88 QIcon::fromTheme(
"export"), tr(
"Export"),
this, SLOT(slotExport()));
89 pToolBar->addSeparator();
101 pToolBar->addAction(QIcon::fromTheme(
"window-close"), tr(
"Close"),
this, SLOT(close()));
103 check = connect(ui->tableView, &QTableView::customContextMenuRequested,
104 this, &CFrmHistory::slotCustomContextMenuRequested);
106 ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
107 ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
110 if(m_pModelHistory) {
111 ui->tableView->setModel(m_pModelHistory);
114 resize(m_pPara->GetWindowSize());
119CFrmHistory::~CFrmHistory()
121 m_pPara->SetWindowSize(this->size());
125void CFrmHistory::slotRefresh()
127 if(m_pModelHistory) {
128 m_pModelHistory->refresh(m_pDateStart->date(), m_pDateEnd->date());
129 ui->tableView->resizeColumnsToContents();
133void CFrmHistory::on_tableView_doubleClicked(
const QModelIndex &index)
135 if(!index.isValid() || !m_pModelHistory)
return;
136 auto item = m_pModelHistory->getItem(index);
137 emit sigOpenUrl(item.url);
140void CFrmHistory::slotCustomContextMenuRequested(
const QPoint &pos)
142 auto index = ui->tableView->indexAt(pos);
143 QItemSelectionModel *selectionModel = ui->tableView->selectionModel();
144 QModelIndexList selectedIndexes;
146 selectedIndexes = selectionModel->selectedRows();
150 if (selectedIndexes.count() > 1) {
152 int selectedCount = selectedIndexes.size();
155 QAction *openSelectedAction = menu.addAction(
156 QIcon::fromTheme(
"document-open"), tr(
"Open the selected %1 urls").arg(selectedCount));
157 if(openSelectedAction) {
158 connect(openSelectedAction, &QAction::triggered,
this, [
this, selectedIndexes]() {
159 onOpenSelectedUrls(selectedIndexes);
164 QAction *deleteSelectedAction = menu.addAction(
165 QIcon::fromTheme(
"edit-delete"), tr(
"Delete the selected %1 urls").arg(selectedCount));
166 if(deleteSelectedAction) {
167 connect(deleteSelectedAction, &QAction::triggered,
this, [
this, selectedIndexes]() {
168 onDeleteSelectedItems(selectedIndexes);
175 QAction *selectAllAction = menu.addAction(
176 QIcon(
":/icons/select_all.png"), tr(
"All selected"));
178 connect(selectAllAction, &QAction::triggered, ui->tableView, &QTableView::selectAll);
181 QAction *deselectAction = menu.addAction(
182 QIcon(
":/icons/deselect.png"), tr(
"Cancel selected"));
184 connect(deselectAction, &QAction::triggered,
185 selectionModel, &QItemSelectionModel::clearSelection);
186 }
else if (index.isValid()) {
188 QString url = ui->tableView->model()->data(
189 index.siblingAtColumn(CHistoryModel::ColumnUrl)).toString();
190 QString title = ui->tableView->model()->data(
191 index.siblingAtColumn(CHistoryModel::ColumnTitle)).toString();
194 QAction *openAction = menu.addAction(QIcon::fromTheme(
"document-open"),
197 connect(openAction, &QAction::triggered,
this, [
this, url]() {
198 emit sigOpenUrl(url);
202 QAction *openNewTabAction = menu.addAction(
203 QIcon::fromTheme(
"document-open"),
204 tr(
"Open in new tab"));
206 connect(openNewTabAction, &QAction::triggered,
this, [
this, url]() {
207 emit sigOpenUrlInNewTab(url);
211 QAction *copyUrlAction = menu.addAction(QIcon::fromTheme(
"edit-copy"),
214 connect(copyUrlAction, &QAction::triggered,
this, [url]() {
215 QApplication::clipboard()->setText(url);
219 QAction *copyTitleAction = menu.addAction(
220 QIcon::fromTheme(
"edit-copy"),
223 connect(copyTitleAction, &QAction::triggered,
this, [title]() {
224 QApplication::clipboard()->setText(title);
230 QAction *deleteAction = menu.addAction(
231 QIcon::fromTheme(
"edit-delete"), tr(
"Delete"));
233 connect(deleteAction, &QAction::triggered,
this, [
this, index]() {
234 onDeleteHistoryItem(index);
237 QAction *deleteAllAction = menu.addAction(
238 QIcon::fromTheme(
"edit-delete"),
239 tr(
"Delete all urls %1").arg(title.left(30)));
241 connect(deleteAllAction, &QAction::triggered,
this, [
this, url]() {
242 m_pModelHistory->removeItems(url);
246 QString domain = extractDomain(url);
247 if (!domain.isEmpty()) {
248 QAction *deleteDomainAction = menu.addAction(
249 QIcon::fromTheme(
"edit-delete"),
250 tr(
"Delete all urls from %1").arg(domain));
251 if(deleteDomainAction)
252 connect(deleteDomainAction, &QAction::triggered,
this, [
this, domain]() {
253 onDeleteHistoryByDomain(domain);
260 QAction *propertiesAction = menu.addAction(
261 QIcon::fromTheme(
"document-properties"), tr(
"Properties"));
263 connect(propertiesAction, &QAction::triggered,
this, [
this, index]() {
264 onShowHistoryProperties(index);
271 QAction *refreshAction = menu.addAction(
272 QIcon::fromTheme(
"view-refresh"), tr(
"Refresh"));
274 connect(refreshAction, &QAction::triggered,
this, [&]() {
276 m_pModelHistory->refresh();
279 QAction *clearAllAction = menu.addAction(
280 QIcon::fromTheme(
"edit-clear"), tr(
"Clear all urls"));
282 connect(clearAllAction, &QAction::triggered,
this, [&]() {
283 if(m_pModelHistory->removeRows(0, m_pModelHistory->rowCount()));
292 menu.exec(ui->tableView->viewport()->mapToGlobal(pos));
295QString CFrmHistory::extractDomain(
const QString &url)
298 if (qurl.isValid()) {
299 QString host = qurl.host();
301 if (host.startsWith(
"www.")) {
309void CFrmHistory::onDeleteHistoryItem(
const QModelIndex &index)
311 if (!index.isValid() || !m_pModelHistory) {
315 QString title = ui->tableView->model()->data(
316 index.siblingAtColumn(CHistoryModel::ColumnTitle)).toString();
317 if(title.isEmpty()) {
318 title = ui->tableView->model()->data(
319 index.siblingAtColumn(CHistoryModel::ColumnUrl)).toString();
322 QMessageBox::StandardButton reply = QMessageBox::question(
324 tr(
"Delete the url"),
325 tr(
"Are you sure you want to delete the url \"%1\"?").arg(title),
326 QMessageBox::Yes | QMessageBox::No,
330 if (reply == QMessageBox::Yes) {
332 if (m_pModelHistory->removeRow(index.row())) {
333 qDebug(log) <<
"History item deleted";
338void CFrmHistory::onDeleteHistoryByDomain(
const QString &domain)
340 if (domain.isEmpty() || !m_pModelHistory) {
345 QMessageBox::StandardButton reply = QMessageBox::question(
347 tr(
"Delete the url"),
348 tr(
"Are you sure you want to delete all url from \"%1\"?").arg(domain),
349 QMessageBox::Yes | QMessageBox::No,
353 if (reply == QMessageBox::Yes) {
354 m_pModelHistory->removeDomainItems(domain);
358void CFrmHistory::onShowHistoryProperties(
const QModelIndex &index)
360 if (!index.isValid() || !m_pModelHistory) {
365 auto item = m_pModelHistory->getItem(index);
367 QString details = QString(
369 "<table border='0' cellspacing='5'>"
370 "<tr><td><b>%2</b></td><td>%3</td></tr>"
371 "<tr><td><b>%4</b></td><td>%5</td></tr>"
373 .arg(item.title.toHtmlEscaped())
375 .arg(item.url.toHtmlEscaped())
376 .arg(tr(
"Visit Time:"))
377 .arg(item.visitTime.toString(QLocale::system().dateFormat()))
380 QMessageBox::information(
this, tr(
"Properties"), details);
383void CFrmHistory::onOpenSelectedUrls(
const QModelIndexList &indexes)
385 if (indexes.isEmpty())
return;
386 for (
const QModelIndex &index : indexes) {
387 QString url = ui->tableView->model()->data(
388 index.siblingAtColumn(CHistoryModel::ColumnUrl)).toString();
389 if (!url.isEmpty()) {
390 emit sigOpenUrlInNewTab(url);
395void CFrmHistory::onDeleteSelectedItems(
const QModelIndexList &indexes)
397 if (indexes.isEmpty() || !m_pModelHistory)
return;
399 QMessageBox::StandardButton reply = QMessageBox::question(
401 tr(
"Delete the urls"),
402 tr(
"Are you sure you want to delete the selected %1 urls?").arg(indexes.size()),
403 QMessageBox::Yes | QMessageBox::No,
407 if (reply == QMessageBox::Yes) {
410 for (
const QModelIndex &index : indexes) {
411 rows.append(index.row());
415 std::sort(rows.begin(), rows.end(), std::greater<int>());
418 for (
int row : rows) {
419 m_pModelHistory->removeRow(row);
422 qDebug(log) <<
"Deleted" << indexes.size() <<
"history items";
426void CFrmHistory::slotImport()
428 QString filename = QFileDialog::getOpenFileName(
429 this, tr(
"Import histories"),
430 RabbitCommon::CDir::Instance()->GetDirUserDocument(),
431 tr(
"JSON (*.json);; All files (*.*)"));
433 if (filename.isEmpty())
return;
435 QFileInfo fi(filename);
436 if(0 == fi.suffix().compare(
"json", Qt::CaseInsensitive)) {
437 if (m_pModelHistory->importFromJson(filename)) {
439 QMessageBox::information(
this, tr(
"Import histories"),
440 tr(
"Successfully imported histories from JSON file: %1").arg(filename));
442 QMessageBox::critical(
this, tr(
"Import histories"),
443 tr(
"Failed to import histories from JSON file: %1").arg(filename));
448 QMessageBox::critical(
this, tr(
"Import histories"),
449 tr(
"Invalid file: %1").arg(filename) +
"\n\n"
450 + tr(
"Please use JSON or CSV file"));
453void CFrmHistory::slotExport()
455 QString filename = QFileDialog::getSaveFileName(
456 this, tr(
"Export histories"),
457 RabbitCommon::CDir::Instance()->GetDirUserDocument(),
458 tr(
"JSON (*.json);; All files (*.*)"));
460 if (filename.isEmpty())
return;
462 QFileInfo fi(filename);
463 if(0 == fi.suffix().compare(
"json", Qt::CaseInsensitive)) {
464 if (m_pModelHistory->exportToJson(filename)) {
465 QMessageBox::information(
this, tr(
"Export histories"),
466 tr(
"Histories successfully exported to JSON file: %1").arg(filename));
468 QMessageBox::critical(
this, tr(
"Export histories"),
469 tr(
"Failed to export histories to JSON file: %1").arg(filename));
474 QMessageBox::critical(
this, tr(
"Export histories"),
475 tr(
"Invalid file: %1").arg(filename) +
"\n\n"
476 + tr(
"Please use JSON or CSV file"));
479void CFrmHistory::slotComboxIndexChanged(
int index)
481 qDebug(log) <<
"Change days";
482 QComboBox* pCB = qobject_cast<QComboBox*>(sender());
484 int d = pCB->itemData(index).toInt();
485 QDate curDate = QDate::currentDate();
487 m_pDateEnd->setDate(curDate);
494 m_pDateStart->setDate(curDate.addDays(-1 * d));
500void CFrmHistory::slotLimit(
int v)
503 m_pPara->SetDatabaseViewLimit(v);
507void CFrmHistory::on_leSearch_textChanged(
const QString &keyword)
509 m_pModelHistory->search(keyword);