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 }