File indexing completed on 2024-05-12 16:39:24
0001 /* This file is part of the KDE project 0002 Copyright (C) 2017 Dag Andersen <danders@get2net.dk> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 Boston, MA 02110-1301, USA. 0018 */ 0019 0020 // clazy:excludeall=qstring-arg 0021 #include "WelcomeView.h" 0022 0023 #include <kptconfigbase.h> 0024 #include "kptcommand.h" 0025 #include "kptdebug.h" 0026 #include "Help.h" 0027 #include "kptpart.h" 0028 #include "kptmaindocument.h" 0029 0030 #include <KoIcon.h> 0031 #include <KoApplication.h> 0032 #include <KoMainWindow.h> 0033 #include <KoDocument.h> 0034 #include <KoFileDialog.h> 0035 0036 #include <QAction> 0037 #include <QStringList> 0038 #include <QStandardItemModel> 0039 #include <QItemSelectionModel> 0040 #include <QItemSelection> 0041 #include <QUrl> 0042 #include <QIcon> 0043 #include <QStandardPaths> 0044 #include <QEvent> 0045 0046 const QLoggingCategory &PLANWELCOME_LOG() 0047 { 0048 static const QLoggingCategory category("calligra.plan.welcome"); 0049 return category; 0050 } 0051 0052 #define debugWelcome qCDebug(PLANWELCOME_LOG)<<Q_FUNC_INFO 0053 #define warnWelcome qCWarning(PLANWELCOME_LOG) 0054 #define errorWelcome qCCritical(PLANWELCOME_LOG) 0055 0056 namespace KPlato { 0057 class RecentFilesModel : public QStandardItemModel 0058 { 0059 public: 0060 RecentFilesModel(QObject *parent = 0); 0061 Qt::ItemFlags flags(const QModelIndex &idx) const override; 0062 QVariant data(const QModelIndex &idx, int role) const override; 0063 void populate(const QList<QAction*> actions); 0064 }; 0065 } 0066 0067 using namespace KPlato; 0068 0069 RecentFilesModel::RecentFilesModel(QObject *parent) 0070 : QStandardItemModel(parent) 0071 { 0072 } 0073 0074 Qt::ItemFlags RecentFilesModel::flags(const QModelIndex &idx) const 0075 { 0076 Qt::ItemFlags f = (QStandardItemModel::flags(idx) & ~Qt::ItemIsEditable); 0077 return f; 0078 } 0079 0080 QVariant RecentFilesModel::data(const QModelIndex &idx, int role) const 0081 { 0082 switch(role) { 0083 case Qt::DecorationRole: 0084 return QIcon::fromTheme(QStringLiteral("document-open")); 0085 case Qt::FontRole: { 0086 break; 0087 } 0088 default: break; 0089 } 0090 return QStandardItemModel::data(idx, role); 0091 } 0092 0093 void RecentFilesModel::populate(const QList<QAction*> actions) 0094 { 0095 clear(); 0096 setColumnCount(1); 0097 setRowCount(actions.count()); 0098 QModelIndex idx = index(rowCount()-1, 0); 0099 for (const QAction *a : actions) { 0100 // KRecentFilesAction format: <name> [<file path>] 0101 // so we split it up and remove the [] 0102 QString s = a->text(); 0103 QString name = s.left(s.indexOf('[')); 0104 QString file = s.mid(s.indexOf('[')+1); 0105 file = file.left(file.lastIndexOf(']')); 0106 QString t = QString("%1\n%2").arg(name, file); 0107 setData(idx, t, Qt::EditRole); 0108 setData(idx, file, Qt::UserRole+1); 0109 idx = idx.sibling(idx.row()-1, idx.column()); 0110 } 0111 } 0112 0113 //----------------------------------- 0114 WelcomeView::WelcomeView(KoPart *part, KoDocument *doc, QWidget *parent) 0115 : ViewBase(part, doc, parent) 0116 , m_projectdialog(0) 0117 , m_filedialog(0) 0118 { 0119 ui.setupUi(this); 0120 ui.recentProjects->setBackgroundRole(QPalette::Midlight); 0121 ui.projectTemplates->setBackgroundRole(QPalette::Midlight); 0122 0123 Help::add(ui.newProjectBtn, 0124 xi18nc("@info:whatsthis", 0125 "<title>Create a new project</title>" 0126 "<para>" 0127 "Creates a new project with default values defined in" 0128 " <emphasis>Settings</emphasis>." 0129 "<nl/>Opens the <emphasis>project dialog</emphasis>" 0130 " so you can define project specific properties like" 0131 " <resource>Project Name</resource>," 0132 " <resource>Target Start</resource>" 0133 " and <resource>- End</resource> times." 0134 "<nl/><link url='%1'>More...</link>" 0135 "</para>", Help::page("Creating_a_Project"))); 0136 0137 Help::add(ui.createResourceFileBtn, 0138 xi18nc("@info:whatsthis", 0139 "<title>Shared resources</title>" 0140 "<para>" 0141 "Create a shared resources file." 0142 "<nl/>This enables you to only create your resources once," 0143 " you just refer to your resources file when you create a new project." 0144 "<nl/>These resources can then be shared between projects" 0145 " to avoid overbooking resources across projects." 0146 "<nl/>Shared resources must be defined in a separate file." 0147 "<nl/><link url='%1'>More...</link>" 0148 "</para>", Help::page("Managing_Resources"))); 0149 0150 Help::add(ui.recentProjects, 0151 xi18nc("@info:whatsthis", 0152 "<title>Recent Projects</title>" 0153 "<para>" 0154 "A list of the 10 most recent project files opened." 0155 "</para><para>" 0156 "<nl/>This enables you to quickly open projects you have worked on recently." 0157 "</para>")); 0158 0159 Help::add(ui.contextHelp, 0160 xi18nc("@info:whatsthis", 0161 "<title>Context help</title>" 0162 "<para>" 0163 "Help is available many places using <emphasis>What's This</emphasis>." 0164 "<nl/>It is activated using the menu entry <interface>Help->What's this?</interface>" 0165 " or the keyboard shortcut <shortcut>Shift+F1</shortcut>." 0166 "</para><para>" 0167 "In dialogs it is available via the <interface>?</interface> in the dialog title bar." 0168 "</para><para>" 0169 "If you see <link url='%1'>More...</link> in the text," 0170 " pressing it will display more information from online resources in your browser." 0171 "</para>", Help::page("Context_Help"))); 0172 0173 m_model = new RecentFilesModel(this); 0174 ui.recentProjects->setModel(m_model); 0175 ui.recentProjects->setSelectionMode(QAbstractItemView::SingleSelection); 0176 0177 setProjectTemplatesModel(); 0178 Help::add(ui.projectTemplates, xi18nc("@info:whatsthis", 0179 "<title>Project Templates</title>" 0180 "<para>" 0181 "Select a template to create a new project based on the selected template." 0182 "</para><para>" 0183 "Plan searches for templates in the places you can define in the settings dialog: <interface>Settings->Configure Plan</interface>." 0184 "</para><para>" 0185 "You can create new templates from a project using the <interface>File->Create Project Template</interface> menu entry." 0186 "<nl/><link url='%1'>More...</link>" 0187 "</para>", Help::page("Creating_a_Project"))); 0188 0189 setupGui(); 0190 0191 connect(ui.newProjectBtn, &QAbstractButton::clicked, this, &WelcomeView::slotNewProject); 0192 connect(ui.createResourceFileBtn, &QAbstractButton::clicked, this, &WelcomeView::slotCreateResourceFile); 0193 connect(ui.openProjectBtn, &QAbstractButton::clicked, this, &WelcomeView::slotOpenProject); 0194 0195 connect(ui.recentProjects, &QAbstractItemView::activated, this, &WelcomeView::slotRecentFileSelected); 0196 0197 connect(ui.projectTemplates, &QAbstractItemView::activated, this, &WelcomeView::slotOpenProjectTemplate); 0198 0199 connect(mainWindow(), &KoMainWindow::loadCompleted, this, &WelcomeView::finished); 0200 } 0201 0202 WelcomeView::~WelcomeView() 0203 { 0204 debugWelcome; 0205 } 0206 0207 Project *WelcomeView::project() const 0208 { 0209 return koDocument() ? koDocument()->project() : nullptr; 0210 } 0211 0212 void WelcomeView::setRecentFiles(const QList<QAction*> &actions) 0213 { 0214 m_model->populate(actions); 0215 } 0216 0217 void WelcomeView::updateReadWrite(bool /*readwrite */) 0218 { 0219 } 0220 0221 void WelcomeView::setGuiActive(bool activate) 0222 { 0223 debugPlan<<activate; 0224 } 0225 0226 void WelcomeView::slotRecentFileSelected(const QModelIndex &idx) 0227 { 0228 if (idx.isValid()) { 0229 QString file = idx.data(Qt::UserRole+1).toString(); 0230 int start = file.indexOf('['); 0231 int end = file.indexOf(']'); 0232 file = file.left(end).mid(start+1); 0233 QUrl url = QUrl::fromUserInput(file); 0234 debugWelcome<<file<<url; 0235 KoPart *part = koDocument()->isEmpty() ? koDocument()->documentPart() : nullptr; 0236 if (url.isValid()) { 0237 emit recentProject(url, part); 0238 } 0239 } 0240 } 0241 0242 void WelcomeView::slotContextMenuRequested(const QModelIndex &/*index*/, const QPoint& /*pos */) 0243 { 0244 //debugPlan<<index.row()<<","<<index.column()<<":"<<pos; 0245 } 0246 0247 void WelcomeView::slotEnableActions(bool on) 0248 { 0249 updateActionsEnabled(on); 0250 } 0251 0252 void WelcomeView::updateActionsEnabled(bool /*on */) 0253 { 0254 } 0255 0256 void WelcomeView::setupGui() 0257 { 0258 // Add the context menu actions for the view options 0259 } 0260 0261 KoPrintJob *WelcomeView::createPrintJob() 0262 { 0263 return 0; 0264 } 0265 0266 void WelcomeView::slotNewProject() 0267 { 0268 Project *p = project(); 0269 if (p) { 0270 p->config().setDefaultValues(*p); 0271 if (!m_projectdialog) { 0272 m_projectdialog = new MainProjectDialog(*p, this, false /*edit*/); 0273 connect(m_projectdialog.data(), &MainProjectDialog::dialogFinished, this, &WelcomeView::slotProjectEditFinished); 0274 connect(m_projectdialog.data(), &MainProjectDialog::sigLoadSharedResources, this, &WelcomeView::slotLoadSharedResources); 0275 } 0276 m_projectdialog->open(); 0277 } 0278 } 0279 0280 void WelcomeView::slotProjectEditFinished(int result) 0281 { 0282 MainProjectDialog *dia = qobject_cast<MainProjectDialog*>(sender()); 0283 if (dia == 0) { 0284 return; 0285 } 0286 if (result == QDialog::Accepted) { 0287 MacroCommand *cmd = dia->buildCommand(); 0288 if (cmd) { 0289 cmd->execute(); 0290 delete cmd; 0291 koDocument()->setModified(true); 0292 } 0293 emit projectCreated(); 0294 emit selectDefaultView(); 0295 emit finished(); 0296 } 0297 dia->deleteLater(); 0298 } 0299 0300 void WelcomeView::slotCreateResourceFile() 0301 { 0302 QString file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "templates/.source/SharedResources.plant"); 0303 emit openTemplate(QUrl::fromUserInput(file)); 0304 emit finished(); 0305 } 0306 0307 void WelcomeView::slotOpenProject() 0308 { 0309 Project *p = project(); 0310 if (p) { 0311 KoFileDialog filedialog(this, KoFileDialog::OpenFile, "OpenDocument"); 0312 filedialog.setCaption(i18n("Open Document")); 0313 filedialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); 0314 filedialog.setMimeTypeFilters(koApp->mimeFilter(KoFilterManager::Import)); 0315 filedialog.setHideNameFilterDetailsOption(); 0316 KoPart *part = koDocument()->isEmpty() ? koDocument()->documentPart() : nullptr; 0317 QUrl url = QUrl::fromUserInput(filedialog.filename()); 0318 if (!url.isEmpty()) { 0319 mainWindow()->openDocument(part, url); 0320 } 0321 } 0322 } 0323 0324 void WelcomeView::slotOpenProjectTemplate(const QModelIndex &idx) 0325 { 0326 if (idx.isValid()) { 0327 QString file = idx.data(Qt::UserRole+1).toString(); 0328 QUrl url = QUrl::fromUserInput(file); 0329 debugWelcome<<file<<url<<koDocument(); 0330 Q_ASSERT(koDocument()); 0331 KoPart *part = koDocument()->documentPart(); 0332 bool ok = false; 0333 if (part && url.isValid()) { 0334 ok = part->openProjectTemplate(url); 0335 } 0336 if (ok) { 0337 Project *p = part->document()->project(); 0338 Q_ASSERT(p); 0339 MainProjectDialog dlg(*p, nullptr, false /*edit*/); 0340 if (dlg.exec()) { 0341 MacroCommand *cmd = dlg.buildCommand(); 0342 if (cmd) { 0343 cmd->execute(); 0344 delete cmd; 0345 part->document()->setModified(true); 0346 if (part->document()->url().isEmpty() && !p->name().isEmpty()) { 0347 part->document()->setUrl(QUrl(p->name() + ".plan")); 0348 } 0349 emit finished(); 0350 } 0351 } else { 0352 warnWelcome<<"cancelled dialog"; 0353 koDocument()->initEmpty(); 0354 } 0355 } else { 0356 warnWelcome<<"Failed to load template:"<<url; 0357 } 0358 } 0359 } 0360 0361 void WelcomeView::slotOpenFileFinished(int result) 0362 { 0363 KoFileDialog *dia = qobject_cast<KoFileDialog*>(sender()); 0364 if (dia == 0) { 0365 return; 0366 } 0367 if (result == QDialog::Accepted) { 0368 QUrl url = QUrl::fromUserInput(dia->filename()); 0369 KoPart *part = koDocument()->isEmpty() ? nullptr : koDocument()->documentPart(); 0370 if (!url.isEmpty() && mainWindow()->openDocument(part, url)) { 0371 emit finished(); 0372 } 0373 } 0374 dia->deleteLater(); 0375 } 0376 0377 void WelcomeView::slotLoadSharedResources(const QString &file, const QUrl &projects, bool loadProjectsAtStartup) 0378 { 0379 QUrl url(file); 0380 if (url.scheme().isEmpty()) { 0381 url.setScheme("file"); 0382 } 0383 if (url.isValid()) { 0384 emit loadSharedResources(url, loadProjectsAtStartup ? projects :QUrl()); 0385 } 0386 } 0387 0388 void WelcomeView::setProjectTemplatesModel() 0389 { 0390 QStandardItemModel *m = new QStandardItemModel(ui.projectTemplates); 0391 const ConfigBase &config = koDocument()->project()->config(); 0392 const QStringList dirs = config.projectTemplatePaths(); 0393 bool addgroups = dirs.count() > 1; 0394 ui.projectTemplates->setRootIsDecorated(addgroups); 0395 for (const QString &path : dirs) { 0396 QStandardItem *parent = nullptr; 0397 if (addgroups) { 0398 QString p = path; 0399 if (p.endsWith('/')) { 0400 p.remove(p.length()-1, 1); 0401 } 0402 p = p.mid(p.lastIndexOf('/')+1); 0403 parent = new QStandardItem(p); 0404 m->appendRow(parent); 0405 } 0406 QDir dir(path, "*.plant"); 0407 for (const QString &file : dir.entryList(QDir::Files)) { 0408 QStandardItem *item = new QStandardItem(file.left(file.lastIndexOf(".plant"))); 0409 item->setData(QString(path + '/' + file)); 0410 item->setToolTip(item->data().toString()); 0411 item->setIcon(koIcon("document-new-from-template")); 0412 if (parent) { 0413 parent->appendRow(item); 0414 } else { 0415 m->appendRow(item); 0416 } 0417 } 0418 } 0419 delete ui.projectTemplates->model(); 0420 ui.projectTemplates->setModel(m); 0421 ui.projectTemplates->expandAll(); 0422 }