File indexing completed on 2024-04-21 03:51:00
0001 /* 0002 SPDX-FileCopyrightText: 2014 Inge Wallin <inge@lysator.liu.se> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "dashboard.h" 0007 0008 #include <QAction> 0009 #include <QDebug> 0010 #include <QMessageBox> 0011 #include <QStandardItemModel> 0012 #include <QTimer> 0013 0014 //#include <KMimeType> 0015 #include <KActionCollection> 0016 #include <KNSWidgets/Button> 0017 0018 #include "../utils.h" 0019 #include "buttondelegate.h" 0020 #include "parleydocument.h" 0021 #include "parleymainwindow.h" 0022 #include "practice/imagewidget.h" 0023 #include "practice/themedbackgroundrenderer.h" 0024 #include "statistics/statisticsmodel.h" 0025 0026 #include "collection.h" 0027 #include "collectionwidget.h" 0028 #include "gradereferencewidget.h" 0029 0030 // ================================================================ 0031 // class Dashboard 0032 0033 int ROWSIZE = 4; // Number of collection widgets (+ 1 initial spacerItem) per row 0034 0035 Dashboard::Dashboard(ParleyMainWindow *parent) 0036 : KXmlGuiWindow(parent) 0037 , m_mainWindow(parent) 0038 { 0039 // KXmlGui 0040 setXMLFile(QStringLiteral("dashboardui.rc")); 0041 setObjectName(QStringLiteral("Dashboard")); 0042 0043 m_widget = new Practice::ImageWidget(this); 0044 0045 m_widget->setScalingEnabled(false, false); 0046 m_widget->setKeepAspectRatio(Qt::IgnoreAspectRatio); 0047 m_widget->setFadingEnabled(false); 0048 0049 m_ui.setupUi(m_widget); 0050 setCentralWidget(m_widget); 0051 0052 QFont font = m_ui.recentLabel->font(); 0053 font.setBold(true); 0054 m_ui.recentLabel->setFont(font); 0055 font = m_ui.completedLabel->font(); 0056 font.setBold(true); 0057 m_ui.completedLabel->setFont(font); 0058 0059 m_ui.newButton->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); 0060 m_ui.openButton->setIcon(QIcon::fromTheme(QStringLiteral("document-open"))); 0061 m_ui.ghnsButton->setIcon(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff"))); 0062 0063 GradeReferenceWidget *gradeReferenceWidget = new GradeReferenceWidget(); 0064 gradeReferenceWidget->setMinimumSize(m_widget->width(), 50); 0065 m_ui.gridLayout->addWidget(gradeReferenceWidget, 1, 0, 1, ROWSIZE, Qt::AlignCenter); 0066 0067 m_subGridLayout = new QGridLayout(); 0068 m_subGridLayout->setHorizontalSpacing(50); 0069 m_subGridLayout->setVerticalSpacing(30); 0070 m_ui.gridLayout_2->addLayout(m_subGridLayout, 2, 0, 1, 1); 0071 0072 m_completedGridLayout = new QGridLayout(); 0073 m_completedGridLayout->setHorizontalSpacing(50); 0074 m_completedGridLayout->setVerticalSpacing(30); 0075 m_ui.gridLayout_2->addLayout(m_completedGridLayout, 6, 0, 1, 1); 0076 0077 populateMap(); 0078 populateGrid(); 0079 0080 // Signals from the main buttons. 0081 ParleyDocument *doc = m_mainWindow->parleyDocument(); 0082 connect(m_ui.newButton, &QAbstractButton::clicked, m_mainWindow, &ParleyMainWindow::slotFileNew); 0083 connect(m_ui.openButton, &QAbstractButton::clicked, doc, &ParleyDocument::slotFileOpen); 0084 connect(m_ui.ghnsButton, &KNSWidgets::Button::dialogFinished, doc, &ParleyDocument::slotGHNS); 0085 m_ui.ghnsButton->setConfigFile("parley.knsrc"); 0086 0087 KConfigGroup cfg(KSharedConfig::openConfig(QStringLiteral("parleyrc")), objectName()); 0088 applyMainWindowSettings(cfg); 0089 0090 m_themedBackgroundRenderer = new Practice::ThemedBackgroundRenderer(this, QStringLiteral("startpagethemecache.bin")); 0091 0092 // Set theme and prepare for future theme changes. 0093 connect(Prefs::self(), &KCoreConfigSkeleton::configChanged, this, &Dashboard::setTheme); 0094 setTheme(); 0095 0096 connect(m_themedBackgroundRenderer, &Practice::ThemedBackgroundRenderer::backgroundChanged, this, &Dashboard::backgroundChanged); 0097 connect(m_widget, &Practice::ImageWidget::sizeChanged, this, &Dashboard::updateBackground); 0098 0099 QAction *updateAction = new QAction(this); 0100 updateAction->connect(updateAction, &QAction::triggered, this, &Dashboard::updateWidgets); 0101 actionCollection()->addAction(QStringLiteral("update_dashboard"), updateAction); 0102 actionCollection()->setDefaultShortcut(updateAction, QKeySequence(Qt::Key_F5)); 0103 } 0104 0105 Dashboard::~Dashboard() 0106 { 0107 KConfigGroup cfg(KSharedConfig::openConfig(QStringLiteral("parleyrc")), objectName()); 0108 saveMainWindowSettings(cfg); 0109 } 0110 0111 void Dashboard::clearGrid() 0112 { 0113 remove(m_subGridLayout, m_subGridLayout->rowCount() - 1, m_subGridLayout->columnCount() - 1, true); 0114 remove(m_completedGridLayout, m_completedGridLayout->rowCount() - 1, m_completedGridLayout->columnCount() - 1, true); 0115 } 0116 0117 /** 0118 * Helper function. Removes all layout items within the given @a layout 0119 * which either span the given @a row or @a column. If @a deleteWidgets 0120 * is true, all concerned child widgets become not only removed from the 0121 * layout, but also deleted. 0122 */ 0123 void Dashboard::remove(QGridLayout *layout, int row, int column, bool deleteWidgets) 0124 { 0125 // We avoid usage of QGridLayout::itemAtPosition() here to improve performance. 0126 for (int i = layout->count() - 1; i >= 0; i--) { 0127 int r, c, rs, cs; 0128 layout->getItemPosition(i, &r, &c, &rs, &cs); 0129 if ((r + rs - 1 <= row) || (c + cs - 1 <= column)) { 0130 // This layout item is subject to deletion. 0131 QLayoutItem *item = layout->takeAt(i); 0132 if (deleteWidgets) { 0133 deleteChildWidgets(item); 0134 } 0135 delete item; 0136 } 0137 } 0138 } 0139 0140 /** 0141 * Helper function. Deletes all child widgets of the given layout @a item. 0142 */ 0143 void Dashboard::deleteChildWidgets(QLayoutItem *item) 0144 { 0145 if (item->layout()) { 0146 // Process all child items recursively. 0147 for (int i = 0; i < item->layout()->count(); i++) { 0148 deleteChildWidgets(item->layout()->itemAt(i)); 0149 } 0150 } 0151 delete item->widget(); 0152 } 0153 0154 void Dashboard::populateMap() 0155 { 0156 KConfig parleyConfig(QStringLiteral("parleyrc")); 0157 KConfigGroup recentFilesGroup(&parleyConfig, "Recent Files"); 0158 for (int i = recentFilesGroup.keyList().count() / 2; i > 0; i--) { 0159 QString urlString = recentFilesGroup.readPathEntry("File" + QString::number(i), QString()); 0160 QString nameString = recentFilesGroup.readEntry("Name" + QString::number(i), QString()); 0161 m_recentFilesMap.insert(urlString, nameString); 0162 } 0163 } 0164 0165 void Dashboard::populateGrid() 0166 { 0167 int j = 0, k = 0, jc = 0, kc = 0; 0168 0169 // key = url, value = title 0170 QMapIterator<QString, QString> it(m_recentFilesMap); 0171 while (it.hasNext()) { 0172 it.next(); 0173 QString urlString = it.key(); 0174 0175 QUrl url(QUrl::fromLocalFile(urlString)); 0176 Collection *collection = new Collection(&url, this); 0177 0178 WordCount dueWords; 0179 int percentageCompleted = dueWords.percentageCompleted(); 0180 0181 m_urlArray[k] = url; 0182 if (percentageCompleted != 100) { 0183 if (j % ROWSIZE == 0) { 0184 m_subGridLayout->addItem(new QSpacerItem(50, 1), j / ROWSIZE, 0); 0185 j++; 0186 } 0187 } else { 0188 if (jc % ROWSIZE == 0) { 0189 m_completedGridLayout->addItem(new QSpacerItem(50, 1), jc / ROWSIZE, 0); 0190 jc++; 0191 } 0192 } 0193 0194 CollectionWidget *backWidget = new CollectionWidget(collection, &dueWords, this); 0195 m_collectionWidgets.append(backWidget); 0196 if (percentageCompleted != 100) { 0197 backWidget->setFixedSize(COLLWIDTH, COLLHEIGHT1); 0198 backWidget->setMinimumSize(COLLWIDTH, COLLHEIGHT1); 0199 m_subGridLayout->addWidget(backWidget, j / ROWSIZE, j % ROWSIZE); 0200 } else { 0201 backWidget->setFixedSize(COLLWIDTH, COLLHEIGHT2); 0202 backWidget->setMinimumSize(COLLWIDTH, COLLHEIGHT2); 0203 m_completedGridLayout->addWidget(backWidget, jc / ROWSIZE, jc % ROWSIZE); 0204 } 0205 0206 connect(backWidget, &CollectionWidget::practiceButtonClicked, this, [=] { 0207 slotPracticeButtonClicked(urlString); 0208 }); 0209 connect(backWidget, &CollectionWidget::removeButtonClicked, this, [=] { 0210 slotRemoveButtonClicked(urlString); 0211 }); 0212 0213 if (percentageCompleted != 100) { 0214 j++; 0215 } else { 0216 jc++; 0217 kc++; 0218 } 0219 0220 k++; 0221 } 0222 0223 m_count = k; 0224 m_completedGridLayout->addItem(new QSpacerItem(50, 1, QSizePolicy::Expanding, QSizePolicy::Fixed), 0225 m_completedGridLayout->rowCount() - 1, 0226 m_completedGridLayout->columnCount()); 0227 m_subGridLayout->addItem(new QSpacerItem(50, 1, QSizePolicy::Expanding, QSizePolicy::Fixed), 0228 m_subGridLayout->rowCount() - 1, 0229 m_subGridLayout->columnCount()); 0230 if (k - kc) { 0231 m_ui.recentLabel->setText(i18n("Active Collections")); 0232 } else { 0233 m_ui.recentLabel->clear(); 0234 } 0235 if (kc) { 0236 m_ui.completedLabel->setText(i18n("Completed Collections")); 0237 } else { 0238 m_ui.completedLabel->clear(); 0239 } 0240 } 0241 0242 void Dashboard::statisticsHandler(const QUrl &url) 0243 { 0244 #if 1 0245 Q_UNUSED(url); 0246 #else 0247 if (!m_mainWindow->parleyDocument()->open(url)) { 0248 return; 0249 } 0250 0251 // Find due words. TODO find a better way. 0252 m_mainWindow->m_sessionManager.setDocument(m_mainWindow->parleyDocument()->document()); 0253 0254 qDebug() << "Session Manager, allEntryCount=" << m_mainWindow->m_sessionManager.allDueEntryCount(); 0255 statisticsWidget->setDocument(m_mainWindow->parleyDocument()->document()); 0256 0257 // Find the percentage completion, to categorize as active or completed collection. 0258 QModelIndex index = statisticsWidget->statisticsModel()->index(0, 2, QModelIndex()); 0259 qDebug() << "Percentage:" << index.data(StatisticsModel::TotalPercent).toInt(); 0260 #endif 0261 } 0262 0263 void Dashboard::slotOpenUrl(const QUrl &url) 0264 { 0265 if (!m_mainWindow->parleyDocument()->open(url)) { 0266 return; 0267 } 0268 m_mainWindow->showEditor(); 0269 } 0270 0271 void Dashboard::slotPracticeButtonClicked(const QString &urlString) 0272 { 0273 // qDebug() << urlString; 0274 QUrl url(QUrl::fromLocalFile(urlString)); 0275 m_openUrl = url; 0276 QTimer::singleShot(0, this, &Dashboard::slotDoubleClickOpen); 0277 } 0278 0279 void Dashboard::slotRemoveButtonClicked(const QString &urlString) 0280 { 0281 qDebug() << urlString; 0282 0283 QMessageBox::StandardButton reply; 0284 reply = QMessageBox::question(this, i18n("Remove"), i18n("Are you sure you want to remove this collection?"), QMessageBox::Yes | QMessageBox::No); 0285 if (reply == QMessageBox::Yes) { 0286 m_recentFilesMap.remove(urlString); 0287 m_mainWindow->removeRecentFile(QUrl::fromLocalFile(urlString)); 0288 clearGrid(); 0289 populateGrid(); 0290 } 0291 } 0292 0293 void Dashboard::slotDoubleClickOpen() 0294 { 0295 slotPracticeUrl(m_openUrl); 0296 } 0297 0298 void Dashboard::slotPracticeUrl(const QUrl &url) 0299 { 0300 if (!m_mainWindow->parleyDocument()->open(url)) { 0301 return; 0302 } 0303 0304 // This used to go to the practice configuration but both I and 0305 // some users wanted to go directly to the practice so I'm testing 0306 // out this for a while. 0307 m_mainWindow->showPracticeConfiguration(); 0308 // m_mainWindow->showPractice(); 0309 } 0310 0311 void Dashboard::backgroundChanged(const QPixmap &pixmap) 0312 { 0313 m_widget->setPixmap(pixmap); 0314 } 0315 0316 void Dashboard::setTheme() 0317 { 0318 m_themedBackgroundRenderer->setTheme(Prefs::theme()); 0319 updateFontColors(); 0320 updateBackground(); 0321 m_widget->setContentsMargins(m_themedBackgroundRenderer->contentMargins()); 0322 } 0323 0324 void Dashboard::updateWidgets() 0325 { 0326 for (CollectionWidget *cw : qAsConst(m_collectionWidgets)) { 0327 cw->updateDue(); 0328 } 0329 } 0330 void Dashboard::updateFontColors() 0331 { 0332 QPalette p(QApplication::palette()); 0333 QColor c = m_themedBackgroundRenderer->fontColor(QStringLiteral("Start"), p.color(QPalette::Active, QPalette::WindowText)); 0334 p.setColor(QPalette::Base, Qt::transparent); 0335 p.setColor(QPalette::Text, c); 0336 p.setColor(QPalette::WindowText, c); 0337 m_widget->setPalette(p); 0338 } 0339 0340 void Dashboard::updateBackground() 0341 { 0342 m_themedBackgroundRenderer->clearRects(); 0343 m_themedBackgroundRenderer->addRect(QStringLiteral("startbackground"), QRect(QPoint(), m_widget->size())); 0344 QPixmap pixmap = m_themedBackgroundRenderer->getScaledBackground(); 0345 if (!pixmap.isNull()) { 0346 m_widget->setPixmap(pixmap); 0347 } 0348 m_themedBackgroundRenderer->updateBackground(); 0349 } 0350 0351 #include "moc_dashboard.cpp"