diff --git a/kuiviewer/kuiviewer.cpp b/kuiviewer/kuiviewer.cpp index cf99275..22c671b 100644 --- a/kuiviewer/kuiviewer.cpp +++ b/kuiviewer/kuiviewer.cpp @@ -1,146 +1,140 @@ /* * This file is part of the kuiviewer package * Copyright (c) 2003 Richard Moore * Copyright (c) 2003 Ian Reinhart Geiser * Copyright (c) 2004 Benjamin C. Meyer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include "kuiviewer.h" #include "kuiviewer_part.h" +#include "kuiviewer_part_interface.h" // KF #include #include #include #include #include #include // Qt #include #include #include #include #include #include KUIViewer::KUIViewer() : KParts::MainWindow() { setObjectName(QStringLiteral("KUIViewer")); // setup our actions setupActions(); setMinimumSize(300, 200); // Bring up the gui setupGUI(); // this routine will find and load our Part. it finds the Part by // name which is a bad idea usually.. but it's alright in this // case since our Part is made for this Shell KPluginFactory* factory = KPluginLoader(QStringLiteral("kuiviewerpart")).factory(); if (factory) { // now that the Part is loaded, we cast it to a Part to get // our hands on it m_part = factory->create(this); if (m_part) { m_part->setObjectName(QStringLiteral("kuiviewer_part")); // tell the KParts::MainWindow that this is indeed the main widget setCentralWidget(m_part->widget()); // and integrate the part's GUI with the shell's createGUI(m_part); } } else { // if we couldn't find our Part, we exit since the Shell by // itself can't do anything useful //FIXME improve message, which Part is this referring to? KMessageBox::error(this, i18n("Unable to locate Kuiviewer kpart.")); QApplication::quit(); // we return here, cause kapp->quit() only means "exit the // next time we enter the event loop... return; } } KUIViewer::~KUIViewer() { } void KUIViewer::load(const QUrl& url) { m_part->openUrl(url); adjustSize(); } void KUIViewer::setupActions() { KStandardAction::open(this, &KUIViewer::fileOpen, actionCollection()); KStandardAction::quit(this, &KUIViewer::close, actionCollection()); } void KUIViewer::fileOpen() { // this slot is called whenever the File->Open menu is selected, // the Open shortcut is pressed (usually CTRL+O) or the Open toolbar // button is clicked QUrl file_name = QFileDialog::getOpenFileUrl(this, QString(), QUrl(), i18n("*.ui *.UI|User Interface Files")); if (!file_name.isEmpty()) { // About this function, the style guide ( // http://developer.kde.org/documentation/standards/kde/style/basics/index.html ) // says that it should open a new window if the document is _not_ // in its initial state. This is what we do here.. if (m_part->url().isEmpty()) { // we open the file in this window... load(file_name); } else { // we open the file in a new window... KUIViewer* newWin = new KUIViewer; newWin->load(file_name); newWin->show(); } } } void KUIViewer::takeScreenshot(const QString& filename, int w, int h) { - if (!m_part) { + auto uiviewerInterface = qobject_cast(m_part); + if (!uiviewerInterface) { return; } - showMinimized(); - if (w != -1 && h != -1) { // resize widget to the desired size - m_part->widget()->setMinimumSize(w, h); - m_part->widget()->setMaximumSize(w, h); - m_part->widget()->repaint(); - // resize app to be as large as desired size - adjustSize(); - // Disable the saving of the size - setAutoSaveSettings(QStringLiteral("MainWindow"), false); + uiviewerInterface->setWidgetSize(QSize(w, h)); } - const QPixmap pixmap = m_part->widget()->grab(); + const QPixmap pixmap = uiviewerInterface->renderWidgetAsPixmap(); pixmap.save(filename, "PNG"); } diff --git a/kuiviewer/kuiviewer_part.cpp b/kuiviewer/kuiviewer_part.cpp index 488d395..a5a20d9 100644 --- a/kuiviewer/kuiviewer_part.cpp +++ b/kuiviewer/kuiviewer_part.cpp @@ -1,215 +1,283 @@ /* * This file is part of the kuiviewer package * Copyright (c) 2003 Richard Moore * Copyright (c) 2003 Ian Reinhart Geiser + * Copyright (c) 2017 Friedrich W. H. Kossebau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #define TRANSLATION_DOMAIN "kuiviewer" #include "kuiviewer_part.h" #include // KF #include #include #include #include #include #include #include #include // Qt #include #include #include #include #include #include -#include +#include +#include +#include K_PLUGIN_FACTORY(KUIViewerPartFactory, registerPlugin();) KUIViewerPart::KUIViewerPart(QWidget* parentWidget, QObject* parent, const QVariantList& /*args*/) : KParts::ReadOnlyPart(parent) + , m_subWindow(nullptr) + , m_view(nullptr) { // we need an instance KAboutData about(QStringLiteral("kuiviewerpart"), i18n("KUIViewerPart"), QStringLiteral("0.2"), i18n("Displays Designer's UI files"), KAboutLicense::LGPL); about.addAuthor(i18n("Richard Moore"), i18n("Original author"), QStringLiteral("rich@kde.org")); about.addAuthor(i18n("Ian Reinhart Geiser"), i18n("Original author"), QStringLiteral("geiseri@kde.org")); setComponentData(about); // this should be your custom internal widget - m_widget = new QWidget(parentWidget); - QVBoxLayout* widgetVBoxLayout = new QVBoxLayout(m_widget); - widgetVBoxLayout->setMargin(0); + m_widget = new QMdiArea(parentWidget); + m_widget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_widget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); // notify the part that this is our internal widget setWidget(m_widget); // set our XML-UI resource file setXMLFile(QStringLiteral("kuiviewer_part.rc")); m_style = actionCollection()->add(QStringLiteral("change_style")); m_style->setText(i18n("Style")); connect(m_style, static_cast(&KSelectAction::triggered), this, &KUIViewerPart::slotStyle); //m_style->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S)); m_style->setEditable(false); m_styleFromConfig = KConfigGroup(KSharedConfig::openConfig(), "General").readEntry("currentWidgetStyle", QString()); const QStringList styles = QStyleFactory::keys(); m_style->setItems(QStringList(i18nc("Default style", "Default")) + styles); m_style->setCurrentItem(0); // empty or incorrect value means the default value of currentWidgetStyle, // which leads to the Default option. if (!m_styleFromConfig.isEmpty()) { QStringList::ConstIterator it = styles.begin(); QStringList::ConstIterator end = styles.end(); // Skip the default item int idx = 1; for (; it != end; ++it, ++idx) { if ((*it).toLower() == m_styleFromConfig.toLower()) { m_style->setCurrentItem(idx); break; } } } m_style->setToolTip(i18n("Set the style used for the view.")); m_style->setMenuAccelsEnabled(true); m_copy = KStandardAction::copy(this, &KUIViewerPart::slotGrab, actionCollection()); m_copy->setText(i18n("Copy as Image")); updateActions(); // Commented out to fix warning (rich) // slot should probably be called saveAs() for consistency with // KParts::ReadWritePart BTW. // KStandardAction::saveAs(this, SLOT(slotSave()), actionCollection()); } KUIViewerPart::~KUIViewerPart() { } static QStringList designerPluginPaths() { QStringList paths; const QStringList& libraryPaths = QApplication::libraryPaths(); for (const auto& path : libraryPaths) { paths.append(path + QLatin1String("/designer")); } return paths; } bool KUIViewerPart::openFile() { // m_file is always local so we can use QFile on it QFile file(localFilePath()); - if (!file.open(QIODevice::ReadOnly)) { + + if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) { + qCDebug(KUIVIEWERPART) << "Could not open UI file: " << file.errorString(); return false; } - delete m_view; + if (m_subWindow) { + m_widget->removeSubWindow(m_subWindow); + delete m_view; + delete m_subWindow; + m_subWindow = nullptr; + } + QFormBuilder builder; builder.setPluginPath(designerPluginPaths()); - m_view = builder.load(&file, m_widget); + m_view = builder.load(&file, nullptr); - file.close(); updateActions(); if (!m_view) { + qCDebug(KUIVIEWERPART) << "Could not load UI file: " << builder.errorString(); return false; } - m_view->show(); - slotStyle(0); + // hack ahead: + // UI files have a size set for the widget they define. The QMdiSubWindow relies on sizeHint() + // during the show event though it seems, to calculate the initial window size, and then discards + // the widget size initially set from the builder in the following layout-ruled geometry update. + // Enforcing the initial size by manually setting it afterwards to the widget itself seems not possible, + // due to the layout government based on window size. + // To inject the initial widget size into the initial window geometry, as hack the min and max sizes are + // temporarily set to the wanted size and, once the window is shown, reset to their initial values. + const QSize widgetSize = m_view->size(); + const QSize origWidgetMinimumSize = m_view->minimumSize(); + const QSize origWidgetMaximumSize = m_view->maximumSize(); + restyleView(m_style->currentText()); + m_view->setMinimumSize(widgetSize); + m_view->setMaximumSize(widgetSize); + + const Qt::WindowFlags windowFlags(Qt::SubWindow|Qt::CustomizeWindowHint|Qt::WindowTitleHint); + m_subWindow = m_widget->addSubWindow(m_view, windowFlags); + // prevent focus stealing by adding the window in disabled state + m_subWindow->setEnabled(false); + m_subWindow->show(); + // and restore minimum size + m_view->setMinimumSize(origWidgetMinimumSize); + m_view->setMaximumSize(origWidgetMaximumSize); + + m_widget->setActiveSubWindow(m_subWindow); + m_subWindow->setEnabled(true); return true; } void KUIViewerPart::updateActions() { const bool hasView = !m_view.isNull(); m_style->setEnabled(hasView); m_copy->setEnabled(hasView); } -void KUIViewerPart::slotStyle(int) +void KUIViewerPart::restyleView(const QString& styleName) +{ + QStyle* style = QStyleFactory::create(styleName); + + m_view->setStyle(style); + + const QList childWidgets = m_view->findChildren(); + for (auto child : childWidgets) { + child->setStyle(style); + } +} + +void KUIViewerPart::setWidgetSize(const QSize& size) { if (m_view.isNull()) { - updateActions(); return; } - const QString styleName = m_style->currentText(); - QStyle* style = QStyleFactory::create(styleName); - qCDebug(KUIVIEWERPART) << "Change style: " << styleName; + // hack: enforce widget size by setting min/max sizes to wanted size + // and then have layout update the complete window + const QSize origWidgetMinimumSize = m_view->minimumSize(); + const QSize origWidgetMaximumSize = m_view->maximumSize(); + m_view->setMinimumSize(size); + m_view->setMaximumSize(size); + m_subWindow->updateGeometry(); + // restore + m_view->setMinimumSize(origWidgetMinimumSize); + m_view->setMaximumSize(origWidgetMaximumSize); +} + +QPixmap KUIViewerPart::renderWidgetAsPixmap() const +{ + if (m_view.isNull()) { + return QPixmap(); + } - m_widget->hide(); - QApplication::setOverrideCursor(Qt::WaitCursor); - m_widget->setStyle(style); + return m_view->grab(); +} - const QList l = m_widget->findChildren(); - for (int i = 0; i < l.size(); ++i) { - l.at(i)->setStyle(style); +void KUIViewerPart::slotStyle(int) +{ + if (m_view.isNull()) { + updateActions(); + return; } - m_widget->show(); - QApplication::restoreOverrideCursor(); + m_view->hide(); + + const QString styleName = m_style->currentText(); + qCDebug(KUIVIEWERPART) << "Style selectd:" << styleName; + restyleView(styleName); + + m_view->show(); /* the style changed, update the configuration */ if (m_styleFromConfig != styleName) { KSharedConfig::Ptr cfg = KSharedConfig::openConfig(); KConfigGroup cg(cfg, "General"); if (m_style->currentItem() > 0) { /* A style different from the default */ cg.writeEntry("currentWidgetStyle", styleName); } else { /* default style: remove the entry */ cg.deleteEntry("currentWidgetStyle"); } cfg->sync(); } } void KUIViewerPart::slotGrab() { if (m_view.isNull()) { updateActions(); return; } - const QPixmap pixmap = m_widget->grab(); + const QPixmap pixmap = m_view->grab(); QApplication::clipboard()->setPixmap(pixmap); } #include "kuiviewer_part.moc" diff --git a/kuiviewer/kuiviewer_part.h b/kuiviewer/kuiviewer_part.h index b18c4f3..50b5c95 100644 --- a/kuiviewer/kuiviewer_part.h +++ b/kuiviewer/kuiviewer_part.h @@ -1,74 +1,89 @@ /* * This file is part of the kuiviewer package * Copyright (c) 2003 Richard Moore * Copyright (c) 2003 Ian Reinhart Geiser + * Copyright (c) 2017 Friedrich W. H. Kossebau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * 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 KUIVIEWERPART_H #define KUIVIEWERPART_H +#include "kuiviewer_part_interface.h" + // KF #include // Qt #include +#include class KSelectAction; +class QMdiArea; +class QMdiSubWindow; /** * This is a "Part". It that does all the real work in a KPart * application. * * @short Main Part * @author Richard Moore * @version 0.1 */ -class KUIViewerPart : public KParts::ReadOnlyPart +class KUIViewerPart : public KParts::ReadOnlyPart, public KUIViewerPartInterface { Q_OBJECT + Q_INTERFACES(KUIViewerPartInterface) public: /** * Default constructor */ KUIViewerPart(QWidget* parentWidget, QObject* parent, const QVariantList& args); /** * Destructor */ ~KUIViewerPart() override; public Q_SLOTS: void slotStyle(int); void slotGrab(); void updateActions(); +public: + void setWidgetSize(const QSize& size) override; + QPixmap renderWidgetAsPixmap() const override; + protected: /** * This must be implemented by each part */ bool openFile() override; private: - QWidget* m_widget; + void restyleView(const QString& styleName); + +private: + QMdiArea* m_widget; + QMdiSubWindow* m_subWindow; QPointer m_view; KSelectAction* m_style; QAction* m_copy; QString m_styleFromConfig; }; #endif // KUIVIEWERPART_H diff --git a/kuiviewer/kuiviewer_part_interface.h b/kuiviewer/kuiviewer_part_interface.h new file mode 100644 index 0000000..eeb3b34 --- /dev/null +++ b/kuiviewer/kuiviewer_part_interface.h @@ -0,0 +1,40 @@ +/* + * This file is part of the kuiviewer package + * Copyright (c) 2017 Friedrich W. H. Kossebau + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * 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 KUIVIEWERPARTINTERFACE_H +#define KUIVIEWERPARTINTERFACE_H + +#include + +class QPixmap; +class QSize; + + +class KUIViewerPartInterface +{ +public: + virtual ~KUIViewerPartInterface() {} + + virtual void setWidgetSize(const QSize& size) = 0; + virtual QPixmap renderWidgetAsPixmap() const = 0; +}; + +Q_DECLARE_INTERFACE(KUIViewerPartInterface, "org.kde.KUIViewerPartInterface") + +#endif // KUIVIEWERPARTINTERFACE_H diff --git a/kuiviewer/main.cpp b/kuiviewer/main.cpp index 2eaac27..c0343c7 100644 --- a/kuiviewer/main.cpp +++ b/kuiviewer/main.cpp @@ -1,93 +1,101 @@ /* * This file is part of the kuiviewer package * Copyright (c) 2003 Richard Moore * Copyright (c) 2003 Ian Reinhart Geiser * Copyright (c) 2004 Benjamin C. Meyer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include "kuiviewer.h" // KF #include #include // Qt #include #include #include #include #include int main(int argc, char** argv) { QApplication app(argc, argv); KLocalizedString::setApplicationDomain("kuiviewer"); KAboutData about(QStringLiteral("kuiviewer"), i18n("KUIViewer"), QStringLiteral("0.2"), i18n("Displays Designer's UI files"), KAboutLicense::LGPL); about.addAuthor(i18n("Richard Moore"), i18n("Original author"), QStringLiteral("rich@kde.org")); about.addAuthor(i18n("Ian Reinhart Geiser"), i18n("Original author"), QStringLiteral("geiseri@kde.org")); // Screenshot capability about.addAuthor(i18n("Benjamin C. Meyer"), i18n("Screenshot capability"), QStringLiteral("ben+kuiviewer@meyerhome.net")); KAboutData::setApplicationData(about); QCommandLineParser parser; parser.addVersionOption(); parser.addHelpOption(); about.setupCommandLine(&parser); parser.addPositionalArgument(QLatin1String("[URL]"), i18n("Document to open")); const QString takeScreenshotOptionKey(QStringLiteral("takescreenshot")); const QString screenshotWidthOptionKey(QStringLiteral("screenshotwidth")); const QString screenshotHeightOptionKey(QStringLiteral("screenshotheight")); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("s") << takeScreenshotOptionKey, i18n("Save screenshot to file and exit"), QLatin1String("filename"))); parser.addOption(QCommandLineOption({QStringLiteral("sw"), screenshotWidthOptionKey}, i18n("Screenshot width"), QLatin1String("int"), QLatin1String("-1"))); parser.addOption(QCommandLineOption({QStringLiteral("sh"), screenshotHeightOptionKey}, i18n("Screenshot height"), QLatin1String("int"), QLatin1String("-1"))); parser.process(app); about.processCommandLine(&parser); // see if we are starting with session management if (app.isSessionRestored()) { RESTORE(KUIViewer) } else { // no session.. just start up normally const auto positionalArguments = parser.positionalArguments(); if (positionalArguments.isEmpty()) { KUIViewer* widget = new KUIViewer; widget->show(); } else { + const bool takeScreenshot = parser.isSet(takeScreenshotOptionKey); KUIViewer* widget = new KUIViewer; + // show before loading, so widget geometries will be properly updated when requested + // TODO: investigate how to do this properly with perhaps showevents & Co.? + if (takeScreenshot) { + widget->showMinimized(); + } else { + widget->show(); + } + widget->load(QUrl::fromUserInput(positionalArguments.at(0), QDir::currentPath())); - if (parser.isSet(takeScreenshotOptionKey)) { + if (takeScreenshot) { widget->takeScreenshot(parser.value(takeScreenshotOptionKey), parser.value(screenshotWidthOptionKey).toInt(), parser.value(screenshotHeightOptionKey).toInt()); return 0; } - widget->show(); } } return app.exec(); }