diff --git a/src/calligraplan_readonly.rc b/src/calligraplan_readonly.rc index 855308b9..077fd291 100644 --- a/src/calligraplan_readonly.rc +++ b/src/calligraplan_readonly.rc @@ -1,25 +1,9 @@ &View - - - - - - - - - - - - - - - - - + diff --git a/src/libs/main/KoMainWindow.cpp b/src/libs/main/KoMainWindow.cpp index c3e4175c..52814368 100644 --- a/src/libs/main/KoMainWindow.cpp +++ b/src/libs/main/KoMainWindow.cpp @@ -1,2162 +1,2163 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2006 David Faure Copyright (C) 2007, 2009 Thomas zander Copyright (C) 2010 Benjamin Port This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "KoMainWindow.h" #include "KoView.h" #include "KoDocument.h" #include "KoFilterManager.h" #include "KoDocumentInfo.h" #include "KoDocumentInfoDlg.h" #include "KoFileDialog.h" #include "KoDockFactoryBase.h" #include "KoDockWidgetTitleBar.h" #include "KoPrintJob.h" #include "KoDocumentEntry.h" #include "KoPart.h" #include #include #include "KoApplication.h" #include #include "KoResourcePaths.h" #include "KoComponentData.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_KACTIVITIES #include #endif // // qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "MainDebug.h" class KoMainWindowPrivate { public: KoMainWindowPrivate(const QByteArray &_nativeMimeType, const KoComponentData &componentData_, KoMainWindow *w) : componentData(componentData_) { nativeMimeType = _nativeMimeType; parent = w; rootDocument = 0; rootPart = 0; partToOpen = 0; mainWindowGuiIsBuilt = false; forQuit = false; activePart = 0; activeView = 0; firstTime = true; progress = 0; showDocumentInfo = 0; saveAction = 0; saveActionAs = 0; printAction = 0; printActionPreview = 0; sendFileAction = 0; exportPdf = 0; closeFile = 0; reloadFile = 0; importFile = 0; exportFile = 0; #if 0 encryptDocument = 0; #ifndef NDEBUG uncompressToDir = 0; #endif #endif isImporting = false; isExporting = false; windowSizeDirty = false; lastExportSpecialOutputFlag = 0; readOnly = false; dockWidgetMenu = 0; deferredClosingEvent = 0; #ifdef HAVE_KACTIVITIES activityResource = 0; #endif m_helpMenu = 0; // PartManger m_activeWidget = 0; m_activePart = 0; noCleanup = false; openingDocument = false; printPreviewJob = nullptr; } ~KoMainWindowPrivate() { qDeleteAll(toolbarList); } void applyDefaultSettings(QPrinter &printer) { QString title = rootDocument->documentInfo()->aboutInfo("title"); if (title.isEmpty()) { title = rootDocument->url().fileName(); // strip off the native extension (I don't want foobar.kwd.ps when printing into a file) QMimeType mime = QMimeDatabase().mimeTypeForName(rootDocument->outputMimeType()); if (mime.isValid()) { const QString extension = mime.preferredSuffix(); if (title.endsWith(extension)) title.chop(extension.length()); } } if (title.isEmpty()) { // #139905 title = i18n("%1 unsaved document (%2)", parent->componentData().componentDisplayName(), QLocale().toString(QDate::currentDate(), QLocale::ShortFormat)); } printer.setDocName(title); } QByteArray nativeMimeType; KoMainWindow *parent; KoDocument *rootDocument; QList rootViews; // PartManager QPointer rootPart; QPointer partToOpen; QPointer activePart; QPointer m_activePart; QPointer m_registeredPart; KoView *activeView; QWidget *m_activeWidget; QPointer progress; QMutex progressMutex; QList toolbarList; bool mainWindowGuiIsBuilt; bool forQuit; bool firstTime; bool windowSizeDirty; bool readOnly; QAction *showDocumentInfo; QAction *saveAction; QAction *saveActionAs; QAction *printAction; QAction *printActionPreview; QAction *sendFileAction; QAction *exportPdf; QAction *closeFile; QAction *reloadFile; QAction *importFile; QAction *exportFile; #if 0 QAction *encryptDocument; #ifndef NDEBUG QAction *uncompressToDir; #endif #endif KToggleAction *toggleDockers; KToggleAction *toggleDockerTitleBars; KRecentFilesAction *recent; bool isImporting; bool isExporting; QUrl lastExportUrl; QByteArray lastExportedFormat; int lastExportSpecialOutputFlag; QMap dockWidgetsMap; KActionMenu *dockWidgetMenu; QMap dockWidgetVisibilityMap; QList dockWidgets; QByteArray m_dockerStateBeforeHiding; QCloseEvent *deferredClosingEvent; #ifdef HAVE_KACTIVITIES KActivities::ResourceInstance *activityResource; #endif KoComponentData componentData; KHelpMenu *m_helpMenu; bool noCleanup; bool openingDocument; KoPrintJob *printPreviewJob; }; KoMainWindow::KoMainWindow(const QByteArray &nativeMimeType, const KoComponentData &componentData) : KXmlGuiWindow() , d(new KoMainWindowPrivate(nativeMimeType, componentData, this)) { #ifdef Q_OS_MAC setUnifiedTitleAndToolBarOnMac(true); #endif setStandardToolBarMenuEnabled(true); setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); connect(this, &KoMainWindow::restoringDone, this, &KoMainWindow::forceDockTabFonts); // PartManager // End QString doc; const QStringList allFiles = KoResourcePaths::findAllResources("data", "calligraplan/calligraplan_shell.rc"); setXMLFile(findMostRecentXMLFile(allFiles, doc)); setLocalXMLFile(KoResourcePaths::locateLocal("data", "calligraplan/calligraplan_shell.rc")); actionCollection()->addAction(KStandardAction::New, "file_new", this, SLOT(slotFileNew())); actionCollection()->addAction(KStandardAction::Open, "file_open", this, SLOT(slotFileOpen())); d->recent = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection()); connect(d->recent, &KRecentFilesAction::recentListCleared, this, &KoMainWindow::saveRecentFiles); d->saveAction = actionCollection()->addAction(KStandardAction::Save, "file_save", this, SLOT(slotFileSave())); d->saveActionAs = actionCollection()->addAction(KStandardAction::SaveAs, "file_save_as", this, SLOT(slotFileSaveAs())); d->printAction = actionCollection()->addAction(KStandardAction::Print, "file_print", this, SLOT(slotFilePrint())); d->printActionPreview = actionCollection()->addAction(KStandardAction::PrintPreview, "file_print_preview", this, SLOT(slotFilePrintPreview())); d->exportPdf = new QAction(i18n("Print to PDF..."), this); d->exportPdf->setIcon(koIcon("application-pdf")); actionCollection()->addAction("file_export_pdf", d->exportPdf); connect(d->exportPdf, &QAction::triggered, this, static_cast(&KoMainWindow::exportToPdf)); d->sendFileAction = actionCollection()->addAction(KStandardAction::Mail, "file_send_file", this, SLOT(slotEmailFile())); d->closeFile = actionCollection()->addAction(KStandardAction::Close, "file_close", this, SLOT(slotFileClose())); actionCollection()->addAction(KStandardAction::Quit, "file_quit", this, SLOT(slotFileQuit())); d->reloadFile = new QAction(i18n("Reload"), this); actionCollection()->addAction("file_reload_file", d->reloadFile); connect(d->reloadFile, &QAction::triggered, this, &KoMainWindow::slotReloadFile); d->importFile = new QAction(koIcon("document-import"), i18n("Import..."), this); actionCollection()->addAction("file_import_file", d->importFile); connect(d->importFile, &QAction::triggered, this, &KoMainWindow::slotImportFile); d->exportFile = new QAction(koIcon("document-export"), i18n("E&xport..."), this); actionCollection()->addAction("file_export_file", d->exportFile); connect(d->exportFile, &QAction::triggered, this, &KoMainWindow::slotExportFile); #if 0 // encryption not supported d->encryptDocument = new QAction(i18n("En&crypt Document"), this); actionCollection()->addAction("file_encrypt_doc", d->encryptDocument); connect(d->encryptDocument, SIGNAL(triggered(bool)), this, SLOT(slotEncryptDocument())); #ifndef NDEBUG d->uncompressToDir = new QAction(i18n("&Uncompress to Directory"), this); actionCollection()->addAction("file_uncompress_doc", d->uncompressToDir); connect(d->uncompressToDir, SIGNAL(triggered(bool)), this, SLOT(slotUncompressToDir())); #endif #endif QAction *actionNewView = new QAction(koIcon("window-new"), i18n("&New View"), this); actionCollection()->addAction("view_newview", actionNewView); connect(actionNewView, &QAction::triggered, this, &KoMainWindow::newView); /* The following entry opens the document information dialog. Since the action is named so it intends to show data this entry should not have a trailing ellipses (...). */ d->showDocumentInfo = new QAction(koIcon("document-properties"), i18n("Document Information"), this); actionCollection()->addAction("file_documentinfo", d->showDocumentInfo); connect(d->showDocumentInfo, &QAction::triggered, this, &KoMainWindow::slotDocumentInfo); KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actionCollection()); KStandardAction::configureToolbars(this, SLOT(slotConfigureToolbars()), actionCollection()); d->showDocumentInfo->setEnabled(false); d->saveActionAs->setEnabled(false); d->reloadFile->setEnabled(false); d->importFile->setEnabled(true); // always enabled like File --> Open d->exportFile->setEnabled(false); d->saveAction->setEnabled(false); d->printAction->setEnabled(false); d->printActionPreview->setEnabled(false); d->sendFileAction->setEnabled(false); d->exportPdf->setEnabled(false); d->closeFile->setEnabled(false); #if 0 d->encryptDocument->setEnabled(false); #ifndef NDEBUG d->uncompressToDir->setEnabled(false); #endif #endif KToggleAction *fullscreenAction = new KToggleAction(koIcon("view-fullscreen"), i18n("Full Screen Mode"), this); actionCollection()->addAction("view_fullscreen", fullscreenAction); actionCollection()->setDefaultShortcut(fullscreenAction, QKeySequence::FullScreen); connect(fullscreenAction, &QAction::toggled, this, &KoMainWindow::viewFullscreen); d->toggleDockers = new KToggleAction(i18n("Show Dockers"), this); d->toggleDockers->setChecked(true); actionCollection()->addAction("view_toggledockers", d->toggleDockers); connect(d->toggleDockers, &QAction::toggled, this, &KoMainWindow::toggleDockersVisibility); d->toggleDockerTitleBars = new KToggleAction(i18nc("@action:inmenu", "Show Docker Titlebars"), this); KConfigGroup configGroupInterface = KSharedConfig::openConfig()->group("Interface"); d->toggleDockerTitleBars->setChecked(configGroupInterface.readEntry("ShowDockerTitleBars", true)); d->toggleDockerTitleBars->setVisible(false); actionCollection()->addAction("view_toggledockertitlebars", d->toggleDockerTitleBars); connect(d->toggleDockerTitleBars, &QAction::toggled, this, &KoMainWindow::showDockerTitleBars); d->dockWidgetMenu = new KActionMenu(i18n("Dockers"), this); actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu); d->dockWidgetMenu->setVisible(false); d->dockWidgetMenu->setDelayed(false); // Load list of recent files KSharedConfigPtr configPtr = componentData.config(); d->recent->loadEntries(configPtr->group("RecentFiles")); createMainwindowGUI(); d->mainWindowGuiIsBuilt = true; // we first figure out some good default size and restore the x,y position. See bug 285804Z. KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray())); if (!restoreGeometry(geom)) { const int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = QApplication::desktop()->availableGeometry(scnum); // if the desktop is virtual then use virtual screen size if (QApplication::desktop()->isVirtualDesktop()) { desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen()); desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen(scnum)); } quint32 x = desk.x(); quint32 y = desk.y(); quint32 w = 0; quint32 h = 0; // Default size -- maximize on small screens, something useful on big screens const int deskWidth = desk.width(); if (deskWidth > 1024) { // a nice width, and slightly less than total available // height to componensate for the window decs w = (deskWidth / 3) * 2; h = (desk.height() / 3) * 2; } else { w = desk.width(); h = desk.height(); } x += (desk.width() - w) / 2; y += (desk.height() - h) / 2; move(x,y); setGeometry(geometry().x(), geometry().y(), w, h); } restoreState(QByteArray::fromBase64(cfg.readEntry("ko_windowstate", QByteArray()))); } void KoMainWindow::setNoCleanup(bool noCleanup) { d->noCleanup = noCleanup; } KoMainWindow::~KoMainWindow() { KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); cfg.writeEntry("ko_geometry", saveGeometry().toBase64()); cfg.writeEntry("ko_windowstate", saveState().toBase64()); // The doc and view might still exist (this is the case when closing the window) if (d->rootPart) d->rootPart->removeMainWindow(this); if (d->partToOpen) { d->partToOpen->removeMainWindow(this); delete d->partToOpen; } // safety first ;) setActivePart(0, 0); if (d->rootViews.indexOf(d->activeView) == -1) { delete d->activeView; d->activeView = 0; } while (!d->rootViews.isEmpty()) { delete d->rootViews.takeFirst(); } if(d->noCleanup) return; // We have to check if this was a root document. // This has to be checked from queryClose, too :) if (d->rootPart && d->rootPart->viewCount() == 0) { //debugMain <<"Destructor. No more views, deleting old doc" << d->rootDocument; delete d->rootDocument; // FIXME: Why delete here and not only in KoPart? } delete d; } void KoMainWindow::setRootDocument(KoDocument *doc, KoPart *part, bool deletePrevious) { if (d->rootDocument == doc) return; if (d->partToOpen && d->partToOpen->document() != doc) { d->partToOpen->removeMainWindow(this); if (deletePrevious) delete d->partToOpen; } d->partToOpen = 0; //debugMain <<"KoMainWindow::setRootDocument this =" << this <<" doc =" << doc; QList oldRootViews = d->rootViews; d->rootViews.clear(); KoDocument *oldRootDoc = d->rootDocument; KoPart *oldRootPart = d->rootPart; if (oldRootDoc) { oldRootDoc->disconnect(this); oldRootPart->removeMainWindow(this); // Hide all dockwidgets and remember their old state d->dockWidgetVisibilityMap.clear(); foreach(QDockWidget* dockWidget, d->dockWidgetsMap) { d->dockWidgetVisibilityMap.insert(dockWidget, dockWidget->isVisible()); dockWidget->setVisible(false); } d->toggleDockerTitleBars->setVisible(false); d->dockWidgetMenu->setVisible(false); } d->rootDocument = doc; // XXX remove this after the splitting if (!part && doc) { d->rootPart = doc->documentPart(); } else { d->rootPart = part; } if (doc) { d->toggleDockerTitleBars->setVisible(true); d->dockWidgetMenu->setVisible(true); d->m_registeredPart = d->rootPart.data(); KoView *view = d->rootPart->createView(doc, this); setCentralWidget(view); d->rootViews.append(view); view->show(); view->setFocus(); // The addMainWindow has been done already if using openUrl if (!d->rootPart->mainWindows().contains(this)) { d->rootPart->addMainWindow(this); } } bool enable = d->rootDocument != 0 ? true : false; d->showDocumentInfo->setEnabled(enable); d->saveAction->setEnabled(enable); d->saveActionAs->setEnabled(enable); d->importFile->setEnabled(enable); d->exportFile->setEnabled(enable); #if 0 d->encryptDocument->setEnabled(enable); #ifndef NDEBUG d->uncompressToDir->setEnabled(enable); #endif #endif d->printAction->setEnabled(enable); d->printActionPreview->setEnabled(enable); d->sendFileAction->setEnabled(enable); d->exportPdf->setEnabled(enable); d->closeFile->setEnabled(enable); updateCaption(); setActivePart(d->rootPart, doc ? d->rootViews.first() : 0); emit restoringDone(); while(!oldRootViews.isEmpty()) { delete oldRootViews.takeFirst(); } if (oldRootPart && oldRootPart->viewCount() == 0) { //debugMain <<"No more views, deleting old doc" << oldRootDoc; oldRootDoc->clearUndoHistory(); if(deletePrevious) delete oldRootDoc; } if (doc && !d->dockWidgetVisibilityMap.isEmpty()) { foreach(QDockWidget* dockWidget, d->dockWidgetsMap) { dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget)); } } if (!d->rootDocument) { statusBar()->setVisible(false); } else { #ifdef Q_OS_MAC statusBar()->setMaximumHeight(28); #endif connect(d->rootDocument, &KoDocument::titleModified, this, &KoMainWindow::slotDocumentTitleModified); } } void KoMainWindow::updateReloadFileAction(KoDocument *doc) { d->reloadFile->setEnabled(doc && !doc->url().isEmpty()); } void KoMainWindow::setReadWrite(bool readwrite) { d->saveAction->setEnabled(readwrite); d->importFile->setEnabled(readwrite); d->readOnly = !readwrite; updateCaption(); } void KoMainWindow::addRecentURL(const QString &projectName, const QUrl &url) { debugMain << "url=" << url.toDisplayString(); // Add entry to recent documents list // (call coming from KoDocument because it must work with cmd line, template dlg, file/open, etc.) if (!url.isEmpty()) { bool ok = true; if (url.isLocalFile()) { QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); const QStringList tmpDirs = QStandardPaths::standardLocations(QStandardPaths::TempLocation); foreach (const QString &tmpDir, tmpDirs) { if (path.startsWith(tmpDir)) { ok = false; // it's in the tmp resource break; } } if (ok) { KRecentDocument::add(QUrl::fromLocalFile(path)); KRecentDirs::add(":OpenDialog", QFileInfo(path).dir().canonicalPath()); } } else { KRecentDocument::add(url.adjusted(QUrl::StripTrailingSlash)); } if (ok) { d->recent->addUrl(url, projectName); } saveRecentFiles(); #ifdef HAVE_KACTIVITIES if (!d->activityResource) { d->activityResource = new KActivities::ResourceInstance(winId(), this); } d->activityResource->setUri(url); #endif } } void KoMainWindow::saveRecentFiles() { // Save list of recent files KSharedConfigPtr config = componentData().config(); debugMain << this << " Saving recent files list into config. componentData()=" << componentData().componentName(); d->recent->saveEntries(config->group("RecentFiles")); config->sync(); // Tell all windows to reload their list, after saving // Doesn't work multi-process, but it's a start foreach(KMainWindow* window, KMainWindow::memberList()) static_cast(window)->reloadRecentFileList(); } void KoMainWindow::reloadRecentFileList() { KSharedConfigPtr config = componentData().config(); d->recent->loadEntries(config->group("RecentFiles")); } KoPart* KoMainWindow::createPart() const { KoDocumentEntry entry = KoDocumentEntry::queryByMimeType(d->nativeMimeType); QString errorMsg; KoPart *part = entry.createKoPart(&errorMsg); if (!part || !errorMsg.isEmpty()) { return 0; } return part; } void KoMainWindow::updateCaption() { debugMain; if (!d->rootDocument) { updateCaption(QString(), false); } else { QString caption( d->rootDocument->caption() ); if (d->readOnly) { caption += ' ' + i18n("(write protected)"); } updateCaption(caption, d->rootDocument->isModified()); if (!rootDocument()->url().fileName().isEmpty()) d->saveAction->setToolTip(i18n("Save as %1", d->rootDocument->url().fileName())); else d->saveAction->setToolTip(i18n("Save")); } } void KoMainWindow::updateCaption(const QString & caption, bool mod) { debugMain << caption << "," << mod; #ifdef PLAN_ALPHA setCaption(QString("ALPHA %1: %2").arg(PLAN_ALPHA).arg(caption), mod); return; #endif #ifdef PLAN_BETA setCaption(QString("BETA %1: %2").arg(PLAN_BETA).arg(caption), mod); return; #endif #ifdef PLAN_RC setCaption(QString("RELEASE CANDIDATE %1: %2").arg(PLAN_RC).arg(caption), mod); return; #endif setCaption(caption, mod); } KoDocument *KoMainWindow::rootDocument() const { return d->rootDocument; } KoView *KoMainWindow::rootView() const { if (d->rootViews.indexOf(d->activeView) != -1) return d->activeView; return d->rootViews.first(); } bool KoMainWindow::openDocument(const QUrl &url) { if (!KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0)) { KMessageBox::error(0, i18n("The file %1 does not exist.", url.url())); d->recent->removeUrl(url); //remove the file from the recent-opened-file-list saveRecentFiles(); return false; } return openDocumentInternal(url); } bool KoMainWindow::openDocument(KoPart *newPart, const QUrl &url) { if (!newPart) { return openDocument(url); } // the part always has a document; the document doesn't know about the part. KoDocument *newdoc = newPart->document(); if (!KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0)) { newdoc->initEmpty(); //create an empty document setRootDocument(newdoc, newPart); newdoc->setUrl(url); QMimeType mime = QMimeDatabase().mimeTypeForUrl(url); QString mimetype = (!mime.isValid() || mime.isDefault()) ? newdoc->nativeFormatMimeType() : mime.name(); newdoc->setMimeTypeAfterLoading(mimetype); updateCaption(); return true; } return openDocumentInternal(url, newPart, newdoc); } bool KoMainWindow::openDocumentInternal(const QUrl &url, KoPart *newpart, KoDocument *newdoc) { debugMain << newpart << newdoc << url.url(); if (!newpart) newpart = createPart(); if (!newpart) return false; if (!newdoc) newdoc = newpart->document(); + KFileItem file(url, newdoc->mimeType(), KFileItem::Unknown); + if (!file.isWritable()) { + newdoc->setReadWrite(false); + } + d->firstTime = true; connect(newdoc, &KoDocument::sigProgress, this, &KoMainWindow::slotProgress); connect(newdoc, &KoDocument::completed, this, &KoMainWindow::slotLoadCompleted); connect(newdoc, &KoDocument::canceled, this, &KoMainWindow::slotLoadCanceled); d->openingDocument = true; newpart->addMainWindow(this); // used by openUrl bool openRet = (!isImporting()) ? newdoc->openUrl(url) : newdoc->importDocument(url); if (!openRet) { newpart->removeMainWindow(this); delete newdoc; delete newpart; d->openingDocument = false; return false; } updateReloadFileAction(newdoc); - KFileItem file(url, newdoc->mimeType(), KFileItem::Unknown); - if (!file.isWritable()) { - setReadWrite(false); - } return true; } // Separate from openDocument to handle async loading (remote URLs) void KoMainWindow::slotLoadCompleted() { debugMain; KoDocument *newdoc = qobject_cast(sender()); KoPart *newpart = newdoc->documentPart(); if (d->rootDocument && d->rootDocument->isEmpty()) { // Replace current empty document setRootDocument(newdoc); } else if (d->rootDocument && !d->rootDocument->isEmpty()) { // Open in a new main window // (Note : could create the main window first and the doc next for this // particular case, that would give a better user feedback...) KoMainWindow *s = newpart->createMainWindow(); s->show(); newpart->removeMainWindow(this); s->setRootDocument(newdoc, newpart); } else { // We had no document, set the new one setRootDocument(newdoc); } slotProgress(-1); disconnect(newdoc, &KoDocument::sigProgress, this, &KoMainWindow::slotProgress); disconnect(newdoc, &KoDocument::completed, this, &KoMainWindow::slotLoadCompleted); disconnect(newdoc, &KoDocument::canceled, this, &KoMainWindow::slotLoadCanceled); d->openingDocument = false; emit loadCompleted(); } void KoMainWindow::slotLoadCanceled(const QString & errMsg) { debugMain; if (!errMsg.isEmpty()) // empty when canceled by user KMessageBox::error(this, errMsg); // ... can't delete the document, it's the one who emitted the signal... KoDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, &KoDocument::sigProgress, this, &KoMainWindow::slotProgress); disconnect(doc, &KoDocument::completed, this, &KoMainWindow::slotLoadCompleted); disconnect(doc, &KoDocument::canceled, this, &KoMainWindow::slotLoadCanceled); d->openingDocument = false; emit loadCanceled(); } void KoMainWindow::slotSaveCanceled(const QString &errMsg) { debugMain; if (!errMsg.isEmpty()) // empty when canceled by user KMessageBox::error(this, errMsg); slotSaveCompleted(); } void KoMainWindow::slotSaveCompleted() { debugMain; KoDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, &KoDocument::sigProgress, this, &KoMainWindow::slotProgress); disconnect(doc, &KoDocument::completed, this, &KoMainWindow::slotSaveCompleted); disconnect(doc, &KoDocument::canceled, this, &KoMainWindow::slotSaveCanceled); if (d->deferredClosingEvent) { KXmlGuiWindow::closeEvent(d->deferredClosingEvent); } } // returns true if we should save, false otherwise. bool KoMainWindow::exportConfirmation(const QByteArray &outputFormat) { KConfigGroup group = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()); if (!group.readEntry("WantExportConfirmation", true)) { return true; } QMimeType mime = QMimeDatabase().mimeTypeForName(outputFormat); QString comment = mime.isValid() ? mime.comment() : i18n("%1 (unknown file type)", QString::fromLatin1(outputFormat)); // Warn the user int ret; if (!isExporting()) { // File --> Save ret = KMessageBox::warningContinueCancel ( this, i18n("Saving as a %1 may result in some loss of formatting." "

Do you still want to save in this format?", QString("%1").arg(comment)), // in case we want to remove the bold later i18n("Confirm Save"), KStandardGuiItem::save(), KStandardGuiItem::cancel(), "NonNativeSaveConfirmation" ); } else { // File --> Export ret = KMessageBox::warningContinueCancel ( this, i18n("Exporting as a %1 may result in some loss of formatting." "

Do you still want to export to this format?", QString("%1").arg(comment)), // in case we want to remove the bold later i18n("Confirm Export"), KGuiItem(i18n("Export")), KStandardGuiItem::cancel(), "NonNativeExportConfirmation" // different to the one used for Save (above) ); } return (ret == KMessageBox::Continue); } bool KoMainWindow::saveDocument(bool saveas, bool silent, int specialOutputFlag) { if (!d->rootDocument || !d->rootPart) { return true; } bool reset_url; if (d->rootDocument->url().isEmpty()) { emit saveDialogShown(); reset_url = true; saveas = true; } else { reset_url = false; } connect(d->rootDocument, &KoDocument::sigProgress, this, &KoMainWindow::slotProgress); connect(d->rootDocument, &KoDocument::completed, this, &KoMainWindow::slotSaveCompleted); connect(d->rootDocument, &KoDocument::canceled, this, &KoMainWindow::slotSaveCanceled); QUrl oldURL = d->rootDocument->url(); QString oldFile = d->rootDocument->localFilePath(); QByteArray _native_format = d->rootDocument->nativeFormatMimeType(); QByteArray oldOutputFormat = d->rootDocument->outputMimeType(); int oldSpecialOutputFlag = d->rootDocument->specialOutputFlag(); QUrl suggestedURL = d->rootDocument->url(); QStringList mimeFilter; QMimeType mime = QMimeDatabase().mimeTypeForName(_native_format); if (!mime.isValid()) // QT5TODO: find if there is no better way to get an object for the default type mime = QMimeDatabase().mimeTypeForName(QStringLiteral("application/octet-stream")); if (specialOutputFlag) mimeFilter = mime.globPatterns(); else mimeFilter = KoFilterManager::mimeFilter(_native_format, KoFilterManager::Export, d->rootDocument->extraNativeMimeTypes()); if (oldOutputFormat.isEmpty() && !d->rootDocument->url().isEmpty()) { // Not been saved yet, but there is a default url so open dialog with this url if (suggestedURL.path() == suggestedURL.fileName()) { // only a filename has been given, so add the default dir KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString path = group.readEntry("SaveDocument"); path += '/' + suggestedURL.fileName(); suggestedURL.setPath(path); suggestedURL.setScheme("file"); } saveas = true; debugMain << "newly created doc, default file name:" << d->rootDocument->url() << "save to:" << suggestedURL; } else if (!mimeFilter.contains(oldOutputFormat) && !isExporting()) { debugMain << "no export filter for" << oldOutputFormat; // --- don't setOutputMimeType in case the user cancels the Save As // dialog and then tries to just plain Save --- // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :)) QString suggestedFilename = suggestedURL.fileName(); if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name int c = suggestedFilename.lastIndexOf('.'); const QString ext = mime.preferredSuffix(); if (!ext.isEmpty()) { if (c < 0) suggestedFilename += ext; else suggestedFilename = suggestedFilename.left(c) + ext; } else { // current filename extension wrong anyway if (c > 0) { // this assumes that a . signifies an extension, not just a . suggestedFilename = suggestedFilename.left(c); } } suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename); suggestedURL.setPath(suggestedURL.path() + suggestedFilename); } // force the user to choose outputMimeType saveas = true; } bool ret = false; if (d->rootDocument->url().isEmpty() || saveas) { // if you're just File/Save As'ing to change filter options you // don't want to be reminded about overwriting files etc. bool justChangingFilterOptions = false; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveDocument"); dialog.setCaption(i18n("untitled")); dialog.setDefaultDir((isExporting() && !d->lastExportUrl.isEmpty()) ? d->lastExportUrl.toLocalFile() : suggestedURL.toLocalFile()); dialog.setMimeTypeFilters(mimeFilter); QUrl newURL = QUrl::fromUserInput(dialog.filename()); if (newURL.isLocalFile()) { QString fn = newURL.toLocalFile(); if (QFileInfo(fn).completeSuffix().isEmpty()) { QMimeType mime = QMimeDatabase().mimeTypeForName(_native_format); fn.append(mime.preferredSuffix()); newURL = QUrl::fromLocalFile(fn); } } QByteArray outputFormat = _native_format; if (!specialOutputFlag) { QMimeType mime = QMimeDatabase().mimeTypeForUrl(newURL); outputFormat = mime.name().toLatin1(); } if (!isExporting()) justChangingFilterOptions = (newURL == d->rootDocument->url()) && (outputFormat == d->rootDocument->mimeType()) && (specialOutputFlag == oldSpecialOutputFlag); else justChangingFilterOptions = (newURL == d->lastExportUrl) && (outputFormat == d->lastExportedFormat) && (specialOutputFlag == d->lastExportSpecialOutputFlag); bool bOk = true; if (newURL.isEmpty()) { bOk = false; } // adjust URL before doing checks on whether the file exists. if (specialOutputFlag) { QString fileName = newURL.fileName(); if ( specialOutputFlag== KoDocument::SaveAsDirectoryStore) { // Do nothing } #if 0 else if (specialOutputFlag == KoDocument::SaveEncrypted) { int dot = fileName.lastIndexOf('.'); QString ext = mime.preferredSuffix(); if (!ext.isEmpty()) { if (dot < 0) fileName += ext; else fileName = fileName.left(dot) + ext; } else { // current filename extension wrong anyway if (dot > 0) fileName = fileName.left(dot); } newURL = newURL.adjusted(QUrl::RemoveFilename); newURL.setPath(newURL.path() + fileName); } #endif } if (bOk) { bool wantToSave = true; // don't change this line unless you know what you're doing :) if (!justChangingFilterOptions || d->rootDocument->confirmNonNativeSave(isExporting())) { if (!d->rootDocument->isNativeFormat(outputFormat)) wantToSave = exportConfirmation(outputFormat); } if (wantToSave) { // // Note: // If the user is stupid enough to Export to the current URL, // we do _not_ change this operation into a Save As. Reasons // follow: // // 1. A check like "isExporting() && oldURL == newURL" // doesn't _always_ work on case-insensitive filesystems // and inconsistent behaviour is bad. // 2. It is probably not a good idea to change d->rootDocument->mimeType // and friends because the next time the user File/Save's, // (not Save As) they won't be expecting that they are // using their File/Export settings // // As a bad side-effect of this, the modified flag will not // be updated and it is possible that what is currently on // their screen is not what is stored on disk (through loss // of formatting). But if you are dumb enough to change // mimetype but not the filename, then arguably, _you_ are // the "bug" :) // // - Clarence // d->rootDocument->setOutputMimeType(outputFormat, specialOutputFlag); if (!isExporting()) { // Save As ret = d->rootDocument->saveAs(newURL); if (ret) { debugMain << "Successful Save As!"; addRecentURL(d->rootDocument->projectName(), newURL); setReadWrite(true); } else { debugMain << "Failed Save As!"; d->rootDocument->setUrl(oldURL); d->rootDocument->setLocalFilePath(oldFile); d->rootDocument->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag); } } else { // Export ret = d->rootDocument->exportDocument(newURL); if (ret) { // a few file dialog convenience things d->lastExportUrl = newURL; d->lastExportedFormat = outputFormat; d->lastExportSpecialOutputFlag = specialOutputFlag; } // always restore output format d->rootDocument->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag); } if (silent) // don't let the document change the window caption d->rootDocument->setTitleModified(); } // if (wantToSave) { else ret = false; } // if (bOk) { else ret = false; } else { // saving bool needConfirm = d->rootDocument->confirmNonNativeSave(false) && !d->rootDocument->isNativeFormat(oldOutputFormat); if (!needConfirm || (needConfirm && exportConfirmation(oldOutputFormat /* not so old :) */)) ) { // be sure d->rootDocument has the correct outputMimeType! if (isExporting() || d->rootDocument->isModified() || d->rootDocument->alwaysAllowSaving()) { ret = d->rootDocument->save(); } if (!ret) { debugMain << "Failed Save!"; d->rootDocument->setUrl(oldURL); d->rootDocument->setLocalFilePath(oldFile); } } else ret = false; } if (!ret && reset_url) d->rootDocument->resetURL(); //clean the suggested filename as the save dialog was rejected updateReloadFileAction(d->rootDocument); updateCaption(); return ret; } void KoMainWindow::closeEvent(QCloseEvent *e) { // If we are in the process of opening a new document, rootDocument() may not have been set yet, // so we must prevent closing to avoid crash. if(d->openingDocument || (rootDocument() && rootDocument()->isLoading())) { e->setAccepted(false); return; } if (queryClose()) { d->deferredClosingEvent = e; if (!d->m_dockerStateBeforeHiding.isEmpty()) { restoreState(d->m_dockerStateBeforeHiding); } statusBar()->setVisible(true); menuBar()->setVisible(true); saveWindowSettings(); if(d->noCleanup) return; setRootDocument(0); if (!d->dockWidgetVisibilityMap.isEmpty()) { // re-enable dockers for persistency foreach(QDockWidget* dockWidget, d->dockWidgetsMap) dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget)); } } else { e->setAccepted(false); } } void KoMainWindow::saveWindowSettings() { KSharedConfigPtr config = componentData().config(); if (d->windowSizeDirty ) { // Save window size into the config file of our componentData // TODO: check if this is ever read again, seems lost over the years debugMain; KConfigGroup mainWindowConfigGroup = config->group("MainWindow"); KWindowConfig::saveWindowSize(windowHandle(), mainWindowConfigGroup); config->sync(); d->windowSizeDirty = false; } if ( rootDocument() && d->rootPart) { // Save toolbar position into the config file of the app, under the doc's component name KConfigGroup group = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()); //debugMain <<"KoMainWindow::closeEvent -> saveMainWindowSettings rootdoc's componentData=" << d->rootPart->componentData().componentName(); saveMainWindowSettings(group); // Save collapsable state of dock widgets for (QMap::const_iterator i = d->dockWidgetsMap.constBegin(); i != d->dockWidgetsMap.constEnd(); ++i) { if (i.value()->widget()) { KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key()); dockGroup.writeEntry("Collapsed", i.value()->widget()->isHidden()); dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool()); dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value())); } } } KSharedConfig::openConfig()->sync(); resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down } void KoMainWindow::resizeEvent(QResizeEvent * e) { d->windowSizeDirty = true; KXmlGuiWindow::resizeEvent(e); } bool KoMainWindow::queryClose() { if (rootDocument() == 0) return true; //debugMain <<"KoMainWindow::queryClose() viewcount=" << rootDocument()->viewCount() // << " mainWindowCount=" << rootDocument()->mainWindowCount() << endl; if (!d->forQuit && d->rootPart && d->rootPart->mainwindowCount() > 1) // there are more open, and we are closing just one, so no problem for closing return true; // main doc + internally stored child documents if (d->rootDocument->isModified()) { QString name; if (rootDocument()->documentInfo()) { name = rootDocument()->documentInfo()->aboutInfo("title"); } if (name.isEmpty()) name = rootDocument()->url().fileName(); if (name.isEmpty()) name = i18n("Untitled"); int res = KMessageBox::warningYesNoCancel(this, i18n("

The document '%1' has been modified.

Do you want to save it?

", name), QString(), KStandardGuiItem::save(), KStandardGuiItem::discard()); switch (res) { case KMessageBox::Yes : { bool isNative = (d->rootDocument->outputMimeType() == d->rootDocument->nativeFormatMimeType()); if (!saveDocument(!isNative)) return false; break; } case KMessageBox::No : rootDocument()->removeAutoSaveFiles(); rootDocument()->setModified(false); // Now when queryClose() is called by closeEvent it won't do anything. break; default : // case KMessageBox::Cancel : return false; } } return true; } // Helper method for slotFileNew and slotFileClose void KoMainWindow::chooseNewDocument(InitDocFlags initDocFlags) { KoDocument* doc = rootDocument(); KoPart *newpart = createPart(); KoDocument *newdoc = newpart->document(); if (!newdoc) return; disconnect(newdoc, &KoDocument::sigProgress, this, &KoMainWindow::slotProgress); if ((!doc && initDocFlags == InitDocFileNew) || (doc && !doc->isEmpty())) { KoMainWindow *s = newpart->createMainWindow(); s->show(); newpart->addMainWindow(s); newpart->showStartUpWidget(s); return; } if (doc) { setRootDocument(0); if(d->rootDocument) d->rootDocument->clearUndoHistory(); delete d->rootDocument; d->rootDocument = 0; } newpart->addMainWindow(this); newpart->showStartUpWidget(this); } void KoMainWindow::slotFileNew() { chooseNewDocument(InitDocFileNew); } void KoMainWindow::slotFileOpen() { QUrl url; if (!isImporting()) { KoFileDialog dialog(this, KoFileDialog::OpenFile, "OpenDocument"); dialog.setCaption(i18n("Open Document")); dialog.setDefaultDir(qApp->applicationName().contains("karbon") ? QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) : QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); dialog.setMimeTypeFilters(koApp->mimeFilter(KoFilterManager::Import)); dialog.setHideNameFilterDetailsOption(); url = QUrl::fromUserInput(dialog.filename()); } else { KoFileDialog dialog(this, KoFileDialog::ImportFile, "OpenDocument"); dialog.setCaption(i18n("Import Document")); dialog.setDefaultDir(qApp->applicationName().contains("karbon") ? QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) : QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); dialog.setMimeTypeFilters(koApp->mimeFilter(KoFilterManager::Import)); dialog.setHideNameFilterDetailsOption(); url = QUrl::fromUserInput(dialog.filename()); } if (url.isEmpty()) return; (void) openDocument(url); } void KoMainWindow::slotFileOpenRecent(const QUrl & url, KoPart *part) { // Create a copy, because the original QUrl in the map of recent files in // KRecentFilesAction may get deleted. (void) openDocument(part, QUrl(url)); } void KoMainWindow::slotFileSave() { if (saveDocument()) emit documentSaved(); } void KoMainWindow::slotFileSaveAs() { if (saveDocument(true)) emit documentSaved(); } void KoMainWindow::slotEncryptDocument() { if (saveDocument(false, false, KoDocument::SaveEncrypted)) emit documentSaved(); } void KoMainWindow::slotUncompressToDir() { if (saveDocument(true, false, KoDocument::SaveAsDirectoryStore)) emit documentSaved(); } void KoMainWindow::slotDocumentInfo() { if (!rootDocument()) return; KoDocumentInfo *docInfo = rootDocument()->documentInfo(); if (!docInfo) return; KoDocumentInfoDlg *dlg = d->rootDocument->createDocumentInfoDialog(this, docInfo); if (dlg->exec()) { if (dlg->isDocumentSaved()) { rootDocument()->setModified(false); } else { rootDocument()->setModified(true); } rootDocument()->setTitleModified(); } delete dlg; } void KoMainWindow::slotFileClose() { if (queryClose()) { saveWindowSettings(); setRootDocument(0); // don't delete this main window when deleting the document if(d->rootDocument) d->rootDocument->clearUndoHistory(); delete d->rootDocument; d->rootDocument = 0; chooseNewDocument(InitDocFileClose); } } void KoMainWindow::slotFileQuit() { close(); } void KoMainWindow::slotFilePrint() { if (!rootView()) return; KoPrintJob *printJob = rootView()->createPrintJob(); if (printJob == 0) return; d->applyDefaultSettings(printJob->printer()); QPrintDialog *printDialog = rootView()->createPrintDialog( printJob, this ); if (printDialog && printDialog->exec() == QDialog::Accepted) printJob->startPrinting(KoPrintJob::DeleteWhenDone); else delete printJob; delete printDialog; } void KoMainWindow::slotFilePrintPreview() { if (!rootView()) return; KoPrintJob *printJob = rootView()->createPrintJob(); if (printJob == 0) return; /* Sets the startPrinting() slot to be blocking. The Qt print-preview dialog requires the printing to be completely blocking and only return when the full document has been printed. By default the KoPrintingDialog is non-blocking and multithreading, setting blocking to true will allow it to be used in the preview dialog */ printJob->setProperty("blocking", true); QPrintPreviewDialog *preview = new QPrintPreviewDialog(&printJob->printer(), this); printJob->setParent(preview); // will take care of deleting the job d->printPreviewJob = printJob; connect(preview, SIGNAL(paintRequested(QPrinter*)), this, SLOT(slotPrintPreviewPaintRequest(QPrinter*))); // clazy:exclude=old-style-connect preview->exec(); delete preview; d->printPreviewJob = nullptr; } void KoMainWindow::slotPrintPreviewPaintRequest(QPrinter *printer) { KoPageLayout pl = rootView()->pageLayout(); pl.updatePageLayout(printer); rootView()->setPageLayout(pl); d->printPreviewJob->startPrinting(); } KoPrintJob* KoMainWindow::exportToPdf() { return exportToPdf(QString()); } KoPrintJob* KoMainWindow::exportToPdf(const QString &_pdfFileName) { if (!rootView()) return 0; KoPageLayout pageLayout; pageLayout = rootView()->pageLayout(); QString pdfFileName = _pdfFileName; if (pdfFileName.isEmpty()) { KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString defaultDir = group.readEntry("SavePdfDialog"); if (defaultDir.isEmpty()) defaultDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QUrl startUrl = QUrl::fromLocalFile(defaultDir); KoDocument* pDoc = rootDocument(); /** if document has a file name, take file name and replace extension with .pdf */ if (pDoc && pDoc->url().isValid()) { startUrl = pDoc->url(); QString fileName = startUrl.fileName(); fileName = fileName.replace( QRegExp( "\\.\\w{2,5}$", Qt::CaseInsensitive ), ".pdf" ); startUrl = startUrl.adjusted(QUrl::RemoveFilename); startUrl.setPath(startUrl.path() + fileName ); } QPointer layoutDlg(new KoPageLayoutDialog(this, pageLayout)); layoutDlg->setWindowModality(Qt::WindowModal); if (layoutDlg->exec() != QDialog::Accepted || !layoutDlg) { delete layoutDlg; return 0; } pageLayout = layoutDlg->pageLayout(); delete layoutDlg; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveDocument"); dialog.setCaption(i18n("Export as PDF")); dialog.setDefaultDir(startUrl.toLocalFile()); dialog.setMimeTypeFilters(QStringList() << "application/pdf"); QUrl url = QUrl::fromUserInput(dialog.filename()); pdfFileName = url.toLocalFile(); if (pdfFileName.isEmpty()) return 0; } KoPrintJob *printJob = rootView()->createPdfPrintJob(); if (printJob == 0) return 0; if (isHidden()) { printJob->setProperty("noprogressdialog", true); } d->applyDefaultSettings(printJob->printer()); // TODO for remote files we have to first save locally and then upload. printJob->printer().setOutputFileName(pdfFileName); printJob->printer().setColorMode(QPrinter::Color); if (pageLayout.format == KoPageFormat::CustomSize) { printJob->printer().setPaperSize(QSizeF(pageLayout.width, pageLayout.height), QPrinter::Millimeter); } else { printJob->printer().setPaperSize(KoPageFormat::printerPageSize(pageLayout.format)); } switch (pageLayout.orientation) { case KoPageFormat::Portrait: printJob->printer().setOrientation(QPrinter::Portrait); break; case KoPageFormat::Landscape: printJob->printer().setOrientation(QPrinter::Landscape); break; } printJob->printer().setPageMargins(pageLayout.leftMargin, pageLayout.topMargin, pageLayout.rightMargin, pageLayout.bottomMargin, QPrinter::Millimeter); //before printing check if the printer can handle printing if (!printJob->canPrint()) { KMessageBox::error(this, i18n("Cannot export to the specified file")); } printJob->startPrinting(KoPrintJob::DeleteWhenDone); rootView()->setPageLayout(pageLayout); return printJob; } void KoMainWindow::slotConfigureKeys() { QAction* undoAction=0; QAction* redoAction=0; QString oldUndoText; QString oldRedoText; if(currentView()) { //The undo/redo action text is "undo" + command, replace by simple text while inside editor undoAction = currentView()->actionCollection()->action("edit_undo"); redoAction = currentView()->actionCollection()->action("edit_redo"); oldUndoText = undoAction->text(); oldRedoText = redoAction->text(); undoAction->setText(i18n("Undo")); redoAction->setText(i18n("Redo")); } guiFactory()->configureShortcuts(); if(currentView()) { undoAction->setText(oldUndoText); redoAction->setText(oldRedoText); } emit keyBindingsChanged(); } void KoMainWindow::slotConfigureToolbars() { if (rootDocument()) { KConfigGroup componentConfigGroup = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()); saveMainWindowSettings(componentConfigGroup); } KEditToolBar edit(factory(), this); connect(&edit, &KEditToolBar::newToolBarConfig, this, &KoMainWindow::slotNewToolbarConfig); (void) edit.exec(); } void KoMainWindow::slotNewToolbarConfig() { if (rootDocument()) { KConfigGroup componentConfigGroup = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()); applyMainWindowSettings(componentConfigGroup); } KXMLGUIFactory *factory = guiFactory(); Q_UNUSED(factory); // Check if there's an active view if (!d->activeView) return; plugActionList("toolbarlist", d->toolbarList); } void KoMainWindow::slotToolbarToggled(bool toggle) { //debugMain <<"KoMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true; // The action (sender) and the toolbar have the same name KToolBar * bar = toolBar(sender()->objectName()); if (bar) { if (toggle) bar->show(); else bar->hide(); if (rootDocument()) { KConfigGroup componentConfigGroup = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()); saveMainWindowSettings(componentConfigGroup); } } else warnMain << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!"; } bool KoMainWindow::toolbarIsVisible(const char *tbName) { QWidget *tb = toolBar(tbName); return !tb->isHidden(); } void KoMainWindow::showToolbar(const char * tbName, bool shown) { QWidget * tb = toolBar(tbName); if (!tb) { warnMain << "KoMainWindow: toolbar " << tbName << " not found."; return; } if (shown) tb->show(); else tb->hide(); // Update the action appropriately foreach(QAction* action, d->toolbarList) { if (action->objectName() != tbName) { //debugMain <<"KoMainWindow::showToolbar setChecked" << shown; static_cast(action)->setChecked(shown); break; } } } void KoMainWindow::viewFullscreen(bool fullScreen) { if (fullScreen) { window()->setWindowState(window()->windowState() | Qt::WindowFullScreen); // set } else { window()->setWindowState(window()->windowState() & ~Qt::WindowFullScreen); // reset } } void KoMainWindow::slotProgress(int value) { QMutexLocker locker(&d->progressMutex); debugMain << value; if (value <= -1 || value >= 100) { if (d->progress) { statusBar()->removeWidget(d->progress); delete d->progress; d->progress = 0; } d->firstTime = true; return; } if (d->firstTime || !d->progress) { // The statusbar might not even be created yet. // So check for that first, and create it if necessary QStatusBar *bar = findChild(); if (!bar) { statusBar()->show(); QApplication::sendPostedEvents(this, QEvent::ChildAdded); } if (d->progress) { statusBar()->removeWidget(d->progress); delete d->progress; d->progress = 0; } d->progress = new QProgressBar(statusBar()); d->progress->setMaximumHeight(statusBar()->fontMetrics().height()); d->progress->setRange(0, 100); statusBar()->addPermanentWidget(d->progress); d->progress->show(); d->firstTime = false; } if (!d->progress.isNull()) { d->progress->setValue(value); } locker.unlock(); qApp->processEvents(); } void KoMainWindow::setMaxRecentItems(uint _number) { d->recent->setMaxItems(_number); } void KoMainWindow::slotEmailFile() { if (!rootDocument()) return; // Subject = Document file name // Attachment = The current file // Message Body = The current document in HTML export? <-- This may be an option. QString theSubject; QStringList urls; QString fileURL; if (rootDocument()->url().isEmpty() || rootDocument()->isModified()) { //Save the file as a temporary file bool const tmp_modified = rootDocument()->isModified(); QUrl const tmp_url = rootDocument()->url(); QByteArray const tmp_mimetype = rootDocument()->outputMimeType(); // a little open, close, delete dance to make sure we have a nice filename // to use, but won't block windows from creating a new file with this name. QTemporaryFile *tmpfile = new QTemporaryFile(); tmpfile->open(); QString fileName = tmpfile->fileName(); tmpfile->close(); delete tmpfile; QUrl u = QUrl::fromLocalFile(fileName); rootDocument()->setUrl(u); rootDocument()->setModified(true); rootDocument()->setOutputMimeType(rootDocument()->nativeFormatMimeType()); saveDocument(false, true); fileURL = fileName; theSubject = i18n("Document"); urls.append(fileURL); rootDocument()->setUrl(tmp_url); rootDocument()->setModified(tmp_modified); rootDocument()->setOutputMimeType(tmp_mimetype); } else { fileURL = rootDocument()->url().url(); theSubject = i18n("Document - %1", rootDocument()->url().fileName()); urls.append(fileURL); } debugMain << "(" << fileURL << ")"; if (!fileURL.isEmpty()) { KToolInvocation::invokeMailer(QString(), QString(), QString(), theSubject, QString(), //body QString(), urls); // attachments } } void KoMainWindow::slotReloadFile() { KoDocument* pDoc = rootDocument(); if (!pDoc || pDoc->url().isEmpty() || !pDoc->isModified()) return; bool bOk = KMessageBox::questionYesNo(this, i18n("You will lose all changes made since your last save\n" "Do you want to continue?"), i18n("Warning")) == KMessageBox::Yes; if (!bOk) return; QUrl url = pDoc->url(); if (!pDoc->isEmpty()) { saveWindowSettings(); setRootDocument(0); // don't delete this main window when deleting the document if(d->rootDocument) d->rootDocument->clearUndoHistory(); delete d->rootDocument; d->rootDocument = 0; } openDocument(url); return; } void KoMainWindow::slotImportFile() { debugMain; d->isImporting = true; slotFileOpen(); d->isImporting = false; } void KoMainWindow::slotExportFile() { debugMain; d->isExporting = true; slotFileSaveAs(); d->isExporting = false; } bool KoMainWindow::isImporting() const { return d->isImporting; } bool KoMainWindow::isExporting() const { return d->isExporting; } void KoMainWindow::setPartToOpen(KoPart *part) { d->partToOpen = part; } KoComponentData KoMainWindow::componentData() const { return d->componentData; } QDockWidget* KoMainWindow::createDockWidget(KoDockFactoryBase* factory) { QDockWidget* dockWidget = 0; if (!d->dockWidgetsMap.contains(factory->id())) { dockWidget = factory->createDockWidget(); // It is quite possible that a dock factory cannot create the dock; don't // do anything in that case. if (!dockWidget) return 0; d->dockWidgets.push_back(dockWidget); KoDockWidgetTitleBar *titleBar = 0; // Check if the dock widget is supposed to be collapsable if (!dockWidget->titleBarWidget()) { titleBar = new KoDockWidgetTitleBar(dockWidget); dockWidget->setTitleBarWidget(titleBar); titleBar->setCollapsable(factory->isCollapsable()); } dockWidget->setObjectName(factory->id()); dockWidget->setParent(this); if (dockWidget->widget() && dockWidget->widget()->layout()) dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1); Qt::DockWidgetArea side = Qt::RightDockWidgetArea; bool visible = true; switch (factory->defaultDockPosition()) { case KoDockFactoryBase::DockTornOff: dockWidget->setFloating(true); // position nicely? break; case KoDockFactoryBase::DockTop: side = Qt::TopDockWidgetArea; break; case KoDockFactoryBase::DockLeft: side = Qt::LeftDockWidgetArea; break; case KoDockFactoryBase::DockBottom: side = Qt::BottomDockWidgetArea; break; case KoDockFactoryBase::DockRight: side = Qt::RightDockWidgetArea; break; case KoDockFactoryBase::DockMinimized: default: side = Qt::RightDockWidgetArea; visible = false; } if (rootDocument()) { KConfigGroup group = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()).group("DockWidget " + factory->id()); side = static_cast(group.readEntry("DockArea", static_cast(side))); if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea; } addDockWidget(side, dockWidget); if (dockWidget->features() & QDockWidget::DockWidgetClosable) { d->dockWidgetMenu->addAction(dockWidget->toggleViewAction()); if (!visible) dockWidget->hide(); } bool collapsed = factory->defaultCollapsed(); bool locked = false; if (rootDocument()) { KConfigGroup group = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()).group("DockWidget " + factory->id()); collapsed = group.readEntry("Collapsed", collapsed); locked = group.readEntry("Locked", locked); } if (titleBar && collapsed) titleBar->setCollapsed(true); if (titleBar && locked) titleBar->setLocked(true); if (titleBar) { KConfigGroup configGroupInterface = KSharedConfig::openConfig()->group("Interface"); titleBar->setVisible(configGroupInterface.readEntry("ShowDockerTitleBars", true)); } d->dockWidgetsMap.insert(factory->id(), dockWidget); } else { dockWidget = d->dockWidgetsMap[ factory->id()]; } #ifdef Q_OS_MAC dockWidget->setAttribute(Qt::WA_MacSmallSize, true); #endif dockWidget->setFont(KoDockRegistry::dockFont()); connect(dockWidget, &QDockWidget::dockLocationChanged, this, &KoMainWindow::forceDockTabFonts); return dockWidget; } void KoMainWindow::forceDockTabFonts() { QObjectList chis = children(); for (int i = 0; i < chis.size(); ++i) { if (chis.at(i)->inherits("QTabBar")) { ((QTabBar *)chis.at(i))->setFont(KoDockRegistry::dockFont()); } } } QList KoMainWindow::dockWidgets() const { return d->dockWidgetsMap.values(); } /*QList KoMainWindow::canvasObservers() const { QList observers; foreach(QDockWidget *docker, dockWidgets()) { KoCanvasObserverBase *observer = dynamic_cast(docker); if (observer) { observers << observer; } } return observers; }*/ void KoMainWindow::toggleDockersVisibility(bool visible) { if (!visible) { d->m_dockerStateBeforeHiding = saveState(); foreach(QObject* widget, children()) { if (widget->inherits("QDockWidget")) { QDockWidget* dw = static_cast(widget); if (dw->isVisible()) { dw->hide(); } } } } else { restoreState(d->m_dockerStateBeforeHiding); } } KRecentFilesAction *KoMainWindow::recentAction() const { return d->recent; } KoView* KoMainWindow::currentView() const { // XXX if (d->activeView) { return d->activeView; } else if (!d->rootViews.isEmpty()) { return d->rootViews.first(); } return 0; } void KoMainWindow::newView() { Q_ASSERT((d != 0 && d->activeView && d->activePart && d->activeView->koDocument())); KoMainWindow *mainWindow = d->activePart->createMainWindow(); mainWindow->setRootDocument(d->activeView->koDocument(), d->activePart); mainWindow->show(); } void KoMainWindow::createMainwindowGUI() { if ( isHelpMenuEnabled() && !d->m_helpMenu ) { d->m_helpMenu = new KHelpMenu( this, componentData().aboutData(), true ); KActionCollection *actions = actionCollection(); QAction *helpContentsAction = d->m_helpMenu->action(KHelpMenu::menuHelpContents); QAction *whatsThisAction = d->m_helpMenu->action(KHelpMenu::menuWhatsThis); QAction *reportBugAction = d->m_helpMenu->action(KHelpMenu::menuReportBug); QAction *switchLanguageAction = d->m_helpMenu->action(KHelpMenu::menuSwitchLanguage); QAction *aboutAppAction = d->m_helpMenu->action(KHelpMenu::menuAboutApp); QAction *aboutKdeAction = d->m_helpMenu->action(KHelpMenu::menuAboutKDE); if (helpContentsAction) { actions->addAction(helpContentsAction->objectName(), helpContentsAction); } if (whatsThisAction) { actions->addAction(whatsThisAction->objectName(), whatsThisAction); } if (reportBugAction) { actions->addAction(reportBugAction->objectName(), reportBugAction); } if (switchLanguageAction) { actions->addAction(switchLanguageAction->objectName(), switchLanguageAction); } if (aboutAppAction) { actions->addAction(aboutAppAction->objectName(), aboutAppAction); } if (aboutKdeAction) { actions->addAction(aboutKdeAction->objectName(), aboutKdeAction); } } QString f = xmlFile(); setXMLFile( QStandardPaths::locate(QStandardPaths::ConfigLocation, QStringLiteral("ui/ui_standards.rc")) ); if ( !f.isEmpty() ) setXMLFile( f, true ); else { QString auto_file( componentData().componentName() + "ui.rc" ); setXMLFile( auto_file, true ); } guiFactory()->addClient( this ); } // PartManager void KoMainWindow::removePart( KoPart *part ) { if (d->m_registeredPart.data() != part) { return; } d->m_registeredPart = 0; if ( part == d->m_activePart ) { setActivePart(0, 0); } } void KoMainWindow::setActivePart(KoPart *part, QWidget *widget ) { if (part && d->m_registeredPart.data() != part) { warnMain << "trying to activate a non-registered part!" << part->objectName(); return; // don't allow someone call setActivePart with a part we don't know about } // don't activate twice if ( d->m_activePart && part && d->m_activePart == part && (!widget || d->m_activeWidget == widget) ) return; KoPart *oldActivePart = d->m_activePart; QWidget *oldActiveWidget = d->m_activeWidget; d->m_activePart = part; d->m_activeWidget = widget; if (oldActivePart) { KoPart *savedActivePart = part; QWidget *savedActiveWidget = widget; if ( oldActiveWidget ) { disconnect( oldActiveWidget, &QObject::destroyed, this, &KoMainWindow::slotWidgetDestroyed ); } d->m_activePart = savedActivePart; d->m_activeWidget = savedActiveWidget; } if (d->m_activePart && d->m_activeWidget ) { connect( d->m_activeWidget, &QObject::destroyed, this, &KoMainWindow::slotWidgetDestroyed ); } // Set the new active instance in KGlobal // KGlobal::setActiveComponent(d->m_activePart ? d->m_activePart->componentData() : KGlobal::mainComponent()); // old slot called from part manager KoPart *newPart = static_cast(d->m_activePart.data()); if (d->activePart && d->activePart == newPart) { //debugMain <<"no need to change the GUI"; return; } KXMLGUIFactory *factory = guiFactory(); if (d->activeView) { factory->removeClient(d->activeView); unplugActionList("toolbarlist"); qDeleteAll(d->toolbarList); d->toolbarList.clear(); } if (!d->mainWindowGuiIsBuilt) { createMainwindowGUI(); } if (newPart && d->m_activeWidget && d->m_activeWidget->inherits("KoView")) { d->activeView = qobject_cast(d->m_activeWidget); d->activeView->actionCollection()->addAction("view_newview", actionCollection()->action("view_newview")); d->activePart = newPart; //debugMain <<"new active part is" << d->activePart; factory->addClient(d->activeView); // Position and show toolbars according to user's preference setAutoSaveSettings(newPart->componentData().componentName(), false); KConfigGroup configGroupInterface = KSharedConfig::openConfig()->group("Interface"); const bool showDockerTitleBar = configGroupInterface.readEntry("ShowDockerTitleBars", true); foreach (QDockWidget *wdg, d->dockWidgets) { if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) { if (wdg->titleBarWidget()) { wdg->titleBarWidget()->setVisible(showDockerTitleBar); } wdg->setVisible(true); } } // Create and plug toolbar list for Settings menu foreach(QWidget* it, factory->containers("ToolBar")) { KToolBar * toolBar = ::qobject_cast(it); if (toolBar) { KToggleAction * act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this); actionCollection()->addAction(toolBar->objectName().toUtf8(), act); act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle()))); connect(act, &QAction::toggled, this, &KoMainWindow::slotToolbarToggled); act->setChecked(!toolBar->isHidden()); d->toolbarList.append(act); } else warnMain << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!"; } plugActionList("toolbarlist", d->toolbarList); } else { d->activeView = 0; d->activePart = 0; } if (d->activeView) { d->activeView->guiActivateEvent(true); } } void KoMainWindow::slotWidgetDestroyed() { debugMain; if ( static_cast( sender() ) == d->m_activeWidget ) setActivePart(0, 0); //do not remove the part because if the part's widget dies, then the //part will delete itself anyway, invoking removePart() in its destructor } void KoMainWindow::slotDocumentTitleModified(const QString &caption, bool mod) { updateCaption(caption, mod); updateReloadFileAction(d->rootDocument); } void KoMainWindow::showDockerTitleBars(bool show) { foreach (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { dock->titleBarWidget()->setVisible(show); } } KConfigGroup configGroupInterface = KSharedConfig::openConfig()->group("Interface"); configGroupInterface.writeEntry("ShowDockerTitleBars", show); } diff --git a/src/libs/main/calligraplan_shell.rc b/src/libs/main/calligraplan_shell.rc index af1a143d..d56fda2e 100644 --- a/src/libs/main/calligraplan_shell.rc +++ b/src/libs/main/calligraplan_shell.rc @@ -1,55 +1,56 @@ + &File &Settings File diff --git a/src/libs/ui/performance/ProjectStatusViewUi.rc b/src/libs/ui/AccountsEditorUi_readonly.rc similarity index 70% copy from src/libs/ui/performance/ProjectStatusViewUi.rc copy to src/libs/ui/AccountsEditorUi_readonly.rc index a6fcbe6f..941587a0 100644 --- a/src/libs/ui/performance/ProjectStatusViewUi.rc +++ b/src/libs/ui/AccountsEditorUi_readonly.rc @@ -1,21 +1,18 @@ - - + + - - - diff --git a/src/libs/ui/CMakeLists.txt b/src/libs/ui/CMakeLists.txt index e3289b19..adad8bd0 100644 --- a/src/libs/ui/CMakeLists.txt +++ b/src/libs/ui/CMakeLists.txt @@ -1,217 +1,225 @@ include_directories( ${PLANKERNEL_INCLUDES} ${PLANMODELS_INCLUDES} ${PLANMAIN_INCLUDES} ${PLANWIDGETS_INCLUDES} ${KDEPIMLIBS_INCLUDE_DIR} ) #add_subdirectory( tests ) ########### KPlato private library ############### if (PLAN_USE_KREPORT) message(STATUS "-- Building plan with reports capability") add_subdirectory(reports/items) set(planreports_LIB_SRC reports/reportview.cpp reports/reportdata.cpp reports/reportsourceeditor.cpp reports/reportscripts.cpp ) set(planreports_ui_LIB_SRCS reports/reportsourceeditor.ui reports/reportnavigator.ui reports/reportsectionswidget.ui reports/reportgroupsectionswidget.ui reports/reporttoolswidget.ui ) endif() set(planui_LIB_SRCS ${planreports_LIB_SRC} RelationEditorDialog.cpp TasksEditController.cpp TasksEditDialog.cpp RichTextWidget.cpp welcome/WelcomeView.cpp reportsgenerator/ReportsGeneratorView.cpp kptganttitemdelegate.cpp kptworkpackagesendpanel.cpp kptworkpackagesenddialog.cpp kptdocumentseditor.cpp kptdocumentspanel.cpp kptdocumentsdialog.cpp kptitemviewsettup.cpp kptsplitterview.cpp kptrelationeditor.cpp kptdependencyeditor.cpp kptusedefforteditor.cpp kpttaskstatusview.cpp kptcalendareditor.cpp kptviewbase.cpp kptaccountseditor.cpp kptperteditor.cpp kptpertresult.cpp kpttaskeditor.cpp kptresourceeditor.cpp kptscheduleeditor.cpp kptsummarytaskdialog.cpp kptsummarytaskgeneralpanel.cpp kptresourceappointmentsview.cpp kptaccountsviewconfigdialog.cpp kptaccountsview.cpp kpttaskcostpanel.cpp kptmilestoneprogresspanel.cpp kptmilestoneprogressdialog.cpp kpttaskdialog.cpp kptmainprojectdialog.cpp kptmainprojectpanel.cpp kptganttview.cpp gantt/DateTimeTimeLine.cpp gantt/DateTimeGrid.cpp kptrelationdialog.cpp kptrequestresourcespanel.cpp kptresourcedialog.cpp kptstandardworktimedialog.cpp kptintervaledit.cpp kpttaskgeneralpanel.cpp kpttaskprogresspanel.cpp kpttaskprogressdialog.cpp kpttaskdescriptiondialog.cpp kptwbsdefinitiondialog.cpp kptwbsdefinitionpanel.cpp kptresourceassignmentview.cpp kptresourceallocationeditor.cpp kptworkpackagemergedialog.cpp kptrecalculatedialog.cpp kpthtmlview.cpp locale/localemon.cpp kptlocaleconfigmoneydialog.cpp ResourceAllocationView.cpp performance/KPlatoChart.cpp performance/PerformanceStatusBase.cpp performance/ProjectStatusView.cpp performance/PerformanceStatusView.cpp performance/PerformanceTableView.cpp ) ki18n_wrap_ui(planui_LIB_SRCS ${planreports_ui_LIB_SRCS} RelationEditorDialog.ui welcome/WelcomeView.ui kptresourceappointmentsdisplayoptions.ui kptganttchartdisplayoptions.ui kptprintingheaderfooter.ui kptganttprintingoptions.ui kptworkpackagesendpanel.ui kptdocumentspanel.ui performance/PerformanceStatus.ui performance/PerformanceStatusViewSettingsPanel.ui kptcpmwidget.ui kptitemviewsettings.ui kptpertresult.ui standardworktimedialogbase.ui kptwbsdefinitionpanelbase.ui kptaccountsviewconfigurepanelbase.ui kptintervaleditbase.ui kpttaskcostpanelbase.ui kpttaskdescriptionpanelbase.ui kptsummarytaskgeneralpanelbase.ui kptmilestoneprogresspanelbase.ui resourcedialogbase.ui kptmainprojectpanelbase.ui relationpanel.ui kpttaskgeneralpanelbase.ui kpttaskprogresspanelbase.ui kptperteditor.ui kptresourceassignmentview.ui kpttaskstatusviewsettingspanel.ui kptworkpackagemergepanel.ui kptrecalculatedialog.ui kptscheduleeditor.ui locale/localemon.ui ) add_library(planui SHARED ${planui_LIB_SRCS}) generate_export_header(planui) target_link_libraries(planui PUBLIC planmain planmodels KF5::KHtml PRIVATE KChart KF5::ItemViews KF5::IconThemes KF5::Archive KF5::TextWidgets KF5::KIOCore KF5::KIOFileWidgets KF5::KIOWidgets KF5::KIONTLM ) if (PLAN_USE_KREPORT) target_link_libraries(planui PUBLIC KReport PRIVATE KPropertyWidgets) endif() if(KF5AkonadiContact_FOUND) target_link_libraries(planui PRIVATE KF5::AkonadiContact) endif() set_target_properties(planui PROPERTIES VERSION ${GENERIC_PLAN_LIB_VERSION} SOVERSION ${GENERIC_PLAN_LIB_SOVERSION} ) install(TARGETS planui ${INSTALL_TARGETS_DEFAULT_ARGS}) install( FILES AccountsEditorUi.rc + AccountsEditorUi_readonly.rc AccountsViewUi.rc CalendarEditorUi.rc + CalendarEditorUi_readonly.rc TaskEditorUi.rc + TaskEditorUi_readonly.rc DependencyEditorUi.rc + DependencyEditorUi_readonly.rc ResourceEditorUi.rc + ResourceEditorUi_readonly.rc ResourceAppointmentsViewUi.rc ScheduleEditorUi.rc + ScheduleEditorUi_readonly.rc GanttViewUi.rc WorkPackageViewUi.rc + WorkPackageViewUi_readonly.rc reportsgenerator/ReportsGeneratorViewUi.rc + reportsgenerator/ReportsGeneratorViewUi_readonly.rc performance/PerformanceStatusViewUi.rc performance/ProjectStatusViewUi.rc PertResultUi.rc TaskViewUi.rc TaskStatusViewUi.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/calligraplan ) # reports files install(FILES reportsgenerator/templates/EmptyTemplate.odt reportsgenerator/templates/ProjectPerformanceCost.odt reportsgenerator/templates/ProjectPerformanceEffort.odt reportsgenerator/templates/TaskStatus.odt DESTINATION ${DATA_INSTALL_DIR}/calligraplan/reports ) diff --git a/src/libs/ui/CalendarEditorUi.rc b/src/libs/ui/CalendarEditorUi.rc index f4363b48..705a7ace 100644 --- a/src/libs/ui/CalendarEditorUi.rc +++ b/src/libs/ui/CalendarEditorUi.rc @@ -1,28 +1,28 @@ - + diff --git a/src/libs/ui/performance/ProjectStatusViewUi.rc b/src/libs/ui/CalendarEditorUi_readonly.rc similarity index 70% copy from src/libs/ui/performance/ProjectStatusViewUi.rc copy to src/libs/ui/CalendarEditorUi_readonly.rc index a6fcbe6f..941587a0 100644 --- a/src/libs/ui/performance/ProjectStatusViewUi.rc +++ b/src/libs/ui/CalendarEditorUi_readonly.rc @@ -1,21 +1,18 @@ - - + + - - - diff --git a/src/libs/ui/performance/ProjectStatusViewUi.rc b/src/libs/ui/DependencyEditorUi_readonly.rc similarity index 79% copy from src/libs/ui/performance/ProjectStatusViewUi.rc copy to src/libs/ui/DependencyEditorUi_readonly.rc index a6fcbe6f..9dd7cf9e 100644 --- a/src/libs/ui/performance/ProjectStatusViewUi.rc +++ b/src/libs/ui/DependencyEditorUi_readonly.rc @@ -1,21 +1,20 @@ - - + + - diff --git a/src/libs/ui/performance/PerformanceStatusViewUi.rc b/src/libs/ui/ResourceEditorUi_readonly.rc similarity index 83% copy from src/libs/ui/performance/PerformanceStatusViewUi.rc copy to src/libs/ui/ResourceEditorUi_readonly.rc index 7b2628bc..b1679592 100644 --- a/src/libs/ui/performance/PerformanceStatusViewUi.rc +++ b/src/libs/ui/ResourceEditorUi_readonly.rc @@ -1,21 +1,21 @@ - - + + diff --git a/src/libs/ui/performance/ProjectStatusViewUi.rc b/src/libs/ui/ScheduleEditorUi_readonly.rc similarity index 63% copy from src/libs/ui/performance/ProjectStatusViewUi.rc copy to src/libs/ui/ScheduleEditorUi_readonly.rc index a6fcbe6f..f4b42eeb 100644 --- a/src/libs/ui/performance/ProjectStatusViewUi.rc +++ b/src/libs/ui/ScheduleEditorUi_readonly.rc @@ -1,21 +1,17 @@ - - + + - - - - diff --git a/src/libs/ui/performance/ProjectStatusViewUi.rc b/src/libs/ui/TaskEditorUi_readonly.rc similarity index 79% copy from src/libs/ui/performance/ProjectStatusViewUi.rc copy to src/libs/ui/TaskEditorUi_readonly.rc index a6fcbe6f..e6010b69 100644 --- a/src/libs/ui/performance/ProjectStatusViewUi.rc +++ b/src/libs/ui/TaskEditorUi_readonly.rc @@ -1,21 +1,20 @@ - - + + - diff --git a/src/libs/ui/performance/PerformanceStatusViewUi.rc b/src/libs/ui/WorkPackageViewUi_readonly.rc similarity index 64% copy from src/libs/ui/performance/PerformanceStatusViewUi.rc copy to src/libs/ui/WorkPackageViewUi_readonly.rc index 7b2628bc..a3abef4c 100644 --- a/src/libs/ui/performance/PerformanceStatusViewUi.rc +++ b/src/libs/ui/WorkPackageViewUi_readonly.rc @@ -1,21 +1,24 @@ - - + + + - - - + + + + + diff --git a/src/libs/ui/kptaccountseditor.cpp b/src/libs/ui/kptaccountseditor.cpp index 5cc310f2..5e81cb93 100644 --- a/src/libs/ui/kptaccountseditor.cpp +++ b/src/libs/ui/kptaccountseditor.cpp @@ -1,386 +1,392 @@ /* This file is part of the KDE project - Copyright (C) 2007 Dag Andersen - Copyright (C) 2011, 2012 Dag Andersen - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -* Boston, MA 02110-1301, USA. -*/ + * Copyright (C) 2007 Dag Andersen + * Copyright (C) 2011, 2012 Dag Andersen + * Copyright (C) 2019 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ // clazy:excludeall=qstring-arg #include "kptaccountseditor.h" #include "kptcommand.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptaccount.h" #include "kptdatetime.h" #include "Help.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { AccountseditorConfigDialog::AccountseditorConfigDialog( ViewBase *view, AccountTreeView *treeview, QWidget *p, bool selectPrint) : KPageDialog(p), m_view( view ), m_treeview( treeview ) { setWindowTitle( i18n("Settings") ); QTabWidget *tab = new QTabWidget(); QWidget *w = ViewBase::createPageLayoutWidget( view ); tab->addTab( w, w->windowTitle() ); m_pagelayout = w->findChild(); Q_ASSERT( m_pagelayout ); m_headerfooter = ViewBase::createHeaderFooterWidget( view ); m_headerfooter->setOptions( view->printingOptions() ); tab->addTab( m_headerfooter, m_headerfooter->windowTitle() ); KPageWidgetItem *page = addPage( tab, i18n( "Printing" ) ); page->setHeader( i18n( "Printing Options" ) ); if (selectPrint) { setCurrentPage(page); } connect( this, &QDialog::accepted, this, &AccountseditorConfigDialog::slotOk); } void AccountseditorConfigDialog::slotOk() { debugPlan; m_view->setPageLayout( m_pagelayout->pageLayout() ); m_view->setPrintingOptions( m_headerfooter->options() ); } //-------------------- AccountTreeView::AccountTreeView( QWidget *parent ) : TreeViewBase( parent ) { setDragPixmap(koIcon("account").pixmap(32)); header()->setContextMenuPolicy( Qt::CustomContextMenu ); setModel( new AccountItemModel( this ) ); setSelectionModel( new QItemSelectionModel( model() ) ); setSelectionMode( QAbstractItemView::ExtendedSelection ); // setSelectionBehavior( QAbstractItemView::SelectRows ); connect( header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotHeaderContextMenuRequested(QPoint)) ); } void AccountTreeView::slotHeaderContextMenuRequested( const QPoint &pos ) { debugPlan<logicalIndexAt(pos)<<" at"<pos()), event->globalPos() ); } void AccountTreeView::selectionChanged( const QItemSelection &sel, const QItemSelection &desel ) { debugPlan<selectedIndexes() ) { debugPlan<selectedIndexes() ); } void AccountTreeView::currentChanged( const QModelIndex & current, const QModelIndex & previous ) { debugPlan; QTreeView::currentChanged( current, previous ); emit currentChanged( current ); // possible bug in qt: in QAbstractItemView::SingleSelection you can select multiple items/rows selectionModel()->select( current, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); } Account *AccountTreeView::currentAccount() const { return model()->account( currentIndex() ); } Account *AccountTreeView::selectedAccount() const { QModelIndexList lst = selectionModel()->selectedRows(); if ( lst.count() == 1 ) { return model()->account( lst.first() ); } return 0; } QList AccountTreeView::selectedAccounts() const { QList lst; foreach ( const QModelIndex &i, selectionModel()->selectedRows() ) { Account *a = model()->account( i ); if ( a ) { lst << a; } } return lst; } //----------------------------------- AccountsEditor::AccountsEditor(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent) { - setXMLFile("AccountsEditorUi.rc"); + if (doc && doc->isReadWrite()) { + setXMLFile("AccountsEditorUi.rc"); + } else { + setXMLFile("AccountsEditorUi_readonly.rc"); + } + setupGui(); QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new AccountTreeView( this ); connect(this, &ViewBase::expandAll, m_view, &TreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &TreeViewBase::slotCollapse); l->addWidget( m_view ); m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode(QAbstractItemView::DragOnly); m_view->setDropIndicatorShown( false ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( false ); m_view->setAcceptDropsOnView( false ); connect( model(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, SIGNAL(currentChanged(QModelIndex)), this, SLOT(slotCurrentChanged(QModelIndex)) ); connect( m_view, SIGNAL(selectionChanged(QModelIndexList)), this, SLOT(slotSelectionChanged(QModelIndexList)) ); connect( m_view, SIGNAL(contextMenuRequested(QModelIndex,QPoint)), this, SLOT(slotContextMenuRequested(QModelIndex,QPoint)) ); connect( m_view, SIGNAL(headerContextMenuRequested(QPoint)), SLOT(slotHeaderContextMenuRequested(QPoint)) ); Help::add(this, xi18nc("@info:whatsthis", "Cost Breakdown Structure Editor" "" "The Cost Breakdown Structure (CBS) consists of accounts" " organized into a tree structure." " Accounts can be tied to tasks or resources." " Usually there will be two top accounts, one for aggregating costs from tasks" " and one for aggregating costs from resources." "" "This view supports printing using the context menu." "More..." "", Help::page("Manual/Cost_Breakdown_Structure_Editor"))); } void AccountsEditor::updateReadWrite( bool readwrite ) { m_view->setReadWrite( readwrite ); } void AccountsEditor::draw( Project &project ) { m_view->setProject( &project ); } void AccountsEditor::draw() { } void AccountsEditor::setGuiActive( bool activate ) { debugPlan<currentIndex().isValid() ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } slotSelectionChanged( m_view->selectionModel()->selectedRows() ); } } void AccountsEditor::slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ) { debugPlan<setContextMenuIndex(index); slotHeaderContextMenuRequested( pos ); m_view->setContextMenuIndex(QModelIndex()); } void AccountsEditor::slotHeaderContextMenuRequested( const QPoint &pos ) { debugPlan; QList lst = contextActionList(); if ( ! lst.isEmpty() ) { QMenu::exec( lst, pos, lst.first() ); } } Account *AccountsEditor::currentAccount() const { return m_view->currentAccount(); } void AccountsEditor::slotCurrentChanged( const QModelIndex &curr ) { debugPlan< lst = m_view->selectedAccounts(); bool one = lst.count() == 1; bool more = lst.count() > 1; actionAddAccount->setEnabled( on && !more ); actionAddSubAccount->setEnabled( on && one ); bool baselined = project() ? project()->isBaselined() : false; actionDeleteSelection->setEnabled( on && one && ! baselined ); } void AccountsEditor::setupGui() { actionAddAccount = new QAction(koIcon("document-new"), i18n("Add Account"), this); actionCollection()->addAction("add_account", actionAddAccount ); actionCollection()->setDefaultShortcut(actionAddAccount, Qt::CTRL + Qt::Key_I); connect( actionAddAccount, &QAction::triggered, this, &AccountsEditor::slotAddAccount ); actionAddSubAccount = new QAction(koIcon("document-new"), i18n("Add Subaccount"), this); actionCollection()->addAction("add_subaccount", actionAddSubAccount ); actionCollection()->setDefaultShortcut(actionAddSubAccount, Qt::SHIFT + Qt::CTRL + Qt::Key_I); connect( actionAddSubAccount, &QAction::triggered, this, &AccountsEditor::slotAddSubAccount ); actionDeleteSelection = new QAction(koIcon("edit-delete"), i18nc("@action", "Delete"), this); actionCollection()->addAction("delete_selection", actionDeleteSelection ); actionCollection()->setDefaultShortcut(actionDeleteSelection, Qt::Key_Delete); connect( actionDeleteSelection, &QAction::triggered, this, &AccountsEditor::slotDeleteSelection ); createOptionActions(ViewBase::OptionExpand | ViewBase::OptionCollapse | ViewBase::OptionPrint | ViewBase::OptionPrintPreview | ViewBase::OptionPrintPdf | ViewBase::OptionPrintConfig); } void AccountsEditor::slotOptions() { debugPlan; AccountseditorConfigDialog *dlg = new AccountseditorConfigDialog( this, m_view, this ); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->open(); } void AccountsEditor::slotAddAccount() { debugPlan; int row = -1; Account *parent = m_view->selectedAccount(); // sibling if ( parent ) { row = parent->parent() ? parent->parent()->indexOf( parent ) : project()->accounts().indexOf( parent ); if ( row >= 0 ) { ++row; } parent = parent->parent(); } insertAccount( new Account(), parent, row ); } void AccountsEditor::slotAddSubAccount() { debugPlan; insertAccount( new Account(), m_view->selectedAccount(), -1 ); } void AccountsEditor::insertAccount( Account *account, Account *parent, int row ) { m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); QModelIndex i = m_view->model()->insertAccount( account, parent, row ); if ( i.isValid() ) { QModelIndex p = m_view->model()->parent( i ); if (parent) debugPlan<<" parent="<name()<<":"<setExpanded( p, true ); } m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); m_view->edit( i ); } } void AccountsEditor::slotDeleteSelection() { debugPlan; m_view->model()->removeAccounts( m_view->selectedAccounts() ); } void AccountsEditor::slotAccountsOk() { debugPlan<<"Account Editor : slotAccountsOk"; //QModelList //QModelIndex i = m_view->model()->insertGroup( g ); } KoPrintJob *AccountsEditor::createPrintJob() { return m_view->createPrintJob( this ); } bool AccountsEditor::loadContext(const KoXmlElement &context) { m_view->loadContext(model()->columnMap(), context); return true; } void AccountsEditor::saveContext(QDomElement &context) const { m_view->saveContext(model()->columnMap(), context); } void AccountsEditor::slotEditCopy() { m_view->editCopy(); } } // namespace KPlato diff --git a/src/libs/ui/kptcalendareditor.cpp b/src/libs/ui/kptcalendareditor.cpp index d4023fd0..b813847d 100644 --- a/src/libs/ui/kptcalendareditor.cpp +++ b/src/libs/ui/kptcalendareditor.cpp @@ -1,864 +1,869 @@ /* This file is part of the KDE project * Copyright (C) 2007, 2012 Dag Andersen * Copyright (C) 2017 Dag Andersen + * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptcalendareditor.h" #include "kcalendar/kdatepicker.h" #include "kcalendar/kdatetable.h" //#include "kptcalendarpanel.h" #include "kptcommand.h" #include "kptcalendarmodel.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptdatetime.h" #include "kptintervaledit.h" #include "kptitemviewsettup.h" #include "Help.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { //-------------------- CalendarTreeView::CalendarTreeView( QWidget *parent ) : TreeViewBase( parent ) { header()->setContextMenuPolicy( Qt::CustomContextMenu ); setModel( new CalendarItemModel() ); setSelectionBehavior( QAbstractItemView::SelectRows ); setSelectionMode( QAbstractItemView::SingleSelection ); setSelectionModel( new QItemSelectionModel( model() ) ); setItemDelegateForColumn( CalendarItemModel::Scope, new EnumDelegate( this ) ); setItemDelegateForColumn( CalendarItemModel::TimeZone, new EnumDelegate( this ) ); // timezone #ifdef HAVE_KHOLIDAYS setItemDelegateForColumn( CalendarItemModel::HolidayRegion, new EnumDelegate( this ) ); #endif connect( header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotHeaderContextMenuRequested(QPoint)) ); } void CalendarTreeView::slotHeaderContextMenuRequested( const QPoint &pos ) { emit contextMenuRequested(QModelIndex(), mapToGlobal(pos)); } void CalendarTreeView::contextMenuEvent ( QContextMenuEvent *event ) { emit contextMenuRequested( indexAt(event->pos()), event->globalPos() ); } void CalendarTreeView::focusInEvent ( QFocusEvent *event ) { //debugPlan; TreeViewBase::focusInEvent( event ); emit focusChanged(); } void CalendarTreeView::focusOutEvent ( QFocusEvent * event ) { //debugPlan; TreeViewBase::focusInEvent( event ); emit focusChanged(); } void CalendarTreeView::selectionChanged( const QItemSelection &sel, const QItemSelection &desel ) { //debugPlan<selectedIndexes() ) { debugPlan<selectedIndexes() ); } void CalendarTreeView::currentChanged( const QModelIndex & current, const QModelIndex & previous ) { //debugPlan; TreeViewBase::currentChanged( current, previous ); // possible bug in qt: in QAbstractItemView::SingleSelection you can select multiple items/rows selectionModel()->select( current, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); emit currentChanged( current ); } Calendar *CalendarTreeView::currentCalendar() const { return model()->calendar( currentIndex() ); } Calendar *CalendarTreeView::selectedCalendar() const { QModelIndexList lst = selectionModel()->selectedRows(); if ( lst.count() == 1 ) { return model()->calendar( lst.first() ); } return 0; } QList CalendarTreeView::selectedCalendars() const { QList lst; foreach ( const QModelIndex &i, selectionModel()->selectedRows() ) { Calendar *a = model()->calendar( i ); if ( a ) { lst << a; } } return lst; } void CalendarTreeView::dragMoveEvent(QDragMoveEvent *event) { if (dragDropMode() == InternalMove && (event->source() != this || !(event->possibleActions() & Qt::MoveAction))) { return; } TreeViewBase::dragMoveEvent( event ); if ( ! event->isAccepted() ) { return; } // QTreeView thinks it's ok to drop, but it might not be... event->ignore(); QModelIndex index = indexAt( event->pos() ); if ( ! index.isValid() ) { if ( model()->dropAllowed( 0, event->mimeData() ) ) { event->accept(); } return; } Calendar *c = model()->calendar( index ); if ( c == 0 ) { errorPlan<<"no calendar to drop on!"; return; // hmmm } switch ( dropIndicatorPosition() ) { case AboveItem: case BelowItem: // c == sibling // if siblings parent is me or child of me: illegal if ( model()->dropAllowed( c->parentCal(), event->mimeData() ) ) { event->accept(); } break; case OnItem: // c == new parent if ( model()->dropAllowed( c, event->mimeData() ) ) { event->accept(); } break; default: break; } } //-------------------- CalendarDayView::CalendarDayView( QWidget *parent ) : QTableView( parent ), m_readwrite( false ) { setTabKeyNavigation( false ); setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ); horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); m_model = new CalendarDayItemModel( this ); setModel(m_model); verticalHeader()->hide(); actionSetWork = new QAction( i18n( "Work..." ), this ); connect( actionSetWork, &QAction::triggered, this, &CalendarDayView::slotSetWork ); actionSetVacation = new QAction( i18n( "Non-working" ), this ); connect( actionSetVacation, &QAction::triggered, this, &CalendarDayView::slotSetVacation ); actionSetUndefined = new QAction( i18n( "Undefined" ), this ); connect( actionSetUndefined, &QAction::triggered, this, &CalendarDayView::slotSetUndefined ); } QSize CalendarDayView::sizeHint() const { QSize s = QTableView::sizeHint(); s.setHeight( horizontalHeader()->height() + rowHeight( 0 ) + frameWidth() * 2 ); return s; } void CalendarDayView::slotSetWork() { debugPlan; if ( receivers( SIGNAL(executeCommand(KUndo2Command*)) ) == 0 ) { return; } Calendar *cal = model()->calendar(); if ( cal == 0 ) { return; } QModelIndexList lst = selectionModel()->selectedIndexes(); if ( lst.isEmpty() ) { lst << currentIndex(); } if ( lst.isEmpty() ) { return; } QList days; foreach ( const QModelIndex &i, lst ) { CalendarDay *day = model()->day( i ); if ( day == 0 ) { continue; } days << day; } IntervalEditDialog *dlg = new IntervalEditDialog( cal, days, this ); connect(dlg, SIGNAL(finished(int)), SLOT(slotIntervalEditDialogFinished(int))); dlg->open(); } void CalendarDayView::slotIntervalEditDialogFinished( int result ) { IntervalEditDialog *dlg = qobject_cast( sender() ); if ( dlg == 0 ) { return; } if ( result == QDialog::Accepted ) { MacroCommand *cmd = dlg->buildCommand(); if ( cmd ) { emit executeCommand( cmd ); } } dlg->deleteLater(); } void CalendarDayView::slotSetVacation() { debugPlan; if ( receivers( SIGNAL(executeCommand(KUndo2Command*)) ) == 0 ) { return; } QModelIndexList lst = selectionModel()->selectedIndexes(); if ( lst.isEmpty() ) { lst << currentIndex(); } if ( lst.isEmpty() ) { return; } bool mod = false; MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify Weekday State" ) ); foreach ( const QModelIndex &i, lst ) { CalendarDay *day = model()->day( i ); if ( day == 0 || day->state() == CalendarDay::NonWorking ) { continue; } mod = true; m->addCommand( new CalendarModifyStateCmd( model()->calendar(), day, CalendarDay::NonWorking ) ); } if ( mod ) { emit executeCommand( m ); } else { delete m; } } void CalendarDayView::slotSetUndefined() { debugPlan; if ( receivers( SIGNAL(executeCommand(KUndo2Command*)) ) == 0 ) { return; } QModelIndexList lst = selectionModel()->selectedIndexes(); if ( lst.isEmpty() ) { lst << currentIndex(); } if ( lst.isEmpty() ) { return; } bool mod = false; MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify Weekday State" ) ); foreach ( const QModelIndex &i, lst ) { CalendarDay *day = model()->day( i ); if ( day == 0 || day->state() == CalendarDay::Undefined ) { continue; } mod = true; m->addCommand( new CalendarModifyStateCmd( model()->calendar(), day, CalendarDay::Undefined ) ); } if ( mod ) { emit executeCommand( m ); } else { delete m; } } void CalendarDayView::setCurrentCalendar( Calendar *calendar ) { model()->setCalendar( calendar ); } void CalendarDayView::contextMenuEvent ( QContextMenuEvent *event ) { //debugPlan; if ( !isReadWrite() || !model()->calendar() || model()->calendar()->isShared() ) { return; } QMenu menu; menu.addAction( actionSetWork ); menu.addAction( actionSetVacation ); menu.addAction( actionSetUndefined ); menu.exec( event->globalPos(), actionSetWork ); //emit contextMenuRequested( indexAt(event->pos()), event->globalPos() ); } void CalendarDayView::focusInEvent ( QFocusEvent *event ) { //debugPlan; QTableView::focusInEvent( event ); emit focusChanged(); } void CalendarDayView::focusOutEvent ( QFocusEvent * event ) { //debugPlan; QTableView::focusInEvent( event ); emit focusChanged(); } void CalendarDayView::selectionChanged( const QItemSelection &sel, const QItemSelection &desel ) { //debugPlan<selectedIndexes() ) { debugPlan<selectedIndexes() ); } void CalendarDayView::currentChanged( const QModelIndex & current, const QModelIndex & previous ) { //debugPlan; QTableView::currentChanged( current, previous ); // selectionModel()->select( current, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); emit currentChanged( current ); } CalendarDay *CalendarDayView::selectedDay() const { QModelIndexList lst = selectionModel()->selectedIndexes(); if ( lst.count() == 1 ) { return model()->day( lst.first() ); } return 0; } //----------------------------------- CalendarEditor::CalendarEditor(KoPart *part, KoDocument *doc, QWidget *parent ) : ViewBase(part, doc, parent ), m_model( new DateTableDataModel( this ) ) { - setXMLFile("CalendarEditorUi.rc"); + if (doc && doc->isReadWrite()) { + setXMLFile("CalendarEditorUi.rc"); + } else { + setXMLFile("CalendarEditorUi_readonly.rc"); + } Help::add(this, xi18nc( "@info:whatsthis", "Work & Vacation Editor" "" "A calendar defines availability for resources or tasks of type Duration. " "A calendar can be specific to a resource or task, or shared by multiple resources or tasks. " "A day can be of type Undefined, Non-working day or Working day. " "A working day has one or more work intervals defined. " "" "A calendar can have sub calendars. If a day is undefined in a calendar, the parent calendar is checked. " "An Undefined day defaults to Non-working if used by a resource, or available all day if used by a task." "" "A calendar can be defined as the Default calendar. " "The default calendar is used by a working resource, when the resources calendar is not explicitly set." "More..." "", Help::page("Manual/Work_and_Vacation_Editor"))); setupGui(); QVBoxLayout *l = new QVBoxLayout( this ); l->setMargin( 0 ); QSplitter *sp = new QSplitter( this ); l->addWidget( sp ); m_calendarview = new CalendarTreeView( sp ); connect(this, &ViewBase::expandAll, m_calendarview, &TreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_calendarview, &TreeViewBase::slotCollapse); QFrame *f = new QFrame( sp ); l = new QVBoxLayout( f ); l->setMargin( 0 ); m_dayview = new CalendarDayView( f ); l->addWidget( m_dayview ); sp = new QSplitter( f ); l->addWidget( sp ); m_datePicker = new KDatePicker( sp ); m_datePicker->setFrameStyle( QFrame::StyledPanel | QFrame::Sunken ); m_datePicker->dateTable()->setWeekNumbersEnabled( true ); m_datePicker->dateTable()->setGridEnabled( true ); m_datePicker->dateTable()->setSelectionMode( KDateTable::ExtendedSelection ); m_datePicker->dateTable()->setDateDelegate( new DateTableDateDelegate( m_datePicker->dateTable() ) ); m_datePicker->dateTable()->setModel( m_model ); m_datePicker->dateTable()->setPopupMenuEnabled( true ); m_calendarview->setDragDropMode( QAbstractItemView::InternalMove ); m_calendarview->setDropIndicatorShown( true ); m_calendarview->setDragEnabled ( true ); m_calendarview->setAcceptDrops( true ); m_calendarview->setAcceptDropsOnView( true ); connect( m_datePicker->dateTable(), SIGNAL(aboutToShowContextMenu(QMenu*,QDate)), SLOT(slotContextMenuDate(QMenu*,QDate)) ); connect( m_datePicker->dateTable(), SIGNAL(aboutToShowContextMenu(QMenu*,QList)), SLOT(slotContextMenuDate(QMenu*,QList)) ); /* const QDate date(2007,7,19); const QColor fgColor(Qt::darkGray); KDateTable::BackgroundMode bgMode = KDateTable::CircleMode; const QColor bgColor( Qt::lightGray); m_datePicker->dateTable()->setCustomDatePainting( date, fgColor, bgMode, bgColor );*/ m_calendarview->setEditTriggers( m_calendarview->editTriggers() | QAbstractItemView::EditKeyPressed ); m_dayview->setEditTriggers( m_dayview->editTriggers() | QAbstractItemView::EditKeyPressed ); m_calendarview->setDragDropMode( QAbstractItemView::InternalMove ); m_calendarview->setDropIndicatorShown ( true ); m_calendarview->setDragEnabled ( true ); m_calendarview->setAcceptDrops( true ); connect( m_calendarview->model(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_dayview->model(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_dayview, &CalendarDayView::executeCommand, doc, &KoDocument::addCommand ); connect( m_calendarview, SIGNAL(currentChanged(QModelIndex)), this, SLOT(slotCurrentCalendarChanged(QModelIndex)) ); connect( m_calendarview, &CalendarTreeView::sigSelectionChanged, this, &CalendarEditor::slotCalendarSelectionChanged ); connect( m_calendarview, SIGNAL(contextMenuRequested(QModelIndex,QPoint)), this, SLOT(slotContextMenuCalendar(QModelIndex,QPoint)) ); connect( m_dayview, SIGNAL(currentChanged(QModelIndex)), this, SLOT(slotCurrentDayChanged(QModelIndex)) ); connect( m_dayview, &CalendarDayView::sigSelectionChanged, this, &CalendarEditor::slotDaySelectionChanged ); connect( m_dayview, &CalendarDayView::contextMenuRequested, this, &CalendarEditor::slotContextMenuDay ); connect( m_dayview->model(), &QAbstractItemModel::dataChanged, this, &CalendarEditor::slotEnableActions ); connect( m_calendarview, &CalendarTreeView::focusChanged, this, &CalendarEditor::slotEnableActions ); connect( m_dayview, &CalendarDayView::focusChanged, this, &CalendarEditor::slotEnableActions ); } void CalendarEditor::draw( Project &project ) { m_calendarview->setProject( &project ); m_dayview->setProject( &project ); } void CalendarEditor::draw() { } void CalendarEditor::setGuiActive( bool activate ) { //debugPlan<currentIndex().isValid() ) { m_calendarview->selectionModel()->setCurrentIndex(m_calendarview->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } //slotSelectionChanged( m_calendarview->selectionModel()->selectedRows() ); } } void CalendarEditor::slotContextMenuDate( QMenu *menu, const QList &dates ) { if ( ! isReadWrite() ) { return; } if (!currentCalendar() || currentCalendar()->isShared()) { return; } if ( dates.isEmpty() ) { m_currentMenuDateList << m_datePicker->date(); } else { m_currentMenuDateList = dates; } menu->addAction( actionSetWork ); menu->addAction( actionSetVacation ); menu->addAction( actionSetUndefined ); } void CalendarEditor::slotContextMenuDate( QMenu *menu, const QDate &date ) { debugPlan<isShared()) { return; } m_currentMenuDateList << date; menu->addAction( actionSetWork ); menu->addAction( actionSetVacation ); menu->addAction( actionSetUndefined ); } void CalendarEditor::slotContextMenuCalendar( const QModelIndex &index, const QPoint& pos ) { if (!index.isValid()) { slotHeaderContextMenuRequested(pos); return; } if ( ! isReadWrite() || !currentCalendar() ) { return; } //debugPlan<model()->calendar( index ); if ( a ) { name = "calendareditor_calendar_popup"; } }*/ //debugPlan<setContextMenuIndex(index); if ( name.isEmpty() ) { slotHeaderContextMenuRequested(pos); m_calendarview->setContextMenuIndex(QModelIndex()); return; } emit requestPopupMenu( name, pos ); m_calendarview->setContextMenuIndex(QModelIndex()); } void CalendarEditor::slotContextMenuDay( const QModelIndex &index, const QPoint& pos ) { if ( ! isReadWrite() ) { return; } debugPlan<model()->day( index ) ) { name = "calendareditor_day_popup"; } } debugPlan<loadContext(m_calendarview->model()->columnMap(), context); } void CalendarEditor::saveContext( QDomElement &context ) const { m_calendarview->saveContext(m_calendarview->model()->columnMap(), context); } Calendar *CalendarEditor::currentCalendar() const { return m_calendarview->currentCalendar(); } void CalendarEditor::slotCurrentCalendarChanged( const QModelIndex & ) { //debugPlan<setCurrentCalendar( currentCalendar() ); if ( m_model ) { m_model->setCalendar( currentCalendar() ); } } void CalendarEditor::slotCalendarSelectionChanged( const QModelIndexList& /*list */) { //debugPlan< lst = m_calendarview->selectedCalendars(); bool one = lst.count() == 1; bool more = lst.count() > 1; actionAddCalendar ->setEnabled( on && !more ); actionAddSubCalendar ->setEnabled( on && one ); actionDeleteSelection->setEnabled( on && ( one || more ) ); } void CalendarEditor::setupGui() { KActionCollection *coll = actionCollection(); actionAddCalendar = new QAction(koIcon("resource-calendar-insert"), i18n("Add Calendar"), this); coll->addAction("add_calendar", actionAddCalendar ); coll->setDefaultShortcut(actionAddCalendar, Qt::CTRL + Qt::Key_I); connect( actionAddCalendar , &QAction::triggered, this, &CalendarEditor::slotAddCalendar ); actionAddSubCalendar = new QAction(koIcon("resource-calendar-child-insert"), i18n("Add Subcalendar"), this); coll->addAction("add_subcalendar", actionAddSubCalendar ); coll->setDefaultShortcut(actionAddSubCalendar, Qt::SHIFT + Qt::CTRL + Qt::Key_I); connect( actionAddSubCalendar , &QAction::triggered, this, &CalendarEditor::slotAddSubCalendar ); actionDeleteSelection = new QAction(koIcon("edit-delete"), xi18nc("@action", "Delete"), this); coll->addAction("delete_selection", actionDeleteSelection ); coll->setDefaultShortcut(actionDeleteSelection, Qt::Key_Delete); connect( actionDeleteSelection, &QAction::triggered, this, &CalendarEditor::slotDeleteCalendar ); actionSetWork = new QAction( i18n( "Work..." ), this ); connect( actionSetWork, &QAction::triggered, this, &CalendarEditor::slotSetWork ); actionSetVacation = new QAction( i18n( "Non-working" ), this ); connect( actionSetVacation, &QAction::triggered, this, &CalendarEditor::slotSetVacation ); actionSetUndefined = new QAction( i18n( "Undefined" ), this ); connect( actionSetUndefined, &QAction::triggered, this, &CalendarEditor::slotSetUndefined ); createOptionActions(ViewBase::OptionExpand | ViewBase::OptionCollapse | ViewBase::OptionViewConfig); } void CalendarEditor::slotOptions() { ItemViewSettupDialog *dlg = new ItemViewSettupDialog( this, m_calendarview, false, this ); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->open(); } void CalendarEditor::updateReadWrite( bool readwrite ) { m_calendarview->setReadWrite( readwrite ); m_dayview->setReadWrite( readwrite ); ViewBase::updateReadWrite( readwrite ); } void CalendarEditor::slotAddCalendar () { //debugPlan; // get parent through sibling Calendar *cal = m_calendarview->selectedCalendar(); Calendar *parent = cal ? cal->parentCal() : 0; int pos = parent ? parent->indexOf( cal ) : project()->indexOf( cal ); if ( pos >= 0 ) { ++pos; // after selected calendar } insertCalendar ( new Calendar(), parent, pos ); } void CalendarEditor::slotAddSubCalendar () { //debugPlan; insertCalendar ( new Calendar (), m_calendarview->selectedCalendar () ); } void CalendarEditor::insertCalendar ( Calendar *calendar, Calendar *parent, int pos ) { m_calendarview->closePersistentEditor( m_calendarview->selectionModel()->currentIndex() ); QModelIndex i = m_calendarview->model()->insertCalendar ( calendar, pos, parent ); if ( i.isValid() ) { QModelIndex p = m_calendarview->model()->parent( i ); //if (parent) debugPlan<<" parent="<name()<<":"<setExpanded( p, true ); m_calendarview->setCurrentIndex( i ); m_calendarview->edit( i ); } } void CalendarEditor::slotDeleteCalendar() { //debugPlan; m_calendarview->model()->removeCalendar( m_calendarview->selectedCalendar() ); } void CalendarEditor::slotAddInterval () { //debugPlan; /* CalendarDay *parent = m_dayview->selectedDay (); if ( parent == 0 ) { TimeInterval *ti = m_dayview->selectedInterval(); if ( ti == 0 ) { return; } parent = m_dayview->model()->parentDay( ti ); if ( parent == 0 ) { return; } } QModelIndex i = m_dayview->model()->insertInterval( new TimeInterval(), parent ); if ( i.isValid() ) { QModelIndex p = m_dayview->model()->index( parent ); m_dayview->setExpanded( p, true ); m_dayview->setCurrentIndex( i ); m_dayview->edit( i ); }*/ } void CalendarEditor::slotDeleteDaySelection() { //debugPlan; /* TimeInterval *ti = m_dayview->selectedInterval(); if ( ti != 0 ) { m_dayview->model()->removeInterval( ti ); return; } CalendarDay *day = m_dayview->selectedDay(); if ( day != 0 ) { m_dayview->model()->removeDay( day ); }*/ } void CalendarEditor::slotAddDay () { //debugPlan; /* Calendar *c = currentCalendar(); if ( c == 0 ) { return; } QDate date = QDate::currentDate(); while ( c->day( date ) ) { date = date.addDays( 1 ); } QModelIndex i = m_dayview->model()->insertDay( new CalendarDay(date, CalendarDay::NonWorking ) ); if ( i.isValid() ) { QModelIndex p = m_dayview->model()->parent( i ); m_dayview->setExpanded( p, true ); m_dayview->setCurrentIndex( i ); m_dayview->edit( i ); }*/ } void CalendarEditor::slotSetWork() { debugPlan<open(); m_currentMenuDateList.clear(); } void CalendarEditor::slotIntervalEditDialogFinished( int result ) { IntervalEditDialog *dlg = qobject_cast( sender() ); if ( dlg == 0 ) { return; } if ( result == QDialog::Accepted ) { MacroCommand *cmd = dlg->buildCommand(); if ( cmd ) { part()->addCommand( cmd ); } } dlg->deleteLater(); } void CalendarEditor::slotSetVacation() { debugPlan<findDay( date ); if ( day == 0 ) { mod = true; day = new CalendarDay( date, CalendarDay::NonWorking ); m->addCommand( new CalendarAddDayCmd( currentCalendar(), day ) ); if ( m_currentMenuDateList.count() == 1 ) { m->setText( kundo2_i18n( "%1: Set to Non-Working", date.toString() ) ); } } else if ( day->state() != CalendarDay::NonWorking ) { mod = true; m->addCommand( new CalendarModifyStateCmd( currentCalendar(), day, CalendarDay::NonWorking ) ); if ( m_currentMenuDateList.count() == 1 ) { m->setText( kundo2_i18n( "%1: Set to Non-Working", date.toString() ) ); } } } if ( mod ) { part()->addCommand( m ); } else { delete m; } m_currentMenuDateList.clear(); } void CalendarEditor::slotSetUndefined() { debugPlan; if ( m_currentMenuDateList.isEmpty() || currentCalendar() == 0 ) { return; } bool mod = false; MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify Calendar" ) ); foreach ( const QDate &date, m_currentMenuDateList ) { CalendarDay *day = currentCalendar()->findDay( date ); if ( day && day->state() != CalendarDay::Undefined ) { mod = true; m->addCommand( new CalendarRemoveDayCmd( currentCalendar(), day ) ); if ( m_currentMenuDateList.count() == 1 ) { m->setText( kundo2_i18n( "Set %1 to Undefined", date.toString() ) ); } } } if ( mod ) { part()->addCommand( m ); } else { delete m; } m_currentMenuDateList.clear(); } } // namespace KPlato diff --git a/src/libs/ui/kptdependencyeditor.cpp b/src/libs/ui/kptdependencyeditor.cpp index 8f909f2b..a542e157 100644 --- a/src/libs/ui/kptdependencyeditor.cpp +++ b/src/libs/ui/kptdependencyeditor.cpp @@ -1,2470 +1,2476 @@ /* This file is part of the KDE project - Copyright (C) 2007 Dag Andersen - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -* Boston, MA 02110-1301, USA. -*/ + * Copyright (C) 2007 Dag Andersen + * Copyright (C) 2019 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ // clazy:excludeall=qstring-arg #include "kptdependencyeditor.h" #include "PlanMacros.h" #include "kptglobal.h" #include "kptcommonstrings.h" #include "kptitemmodelbase.h" #include "kptcommand.h" #include "kptproject.h" #include "kptrelation.h" #include "kptschedule.h" #include "kptdebug.h" #include "config.h" #include "Help.h" #include "RelationEditorDialog.h" #include "KoPageLayoutWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ConnectCursor Qt::DragLinkCursor namespace KPlato { void plan_paintFocusSelectedItem( QPainter *painter, const QStyleOptionGraphicsItem *option ) { if ( option->state & ( QStyle::State_Selected | QStyle::State_HasFocus ) ) { painter->save(); if (option->state & QStyle::State_Selected) { debugPlanDepEditor<<"selected"; QPalette::ColorGroup cg = option->state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(option->state & QStyle::State_Active)) cg = QPalette::Inactive; QLinearGradient g( 0.0, option->rect.top(), 0.0, option->rect.bottom() ); QColor col = option->palette.brush(cg, QPalette::Highlight).color(); g.setColorAt( 0.0, col.lighter( 125 ) ); g.setColorAt( 1.0, col.lighter( 60 ) ); painter->setPen( Qt::NoPen ); painter->setBrush( QBrush( g ) ); painter->drawRect( option->exposedRect ); } if ( option->state & QStyle::State_HasFocus ) { debugPlanDepEditor<<"has focus"; QPalette::ColorGroup cg = option->state & QStyle::State_Enabled ? QPalette::Active : QPalette::Disabled; if (cg == QPalette::Active && !(option->state & QStyle::State_Active)) cg = QPalette::Inactive; QPen p( Qt::DotLine ); p.setWidthF( 2. ); if ( option->state & QStyle::State_Selected ) { p.setColor( option->palette.color( cg, QPalette::Shadow ) ); debugPlanDepEditor<<"focus: selected"<palette.color( cg, QPalette::Highlight ) ); debugPlanDepEditor<<"focus: not selected"<setPen( p ); painter->setBrush( Qt::NoBrush ); painter->drawRect( option->exposedRect ); } painter->restore(); } } //---------------------- DependecyViewPrintingDialog::DependecyViewPrintingDialog( ViewBase *parent, DependencyView *view ) : PrintingDialog( parent ), m_depview( view ) { debugPlanDepEditor< DependecyViewPrintingDialog::createOptionWidgets() const { QList lst; lst << createPageLayoutWidget(); lst += PrintingDialog::createOptionWidgets(); return lst; } void DependecyViewPrintingDialog::printPage( int page, QPainter &painter ) { painter.save(); QRect hRect = headerRect(); QRect fRect = footerRect(); QRect pageRect = printer().pageRect(); pageRect.moveTo( 0, 0 ); debugPlanDepEditor<project()) ); int gap = 8; int pageHeight = pageRect.height(); if ( hRect.isValid() ) { pageHeight -= ( hRect.height() + gap ); } if ( fRect.isValid() ) { pageHeight -= ( fRect.height() + gap ); } painter.translate( 0, hRect.height() + gap ); QRect r( 0, 0, pageRect.width(), pageHeight ); m_depview->itemScene()->render( &painter, r ); painter.restore(); } DependencyLinkItemBase::DependencyLinkItemBase( QGraphicsItem *parent ) : QGraphicsPathItem( parent ), m_editable( false ), predItem( 0 ), succItem( 0 ), relation( 0 ), m_arrow( new QGraphicsPathItem( this ) ) { } DependencyLinkItemBase::DependencyLinkItemBase( DependencyNodeItem *predecessor, DependencyNodeItem *successor, Relation *rel, QGraphicsItem *parent ) : QGraphicsPathItem( parent ), m_editable( false ), predItem( predecessor ), succItem( successor ), relation( rel ), m_arrow( new QGraphicsPathItem( this ) ) { } DependencyLinkItemBase::~DependencyLinkItemBase() { } DependencyScene *DependencyLinkItemBase::itemScene() const { return static_cast( scene() ); } void DependencyLinkItemBase::createPath( const QPointF &sp, int starttype, const QPointF &ep, int endtype ) { //if ( predItem && succItem ) debugPlanDepEditor<text()<<" ->"<text()<<" visible="<horizontalGap(); bool up = sp.y() > ep.y(); bool right = sp.x() < ep.x(); bool same = sp.x() == ep.x(); QPainterPath link( sp ); qreal x = sp.x(); qreal y = sp.y(); if ( right && starttype == DependencyNodeItem::Finish) { x = ep.x(); x += endtype == DependencyNodeItem::Start ? - hgap/2 - 6 : hgap/2 - 6; link.lineTo( x, y ); x += 6; QPointF cp( x, y ); y += up ? -6 : +6; link.quadTo( cp, QPointF( x, y ) ); y = up ? ep.y() + 6 : ep.y() - 6; link.lineTo( x, y ); y = ep.y(); cp = QPointF( x, y ); x += endtype == DependencyNodeItem::Start ? 6 : -6; link.quadTo( cp, QPointF( x, y ) ); } else if ( right && starttype == DependencyNodeItem::Start ) { x = sp.x() - hgap/2 + 6; link.lineTo( x, y ); x -= 6; QPointF cp( x, y ); y += up ? -6 : +6; link.quadTo( cp, QPointF( x, y ) ); y = up ? ep.y() + 6 : ep.y() - 6; link.lineTo( x, y ); y = ep.y(); cp = QPointF( x, y ); x += endtype == DependencyNodeItem::Start ? 6 : -6; link.quadTo( cp, QPointF( x, y ) ); } else if ( same ) { x = ep.x(); x += endtype == DependencyNodeItem::Start ? - hgap/2 + 6 : hgap/2 - 6; link.lineTo( x, y ); x += endtype == DependencyNodeItem::Start ? -6 : +6; QPointF cp( x, y ); y += up ? -6 : 6; link.quadTo( cp, QPointF( x, y ) ); y = up ? ep.y() + 6 : ep.y() - 6; link.lineTo( x, y ); y = ep.y(); cp = QPointF( x, y ); if ( endtype == DependencyNodeItem::Start ) { x += 6; } else { x -= 6; } link.quadTo( cp, QPointF( x, y ) ); } else { x = ep.x(); x += endtype == DependencyNodeItem::Start ? - hgap/2 + 6 : hgap/2 + 6; link.lineTo( x, y ); x -= 6; QPointF cp( x, y ); y += up ? -6 : 6; link.quadTo( cp, QPointF( x, y ) ); y = up ? ep.y() + 6 : ep.y() - 6; link.lineTo( x, y ); y = ep.y(); cp = QPointF( x, y ); x += endtype == DependencyNodeItem::Start ? 6 : -6; link.quadTo( cp, QPointF( x, y ) ); } link.lineTo( ep ); setPath( link ); QPainterPath arrow; x = endtype == DependencyNodeItem::Start ? -6 : 6; arrow.moveTo( ep ); arrow.lineTo( ep.x() + x, ep.y() - 3 ); arrow.lineTo( ep.x() + x, ep.y() + 3 ); arrow.lineTo( ep ); m_arrow->setPath( arrow ); m_arrow->show(); } //-------------------------------- DependencyLinkItem::DependencyLinkItem( DependencyNodeItem *predecessor, DependencyNodeItem *successor, Relation *rel, QGraphicsItem *parent ) : DependencyLinkItemBase( predecessor, successor, rel, parent ) { setZValue( 100.0 ); setAcceptHoverEvents( true ); //debugPlanDepEditor<text()<<"("<column()<<") -"<text(); predItem->addChildRelation( this ); succItem->addParentRelation( this ); succItem->setColumn(); m_arrow->setBrush( Qt::black ); m_pen = pen(); } DependencyLinkItem::~DependencyLinkItem() { if ( predItem ) { predItem->takeChildRelation( this ); } if ( succItem ) { succItem->takeParentRelation( this ); } } int DependencyLinkItem::newChildColumn() const { int col = predItem->column(); if ( relation->type() == Relation::FinishStart ) { ++col; } //debugPlanDepEditor<<"new col="<isVisible() && succItem->isVisible() ); } void DependencyLinkItem::createPath() { setVisible( predItem->isVisible() && succItem->isVisible() ); if ( ! isVisible() ) { //debugPlanDepEditor<<"Visible="<node()->name()<<" -"<node()->name(); return; } QPointF sp = startPoint(); QPointF ep = endPoint(); int stype = 0, etype = 0; switch ( relation->type() ) { case Relation::StartStart: stype = DependencyNodeItem::Start; etype = DependencyNodeItem::Start; break; case Relation::FinishStart: stype = DependencyNodeItem::Finish; etype = DependencyNodeItem::Start; break; case Relation::FinishFinish: stype = DependencyNodeItem::Finish; etype = DependencyNodeItem::Finish; break; default: break; } DependencyLinkItemBase::createPath( sp, stype, ep, etype ); } QPointF DependencyLinkItem::startPoint() const { if ( relation->type() == Relation::StartStart ) { return predItem->connectorPoint( DependencyNodeItem::Start ); } return predItem->connectorPoint( DependencyNodeItem::Finish ); } QPointF DependencyLinkItem::endPoint() const { if ( relation->type() == Relation::FinishFinish ) { return succItem->connectorPoint( DependencyNodeItem::Finish ); } return succItem->connectorPoint( DependencyNodeItem::Start ); } void DependencyLinkItem::hoverEnterEvent( QGraphicsSceneHoverEvent * /*event*/ ) { setZValue( zValue() + 1 ); setPen( QPen( Qt::black, 2 ) ); m_arrow->setPen( pen() ); update(); } void DependencyLinkItem::hoverLeaveEvent( QGraphicsSceneHoverEvent * /*event*/ ) { resetHooverIndication(); } void DependencyLinkItem::resetHooverIndication() { setZValue( zValue() - 1 ); setPen( m_pen ); m_arrow->setPen( m_pen ); update(); } void DependencyLinkItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { //debugPlanDepEditor; QGraphicsItem::GraphicsItemFlags f = flags(); if ( isEditable() && itemScene()->connectionMode() ) { itemScene()->clearConnection(); setFlags( f & ~QGraphicsItem::ItemIsSelectable ); } QGraphicsPathItem::mousePressEvent( event ); if ( f != flags() ) { setFlags( f ); } } //-------------------- DependencyCreatorItem::DependencyCreatorItem( QGraphicsItem *parent ) : DependencyLinkItemBase( parent ), predConnector( 0 ), succConnector( 0 ), m_editable( false ) { setZValue( 1000.0 ); clear(); setPen( QPen( Qt::blue, 2 ) ); m_arrow->setBrush( Qt::blue ); m_arrow->setPen( QPen( Qt::blue, 2 ) ); } void DependencyCreatorItem::clear() { hide(); if ( predConnector && predConnector->parentItem() ) { static_cast( predConnector->parentItem() )->setConnectorHoverMode( true ); } else if ( succConnector && succConnector->parentItem() ) { static_cast( succConnector->parentItem() )->setConnectorHoverMode( true ); } predConnector = 0; succConnector = 0; setPath( QPainterPath() ); m_arrow->setPath( QPainterPath() ); } void DependencyCreatorItem::setPredConnector( DependencyConnectorItem *item ) { predConnector = item; //static_cast( item->parentItem() )->setConnectorHoverMode( false ); } void DependencyCreatorItem::setSuccConnector( DependencyConnectorItem *item ) { succConnector = item; } void DependencyCreatorItem::createPath() { if ( predConnector == 0 ) { return; } if ( succConnector == 0 ) { return; } QPointF sp = predConnector->connectorPoint(); QPointF ep = succConnector->connectorPoint(); DependencyLinkItemBase::createPath( sp, predConnector->ctype(), ep, succConnector->ctype() ); } void DependencyCreatorItem::createPath( const QPointF &ep ) { m_arrow->hide(); if ( succConnector ) { return createPath(); } if ( predConnector == 0 ) { return; } QPointF sp = predConnector->connectorPoint(); QPainterPath link( sp ); link.lineTo( ep ); setPath( link ); } QPointF DependencyCreatorItem::startPoint() const { return predConnector == 0 ? QPointF() : predConnector->connectorPoint(); } QPointF DependencyCreatorItem::endPoint() const { return succConnector == 0 ? QPointF() : succConnector->connectorPoint(); } //-------------------- DependencyConnectorItem::DependencyConnectorItem( DependencyNodeItem::ConnectorType type, DependencyNodeItem *parent ) : QGraphicsRectItem( parent ), m_ctype( type ), m_editable( false ) { setCursor( ConnectCursor); setAcceptHoverEvents( true ); setZValue( 500.0 ); setFlag( QGraphicsItem::ItemIsFocusable ); } DependencyScene *DependencyConnectorItem::itemScene() const { return static_cast( scene() ); } DependencyNodeItem *DependencyConnectorItem::nodeItem() const { return static_cast( parentItem() ); } Node *DependencyConnectorItem::node() const { return static_cast( parentItem() )->node(); } QPointF DependencyConnectorItem::connectorPoint() const { QRectF r = rect(); return QPointF( r.x()+r.width(), r.y() + r.height()/2 ); } void DependencyConnectorItem::hoverEnterEvent( QGraphicsSceneHoverEvent * /*event*/ ) { itemScene()->connectorEntered( this, true ); } void DependencyConnectorItem::hoverLeaveEvent( QGraphicsSceneHoverEvent * /*event*/ ) { itemScene()->connectorEntered( this, false ); } void DependencyConnectorItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { if ( ! isEditable() ) { event->ignore(); return; } if (event->button() == Qt::LeftButton ) { m_mousePressPos = event->pos(); } else { event->ignore(); } } void DependencyConnectorItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { m_mousePressPos = QPointF(); if (event->button() != Qt::LeftButton ) { event->ignore(); return; } if ( rect().contains( event->scenePos() ) ) { // user clicked on this item bool multiSelect = (event->modifiers() & Qt::ControlModifier) != 0; if (multiSelect) { itemScene()->multiConnectorClicked( this ); } else { itemScene()->singleConnectorClicked( this ); } return; } QGraphicsItem *item = 0; foreach ( QGraphicsItem *i, itemScene()->items( event->scenePos() ) ) { if ( i->type() == DependencyConnectorItem::Type ) { item = i; break; } } if ( item == 0 || item == itemScene()->fromItem() ) { itemScene()->setFromItem( 0 ); return; } itemScene()->singleConnectorClicked( static_cast( item ) ); } void DependencyConnectorItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (event->buttons() == Qt::LeftButton ) { if ( ! m_mousePressPos.isNull() ) { itemScene()->setFromItem( this ); m_mousePressPos = QPointF(); } QGraphicsItem *item = 0; foreach ( QGraphicsItem *i, itemScene()->items( event->scenePos() ) ) { if ( i->type() == DependencyConnectorItem::Type ) { item = i; break; } } if ( item != this ) { itemScene()->connectorEntered( this, false ); } if ( item != 0 ) { itemScene()->connectorEntered( static_cast( item ), true ); } } else { event->ignore(); } } void DependencyConnectorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget */*widget*/) { //debugPlanDepEditor; QStyleOptionGraphicsItem opt( *option ); opt.exposedRect = rect(); if ( itemScene()->fromItem() == this ) { opt.state |= QStyle::State_Selected; } if ( itemScene()->focusItem() == this ) { opt.state |= QStyle::State_HasFocus; } plan_paintFocusSelectedItem( painter, &opt ); QRectF r = rect(); if ( ctype() == DependencyNodeItem::Start ) { r.setRect( r.right() - (r.width()/2.0) + 1.0, r.y() + ( r.height() * 0.33 ), r.width() / 2.0, r.height() * 0.33 ); } else { r.setRect( r.right() - (r.width()/2.0) - 1.0, r.y() + ( r.height() * 0.33 ), r.width() / 2.0, r.height() * 0.33 ); } painter->fillRect( r, Qt::black ); } QList DependencyConnectorItem::predecessorItems() const { return nodeItem()->predecessorItems( m_ctype ); } QList DependencyConnectorItem::successorItems() const { return nodeItem()->successorItems( m_ctype ); } //-------------------- DependencyNodeItem::DependencyNodeItem( Node *node, DependencyNodeItem *parent ) : QGraphicsRectItem( parent ), m_node( node ), m_parent( 0 ), m_editable( false ) { setAcceptHoverEvents( true ); setZValue( 400.0 ); setParentItem( parent ); m_start = new DependencyConnectorItem( DependencyNodeItem::Start, this ); m_finish = new DependencyConnectorItem( DependencyNodeItem::Finish, this ); m_text = new QGraphicsTextItem( this ); m_textFont = m_text->font(); m_textFont.setPointSize( 10 ); m_text->setFont( m_textFont ); setText(); setFlags( QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable ); // do not attach this item to the scene as it gives continuous paint events when a node item is selected m_symbol = new DependencyNodeSymbolItem(); m_symbol->setZValue( zValue() + 10.0 ); setSymbol(); m_treeIndicator = new QGraphicsPathItem( this ); m_treeIndicator->setPen( QPen( Qt::gray ) ); } DependencyNodeItem::~DependencyNodeItem() { qDeleteAll( m_childrelations ); qDeleteAll( m_parentrelations ); //qDeleteAll( m_children ); delete m_symbol; } void DependencyNodeItem::setText() { m_text->setPlainText( m_node == 0 ? QString() : QString( "%1 %2").arg( m_node->wbsCode() ).arg(m_node->name() ) ); } DependencyScene *DependencyNodeItem::itemScene() const { return static_cast( scene() ); } void DependencyNodeItem::setSymbol() { m_symbol->setSymbol( m_node->type(), itemScene()->symbolRect() ); } QPointF DependencyNodeItem::connectorPoint( DependencyNodeItem::ConnectorType type ) const { QRectF r; if ( type == Start ) { return m_start->connectorPoint(); } return m_finish->connectorPoint(); } void DependencyNodeItem::setConnectorHoverMode( bool mode ) { m_start->setAcceptHoverEvents( mode ); m_finish->setAcceptHoverEvents( mode ); } void DependencyNodeItem::setParentItem( DependencyNodeItem *parent ) { if ( m_parent ) { m_parent->takeChild( this ); } m_parent = parent; if ( m_parent ) { m_parent->addChild( this ); } } void DependencyNodeItem::setExpanded( bool mode ) { foreach ( DependencyNodeItem *ch, m_children ) { itemScene()->setItemVisible( ch, mode ); ch->setExpanded( mode ); } } void DependencyNodeItem::setItemVisible( bool show ) { setVisible( show ); //debugPlanDepEditor<name(); foreach ( DependencyLinkItem *i, m_parentrelations ) { i->setItemVisible( show ); } foreach ( DependencyLinkItem *i, m_childrelations ) { i->setItemVisible( show ); } } DependencyNodeItem *DependencyNodeItem::takeChild( DependencyNodeItem *ch ) { int i = m_children.indexOf( ch ); if ( i == -1 ) { return 0; } return m_children.takeAt( i ); } void DependencyNodeItem::setRectangle( const QRectF &rect ) { //debugPlanDepEditor<( scene() )->connectorWidth(); m_start->setRect( rect.x() + connection, rect.y(), -connection, rect.height() ); m_finish->setRect( rect.right() - connection, rect.y(), connection, rect.height() ); m_text->setPos( m_finish->rect().right() + 2.0, itemScene()->gridY( row() ) ); m_symbol->setPos( rect.topLeft() + QPointF( connection, 0 ) + QPointF( 2.0, 2.0 ) ); } void DependencyNodeItem::moveToY( qreal y ) { QRectF r = rect(); r. moveTop( y ); setRectangle( r ); //debugPlanDepEditor<createPath(); } foreach ( DependencyLinkItem *i, m_childrelations ) { i->createPath(); } DependencyNodeItem *par = this; while ( par->parentItem() ) { par = par->parentItem(); } par->setTreeIndicator( true ); } void DependencyNodeItem::setRow( int row ) { moveToY( itemScene()->itemY( row ) ); } int DependencyNodeItem::row() const { return itemScene()->row( rect().y() ); } void DependencyNodeItem::moveToX( qreal x ) { QRectF r = rect(); r. moveLeft( x ); setRectangle( r ); //debugPlanDepEditor<toPlainText()<<" to="<createPath(); } foreach ( DependencyLinkItem *i, m_childrelations ) { i->createPath(); } DependencyNodeItem *par = this; while ( par->parentItem() ) { par = par->parentItem(); } par->setTreeIndicator( true ); } void DependencyNodeItem::setColumn() { int col = m_parent == 0 ? 0 : m_parent->column() + 1; //debugPlanDepEditor<newChildColumn() ); } if ( col != column() ) { setColumn( col ); foreach ( DependencyLinkItem *i, m_childrelations ) { i->succItem->setColumn(); } //debugPlanDepEditor<setColumn(); } } } void DependencyNodeItem::setColumn( int col ) { moveToX( itemScene()->itemX( col ) ); } int DependencyNodeItem::column() const { return itemScene()->column( rect().x() ); } DependencyLinkItem *DependencyNodeItem::takeParentRelation( DependencyLinkItem *r ) { int i = m_parentrelations.indexOf( r ); if ( i == -1 ) { return 0; } DependencyLinkItem *dep = m_parentrelations.takeAt( i ); setColumn(); return dep; } DependencyLinkItem *DependencyNodeItem::takeChildRelation( DependencyLinkItem *r ) { int i = m_childrelations.indexOf( r ); if ( i == -1 ) { return 0; } return m_childrelations.takeAt( i ); } void DependencyNodeItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { debugPlanDepEditor; QGraphicsItem::GraphicsItemFlags f = flags(); if ( itemScene()->connectionMode() ) { itemScene()->clearConnection(); setFlags( f & ~QGraphicsItem::ItemIsSelectable ); } QGraphicsRectItem::mousePressEvent( event ); if ( f != flags() ) { setFlags( f ); } } void DependencyNodeItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * ) { //debugPlanDepEditor; QLinearGradient g( 0.0, rect().top(), 0.0, rect().bottom() ); g.setColorAt( 0.0, option->palette.color( QPalette::Midlight ) ); g.setColorAt( 1.0, option->palette.color( QPalette::Dark ) ); QBrush b( g ); painter->setBrush( b ); painter->setPen( QPen( Qt::NoPen ) ); painter->drawRect( rect() ); QStyleOptionGraphicsItem opt( *option ); opt.exposedRect = rect().adjusted( -m_start->rect().width(), 0.0, -m_finish->rect().width(), 0.0 ); if ( this == itemScene()->focusItem() ) { opt.state |= QStyle::State_HasFocus; } plan_paintFocusSelectedItem( painter, &opt ); // paint the symbol m_symbol->paint( itemScene()->project(), painter, &opt ); } DependencyConnectorItem *DependencyNodeItem::connectorItem( ConnectorType ctype ) const { switch ( ctype ) { case Start: return m_start; case Finish: return m_finish; default: break; } return 0; } QList DependencyNodeItem::predecessorItems( ConnectorType ctype ) const { QList lst; foreach ( DependencyLinkItem *i, m_parentrelations ) { if ( ctype == Start && ( i->relation->type() == Relation::StartStart || i->relation->type() == Relation::FinishStart ) ) { lst << i; } if ( ctype == Finish && i->relation->type() == Relation::FinishFinish ) { lst << i; } } return lst; } QList DependencyNodeItem::successorItems( ConnectorType ctype ) const { QList lst; foreach ( DependencyLinkItem *i, m_childrelations ) { if ( ctype == Start && i->relation->type() == Relation::StartStart ) { lst << i; } if ( ctype == Finish && ( i->relation->type() == Relation::FinishFinish || i->relation->type() == Relation::FinishStart ) ) { lst << i; } } return lst; } qreal DependencyNodeItem::treeIndicatorX() const { return rect().x() + 18.0; } void DependencyNodeItem::setTreeIndicator( bool on ) { paintTreeIndicator( on ); foreach ( DependencyNodeItem *i, m_children ) { if ( i->isVisible() ) { i->setTreeIndicator( on ); } } } void DependencyNodeItem::paintTreeIndicator( bool on ) { if ( ! on ) { m_treeIndicator->hide(); return; } QPainterPath p; qreal y1 = itemScene()->gridY( row() ); qreal y2 = itemScene()->gridY( row() + 1 ); for ( DependencyNodeItem *par = m_parent; par; par = par->parentItem() ) { qreal x = par->treeIndicatorX(); p.moveTo( x, y1 ); if ( par == m_parent ) { p.lineTo( x, (y1 + y2) / 2.0 ); p.lineTo( x + 6, (y1 + y2) / 2.0 ); if ( m_node->siblingAfter() ) { p.moveTo( x, (y1 + y2) / 2.0 ); p.lineTo( x, y2 ); } } else { const QList &children = par->children(); if ( children.last()->rect().y() > rect().y() ) { p.lineTo( x, (y1 + y2) / 2.0 ); p.lineTo( x, y2 ); } } } if ( ! m_children.isEmpty() ) { qreal x = treeIndicatorX(); qreal y = rect().bottom(); p.moveTo( x, y ); p.lineTo( x, itemScene()->gridY( row() + 1 ) ); } if ( p.isEmpty() ) { m_treeIndicator->hide(); } else { m_treeIndicator->setPath( p ); m_treeIndicator->show(); } //debugPlanDepEditor<setBrush( p->config().summaryTaskDefaultColor() ); break; case Node::Type_Task: painter->setBrush( p->config().taskNormalColor() ); break; case Node::Type_Milestone: painter->setBrush( p->config().milestoneNormalColor() ); break; default: painter->setBrush( m_delegate.defaultBrush( m_itemtype ) ); break; } } else { painter->setBrush( m_delegate.defaultBrush( m_itemtype ) ); } painter->setPen( Qt::NoPen ); painter->translate( option->exposedRect.x() + 2.0, option->exposedRect.y() + 2.0 ); painter->drawPath( path() ); } //-------------------- DependencyScene::DependencyScene( QWidget *parent ) : QGraphicsScene( parent ), m_model( 0 ), m_readwrite( false ) { setSceneRect( QRectF() ); m_connectionitem = new DependencyCreatorItem(); addItem( m_connectionitem ); //debugPlanDepEditor; m_connectionitem->hide(); } DependencyScene::~DependencyScene() { //debugPlanDepEditor<<" DELETED"; clearScene(); } void DependencyScene::setFromItem( DependencyConnectorItem *item ) { DependencyConnectorItem *old = fromItem(); m_connectionitem->clear(); if ( old && old->parentItem() ) { old->parentItem()->update(); } if ( item ) { foreach ( QGraphicsItem *i, items() ) { if ( i != m_connectionitem && i->type() != DependencyConnectorItem::Type ) { i->setAcceptHoverEvents( false ); if ( i->type() == DependencyLinkItem::Type ) { static_cast( i )->resetHooverIndication(); } } } item->setCursor( ConnectCursor ); m_connectionitem->setPredConnector( item ); m_connectionitem->show(); } else { foreach ( QGraphicsItem *i, items() ) { if ( i != m_connectionitem && i->type() != DependencyConnectorItem::Type ) i->setAcceptHoverEvents( true ); } } if ( item && item->parentItem() ) { item->parentItem()->update(); } } bool DependencyScene::connectionIsValid( DependencyConnectorItem *pred, DependencyConnectorItem *succ ) { if ( pred->ctype() == DependencyNodeItem::Start && succ->ctype() == DependencyNodeItem::Finish ) { return false; } Node *par = static_cast( pred->parentItem() )->node(); Node *ch = static_cast( succ->parentItem() )->node(); return m_project->linkExists( par, ch ) || m_project->legalToLink( par, ch ); } void DependencyScene::connectorEntered( DependencyConnectorItem *item, bool entered ) { //debugPlanDepEditor<setCursor( ConnectCursor ); if ( ! entered ) { // when we leave a connector we don't have a successor m_connectionitem->setSuccConnector( 0 ); return; } if ( m_connectionitem->predConnector == item ) { // when inside the predecessor, clicking is allowed (deselects connector) item->setCursor( ConnectCursor ); return; } if ( ! m_connectionitem->isVisible() ) { // we are not in connection mode return; } if ( m_connectionitem->predConnector == 0 ) { // nothing we can do if we don't have a predecessor (shouldn't happen) return; } if ( item->parentItem() == m_connectionitem->predConnector->parentItem() ) { // not allowed to connect to the same node item->setCursor( Qt::ForbiddenCursor ); return; } if ( ! ( connectionIsValid( m_connectionitem->predConnector, item ) ) ) { // invalid connection (circular dependency, connecting to parent node, etc) item->setCursor( Qt::ForbiddenCursor ); return; } m_connectionitem->setSuccConnector( item ); m_connectionitem->createPath(); } void DependencyScene::drawBackground ( QPainter *painter, const QRectF &rect ) { QGraphicsScene::drawBackground( painter, rect ); QStyleOptionViewItem opt; QBrush br( opt.palette.brush( QPalette::AlternateBase ) ); int first = row( rect.y() ); int last = row( rect.bottom() ); for ( int r = first; r <= last; ++r ) { if ( r % 2 == 1 ) { qreal oy = gridY( r ); QRectF rct( rect.x(), oy, rect.width(), gridHeight() ); painter->fillRect( rct, br ); //debugPlanDepEditor< DependencyScene::itemList( int type ) const { QList lst; foreach ( QGraphicsItem *i, items() ) { if ( i->type() == type ) { lst << i; } } return lst; } void DependencyScene::clearScene() { m_connectionitem->clear(); QList its, deps; foreach ( QGraphicsItem *i, items() ) { if ( i->type() == DependencyNodeItem::Type && i->parentItem() == 0 ) { its << i; } else if ( i->type() == DependencyLinkItem::Type ) { deps << i; } } qDeleteAll( deps ); qDeleteAll( its ); removeItem( m_connectionitem ); qDeleteAll( items() ); setSceneRect( QRectF() ); addItem( m_connectionitem ); //debugPlanDepEditor; } QList DependencyScene::removeChildItems( DependencyNodeItem *item ) { QList lst; foreach ( DependencyNodeItem *i, item->children() ) { m_allItems.removeAt( m_allItems.indexOf( i ) ); lst << i; lst += removeChildItems( i ); } return lst; } void DependencyScene::moveItem( DependencyNodeItem *item, const QList &lst ) { //debugPlanDepEditor<text(); int idx = m_allItems.indexOf( item ); int ndx = lst.indexOf( item->node() ); Q_ASSERT( idx != -1 && ndx != -1 ); Node *oldParent = item->parentItem() == 0 ? 0 : item->parentItem()->node(); Node *newParent = item->node()->parentNode(); if ( newParent == m_project ) { newParent = 0; } else debugPlanDepEditor<name()<level(); if ( idx != ndx || oldParent != newParent ) { // If I have children, these must be moved too. QList items = removeChildItems( item ); m_allItems.removeAt( idx ); m_allItems.insert( ndx, item ); item->setParentItem( m_allItems.value( lst.indexOf( newParent ) ) ); item->setColumn(); //debugPlanDepEditor<text()<<":"<"<column()<setColumn(); //debugPlanDepEditor<text()<<": ->"<column()<flatNodeList() ); // might have been moved } m_hiddenItems.clear(); m_visibleItems.clear(); int viewrow = 0; for ( int i = 0; i < m_allItems.count(); ++i ) { DependencyNodeItem *itm = m_allItems[ i ]; if ( itm->isVisible() ) { m_visibleItems.insert( i, itm ); //debugPlanDepEditor<text()<<":"<setRow( viewrow ); ++viewrow; } else { m_hiddenItems.insert( i, itm ); } } } DependencyNodeItem *DependencyScene::findPrevItem( Node *node ) const { if ( node->numChildren() == 0 ) { return findItem( node ); } return findPrevItem( node->childNodeIterator().last() ); } DependencyNodeItem *DependencyScene::itemBefore( DependencyNodeItem *parent, Node *node ) const { Node *sib = node->siblingBefore(); DependencyNodeItem *bef = parent; if ( sib ) { bef = findPrevItem( sib ); } return bef; } DependencyNodeItem *DependencyScene::createItem( Node *node ) { DependencyNodeItem *parent = findItem( node->parentNode() ); DependencyNodeItem *after = itemBefore( parent, node ); int i = m_allItems.count()-1; if ( after ) { i = m_allItems.indexOf( after ); //debugPlanDepEditor<<"after="<node()->name()<<" pos="<scene() != this ) { addItem( item ); } item->setEditable( m_readwrite ); item->startConnector()->setEditable( m_readwrite ); item->finishConnector()->setEditable( m_readwrite ); //debugPlanDepEditor<text()<column() + 1; } item->setRectangle( QRectF( itemX( col ), itemY(), itemWidth(), itemHeight() ) ); m_allItems.insert( i+1, item ); setItemVisible( item, true ); return item; } DependencyLinkItem *DependencyScene::findItem( const Relation* rel ) const { foreach ( QGraphicsItem *i, itemList( DependencyLinkItem::Type ) ) { if ( static_cast( i )->relation == rel ) { return static_cast( i ); } } return 0; } DependencyLinkItem *DependencyScene::findItem( const DependencyConnectorItem *c1, const DependencyConnectorItem *c2, bool exact ) const { DependencyNodeItem *n1 = c1->nodeItem(); DependencyNodeItem *n2 = c2->nodeItem(); foreach ( QGraphicsItem *i, itemList( DependencyLinkItem::Type ) ) { DependencyLinkItem *link = static_cast( i ); if ( link->predItem == n1 && link->succItem == n2 ) { switch ( link->relation->type() ) { case Relation::StartStart: if ( c1->ctype() == DependencyNodeItem::Start && c2->ctype() == DependencyNodeItem::Start ) { return link; } break; case Relation::FinishStart: if ( c1->ctype() == DependencyNodeItem::Finish && c2->ctype() == DependencyNodeItem::Start ) { return link; } break; case Relation::FinishFinish: if ( c1->ctype() == DependencyNodeItem::Finish && c2->ctype() == DependencyNodeItem::Finish ) { return link; } break; default: break; } return 0; } if ( link->predItem == n2 && link->succItem == n1 ) { if ( exact ) { return 0; } switch ( link->relation->type() ) { case Relation::StartStart: if ( c2->ctype() == DependencyNodeItem::Start && c1->ctype() == DependencyNodeItem::Start ) { return link; } break; case Relation::FinishStart: if ( c2->ctype() == DependencyNodeItem::Finish && c1->ctype() == DependencyNodeItem::Start ) { return link; } break; case Relation::FinishFinish: if ( c2->ctype() == DependencyNodeItem::Finish && c1->ctype() == DependencyNodeItem::Finish ) { return link; } break; default: break; } return 0; } } return 0; } DependencyNodeItem *DependencyScene::findItem( const Node *node ) const { foreach ( QGraphicsItem *i, itemList( DependencyNodeItem::Type ) ) { if ( static_cast( i )->node() == node ) { return static_cast( i ); } } return 0; } void DependencyScene::createLinks() { foreach ( DependencyNodeItem *i, m_allItems ) { createLinks( i ); } } void DependencyScene::createLinks( DependencyNodeItem *item ) { foreach ( Relation *rel, item->node()->dependChildNodes() ) { createLink( item, rel ); } } void DependencyScene::createLink( DependencyNodeItem *parent, Relation *rel ) { DependencyNodeItem *child = findItem( rel->child() ); if ( parent == 0 || child == 0 ) { return; } DependencyLinkItem *dep = new DependencyLinkItem( parent, child, rel ); dep->setEditable( m_readwrite ); addItem( dep ); //debugPlanDepEditor; dep->createPath(); } void DependencyScene::mouseMoveEvent( QGraphicsSceneMouseEvent *mouseEvent ) { if ( m_connectionitem->isVisible() ) { int x = qMin( qMax( sceneRect().left() + 2, mouseEvent->scenePos().x() ), sceneRect().right() - 4 ); int y = qMin( qMax( sceneRect().top() + 2, mouseEvent->scenePos().y() ), sceneRect().bottom() - 4 ); m_connectionitem->createPath( QPoint( x, y ) ); } QGraphicsScene::mouseMoveEvent( mouseEvent ); //debugPlanDepEditor<scenePos()<<","<isAccepted(); } void DependencyScene::keyPressEvent( QKeyEvent *keyEvent ) { //debugPlanDepEditor<update(); } emit focusItemChanged( focusItem() ); return; } switch ( keyEvent->key() ) { case Qt::Key_Left: { if ( fitem->type() == DependencyNodeItem::Type ) { DependencyConnectorItem *item = static_cast( fitem )->startConnector(); if ( item ) { setFocusItem( item ); } } else if ( fitem->type() == DependencyConnectorItem::Type ) { DependencyConnectorItem *citem = static_cast( fitem ); if ( citem->ctype() == DependencyNodeItem::Start ) { //Goto prev nodes finishConnector DependencyNodeItem *nitem = static_cast( citem->parentItem() ); DependencyNodeItem *item = nodeItem( nitem->row() - 1 ); if ( item ) { setFocusItem( item->finishConnector() ); } } else { // Goto node item (parent) setFocusItem( citem->parentItem() ); } } break; } case Qt::Key_Right: { if ( fitem->type() == DependencyNodeItem::Type ) { DependencyConnectorItem *item = static_cast( fitem )->finishConnector(); if ( item ) { setFocusItem( item ); } } else if ( fitem->type() == DependencyConnectorItem::Type ) { DependencyConnectorItem *citem = static_cast( fitem ); if ( citem->ctype() == DependencyNodeItem::Finish ) { //Goto prev nodes startConnector DependencyNodeItem *nitem = static_cast( citem->parentItem() ); DependencyNodeItem *item = nodeItem( nitem->row() + 1 ); if ( item ) { setFocusItem( item->startConnector() ); } } else { // Goto node item (parent) setFocusItem( citem->parentItem() ); } } break; } case Qt::Key_Up: { if ( fitem->type() == DependencyNodeItem::Type ) { DependencyNodeItem *item = nodeItem( static_cast( fitem )->row() - 1 ); if ( item ) { setFocusItem( item ); } } else if ( fitem->type() == DependencyConnectorItem::Type ) { DependencyConnectorItem *citem = static_cast( fitem ); DependencyNodeItem *nitem = static_cast( citem->parentItem() ); if ( citem->ctype() == DependencyNodeItem::Finish ) { DependencyNodeItem *item = nodeItem( nitem->row() - 1 ); if ( item ) { setFocusItem( item->finishConnector() ); } } else { DependencyNodeItem *item = nodeItem( static_cast( fitem )->row() - 1 ); if ( item ) { setFocusItem( item->startConnector() ); } } } break; } case Qt::Key_Down: { if ( fitem->type() == DependencyNodeItem::Type ) { DependencyNodeItem *item = nodeItem( static_cast( fitem )->row() + 1 ); if ( item ) { setFocusItem( item ); } } else if ( fitem->type() == DependencyConnectorItem::Type ) { DependencyConnectorItem *citem = static_cast( fitem ); DependencyNodeItem *nitem = static_cast( citem->parentItem() ); if ( citem->ctype() == DependencyNodeItem::Finish ) { DependencyNodeItem *item = nodeItem( nitem->row() + 1 ); if ( item ) { setFocusItem( item->finishConnector() ); } } else { DependencyNodeItem *item = nodeItem( static_cast( fitem )->row() + 1 ); if ( item ) { setFocusItem( item->startConnector() ); } } } break; } case Qt::Key_Space: case Qt::Key_Select: { if ( fitem->type() == DependencyConnectorItem::Type ) { singleConnectorClicked( static_cast( fitem ) ); } else if ( fitem->type() == DependencyNodeItem::Type ) { singleConnectorClicked( 0 ); foreach ( QGraphicsItem *i, selectedItems() ) { i->setSelected( false ); } fitem->setSelected( true ); } return; } default: QGraphicsScene::keyPressEvent( keyEvent ); } if ( fitem ) { fitem->parentItem() ? fitem->parentItem()->update() : fitem->update(); } if ( focusItem() ) { focusItem()->parentItem() ? focusItem()->parentItem()->update() : focusItem()->update(); } if ( fitem != focusItem() ) { emit focusItemChanged( focusItem() ); } } DependencyNodeItem *DependencyScene::nodeItem( int row ) const { if ( row < 0 || m_visibleItems.isEmpty() ) { return 0; } foreach ( DependencyNodeItem *i, m_visibleItems ) { if ( i->row() == row ) { return i; } } return 0; } void DependencyScene::singleConnectorClicked( DependencyConnectorItem *item ) { //debugPlanDepEditor; clearSelection(); QList lst; if ( item == 0 || item == fromItem() ) { setFromItem( 0 ); m_clickedItems = lst; } else if ( fromItem() == 0 ) { setFromItem( item ); } else if ( connectionIsValid( fromItem(), item ) ) { emit connectItems( fromItem(), item ); setFromItem( 0 ); } else { setFromItem( 0 ); } emit connectorClicked( item ); } void DependencyScene::multiConnectorClicked( DependencyConnectorItem *item ) { //debugPlanDepEditor; singleConnectorClicked( item ); } void DependencyScene::clearConnection() { setFromItem( 0 ); m_clickedItems.clear(); } void DependencyScene::mousePressEvent( QGraphicsSceneMouseEvent *mouseEvent ) { //debugPlanDepEditor; QGraphicsScene::mousePressEvent( mouseEvent ); if ( ! mouseEvent->isAccepted() ) { clearConnection(); } } void DependencyScene::mouseDoubleClickEvent ( QGraphicsSceneMouseEvent *event ) { //debugPlanDepEditor<pos()<scenePos()<screenPos(); QGraphicsScene::mouseDoubleClickEvent( event ); emit itemDoubleClicked( itemAt( event->scenePos(), QTransform() ) ); } void DependencyScene::contextMenuEvent ( QGraphicsSceneContextMenuEvent *event ) { if ( event->reason() == QGraphicsSceneContextMenuEvent::Mouse ) { debugPlanDepEditor<<"Mouse:"<scenePos(), QTransform())<pos()<scenePos()<screenPos(); emit contextMenuRequested( itemAt( event->scenePos(), QTransform() ), event->screenPos() ); return; } if ( focusItem() ) { if ( focusItem()->type() == DependencyConnectorItem::Type ) { DependencyConnectorItem *to = static_cast( focusItem() ); DependencyConnectorItem *from = fromItem(); debugPlanDepEditor<<"DependencyConnectorItem:"<type() == DependencyConnectorItem::Type ) { static_cast( i )->setEditable( on ); } else if ( i->type() == DependencyLinkItem::Type ) { static_cast( i )->setEditable( on ); } } } //-------------------- DependencyView::DependencyView( QWidget *parent ) : QGraphicsView( parent ), m_project( 0 ), m_dirty( false ), m_active( false ) { setItemScene( new DependencyScene( this ) ); setAlignment( Qt::AlignLeft | Qt::AlignTop ); connect(scene(), &QGraphicsScene::selectionChanged, this, &DependencyView::slotSelectionChanged); connect(itemScene(), &DependencyScene::connectItems, this, &DependencyView::makeConnection); connect(itemScene(), static_cast(&DependencyScene::contextMenuRequested), this, &DependencyView::slotContextMenuRequested); connect(itemScene(), &DependencyScene::dependencyContextMenuRequested, this, &DependencyView::slotDependencyContextMenuRequested ); connect(itemScene(), SIGNAL(contextMenuRequested(QGraphicsItem*,QPoint)), this, SIGNAL(contextMenuRequested(QGraphicsItem*,QPoint))); // clazy:exclude=old-style-connect connect(itemScene(), static_cast(&DependencyScene::focusItemChanged), this, &DependencyView::slotFocusItemChanged); m_autoScrollTimer.start( 100 ); connect( &m_autoScrollTimer, &QTimer::timeout, this, &DependencyView::slotAutoScroll ); } void DependencyView::slotContextMenuRequested( QGraphicsItem *item ) { if ( item ) { debugPlanDepEditor<boundingRect()<<(item->mapToScene( item->pos() ).toPoint())<<(mapToGlobal( item->mapToParent( item->pos() ).toPoint())); emit contextMenuRequested( item, mapToGlobal( item->mapToScene( item->boundingRect().topRight() ).toPoint() ) ); } } void DependencyView::slotDependencyContextMenuRequested( DependencyLinkItem *item, DependencyConnectorItem */*connector */) { if ( item ) { debugPlanDepEditor<boundingRect()<<(item->mapToScene( item->pos() ).toPoint())<<(mapToGlobal( item->mapToParent( item->pos() ).toPoint())); emit contextMenuRequested( item, mapToGlobal( item->mapToScene( item->boundingRect().topRight() ).toPoint() ) ); } } void DependencyView::slotConnectorClicked( DependencyConnectorItem *item ) { if ( itemScene()->fromItem() == 0 ) { itemScene()->setFromItem( item ); } else { //debugPlanDepEditor<<"emit makeConnection:"<( item->parentItem() )->text(); emit makeConnection( itemScene()->fromItem(), item ); } } void DependencyView::slotSelectionChanged() { //HACK because of tt bug 160653 QTimer::singleShot(0, this, &DependencyView::slotSelectedItems); } void DependencyView::slotSelectedItems() { emit selectionChanged( itemScene()->selectedItems() ); } void DependencyView::slotFocusItemChanged( QGraphicsItem *item ) { ensureVisible( item, 10, 10 ); } void DependencyView::setItemScene( DependencyScene *scene ) { setScene( scene ); scene->setProject( m_project ); //slotResizeScene( m_treeview->viewport()->size() ); if ( m_project ) { createItems(); } } void DependencyView::setActive( bool activate ) { m_active = activate; if ( m_active && m_dirty ) { createItems(); } } void DependencyView::setProject( Project *project ) { if ( m_project ) { disconnect( m_project, &Project::relationAdded, this, &DependencyView::slotRelationAdded ); disconnect( m_project, &Project::relationRemoved, this, &DependencyView::slotRelationRemoved ); disconnect( m_project, &Project::relationModified, this, &DependencyView::slotRelationModified ); disconnect( m_project, &Project::nodeAdded, this, &DependencyView::slotNodeAdded ); disconnect( m_project, &Project::nodeRemoved, this, &DependencyView::slotNodeRemoved ); disconnect( m_project, &Project::nodeChanged, this, &DependencyView::slotNodeChanged ); disconnect( m_project, &Project::nodeMoved, this, &DependencyView::slotNodeMoved ); if ( itemScene() ) { itemScene()->clearScene(); } } m_project = project; if ( project ) { connect( m_project, &Project::relationAdded, this, &DependencyView::slotRelationAdded ); connect( m_project, &Project::relationRemoved, this, &DependencyView::slotRelationRemoved ); connect( m_project, &Project::relationModified, this, &DependencyView::slotRelationModified ); connect( m_project, &Project::nodeAdded, this, &DependencyView::slotNodeAdded ); connect( m_project, &Project::nodeRemoved, this, &DependencyView::slotNodeRemoved ); connect( m_project, &Project::nodeChanged, this, &DependencyView::slotNodeChanged ); connect( m_project, &Project::nodeMoved, this, &DependencyView::slotNodeMoved ); connect( m_project, &Project::wbsDefinitionChanged, this, &DependencyView::slotWbsCodeChanged ); if ( itemScene() ) { itemScene()->setProject( project ); if ( m_active ) { createItems(); } else { m_dirty = true; } } } } DependencyLinkItem *DependencyView::findItem( const Relation* rel ) const { return itemScene()->findItem( rel ); } DependencyNodeItem *DependencyView::findItem( const Node *node ) const { return itemScene()->findItem( node ); } void DependencyView::slotRelationAdded( Relation* rel ) { if ( m_dirty ) { return; } DependencyLinkItem *item = findItem( rel ); if ( item == 0 ) { DependencyNodeItem *p = findItem( rel->parent() ); DependencyNodeItem *c = findItem( rel->child() ); DependencyLinkItem *r = new DependencyLinkItem( p, c, rel ); scene()->addItem( r ); //debugPlanDepEditor; r->createPath(); r->setVisible( c->isVisible() && p->isVisible() ); } else debugPlanDepEditor<<"Relation already exists!"; } void DependencyView::slotRelationRemoved( Relation* rel ) { if ( m_dirty ) { return; } DependencyLinkItem *item = findItem( rel ); if ( item ) { scene()->removeItem( item ); delete item; } else debugPlanDepEditor<<"Relation does not exist!"; } void DependencyView::slotRelationModified( Relation* rel ) { //debugPlanDepEditor; if ( m_dirty ) { return; } slotRelationRemoved( rel ); slotRelationAdded( rel ); } void DependencyView::slotNodeAdded( Node *node ) { //debugPlanDepEditor; if ( m_dirty ) { return; } DependencyNodeItem *item = findItem( node ); if ( item == 0 ) { item = createItem( node ); } else { //debugPlanDepEditor<name(); itemScene()->setItemVisible( item, true ); } ensureVisible( item ); slotWbsCodeChanged(); } void DependencyView::slotNodeRemoved( Node *node ) { if ( m_dirty ) { return; } DependencyNodeItem *item = findItem( node ); if ( item ) { //debugPlanDepEditor<name(); itemScene()->setItemVisible( item, false ); } else debugPlanDepEditor<<"Node does not exist!"; slotWbsCodeChanged(); } void DependencyView::slotNodeChanged( Node *node ) { if ( m_dirty ) { return; } DependencyNodeItem *item = findItem( node ); if ( item && item->isVisible() ) { item->setText(); item->setSymbol(); } else debugPlanDepEditor<<"Node does not exist!"; } void DependencyView::slotWbsCodeChanged() { if ( m_dirty ) { return; } foreach( DependencyNodeItem *i, itemScene()->nodeItems() ) { if ( i->isVisible() ) { i->setText(); } } } void DependencyView::slotNodeMoved( Node *node ) { if ( m_dirty ) { return; } slotNodeRemoved( node ); slotNodeAdded( node ); } void DependencyView::setItemExpanded( int , bool ) { } void DependencyView::createItems() { itemScene()->clearScene(); m_dirty = false; if ( m_project == 0 ) { return; } scene()->addLine( 0.0, 0.0, 1.0, 0.0 ); createItems( m_project ); createLinks(); } DependencyNodeItem *DependencyView::createItem( Node *node ) { return itemScene()->createItem( node ); } void DependencyView::createItems( Node *node ) { if ( node != m_project ) { //debugPlanDepEditor<name()<<" ("<numChildren()<<")"; DependencyNodeItem *i = createItem( node ); if ( i == 0 ) { return; } } foreach ( Node *n, node->childNodeIterator() ) { createItems( n ); } } void DependencyView::createLinks() { //debugPlanDepEditor; itemScene()->createLinks(); } void DependencyView::keyPressEvent(QKeyEvent *event) { if ( event->modifiers() & Qt::ControlModifier ) { switch ( event->key() ) { case Qt::Key_Plus: return scale( 1.1, 1.1 ); case Qt::Key_Minus: return scale( 0.9, 0.9 ); default: break; } } QGraphicsView::keyPressEvent(event); } void DependencyView::mouseMoveEvent( QMouseEvent *mouseEvent ) { m_cursorPos = mouseEvent->pos(); if ( itemScene()->connectionMode() && itemScene()->mouseGrabberItem() ) { QPointF spos = mapToScene( m_cursorPos ); Qt::CursorShape c = Qt::ArrowCursor; foreach ( QGraphicsItem *i, itemScene()->items( spos ) ) { if ( i->type() == DependencyConnectorItem::Type ) { if ( i == itemScene()->fromItem() ) { c = ConnectCursor; } else { if ( itemScene()->connectionIsValid( itemScene()->fromItem(), static_cast( i ) ) ) { c = ConnectCursor; } else { c = Qt::ForbiddenCursor; } } } } if ( viewport()->cursor().shape() != c ) { viewport()->setCursor( c ); } } QGraphicsView::mouseMoveEvent( mouseEvent ); //debugPlanDepEditor<scenePos()<<","<isAccepted(); } void DependencyView::slotAutoScroll() { if ( itemScene()->connectionMode() ) { ensureVisible( QRectF( mapToScene( m_cursorPos ), QSizeF( 1, 1 ) ), 2, 2 ); } } //----------------------------------- DependencyeditorConfigDialog::DependencyeditorConfigDialog( ViewBase *view, QWidget *p, bool selectPrint) : KPageDialog(p), m_view( view ) { setWindowTitle( i18n("Settings") ); QTabWidget *tab = new QTabWidget(); QWidget *w = ViewBase::createPageLayoutWidget( view ); tab->addTab( w, w->windowTitle() ); m_pagelayout = w->findChild(); Q_ASSERT( m_pagelayout ); m_headerfooter = ViewBase::createHeaderFooterWidget( view ); m_headerfooter->setOptions( view->printingOptions() ); tab->addTab( m_headerfooter, m_headerfooter->windowTitle() ); KPageWidgetItem *page = addPage( tab, i18n( "Printing" ) ); page->setHeader( i18n( "Printing Options" ) ); if (selectPrint) { setCurrentPage(page); } connect( this, &QDialog::accepted, this, &DependencyeditorConfigDialog::slotOk); } void DependencyeditorConfigDialog::slotOk() { debugPlan; m_view->setPageLayout( m_pagelayout->pageLayout() ); m_view->setPrintingOptions( m_headerfooter->options() ); } //-------------------- DependencyEditor::DependencyEditor(KoPart *part, KoDocument *doc, QWidget *parent ) : ViewBase(part, doc, parent), m_currentnode( 0 ), m_manager( 0 ) { - setXMLFile("DependencyEditorUi.rc"); + if (doc && doc->isReadWrite()) { + setXMLFile("DependencyEditorUi.rc"); + } else { + setXMLFile("DependencyEditorUi_readonly.rc"); + } + setupGui(); QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new DependencyView( this ); l->addWidget( m_view ); connect(m_view, &DependencyView::makeConnection, this, &DependencyEditor::slotCreateRelation ); connect(m_view, SIGNAL(selectionChanged(QList)), this, SLOT(slotSelectionChanged(QList))); // clazy::exclude=old-style-connect connect(m_view->itemScene(), &DependencyScene::itemDoubleClicked, this, &DependencyEditor::slotItemDoubleClicked ); connect(m_view, &DependencyView::contextMenuRequested, this, &DependencyEditor::slotContextMenuRequested ); Help::add(this, xi18nc("@info:whatsthis", "Task Dependency Editor" "" "Edit dependencies between tasks." "Dependencies can be added by dragging a connection area (start or finish)" " from one task to a connection area of a different task." " You can edit or delete a dependency using the context menu." "" "This view supports printing using the context menu." "More..." "", Help::page("Manual/Task_Dependency_Editor_(Graphical)"))); } void DependencyEditor::updateReadWrite( bool on ) { m_view->itemScene()->setReadWrite( on ); ViewBase::updateReadWrite( on ); } void DependencyEditor::slotItemDoubleClicked( QGraphicsItem *item ) { //debugPlanDepEditor; if ( ! isReadWrite() ) { return; } if ( item && item->type() == DependencyLinkItem::Type ) { emit editRelation( static_cast( item )->relation ); return; } if ( item && item->type() == DependencyNodeItem::Type ) { emit editNode( static_cast( item )->node() ); return; } if ( item && item->type() == DependencyNodeSymbolItem::Type ) { emit editNode( static_cast( item->parentItem() )->node() ); return; } } void DependencyEditor::slotCreateRelation( DependencyConnectorItem *pred, DependencyConnectorItem *succ ) { //debugPlanDepEditor; if ( ! isReadWrite() ) { return; } Node *par = pred->node(); Node *ch = succ->node(); Relation::Type type = Relation::FinishStart; if ( pred->ctype() == DependencyNodeItem::Start ) { if ( succ->ctype() == DependencyNodeItem::Start ) { type = Relation::StartStart; } } else { if ( succ->ctype() == DependencyNodeItem::Start ) { type = Relation::FinishStart; } else { type = Relation::FinishFinish; } } Relation *rel = ch->findRelation( par ); if ( rel == 0 ) { //debugPlanDepEditor<<"New:"<name()<<" ->"<name()<<","<type() != type ) { //debugPlanDepEditor<<"Mod:"<name()<<" ->"<name()<<","<setProject( &project ); } void DependencyEditor::draw() { } void DependencyEditor::setGuiActive( bool activate ) { //debugPlanDepEditor<setActive( activate ); /* if ( activate && !m_view->selectionModel()->currentIndex().isValid() ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); }*/ } void DependencyEditor::slotCurrentChanged( const QModelIndex &, const QModelIndex & ) { //debugPlanDepEditor<& ) { //debugPlanDepEditor< DependencyEditor::selectedNodes() const { QList lst; foreach ( QGraphicsItem *i, m_view->itemScene()->selectedItems() ) { if ( i->type() == DependencyNodeItem::Type ) { lst << static_cast( i )->node(); } } return lst; } Node *DependencyEditor::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *DependencyEditor::currentNode() const { return m_currentnode; /* Node * n = 0; QGraphicsItem *i = m_view->itemScene()->focusItem(); if ( i && i->type() == DependencyNodeItem::Type ) { n = static_cast( i )->node(); } if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n;*/ } Relation *DependencyEditor::currentRelation() const { return m_currentrelation; } void DependencyEditor::setScheduleManager( ScheduleManager *sm ) { m_manager = sm; } void DependencyEditor::slotContextMenuRequested( QGraphicsItem *item, const QPoint& pos ) { //debugPlanDepEditor<type() == DependencyNodeSymbolItem::Type ) { item = item->parentItem(); } if ( item ) { if ( item->type() == DependencyNodeItem::Type ) { m_currentnode = static_cast( item )->node(); if ( m_currentnode == 0 ) { //debugPlanDepEditor<<"No node"; return; } bool scheduled = m_manager != 0 && m_currentnode->isScheduled( m_manager->scheduleId() ); switch ( m_currentnode->type() ) { case Node::Type_Task: name = scheduled ? "task_popup" : "task_edit_popup"; break; case Node::Type_Milestone: name = scheduled ? "taskeditor_milestone_popup" : "task_edit_popup"; break; case Node::Type_Summarytask: name = "summarytask_popup"; break; default: break; } //debugPlanDepEditor<name()<<" :"<type() == DependencyLinkItem::Type ) { m_currentrelation = static_cast( item )->relation; if ( m_currentrelation ) { name = "relation_popup"; } } else if ( item->type() == DependencyConnectorItem::Type ) { DependencyConnectorItem *c = static_cast( item ); QList items; QList actions; QMenu menu; foreach ( DependencyLinkItem *i, c->predecessorItems() ) { items << i; actions << menu.addAction(koIcon("document-properties"), i->predItem->text()); } menu.addSeparator(); foreach ( DependencyLinkItem *i, c->successorItems() ) { items << i; actions << menu.addAction(koIcon("document-properties"), i->succItem->text()); } if ( ! actions.isEmpty() ) { QAction *action = menu.exec( pos ); if ( action && actions.contains( action ) ) { emit editRelation( items[ actions.indexOf( action ) ]->relation ); return; } } } } //debugPlanDepEditor< lst = contextActionList(); if ( ! lst.isEmpty() ) { QMenu::exec( lst, pos, lst.first() ); } } m_currentnode = 0; m_currentrelation = 0; } void DependencyEditor::slotEnableActions() { updateActionsEnabled( true ); } void DependencyEditor::updateActionsEnabled( bool on ) { if ( ! on || ! isReadWrite() ) { //FIXME: read-write is not set properly menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionLinkTask->setEnabled( false ); return; } int selCount = selectedNodeCount(); if ( selCount == 0 ) { menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionLinkTask->setEnabled( false ); return; } Node *n = selectedNode(); if ( n && n->type() != Node::Type_Task && n->type() != Node::Type_Milestone && n->type() != Node::Type_Summarytask ) { n = 0; } if ( selCount == 1 && n == 0 ) { // only project selected menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( true ); actionAddSubtask->setEnabled( true ); actionAddSubMilestone->setEnabled( true ); actionDeleteTask->setEnabled( false ); actionLinkTask->setEnabled( false ); return; } bool baselined = false; Project *p = m_view->project(); if ( p && p->isBaselined() ) { foreach ( Node *n, selectedNodes() ) { if ( n->isBaselined() ) { baselined = true; break; } } } if ( selCount == 1 ) { menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionAddSubtask->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionAddSubMilestone->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionDeleteTask->setEnabled( ! baselined ); actionLinkTask->setEnabled( ! baselined ); return; } // selCount > 1 menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( ! baselined ); actionLinkTask->setEnabled( false ); } void DependencyEditor::setupGui() { KActionCollection *coll = actionCollection(); menuAddTask = new KActionMenu(koIcon("view-task-add"), i18n("Add Task"), this); coll->addAction("add_task", menuAddTask ); connect( menuAddTask, &QAction::triggered, this, &DependencyEditor::slotAddTask ); actionAddTask = new QAction( i18n("Add Task..."), this); actionAddTask->setShortcut( Qt::CTRL + Qt::Key_I ); connect( actionAddTask, &QAction::triggered, this, &DependencyEditor::slotAddTask ); menuAddTask->addAction( actionAddTask ); actionAddMilestone = new QAction( i18n("Add Milestone..."), this ); actionAddMilestone->setShortcut( Qt::CTRL + Qt::ALT + Qt::Key_I ); connect( actionAddMilestone, &QAction::triggered, this, &DependencyEditor::slotAddMilestone ); menuAddTask->addAction( actionAddMilestone ); menuAddSubTask = new KActionMenu(koIcon("view-task-child-add"), i18n("Add Sub-Task"), this); coll->addAction("add_subtask", menuAddSubTask ); connect( menuAddSubTask, &QAction::triggered, this, &DependencyEditor::slotAddSubtask ); actionAddSubtask = new QAction( i18n("Add Sub-Task..."), this ); actionAddSubtask->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_I ); connect( actionAddSubtask, &QAction::triggered, this, &DependencyEditor::slotAddSubtask ); menuAddSubTask->addAction( actionAddSubtask ); actionAddSubMilestone = new QAction( i18n("Add Sub-Milestone..."), this ); actionAddSubMilestone->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::ALT + Qt::Key_I ); connect( actionAddSubMilestone, &QAction::triggered, this, &DependencyEditor::slotAddSubMilestone ); menuAddSubTask->addAction( actionAddSubMilestone ); actionDeleteTask = new QAction(koIcon("edit-delete"), xi18nc("@action", "Delete"), this); coll->addAction("delete_task", actionDeleteTask ); coll->setDefaultShortcut(actionDeleteTask, Qt::Key_Delete); connect( actionDeleteTask, &QAction::triggered, this, &DependencyEditor::slotDeleteTask ); actionLinkTask = new QAction(koIcon("link"), xi18nc("@action", "Link"), this); actionCollection()->setDefaultShortcut( actionLinkTask, Qt::CTRL + Qt::Key_L ); actionCollection()->addAction("link_task", actionLinkTask ); connect( actionLinkTask, &QAction::triggered, this, &DependencyEditor::slotLinkTask ); createOptionActions(ViewBase::OptionPrint | ViewBase::OptionPrintPreview | ViewBase::OptionPrintPdf | ViewBase::OptionPrintConfig); } void DependencyEditor::slotOptions() { debugPlan; DependencyeditorConfigDialog *dlg = new DependencyeditorConfigDialog( this, this, sender()->objectName() == "print_options" ); connect(dlg, &QDialog::finished, this, &DependencyEditor::slotOptionsFinished); dlg->open(); } void DependencyEditor::slotAddTask() { //debugPlanDepEditor; m_currentnode = selectedNode(); emit addTask(); m_currentnode = 0; } void DependencyEditor::slotAddMilestone() { //debugPlanDepEditor; m_currentnode = selectedNode(); // sibling emit addMilestone(); m_currentnode = 0; } void DependencyEditor::slotAddSubtask() { //debugPlanDepEditor; m_currentnode = selectedNode(); if ( m_currentnode == 0 ) { return; } emit addSubtask(); m_currentnode = 0; } void DependencyEditor::slotAddSubMilestone() { debugPlanDepEditor; m_currentnode = selectedNode(); if ( m_currentnode == 0 ) { return; } emit addSubMilestone(); m_currentnode = 0; } void DependencyEditor::edit( const QModelIndex &i ) { if ( i.isValid() ) { /* QModelIndex p = m_view->itemModel()->parent( i ); m_view->treeView()->setExpanded( p, true ); m_view->treeView()->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); m_view->treeView()->edit( i );*/ } } void DependencyEditor::slotDeleteTask() { //debugPlanDepEditor; QList lst = selectedNodes(); while ( true ) { // remove children of selected tasks, as parents delete their children Node *ch = 0; foreach ( Node *n1, lst ) { foreach ( Node *n2, lst ) { if ( n2->isChildOf( n1 ) ) { ch = n2; break; } } if ( ch != 0 ) { break; } } if ( ch == 0 ) { break; } lst.removeAt( lst.indexOf( ch ) ); } foreach ( Node* n, lst ) { debugPlanDepEditor<name(); } emit deleteTaskList( lst ); } void DependencyEditor::slotLinkTask() { //debugPlan; Node *n = selectedNode(); if (n && project()) { RelationEditorDialog dlg(project(), n); if (dlg.exec()) { KUndo2Command *cmd = dlg.buildCommand(); if (cmd) { koDocument()->addCommand(cmd); } } } } KoPrintJob *DependencyEditor::createPrintJob() { DependecyViewPrintingDialog *dia = new DependecyViewPrintingDialog( this, m_view ); dia->printer().setCreator( QString( "Plan %1" ).arg( PLAN_VERSION_STRING ) ); // dia->printer().setFullPage(true); // ignore printer margins return dia; } } // namespace KPlato diff --git a/src/libs/ui/kptresourceeditor.cpp b/src/libs/ui/kptresourceeditor.cpp index d51b01b4..acee50df 100644 --- a/src/libs/ui/kptresourceeditor.cpp +++ b/src/libs/ui/kptresourceeditor.cpp @@ -1,414 +1,419 @@ /* This file is part of the KDE project - Copyright (C) 2006 - 2011, 2012 Dag Andersen - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -* Boston, MA 02110-1301, USA. -*/ + * Copyright (C) 2019 Dag Andersen + * Copyright (C) 2006 - 2011, 2012 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ // clazy:excludeall=qstring-arg #include "kptresourceeditor.h" #include "kptresourcemodel.h" #include "kptcommand.h" #include "kptitemmodelbase.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptresource.h" #include "kptdatetime.h" #include "kptitemviewsettup.h" #include "Help.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include namespace KPlato { ResourceTreeView::ResourceTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { setDragPixmap(koIcon("resource-group").pixmap(32)); // header()->setContextMenuPolicy( Qt::CustomContextMenu ); setStretchLastSection( false ); ResourceItemModel *m = new ResourceItemModel( this ); setModel( m ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); createItemDelegates( m ); connect( this, &DoubleTreeViewBase::dropAllowed, this, &ResourceTreeView::slotDropAllowed ); } void ResourceTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { event->ignore(); if ( model()->dropAllowed( index, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); } } QObject *ResourceTreeView::currentObject() const { return model()->object( selectionModel()->currentIndex() ); } QList ResourceTreeView::selectedObjects() const { QList lst; foreach (const QModelIndex &i, selectionModel()->selectedRows() ) { lst << static_cast( i.internalPointer() ); } return lst; } QList ResourceTreeView::selectedGroups() const { QList gl; foreach ( QObject *o, selectedObjects() ) { ResourceGroup* g = qobject_cast( o ); if ( g ) { gl << g; } } return gl; } QList ResourceTreeView::selectedResources() const { QList rl; foreach ( QObject *o, selectedObjects() ) { Resource* r = qobject_cast( o ); if ( r ) { rl << r; } } return rl; } //----------------------------------- ResourceEditor::ResourceEditor(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent) { - setXMLFile("ResourceEditorUi.rc"); + if (doc && doc->isReadWrite()) { + setXMLFile("ResourceEditorUi.rc"); + } else { + setXMLFile("ResourceEditorUi_readonly.rc"); + } Help::add(this, xi18nc("@info:whatsthis", "Resource Editor" "" "Resources are organized in a Resource Breakdown Structure. " "Resources can be of type Work or Material. " "When assigned to a task, a resource of type Work can affect the duration of the task, while a resource of type Material does not. " "A resource must refer to a Calendar defined in the Work and Vacation Editor." "More..." "", Help::page("Manual/Resource_Editor"))); QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new ResourceTreeView( this ); m_doubleTreeView = m_view; connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); l->addWidget( m_view ); setupGui(); m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode( QAbstractItemView::DragDrop ); m_view->setDropIndicatorShown( true ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( true ); // m_view->setAcceptDropsOnView( true ); QList lst1; lst1 << 1 << -1; QList lst2; lst2 << 0 << ResourceModel::ResourceOvertimeRate; m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << 0 ); QList show; for ( int c = 1; c < model()->columnCount(); ++c ) { if (c != ResourceModel::ResourceOvertimeRate) { show << c; } } m_view->slaveView()->setDefaultColumns( show ); connect( model(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &ResourceEditor::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &ResourceEditor::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &ResourceEditor::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); } void ResourceEditor::updateReadWrite( bool readwrite ) { m_view->setReadWrite( readwrite ); } void ResourceEditor::setProject( Project *project ) { debugPlan<setProject( project ); ViewBase::setProject( project ); } void ResourceEditor::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void ResourceEditor::slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ) { //debugPlan<model()->object( index ); ResourceGroup *g = qobject_cast( obj ); if ( g ) { //name = "resourceeditor_group_popup"; } else { Resource *r = qobject_cast( obj ); if ( r && !r->isShared() ) { name = "resourceeditor_resource_popup"; } } } m_view->setContextMenuIndex(index); if ( name.isEmpty() ) { slotHeaderContextMenuRequested( pos ); m_view->setContextMenuIndex(QModelIndex()); return; } emit requestPopupMenu( name, pos ); m_view->setContextMenuIndex(QModelIndex()); } Resource *ResourceEditor::currentResource() const { return qobject_cast( m_view->currentObject() ); } ResourceGroup *ResourceEditor::currentResourceGroup() const { return qobject_cast( m_view->currentObject() ); } void ResourceEditor::slotCurrentChanged( const QModelIndex & ) { //debugPlan<project(); QList groupList = m_view->selectedGroups(); bool nogroup = groupList.isEmpty(); bool group = groupList.count() == 1; QList resourceList = m_view->selectedResources(); bool noresource = resourceList.isEmpty(); bool resource = resourceList.count() == 1; bool any = !nogroup || !noresource; actionAddResource->setEnabled( o && ( (group && noresource) || (resource && nogroup) ) ); actionAddGroup->setEnabled( o ); if ( o && any ) { foreach ( ResourceGroup *g, groupList ) { if ( g->isBaselined() ) { o = false; break; } } } if ( o && any ) { foreach ( Resource *r, resourceList ) { if ( r->isBaselined() ) { o = false; break; } } } actionDeleteSelection->setEnabled( o && any ); } void ResourceEditor::setupGui() { actionAddGroup = new QAction(koIcon("resource-group-new"), i18n("Add Resource Group"), this); actionCollection()->addAction("add_group", actionAddGroup ); actionCollection()->setDefaultShortcut(actionAddGroup, Qt::CTRL + Qt::Key_I); connect( actionAddGroup, &QAction::triggered, this, &ResourceEditor::slotAddGroup ); actionAddResource = new QAction(koIcon("list-add-user"), i18n("Add Resource"), this); actionCollection()->addAction("add_resource", actionAddResource ); actionCollection()->setDefaultShortcut(actionAddResource, Qt::CTRL + Qt::SHIFT + Qt::Key_I); connect( actionAddResource, &QAction::triggered, this, &ResourceEditor::slotAddResource ); actionDeleteSelection = new QAction(koIcon("edit-delete"), xi18nc("@action", "Delete"), this); actionCollection()->addAction("delete_selection", actionDeleteSelection ); actionCollection()->setDefaultShortcut(actionDeleteSelection, Qt::Key_Delete); connect( actionDeleteSelection, &QAction::triggered, this, &ResourceEditor::slotDeleteSelection ); // Add the context menu actions for the view options actionCollection()->addAction(m_view->actionSplitView()->objectName(), m_view->actionSplitView()); connect(m_view->actionSplitView(), &QAction::triggered, this, &ResourceEditor::slotSplitView); addContextAction( m_view->actionSplitView() ); createOptionActions(ViewBase::OptionAll); } void ResourceEditor::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void ResourceEditor::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print_options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->open(); } void ResourceEditor::slotAddResource() { //debugPlan; QList gl = m_view->selectedGroups(); if ( gl.count() > 1 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); ResourceGroup *g = 0; if ( !gl.isEmpty() ) { g = gl.first(); } else { QList rl = m_view->selectedResources(); if ( rl.count() != 1 ) { return; } g = rl.first()->parentGroup(); } if ( g == 0 ) { return; } Resource *r = new Resource(); if ( g->type() == ResourceGroup::Type_Material ) { r->setType( Resource::Type_Material ); } QModelIndex i = m_view->model()->insertResource( g, r ); if ( i.isValid() ) { m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); m_view->edit( i ); } } void ResourceEditor::slotAddGroup() { //debugPlan; m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); ResourceGroup *g = new ResourceGroup(); QModelIndex i = m_view->model()->insertGroup( g ); if ( i.isValid() ) { m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); m_view->edit( i ); } } void ResourceEditor::slotDeleteSelection() { QObjectList lst = m_view->selectedObjects(); //debugPlan<selectionModel()->currentIndex(); if ( i.isValid() ) { m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } } bool ResourceEditor::loadContext( const KoXmlElement &context ) { debugPlan<loadContext( model()->columnMap(), context ); } void ResourceEditor::saveContext( QDomElement &context ) const { debugPlan<saveContext( model()->columnMap(), context ); } KoPrintJob *ResourceEditor::createPrintJob() { return m_view->createPrintJob( this ); } void ResourceEditor::slotEditCopy() { m_view->editCopy(); } } // namespace KPlato diff --git a/src/libs/ui/kptscheduleeditor.cpp b/src/libs/ui/kptscheduleeditor.cpp index cd095401..abee373d 100644 --- a/src/libs/ui/kptscheduleeditor.cpp +++ b/src/libs/ui/kptscheduleeditor.cpp @@ -1,836 +1,848 @@ /* This file is part of the KDE project - Copyright (C) 2006-2011, 2012 Dag Andersen - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ + * Copyright (C) 2019 Dag Andersen + * Copyright (C) 2006-2011, 2012 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ // clazy:excludeall=qstring-arg #include "kptscheduleeditor.h" #include "kptcommand.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptschedule.h" #include "kptdatetime.h" #include "kptpertresult.h" #include "kptitemviewsettup.h" #include "kptrecalculatedialog.h" #include "Help.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { ScheduleTreeView::ScheduleTreeView( QWidget *parent ) : TreeViewBase( parent ) { header()->setStretchLastSection ( false ); ScheduleItemModel *m = new ScheduleItemModel( this ); setModel( m ); //setSelectionBehavior( QAbstractItemView::SelectItems ); setSelectionMode( QAbstractItemView::SingleSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); setTreePosition(-1); // always visual index 0 createItemDelegates( m ); } void ScheduleTreeView::selectionChanged( const QItemSelection &sel, const QItemSelection &desel ) { //debugPlan<selectedIndexes() ) { Q_UNUSED(i); //debugPlan<selectedIndexes() ); } void ScheduleTreeView::currentChanged( const QModelIndex & current, const QModelIndex & previous ) { //debugPlan<select( current, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); } ScheduleManager *ScheduleTreeView::manager( const QModelIndex &idx ) const { return model()->manager( idx ); } ScheduleManager *ScheduleTreeView::currentManager() const { return model()->manager( currentIndex() ); } QModelIndexList ScheduleTreeView::selectedRows() const { QModelIndexList lst = selectionModel()->selectedRows(); debugPlan<manager( lst.first() ); } return sm; } //----------------------------------- ScheduleEditor::ScheduleEditor(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent) { - setXMLFile("ScheduleEditorUi.rc"); + if (doc && doc->isReadWrite()) { + setXMLFile("ScheduleEditorUi.rc"); + } else { + setXMLFile("ScheduleEditorUi_readonly.rc"); + } setupGui(); slotEnableActions(); QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_schedulingRange = new SchedulingRange(doc, this); l->addWidget( m_schedulingRange ); m_view = new ScheduleTreeView( this ); connect(this, &ViewBase::expandAll, m_view, &TreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &TreeViewBase::slotCollapse); l->addWidget( m_view ); m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); QList show; show << ScheduleModel::ScheduleName << ScheduleModel::ScheduleState << ScheduleModel::ScheduleDirection << ScheduleModel::ScheduleOverbooking << ScheduleModel::ScheduleDistribution << ScheduleModel::SchedulePlannedStart << ScheduleModel::SchedulePlannedFinish << ScheduleModel::ScheduleScheduler << ScheduleModel::ScheduleGranularity << ScheduleModel::ScheduleMode ; QList lst; for ( int c = 0; c < model()->columnCount(); ++c ) { if ( ! show.contains( c ) ) { lst << c; } } m_view->setColumnsHidden( lst ); m_view->setDefaultColumns( show ); connect( model(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, SIGNAL(currentChanged(QModelIndex)), this, SLOT(slotCurrentChanged(QModelIndex)) ); connect( m_view, SIGNAL(selectionChanged(QModelIndexList)), this, SLOT(slotSelectionChanged(QModelIndexList)) ); connect( model(), &QAbstractItemModel::dataChanged, this, &ScheduleEditor::updateActionsEnabled ); connect( m_view, &TreeViewBase::contextMenuRequested, this, &ScheduleEditor::slotContextMenuRequested ); connect( m_view, &TreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); Help::add(this, xi18nc("@info:whatsthis", "Schedule Editor" "" "The Schedule Editor is used to create, edit, calculate and delete schedules." "" "If Mode is set to Auto, the schedule is calculated automatically." " Only one schedule can be in Auto Mode simultaneously" " and it must be a top level schedule without sub-schedules." "" "A schedule can have sub-schedules. A sub-schedule can use the projects progress data" " in order to reschedule only tasks that are not yet finished." " Rescheduling will then use e.g. actual start and remaining effort for the tasks." "More..." "", Help::page("Manual/Schedule_Editor"))); } void ScheduleEditor::draw( Project &project ) { m_view->setProject( &project ); m_schedulingRange->setProject(&project); } void ScheduleEditor::draw() { } void ScheduleEditor::setGuiActive( bool activate ) { //debugPlan<currentIndex().isValid() ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void ScheduleEditor::slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ) { debugPlan<setContextMenuIndex(index); if ( name.isEmpty() ) { slotHeaderContextMenuRequested( pos ); m_view->setContextMenuIndex(QModelIndex()); return; } debugPlan<setContextMenuIndex(QModelIndex()); } void ScheduleEditor::slotCurrentChanged( const QModelIndex & ) { //debugPlan<selectedRows(); // gets column 0 in each row (should be 1 or 0 rows) if ( lst.count() == 1 ) { ScheduleManager *sm = m_view->model()->manager( lst.first() ); emit scheduleSelectionChanged( sm ); } else { emit scheduleSelectionChanged( 0 ); } slotEnableActions(); } void ScheduleEditor::updateActionsEnabled( const QModelIndex &index ) { debugPlan<setEnabled( false ); actionAddSubSchedule->setEnabled( false ); actionDeleteSelection->setEnabled( false ); actionCalculateSchedule->setEnabled( false ); actionBaselineSchedule->setEnabled( false ); actionMoveLeft->setEnabled( false ); return; } QModelIndexList lst = m_view->selectedRows(); if ( lst.isEmpty() ) { actionAddSchedule->setEnabled( true ); actionAddSubSchedule->setEnabled( false ); actionDeleteSelection->setEnabled( false ); actionCalculateSchedule->setEnabled( false ); actionBaselineSchedule->setEnabled( false ); actionMoveLeft->setEnabled( false ); return; } if ( lst.count() > 1 ) { actionAddSchedule->setEnabled( false ); actionAddSubSchedule->setEnabled( false ); actionDeleteSelection->setEnabled( false ); actionCalculateSchedule->setEnabled( false ); actionBaselineSchedule->setEnabled( false ); actionMoveLeft->setEnabled( false ); return; } // one and only one manager selected ScheduleManager *sm = m_view->manager( lst.first() ); Q_ASSERT( sm ); actionAddSchedule->setEnabled( true ); actionAddSubSchedule->setEnabled( sm->isScheduled() ); actionDeleteSelection->setEnabled( ! ( sm->isBaselined() || sm->isChildBaselined() ) ); actionCalculateSchedule->setEnabled( ! sm->scheduling() && sm->childCount() == 0 && ! ( sm->isBaselined() || sm->isChildBaselined() ) ); const char *const actionBaselineScheduleIconName = sm->isBaselined() ? koIconNameCStr("view-time-schedule-baselined-remove") : koIconNameCStr("view-time-schedule-baselined-add"); actionBaselineSchedule->setIcon(QIcon::fromTheme(QLatin1String(actionBaselineScheduleIconName))); // enable if scheduled and no one else is baselined bool en = sm->isScheduled() && ( sm->isBaselined() || ! m_view->project()->isBaselined() ); actionBaselineSchedule->setEnabled( en ); actionMoveLeft->setEnabled( sm->parentManager() ); } void ScheduleEditor::setupGui() { actionAddSchedule = new QAction(koIcon("view-time-schedule-insert"), i18n("Add Schedule"), this); actionCollection()->setDefaultShortcut(actionAddSchedule, Qt::CTRL + Qt::Key_I); actionCollection()->addAction("add_schedule", actionAddSchedule ); connect( actionAddSchedule, &QAction::triggered, this, &ScheduleEditor::slotAddSchedule ); actionAddSubSchedule = new QAction(koIcon("view-time-schedule-child-insert"), i18n("Add Sub-schedule"), this); actionCollection()->setDefaultShortcut(actionAddSubSchedule, Qt::CTRL + Qt::SHIFT + Qt::Key_I); actionCollection()->addAction("add_subschedule", actionAddSubSchedule ); connect( actionAddSubSchedule, &QAction::triggered, this, &ScheduleEditor::slotAddSubSchedule ); actionDeleteSelection = new QAction(koIcon("edit-delete"), xi18nc("@action", "Delete"), this ); actionCollection()->setDefaultShortcut(actionDeleteSelection, Qt::Key_Delete); actionCollection()->addAction("delete_selection", actionDeleteSelection ); connect( actionDeleteSelection, &QAction::triggered, this, &ScheduleEditor::slotDeleteSelection ); actionCalculateSchedule = new QAction(koIcon("view-time-schedule-calculus"), i18n("Calculate"), this); // actionCollection()->setDefaultShortcut(actionCalculateSchedule, Qt::CTRL + Qt::Key_C); actionCollection()->addAction("calculate_schedule", actionCalculateSchedule ); connect( actionCalculateSchedule, &QAction::triggered, this, &ScheduleEditor::slotCalculateSchedule ); actionBaselineSchedule = new QAction(koIcon("view-time-schedule-baselined-add"), i18n("Baseline"), this); // actionCollection()->setDefaultShortcut(actionBaselineSchedule, Qt::CTRL + Qt::Key_B); actionCollection()->addAction("schedule_baseline", actionBaselineSchedule ); connect( actionBaselineSchedule, &QAction::triggered, this, &ScheduleEditor::slotBaselineSchedule ); actionMoveLeft = new QAction(koIcon("go-first"), xi18nc("@action", "Detach"), this); actionCollection()->addAction("schedule_move_left", actionMoveLeft ); connect( actionMoveLeft, &QAction::triggered, this, &ScheduleEditor::slotMoveLeft ); // Add the context menu actions for the view options createOptionActions(ViewBase::OptionExpand | ViewBase::OptionCollapse | ViewBase::OptionViewConfig); } void ScheduleEditor::updateReadWrite( bool readwrite ) { debugPlan<setReadWrite( readwrite ); + m_schedulingRange->setReadWrite(readwrite); slotEnableActions(); } void ScheduleEditor::slotOptions() { debugPlan; ItemViewSettupDialog *dlg = new ItemViewSettupDialog( this, m_view, true, this ); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->open(); } void ScheduleEditor::slotCalculateSchedule() { //debugPlan; ScheduleManager *sm = m_view->selectedManager(); if ( sm == 0 ) { return; } if ( sm->parentManager() || (sm->isScheduled() && project()->isStarted()) ) { RecalculateDialog dlg; if ( dlg.exec() == QDialog::Rejected ) { return; } sm->setRecalculate( true ); sm->setRecalculateFrom( DateTime( dlg.dateTime() ) ); } emit calculateSchedule( m_view->project(), sm ); } void ScheduleEditor::slotAddSchedule() { //debugPlan; int idx = -1; ScheduleManager *sm = m_view->selectedManager(); if ( sm ) { idx = sm->parentManager() ? sm->parentManager()->indexOf( sm ) : m_view->project()->indexOf( sm ); if ( idx >= 0 ) { ++idx; } } if ( sm && sm->parentManager() ) { sm = sm->parentManager(); ScheduleManager *m = m_view->project()->createScheduleManager( sm->name() + QString(".%1").arg( sm->children().count() + 1 ) ); part()->addCommand( new AddScheduleManagerCmd( sm, m, idx, kundo2_i18n( "Create sub-schedule" ) ) ); QModelIndex idx = model()->index( m ); if ( idx.isValid() ) { m_view->setFocus(); m_view->scrollTo( idx ); m_view->selectionModel()->select( idx, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::NoUpdate ); } } else { Project *p = m_view->project(); ScheduleManager *m = p->createScheduleManager(); AddScheduleManagerCmd *cmd = new AddScheduleManagerCmd( *p, m, idx, kundo2_i18n( "Add schedule %1", m->name() ) ); part() ->addCommand( cmd ); QModelIndex idx = model()->index( m ); if ( idx.isValid() ) { m_view->setFocus(); m_view->scrollTo( idx ); m_view->selectionModel()->select( idx, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::NoUpdate ); } } } void ScheduleEditor::slotAddSubSchedule() { //debugPlan; ScheduleManager *sm = m_view->selectedManager(); if ( sm ) { int row = sm->parentManager() ? sm->parentManager()->indexOf( sm ) : m_view->project()->indexOf( sm ); if ( row >= 0 ) { ++row; } ScheduleManager *m = m_view->project()->createScheduleManager( sm->name() + QString(".%1").arg( sm->children().count() + 1 ) ); part()->addCommand( new AddScheduleManagerCmd( sm, m, row, kundo2_i18n( "Create sub-schedule" ) ) ); m_view->expand( model()->index( sm ) ); QModelIndex idx = model()->index( m ); if ( idx.isValid() ) { m_view->selectionModel()->select( idx, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::NoUpdate ); } } else { slotAddSchedule(); } } void ScheduleEditor::slotBaselineSchedule() { //debugPlan; ScheduleManager *sm = m_view->selectedManager(); if ( sm ) { emit baselineSchedule( m_view->project(), sm ); } } void ScheduleEditor::slotDeleteSelection() { //debugPlan; ScheduleManager *sm = m_view->selectedManager(); if ( sm ) { emit deleteScheduleManager( m_view->project(), sm ); } } void ScheduleEditor::slotMoveLeft() { ScheduleManager *sm = m_view->selectedManager(); if ( sm ) { int index = -1; for ( ScheduleManager *m = sm; m != 0; m = m->parentManager() ) { if ( m->parentManager() == 0 ) { index = m->project().indexOf( m ) + 1; } } debugPlan<name()<loadContext( model()->columnMap(), context ); } void ScheduleEditor::saveContext( QDomElement &context ) const { m_view->saveContext( model()->columnMap(), context ); } KoPrintJob *ScheduleEditor::createPrintJob() { return m_view->createPrintJob( this ); } //----------------------------------------- ScheduleLogTreeView::ScheduleLogTreeView( QWidget *parent ) : QTreeView( parent ) { header()->setStretchLastSection ( true ); header()->setContextMenuPolicy( Qt::CustomContextMenu ); m_model = new QSortFilterProxyModel( this ); m_model->setFilterRole( Qt::UserRole+1 ); m_model->setFilterKeyColumn ( 2 ); // severity m_model->setFilterWildcard( "[^0]" ); // Filter out Debug m_model->setSourceModel( new ScheduleLogItemModel( this ) ); setModel( m_model ); setRootIsDecorated( false ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); setAlternatingRowColors( true ); connect( header(), &QWidget::customContextMenuRequested, this, &ScheduleLogTreeView::headerContextMenuRequested ); actionShowDebug = new KToggleAction( xi18nc( "@action", "Show Debug Information" ), this ); connect( actionShowDebug, &QAction::toggled, this, &ScheduleLogTreeView::slotShowDebug); } void ScheduleLogTreeView::setFilterWildcard( const QString &filter ) { m_model->setFilterWildcard( filter ); } QRegExp ScheduleLogTreeView::filterRegExp() const { return m_model->filterRegExp(); } void ScheduleLogTreeView::slotShowDebug( bool on ) { on ? setFilterWildcard( QString() ) : setFilterWildcard("[^0]" ); } void ScheduleLogTreeView::contextMenuEvent ( QContextMenuEvent *e ) { debugPlan<pos())<<" at"<pos(); emit contextMenuRequested( indexAt( e->pos() ), e->globalPos() ); } void ScheduleLogTreeView::headerContextMenuRequested( const QPoint &pos ) { //debugPlan<logicalIndexAt(pos)<<" at"<addAction( actionShowDebug ); m->exec( mapToGlobal( pos ) ); delete m; } void ScheduleLogTreeView::selectionChanged( const QItemSelection &sel, const QItemSelection &desel ) { //debugPlan<selectedIndexes() ) { Q_UNUSED(i); //debugPlan<selectedIndexes() ); } void ScheduleLogTreeView::currentChanged( const QModelIndex & current, const QModelIndex & previous ) { //debugPlan<select( current, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); } void ScheduleLogTreeView::slotEditCopy() { QStringList lst; // int row = 0; QString s; QHeaderView *h = header(); foreach( const QModelIndex &i, selectionModel()->selectedRows() ) { QString s; for ( int section = 0; section < h->count(); ++section ) { QModelIndex idx = model()->index( i.row(), h->logicalIndex( section ) ); if ( ! idx.isValid() || isColumnHidden( idx.column() ) ) { continue; } if ( ! s.isEmpty() ) { s += ' '; } s = QString( "%1%2" ).arg( s ).arg( idx.data().toString(), -10 ); } if ( ! s.isEmpty() ) { lst << s; } } if ( ! lst.isEmpty() ) { QApplication::clipboard()->setText( lst.join( "\n" ) ); } } //----------------------------------- ScheduleLogView::ScheduleLogView(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent ) { setupGui(); slotEnableActions( 0 ); QVBoxLayout * l = new QVBoxLayout( this ); m_view = new ScheduleLogTreeView( this ); l->addWidget( m_view ); // m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); connect( m_view, SIGNAL(currentChanged(QModelIndex)), this, SLOT(slotCurrentChanged(QModelIndex)) ); connect( m_view, SIGNAL(selectionChanged(QModelIndexList)), this, SLOT(slotSelectionChanged(QModelIndexList)) ); connect( baseModel(), &QAbstractItemModel::dataChanged, this, &ScheduleLogView::updateActionsEnabled ); connect( m_view, &ScheduleLogTreeView::contextMenuRequested, this, &ScheduleLogView::slotContextMenuRequested ); } void ScheduleLogView::setProject( Project *project ) { m_view->setProject( project ); } void ScheduleLogView::draw( Project &project ) { setProject( &project ); } void ScheduleLogView::setGuiActive( bool activate ) { //debugPlan<currentIndex().isValid() ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); }*/ } void ScheduleLogView::slotEdit() { QString id = sender()->property( "p_identity" ).toString(); if ( id.isEmpty() ) { emit editNode( project() ); return; } Node *n = project()->findNode( id ); if ( n ) { emit editNode( n ); return; } Resource *r = project()->findResource( id ); if ( r ) { emit editResource( r ); return; } warnPlan<<"No object"; } void ScheduleLogView::slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ) { if ( ! isReadWrite() || ! index.isValid() ) { return; } QMenu *m = new QMenu( this ); QString id = index.data( ScheduleLogItemModel::IdentityRole ).toString(); if ( id.isEmpty() ) { return; } QAction *a = new QAction(koIcon("document-edit"), i18n( "Edit..." ), m); a->setProperty( "p_identity", id ); m->addAction( a ); connect(a, &QAction::triggered, this, &ScheduleLogView::slotEdit); m->addSeparator(); m->exec( pos ); delete m; } void ScheduleLogView::slotScheduleSelectionChanged( ScheduleManager *sm ) { baseModel()->setManager( sm ); } void ScheduleLogView::slotCurrentChanged( const QModelIndex & ) { //debugPlan<setReadWrite( readwrite ); //slotEnableActions( m_view->currentManager() ); } void ScheduleLogView::slotOptions() { debugPlan; } void ScheduleLogView::slotEditCopy() { m_view->slotEditCopy(); } bool ScheduleLogView::loadContext( const KoXmlElement &/*context */) { debugPlan; return true;//m_view->loadContext( model()->columnMap(), context ); } void ScheduleLogView::saveContext( QDomElement &/*context */) const { //m_view->saveContext( model()->columnMap(), context ); } //--------------------------- ScheduleHandlerView::ScheduleHandlerView(KoPart *part, KoDocument *doc, QWidget *parent ) : SplitterView(part, doc, parent) { debugPlan<<"---------------- Create ScheduleHandlerView ------------------"; m_scheduleEditor = new ScheduleEditor(part, doc, this ); m_scheduleEditor->setObjectName( "ScheduleEditor" ); addView( m_scheduleEditor ); insertChildClient(m_scheduleEditor); QTabWidget *tab = addTabWidget(); PertResult *p = new PertResult(part, doc, tab); p->setObjectName( "PertResult" ); addView( p, tab, i18n( "Result" ) ); connect( m_scheduleEditor, &ScheduleEditor::scheduleSelectionChanged, p, &PertResult::slotScheduleSelectionChanged ); PertCpmView *c = new PertCpmView(part, doc, tab); c->setObjectName( "PertCpmView" ); addView( c, tab, i18n( "Critical Path" ) ); connect( m_scheduleEditor, &ScheduleEditor::scheduleSelectionChanged, c, &PertCpmView::slotScheduleSelectionChanged ); ScheduleLogView *v = new ScheduleLogView(part, doc, tab); v->setObjectName( "ScheduleLogView" ); addView( v, tab, i18n( "Scheduling Log" ) ); connect( m_scheduleEditor, SIGNAL(scheduleSelectionChanged(KPlato::ScheduleManager*)), v, SLOT(slotScheduleSelectionChanged(KPlato::ScheduleManager*)) ); connect(v, &ScheduleLogView::editNode, this, &ScheduleHandlerView::editNode); connect(v, &ScheduleLogView::editResource, this, &ScheduleHandlerView::editResource); } void ScheduleHandlerView::currentTabChanged( int ) { } ViewBase *ScheduleHandlerView::hitView( const QPoint &/*glpos */) { //debugPlan<"<() ) { v->setGuiActive( active ); } m_activeview = active ? this : 0; emit guiActivated( this, active ); } void ScheduleHandlerView::slotGuiActivated( ViewBase *, bool ) { } SchedulingRange::SchedulingRange(KoDocument *doc, QWidget *parent) : QWidget(parent) , m_doc(doc) , m_project(0) { setupUi(this); connect(targetStartTime, &QAbstractSpinBox::editingFinished, this, &SchedulingRange::slotStartChanged); connect(targetEndTime, &QAbstractSpinBox::editingFinished, this, &SchedulingRange::slotEndChanged); } +void SchedulingRange::setReadWrite(bool rw) +{ + targetStartTime->setEnabled(rw); + targetEndTime->setEnabled(rw); +} + void SchedulingRange::setProject(Project *project) { if (m_project) { disconnect(m_project, &Project::nodeChanged, this, &SchedulingRange::slotProjectChanged); } m_project = project; if (m_project) { connect(m_project, &Project::nodeChanged, this, &SchedulingRange::slotProjectChanged); slotProjectChanged(m_project); } } void SchedulingRange::slotProjectChanged(Node *node) { if (node != m_project) { return; } if (targetStartTime->dateTime() != m_project->constraintStartTime()) { targetStartTime->setDateTime(m_project->constraintStartTime()); } if (targetEndTime->dateTime() != m_project->constraintEndTime()) { targetEndTime->setDateTime(m_project->constraintEndTime()); } } void SchedulingRange::slotStartChanged() { if (!m_project || !m_doc) { return; } if (targetStartTime->dateTime() == m_project->constraintStartTime()) { return; } ProjectModifyStartTimeCmd *cmd = new ProjectModifyStartTimeCmd(*m_project, targetStartTime->dateTime(), kundo2_i18n("Modify project target start time")); m_doc->addCommand(cmd); } void SchedulingRange::slotEndChanged() { if (!m_project || !m_doc) { return; } if (targetEndTime->dateTime() == m_project->constraintEndTime()) { return; } ProjectModifyEndTimeCmd *cmd = new ProjectModifyEndTimeCmd(*m_project, targetEndTime->dateTime(), kundo2_i18n("Modify project target end time")); m_doc->addCommand(cmd); } } // namespace KPlato diff --git a/src/libs/ui/kptscheduleeditor.h b/src/libs/ui/kptscheduleeditor.h index 5b95ed3a..8bbbbae9 100644 --- a/src/libs/ui/kptscheduleeditor.h +++ b/src/libs/ui/kptscheduleeditor.h @@ -1,286 +1,288 @@ /* This file is part of the KDE project Copyright (C) 2006-2008 Dag Andersen Copyright (C) 2006-2007 Menard Alexis This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTSCHEDULEEDITOR_H #define KPTSCHEDULEEDITOR_H #include "planui_export.h" #include #include "kptsplitterview.h" #include "kptschedulemodel.h" #include "ui_kptscheduleeditor.h" #include class KoDocument; class KToggleAction; class QPoint; class QSortFilterProxyModel; namespace KPlato { class Project; class ScheduleManager; class SchedulingRange; class PLANUI_EXPORT ScheduleTreeView : public TreeViewBase { Q_OBJECT public: explicit ScheduleTreeView(QWidget *parent); ScheduleItemModel *model() const { return static_cast( TreeViewBase::model() ); } Project *project() const { return model()->project(); } void setProject( Project *project ) { model()->setProject( project ); } ScheduleManager *manager( const QModelIndex &idx ) const; ScheduleManager *currentManager() const; ScheduleManager *selectedManager() const; QModelIndexList selectedRows() const; Q_SIGNALS: void currentChanged( const QModelIndex& ); void currentColumnChanged( const QModelIndex&, const QModelIndex& ); void selectionChanged( const QModelIndexList& ); protected Q_SLOTS: void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override; void currentChanged ( const QModelIndex & current, const QModelIndex & previous ) override; }; class PLANUI_EXPORT ScheduleEditor : public ViewBase { Q_OBJECT public: ScheduleEditor(KoPart *part, KoDocument *doc, QWidget *parent); void setupGui(); Project *project() const override { return m_view->project(); } void draw( Project &project ) override; void draw() override; ScheduleItemModel *model() const { return m_view->model(); } void updateReadWrite( bool readwrite ) override; /// Loads context info into this view. Reimplement. bool loadContext( const KoXmlElement &/*context*/ ) override; /// Save context info from this view. Reimplement. void saveContext( QDomElement &/*context*/ ) const override; KoPrintJob *createPrintJob() override; Q_SIGNALS: void calculateSchedule(KPlato::Project*, KPlato::ScheduleManager*); void baselineSchedule(KPlato::Project*, KPlato::ScheduleManager*); void addScheduleManager(KPlato::Project*); void deleteScheduleManager(KPlato::Project*, KPlato::ScheduleManager*); void SelectionScheduleChanged(); /** * Emitted when schedule selection changes. * @param sm is the new schedule manager. If @p is 0, no schedule is selected. */ void scheduleSelectionChanged(KPlato::ScheduleManager *sm); void moveScheduleManager(KPlato::ScheduleManager *sm, KPlato::ScheduleManager *newparent, int index); public Q_SLOTS: /// Activate/deactivate the gui void setGuiActive( bool activate ) override; protected Q_SLOTS: void slotOptions() override; private Q_SLOTS: void slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ); void slotSelectionChanged( const QModelIndexList& ); void slotCurrentChanged( const QModelIndex& ); void updateActionsEnabled( const QModelIndex &index ); void slotEnableActions(); void slotCalculateSchedule(); void slotBaselineSchedule(); void slotAddSchedule(); void slotAddSubSchedule(); void slotDeleteSelection(); void slotMoveLeft(); private: ScheduleTreeView *m_view; SchedulingRange *m_schedulingRange; QAction *actionCalculateSchedule; QAction *actionBaselineSchedule; QAction *actionAddSchedule; QAction *actionAddSubSchedule; QAction *actionDeleteSelection; QAction *actionMoveLeft; }; //----------------------------- class PLANUI_EXPORT ScheduleLogTreeView : public QTreeView { Q_OBJECT public: explicit ScheduleLogTreeView(QWidget *parent); Project *project() const { return logModel()->project(); } void setProject( Project *project ) { logModel()->setProject( project ); } ScheduleLogItemModel *logModel() const { return static_cast( m_model->sourceModel() ); } ScheduleManager *scheduleManager() const { return logModel()->manager(); } void setScheduleManager( ScheduleManager *manager ) { logModel()->setManager( manager ); } void setFilterWildcard( const QString &filter ); QRegExp filterRegExp() const; Q_SIGNALS: void currentChanged( const QModelIndex& ); void currentColumnChanged( const QModelIndex&, const QModelIndex& ); void selectionChanged( const QModelIndexList& ); void contextMenuRequested( const QModelIndex&, const QPoint& ); public Q_SLOTS: void slotEditCopy(); protected Q_SLOTS: void contextMenuEvent ( QContextMenuEvent *e ) override; void headerContextMenuRequested( const QPoint &pos ); void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override; void currentChanged ( const QModelIndex & current, const QModelIndex & previous ) override; void slotShowDebug( bool ); private: QSortFilterProxyModel *m_model; KToggleAction *actionShowDebug; }; //---------------------------------------------- class PLANUI_EXPORT ScheduleLogView : public ViewBase { Q_OBJECT public: ScheduleLogView(KoPart *part, KoDocument *doc, QWidget *parent); void setupGui(); void setProject( Project *project ) override; Project *project() const override { return m_view->project(); } using ViewBase::draw; void draw( Project &project ) override; ScheduleLogItemModel *baseModel() const { return m_view->logModel(); } void updateReadWrite( bool readwrite ) override; /// Loads context info into this view. bool loadContext( const KoXmlElement &/*context*/ ) override; /// Save context info from this view. void saveContext( QDomElement &/*context*/ ) const override; Q_SIGNALS: void editNode(KPlato::Node *node); void editResource(KPlato::Resource *resource); public Q_SLOTS: /// Activate/deactivate the gui void setGuiActive( bool activate ) override; void slotEditCopy() override; void slotEdit(); protected Q_SLOTS: void slotOptions() override; private Q_SLOTS: void slotContextMenuRequested( const QModelIndex &index, const QPoint& pos ); void slotScheduleSelectionChanged(KPlato::ScheduleManager *sm); void slotEnableActions(const KPlato::ScheduleManager *sm); void slotSelectionChanged( const QModelIndexList& ); void slotCurrentChanged( const QModelIndex& ); void updateActionsEnabled( const QModelIndex &index ); private: ScheduleLogTreeView *m_view; }; //----------------------------- class PLANUI_EXPORT ScheduleHandlerView : public SplitterView { Q_OBJECT public: ScheduleHandlerView(KoPart *part, KoDocument *doc, QWidget *parent); Project *project() const override { return 0; } ScheduleEditor *scheduleEditor() const { return m_scheduleEditor; } /// Always returns this (if we are called, we are hit) virtual ViewBase *hitView( const QPoint &glpos ); Q_SIGNALS: void currentScheduleManagerChanged(KPlato::ScheduleManager*); void editNode(KPlato::Node *node); void editResource(KPlato::Resource *resource); public Q_SLOTS: /// Activate/deactivate the gui (also of subviews) void setGuiActive( bool activate ) override; protected Q_SLOTS: /// Noop, we handle subviews ourselves void slotGuiActivated(KPlato::ViewBase *v, bool active ) override; void currentTabChanged( int i ) override; private: ScheduleEditor *m_scheduleEditor; }; class SchedulingRange : public QWidget, public Ui::SchedulingRange { Q_OBJECT public: SchedulingRange(KoDocument *doc, QWidget *parent = 0); + void setReadWrite(bool rw); + public Q_SLOTS: void setProject(KPlato::Project *project); void slotProjectChanged(KPlato::Node*); void slotStartChanged(); void slotEndChanged(); protected: KoDocument *m_doc; Project *m_project; }; } //KPlato namespace #endif diff --git a/src/libs/ui/kpttaskeditor.cpp b/src/libs/ui/kpttaskeditor.cpp index e76f7555..dccb839f 100644 --- a/src/libs/ui/kpttaskeditor.cpp +++ b/src/libs/ui/kpttaskeditor.cpp @@ -1,1810 +1,1818 @@ /* This file is part of the KDE project Copyright (C) 2006 - 2010, 2012 Dag Andersen Copyright (C) 2019 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kpttaskeditor.h" #include "kptglobal.h" #include "kptcommonstrings.h" #include "kptnodeitemmodel.h" #include "kptcommand.h" #include "kptproject.h" #include "kptitemviewsettup.h" #include "kptworkpackagesenddialog.h" #include "kptworkpackagesendpanel.h" #include "kptdatetime.h" #include "kptdebug.h" #include "kptresourcemodel.h" #include "kptresourceallocationmodel.h" #include "ResourceAllocationView.h" #include "kpttaskdialog.h" #include "TasksEditController.h" #include "Help.h" #include "kpttaskdescriptiondialog.h" #include "RelationEditorDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { //-------------------- TaskEditorItemModel::TaskEditorItemModel( QObject *parent ) : NodeItemModel( parent ) { } void TaskEditorItemModel::setScheduleManager(ScheduleManager *sm) { if (sm != manager()) { NodeItemModel::setScheduleManager(sm); } } Qt::ItemFlags TaskEditorItemModel::flags( const QModelIndex &index ) const { if ( index.column() == NodeModel::NodeType ) { if ( ! m_readWrite || isColumnReadOnly( index.column() ) ) { return QAbstractItemModel::flags( index ); } Node *n = node( index ); bool baselined = n ? n->isBaselined() : false; if ( n && ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { return QAbstractItemModel::flags( index ) | Qt::ItemIsEditable | Qt::ItemIsDropEnabled; } return QAbstractItemModel::flags( index ) | Qt::ItemIsDropEnabled; } return NodeItemModel::flags( index ); } QVariant TaskEditorItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal && section == NodeModel::NodeType ) { if ( role == Qt::ToolTipRole ) { return xi18nc( "@info:tooltip", "The type of task or the estimate type of the task" ); } else if ( role == Qt::WhatsThisRole ) { return xi18nc( "@info:whatsthis", "

Indicates the type of task or the estimate type of the task.

" "The type can be set to Milestone, Effort or Duration." "If the type is Summary or Project the type is not editable."); } } return NodeItemModel::headerData(section, orientation, role); } QVariant TaskEditorItemModel::data( const QModelIndex &index, int role ) const { if ( role == Qt::TextAlignmentRole ) { return NodeItemModel::data( index, role ); } Node *n = node( index ); if ( n != 0 && index.column() == NodeModel::NodeType ) { return type( n, role ); } return NodeItemModel::data( index, role ); } bool TaskEditorItemModel::setData( const QModelIndex &index, const QVariant &value, int role ) { Node *n = node( index ); if ( n != 0 && role == Qt::EditRole && index.column() == NodeModel::NodeType ) { return setType( n, value, role ); } return NodeItemModel::setData( index, value, role ); } QVariant TaskEditorItemModel::type( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( node->type() == Node::Type_Task ) { return node->estimate()->typeToString( true ); } return node->typeToString( true ); } case Qt::EditRole: return node->type(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::ToolTipRole: { if ( node->type() == Node::Type_Task ) { return xi18nc( "@info:tooltip", "Task with estimate type: %1", node->estimate()->typeToString( true ) ); } return xi18nc( "@info:tooltip", "Task type: %1", node->typeToString( true ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::EnumListValue: { if ( node->type() == Node::Type_Milestone ) { return 0; } if ( node->type() == Node::Type_Task ) { return node->estimate()->type() + 1; } return -1; } case Role::EnumList: { QStringList lst; lst << Node::typeToString( Node::Type_Milestone, true ); lst += Estimate::typeToStringList( true ); return lst; } } return QVariant(); } bool TaskEditorItemModel::setType( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { if ( node->type() == Node::Type_Summarytask ) { return false; } int v = value.toInt(); switch ( v ) { case 0: { // Milestone NamedCommand *cmd = 0; if ( node->constraint() == Node::FixedInterval ) { cmd = new NodeModifyConstraintEndTimeCmd( *node, node->constraintStartTime(), kundo2_i18n( "Set type to Milestone" ) ); } else { cmd = new ModifyEstimateCmd( *node, node->estimate()->expectedEstimate(), 0.0, kundo2_i18n( "Set type to Milestone" ) ); } emit executeCommand( cmd ); return true; } default: { // Estimate --v; MacroCommand *m = new MacroCommand( kundo2_i18n( "Set type to %1", Estimate::typeToString( (Estimate::Type)v, true ) ) ); m->addCommand( new ModifyEstimateTypeCmd( *node, node->estimate()->type(), v ) ); if ( node->type() == Node::Type_Milestone ) { if ( node->constraint() == Node::FixedInterval ) { m->addCommand( new NodeModifyConstraintEndTimeCmd( *node, node->constraintStartTime().addDays( 1 ) ) ); } else { m->addCommand( new ModifyEstimateUnitCmd( *node, node->estimate()->unit(), Duration::Unit_d ) ); m->addCommand( new ModifyEstimateCmd( *node, node->estimate()->expectedEstimate(), 1.0 ) ); } } emit executeCommand( m ); return true; } } break; } default: break; } return false; } //-------------------- TaskEditorTreeView::TaskEditorTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { setDragPixmap(koIcon("view-task").pixmap(32)); TaskEditorItemModel *m = new TaskEditorItemModel( this ); setModel( m ); //setSelectionBehavior( QAbstractItemView::SelectItems ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); setDefaultDropAction(Qt::CopyAction); createItemDelegates( m ); setItemDelegateForColumn( NodeModel::NodeType, new EnumDelegate( this ) ); connect( this, &DoubleTreeViewBase::dropAllowed, this, &TaskEditorTreeView::slotDropAllowed ); } NodeItemModel *TaskEditorTreeView::baseModel() const { NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { return static_cast( pr->sourceModel() ); } return static_cast( model() ); } void TaskEditorTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { QModelIndex idx = index; NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { idx = pr->mapToSource( index ); } event->ignore(); if ( baseModel()->dropAllowed( idx, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); } } void TaskEditorTreeView::editPaste() { QModelIndex idx = m_leftview->currentIndex(); const QMimeData *data = QGuiApplication::clipboard()->mimeData(); model()->dropMimeData(data, Qt::CopyAction, idx.row()+1, 0, idx.parent()); } //-------------------- NodeTreeView::NodeTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { setDragPixmap(koIcon("view-task").pixmap(32)); NodeItemModel *m = new NodeItemModel( this ); setModel( m ); //setSelectionBehavior( QAbstractItemView::SelectItems ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); createItemDelegates( m ); connect( this, &DoubleTreeViewBase::dropAllowed, this, &NodeTreeView::slotDropAllowed ); } NodeItemModel *NodeTreeView::baseModel() const { NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { return static_cast( pr->sourceModel() ); } return static_cast( model() ); } void NodeTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { QModelIndex idx = index; NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { idx = pr->mapToSource( index ); } event->ignore(); if ( baseModel()->dropAllowed( idx, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); } } //----------------------------------- TaskEditor::TaskEditor(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent ) { debugPlan<<"----------------- Create TaskEditor ----------------------"; - setXMLFile("TaskEditorUi.rc"); + if (doc && doc->isReadWrite()) { + setXMLFile("TaskEditorUi.rc"); + } else { + setXMLFile("TaskEditorUi_readonly.rc"); + } QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new TaskEditorTreeView( this ); m_doubleTreeView = m_view; connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); l->addWidget( m_view ); debugPlan<actionSplitView(); setupGui(); m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode( QAbstractItemView::DragDrop ); m_view->setDropIndicatorShown( true ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( true ); m_view->setAcceptDropsOnView( true ); QList lst1; lst1 << 1 << -1; // only display column 0 (NodeName) in left view QList show; show << NodeModel::NodeResponsible << NodeModel::NodeAllocation << NodeModel::NodeType << NodeModel::NodePriority << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription; QList lst2; for ( int i = 0; i < model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } for ( int i = 0; i < show.count(); ++i ) { int sec = m_view->slaveView()->header()->visualIndex( show[ i ] ); //debugPlan<<"move section:"<slaveView()->header()->moveSection( sec, i ); } } m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << NodeModel::NodeName ); m_view->slaveView()->setDefaultColumns( show ); connect( model(), SIGNAL(executeCommand(KUndo2Command*)), doc, SLOT(addCommand(KUndo2Command*)) ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &TaskEditor::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &TaskEditor::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &TaskEditor::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); connect(m_view->masterView(), &TreeViewBase::doubleClicked, this, &TaskEditor::itemDoubleClicked); connect(m_view->slaveView(), &TreeViewBase::doubleClicked, this, &TaskEditor::itemDoubleClicked); connect(baseModel(), &NodeItemModel::projectShownChanged, this, &TaskEditor::slotProjectShown); connect(model(), &QAbstractItemModel::rowsMoved, this, &TaskEditor::slotEnableActions); Help::add(this, xi18nc("@info:whatsthis", "Task Editor" "" "The Task Editor is used to create, edit, and delete tasks. " "Tasks are organized into a Work Breakdown Structure (WBS) to any depth." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Task_Editor"))); } void TaskEditor::itemDoubleClicked(const QPersistentModelIndex &idx) { if (idx.column() == NodeModel::NodeDescription) { emit openTaskDescription(isReadWrite() && (idx.flags() & Qt::ItemIsEditable)); } } void TaskEditor::slotProjectShown( bool on ) { debugPlan<rowCount() > 0 ) { idx = proxyModel()->index( 0, 0 ); m_view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows ); } } else if ( baseModel() && baseModel()->rowCount() > 0 ) { idx = baseModel()->index( 0, 0 ); m_view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows ); } if ( on && idx.isValid() ) { m_view->masterView()->expand( idx ); } slotEnableActions(); } void TaskEditor::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); ViewBase::updateReadWrite( rw ); } void TaskEditor::setProject( Project *project ) { debugPlan<setProject( project ); ViewBase::setProject( project ); } void TaskEditor::createDockers() { // Add dockers DockWidget *ds = 0; { ds = new DockWidget( this, "Allocations", xi18nc( "@title resource allocations", "Allocations" ) ); QTreeView *x = new QTreeView( ds ); AllocatedResourceItemModel *m1 = new AllocatedResourceItemModel( x ); x->setModel( m1 ); m1->setProject( project() ); // x->setHeaderHidden( true ); x->setSelectionBehavior( QAbstractItemView::SelectRows ); x->setSelectionMode( QAbstractItemView::ExtendedSelection ); x->expandAll(); x->resizeColumnToContents( 0 ); x->setDragDropMode( QAbstractItemView::DragOnly ); x->setDragEnabled ( true ); ds->setWidget( x ); connect(this, &ViewBase::projectChanged, m1, &AllocatedResourceItemModel::setProject); connect(this, &TaskEditor::taskSelected, m1, &AllocatedResourceItemModel::setTask); connect(m1, &AllocatedResourceItemModel::expandAll, x, &QTreeView::expandAll); connect(m1, &AllocatedResourceItemModel::resizeColumnToContents, x, &QTreeView::resizeColumnToContents); addDocker( ds ); } { ds = new DockWidget( this, "Resources", xi18nc( "@title", "Resources" ) ); ds->setToolTip( xi18nc( "@info:tooltip", "Drag resources into the Task Editor" " and drop into the allocations- or responsible column" ) ); ResourceAllocationView *e = new ResourceAllocationView(part(), ds ); ResourceItemModel *m = new ResourceItemModel( e ); e->setModel( m ); m->setProject( project() ); m->setReadWrite( isReadWrite() ); QList show; show << ResourceModel::ResourceName; for ( int i = m->columnCount() - 1; i >= 0; --i ) { e->setColumnHidden( i, ! show.contains( i ) ); } e->setHeaderHidden( true ); e->setSelectionBehavior( QAbstractItemView::SelectRows ); e->setSelectionMode( QAbstractItemView::ExtendedSelection ); e->expandAll(); e->resizeColumnToContents( ResourceModel::ResourceName ); e->setDragDropMode( QAbstractItemView::DragOnly ); e->setDragEnabled ( true ); ds->setWidget( e ); connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, e, &ResourceAllocationView::setSelectedTasks); connect(this, SIGNAL(projectChanged(KPlato::Project*)), m, SLOT(setProject(KPlato::Project*))); connect(this, &ViewBase::readWriteChanged, m, &ItemModelBase::setReadWrite); connect(m, &ItemModelBase::executeCommand, part(), &KoDocument::addCommand); addDocker( ds ); } { ds = new DockWidget( this, "Taskmodules", xi18nc( "@title", "Task Modules" ) ); ds->setToolTip( xi18nc( "@info:tooltip", "Drag a task module into the Task Editor to add it to the project" ) ); ds->setLocation( Qt::LeftDockWidgetArea ); ds->setShown(false); // hide by default QTreeView *e = new QTreeView( ds ); QSortFilterProxyModel *sf = new QSortFilterProxyModel(e); TaskModuleModel *m = new TaskModuleModel(sf); sf->setSourceModel(m); e->setModel(sf); e->sortByColumn(0, Qt::AscendingOrder); e->setSortingEnabled(true); e->setHeaderHidden( true ); e->setRootIsDecorated( false ); e->setSelectionBehavior( QAbstractItemView::SelectRows ); e->setSelectionMode( QAbstractItemView::SingleSelection ); // e->resizeColumnToContents( 0 ); e->setDragDropMode( QAbstractItemView::DragDrop ); e->setAcceptDrops( true ); e->setDragEnabled ( true ); ds->setWidget( e ); connect(e, &QAbstractItemView::doubleClicked, this, &TaskEditor::taskModuleDoubleClicked); connect(this, &TaskEditor::loadTaskModules, m, &TaskModuleModel::loadTaskModules); connect(m, &TaskModuleModel::saveTaskModule, this, &TaskEditor::saveTaskModule); connect(m, &TaskModuleModel::removeTaskModule, this, &TaskEditor::removeTaskModule); addDocker( ds ); } } void TaskEditor::taskModuleDoubleClicked(QModelIndex idx) { QUrl url = idx.data(Qt::UserRole).toUrl(); if (url.isValid()) { emit openDocument(url); } } void TaskEditor::setTaskModules(const QStringList& files) { emit loadTaskModules( files ); } void TaskEditor::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() && m_view->model()->rowCount() > 0 ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void TaskEditor::slotCurrentChanged( const QModelIndex &curr, const QModelIndex & ) { debugPlan<( selectedNode() ) ); } QModelIndexList TaskEditor::selectedRows() const { #if 0 // Qt bug? return m_view->selectionModel()->selectedRows(); #else QModelIndexList lst; foreach ( QModelIndex i, m_view->selectionModel()->selectedIndexes() ) { if ( i.column() == 0 ) { lst << i; } } return lst; #endif } int TaskEditor::selectedRowCount() const { return selectedRows().count(); } QList TaskEditor::selectedNodes() const { QList lst; foreach ( const QModelIndex &i, selectedRows() ) { Node * n = m_view->baseModel()->node( i ); if ( n != 0 && n->type() != Node::Type_Project ) { lst.append( n ); } } return lst; } Node *TaskEditor::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *TaskEditor::currentNode() const { Node * n = m_view->baseModel()->node( m_view->selectionModel()->currentIndex() ); if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n; } void TaskEditor::slotContextMenuRequested( const QModelIndex& index, const QPoint& pos, const QModelIndexList &rows ) { QString name; if (rows.count() > 1) { debugPlan< summarytasks; QList tasks; QList milestones; for (const QModelIndex &idx : rows) { Node *node = m_view->baseModel()->node( idx ); if (node) { switch ( node->type() ) { case Node::Type_Task: tasks << static_cast(node); break; case Node::Type_Milestone: milestones << static_cast(node); break; case Node::Type_Summarytask: summarytasks << static_cast(node); break; default: break; } } } if (!tasks.isEmpty()) { editTasks(tasks, pos); return; } return; } Node *node = m_view->baseModel()->node( index ); if ( node == 0 ) { return; } debugPlan<name()<<" :"<type() ) { case Node::Type_Project: name = "task_edit_popup"; break; case Node::Type_Task: name = node->isScheduled( baseModel()->id() ) ? "task_popup" : "task_edit_popup"; break; case Node::Type_Milestone: name = node->isScheduled( baseModel()->id() ) ? "taskeditor_milestone_popup" : "task_edit_popup"; break; case Node::Type_Summarytask: name = "summarytask_popup"; break; default: name = "node_popup"; break; } m_view->setContextMenuIndex(index); if ( name.isEmpty() ) { slotHeaderContextMenuRequested( pos ); m_view->setContextMenuIndex(QModelIndex()); return; } debugPlan<setContextMenuIndex(QModelIndex()); } void TaskEditor::editTasks(const QList &tasks, const QPoint &pos) { QList lst; QAction tasksEdit(i18n( "Edit..."), nullptr); if (!tasks.isEmpty()) { TasksEditController *ted = new TasksEditController(*project(), tasks, this); connect(&tasksEdit, &QAction::triggered, ted, &TasksEditController::activate); connect(ted, &TasksEditController::addCommand, koDocument(), &KoDocument::addCommand); lst << &tasksEdit; } lst += contextActionList(); if (!lst.isEmpty()) { QMenu::exec( lst, pos, lst.first() ); } } void TaskEditor::setScheduleManager( ScheduleManager *sm ) { if (!sm && scheduleManager()) { // we should only get here if the only schedule manager is scheduled, // or when last schedule manager is deleted m_domdoc.clear(); QDomElement element = m_domdoc.createElement("expanded"); m_domdoc.appendChild(element); m_view->masterView()->saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); QDomDocument doc; bool expand = sm && scheduleManager(); if (expand) { m_view->masterView()->setObjectName("TaskEditor"); QDomElement element = doc.createElement("expanded"); doc.appendChild(element); m_view->masterView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); m_view->baseModel()->setScheduleManager( sm ); if (expand) { m_view->masterView()->doExpand(doc); } else if (tryexpand) { m_view->masterView()->doExpand(m_domdoc); } } void TaskEditor::slotEnableActions() { updateActionsEnabled( isReadWrite() ); } void TaskEditor::updateActionsEnabled( bool on ) { // debugPlan<setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionLinkTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); return; } int selCount = selectedRowCount(); if ( selCount == 0 ) { if ( currentNode() ) { // there are tasks but none is selected menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionLinkTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); } else { // we need to be able to add the first task menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( false ); actionLinkTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); } return; } Node *n = selectedNode(); // 0 if not a single task, summarytask or milestone if ( selCount == 1 && n == 0 ) { // only project selected menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( true ); actionAddSubtask->setEnabled( true ); actionAddSubMilestone->setEnabled( true ); actionDeleteTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); return; } bool baselined = false; Project *p = m_view->project(); if ( p && p->isBaselined() ) { foreach ( Node *n, selectedNodes() ) { if ( n->isBaselined() ) { baselined = true; break; } } } if ( selCount == 1 ) { menuAddTask->setEnabled( true ); actionAddTask->setEnabled( true ); actionAddMilestone->setEnabled( true ); menuAddSubTask->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionAddSubtask->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionAddSubMilestone->setEnabled( ! baselined || n->type() == Node::Type_Summarytask ); actionDeleteTask->setEnabled( ! baselined ); actionLinkTask->setEnabled( ! baselined ); Node *s = n->siblingBefore(); actionMoveTaskUp->setEnabled( s ); actionMoveTaskDown->setEnabled( n->siblingAfter() ); s = n->siblingBefore(); actionIndentTask->setEnabled( ! baselined && s && ! s->isBaselined() ); actionUnindentTask->setEnabled( ! baselined && n->level() > 1 ); return; } // selCount > 1 menuAddTask->setEnabled( false ); actionAddTask->setEnabled( false ); actionAddMilestone->setEnabled( false ); menuAddSubTask->setEnabled( false ); actionAddSubtask->setEnabled( false ); actionAddSubMilestone->setEnabled( false ); actionDeleteTask->setEnabled( ! baselined ); actionLinkTask->setEnabled( false ); actionMoveTaskUp->setEnabled( false ); actionMoveTaskDown->setEnabled( false ); actionIndentTask->setEnabled( false ); actionUnindentTask->setEnabled( false ); } void TaskEditor::setupGui() { menuAddTask = new KActionMenu(koIcon("view-task-add"), i18n("Add Task"), this); actionCollection()->addAction("add_task", menuAddTask ); connect( menuAddTask, &QAction::triggered, this, &TaskEditor::slotAddTask ); actionAddTask = new QAction( i18n( "Add Task" ), this); actionAddTask->setShortcut( Qt::CTRL + Qt::Key_I ); connect( actionAddTask, &QAction::triggered, this, &TaskEditor::slotAddTask ); menuAddTask->addAction( actionAddTask ); actionAddMilestone = new QAction( i18n( "Add Milestone" ), this ); actionAddMilestone->setShortcut( Qt::CTRL + Qt::ALT + Qt::Key_I ); connect( actionAddMilestone, &QAction::triggered, this, &TaskEditor::slotAddMilestone ); menuAddTask->addAction( actionAddMilestone ); menuAddSubTask = new KActionMenu(koIcon("view-task-child-add"), i18n("Add Sub-Task"), this); actionCollection()->addAction("add_subtask", menuAddSubTask ); connect( menuAddSubTask, &QAction::triggered, this, &TaskEditor::slotAddSubtask ); actionAddSubtask = new QAction( i18n( "Add Sub-Task" ), this ); actionAddSubtask->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_I ); connect( actionAddSubtask, &QAction::triggered, this, &TaskEditor::slotAddSubtask ); menuAddSubTask->addAction( actionAddSubtask ); actionAddSubMilestone = new QAction( i18n( "Add Sub-Milestone" ), this ); actionAddSubMilestone->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::ALT + Qt::Key_I ); connect( actionAddSubMilestone, &QAction::triggered, this, &TaskEditor::slotAddSubMilestone ); menuAddSubTask->addAction( actionAddSubMilestone ); actionDeleteTask = new QAction(koIcon("edit-delete"), xi18nc("@action", "Delete"), this); actionCollection()->setDefaultShortcut( actionDeleteTask, Qt::Key_Delete ); actionCollection()->addAction("delete_task", actionDeleteTask ); connect( actionDeleteTask, &QAction::triggered, this, &TaskEditor::slotDeleteTask ); actionLinkTask = new QAction(koIcon("link"), xi18nc("@action", "Link"), this); actionCollection()->setDefaultShortcut( actionLinkTask, Qt::CTRL + Qt::Key_L ); actionCollection()->addAction("link_task", actionLinkTask ); connect( actionLinkTask, &QAction::triggered, this, &TaskEditor::slotLinkTask ); actionIndentTask = new QAction(koIcon("format-indent-more"), i18n("Indent Task"), this); actionCollection()->addAction("indent_task", actionIndentTask ); connect(actionIndentTask, &QAction::triggered, this, &TaskEditor::slotIndentTask); actionUnindentTask = new QAction(koIcon("format-indent-less"), i18n("Unindent Task"), this); actionCollection()->addAction("unindent_task", actionUnindentTask ); connect(actionUnindentTask, &QAction::triggered, this, &TaskEditor::slotUnindentTask); actionMoveTaskUp = new QAction(koIcon("arrow-up"), i18n("Move Up"), this); actionCollection()->addAction("move_task_up", actionMoveTaskUp ); connect(actionMoveTaskUp, &QAction::triggered, this, &TaskEditor::slotMoveTaskUp); actionMoveTaskDown = new QAction(koIcon("arrow-down"), i18n("Move Down"), this); actionCollection()->addAction("move_task_down", actionMoveTaskDown ); connect(actionMoveTaskDown, &QAction::triggered, this, &TaskEditor::slotMoveTaskDown); // Add the context menu actions for the view options actionShowProject = new KToggleAction( i18n( "Show Project" ), this ); actionCollection()->addAction("show_project", actionShowProject); connect(actionShowProject, &QAction::triggered, baseModel(), &NodeItemModel::setShowProject); addContextAction(actionShowProject); actionCollection()->addAction(m_view->actionSplitView()->objectName(), m_view->actionSplitView()); connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskEditor::slotSplitView); addContextAction(m_view->actionSplitView()); createOptionActions(ViewBase::OptionAll); createDockers(); } void TaskEditor::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskEditor::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print_options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->open(); } void TaskEditor::slotAddTask() { debugPlan; if ( selectedRowCount() == 0 || ( selectedRowCount() == 1 && selectedNode() == 0 ) ) { m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); QModelIndex idx = m_view->baseModel()->insertSubtask( t, m_view->project() ); Q_ASSERT( idx.isValid() ); edit( idx ); return; } Node *sib = selectedNode(); if ( sib == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); QModelIndex idx = m_view->baseModel()->insertTask( t, sib ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::slotAddMilestone() { debugPlan; if ( selectedRowCount() == 0 || ( selectedRowCount() == 1 && selectedNode() == 0 ) ) { // None selected or only project selected: insert under main project m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask(); t->estimate()->clear(); QModelIndex idx = m_view->baseModel()->insertSubtask( t, m_view->project() ); Q_ASSERT( idx.isValid() ); edit( idx ); return; } Node *sib = selectedNode(); // sibling if ( sib == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask(); t->estimate()->clear(); QModelIndex idx = m_view->baseModel()->insertTask( t, sib ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::slotAddSubMilestone() { debugPlan; Node *parent = selectedNode(); if ( parent == 0 && selectedRowCount() == 1 ) { // project selected parent = m_view->project(); } if ( parent == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); t->estimate()->clear(); QModelIndex idx = m_view->baseModel()->insertSubtask( t, parent ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::slotAddSubtask() { debugPlan; Node *parent = selectedNode(); if ( parent == 0 && selectedRowCount() == 1 ) { // project selected parent = m_view->project(); } if ( parent == 0 ) { return; } m_view->closePersistentEditor( m_view->selectionModel()->currentIndex() ); Task *t = m_view->project()->createTask( m_view->project()->taskDefaults() ); QModelIndex idx = m_view->baseModel()->insertSubtask( t, parent ); Q_ASSERT( idx.isValid() ); edit( idx ); } void TaskEditor::edit( const QModelIndex &i ) { if ( i.isValid() ) { m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->setParentsExpanded( i, true ); // in case treeview does not have focus m_view->edit( i ); } } void TaskEditor::slotDeleteTask() { //debugPlan; QList lst = selectedNodes(); while ( true ) { // remove children of selected tasks, as parents delete their children Node *ch = 0; foreach ( Node *n1, lst ) { foreach ( Node *n2, lst ) { if ( n2->isChildOf( n1 ) ) { ch = n2; break; } } if ( ch != 0 ) { break; } } if ( ch == 0 ) { break; } lst.removeAt( lst.indexOf( ch ) ); } //foreach ( Node* n, lst ) { debugPlan<name(); } emit deleteTaskList( lst ); QModelIndex i = m_view->selectionModel()->currentIndex(); if ( i.isValid() ) { m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } void TaskEditor::slotLinkTask() { //debugPlan; Node *n = currentNode(); if (n && project()) { RelationEditorDialog dlg(project(), n); if (dlg.exec()) { KUndo2Command *cmd = dlg.buildCommand(); if (cmd) { koDocument()->addCommand(cmd); } } } } void TaskEditor::slotIndentTask() { debugPlan; Node *n = selectedNode(); if ( n ) { emit indentTask(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); m_view->setParentsExpanded( i, true ); } } void TaskEditor::slotUnindentTask() { debugPlan; Node *n = selectedNode(); if ( n ) { emit unindentTask(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } void TaskEditor::slotMoveTaskUp() { debugPlan; Node *n = selectedNode(); if ( n ) { emit moveTaskUp(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } void TaskEditor::slotMoveTaskDown() { debugPlan; Node *n = selectedNode(); if ( n ) { emit moveTaskDown(); QModelIndex i = baseModel()->index( n ); m_view->selectionModel()->select( i, QItemSelectionModel::Rows | QItemSelectionModel::Current | QItemSelectionModel::ClearAndSelect ); m_view->selectionModel()->setCurrentIndex( i, QItemSelectionModel::NoUpdate ); } } bool TaskEditor::loadContext( const KoXmlElement &context ) { ViewBase::loadContext( context ); bool show = (bool)(context.attribute( "show-project", "0" ).toInt() ); actionShowProject->setChecked( show ); baseModel()->setShowProject( show ); // why is this not called by the action? bool res = m_view->loadContext( baseModel()->columnMap(), context ); return res; } void TaskEditor::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); context.setAttribute( "show-project", QString::number(baseModel()->projectShown()) ); m_view->saveContext( baseModel()->columnMap(), context ); } KoPrintJob *TaskEditor::createPrintJob() { return m_view->createPrintJob( this ); } void TaskEditor::slotEditCopy() { m_view->editCopy(); } void TaskEditor::slotEditPaste() { m_view->editPaste(); } //----------------------------------- TaskView::TaskView(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent) { setXMLFile("TaskViewUi.rc"); QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new NodeTreeView( this ); m_doubleTreeView = m_view; connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); NodeSortFilterProxyModel *p = new NodeSortFilterProxyModel( m_view->baseModel(), m_view ); m_view->setModel( p ); l->addWidget( m_view ); setupGui(); //m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode(QAbstractItemView::DragOnly); m_view->setDropIndicatorShown( false ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( false ); m_view->setAcceptDropsOnView( false ); QList readonly; readonly << NodeModel::NodeName << NodeModel::NodeResponsible << NodeModel::NodeAllocation << NodeModel::NodeEstimateType << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription; foreach ( int c, readonly ) { m_view->baseModel()->setReadOnly( c, true ); } QList lst1; lst1 << 1 << -1; QList show; show << NodeModel::NodeStatus << NodeModel::NodeCompleted << NodeModel::NodeResponsible << NodeModel::NodeAssignments << NodeModel::NodePerformanceIndex << NodeModel::NodeBCWS << NodeModel::NodeBCWP << NodeModel::NodeACWP << NodeModel::NodeDescription; for ( int s = 0; s < show.count(); ++s ) { m_view->slaveView()->mapToSection( show[s], s ); } QList lst2; for ( int i = 0; i < m_view->model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << 0 ); m_view->slaveView()->setDefaultColumns( show ); connect( m_view->baseModel(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &TaskView::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &TaskView::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &TaskView::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); connect(m_view->masterView(), &TreeViewBase::doubleClicked, this, &TaskView::itemDoubleClicked); connect(m_view->slaveView(), &TreeViewBase::doubleClicked, this, &TaskView::itemDoubleClicked); Help::add(this, xi18nc("@info:whatsthis", "Task Execution View" "" "The view is used to edit and inspect task progress during project execution." "" "This view supports configuration and printing using the context menu." "More..." "", Help::page("Manual/Task_Execution_View"))); } void TaskView::itemDoubleClicked(const QPersistentModelIndex &idx) { if (idx.column() == NodeModel::NodeDescription) { emit openTaskDescription(isReadWrite() && (idx.flags() & Qt::ItemIsEditable)); } } void TaskView::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); ViewBase::updateReadWrite( rw ); } void TaskView::draw( Project &project ) { m_view->setProject( &project ); } void TaskView::draw() { } void TaskView::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() && m_view->model()->rowCount() > 0 ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void TaskView::slotCurrentChanged( const QModelIndex &curr, const QModelIndex & ) { debugPlan<selectionModel(); return sm->selectedRows().count(); } QList TaskView::selectedNodes() const { QList lst; QItemSelectionModel* sm = m_view->selectionModel(); if ( sm == 0 ) { return lst; } foreach ( const QModelIndex &i, sm->selectedRows() ) { Node * n = m_view->baseModel()->node( proxyModel()->mapToSource( i ) ); if ( n != 0 && n->type() != Node::Type_Project ) { lst.append( n ); } } return lst; } Node *TaskView::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *TaskView::currentNode() const { Node * n = m_view->baseModel()->node( proxyModel()->mapToSource( m_view->selectionModel()->currentIndex() ) ); if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n; } void TaskView::slotContextMenuRequested( const QModelIndex& index, const QPoint& pos ) { QString name; Node *node = m_view->baseModel()->node( proxyModel()->mapToSource( index ) ); if ( node ) { switch ( node->type() ) { case Node::Type_Project: name = "taskview_project_popup"; break; case Node::Type_Task: name = "taskview_popup"; break; case Node::Type_Milestone: name = "taskview_milestone_popup"; break; case Node::Type_Summarytask: name = "taskview_summary_popup"; break; default: break; } } else debugPlan<<"No node: "<setContextMenuIndex(index); emit requestPopupMenu( name, pos ); m_view->setContextMenuIndex(QModelIndex()); } void TaskView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<masterView()->saveExpanded(element); } bool tryexpand = sm && !scheduleManager(); QDomDocument doc; bool expand = sm && scheduleManager() && sm != scheduleManager(); if (expand) { m_view->masterView()->setObjectName("TaskEditor"); QDomElement element = doc.createElement("expanded"); doc.appendChild(element); m_view->masterView()->saveExpanded(element); } ViewBase::setScheduleManager(sm); m_view->baseModel()->setScheduleManager( sm ); if (expand) { m_view->masterView()->doExpand(doc); } else if (tryexpand) { m_view->masterView()->doExpand(m_domdoc); } } void TaskView::slotEnableActions() { updateActionsEnabled( true ); } void TaskView::updateActionsEnabled( bool /*on*/ ) { } void TaskView::setupGui() { // KActionCollection *coll = actionCollection(); // Add the context menu actions for the view options actionShowProject = new KToggleAction( i18n( "Show Project" ), this ); actionCollection()->addAction("show_project", actionShowProject); connect(actionShowProject, &QAction::triggered, baseModel(), &NodeItemModel::setShowProject); addContextAction( actionShowProject ); actionCollection()->addAction(m_view->actionSplitView()->objectName(), m_view->actionSplitView()); connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskView::slotSplitView); addContextAction( m_view->actionSplitView() ); createOptionActions(ViewBase::OptionAll); } void TaskView::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskView::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print_options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->open(); } bool TaskView::loadContext( const KoXmlElement &context ) { ViewBase::loadContext( context ); bool show = (bool)(context.attribute( "show-project", "0" ).toInt() ); actionShowProject->setChecked( show ); baseModel()->setShowProject( show ); // why is this not called by the action? return m_view->loadContext( m_view->baseModel()->columnMap(), context ); } void TaskView::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); context.setAttribute( "show-project", QString::number(baseModel()->projectShown()) ); m_view->saveContext( m_view->baseModel()->columnMap(), context ); } KoPrintJob *TaskView::createPrintJob() { return m_view->createPrintJob( this ); } void TaskView::slotEditCopy() { m_view->editCopy(); } //--------------------------------- WorkPackageTreeView::WorkPackageTreeView( QWidget *parent ) : DoubleTreeViewBase( parent ) { debugPlan<<"----------"<baseModel(); } void WorkPackageTreeView::slotDropAllowed( const QModelIndex &index, int dropIndicatorPosition, QDragMoveEvent *event ) { Q_UNUSED(index); Q_UNUSED(dropIndicatorPosition); Q_UNUSED(event); /* QModelIndex idx = index; NodeSortFilterProxyModel *pr = proxyModel(); if ( pr ) { idx = pr->mapToSource( index ); } event->ignore(); if ( baseModel()->dropAllowed( idx, dropIndicatorPosition, event->mimeData() ) ) { event->accept(); }*/ } //-------------------------------- TaskWorkPackageView::TaskWorkPackageView(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent ), m_cmd( 0 ) { - setXMLFile("WorkPackageViewUi.rc"); + if (doc && doc->isReadWrite()) { + setXMLFile("WorkPackageViewUi.rc"); + } else { + setXMLFile("WorkPackageViewUi_readonly.rc"); + } QVBoxLayout * l = new QVBoxLayout( this ); l->setMargin( 0 ); m_view = new WorkPackageTreeView( this ); m_doubleTreeView = m_view; connect(this, &ViewBase::expandAll, m_view, &DoubleTreeViewBase::slotExpand); connect(this, &ViewBase::collapseAll, m_view, &DoubleTreeViewBase::slotCollapse); l->addWidget( m_view ); setupGui(); //m_view->setEditTriggers( m_view->editTriggers() | QAbstractItemView::EditKeyPressed ); m_view->setDragDropMode( QAbstractItemView::DragDrop ); m_view->setDropIndicatorShown( true ); m_view->setDragEnabled ( true ); m_view->setAcceptDrops( true ); m_view->setAcceptDropsOnView( true ); m_view->setDefaultDropAction(Qt::CopyAction); QList readonly; readonly << NodeModel::NodeName << NodeModel::NodeResponsible << NodeModel::NodeAllocation << NodeModel::NodeEstimateType << NodeModel::NodeEstimateCalendar << NodeModel::NodeEstimate << NodeModel::NodeOptimisticRatio << NodeModel::NodePessimisticRatio << NodeModel::NodeRisk << NodeModel::NodeConstraint << NodeModel::NodeConstraintStart << NodeModel::NodeConstraintEnd << NodeModel::NodeRunningAccount << NodeModel::NodeStartupAccount << NodeModel::NodeStartupCost << NodeModel::NodeShutdownAccount << NodeModel::NodeShutdownCost << NodeModel::NodeDescription; foreach ( int c, readonly ) { m_view->baseModel()->setReadOnly( c, true ); } QList lst1; lst1 << 1 << -1; QList show; show << NodeModel::NodeStatus << NodeModel::NodeCompleted << NodeModel::NodeResponsible << NodeModel::NodeAssignments << NodeModel::NodeDescription; for ( int s = 0; s < show.count(); ++s ) { m_view->slaveView()->mapToSection( show[s], s ); } QList lst2; for ( int i = 0; i < m_view->model()->columnCount(); ++i ) { if ( ! show.contains( i ) ) { lst2 << i; } } m_view->hideColumns( lst1, lst2 ); m_view->masterView()->setDefaultColumns( QList() << 0 ); m_view->slaveView()->setDefaultColumns( show ); connect( m_view->baseModel(), &ItemModelBase::executeCommand, doc, &KoDocument::addCommand ); connect( m_view, &DoubleTreeViewBase::currentChanged, this, &TaskWorkPackageView::slotCurrentChanged ); connect( m_view, &DoubleTreeViewBase::selectionChanged, this, &TaskWorkPackageView::slotSelectionChanged ); connect( m_view, &DoubleTreeViewBase::contextMenuRequested, this, &TaskWorkPackageView::slotContextMenuRequested ); connect( m_view, &DoubleTreeViewBase::headerContextMenuRequested, this, &ViewBase::slotHeaderContextMenuRequested ); connect(m_view->proxyModel(), &WorkPackageProxyModel::loadWorkPackage, this, &TaskWorkPackageView::slotLoadWorkPackage); connect(m_view->masterView(), &TreeViewBase::doubleClicked, this, &TaskWorkPackageView::itemDoubleClicked); connect(m_view->slaveView(), &TreeViewBase::doubleClicked, this, &TaskWorkPackageView::itemDoubleClicked); } void TaskWorkPackageView::itemDoubleClicked(const QPersistentModelIndex &idx) { if (idx.column() == NodeModel::NodeDescription) { emit openTaskDescription(isReadWrite() && (idx.flags() & Qt::ItemIsEditable)); } } Project *TaskWorkPackageView::project() const { return m_view->project(); } void TaskWorkPackageView::setProject( Project *project ) { m_view->setProject( project ); } WorkPackageProxyModel *TaskWorkPackageView::proxyModel() const { return m_view->proxyModel(); } void TaskWorkPackageView::updateReadWrite( bool rw ) { m_view->setReadWrite( rw ); ViewBase::updateReadWrite( rw ); } void TaskWorkPackageView::setGuiActive( bool activate ) { debugPlan<selectionModel()->currentIndex().isValid() && m_view->model()->rowCount() > 0 ) { m_view->selectionModel()->setCurrentIndex(m_view->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } } void TaskWorkPackageView::slotRefreshView() { emit checkForWorkPackages(false); } void TaskWorkPackageView::slotCurrentChanged( const QModelIndex &curr, const QModelIndex & ) { debugPlan<selectionModel(); return sm->selectedRows().count(); } QList TaskWorkPackageView::selectedNodes() const { QList lst; QItemSelectionModel* sm = m_view->selectionModel(); if ( sm == 0 ) { return lst; } foreach ( const QModelIndex &i, sm->selectedRows() ) { Node * n = proxyModel()->taskFromIndex( i ); if ( n != 0 && n->type() != Node::Type_Project ) { lst.append( n ); } } return lst; } Node *TaskWorkPackageView::selectedNode() const { QList lst = selectedNodes(); if ( lst.count() != 1 ) { return 0; } return lst.first(); } Node *TaskWorkPackageView::currentNode() const { Node * n = proxyModel()->taskFromIndex( m_view->selectionModel()->currentIndex() ); if ( n == 0 || n->type() == Node::Type_Project ) { return 0; } return n; } void TaskWorkPackageView::slotContextMenuRequested( const QModelIndex& index, const QPoint& pos ) { QString name; Node *node = proxyModel()->taskFromIndex( index ); if ( node ) { switch ( node->type() ) { case Node::Type_Task: name = "workpackage_popup"; break; case Node::Type_Milestone: name = "taskview_milestone_popup"; break; case Node::Type_Summarytask: name = "taskview_summary_popup"; break; default: break; } } else debugPlan<<"No node: "<setContextMenuIndex(index); emit requestPopupMenu(name, pos); m_view->setContextMenuIndex(QModelIndex()); } void TaskWorkPackageView::setScheduleManager( ScheduleManager *sm ) { //debugPlan<baseModel()->setScheduleManager( sm ); } void TaskWorkPackageView::slotEnableActions() { updateActionsEnabled( true ); } void TaskWorkPackageView::updateActionsEnabled( bool on ) { bool o = ! selectedNodes().isEmpty(); actionMailWorkpackage->setEnabled( o && on ); } void TaskWorkPackageView::setupGui() { actionMailWorkpackage = new QAction(koIcon("cloud-upload"), i18n("Publish..."), this); actionCollection()->addAction("send_workpackage", actionMailWorkpackage ); connect( actionMailWorkpackage, &QAction::triggered, this, &TaskWorkPackageView::slotMailWorkpackage ); actionOpenWorkpackages = new QAction(koIcon("view-task"), i18n("Work Packages..."), this); actionCollection()->addAction("open_workpackages", actionOpenWorkpackages ); actionOpenWorkpackages->setEnabled(false); connect( actionOpenWorkpackages, &QAction::triggered, this, &TaskWorkPackageView::openWorkpackages ); // Add the context menu actions for the view options addContextAction( m_view->actionSplitView() ); actionCollection()->addAction(m_view->actionSplitView()->objectName(), m_view->actionSplitView()); connect(m_view->actionSplitView(), &QAction::triggered, this, &TaskWorkPackageView::slotSplitView); createOptionActions(ViewBase::OptionAll); } void TaskWorkPackageView::slotMailWorkpackage() { QList lst = selectedNodes(); if ( ! lst.isEmpty() ) { // TODO find a better way to log to avoid undo/redo m_cmd = new MacroCommand( kundo2_i18n( "Log Send Workpackage" ) ); QPointer dlg = new WorkPackageSendDialog( lst, scheduleManager(), this ); connect ( dlg->panel(), &WorkPackageSendPanel::sendWorkpackages, this, &TaskWorkPackageView::publishWorkpackages ); connect ( dlg->panel(), &WorkPackageSendPanel::sendWorkpackages, this, &TaskWorkPackageView::slotWorkPackageSent ); dlg->exec(); delete dlg; if ( ! m_cmd->isEmpty() ) { part()->addCommand( m_cmd ); m_cmd = 0; } delete m_cmd; m_cmd = 0; } } void TaskWorkPackageView::slotWorkPackageSent( const QList &nodes, Resource *resource ) { foreach ( Node *n, nodes ) { WorkPackage *wp = new WorkPackage( static_cast( n )->workPackage() ); wp->setOwnerName( resource->name() ); wp->setOwnerId( resource->id() ); wp->setTransmitionTime( DateTime::currentDateTime() ); wp->setTransmitionStatus( WorkPackage::TS_Send ); m_cmd->addCommand( new WorkPackageAddCmd( static_cast( n->projectNode() ), n, wp ) ); } } void TaskWorkPackageView::slotSplitView() { debugPlan; m_view->setViewSplitMode( ! m_view->isViewSplit() ); emit optionsModified(); } void TaskWorkPackageView::slotOptions() { debugPlan; SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog( this, m_view, this ); dlg->addPrintingOptions(sender()->objectName() == "print_options"); connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); dlg->open(); } bool TaskWorkPackageView::loadContext( const KoXmlElement &context ) { debugPlan; ViewBase::loadContext( context ); return m_view->loadContext( m_view->baseModel()->columnMap(), context ); } void TaskWorkPackageView::saveContext( QDomElement &context ) const { ViewBase::saveContext( context ); m_view->saveContext( m_view->baseModel()->columnMap(), context ); } KoPrintJob *TaskWorkPackageView::createPrintJob() { return m_view->createPrintJob( this ); } void TaskWorkPackageView::slotEditCopy() { m_view->editCopy(); } void TaskWorkPackageView::slotWorkpackagesAvailable(bool value) { actionOpenWorkpackages->setEnabled(value); } void TaskWorkPackageView::slotLoadWorkPackage(QList files) { QList urls; for (const QString &f : files) { QUrl url = QUrl(f); if (url.isValid()) { urls << url; } } emit loadWorkPackageUrl(m_view->project(), urls); } } // namespace KPlato diff --git a/src/libs/ui/kptviewbase.cpp b/src/libs/ui/kptviewbase.cpp index dcfa039d..2666f446 100644 --- a/src/libs/ui/kptviewbase.cpp +++ b/src/libs/ui/kptviewbase.cpp @@ -1,2808 +1,2808 @@ /* This file is part of the KDE project Copyright (C) 2006 -2010, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "kptviewbase.h" #include "kptitemmodelbase.h" #include "kptproject.h" #include "kptdebug.h" #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { // sort indexes the way they are displayed void sort(const QTreeView *view, QModelIndexList &list) { QModelIndexList i; i << list.takeFirst(); for (QModelIndex idx = view->indexAbove(i.first()); idx.isValid() && !list.isEmpty(); idx = view->indexAbove(idx)) { if (list.contains(idx)) { i.prepend(idx); list.removeOne(idx); } } for (QModelIndex idx = view->indexBelow(i.last()); idx.isValid() && !list.isEmpty(); idx = view->indexBelow(idx)) { if (list.contains(idx)) { i.append(idx); list.removeOne(idx); } } list = i; } DockWidget::DockWidget( ViewBase *v, const QString &identity, const QString &title ) : QDockWidget( v ), view( v ), id( identity ), location( Qt::RightDockWidgetArea ), editor( false ), m_shown( true ) { setWindowTitle( title ); setObjectName( v->objectName() + '-' + identity ); toggleViewAction()->setObjectName( objectName() ); connect(this, &QDockWidget::dockLocationChanged, this, &DockWidget::setLocation); } void DockWidget::activate( KoMainWindow *mainWindow ) { connect(this, &QDockWidget::visibilityChanged, this, &DockWidget::setShown); setVisible( m_shown ); mainWindow->addDockWidget( location, this ); foreach(const KActionCollection *c, KActionCollection::allCollections()) { KActionMenu *a = qobject_cast(c->action("settings_dockers_menu")); if ( a ) { a->addAction( toggleViewAction() ); break; } } } void DockWidget::deactivate( KoMainWindow *mainWindow ) { disconnect(this, &QDockWidget::visibilityChanged, this, &DockWidget::setShown); mainWindow->removeDockWidget( this ); // activation re-parents to QMainWindow, so re-parent back to view setParent( const_cast( view ) ); foreach(const KActionCollection *c, KActionCollection::allCollections()) { KActionMenu *a = qobject_cast(c->action("settings_dockers_menu")); if ( a ) { a->removeAction( toggleViewAction() ); break; } } } void DockWidget::setShown( bool show ) { m_shown = show; setVisible( show ); } bool KPlato::DockWidget::shown() const { return m_shown; } void DockWidget::setLocation( Qt::DockWidgetArea area ) { location = area; } bool DockWidget::saveXml( QDomElement &context ) const { QDomElement e = context.ownerDocument().createElement( "docker" ); context.appendChild( e ); e.setAttribute( "id", id ); e.setAttribute( "location", QString::number(location) ); e.setAttribute( "floating", QString::number(isFloating()) ); e.setAttribute( "visible", QString::number(m_shown) ); return true; } void DockWidget::loadXml(const KoXmlElement& context) { location = static_cast( context.attribute( "location", "0" ).toInt() ); setFloating( (bool) context.attribute( "floating", "0" ).toInt() ); m_shown = context.attribute( "visible", "1" ).toInt(); } //------------------------ bool PrintingOptions::loadXml( KoXmlElement &element ) { KoXmlElement e; forEachElement( e, element ) { if ( e.tagName() == "header" ) { headerOptions.group = e.attribute( "group", "0" ).toInt(); headerOptions.project = static_cast( e.attribute( "project", "0" ).toInt() ); headerOptions.date = static_cast( e.attribute( "date", "0" ).toInt() ); headerOptions.manager = static_cast( e.attribute( "manager", "0" ).toInt() ); headerOptions.page = static_cast( e.attribute( "page", "0" ).toInt() ); } else if ( e.tagName() == "footer" ) { footerOptions.group = e.attribute( "group", "0" ).toInt(); footerOptions.project = static_cast( e.attribute( "project", "0" ).toInt() ); footerOptions.date = static_cast( e.attribute( "date", "0" ).toInt() ); footerOptions.manager = static_cast( e.attribute( "manager", "0" ).toInt() ); footerOptions.page = static_cast( e.attribute( "page", "0" ).toInt() ); } } return true; } void PrintingOptions::saveXml( QDomElement &element ) const { QDomElement me = element.ownerDocument().createElement( "printing-options" ); element.appendChild( me ); QDomElement h = me.ownerDocument().createElement( "header" ); me.appendChild( h ); h.setAttribute( "group", QString::number(headerOptions.group) ); h.setAttribute( "project", QString::number(headerOptions.project) ); h.setAttribute( "date", QString::number(headerOptions.date) ); h.setAttribute( "manager", QString::number(headerOptions.manager) ); h.setAttribute( "page", QString::number(headerOptions.page) ); QDomElement f = me.ownerDocument().createElement( "footer" ); me.appendChild( f ); f.setAttribute( "group", QString::number(footerOptions.group) ); f.setAttribute( "project", QString::number(footerOptions.project) ); f.setAttribute( "date", QString::number(footerOptions.date) ); f.setAttribute( "manager", QString::number(footerOptions.manager) ); f.setAttribute( "page", QString::number(footerOptions.page) ); } //---------------------- PrintingHeaderFooter::PrintingHeaderFooter( const PrintingOptions &opt, QWidget *parent ) : QWidget( parent ) { setupUi( this ); setWindowTitle( i18n("Header and Footer" )); setOptions( opt ); connect(ui_header, &QGroupBox::toggled, this, &PrintingHeaderFooter::slotChanged); connect(ui_headerProject, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_headerPage, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_headerManager, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_headerDate, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_footer, &QGroupBox::toggled, this, &PrintingHeaderFooter::slotChanged); connect(ui_footerProject, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_footerPage, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_footerManager, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); connect(ui_footerDate, &QCheckBox::stateChanged, this, &PrintingHeaderFooter::slotChanged); } PrintingHeaderFooter::~PrintingHeaderFooter() { //debugPlan; } void PrintingHeaderFooter::slotChanged() { debugPlan; emit changed( options() ); } void PrintingHeaderFooter::setOptions( const PrintingOptions &options ) { m_options = options; ui_header->setChecked( m_options.headerOptions.group ); ui_headerProject->setCheckState( m_options.headerOptions.project ); ui_headerDate->setCheckState( m_options.headerOptions.date ); ui_headerManager->setCheckState( m_options.headerOptions.manager ); ui_headerPage->setCheckState( m_options.headerOptions.page ); ui_footer->setChecked( m_options.footerOptions.group ); ui_footerProject->setCheckState( m_options.footerOptions.project ); ui_footerDate->setCheckState( m_options.footerOptions.date ); ui_footerManager->setCheckState( m_options.footerOptions.manager ); ui_footerPage->setCheckState( m_options.footerOptions.page ); } PrintingOptions PrintingHeaderFooter::options() const { //debugPlan; PrintingOptions opt; opt.headerOptions.group = ui_header->isChecked(); opt.headerOptions.project = ui_headerProject->checkState(); opt.headerOptions.date = ui_headerDate->checkState(); opt.headerOptions.manager = ui_headerManager->checkState(); opt.headerOptions.page = ui_headerPage->checkState(); opt.footerOptions.group = ui_footer->isChecked(); opt.footerOptions.project = ui_footerProject->checkState(); opt.footerOptions.date = ui_footerDate->checkState( ); opt.footerOptions.manager = ui_footerManager->checkState(); opt.footerOptions.page = ui_footerPage->checkState(); return opt; } PrintingDialog::PrintingDialog( ViewBase *view ) : KoPrintingDialog( view ), m_view( view ), m_widget( 0 ) { setPrinterPageLayout( view->pageLayout() ); QImage px( 100, 600, QImage::Format_Mono ); int dpm = printer().resolution() * 40; px.setDotsPerMeterX( dpm ); px.setDotsPerMeterY( dpm ); QPainter p( &px ); m_textheight = p.boundingRect( QRectF(), Qt::AlignTop, "Aj" ).height(); debugPlan<<"textheight:"<printingOptions(); } void PrintingDialog::setPrintingOptions( const PrintingOptions &opt ) { debugPlan; m_view->setPrintingOptions( opt ); emit changed( opt ); emit changed(); } void PrintingDialog::setPrinterPageLayout( const KoPageLayout &pagelayout ) { QPrinter &p = printer(); QPrinter::Orientation o; switch ( pagelayout.orientation ) { case KoPageFormat::Portrait: o = QPrinter::Portrait; break; case KoPageFormat::Landscape: o = QPrinter::Landscape; break; default: o = QPrinter::Portrait; break; } p.setOrientation( o ); p.setPaperSize( KoPageFormat::printerPageSize( pagelayout.format ) ); p.setPageMargins( pagelayout.leftMargin, pagelayout.topMargin, pagelayout.rightMargin, pagelayout.bottomMargin, QPrinter::Point ); } void PrintingDialog::startPrinting(RemovePolicy removePolicy ) { setPrinterPageLayout( m_view->pageLayout() ); // FIXME: Something resets printer().paperSize() to A4 ! KoPrintingDialog::startPrinting( removePolicy ); } QWidget *PrintingDialog::createPageLayoutWidget() const { QWidget *w = ViewBase::createPageLayoutWidget( m_view ); KoPageLayoutWidget *pw = w->findChild(); connect(pw, SIGNAL(layoutChanged(KoPageLayout)), m_view, SLOT(setPageLayout(KoPageLayout))); connect(pw, &KoPageLayoutWidget::layoutChanged, this, &PrintingDialog::setPrinterPageLayout); connect(pw, SIGNAL(layoutChanged(KoPageLayout)), this, SIGNAL(changed())); return w; } QList PrintingDialog::createOptionWidgets() const { //debugPlan; PrintingHeaderFooter *w = new PrintingHeaderFooter( printingOptions() ); connect(w, &PrintingHeaderFooter::changed, this, &PrintingDialog::setPrintingOptions); const_cast( this )->m_widget = w; return QList() << w; } /*QList PrintingDialog::shapesOnPage(int) { return QList(); }*/ void PrintingDialog::drawRect( QPainter &p, const QRect &r, Qt::Edges edges ) { p.save(); QPen pen = p.pen(); pen.setColor(Qt::gray); p.setPen(pen); if (edges & Qt::LeftEdge) { p.drawLine( r.topLeft(), r.bottomLeft() ); } if (edges & Qt::BottomEdge) { p.drawLine( r.bottomLeft(), r.bottomRight() ); } if (edges & Qt::TopEdge) { p.drawLine( r.topRight(), r.bottomRight() ); } if (edges & Qt::RightEdge) { p.drawLine( r.topRight(), r.bottomRight() ); } p.restore(); } QRect PrintingDialog::headerRect() const { PrintingOptions options = m_view->printingOptions(); if ( options.headerOptions.group == false ) { return QRect(); } int height = headerFooterHeight( options.headerOptions ); return QRect( 0, 0, const_cast( this )->printer().pageRect().width(), height ); } QRect PrintingDialog::footerRect() const { PrintingOptions options = m_view->printingOptions(); if ( options.footerOptions.group == false ) { return QRect(); } int height = headerFooterHeight( options.footerOptions ); QRect r = const_cast( this )->printer().pageRect(); return QRect( 0, r.height() - height, r.width(), height ); } int PrintingDialog::headerFooterHeight( const PrintingOptions::Data &options ) const { int height = 0.0; if ( options.page == Qt::Checked || options.project == Qt::Checked || options.manager == Qt::Checked || options.date == Qt::Checked ) { height += m_textheight * 1.5; } if ( options.project == Qt::Checked && options.manager == Qt::Checked && ( options.date == Qt::Checked || options.page == Qt::Checked ) ) { height *= 2.0; } debugPlan< pageRect.left()) { pageRect.setLeft(r.left()); } p.restore(); if ( options.project == Qt::Checked || options.manager == Qt::Checked || options.date == Qt::Checked ) { p.drawLine( rect_1.topRight(), rect_1.bottomRight() ); } } if ( options.date == Qt::Checked ) { p.save(); QFont f = p.font(); f.setPointSize( f.pointSize() * 0.5 ); p.setFont( f ); QRect rct = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, i18n("Date:") ); p.restore(); if ( ( options.project == Qt::Checked && options.manager != Qt::Checked ) || ( options.project != Qt::Checked && options.manager == Qt::Checked ) ) { dateRect = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, date ); dateRect.setHeight( rect_1.height() ); rect_1.setRight( dateRect.left() - 2 ); if (rct.right() > dateRect.right()) { dateRect.setRight(rct.right()); } p.drawLine( rect_1.topRight(), rect_1.bottomRight() ); } else if ( options.project == Qt::Checked && options.manager == Qt::Checked ) { dateRect = p.boundingRect( rect_2, Qt::AlignRight|Qt::AlignTop, date ); dateRect.setHeight( rect_2.height() ); rect_2.setRight( dateRect.left() - 2 ); if (rct.right() > dateRect.right()) { dateRect.setRight(rct.right()); } p.drawLine( rect_2.topRight(), rect_2.bottomRight() ); } else { dateRect = p.boundingRect( rect_2, Qt::AlignLeft|Qt::AlignTop, date ); if (rct.right() > dateRect.right()) { dateRect.setRight(rct.right()); } dateRect.setHeight( rect_2.height() ); rect_2.setRight( dateRect.left() - 2 ); if ( rect_2.left() != rect.left() ) { p.drawLine( rect_2.topRight(), rect_2.bottomRight() ); } } } if ( options.manager == Qt::Checked ) { p.save(); QFont f = p.font(); f.setPointSize( f.pointSize() * 0.5 ); p.setFont( f ); QRect rct = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, i18n("Manager:") ); p.restore(); if ( options.project != Qt::Checked ) { managerRect = p.boundingRect( rect_1, Qt::AlignLeft|Qt::AlignTop, manager ); managerRect.setHeight( rect_1.height() ); if (rct.right() > managerRect.right()) { managerRect.setRight(rct.right()); } } else if ( options.date != Qt::Checked && options.page != Qt::Checked ) { managerRect = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, manager ); managerRect.setHeight( rect_1.height() ); if (rct.right() > managerRect.right()) { managerRect.setRight(rct.right()); } rect_1.setRight( managerRect.left() - 2 ); p.drawLine( rect_1.topRight(), rect_1.bottomRight() ); } else { managerRect = p.boundingRect( rect_2, Qt::AlignLeft|Qt::AlignTop, manager ); managerRect.setHeight( rect_2.height() ); if (rct.right() > managerRect.right()) { managerRect.setRight(rct.right()); } } } if ( options.project == Qt::Checked ) { projectRect = p.boundingRect( rect_1, Qt::AlignLeft|Qt::AlignTop, projectName ); projectRect.setHeight( rect_1.height() ); p.save(); QFont f = p.font(); f.setPointSize( f.pointSize() * 0.5 ); p.setFont( f ); QRect rct = p.boundingRect( rect_1, Qt::AlignRight|Qt::AlignTop, i18n("Project:") ); if (rct.right() > projectRect.right()) { projectRect.setRight(rct.right()); } p.restore(); } if ( options.page == Qt::Checked ) { p.drawText( pageRect, Qt::AlignHCenter|Qt::AlignBottom, page ); } if ( options.project == Qt::Checked ) { p.drawText( projectRect.adjusted(3, 0, 3, 0), Qt::AlignLeft|Qt::AlignBottom, projectName ); } if ( options.date == Qt::Checked ) { p.drawText( dateRect, Qt::AlignHCenter|Qt::AlignBottom, date ); } if ( options.manager == Qt::Checked ) { p.drawText( managerRect.adjusted(3, 0, 3, 0), Qt::AlignLeft|Qt::AlignBottom, manager ); } QFont f = p.font(); f.setPointSize( f.pointSize() * 0.5 ); p.setFont( f ); if ( options.page == Qt::Checked ) { p.drawText( pageRect, Qt::AlignTop|Qt::AlignLeft, i18n( "Page:" ) ); } if ( options.project == Qt::Checked ) { p.drawText( projectRect, Qt::AlignTop|Qt::AlignLeft, i18n( "Project:" ) ); } if ( options.date == Qt::Checked ) { p.drawText( dateRect, Qt::AlignTop|Qt::AlignLeft, i18n( "Date:" ) ); } if ( options.manager == Qt::Checked ) { p.drawText( managerRect, Qt::AlignTop|Qt::AlignLeft, i18n( "Manager:" ) ); } p.restore(); } //-------------- ViewBase::ViewBase(KoPart *part, KoDocument *doc, QWidget *parent) : KoView(part, doc, parent), m_readWrite( false ), m_proj( 0 ), m_schedulemanager( 0 ), m_singleTreeView(nullptr), m_doubleTreeView(nullptr) { } ViewBase::~ViewBase() { if (factory()) { factory()->removeClient(this); } if ( koDocument() ) { //HACK to avoid ~View to access koDocument() setDocumentDeleted(); } } void ViewBase::openPopupMenu(const QString &name, const QPoint &pos) { QMenu *menu = popupMenu(name); if (menu) { menu->exec(pos); } } QMenu *ViewBase::popupMenu(const QString& name) { //debugPlan; if (factory()) { return dynamic_cast(factory()->container(name, this)); } return nullptr; } void ViewBase::setProject( Project *project ) { m_proj = project; emit projectChanged( project ); } KoDocument *ViewBase::part() const { return koDocument(); } KoPageLayout ViewBase::pageLayout() const { return m_pagelayout; } void ViewBase::setPageLayout( const KoPageLayout &layout ) { m_pagelayout = layout; } bool ViewBase::isActive() const { if ( hasFocus() ) { return true; } foreach ( QWidget *v, findChildren() ) { if ( v->hasFocus() ) { return true; } } return false; } void ViewBase::updateReadWrite( bool readwrite ) { m_readWrite = readwrite; emit readWriteChanged( readwrite ); } void ViewBase::setGuiActive( bool active ) // virtual slot { //debugPlan<setWindowTitle( xi18nc( "@title:tab", "Page Layout" ) ); QHBoxLayout *lay = new QHBoxLayout(widget); KoPageLayoutWidget *w = new KoPageLayoutWidget( widget, view->pageLayout() ); w->showPageSpread( false ); lay->addWidget( w, 1 ); KoPagePreviewWidget *prev = new KoPagePreviewWidget( widget ); prev->setPageLayout( view->pageLayout() ); lay->addWidget( prev, 1 ); connect (w, &KoPageLayoutWidget::layoutChanged, prev, &KoPagePreviewWidget::setPageLayout); return widget; } /*static*/ PrintingHeaderFooter *ViewBase::createHeaderFooterWidget( ViewBase *view ) { PrintingHeaderFooter *widget = new PrintingHeaderFooter( view->printingOptions() ); widget->setWindowTitle( xi18nc( "@title:tab", "Header and Footer" ) ); widget->setOptions( view->printingOptions() ); return widget; } void ViewBase::slotHeaderContextMenuRequested( const QPoint &pos ) { debugPlan; QList lst = contextActionList(); if ( ! lst.isEmpty() ) { QMenu::exec( lst, pos, lst.first() ); } } void ViewBase::createOptionActions(int actions, const QString &prefix) { QAction *action; // These goes at the top action = new QAction(this); action->setSeparator(true); int pos = 0; addContextAction(action, pos++); if (actions & OptionExpand) { action = new QAction(koIcon("arrow-down"), i18n("Expand All"), this); actionCollection()->addAction("expand_all", action); connect(action, &QAction::triggered, this, &ViewBase::expandAll); addContextAction(action, pos++); } if (actions & OptionCollapse) { action = new QAction(koIcon("arrow-up"), i18n("Collapse All"), this); actionCollection()->addAction("collapse_all", action); connect(action, &QAction::triggered, this, &ViewBase::collapseAll); addContextAction(action, pos++); } action = new QAction(this); action->setSeparator(true); addContextAction(action, pos++); // rest are appended action = new QAction(this); action->setSeparator(true); addContextAction(action); if (actions & OptionPrint) { action = KStandardAction::create(KStandardAction::Print, mainWindow(), SLOT(slotFilePrint()), this); actionCollection()->addAction("print", action); addContextAction(action); } if (actions & OptionPrintPreview) { action = KStandardAction::create(KStandardAction::PrintPreview, mainWindow(), SLOT(slotFilePrintPreview()), this); actionCollection()->addAction("print_preview", action); addContextAction(action); } if (actions & OptionPrintPdf) { action = new QAction(koIcon("application-pdf"), i18n("Print to PDF..."), this); actionCollection()->addAction("print_pdf", action); connect(action, SIGNAL(triggered()), mainWindow(), SLOT(exportToPdf())); addContextAction(action); } if (actions & OptionPrintConfig) { action = new QAction(koIcon("configure"), i18n("Print Options..."), this); actionCollection()->addAction("print_options", action); connect(action, &QAction::triggered, this, &ViewBase::slotOptions); addContextAction(action); } action = new QAction(this); action->setSeparator(true); addContextAction(action); if (actions & OptionViewConfig) { action = new QAction(koIcon("configure"), i18n("Configure View..."), this); actionCollection()->addAction("configure_view", action); connect(action, &QAction::triggered, this, &ViewBase::slotOptions); addContextAction(action); } } void ViewBase::slotOptionsFinished( int result ) { if ( result == QDialog::Accepted ) { emit optionsModified(); } if ( sender() ) { sender()->deleteLater(); } } bool ViewBase::loadContext( const KoXmlElement &context ) { KoXmlElement me; forEachElement( me, context ) { if ( me.tagName() == "page-layout" ) { m_pagelayout.format = KoPageFormat::formatFromString( me.attribute( "format" ) ); m_pagelayout.orientation = me.attribute( "orientation" ) == "landscape" ? KoPageFormat::Landscape : KoPageFormat::Portrait; m_pagelayout.width = me.attribute( "width", "0.0" ).toDouble(); m_pagelayout.height = me.attribute( "height", "0.0" ).toDouble(); m_pagelayout.leftMargin = me.attribute( "left-margin", QString::number( MM_TO_POINT( 20.0 ) ) ).toDouble(); m_pagelayout.rightMargin = me.attribute( "right-margin", QString::number( MM_TO_POINT( 20.0 ) ) ).toDouble(); m_pagelayout.topMargin = me.attribute( "top-margin", QString::number( MM_TO_POINT( 20.0 ) ) ).toDouble(); m_pagelayout.bottomMargin = me.attribute( "bottom-margin", QString::number( MM_TO_POINT( 20.0 ) ) ).toDouble(); } else if ( me.tagName() == "printing-options" ) { m_printingOptions.loadXml( me ); } else if ( me.tagName() == "dockers" ) { KoXmlElement e; forEachElement ( e, me ) { DockWidget *ds = findDocker( e.attribute( "id" ) ); if ( ds ) { ds->loadXml( e ); } } } } return true; } void ViewBase::saveContext( QDomElement &context ) const { QDomElement me = context.ownerDocument().createElement( "page-layout" ); context.appendChild( me ); me.setAttribute( "format", KoPageFormat::formatString( m_pagelayout.format ) ); me.setAttribute( "orientation", m_pagelayout.orientation == KoPageFormat::Portrait ? "portrait" : "landscape" ); me.setAttribute( "width", QString::number(m_pagelayout.width) ); me.setAttribute( "height",QString::number(m_pagelayout. height) ); me.setAttribute( "left-margin", QString::number(m_pagelayout.leftMargin) ); me.setAttribute( "right-margin", QString::number(m_pagelayout.rightMargin) ); me.setAttribute( "top-margin", QString::number(m_pagelayout.topMargin) ); me.setAttribute( "bottom-margin",QString::number( m_pagelayout.bottomMargin) ); m_printingOptions.saveXml( context ); if ( ! m_dockers.isEmpty() ) { QDomElement e = context.ownerDocument().createElement( "dockers" ); context.appendChild( e ); foreach ( const DockWidget *ds, m_dockers ) { ds->saveXml( e ); } } } void ViewBase::addDocker( DockWidget *ds ) { //addAction( "view_docker_list", ds->toggleViewAction() ); m_dockers << ds; } QList ViewBase::dockers() const { return m_dockers; } DockWidget* ViewBase::findDocker( const QString &id ) const { foreach ( DockWidget *ds, m_dockers ) { if ( ds->id == id ) { return ds; } } return 0; } void ViewBase::setViewSplitMode(bool split) { if (m_doubleTreeView) { m_doubleTreeView->setViewSplitMode(split); } } void ViewBase::showColumns(const QList &left, const QList &right) { TreeViewBase *view1 = m_singleTreeView; TreeViewBase *view2 = nullptr; if (m_doubleTreeView) { view1 = m_doubleTreeView->masterView(); view2 = m_doubleTreeView->slaveView(); m_doubleTreeView->setViewSplitMode(!right.isEmpty()); } if (view1) { const QAbstractItemModel *model = view1->model(); if (model) { int count = model->columnCount(); for (int i = 0; i < count; ++i) { view1->setColumnHidden(i, !left.contains(i)); if (view2) { view2->setColumnHidden(i, !right.contains(i)); } } // sort columns for (int i = 0; i < left.count(); ++i) { view1->mapToSection(left.at(i), i); } if (view2) { for (int i = 0; i < right.count(); ++i) { view2->mapToSection(right.at(i), i); } } } } } //---------------------- TreeViewPrintingDialog::TreeViewPrintingDialog( ViewBase *view, TreeViewBase *treeview, Project *project ) : PrintingDialog( view ), m_tree( treeview ), m_project( project ), m_firstRow( -1 ) { printer().setFromTo( documentFirstPage(), documentLastPage() ); } int TreeViewPrintingDialog::documentLastPage() const { int page = documentFirstPage(); while ( firstRow( page ) != -1 ) { ++page; } if ( page > documentFirstPage() ) { --page; } return page; } int TreeViewPrintingDialog::firstRow( int page ) const { debugPlan<header(); int height = mh->height(); int hHeight = headerRect().height(); int fHeight = footerRect().height(); QRect pageRect = const_cast( this )->printer().pageRect(); int gap = 8; int pageHeight = pageRect.height() - height; if ( hHeight > 0 ) { pageHeight -= ( hHeight + gap ); } if ( fHeight > 0 ) { pageHeight -= ( fHeight + gap ); } int rowsPrPage = pageHeight / height; int rows = m_tree->model()->rowCount(); int row = -1; for ( int i = 0; i < rows; ++i ) { if ( ! m_tree->isRowHidden( i, QModelIndex() ) ) { row = i; break; } } if ( row != -1 ) { QModelIndex idx = m_tree->model()->index( row, 0, QModelIndex() ); row = 0; while ( idx.isValid() ) { if ( row >= rowsPrPage * pageNumber ) { debugPlan<indexBelow( idx ); } if ( ! idx.isValid() ) { row = -1; } } debugPlan<<"Page"< TreeViewPrintingDialog::createOptionWidgets() const { QList lst; lst << createPageLayoutWidget(); lst += PrintingDialog::createOptionWidgets(); return lst; } void TreeViewPrintingDialog::printPage( int page, QPainter &painter ) { m_firstRow = firstRow( page ); QHeaderView *mh = m_tree->header(); int length = mh->length(); int height = mh->height(); QRect hRect = headerRect(); QRect fRect = footerRect(); QRect pageRect = printer().pageRect(); pageRect.moveTo( 0, 0 ); QRect paperRect = printer().paperRect(); QAbstractItemModel *model = m_tree->model(); debugPlan<printingOptions(), page, *(m_project) ); } int gap = 8; int pageHeight = pageRect.height() - height; if ( hRect.isValid() ) { pageHeight -= ( hRect.height() + gap ); } if ( fRect.isValid() ) { pageHeight -= ( fRect.height() + gap ); } int rowsPrPage = pageHeight / height; double sx = pageRect.width() > length ? 1.0 : (double)pageRect.width() / (double)length; double sy = 1.0; painter.scale( sx, sy ); int h = 0; painter.translate( 0, hRect.height() + gap ); h = hRect.height() + gap; painter.setPen(Qt::black); painter.setBrush( Qt::lightGray ); int higestIndex = 0; int rightpos = 0; for ( int i = 0; i < mh->count(); ++i ) { QString text = model->headerData( i, Qt::Horizontal ).toString(); QVariant a = model->headerData( i, Qt::Horizontal, Qt::TextAlignmentRole ); int align = a.isValid() ? a.toInt() : (int)(Qt::AlignLeft|Qt::AlignVCenter); if ( ! mh->isSectionHidden( i ) ) { QRect r = QRect( mh->sectionPosition( i ), 0, mh->sectionSize( i ), height ).adjusted(0, 0, 0, -painter.pen().width()); if (rightpos < r.right()) { higestIndex = i; rightpos = r.right(); } painter.drawRect( r ); // FIXME There is a bug somewhere, the text somehow overwites the rect outline for the first column! painter.save(); painter.setBrush(QBrush()); painter.drawText( r.adjusted(3, 1, -3, -1), align, text ); painter.drawRect( r ); painter.restore(); } //debugPlan<isSectionHidden( i )<sectionPosition( i ); } if ( m_firstRow == -1 ) { debugPlan<<"No data"; return; } painter.setBrush( QBrush() ); QModelIndex idx = model->index( m_firstRow, 0, QModelIndex() ); int numRows = 0; //debugPlan<count(); ++i ) { if ( mh->isSectionHidden( i ) ) { continue; } Qt::Edges edges = Qt::BottomEdge | Qt::LeftEdge; QModelIndex index = model->index( idx.row(), i, idx.parent() ); QString text = model->data( index ).toString(); QVariant a = model->data( index, Qt::TextAlignmentRole ); int align = a.isValid() ? a.toInt() : (int)(Qt::AlignLeft|Qt::AlignVCenter); QRect r( mh->sectionPosition( i ), 0, mh->sectionSize( i ), height ); if (higestIndex == i) { edges |= Qt::RightEdge; r.adjust(0, 0, 1, 0); } drawRect( painter, r, edges ); painter.drawText( r.adjusted(3, 1, -3, -1) , align, text ); } ++numRows; idx = m_tree->indexBelow( idx ); } } /** * TreeViewBase is a QTreeView adapted for operation by keyboard only and as components in DoubleTreeViewBase. * Note that keyboard navigation and selection behavior may not be fully compliant with QTreeView. * If you use other settings than QAbstractItemView::ExtendedSelection and QAbstractItemView::SelectRows, * you should have a look at the implementation keyPressEvent() and updateSelection(). */ TreeViewBase::TreeViewBase( QWidget *parent ) : QTreeView( parent ), m_arrowKeyNavigation( true ), m_acceptDropsOnView( false ), m_readWrite( false ), m_handleDrag(true) { m_dragPixmap = koIcon("application-x-vnd.kde.plan").pixmap(32); setDefaultDropAction( Qt::MoveAction ); setItemDelegate( new ItemDelegate( this ) ); setAlternatingRowColors ( true ); setExpandsOnDoubleClick(false); header()->setContextMenuPolicy( Qt::CustomContextMenu ); connect( header(), &QWidget::customContextMenuRequested, this, &TreeViewBase::slotHeaderContextMenuRequested ); } void TreeViewBase::dropEvent( QDropEvent *e ) { debugPlan; QTreeView::dropEvent( e ); } KoPrintJob * TreeViewBase::createPrintJob( ViewBase *parent ) { TreeViewPrintingDialog *dia = new TreeViewPrintingDialog( parent, this, parent->project() ); dia->printer().setCreator( QString( "Plan %1" ).arg( PLAN_VERSION_STRING ) ); // dia->printer().setFullPage(true); // ignore printer margins return dia; } void TreeViewBase::setReadWrite( bool rw ) { m_readWrite = rw; if ( model() ) { model()->setData( QModelIndex(), rw, Role::ReadWrite ); } } void TreeViewBase::createItemDelegates( ItemModelBase *model ) { for ( int c = 0; c < model->columnCount(); ++c ) { QAbstractItemDelegate *delegate = model->createDelegate( c, this ); if ( delegate ) { setItemDelegateForColumn( c, delegate ); } } } void TreeViewBase::slotHeaderContextMenuRequested( const QPoint& pos ) { //debugPlan; emit headerContextMenuRequested( header()->mapToGlobal( pos ) ); } void TreeViewBase::setColumnsHidden( const QList &lst ) { //debugPlan< xlst; foreach ( int c, lst ) { if ( c == -1 ) { // hide rest for ( int i = prev+1; i < model()->columnCount(); ++i ) { if ( ! lst.contains( i ) ) { xlst << i; } } break; } xlst << c; prev = c; } for ( int c = 0; c < model()->columnCount(); ++c ) { setColumnHidden( c, xlst.contains( c ) ); } } QModelIndex TreeViewBase::firstColumn( int row, const QModelIndex &parent ) { int s; for ( s = 0; s < header()->count(); ++s ) { if ( ! header()->isSectionHidden( header()->logicalIndex( s ) ) ) { break; } } if ( s == -1 ) { return QModelIndex(); } return model()->index( row, header()->logicalIndex( s ), parent ); } QModelIndex TreeViewBase::lastColumn( int row, const QModelIndex &parent ) { int s; for ( s = header()->count() - 1; s >= 0; --s ) { if ( ! header()->isSectionHidden( header()->logicalIndex( s ) ) ) { break; } } if ( s == -1 ) { return QModelIndex(); } return model()->index( row, header()->logicalIndex( s ), parent ); } QModelIndex TreeViewBase::nextColumn( const QModelIndex &curr ) { return moveCursor( curr, QAbstractItemView::MoveRight ); } QModelIndex TreeViewBase::previousColumn( const QModelIndex &curr ) { return moveCursor( curr, QAbstractItemView::MoveLeft ); } QModelIndex TreeViewBase::firstEditable( int row, const QModelIndex &parent ) { QModelIndex index = firstColumn( row, parent ); if ( model()->flags( index ) & Qt::ItemIsEditable ) { return index; } return moveToEditable( index, QAbstractItemView::MoveRight ); } QModelIndex TreeViewBase::lastEditable( int row, const QModelIndex &parent ) { QModelIndex index = lastColumn( row, parent ); if ( model()->flags( index ) & Qt::ItemIsEditable ) { return index; } return moveToEditable( index, QAbstractItemView::MoveLeft ); } // Reimplemented to fix qt bug 160083: Doesn't scroll horizontally. void TreeViewBase::scrollTo(const QModelIndex &index, ScrollHint hint) { //debugPlan<width(); int horizontalOffset = header()->offset(); int horizontalPosition = header()->sectionPosition(index.column()); int cellWidth = header()->sectionSize(index.column()); if (hint == PositionAtCenter) { horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2)); } else { if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth) horizontalScrollBar()->setValue(horizontalPosition); else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth) horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth); } } void TreeViewBase::focusInEvent(QFocusEvent *event) { //debugPlan<reason(); QAbstractScrollArea::focusInEvent(event); //NOTE: not QTreeView if ( event->reason() == Qt::MouseFocusReason ) { return; } QModelIndex curr = currentIndex(); if ( ! curr.isValid() || ! isIndexHidden( curr ) ) { return; } QModelIndex idx = curr; for ( int s = 0; s < header()->count(); ++s) { idx = model()->index( curr.row(), header()->logicalIndex( s ), curr.parent() ); if ( ! isIndexHidden( idx ) ) { selectionModel()->setCurrentIndex(idx, QItemSelectionModel::NoUpdate); scrollTo( idx ); break; } } } /*! \reimp */ void TreeViewBase::keyPressEvent(QKeyEvent *event) { //debugPlan<key()<<","<key()) { case Qt::Key_Right: { QModelIndex nxt = moveCursor( MoveRight, Qt::NoModifier ); if ( nxt.isValid() ) { selectionModel()->setCurrentIndex( nxt, QItemSelectionModel::NoUpdate ); } else { emit moveAfterLastColumn( current ); } event->accept(); return; break; } case Qt::Key_Left: { QModelIndex prv = moveCursor( MoveLeft, Qt::NoModifier ); if ( prv.isValid() ) { selectionModel()->setCurrentIndex( prv, QItemSelectionModel::NoUpdate ); } else { emit moveBeforeFirstColumn( current ); } event->accept(); return; break; } case Qt::Key_Down: { QModelIndex i = moveCursor( MoveDown, Qt::NoModifier ); updateSelection( current, i, event ); event->accept(); return; break; } case Qt::Key_Up: { QModelIndex i = moveCursor( MoveUp, Qt::NoModifier ); updateSelection( current, i, event ); event->accept(); return; break; } default: break; } } QTreeView::keyPressEvent(event); } void TreeViewBase::updateSelection( const QModelIndex &oldidx, const QModelIndex &newidx, QKeyEvent *event ) { if ( newidx == oldidx || ! newidx.isValid() ) { return; } if ( !hasFocus() && QApplication::focusWidget() == indexWidget(oldidx) ) { setFocus(); } QItemSelectionModel::SelectionFlags command; // NoUpdate on Key movement and Ctrl Qt::KeyboardModifiers modifiers = static_cast(event)->modifiers(); switch (static_cast(event)->key()) { case Qt::Key_Backtab: modifiers = modifiers & ~Qt::ShiftModifier; // special case for backtab Q_FALLTHROUGH(); case Qt::Key_Down: case Qt::Key_Up: case Qt::Key_Left: case Qt::Key_Right: if (modifiers & Qt::ControlModifier) command = QItemSelectionModel::NoUpdate; else if (modifiers & Qt::ShiftModifier) command = QItemSelectionModel::Select | selectionBehaviorFlags(); else command = QItemSelectionModel::ClearAndSelect | selectionBehaviorFlags(); break; default: break; } selectionModel()->setCurrentIndex( newidx, command ); } void TreeViewBase::mousePressEvent(QMouseEvent *event) { // If the mouse is pressed outside any item, the current item should be/remain selected QPoint pos = event->pos(); QModelIndex index = indexAt(pos); debugPlan<pos()<currentIndex(); if ( index.isValid() && ! selectionModel()->isSelected( index ) ) { pos = visualRect( index ).center(); QMouseEvent e( event->type(), pos, mapToGlobal( pos ), event->button(), event->buttons(), event->modifiers() ); QTreeView::mousePressEvent( &e ); event->setAccepted( e.isAccepted() ); debugPlan<( sender() ); if ( delegate == 0 ) { warnPlan<<"Not a KPlato::ItemDelegate, try standard treatment"<endEditHint(); // Close editor, do nothing else QTreeView::closeEditor( editor, QAbstractItemDelegate::NoHint ); QModelIndex index; switch ( endHint ) { case Delegate::EditLeftItem: index = moveToEditable( currentIndex(), MoveLeft ); break; case Delegate::EditRightItem: index = moveToEditable( currentIndex(), MoveRight ); break; case Delegate::EditDownItem: index = moveToEditable( currentIndex(), MoveDown ); break; case Delegate::EditUpItem: index = moveToEditable( currentIndex(), MoveUp ); break; default: //debugPlan<<"Standard treatment"<setCurrentIndex(persistent, flags); // currentChanged signal would have already started editing if (!(editTriggers() & QAbstractItemView::CurrentChanged)) { edit(persistent); } } } QModelIndex TreeViewBase::moveToEditable( const QModelIndex &index, CursorAction cursorAction ) { QModelIndex ix = index; do { ix = moveCursor( ix, cursorAction ); } while ( ix.isValid() && ! ( model()->flags( ix ) & Qt::ItemIsEditable ) ); //debugPlan<= model()->columnCount(ix.parent()) ) { //debugPlan<columnCount(ix.parent())<index( ix.row(), col, ix.parent() ); } // else Here we could go to the top return ix; } case MoveUp: { // TODO: span // Fetch the index above current. // This should be the previous non-hidden row, same column as current, // that has a column in current.column() ix = indexAbove( current ); while ( ix.isValid() && col >= model()->columnCount(ix.parent()) ) { ix = indexAbove( ix ); } if ( ix.isValid() ) { ix = model()->index( ix.row(), col, ix.parent() ); } // else Here we could go to the bottom return ix; } case MovePrevious: case MoveLeft: { for ( int s = header()->visualIndex( col ) - 1; s >= 0; --s ) { if ( ! header()->isSectionHidden( header()->logicalIndex( s ) ) ) { ix = model()->index( current.row(), header()->logicalIndex( s ), current.parent() ); break; } } return ix; } case MoveNext: case MoveRight: { for ( int s = header()->visualIndex( col ) + 1; s < header()->count(); ++s ) { if ( ! header()->isSectionHidden( header()->logicalIndex( s ) ) ) { ix = model()->index( current.row(), header()->logicalIndex( s ), current.parent() ); break; } } return ix; } case MovePageUp: case MovePageDown: { ix = QTreeView::moveCursor( cursorAction, modifiers ); // Now we are at the correct row, so move to correct column if ( ix.isValid() ) { ix = model()->index( ix.row(), col, ix.parent() ); } // else Here we could go to the bottom return ix; } case MoveHome: { if ( ( modifiers & Qt::ControlModifier ) == 0 ) { ix = QTreeView::moveCursor( cursorAction, modifiers ); // move to first row } else { //stay at this row ix = current; } for ( int s = 0; s < header()->count(); ++s ) { int logicalIndex = header()->logicalIndex( s ); if ( ! isColumnHidden( logicalIndex ) ) { ix = model()->index( ix.row(), header()->logicalIndex( s ), ix.parent() ); break; } } return ix; } case MoveEnd: { if ( ( modifiers & Qt::ControlModifier ) == 0 ) { ix = QTreeView::moveCursor( cursorAction, modifiers ); // move to last row } else { //stay at this row ix = current; } for ( int s = header()->count() - 1; s >= 0; --s ) { int logicalIndex = header()->logicalIndex( s ); if ( ! isColumnHidden( logicalIndex ) ) { ix = model()->index( ix.row(), logicalIndex, ix.parent() ); break; } } return ix; } default: break; } return ix; } void TreeViewBase::contextMenuEvent ( QContextMenuEvent *event ) { debugPlan<selectedRows(); emit contextMenuRequested( indexAt(event->pos()), event->globalPos(), selectionModel()->selectedRows() ); } void TreeViewBase::slotCurrentChanged( const QModelIndex ¤t, const QModelIndex & ) { if ( current.isValid() ) { scrollTo( current ); } } void TreeViewBase::setModel( QAbstractItemModel *model ) { if ( selectionModel() ) { disconnect( selectionModel(), &QItemSelectionModel::currentChanged, this, &TreeViewBase::slotCurrentChanged ); } QTreeView::setModel( model ); if ( selectionModel() ) { connect( selectionModel(), &QItemSelectionModel::currentChanged, this, &TreeViewBase::slotCurrentChanged ); } setReadWrite( m_readWrite ); } void TreeViewBase::setSelectionModel( QItemSelectionModel *model ) { if ( selectionModel() ) { disconnect( selectionModel(), &QItemSelectionModel::currentChanged, this, &TreeViewBase::slotCurrentChanged ); } QTreeView::setSelectionModel( model ); if ( selectionModel() ) { connect( selectionModel(), &QItemSelectionModel::currentChanged, this, &TreeViewBase::slotCurrentChanged ); } } void TreeViewBase::setStretchLastSection( bool mode ) { header()->setStretchLastSection( mode ); } void TreeViewBase::mapToSection( int col, int section ) { header()->moveSection( header()->visualIndex( col ), section ); } int TreeViewBase::section( int col ) const { return header()->visualIndex( col ); } void TreeViewBase::dragMoveEvent(QDragMoveEvent *event) { //debugPlan; if (dragDropMode() == InternalMove && (event->source() != this || !(event->possibleActions() & Qt::MoveAction))) { //debugPlan<<"Internal:"<isAccepted(); return; } QTreeView::dragMoveEvent( event ); if ( dropIndicatorPosition() == QAbstractItemView::OnViewport ) { if ( ! m_acceptDropsOnView ) { event->ignore(); } debugPlan<<"On viewport:"<isAccepted(); } else { QModelIndex index = indexAt( event->pos() ); - if ( index.isValid() ) { + if ( index.isValid() && m_readWrite) { emit dropAllowed( index, dropIndicatorPosition(), event ); } else { event->ignore(); debugPlan<<"Invalid index:"<isAccepted(); } } if ( event->isAccepted() ) { if ( viewport()->cursor().shape() == Qt::ForbiddenCursor ) { viewport()->unsetCursor(); } } else if ( viewport()->cursor().shape() != Qt::ForbiddenCursor ) { viewport()->setCursor( Qt::ForbiddenCursor ); } debugPlan<isAccepted()<cursor().shape(); } QModelIndex TreeViewBase::firstVisibleIndex( const QModelIndex &idx ) const { int count = model()->columnCount(); for ( int c = 0; c < count; ++c ) { if ( ! isColumnHidden( c ) ) { return model()->index( idx.row(), c, model()->parent( idx ) ); } } return QModelIndex(); } bool TreeViewBase::loadContext( const QMetaEnum &map, const KoXmlElement &element, bool expand ) { debugPlan<setStretchLastSection( (bool)( element.attribute( "stretch-last-column", "1" ).toInt() ) ); KoXmlElement e = element.namedItem( "columns" ).toElement(); if ( ! e.isNull() ) { if ( ! map.isValid() ) { // try numbers debugPlan<<"invalid map"; for ( int i = model()->columnCount() - 1; i >= 0; --i ) { QString s = e.attribute( QString( "column-%1" ).arg( i ), "" ); if ( s == "hidden" ) { hideColumn( i ); } else if ( s == "shown" ) { showColumn( i ); } else debugPlan<columnCount() - 1; i >= 0; --i ) { QString n = map.key( i ); //debugPlan<count(); ++i ) { if ( e.hasAttribute( s.arg( i ) ) ) { int index = e.attribute( s.arg( i ), "-1" ).toInt(); if ( index >= 0 && index < h->count() ) { header()->moveSection( h->visualIndex( index ), i ); } } } } else { QMap m; // QMap for ( int i = 0; i < h->count(); ++i ) { QString n = e.attribute( s.arg( i ) ); if ( n.isEmpty() ) { continue; } int col = map.keyToValue( n.toUtf8() ); if ( col >= 0 && col < h->count() ) { m.insert( i, col ); } } for ( QMap::const_iterator it = m.constBegin(); it != m.constEnd(); ++it ) { QString n = e.attribute( s.arg( it.key() ) ); int current = h->visualIndex( it.value() ); header()->moveSection( current, it.key() ); } } } if (expand) { loadExpanded(element); } if (!e.isNull()) { // FIXME: This only works for column 0 QHeaderView *h = header(); QString s("size-%1"); for (int i = 0; i < model()->columnCount(); ++i) { if (!h->isSectionHidden(i) && e.hasAttribute(s.arg(i))) { int size = e.attribute(s.arg(i)).toInt(); if (size > 0) { h->resizeSection(i, size); } } } } return true; } void TreeViewBase::saveContext( const QMetaEnum &map, QDomElement &element, bool expand ) const { //debugPlan<stretchLastSection()) ); QDomElement e = element.ownerDocument().createElement( "columns" ); element.appendChild( e ); for ( int i = 0; i < model()->columnCount(); ++i ) { bool h = isColumnHidden( i ); if ( ! map.isValid() ) { debugPlan<<"invalid map"; e.setAttribute( QString( "column-%1" ).arg( i ), h ? "hidden" : "shown" ); } else { QString n = map.key( i ); //debugPlan<count(); ++i ) { if ( ! isColumnHidden( h->logicalIndex( i ) ) ) { if ( ! map.isValid() ) { e.setAttribute( QString( "section-%1" ).arg( i ), h->logicalIndex( i ) ); } else { QString n = map.key( h->logicalIndex( i ) ); if ( ! n.isEmpty() ) { e.setAttribute( QString( "section-%1" ).arg( i ), n ); e.setAttribute( QString("size-%1").arg(i), h->sectionSize(h->logicalIndex(i))); } } } } if (expand) { QDomElement expanded = element.ownerDocument().createElement("expanded"); element.appendChild(expanded); saveExpanded(expanded); } } ItemModelBase *TreeViewBase::itemModel() const { QAbstractItemModel *m = model(); QAbstractProxyModel *p = qobject_cast( m ); while ( p ) { m = p->sourceModel(); p = qobject_cast( m ); } return qobject_cast( m ); } void TreeViewBase::expandRecursive(const QModelIndex &idx, bool xpand) { int rowCount = model()->rowCount(idx); if (rowCount == 0) { return; } xpand ? expand(idx) : collapse(idx); for (int r = 0; r < rowCount; ++r) { QModelIndex i = model()->index(r, 0, idx); Q_ASSERT(i.isValid()); expandRecursive(i, xpand); } } void TreeViewBase::slotExpand() { // NOTE: Do not use this, KGantt does not like it // if (!m_contextMenuIndex.isValid()) { // expandAll(); // return; // } QModelIndex idx = m_contextMenuIndex; if (idx.column() > 0) { idx = idx.model()->index(idx.row(), idx.column(), idx.parent()); } expandRecursive(idx, true); } void TreeViewBase::slotCollapse() { // NOTE: Do not use this, KGantt does not like it // if (!m_contextMenuIndex.isValid()) { // collapseAll(); // return; // } QModelIndex idx = m_contextMenuIndex; if (idx.column() > 0) { idx = idx.model()->index(idx.row(), 0, idx.parent()); } expandRecursive(idx, false); } void TreeViewBase::setContextMenuIndex(const QModelIndex &idx) { m_contextMenuIndex = idx; } void TreeViewBase::loadExpanded(const KoXmlElement &element) { // we get here on loadContext() m_loadContextDoc.clear(); KoXmlElement expanded = element.namedItem("expanded").toElement(); if (expanded.isNull()) { return; } KoXml::asQDomElement(m_loadContextDoc, expanded); // FIXME: // if data is dependent on schedule manger // we cannot do anything until schedulemanger is set, // so we wait a bit and hope everything is ok QTimer::singleShot(500, this, &TreeViewBase::doContextExpanded); } void TreeViewBase::expandRecursivly(QDomElement element, const QModelIndex &parent) { if (element.isNull()) { return; } for(QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); if (e.tagName() != "item") { continue; } int childRow = e.attribute("row", "-1").toInt(); if (childRow > -1) { QModelIndex idx = model()->index(childRow, 0, parent); if (idx.isValid()) { setExpanded(idx, true); expandRecursivly(e, idx); } } } } void TreeViewBase::doExpand(QDomDocument &doc) { // we get here on setScheduleManager() m_expandDoc = doc; QTimer::singleShot(0, this, &TreeViewBase::doExpanded); } void TreeViewBase::doContextExpanded() { expandRecursivly(m_loadContextDoc.documentElement()); } void TreeViewBase::doExpanded() { expandRecursivly(m_expandDoc.documentElement()); } void TreeViewBase::saveExpanded(QDomElement &element, const QModelIndex &parent) const { for (int r = 0; r < model()->rowCount(parent); ++r) { QModelIndex idx = model()->index(r, 0, parent); if (isExpanded(idx)) { QDomElement e = element.ownerDocument().createElement("item"); e.setAttribute("row", r); element.appendChild(e); saveExpanded(e, idx); } } } void TreeViewBase::setHandleDrag(bool state) { m_handleDrag = state; } void TreeViewBase::startDrag(Qt::DropActions supportedActions) { Qt::DropAction defaultDropAction = Qt::IgnoreAction; if (this->defaultDropAction() != Qt::IgnoreAction && (supportedActions & this->defaultDropAction())) { defaultDropAction = this->defaultDropAction(); } else if (supportedActions & Qt::CopyAction && dragDropMode() != QAbstractItemView::InternalMove) { defaultDropAction = Qt::CopyAction; } if (m_handleDrag) { QMimeData *data = mimeData(); if (!data) { debugPlan<<"No mimedata"; return; } QDrag *drag = new QDrag(this); drag->setPixmap(m_dragPixmap); drag->setMimeData(data); drag->exec(supportedActions, defaultDropAction); } else { static_cast(parent())->handleDrag(supportedActions, defaultDropAction); } } QList TreeViewBase::visualColumns() const { if (!isVisible()) { return QList(); } QMap columns; for (int i = 0; i < model()->columnCount(); ++i) { if (!isColumnHidden(i)) { columns.insert(header()->visualIndex(i), i); } } return columns.values(); } void TreeViewBase::setDragPixmap(const QPixmap &pixmap) { m_dragPixmap = pixmap; } QPixmap TreeViewBase::dragPixmap() const { return m_dragPixmap; } QMimeData *TreeViewBase::mimeData() const { QModelIndexList rows = selectionModel()->selectedRows(); sort(this, rows); if (rows.isEmpty()) { debugPlan<<"No rows selected"; return nullptr; } QList columns;; columns = visualColumns(); QModelIndexList indexes; for (int r = 0; r < rows.count(); ++r) { int row = rows.at(r).row(); const QModelIndex &parent = rows.at(r).parent(); for (int i = 0; i < columns.count(); ++i) { indexes << model()->index(row, columns.at(i), parent); } } return model()->mimeData(indexes); } void TreeViewBase::editCopy() { QMimeData *data = mimeData(); if (!data) { debugPlan<<"No mimedata"; return; } QClipboard *clipboard = QGuiApplication::clipboard(); clipboard->setMimeData(data); } void TreeViewBase::editPaste() { } QModelIndexList TreeViewBase::selectedIndexes() const { QModelIndexList viewSelected; QModelIndexList modelSelected; if (selectionModel()) modelSelected = selectionModel()->selectedIndexes(); for (int i = 0; i < modelSelected.count(); ++i) { // check that neither the parents nor the index is hidden before we add QModelIndex index = modelSelected.at(i); while (index.isValid() && !isIndexHidden(index)) { int column = index.column(); index = index.parent(); if (index.isValid() && column != index.column()) { index = index.sibling(index.row(), column); } } if (index.isValid()) continue; viewSelected.append(modelSelected.at(i)); } return viewSelected; } //---------------------- DoubleTreeViewPrintingDialog::DoubleTreeViewPrintingDialog( ViewBase *view, DoubleTreeViewBase *treeview, Project *project ) : PrintingDialog( view ), m_tree( treeview ), m_project( project ), m_firstRow( -1 ) { printer().setFromTo( documentFirstPage(), documentLastPage() ); } int DoubleTreeViewPrintingDialog::documentLastPage() const { debugPlan<pageLayout().format ); int page = documentFirstPage(); while ( firstRow( page ) != -1 ) { ++page; } if ( page > documentFirstPage() ) { --page; } return page; } int DoubleTreeViewPrintingDialog::firstRow( int page ) const { debugPlan<masterView()->header(); QHeaderView *sh = m_tree->slaveView()->header(); int height = mh->height() > sh->height() ? mh->height() : sh->height(); int hHeight = headerRect().height(); int fHeight = footerRect().height(); QRect pageRect = const_cast( this )->printer().pageRect(); int gap = 8; int pageHeight = pageRect.height() - height; if ( hHeight > 0 ) { pageHeight -= ( hHeight + gap ); } if ( fHeight > 0 ) { pageHeight -= ( fHeight + gap ); } int rowsPrPage = pageHeight / height; debugPlan<<"rowsPrPage"< 0 ); int rows = m_tree->model()->rowCount(); int row = -1; for ( int i = 0; i < rows; ++i ) { if ( ! m_tree->masterView()->isRowHidden( i, QModelIndex() ) ) { row = i; break; } } if ( row != -1 ) { QModelIndex idx = m_tree->model()->index( row, 0, QModelIndex() ); row = 0; while ( idx.isValid() ) { if ( row >= rowsPrPage * pageNumber ) { debugPlan<masterView()->indexBelow( idx ); } if ( ! idx.isValid() ) { row = -1; } } debugPlan<<"Page"< DoubleTreeViewPrintingDialog::createOptionWidgets() const { QList lst; lst << createPageLayoutWidget(); lst += PrintingDialog::createOptionWidgets(); return lst; } void DoubleTreeViewPrintingDialog::printPage( int page, QPainter &painter ) { debugPlan<pageLayout() ); qreal t, l, b, r; printer().getPageMargins( &l, &t, &r, &b, QPrinter::Point ); debugPlan<masterView()->header(); QHeaderView *sh = m_tree->slaveView()->header(); int length = mh->length() + sh->length(); int height = mh->height() > sh->height() ? mh->height() : sh->height(); QRect hRect = headerRect(); QRect fRect = footerRect(); QRect pageRect = printer().pageRect(); pageRect.moveTo( 0, 0 ); QRect paperRect = printer().paperRect(); QAbstractItemModel *model = m_tree->model(); Q_ASSERT( model != 0 ); debugPlan< length ? 1.0 : (double)pageRect.width() / (double)length; double sy = 1.0; painter.scale( sx, sy ); int h = 0; painter.translate( 0, hRect.height() + gap ); h = hRect.height() + gap; painter.setPen(Qt::black); painter.setBrush( Qt::lightGray ); int higestIndex = 0; int rightpos = 0; for ( int i = 0; i < mh->count(); ++i ) { QString text = model->headerData( i, Qt::Horizontal ).toString(); QVariant a = model->headerData( i, Qt::Horizontal, Qt::TextAlignmentRole ); int align = a.isValid() ? a.toInt() : (int)(Qt::AlignLeft|Qt::AlignVCenter); if ( ! mh->isSectionHidden( i ) ) { QRect r = QRect( mh->sectionPosition( i ), 0, mh->sectionSize( i ), height ).adjusted(0, 0, 0, -painter.pen().width()); if (rightpos < r.right()) { higestIndex = i; rightpos = r.right(); } painter.drawRect( r ); // FIXME There is a bug somewhere, the text somehow overwites the rect outline for the first column! painter.save(); painter.setBrush(QBrush()); painter.drawText( r.adjusted(3, 1, -3, -1), align, text ); painter.drawRect( r ); painter.restore(); } if ( ! sh->isSectionHidden( i ) ) { QRect r = QRect( sh->sectionPosition( i ) + mh->length(), 0, sh->sectionSize( i ), height ).adjusted(0, 0, 0, -painter.pen().width()); if (rightpos < r.right()) { higestIndex = i; rightpos = r.right(); } painter.drawRect( r ); painter.drawText( r.adjusted(3, 1, -3, -1), align, text ); } //debugPlan<isSectionHidden( i )<sectionPosition( i ); } if ( m_firstRow == -1 || model->rowCount() == 0 ) { debugPlan<<"No data"; painter.restore(); return; } painter.setBrush( QBrush() ); QModelIndex idx = model->index( 0, 0 ); for ( int r = 0; r < m_firstRow && idx.isValid(); ++r ) { idx = m_tree->masterView()->indexBelow( idx ); } int numRows = 0; //debugPlan<count(); ++i ) { if ( mh->isSectionHidden( i ) && sh->isSectionHidden( i ) ) { continue; } Qt::Edges edges = Qt::BottomEdge | Qt::LeftEdge; QModelIndex index = model->index( idx.row(), i, idx.parent() ); QString text = model->data( index ).toString(); QVariant a = model->data( index, Qt::TextAlignmentRole ); int align = a.isValid() ? a.toInt() : (int)(Qt::AlignLeft|Qt::AlignVCenter); if ( ! mh->isSectionHidden( i ) ) { QRect r( mh->sectionPosition( i ), 0, mh->sectionSize( i ), height ); if (higestIndex == i) { edges |= Qt::RightEdge; r.adjust(0, 0, 1, 0); } drawRect( painter, r, edges ); painter.drawText( r.adjusted(3, 1, -3, -1) , align, text ); } if ( ! sh->isSectionHidden( i ) ) { QRect r( sh->sectionPosition( i ) + mh->length(), 0, sh->sectionSize( i ), height ); if (higestIndex == i) { edges |= Qt::RightEdge; r.adjust(0, 0, 1, 0); } drawRect( painter, r, edges ); painter.drawText( r.adjusted(3, 1, -3, -1), align, text ); } } ++numRows; idx = m_tree->masterView()->indexBelow( idx ); } painter.restore(); } /** * DoubleTreeViewBase is a QSplitter containing two treeviews. * This makes it possible to keep columns visible in one view when scrolling the other view horizontally. */ DoubleTreeViewBase::DoubleTreeViewBase( bool /*mode*/, QWidget *parent ) : QSplitter( parent ), m_rightview( 0 ), m_selectionmodel( 0 ), m_readWrite( false ), m_mode( false ) { init(); } DoubleTreeViewBase::DoubleTreeViewBase( QWidget *parent ) : QSplitter( parent ), m_rightview( 0 ), m_selectionmodel( 0 ), m_mode( false ) { init(); } DoubleTreeViewBase::~DoubleTreeViewBase() { } KoPrintJob *DoubleTreeViewBase::createPrintJob( ViewBase *parent ) { DoubleTreeViewPrintingDialog *dia = new DoubleTreeViewPrintingDialog( parent, this, parent->project() ); dia->printer().setCreator( QString( "Plan %1" ).arg( PLAN_VERSION_STRING ) ); // dia->printer().setFullPage(true); // ignore printer margins return dia; } void DoubleTreeViewBase::slotExpand() { m_leftview->slotExpand(); } void DoubleTreeViewBase::slotCollapse() { m_leftview->slotCollapse(); } void DoubleTreeViewBase::setParentsExpanded( const QModelIndex &idx, bool expanded ) { //debugPlan<isExpanded( idx )<isExpanded( idx ); QModelIndex p = model()->parent( idx ); QList lst; while ( p.isValid() ) { lst << p; p = model()->parent( p ); } while ( ! lst.isEmpty() ) { p = lst.takeLast(); m_leftview->setExpanded( p, expanded ); m_rightview->setExpanded( m_rightview->firstVisibleIndex( p ), expanded ); //HACK: qt can't handle that column 0 is hidden! //debugPlan<isExpanded( p )<isExpanded( p ); } } void DoubleTreeViewBase::init() { setOrientation( Qt::Horizontal ); setHandleWidth( 3 ); m_leftview = new TreeViewBase(this); m_leftview->setObjectName("Left view"); m_leftview->setHandleDrag(false); addWidget( m_leftview ); setStretchFactor( 0, 1 ); m_rightview = new TreeViewBase(this); m_rightview->setObjectName("Right view"); m_rightview->setHandleDrag(false); addWidget( m_rightview ); setStretchFactor( 1, 1 ); m_leftview->setTreePosition(-1); // always visual index 0 connect( m_leftview, &TreeViewBase::contextMenuRequested, this, &DoubleTreeViewBase::contextMenuRequested ); connect( m_leftview, &TreeViewBase::headerContextMenuRequested, this, &DoubleTreeViewBase::slotLeftHeaderContextMenuRequested ); connect( m_rightview, &TreeViewBase::contextMenuRequested, this, &DoubleTreeViewBase::contextMenuRequested ); connect( m_rightview, &TreeViewBase::headerContextMenuRequested, this, &DoubleTreeViewBase::slotRightHeaderContextMenuRequested ); connect( m_leftview->verticalScrollBar(), &QAbstractSlider::valueChanged, m_rightview->verticalScrollBar(), &QAbstractSlider::setValue ); connect( m_rightview->verticalScrollBar(), &QAbstractSlider::valueChanged, m_leftview->verticalScrollBar(), &QAbstractSlider::setValue ); connect( m_leftview, &TreeViewBase::moveAfterLastColumn, this, &DoubleTreeViewBase::slotToRightView ); connect( m_rightview, &TreeViewBase::moveBeforeFirstColumn, this, &DoubleTreeViewBase::slotToLeftView ); connect( m_leftview, &TreeViewBase::editAfterLastColumn, this, &DoubleTreeViewBase::slotEditToRightView ); connect( m_rightview, &TreeViewBase::editBeforeFirstColumn, this, &DoubleTreeViewBase::slotEditToLeftView ); connect( m_leftview, &QTreeView::expanded, m_rightview, &QTreeView::expand ); connect( m_leftview, &QTreeView::collapsed, m_rightview, &QTreeView::collapse ); connect( m_rightview, &QTreeView::expanded, m_leftview, &QTreeView::expand ); connect( m_rightview, &QTreeView::collapsed, m_leftview, &QTreeView::collapse ); connect( m_leftview, &TreeViewBase::dropAllowed, this, &DoubleTreeViewBase::dropAllowed ); connect( m_rightview, &TreeViewBase::dropAllowed, this, &DoubleTreeViewBase::dropAllowed ); m_actionSplitView = new QAction(koIcon("view-split-left-right"), QString(), this); m_actionSplitView->setObjectName("split_view"); setViewSplitMode( true ); connect( m_leftview->header(), &QHeaderView::sortIndicatorChanged, this, &DoubleTreeViewBase::slotLeftSortIndicatorChanged ); connect( m_rightview->header(), &QHeaderView::sortIndicatorChanged, this, &DoubleTreeViewBase::slotRightSortIndicatorChanged ); } void DoubleTreeViewBase::slotLeftSortIndicatorChanged( int logicalIndex, Qt::SortOrder /*order*/ ) { QSortFilterProxyModel *sf = qobject_cast( model() ); if ( sf ) { ItemModelBase *m = m_rightview->itemModel(); if ( m ) { sf->setSortRole( m->sortRole( logicalIndex ) ); } } m_leftview->header()->setSortIndicatorShown( true ); // sorting controlled by left treeview, turn right off m_rightview->header()->setSortIndicatorShown( false ); } void DoubleTreeViewBase::slotRightSortIndicatorChanged( int logicalIndex, Qt::SortOrder /*order*/ ) { QSortFilterProxyModel *sf = qobject_cast( model() ); if ( sf ) { ItemModelBase *m = m_rightview->itemModel(); if ( m ) { sf->setSortRole( m->sortRole( logicalIndex ) ); } } m_rightview->header()->setSortIndicatorShown( true ); // sorting controlled by right treeview, turn left off m_leftview->header()->setSortIndicatorShown( false ); } QList DoubleTreeViewBase::expandColumnList( const QList &lst ) const { QList mlst = lst; if ( ! mlst.isEmpty() ) { int v = 0; if ( mlst.last() == -1 && mlst.count() > 1 ) { v = mlst[ mlst.count() - 2 ] + 1; mlst.removeLast(); } for ( int c = v; c < model()->columnCount(); ++c ) { mlst << c; } } return mlst; } void DoubleTreeViewBase::hideColumns( TreeViewBase *view, const QList &list ) { view->setColumnsHidden( list ); } void DoubleTreeViewBase::hideColumns( const QList &masterList, const QList &slaveList ) { m_leftview->setColumnsHidden( masterList ); m_rightview->setColumnsHidden( slaveList ); if ( m_rightview->isHidden() ) { QList mlst = expandColumnList( masterList ); QList slst = expandColumnList( slaveList ); QList lst; for ( int c = 0; c < model()->columnCount(); ++c ) { // only hide columns hidden in *both* views //debugPlan<= 0) && (slst.indexOf( c ) >= 0) ) { lst << c; } } //debugPlan<setColumnsHidden( lst ); } else { setStretchFactors(); } } void DoubleTreeViewBase::slotToRightView( const QModelIndex &index ) { //debugPlan<firstColumn( index.row(), model()->parent( index ) ); m_rightview->setFocus(); if ( nxt.isValid() ) { m_selectionmodel->setCurrentIndex( nxt, QItemSelectionModel::NoUpdate ); } } void DoubleTreeViewBase::slotToLeftView( const QModelIndex &index ) { //debugPlan<lastColumn( index.row(), model()->parent( index ) ); m_leftview->setFocus(); if ( prv.isValid() ) { m_selectionmodel->setCurrentIndex( prv, QItemSelectionModel::NoUpdate ); } } void DoubleTreeViewBase::slotEditToRightView( const QModelIndex &index ) { //debugPlan<isHidden() ) { return; } m_rightview->setFocus(); QModelIndex nxt = m_rightview->firstEditable( index.row(), model()->parent ( index ) ); if ( nxt.isValid() && ( model()->flags( nxt ) & Qt::ItemIsEditable ) ) { m_selectionmodel->setCurrentIndex( nxt, QItemSelectionModel::NoUpdate ); m_rightview->edit( nxt ); } else { slotToRightView( index ); } } void DoubleTreeViewBase::slotEditToLeftView( const QModelIndex &index ) { //debugPlan<isHidden() ) { return; } m_leftview->setFocus(); QModelIndex nxt = m_leftview->lastEditable( index.row(), model()->parent ( index ) ); if ( nxt.isValid() && ( model()->flags( nxt ) & Qt::ItemIsEditable ) ) { m_selectionmodel->setCurrentIndex( nxt, QItemSelectionModel::NoUpdate ); m_leftview->edit( nxt ); } else { slotToLeftView( index ); } } void DoubleTreeViewBase::setReadWrite( bool rw ) { m_readWrite = rw; m_leftview->setReadWrite( rw ); m_rightview->setReadWrite( rw ); } void DoubleTreeViewBase::closePersistentEditor( const QModelIndex &index ) { m_leftview->closePersistentEditor( index ); m_rightview->closePersistentEditor( index ); } void DoubleTreeViewBase::setModel( QAbstractItemModel *model ) { m_leftview->setModel( model ); m_rightview->setModel( model ); if ( m_selectionmodel ) { disconnect( m_selectionmodel, &QItemSelectionModel::selectionChanged, this, &DoubleTreeViewBase::slotSelectionChanged ); disconnect( m_selectionmodel, &QItemSelectionModel::currentChanged, this, &DoubleTreeViewBase::currentChanged ); } m_selectionmodel = m_leftview->selectionModel(); m_rightview->setSelectionModel( m_selectionmodel ); connect( m_selectionmodel, &QItemSelectionModel::selectionChanged, this, &DoubleTreeViewBase::slotSelectionChanged ); connect( m_selectionmodel, &QItemSelectionModel::currentChanged, this, &DoubleTreeViewBase::currentChanged ); setReadWrite( m_readWrite ); } QAbstractItemModel *DoubleTreeViewBase::model() const { return m_leftview->model(); } void DoubleTreeViewBase::slotSelectionChanged( const QItemSelection &sel, const QItemSelection & ) { emit selectionChanged( sel.indexes() ); } void DoubleTreeViewBase::setSelectionModel( QItemSelectionModel *model ) { m_leftview->setSelectionModel( model ); m_rightview->setSelectionModel( model ); } void DoubleTreeViewBase::setSelectionMode( QAbstractItemView::SelectionMode mode ) { m_leftview->setSelectionMode( mode ); m_rightview->setSelectionMode( mode ); } void DoubleTreeViewBase::setSelectionBehavior( QAbstractItemView::SelectionBehavior mode ) { m_leftview->setSelectionBehavior( mode ); m_rightview->setSelectionBehavior( mode ); } void DoubleTreeViewBase::setItemDelegateForColumn( int col, QAbstractItemDelegate * delegate ) { m_leftview->setItemDelegateForColumn( col, delegate ); m_rightview->setItemDelegateForColumn( col, delegate ); } void DoubleTreeViewBase::createItemDelegates( ItemModelBase *model ) { m_leftview->createItemDelegates( model ); m_rightview->createItemDelegates( model ); } void DoubleTreeViewBase::setEditTriggers( QAbstractItemView::EditTriggers mode ) { m_leftview->setEditTriggers( mode ); m_rightview->setEditTriggers( mode ); } QAbstractItemView::EditTriggers DoubleTreeViewBase::editTriggers() const { return m_leftview->editTriggers(); } void DoubleTreeViewBase::setStretchLastSection( bool mode ) { m_rightview->header()->setStretchLastSection( mode ); if ( m_rightview->isHidden() ) { m_leftview->header()->setStretchLastSection( mode ); } } void DoubleTreeViewBase::edit( const QModelIndex &index ) { if ( ! m_leftview->isColumnHidden( index.column() ) ) { m_leftview->edit( index ); } else if ( ! m_rightview->isHidden() && ! m_rightview->isColumnHidden( index.column() ) ) { m_rightview->edit( index ); } } void DoubleTreeViewBase::setDragDropMode( QAbstractItemView::DragDropMode mode ) { m_leftview->setDragDropMode( mode ); m_rightview->setDragDropMode( mode ); } void DoubleTreeViewBase::setDragDropOverwriteMode( bool mode ) { m_leftview->setDragDropOverwriteMode( mode ); m_rightview->setDragDropOverwriteMode( mode ); } void DoubleTreeViewBase::setDropIndicatorShown( bool mode ) { m_leftview->setDropIndicatorShown( mode ); m_rightview->setDropIndicatorShown( mode ); } void DoubleTreeViewBase::setDragEnabled ( bool mode ) { m_leftview->setDragEnabled( mode ); m_rightview->setDragEnabled( mode ); } void DoubleTreeViewBase::setAcceptDrops( bool mode ) { m_leftview->setAcceptDrops( mode ); m_rightview->setAcceptDrops( mode ); } void DoubleTreeViewBase::setAcceptDropsOnView( bool mode ) { m_leftview->setAcceptDropsOnView( mode ); m_rightview->setAcceptDropsOnView( mode ); } void DoubleTreeViewBase::setDefaultDropAction( Qt::DropAction action ) { m_leftview->setDefaultDropAction( action ); m_rightview->setDefaultDropAction( action ); } void DoubleTreeViewBase::slotRightHeaderContextMenuRequested( const QPoint &pos ) { //debugPlan; emit slaveHeaderContextMenuRequested( pos ); emit headerContextMenuRequested( pos ); } void DoubleTreeViewBase::slotLeftHeaderContextMenuRequested( const QPoint &pos ) { //debugPlan; emit masterHeaderContextMenuRequested( pos ); emit headerContextMenuRequested( pos ); } void DoubleTreeViewBase::setStretchFactors() { int lc = m_leftview->header()->count() - m_leftview->header()->hiddenSectionCount(); int rc = m_rightview->header()->count() - m_rightview->header()->hiddenSectionCount(); setStretchFactor( indexOf( m_rightview ), qMax( 1, qMin( 4, rc / qMax( 1, lc ) ) ) ); //debugPlan<loadContext(map, slave, false); } KoXmlElement master = element.namedItem("master").toElement(); if (!master.isNull()) { m_leftview->loadContext(map, master); } return true; } void DoubleTreeViewBase::saveContext( const QMetaEnum &map, QDomElement &element ) const { QDomElement master = element.ownerDocument().createElement( "master" ); element.appendChild(master); m_leftview->saveContext(map, master); QDomElement slave = element.ownerDocument().createElement( "slave" ); element.appendChild(slave); if (m_rightview->isHidden()) { slave.setAttribute("hidden", "true"); } m_rightview->saveContext(map, slave, false); } void DoubleTreeViewBase::setViewSplitMode( bool split ) { if ( split ) { m_actionSplitView->setText( i18n( "Unsplit View" ) ); m_actionSplitView->setIcon(koIcon("view-close")); } else { m_actionSplitView->setText( i18n( "Split View" ) ); m_actionSplitView->setIcon(koIcon("view-split-left-right")); } if ( m_mode == split ) { return; } m_mode = split; if ( split ) { m_leftview->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); m_leftview->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); if ( model() ) { m_rightview->setColumnHidden( 0, true ); m_leftview->resizeColumnToContents( 0 ); for ( int c = 1; c < m_rightview->model()->columnCount(); ++c ) { if ( m_leftview->isColumnHidden( c ) ) { m_rightview->setColumnHidden( c, true ); } else { m_rightview->setColumnHidden( c, false ); m_rightview->mapToSection( c, m_leftview->section( c ) ); m_leftview->setColumnHidden( c, true ); m_rightview->resizeColumnToContents( c ); } } } m_rightview->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); m_rightview->show(); } else { m_rightview->hide(); if ( model() ) { int offset = m_rightview->isColumnHidden( 0 ) ? 1 : 0; for ( int c = 0; c < model()->columnCount(); ++c ) { if ( ! m_rightview->isColumnHidden( c ) ) { m_leftview->setColumnHidden( c, false ); m_leftview->mapToSection( c, m_rightview->section( c ) + offset ); m_leftview->resizeColumnToContents( c ); } } } m_leftview->setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded ); m_leftview->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); } } void DoubleTreeViewBase::setRootIsDecorated ( bool show ) { m_leftview->setRootIsDecorated( show ); m_rightview->setRootIsDecorated( show ); } QModelIndex DoubleTreeViewBase::indexAt( const QPoint &pos ) const { QModelIndex idx = m_leftview->indexAt( pos ); if ( ! idx.isValid() ) { idx = m_rightview->indexAt( pos ); } return idx; } void DoubleTreeViewBase::setContextMenuIndex(const QModelIndex &idx) { m_leftview->setContextMenuIndex(idx); m_rightview->setContextMenuIndex(idx); } QMimeData *DoubleTreeViewBase::mimeData() const { QModelIndexList rows = selectionModel()->selectedRows(); sort(m_leftview, rows); if (rows.isEmpty()) { debugPlan<<"No rows selected"; return 0; } QList columns;; columns = m_leftview->visualColumns() + m_rightview->visualColumns(); QModelIndexList indexes; for (int r = 0; r < rows.count(); ++r) { int row = rows.at(r).row(); const QModelIndex &parent = rows.at(r).parent(); for (int i = 0; i < columns.count(); ++i) { indexes << model()->index(row, columns.at(i), parent); } } return model()->mimeData(indexes); } void DoubleTreeViewBase::handleDrag(Qt::DropActions supportedActions, Qt::DropAction defaultDropAction) { QMimeData *data = mimeData(); if (!data) { debugPlan<<"No mimedata"; return; } QDrag *drag = new QDrag(this); drag->setPixmap(m_leftview->dragPixmap()); drag->setMimeData(data); Qt::DropAction a = drag->exec(supportedActions, defaultDropAction); } void DoubleTreeViewBase::setDragPixmap(const QPixmap &pixmap) { m_leftview->setDragPixmap(pixmap); } QPixmap DoubleTreeViewBase::dragPixmap() const { return m_leftview->dragPixmap(); } void DoubleTreeViewBase::editCopy() { QMimeData *data = mimeData(); if (!data) { debugPlan<<"No mimedata"; return; } QClipboard *clipboard = QGuiApplication::clipboard(); clipboard->setMimeData(data); } void DoubleTreeViewBase::editPaste() { } } // namespace KPlato diff --git a/src/libs/ui/performance/PerformanceStatusViewUi.rc b/src/libs/ui/performance/PerformanceStatusViewUi.rc index 7b2628bc..d8a2d78e 100644 --- a/src/libs/ui/performance/PerformanceStatusViewUi.rc +++ b/src/libs/ui/performance/PerformanceStatusViewUi.rc @@ -1,21 +1,21 @@ - + diff --git a/src/libs/ui/performance/ProjectStatusViewUi.rc b/src/libs/ui/performance/ProjectStatusViewUi.rc index a6fcbe6f..5d30a9a0 100644 --- a/src/libs/ui/performance/ProjectStatusViewUi.rc +++ b/src/libs/ui/performance/ProjectStatusViewUi.rc @@ -1,21 +1,21 @@ - + diff --git a/src/libs/ui/reportsgenerator/ReportsGeneratorView.cpp b/src/libs/ui/reportsgenerator/ReportsGeneratorView.cpp index 7ddbff4e..c6129b45 100644 --- a/src/libs/ui/reportsgenerator/ReportsGeneratorView.cpp +++ b/src/libs/ui/reportsgenerator/ReportsGeneratorView.cpp @@ -1,537 +1,560 @@ /* This file is part of the KDE project Copyright (C) 2017 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "ReportsGeneratorView.h" #include "reportgenerator/ReportGenerator.h" #include "Help.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { #define FULLPATHROLE Qt::UserRole + 123 class TemplateFileDelegate : public QStyledItemDelegate { public: TemplateFileDelegate(QObject *parent); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; QMap files; }; TemplateFileDelegate::TemplateFileDelegate(QObject *parent) : QStyledItemDelegate(parent) { } QWidget *TemplateFileDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option) Q_UNUSED(index); return new QComboBox(parent); } void TemplateFileDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QComboBox *cb = qobject_cast(editor); debugPlan<setEditable(true); cb->addItems(files.keys()); QString file = index.data().toString(); cb->setCurrentText(file); } void TemplateFileDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QComboBox *cb = qobject_cast(editor); debugPlan<currentText(); debugPlan<<"template file:"<setData(index, nfile); if (files.contains(nfile)) { nfile = files[nfile].url(); } model->setData(index, nfile, FULLPATHROLE); model->setData(index, nfile, Qt::ToolTipRole); } } else debugPlan<<" No combo box editor!!"; } class FileItemDelegate : public QStyledItemDelegate { public: FileItemDelegate(QObject *parent); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; QMap files; }; FileItemDelegate::FileItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { } QWidget *FileItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); Q_UNUSED(index); KUrlRequester *u = new KUrlRequester(parent); u->setMode(KFile::File); return u; } void FileItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { KUrlRequester *u = qobject_cast(editor); QString file = index.data().toString(); if (!file.isEmpty()) { u->setUrl(QUrl(file)); } } void FileItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { KUrlRequester *u = qobject_cast(editor); if (u && index.isValid()) { model->setData(index, u->url().url()); } } class FileNameExtensionDelegate : public QStyledItemDelegate { public: FileNameExtensionDelegate(QObject *parent); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; }; FileNameExtensionDelegate::FileNameExtensionDelegate(QObject *parent) : QStyledItemDelegate(parent) { } QWidget *FileNameExtensionDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); Q_UNUSED(index); QComboBox *cb = new QComboBox(parent); for (int i = 0; i < ReportsGeneratorView::addOptions().count(); ++i) { cb->addItem(ReportsGeneratorView::addOptions().at(i), ReportsGeneratorView::addTags().value(i)); } return cb; } void FileNameExtensionDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QComboBox *cb = qobject_cast(editor); if (cb) { int idx = ReportsGeneratorView::addTags().indexOf(index.data(Qt::UserRole).toString()); cb->setCurrentIndex(idx < 0 ? 0 : idx); } } void FileNameExtensionDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QComboBox *cb = qobject_cast(editor); if (cb && index.isValid()) { model->setData(index, cb->currentData(), Qt::UserRole); model->setData(index, cb->currentText()); } } QStringList ReportsGeneratorView::addOptions() { return QStringList() << i18n("Nothing") << i18n("Date") << i18n("Number"); } QStringList ReportsGeneratorView::addTags() { return QStringList() << "Nothing" << "Date" << "Number"; } ReportsGeneratorView::ReportsGeneratorView(KoPart *part, KoDocument *doc, QWidget *parent) : ViewBase(part, doc, parent) { debugPlan<<"----------------- Create ReportsGeneratorView ----------------------"; - setXMLFile("ReportsGeneratorViewUi.rc"); + if (doc && doc->isReadWrite()) { + setXMLFile("ReportsGeneratorViewUi.rc"); + } else { + setXMLFile("ReportsGeneratorViewUi_readonly.rc"); + } QVBoxLayout * l = new QVBoxLayout(this); l->setMargin(0); m_view = new QTreeView(this); QStandardItemModel *m = new QStandardItemModel(m_view); m->setHorizontalHeaderLabels(QStringList() << i18n("Name") << i18n("Report Template") << i18n("Report File") << i18n("Add")); m->setHeaderData(0, Qt::Horizontal, xi18nc("@info:tooltip", "Report name"), Qt::ToolTipRole); m->setHeaderData(1, Qt::Horizontal, xi18nc("@info:tooltip", "Report template file name"), Qt::ToolTipRole); m->setHeaderData(2, Qt::Horizontal, xi18nc("@info:tooltip", "Name of the generated report file"), Qt::ToolTipRole); m->setHeaderData(3, Qt::Horizontal, xi18nc("@info:tooltip", "Information added to filename"), Qt::ToolTipRole); m_view->setModel(m); m_view->setContextMenuPolicy(Qt::CustomContextMenu); m_view->setRootIsDecorated(false); m_view->setAlternatingRowColors(true); + updateReadWrite(doc && doc->isReadWrite()); + connect(m_view, &QWidget::customContextMenuRequested, this, &ReportsGeneratorView::slotContextMenuRequested); l->addWidget(m_view); TemplateFileDelegate *del = new TemplateFileDelegate(m_view); QString path = QStandardPaths::locate(QStandardPaths::AppDataLocation, "reports", QStandardPaths::LocateDirectory); debugPlan<<"standardpath:"<files.insert(url.fileName(), url); } } m_view->setItemDelegateForColumn(1, del); m_view->setItemDelegateForColumn(2, new FileItemDelegate(m_view)); m_view->setItemDelegateForColumn(3, new FileNameExtensionDelegate(m_view)); m_view->header()->setSectionResizeMode(3, QHeaderView::Fixed); m_view->header()->resizeSection(3, 12); connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ReportsGeneratorView::slotSelectionChanged); setupGui(); Help::add(this, xi18nc("@info:whatsthis", "Add and generate reports" "" "Enables you to add and generate reports based on Open Document (.odf) files." "" "You can create a report template using any Open Document text editor." "More..." "", Help::page("Manual/Reports_Generator_View"))); } +void ReportsGeneratorView::updateReadWrite(bool rw) +{ + QStandardItemModel *m = static_cast(m_view->model()); + for (int r = 0; r < m->rowCount(); ++r) { + for (int c = 0; c < m->columnCount(); ++c) { + QStandardItem *item = m->itemFromIndex(m->index(r, c)); + if (rw) { + item->setFlags(item->flags() | Qt::ItemIsEditable); + } else { + item->setFlags(item->flags() & ~Qt::ItemIsEditable); + } + } + } + ViewBase::updateReadWrite(rw); +} + void ReportsGeneratorView::setGuiActive(bool activate) { debugPlan<selectionModel()->selectedRows(); } int ReportsGeneratorView::selectedRowCount() const { return selectedRows().count(); } void ReportsGeneratorView::slotContextMenuRequested(const QPoint& pos) { debugPlan; emit requestPopupMenu("reportsgeneratorview_popup", m_view->mapToGlobal(pos)); } void ReportsGeneratorView::slotEnableActions() { updateActionsEnabled(isReadWrite()); } void ReportsGeneratorView::updateActionsEnabled(bool on) { actionAddReport->setEnabled(on); actionRemoveReport->setEnabled(on && selectedRowCount() > 0); actionGenerateReport->setEnabled(on && selectedRowCount() > 0); } void ReportsGeneratorView::setupGui() { KActionCollection *coll = actionCollection(); actionAddReport = new QAction(koIcon("list-add"), i18n("Add Report"), this); coll->addAction("add_report", actionAddReport); coll->setDefaultShortcut(actionAddReport, Qt::CTRL + Qt::Key_I); connect(actionAddReport, &QAction::triggered, this, &ReportsGeneratorView::slotAddReport); addContextAction(actionAddReport); actionRemoveReport = new QAction(koIcon("list-remove"), i18n("Remove Report"), this); coll->addAction("remove_report", actionRemoveReport); coll->setDefaultShortcut(actionRemoveReport, Qt::CTRL + Qt::Key_D); connect(actionRemoveReport, &QAction::triggered, this, &ReportsGeneratorView::slotRemoveReport); addContextAction(actionRemoveReport); actionGenerateReport = new QAction(koIcon("document-export"), i18n("Generate Report"), this); coll->addAction("generate_report", actionGenerateReport); coll->setDefaultShortcut(actionGenerateReport, Qt::CTRL + Qt::Key_G); connect(actionGenerateReport, &QAction::triggered, this, &ReportsGeneratorView::slotGenerateReport); addContextAction(actionGenerateReport); // createOptionAction(); } void ReportsGeneratorView::slotOptions() { debugPlan; // SplitItemViewSettupDialog *dlg = new SplitItemViewSettupDialog(this, m_view, this); // dlg->addPrintingOptions(); // connect(dlg, SIGNAL(finished(int)), SLOT(slotOptionsFinished(int))); // dlg->show(); // dlg->raise(); // dlg->activateWindow(); } void ReportsGeneratorView::slotAddReport() { debugPlan; QAbstractItemModel *m = m_view->model(); int row = m->rowCount(); m->insertRow(row); QModelIndex idx = m->index(row, 0); m->setData(idx, i18n("New report")); QModelIndex add = m->index(row, 3); m->setData(add, ReportsGeneratorView::addOptions().at(0)); m->setData(add, ReportsGeneratorView::addTags().at(0), Qt::UserRole); m_view->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect); m_view->edit(idx); emit optionsModified(); } void ReportsGeneratorView::slotRemoveReport() { debugPlan<model(); QModelIndexList lst = selectedRows(); if (lst.isEmpty()) { return; } // Assumption: model is flat // We must do this in descending row order QMap map; for (int i = 0; i < lst.count(); ++i) { map.insert(-lst.at(i).row(), lst.at(i)); // sort descending } for (const QModelIndex &idx : map) { Q_ASSERT(!idx.parent().isValid()); // must be flat m->removeRow(idx.row(), idx.parent()); } emit optionsModified(); } void ReportsGeneratorView::slotGenerateReport() { debugPlan; QAbstractItemModel *model = m_view->model(); foreach(const QModelIndex &idx, selectedRows()) { QString name = model->index(idx.row(), 0).data().toString(); QString tmp = model->index(idx.row(), 1).data(FULLPATHROLE).toString(); QString file = model->index(idx.row(), 2).data().toString(); if (tmp.isEmpty()) { QMessageBox::information(this, xi18nc("@title:window", "Generate Report"), i18n("Failed to generate %1." "\nTemplate file name is empty.", name)); continue; } if (file.isEmpty()) { debugPlan<<"No files for report:"<index(idx.row(), 3).data(Qt::UserRole).toString(); if (addition == "Date") { int dotpos = file.lastIndexOf('.'); QString date = QDate::currentDate().toString(); file = file.insert(dotpos, date.prepend('-')); } else if (addition == "Number") { int dotpos = file.lastIndexOf('.'); QString fn = file; for (int i = 1; QFile::exists(fn); ++i) { fn = file.insert(dotpos, QString::number(i).prepend('-')); } file = fn; } // warn if file exists if (QFile::exists(QUrl(file).path())) { if (QMessageBox::question(this, i18n("Report Generation"), i18n("File exists. Continue?")) == QMessageBox::No) { return; } } generateReport(tmp, file); } } bool ReportsGeneratorView::generateReport(const QString &templateFile, const QString &file) { ReportGenerator rg; rg.setReportType("odt"); // TODO: handle different report types rg.setTemplateFile(templateFile); rg.setReportFile(file); rg.setProject(project()); rg.setScheduleManager(scheduleManager()); if (!rg.open()) { debugPlan<<"Failed to open report generator"; QMessageBox::warning(this, i18n("Failed to open report generator"), rg.lastError()); return false; } if (!rg.createReport()) { QMessageBox::warning(this, i18n("Failed to create report"), rg.lastError()); return false; } if (QMessageBox::question(this, i18nc("@title", "Report Generation"), i18nc("@info", "Report file generated:%1", file), QMessageBox::Open|QMessageBox::Close, QMessageBox::Close) == QMessageBox::Open) { return KRun::runUrl(QUrl(file), "application/vnd.oasis.opendocument.text", window(), (KRun::RunFlags)0); } return true; } bool ReportsGeneratorView::loadContext(const KoXmlElement &context) { debugPlan; m_view->header()->setStretchLastSection((bool)(context.attribute("stretch-last-column", "1").toInt())); KoXmlElement e = context.namedItem("sections").toElement(); if (!e.isNull()) { QHeaderView *h = m_view->header(); QString s("section-%1"); for (int i = 0; i < h->count(); ++i) { if (e.hasAttribute(s.arg(i))) { int index = e.attribute(s.arg(i), "-1").toInt(); if (index >= 0 && index < h->count()) { h->moveSection(h->visualIndex(index), i); } } } } KoXmlElement parent = context.namedItem("data").toElement(); if (!parent.isNull()) { debugPlan<<"Load data"; int row = 0; QAbstractItemModel *model = m_view->model(); forEachElement(e, parent) { if (e.tagName() != "row") { continue; } model->insertRow(row); QString name = e.attribute("name"); QString tmp = e.attribute("template"); QString file = e.attribute("file"); QString add = e.attribute("add"); QModelIndex idx = model->index(row, 0); model->setData(idx, name); idx = model->index(row, 1); model->setData(idx, tmp, FULLPATHROLE); model->setData(idx, tmp, Qt::ToolTipRole); model->setData(idx, QUrl(tmp).fileName()); idx = model->index(row, 2); model->setData(idx, file); idx = model->index(row, 3); model->setData(idx, add, Qt::UserRole); model->setData(idx, ReportsGeneratorView::addOptions().value(ReportsGeneratorView::addTags().indexOf(add))); ++row; } } ViewBase::loadContext(context); for (int c = 0; c < m_view->header()->count(); ++c) { m_view->resizeColumnToContents(c); } + updateReadWrite(isReadWrite()); return true; } void ReportsGeneratorView::saveContext(QDomElement &context) const { debugPlan; context.setAttribute( "stretch-last-column", QString::number(m_view->header()->stretchLastSection()) ); QDomElement e = context.ownerDocument().createElement("sections"); context.appendChild(e); QHeaderView *h = m_view->header(); for (int i = 0; i < h->count(); ++i) { e.setAttribute(QString("section-%1").arg(i), h->logicalIndex(i)); } QDomElement data = context.ownerDocument().createElement("data"); context.appendChild(data); const QAbstractItemModel *model = m_view->model(); for (int row = 0; row < model->rowCount(); ++row) { e = data.ownerDocument().createElement("row"); data.appendChild(e); QModelIndex idx = model->index(row, 0); e.setAttribute("name", idx.data().toString()); idx = model->index(row, 1); e.setAttribute("template", idx.data(FULLPATHROLE).toString()); idx = model->index(row, 2); e.setAttribute("file", idx.data().toString()); idx = model->index(row, 3); e.setAttribute("add", idx.data(Qt::UserRole).toString()); } ViewBase::saveContext(context); } } // namespace KPlato diff --git a/src/libs/ui/reportsgenerator/ReportsGeneratorView.h b/src/libs/ui/reportsgenerator/ReportsGeneratorView.h index f1dabbea..5015d6e6 100644 --- a/src/libs/ui/reportsgenerator/ReportsGeneratorView.h +++ b/src/libs/ui/reportsgenerator/ReportsGeneratorView.h @@ -1,92 +1,94 @@ /* This file is part of the KDE project Copyright (C) 2017 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef REPORTSGENERATORVIEW_H #define REPORTSGENERATORVIEW_H #include "planui_export.h" #include "kptglobal.h" #include "kptviewbase.h" class KoDocument; class KActionMenu; class QWidget; class QTreeView; namespace KPlato { class PLANUI_EXPORT ReportsGeneratorView : public ViewBase { Q_OBJECT public: ReportsGeneratorView(KoPart *part, KoDocument *doc, QWidget *parent); void setupGui(); /// Loads context info into this view. Reimplement. bool loadContext( const KoXmlElement &/*context*/ ) override; /// Save context info from this view. Reimplement. void saveContext( QDomElement &/*context*/ ) const override; static QStringList addOptions(); static QStringList addTags(); + void updateReadWrite( bool ) override; + public Q_SLOTS: /// Activate/deactivate the gui void setGuiActive( bool activate ) override; void slotAddReport(); void slotRemoveReport(); void slotGenerateReport(); protected: void updateActionsEnabled( bool on ); int selectedRowCount() const; QModelIndexList selectedRows() const; bool generateReport(const QString &templateFile, const QString &file); protected Q_SLOTS: void slotOptions() override; private Q_SLOTS: void slotSelectionChanged(); void slotCurrentChanged( const QModelIndex&, const QModelIndex& ); void slotContextMenuRequested(const QPoint &pos); void slotEnableActions(); private: QTreeView *m_view; QAction *actionAddReport; QAction *actionRemoveReport; QAction *actionGenerateReport; }; } //namespace KPlato #endif diff --git a/src/libs/ui/reportsgenerator/ReportsGeneratorViewUi.rc b/src/libs/ui/reportsgenerator/ReportsGeneratorViewUi.rc index b0c0d131..1c3d4964 100644 --- a/src/libs/ui/reportsgenerator/ReportsGeneratorViewUi.rc +++ b/src/libs/ui/reportsgenerator/ReportsGeneratorViewUi.rc @@ -1,28 +1,28 @@ - + diff --git a/src/libs/ui/performance/ProjectStatusViewUi.rc b/src/libs/ui/reportsgenerator/ReportsGeneratorViewUi_readonly.rc similarity index 73% copy from src/libs/ui/performance/ProjectStatusViewUi.rc copy to src/libs/ui/reportsgenerator/ReportsGeneratorViewUi_readonly.rc index a6fcbe6f..f46391d7 100644 --- a/src/libs/ui/performance/ProjectStatusViewUi.rc +++ b/src/libs/ui/reportsgenerator/ReportsGeneratorViewUi_readonly.rc @@ -1,21 +1,18 @@ - + - - -