8#include <QLoggingCategory>
16#include "FrmHistory.h"
17#include "RabbitCommonDir.h"
18#include "ui_FrmHistory.h"
20static Q_LOGGING_CATEGORY(log,
"WebBrowser.History")
25 , m_pModelHistory(
nullptr)
27 , m_pDateStart(
nullptr)
34 setWindowTitle(tr(
"History"));
36 QToolBar* pToolBar =
new QToolBar(
this);
38 QDate curDate = QDate::currentDate();
39 m_pDateStart =
new QDateEdit(curDate.addDays(-7), pToolBar);
41 m_pDateStart->setToolTip(tr(
"Start date"));
42 m_pDateEnd =
new QDateEdit(curDate, pToolBar);
44 m_pDateEnd->setToolTip(tr(
"End date"));
45 QComboBox* pCB =
new QComboBox(pToolBar);
46 pCB->addItem(tr(
"One day"), 1);
47 pCB->addItem(tr(
"Two days"), 2);
48 pCB->addItem(tr(
"One Week"), 7);
49 pCB->addItem(tr(
"One month"), curDate.daysInMonth());
50 pCB->setCurrentIndex(2);
51 check = connect(pCB, SIGNAL(currentIndexChanged(
int)),
52 this, SLOT(slotComboxIndexChanged(
int)));
55 pToolBar->addWidget(pCB);
56 pToolBar->addWidget(m_pDateStart);
57 pToolBar->addWidget(m_pDateEnd);
59 QAction* pRefresh = pToolBar->addAction(
60 QIcon::fromTheme(
"view-refresh"), tr(
"Refresh"),
61 this, &CFrmHistory::slotRefresh);
63 pRefresh->setToolTip(pRefresh->text());
64 pRefresh->setStatusTip(pRefresh->text());
67 pToolBar->addSeparator();
68 QSpinBox* pSBLimit =
new QSpinBox(pToolBar);
70 pToolBar->addWidget(pSBLimit);
71 pSBLimit->setToolTip(tr(
"Limit"));
74 nMax = qMax(nMax, m_pPara->GetDatabaseViewLimit());
75 pSBLimit->setRange(-1, nMax);
77 pSBLimit->setValue(m_pPara->GetDatabaseViewLimit());
78 check = connect(pSBLimit, SIGNAL(valueChanged(
int)),
this, SLOT(slotLimit(
int)));
82 pToolBar->addSeparator();
84 QIcon::fromTheme(
"import"), tr(
"Import"),
this, SLOT(slotImport()));
86 QIcon::fromTheme(
"export"), tr(
"Export"),
this, SLOT(slotExport()));
87 pToolBar->addSeparator();
99 pToolBar->addAction(QIcon::fromTheme(
"window-close"), tr(
"Close"),
this, SLOT(close()));
101 QLayout* pLayout =
nullptr;
102 pLayout =
new QVBoxLayout(
this);
103 pLayout->addWidget(pToolBar);
104 pLayout->addWidget(ui->splitter);
107 check = connect(ui->tableView, &QTableView::customContextMenuRequested,
108 this, &CFrmHistory::slotCustomContextMenuRequested);
110 ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
111 ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
114 if(m_pModelHistory) {
115 ui->tableView->setModel(m_pModelHistory);
118 resize(m_pPara->GetWindowSize());
123CFrmHistory::~CFrmHistory()
125 m_pPara->SetWindowSize(this->size());
129void CFrmHistory::slotRefresh()
131 if(m_pModelHistory) {
132 m_pModelHistory->refresh(m_pDateStart->date(), m_pDateEnd->date());
133 ui->tableView->resizeColumnsToContents();
137void CFrmHistory::on_tableView_doubleClicked(
const QModelIndex &index)
139 if(!index.isValid() || !m_pModelHistory)
return;
140 auto item = m_pModelHistory->getItem(index);
141 emit sigOpenUrl(item.url);
144void CFrmHistory::slotCustomContextMenuRequested(
const QPoint &pos)
146 auto index = ui->tableView->indexAt(pos);
147 QItemSelectionModel *selectionModel = ui->tableView->selectionModel();
148 QModelIndexList selectedIndexes;
150 selectedIndexes = selectionModel->selectedRows();
154 if (selectedIndexes.count() > 1) {
156 int selectedCount = selectedIndexes.size();
159 QAction *openSelectedAction = menu.addAction(
160 QIcon::fromTheme(
"document-open"), tr(
"Open the selected %1 urls").arg(selectedCount));
161 if(openSelectedAction) {
162 connect(openSelectedAction, &QAction::triggered,
this, [
this, selectedIndexes]() {
163 onOpenSelectedUrls(selectedIndexes);
168 QAction *deleteSelectedAction = menu.addAction(
169 QIcon::fromTheme(
"edit-delete"), tr(
"Delete the selected %1 urls").arg(selectedCount));
170 if(deleteSelectedAction) {
171 connect(deleteSelectedAction, &QAction::triggered,
this, [
this, selectedIndexes]() {
172 onDeleteSelectedItems(selectedIndexes);
179 QAction *selectAllAction = menu.addAction(
180 QIcon(
":/icons/select_all.png"), tr(
"All selected"));
182 connect(selectAllAction, &QAction::triggered, ui->tableView, &QTableView::selectAll);
185 QAction *deselectAction = menu.addAction(
186 QIcon(
":/icons/deselect.png"), tr(
"Cancel selected"));
188 connect(deselectAction, &QAction::triggered,
189 selectionModel, &QItemSelectionModel::clearSelection);
190 }
else if (index.isValid()) {
192 QString url = ui->tableView->model()->data(
193 index.siblingAtColumn(CHistoryModel::ColumnUrl)).toString();
194 QString title = ui->tableView->model()->data(
195 index.siblingAtColumn(CHistoryModel::ColumnTitle)).toString();
198 QAction *openAction = menu.addAction(QIcon::fromTheme(
"document-open"),
201 connect(openAction, &QAction::triggered,
this, [
this, url]() {
202 emit sigOpenUrl(url);
206 QAction *openNewTabAction = menu.addAction(
207 QIcon::fromTheme(
"document-open"),
208 tr(
"Open in new tab"));
210 connect(openNewTabAction, &QAction::triggered,
this, [
this, url]() {
211 emit sigOpenUrlInNewTab(url);
215 QAction *copyUrlAction = menu.addAction(QIcon::fromTheme(
"edit-copy"),
218 connect(copyUrlAction, &QAction::triggered,
this, [url]() {
219 QApplication::clipboard()->setText(url);
223 QAction *copyTitleAction = menu.addAction(
224 QIcon::fromTheme(
"edit-copy"),
227 connect(copyTitleAction, &QAction::triggered,
this, [title]() {
228 QApplication::clipboard()->setText(title);
234 QAction *deleteAction = menu.addAction(
235 QIcon::fromTheme(
"edit-delete"), tr(
"Delete"));
237 connect(deleteAction, &QAction::triggered,
this, [
this, index]() {
238 onDeleteHistoryItem(index);
241 QAction *deleteAllAction = menu.addAction(
242 QIcon::fromTheme(
"edit-delete"),
243 tr(
"Delete all urls %1").arg(title.left(30)));
245 connect(deleteAllAction, &QAction::triggered,
this, [
this, url]() {
246 m_pModelHistory->removeItems(url);
250 QString domain = extractDomain(url);
251 if (!domain.isEmpty()) {
252 QAction *deleteDomainAction = menu.addAction(
253 QIcon::fromTheme(
"edit-delete"),
254 tr(
"Delete all urls from %1").arg(domain));
255 if(deleteDomainAction)
256 connect(deleteDomainAction, &QAction::triggered,
this, [
this, domain]() {
257 onDeleteHistoryByDomain(domain);
264 QAction *propertiesAction = menu.addAction(
265 QIcon::fromTheme(
"document-properties"), tr(
"Properties"));
267 connect(propertiesAction, &QAction::triggered,
this, [
this, index]() {
268 onShowHistoryProperties(index);
275 QAction *refreshAction = menu.addAction(
276 QIcon::fromTheme(
"view-refresh"), tr(
"Refresh"));
278 connect(refreshAction, &QAction::triggered,
this, [&]() {
280 m_pModelHistory->refresh();
283 QAction *clearAllAction = menu.addAction(
284 QIcon::fromTheme(
"edit-clear"), tr(
"Clear all urls"));
286 connect(clearAllAction, &QAction::triggered,
this, [&]() {
287 if(m_pModelHistory->removeRows(0, m_pModelHistory->rowCount()));
296 menu.exec(ui->tableView->viewport()->mapToGlobal(pos));
299QString CFrmHistory::extractDomain(
const QString &url)
302 if (qurl.isValid()) {
303 QString host = qurl.host();
305 if (host.startsWith(
"www.")) {
313void CFrmHistory::onDeleteHistoryItem(
const QModelIndex &index)
315 if (!index.isValid() || !m_pModelHistory) {
319 QString title = ui->tableView->model()->data(
320 index.siblingAtColumn(CHistoryModel::ColumnTitle)).toString();
321 if(title.isEmpty()) {
322 title = ui->tableView->model()->data(
323 index.siblingAtColumn(CHistoryModel::ColumnUrl)).toString();
326 QMessageBox::StandardButton reply = QMessageBox::question(
328 tr(
"Delete the url"),
329 tr(
"Are you sure you want to delete the url \"%1\"?").arg(title),
330 QMessageBox::Yes | QMessageBox::No,
334 if (reply == QMessageBox::Yes) {
336 if (m_pModelHistory->removeRow(index.row())) {
337 qDebug(log) <<
"History item deleted";
342void CFrmHistory::onDeleteHistoryByDomain(
const QString &domain)
344 if (domain.isEmpty() || !m_pModelHistory) {
349 QMessageBox::StandardButton reply = QMessageBox::question(
351 tr(
"Delete the url"),
352 tr(
"Are you sure you want to delete all url from \"%1\"?").arg(domain),
353 QMessageBox::Yes | QMessageBox::No,
357 if (reply == QMessageBox::Yes) {
358 m_pModelHistory->removeDomainItems(domain);
362void CFrmHistory::onShowHistoryProperties(
const QModelIndex &index)
364 if (!index.isValid() || !m_pModelHistory) {
369 auto item = m_pModelHistory->getItem(index);
371 QString details = QString(
373 "<table border='0' cellspacing='5'>"
374 "<tr><td><b>%2</b></td><td>%3</td></tr>"
375 "<tr><td><b>%4</b></td><td>%5</td></tr>"
377 .arg(item.title.toHtmlEscaped())
379 .arg(item.url.toHtmlEscaped())
380 .arg(tr(
"Visit Time:"))
381 .arg(item.visitTime.toString(QLocale::system().dateFormat()))
384 QMessageBox::information(
this, tr(
"Properties"), details);
387void CFrmHistory::onOpenSelectedUrls(
const QModelIndexList &indexes)
389 if (indexes.isEmpty())
return;
390 for (
const QModelIndex &index : indexes) {
391 QString url = ui->tableView->model()->data(
392 index.siblingAtColumn(CHistoryModel::ColumnUrl)).toString();
393 if (!url.isEmpty()) {
394 emit sigOpenUrlInNewTab(url);
399void CFrmHistory::onDeleteSelectedItems(
const QModelIndexList &indexes)
401 if (indexes.isEmpty() || !m_pModelHistory)
return;
403 QMessageBox::StandardButton reply = QMessageBox::question(
405 tr(
"Delete the urls"),
406 tr(
"Are you sure you want to delete the selected %1 urls?").arg(indexes.size()),
407 QMessageBox::Yes | QMessageBox::No,
411 if (reply == QMessageBox::Yes) {
414 for (
const QModelIndex &index : indexes) {
415 rows.append(index.row());
419 std::sort(rows.begin(), rows.end(), std::greater<int>());
422 for (
int row : rows) {
423 m_pModelHistory->removeRow(row);
426 qDebug(log) <<
"Deleted" << indexes.size() <<
"history items";
430void CFrmHistory::slotImport()
432 QString filename = QFileDialog::getOpenFileName(
433 this, tr(
"Import histories"),
434 RabbitCommon::CDir::Instance()->GetDirUserDocument(),
435 tr(
"JSON (*.json);; CSV file (*.csv);; All files (*.*)"));
437 if (!filename.isEmpty()) {
438 QFileInfo fi(filename);
439 if(0 == fi.suffix().compare(
"json", Qt::CaseInsensitive)) {
440 if (m_pModelHistory->importFromJson(filename)) {
442 QMessageBox::information(
this, tr(
"Success"), tr(
"Histories import from json file successfully"));
444 QMessageBox::warning(
this, tr(
"Failure"), tr(
"Failed to import histories from json file"));
448 if(0 == fi.suffix().compare(
"csv", Qt::CaseInsensitive)) {
449 if(m_pModelHistory->importFromCSV(filename)) {
451 QMessageBox::information(
this, tr(
"Success"), tr(
"Histories import from csv file successfully"));
453 QMessageBox::warning(
this, tr(
"Failure"), tr(
"Failed to import histories from csv file"));
459void CFrmHistory::slotExport()
461 QString filename = QFileDialog::getSaveFileName(
462 this, tr(
"Export histories"),
463 RabbitCommon::CDir::Instance()->GetDirUserDocument(),
464 tr(
"JSON (*.json);; CSV (*.csv);; All files (*.*)"));
466 if (!filename.isEmpty()) {
467 QFileInfo fi(filename);
468 if(0 == fi.suffix().compare(
"json", Qt::CaseInsensitive)) {
469 if (m_pModelHistory->exportToJson(filename)) {
470 QMessageBox::information(
this, tr(
"Success"), tr(
"Histories exported to json file successfully"));
472 QMessageBox::warning(
this, tr(
"Failure"), tr(
"Failed to export histories to json file"));
476 if(0 == fi.suffix().compare(
"csv", Qt::CaseInsensitive)) {
477 if (m_pModelHistory->exportToCSV(filename)) {
478 QMessageBox::information(
this, tr(
"Success"), tr(
"Histories exported to csv file successfully"));
480 QMessageBox::warning(
this, tr(
"Failure"), tr(
"Failed to export histories to csv file"));
486void CFrmHistory::slotComboxIndexChanged(
int index)
488 qDebug(log) <<
"Change days";
489 QComboBox* pCB = qobject_cast<QComboBox*>(sender());
491 int d = pCB->itemData(index).toInt();
492 QDate curDate = QDate::currentDate();
494 m_pDateEnd->setDate(curDate);
501 m_pDateStart->setDate(curDate.addDays(-1 * d));
507void CFrmHistory::slotLimit(
int v)
510 m_pPara->SetDatabaseViewLimit(v);