summaryrefslogtreecommitdiffstats
path: root/lib/kofficecore
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
commit8362bf63dea22bbf6736609b0f49c152f975eb63 (patch)
tree0eea3928e39e50fae91d4e68b21b1e6cbae25604 /lib/kofficecore
downloadkoffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz
koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'lib/kofficecore')
-rw-r--r--lib/kofficecore/DESIGN113
-rw-r--r--lib/kofficecore/KoApplication.cpp249
-rw-r--r--lib/kofficecore/KoApplication.h85
-rw-r--r--lib/kofficecore/KoApplicationIface.cc97
-rw-r--r--lib/kofficecore/KoApplicationIface.h66
-rw-r--r--lib/kofficecore/KoChild.cpp366
-rw-r--r--lib/kofficecore/KoChild.h312
-rw-r--r--lib/kofficecore/KoContainerHandler.cpp395
-rw-r--r--lib/kofficecore/KoContainerHandler.h137
-rw-r--r--lib/kofficecore/KoDetailsPane.cpp470
-rw-r--r--lib/kofficecore/KoDetailsPane.h132
-rw-r--r--lib/kofficecore/KoDocInfoPropsFactory.cpp27
-rw-r--r--lib/kofficecore/KoDocument.cpp2672
-rw-r--r--lib/kofficecore/KoDocument.h1158
-rw-r--r--lib/kofficecore/KoDocumentChild.cpp523
-rw-r--r--lib/kofficecore/KoDocumentChild.h180
-rw-r--r--lib/kofficecore/KoDocumentIface.cc568
-rw-r--r--lib/kofficecore/KoDocumentIface.h157
-rw-r--r--lib/kofficecore/KoDocumentInfo.cpp943
-rw-r--r--lib/kofficecore/KoDocumentInfo.h196
-rw-r--r--lib/kofficecore/KoDocumentInfoDlg.cpp555
-rw-r--r--lib/kofficecore/KoDocumentInfoDlg.h85
-rw-r--r--lib/kofficecore/KoDocument_p.h41
-rw-r--r--lib/kofficecore/KoDom.cpp30
-rw-r--r--lib/kofficecore/KoDom.h70
-rw-r--r--lib/kofficecore/KoFactory.cpp45
-rw-r--r--lib/kofficecore/KoFactory.h39
-rw-r--r--lib/kofficecore/KoFileDialog.cpp125
-rw-r--r--lib/kofficecore/KoFileDialog.h55
-rw-r--r--lib/kofficecore/KoFilter.cpp151
-rw-r--r--lib/kofficecore/KoFilter.h315
-rw-r--r--lib/kofficecore/KoFilterChain.cpp936
-rw-r--r--lib/kofficecore/KoFilterChain.h410
-rw-r--r--lib/kofficecore/KoFilterManager.cpp602
-rw-r--r--lib/kofficecore/KoFilterManager.h178
-rw-r--r--lib/kofficecore/KoFilterManager_p.h46
-rw-r--r--lib/kofficecore/KoFrame.cpp393
-rw-r--r--lib/kofficecore/KoFrame.h65
-rw-r--r--lib/kofficecore/KoGenStyles.cpp405
-rw-r--r--lib/kofficecore/KoGenStyles.h463
-rw-r--r--lib/kofficecore/KoGlobal.cpp205
-rw-r--r--lib/kofficecore/KoGlobal.h107
-rw-r--r--lib/kofficecore/KoMainWindow.cpp1701
-rw-r--r--lib/kofficecore/KoMainWindow.h387
-rw-r--r--lib/kofficecore/KoMainWindowIface.cc65
-rw-r--r--lib/kofficecore/KoMainWindowIface.h48
-rw-r--r--lib/kofficecore/KoOasisLoadingContext.cpp124
-rw-r--r--lib/kofficecore/KoOasisLoadingContext.h122
-rw-r--r--lib/kofficecore/KoOasisSettings.cpp224
-rw-r--r--lib/kofficecore/KoOasisSettings.h179
-rw-r--r--lib/kofficecore/KoOasisStore.cpp194
-rw-r--r--lib/kofficecore/KoOasisStore.h96
-rw-r--r--lib/kofficecore/KoOasisStyles.cpp1604
-rw-r--r--lib/kofficecore/KoOasisStyles.h165
-rw-r--r--lib/kofficecore/KoOpenPane.cpp315
-rw-r--r--lib/kofficecore/KoOpenPane.h90
-rw-r--r--lib/kofficecore/KoPageLayout.cpp242
-rw-r--r--lib/kofficecore/KoPageLayout.h260
-rw-r--r--lib/kofficecore/KoPicture.cpp290
-rw-r--r--lib/kofficecore/KoPicture.h259
-rw-r--r--lib/kofficecore/KoPictureBase.cpp142
-rw-r--r--lib/kofficecore/KoPictureBase.h128
-rw-r--r--lib/kofficecore/KoPictureClipart.cpp150
-rw-r--r--lib/kofficecore/KoPictureClipart.h102
-rw-r--r--lib/kofficecore/KoPictureCollection.cpp324
-rw-r--r--lib/kofficecore/KoPictureCollection.h171
-rw-r--r--lib/kofficecore/KoPictureEps.cpp447
-rw-r--r--lib/kofficecore/KoPictureEps.h136
-rw-r--r--lib/kofficecore/KoPictureImage.cpp194
-rw-r--r--lib/kofficecore/KoPictureImage.h123
-rw-r--r--lib/kofficecore/KoPictureKey.cpp151
-rw-r--r--lib/kofficecore/KoPictureKey.h154
-rw-r--r--lib/kofficecore/KoPictureShared.cpp535
-rw-r--r--lib/kofficecore/KoPictureShared.h214
-rw-r--r--lib/kofficecore/KoPictureWmf.cpp137
-rw-r--r--lib/kofficecore/KoPictureWmf.h108
-rw-r--r--lib/kofficecore/KoPoint.h128
-rw-r--r--lib/kofficecore/KoQueryTrader.cpp214
-rw-r--r--lib/kofficecore/KoQueryTrader.h178
-rw-r--r--lib/kofficecore/KoRect.cpp274
-rw-r--r--lib/kofficecore/KoRect.h148
-rw-r--r--lib/kofficecore/KoSetPropCommand.h86
-rw-r--r--lib/kofficecore/KoSize.h230
-rw-r--r--lib/kofficecore/KoSpeaker.cpp602
-rw-r--r--lib/kofficecore/KoSpeaker.h204
-rw-r--r--lib/kofficecore/KoStyleStack.cpp296
-rw-r--r--lib/kofficecore/KoStyleStack.h201
-rw-r--r--lib/kofficecore/KoTemplates.cpp379
-rw-r--r--lib/kofficecore/KoTemplates.h150
-rw-r--r--lib/kofficecore/KoUnit.cpp215
-rw-r--r--lib/kofficecore/KoUnit.h175
-rw-r--r--lib/kofficecore/KoView.cpp868
-rw-r--r--lib/kofficecore/KoView.h502
-rw-r--r--lib/kofficecore/KoViewIface.cc76
-rw-r--r--lib/kofficecore/KoViewIface.h56
-rw-r--r--lib/kofficecore/KoXmlNS.cpp44
-rw-r--r--lib/kofficecore/KoXmlNS.h55
-rw-r--r--lib/kofficecore/KoXmlReader.cpp1568
-rw-r--r--lib/kofficecore/KoXmlReader.h320
-rw-r--r--lib/kofficecore/Koversiondialog.cpp143
-rw-r--r--lib/kofficecore/Koversiondialog.h68
-rw-r--r--lib/kofficecore/Makefile.am70
-rw-r--r--lib/kofficecore/THOUGHTS164
-rw-r--r--lib/kofficecore/document-info.dtd80
-rw-r--r--lib/kofficecore/kkbdaccessextensions.cpp667
-rw-r--r--lib/kofficecore/kkbdaccessextensions.h175
-rw-r--r--lib/kofficecore/koDetailsPaneBase.ui238
-rw-r--r--lib/kofficecore/koDocumentInfoAboutWidget.ui352
-rw-r--r--lib/kofficecore/koDocumentInfoAuthorWidget.ui311
-rw-r--r--lib/kofficecore/koDocumentInfoUserMetadataWidget.ui233
-rw-r--r--lib/kofficecore/koOpenPaneBase.ui144
-rw-r--r--lib/kofficecore/kodocinfopropspage.desktop68
-rw-r--r--lib/kofficecore/koffice_export.h210
-rw-r--r--lib/kofficecore/koffice_shell.rc58
-rw-r--r--lib/kofficecore/kofficeversion.cc46
-rw-r--r--lib/kofficecore/kofficeversion.h86
-rw-r--r--lib/kofficecore/priorityqueue.h216
-rw-r--r--lib/kofficecore/tests/Makefile.am40
-rw-r--r--lib/kofficecore/tests/filter_graph.cpp115
-rw-r--r--lib/kofficecore/tests/filterchain_test.cpp107
-rw-r--r--lib/kofficecore/tests/kodomtest.cpp129
-rw-r--r--lib/kofficecore/tests/kogenstylestest.cpp270
-rw-r--r--lib/kofficecore/tests/kooasissettingstest.cpp106
-rw-r--r--lib/kofficecore/tests/kooasisstoretest.cpp58
-rw-r--r--lib/kofficecore/tests/kopointtest.cpp89
-rw-r--r--lib/kofficecore/tests/korecttest.cpp53
-rw-r--r--lib/kofficecore/tests/koxmlreadertest.cpp2074
-rw-r--r--lib/kofficecore/tests/priorityqueue_test.cpp87
128 files changed, 37174 insertions, 0 deletions
diff --git a/lib/kofficecore/DESIGN b/lib/kofficecore/DESIGN
new file mode 100644
index 00000000..c5409b30
--- /dev/null
+++ b/lib/kofficecore/DESIGN
@@ -0,0 +1,113 @@
+-------------------------------------------------------------------------
+| This document answers some questions about how the startup of KOffice
+| applications works. This file was put together by
+| Eric R. Turner <eric.r.turner@bitbreather.com>
+| from the answers given by David Faure <faure@kde.org>
+-------------------------------------------------------------------------
+
+Q: Where is the pointer to the application's instance of KAboutData
+data stored?
+
+A: In a static member of the KCmdLineArgs class.
+
+
+
+Q: One of the arguments to KAboutData's constructor is the program
+name. Will this be used later to run the program?
+
+A: No (by the time the KAboutData's constructor is called, the program
+is already running). The program name is in fact the "instance name",
+which is used to locate the application's own data
+(/share/apps/<application name>/...) and many other things.
+
+
+
+Q: What is the "+[file]" option passed to KCmdLineArgs?
+
+A: KCmdLineOptions describe the KCmdLineArgs. "+[file]" means that the
+command line arguments can be one or more file names, and that these
+arguments are optional.
+
+
+
+Q: What is I18N_NOOP()?
+
+A: I18N_NOOP() marks the options description for translation to other
+languages.
+
+
+
+Q: What is a KoApplicationInterface used for?
+
+A: It allows a KOffice application to participate in Inter-Process
+Communication and Remote Procedure Calls via DCOP.
+
+
+
+Q: What is the .desktop file?
+
+A: It's responsible for the menu entry, standard KDE stuff. In KOffice
+we add a few useful tags to it, in particular the "native mime type"
+of the component (e.g. application/x-kword for kword).
+
+
+
+Q: Is the .desktop file generated by hand?
+
+A: Yes.
+
+
+
+Q: What's a KoDocumentEntry? I couldn't find it in lib/kofficecore.
+
+A: KoDocumentEntry describes a KOffice component (i.e. it's a
+representation in memory of the data contained in the .desktop
+file). It's in kofficecore/koQueryTrader.h.
+
+
+
+Q: Is the show() method of KoMainWindow where the application actually
+appears to the user?
+
+A: Yes.
+
+
+
+Q: How does KoMainWindow->show() bring up the correct application (how
+does it know we want to show one app or another)?
+
+A: The KoMainWindow is the same for all. But KoDocumentEntry was
+created from specific application data, and koQueryTrader klopens the
+library containing this component. The very same mechanism is used to
+1) embed other components inside the app, 2) create any kind of
+koffice document, e.g. in koshell. All the KOffice applications are in
+fact very small wrappers that simply dlopen their own compnent - with
+the code in kofficecore, which can also dlopen ANY component.
+
+
+
+Q: The KoApplication object doesn't store pointers to the KoDocument
+nd KoMainWindow objects. Do these objects communicate with each other
+later on, and if so, is that done through DCOP?
+
+A: The KoMainWindow knows about the KoDocument[s] inside it. The
+KoMainWindows are in a static linked list, if one really wants to
+iterateover all windows in the application, but this is rarely needed.
+
+
+Q: How does KoMainWindow relate to KoDocument and KoView?
+
+A: KOffice applications reimplement KoDocument and KoView. But the
+KoMainWindow is the same class for all, since it doesn't have any
+app-specific stuff. It's GUI, i.e. menus and toolbars, are provided by
+1) itself (the standard stuff like the File and Help menus), 2) the
+document's actions, and 3) the view's actions.
+
+
+
+Q: How do KoDocument and KoView relate to each other?
+
+A: KoDocument can have one or more KoViews, which allows the user to
+have multiple views of the same document. In a single KoMainWindow
+this might be accomplished using the "split view" feature. KoView[s]
+of the same document can be in separate KoMainWindow[s].
diff --git a/lib/kofficecore/KoApplication.cpp b/lib/kofficecore/KoApplication.cpp
new file mode 100644
index 00000000..3f2f6b23
--- /dev/null
+++ b/lib/kofficecore/KoApplication.cpp
@@ -0,0 +1,249 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ 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.
+*/
+
+#include "KoApplication.h"
+#include <config.h>
+#include <qfile.h>
+#include <qregexp.h>
+#include <dcopclient.h>
+#include <KoApplicationIface.h>
+#include <KoQueryTrader.h>
+#include <KoDocument.h>
+#include <KoMainWindow.h>
+#include <klocale.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <kdesktopfile.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <stdlib.h>
+
+void qt_generate_epsf( bool b );
+
+static const KCmdLineOptions options[]=
+{
+ {"print", I18N_NOOP("Only print and exit"),0},
+ {"template", I18N_NOOP("Open a new document with a template"), 0},
+ {"dpi <dpiX,dpiY>", I18N_NOOP("Override display DPI"), 0},
+ KCmdLineLastOption
+};
+
+bool KoApplication::m_starting = true;
+
+class KoApplicationPrivate
+{
+public:
+ KoApplicationPrivate() {
+ m_appIface = 0L;
+ }
+ KoApplicationIface *m_appIface; // to avoid a leak
+};
+
+KoApplication::KoApplication()
+ : KApplication( initHack() )
+{
+ d = new KoApplicationPrivate;
+
+ // Initialize all KOffice directories etc.
+ KoGlobal::initialize();
+
+ // Prepare a DCOP interface
+ d->m_appIface = new KoApplicationIface;
+ dcopClient()->setDefaultObject( d->m_appIface->objId() );
+
+ m_starting = true;
+}
+
+// This gets called before entering KApplication::KApplication
+bool KoApplication::initHack()
+{
+ KCmdLineArgs::addCmdLineOptions( options, I18N_NOOP("KOffice"), "koffice", "kde" );
+ return true;
+}
+
+// Small helper for start() so that we don't forget to reset m_starting before a return
+class KoApplication::ResetStarting
+{
+public:
+ ~ResetStarting() {
+ KoApplication::m_starting = false;
+ }
+};
+
+bool KoApplication::start()
+{
+ ResetStarting resetStarting; // reset m_starting to false when we're done
+ Q_UNUSED( resetStarting );
+
+ // Find the *.desktop file corresponding to the kapp instance name
+ KoDocumentEntry entry = KoDocumentEntry( KoDocument::readNativeService() );
+ if ( entry.isEmpty() )
+ {
+ kdError( 30003 ) << instanceName() << "part.desktop not found." << endl;
+ kdError( 30003 ) << "Run 'kde-config --path services' to see which directories were searched, assuming kde startup had the same environment as your current shell." << endl;
+ kdError( 30003 ) << "Check your installation (did you install KOffice in a different prefix than KDE, without adding the prefix to /etc/kderc ?)" << endl;
+ return false;
+ }
+
+ // Get the command line arguments which we have to parse
+ KCmdLineArgs *args= KCmdLineArgs::parsedArgs();
+ int argsCount = args->count();
+
+ KCmdLineArgs *koargs = KCmdLineArgs::parsedArgs("koffice");
+ QCString dpiValues = koargs->getOption( "dpi" );
+ if ( !dpiValues.isEmpty() ) {
+ int sep = dpiValues.find( QRegExp( "[x, ]" ) );
+ int dpiX;
+ int dpiY = 0;
+ bool ok = true;
+ if ( sep != -1 ) {
+ dpiY = dpiValues.mid( sep+1 ).toInt( &ok );
+ dpiValues.truncate( sep );
+ }
+ if ( ok ) {
+ dpiX = dpiValues.toInt( &ok );
+ if ( ok ) {
+ if ( !dpiY ) dpiY = dpiX;
+ KoGlobal::setDPI( dpiX, dpiY );
+ }
+ }
+ }
+
+ // No argument -> create an empty document
+ if ( !argsCount ) {
+ KoDocument* doc = entry.createDoc( 0, "Document" );
+ if ( !doc )
+ return false;
+ KoMainWindow *shell = new KoMainWindow( doc->instance() );
+ shell->show();
+ QObject::connect(doc, SIGNAL(sigProgress(int)), shell, SLOT(slotProgress(int)));
+ // for initDoc to fill in the recent docs list
+ // and for KoDocument::slotStarted
+ doc->addShell( shell );
+
+ if ( doc->checkAutoSaveFile() ) {
+ shell->setRootDocument( doc );
+ } else {
+ doc->showStartUpWidget( shell );
+ }
+
+ // FIXME This needs to be moved someplace else
+ QObject::disconnect(doc, SIGNAL(sigProgress(int)), shell, SLOT(slotProgress(int)));
+ } else {
+ bool print = koargs->isSet("print");
+ bool doTemplate = koargs->isSet("template");
+ koargs->clear();
+
+ // Loop through arguments
+
+ short int n=0; // number of documents open
+ short int nPrinted = 0;
+ for(int i=0; i < argsCount; i++ )
+ {
+ // For now create an empty document
+ KoDocument* doc = entry.createDoc( 0 );
+ if ( doc )
+ {
+ // show a shell asap
+ KoMainWindow *shell = new KoMainWindow( doc->instance() );
+ if (!print)
+ shell->show();
+ // are we just trying to open a template?
+ if ( doTemplate ) {
+ QStringList paths;
+ if ( args->url(i).isLocalFile() && QFile::exists(args->url(i).path()) )
+ {
+ paths << QString(args->url(i).path());
+ kdDebug(30003) << "using full path..." << endl;
+ } else {
+ QString desktopName(args->arg(i));
+ QString appName = KGlobal::instance()->instanceName();
+
+ paths = KGlobal::dirs()->findAllResources("data", appName +"/templates/*/" + desktopName );
+ if ( paths.isEmpty()) {
+ paths = KGlobal::dirs()->findAllResources("data", appName +"/templates/" + desktopName );
+ }
+ if ( paths.isEmpty()) {
+ KMessageBox::error(0L, i18n("No template found for: %1 ").arg(desktopName) );
+ delete shell;
+ } else if ( paths.count() > 1 ) {
+ KMessageBox::error(0L, i18n("Too many templates found for: %1").arg(desktopName) );
+ delete shell;
+ }
+ }
+
+ if ( !paths.isEmpty() ) {
+ KURL templateBase;
+ templateBase.setPath(paths[0]);
+ KDesktopFile templateInfo(paths[0]);
+
+ QString templateName = templateInfo.readURL();
+ KURL templateURL;
+ templateURL.setPath( templateBase.directory() + "/" + templateName );
+ if ( shell->openDocument(doc, templateURL )) {
+ doc->resetURL();
+ doc->setEmpty();
+ doc->setTitleModified();
+ kdDebug(30003) << "Template loaded..." << endl;
+ n++;
+ } else {
+ KMessageBox::error(0L, i18n("Template %1 failed to load.").arg(templateURL.prettyURL()) );
+ delete shell;
+ }
+ }
+ // now try to load
+ } else if ( shell->openDocument( doc, args->url(i) ) ) {
+ if ( print ) {
+ shell->print(false /*we want to get the dialog*/);
+ // delete shell; done by ~KoDocument
+ nPrinted++;
+ } else {
+ // Normal case, success
+ n++;
+ }
+ } else {
+ // .... if failed
+ // delete doc; done by openDocument
+ // delete shell; done by ~KoDocument
+ }
+ }
+ }
+ if ( print )
+ return nPrinted > 0;
+ if (n == 0) // no doc, e.g. all URLs were malformed
+ return false;
+ }
+
+ args->clear();
+ // not calling this before since the program will quit there.
+ return true;
+}
+
+KoApplication::~KoApplication()
+{
+ delete d->m_appIface;
+ delete d;
+}
+
+bool KoApplication::isStarting()
+{
+ return KoApplication::m_starting;
+}
+
+#include <KoApplication.moc>
diff --git a/lib/kofficecore/KoApplication.h b/lib/kofficecore/KoApplication.h
new file mode 100644
index 00000000..ecd9b943
--- /dev/null
+++ b/lib/kofficecore/KoApplication.h
@@ -0,0 +1,85 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ 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 __ko_app_h__
+#define __ko_app_h__
+
+namespace std { }
+using namespace std;
+#include <kapplication.h>
+#include <koffice_export.h>
+
+class KoApplicationPrivate;
+
+/**
+ * @brief Base class for all %KOffice apps
+ *
+ * This class handles arguments given on the command line and
+ * shows a generic about dialog for all KOffice apps.
+ *
+ * In addition it adds the standard directories where KOffice applications
+ * can find their images etc.
+ *
+ * If the last mainwindow becomes closed, KoApplication automatically
+ * calls QApplication::quit.
+ */
+class KOFFICECORE_EXPORT KoApplication : public KApplication
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Creates an application object, adds some standard directories and
+ * initializes kimgio.
+ */
+ KoApplication();
+
+ /**
+ * Destructor.
+ */
+ virtual ~KoApplication();
+
+ // ######### Bad name
+ /**
+ * Call this to start the application.
+ *
+ * Parses command line arguments and creates the initial shells and docs
+ * from them (or an empty doc if no cmd-line argument is specified ).
+ *
+ * You must call this method directly before calling QApplication::exec.
+ *
+ * It is valid behaviour not to call this method at all. In this case you
+ * have to process your command line parameters by yourself.
+ */
+ virtual bool start();
+
+ /**
+ * @return true if the application is starting
+ */
+ static bool isStarting();
+
+private:
+ bool initHack();
+ KoApplicationPrivate* d;
+ static bool m_starting ; ///< is the application starting or not
+ class ResetStarting;
+ friend class ResetStarting;
+};
+
+#endif
diff --git a/lib/kofficecore/KoApplicationIface.cc b/lib/kofficecore/KoApplicationIface.cc
new file mode 100644
index 00000000..eb6f4d2d
--- /dev/null
+++ b/lib/kofficecore/KoApplicationIface.cc
@@ -0,0 +1,97 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ 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.
+
+ 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 <dcopclient.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "KoApplication.h"
+#include "KoApplicationIface.h"
+#include "KoDocument.h"
+#include "KoDocumentIface.h"
+#include "KoMainWindow.h"
+#include "KoQueryTrader.h"
+#include "KoView.h"
+
+KoApplicationIface::KoApplicationIface()
+ : DCOPObject( "KoApplicationIface" )
+{
+}
+
+KoApplicationIface::~KoApplicationIface()
+{
+}
+
+DCOPRef KoApplicationIface::createDocument( const QString &nativeFormat )
+{
+ KoDocumentEntry entry = KoDocumentEntry::queryByMimeType( nativeFormat );
+ if ( entry.isEmpty() )
+ {
+ KMessageBox::questionYesNo( 0, i18n( "Unknown KOffice MimeType %s. Check your installation." ).arg( nativeFormat ) );
+ return DCOPRef();
+ }
+ KoDocument* doc = entry.createDoc( 0 );
+ return DCOPRef( kapp->dcopClient()->appId(), doc->dcopObject()->objId() );
+}
+
+QValueList<DCOPRef> KoApplicationIface::getDocuments()
+{
+ QValueList<DCOPRef> lst;
+ QPtrList<KoDocument> *documents = KoDocument::documentList();
+ if ( documents )
+ {
+ QPtrListIterator<KoDocument> it( *documents );
+ for (; it.current(); ++it )
+ lst.append( DCOPRef( kapp->dcopClient()->appId(), it.current()->dcopObject()->objId() ) );
+ }
+ return lst;
+}
+
+QValueList<DCOPRef> KoApplicationIface::getViews()
+{
+ QValueList<DCOPRef> lst;
+ QPtrList<KoDocument> *documents = KoDocument::documentList();
+ if ( documents )
+ {
+ QPtrListIterator<KoDocument> it( *documents );
+ for (; it.current(); ++it )
+ {
+ QPtrListIterator<KoView> itview( it.current()->views() );
+ for ( ; itview.current(); ++itview )
+ lst.append( DCOPRef( kapp->dcopClient()->appId(), itview.current()->dcopObject()->objId() ) );
+ }
+ }
+ return lst;
+}
+
+QValueList<DCOPRef> KoApplicationIface::getWindows()
+{
+ QValueList<DCOPRef> lst;
+ QPtrList<KMainWindow> *mainWindows = KMainWindow::memberList;
+ if ( mainWindows )
+ {
+ QPtrListIterator<KMainWindow> it( *mainWindows );
+ for (; it.current(); ++it )
+ lst.append( DCOPRef( kapp->dcopClient()->appId(),
+ static_cast<KoMainWindow *>(it.current())->dcopObject()->objId() ) );
+ }
+ return lst;
+}
diff --git a/lib/kofficecore/KoApplicationIface.h b/lib/kofficecore/KoApplicationIface.h
new file mode 100644
index 00000000..ac72f09a
--- /dev/null
+++ b/lib/kofficecore/KoApplicationIface.h
@@ -0,0 +1,66 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ 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.
+
+ 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 __KoApplicationIface_h__
+#define __KoApplicationIface_h__
+
+#include <dcopobject.h>
+#include <qvaluelist.h>
+#include <dcopref.h>
+
+/**
+ * DCOP interface for any KOffice application (entry point)
+ */
+class KoApplicationIface : public DCOPObject
+{
+ K_DCOP
+public:
+
+ KoApplicationIface();
+ ~KoApplicationIface();
+
+k_dcop:
+ /**
+ * Creates a new document for the given native mimetype
+ * Use it to create a shell and to load an existing file, if any
+ */
+ DCOPRef createDocument( const QString &nativeFormat );
+
+ /**
+ * @return a list of references to all the documents
+ * (see KoDocumentIface)
+ */
+ QValueList<DCOPRef> getDocuments();
+
+ /**
+ * @return a list of references to all the views
+ * (see KoViewIface)
+ * Convenience method to avoid iterating over all documents to get all the views.
+ */
+ QValueList<DCOPRef> getViews();
+
+ /**
+ * @return a list of references to all the windows
+ * (see KoMainWindowIface)
+ */
+ QValueList<DCOPRef> getWindows();
+};
+
+#endif
+
diff --git a/lib/kofficecore/KoChild.cpp b/lib/kofficecore/KoChild.cpp
new file mode 100644
index 00000000..8207d086
--- /dev/null
+++ b/lib/kofficecore/KoChild.cpp
@@ -0,0 +1,366 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ 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.
+*/
+
+#include <KoChild.h>
+
+#include <qpainter.h>
+
+#include <kdebug.h>
+
+class KoChild::KoChildPrivate
+{
+public:
+ KoChildPrivate()
+ {
+ m_contentsX = m_contentsY = 0;
+ }
+ ~KoChildPrivate()
+ {
+ }
+
+ QRect m_geometry;
+
+ double m_rotation;
+ double m_shearX;
+ double m_shearY;
+ QPoint m_rotationPoint;
+ double m_scaleX;
+ double m_scaleY;
+ QWMatrix m_matrix;
+ bool m_lock;
+ QPointArray m_old;
+ bool m_transparent;
+ int m_contentsX;
+ int m_contentsY;
+};
+
+KoChild::KoChild( QObject *parent, const char *name )
+: QObject( parent, name )
+{
+ d = new KoChildPrivate;
+
+ d->m_scaleX = d->m_scaleY = 1.0;
+ d->m_shearX = d->m_shearY = 0.0;
+ d->m_rotation = 0.0;
+ d->m_lock = false;
+ d->m_transparent = false;
+
+ updateMatrix();
+}
+
+KoChild::~KoChild()
+{
+ delete d;
+}
+
+void KoChild::setGeometry( const QRect &rect, bool noEmit )
+{
+ if ( !d->m_lock )
+ d->m_old = framePointArray();
+
+ d->m_geometry = rect;
+
+ // Embedded objects should have a minimum size of 3, so they can be selected
+ if( d->m_geometry.width() < 3 )
+ d->m_geometry.setWidth( 3 );
+
+ if( d->m_geometry.height() < 3 )
+ d->m_geometry.setHeight( 3 );
+
+ updateMatrix();
+
+ if ( !d->m_lock && !noEmit )
+ emit changed( this );
+}
+
+QRect KoChild::geometry() const
+{
+ return d->m_geometry;
+}
+
+QRegion KoChild::region( const QWMatrix &matrix ) const
+{
+ return QRegion( pointArray( matrix ) );
+}
+
+QPointArray KoChild::pointArray( const QWMatrix &matrix ) const
+{
+ return pointArray( QRect( 0, 0, d->m_geometry.width(), d->m_geometry.height() ), matrix );
+}
+
+//bool KoChild::contains( const QPoint &point ) const
+//{
+// return region().contains( point );
+//}
+
+QRect KoChild::boundingRect() const
+{
+ return pointArray().boundingRect();
+}
+
+bool KoChild::isRectangle() const
+{
+ return !( d->m_shearX != 0.0 || d->m_shearY != 0.0 || d->m_rotation != 0.0 );
+}
+
+void KoChild::setClipRegion( QPainter &painter, bool combine )
+{
+ painter.setClipping( true );
+ if ( combine && !painter.clipRegion().isEmpty() )
+ painter.setClipRegion( region( painter.worldMatrix() ).intersect( painter.clipRegion() ) );
+ else
+ painter.setClipRegion( region( painter.worldMatrix() ) );
+}
+
+void KoChild::setScaling( double x, double y )
+{
+ if ( !d->m_lock )
+ d->m_old = framePointArray();
+
+ d->m_scaleX = x;
+ d->m_scaleY = y;
+
+ // why is that commented out? (Simon)
+ // This is commented out, because KoChild::transform() scales
+ // the world matrix explicitly and updateMatrix() doesn't even
+ // handle scaling (Werner)
+ //updateMatrix()
+
+ if ( !d->m_lock )
+ emit changed( this );
+}
+
+double KoChild::xScaling() const
+{
+ return d->m_scaleX;
+}
+
+double KoChild::yScaling() const
+{
+ return d->m_scaleY;
+}
+
+void KoChild::setShearing( double x, double y )
+{
+ if ( !d->m_lock )
+ d->m_old = framePointArray();
+
+ d->m_shearX = x;
+ d->m_shearY = y;
+
+ updateMatrix();
+
+ if ( !d->m_lock )
+ emit changed( this );
+}
+
+double KoChild::xShearing() const
+{
+ return d->m_shearX;
+}
+
+double KoChild::yShearing() const
+{
+ return d->m_shearY;
+}
+
+void KoChild::setRotation( double rot )
+{
+ if ( !d->m_lock )
+ d->m_old = framePointArray();
+
+ d->m_rotation = rot;
+ updateMatrix();
+
+ if ( !d->m_lock )
+ emit changed( this );
+}
+
+double KoChild::rotation() const
+{
+ return d->m_rotation;
+}
+
+void KoChild::setRotationPoint( const QPoint &pos )
+{
+ if ( !d->m_lock )
+ d->m_old = framePointArray();
+
+ d->m_rotationPoint = pos;
+ updateMatrix();
+
+ if ( !d->m_lock )
+ emit changed( this );
+}
+
+QPoint KoChild::rotationPoint() const
+{
+ return d->m_rotationPoint;
+}
+
+void KoChild::transform( QPainter &painter )
+{
+ setClipRegion( painter, true );
+
+ QWMatrix m = painter.worldMatrix();
+ m = d->m_matrix * m;
+ m.scale( d->m_scaleX, d->m_scaleY );
+ painter.setWorldMatrix( m );
+}
+
+void KoChild::setContentsPos( int x, int y )
+{
+ d->m_contentsX = x;
+ d->m_contentsY = y;
+}
+
+QRect KoChild::contentRect() const
+{
+ return QRect( d->m_contentsX, d->m_contentsY, int(d->m_geometry.width() / d->m_scaleX),
+ int(d->m_geometry.height() / d->m_scaleY) );
+}
+
+QPointArray KoChild::framePointArray( const QWMatrix &matrix ) const
+{
+ return pointArray( QRect( -6, -6, d->m_geometry.width() + 12, d->m_geometry.height() + 12 ), matrix );
+}
+
+QRegion KoChild::frameRegion( const QWMatrix &matrix, bool solid ) const
+{
+ const QPointArray arr = framePointArray( matrix );
+ const QRegion frameReg( arr );
+
+ if ( solid )
+ return frameReg;
+
+ const QRegion reg = region( matrix );
+ return frameReg.subtract( reg );
+}
+
+QPointArray KoChild::pointArray( const QRect &r, const QWMatrix &matrix ) const
+{
+ QPoint topleft = d->m_matrix.map( QPoint( r.left(), r.top() ) );
+ QPoint topright = d->m_matrix.map( QPoint( r.right(), r.top() ) );
+ QPoint bottomleft = d->m_matrix.map( QPoint( r.left(), r.bottom() ) );
+ QPoint bottomright = d->m_matrix.map( QPoint( r.right(), r.bottom() ) );
+
+ QPointArray arr( 4 );
+ arr.setPoint( 0, topleft );
+ arr.setPoint( 1, topright );
+ arr.setPoint( 2, bottomright );
+ arr.setPoint( 3, bottomleft );
+
+ for( int i = 0; i < 4; ++i )
+ arr.setPoint( i, matrix.map( arr.point( i ) ) );
+
+ return arr;
+}
+
+void KoChild::updateMatrix()
+{
+ QWMatrix r;
+ r.rotate( - d->m_rotation );
+ QPoint p = r.map( QPoint( d->m_rotationPoint.x(),
+ d->m_rotationPoint.y() ) );
+
+ QWMatrix m;
+ m.rotate( d->m_rotation );
+ m.translate( -d->m_rotationPoint.x() + d->m_geometry.x(), -d->m_rotationPoint.y() + d->m_geometry.y() );
+ m.translate( p.x(), p.y() );
+ m.shear( d->m_shearX, d->m_shearY );
+
+ d->m_matrix = m;
+}
+
+QWMatrix KoChild::matrix() const
+{
+ return d->m_matrix;
+}
+
+void KoChild::lock()
+{
+ if ( d->m_lock )
+ return;
+
+ d->m_old = framePointArray();
+ d->m_lock = true;
+}
+
+void KoChild::unlock()
+{
+ if ( !d->m_lock )
+ return;
+
+ d->m_lock = false;
+ emit changed( this );
+}
+
+bool KoChild::locked() const
+{
+ return d->m_lock;
+}
+
+QPointArray KoChild::oldPointArray( const QWMatrix &matrix )
+{
+ QPointArray arr = d->m_old;
+
+ for( int i = 0; i < 4; ++i )
+ arr.setPoint( i, matrix.map( arr.point( i ) ) );
+
+ return arr;
+}
+
+void KoChild::setTransparent( bool transparent )
+{
+ d->m_transparent = transparent;
+}
+
+bool KoChild::isTransparent() const
+{
+ return d->m_transparent;
+}
+
+KoChild::Gadget KoChild::gadgetHitTest( const QPoint &p )
+{
+ if ( !frameRegion().contains( p ) )
+ return NoGadget;
+
+ if ( QRegion( pointArray( QRect( -5, -5, 5, 5 ) ) ).contains( p ) )
+ return TopLeft;
+ if ( QRegion( pointArray( QRect( d->m_geometry.width() / 2 - 3, -5, 5, 5 ) ) ).contains( p ) )
+ return TopMid;
+ if ( QRegion( pointArray( QRect( d->m_geometry.width(), -5, 5, 5 ) ) ).contains( p ) )
+ return TopRight;
+ if ( QRegion( pointArray( QRect( -5, d->m_geometry.height() / 2 - 3, 5, 5 ) ) ).contains( p ) )
+ return MidLeft;
+ if ( QRegion( pointArray( QRect( -5, d->m_geometry.height(), 5, 5 ) ) ).contains( p ) )
+ return BottomLeft;
+ if ( QRegion( pointArray( QRect( d->m_geometry.width() / 2 - 3,
+ d->m_geometry.height(), 5, 5 ) ) ).contains( p ) )
+ return BottomMid;
+ if ( QRegion( pointArray( QRect( d->m_geometry.width(), d->m_geometry.height(), 5, 5 ) ) ).contains( p ) )
+ return BottomRight;
+ if ( QRegion( pointArray( QRect( d->m_geometry.width(),
+ d->m_geometry.height() / 2 - 3, 5, 5 ) ) ).contains( p ) )
+ return MidRight;
+
+ return Move;
+}
+
+#include <KoChild.moc>
diff --git a/lib/kofficecore/KoChild.h b/lib/kofficecore/KoChild.h
new file mode 100644
index 00000000..26ad4a08
--- /dev/null
+++ b/lib/kofficecore/KoChild.h
@@ -0,0 +1,312 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ 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 __koChild_h__
+#define __koChild_h__
+
+#include <qobject.h>
+#include <qwmatrix.h>
+#include <koffice_export.h>
+
+/**
+ * KoChild is an abstract base class that represents the geometry
+ * associated with an embedded document. In general it handles its position
+ * relative to the embedded document's parent.
+ *
+ * In detail it handles size, matrix operations and can give you
+ * a clip region. It can deal with scaling, rotation etc. because it
+ * makes heavy usage of QWMatrix.
+ *
+ * After applying the matrix, viewGeometry() applies zooming, but can be
+ * reimplemented to also apply e.g. some translation by the application
+ * (e.g. for centering the page).
+ *
+ * @see KoDocumentChild KoViewChild
+ */
+class KOFFICECORE_EXPORT KoChild : public QObject
+{
+ Q_OBJECT
+public:
+
+ /**
+ * The gadget generally identifies where a child has been hit (generally
+ * by the mouse pointer).
+ * Based on this information different actions can be taken, for example
+ * moving the child or opening a context menu. NoGadget means that
+ * this child has not been hit.
+ *
+ * @see #gadgetHitTest
+ */
+ enum Gadget { NoGadget, TopLeft, TopMid, TopRight, MidLeft, MidRight,
+ BottomLeft, BottomMid, BottomRight, Move };
+
+ KoChild( QObject *parent = 0, const char *name = 0 );
+ virtual ~KoChild();
+
+ /**
+ * Sets a new geometry for this child document.
+ * Use noEmit = true if you do not want the 'changed'-signal to be emitted
+ */
+ void setGeometry( const QRect &rect, bool noEmit = false );
+
+ /**
+ * @return the rectangle that would be used to display this
+ * child document if the child is not rotated or
+ * subject to some other geometric transformation.
+ * The rectangle is in the coordinate system of the parent,
+ * using unzoomed coordinates in points.
+ *
+ * @see #setGeometry
+ */
+ QRect geometry() const;
+
+ /**
+ * @return the region of this child part relative to the
+ * coordinate system of the parent.
+ * The region is transformed with the passed
+ * matrix.
+ */
+ virtual QRegion region( const QWMatrix& = QWMatrix() ) const;
+
+ /**
+ * @return the polygon which surrounds the child part. The points
+ * are in coordinates of the parent.
+ * The points are transformed with the
+ * passed matrix.
+ */
+ virtual QPointArray pointArray( const QWMatrix &matrix = QWMatrix() ) const;
+
+ /**
+ * Tests whether the part contains a certain point. The point is
+ * in the coordinate system of the parent.
+ */
+ //virtual bool contains( const QPoint& ) const;
+
+ /**
+ * @return the effective bounding rect after all transformations.
+ * The coordinates of the rectangle are in the coordinate system
+ * of the parent.
+ */
+ QRect boundingRect() const;
+
+ /**
+ * Scales the content of the child part. However, that does not
+ * affect the size of the child part.
+ */
+ virtual void setScaling( double x, double y );
+
+ /**
+ * @return the x axis scaling of the child part
+ */
+ virtual double xScaling() const;
+
+ /**
+ * @return the y axis scaling of the child part
+ */
+ virtual double yScaling() const;
+
+ /**
+ * Shears the content of the child part.
+ */
+ virtual void setShearing( double x, double y );
+
+ /**
+ * @return the x axis shearing of the child part
+ */
+ virtual double xShearing() const;
+
+ /**
+ * @return the y axis shearing of the child part
+ */
+ virtual double yShearing() const;
+
+ /**
+ * Sets the angle of rotation.
+ */
+ virtual void setRotation( double );
+
+ /**
+ * @return the angle of rotation
+ */
+ virtual double rotation() const;
+
+ /**
+ * Sets the center of the rotation to the point @p pos.
+ */
+ virtual void setRotationPoint( const QPoint& pos );
+
+ /**
+ * @return the center of the rotation
+ */
+ virtual QPoint rotationPoint() const;
+
+ /**
+ * @return true if the child part is an orthogonal rectangle relative
+ * to its parents coordinate system.
+ */
+ bool isRectangle() const;
+
+ /**
+ * Sets the clip region of the painter, so that only pixels of the
+ * child part can be drawn.
+ *
+ * @param painter the painter do modify.
+ * @param combine tells whether the new clip region is an intersection
+ * of the current region with the childs region or whether only
+ * the childs region is set.
+ */
+ virtual void setClipRegion( QPainter& painter, bool combine = true );
+
+ /**
+ * Transforms the painter (its worldmatrix and the clipping)
+ * in such a way that the painter can be passed to the child part
+ * for drawing.
+ */
+ virtual void transform( QPainter& painter );
+
+ /**
+ * Sets the position of the content relative to the child frame.
+ * This can be used to create a border between the frame border
+ * and the actual content.
+ */
+ virtual void setContentsPos( int x, int y );
+
+ /**
+ * @return the contents rectangle that is visible.
+ * This value depends on the scaling and the geometry.
+ * This is the value that is passed to KoDocument::paintContent.
+ *
+ * @see #xScaling #geometry
+ */
+ virtual QRect contentRect() const;
+
+ /**
+ * @return the region of the child frame.
+ * If solid is set to true the complete area of the child region
+ * is returned, otherwise only the child border is returned.
+ */
+ virtual QRegion frameRegion( const QWMatrix& matrix = QWMatrix(), bool solid = false ) const;
+
+ /**
+ * @return the frame geometry including a border (6 pixels) as a point
+ * array with 4 points, one for each corner, transformed by given matrix.
+ */
+ virtual QPointArray framePointArray( const QWMatrix &matrix = QWMatrix() ) const;
+
+ /**
+ * @return the current transformation of this child as matrix.
+ * This includes translation, scale, rotation, shearing.
+ *
+ * @see #updateMatrix
+ */
+ virtual QWMatrix matrix() const;
+
+ /**
+ * Locks this child and stores the current transformation.
+ * A locked child does not emit changed signals.
+ *
+ * This is useful if a series of changes are done on this
+ * child and only the final result is of interest (GUI updating,...).
+ *
+ * @see #locked #unlock
+ */
+ void lock();
+
+ /**
+ * Unlocks this child and emits a changed signal.
+ */
+ void unlock();
+
+ /**
+ * If the child is locked, geometry changes
+ * (including scaling, rotation, ...) are not backed up.
+ *
+ * As long as this child is locked, the backed up
+ * geometry state can be recovered with oldPointArray.
+ *
+ * @return true when this child is locked.
+ *
+ * @see #locked #unlock #oldPointArray
+ */
+ bool locked() const;
+
+ /**
+ * @return the backed up geometry transformed by given matrix.
+ */
+ virtual QPointArray oldPointArray( const QWMatrix &matrix );
+
+ /**
+ * Marks this child as either transparent or not.
+ * @param transparent set this child to transparent (true)
+ * or opaque (false).
+ *
+ * @see #isTransparent
+ */
+ virtual void setTransparent( bool transparent );
+
+ /**
+ * It might be interesting for view updates and repainting in general
+ * whether a child is transparent or not.
+ * @return true when this child is marked as transparent.
+ */
+ virtual bool isTransparent() const;
+
+ /**
+ * Different actions are taken depending on where a child frame is
+ * hit. Two gadgets are known: one for the border (5 pixels) and one
+ * for the inner area.
+ * @return the gadget identification for the hit area.
+ * @param p the hit position.
+ *
+ * @see #Gadget
+ */
+ virtual Gadget gadgetHitTest( const QPoint& p );
+
+signals:
+
+ /**
+ * Emitted every time this child changes, but only if this child is not
+ * locked.
+ * @see #locked
+ */
+ void changed( KoChild *thisChild );
+
+protected:
+
+ /**
+ * @return point array with the 4 corners of given rectangle, which is
+ * transformed by given matrix.
+ *
+ * @param matrix the transformation of r.
+ * @param r the rectangle for which the point array should be created.
+ */
+ virtual QPointArray pointArray( const QRect& r, const QWMatrix& matrix = QWMatrix() ) const;
+
+ /**
+ * Stores the current transformation of this child into a matrix.
+ *
+ * @see #matrix
+ */
+ virtual void updateMatrix();
+private:
+
+ class KoChildPrivate;
+ KoChildPrivate *d;
+};
+
+#endif
diff --git a/lib/kofficecore/KoContainerHandler.cpp b/lib/kofficecore/KoContainerHandler.cpp
new file mode 100644
index 00000000..78060592
--- /dev/null
+++ b/lib/kofficecore/KoContainerHandler.cpp
@@ -0,0 +1,395 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
+
+ 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.
+*/
+
+#include "KoContainerHandler.h"
+#include <KoView.h>
+#include <math.h>
+#include <kcursor.h>
+#include <kdebug.h>
+
+KoEventHandler::KoEventHandler( QObject* target )
+{
+ m_target = target;
+
+ m_target->installEventFilter( this );
+}
+
+KoEventHandler::~KoEventHandler()
+{
+}
+
+QObject* KoEventHandler::target()
+{
+ return m_target;
+}
+
+// ------------------------------------------------------
+
+class KoPartResizeHandlerPrivate {
+public:
+ KoPartResizeHandlerPrivate( const QWMatrix& matrix, KoView *view, KoChild* child,
+ KoChild::Gadget gadget, const QPoint& point ) :
+ m_gadget(gadget), m_view(view), m_child(child), m_parentMatrix(matrix) {
+
+ m_geometryStart = child->geometry();
+ m_matrix = child->matrix() * matrix;
+ m_invertParentMatrix = matrix.invert();
+
+ bool ok = true;
+ m_invert = m_matrix.invert( &ok );
+ Q_ASSERT( ok );
+ m_mouseStart = m_invert.map( m_invertParentMatrix.map( point ) );
+ }
+ ~KoPartResizeHandlerPrivate() {}
+
+ KoChild::Gadget m_gadget;
+ QPoint m_mouseStart;
+ QRect m_geometryStart;
+ KoView* m_view;
+ KoChild* m_child;
+ QWMatrix m_invert;
+ QWMatrix m_matrix;
+ QWMatrix m_parentMatrix;
+ QWMatrix m_invertParentMatrix;
+};
+
+KoPartResizeHandler::KoPartResizeHandler( QWidget* widget, const QWMatrix& matrix, KoView* view, KoChild* child,
+ KoChild::Gadget gadget, const QPoint& point )
+ : KoEventHandler( widget )
+{
+ child->lock();
+ d=new KoPartResizeHandlerPrivate(matrix, view, child, gadget, point);
+}
+
+KoPartResizeHandler::~KoPartResizeHandler()
+{
+ d->m_child->unlock();
+ delete d;
+ d=0L;
+}
+
+void KoPartResizeHandler::repaint(QRegion &rgn)
+{
+ rgn = rgn.unite( d->m_child->frameRegion( d->m_parentMatrix, true ) );
+ // rgn.translate(- d->m_view->canvasXOffset(), - d->m_view->canvasYOffset());
+ ((QWidget*)target())->repaint( rgn );
+}
+
+bool KoPartResizeHandler::eventFilter( QObject*, QEvent* ev )
+{
+ if ( ev->type() == QEvent::MouseButtonRelease )
+ {
+ delete this;
+ return true;
+ }
+ else if ( ev->type() == QEvent::MouseMove )
+ {
+ QMouseEvent* e = (QMouseEvent*)ev;
+ QPoint p = d->m_invert.map( d->m_invertParentMatrix.map( e->pos() /*+ QPoint(d->m_view->canvasXOffset(), d->m_view->canvasYOffset()) */ ) );
+ QRegion rgn( d->m_child->frameRegion( d->m_parentMatrix, true ) );
+
+ double x1_x, x1_y, x2_x, x2_y;
+ d->m_matrix.map( double( p.x() ), 0.0, &x1_x, &x1_y );
+ d->m_matrix.map( double( d->m_mouseStart.x() ), 0.0, &x2_x, &x2_y );
+ double y1_x, y1_y, y2_x, y2_y;
+ d->m_matrix.map( 0.0, double( p.y() ), &y1_x, &y1_y );
+ d->m_matrix.map( 0.0, double( d->m_mouseStart.y() ), &y2_x, &y2_y );
+
+ double dx = x2_x - x1_x;
+ double dy = x2_y - x1_y;
+ int x = int( sqrt( dx * dx + dy * dy ) * ( d->m_mouseStart.x() < p.x() ? 1.0 : -1.0 ) );
+
+ dx = y2_x - y1_x;
+ dy = y2_y - y1_y;
+ int y = int( sqrt( dx * dx + dy * dy ) * ( d->m_mouseStart.y() < p.y() ? 1.0 : -1.0 ) );
+
+ switch( d->m_gadget )
+ {
+ case KoChild::TopLeft:
+ {
+ x = QMIN( d->m_geometryStart.width() - 1, x );
+ y = QMIN( d->m_geometryStart.height() - 1, y );
+
+ d->m_child->setGeometry( QRect( d->m_geometryStart.x() + x, d->m_geometryStart.y() + y,
+ d->m_geometryStart.width() - x, d->m_geometryStart.height() - y ) );
+ repaint(rgn);
+ }
+ break;
+ case KoChild::TopMid:
+ {
+ y = QMIN( d->m_geometryStart.height() - 1, y );
+
+ d->m_child->setGeometry( QRect( d->m_geometryStart.x(), d->m_geometryStart.y() + y,
+ d->m_geometryStart.width(), d->m_geometryStart.height() - y ) );
+ repaint(rgn);
+ }
+ break;
+ case KoChild::TopRight:
+ {
+ x = QMAX( -d->m_geometryStart.width() + 1, x );
+ y = QMIN( d->m_geometryStart.height() - 1, y );
+
+ d->m_child->setGeometry( QRect( d->m_geometryStart.x(), d->m_geometryStart.y() + y,
+ d->m_geometryStart.width() + x, d->m_geometryStart.height() - y ) );
+ repaint(rgn);
+ }
+ break;
+ case KoChild::MidLeft:
+ {
+ x = QMIN( d->m_geometryStart.width() - 1, x );
+
+ d->m_child->setGeometry( QRect( d->m_geometryStart.x() + x, d->m_geometryStart.y(),
+ d->m_geometryStart.width() - x, d->m_geometryStart.height() ) );
+ repaint(rgn);
+ }
+ break;
+ case KoChild::MidRight:
+ {
+ x = QMAX( -d->m_geometryStart.width() + 1, x );
+
+ d->m_child->setGeometry( QRect( d->m_geometryStart.x(), d->m_geometryStart.y(),
+ d->m_geometryStart.width() + x, d->m_geometryStart.height() ) );
+ repaint(rgn);
+ }
+ break;
+ case KoChild::BottomLeft:
+ {
+ x = QMIN( d->m_geometryStart.width() - 1, x );
+ y = QMAX( -d->m_geometryStart.height() + 1, y );
+
+ d->m_child->setGeometry( QRect( d->m_geometryStart.x() + x, d->m_geometryStart.y(),
+ d->m_geometryStart.width() - x, d->m_geometryStart.height() + y ) );
+ repaint(rgn);
+ }
+ break;
+ case KoChild::BottomMid:
+ {
+ y = QMAX( -d->m_geometryStart.height() + 1, y );
+
+ d->m_child->setGeometry( QRect( d->m_geometryStart.x(), d->m_geometryStart.y(),
+ d->m_geometryStart.width(), d->m_geometryStart.height() + y ) );
+ repaint(rgn);
+ }
+ break;
+ case KoChild::BottomRight:
+ {
+ x = QMAX( -d->m_geometryStart.width() + 1, x );
+ y = QMAX( -d->m_geometryStart.height() + 1, y );
+
+ d->m_child->setGeometry( QRect( d->m_geometryStart.x(), d->m_geometryStart.y(),
+ d->m_geometryStart.width() + x, d->m_geometryStart.height() + y ) );
+ repaint(rgn);
+ }
+ break;
+ default:
+ Q_ASSERT( 0 );
+ }
+ return true;
+ }
+ return false;
+}
+
+// --------------------------------------------------------------
+
+class KoPartMoveHandlerPrivate {
+public:
+ KoPartMoveHandlerPrivate( const QWMatrix& matrix, KoView* view, KoChild* child,
+ const QPoint& point) : m_view(view), m_dragChild(child),
+ m_parentMatrix(matrix) {
+ m_invertParentMatrix = matrix.invert();
+ m_mouseDragStart = m_invertParentMatrix.map( point );
+ m_geometryDragStart = m_dragChild->geometry();
+ m_rotationDragStart = m_dragChild->rotationPoint();
+ }
+ ~KoPartMoveHandlerPrivate() {}
+
+ KoView* m_view;
+ KoChild* m_dragChild;
+ QPoint m_mouseDragStart;
+ QRect m_geometryDragStart;
+ QPoint m_rotationDragStart;
+ QWMatrix m_invertParentMatrix;
+ QWMatrix m_parentMatrix;
+};
+
+KoPartMoveHandler::KoPartMoveHandler( QWidget* widget, const QWMatrix& matrix, KoView* view, KoChild* child,
+ const QPoint& point )
+ : KoEventHandler( widget )
+{
+ child->lock();
+ d=new KoPartMoveHandlerPrivate(matrix, view, child, point);
+}
+
+KoPartMoveHandler::~KoPartMoveHandler()
+{
+ d->m_dragChild->unlock();
+ delete d;
+ d=0L;
+}
+
+bool KoPartMoveHandler::eventFilter( QObject*, QEvent* ev )
+{
+ if ( ev->type() == QEvent::MouseButtonRelease )
+ {
+ delete this;
+ return true;
+ }
+ else if ( ev->type() == QEvent::MouseMove )
+ {
+ QMouseEvent* e = (QMouseEvent*)ev;
+
+ QRegion bound = d->m_dragChild->frameRegion( d->m_parentMatrix, true );
+ QPoint pos = d->m_invertParentMatrix.map( e->pos() /* + QPoint(d->m_view->canvasXOffset(), d->m_view->canvasYOffset()) */ );
+ d->m_dragChild->setGeometry( QRect( d->m_geometryDragStart.x() + pos.x() - d->m_mouseDragStart.x(),
+ d->m_geometryDragStart.y() + pos.y() - d->m_mouseDragStart.y(),
+ d->m_geometryDragStart.width(), d->m_geometryDragStart.height() ) );
+ d->m_dragChild->setRotationPoint( QPoint( d->m_rotationDragStart.x() + pos.x() - d->m_mouseDragStart.x(),
+ d->m_rotationDragStart.y() + pos.y() - d->m_mouseDragStart.y() ) );
+ bound = bound.unite( d->m_dragChild->frameRegion( d->m_parentMatrix, false ) );
+ // bound.translate(- d->m_view->canvasXOffset(), - d->m_view->canvasYOffset());
+ ((QWidget*)target())->repaint( bound );
+
+ return true;
+ }
+
+ return false;
+}
+
+// -------------------------------------------------------
+
+KoContainerHandler::KoContainerHandler( KoView* view, QWidget* widget )
+ : KoEventHandler( widget )
+{
+ m_view = view;
+}
+
+KoContainerHandler::~KoContainerHandler()
+{
+}
+
+bool KoContainerHandler::eventFilter( QObject*, QEvent* ev )
+{
+ if ( ev->type() == QEvent::KeyPress )
+ {
+ QKeyEvent* keyEvent=(QKeyEvent*)ev;
+
+ KoChild* child=m_view->selectedChild();
+
+ if (child != 0)
+ {
+ if (keyEvent->key()==Qt::Key_Delete)
+ emit deleteChild(child);
+ }
+ }
+
+ if ( ev->type() == QEvent::MouseButtonPress )
+ {
+ KoChild::Gadget gadget;
+ QPoint pos;
+ QMouseEvent *e=static_cast<QMouseEvent*>(ev);
+ KoChild *ch=child(gadget, pos, e);
+
+ if ( e->button() == RightButton && gadget != KoChild::NoGadget )
+ {
+ emit popupMenu( ch, e->globalPos() );
+ return true;
+ }
+ else if ( e->button() == LeftButton && gadget == KoChild::Move )
+ {
+ (void)new KoPartMoveHandler( static_cast<QWidget*>(target()), m_view->matrix(), m_view, ch, pos );
+ return true;
+ }
+ else if ( e->button() == LeftButton && gadget != KoChild::NoGadget )
+ {
+ (void)new KoPartResizeHandler( static_cast<QWidget*>(target()), m_view->matrix(), m_view, ch, gadget, pos );
+ return true;
+ }
+ return false;
+ }
+ else if ( ev->type() == QEvent::MouseMove )
+ {
+ QWidget *targetWidget = static_cast<QWidget *>( target() );
+ KoChild::Gadget gadget;
+ QPoint pos;
+ QMouseEvent *e=static_cast<QMouseEvent*>(ev);
+ child(gadget, pos, e);
+
+ bool retval = true;
+ if ( gadget == KoChild::NoGadget )
+ retval = false;
+
+ if ( gadget == KoChild::TopLeft || gadget == KoChild::BottomRight )
+ targetWidget->setCursor( sizeFDiagCursor );
+ else if ( gadget == KoChild::TopRight || gadget == KoChild::BottomLeft )
+ targetWidget->setCursor( sizeBDiagCursor );
+ else if ( gadget == KoChild::TopMid || gadget == KoChild::BottomMid )
+ targetWidget->setCursor( sizeVerCursor );
+ else if ( gadget == KoChild::MidLeft || gadget == KoChild::MidRight )
+ targetWidget->setCursor( sizeHorCursor );
+ else if ( gadget == KoChild::Move )
+ targetWidget->setCursor( KCursor::handCursor() );
+ else
+ {
+// targetWidget->setCursor( arrowCursor );
+ return false;
+ }
+ return retval;
+ }
+ return false;
+}
+
+KoChild *KoContainerHandler::child(KoChild::Gadget &gadget, QPoint &pos, const QMouseEvent *ev) {
+
+ pos = ev->pos(); //+ QPoint(m_view->canvasXOffset(), m_view->canvasYOffset());
+
+ pos = m_view->reverseViewTransformations( pos );
+
+ KoChild *child = 0;
+ KoDocumentChild* docChild = m_view->selectedChild();
+ gadget = KoChild::NoGadget;
+ if ( docChild )
+ {
+ KoViewChild *viewChild = m_view->child( docChild->document() );
+
+ if ( viewChild )
+ child = viewChild;
+ else
+ child = docChild;
+
+ gadget = child->gadgetHitTest( pos );
+ }
+ if ( gadget == KoChild::NoGadget )
+ {
+ docChild = m_view->activeChild();
+ if ( docChild )
+ {
+ KoViewChild *viewChild = m_view->child( docChild->document() );
+
+ if ( viewChild )
+ child = viewChild;
+ else
+ child = docChild;
+
+ gadget = child->gadgetHitTest( pos );
+ }
+ }
+ return child;
+}
+
+#include "KoContainerHandler.moc"
diff --git a/lib/kofficecore/KoContainerHandler.h b/lib/kofficecore/KoContainerHandler.h
new file mode 100644
index 00000000..d34d0500
--- /dev/null
+++ b/lib/kofficecore/KoContainerHandler.h
@@ -0,0 +1,137 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
+
+ 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 HANDLER_H
+#define HANDLER_H
+
+#include <qobject.h>
+#include <KoDocumentChild.h>
+
+class QWMatrix;
+
+class KoView;
+class KoPartResizeHandlerPrivate;
+class KoPartMoveHandlerPrivate;
+
+/**
+ * @brief An abstract base class for event handlers.
+ *
+ * The idea of an event handler is that it is created for a
+ * certain purpose, for example moving or resizing of a part.
+ * Once that action is finished, the handler will destroy
+ * itself.
+ *
+ * This design pattern helps you to keep your event filters
+ * and your mousePressEvent, mouseMoveEvent etc. methods clean.
+ */
+class KOFFICECORE_EXPORT KoEventHandler : public QObject
+{
+ Q_OBJECT
+public:
+ KoEventHandler( QObject* target );
+ ~KoEventHandler();
+
+ QObject* target();
+
+private:
+ QObject* m_target;
+};
+
+/**
+ * Used by @ref KoContainerHandler internally to handle resizing of
+ * embedded documents.
+ */
+class KoPartResizeHandler : public KoEventHandler
+{
+ Q_OBJECT
+public:
+ KoPartResizeHandler( QWidget* widget, const QWMatrix& matrix, KoView* view, KoChild* child,
+ KoChild::Gadget gadget, const QPoint& point );
+ ~KoPartResizeHandler();
+
+protected:
+ void repaint(QRegion &rgn);
+ bool eventFilter( QObject*, QEvent* );
+
+private:
+ KoPartResizeHandlerPrivate *d;
+};
+
+/**
+ * Used by @ref KoContainerHandler internally to handle moving of
+ * embedded documents.
+ */
+class KoPartMoveHandler : public KoEventHandler
+{
+ Q_OBJECT
+public:
+ KoPartMoveHandler( QWidget* widget, const QWMatrix& matrix, KoView* view, KoChild* child,
+ const QPoint& point );
+ ~KoPartMoveHandler();
+
+protected:
+ bool eventFilter( QObject*, QEvent* );
+
+private:
+ KoPartMoveHandlerPrivate *d;
+};
+
+/**
+ * This class can handle moving and resizing of embedded
+ * documents in your class derived from @ref KoView.
+ *
+ * Just create one instance per view of this class and parts
+ * will magically be moved around on your document.
+ *
+ * This class acts like an event filter on your view, so the
+ * mouse events which are used for parts moving and resizing
+ * will never show up in your view.
+ *
+ * @see KoPartMoveHandlerPrivate
+ * @see KoPartResizeHandlerPrivate
+ */
+class KOFFICECORE_EXPORT KoContainerHandler : public KoEventHandler
+{
+ Q_OBJECT
+public:
+ KoContainerHandler( KoView* view, QWidget* widget );
+ ~KoContainerHandler();
+
+signals:
+ /**
+ * Emitted if the user wants to open the popup menu for some
+ * child object.
+ */
+ void popupMenu( KoChild*, const QPoint& global_pos );
+
+ /**
+ * Emitted if the user pressed the delete key whilst a child was selected
+ */
+ void deleteChild( KoChild* );
+
+protected:
+ bool eventFilter( QObject*, QEvent* );
+
+private:
+ /// This is a little helper function to get rid of some duplicated code
+ KoChild *child(KoChild::Gadget &gadget, QPoint &pos, const QMouseEvent *ev);
+ KoView* m_view;
+};
+
+#endif
diff --git a/lib/kofficecore/KoDetailsPane.cpp b/lib/kofficecore/KoDetailsPane.cpp
new file mode 100644
index 00000000..d94a01f7
--- /dev/null
+++ b/lib/kofficecore/KoDetailsPane.cpp
@@ -0,0 +1,470 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+
+ 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.
+*/
+#include "KoDetailsPane.h"
+
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qfile.h>
+#include <qimage.h>
+#include <qheader.h>
+#include <qrect.h>
+#include <qbrush.h>
+#include <qpainter.h>
+#include <qsimplerichtext.h>
+#include <qevent.h>
+#include <qsplitter.h>
+
+#include <kinstance.h>
+#include <klocale.h>
+#include <klistview.h>
+#include <kpushbutton.h>
+#include <kconfig.h>
+#include <kurl.h>
+#include <kfileitem.h>
+#include <kio/previewjob.h>
+#include <kdebug.h>
+#include <ktextbrowser.h>
+#include <kapplication.h>
+
+#include "KoTemplates.h"
+
+class KoFileListItem : public KListViewItem
+{
+ public:
+ KoFileListItem(KListView* listView, QListViewItem* after, const QString& filename,
+ const QString& fullPath, KFileItem* fileItem)
+ : KListViewItem(listView, after, filename, fullPath), m_fileItem(fileItem)
+ {
+ }
+
+ ~KoFileListItem()
+ {
+ delete m_fileItem;
+ }
+
+ KFileItem* fileItem() const
+ {
+ return m_fileItem;
+ }
+
+ private:
+ KFileItem* m_fileItem;
+};
+
+class KoTemplatesPanePrivate
+{
+ public:
+ KoTemplatesPanePrivate()
+ : m_instance(0), m_selected(false)
+ {
+ }
+
+ KInstance* m_instance;
+ bool m_selected;
+ QString m_alwaysUseTemplate;
+};
+
+
+KoTemplatesPane::KoTemplatesPane(QWidget* parent, KInstance* instance,
+ KoTemplateGroup *group, KoTemplate* /*defaultTemplate*/)
+ : KoDetailsPaneBase(parent, "TemplatesPane")
+{
+ d = new KoTemplatesPanePrivate;
+ d->m_instance = instance;
+ m_previewLabel->installEventFilter(this);
+ m_documentList->installEventFilter(this);
+ setFocusProxy(m_documentList);
+#if KDE_IS_VERSION(3,4,0)
+ m_documentList->setShadeSortColumn(false);
+#endif
+
+ KGuiItem openGItem(i18n("Use This Template"));
+ m_openButton->setGuiItem(openGItem);
+ m_documentList->header()->hide();
+ KConfigGroup cfgGrp(d->m_instance->config(), "TemplateChooserDialog");
+ QString fullTemplateName = cfgGrp.readPathEntry("FullTemplateName");
+ d->m_alwaysUseTemplate = cfgGrp.readPathEntry("AlwaysUseTemplate");
+ connect(m_alwaysUseCheckBox, SIGNAL(clicked()), this, SLOT(alwaysUseClicked()));
+ changePalette();
+
+ if(kapp) {
+ connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(changePalette()));
+ }
+
+ KListViewItem* selectItem = 0;
+
+ for (KoTemplate* t = group->first(); t != 0L; t = group->next()) {
+ KListViewItem* item = new KListViewItem(m_documentList, t->name(), t->description(), t->file());
+ QImage icon = t->loadPicture(instance).convertToImage();
+ icon = icon.smoothScale(64, 64, QImage::ScaleMin);
+ icon.setAlphaBuffer(true);
+ icon = icon.copy((icon.width() - 64) / 2, (icon.height() - 64) / 2, 64, 64);
+ item->setPixmap(0, QPixmap(icon));
+ item->setPixmap(2, t->loadPicture(instance));
+
+ if(d->m_alwaysUseTemplate == t->file()) {
+ selectItem = item;
+ } else if(!selectItem && (t->file() == fullTemplateName)) {
+ selectItem = item;
+ }
+ }
+
+ connect(m_documentList, SIGNAL(selectionChanged(QListViewItem*)),
+ this, SLOT(selectionChanged(QListViewItem*)));
+ connect(m_documentList, SIGNAL(doubleClicked(QListViewItem*, const QPoint&, int)),
+ this, SLOT(openTemplate(QListViewItem*)));
+ connect(m_documentList, SIGNAL(returnPressed(QListViewItem*)),
+ this, SLOT(openTemplate(QListViewItem*)));
+ connect(m_openButton, SIGNAL(clicked()), this, SLOT(openTemplate()));
+
+ if(selectItem) {
+ m_documentList->setSelected(selectItem, true);
+ d->m_selected = true;
+ } else {
+ m_documentList->setSelected(m_documentList->firstChild(), true);
+ }
+}
+
+KoTemplatesPane::~KoTemplatesPane()
+{
+ delete d;
+}
+
+void KoTemplatesPane::selectionChanged(QListViewItem* item)
+{
+ if(item) {
+ m_openButton->setEnabled(true);
+ m_alwaysUseCheckBox->setEnabled(true);
+ m_titleLabel->setText(item->text(0));
+ m_previewLabel->setPixmap(*(item->pixmap(2)));
+ m_detailsLabel->setText(item->text(1));
+ m_alwaysUseCheckBox->setChecked(item->text(2) == d->m_alwaysUseTemplate);
+ } else {
+ m_openButton->setEnabled(false);
+ m_alwaysUseCheckBox->setEnabled(false);
+ m_alwaysUseCheckBox->setChecked(false);
+ m_titleLabel->setText(QString::null);
+ m_previewLabel->setPixmap(QPixmap());
+ }
+}
+
+void KoTemplatesPane::openTemplate()
+{
+ QListViewItem* item = m_documentList->selectedItem();
+ openTemplate(item);
+}
+
+void KoTemplatesPane::openTemplate(QListViewItem* item)
+{
+ if(item) {
+ KConfigGroup cfgGrp(d->m_instance->config(), "TemplateChooserDialog");
+ cfgGrp.writePathEntry("FullTemplateName", item->text(2));
+ cfgGrp.writeEntry("LastReturnType", "Template");
+ cfgGrp.writeEntry("AlwaysUseTemplate", d->m_alwaysUseTemplate);
+ emit openTemplate(item->text(2));
+ }
+}
+
+void KoTemplatesPane::changePalette()
+{
+ QPalette p = kapp ? kapp->palette() : palette();
+ p.setBrush(QColorGroup::Base, p.brush(QPalette::Normal, QColorGroup::Background));
+ p.setColor(QColorGroup::Text, p.color(QPalette::Normal, QColorGroup::Foreground));
+ m_detailsLabel->setPalette(p);
+}
+
+bool KoTemplatesPane::isSelected()
+{
+ return d->m_selected;
+}
+
+void KoTemplatesPane::alwaysUseClicked()
+{
+ QListViewItem* item = m_documentList->selectedItem();
+
+ if(!m_alwaysUseCheckBox->isChecked()) {
+ KConfigGroup cfgGrp(d->m_instance->config(), "TemplateChooserDialog");
+ cfgGrp.writeEntry("AlwaysUseTemplate", QString::null);
+ d->m_alwaysUseTemplate = QString::null;
+ } else {
+ d->m_alwaysUseTemplate = item->text(2);
+ }
+
+ emit alwaysUseChanged(this, d->m_alwaysUseTemplate);
+}
+
+void KoTemplatesPane::changeAlwaysUseTemplate(KoTemplatesPane* sender, const QString& alwaysUse)
+{
+ if(this == sender)
+ return;
+
+ QListViewItem* item = m_documentList->selectedItem();
+
+ // If the old always use template is selected uncheck the checkbox
+ if(item && (item->text(2) == d->m_alwaysUseTemplate)) {
+ m_alwaysUseCheckBox->setChecked(false);
+ }
+
+ d->m_alwaysUseTemplate = alwaysUse;
+}
+
+bool KoTemplatesPane::eventFilter(QObject* watched, QEvent* e)
+{
+ if(watched == m_previewLabel) {
+ if(e->type() == QEvent::MouseButtonDblClick) {
+ openTemplate();
+ }
+ }
+
+ if(watched == m_documentList) {
+ if((e->type() == QEvent::Resize) && isShown()) {
+ emit splitterResized(this, m_splitter->sizes());
+ }
+ }
+
+ return false;
+}
+
+void KoTemplatesPane::resizeSplitter(KoDetailsPaneBase* sender, const QValueList<int>& sizes)
+{
+ if(sender == this)
+ return;
+
+ m_splitter->setSizes(sizes);
+}
+
+
+class KoRecentDocumentsPanePrivate
+{
+ public:
+ KoRecentDocumentsPanePrivate()
+ : m_previewJob(0), m_instance(0)
+ {
+ }
+
+ ~KoRecentDocumentsPanePrivate()
+ {
+ if(m_previewJob)
+ m_previewJob->kill();
+ }
+
+ KIO::PreviewJob* m_previewJob;
+ KInstance* m_instance;
+};
+
+KoRecentDocumentsPane::KoRecentDocumentsPane(QWidget* parent, KInstance* instance)
+ : KoDetailsPaneBase(parent, "RecentDocsPane")
+{
+ d = new KoRecentDocumentsPanePrivate;
+ d->m_instance = instance;
+ m_previewLabel->installEventFilter(this);
+ m_documentList->installEventFilter(this);
+ setFocusProxy(m_documentList);
+ KGuiItem openGItem(i18n("Open This Document"), "fileopen");
+ m_openButton->setGuiItem(openGItem);
+ m_alwaysUseCheckBox->hide();
+ m_documentList->header()->hide();
+ m_documentList->setSorting(-1); // Disable sorting
+ changePalette();
+
+ if(kapp) {
+ connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(changePalette()));
+ }
+
+ QString oldGroup = instance->config()->group();
+ instance->config()->setGroup("RecentFiles");
+
+ int i = 0;
+ QString value;
+ KFileItemList fileList;
+ fileList.setAutoDelete(false);
+
+ do {
+ QString key = QString("File%1").arg(i);
+ value = instance->config()->readPathEntry(key);
+
+ if(!value.isEmpty()) {
+ QString path = value;
+ QString name;
+
+ // Support for kdelibs-3.5's new RecentFiles format: name[url]
+ if(path.endsWith("]")) {
+ int pos = path.find("[");
+ name = path.mid(0, pos - 1);
+ path = path.mid(pos + 1, path.length() - pos - 2);
+ }
+
+ KURL url(path);
+
+ if(name.isEmpty())
+ name = url.filename();
+
+ if(!url.isLocalFile() || QFile::exists(url.path())) {
+ KFileItem* fileItem = new KFileItem(KFileItem::Unknown, KFileItem::Unknown, url);
+ fileList.append(fileItem);
+ KoFileListItem* item = new KoFileListItem(m_documentList,
+ m_documentList->lastItem(), name, url.url(), fileItem);
+ //center all icons in 64x64 area
+ QImage icon = fileItem->pixmap(64).convertToImage();
+ icon.setAlphaBuffer(true);
+ icon = icon.copy((icon.width() - 64) / 2, (icon.height() - 64) / 2, 64, 64);
+ item->setPixmap(0, QPixmap(icon));
+ item->setPixmap(2, fileItem->pixmap(128));
+ }
+ }
+
+ i++;
+ } while ( !value.isEmpty() || i<=10 );
+
+ instance->config()->setGroup( oldGroup );
+
+ connect(m_documentList, SIGNAL(selectionChanged(QListViewItem*)),
+ this, SLOT(selectionChanged(QListViewItem*)));
+ connect(m_documentList, SIGNAL(clicked(QListViewItem*)),
+ this, SLOT(selectionChanged(QListViewItem*)));
+ connect(m_documentList, SIGNAL(doubleClicked(QListViewItem*, const QPoint&, int)),
+ this, SLOT(openFile(QListViewItem*)));
+ connect(m_documentList, SIGNAL(returnPressed(QListViewItem*)),
+ this, SLOT(openFile(QListViewItem*)));
+ connect(m_openButton, SIGNAL(clicked()), this, SLOT(openFile()));
+
+ m_documentList->setSelected(m_documentList->firstChild(), true);
+
+ d->m_previewJob = KIO::filePreview(fileList, 200, 200);
+
+ connect(d->m_previewJob, SIGNAL(result(KIO::Job*)), this, SLOT(previewResult(KIO::Job*)));
+ connect(d->m_previewJob, SIGNAL(gotPreview(const KFileItem*, const QPixmap&)),
+ this, SLOT(updatePreview(const KFileItem*, const QPixmap&)));
+}
+
+KoRecentDocumentsPane::~KoRecentDocumentsPane()
+{
+ delete d;
+}
+
+void KoRecentDocumentsPane::selectionChanged(QListViewItem* item)
+{
+ if(item) {
+ m_openButton->setEnabled(true);
+ m_titleLabel->setText(item->text(0));
+ m_previewLabel->setPixmap(*(item->pixmap(2)));
+
+ if(static_cast<KoFileListItem*>(item)->fileItem()) {
+ KFileItem* fileItem = static_cast<KoFileListItem*>(item)->fileItem();
+ QString details = "<center><table border=\"0\">";
+ details += i18n("File modification date and time. %1 is date time", "<tr><td><b>Modified:</b></td><td>%1</td></tr>")
+ .arg(fileItem->timeString(KIO::UDS_MODIFICATION_TIME));
+ details += i18n("File access date and time. %1 is date time", "<tr><td><b>Accessed:</b></td><td>%1</td></tr>")
+ .arg(fileItem->timeString(KIO::UDS_ACCESS_TIME));
+ details += "</table></center>";
+ m_detailsLabel->setText(details);
+ } else {
+ m_detailsLabel->setText(QString::null);
+ }
+ } else {
+ m_openButton->setEnabled(false);
+ m_titleLabel->setText(QString::null);
+ m_previewLabel->setPixmap(QPixmap());
+ m_detailsLabel->setText(QString::null);
+ }
+}
+
+void KoRecentDocumentsPane::openFile()
+{
+ QListViewItem* item = m_documentList->selectedItem();
+ openFile(item);
+}
+
+void KoRecentDocumentsPane::openFile(QListViewItem* item)
+{
+ KConfigGroup cfgGrp(d->m_instance->config(), "TemplateChooserDialog");
+ cfgGrp.writeEntry("LastReturnType", "File");
+
+ if(item)
+ emit openFile(item->text(1));
+}
+
+void KoRecentDocumentsPane::previewResult(KIO::Job* job)
+{
+ if(d->m_previewJob == job)
+ d->m_previewJob = 0;
+}
+
+void KoRecentDocumentsPane::updatePreview(const KFileItem* fileItem, const QPixmap& preview)
+{
+ if(preview.isNull()) {
+ return;
+ }
+
+ QListViewItemIterator it(m_documentList);
+
+ while(it.current()) {
+ if(it.current()->text(1) == fileItem->url().url()) {
+ it.current()->setPixmap(2, preview);
+ QImage icon = preview.convertToImage();
+ icon = icon.smoothScale(64, 64, QImage::ScaleMin);
+ icon.setAlphaBuffer(true);
+ icon = icon.copy((icon.width() - 64) / 2, (icon.height() - 64) / 2, 64, 64);
+ it.current()->setPixmap(0, QPixmap(icon));
+
+ if(it.current()->isSelected()) {
+ m_previewLabel->setPixmap(preview);
+ }
+
+ break;
+ }
+
+ it++;
+ }
+}
+
+void KoRecentDocumentsPane::changePalette()
+{
+ QPalette p = kapp ? kapp->palette() : palette();
+ p.setBrush(QColorGroup::Base, p.brush(QPalette::Normal, QColorGroup::Background));
+ p.setColor(QColorGroup::Text, p.color(QPalette::Normal, QColorGroup::Foreground));
+ m_detailsLabel->setPalette(p);
+}
+
+bool KoRecentDocumentsPane::eventFilter(QObject* watched, QEvent* e)
+{
+ if(watched == m_previewLabel) {
+ if(e->type() == QEvent::MouseButtonDblClick) {
+ openFile();
+ }
+ }
+
+ if(watched == m_documentList) {
+ if((e->type() == QEvent::Resize) && isShown()) {
+ emit splitterResized(this, m_splitter->sizes());
+ }
+ }
+
+ return false;
+}
+
+void KoRecentDocumentsPane::resizeSplitter(KoDetailsPaneBase* sender, const QValueList<int>& sizes)
+{
+ if(sender == this)
+ return;
+
+ m_splitter->setSizes(sizes);
+}
+
+#include "KoDetailsPane.moc"
diff --git a/lib/kofficecore/KoDetailsPane.h b/lib/kofficecore/KoDetailsPane.h
new file mode 100644
index 00000000..96a92b98
--- /dev/null
+++ b/lib/kofficecore/KoDetailsPane.h
@@ -0,0 +1,132 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+
+ 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 KODETAILSPANE_H
+#define KODETAILSPANE_H
+
+#include <klistview.h>
+
+#include "koDetailsPaneBase.h"
+
+class KoTemplateGroup;
+class KoTemplate;
+class KInstance;
+class QListViewItem;
+class KoRecentDocumentsPanePrivate;
+class KoRichTextListItemPrivate;
+class KFileItem;
+class QPixmap;
+
+namespace KIO {
+ class Job;
+}
+
+class KoTemplatesPanePrivate;
+
+/**
+ * This widget is the right-side part of the template opening widget.
+ * The parent widget is initial widget in the document space of each KOffice component.
+ * This widget shows a list of templates and can show their details or open it.
+ */
+class KoTemplatesPane : public KoDetailsPaneBase
+{
+ Q_OBJECT
+ public:
+ /**
+ * Constructor.
+ * @param parent the parent widget
+ * @param instance the instance object for the app
+ * @param group the group of templates this widget will show.
+ * @param defaultTemplate pointer to the default template. Used to select a
+ * template when none has been selected before.
+ */
+ KoTemplatesPane(QWidget* parent, KInstance* instance,
+ KoTemplateGroup* group, KoTemplate* defaultTemplate);
+ ~KoTemplatesPane();
+
+ /// Returns true if a template in this group was the last one selected
+ bool isSelected();
+
+ virtual bool eventFilter(QObject* watched, QEvent* e);
+
+ signals:
+ void openTemplate(const QString&);
+ /// Emited when the always use checkbox is selected
+ void alwaysUseChanged(KoTemplatesPane* sender, const QString& alwaysUse);
+
+ void splitterResized(KoDetailsPaneBase* sender, const QValueList<int>& sizes);
+
+ public slots:
+ void resizeSplitter(KoDetailsPaneBase* sender, const QValueList<int>& sizes);
+
+ protected slots:
+ void selectionChanged(QListViewItem* item);
+ void openTemplate();
+ void openTemplate(QListViewItem* item);
+ void alwaysUseClicked();
+ void changeAlwaysUseTemplate(KoTemplatesPane* sender, const QString& alwaysUse);
+
+ void changePalette();
+
+ private:
+ KoTemplatesPanePrivate* d;
+};
+
+
+/**
+ * This widget is the recent doc part of the template opening widget.
+ * The parent widget is initial widget in the document space of each KOffice component.
+ * This widget shows a list of recent documents and can show their details or open it.
+ */
+class KoRecentDocumentsPane : public KoDetailsPaneBase
+{
+ Q_OBJECT
+ public:
+ /**
+ * Constructor.
+ * @param parent the parent widget
+ * @param instance the instance object for the app
+ */
+ KoRecentDocumentsPane(QWidget* parent, KInstance* instance);
+ ~KoRecentDocumentsPane();
+
+ virtual bool eventFilter(QObject* watched, QEvent* e);
+
+ signals:
+ void openFile(const QString&);
+
+ void splitterResized(KoDetailsPaneBase* sender, const QValueList<int>& sizes);
+
+ public slots:
+ void resizeSplitter(KoDetailsPaneBase* sender, const QValueList<int>& sizes);
+
+ protected slots:
+ void selectionChanged(QListViewItem* item);
+ void openFile();
+ void openFile(QListViewItem* item);
+
+ void previewResult(KIO::Job* job);
+ void updatePreview(const KFileItem* fileItem, const QPixmap& preview);
+
+ void changePalette();
+
+ private:
+ KoRecentDocumentsPanePrivate* d;
+};
+
+#endif //KODETAILSPANE_H
diff --git a/lib/kofficecore/KoDocInfoPropsFactory.cpp b/lib/kofficecore/KoDocInfoPropsFactory.cpp
new file mode 100644
index 00000000..b08deeb2
--- /dev/null
+++ b/lib/kofficecore/KoDocInfoPropsFactory.cpp
@@ -0,0 +1,27 @@
+/* This file is part of the KDE project
+ Copyright (c) 2000 Simon Hausmann <hausmann@kde.org>
+
+ $Id: KoDocInfoPropsFactory.cpp 508787 2006-02-12 18:28:12Z ingwa $
+
+ 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.
+*/
+
+#include <KoDocumentInfoDlg.h>
+
+#include <kgenericfactory.h>
+
+typedef KGenericFactory<KoDocumentInfoPropsPage, KPropertiesDialog> PropsDlgFactory;
+K_EXPORT_COMPONENT_FACTORY( kodocinfopropspage, PropsDlgFactory( "koffice" ) )
diff --git a/lib/kofficecore/KoDocument.cpp b/lib/kofficecore/KoDocument.cpp
new file mode 100644
index 00000000..50003627
--- /dev/null
+++ b/lib/kofficecore/KoDocument.cpp
@@ -0,0 +1,2672 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ Copyright (C) 2000-2005 David Faure <faure@kde.org>
+
+ 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.
+*/
+
+#include "KoDocument.h"
+
+#include "KoDocument_p.h"
+#include "KoDocumentIface.h"
+#include "KoDocumentChild.h"
+#include "KoView.h"
+#include "KoMainWindow.h"
+#include "KoFilterManager.h"
+#include "KoDocumentInfo.h"
+#include "KoOasisStyles.h"
+#include "KoOasisStore.h"
+#include "KoXmlNS.h"
+#include "KoOpenPane.h"
+
+#include <KoStoreDevice.h>
+#include <KoXmlWriter.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <kfileitem.h>
+#include <kiconloader.h>
+#include <kio/job.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <kparts/partmanager.h>
+#include <kprinter.h>
+#include <ksavefile.h>
+
+#include <qbuffer.h>
+#include <qcursor.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qimage.h>
+#include <qmap.h>
+#include <qpainter.h>
+#include <qtimer.h>
+#include <qxml.h>
+#include <qlayout.h>
+
+#include <config.h>
+#include <assert.h>
+#include <locale.h>
+
+
+// Define the protocol used here for embedded documents' URL
+// This used to "store" but KURL didn't like it,
+// so let's simply make it "tar" !
+#define STORE_PROTOCOL "tar"
+// The internal path is a hack to make KURL happy and still pass
+// some kind of relative path to KoDocumentChild
+#define INTERNAL_PROTOCOL "intern"
+#define INTERNAL_PREFIX "intern:/"
+// Warning, keep it sync in koStore.cc and koDocumentChild.cc
+
+QPtrList<KoDocument> *KoDocument::s_documentList=0L;
+
+using namespace std;
+class KoViewWrapperWidget;
+
+/**********************************************************
+ *
+ * KoDocument
+ *
+ **********************************************************/
+
+const int KoDocument::s_defaultAutoSave = 300; // 5 minutes
+
+class KoDocument::Private
+{
+public:
+ Private() :
+ m_dcopObject( 0L ),
+ filterManager( 0L ),
+ m_specialOutputFlag( 0 ), // default is native format
+ m_isImporting( false ), m_isExporting( false ),
+ m_numOperations( 0 ),
+ modifiedAfterAutosave( false ),
+ m_autosaving( false ),
+ m_shouldCheckAutoSaveFile( true ),
+ m_autoErrorHandlingEnabled( true ),
+ m_backupFile( true ),
+ m_backupPath( QString::null ),
+ m_doNotSaveExtDoc( false ),
+ m_current( false ),
+ m_storeInternal( false ),
+ m_bLoading( false ),
+ m_startUpWidget( 0 )
+ {
+ m_confirmNonNativeSave[0] = true;
+ m_confirmNonNativeSave[1] = true;
+ if ( KGlobal::locale()->measureSystem() == KLocale::Imperial ) {
+ m_unit = KoUnit::U_INCH;
+ } else {
+ m_unit = KoUnit::U_CM;
+ }
+ }
+
+ QPtrList<KoView> m_views;
+ QPtrList<KoDocumentChild> m_children;
+ QPtrList<KoMainWindow> m_shells;
+ QValueList<QDomDocument> m_viewBuildDocuments;
+
+ KoViewWrapperWidget *m_wrapperWidget;
+ KoDocumentIface * m_dcopObject;
+ KoDocumentInfo *m_docInfo;
+ KoView* hitTestView;
+
+ KoUnit::Unit m_unit;
+
+ KoFilterManager * filterManager; // The filter-manager to use when loading/saving [for the options]
+
+ QCString mimeType; // The actual mimetype of the document
+ QCString outputMimeType; // The mimetype to use when saving
+ bool m_confirmNonNativeSave [2]; // used to pop up a dialog when saving for the
+ // first time if the file is in a foreign format
+ // (Save/Save As, Export)
+ int m_specialOutputFlag; // See KoFileDialog in koMainWindow.cc
+ bool m_isImporting, m_isExporting; // File --> Import/Export vs File --> Open/Save
+
+ QTimer m_autoSaveTimer;
+ QString lastErrorMessage; // see openFile()
+ int m_autoSaveDelay; // in seconds, 0 to disable.
+ int m_numOperations;
+ bool modifiedAfterAutosave;
+ bool m_bSingleViewMode;
+ bool m_autosaving;
+ bool m_shouldCheckAutoSaveFile; // usually true
+ bool m_autoErrorHandlingEnabled; // usually true
+ bool m_backupFile;
+ QString m_backupPath;
+ bool m_doNotSaveExtDoc; // makes it possible to save only internally stored child documents
+ bool m_current;
+ bool m_storeInternal; // Store this doc internally even if url is external
+ bool m_bLoading; // True while loading (openURL is async)
+
+ KoOpenPane* m_startUpWidget;
+ QString m_templateType;
+};
+
+// Used in singleViewMode
+class KoViewWrapperWidget : public QWidget
+{
+public:
+ KoViewWrapperWidget( QWidget *parent, const char *name )
+ : QWidget( parent, name )
+ {
+ KGlobal::locale()->insertCatalogue("koffice");
+ // Tell the iconloader about share/apps/koffice/icons
+ KGlobal::iconLoader()->addAppDir("koffice");
+ m_view = 0L;
+ // Avoid warning from KParts - we'll have the KoView as focus proxy anyway
+ setFocusPolicy( ClickFocus );
+ }
+
+ virtual ~KoViewWrapperWidget() {
+ setFocusProxy( 0 ); // to prevent a crash due to clearFocus (#53466)
+ }
+
+ virtual void resizeEvent( QResizeEvent * )
+ {
+ QObject *wid = child( 0, "QWidget" );
+ if ( wid )
+ static_cast<QWidget *>(wid)->setGeometry( 0, 0, width(), height() );
+ }
+
+ virtual void childEvent( QChildEvent *ev )
+ {
+ if ( ev->type() == QEvent::ChildInserted )
+ resizeEvent( 0L );
+ }
+
+ // Called by openFile()
+ void setKoView( KoView * view ) {
+ m_view = view;
+ setFocusProxy( m_view );
+ }
+ KoView * koView() const { return m_view; }
+private:
+ KoView* m_view;
+};
+
+KoBrowserExtension::KoBrowserExtension( KoDocument * doc, const char * name )
+ : KParts::BrowserExtension( doc, name )
+{
+ emit enableAction( "print", true );
+}
+
+void KoBrowserExtension::print()
+{
+ KoDocument * doc = static_cast<KoDocument *>( parent() );
+ KoViewWrapperWidget * wrapper = static_cast<KoViewWrapperWidget *>( doc->widget() );
+ KoView * view = wrapper->koView();
+ // TODO remove code duplication (KoMainWindow), by moving this to KoView
+ KPrinter printer;
+ // ### TODO: apply global koffice settings here
+ view->setupPrinter( printer );
+ if ( printer.setup( view ) )
+ view->print( printer );
+}
+
+KoDocument::KoDocument( QWidget * parentWidget, const char *widgetName, QObject* parent, const char* name, bool singleViewMode )
+ : KParts::ReadWritePart( parent, name )
+{
+ if(s_documentList==0L)
+ s_documentList=new QPtrList<KoDocument>;
+ s_documentList->append(this);
+
+ d = new Private;
+ m_bEmpty = TRUE;
+ connect( &d->m_autoSaveTimer, SIGNAL( timeout() ), this, SLOT( slotAutoSave() ) );
+ setAutoSave( s_defaultAutoSave );
+ d->m_bSingleViewMode = singleViewMode;
+
+
+ // the parent setting *always* overrides! (Simon)
+ if ( parent )
+ {
+ if ( parent->inherits( "KoDocument" ) )
+ d->m_bSingleViewMode = ((KoDocument *)parent)->isSingleViewMode();
+ else if ( parent->inherits( "KParts::Part" ) )
+ d->m_bSingleViewMode = true;
+ }
+
+ if ( singleViewMode )
+ {
+ d->m_wrapperWidget = new KoViewWrapperWidget( parentWidget, widgetName );
+ setWidget( d->m_wrapperWidget );
+ kdDebug(30003) << "creating KoBrowserExtension" << endl;
+ (void) new KoBrowserExtension( this ); // ## only if embedded into a browser?
+ }
+
+ d->m_docInfo = new KoDocumentInfo( this, "document info" );
+
+ m_pageLayout.ptWidth = 0;
+ m_pageLayout.ptHeight = 0;
+ m_pageLayout.ptTop = 0;
+ m_pageLayout.ptBottom = 0;
+ m_pageLayout.ptLeft = 0;
+ m_pageLayout.ptRight = 0;
+
+ // A way to 'fix' the job's window, since we have no widget known to KParts
+ if ( !singleViewMode )
+ connect( this, SIGNAL( started( KIO::Job* ) ), SLOT( slotStarted( KIO::Job* ) ) );
+}
+
+KoDocument::~KoDocument()
+{
+ d->m_autoSaveTimer.stop();
+
+ QPtrListIterator<KoDocumentChild> childIt( d->m_children );
+ for (; childIt.current(); ++childIt )
+ disconnect( childIt.current(), SIGNAL( destroyed() ),
+ this, SLOT( slotChildDestroyed() ) );
+
+ // Tell our views that the document is already destroyed and
+ // that they shouldn't try to access it.
+ QPtrListIterator<KoView> vIt( d->m_views );
+ for (; vIt.current(); ++vIt )
+ vIt.current()->setDocumentDeleted();
+
+ delete d->m_startUpWidget;
+ d->m_startUpWidget = 0;
+
+ d->m_children.setAutoDelete( true );
+ d->m_children.clear();
+
+ d->m_shells.setAutoDelete( true );
+ d->m_shells.clear();
+
+ delete d->m_dcopObject;
+ delete d->filterManager;
+ delete d;
+ s_documentList->removeRef(this);
+ // last one?
+ if(s_documentList->isEmpty()) {
+ delete s_documentList;
+ s_documentList=0;
+ }
+}
+
+bool KoDocument::isSingleViewMode() const
+{
+ return d->m_bSingleViewMode;
+}
+
+bool KoDocument::isEmbedded() const
+{
+ return dynamic_cast<KoDocument *>( parent() ) != 0;
+}
+
+KoView *KoDocument::createView( QWidget *parent, const char *name )
+{
+ KoView *view=createViewInstance(parent, name);
+ addView(view);
+ return view;
+}
+
+bool KoDocument::exp0rt( const KURL & _url )
+{
+ bool ret;
+
+ d->m_isExporting = true;
+
+ //
+ // Preserve a lot of state here because we need to restore it in order to
+ // be able to fake a File --> Export. Can't do this in saveFile() because,
+ // for a start, KParts has already set m_url and m_file and because we need
+ // to restore the modified flag etc. and don't want to put a load on anyone
+ // reimplementing saveFile() (Note: import() and export() will remain
+ // non-virtual).
+ //
+ KURL oldURL = m_url;
+ QString oldFile = m_file;
+
+ bool wasModified = isModified ();
+ QCString oldMimeType = mimeType ();
+
+
+ // save...
+ ret = saveAs( _url );
+
+
+ //
+ // This is sooooo hacky :(
+ // Hopefully we will restore enough state.
+ //
+ kdDebug(30003) << "Restoring KoDocument state to before export" << endl;
+
+ // always restore m_url & m_file because KParts has changed them
+ // (regardless of failure or success)
+ m_url = oldURL;
+ m_file = oldFile;
+
+ // on successful export we need to restore modified etc. too
+ // on failed export, mimetype/modified hasn't changed anyway
+ if (ret)
+ {
+ setModified (wasModified);
+ d->mimeType = oldMimeType;
+ }
+
+
+ d->m_isExporting = false;
+
+ return ret;
+}
+
+bool KoDocument::saveFile()
+{
+ kdDebug(30003) << "KoDocument::saveFile() doc='" << url().url() <<"'"<< endl;
+
+ // set local again as it can happen that it gets resetted
+ // so that all saved numbers have a . and not e.g a , as a
+ // decimal seperator
+ setlocale( LC_NUMERIC, "C" );
+
+ // Save it to be able to restore it after a failed save
+ const bool wasModified = isModified ();
+
+ // The output format is set by koMainWindow, and by openFile
+ QCString outputMimeType = d->outputMimeType;
+ //Q_ASSERT( !outputMimeType.isEmpty() ); // happens when using the DCOP method saveAs
+ if ( outputMimeType.isEmpty() )
+ outputMimeType = d->outputMimeType = nativeFormatMimeType();
+
+ QApplication::setOverrideCursor( waitCursor );
+
+ if ( backupFile() ) {
+ if ( url().isLocalFile() )
+ KSaveFile::backupFile( url().path(), d->m_backupPath );
+ else {
+ KIO::UDSEntry entry;
+ if ( KIO::NetAccess::stat( url(), entry, shells().current() ) ) { // this file exists => backup
+ emit sigStatusBarMessage( i18n("Making backup...") );
+ KURL backup;
+ if ( d->m_backupPath.isEmpty())
+ backup = url();
+ else
+ backup = d->m_backupPath +"/"+url().fileName();
+ backup.setPath( backup.path() + QString::fromLatin1("~") );
+ KFileItem item( entry, url() );
+ Q_ASSERT( item.name() == url().fileName() );
+ KIO::NetAccess::file_copy( url(), backup, item.permissions(), true /*overwrite*/, false /*resume*/, shells().current() );
+ }
+ }
+ }
+
+ emit sigStatusBarMessage( i18n("Saving...") );
+ bool ret = false;
+ bool suppressErrorDialog = false;
+ if ( !isNativeFormat( outputMimeType ) ) {
+ kdDebug(30003) << "Saving to format " << outputMimeType << " in " << m_file << endl;
+ // Not native format : save using export filter
+ if ( !d->filterManager )
+ d->filterManager = new KoFilterManager( this );
+
+ KoFilter::ConversionStatus status = d->filterManager->exp0rt( m_file, outputMimeType );
+ ret = status == KoFilter::OK;
+ suppressErrorDialog = (status == KoFilter::UserCancelled || status == KoFilter::BadConversionGraph );
+ } else {
+ // Native format => normal save
+ Q_ASSERT( !m_file.isEmpty() );
+ ret = saveNativeFormat( m_file );
+ }
+
+ if ( ret ) {
+ removeAutoSaveFiles();
+ // Restart the autosave timer
+ // (we don't want to autosave again 2 seconds after a real save)
+ setAutoSave( d->m_autoSaveDelay );
+ }
+
+ QApplication::restoreOverrideCursor();
+ if ( !ret )
+ {
+ if ( !suppressErrorDialog )
+ {
+ showSavingErrorDialog();
+ }
+
+ // couldn't save file so this new URL is invalid
+ // FIXME: we should restore the current document's true URL instead of
+ // setting it to nothing otherwise anything that depends on the URL
+ // being correct will not work (i.e. the document will be called
+ // "Untitled" which may not be true)
+ //
+ // Update: now the URL is restored in KoMainWindow but really, this
+ // should still be fixed in KoDocument/KParts (ditto for m_file).
+ // We still resetURL() here since we may or may not have been called
+ // by KoMainWindow - Clarence
+ resetURL();
+
+ // As we did not save, restore the "was modified" status
+ setModified( wasModified );
+ }
+
+ if ( ret )
+ {
+ d->mimeType = outputMimeType;
+ setConfirmNonNativeSave ( isExporting (), false );
+ }
+ emit sigClearStatusBarMessage();
+
+ return ret;
+}
+
+QCString KoDocument::mimeType() const
+{
+ return d->mimeType;
+}
+
+void KoDocument::setMimeType( const QCString & mimeType )
+{
+ d->mimeType = mimeType;
+}
+
+void KoDocument::setOutputMimeType( const QCString & mimeType, int specialOutputFlag )
+{
+ d->outputMimeType = mimeType;
+ d->m_specialOutputFlag = specialOutputFlag;
+}
+
+QCString KoDocument::outputMimeType() const
+{
+ return d->outputMimeType;
+}
+
+int KoDocument::specialOutputFlag() const
+{
+ return d->m_specialOutputFlag;
+}
+
+bool KoDocument::confirmNonNativeSave( const bool exporting ) const
+{
+ // "exporting ? 1 : 0" is different from "exporting" because a bool is
+ // usually implemented like an "int", not "unsigned : 1"
+ return d->m_confirmNonNativeSave [ exporting ? 1 : 0 ];
+}
+
+void KoDocument::setConfirmNonNativeSave( const bool exporting, const bool on )
+{
+ d->m_confirmNonNativeSave [ exporting ? 1 : 0] = on;
+}
+
+bool KoDocument::wantExportConfirmation() const
+{
+ return true;
+}
+
+bool KoDocument::isImporting() const
+{
+ return d->m_isImporting;
+}
+
+bool KoDocument::isExporting() const
+{
+ return d->m_isExporting;
+}
+
+void KoDocument::setCheckAutoSaveFile( bool b )
+{
+ d->m_shouldCheckAutoSaveFile = b;
+}
+
+void KoDocument::setAutoErrorHandlingEnabled( bool b )
+{
+ d->m_autoErrorHandlingEnabled = b;
+}
+
+bool KoDocument::isAutoErrorHandlingEnabled() const
+{
+ return d->m_autoErrorHandlingEnabled;
+}
+
+void KoDocument::slotAutoSave()
+{
+ if ( isModified() && d->modifiedAfterAutosave && !d->m_bLoading )
+ {
+ connect( this, SIGNAL( sigProgress( int ) ), shells().current(), SLOT( slotProgress( int ) ) );
+ emit sigStatusBarMessage( i18n("Autosaving...") );
+ d->m_autosaving = true;
+ bool ret = saveNativeFormat( autoSaveFile( m_file ) );
+ setModified( true );
+ if ( ret ) {
+ d->modifiedAfterAutosave = false;
+ d->m_autoSaveTimer.stop(); // until the next change
+ }
+ d->m_autosaving = false;
+ emit sigClearStatusBarMessage();
+ disconnect( this, SIGNAL( sigProgress( int ) ), shells().current(), SLOT( slotProgress( int ) ) );
+ if ( !ret )
+ emit sigStatusBarMessage( i18n("Error during autosave! Partition full?") );
+ }
+}
+
+KAction *KoDocument::action( const QDomElement &element ) const
+{
+ // First look in the document itself
+ KAction* act = KParts::ReadWritePart::action( element );
+ if ( act )
+ return act;
+
+ Q_ASSERT( d->m_bSingleViewMode );
+ // Then look in the first view (this is for the single view mode)
+ if ( !d->m_views.isEmpty() )
+ return d->m_views.getFirst()->action( element );
+ else
+ return 0L;
+}
+
+QDomDocument KoDocument::domDocument() const
+{
+ // When embedded into e.g. konqueror, we want the view's GUI (hopefully a reduced one)
+ // to be used.
+ Q_ASSERT( d->m_bSingleViewMode );
+ if ( d->m_views.isEmpty() )
+ return QDomDocument();
+ else
+ return d->m_views.getFirst()->domDocument();
+}
+
+void KoDocument::setManager( KParts::PartManager *manager )
+{
+ KParts::ReadWritePart::setManager( manager );
+ if ( d->m_bSingleViewMode && d->m_views.count() == 1 )
+ d->m_views.getFirst()->setPartManager( manager );
+
+ if ( manager )
+ {
+ QPtrListIterator<KoDocumentChild> it( d->m_children );
+ for (; it.current(); ++it )
+ if ( it.current()->document() )
+ manager->addPart( it.current()->document(), false );
+ }
+}
+
+void KoDocument::setReadWrite( bool readwrite )
+{
+ KParts::ReadWritePart::setReadWrite( readwrite );
+
+ QPtrListIterator<KoView> vIt( d->m_views );
+ for (; vIt.current(); ++vIt )
+ vIt.current()->updateReadWrite( readwrite );
+
+ QPtrListIterator<KoDocumentChild> dIt( d->m_children );
+ for (; dIt.current(); ++dIt )
+ if ( dIt.current()->document() )
+ dIt.current()->document()->setReadWrite( readwrite );
+
+ setAutoSave( d->m_autoSaveDelay );
+}
+
+void KoDocument::setAutoSave( int delay )
+{
+ d->m_autoSaveDelay = delay;
+ if ( isReadWrite() && !isEmbedded() && d->m_autoSaveDelay > 0 )
+ d->m_autoSaveTimer.start( d->m_autoSaveDelay * 1000 );
+ else
+ d->m_autoSaveTimer.stop();
+}
+
+void KoDocument::addView( KoView *view )
+{
+ if ( !view )
+ return;
+
+ d->m_views.append( view );
+ view->updateReadWrite( isReadWrite() );
+}
+
+void KoDocument::removeView( KoView *view )
+{
+ d->m_views.removeRef( view );
+}
+
+const QPtrList<KoView>& KoDocument::views() const
+{
+ return d->m_views;
+}
+
+int KoDocument::viewCount() const
+{
+ return d->m_views.count();
+}
+
+void KoDocument::insertChild( KoDocumentChild *child )
+{
+ setModified( true );
+
+ d->m_children.append( child );
+
+ connect( child, SIGNAL( changed( KoChild * ) ),
+ this, SLOT( slotChildChanged( KoChild * ) ) );
+ connect( child, SIGNAL( destroyed() ),
+ this, SLOT( slotChildDestroyed() ) );
+
+ // It may be that insertChild is called without the KoDocumentChild
+ // having a KoDocument attached, yet. This happens for example
+ // when KPresenter loads a document with embedded objects. For those
+ // KPresenterChild objects are allocated and insertChild is called.
+ // Later in loadChildren() KPresenter iterates over the child list
+ // and calls loadDocument for each child. That's exactly where we
+ // will try to do what we cannot do now: Register the child document
+ // at the partmanager (Simon)
+ if ( manager() && !isSingleViewMode() && child->document() )
+ manager()->addPart( child->document(), false );
+}
+
+void KoDocument::slotChildChanged( KoChild *c )
+{
+ assert( c->inherits( "KoDocumentChild" ) );
+ emit childChanged( static_cast<KoDocumentChild *>( c ) );
+}
+
+void KoDocument::slotChildDestroyed()
+{
+ setModified( true );
+
+ const KoDocumentChild *child = static_cast<const KoDocumentChild *>( sender() );
+ d->m_children.removeRef( child );
+}
+
+const QPtrList<KoDocumentChild>& KoDocument::children() const
+{
+ return d->m_children;
+}
+
+KParts::Part *KoDocument::hitTest( QWidget *widget, const QPoint &globalPos )
+{
+ QPtrListIterator<KoView> it( d->m_views );
+ for (; it.current(); ++it )
+ if ( static_cast<QWidget *>(it.current()) == widget )
+ {
+ KoView* view = it.current();
+ d->hitTestView = view; // koffice-1.x hack
+ QPoint canvasPos( view->canvas()->mapFromGlobal( globalPos ) );
+ canvasPos.rx() += view->canvasXOffset();
+ canvasPos.ry() += view->canvasYOffset();
+
+ KParts::Part *part = view->hitTest( canvasPos );
+ d->hitTestView = 0;
+ if ( part )
+ return part;
+ }
+
+ return 0L;
+}
+
+KoView* KoDocument::hitTestView()
+{
+ return d->hitTestView;
+}
+
+KoDocument* KoDocument::hitTest( const QPoint &pos, const QWMatrix &matrix )
+{
+ // Call KoDocumentChild::hitTest for any child document
+ QPtrListIterator<KoDocumentChild> it( d->m_children );
+ for (; it.current(); ++it )
+ {
+ KoDocument *doc = it.current()->hitTest( pos, matrix );
+ if ( doc )
+ return doc;
+ }
+
+ // Unless we hit an embedded document, the hit is on this document itself.
+ return this;
+}
+
+KoDocumentChild *KoDocument::child( KoDocument *doc )
+{
+ QPtrListIterator<KoDocumentChild> it( d->m_children );
+ for (; it.current(); ++it )
+ if ( it.current()->document() == doc )
+ return it.current();
+
+ return 0L;
+}
+
+KoDocumentInfo *KoDocument::documentInfo() const
+{
+ return d->m_docInfo;
+}
+
+void KoDocument::setViewBuildDocument( KoView *view, const QDomDocument &doc )
+{
+ if ( d->m_views.find( view ) == -1 )
+ return;
+
+ uint viewIdx = d->m_views.at();
+
+ if ( d->m_viewBuildDocuments.count() == viewIdx )
+ d->m_viewBuildDocuments.append( doc );
+ else if ( d->m_viewBuildDocuments.count() > viewIdx )
+ d->m_viewBuildDocuments[ viewIdx ] = doc;
+}
+
+QDomDocument KoDocument::viewBuildDocument( KoView *view )
+{
+ QDomDocument res;
+
+ if ( d->m_views.find( view ) == -1 )
+ return res;
+
+ uint viewIdx = d->m_views.at();
+
+ if ( viewIdx >= d->m_viewBuildDocuments.count() )
+ return res;
+
+ res = d->m_viewBuildDocuments[ viewIdx ];
+
+ // make this entry empty. otherwise we get a segfault in QMap ;-(
+ d->m_viewBuildDocuments[ viewIdx ] = QDomDocument();
+
+ return res;
+}
+
+void KoDocument::paintEverything( QPainter &painter, const QRect &rect, bool transparent, KoView *view, double zoomX, double zoomY )
+{
+ paintContent( painter, rect, transparent, zoomX, zoomY );
+ paintChildren( painter, rect, view, zoomX, zoomY );
+}
+
+void KoDocument::paintChildren( QPainter &painter, const QRect &/*rect*/, KoView *view, double zoomX, double zoomY )
+{
+ QPtrListIterator<KoDocumentChild> it( d->m_children );
+ for (; it.current(); ++it )
+ {
+ // #### todo: paint only if child is visible inside rect
+ painter.save();
+ paintChild( it.current(), painter, view, zoomX, zoomY );
+ painter.restore();
+ }
+}
+
+void KoDocument::paintChild( KoDocumentChild *child, QPainter &painter, KoView *view, double zoomX, double zoomY )
+{
+ if ( child->isDeleted() )
+ return;
+
+ // QRegion rgn = painter.clipRegion();
+
+ child->transform( painter );
+ child->document()->paintEverything( painter, child->contentRect(), child->isTransparent(), view, zoomX, zoomY );
+
+ if ( view && view->partManager() )
+ {
+ // ### do we need to apply zoomX and zoomY here ?
+ KParts::PartManager *manager = view->partManager();
+
+ painter.scale( 1.0 / child->xScaling(), 1.0 / child->yScaling() );
+
+ int w = int( (double)child->contentRect().width() * child->xScaling() );
+ int h = int( (double)child->contentRect().height() * child->yScaling() );
+ if ( ( manager->selectedPart() == (KParts::Part *)child->document() &&
+ manager->selectedWidget() == (QWidget *)view ) ||
+ ( manager->activePart() == (KParts::Part *)child->document() &&
+ manager->activeWidget() == (QWidget *)view ) )
+ {
+ // painter.setClipRegion( rgn );
+ painter.setClipping( FALSE );
+
+ painter.setPen( black );
+ painter.fillRect( -5, -5, w + 10, 5, white );
+ painter.fillRect( -5, h, w + 10, 5, white );
+ painter.fillRect( -5, -5, 5, h + 10, white );
+ painter.fillRect( w, -5, 5, h + 10, white );
+ painter.fillRect( -5, -5, w + 10, 5, BDiagPattern );
+ painter.fillRect( -5, h, w + 10, 5, BDiagPattern );
+ painter.fillRect( -5, -5, 5, h + 10, BDiagPattern );
+ painter.fillRect( w, -5, 5, h + 10, BDiagPattern );
+
+ if ( manager->selectedPart() == (KParts::Part *)child->document() &&
+ manager->selectedWidget() == (QWidget *)view )
+ {
+ QColor color;
+ if ( view->koDocument() == this )
+ color = black;
+ else
+ color = gray;
+ painter.fillRect( -5, -5, 5, 5, color );
+ painter.fillRect( -5, h, 5, 5, color );
+ painter.fillRect( w, h, 5, 5, color );
+ painter.fillRect( w, -5, 5, 5, color );
+ painter.fillRect( w / 2 - 3, -5, 5, 5, color );
+ painter.fillRect( w / 2 - 3, h, 5, 5, color );
+ painter.fillRect( -5, h / 2 - 3, 5, 5, color );
+ painter.fillRect( w, h / 2 - 3, 5, 5, color );
+ }
+
+ painter.setClipping( TRUE );
+ }
+ }
+}
+
+bool KoDocument::isModified() const
+{
+ if ( KParts::ReadWritePart::isModified() )
+ {
+ //kdDebug(30003)<<k_funcinfo<<" Modified doc='"<<url().url()<<"' extern="<<isStoredExtern()<<endl;
+ return true;
+ }
+ // Then go through internally stored children (considered to be part of this doc)
+ QPtrListIterator<KoDocumentChild> it = children();
+ for (; it.current(); ++it )
+ {
+ KoDocument *doc = it.current()->document();
+ if ( doc && !it.current()->isStoredExtern() && !it.current()->isDeleted() && doc->isModified() )
+ return true;
+ }
+ return false;
+}
+
+bool KoDocument::saveChildren( KoStore* _store )
+{
+ //kdDebug(30003)<<k_funcinfo<<" checking children of doc='"<<url().url()<<"'"<<endl;
+ int i = 0;
+ QPtrListIterator<KoDocumentChild> it( children() );
+ for( ; it.current(); ++it ) {
+ KoDocument* childDoc = it.current()->document();
+ if (childDoc && !it.current()->isDeleted())
+ {
+ if ( !childDoc->isStoredExtern() )
+ {
+ //kdDebug(30003) << "KoDocument::saveChildren internal url: /" << i << endl;
+ if ( !childDoc->saveToStore( _store, QString::number( i++ ) ) )
+ return FALSE;
+
+ if (!isExporting ())
+ childDoc->setModified( false );
+ }
+ //else kdDebug(30003)<<k_funcinfo<<" external (don't save) url:" << childDoc->url().url()<<endl;
+ }
+ }
+ return true;
+}
+
+bool KoDocument::saveChildrenOasis( KoStore* store, KoXmlWriter* manifestWriter )
+{
+ //kdDebug(30003)<<k_funcinfo<<" checking children of doc='"<<url().url()<<"'"<<endl;
+ QPtrListIterator<KoDocumentChild> it( children() );
+ for( ; it.current(); ++it ) {
+ KoDocument* childDoc = it.current()->document();
+ if ( childDoc && !it.current()->isDeleted() )
+ {
+ if ( !it.current()->saveOasis( store, manifestWriter ) )
+ return false;
+ if ( !childDoc->isStoredExtern() && !isExporting () )
+ childDoc->setModified( false );
+ }
+ }
+ return true;
+}
+
+bool KoDocument::saveExternalChildren()
+{
+ if ( d->m_doNotSaveExtDoc )
+ {
+ //kdDebug(30003)<<k_funcinfo<<" Don't save external docs in doc='"<<url().url()<<"'"<<endl;
+ d->m_doNotSaveExtDoc = false;
+ return true;
+ }
+
+ //kdDebug(30003)<<k_funcinfo<<" checking children of doc='"<<url().url()<<"'"<<endl;
+ KoDocumentChild *ch;
+ QPtrListIterator<KoDocumentChild> it = children();
+ for (; (ch = it.current()); ++it )
+ {
+ if ( !ch->isDeleted() )
+ {
+ KoDocument* doc = ch->document();
+ if ( doc && doc->isStoredExtern() && doc->isModified() )
+ {
+ kdDebug(30003)<<" save external doc='"<<url().url()<<"'"<<endl;
+ doc->setDoNotSaveExtDoc(); // Only save doc + it's internal children
+ if ( !doc->save() )
+ return false; // error
+ }
+ //kdDebug(30003)<<k_funcinfo<<" not modified doc='"<<url().url()<<"'"<<endl;
+ // save possible external docs inside doc
+ if ( doc && !doc->saveExternalChildren() )
+ return false;
+ }
+ }
+ return true;
+}
+
+bool KoDocument::saveNativeFormat( const QString & file )
+{
+ d->lastErrorMessage = QString::null;
+ //kdDebug(30003) << "Saving to store" << endl;
+
+ KoStore::Backend backend = KoStore::Auto;
+#if 0
+ if ( d->m_specialOutputFlag == SaveAsKOffice1dot1 )
+ {
+ kdDebug(30003) << "Saving as KOffice-1.1 format, using a tar.gz" << endl;
+ backend = KoStore::Tar; // KOffice-1.0/1.1 used tar.gz for the native mimetype
+ //// TODO more backwards compat stuff (embedded docs etc.)
+ }
+ else
+#endif
+ if ( d->m_specialOutputFlag == SaveAsDirectoryStore )
+ {
+ backend = KoStore::Directory;
+ kdDebug(30003) << "Saving as uncompressed XML, using directory store." << endl;
+ }
+ else if ( d->m_specialOutputFlag == SaveAsFlatXML )
+ {
+ kdDebug(30003) << "Saving as a flat XML file." << endl;
+ QFile f( file );
+ if ( f.open( IO_WriteOnly | IO_Translate ) )
+ {
+ bool success = saveToStream( &f );
+ f.close();
+ return success;
+ }
+ else
+ return false;
+ }
+
+ kdDebug(30003) << "KoDocument::saveNativeFormat nativeFormatMimeType=" << nativeFormatMimeType() << endl;
+ // OLD: bool oasis = d->m_specialOutputFlag == SaveAsOASIS;
+ // OLD: QCString mimeType = oasis ? nativeOasisMimeType() : nativeFormatMimeType();
+ QCString mimeType = d->outputMimeType;
+ QCString nativeOasisMime = nativeOasisMimeType();
+ bool oasis = !mimeType.isEmpty() && ( mimeType == nativeOasisMime || mimeType == nativeOasisMime + "-template" );
+ // TODO: use std::auto_ptr or create store on stack [needs API fixing],
+ // to remove all the 'delete store' in all the branches
+ KoStore* store = KoStore::createStore( file, KoStore::Write, mimeType, backend );
+ if ( store->bad() )
+ {
+ d->lastErrorMessage = i18n( "Could not create the file for saving" ); // more details needed?
+ delete store;
+ return false;
+ }
+
+ if ( oasis )
+ {
+ kdDebug(30003) << "Saving to OASIS format" << endl;
+ // Tell KoStore not to touch the file names
+ store->disallowNameExpansion();
+ KoOasisStore oasisStore( store );
+ KoXmlWriter* manifestWriter = oasisStore.manifestWriter( mimeType );
+
+ if ( !saveOasis( store, manifestWriter ) )
+ {
+ kdDebug(30003) << "saveOasis failed" << endl;
+ delete store;
+ return false;
+ }
+
+ // Save embedded objects
+ if ( !saveChildrenOasis( store, manifestWriter ) )
+ {
+ kdDebug(30003) << "saveChildrenOasis failed" << endl;
+ delete store;
+ return false;
+ }
+
+ if ( store->open( "meta.xml" ) )
+ {
+ if ( !d->m_docInfo->saveOasis( store ) || !store->close() ) {
+ delete store;
+ return false;
+ }
+ manifestWriter->addManifestEntry( "meta.xml", "text/xml" );
+ }
+ else
+ {
+ d->lastErrorMessage = i18n( "Not able to write '%1'. Partition full?" ).arg( "meta.xml" );
+ delete store;
+ return false;
+ }
+
+ if ( store->open( "Thumbnails/thumbnail.png" ) )
+ {
+ if ( !saveOasisPreview( store, manifestWriter ) || !store->close() ) {
+ d->lastErrorMessage = i18n( "Error while trying to write '%1'. Partition full?" ).arg( "Thumbnails/thumbnail.png" );
+ delete store;
+ return false;
+ }
+ // No manifest entry!
+ }
+ else
+ {
+ d->lastErrorMessage = i18n( "Not able to write '%1'. Partition full?" ).arg( "Thumbnails/thumbnail.png" );
+ delete store;
+ return false;
+ }
+
+ // Write out manifest file
+ if ( !oasisStore.closeManifestWriter() )
+ {
+ d->lastErrorMessage = i18n( "Error while trying to write '%1'. Partition full?" ).arg( "META-INF/manifest.xml" );
+ delete store;
+ return false;
+ }
+
+ delete store;
+ }
+ else
+ {
+ // Save internal children first since they might get a new url
+ if ( !saveChildren( store ) && !oasis )
+ {
+ if ( d->lastErrorMessage.isEmpty() )
+ d->lastErrorMessage = i18n( "Error while saving embedded documents" ); // more details needed
+ delete store;
+ return false;
+ }
+
+ kdDebug(30003) << "Saving root" << endl;
+ if ( store->open( "root" ) )
+ {
+ KoStoreDevice dev( store );
+ if ( !saveToStream( &dev ) || !store->close() )
+ {
+ kdDebug(30003) << "saveToStream failed" << endl;
+ delete store;
+ return false;
+ }
+ }
+ else
+ {
+ d->lastErrorMessage = i18n( "Not able to write '%1'. Partition full?" ).arg( "maindoc.xml" );
+ delete store;
+ return false;
+ }
+ if ( store->open( "documentinfo.xml" ) )
+ {
+ QDomDocument doc = d->m_docInfo->save();
+ KoStoreDevice dev( store );
+
+ QCString s = doc.toCString(); // this is already Utf8!
+ (void)dev.writeBlock( s.data(), s.size()-1 );
+ (void)store->close();
+ }
+
+ if ( store->open( "preview.png" ) )
+ {
+ // ### TODO: missing error checking (The partition could be full!)
+ savePreview( store );
+ (void)store->close();
+ }
+
+ if ( !completeSaving( store ) )
+ {
+ delete store;
+ return false;
+ }
+ kdDebug(30003) << "Saving done of url: " << url().url() << endl;
+ delete store;
+ }
+ if ( !saveExternalChildren() )
+ {
+ return false;
+ }
+ return true;
+}
+
+bool KoDocument::saveToStream( QIODevice * dev )
+{
+ QDomDocument doc = saveXML();
+ // Save to buffer
+ QCString s = doc.toCString(); // utf8 already
+ // We use QCString::size()-1 here, not the official QCString::length
+ // It works here, as we are not modifying QCString as QByteArray
+ int nwritten = dev->writeBlock( s.data(), s.size()-1 );
+ if ( nwritten != (int)s.size()-1 )
+ kdWarning(30003) << "KoDocument::saveToStream wrote " << nwritten << " - expected " << s.size()-1 << endl;
+ return nwritten == (int)s.size()-1;
+}
+
+// Called for embedded documents
+bool KoDocument::saveToStore( KoStore* _store, const QString & _path )
+{
+ kdDebug(30003) << "Saving document to store " << _path << endl;
+
+ // Use the path as the internal url
+ if ( _path.startsWith( STORE_PROTOCOL ) )
+ m_url = KURL( _path );
+ else // ugly hack to pass a relative URI
+ m_url = KURL( INTERNAL_PREFIX + _path );
+
+ // To make the children happy cd to the correct directory
+ _store->pushDirectory();
+ _store->enterDirectory( _path );
+
+ // Save childen first since they might get a new url
+ if ( !saveChildren( _store ) )
+ return false;
+
+ // In the current directory we're the king :-)
+ if ( _store->open( "root" ) )
+ {
+ KoStoreDevice dev( _store );
+ if ( !saveToStream( &dev ) )
+ {
+ _store->close();
+ return false;
+ }
+ if ( !_store->close() )
+ return false;
+ }
+
+ if ( !completeSaving( _store ) )
+ return false;
+
+ // Now that we're done leave the directory again
+ _store->popDirectory();
+
+ kdDebug(30003) << "Saved document to store" << endl;
+
+ return true;
+}
+
+bool KoDocument::saveOasisPreview( KoStore* store, KoXmlWriter* manifestWriter )
+{
+ const QPixmap pix = generatePreview( QSize( 128, 128 ) );
+ QImage preview ( pix.convertToImage().convertDepth( 32, Qt::ColorOnly ) );
+ if ( !preview.hasAlphaBuffer() )
+ {
+ preview.setAlphaBuffer( true );
+ }
+ // ### TODO: freedesktop.org Thumbnail specification (date...)
+ KoStoreDevice io ( store );
+ if ( !io.open( IO_WriteOnly ) )
+ return false;
+ if ( ! preview.save( &io, "PNG", 0 ) )
+ return false;
+ io.close();
+ manifestWriter->addManifestEntry( "Thumbnails/", "" );
+ manifestWriter->addManifestEntry( "Thumbnails/thumbnail.png", "" );
+ return true;
+}
+
+bool KoDocument::savePreview( KoStore* store )
+{
+ QPixmap pix = generatePreview(QSize(256, 256));
+ // Reducing to 8bpp reduces file sizes quite a lot.
+ const QImage preview ( pix.convertToImage().convertDepth( 8, Qt::AvoidDither | Qt::DiffuseDither) );
+ KoStoreDevice io ( store );
+ if ( !io.open( IO_WriteOnly ) )
+ return false;
+ if ( ! preview.save( &io, "PNG" ) ) // ### TODO What is -9 in quality terms?
+ return false;
+ io.close();
+ return true;
+}
+
+QPixmap KoDocument::generatePreview( const QSize& size )
+{
+ double docWidth, docHeight;
+ int pixmapSize = QMAX(size.width(), size.height());
+
+ if (m_pageLayout.ptWidth > 1.0) {
+ docWidth = m_pageLayout.ptWidth / 72 * KoGlobal::dpiX();
+ docHeight = m_pageLayout.ptHeight / 72 * KoGlobal::dpiY();
+
+ } else {
+ // If we don't have a page layout, just draw the top left hand corner
+ docWidth = 500.0;
+ docHeight = 500.0;
+ }
+
+ double ratio = docWidth / docHeight;
+
+ QPixmap pix;
+ int previewWidth, previewHeight;
+ if (ratio > 1.0)
+ {
+ previewWidth = (int) pixmapSize;
+ previewHeight = (int) (pixmapSize / ratio);
+ }
+ else
+ {
+ previewWidth = (int) (pixmapSize * ratio);
+ previewHeight = (int) pixmapSize;
+ }
+
+ pix.resize((int)docWidth, (int)docHeight);
+
+ pix.fill( QColor( 245, 245, 245 ) );
+
+ QRect rc(0, 0, pix.width(), pix.height());
+
+ QPainter p;
+ p.begin(&pix);
+ paintEverything(p, rc, false);
+ p.end();
+
+ // ### TODO: why re-convert it to a QPixmap, when mostly it will be re-converted back to a QImage in the calling function
+ pix.convertFromImage(pix.convertToImage().smoothScale(previewWidth, previewHeight));
+
+ return pix;
+}
+
+QString KoDocument::autoSaveFile( const QString & path ) const
+{
+ // set local again as it can happen that it gets resetted
+ // so that all saved numbers have a . and not e.g a , as a
+ // decimal seperator
+ setlocale( LC_NUMERIC, "C" );
+
+ // Using the extension allows to avoid relying on the mime magic when opening
+ KMimeType::Ptr mime = KMimeType::mimeType( nativeFormatMimeType() );
+ QString extension = mime->property( "X-KDE-NativeExtension" ).toString();
+ if ( path.isEmpty() )
+ {
+ // Never saved? Use a temp file in $HOME then
+ // Yes, two open unnamed docs will overwrite each other's autosave file,
+ // but hmm, we can only do something if that's in the same process anyway...
+ QString ret = QDir::homeDirPath() + "/." + QString::fromLatin1(instance()->instanceName()) + ".autosave" + extension;
+ return ret;
+ }
+ else
+ {
+ KURL url( path );
+ Q_ASSERT( url.isLocalFile() );
+ QString dir = url.directory(false);
+ QString filename = url.fileName();
+ return dir + "." + filename + ".autosave" + extension;
+ }
+}
+
+bool KoDocument::checkAutoSaveFile()
+{
+ QString asf = autoSaveFile( QString::null ); // the one in $HOME
+ //kdDebug(30003) << "asf=" << asf << endl;
+ if ( QFile::exists( asf ) )
+ {
+ QDateTime date = QFileInfo(asf).lastModified();
+ QString dateStr = date.toString(Qt::LocalDate);
+ int res = KMessageBox::warningYesNoCancel(
+ 0, i18n( "An autosaved file for an unnamed document exists in %1.\nThis file is dated %2\nDo you want to open it?" )
+ .arg(asf, dateStr) );
+ switch(res) {
+ case KMessageBox::Yes : {
+ KURL url;
+ url.setPath( asf );
+ bool ret = openURL( url );
+ if ( ret )
+ resetURL();
+ return ret;
+ }
+ case KMessageBox::No :
+ QFile::remove( asf );
+ return false;
+ default: // Cancel
+ return false;
+ }
+ }
+ return false;
+}
+
+bool KoDocument::import( const KURL & _url )
+{
+ bool ret;
+
+ kdDebug (30003) << "KoDocument::import url=" << _url.url() << endl;
+ d->m_isImporting = true;
+
+ // open...
+ ret = openURL (_url);
+
+ // reset m_url & m_file (kindly? set by KParts::openURL()) to simulate a
+ // File --> Import
+ if (ret)
+ {
+ kdDebug (30003) << "KoDocument::import success, resetting url" << endl;
+ resetURL ();
+ setTitleModified ();
+ }
+
+ d->m_isImporting = false;
+
+ return ret;
+}
+
+bool KoDocument::openURL( const KURL & _url )
+{
+ kdDebug(30003) << "KoDocument::openURL url=" << _url.url() << endl;
+ d->lastErrorMessage = QString::null;
+
+ // Reimplemented, to add a check for autosave files and to improve error reporting
+ if ( !_url.isValid() )
+ {
+ d->lastErrorMessage = i18n( "Malformed URL\n%1" ).arg( _url.url() ); // ## used anywhere ?
+ return false;
+ }
+ if ( !closeURL() )
+ return false;
+
+ KURL url( _url );
+ bool autosaveOpened = false;
+ d->m_bLoading = true;
+ if ( url.isLocalFile() && d->m_shouldCheckAutoSaveFile )
+ {
+ QString file = url.path();
+ QString asf = autoSaveFile( file );
+ if ( QFile::exists( asf ) )
+ {
+ //kdDebug(30003) << "KoDocument::openURL asf=" << asf << endl;
+ // ## TODO compare timestamps ?
+ int res = KMessageBox::warningYesNoCancel( 0,
+ i18n( "An autosaved file exists for this document.\nDo you want to open it instead?" ));
+ switch(res) {
+ case KMessageBox::Yes :
+ url.setPath( asf );
+ autosaveOpened = true;
+ break;
+ case KMessageBox::No :
+ QFile::remove( asf );
+ break;
+ default: // Cancel
+ d->m_bLoading = false;
+ return false;
+ }
+ }
+ }
+
+ bool ret = KParts::ReadWritePart::openURL( url );
+
+ if ( autosaveOpened )
+ resetURL(); // Force save to act like 'Save As'
+ else
+ {
+ // We have no koffice shell when we are being embedded as a readonly part.
+ //if ( d->m_shells.isEmpty() )
+ // kdWarning(30003) << "KoDocument::openURL no shell yet !" << endl;
+ // Add to recent actions list in our shells
+ QPtrListIterator<KoMainWindow> it( d->m_shells );
+ for (; it.current(); ++it )
+ it.current()->addRecentURL( _url );
+ }
+ return ret;
+}
+
+bool KoDocument::openFile()
+{
+ //kdDebug(30003) << "KoDocument::openFile for " << m_file << endl;
+ if ( !QFile::exists(m_file) )
+ {
+ QApplication::restoreOverrideCursor();
+ if ( d->m_autoErrorHandlingEnabled )
+ // Maybe offer to create a new document with that name ?
+ KMessageBox::error(0L, i18n("The file %1 does not exist.").arg(m_file) );
+ d->m_bLoading = false;
+ return false;
+ }
+
+ QApplication::setOverrideCursor( waitCursor );
+
+ d->m_specialOutputFlag = 0;
+ QCString _native_format = nativeFormatMimeType();
+
+ KURL u;
+ u.setPath( m_file );
+ QString typeName = KMimeType::findByURL( u, 0, true )->name();
+
+ // Allow to open backup files, don't keep the mimetype application/x-trash.
+ if ( typeName == "application/x-trash" )
+ {
+ QString path = u.path();
+ QStringList patterns = KMimeType::mimeType( typeName )->patterns();
+ // Find the extension that makes it a backup file, and remove it
+ for( QStringList::Iterator it = patterns.begin(); it != patterns.end(); ++it ) {
+ QString ext = *it;
+ if ( !ext.isEmpty() && ext[0] == '*' )
+ {
+ ext.remove(0, 1);
+ if ( path.endsWith( ext ) ) {
+ path.truncate( path.length() - ext.length() );
+ break;
+ }
+ }
+ }
+ typeName = KMimeType::findByPath( path, 0, true )->name();
+ }
+
+ // Special case for flat XML files (e.g. using directory store)
+ if ( u.fileName() == "maindoc.xml" || u.fileName() == "content.xml" || typeName == "inode/directory" )
+ {
+ typeName = _native_format; // Hmm, what if it's from another app? ### Check mimetype
+ d->m_specialOutputFlag = SaveAsDirectoryStore;
+ kdDebug(30003) << "KoDocument::openFile loading " << u.fileName() << ", using directory store for " << m_file << "; typeName=" << typeName << endl;
+ }
+ kdDebug(30003) << "KoDocument::openFile " << m_file << " type:" << typeName << endl;
+
+ QString importedFile = m_file;
+
+ if ( !isNativeFormat( typeName.latin1() ) ) {
+ if ( !d->filterManager )
+ d->filterManager = new KoFilterManager( this );
+ KoFilter::ConversionStatus status;
+ importedFile = d->filterManager->import( m_file, status );
+ if ( status != KoFilter::OK )
+ {
+ QApplication::restoreOverrideCursor();
+
+ QString msg;
+ switch( status )
+ {
+ case KoFilter::OK: break;
+
+ case KoFilter::CreationError:
+ msg = i18n( "Creation error" ); break;
+
+ case KoFilter::FileNotFound:
+ msg = i18n( "File not found" ); break;
+
+ case KoFilter::StorageCreationError:
+ msg = i18n( "Cannot create storage" ); break;
+
+ case KoFilter::BadMimeType:
+ msg = i18n( "Bad MIME type" ); break;
+
+ case KoFilter::EmbeddedDocError:
+ msg = i18n( "Error in embedded document" ); break;
+
+ case KoFilter::WrongFormat:
+ msg = i18n( "Format not recognized" ); break;
+
+ case KoFilter::NotImplemented:
+ msg = i18n( "Not implemented" ); break;
+
+ case KoFilter::ParsingError:
+ msg = i18n( "Parsing error" ); break;
+
+ case KoFilter::PasswordProtected:
+ msg = i18n( "Document is password protected" ); break;
+
+ case KoFilter::InternalError:
+ case KoFilter::UnexpectedEOF:
+ case KoFilter::UnexpectedOpcode:
+ case KoFilter::StupidError: // ?? what is this ??
+ case KoFilter::UsageError:
+ msg = i18n( "Internal error" ); break;
+
+ case KoFilter::OutOfMemory:
+ msg = i18n( "Out of memory" ); break;
+
+ case KoFilter::UserCancelled:
+ case KoFilter::BadConversionGraph:
+ // intentionally we do not prompt the error message here
+ break;
+
+ default: msg = i18n( "Unknown error" ); break;
+ }
+
+ if( d->m_autoErrorHandlingEnabled && !msg.isEmpty())
+ {
+ QString errorMsg( i18n( "Could not open\n%2.\nReason: %1" ) );
+ QString docUrl = url().prettyURL( 0, KURL::StripFileProtocol );
+ KMessageBox::error( 0L, errorMsg.arg(msg).arg(docUrl) );
+ }
+
+ d->m_bLoading = false;
+ return false;
+ }
+ kdDebug(30003) << "KoDocument::openFile - importedFile '" << importedFile
+ << "', status: " << static_cast<int>( status ) << endl;
+ }
+
+ QApplication::restoreOverrideCursor();
+
+ bool ok = true;
+
+ if (!importedFile.isEmpty()) // Something to load (tmp or native file) ?
+ {
+ // The filter, if any, has been applied. It's all native format now.
+ if ( !loadNativeFormat( importedFile ) )
+ {
+ ok = false;
+ if ( d->m_autoErrorHandlingEnabled )
+ {
+ showLoadingErrorDialog();
+ }
+ }
+ }
+
+ if ( importedFile != m_file )
+ {
+ // We opened a temporary file (result of an import filter)
+ // Set document URL to empty - we don't want to save in /tmp !
+ // But only if in readwrite mode (no saving problem otherwise)
+ // --
+ // But this isn't true at all. If this is the result of an
+ // import, then importedFile=temporary_file.kwd and
+ // m_file/m_url=foreignformat.ext so m_url is correct!
+ // So don't resetURL() or else the caption won't be set when
+ // foreign files are opened (an annoying bug).
+ // - Clarence
+ //
+#if 0
+ if ( isReadWrite() )
+ resetURL();
+#endif
+
+ // remove temp file - uncomment this to debug import filters
+ if(!importedFile.isEmpty()) {
+ QFile::remove( importedFile );
+ }
+ }
+
+ if ( ok && d->m_bSingleViewMode )
+ {
+ // See addClient below
+ KXMLGUIFactory* guiFactory = factory();
+ if( guiFactory ) // 0L when splitting views in konq, for some reason
+ guiFactory->removeClient( this );
+
+ if ( !d->m_views.isEmpty() )
+ {
+ // We already had a view (this happens when doing reload in konqueror)
+ KoView* v = d->m_views.first();
+ if( guiFactory )
+ guiFactory->removeClient( v );
+ removeView( v );
+ delete v;
+ Q_ASSERT( d->m_views.isEmpty() );
+ }
+
+ KoView *view = createView( d->m_wrapperWidget );
+ d->m_wrapperWidget->setKoView( view );
+ view->show();
+
+ // Ok, now we have a view, so action() and domDocument() will work as expected
+ // -> rebuild GUI
+ if ( guiFactory )
+ guiFactory->addClient( this );
+ }
+
+ if ( ok )
+ {
+ setMimeTypeAfterLoading( typeName );
+ }
+ d->m_bLoading = false;
+ return ok;
+}
+
+// shared between openFile and koMainWindow's "create new empty document" code
+void KoDocument::setMimeTypeAfterLoading( const QString& mimeType )
+{
+ d->mimeType = mimeType.latin1();
+
+ d->outputMimeType = d->mimeType;
+
+ const bool needConfirm = !isNativeFormat( d->mimeType );
+ setConfirmNonNativeSave( false, needConfirm );
+ setConfirmNonNativeSave( true, needConfirm );
+}
+
+// The caller must call store->close() if loadAndParse returns true.
+bool KoDocument::oldLoadAndParse(KoStore* store, const QString& filename, QDomDocument& doc)
+{
+ //kdDebug(30003) << "oldLoadAndParse: Trying to open " << filename << endl;
+
+ if (!store->open(filename))
+ {
+ kdWarning(30003) << "Entry " << filename << " not found!" << endl;
+ d->lastErrorMessage = i18n( "Could not find %1" ).arg( filename );
+ return false;
+ }
+ // Error variables for QDomDocument::setContent
+ QString errorMsg;
+ int errorLine, errorColumn;
+ bool ok = doc.setContent( store->device(), &errorMsg, &errorLine, &errorColumn );
+ if ( !ok )
+ {
+ kdError(30003) << "Parsing error in " << filename << "! Aborting!" << endl
+ << " In line: " << errorLine << ", column: " << errorColumn << endl
+ << " Error message: " << errorMsg << endl;
+ d->lastErrorMessage = i18n( "Parsing error in %1 at line %2, column %3\nError message: %4" )
+ .arg( filename ).arg( errorLine ).arg( errorColumn )
+ .arg( i18n ( "QXml", errorMsg.utf8() ) );
+ store->close();
+ return false;
+ }
+ kdDebug(30003) << "File " << filename << " loaded and parsed" << endl;
+ return true;
+}
+
+bool KoDocument::loadNativeFormat( const QString & file )
+{
+ QFileInfo fileInfo( file );
+ if ( !fileInfo.exists() ) // check duplicated from openURL, but this is useful for templates
+ {
+ d->lastErrorMessage = i18n("The file %1 does not exist.").arg(file);
+ return false;
+ }
+ if ( !fileInfo.isFile() )
+ {
+ d->lastErrorMessage = i18n( "%1 is not a file." ).arg(file);
+ return false;
+ }
+
+ QApplication::setOverrideCursor( waitCursor );
+
+ kdDebug(30003) << "KoDocument::loadNativeFormat( " << file << " )" << endl;
+
+ QFile in;
+ bool isRawXML = false;
+ if ( d->m_specialOutputFlag != SaveAsDirectoryStore ) // Don't try to open a directory ;)
+ {
+ in.setName(file);
+ if ( !in.open( IO_ReadOnly ) )
+ {
+ QApplication::restoreOverrideCursor();
+ d->lastErrorMessage = i18n( "Could not open the file for reading (check read permissions)." );
+ return false;
+ }
+
+ // Try to find out whether it is a mime multi part file
+ char buf[5];
+ if ( in.readBlock( buf, 4 ) < 4 )
+ {
+ QApplication::restoreOverrideCursor();
+ in.close();
+ d->lastErrorMessage = i18n( "Could not read the beginning of the file." );
+ return false;
+ }
+ // ### TODO: allow UTF-16
+ isRawXML = (strncasecmp( buf, "<?xm", 4 ) == 0);
+ if ( !isRawXML ) {
+ // Still check for broken MathML files, which seem to be rather common
+ in.reset();
+ // Remove spaces
+ do {
+ if ( in.readBlock( buf , 1 ) < 1 )
+ {
+ QApplication::restoreOverrideCursor();
+ in.close();
+ d->lastErrorMessage = i18n( "Could not read the beginning of the file." );
+ return false;
+ }
+ } while ( QChar( buf[0] ).isSpace() );
+ if ( buf[0] == '<' ) { // First not-space character
+ if ( in.readBlock( buf , 4 ) < 4 )
+ {
+ QApplication::restoreOverrideCursor();
+ in.close();
+ d->lastErrorMessage = i18n( "Could not read the beginning of the file." );
+ return false;
+ }
+ isRawXML = (strncasecmp( buf, "math", 4 ) == 0); // file begins with <math ?
+ }
+ }
+ //kdDebug(30003) << "PATTERN=" << buf << endl;
+ }
+ // Is it plain XML?
+ if ( isRawXML )
+ {
+ in.at(0);
+ QString errorMsg;
+ int errorLine;
+ int errorColumn;
+ QDomDocument doc;
+ bool res;
+ if ( doc.setContent( &in, true, &errorMsg, &errorLine, &errorColumn ) )
+ {
+ res = loadXML( &in, doc );
+ if ( res )
+ res = completeLoading( 0L );
+ }
+ else
+ {
+ kdError (30003) << "Parsing Error! Aborting! (in KoDocument::loadNativeFormat (QFile))" << endl
+ << " Line: " << errorLine << " Column: " << errorColumn << endl
+ << " Message: " << errorMsg << endl;
+ d->lastErrorMessage = i18n( "parsing error in the main document at line %1, column %2\nError message: %3" )
+ .arg( errorLine ).arg( errorColumn ).arg( i18n ( errorMsg.utf8() ) );
+ res=false;
+ }
+
+ QApplication::restoreOverrideCursor();
+ in.close();
+ m_bEmpty = false;
+ return res;
+ } else
+ { // It's a koffice store (tar.gz, zip, directory, etc.)
+ in.close();
+
+ return loadNativeFormatFromStore( file );
+ }
+}
+
+bool KoDocument::loadNativeFormatFromStore( const QString& file )
+{
+ KoStore::Backend backend = (d->m_specialOutputFlag == SaveAsDirectoryStore) ? KoStore::Directory : KoStore::Auto;
+ KoStore * store = KoStore::createStore( file, KoStore::Read, "", backend );
+
+ if ( store->bad() )
+ {
+ d->lastErrorMessage = i18n( "Not a valid KOffice file: %1" ).arg( file );
+ delete store;
+ QApplication::restoreOverrideCursor();
+ return false;
+ }
+
+ bool oasis = true;
+ // OASIS/OOo file format?
+ if ( store->hasFile( "content.xml" ) )
+ {
+ store->disallowNameExpansion();
+
+ KoOasisStore oasisStore( store );
+ // We could check the 'mimetype' file, but let's skip that and be tolerant.
+
+ if ( !loadOasisFromStore( store ) ) {
+ delete store;
+ QApplication::restoreOverrideCursor();
+ return false;
+ }
+
+ } else if ( store->hasFile( "root" ) ) // Fallback to "old" file format (maindoc.xml)
+ {
+ oasis = false;
+
+ QDomDocument doc;
+ bool ok = oldLoadAndParse( store, "root", doc );
+ if ( ok )
+ ok = loadXML( store->device(), doc );
+ if ( !ok )
+ {
+ delete store;
+ QApplication::restoreOverrideCursor();
+ return false;
+ }
+ store->close();
+
+ if ( !loadChildren( store ) )
+ {
+ kdError(30003) << "ERROR: Could not load children" << endl;
+ // Don't abort, proceed nonetheless
+ }
+
+ } else
+ {
+ kdError(30003) << "ERROR: No maindoc.xml" << endl;
+ d->lastErrorMessage = i18n( "Invalid document: no file 'maindoc.xml'." );
+ delete store;
+ QApplication::restoreOverrideCursor();
+ return false;
+ }
+
+ if ( oasis && store->hasFile( "meta.xml" ) ) {
+ QDomDocument metaDoc;
+ KoOasisStore oasisStore( store );
+ if ( oasisStore.loadAndParse( "meta.xml", metaDoc, d->lastErrorMessage ) ) {
+ d->m_docInfo->loadOasis( metaDoc );
+ }
+ }
+ else if ( !oasis && store->hasFile( "documentinfo.xml" ) )
+ {
+ QDomDocument doc;
+ if ( oldLoadAndParse( store, "documentinfo.xml", doc ) ) {
+ store->close();
+ d->m_docInfo->load( doc );
+ }
+ }
+ else
+ {
+ //kdDebug( 30003 ) << "cannot open document info" << endl;
+ delete d->m_docInfo;
+ d->m_docInfo = new KoDocumentInfo( this, "document info" );
+ }
+
+ bool res = completeLoading( store );
+ delete store;
+ QApplication::restoreOverrideCursor();
+ m_bEmpty = false;
+ return res;
+}
+
+// For embedded documents
+bool KoDocument::loadFromStore( KoStore* _store, const QString& url )
+{
+ if ( _store->open( url ) )
+ {
+ QDomDocument doc;
+ doc.setContent( _store->device() );
+ if ( !loadXML( _store->device(), doc ) )
+ {
+ _store->close();
+ return false;
+ }
+ _store->close();
+ } else {
+ kdWarning() << "couldn't open " << url << endl;
+ }
+
+ _store->pushDirectory();
+ // Store as document URL
+ if ( url.startsWith( STORE_PROTOCOL ) ) {
+ m_url = KURL( url );
+ } else {
+ m_url = KURL( INTERNAL_PREFIX + url );
+ _store->enterDirectory( url );
+ }
+
+ if ( !loadChildren( _store ) )
+ {
+ kdError(30003) << "ERROR: Could not load children" << endl;
+#if 0
+ return false;
+#endif
+ }
+
+ bool result = completeLoading( _store );
+
+ // Restore the "old" path
+ _store->popDirectory();
+
+ return result;
+}
+
+bool KoDocument::loadOasisFromStore( KoStore* store )
+{
+ KoOasisStyles oasisStyles;
+ QDomDocument contentDoc;
+ QDomDocument settingsDoc;
+ KoOasisStore oasisStore( store );
+ bool ok = oasisStore.loadAndParse( "content.xml", contentDoc, d->lastErrorMessage );
+ if ( !ok )
+ return false;
+
+ QDomDocument stylesDoc;
+ (void)oasisStore.loadAndParse( "styles.xml", stylesDoc, d->lastErrorMessage );
+ // Load styles from style.xml
+ oasisStyles.createStyleMap( stylesDoc, true );
+ // Also load styles from content.xml
+ oasisStyles.createStyleMap( contentDoc, false );
+
+ // TODO post 1.4, pass manifestDoc to the apps so that they don't have to do it themselves
+ // (when calling KoDocumentChild::loadOasisDocument)
+ //QDomDocument manifestDoc;
+ //KoOasisStore oasisStore( store );
+ //if ( !oasisStore.loadAndParse( "tar:/META-INF/manifest.xml", manifestDoc, d->lastErrorMessage ) )
+ // return false;
+
+ if ( store->hasFile( "settings.xml" ) ) {
+ (void)oasisStore.loadAndParse( "settings.xml", settingsDoc, d->lastErrorMessage );
+ }
+ if ( !loadOasis( contentDoc, oasisStyles, settingsDoc, store ) )
+ return false;
+
+ return true;
+}
+
+bool KoDocument::isInOperation() const
+{
+ return d->m_numOperations > 0;
+}
+
+void KoDocument::emitBeginOperation()
+{
+
+ /* if we're already in an operation, don't send the signal again */
+ if (!isInOperation())
+ emit sigBeginOperation();
+ d->m_numOperations++;
+}
+
+void KoDocument::emitEndOperation()
+{
+ d->m_numOperations--;
+
+ /* don't end the operation till we've cleared all the nested operations */
+ if (d->m_numOperations == 0)
+ emit sigEndOperation();
+ else if (d->m_numOperations < 0)
+ /* ignore 'end' calls with no matching 'begin' call */
+ d->m_numOperations = 0;
+}
+
+
+bool KoDocument::isStoredExtern() const
+{
+ return !storeInternal() && hasExternURL();
+}
+
+void KoDocument::setModified( bool mod )
+{
+ if ( isAutosaving() ) // ignore setModified calls due to autosaving
+ return;
+
+ //kdDebug(30003)<<k_funcinfo<<" url:" << m_url.path() << endl;
+ //kdDebug(30003)<<k_funcinfo<<" mod="<<mod<<" MParts mod="<<KParts::ReadWritePart::isModified()<<" isModified="<<isModified()<<endl;
+
+ if ( mod && !d->modifiedAfterAutosave ) {
+ // First change since last autosave -> start the autosave timer
+ setAutoSave( d->m_autoSaveDelay );
+ }
+ d->modifiedAfterAutosave = mod;
+
+ if ( mod == isModified() )
+ return;
+
+ KParts::ReadWritePart::setModified( mod );
+
+ if ( mod ) {
+ m_bEmpty = FALSE;
+ } else {
+ // When saving this document, all non-external child documents get saved too.
+ QPtrListIterator<KoDocumentChild> it = children();
+ for (; it.current(); ++it )
+ {
+ KoDocument *doc = it.current()->document();
+ if ( doc && !it.current()->isStoredExtern() && !it.current()->isDeleted() && doc->isModified() )
+ doc->setModified( false );
+ }
+ }
+
+ // This influences the title
+ setTitleModified();
+ emit modified( mod );
+}
+
+void KoDocument::setDoNotSaveExtDoc( bool on )
+{
+ d->m_doNotSaveExtDoc = on;
+}
+
+int KoDocument::queryCloseDia()
+{
+ //kdDebug(30003)<<k_funcinfo<<endl;
+
+ QString name;
+ if ( documentInfo() )
+ {
+ name = documentInfo()->title();
+ }
+ if ( name.isEmpty() )
+ name = url().fileName();
+
+ if ( name.isEmpty() )
+ name = i18n( "Untitled" );
+
+ int res = KMessageBox::warningYesNoCancel( 0L,
+ i18n( "<p>The document <b>'%1'</b> has been modified.</p><p>Do you want to save it?</p>" ).arg(name));
+
+ switch(res)
+ {
+ case KMessageBox::Yes :
+ setDoNotSaveExtDoc(); // Let save() only save myself and my internal docs
+ save(); // NOTE: External files always in native format. ###TODO: Handle non-native format
+ setModified( false ); // Now when queryClose() is called by closeEvent it won't do anything.
+ break;
+ case KMessageBox::No :
+ removeAutoSaveFiles();
+ setModified( false ); // Now when queryClose() is called by closeEvent it won't do anything.
+ break;
+ default : // case KMessageBox::Cancel :
+ return res; // cancels the rest of the files
+ }
+ return res;
+}
+
+int KoDocument::queryCloseExternalChildren()
+{
+ //kdDebug(30003)<<k_funcinfo<<" checking for children in: "<<url().url()<<endl;
+ setDoNotSaveExtDoc(false);
+ QPtrListIterator<KoDocumentChild> it( children() );
+ for (; it.current(); ++it )
+ {
+ if ( !it.current()->isDeleted() )
+ {
+ KoDocument *doc = it.current()->document();
+ if ( doc )
+ {
+ bool foo = doc->isStoredExtern();
+ kdDebug(36001) << "========== isStoredExtern() returned "
+ << foo << " ==========" << endl;
+
+ if ( foo ) //###TODO: Handle non-native mimetype docs
+ {
+ {
+ kdDebug(30003)<<k_funcinfo<<" found modified child: "<<doc->url().url()<<" extern="<<doc->isStoredExtern()<<endl;
+ if ( doc->queryCloseDia() == KMessageBox::Cancel )
+ return KMessageBox::Cancel;
+ }
+ }
+ if ( doc->queryCloseExternalChildren() == KMessageBox::Cancel )
+ return KMessageBox::Cancel;
+ }
+ }
+ }
+ return KMessageBox::Ok;
+}
+
+void KoDocument::setTitleModified( const QString caption, bool mod )
+{
+ //kdDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" caption: "<<caption<<" mod: "<<mod<<endl;
+ KoDocument *doc = dynamic_cast<KoDocument *>( parent() );
+ if ( doc )
+ {
+ doc->setTitleModified( caption, mod );
+ return;
+ }
+ // we must be root doc so update caption in all related windows
+ QPtrListIterator<KoMainWindow> it( d->m_shells );
+ for (; it.current(); ++it )
+ {
+ it.current()->updateCaption();
+ it.current()->updateReloadFileAction(this);
+ it.current()->updateVersionsFileAction(this);
+ }
+}
+
+void KoDocument::setTitleModified()
+{
+ //kdDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" extern: "<<isStoredExtern()<<" current: "<<d->m_current<<endl;
+ KoDocument *doc = dynamic_cast<KoDocument *>( parent() );
+ QString caption;
+ if ( (url().isEmpty() || isStoredExtern()) && d->m_current )
+ {
+ // Get caption from document info (title(), in about page)
+ if ( documentInfo() )
+ {
+ KoDocumentInfoPage * page = documentInfo()->page( QString::fromLatin1("about") );
+ if (page)
+ caption = static_cast<KoDocumentInfoAbout *>(page)->title();
+ }
+ if ( caption.isEmpty() )
+ caption = url().prettyURL( 0, KURL::StripFileProtocol ); // Fall back to document URL
+
+ //kdDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" caption: "<<caption<<endl;
+ if ( doc )
+ {
+ doc->setTitleModified( caption, isModified() );
+ return;
+ }
+ else
+ {
+ // we must be root doc so update caption in all related windows
+ setTitleModified( caption, isModified() );
+ return;
+ }
+ }
+ if ( doc )
+ {
+ // internal doc or not current doc, so pass on the buck
+ doc->setTitleModified();
+ }
+}
+
+bool KoDocument::loadChildren( KoStore* )
+{
+ return true;
+}
+
+bool KoDocument::completeLoading( KoStore* )
+{
+ return true;
+}
+
+bool KoDocument::completeSaving( KoStore* )
+{
+ return true;
+}
+
+QDomDocument KoDocument::createDomDocument( const QString& tagName, const QString& version ) const
+{
+ return createDomDocument( instance()->instanceName(), tagName, version );
+}
+
+//static
+QDomDocument KoDocument::createDomDocument( const QString& appName, const QString& tagName, const QString& version )
+{
+ QDomImplementation impl;
+ QString url = QString("http://www.koffice.org/DTD/%1-%1.dtd").arg(appName).arg(version);
+ QDomDocumentType dtype = impl.createDocumentType( tagName,
+ QString("-//KDE//DTD %1 %1//EN").arg(appName).arg(version),
+ url );
+ // The namespace URN doesn't need to include the version number.
+ QString namespaceURN = QString("http://www.koffice.org/DTD/%1").arg(appName);
+ QDomDocument doc = impl.createDocument( namespaceURN, tagName, dtype );
+ doc.insertBefore( doc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ), doc.documentElement() );
+ return doc;
+}
+
+KoXmlWriter* KoDocument::createOasisXmlWriter( QIODevice* dev, const char* rootElementName )
+{
+ KoXmlWriter* writer = new KoXmlWriter( dev );
+ writer->startDocument( rootElementName );
+ writer->startElement( rootElementName );
+ writer->addAttribute( "xmlns:office", KoXmlNS::office );
+ writer->addAttribute( "xmlns:meta", KoXmlNS::meta );
+
+ if ( qstrcmp( rootElementName, "office:document-meta" ) != 0 ) {
+ writer->addAttribute( "xmlns:config", KoXmlNS::config );
+ writer->addAttribute( "xmlns:text", KoXmlNS::text );
+ writer->addAttribute( "xmlns:table", KoXmlNS::table );
+ writer->addAttribute( "xmlns:draw", KoXmlNS::draw );
+ writer->addAttribute( "xmlns:presentation", KoXmlNS::presentation );
+ writer->addAttribute( "xmlns:dr3d", KoXmlNS::dr3d );
+ writer->addAttribute( "xmlns:chart", KoXmlNS::chart );
+ writer->addAttribute( "xmlns:form", KoXmlNS::form );
+ writer->addAttribute( "xmlns:script", KoXmlNS::script );
+ writer->addAttribute( "xmlns:style", KoXmlNS::style );
+ writer->addAttribute( "xmlns:number", KoXmlNS::number );
+ writer->addAttribute( "xmlns:math", KoXmlNS::math );
+ writer->addAttribute( "xmlns:svg", KoXmlNS::svg );
+ writer->addAttribute( "xmlns:fo", KoXmlNS::fo );
+ writer->addAttribute( "xmlns:koffice", KoXmlNS::koffice );
+ }
+ // missing: office:version="1.0"
+
+ writer->addAttribute( "xmlns:dc", KoXmlNS::dc );
+ writer->addAttribute( "xmlns:xlink", KoXmlNS::xlink );
+ return writer;
+}
+
+QDomDocument KoDocument::saveXML()
+{
+ kdError(30003) << "KoDocument::saveXML not implemented" << endl;
+ d->lastErrorMessage = i18n( "Internal error: saveXML not implemented" );
+ return QDomDocument();
+}
+
+KService::Ptr KoDocument::nativeService()
+{
+ if ( !m_nativeService )
+ m_nativeService = readNativeService( instance() );
+
+ return m_nativeService;
+}
+
+QCString KoDocument::nativeFormatMimeType() const
+{
+ KService::Ptr service = const_cast<KoDocument *>(this)->nativeService();
+ if ( !service )
+ return QCString();
+ QCString nativeMimeType = service->property( "X-KDE-NativeMimeType" ).toString().latin1();
+ if ( nativeMimeType.isEmpty() ) {
+ // shouldn't happen, let's find out why it happened
+ if ( !service->serviceTypes().contains( "KOfficePart" ) )
+ kdWarning(30003) << "Wrong desktop file, KOfficePart isn't mentionned" << endl;
+ else if ( !KServiceType::serviceType( "KOfficePart" ) )
+ kdWarning(30003) << "The KOfficePart service type isn't installed!" << endl;
+ }
+ return nativeMimeType;
+}
+
+QCString KoDocument::nativeOasisMimeType() const
+{
+ KService::Ptr service = const_cast<KoDocument *>(this)->nativeService();
+ if ( !service )
+ return QCString();
+ return service->property( "X-KDE-NativeOasisMimeType" ).toString().latin1();
+}
+
+
+//static
+KService::Ptr KoDocument::readNativeService( KInstance *instance )
+{
+ QString instname = instance ? instance->instanceName() : kapp->instanceName();
+
+ // The new way is: we look for a foopart.desktop in the kde_services dir.
+ QString servicepartname = instname + "part.desktop";
+ KService::Ptr service = KService::serviceByDesktopPath( servicepartname );
+ if ( service )
+ kdDebug(30003) << servicepartname << " found." << endl;
+ if ( !service )
+ {
+ // The old way is kept as fallback for compatibility, but in theory this is really never used anymore.
+
+ // Try by path first, so that we find the global one (which has the native mimetype)
+ // even if the user created a kword.desktop in ~/.kde/share/applnk or any subdir of it.
+ // If he created it under ~/.kde/share/applnk/Office/ then no problem anyway.
+ service = KService::serviceByDesktopPath( QString::fromLatin1("Office/%1.desktop").arg(instname) );
+ }
+ if ( !service )
+ service = KService::serviceByDesktopName( instname );
+
+ return service;
+}
+
+QCString KoDocument::readNativeFormatMimeType( KInstance *instance ) //static
+{
+ KService::Ptr service = readNativeService( instance );
+ if ( !service )
+ return QCString();
+
+ if ( service->property( "X-KDE-NativeMimeType" ).toString().isEmpty() )
+ {
+ // It may be that the servicetype "KOfficePart" is missing, which leads to this property not being known
+ if ( KServiceType::serviceType( "KOfficePart" ) == 0L )
+ kdError(30003) << "The serviceType KOfficePart is missing. Check that you have a kofficepart.desktop file in the share/servicetypes directory." << endl;
+ else {
+ QString instname = instance ? instance->instanceName() : kapp->instanceName();
+ if ( instname != "koshell" ) // hack for koshell
+ kdWarning(30003) << service->desktopEntryPath() << ": no X-KDE-NativeMimeType entry!" << endl;
+ }
+ }
+
+ return service->property( "X-KDE-NativeMimeType" ).toString().latin1();
+}
+
+QStringList KoDocument::readExtraNativeMimeTypes( KInstance *instance ) //static
+{
+ KService::Ptr service = readNativeService( instance );
+ if ( !service )
+ return QStringList();
+ return service->property( "X-KDE-ExtraNativeMimeTypes" ).toStringList();
+}
+
+void KoDocument::setupXmlReader( QXmlSimpleReader& reader, bool namespaceProcessing )
+{
+ if ( namespaceProcessing )
+ {
+ reader.setFeature( "http://xml.org/sax/features/namespaces", TRUE );
+ reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", FALSE );
+ }
+ else
+ {
+ reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE );
+ reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", TRUE );
+ }
+ reader.setFeature( "http://trolltech.com/xml/features/report-whitespace-only-CharData", TRUE );
+}
+
+
+bool KoDocument::isNativeFormat( const QCString& mimetype ) const
+{
+ if ( mimetype == nativeFormatMimeType() )
+ return true;
+ return extraNativeMimeTypes().contains( mimetype );
+}
+
+QStringList KoDocument::extraNativeMimeTypes() const
+{
+ QStringList lst;
+ // This implementation is temporary while we treat both koffice-1.3 and OASIS formats as native.
+ // But it's good to have this virtual method, in case some app want to
+ // support more than one native format.
+ KService::Ptr service = const_cast<KoDocument *>(this)->nativeService();
+ if ( !service ) // can't happen
+ return lst;
+ return service->property( "X-KDE-ExtraNativeMimeTypes" ).toStringList();
+}
+
+int KoDocument::supportedSpecialFormats() const
+{
+ // Apps which support special output flags can add reimplement and add to this.
+ // E.g. this is how did "saving in the 1.1 format".
+ // SaveAsDirectoryStore is a given since it's implemented by KoDocument itself.
+ return SaveAsDirectoryStore;
+}
+
+void KoDocument::addShell( KoMainWindow *shell )
+{
+ if ( d->m_shells.findRef( shell ) == -1 )
+ {
+ //kdDebug(30003) << "addShell: shell " << (void*)shell << " added to doc " << this << endl;
+ d->m_shells.append( shell );
+ }
+}
+
+void KoDocument::removeShell( KoMainWindow *shell )
+{
+ //kdDebug(30003) << "removeShell: shell " << (void*)shell << " removed from doc " << this << endl;
+ d->m_shells.removeRef( shell );
+}
+
+const QPtrList<KoMainWindow>& KoDocument::shells() const
+{
+ return d->m_shells;
+}
+
+int KoDocument::shellCount() const
+{
+ return d->m_shells.count();
+}
+
+DCOPObject * KoDocument::dcopObject()
+{
+ if ( !d->m_dcopObject )
+ d->m_dcopObject = new KoDocumentIface( this );
+ return d->m_dcopObject;
+}
+
+QCString KoDocument::dcopObjectId() const
+{
+ return const_cast<KoDocument *>(this)->dcopObject()->objId();
+}
+
+void KoDocument::setErrorMessage( const QString& errMsg )
+{
+ d->lastErrorMessage = errMsg;
+}
+
+QString KoDocument::errorMessage() const
+{
+ return d->lastErrorMessage;
+}
+
+void KoDocument::showSavingErrorDialog()
+{
+ if ( d->lastErrorMessage.isEmpty() )
+ {
+ KMessageBox::error( 0L, i18n( "Could not save\n%1" ).arg( m_file ) );
+ }
+ else if ( d->lastErrorMessage != "USER_CANCELED" )
+ {
+ KMessageBox::error( 0L, i18n( "Could not save %1\nReason: %2" ).arg( m_file, d->lastErrorMessage ) );
+ }
+}
+
+void KoDocument::showLoadingErrorDialog()
+{
+ if ( d->lastErrorMessage.isEmpty() )
+ {
+ KMessageBox::error( 0L, i18n( "Could not open\n%1" ).arg( url().prettyURL( 0, KURL::StripFileProtocol ) ) );
+ }
+ else if ( d->lastErrorMessage != "USER_CANCELED" )
+ {
+ KMessageBox::error( 0L, i18n( "Could not open %1\nReason: %2" ).arg( url().prettyURL( 0, KURL::StripFileProtocol ), d->lastErrorMessage ) );
+ }
+}
+
+bool KoDocument::isAutosaving() const
+{
+ return d->m_autosaving;
+}
+
+bool KoDocument::isLoading() const
+{
+ return d->m_bLoading;
+}
+
+void KoDocument::removeAutoSaveFiles()
+{
+ // Eliminate any auto-save file
+ QString asf = autoSaveFile( m_file ); // the one in the current dir
+ if ( QFile::exists( asf ) )
+ QFile::remove( asf );
+ asf = autoSaveFile( QString::null ); // and the one in $HOME
+ if ( QFile::exists( asf ) )
+ QFile::remove( asf );
+}
+
+void KoDocument::setBackupFile( bool _b )
+{
+ d->m_backupFile = _b;
+}
+
+bool KoDocument::backupFile()const
+{
+ return d->m_backupFile;
+}
+
+
+void KoDocument::setBackupPath( const QString & _path)
+{
+ d->m_backupPath = _path;
+}
+
+QString KoDocument::backupPath()const
+{
+ return d->m_backupPath;
+}
+
+void KoDocument::setCurrent( bool on )
+{
+ //kdDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" set to: "<<on<<endl;
+ KoDocument *doc = dynamic_cast<KoDocument *>( parent() );
+ if ( doc )
+ {
+ if ( !isStoredExtern() )
+ {
+ // internal doc so set next external to current (for safety)
+ doc->setCurrent( true );
+ return;
+ }
+ // only externally stored docs shall have file name in title
+ d->m_current = on;
+ if ( !on )
+ {
+ doc->setCurrent( true ); // let my next external parent take over
+ return;
+ }
+ doc->forceCurrent( false ); // everybody else should keep off
+ }
+ else
+ d->m_current = on;
+
+ setTitleModified();
+}
+
+void KoDocument::forceCurrent( bool on )
+{
+ //kdDebug(30003)<<k_funcinfo<<" url: "<<url().url()<<" force to: "<<on<<endl;
+ d->m_current = on;
+ KoDocument *doc = dynamic_cast<KoDocument *>( parent() );
+ if ( doc )
+ {
+ doc->forceCurrent( false );
+ }
+}
+
+bool KoDocument::isCurrent() const
+{
+ return d->m_current;
+}
+
+bool KoDocument::storeInternal() const
+{
+ return d->m_storeInternal;
+}
+
+void KoDocument::setStoreInternal( bool i )
+{
+ d->m_storeInternal = i;
+ //kdDebug(30003)<<k_funcinfo<<"="<<d->m_storeInternal<<" doc: "<<url().url()<<endl;
+}
+
+bool KoDocument::hasExternURL() const
+{
+ return !url().protocol().isEmpty() && url().protocol() != STORE_PROTOCOL && url().protocol() != INTERNAL_PROTOCOL;
+}
+
+void KoDocument::slotStarted( KIO::Job* job )
+{
+ if ( job )
+ {
+ job->setWindow( d->m_shells.current() );
+ }
+}
+
+static const struct {
+ const char* localName;
+ const char* documentType;
+} TN2DTArray[] = {
+ { "text", I18N_NOOP( "a word processing" ) },
+ { "spreadsheet", I18N_NOOP( "a spreadsheet" ) },
+ { "presentation", I18N_NOOP( "a presentation" ) },
+ { "chart", I18N_NOOP( "a chart" ) },
+ { "drawing", I18N_NOOP( "a drawing" ) }
+};
+static const unsigned int numTN2DT = sizeof( TN2DTArray ) / sizeof( *TN2DTArray );
+
+QString KoDocument::tagNameToDocumentType( const QString& localName )
+{
+ for ( unsigned int i = 0 ; i < numTN2DT ; ++i )
+ if ( localName == TN2DTArray[i].localName )
+ return i18n( TN2DTArray[i].documentType );
+ return localName;
+}
+
+QValueList<KoTextDocument *> KoDocument::allTextDocuments() const
+{
+ return QValueList<KoTextDocument *>();
+}
+
+KoPageLayout KoDocument::pageLayout(int /*pageNumber*/) const
+{
+ return m_pageLayout;
+}
+
+KoUnit::Unit KoDocument::unit() const
+{
+ return d->m_unit;
+}
+
+void KoDocument::setUnit( KoUnit::Unit unit )
+{
+ if ( d->m_unit != unit )
+ {
+ d->m_unit = unit;
+ emit unitChanged( unit );
+ }
+}
+
+QString KoDocument::unitName() const
+{
+ return KoUnit::unitName( unit() );
+}
+
+void KoDocument::showStartUpWidget( KoMainWindow* parent, bool alwaysShow )
+{
+ if(!alwaysShow) {
+ KConfigGroup cfgGrp( instance()->config(), "TemplateChooserDialog" );
+ QString fullTemplateName = cfgGrp.readPathEntry( "AlwaysUseTemplate" );
+
+ if( !fullTemplateName.isEmpty() ) {
+ openTemplate( fullTemplateName );
+ shells().getFirst()->setRootDocument( this );
+ return;
+ }
+ }
+
+ if(d->m_startUpWidget){
+ d->m_startUpWidget->show();
+ } else {
+ d->m_startUpWidget = createOpenPane( parent->centralWidget(), instance(), templateType() );
+ }
+
+ parent->setDocToOpen( this );
+ parent->factory()->container("mainToolBar", parent)->hide();
+}
+
+void KoDocument::openExistingFile( const QString& file )
+{
+ KURL url( file );
+ bool ok = openURL( url );
+ setModified( false );
+
+ if( ok )
+ QTimer::singleShot( 0, this, SLOT( deleteOpenPane() ) );
+}
+
+void KoDocument::openTemplate( const QString& file )
+{
+ bool ok = loadNativeFormat( file );
+ setModified( false );
+
+ if ( ok ) {
+ deleteOpenPane();
+ resetURL();
+ setEmpty();
+ } else {
+ showLoadingErrorDialog();
+ initEmpty();
+ }
+}
+
+void KoDocument::initEmpty()
+{
+ setEmpty();
+ setModified(false);
+}
+
+void KoDocument::startCustomDocument() {
+ deleteOpenPane();
+}
+
+KoOpenPane* KoDocument::createOpenPane( QWidget* parent, KInstance* instance,
+ const QString& templateType )
+{
+ KoOpenPane* openPane = new KoOpenPane( parent, instance, templateType );
+ QWidget *customDoc = createCustomDocumentWidget(openPane);
+ if(customDoc) {
+ openPane->setCustomDocumentWidget( customDoc );
+ connect( customDoc, SIGNAL( documentSelected() ), this, SLOT( startCustomDocument() ) );
+ }
+ openPane->show();
+
+ connect( openPane, SIGNAL( openExistingFile( const QString& ) ),
+ this, SLOT( openExistingFile( const QString& ) ) );
+ connect( openPane, SIGNAL( openTemplate( const QString& ) ),
+ this, SLOT( openTemplate( const QString& ) ) );
+
+ return openPane;
+}
+
+void KoDocument::setTemplateType( const QString& _templateType )
+{
+ d->m_templateType = _templateType;
+}
+
+QString KoDocument::templateType() const
+{
+ return d->m_templateType;
+}
+
+void KoDocument::deleteOpenPane()
+{
+ if( d->m_startUpWidget ) {
+ d->m_startUpWidget->hide();
+ QTimer::singleShot(1000, this, SLOT(deleteOpenPaneDelayed()));
+
+ shells().getFirst()->factory()->container("mainToolBar", shells().getFirst())->show();
+ shells().getFirst()->setRootDocument( this );
+ } else {
+ emit closeEmbedInitDialog();
+ }
+}
+
+void KoDocument::deleteOpenPaneDelayed()
+{
+ delete d->m_startUpWidget;
+ d->m_startUpWidget = 0;
+}
+
+QWidget* KoDocument::createCustomDocumentWidget(QWidget */*parent*/) {
+ return 0;
+}
+
+bool KoDocument::showEmbedInitDialog(QWidget* parent)
+{
+ KDialogBase dlg(parent, "EmbedInitDialog", true, i18n("Embedding Object"), 0, KDialogBase::NoDefault);
+ KoOpenPane* pane = createOpenPane(&dlg, instance(), templateType());
+ pane->layout()->setMargin(0);
+ dlg.setMainWidget(pane);
+ dlg.setInitialSize(dlg.configDialogSize("EmbedInitDialog"));
+ connect(this, SIGNAL(closeEmbedInitDialog()), &dlg, SLOT(slotOk()));
+
+ bool ok = dlg.exec() == QDialog::Accepted;
+
+ dlg.saveDialogSize("EmbedInitDialog");
+
+ return ok;
+}
+
+#include "KoDocument_p.moc"
+#include "KoDocument.moc"
diff --git a/lib/kofficecore/KoDocument.h b/lib/kofficecore/KoDocument.h
new file mode 100644
index 00000000..1e8f1f83
--- /dev/null
+++ b/lib/kofficecore/KoDocument.h
@@ -0,0 +1,1158 @@
+// -*- c-basic-offset: 4 -*-
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ Copyright (C) 2000-2005 David Faure <faure@kde.org>
+
+ 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 __ko_document_h__
+#define __ko_document_h__
+
+class KoTextDocument;
+namespace std { }
+using namespace std;
+#include <qwmatrix.h>
+
+#include <kparts/part.h>
+#include <kurl.h>
+#include <kservice.h>
+#include <KoGlobal.h>
+#include <KoUnit.h>
+#include <KoPageLayout.h>
+#include <koffice_export.h>
+
+class QDomElement;
+class QDomDocument;
+class QXmlSimpleReader;
+
+class KoStore;
+class KoMainWindow;
+
+class KoChild;
+class KoDocumentChild;
+class KoView;
+class KoDocumentInfo;
+class DCOPObject;
+class KoOasisStyles;
+class KoXmlWriter;
+class KoOpenPane;
+
+/**
+ * The %KOffice document class
+ *
+ * This class provides some functionality each %KOffice document should have.
+ *
+ * @short The %KOffice document class
+ */
+class KOFFICECORE_EXPORT KoDocument : public KParts::ReadWritePart
+{
+ Q_OBJECT
+ Q_PROPERTY( QCString dcopObjectId READ dcopObjectId)
+ Q_PROPERTY( bool backupFile READ backupFile WRITE setBackupFile )
+
+public:
+
+ /**
+ * Constructor.
+ * The first 4 arguments are the same as the ones passed to KParts::Factory::createPart.
+ *
+ * @param parentWidget the parent widget, in case we create a wrapper widget
+ * (in single view mode).
+ * Usually the first argument passed by KParts::Factory::createPart.
+ * @param widgetName name of the widget.
+ * @param parent may be another KoDocument, or anything else.
+ * Usually the third argument of KParts::Factory::createPart.
+ * @param name is used to identify this document via DCOP so you may want to
+ * pass a meaningful name here which matches the pattern [A-Za-z_][A-Za-z_0-9]*.
+ * @param singleViewMode determines whether the document may only have one view. In this case
+ * the @p parent must be a QWidget derived class. KoDocument will then create a wrapper widget
+ * (KoViewWrapperWidget) which is a child of @p parentWidget.
+ * This widget can be retrieved by calling widget().
+ *
+ * @todo explain what the purpose of widgetName is.
+ */
+ KoDocument( QWidget* parentWidget,
+ const char* widgetName,
+ QObject* parent,
+ const char* name,
+ bool singleViewMode = false );
+
+ /**
+ * Destructor.
+ *
+ * The destructor does not delete any attached KoView objects and it does not
+ * delete the attached widget as returned by widget().
+ */
+ virtual ~KoDocument();
+
+ /**
+ * Tells whether this document is in singleview mode. This mode can only be set
+ * in the constructor.
+ */
+ bool isSingleViewMode() const;
+
+ /**
+ * Is the document embedded?
+ */
+ bool isEmbedded() const;
+
+ /**
+ * Returns the action described action object. In fact only the "name" attribute
+ * of @p element is of interest here. The method searches first in the
+ * KActionCollection of the first view and then in the KActionCollection of this
+ * document.
+ * This allows %KOffice applications to define actions in both the view and the document.
+ * They should only define view-actions (like zooming and stuff) in the view.
+ * Every action which changes the document should be defined in the document.
+ *
+ * Please notice that KoDocument indirectly inherits KXMLGUIClient.
+ *
+ * @see KXMLGUIClient
+ * @see KXMLGUIClient::actionCollection
+ * @see KoView::action
+ */
+ virtual KAction *action( const QDomElement &element ) const;
+
+ /**
+ * Returns the DOM document which describes the GUI of the
+ * first view.
+ */
+ virtual QDomDocument domDocument() const;
+
+ /**
+ * @internal
+ */
+ virtual void setManager( KParts::PartManager *manager );
+
+ /**
+ * Reimplemented from KParts::ReadWritePart for internal reasons
+ * (for the autosave functionality)
+ */
+ virtual bool openURL( const KURL & url );
+
+ /**
+ * Opens the document given by @p url, without storing the URL
+ * in the KoDocument.
+ * Call this instead of openURL() to implement KoMainWindow's
+ * File --> Import feature.
+ *
+ * @note This will call openURL(). To differentiate this from an ordinary
+ * Open operation (in any reimplementation of openURL() or openFile())
+ * call isImporting().
+ */
+ bool import( const KURL &url );
+
+ /**
+ * Saves the document as @p url without changing the state of the
+ * KoDocument (URL, modified flag etc.). Call this instead of
+ * KParts::ReadWritePart::saveAs() to implement KoMainWindow's
+ * File --> Export feature.
+ *
+ * @note This will call KoDocument::saveAs(). To differentiate this
+ * from an ordinary Save operation (in any reimplementation of
+ * saveFile()) call isExporting().
+ */
+ bool exp0rt( const KURL &url );
+
+ /**
+ * @brief Sets whether the document can be edited or is read only.
+ *
+ * This recursively applied to all child documents and
+ * KoView::updateReadWrite is called for every attached
+ * view.
+ */
+ virtual void setReadWrite( bool readwrite = true );
+
+ /**
+ * @brief Used by KoApplication, and by KoMainWindow, when no document exists yet.
+ *
+ * With the help of @p instance or KApplication::instance() this
+ * method figures out which .desktop file matches this application. In this
+ * file it searches for the "X-KDE-NativeMimeType" entry and returns it.
+ *
+ * @see KService
+ * @see KDesktopFile
+ */
+ static QCString readNativeFormatMimeType( KInstance *instance = 0 );
+
+ /**
+ * Used by KoMainWindow, when no document exists yet.
+ *
+ * With the help of @p instance or KApplication::instance() this
+ * method figures out which .desktop file matches this application. In this
+ * file it searches for the "X-KDE-ExtraNativeMimeTypes" entry and returns it.
+ *
+ * @see KService
+ * @see KDesktopFile
+ */
+ static QStringList readExtraNativeMimeTypes( KInstance *instance = 0 );
+
+ /**
+ * With the help of @p instance or KApplication::instance() this
+ * method figures out which .desktop file matches this application,
+ * and returns the KService instance for it.
+ */
+ static KService::Ptr readNativeService( KInstance *instance = 0 );
+
+ /**
+ * setup the XML reader, so that we don't have to duplicate the code.
+ */
+ static void setupXmlReader( QXmlSimpleReader& reader, bool namespaceProcessing = false );
+
+ /**
+ * To be preferred when a document exists. It is fast when calling
+ * it multiple times since it caches the result that readNativeFormatMimeType()
+ * delivers.
+ * This comes from the X-KDE-NativeMimeType key in the .desktop file
+ * You do NOT have to reimplement this (it is only virtual for kounavail).
+ */
+ virtual QCString nativeFormatMimeType() const;
+
+ /**
+ * Returns the OASIS OpenDocument mimetype of the document, if supported
+ * This comes from the X-KDE-NativeOasisMimeType key in the .desktop file
+ */
+ QCString nativeOasisMimeType() const;
+
+ /// Checks whether a given mimetype can be handled natively.
+ bool isNativeFormat( const QCString& mimetype ) const;
+
+ /// Returns a list of the mimetypes considered "native", i.e. which can
+ /// be saved by KoDocument without a filter, in *addition* to the main one
+ virtual QStringList extraNativeMimeTypes() const;
+
+ /// Enum values used by specialOutputFlag - note that it's a bitfield for supportedSpecialFormats
+ enum { /*SaveAsKOffice1dot1 = 1,*/ // old and removed
+ SaveAsDirectoryStore = 2,
+ SaveAsFlatXML = 4
+ // bitfield! next value is 8
+ };
+
+ /**
+ * Return the set of SupportedSpecialFormats that the application wants to
+ * offer in the "Save" file dialog.
+ */
+ virtual int supportedSpecialFormats() const;
+
+ /**
+ * Returns the actual mimetype of the document
+ */
+ QCString mimeType() const;
+
+ /**
+ * @brief Sets the mime type for the document.
+ *
+ * When choosing "save as" this is also the mime type
+ * selected by default.
+ */
+ void setMimeType( const QCString & mimeType );
+
+ /**
+ * @brief Set the format in which the document should be saved.
+ *
+ * This is called on loading, and in "save as", so you shouldn't
+ * have to call it.
+ *
+ * @param mimeType the mime type (format) to use.
+ * @param specialOutputFlag is for "save as older version" etc.
+ */
+ void setOutputMimeType( const QCString & mimeType, int specialOutputFlag = 0 );
+ QCString outputMimeType() const;
+ int specialOutputFlag() const;
+
+ /**
+ * Returns true if this document was the result of opening a foreign
+ * file format and if the user hasn't yet saved the document (in any
+ * format).
+ *
+ * Used by KoMainWindow to warn the user when s/he lazily presses
+ * CTRL+S to save in the same foreign format, putting all his/her
+ * formatting at risk (normally an export confirmation only comes up
+ * with Save As).
+ *
+ * @param exporting specifies whether this is the setting for a
+ * File --> Export or File --> Save/Save As operation.
+ */
+ bool confirmNonNativeSave( const bool exporting ) const;
+ void setConfirmNonNativeSave( const bool exporting, const bool on );
+
+ virtual bool wantExportConfirmation() const;
+
+ /**
+ * Sets the error message to be shown to the user (use i18n()!)
+ * when loading or saving fails.
+ * If you asked the user about something and he chose "Cancel",
+ * set the message to the magic string "USER_CANCELED", to skip the error dialog.
+ */
+ void setErrorMessage( const QString& errMsg );
+
+ /**
+ * Return the last error message. Usually KoDocument takes care of
+ * showing it; this method is mostly provided for non-interactive use.
+ * @since 1.4
+ */
+ QString errorMessage() const;
+
+ /**
+ * Show the last error message in a message box.
+ * The dialog box will mention a saving problem.
+ * Note that save/saveFile takes care of doing it.
+ * @since 1.4
+ */
+ void showSavingErrorDialog();
+
+ /**
+ * Show the last error message in a message box.
+ * The dialog box will mention a loading problem.
+ * openURL/openFile takes care of doing it, but not loadNativeFormat itself,
+ * so this is often called after loadNativeFormat returned false.
+ * @since 1.4
+ */
+ void showLoadingErrorDialog();
+
+ /**
+ * Create a new view for the document.
+ */
+ KoView *createView( QWidget *parent = 0, const char *name = 0 );
+
+ /**
+ * Adds a view to the document.
+ *
+ * This calls KoView::updateReadWrite to tell the new view
+ * whether the document is readonly or not.
+ */
+ virtual void addView( KoView *view );
+
+ /**
+ * Removes a view of the document.
+ */
+ virtual void removeView( KoView *view );
+
+ /**
+ * @return a list of views this document is displayed in
+ */
+ const QPtrList<KoView> & views() const;
+
+ /**
+ * @return number of views this document is displayed in
+ */
+ int viewCount() const;
+
+ /**
+ * Reimplemented from KParts::Part
+ */
+ virtual KParts::Part *hitTest( QWidget *widget, const QPoint &globalPos );
+
+ /**
+ * Find the most nested child document which contains the
+ * requested point. The point is in the coordinate system
+ * of this part. If no child document contains this point, then
+ * a pointer to this document is returned.
+ *
+ * This function has to be overloaded if the document features child documents.
+ *
+ * @param pos is in (unzoomed) document coordinates
+ * @param matrix transforms points from the documents coordinate system
+ * to the coordinate system of the requested point. This is used by
+ * transformed child documents, see KoDocumentChild/KoChild.
+ *
+ * @return Pointer to the document under the mouse at that position
+ */
+ virtual KoDocument *hitTest( const QPoint &pos, const QWMatrix& matrix = QWMatrix() );
+ /// Temporary API for accessing the view that calls hitTest.
+ /// Will be passed to hitTest() in 2.x.
+ /// Only call this from within hitTest()!
+ KoView* hitTestView();
+
+ /**
+ * Paints the whole document into the given painter object.
+ *
+ * @param painter The painter object onto which will be drawn.
+ * @param rect The rect that should be used in the painter object.
+ * @param transparent If true then the entire rectangle is erased before painting.
+ * @param view The KoView is needed to fiddle about with the active widget, when painting children.
+ * @param zoomX The zoom value to be applied to X coordinates when painting.
+ * @param zoomY The zoom value to be applied to Y coordinates when painting.
+ */
+ virtual void paintEverything( QPainter &painter, const QRect &rect, bool transparent = false,
+ KoView *view = 0L, double zoomX = 1.0, double zoomY = 1.0 );
+
+ /**
+ * @brief Generates a preview picture of the document
+ * @note The preview is used in the File Dialog and also to create the Thumbnail
+ */
+ virtual QPixmap generatePreview( const QSize& size );
+
+ /**
+ * Paints all of the documents children into the given painter object.
+ *
+ * @param painter The painter object onto which will be drawn.
+ * @param rect The rect that should be used in the painter object.
+ * @param view The KoView is needed to fiddle about with the active widget.
+ * @param zoomX The zoom value to be applied to X coordinates when painting.
+ * @param zoomY The zoom value to be applied to Y coordinates when painting.
+ *
+ * @see #paintChild #paintEverything #paintContent
+ */
+ virtual void paintChildren( QPainter &painter, const QRect &rect, KoView *view, double zoomX = 1.0, double zoomY = 1.0 );
+
+ /**
+ * Paint a given child. Normally called by paintChildren().
+ *
+ * @param child The child to be painted.
+ * @param painter The painter object onto which will be drawn.
+ * @param view The KoView is needed to fiddle about with the active widget.
+ * @param zoomX The zoom value to be applied to X coordinates when painting.
+ * @param zoomY The zoom value to be applied to Y coordinates when painting.
+ *
+ * @see #paintEverything #paintChildren #paintContent
+ */
+ virtual void paintChild( KoDocumentChild *child, QPainter &painter, KoView *view,
+ double zoomX = 1.0, double zoomY = 1.0 );
+
+ /**
+ * Paints the data itself. Normally called by paintEverything(). It does not
+ * paint the children.
+ * It's this method that %KOffice Parts have to implement.
+ *
+ * @param painter The painter object onto which will be drawn.
+ * @param rect The rect that should be used in the painter object.
+ * @param transparent If false the implementing method should fill the background.
+ * @param zoomX The zoom value to be applied to X coordinates when painting.
+ * @param zoomY The zoom value to be applied to Y coordinates when painting.
+ *
+ * @see #paintEverything
+ */
+ virtual void paintContent( QPainter &painter, const QRect &rect, bool transparent = false,
+ double zoomX = 1.0, double zoomY = 1.0 ) = 0;
+
+ /**
+ * Called by koApplication to check for an autosave file in $HOME
+ */
+ bool checkAutoSaveFile();
+
+ /**
+ * This setting indicates who is calling initDoc.
+ * Usually the app will want to
+ * - show the template dialog with 'everything' if InitDocAppStarting, InitDocFileClose or InitDocEmbedded
+ * - show the template dialog with 'templates only' if InitDocFileNew
+ * - create an empty document with default settings if InitDocEmpty
+ */
+ enum InitDocFlags { InitDocAppStarting, InitDocFileNew, InitDocFileClose, InitDocEmbedded, InitDocEmpty };
+
+ /**
+ * Initializes an empty document (display the template dialog!).
+ * You have to overload this method to initialize all your document variables.
+ * @param flags see InitDocFlags
+ * @param parentWidget the widget this document belongs with
+ */
+ virtual bool initDoc(InitDocFlags flags, QWidget* parentWidget=0) = 0;
+
+ /**
+ * Creates and shows the start up widget.
+ * @param parent the KoMainWindow used as parent for the widget.
+ * @param alwaysShow always show the widget even if the user has configured it to not show.
+ * @since 1.5
+ */
+ virtual void showStartUpWidget(KoMainWindow* parent, bool alwaysShow = false);
+
+ /**
+ * Sets the modified flag on the document. This means that it has
+ * to be saved or not before deleting it.
+ */
+ virtual void setModified( bool _mod );
+
+ /**
+ * Tells the document that its title has been modified, either because
+ * the modified status changes (this is done by setModified() ) or
+ * because the URL or the document-info's title changed.
+ */
+ virtual void setTitleModified();
+
+ /**
+ * @return true if the document is empty.
+ */
+ virtual bool isEmpty() const { return m_bEmpty; }
+
+ /**
+ * @brief Sets the document to empty.
+ *
+ * Used after loading a template
+ * (which is not empty, but not the user's input).
+ *
+ * @see isEmpty()
+ */
+ virtual void setEmpty() { m_bEmpty = true; }
+
+ /**
+ * @brief Loads a document from a store.
+ *
+ * You should never have to reimplement.
+ *
+ * @param store The store to load from
+ * @param url An internal url, like tar:/1/2
+ */
+ virtual bool loadFromStore( KoStore* store, const QString& url );
+
+ /**
+ * @brief Loads an OASIS document from a store.
+ * This is used for both the main document and embedded objects.
+ */
+ virtual bool loadOasisFromStore( KoStore* store );
+
+ /**
+ * @brief Saves a document to a store.
+ *
+ * You should not have to reimplement this - but call it in saveChildren().
+ */
+ virtual bool saveToStore( KoStore* store, const QString& path );
+
+ /**
+ * Reimplement this method to load the contents of your %KOffice document,
+ * from the XML document. This is for the pre-Oasis file format (maindoc.xml).
+ *
+ * You are supposed to use the QDomDocument. The QIODevice is provided only
+ * for the cases where some pre-processing is needed, like kpresenter's kprconverter.
+ * Note that the QIODevice could be 0L, when called from an import filter.
+ */
+ virtual bool loadXML( QIODevice *, const QDomDocument & doc ) = 0;
+
+ /**
+ * Reimplement this method to load the contents of your %KOffice document,
+ * from the XML document ("content.xml"). The styles have been parsed already,
+ * you can find them in the oasisStyles parameter. The store can be used
+ * to load images and embedded documents.
+ */
+ virtual bool loadOasis( const QDomDocument & doc, KoOasisStyles& oasisStyles,
+ const QDomDocument & settings, KoStore* store ) = 0;
+
+ /**
+ * Reimplement this method to save the contents of your %KOffice document,
+ * using the OASIS format.
+ */
+ virtual bool saveOasis( KoStore* store, KoXmlWriter* manifestWriter ) = 0;
+
+ /**
+ * Reimplement this to save the contents of the %KOffice document into
+ * a QDomDocument. The framework takes care of saving it to the store.
+ */
+ virtual QDomDocument saveXML();
+
+ /**
+ * Return a correctly created QDomDocument for this KoDocument,
+ * including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element.
+ * @param tagName the name of the tag for the root element
+ * @param version the DTD version (usually the application's version).
+ * @deprecated use createOasisXmlWriter instead
+ */
+ QDomDocument createDomDocument( const QString& tagName, const QString& version ) const;
+
+ /**
+ * Return an XML writer for saving Oasis XML into the device @p dev,
+ * including the XML processing instruction,
+ * and the root element with all its namespaces.
+ * You can add more namespaces afterwards with addAttribute.
+ *
+ * @param dev the device into which the XML will be written.
+ * @param rootElementName the tag name of the root element.
+ * This is either office:document, office:document-content,
+ * office:document-styles, office:document-meta or office:document-settings
+ * @return the KoXmlWriter instance. It becomes owned by the caller, which
+ * must delete it at some point.
+ *
+ * Once done with writing the contents of the root element, you
+ * will need to call endElement(); endDocument(); before destroying the KoXmlWriter.
+ * @note OASIS-specific
+ */
+ static KoXmlWriter* createOasisXmlWriter( QIODevice* dev, const char* rootElementName );
+
+ /**
+ * Return a correctly created QDomDocument for an old (1.3-style) %KOffice document,
+ * including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element.
+ * This static method can be used e.g. by filters.
+ * @param appName the app's instance name, e.g. kword, kspread, kpresenter etc.
+ * @param tagName the name of the tag for the root element, e.g. DOC for kword/kpresenter.
+ * @param version the DTD version (usually the application's version).
+ */
+ static QDomDocument createDomDocument( const QString& appName, const QString& tagName, const QString& version );
+
+ /**
+ * The first thing to do in loadOasis is get hold of the office:body tag, then its child.
+ * If the child isn't the expected one, the error message can indicate what it is instead.
+ * This method returns a translated name for the type of document,
+ * e.g. i18n("Word Processing") for office:text.
+ */
+ static QString tagNameToDocumentType( const QString& localName );
+
+ /**
+ * Save the document. The default implementation is to call
+ * saveXML(). This method exists only for applications that
+ * don't use QDomDocument for saving, i.e. kword and kpresenter.
+ */
+ virtual bool saveToStream( QIODevice * dev );
+
+ /**
+ * Loads a document in the native format from a given URL.
+ * Reimplement if your native format isn't XML.
+ *
+ * @param file the file to load - usually KReadOnlyPart::m_file or the result of a filter
+ */
+ virtual bool loadNativeFormat( const QString & file );
+
+ /**
+ * Saves the document in native format, to a given file
+ * You should never have to reimplement.
+ * Made public for writing templates.
+ */
+ virtual bool saveNativeFormat( const QString & file );
+
+ /**
+ * Activate/deactivate/configure the autosave feature.
+ * @param delay in seconds, 0 to disable
+ */
+ void setAutoSave( int delay );
+
+ /**
+ * Checks whether the document is currently in the process of autosaving
+ */
+ bool isAutosaving() const;
+
+ /**
+ * Set whether the next openURL call should check for an auto-saved file
+ * and offer to open it. This is usually true, but can be turned off
+ * (e.g. for the preview module).
+ */
+ void setCheckAutoSaveFile( bool b );
+
+ /**
+ * Set whether the next openURL call should show error message boxes in case
+ * of errors. This is usually the case, but e.g. not when generating thumbnail
+ * previews.
+ */
+ void setAutoErrorHandlingEnabled( bool b );
+
+ /**
+ * Checks whether error message boxes should be shown.
+ * @since 1.3.1
+ */
+ bool isAutoErrorHandlingEnabled() const;
+
+ /**
+ * Retrieve the default value for autosave in seconds.
+ * Called by the applications to use the correct default in their config
+ */
+ static int defaultAutoSave() { return s_defaultAutoSave; }
+
+ /**
+ * @return the list of all children. Do not modify the
+ * returned list.
+ */
+ const QPtrList<KoDocumentChild>& children() const;
+
+ /**
+ * @return the KoDocumentChild associated with the given Document, but only if
+ * @p doc is a direct child of this document.
+ *
+ * This is a convenience function. You could get the same result
+ * by traversing the list returned by children().
+ */
+ KoDocumentChild *child( KoDocument *doc );
+
+ /**
+ * @return the information concerning this document.
+ * @see KoDocumentInfo
+ */
+ KoDocumentInfo *documentInfo() const;
+
+ void setViewBuildDocument( KoView *view, const QDomDocument &doc );
+ QDomDocument viewBuildDocument( KoView *view );
+
+ /**
+ * Appends the shell to the list of shells which show this
+ * document as their root document.
+ *
+ * This method is automatically called from KoMainWindow::setRootDocument,
+ * so you dont need to call it.
+ */
+ virtual void addShell( KoMainWindow *shell );
+
+ /**
+ * Removes the shell from the list. That happens automatically if the shell changes its
+ * root document. Usually you dont need to call this method.
+ */
+ virtual void removeShell( KoMainWindow *shell );
+
+ /**
+ * @return the list of shells for the main window
+ */
+ const QPtrList<KoMainWindow>& shells() const;
+
+ /**
+ * @return the number of shells for the main window
+ */
+ int shellCount() const;
+
+ /**
+ * @return the list of all the currently opened documents
+ */
+ static QPtrList<KoDocument> *documentList() { return s_documentList; }
+
+ /**
+ * @brief Return a DCOP interface for this document
+ *
+ * %KOffice parts are strongly recommended to reimplement this method,
+ * so that their DCOP interface provides more functionality than the basic KoDocumentIface
+ */
+ virtual DCOPObject * dcopObject();
+
+ /**
+ * @return the ID of the DCOP interface for this document.
+ **/
+ QCString dcopObjectId() const;
+
+ /**
+ * Signal the progress of operations such as loading or saving.
+ */
+ void emitProgress( int value ) { emit sigProgress( value ); }
+
+ bool isInOperation() const;
+ virtual void emitBeginOperation();
+ virtual void emitEndOperation();
+
+ /**
+ * Return true if url() is a real filename, false if url() is
+ * an internal url in the store, like "tar:/..."
+ */
+ virtual bool isStoredExtern() const;
+
+ /**
+ * @return the page layout associated with this document (margins, pageSize, etc).
+ * Override this if you want to provide different sized pages.
+ *
+ * @see KoPageLayout
+ */
+ virtual KoPageLayout pageLayout(int pageNumber = 0) const;
+
+ /**
+ * Performs a cleanup of unneeded backup files
+ */
+ void removeAutoSaveFiles();
+
+ void setBackupFile( bool _b );
+
+ bool backupFile()const;
+
+ /**
+ * Returns true if this document or any of its internal child documents are modified.
+ */
+ bool isModified() const;
+
+ /**
+ * Returns true during loading (openURL can be asynchronous)
+ */
+ bool isLoading() const;
+
+ int queryCloseExternalChildren();
+ int queryCloseDia();
+
+ /**
+ * @brief Set when we do not want to save external children when saving our 'main' doc.
+ *
+ * This makes it possible to save 'main' doc + all its internal children first, then
+ * go on to save external children. Typically used by query close.
+ * Use:
+ * @code
+ * doc->setDoNotSaveExtDoc();
+ * doc->save(); // saves doc and its internal children,
+ * //also calls saveExternalChildren() which sets setDoNotSaveExtDoc(false)
+ * doc->saveExternalChildren();
+ * @endcode
+ */
+ void setDoNotSaveExtDoc( bool on = true );
+
+ /**
+ * Sets the backup path of the document
+ */
+ void setBackupPath( const QString & _path );
+
+ /**
+ * @return path to the backup document
+ */
+ QString backupPath()const;
+
+ /**
+ * Indicates that this document is currently viewed
+ * and thus should control the title caption.
+ * Also resets current flag for all parents.
+ */
+ void setCurrent( bool on = true );
+
+ /**
+ * Sets current flag for this document and all its parents
+ */
+ void forceCurrent( bool on );
+ bool isCurrent() const;
+
+ void setTitleModified( const QString caption, bool mod );
+
+ /**
+ * Sets the document URL to empty URL
+ * KParts doesn't allow this, but %KOffice apps have e.g. templates
+ * After using loadNativeFormat on a template, one wants
+ * to set the url to KURL()
+ */
+ void resetURL() { m_url = KURL(); m_file = QString::null; }
+
+ /**
+ * Set when you want an external embedded document to be stored internally
+ */
+ void setStoreInternal( bool i );
+
+ /**
+ * @return true when external embedded documents are stored internally
+ */
+ bool storeInternal() const;
+
+ bool hasExternURL() const;
+
+ /**
+ * Sets the document URL to @p url
+ * KParts doesn't really allow this, but it is needed for undo of setStoreInternal()
+ */
+ void setURL( const KURL& url ) { m_url = url; }
+
+ /**
+ * _Only_ use these functions to restore m_file (in KoMainWindow) after a
+ * failed save (remember to use setURL() to restore the URL as well).
+ *
+ * @warning Do _not_ use these functions for any other purpose.
+ *
+ * @internal
+ */
+ QString &file() { return m_file; }
+
+ /**
+ * _Only_ use these functions to restore m_file (in KoMainWindow) after a
+ * failed save (remember to use setURL() to restore the URL as well).
+ *
+ * @warning Do _not_ use these functions for any other purpose.
+ *
+ * @internal
+ */
+ void setFile( const QString &file ) { m_file = file; }
+
+ /**
+ * @internal (public for KoMainWindow)
+ */
+ void setMimeTypeAfterLoading( const QString& mimeType );
+
+ /**
+ * @return returns the number of pages in the document.
+ */
+ virtual int pageCount() const { return 1; }
+
+ /**
+ * @return all kotext-based text objects in the document
+ * This is used by some text-analyzer plugins.
+ * @since 1.5
+ */
+ virtual QValueList<KoTextDocument *> allTextDocuments() const;
+
+ /**
+ * Returns the unit used to display all measures/distances.
+ * @since 1.5
+ */
+ KoUnit::Unit unit() const;
+
+ /**
+ * Sets the unit used to display all measures/distances.
+ * @since 1.5
+ */
+ void setUnit( KoUnit::Unit u );
+
+ /**
+ * Returns the name of the unit used to display all measures/distances.
+ * Use this method for displaying it in the user interface, but use
+ * unit() for everything else (conversions etc.)
+ * @since 1.5
+ */
+ QString unitName() const;
+
+ /**
+ * Set the template type used. This is used by the start up widget to show
+ * the correct templates.
+ * @since 1.5
+ */
+ void setTemplateType(const QString& _templateType);
+ /**
+ * Template type used. This is used by the start up widget to show
+ * the correct templates.
+ * @since 1.5
+ */
+ QString templateType() const;
+
+ /**
+ * Shows the init dialog when embeding
+ * @param parent the parent widget
+ * @since 1.5
+ */
+ virtual bool showEmbedInitDialog(QWidget* parent);
+
+public slots:
+ /**
+ * Initialize an empty document using default values
+ * @since 1.5
+ */
+ virtual void initEmpty();
+
+signals:
+
+ /**
+ * This signal is emitted when the unit is changed by setUnit()
+ * It is common to connect views to it, in order to change the displayed units
+ * (e.g. in the rulers)
+ */
+ void unitChanged(KoUnit::Unit);
+
+ /**
+ * This signal is emitted when a direct or indirect child document changes
+ * and needs to be updated in all views.
+ *
+ * If one of your child documents emits the childChanged signal, then you may
+ * usually just want to redraw this child. In this case you can ignore the parameter
+ * passed by the signal.
+ */
+ void childChanged( KoDocumentChild *child );
+
+ /**
+ * Progress info while loading or saving. The value is in percents (i.e. a number between 0 and 100)
+ * Your KoDocument-derived class should emit the signal now and then during load/save.
+ * KoMainWindow will take care of displaying a progress bar automatically.
+ */
+ void sigProgress( int value );
+
+ /**
+ * Emitted e.g. at the beginning of a save operation
+ * This is emitted by KoDocument and used by KoView to display a statusbar message
+ */
+ void sigStatusBarMessage( const QString& text );
+
+ /**
+ * Emitted e.g. at the end of a save operation
+ * This is emitted by KoDocument and used by KoView to clear the statusbar message
+ */
+ void sigClearStatusBarMessage();
+
+ void sigBeginOperation();
+ void sigEndOperation();
+
+ /**
+ * Emitted when the document is modified
+ */
+ void modified( bool );
+
+ void closeEmbedInitDialog();
+
+protected slots:
+ /**
+ * This slot loads an existing file and deletes the start up widget.
+ * @param file the file to load (including path)
+ * @since 1.5
+ */
+ virtual void openExistingFile( const QString& file );
+ /**
+ * This slot loads a template and deletes the start up widget.
+ * @param file the template to load
+ * @since 1.5
+ */
+ virtual void openTemplate( const QString& file );
+
+ void deleteOpenPaneDelayed();
+
+protected:
+
+ QString autoSaveFile( const QString & path ) const;
+
+ virtual KoView *createViewInstance( QWidget *parent, const char *name ) = 0;
+
+ /**
+ * Loads a document from KReadOnlyPart::m_file (KParts takes care of downloading
+ * remote documents).
+ * Applies a filter if necessary, and calls loadNativeFormat in any case
+ * You should not have to reimplement, except for very special cases.
+ *
+ * This method is called from the KReadOnlyPart::openURL method.
+ */
+ virtual bool openFile();
+
+ /**
+ * Saves a document to KReadOnlyPart::m_file (KParts takes care of uploading
+ * remote documents)
+ * Applies a filter if necessary, and calls saveNativeFormat in any case
+ * You should not have to reimplement, except for very special cases.
+ */
+ virtual bool saveFile();
+
+ /**
+ * Override this method in your derived class to show a widget in the startup 'dialog'.
+ * This widget should allow the user to set settings for a custom document (i.e. one
+ * not based on a template).
+ * The returned widget should provide its own button (preferrably 'Create') and
+ * implement the logic to implement the document instance correctly.
+ * After initializing the widget should emit a signal called 'documentSelected()' which
+ * will remove the startupWidget and show the document.
+ * @param parent the parent of the to be created widget.
+ */
+ virtual QWidget* createCustomDocumentWidget(QWidget *parent);
+
+ /**
+ * OLD XML method. For OASIS just call KoDocumentChild::loadOasisDocument
+ * after KoDocumentChild::loadOasis.
+ *
+ * You need to overload this function if your document may contain
+ * embedded documents. This function is called to load embedded documents.
+ *
+ * An example implementation may look like this:
+ * @code
+ * QPtrListIterator<KoDocumentChild> it( children() );
+ * for( ; it.current(); ++it )
+ * {
+ * if ( !it.current()->loadDocument( _store ) )
+ * {
+ * return false;
+ * }
+ * }
+ * return true;
+ * @endcode
+ */
+ virtual bool loadChildren( KoStore* );
+
+ /**
+ * Saves all internal children (only!).
+ * @see saveExternalChildren if you have external children.
+ * Returns true on success.
+ */
+ virtual bool saveChildren( KoStore* store );
+
+ /**
+ * Saves all internal children (only!), to the store, using the OASIS format.
+ * This is called automatically during saveNativeFormat.
+ * @see saveExternalChildren if you have external children.
+ * Returns true on success.
+ */
+ virtual bool saveChildrenOasis( KoStore* store, KoXmlWriter* manifestWriter );
+
+ /**
+ * Overload this function if you have to load additional files
+ * from a store. This function is called after loadXML()
+ * and after loadChildren() have been called.
+ */
+ virtual bool completeLoading( KoStore* store );
+
+ /**
+ * If you want to write additional files to a store,
+ * then you must do it here.
+ * In the implementation, you should prepend the document
+ * url (using url().url()) before the filename, so that everything is kept relative
+ * to this document. For instance it will produce urls such as
+ * tar:/1/pictures/picture0.png, if the doc url is tar:/1
+ * But do this ONLY if the document is not stored extern (see isStoredExtern() ).
+ * If it is, then the pictures should be saved to tar:/pictures.
+ */
+ virtual bool completeSaving( KoStore* store );
+
+ /**
+ * Inserts the new child in the list of children and emits the
+ * childChanged() signal.
+ *
+ * At the same time this method marks this document as modified.
+ *
+ * To remove a child, just delete it. KoDocument will detect this
+ * and remove the child from its lists.
+ *
+ * @see #isModified
+ */
+ virtual void insertChild( KoDocumentChild *child );
+
+ /** @internal */
+ virtual void setModified() { KParts::ReadWritePart::setModified(); }
+
+ /** @internal */
+ virtual void insertChild(QObject *o) { QObject::insertChild(o); }
+
+ KoPageLayout m_pageLayout;
+
+ /**
+ * Saves all externally stored children.
+ * Returns true on success.
+ * @see #saveChildren for internal children
+ */
+ virtual bool saveExternalChildren();
+
+ /**
+ * Returns whether or not the current openURL() or openFile() call is
+ * actually an import operation (like File --> Import).
+ * This is for informational purposes only.
+ */
+ bool isImporting() const;
+
+ /**
+ * Returns whether or not the current saveFile() call is actually an export
+ * operation (like File --> Export).
+ * If this function returns true during saveFile() and you are changing
+ * some sort of state, you _must_ restore it before the end of saveFile();
+ * otherwise, File --> Export will not work properly.
+ */
+ bool isExporting() const;
+
+ /**
+ * Creates the open widget showed at application start up.
+ * @param parent the parent widget
+ * @param instance the KInstance to be used for KConfig data
+ * @param templateType the template-type (group) that should be selected on creation.
+ * @since 1.5
+ */
+ KoOpenPane* createOpenPane( QWidget* parent, KInstance* instance,
+ const QString& templateType = QString::null);
+
+private slots:
+ void slotChildChanged( KoChild *c );
+ void slotChildDestroyed();
+ void slotAutoSave();
+ void slotStarted( KIO::Job* );
+ void startCustomDocument();
+ /**
+ * Removes the open widget showed at application start up.
+ * @since 1.5
+ */
+ void deleteOpenPane();
+
+private:
+ KService::Ptr nativeService();
+ bool oldLoadAndParse( KoStore* store, const QString& filename, QDomDocument& doc );
+ bool loadNativeFormatFromStore( const QString& file );
+ bool savePreview( KoStore* store );
+ bool saveOasisPreview( KoStore* store, KoXmlWriter* manifestWriter );
+ class Private;
+ Private *d;
+ KService::Ptr m_nativeService;
+ bool m_bEmpty;
+ static QPtrList<KoDocument> *s_documentList;
+ static const int s_defaultAutoSave;
+};
+
+#endif
diff --git a/lib/kofficecore/KoDocumentChild.cpp b/lib/kofficecore/KoDocumentChild.cpp
new file mode 100644
index 00000000..8157b95a
--- /dev/null
+++ b/lib/kofficecore/KoDocumentChild.cpp
@@ -0,0 +1,523 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ Copyright (C) 2004-2006 David Faure <faure@kde.org>
+
+ 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.
+*/
+
+#include "KoDocumentChild.h"
+#include "KoOasisStore.h"
+#include <KoDocument.h>
+#include <KoQueryTrader.h>
+#include <KoXmlWriter.h>
+#include <KoXmlNS.h>
+#include <KoUnit.h>
+#include <KoStore.h>
+#include <KoStoreDevice.h>
+
+#include <kparts/partmanager.h>
+
+#include <kmimetype.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+
+#include <qapplication.h>
+
+#include <assert.h>
+
+// Define the protocol used here for embedded documents' URL
+// This used to "store" but KURL didn't like it,
+// so let's simply make it "tar" !
+#define STORE_PROTOCOL "tar"
+#define INTERNAL_PROTOCOL "intern"
+// Warning, keep it sync in koStore.cc and koDocument.cc
+
+/**********************************************************
+ *
+ * KoDocumentChild
+ *
+ **********************************************************/
+
+class KoDocumentChildPrivate
+{
+public:
+ KoDocumentChildPrivate()
+ {
+ }
+ ~KoDocumentChildPrivate()
+ {
+ }
+
+ KoDocument *m_parent;
+ KoDocument *m_doc;
+ bool m_deleted;
+};
+
+KoDocumentChild::KoDocumentChild( KoDocument* parent, KoDocument* doc, const QRect& geometry )
+ : KoChild( parent )
+{
+ d = new KoDocumentChildPrivate;
+ d->m_parent = parent;
+ d->m_doc = doc;
+ setGeometry( geometry );
+ d->m_deleted = false;
+ if ( doc )
+ doc->setStoreInternal( !doc->hasExternURL() );
+}
+
+KoDocumentChild::KoDocumentChild( KoDocument* parent )
+ : KoChild( parent )
+{
+ d = new KoDocumentChildPrivate;
+ d->m_parent = parent;
+ d->m_doc = 0L;
+ d->m_deleted = false;
+}
+
+void KoDocumentChild::setDocument( KoDocument *doc, const QRect &geometry )
+{
+ kdDebug()<<k_funcinfo<<"doc: "<<doc->url().url()<<endl;
+ d->m_doc = doc;
+ setGeometry( geometry );
+
+ updateMatrix();
+}
+
+KoDocument *KoDocumentChild::document() const
+{
+ return d->m_doc;
+}
+
+KoDocument* KoDocumentChild::parentDocument() const
+{
+ return d->m_parent;
+}
+
+KoDocument* KoDocumentChild::hitTest( const QPoint &p, const QWMatrix &_matrix )
+{
+ if ( !region( _matrix ).contains( p ) || !d->m_doc )
+ return 0L;
+
+ QWMatrix m( _matrix );
+ m = matrix() * m;
+ m.scale( xScaling(), yScaling() );
+
+ return d->m_doc->hitTest( p, m );
+}
+
+void KoDocumentChild::loadOasis( const QDomElement &frameElement, const QDomElement& objectElement )
+{
+ double x, y, w, h;
+ x = KoUnit::parseValue( frameElement.attributeNS( KoXmlNS::svg, "x", QString::null ) );
+ y = KoUnit::parseValue( frameElement.attributeNS( KoXmlNS::svg, "y", QString::null ) );
+ w = KoUnit::parseValue( frameElement.attributeNS( KoXmlNS::svg, "width", QString::null ) );
+ h = KoUnit::parseValue( frameElement.attributeNS( KoXmlNS::svg, "height", QString::null ) );
+ m_tmpGeometry = QRect((int)x, (int)y, (int)w, (int)h); // #### double->int conversion
+ setGeometry(m_tmpGeometry);
+
+ QString url = objectElement.attributeNS( KoXmlNS::xlink, "href", QString::null );
+ if ( url[0] == '#' )
+ url = url.mid( 1 );
+ if ( url.startsWith( "./" ) )
+ m_tmpURL = QString( INTERNAL_PROTOCOL ) + ":/" + url.mid( 2 );
+ else
+ m_tmpURL = url;
+ kdDebug() << k_funcinfo << m_tmpURL << endl;
+}
+
+
+bool KoDocumentChild::load( const QDomElement& element, bool uppercase )
+{
+ if ( element.hasAttribute( "url" ) )
+ m_tmpURL = element.attribute("url");
+ if ( element.hasAttribute("mime") )
+ m_tmpMimeType = element.attribute("mime");
+
+ if ( m_tmpURL.isEmpty() )
+ {
+ kdDebug(30003) << "Empty 'url' attribute in OBJECT" << endl;
+ return false;
+ }
+ if ( m_tmpMimeType.isEmpty() )
+ {
+ kdDebug(30003) << "Empty 'mime' attribute in OBJECT" << endl;
+ return false;
+ }
+
+ bool brect = FALSE;
+ QDomNode n = element.firstChild();
+ for( ; !n.isNull(); n = n.nextSibling() )
+ {
+ QDomElement e = n.toElement();
+ if ( e.isNull() ) continue;
+ if ( e.tagName() == "rect" || ( uppercase && e.tagName() == "RECT" ) )
+ {
+ brect = true;
+ int x, y, w, h;
+ x=y=w=h=0;
+ if ( e.hasAttribute( "x" ) )
+ x = e.attribute( "x" ).toInt(&brect);
+ if ( e.hasAttribute( "y" ) )
+ y = e.attribute( "y" ).toInt(&brect);
+ if ( e.hasAttribute( "w" ) )
+ w = e.attribute( "w" ).toInt(&brect);
+ if ( e.hasAttribute( "h" ) )
+ h = e.attribute( "h" ).toInt(&brect);
+ m_tmpGeometry = QRect(x, y, w, h);
+ setGeometry(m_tmpGeometry);
+ }
+ }
+
+ if ( !brect )
+ {
+ kdDebug(30003) << "Missing RECT in OBJECT" << endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool KoDocumentChild::loadDocument( KoStore* store )
+{
+ assert( !m_tmpURL.isEmpty() );
+
+ kdDebug(30003) << "KoDocumentChild::loadDocument: trying to load " << m_tmpURL << endl;
+
+ // Backwards compatibility
+ if ( m_tmpMimeType == "application/x-killustrator" )
+ m_tmpMimeType = "application/x-kontour";
+
+ KoDocumentEntry e = KoDocumentEntry::queryByMimeType( m_tmpMimeType );
+ if ( e.isEmpty() )
+ {
+ kdWarning(30003) << "Could not create child document with type " << m_tmpMimeType << endl;
+ bool res = createUnavailDocument( store, true, m_tmpMimeType );
+ if ( res )
+ {
+ // Try to turn the mimetype name into its comment
+ QString mimeName = m_tmpMimeType;
+ KMimeType::Ptr mime = KMimeType::mimeType( m_tmpMimeType );
+ if ( mime->name() != KMimeType::defaultMimeType() )
+ mimeName = mime->comment();
+ d->m_doc->setProperty( "unavailReason", i18n( "No handler found for %1" ).arg( mimeName ) );
+ }
+ return res;
+ }
+
+ return loadDocumentInternal( store, e, true /*open url*/, false /*not oasis*/ );
+}
+
+bool KoDocumentChild::loadOasisDocument( KoStore* store, const QDomDocument& manifestDoc )
+{
+ QString path = m_tmpURL;
+ if ( m_tmpURL.startsWith( INTERNAL_PROTOCOL ) ) {
+ path = store->currentDirectory();
+ if ( !path.isEmpty() )
+ path += '/';
+ QString relPath = KURL( m_tmpURL ).path();
+ path += relPath.mid( 1 ); // remove leading '/'
+ }
+ if ( !path.endsWith( "/" ) )
+ path += '/';
+ const QString mimeType = KoOasisStore::mimeForPath( manifestDoc, path );
+ kdDebug() << k_funcinfo << "path for manifest file=" << path << " mimeType=" << mimeType << endl;
+ if ( mimeType.isEmpty() ) {
+ kdError(30003) << "Manifest doesn't have media-type for " << path << endl;
+ return false;
+ }
+
+ KoDocumentEntry e = KoDocumentEntry::queryByMimeType( mimeType );
+ if ( e.isEmpty() )
+ {
+ kdWarning(30003) << "Could not create child document with type " << mimeType << endl;
+ bool res = createUnavailDocument( store, true, mimeType );
+ if ( res )
+ {
+ // Try to turn the mimetype name into its comment
+ QString mimeName = mimeType;
+ KMimeType::Ptr mime = KMimeType::mimeType( mimeType );
+ if ( mime->name() != KMimeType::defaultMimeType() )
+ mimeName = mime->comment();
+ d->m_doc->setProperty( "unavailReason", i18n( "No handler found for %1" ).arg( mimeName ) );
+ }
+ return res;
+ }
+
+ const bool oasis = mimeType.startsWith( "application/vnd.oasis.opendocument" );
+ if ( !oasis ) {
+ m_tmpURL += "/maindoc.xml";
+ kdDebug() << " m_tmpURL adjusted to " << m_tmpURL << endl;
+ }
+ return loadDocumentInternal( store, e, true /*open url*/, oasis );
+}
+
+bool KoDocumentChild::loadDocumentInternal( KoStore* store, const KoDocumentEntry& e, bool doOpenURL, bool oasis )
+{
+ kdDebug(30003) << "KoDocumentChild::loadDocumentInternal doOpenURL=" << doOpenURL << " m_tmpURL=" << m_tmpURL << endl;
+ KoDocument * doc = e.createDoc( d->m_parent );
+ if (!doc) {
+ kdWarning(30003) << "createDoc failed" << endl;
+ return false;
+ }
+ setDocument( doc, m_tmpGeometry );
+
+ bool res = true;
+ if ( doOpenURL )
+ {
+ bool internalURL = false;
+ if ( m_tmpURL.startsWith( STORE_PROTOCOL ) || m_tmpURL.startsWith( INTERNAL_PROTOCOL ) || KURL::isRelativeURL( m_tmpURL ) )
+ {
+ if ( oasis ) {
+ store->pushDirectory();
+ assert( m_tmpURL.startsWith( INTERNAL_PROTOCOL ) );
+ QString relPath = KURL( m_tmpURL ).path().mid( 1 );
+ store->enterDirectory( relPath );
+ res = d->m_doc->loadOasisFromStore( store );
+ store->popDirectory();
+ } else {
+ if ( m_tmpURL.startsWith( INTERNAL_PROTOCOL ) )
+ m_tmpURL = KURL( m_tmpURL ).path().mid( 1 );
+ res = d->m_doc->loadFromStore( store, m_tmpURL );
+ }
+ internalURL = true;
+ d->m_doc->setStoreInternal( true );
+ }
+ else
+ {
+ // Reference to an external document. Hmmm...
+ d->m_doc->setStoreInternal( false );
+ KURL url( m_tmpURL );
+ if ( !url.isLocalFile() )
+ {
+ QApplication::restoreOverrideCursor();
+ // For security reasons we need to ask confirmation if the url is remote
+ int result = KMessageBox::warningYesNoCancel(
+ 0, i18n( "This document contains an external link to a remote document\n%1").arg(m_tmpURL),
+ i18n( "Confirmation Required" ), i18n( "Download" ), i18n( "Skip" ) );
+
+ if ( result == KMessageBox::Cancel )
+ {
+ d->m_parent->setErrorMessage("USER_CANCELED");
+ return false;
+ }
+ if ( result == KMessageBox::Yes )
+ res = d->m_doc->openURL( url );
+ // and if == No, res will still be false so we'll use a kounavail below
+ }
+ else
+ res = d->m_doc->openURL( url );
+ }
+ if ( !res )
+ {
+ // Keep the error message from the attempted loading
+ QString errorMessage = d->m_doc->errorMessage();
+ delete d->m_doc;
+ d->m_doc = 0;
+ QString tmpURL = m_tmpURL; // keep a copy, createUnavailDocument will erase it
+ // Not found -> use a kounavail instead
+ res = createUnavailDocument( store, false /* the URL doesn't exist, don't try to open it */, m_tmpMimeType );
+ if ( res )
+ {
+ d->m_doc->setProperty( "realURL", tmpURL ); // so that it gets saved correctly
+ d->m_doc->setStoreInternal( true );
+ if ( internalURL )
+ d->m_doc->setProperty( "unavailReason", i18n( "Could not load embedded object:\n%1" ).arg( errorMessage ) );
+ else
+ d->m_doc->setProperty( "unavailReason", i18n( "Could not load external document %1:\n%2" ).arg( tmpURL, errorMessage ) );
+ }
+ return res;
+ }
+ // Still waiting...
+ QApplication::setOverrideCursor( waitCursor );
+ }
+
+ m_tmpURL = QString::null;
+
+ // see KoDocument::insertChild for an explanation what's going on
+ // now :-)
+ if ( parentDocument() )
+ {
+ KoDocument *parent = parentDocument();
+
+ if ( parent->manager() && parent->manager()->parts() )
+ {
+ KParts::PartManager *manager = parent->manager();
+
+ if ( !manager->parts()->containsRef( d->m_doc ) &&
+ !parent->isSingleViewMode() )
+ manager->addPart( d->m_doc, false );
+ }
+ }
+
+ QApplication::restoreOverrideCursor();
+
+ return res;
+}
+
+bool KoDocumentChild::createUnavailDocument( KoStore* store, bool doOpenURL, const QString& mimeType )
+{
+ // We don't need a trader query here. We're looking for a very specific component.
+ KService::Ptr serv = KService::serviceByDesktopName( "kounavail" );
+ if ( serv == 0L )
+ {
+ kdWarning(30003) << "ERROR: service kounavail not found " << endl;
+ return false;
+ }
+ KoDocumentEntry e( serv );
+ if ( !loadDocumentInternal( store, e, doOpenURL, false ) )
+ return false;
+ d->m_doc->setProperty( "mimetype", mimeType );
+ return true;
+}
+
+bool KoDocumentChild::saveOasis( KoStore* store, KoXmlWriter* manifestWriter )
+{
+ QString path;
+ if ( d->m_doc->isStoredExtern() )
+ {
+ kdDebug(30003)<<k_funcinfo<<" external (don't save) url:" << d->m_doc->url().url()<<endl;
+ path = d->m_doc->url().url();
+ }
+ else
+ {
+ // The name comes from KoDocumentChild (which was set while saving the
+ // parent document).
+ assert( d->m_doc->url().protocol() == INTERNAL_PROTOCOL );
+ const QString name = d->m_doc->url().path();
+ kdDebug() << k_funcinfo << "saving " << name << endl;
+
+ if ( d->m_doc->nativeOasisMimeType().isEmpty() ) {
+ // Embedded object doesn't support OASIS OpenDocument, save in the old format.
+ kdDebug() << k_funcinfo << "Embedded object doesn't support OASIS OpenDocument, save in the old format." << endl;
+
+ if ( !d->m_doc->saveToStore( store, name ) )
+ return false;
+ }
+ else
+ {
+ // To make the children happy cd to the correct directory
+ store->pushDirectory();
+ store->enterDirectory( name );
+
+ if ( !d->m_doc->saveOasis( store, manifestWriter ) ) {
+ kdWarning(30003) << "KoDocumentChild::saveOasis failed" << endl;
+ return false;
+ }
+ // Now that we're done leave the directory again
+ store->popDirectory();
+ }
+
+ assert( d->m_doc->url().protocol() == INTERNAL_PROTOCOL );
+ path = store->currentDirectory();
+ if ( !path.isEmpty() )
+ path += '/';
+ path += d->m_doc->url().path();
+ if ( path.startsWith( "/" ) )
+ path = path.mid( 1 ); // remove leading '/', no wanted in manifest
+ }
+
+ // OOo uses a trailing slash for the path to embedded objects (== directories)
+ if ( !path.endsWith( "/" ) )
+ path += '/';
+ QCString mimetype = d->m_doc->nativeOasisMimeType();
+ if ( mimetype.isEmpty() )
+ mimetype = d->m_doc->nativeFormatMimeType();
+ manifestWriter->addManifestEntry( path, mimetype );
+
+ return true;
+}
+
+void KoDocumentChild::saveOasisAttributes( KoXmlWriter &xmlWriter, const QString& name )
+{
+ if ( !d->m_doc->isStoredExtern() )
+ {
+ // set URL in document so that KoDocument::saveChildrenOasis will save
+ // the actual embedded object with the right name in the store.
+ KURL u;
+ u.setProtocol( INTERNAL_PROTOCOL );
+ u.setPath( name );
+ kdDebug() << k_funcinfo << u << endl;
+ d->m_doc->setURL( u );
+ }
+
+ //<draw:object draw:style-name="standard" draw:id="1" draw:layer="layout" svg:width="14.973cm" svg:height="4.478cm" svg:x="11.641cm" svg:y="14.613cm" xlink:href="#./Object 1" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/>
+ xmlWriter.addAttribute( "xlink:type", "simple" );
+ xmlWriter.addAttribute( "xlink:show", "embed" );
+ xmlWriter.addAttribute( "xlink:actuate", "onLoad" );
+
+ const QString ref = d->m_doc->isStoredExtern() ? d->m_doc->url().url() : "./"+ name;
+ kdDebug(30003) << "KoDocumentChild::saveOasis saving reference to embedded document as " << ref << endl;
+ xmlWriter.addAttribute( "xlink:href", /*"#" + */ref );
+}
+
+QDomElement KoDocumentChild::save( QDomDocument& doc, bool uppercase )
+{
+ if( d->m_doc )
+ {
+ QDomElement e = doc.createElement( ( uppercase ? "OBJECT" : "object" ) );
+ if ( d->m_doc->url().protocol() != INTERNAL_PROTOCOL ) {
+ e.setAttribute( "url", d->m_doc->url().url() );
+ kdDebug() << "KoDocumentChild::save url=" << d->m_doc->url().url() << endl;
+ }
+ else {
+ e.setAttribute( "url", d->m_doc->url().path().mid( 1 ) );
+ kdDebug() << "KoDocumentChild::save url=" << d->m_doc->url().path().mid( 1 ) << endl;
+ }
+ e.setAttribute( "mime", d->m_doc->nativeFormatMimeType() );
+ kdDebug() << "KoDocumentChild::save mime=" << d->m_doc->nativeFormatMimeType() << endl;
+ QDomElement rect = doc.createElement( ( uppercase ? "RECT" : "rect" ) );
+ rect.setAttribute( "x", geometry().left() );
+ rect.setAttribute( "y", geometry().top() );
+ rect.setAttribute( "w", geometry().width() );
+ rect.setAttribute( "h", geometry().height() );
+ e.appendChild(rect);
+ return e;
+ }
+ return QDomElement();
+}
+
+bool KoDocumentChild::isStoredExtern() const
+{
+ assert( d->m_doc );
+ return d->m_doc->isStoredExtern();
+}
+
+KURL KoDocumentChild::url() const
+{
+ return ( d->m_doc ? d->m_doc->url() : KURL() );
+}
+
+KoDocumentChild::~KoDocumentChild()
+{
+ if ( d->m_doc ) {
+ delete d->m_doc;
+ d->m_doc=0L;
+ }
+ delete d;
+}
+
+bool KoDocumentChild::isDeleted() const
+{
+ return d->m_deleted;
+}
+
+void KoDocumentChild::setDeleted( bool on )
+{
+ d->m_deleted = on;
+}
+
+#include "KoDocumentChild.moc"
diff --git a/lib/kofficecore/KoDocumentChild.h b/lib/kofficecore/KoDocumentChild.h
new file mode 100644
index 00000000..349961ed
--- /dev/null
+++ b/lib/kofficecore/KoDocumentChild.h
@@ -0,0 +1,180 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ 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 __koDocumentChild_h__
+#define __koDocumentChild_h__
+
+#include <KoChild.h>
+#include <koffice_export.h>
+class QDomDocument;
+class QDomElement;
+class KURL;
+class KoStore;
+class KoDocument;
+class KoDocumentChildPrivate;
+class KoDocumentEntry;
+class KoXmlWriter;
+
+/**
+ * @brief Holds an embedded object.
+ *
+ * In addition to its base class @ref KoChild it cares about the content
+ * of an embedded document. That means it supports operations like
+ * loading and saving.
+ *
+ * If you need a representation for embedded documents in your KOffice
+ * component then you should inherit from this class.
+ */
+class KOFFICECORE_EXPORT KoDocumentChild : public KoChild
+{
+ Q_OBJECT
+public:
+ KoDocumentChild( KoDocument* parent, KoDocument* doc, const QRect& geometry );
+
+ /**
+ * When using this constructor you must call @ref #setDocument before
+ * you can call any other function of this class.
+ */
+ KoDocumentChild( KoDocument* parent );
+
+ virtual ~KoDocumentChild();
+
+ /**
+ * Call this function only directly after calling the constructor
+ * that takes only a parent as argument.
+ */
+ virtual void setDocument( KoDocument *doc, const QRect &geometry );
+
+ /**
+ * @return document contained in this child
+ *
+ * @see KoDocument
+ */
+ KoDocument *document() const;
+
+ /**
+ * @return parent document of this child
+ *
+ * @see KoDocument
+ */
+ virtual KoDocument *parentDocument() const; // TODO: remove virtual, makes no sense
+
+ virtual KoDocument* hitTest( const QPoint& p, const QWMatrix& _matrix = QWMatrix() );
+
+ /**
+ * @note Can be empty (which is why it doesn't return a const KURL &)
+ */
+ KURL url() const;
+
+ /**
+ * Writes the "object" tag, but does NOT write the content of the
+ * embedded documents. Saving the embedded documents themselves
+ * is done in @ref KoDocument::saveChildren. This function just stores information
+ * about the position and id of the embedded document and should be
+ * called from within KoDocument::saveXML.
+ *
+ * The "object" element is not added to the document. It is just created
+ * and returned.
+ *
+ * @return the element containing the "object" tag.
+ */
+ virtual QDomElement save( QDomDocument& doc, bool uppercase=false );
+
+ /**
+ * Save an embedded object to OASIS.
+ * This method sets the attributes for the draw:object element in the parent XML document.
+ * It also prepares the embedded object for being saved into the store at
+ * the end of saving (see saveOasisToStore).
+ * Note that @p name is only used for "internal" documents (not extern).
+ */
+ void saveOasisAttributes( KoXmlWriter &xmlWriter, const QString& name );
+
+ /**
+ * Save an embedded object to an OASIS store.
+ * This is called automatically by the parent KoDocument's saveOasis
+ */
+ virtual bool saveOasis( KoStore* store, KoXmlWriter* manifestWriter );
+
+ /**
+ * Parses the "object" tag. This does NOT mean creating the child documents.
+ * AFTER the 'parser' has finished parsing, you must use @ref #loadDocument
+ * to actually load the embedded documents.
+ *
+ * What you should have in mind is that this method is called from within
+ * @ref KoDocument::loadXML while @ref #loadDocument is called from within
+ * @ref KoDocument::loadChildren, respectively from your implementation
+ * of these methods.
+ */
+ virtual bool load( const QDomElement& element, bool uppercase=false );
+
+ void loadOasis( const QDomElement &frameElement, const QDomElement& objectElement );
+
+ /**
+ * Actually loads the document from the disk/net or from the store,
+ * depending on @ref #url
+ */
+ virtual bool loadDocument( KoStore* );
+
+ /**
+ * Actually loads the document from the disk/net or from the store
+ * depending on the document's url
+ */
+ virtual bool loadOasisDocument( KoStore* store, const QDomDocument& manifestDoc );
+
+ virtual bool isStoredExtern() const;
+
+ /**
+ * This document (child) is deleted.
+ */
+ bool isDeleted() const;
+ void setDeleted( bool on = true );
+
+protected: // Should be private, but KWord needs access to the variables
+ // because it reimplements load/save (for uppercase tags)
+
+ /**
+ * Holds the source of this object, for example "file:/home/weis/image.gif"
+ * or "tar:/table1/2" if it is stored in a koffice store. This variable is
+ * set after parsing the OBJECT tag in @ref #load and is reset after
+ * calling @ref #loadDocument.
+ */
+ QString m_tmpURL;
+
+ /**
+ * This variable is
+ * set after parsing the OBJECT tag in @ref #load and is reset after
+ * calling @ref #loadDocument.
+ */
+ QRect m_tmpGeometry;
+
+ /**
+ * This variable is
+ * set after parsing the OBJECT tag in @ref #load and is reset after
+ * calling @ref #loadDocument.
+ */
+ QString m_tmpMimeType;
+
+private:
+ bool createUnavailDocument( KoStore* store, bool doOpenURL, const QString& mimeType );
+ bool loadDocumentInternal( KoStore* _store, const KoDocumentEntry& e, bool doOpenURL, bool oasis );
+
+private:
+ KoDocumentChildPrivate *d;
+};
+
+#endif
diff --git a/lib/kofficecore/KoDocumentIface.cc b/lib/kofficecore/KoDocumentIface.cc
new file mode 100644
index 00000000..2ff2f5fb
--- /dev/null
+++ b/lib/kofficecore/KoDocumentIface.cc
@@ -0,0 +1,568 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ 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.
+
+ 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 "KoDocument.h"
+#include "KoDocumentIface.h"
+#include "KoDocumentInfoDlg.h"
+#include "KoDocumentInfo.h"
+#include "KoView.h"
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kdcopactionproxy.h>
+#include <kaction.h>
+#include <kdebug.h>
+#include <kdcoppropertyproxy.h>
+
+//static
+QCString KoDocumentIface::newIfaceName()
+{
+ static int s_docIFNumber = 0;
+ QCString name; name.setNum( s_docIFNumber++ ); name.prepend("Document-");
+ return name;
+}
+
+KoDocumentIface::KoDocumentIface( KoDocument * doc, const char * name )
+ : DCOPObject( name ? QCString(name) : newIfaceName() )
+{
+ m_pDoc = doc;
+ m_actionProxy = new KDCOPActionProxy( doc->actionCollection(), this );
+}
+
+KoDocumentIface::~KoDocumentIface()
+{
+ delete m_actionProxy;
+}
+
+void KoDocumentIface::openURL( QString url )
+{
+ m_pDoc->openURL( KURL( url ) );
+}
+
+bool KoDocumentIface::isLoading()
+{
+ return m_pDoc->isLoading();
+}
+
+QString KoDocumentIface::url()
+{
+ return m_pDoc->url().url();
+}
+
+bool KoDocumentIface::isModified()
+{
+ return m_pDoc->isModified();
+}
+
+int KoDocumentIface::viewCount()
+{
+ return m_pDoc->viewCount();
+}
+
+DCOPRef KoDocumentIface::view( int idx )
+{
+ QPtrList<KoView> views = m_pDoc->views();
+ KoView *v = views.at( idx );
+ if ( !v )
+ return DCOPRef();
+
+ DCOPObject *obj = v->dcopObject();
+
+ if ( !obj )
+ return DCOPRef();
+
+ return DCOPRef( kapp->dcopClient()->appId(), obj->objId() );
+}
+
+DCOPRef KoDocumentIface::action( const QCString &name )
+{
+ return DCOPRef( kapp->dcopClient()->appId(), m_actionProxy->actionObjectId( name ) );
+}
+
+QCStringList KoDocumentIface::actions()
+{
+ QCStringList res;
+ QValueList<KAction *> lst = m_actionProxy->actions();
+ QValueList<KAction *>::ConstIterator it = lst.begin();
+ QValueList<KAction *>::ConstIterator end = lst.end();
+ for (; it != end; ++it )
+ res.append( (*it)->name() );
+
+ return res;
+}
+
+QMap<QCString,DCOPRef> KoDocumentIface::actionMap()
+{
+ return m_actionProxy->actionMap();
+}
+
+void KoDocumentIface::save()
+{
+ m_pDoc->save();
+}
+
+void KoDocumentIface::saveAs( const QString & url )
+{
+ m_pDoc->saveAs( KURL( url ) );
+ m_pDoc->waitSaveComplete(); // see ReadWritePart
+}
+
+void KoDocumentIface::setOutputMimeType( const QCString & mimetype )
+{
+ m_pDoc->setOutputMimeType( mimetype );
+}
+
+QString KoDocumentIface::documentInfoAuthorName() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->fullName();
+}
+
+QString KoDocumentIface::documentInfoEmail() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->email();
+}
+
+QString KoDocumentIface::documentInfoCompanyName() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->company();
+}
+
+QString KoDocumentIface::documentInfoTelephone() const
+{
+ kdDebug()<<" Keep compatibility with koffice <= 1.3 : use documentInfoTelephoneWork\n";
+ return documentInfoTelephoneWork();
+}
+
+QString KoDocumentIface::documentInfoTelephoneWork() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->telephoneWork();
+}
+
+QString KoDocumentIface::documentInfoTelephoneHome() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->telephoneHome();
+}
+
+
+QString KoDocumentIface::documentInfoFax() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->fax();
+
+}
+QString KoDocumentIface::documentInfoCountry() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->country();
+
+}
+QString KoDocumentIface::documentInfoPostalCode() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->postalCode();
+
+}
+QString KoDocumentIface::documentInfoCity() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->city();
+
+}
+
+QString KoDocumentIface::documentInfoInitial() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->initial();
+}
+
+QString KoDocumentIface::documentInfoAuthorPostion() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->position();
+}
+
+
+QString KoDocumentIface::documentInfoStreet() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->street();
+
+}
+
+QString KoDocumentIface::documentInfoTitle() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" ));
+ if ( !aboutPage )
+ {
+ kdWarning() << "'About' page not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return aboutPage->title();
+
+}
+
+QString KoDocumentIface::documentInfoAbstract() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" ));
+ if ( !aboutPage )
+ {
+ kdWarning() << "'About' page not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return aboutPage->abstract();
+}
+
+QString KoDocumentIface::documentInfoKeywords() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" ));
+ if ( !aboutPage )
+ {
+ kdWarning() << "'About' page not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return aboutPage->keywords();
+}
+
+QString KoDocumentIface::documentInfoSubject() const
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" ));
+ if ( !aboutPage )
+ {
+ kdWarning() << "'About' page not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return aboutPage->subject();
+}
+void KoDocumentIface::setDocumentInfoKeywords(const QString & text )
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" ));
+ if ( !aboutPage )
+ {
+ kdWarning() << "'About' page not found in documentInfo !" << endl;
+ }
+ else
+ aboutPage->setKeywords(text);
+}
+
+void KoDocumentIface::setDocumentInfoSubject(const QString & text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" ));
+ if ( !aboutPage )
+ {
+ kdWarning() << "'About' page not found in documentInfo !" << endl;
+ }
+ else
+ aboutPage->setSubject(text);
+}
+
+void KoDocumentIface::setDocumentInfoAuthorName(const QString & text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ }
+ else
+ authorPage->setFullName(text);
+
+}
+
+void KoDocumentIface::setDocumentInfoEmail(const QString &text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ }
+ else
+ authorPage->setEmail(text);
+}
+
+void KoDocumentIface::setDocumentInfoCompanyName(const QString &text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ }
+ else
+ authorPage->setCompany(text);
+}
+
+void KoDocumentIface::setDocumentInfoAuthorPosition(const QString &text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ }
+ else
+ authorPage->setPosition(text);
+}
+
+
+void KoDocumentIface::setDocumentInfoTelephone(const QString &text)
+{
+ kdDebug()<<"Keep compatibility with koffice <= 1.3 : use setDocumentInfoTelephoneWork\n";
+ setDocumentInfoTelephoneWork(text);
+}
+
+void KoDocumentIface::setDocumentInfoTelephoneWork(const QString &text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ }
+ else
+ authorPage->setTelephoneWork(text);
+}
+
+void KoDocumentIface::setDocumentInfoTelephoneHome(const QString &text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ }
+ else
+ authorPage->setTelephoneHome(text);
+}
+
+
+void KoDocumentIface::setDocumentInfoFax(const QString &text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ }
+ else
+ authorPage->setFax(text);
+}
+
+void KoDocumentIface::setDocumentInfoCountry(const QString &text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ }
+ else
+ authorPage->setCountry(text);
+
+}
+
+void KoDocumentIface::setDocumentInfoTitle(const QString & text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" ));
+ if ( !aboutPage )
+ {
+ kdWarning() << "'About' page not found in documentInfo !" << endl;
+ }
+ else
+ aboutPage->setTitle(text);
+}
+
+void KoDocumentIface::setDocumentInfoPostalCode(const QString &text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ }
+ else
+ authorPage->setPostalCode(text);
+
+}
+
+
+void KoDocumentIface::setDocumentInfoCity(const QString & text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ }
+ else
+ authorPage->setCity(text);
+}
+
+void KoDocumentIface::setDocumentInfoInitial(const QString & text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ }
+ else
+ authorPage->setInitial(text);
+}
+
+
+void KoDocumentIface::setDocumentInfoStreet(const QString &text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
+ if ( !authorPage )
+ {
+ kdWarning() << "Author information not found in documentInfo !" << endl;
+ }
+ else
+ authorPage->setStreet(text);
+
+}
+
+
+void KoDocumentIface::setDocumentInfoAbstract(const QString &text)
+{
+ KoDocumentInfo * info = m_pDoc->documentInfo();
+ KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" ));
+ if ( !aboutPage )
+ {
+ kdWarning() << "'About' page not found in documentInfo !" << endl;
+ }
+ else
+ aboutPage->setAbstract(text);
+}
+
+QCStringList KoDocumentIface::functionsDynamic()
+{
+ return DCOPObject::functionsDynamic() + KDCOPPropertyProxy::functions( m_pDoc );
+}
+
+bool KoDocumentIface::processDynamic( const QCString &fun, const QByteArray &data,
+ QCString& replyType, QByteArray &replyData )
+{
+ if ( KDCOPPropertyProxy::isPropertyRequest( fun, m_pDoc ) )
+ return KDCOPPropertyProxy::processPropertyRequest( fun, data, replyType, replyData, m_pDoc );
+
+ return DCOPObject::processDynamic( fun, data, replyType, replyData );
+}
+
diff --git a/lib/kofficecore/KoDocumentIface.h b/lib/kofficecore/KoDocumentIface.h
new file mode 100644
index 00000000..2713cb43
--- /dev/null
+++ b/lib/kofficecore/KoDocumentIface.h
@@ -0,0 +1,157 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ 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.
+
+ 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 __KoDocumentIface_h__
+#define __KoDocumentIface_h__
+
+#include <dcopobject.h>
+#include <qvaluelist.h>
+#include <dcopref.h>
+#include <koffice_export.h>
+class KoDocument;
+class KDCOPActionProxy;
+
+/**
+ * DCOP interface for any KOffice document
+ * Use KoApplicationIface to get hold of an existing document's interface,
+ * or to create a document.
+ *
+ * Note: KOffice Applications may (and should) reimplement KoDocument::dcopObject()
+ * In this case, don't look here... (unless the DCOP interface for the document
+ * inherits KoDocumentIface, which is a good thing to do)
+ */
+class KOFFICECORE_EXPORT KoDocumentIface : public DCOPObject
+{
+ K_DCOP
+public:
+
+ KoDocumentIface( KoDocument * doc, const char * name = 0 );
+ ~KoDocumentIface();
+
+ /**
+ * Generate a name for this interface. Automatically used if name=0 is
+ * passed to the constructor
+ */
+ static QCString newIfaceName();
+
+k_dcop:
+ /**
+ * Returns the URL for this document (empty, real URL, or internal one)
+ */
+ QString url();
+
+ /**
+ * Opens a document stored in @p url
+ * Warning: this is asynchronous. The document might not be loaded yet when
+ * this call returns. See isLoading.
+ */
+ void openURL( QString url );
+
+ /**
+ * @return TRUE is the document is still loading
+ */
+ bool isLoading();
+
+ /**
+ * @return TRUE is the document has been modified
+ */
+ bool isModified();
+
+ /**
+ * @return the number of views this document is displayed in
+ */
+ int viewCount();
+
+ /**
+ * @return a DCOP reference (DCOPRef) to the view with index @p idx
+ */
+ DCOPRef view( int idx );
+
+ /**
+ * DCOP-action proxy
+ */
+ DCOPRef action( const QCString &name );
+ /**
+ * @return list of actions
+ */
+ QCStringList actions();
+ /**
+ * @return a map of (action name, DCOP reference)
+ */
+ QMap<QCString,DCOPRef> actionMap();
+
+ /**
+ * Saves the document under its existing filename
+ */
+ void save();
+
+ /**
+ * Saves the document under a new name
+ */
+ void saveAs( const QString & url );
+
+ void setOutputMimeType( const QCString & mimetype );
+
+ QString documentInfoAuthorName() const;
+ QString documentInfoEmail() const;
+ QString documentInfoCompanyName() const;
+ QString documentInfoTitle() const;
+ QString documentInfoAbstract() const;
+ QString documentInfoKeywords() const;
+ QString documentInfoSubject() const;
+ QString documentInfoTelephone() const;
+ QString documentInfoTelephoneWork() const;
+ QString documentInfoTelephoneHome() const;
+ QString documentInfoFax() const;
+ QString documentInfoCountry() const;
+ QString documentInfoPostalCode() const;
+ QString documentInfoCity() const;
+ QString documentInfoStreet() const;
+ QString documentInfoInitial() const;
+ QString documentInfoAuthorPostion() const;
+ void setDocumentInfoAuthorName(const QString & text);
+ void setDocumentInfoEmail(const QString &text);
+ void setDocumentInfoCompanyName(const QString &text);
+ void setDocumentInfoTelephone(const QString &text);
+ void setDocumentInfoTelephoneWork(const QString &text);
+ void setDocumentInfoTelephoneHome(const QString &text);
+ void setDocumentInfoFax(const QString &text);
+ void setDocumentInfoCountry(const QString &text);
+ void setDocumentInfoTitle(const QString & text);
+ void setDocumentInfoPostalCode(const QString &text);
+ void setDocumentInfoCity(const QString & text);
+ void setDocumentInfoStreet(const QString &text);
+ void setDocumentInfoAbstract(const QString &text);
+ void setDocumentInfoInitial(const QString & text);
+ void setDocumentInfoKeywords(const QString & text );
+ void setDocumentInfoSubject(const QString & text);
+ void setDocumentInfoAuthorPosition(const QString & text);
+
+public:
+ virtual QCStringList functionsDynamic();
+ virtual bool processDynamic( const QCString &fun, const QByteArray &data,
+ QCString& replyType, QByteArray &replyData );
+
+protected:
+ KoDocument * m_pDoc;
+ KDCOPActionProxy *m_actionProxy;
+};
+
+#endif
+
diff --git a/lib/kofficecore/KoDocumentInfo.cpp b/lib/kofficecore/KoDocumentInfo.cpp
new file mode 100644
index 00000000..3d4e0505
--- /dev/null
+++ b/lib/kofficecore/KoDocumentInfo.cpp
@@ -0,0 +1,943 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
+ Copyright (C) 2004 David Faure <faure@kde.org>
+
+ 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.
+*/
+
+#include "KoDocumentInfo.h"
+#include "KoDom.h"
+#include "KoDocument.h"
+#include "kofficeversion.h"
+#include "KoApplication.h"
+
+#include <KoStoreDevice.h>
+#include <KoXmlWriter.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+
+#include <qobjectlist.h>
+#include <qdatetime.h>
+#include "KoXmlNS.h"
+
+/*****************************************
+ *
+ * KoDocumentInfo
+ *
+ *****************************************/
+
+KoDocumentInfo::KoDocumentInfo( QObject* parent, const char* name )
+ : QObject( parent, name )
+{
+ (void)new KoDocumentInfoUserMetadata( this );
+ (void)new KoDocumentInfoAuthor( this );
+ (void)new KoDocumentInfoAbout( this );
+}
+
+KoDocumentInfo::~KoDocumentInfo()
+{
+}
+
+// KOffice-1.3 format
+bool KoDocumentInfo::load( const QDomDocument& doc )
+{
+ QStringList lst = pages();
+ QStringList::ConstIterator it = lst.begin();
+ for( ; it != lst.end(); ++it )
+ {
+ KoDocumentInfoPage* p = page( *it );
+ Q_ASSERT( p );
+ if ( !p->load( doc.documentElement() ) )
+ return false;
+ }
+
+ return true;
+}
+
+bool KoDocumentInfo::loadOasis( const QDomDocument& metaDoc )
+{
+ //kdDebug()<<" metaDoc.toString() :"<<metaDoc.toString()<<endl;
+ QStringList lst = pages();
+ QStringList::ConstIterator it = lst.begin();
+ for( ; it != lst.end(); ++it )
+ {
+ KoDocumentInfoPage* p = page( *it );
+ Q_ASSERT( p );
+
+ QDomNode meta = KoDom::namedItemNS( metaDoc, KoXmlNS::office, "document-meta" );
+ QDomNode office = KoDom::namedItemNS( meta, KoXmlNS::office, "meta" );
+
+ if ( office.isNull() )
+ return false;
+
+ if ( !p->loadOasis( office ) )
+ return false;
+ }
+ return true;
+}
+
+// KOffice-1.3 format
+QDomDocument KoDocumentInfo::save()
+{
+ QDomDocument doc = KoDocument::createDomDocument( "document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1" );
+ QDomElement e = doc.documentElement();
+
+ const QStringList lst = pages();
+ QStringList::ConstIterator it = lst.begin();
+ for( ; it != lst.end(); ++it )
+ {
+ KoDocumentInfoPage* p = page( *it );
+ Q_ASSERT( p );
+ QDomElement s = p->save( doc );
+ if ( s.isNull() )
+ continue;
+ e.appendChild( s );
+ }
+ if ( e.isNull() )
+ return QDomDocument();
+
+ return doc;
+}
+
+bool KoDocumentInfo::saveOasis( KoStore* store )
+{
+ KoStoreDevice dev( store );
+ KoXmlWriter* xmlWriter = KoDocument::createOasisXmlWriter( &dev, "office:document-meta" );
+ xmlWriter->startElement( "office:meta" );
+
+ xmlWriter->startElement( "meta:generator");
+ xmlWriter->addTextNode( QString( "KOffice/%1" ).arg( KOFFICE_VERSION_STRING ) );
+ xmlWriter->endElement();
+ QStringList lst = pages();
+ QStringList::ConstIterator it = lst.begin();
+ for( ; it != lst.end(); ++it )
+ {
+ KoDocumentInfoPage* p = page( *it );
+ Q_ASSERT( p );
+ if ( !p->saveOasis( *xmlWriter ) )
+ return false;
+ }
+ xmlWriter->endElement();
+ xmlWriter->endElement(); // root element
+ xmlWriter->endDocument();
+ delete xmlWriter;
+ return true;
+}
+
+KoDocumentInfoPage* KoDocumentInfo::page( const QString& name ) const
+{
+ QObject* obj = const_cast<KoDocumentInfo*>(this)->child( name.latin1() );
+
+ return (KoDocumentInfoPage*)obj;
+}
+
+QStringList KoDocumentInfo::pages() const
+{
+ QStringList ret;
+
+ const QObjectList *list = children();
+ if ( list )
+ {
+ QObjectListIt it( *list );
+ QObject *obj;
+ while ( ( obj = it.current() ) )
+ {
+ ret.prepend( obj->name() );
+ ++it;
+ }
+ }
+
+ return ret;
+}
+
+QString KoDocumentInfo::title() const
+{
+ KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(page( "about" ));
+ if ( !aboutPage ) {
+ kdWarning() << "'About' page not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return aboutPage->title();
+}
+
+QString KoDocumentInfo::creator() const
+{
+ KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(page( "author" ));
+ if ( !authorPage ) {
+ kdWarning() << "'Author' page not found in documentInfo !" << endl;
+ return QString::null;
+ }
+ else
+ return authorPage->fullName();
+}
+
+/*****************************************
+ *
+ * KoDocumentInfoPage
+ *
+ *****************************************/
+
+KoDocumentInfoPage::KoDocumentInfoPage( QObject* parent, const char* name )
+ : QObject( parent, name )
+{
+}
+
+/*****************************************
+ *
+ * KoDocumentInfoAuthor
+ *
+ *****************************************/
+
+KoDocumentInfoAuthor::KoDocumentInfoAuthor( KoDocumentInfo* info )
+ : KoDocumentInfoPage( info, "author" )
+{
+ initParameters();
+}
+
+KoDocumentInfoAuthor::~KoDocumentInfoAuthor()
+{
+ delete m_emailCfg;
+}
+void KoDocumentInfoAuthor::initParameters()
+{
+ KConfig* config = KoGlobal::kofficeConfig();
+ if ( config->hasGroup( "Author" ) ) {
+ KConfigGroupSaver cgs( config, "Author" );
+ m_telephoneHome=config->readEntry( "telephone" );
+ m_telephoneWork=config->readEntry( "telephone-work" );
+ m_fax=config->readEntry( "fax" );
+ m_country=config->readEntry( "country" );
+ m_postalCode=config->readEntry( "postal-code" );
+ m_city=config->readEntry( "city" );
+ m_street=config->readEntry( "street" );
+ }
+
+ m_emailCfg = new KConfig( "emaildefaults", true );
+ m_emailCfg->setGroup( "Defaults" );
+ QString group = m_emailCfg->readEntry("Profile","Default");
+ m_emailCfg->setGroup(QString("PROFILE_%1").arg(group));
+
+ if ( m_fullName.isNull() ) // only if null. Empty means the user made it explicitly empty.
+ {
+ QString name = m_emailCfg->readEntry( "FullName" );
+ if ( !name.isEmpty() )
+ m_fullName = name;
+ }
+ if ( m_company.isNull() )
+ {
+ QString name = m_emailCfg->readEntry( "Organization" );
+ if ( !name.isEmpty() )
+ m_company = name;
+ }
+}
+
+bool KoDocumentInfoAuthor::saveOasis( KoXmlWriter &xmlWriter )
+{
+ if ( !m_fullName.isEmpty() )
+ {
+ xmlWriter.startElement( "dc:creator");
+ xmlWriter.addTextNode( m_fullName );
+ xmlWriter.endElement();
+ }
+ if ( !m_initial.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", "initial" );
+ xmlWriter.addTextNode( m_initial );
+ xmlWriter.endElement();
+ }
+ if ( !m_title.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", "author-title" );
+ xmlWriter.addTextNode( m_title );
+ xmlWriter.endElement();
+ }
+ if ( !m_company.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", "company" );
+ xmlWriter.addTextNode( m_company );
+ xmlWriter.endElement();
+ }
+ if ( !m_email.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", "email" );
+ xmlWriter.addTextNode( m_email );
+ xmlWriter.endElement();
+ }
+ if ( !m_telephoneHome.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", "telephone" );
+ xmlWriter.addTextNode( m_telephoneHome );
+ xmlWriter.endElement();
+ }
+ if ( !m_telephoneWork.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", "telephone-work" );
+ xmlWriter.addTextNode( m_telephoneWork );
+ xmlWriter.endElement();
+ }
+ if ( !m_fax.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", "fax" );
+ xmlWriter.addTextNode( m_fax );
+ xmlWriter.endElement();
+ }
+ if ( !m_country.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", "country" );
+ xmlWriter.addTextNode( m_country );
+ xmlWriter.endElement();
+ }
+ if ( !m_postalCode.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", "postal-code" );
+ xmlWriter.addTextNode( m_postalCode );
+ xmlWriter.endElement();
+ }
+ if ( !m_city.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", "city" );
+ xmlWriter.addTextNode( m_city );
+ xmlWriter.endElement();
+ }
+ if ( !m_street.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", "street" );
+ xmlWriter.addTextNode( m_street );
+ xmlWriter.endElement();
+ }
+ if ( !m_position.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", "position" );
+ xmlWriter.addTextNode( m_position );
+ xmlWriter.endElement();
+ }
+ return true;
+}
+
+bool KoDocumentInfoAuthor::loadOasis( const QDomNode& metaDoc )
+{
+ QDomElement e = KoDom::namedItemNS( metaDoc, KoXmlNS::dc, "creator" );
+ if ( !e.isNull() && !e.text().isEmpty() )
+ m_fullName = e.text();
+ QDomNode n = metaDoc.firstChild();
+ for ( ; !n.isNull(); n = n.nextSibling() )
+ {
+ if (n.isElement())
+ {
+ QDomElement e = n.toElement();
+ if ( e.namespaceURI() == KoXmlNS::meta && e.localName() == "user-defined" && !e.text().isEmpty() )
+ {
+ QString name = e.attributeNS( KoXmlNS::meta, "name", QString::null );
+ if ( name == "initial" )
+ m_initial = e.text();
+ else if ( name == "author-title" )
+ m_title = e.text();
+ else if ( name == "company" )
+ m_company = e.text();
+ else if ( name == "email" )
+ m_email = e.text();
+ else if ( name == "telephone" )
+ m_telephoneHome = e.text();
+ else if ( name == "telephone-work" )
+ m_telephoneWork = e.text();
+ else if ( name == "fax" )
+ m_fax = e.text();
+ else if ( name == "country" )
+ m_country = e.text();
+ else if ( name == "postal-code" )
+ m_postalCode = e.text();
+ else if ( name == "city" )
+ m_city = e.text();
+ else if ( name == "street" )
+ m_street = e.text();
+ else if ( name == "position" )
+ m_position = e.text();
+ }
+ }
+ }
+ return true;
+}
+
+// KOffice-1.3 format
+bool KoDocumentInfoAuthor::load( const QDomElement& e )
+{
+ QDomNode n = e.namedItem( "author" ).firstChild();
+ for( ; !n.isNull(); n = n.nextSibling() )
+ {
+ QDomElement e = n.toElement();
+ if ( e.isNull() ) continue;
+ if ( e.tagName() == "full-name" )
+ m_fullName = e.text();
+ else if ( e.tagName() == "initial" )
+ m_initial = e.text();
+ else if ( e.tagName() == "title" )
+ m_title = e.text();
+ else if ( e.tagName() == "company" )
+ m_company = e.text();
+ else if ( e.tagName() == "email" )
+ m_email = e.text();
+ else if ( e.tagName() == "telephone" )
+ m_telephoneHome = e.text();
+ else if ( e.tagName() == "telephone-work" )
+ m_telephoneWork = e.text();
+ else if ( e.tagName() == "fax" )
+ m_fax = e.text();
+ else if ( e.tagName() == "country" )
+ m_country = e.text();
+ else if ( e.tagName() == "postal-code" )
+ m_postalCode = e.text();
+ else if ( e.tagName() == "city" )
+ m_city = e.text();
+ else if ( e.tagName() == "street" )
+ m_street = e.text();
+ else if ( e.tagName() == "position" )
+ m_position = e.text();
+ }
+ return true;
+}
+
+QDomElement KoDocumentInfoAuthor::save( QDomDocument& doc )
+{
+ QDomElement e = doc.createElement( "author" );
+
+ QDomElement t = doc.createElement( "full-name" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_fullName ) );
+
+ t = doc.createElement( "initial" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_initial ) );
+
+
+ t = doc.createElement( "title" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_title ) );
+
+ t = doc.createElement( "company" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_company ) );
+
+ t = doc.createElement( "email" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_email ) );
+
+ t = doc.createElement( "telephone" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_telephoneHome ) );
+
+ t = doc.createElement( "telephone-work" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_telephoneWork ) );
+
+ t = doc.createElement( "fax" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_fax ) );
+
+ t = doc.createElement( "country" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_country ) );
+
+ t = doc.createElement( "postal-code" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_postalCode ) );
+
+ t = doc.createElement( "city" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_city ) );
+
+ t = doc.createElement( "street" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_street ) );
+
+ t = doc.createElement( "position" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_position ) );
+
+ return e;
+}
+
+QString KoDocumentInfoAuthor::fullName() const
+{
+ return m_fullName;
+}
+
+QString KoDocumentInfoAuthor::initial() const
+{
+ return m_initial;
+}
+
+QString KoDocumentInfoAuthor::title() const
+{
+ return m_title;
+}
+
+QString KoDocumentInfoAuthor::company() const
+{
+ return m_company;
+}
+
+QString KoDocumentInfoAuthor::email() const
+{
+ return m_email;
+}
+
+QString KoDocumentInfoAuthor::telephoneHome() const
+{
+ return m_telephoneHome;
+}
+
+QString KoDocumentInfoAuthor::telephoneWork() const
+{
+ return m_telephoneWork;
+}
+
+QString KoDocumentInfoAuthor::fax() const
+{
+ return m_fax;
+}
+
+QString KoDocumentInfoAuthor::country() const
+{
+ return m_country;
+}
+
+QString KoDocumentInfoAuthor::postalCode() const
+{
+ return m_postalCode;
+}
+
+QString KoDocumentInfoAuthor::city() const
+{
+ return m_city;
+}
+
+QString KoDocumentInfoAuthor::street() const
+{
+ return m_street;
+}
+
+QString KoDocumentInfoAuthor::position() const
+{
+ return m_position;
+}
+
+void KoDocumentInfoAuthor::setFullName( const QString& n )
+{
+ m_fullName = n;
+}
+
+void KoDocumentInfoAuthor::setInitial( const QString& n )
+{
+ m_initial = n;
+}
+
+void KoDocumentInfoAuthor::setTitle( const QString& n )
+{
+ m_title = n;
+}
+
+void KoDocumentInfoAuthor::setCompany( const QString& n )
+{
+ m_company = n;
+}
+
+void KoDocumentInfoAuthor::setEmail( const QString& n )
+{
+ m_email = n;
+}
+
+void KoDocumentInfoAuthor::setTelephoneHome( const QString& n )
+{
+ m_telephoneHome = n;
+}
+
+void KoDocumentInfoAuthor::setTelephoneWork( const QString& n )
+{
+ m_telephoneWork = n;
+}
+
+void KoDocumentInfoAuthor::setFax( const QString& n )
+{
+ m_fax = n;
+}
+
+void KoDocumentInfoAuthor::setCountry( const QString& n )
+{
+ m_country = n;
+}
+
+void KoDocumentInfoAuthor::setPostalCode( const QString& n )
+{
+ m_postalCode = n;
+}
+
+void KoDocumentInfoAuthor::setCity( const QString& n )
+{
+ m_city = n;
+}
+
+void KoDocumentInfoAuthor::setStreet( const QString& n )
+{
+ m_street = n;
+}
+
+void KoDocumentInfoAuthor::setPosition( const QString& n )
+{
+ m_position = n;
+}
+
+
+/*****************************************
+ *
+ * KoDocumentInfoAbout
+ *
+ *****************************************/
+
+KoDocumentInfoAbout::KoDocumentInfoAbout( KoDocumentInfo* info )
+ : KoDocumentInfoPage( info, "about" )
+{
+ m_firstSave = true;
+ m_docInfo = info;
+ m_editingCycles = 0;
+ m_initialCreator = m_docInfo->creator();
+ m_creationDate = QDateTime::currentDateTime();
+}
+
+void KoDocumentInfoAbout::saveParameters()
+{
+ KoDocument* doc = dynamic_cast< KoDocument* >( m_docInfo->parent() );
+ if ( m_firstSave && doc && !doc->isAutosaving() )
+ m_editingCycles++;
+ m_modificationDate = QDateTime::currentDateTime();
+ m_firstSave = false;
+}
+
+bool KoDocumentInfoAbout::saveOasis( KoXmlWriter &xmlWriter )
+{
+ saveParameters();
+ if ( !m_title.isEmpty() )
+ {
+ xmlWriter.startElement( "dc:title" );
+ xmlWriter.addTextNode( m_title );
+ xmlWriter.endElement();
+ }
+ if ( !m_abstract.isEmpty() )
+ {
+ xmlWriter.startElement( "dc:description" );
+ xmlWriter.addTextNode( m_abstract );
+ xmlWriter.endElement();
+ }
+ if ( !m_keywords.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:keyword" );
+ xmlWriter.addTextNode( m_keywords );
+ xmlWriter.endElement();
+ }
+ if ( !m_subject.isEmpty() )
+ {
+ xmlWriter.startElement( "dc:subject" );
+ xmlWriter.addTextNode( m_subject );
+ xmlWriter.endElement();
+ }
+ if ( !m_initialCreator.isEmpty() )
+ {
+ xmlWriter.startElement( "meta:initial-creator" );
+ xmlWriter.addTextNode( m_initialCreator );
+ xmlWriter.endElement();
+ }
+
+ xmlWriter.startElement( "meta:editing-cycles" );
+ xmlWriter.addTextNode( QString::number( m_editingCycles ) );
+ xmlWriter.endElement();
+
+ if ( m_creationDate.isValid() )
+ {
+ xmlWriter.startElement( "meta:creation-date" );
+ xmlWriter.addTextNode( m_creationDate.toString( Qt::ISODate ) );
+ xmlWriter.endElement();
+ }
+
+ if ( m_modificationDate.isValid() )
+ {
+ xmlWriter.startElement( "dc:date" );
+ xmlWriter.addTextNode( m_modificationDate.toString( Qt::ISODate ) );
+ xmlWriter.endElement();
+ }
+ return true;
+}
+
+bool KoDocumentInfoAbout::loadOasis( const QDomNode& metaDoc )
+{
+ QDomElement e = KoDom::namedItemNS( metaDoc, KoXmlNS::dc, "title" );
+ if ( !e.isNull() && !e.text().isEmpty() )
+ {
+ m_title = e.text();
+ }
+ e = KoDom::namedItemNS( metaDoc, KoXmlNS::dc, "description" );
+ if ( !e.isNull() && !e.text().isEmpty() )
+ {
+ m_abstract = e.text();
+ }
+ e = KoDom::namedItemNS( metaDoc, KoXmlNS::dc, "subject" );
+ if ( !e.isNull() && !e.text().isEmpty() )
+ {
+ m_subject = e.text();
+ }
+ e = KoDom::namedItemNS( metaDoc, KoXmlNS::meta, "keyword" );
+ if ( !e.isNull() && !e.text().isEmpty() )
+ {
+ m_keywords = e.text();
+ }
+ e = KoDom::namedItemNS( metaDoc, KoXmlNS::meta, "initial-creator" );
+ if ( !e.isNull() && !e.text().isEmpty() )
+ m_initialCreator = e.text();
+ else
+ m_initialCreator = i18n( "Unknown" );
+
+ e = KoDom::namedItemNS( metaDoc, KoXmlNS::meta, "editing-cycles" );
+ if ( !e.isNull() && !e.text().isEmpty() )
+ m_editingCycles = e.text().toInt();
+
+ e = KoDom::namedItemNS( metaDoc, KoXmlNS::meta, "creation-date" );
+ if ( !e.isNull() && !e.text().isEmpty() )
+ m_creationDate = QDateTime::fromString( e.text(), Qt::ISODate );
+ else
+ m_creationDate = QDateTime();
+
+ e = KoDom::namedItemNS( metaDoc, KoXmlNS::dc, "date" );
+ if ( !e.isNull() && !e.text().isEmpty() )
+ m_modificationDate = QDateTime::fromString( e.text(), Qt::ISODate );
+ return true;
+}
+
+// KOffice-1.3 format
+bool KoDocumentInfoAbout::load( const QDomElement& e )
+{
+ QDomNode n = e.namedItem( "about" ).firstChild();
+ for( ; !n.isNull(); n = n.nextSibling() )
+ {
+ QDomElement e = n.toElement();
+ if ( e.isNull() ) continue;
+ if ( e.tagName() == "abstract" )
+ m_abstract = e.text();
+ else if ( e.tagName() == "title" )
+ m_title = e.text();
+ else if ( e.tagName() == "subject" )
+ m_subject = e.text();
+ else if ( e.tagName() == "keyword" )
+ m_keywords = e.text();
+ else if ( e.tagName() == "initial-creator" )
+ m_initialCreator = e.text();
+ else if ( e.tagName() == "editing-cycles" )
+ m_editingCycles = e.text().toInt();
+ else if ( e.tagName() == "creation-date" )
+ m_creationDate = QDateTime::fromString( e.text(), Qt::ISODate );
+ else if ( e.tagName() == "date" )
+ m_modificationDate = QDateTime::fromString( e.text(), Qt::ISODate );
+ }
+
+ return true;
+}
+
+// KOffice-1.3 format
+QDomElement KoDocumentInfoAbout::save( QDomDocument& doc )
+{
+ saveParameters();
+ QDomElement e = doc.createElement( "about" );
+
+ QDomElement t = doc.createElement( "abstract" );
+ e.appendChild( t );
+ t.appendChild( doc.createCDATASection( m_abstract ) );
+
+ t = doc.createElement( "title" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_title ) );
+
+ t = doc.createElement( "keyword" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_keywords ) );
+
+ t = doc.createElement( "subject" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_subject ) );
+
+ t = doc.createElement( "initial-creator" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_initialCreator ) );
+
+ t = doc.createElement( "editing-cycles" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( QString::number( m_editingCycles ) ) );
+
+ t = doc.createElement( "creation-date" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_creationDate.toString( Qt::ISODate ) ) );
+
+ t = doc.createElement( "date" );
+ e.appendChild( t );
+ t.appendChild( doc.createTextNode( m_modificationDate.toString( Qt::ISODate ) ) );
+ return e;
+}
+
+QString KoDocumentInfoAbout::title() const
+{
+ return m_title;
+}
+
+QString KoDocumentInfoAbout::abstract() const
+{
+ return m_abstract;
+}
+
+QString KoDocumentInfoAbout::initialCreator() const
+{
+ return m_initialCreator;
+}
+
+QString KoDocumentInfoAbout::editingCycles() const
+{
+ return QString::number( m_editingCycles );
+}
+
+QString KoDocumentInfoAbout::creationDate() const
+{
+ if ( m_creationDate.isValid() )
+ return KGlobal::locale()->formatDateTime( m_creationDate );
+ else
+ return QString::null;
+}
+
+QString KoDocumentInfoAbout::modificationDate() const
+{
+ if ( m_modificationDate.isValid() )
+ return KGlobal::locale()->formatDateTime( m_modificationDate );
+ else
+ return QString::null;
+}
+
+void KoDocumentInfoAbout::setTitle( const QString& n )
+{
+ m_title = n;
+}
+
+void KoDocumentInfoAbout::setAbstract( const QString& n )
+{
+ m_abstract = n;
+}
+
+QString KoDocumentInfoAbout::keywords() const
+{
+ return m_keywords;
+}
+
+QString KoDocumentInfoAbout::subject() const
+{
+ return m_subject;
+}
+
+void KoDocumentInfoAbout::setKeywords( const QString& n )
+{
+ m_keywords = n;
+}
+
+void KoDocumentInfoAbout::setSubject( const QString& n )
+{
+ m_subject = n;
+}
+
+void KoDocumentInfoAbout::resetMetaData()
+{
+ m_editingCycles = 0;
+ m_initialCreator = m_docInfo->creator();
+ m_creationDate = QDateTime::currentDateTime();
+ m_modificationDate = QDateTime();
+}
+
+/*****************************************
+ *
+ * KoDocumentInfoUserMetadata
+ *
+ *****************************************/
+
+KoDocumentInfoUserMetadata::KoDocumentInfoUserMetadata( KoDocumentInfo* info )
+ : KoDocumentInfoPage( info, "user_metadata" )
+{
+ m_reserved << "initial" << "author-title" << "company" << "email" << "telephone"
+ << "telephone-work" << "fax" << "country" << "postal-code" << "city" << "street"
+ << "position";
+}
+
+bool KoDocumentInfoUserMetadata::saveOasis( KoXmlWriter &xmlWriter )
+{
+ QMap<QString, QString>::iterator it;
+ for ( it = m_metaList.begin(); it != m_metaList.end(); ++it )
+ {
+ xmlWriter.startElement( "meta:user-defined");
+ xmlWriter.addAttribute( "meta:name", it.key() );
+ xmlWriter.addTextNode( it.data() );
+ xmlWriter.endElement();
+ }
+ return true;
+}
+
+bool KoDocumentInfoUserMetadata::loadOasis( const QDomNode& metaDoc )
+{
+ QDomNode n = metaDoc.firstChild();
+ for ( ; !n.isNull(); n = n.nextSibling() )
+ {
+ if (n.isElement())
+ {
+ QDomElement e = n.toElement();
+ if ( e.namespaceURI() == KoXmlNS::meta && e.localName() == "user-defined" && !e.text().isEmpty() )
+ {
+ QString name = e.attributeNS( KoXmlNS::meta, "name", QString::null );
+ if ( !m_reserved.contains( name ) )
+ m_metaList[ name ] = e.text();
+ }
+ }
+ }
+ return true;
+}
+
+// KOffice-1.3 format
+bool KoDocumentInfoUserMetadata::load( const QDomElement& )
+{
+ return true;
+}
+
+// KOffice-1.3 format
+QDomElement KoDocumentInfoUserMetadata::save( QDomDocument& )
+{
+ return QDomElement();
+}
+
+#include <KoDocumentInfo.moc>
diff --git a/lib/kofficecore/KoDocumentInfo.h b/lib/kofficecore/KoDocumentInfo.h
new file mode 100644
index 00000000..30e74cf7
--- /dev/null
+++ b/lib/kofficecore/KoDocumentInfo.h
@@ -0,0 +1,196 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
+ Copyright (C) 2004 David Faure <faure@kde.org>
+
+ 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 KO_DOCUMENT_INFO_H
+#define KO_DOCUMENT_INFO_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <kconfig.h>
+#include <koffice_export.h>
+
+class QString;
+class QStringList;
+class QDomDocument;
+class QDomElement;
+class QDomNode;
+class QDateTime;
+class KoStore;
+class KoDocumentInfoPage;
+class KoXmlWriter;
+
+class KOFFICECORE_EXPORT KoDocumentInfo : public QObject
+{
+ Q_OBJECT
+public:
+ KoDocumentInfo( QObject* parent = 0, const char* name = 0 );
+ virtual ~KoDocumentInfo();
+
+ bool load( const QDomDocument& doc );
+ bool loadOasis( const QDomDocument& metaDoc );
+
+ QDomDocument save();
+ bool saveOasis( KoStore* store );
+
+ /**
+ * This info has an accessor because it's the most commonly used.
+ * Equivalent to page("about")->title() (but checking that the page exists)
+ */
+ QString title() const;
+ QString creator() const;
+
+ KoDocumentInfoPage* page( const QString& name ) const;
+ QStringList pages() const;
+ void documentInfochanged() { emit sigDocumentInfoModifed();}
+ signals:
+ void sigDocumentInfoModifed();
+};
+
+class KOFFICECORE_EXPORT KoDocumentInfoPage : public QObject
+{
+public:
+ KoDocumentInfoPage( QObject *parent, const char* name );
+
+ virtual bool load( const QDomElement& e ) = 0;
+ virtual QDomElement save( QDomDocument& doc ) = 0;
+ virtual bool loadOasis( const QDomNode& metaDoc ) = 0;
+ virtual bool saveOasis( KoXmlWriter &xmlWriter ) = 0;
+};
+
+class KOFFICECORE_EXPORT KoDocumentInfoAuthor : public KoDocumentInfoPage
+{
+ Q_OBJECT
+public:
+ KoDocumentInfoAuthor( KoDocumentInfo* info );
+ ~KoDocumentInfoAuthor();
+
+ virtual bool load( const QDomElement& e );
+ virtual QDomElement save( QDomDocument& doc );
+ virtual bool loadOasis( const QDomNode& metaDoc );
+ virtual bool saveOasis( KoXmlWriter &xmlWriter );
+
+ QString fullName() const;
+ QString initial() const;
+ QString title() const;
+ QString company() const;
+ QString email() const;
+ QString telephoneHome() const;
+ QString telephoneWork() const;
+ QString fax() const;
+ QString country() const;
+ QString postalCode() const;
+ QString city() const;
+ QString street() const;
+ QString position() const;
+
+ void setFullName( const QString& n );
+ void setTitle( const QString& n );
+ void setCompany( const QString& n );
+ void setEmail( const QString& n );
+ void setTelephoneHome( const QString& n );
+ void setTelephoneWork( const QString& n );
+ void setFax( const QString& n );
+ void setCountry( const QString& n );
+ void setPostalCode( const QString& n );
+ void setCity( const QString& n );
+ void setStreet( const QString& n );
+ void setInitial( const QString& n );
+ void setPosition( const QString& n );
+ void initParameters();
+
+private:
+ QString m_fullName;
+ QString m_title;
+ QString m_company;
+ QString m_email;
+ QString m_telephoneHome;
+ QString m_telephoneWork;
+ QString m_fax;
+ QString m_country;
+ QString m_postalCode;
+ QString m_city;
+ QString m_street;
+ QString m_initial;
+ QString m_position;
+ KConfig *m_emailCfg;
+};
+
+class KOFFICECORE_EXPORT KoDocumentInfoAbout : public KoDocumentInfoPage
+{
+ Q_OBJECT
+public:
+ KoDocumentInfoAbout( KoDocumentInfo* info );
+
+ virtual bool load( const QDomElement& e );
+ virtual QDomElement save( QDomDocument& doc );
+ virtual bool loadOasis( const QDomNode& metaDoc );
+ virtual bool saveOasis( KoXmlWriter &xmlWriter );
+
+ QString title() const;
+ QString abstract() const;
+ QString subject() const;
+ QString keywords() const;
+ QString initialCreator() const;
+ QString editingCycles() const;
+ QString creationDate() const;
+ QString modificationDate() const;
+
+ void setKeywords( const QString &n );
+ void setSubject( const QString& n );
+
+ void setTitle( const QString& n );
+ void setAbstract( const QString& n );
+ void saveParameters();
+
+public slots:
+ void resetMetaData();
+
+private:
+ KoDocumentInfo* m_docInfo;
+ QString m_title;
+ QString m_abstract;
+ QString m_keywords;
+ QString m_subject;
+ QString m_initialCreator;
+ QDateTime m_creationDate;
+ QDateTime m_modificationDate;
+ int m_editingCycles;
+ bool m_firstSave;
+};
+
+class KOFFICECORE_EXPORT KoDocumentInfoUserMetadata : public KoDocumentInfoPage
+{
+ Q_OBJECT
+public:
+ KoDocumentInfoUserMetadata( KoDocumentInfo* info );
+
+ virtual bool load( const QDomElement& e );
+ virtual QDomElement save( QDomDocument& doc );
+ virtual bool loadOasis( const QDomNode& metaDoc );
+ virtual bool saveOasis( KoXmlWriter &xmlWriter );
+
+ QMap<QString, QString>* metadataList() { return &m_metaList; }
+
+private:
+ QMap<QString, QString> m_metaList;
+ QStringList m_reserved;
+};
+
+#endif
diff --git a/lib/kofficecore/KoDocumentInfoDlg.cpp b/lib/kofficecore/KoDocumentInfoDlg.cpp
new file mode 100644
index 00000000..f9ad8904
--- /dev/null
+++ b/lib/kofficecore/KoDocumentInfoDlg.cpp
@@ -0,0 +1,555 @@
+/* This file is part of the KDE project
+ Copyright (c) 2000 Simon Hausmann <hausmann@kde.org>
+
+ $Id: KoDocumentInfoDlg.cpp 512890 2006-02-23 21:37:08Z illissius $
+
+ 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.
+*/
+
+#include "KoDocumentInfoDlg.h"
+#include "KoDocumentInfo.h"
+#include "koDocumentInfoAboutWidget.h"
+#include "koDocumentInfoAuthorWidget.h"
+#include "koDocumentInfoUserMetadataWidget.h"
+#include "KoDocument.h"
+
+#include <KoGlobal.h>
+#include <KoStore.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <qlabel.h>
+#include <qbuffer.h>
+#include <qdom.h>
+#include <qdir.h>
+#include <qvbox.h>
+#include <qdatetime.h>
+
+#include <kabc/addressee.h>
+#include <kabc/stdaddressbook.h>
+#include <kdeversion.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <ktar.h>
+#include <kdebug.h>
+#include <ktempfile.h>
+#include <kmimetype.h>
+#include <qlayout.h>
+#include <klistview.h>
+#include <qgrid.h>
+#include <qmap.h>
+#include <kfilterdev.h>
+#include <klineedit.h>
+#include <ktextedit.h>
+#include <kiconloader.h>
+#include <kpushbutton.h>
+#include <klocale.h>
+
+class KoDocumentInfoDlg::KoDocumentInfoDlgPrivate
+{
+public:
+ KoDocumentInfoDlgPrivate()
+ {
+ }
+ ~KoDocumentInfoDlgPrivate()
+ {
+ }
+
+ KoDocumentInfo *m_info;
+ KoDocumentInfoAboutWidget *m_aboutWidget;
+ KoDocumentInfoAuthorWidget *m_authorWidget;
+ KoDocumentInfoUserMetadataWidget *m_metaWidget;
+
+ bool m_bDeleteDialog;
+ KDialogBase *m_dialog;
+};
+
+KoDocumentInfoDlg::KoDocumentInfoDlg( KoDocumentInfo *docInfo, QWidget *parent, const char *name,
+ KDialogBase *dialog )
+: QObject( parent, "docinfodlg" )
+{
+ d = new KoDocumentInfoDlgPrivate;
+ d->m_info = docInfo;
+
+ d->m_dialog = dialog;
+ d->m_bDeleteDialog = false;
+
+ if ( !dialog )
+ {
+ d->m_dialog = new KDialogBase( KDialogBase::Tabbed,
+ i18n( "Document Information" ),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Ok, parent, name, true, false );
+ d->m_dialog->setInitialSize( QSize( 500, 500 ) );
+ d->m_bDeleteDialog = true;
+ }
+
+ QStringList pages = docInfo->pages();
+ QStringList::ConstIterator it = pages.begin();
+ QStringList::ConstIterator end = pages.end();
+ for (; it != end; ++it )
+ {
+ KoDocumentInfoPage *pg = docInfo->page( *it );
+ if ( pg->inherits( "KoDocumentInfoAuthor" ) )
+ addAuthorPage( static_cast<KoDocumentInfoAuthor *>( pg ) );
+ else if ( pg->inherits( "KoDocumentInfoAbout" ) )
+ addAboutPage( static_cast<KoDocumentInfoAbout *>( pg ) );
+/* else if ( pg->inherits( "KoDocumentInfoUserMetadata" ) )
+ addUserMetadataPage( static_cast<KoDocumentInfoUserMetadata *>( pg ) );*/
+ }
+}
+
+KoDocumentInfoDlg::~KoDocumentInfoDlg()
+{
+ if ( d->m_bDeleteDialog )
+ delete d->m_dialog;
+
+ delete d;
+}
+
+int KoDocumentInfoDlg::exec()
+{
+ return d->m_dialog->exec();
+}
+
+KDialogBase *KoDocumentInfoDlg::dialog() const
+{
+ return d->m_dialog;
+}
+
+void KoDocumentInfoDlg::loadFromKABC()
+{
+ KABC::StdAddressBook *ab = static_cast<KABC::StdAddressBook*>
+ ( KABC::StdAddressBook::self() );
+
+ if ( !ab )
+ return;
+
+ KABC::Addressee addr = ab->whoAmI();
+ if ( addr.isEmpty() )
+ {
+ KMessageBox::sorry( 0L, i18n( "No personal contact data set, please use the option \
+ \"Set as Personal Contact Data\" from the \"Edit\" menu in KAddressbook to set one." ) );
+ return;
+ }
+
+ d->m_authorWidget->leFullName->setText( addr.formattedName() );
+ d->m_authorWidget->leInitial->setText( addr.givenName()[ 0 ] + ". " +
+ addr.familyName()[ 0 ] + "." );
+ d->m_authorWidget->leAuthorTitle->setText( addr.title() );
+ d->m_authorWidget->leCompany->setText( addr.organization() );
+ d->m_authorWidget->leEmail->setText( addr.preferredEmail() );
+
+ KABC::PhoneNumber phone = addr.phoneNumber( KABC::PhoneNumber::Home );
+ d->m_authorWidget->leTelephoneHome->setText( phone.number() );
+ phone = addr.phoneNumber( KABC::PhoneNumber::Work );
+ d->m_authorWidget->leTelephoneWork->setText( phone.number() );
+
+ phone = addr.phoneNumber( KABC::PhoneNumber::Fax );
+ d->m_authorWidget->leFax->setText( phone.number() );
+
+ KABC::Address a = addr.address( KABC::Address::Home );
+ d->m_authorWidget->leCountry->setText( a.country() );
+ d->m_authorWidget->lePostalCode->setText( a.postalCode() );
+ d->m_authorWidget->leCity->setText( a.locality() );
+ d->m_authorWidget->leStreet->setText( a.street() );
+
+ emit changed();
+}
+
+void KoDocumentInfoDlg::deleteInfo()
+{
+ d->m_authorWidget->leFullName->setText( QString::null );
+ d->m_authorWidget->leInitial->setText( QString::null );
+ d->m_authorWidget->leAuthorTitle->setText( QString::null );
+ d->m_authorWidget->leCompany->setText( QString::null );
+ d->m_authorWidget->leEmail->setText( QString::null );
+ d->m_authorWidget->leTelephoneHome->setText( QString::null );
+ d->m_authorWidget->leTelephoneWork->setText( QString::null );
+ d->m_authorWidget->leFax->setText( QString::null );
+ d->m_authorWidget->leCountry->setText( QString::null );
+ d->m_authorWidget->lePostalCode->setText( QString::null );
+ d->m_authorWidget->leCity->setText( QString::null );
+ d->m_authorWidget->leStreet->setText( QString::null );
+ emit changed();
+}
+
+void KoDocumentInfoDlg::resetMetaData()
+{
+ QString s = KGlobal::locale()->formatDateTime( QDateTime::currentDateTime() );
+ d->m_aboutWidget->labelCreated->setText( s + ", " + d->m_info->creator() );
+ d->m_aboutWidget->labelModified->setText( "" );
+ d->m_aboutWidget->labelRevision->setText( "0" );
+ emit changed();
+}
+
+void KoDocumentInfoDlg::addAuthorPage( KoDocumentInfoAuthor *authorInfo )
+{
+ QVBox *page = d->m_dialog->addVBoxPage( i18n( "Author" ) );
+ d->m_authorWidget = new KoDocumentInfoAuthorWidget( page );
+ d->m_authorWidget->labelAuthor->setPixmap( KGlobal::iconLoader()->loadIcon( "personal", KIcon::Desktop, 48 ) );
+ d->m_authorWidget->pbLoadKABC->setIconSet( QIconSet( KGlobal::iconLoader()->loadIcon( "kaddressbook", KIcon::Small ) ) );
+ d->m_authorWidget->pbDelete->setIconSet( QIconSet( KGlobal::iconLoader()->loadIcon( "eraser", KIcon::Small ) ) );
+
+ d->m_authorWidget->leFullName->setText( authorInfo->fullName() );
+ d->m_authorWidget->leInitial->setText( authorInfo->initial() );
+ d->m_authorWidget->leAuthorTitle->setText( authorInfo->title() );
+ d->m_authorWidget->leCompany->setText( authorInfo->company() );
+ d->m_authorWidget->leEmail->setText( authorInfo->email() );
+ d->m_authorWidget->leTelephoneWork->setText( authorInfo->telephoneWork() );
+ d->m_authorWidget->leTelephoneHome->setText( authorInfo->telephoneHome() );
+ d->m_authorWidget->leFax->setText( authorInfo->fax() );
+ d->m_authorWidget->leCountry->setText( authorInfo->country() );
+ d->m_authorWidget->lePostalCode->setText( authorInfo->postalCode() );
+ d->m_authorWidget->leCity->setText( authorInfo->city() );
+ d->m_authorWidget->leStreet->setText( authorInfo->street() );
+ d->m_authorWidget->leAuthorPosition->setText( authorInfo->position() );
+
+ connect( d->m_authorWidget->leFullName, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->leInitial, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->leAuthorTitle, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->leCompany, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->leEmail, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->leTelephoneWork, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->leTelephoneHome, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->leFax, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->leCountry, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->lePostalCode, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->leCity, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->leStreet, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->leAuthorPosition, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_authorWidget->pbLoadKABC, SIGNAL( clicked() ),
+ this, SLOT( loadFromKABC() ) );
+ connect( d->m_authorWidget->pbDelete, SIGNAL( clicked() ),
+ this, SLOT( deleteInfo() ) );
+}
+
+void KoDocumentInfoDlg::addAboutPage( KoDocumentInfoAbout *aboutInfo )
+{
+ QVBox *page = d->m_dialog->addVBoxPage( i18n( "General" ) );
+ d->m_aboutWidget = new KoDocumentInfoAboutWidget( page );
+ d->m_aboutWidget->pbReset->setIconSet( QIconSet( KGlobal::iconLoader()->loadIcon( "reload", KIcon::Small ) ) );
+ KoDocument* doc = dynamic_cast< KoDocument* >( d->m_info->parent() );
+ if ( doc )
+ {
+ d->m_aboutWidget->leDocFile->setText( doc->file() );
+ d->m_aboutWidget->labelType->setText( KMimeType::mimeType( doc->mimeType() )->comment() );
+ d->m_aboutWidget->pixmapLabel->setPixmap( KMimeType::mimeType( doc->mimeType() )->pixmap( KIcon::Desktop, 48 ) );
+ }
+ if ( aboutInfo->creationDate() != QString::null )
+ d->m_aboutWidget->labelCreated->setText( aboutInfo->creationDate() + ", " + aboutInfo->initialCreator() );
+ if ( aboutInfo->modificationDate() != QString::null )
+ d->m_aboutWidget->labelModified->setText( aboutInfo->modificationDate() + ", " + d->m_info->creator() );
+ d->m_aboutWidget->labelRevision->setText( aboutInfo->editingCycles() );
+ d->m_aboutWidget->leDocTitle->setText( aboutInfo->title() );
+ d->m_aboutWidget->leDocSubject->setText( aboutInfo->subject() );
+ d->m_aboutWidget->leDocKeywords->setText( aboutInfo->keywords() );
+ d->m_aboutWidget->meDocAbstract->setText( aboutInfo->abstract() );
+
+ connect( d->m_aboutWidget->leDocTitle, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_aboutWidget->meDocAbstract, SIGNAL( textChanged() ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_aboutWidget->leDocSubject, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_aboutWidget->leDocKeywords, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( d->m_aboutWidget->pbReset, SIGNAL( clicked() ),
+ aboutInfo, SLOT( resetMetaData() ) );
+ connect( d->m_aboutWidget->pbReset, SIGNAL( clicked() ),
+ this, SLOT( resetMetaData() ) );
+}
+
+void KoDocumentInfoDlg::addUserMetadataPage( KoDocumentInfoUserMetadata *userMetadataInfo )
+{
+ QVBox *page = d->m_dialog->addVBoxPage( i18n( "User-Defined Metadata" ) );
+ d->m_metaWidget = new KoDocumentInfoUserMetadataWidget( page );
+
+ d->m_metaWidget->metaListView->addColumn( "Name" );
+ d->m_metaWidget->metaListView->setFullWidth( true );
+
+ QMap<QString, QString>::iterator it;
+ for ( it = userMetadataInfo->metadataList()->begin(); it != userMetadataInfo->metadataList()->end(); ++it )
+ {
+ QString name = it.key();
+ QString value = it.data();
+ KListViewItem* it = new KListViewItem( d->m_metaWidget->metaListView, name, value );
+ it->setPixmap( 0, KGlobal::iconLoader()->loadIcon( "text", KIcon::Small ) );
+ }
+}
+
+void KoDocumentInfoDlg::save()
+{
+ QStringList pages = d->m_info->pages();
+ QStringList::ConstIterator it = pages.begin();
+ QStringList::ConstIterator end = pages.end();
+ bool saveInfo=false;
+ for (; it != end; ++it )
+ {
+ KoDocumentInfoPage *pg = d->m_info->page( *it );
+ if ( pg->inherits( "KoDocumentInfoAuthor" ) )
+ {
+ saveInfo=true;
+ save( static_cast<KoDocumentInfoAuthor *>( pg ) );
+ }
+ else if ( pg->inherits( "KoDocumentInfoAbout" ) )
+ {
+ saveInfo=true;
+ save( static_cast<KoDocumentInfoAbout *>( pg ) );
+ }
+ }
+ if(saveInfo)
+ d->m_info->documentInfochanged();
+}
+
+void KoDocumentInfoDlg::save( KoDocumentInfoAuthor *authorInfo )
+{
+ authorInfo->setFullName( d->m_authorWidget->leFullName->text() );
+ authorInfo->setInitial( d->m_authorWidget->leInitial->text() );
+ authorInfo->setTitle( d->m_authorWidget->leAuthorTitle->text() );
+ authorInfo->setCompany( d->m_authorWidget->leCompany->text() );
+ authorInfo->setEmail( d->m_authorWidget->leEmail->text() );
+ authorInfo->setTelephoneWork( d->m_authorWidget->leTelephoneWork->text() );
+ authorInfo->setTelephoneHome( d->m_authorWidget->leTelephoneHome->text() );
+ authorInfo->setFax( d->m_authorWidget->leFax->text() );
+ authorInfo->setCountry( d->m_authorWidget->leCountry->text() );
+ authorInfo->setPostalCode( d->m_authorWidget->lePostalCode->text() );
+ authorInfo->setCity( d->m_authorWidget->leCity->text() );
+ authorInfo->setStreet( d->m_authorWidget->leStreet->text() );
+ authorInfo->setPosition( d->m_authorWidget->leAuthorPosition->text() );
+
+ KConfig* config = KoGlobal::kofficeConfig();
+ KConfigGroupSaver cgs( config, "Author" );
+ config->writeEntry("telephone", d->m_authorWidget->leTelephoneHome->text());
+ config->writeEntry("telephone-work", d->m_authorWidget->leTelephoneWork->text());
+ config->writeEntry("fax", d->m_authorWidget->leFax->text());
+ config->writeEntry("country",d->m_authorWidget->leCountry->text());
+ config->writeEntry("postal-code",d->m_authorWidget->lePostalCode->text());
+ config->writeEntry("city", d->m_authorWidget->leCity->text());
+ config->writeEntry("street", d->m_authorWidget->leStreet->text());
+ config->sync();
+}
+
+void KoDocumentInfoDlg::save( KoDocumentInfoAbout *aboutInfo )
+{
+ aboutInfo->setTitle( d->m_aboutWidget->leDocTitle->text() );
+ aboutInfo->setSubject( d->m_aboutWidget->leDocSubject->text() );
+ aboutInfo->setKeywords( d->m_aboutWidget->leDocKeywords->text() );
+ aboutInfo->setAbstract( d->m_aboutWidget->meDocAbstract->text() );
+}
+
+void KoDocumentInfoDlg::save( KoDocumentInfoUserMetadata* )
+{
+ // FIXME
+}
+
+class KoDocumentInfoPropsPage::KoDocumentInfoPropsPagePrivate
+{
+public:
+ KoDocumentInfo *m_info;
+ KoDocumentInfoDlg *m_dlg;
+ KURL m_url;
+ KTarGz *m_src;
+ KTarGz *m_dst;
+
+ const KTarFile *m_docInfoFile;
+};
+
+KoDocumentInfoPropsPage::KoDocumentInfoPropsPage( KPropertiesDialog *props,
+ const char *,
+ const QStringList & )
+: KPropsDlgPlugin( props )
+{
+ d = new KoDocumentInfoPropsPagePrivate;
+ d->m_info = new KoDocumentInfo( this, "docinfo" );
+ d->m_url = props->item()->url();
+ d->m_dlg = 0;
+
+ if ( !d->m_url.isLocalFile() )
+ return;
+
+ d->m_dst = 0;
+
+#ifdef __GNUC__
+#warning TODO port this to KoStore !!!
+#endif
+ d->m_src = new KTarGz( d->m_url.path(), "application/x-gzip" );
+
+ if ( !d->m_src->open( IO_ReadOnly ) )
+ return;
+
+ const KTarDirectory *root = d->m_src->directory();
+ if ( !root )
+ return;
+
+ const KTarEntry *entry = root->entry( "documentinfo.xml" );
+
+ if ( entry && entry->isFile() )
+ {
+ d->m_docInfoFile = static_cast<const KTarFile *>( entry );
+
+ QBuffer buffer( d->m_docInfoFile->data() );
+ buffer.open( IO_ReadOnly );
+
+ QDomDocument doc;
+ doc.setContent( &buffer );
+
+ d->m_info->load( doc );
+ }
+
+ d->m_dlg = new KoDocumentInfoDlg( d->m_info, 0, 0, props );
+ connect( d->m_dlg, SIGNAL( changed() ),
+ this, SIGNAL( changed() ) );
+}
+
+KoDocumentInfoPropsPage::~KoDocumentInfoPropsPage()
+{
+ delete d->m_info;
+ delete d->m_src;
+ delete d->m_dst;
+ delete d->m_dlg;
+ delete d;
+}
+
+void KoDocumentInfoPropsPage::applyChanges()
+{
+ const KTarDirectory *root = d->m_src->directory();
+ if ( !root )
+ return;
+
+ struct stat statBuff;
+
+ if ( stat( QFile::encodeName( d->m_url.path() ), &statBuff ) != 0 )
+ return;
+
+ KTempFile tempFile( d->m_url.path(), QString::null, statBuff.st_mode );
+
+ tempFile.setAutoDelete( true );
+
+ if ( tempFile.status() != 0 )
+ return;
+
+ if ( !tempFile.close() )
+ return;
+
+ d->m_dst = new KTarGz( tempFile.name(), "application/x-gzip" );
+
+ if ( !d->m_dst->open( IO_WriteOnly ) )
+ return;
+
+ KMimeType::Ptr mimeType = KMimeType::findByURL( d->m_url, 0, true );
+ if ( mimeType && dynamic_cast<KFilterDev *>( d->m_dst->device() ) != 0 )
+ {
+ QCString appIdentification( "KOffice " ); // We are limited in the number of chars.
+ appIdentification += mimeType->name().latin1();
+ appIdentification += '\004'; // Two magic bytes to make the identification
+ appIdentification += '\006'; // more reliable (DF)
+ d->m_dst->setOrigFileName( appIdentification );
+ }
+
+ bool docInfoSaved = false;
+
+ QStringList entries = root->entries();
+ QStringList::ConstIterator it = entries.begin();
+ QStringList::ConstIterator end = entries.end();
+ for (; it != end; ++it )
+ {
+ const KTarEntry *entry = root->entry( *it );
+
+ assert( entry );
+
+ if ( entry->name() == "documentinfo.xml" ||
+ ( !docInfoSaved && !entries.contains( "documentinfo.xml" ) ) )
+ {
+ d->m_dlg->save();
+
+ QBuffer buffer;
+ buffer.open( IO_WriteOnly );
+ QTextStream str( &buffer );
+ str << d->m_info->save();
+ buffer.close();
+
+ kdDebug( 30003 ) << "writing documentinfo.xml" << endl;
+ d->m_dst->writeFile( "documentinfo.xml", entry->user(), entry->group(), buffer.buffer().size(),
+ buffer.buffer().data() );
+
+ docInfoSaved = true;
+ }
+ else
+ copy( QString::null, entry );
+ }
+
+ d->m_dst->close();
+
+ QDir dir;
+ dir.rename( tempFile.name(), d->m_url.path() );
+
+ delete d->m_dst;
+ d->m_dst = 0;
+}
+
+void KoDocumentInfoPropsPage::copy( const QString &path, const KArchiveEntry *entry )
+{
+ kdDebug( 30003 ) << "copy " << entry->name() << endl;
+ if ( entry->isFile() )
+ {
+ const KTarFile *file = static_cast<const KTarFile *>( entry );
+ kdDebug( 30003 ) << "file :" << entry->name() << endl;
+ kdDebug( 30003 ) << "full path is: " << path << entry->name() << endl;
+ d->m_dst->writeFile( path + entry->name(), entry->user(), entry->group(),
+ file->size(),
+ file->data().data() );
+ }
+ else
+ {
+ const KTarDirectory *dir = static_cast<const KTarDirectory *>( entry );
+ kdDebug( 30003 ) << "dir : " << entry->name() << endl;
+ kdDebug( 30003 ) << "full path is: " << path << entry->name() << endl;
+
+ QString p = path + entry->name();
+ if ( p != "/" )
+ {
+ d->m_dst->writeDir( p, entry->user(), entry->group() );
+ p.append( "/" );
+ }
+
+ QStringList entries = dir->entries();
+ QStringList::ConstIterator it = entries.begin();
+ QStringList::ConstIterator end = entries.end();
+ for (; it != end; ++it )
+ copy( p, dir->entry( *it ) );
+ }
+}
+
+/* vim: sw=2 et
+ */
+
+#include "KoDocumentInfoDlg.moc"
diff --git a/lib/kofficecore/KoDocumentInfoDlg.h b/lib/kofficecore/KoDocumentInfoDlg.h
new file mode 100644
index 00000000..d3e1e9a3
--- /dev/null
+++ b/lib/kofficecore/KoDocumentInfoDlg.h
@@ -0,0 +1,85 @@
+/* This file is part of the KDE project
+ Copyright (c) 2000 Simon Hausmann <hausmann@kde.org>
+
+ $Id: KoDocumentInfoDlg.h 508787 2006-02-12 18:28:12Z ingwa $
+
+ 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 __koDocumentInfoDlg_h__
+#define __koDocumentInfoDlg_h__
+
+#include <kpropertiesdialog.h>
+#include <koffice_export.h>
+
+class KDialogBase;
+class KoDocumentInfo;
+class KoDocumentInfoAuthor;
+class KoDocumentInfoAbout;
+class KoDocumentInfoUserMetadata;
+class KArchiveEntry;
+
+class KOFFICECORE_EXPORT KoDocumentInfoDlg : public QObject
+{
+ Q_OBJECT
+public:
+ KoDocumentInfoDlg( KoDocumentInfo *docInfo, QWidget *parent = 0, const char *name = 0,
+ KDialogBase *dialog = 0 );
+ virtual ~KoDocumentInfoDlg();
+
+ int exec();
+ KDialogBase *dialog() const;
+
+ void save();
+
+signals:
+ void changed();
+
+private slots:
+ void loadFromKABC();
+ void deleteInfo();
+ void resetMetaData();
+
+private:
+ void addAuthorPage( KoDocumentInfoAuthor *authorInfo );
+ void addAboutPage( KoDocumentInfoAbout *aboutInfo );
+ void addUserMetadataPage( KoDocumentInfoUserMetadata *userMetadataInfo );
+
+ void save( KoDocumentInfoAuthor *authorInfo );
+ void save( KoDocumentInfoAbout *aboutInfo );
+ void save( KoDocumentInfoUserMetadata *userMetadataInfo );
+
+ class KoDocumentInfoDlgPrivate;
+ KoDocumentInfoDlgPrivate *d;
+};
+
+class KOFFICECORE_EXPORT KoDocumentInfoPropsPage : public KPropsDlgPlugin
+{
+ Q_OBJECT
+public:
+ KoDocumentInfoPropsPage( KPropertiesDialog *props, const char *name = 0,
+ const QStringList & = QStringList() );
+ virtual ~KoDocumentInfoPropsPage();
+
+ virtual void applyChanges();
+
+private:
+ void copy( const QString &path, const KArchiveEntry *entry );
+ class KoDocumentInfoPropsPagePrivate;
+ KoDocumentInfoPropsPagePrivate *d;
+};
+
+#endif
diff --git a/lib/kofficecore/KoDocument_p.h b/lib/kofficecore/KoDocument_p.h
new file mode 100644
index 00000000..1b21f69a
--- /dev/null
+++ b/lib/kofficecore/KoDocument_p.h
@@ -0,0 +1,41 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 David Faure <faure@kde.org>
+
+ 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.
+
+ 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 __koDocument_p_h__
+#define __koDocument_p_h__
+
+#include <kparts/browserextension.h>
+
+class KoDocument;
+
+/**
+ * Used in singleViewMode, when embedded into a browser
+ * @internal
+ */
+class KoBrowserExtension : public KParts::BrowserExtension
+{
+ Q_OBJECT
+public:
+ KoBrowserExtension( KoDocument * doc, const char * name = 0 );
+
+public slots:
+ /// Automatically detected by konqueror
+ void print();
+};
+
+#endif
diff --git a/lib/kofficecore/KoDom.cpp b/lib/kofficecore/KoDom.cpp
new file mode 100644
index 00000000..45b00acd
--- /dev/null
+++ b/lib/kofficecore/KoDom.cpp
@@ -0,0 +1,30 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 David Faure <faure@kde.org>
+
+ 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.
+*/
+
+#include "KoDom.h"
+
+QDomElement KoDom::namedItemNS( const QDomNode& node, const char* nsURI, const char* localName )
+{
+ QDomNode n = node.firstChild();
+ for ( ; !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isElement() && n.localName() == localName && n.namespaceURI() == nsURI )
+ return n.toElement();
+ }
+ return QDomElement();
+}
diff --git a/lib/kofficecore/KoDom.h b/lib/kofficecore/KoDom.h
new file mode 100644
index 00000000..4d95a42b
--- /dev/null
+++ b/lib/kofficecore/KoDom.h
@@ -0,0 +1,70 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 David Faure <faure@kde.org>
+
+ 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 KODOM_H
+#define KODOM_H
+
+#include <qdom.h>
+#include <koffice_export.h>
+/**
+ * This namespace contains a few convenience functions to simplify code using QDom
+ * (when loading OASIS documents, in particular).
+ *
+ * To find the child element with a given name, use KoDom::namedItemNS.
+ *
+ * To find all child elements with a given name, use
+ * QDomElement e;
+ * forEachElement( e, parent )
+ * {
+ * if ( e.localName() == "..." && e.namespaceURI() == KoXmlNS::... )
+ * {
+ * ...
+ * }
+ * }
+ * Note that this means you don't ever need to use QDomNode nor toElement anymore!
+ * Also note that localName is the part without the prefix, this is the whole point
+ * of namespace-aware methods.
+ *
+ * To find the attribute with a given name, use QDomElement::attributeNS.
+ *
+ * Do not use getElementsByTagNameNS, it's recursive (which is never needed in KOffice).
+ * Do not use tagName() or nodeName() or prefix(), since the prefix isn't fixed.
+ *
+ * @author David Faure <faure@kde.org>
+ */
+namespace KoDom {
+
+ /**
+ * A namespace-aware version of QDomNode::namedItem(),
+ * which also takes care of casting to a QDomElement.
+ * Use this when a domelement is known to have only *one* child element
+ * with a given tagname.
+ *
+ * Note: do *NOT* use getElementsByTagNameNS, it's recursive!
+ */
+ KOFFICECORE_EXPORT QDomElement namedItemNS( const QDomNode& node, const char* nsURI, const char* localName );
+
+}
+
+#define forEachElement( elem, parent ) \
+ for ( QDomNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling() ) \
+ if ( !( elem = _node.toElement() ).isNull() )
+
+#endif /* KODOM_H */
+
diff --git a/lib/kofficecore/KoFactory.cpp b/lib/kofficecore/KoFactory.cpp
new file mode 100644
index 00000000..d858e8e9
--- /dev/null
+++ b/lib/kofficecore/KoFactory.cpp
@@ -0,0 +1,45 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
+
+ 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.
+*/
+
+#include <KoFactory.h>
+
+
+class KoFactory::KoFactoryPrivate
+{
+public:
+ KoFactoryPrivate()
+ {
+ }
+ ~KoFactoryPrivate()
+ {
+ }
+};
+
+KoFactory::KoFactory( QObject *parent, const char *name )
+ : KParts::Factory( parent, name )
+{
+ //d = new KoFactoryPrivate;
+}
+
+KoFactory::~KoFactory()
+{
+ //delete d;
+}
+
+#include <KoFactory.moc>
diff --git a/lib/kofficecore/KoFactory.h b/lib/kofficecore/KoFactory.h
new file mode 100644
index 00000000..bcc77439
--- /dev/null
+++ b/lib/kofficecore/KoFactory.h
@@ -0,0 +1,39 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
+
+ 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 __koFactory_h__
+#define __koFactory_h__
+
+#include <kparts/factory.h>
+#include <koffice_export.h>
+class KInstance;
+
+class KOFFICECORE_EXPORT KoFactory : public KParts::Factory
+{
+ Q_OBJECT
+public:
+ KoFactory( QObject *parent = 0, const char *name = 0 );
+ virtual ~KoFactory();
+
+private:
+ class KoFactoryPrivate;
+ KoFactoryPrivate *d;
+};
+
+#endif
diff --git a/lib/kofficecore/KoFileDialog.cpp b/lib/kofficecore/KoFileDialog.cpp
new file mode 100644
index 00000000..b80c23e0
--- /dev/null
+++ b/lib/kofficecore/KoFileDialog.cpp
@@ -0,0 +1,125 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2005 David Faure <faure@kde.org>
+ Copyright (C) 2002-2004 Clarence Dang <dang@kde.org>
+
+ 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.
+*/
+
+#include "KoFileDialog.h"
+#include "KoDocument.h"
+#include <kfilefiltercombo.h>
+#include <klocale.h>
+#include <kdiroperator.h>
+#include <kdebug.h>
+
+KoFileDialog::KoFileDialog(const QString& startDir, const QString& filter,
+ QWidget *parent, const char *name,
+ bool modal)
+ : KFileDialog( startDir, filter, parent, name, modal )
+{
+ connect( filterWidget, SIGNAL( activated( int) ),
+ this, SLOT( slotChangedfilter( int ) ) );
+}
+
+void KoFileDialog::slotChangedfilter( int index )
+{
+ // Switch to "directory selection" mode for SaveAsDirectoryStore,
+ // switch back to "file selection" mode otherwise.
+ KFile::Mode newMode = KFile::File;
+ if ( index >= 1 && index <= (int)m_specialFormats.count()
+ && m_specialFormats[index-1] == KoDocument::SaveAsDirectoryStore ) {
+ newMode = KFile::Directory;
+ }
+ if ( newMode != mode() )
+ {
+ ops->setMode( newMode );
+ updateAutoSelectExtension();
+ }
+}
+
+void KoFileDialog::setSpecialMimeFilter( QStringList& mimeFilter,
+ const QString& currentFormat, const int specialOutputFlag,
+ const QString& nativeFormat,
+ int supportedSpecialFormats )
+{
+ Q_ASSERT( !mimeFilter.isEmpty() );
+ Q_ASSERT( mimeFilter[0] == nativeFormat );
+
+ bool addUncompressed = supportedSpecialFormats & KoDocument::SaveAsDirectoryStore;
+ bool addFlatXML = supportedSpecialFormats & KoDocument::SaveAsFlatXML;
+
+ int idxSpecialOutputFlag = 0;
+ int numSpecialEntries = 0;
+ if ( addUncompressed ) {
+ ++numSpecialEntries;
+ m_specialFormats.append( KoDocument::SaveAsDirectoryStore );
+ if ( specialOutputFlag == KoDocument::SaveAsDirectoryStore )
+ idxSpecialOutputFlag = numSpecialEntries;
+ }
+ if ( addFlatXML ) {
+ ++numSpecialEntries;
+ m_specialFormats.append( KoDocument::SaveAsFlatXML );
+ if ( specialOutputFlag == KoDocument::SaveAsFlatXML )
+ idxSpecialOutputFlag = numSpecialEntries;
+ }
+
+ // Insert numSpecialEntries entries with native mimetypes, for the special entries.
+ QStringList::Iterator mimeFilterIt = mimeFilter.at( 1 );
+ mimeFilter.insert( mimeFilterIt /* before 1 -> after 0 */, numSpecialEntries, nativeFormat );
+
+ // Fill in filter combo
+ // Note: if currentFormat doesn't exist in mimeFilter, filterWidget
+ // will default to the first item (native format)
+ setMimeFilter( mimeFilter, currentFormat.isEmpty() ? nativeFormat : currentFormat );
+
+ // To get a different description in the combo, we need to change its entries afterwards
+ KMimeType::Ptr type = KMimeType::mimeType( nativeFormat );
+ int idx = 1; // 0 is the native format
+
+ if ( addUncompressed )
+ filterWidget->changeItem( i18n("%1 (Uncompressed XML Files)").arg( type->comment() ), idx++ );
+ if ( addFlatXML )
+ filterWidget->changeItem( i18n("%1 (Flat XML File)").arg( type->comment() ), idx++ );
+ // if you add an entry here, update numSpecialEntries above and specialEntrySelected() below
+
+ // For native format...
+ if (currentFormat == nativeFormat || currentFormat.isEmpty())
+ {
+ // KFileFilterCombo selected the _last_ "native mimetype" entry, select the correct one
+ filterWidget->setCurrentItem( idxSpecialOutputFlag );
+ slotChangedfilter( filterWidget->currentItem() );
+ }
+ // [Mainly KWord] Tell MS Office users that they can save in RTF!
+ int i = 0;
+ for (mimeFilterIt = mimeFilter.begin (); mimeFilterIt != mimeFilter.end (); ++mimeFilterIt, i++)
+ {
+ KMimeType::Ptr mime = KMimeType::mimeType (*mimeFilterIt);
+ QString compatString = mime->property ("X-KDE-CompatibleApplication").toString ();
+ if (!compatString.isEmpty ())
+ filterWidget->changeItem (i18n ("%1 (%2 Compatible)").arg (mime->comment ()).arg (compatString), i);
+ }
+}
+
+int KoFileDialog::specialEntrySelected()
+{
+ int i = filterWidget->currentItem();
+ // Item 0 is the native format, the following ones are the special formats
+ if ( i >= 1 && i <= (int)m_specialFormats.count() )
+ return m_specialFormats[i-1];
+ return 0;
+}
+
+#include "KoFileDialog.moc"
diff --git a/lib/kofficecore/KoFileDialog.h b/lib/kofficecore/KoFileDialog.h
new file mode 100644
index 00000000..fc3b61e2
--- /dev/null
+++ b/lib/kofficecore/KoFileDialog.h
@@ -0,0 +1,55 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2004 David Faure <faure@kde.org>
+ Copyright (C) 2002-2004 Clarence Dang <dang@kde.org>
+
+ 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 KOFILEDIALOG_H
+#define KOFILEDIALOG_H
+
+#include <kfiledialog.h>
+#include <qvaluevector.h>
+
+/**
+ * Extension to KFileDialog in order to add special entries to the filter combo,
+ * like "save as koffice-1.1", "save as dir" etc.
+ * @note Used only when saving!
+ *
+ * This class is INTERNAL to KoMainWindow, do NOT use in your application.
+ * Binary compatibility is not guaranteed.
+ */
+class KoFileDialog : public KFileDialog
+{
+ Q_OBJECT
+public:
+ KoFileDialog( const QString& startDir, const QString& filter,
+ QWidget *parent, const char *name,
+ bool modal );
+ void setSpecialMimeFilter( QStringList& mimeFilter,
+ const QString& currentFormat, const int specialOutputFlag,
+ const QString& nativeFormat,
+ int supportedSpecialFormats );
+ int specialEntrySelected();
+
+private slots:
+ void slotChangedfilter( int index );
+
+private:
+ QValueVector<int> m_specialFormats;
+};
+
+#endif /* KOFILEDIALOG_H */
diff --git a/lib/kofficecore/KoFilter.cpp b/lib/kofficecore/KoFilter.cpp
new file mode 100644
index 00000000..5b1d4898
--- /dev/null
+++ b/lib/kofficecore/KoFilter.cpp
@@ -0,0 +1,151 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Werner Trobin <trobin@kde.org>
+ 2002 Werner Trobin <trobin@kde.org>
+
+ 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 <KoFilter.h>
+
+#include <qfile.h>
+
+#include <kurl.h>
+#include <kmimetype.h>
+#include <ktempfile.h>
+#include <kdebug.h>
+#include <KoFilterManager.h>
+
+
+KoFilter::KoFilter() : QObject( 0, 0 ), m_chain( 0 )
+{
+}
+
+KoFilter::~KoFilter()
+{
+}
+
+
+KoEmbeddingFilter::~KoEmbeddingFilter()
+{
+ if ( m_partStack.count() != 1 )
+ kdWarning() << "Someone messed with the part stack" << endl;
+ delete m_partStack.pop();
+}
+
+int KoEmbeddingFilter::lruPartIndex() const
+{
+ return m_partStack.top()->m_lruPartIndex;
+}
+
+QString KoEmbeddingFilter::mimeTypeByExtension( const QString& extension )
+{
+ // We need to resort to an ugly hack to determine the mimetype
+ // from the extension, as kservicetypefactory.h isn't installed
+ KURL url;
+ url.setPath( QString( "dummy.%1" ).arg( extension ) );
+ KMimeType::Ptr m( KMimeType::findByURL( url, 0, true, true ) );
+ return m->name();
+}
+
+KoEmbeddingFilter::KoEmbeddingFilter() : KoFilter()
+{
+ m_partStack.push( new PartState() );
+}
+
+int KoEmbeddingFilter::embedPart( const QCString& from, QCString& to,
+ KoFilter::ConversionStatus& status, const QString& key )
+{
+ ++( m_partStack.top()->m_lruPartIndex );
+
+ KTempFile tempIn;
+ tempIn.setAutoDelete( true );
+ savePartContents( tempIn.file() );
+ tempIn.file()->close();
+
+ KoFilterManager *manager = new KoFilterManager( tempIn.name(), from, m_chain );
+ status = manager->exp0rt( QString::null, to );
+ delete manager;
+
+ // Add the part to the current "stack frame", using the number as key
+ // if the key string is empty
+ PartReference ref( lruPartIndex(), to );
+ m_partStack.top()->m_partReferences.insert( key.isEmpty() ? QString::number( lruPartIndex() ) : key, ref );
+
+ return lruPartIndex();
+}
+
+void KoEmbeddingFilter::startInternalEmbedding( const QString& key, const QCString& mimeType )
+{
+ filterChainEnterDirectory( QString::number( ++( m_partStack.top()->m_lruPartIndex ) ) );
+ PartReference ref( lruPartIndex(), mimeType );
+ m_partStack.top()->m_partReferences.insert( key, ref );
+ m_partStack.push( new PartState() );
+}
+
+void KoEmbeddingFilter::endInternalEmbedding()
+{
+ if ( m_partStack.count() == 1 ) {
+ kdError( 30500 ) << "You're trying to endInternalEmbedding more often than you started it" << endl;
+ return;
+ }
+ delete m_partStack.pop();
+ filterChainLeaveDirectory();
+}
+
+int KoEmbeddingFilter::internalPartReference( const QString& key ) const
+{
+ QMapConstIterator<QString, PartReference> it = m_partStack.top()->m_partReferences.find( key );
+ if ( it == m_partStack.top()->m_partReferences.end() )
+ return -1;
+ return it.data().m_index;
+}
+
+QCString KoEmbeddingFilter::internalPartMimeType( const QString& key ) const
+{
+ QMapConstIterator<QString, PartReference> it = m_partStack.top()->m_partReferences.find( key );
+ if ( it == m_partStack.top()->m_partReferences.end() )
+ return QCString();
+ return it.data().m_mimeType;
+}
+
+KoEmbeddingFilter::PartReference::PartReference( int index, const QCString& mimeType ) :
+ m_index( index ), m_mimeType( mimeType )
+{
+}
+
+bool KoEmbeddingFilter::PartReference::isValid() const
+{
+ return m_index != 1 && !m_mimeType.isEmpty();
+}
+
+KoEmbeddingFilter::PartState::PartState() : m_lruPartIndex( 0 )
+{
+}
+
+void KoEmbeddingFilter::savePartContents( QIODevice* )
+{
+}
+
+void KoEmbeddingFilter::filterChainEnterDirectory( const QString& directory ) const
+{
+ m_chain->enterDirectory( directory );
+}
+
+void KoEmbeddingFilter::filterChainLeaveDirectory() const
+{
+ m_chain->leaveDirectory();
+}
+
+#include <KoFilter.moc>
diff --git a/lib/kofficecore/KoFilter.h b/lib/kofficecore/KoFilter.h
new file mode 100644
index 00000000..9d82a34f
--- /dev/null
+++ b/lib/kofficecore/KoFilter.h
@@ -0,0 +1,315 @@
+/* This file is part of the KOffice libraries
+ Copyright (C) 2001 Werner Trobin <trobin@kde.org>
+ 2002 Werner Trobin <trobin@kde.org>
+
+ 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 __koffice_filter_h__
+#define __koffice_filter_h__
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qptrstack.h>
+#include <koffice_export.h>
+class QIODevice;
+class KoFilterChain;
+
+/**
+ * @brief The base class for import and export filters.
+ *
+ * Derive your filter class from this base class and implement
+ * the @ref convert() method. Don't forget to specify the Q_OBJECT
+ * macro in your class even if you don't use signals or slots.
+ * This is needed as filters are created on the fly.
+ * The m_chain member allows access to the @ref KoFilterChain
+ * which invokes the filter to query for input/output.
+ *
+ * @note Take care: The m_chain pointer is invalid while the constructor
+ * runs due to the implementation -- @em don't use it in the constructor.
+ * After the constructor, when running the @ref convert() method it's
+ * guaranteed to be valid, so no need to check against 0.
+ *
+ * @author Werner Trobin <trobin@kde.org>
+ * @todo the class has no constructor and therefore cannot initialize its private class
+ */
+class KOFFICECORE_EXPORT KoFilter : public QObject
+{
+ Q_OBJECT
+
+ friend class KoFilterEntry; // needed for the filter chain pointer :(
+ friend class KoFilterChain;
+
+public:
+ /**
+ * This enum is used to signal the return state of your filter.
+ * Return OK in @ref convert() in case everything worked as expected.
+ * Feel free to add some more error conditions @em before the last item
+ * if it's needed.
+ */
+ enum ConversionStatus { OK, StupidError, UsageError, CreationError, FileNotFound,
+ StorageCreationError, BadMimeType, BadConversionGraph,
+ EmbeddedDocError, WrongFormat, NotImplemented,
+ ParsingError, InternalError, UnexpectedEOF,
+ UnexpectedOpcode, UserCancelled, OutOfMemory,
+ PasswordProtected,
+ JustInCaseSomeBrokenCompilerUsesLessThanAByte = 255 };
+
+ virtual ~KoFilter();
+
+ /**
+ * The filter chain calls this method to perform the actual conversion.
+ * The passed mimetypes should be a pair of those you specified in your
+ * .desktop file.
+ * You @em have to implement this method to make the filter work.
+ *
+ * @param from The mimetype of the source file/document
+ * @param to The mimetype of the destination file/document
+ * @return The error status, see the @ref #ConversionStatus enum.
+ * KoFilter::OK means that everything is alright.
+ */
+ virtual ConversionStatus convert( const QCString& from, const QCString& to ) = 0;
+
+signals:
+ /**
+ * Emit this signal with a value in the range of 1...100 to have some
+ * progress feedback for the user in the statusbar of the application.
+ *
+ * @param value The actual progress state. Should always remain in
+ * the range 1..100.
+ */
+ void sigProgress( int value );
+
+protected:
+ /**
+ * This is the constructor your filter has to call, obviously.
+ */
+ KoFilter();
+
+ /**
+ * Use this pointer to access all information about input/output
+ * during the conversion. @em Don't use it in the constructor -
+ * it's invalid while constructing the object!
+ */
+ KoFilterChain* m_chain;
+
+private:
+ KoFilter( const KoFilter& rhs );
+ KoFilter& operator=( const KoFilter& rhs );
+
+ class Private;
+ Private* d;
+};
+
+
+/**
+ * The base class for all @em import filters embedding other filters. Right
+ * now we don't support embedding for export filters, but if there's a
+ * request for that feature please don't hesitate to contact Werner Trobin
+ * <trobin@kde.org>.
+ *
+ * To make use of embedding features you have to know that there are two kinds
+ * of embedding for filters: embedding the output of a different filter (library)
+ * or embedding the output of several internal filters (no separate library).
+ * The first case is the simpler one. You just have to override savePartContents()
+ * and call @ref #embedPart to trigger the embedding process. One example for such
+ * a filter is Kontour's MSOD (MS Office Drawing) filter.
+ *
+ * The more complex case is embedding various streams from within the same filter
+ * library. This is neccesary for OLE like files (at least with the current design
+ * of the OLEFilter). In this case you have to use @ref #startInternalEmbedding and
+ * @ref #endInternalEmbedding accordingly. Try to use the previous method if possible.
+ *
+ * If you're using this class you can also setup a signal/slot communication
+ * between parent and child filter. To make that work you simply have to define
+ * signals and slots along the following rules:
+ * Signals should be named "commSignal\<name\>" where \<name\> is the name of the signal,
+ * slots should be named "commSlot\<name\>". The connection will be done automatically
+ * if names and signatures are matching.
+ *
+ * @author Werner Trobin
+ * @todo the class has no constructor and therefore cannot initialize its private class
+ */
+class KOFFICECORE_EXPORT KoEmbeddingFilter : public KoFilter
+{
+ Q_OBJECT
+
+ friend class KoFilterChain;
+
+public:
+ virtual ~KoEmbeddingFilter();
+
+ /**
+ * @internal
+ * This method returns the last recently used part index at the
+ * current directory level. It can be (and is ;) used to generate
+ * the per-directory-unique address for the next part we have to save.
+ * It will get updated automatically, you most likely don't have to
+ * care about that one at all.
+ */
+ int lruPartIndex() const;
+
+ /**
+ * A static helper method to determine the mimetype via the
+ * file extension. It allows to go from "wmf" to image/x-wmf
+ * and so on. Note that you should only pass the pure extension
+ * and not a whole pattern like "*.doc" or so.
+ */
+ static QString mimeTypeByExtension( const QString& extension );
+
+protected:
+ /**
+ * Constructs a filter. Note that the m_chain pointer is 0 inside
+ * the constructor. Most likely your constructor will be empty.
+ */
+ KoEmbeddingFilter();
+
+ /**
+ * Embed some document using an external filter (i.e. a different
+ * filter library). This method works according to the template method
+ * pattern and calls savePartContents() during execution.
+ * Call this method when you want to convert some data using one or more
+ * KOffice filters selected via the filter manager.
+ * This is the way to go when it comes to embedding unless you have very
+ * special requirements.
+ *
+ * @param from The mimetype of the source data
+ * @param to The mimetype of the destination part. If this field is set
+ * to "" the filter manager will try to find the best native
+ * KOffice mimetype. When the method returns this parameter will
+ * hold the string of the used mimetype.
+ * @param status Returns the error status of the filter
+ * @param key Optional key field to allow custom keys inside the part
+ * map (see @ref #internalPartReference). If this field is left
+ * empty we generate a key from the part number (e.g. 1 -> "1")
+ * @return The number of the part (can be used to refer to the part from
+ * within the embedding filter).
+ */
+ int embedPart( const QCString& from, QCString& to,
+ KoFilter::ConversionStatus& status,
+ const QString& key = QString::null );
+
+ /**
+ * Method to perform "internal" embedding of parts in olefilter-style.
+ * This method can be used to signal the start of a new embedding
+ * level within your filter. Very evil, but what shall I say ;)
+ * Unless you really have to you should always use @ref #embedPart as
+ * it's easier to use and not as hacky.
+ *
+ * @param key The key we use to store reference/mimetype of your new part
+ * @param mimeType The mimetype of the part you're about to embed
+ */
+ void startInternalEmbedding( const QString& key, const QCString& mimeType );
+
+ /**
+ * This method signals the end of an internal embedding session.
+ * You have to call that exactly as often as you call @ref #startInternalEmbedding
+ * or you'll mess up the internal stack and your file will be invalid.
+ * Again: use @ref #embedPart if you can :-)
+ */
+ void endInternalEmbedding();
+
+ /**
+ * Query the internal part map for the reference of the part
+ * matching the given key. Note that you can use that plain
+ * simple int to refer to the respective part (when used as string).
+ *
+ * @param key The key you would like to look up
+ * @return The reference or -1 if we didn't find a part with the
+ * given key
+ */
+ int internalPartReference( const QString& key ) const;
+
+ /**
+ * Query the internal part map for the mimetype of the part
+ * matching the given key.
+ *
+ * @param key The key you would like to look up
+ * @return The mimetype, might be empty if the part matching
+ * the given key doesn't exist.
+ */
+ QCString internalPartMimeType( const QString& key ) const;
+
+private:
+ /**
+ * Holds the directory's number and the mimetype of the part
+ * for internal parts. This is all we need to locate a part.
+ * @internal
+ */
+ struct PartReference
+ {
+ PartReference( int index = -1, const QCString& mimeType = "" );
+ bool isValid() const;
+
+ int m_index;
+ QCString m_mimeType;
+ };
+
+ /**
+ * This struct keeps track of the last used index for a
+ * child part and all references to existing children
+ * We use it to build a whole stack, one PartState per
+ * embedding level.
+ * @internal
+ */
+ struct PartState
+ {
+ PartState();
+
+ int m_lruPartIndex;
+ QMap<QString, PartReference> m_partReferences;
+ };
+
+ /// Better do not copy the filters
+ KoEmbeddingFilter( const KoEmbeddingFilter& rhs );
+ /// Better do not assign the filters
+ KoEmbeddingFilter& operator=( const KoEmbeddingFilter& rhs );
+
+ /**
+ * This method will be called by @ref #embedPart as soon as it
+ * needs the data of the part (template method pattern). You
+ * have to override that and simply save the part data to the
+ * (already opened) file.
+ * No need to override that when you're not using @ref #embedPart
+ * (as you should ;)
+ *
+ * @param file An already opened file
+ */
+ virtual void savePartContents( QIODevice* file );
+
+ /**
+ * Internal methods to support the start/endInternalEmbedding
+ * methods (we have to change directories and stuff).
+ * These methods are declared friends of the KoFilterChain
+ */
+ void filterChainEnterDirectory( const QString& directory ) const;
+ /**
+ * Internal methods to support the start/endInternalEmbedding
+ * methods (we have to change directories and stuff).
+ * These methods are declared friends of the KoFilterChain
+ */
+ void filterChainLeaveDirectory() const;
+
+ /**
+ * A stack which keeps track of the current part references.
+ * We push one PartState structure for every embedding level.
+ */
+ QPtrStack<PartState> m_partStack;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/lib/kofficecore/KoFilterChain.cpp b/lib/kofficecore/KoFilterChain.cpp
new file mode 100644
index 00000000..2ab53780
--- /dev/null
+++ b/lib/kofficecore/KoFilterChain.cpp
@@ -0,0 +1,936 @@
+/* This file is part of the KOffice libraries
+ Copyright (C) 2001 Werner Trobin <trobin@kde.org>
+
+ 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 <qmetaobject.h>
+#include <ktempfile.h>
+#include <kmimetype.h>
+#include <KoFilterChain.h>
+#include <KoQueryTrader.h>
+#include <KoFilterManager.h> // KoFilterManager::filterAvailable, private API
+#include <KoDocument.h>
+#include <kdebug.h>
+
+#include <priorityqueue.h>
+
+#include <limits.h> // UINT_MAX
+
+// Those "defines" are needed in the setupConnections method below.
+// Please always keep the strings and the length in sync!
+namespace {
+ const char* const SIGNAL_PREFIX = "commSignal";
+ const int SIGNAL_PREFIX_LEN = 10;
+ const char* const SLOT_PREFIX = "commSlot";
+ const int SLOT_PREFIX_LEN = 8;
+}
+
+
+KoFilterChain::ChainLink::ChainLink( KoFilterChain* chain, KoFilterEntry::Ptr filterEntry,
+ const QCString& from, const QCString& to ) :
+ m_chain( chain ), m_filterEntry( filterEntry ), m_from( from ), m_to( to ),
+ m_filter( 0 ), d( 0 )
+{
+}
+
+KoFilter::ConversionStatus KoFilterChain::ChainLink::invokeFilter( const ChainLink* const parentChainLink )
+{
+ if ( !m_filterEntry ) {
+ kdError( 30500 ) << "This filter entry is null. Strange stuff going on." << endl;
+ return KoFilter::CreationError;
+ }
+
+ m_filter = m_filterEntry->createFilter( m_chain, 0, 0 );
+
+ if ( !m_filter ) {
+ kdError( 30500 ) << "Couldn't create the filter." << endl;
+ return KoFilter::CreationError;
+ }
+
+ if ( parentChainLink )
+ setupCommunication( parentChainLink->m_filter );
+
+ KoFilter::ConversionStatus status = m_filter->convert( m_from, m_to );
+ delete m_filter;
+ m_filter=0;
+ return status;
+}
+
+void KoFilterChain::ChainLink::dump() const
+{
+ kdDebug( 30500 ) << " Link: " << m_filterEntry->service()->name() << endl;
+}
+
+int KoFilterChain::ChainLink::lruPartIndex() const
+{
+ if ( m_filter && m_filter->inherits( "KoEmbeddingFilter" ) )
+ return static_cast<KoEmbeddingFilter*>( m_filter )->lruPartIndex();
+ return -1;
+}
+
+void KoFilterChain::ChainLink::setupCommunication( const KoFilter* const parentFilter ) const
+{
+ // progress information
+ QObject::connect( m_filter, SIGNAL( sigProgress( int ) ),
+ m_chain->manager(), SIGNAL( sigProgress( int ) ) );
+
+ if ( !parentFilter )
+ return;
+
+ const QMetaObject* const parent = parentFilter->metaObject();
+ const QMetaObject* const child = m_filter->metaObject();
+ if ( !parent || !child )
+ return;
+
+ setupConnections( parentFilter, parent->signalNames(), m_filter, child->slotNames() );
+ setupConnections( m_filter, child->signalNames(), parentFilter, parent->slotNames() );
+}
+
+void KoFilterChain::ChainLink::setupConnections( const KoFilter* sender, const QStrList& sigs,
+ const KoFilter* receiver, const QStrList& sl0ts ) const
+{
+ QStrListIterator signalIt( sigs );
+ for ( ; signalIt.current(); ++signalIt ) {
+ if ( strncmp( signalIt.current(), SIGNAL_PREFIX, SIGNAL_PREFIX_LEN ) == 0 ) {
+ QStrListIterator slotIt( sl0ts );
+ for ( ; slotIt.current(); ++slotIt ) {
+ if ( strncmp( slotIt.current(), SLOT_PREFIX, SLOT_PREFIX_LEN ) == 0 ) {
+ if ( strcmp( signalIt.current() + SIGNAL_PREFIX_LEN, slotIt.current() + SLOT_PREFIX_LEN ) == 0 ) {
+ QCString signalString;
+ signalString.setNum( QSIGNAL_CODE );
+ signalString += signalIt.current();
+ QCString slotString;
+ slotString.setNum( QSLOT_CODE );
+ slotString += slotIt.current();
+ QObject::connect( sender, signalString, receiver, slotString );
+ }
+ }
+ }
+ }
+ }
+}
+
+
+KoFilterChain::~KoFilterChain()
+{
+ if ( filterManagerParentChain() && filterManagerParentChain()->m_outputStorage )
+ filterManagerParentChain()->m_outputStorage->leaveDirectory();
+ manageIO(); // Called for the 2nd time in a row -> clean up
+}
+
+KoFilter::ConversionStatus KoFilterChain::invokeChain()
+{
+ KoFilter::ConversionStatus status = KoFilter::OK;
+
+ m_state = Beginning;
+ int count = m_chainLinks.count();
+
+ // This is needed due to nasty Microsoft design
+ const ChainLink* parentChainLink = 0;
+ if ( filterManagerParentChain() )
+ parentChainLink = filterManagerParentChain()->m_chainLinks.current();
+
+ // No iterator here, as we need m_chainLinks.current() in outputDocument()
+ m_chainLinks.first();
+ for ( ; count > 1 && m_chainLinks.current() && status == KoFilter::OK;
+ m_chainLinks.next(), --count ) {
+ status = m_chainLinks.current()->invokeFilter( parentChainLink );
+ m_state = Middle;
+ manageIO();
+ }
+
+ if ( !m_chainLinks.current() ) {
+ kdWarning( 30500 ) << "Huh?? Found a null pointer in the chain" << endl;
+ return KoFilter::StupidError;
+ }
+
+ if ( status == KoFilter::OK ) {
+ if ( m_state & Beginning )
+ m_state |= End;
+ else
+ m_state = End;
+ status = m_chainLinks.current()->invokeFilter( parentChainLink );
+ manageIO();
+ }
+
+ m_state = Done;
+ if (status == KoFilter::OK)
+ finalizeIO();
+ return status;
+}
+
+QString KoFilterChain::chainOutput() const
+{
+ if ( m_state == Done )
+ return m_inputFile; // as we already called manageIO()
+ return QString::null;
+}
+
+QString KoFilterChain::inputFile()
+{
+ if ( m_inputQueried == File )
+ return m_inputFile;
+ else if ( m_inputQueried != Nil ) {
+ kdWarning( 30500 ) << "You already asked for some different source." << endl;
+ return QString::null;
+ }
+ m_inputQueried = File;
+
+ if ( m_state & Beginning ) {
+ if ( static_cast<KoFilterManager::Direction>( filterManagerDirection() ) ==
+ KoFilterManager::Import )
+ m_inputFile = filterManagerImportFile();
+ else
+ inputFileHelper( filterManagerKoDocument(), filterManagerImportFile() );
+ }
+ else
+ if ( m_inputFile.isEmpty() )
+ inputFileHelper( m_inputDocument, QString::null );
+
+ return m_inputFile;
+}
+
+QString KoFilterChain::outputFile()
+{
+ // sanity check: No embedded filter should ask for a plain file
+ // ###### CHECK: This will break as soon as we support exporting embedding filters
+ if ( filterManagerParentChain() )
+ kdWarning( 30500 )<< "An embedded filter has to use storageFile()!" << endl;
+
+ if ( m_outputQueried == File )
+ return m_outputFile;
+ else if ( m_outputQueried != Nil ) {
+ kdWarning( 30500 ) << "You already asked for some different destination." << endl;
+ return QString::null;
+ }
+ m_outputQueried = File;
+
+ if ( m_state & End ) {
+ if ( static_cast<KoFilterManager::Direction>( filterManagerDirection() ) ==
+ KoFilterManager::Import )
+ outputFileHelper( false ); // This (last) one gets deleted by the caller
+ else
+ m_outputFile = filterManagerExportFile();
+ }
+ else
+ outputFileHelper( true );
+
+ return m_outputFile;
+}
+
+KoStoreDevice* KoFilterChain::storageFile( const QString& name, KoStore::Mode mode )
+{
+ // ###### CHECK: This works only for import filters. Do we want something like
+ // that for export filters too?
+ if ( m_outputQueried == Nil && mode == KoStore::Write && filterManagerParentChain() )
+ return storageInitEmbedding( name );
+
+ // Plain normal use case
+ if ( m_inputQueried == Storage && mode == KoStore::Read &&
+ m_inputStorage && m_inputStorage->mode() == KoStore::Read )
+ return storageNewStreamHelper( &m_inputStorage, &m_inputStorageDevice, name );
+ else if ( m_outputQueried == Storage && mode == KoStore::Write &&
+ m_outputStorage && m_outputStorage->mode() == KoStore::Write )
+ return storageNewStreamHelper( &m_outputStorage, &m_outputStorageDevice, name );
+ else if ( m_inputQueried == Nil && mode == KoStore::Read )
+ return storageHelper( inputFile(), name, KoStore::Read,
+ &m_inputStorage, &m_inputStorageDevice );
+ else if ( m_outputQueried == Nil && mode == KoStore::Write )
+ return storageHelper( outputFile(), name, KoStore::Write,
+ &m_outputStorage, &m_outputStorageDevice );
+ else {
+ kdWarning( 30500 ) << "Oooops, how did we get here? You already asked for a"
+ << " different source/destination?" << endl;
+ return 0;
+ }
+}
+
+KoDocument* KoFilterChain::inputDocument()
+{
+ if ( m_inputQueried == Document )
+ return m_inputDocument;
+ else if ( m_inputQueried != Nil ) {
+ kdWarning( 30500 ) << "You already asked for some different source." << endl;
+ return 0;
+ }
+
+ if ( ( m_state & Beginning ) &&
+ static_cast<KoFilterManager::Direction>( filterManagerDirection() ) == KoFilterManager::Export &&
+ filterManagerKoDocument() )
+ m_inputDocument = filterManagerKoDocument();
+ else if ( !m_inputDocument )
+ m_inputDocument = createDocument( inputFile() );
+
+ m_inputQueried = Document;
+ return m_inputDocument;
+}
+
+KoDocument* KoFilterChain::outputDocument()
+{
+ // sanity check: No embedded filter should ask for a document
+ // ###### CHECK: This will break as soon as we support exporting embedding filters
+ if ( filterManagerParentChain() ) {
+ kdWarning( 30500 )<< "An embedded filter has to use storageFile()!" << endl;
+ return 0;
+ }
+
+ if ( m_outputQueried == Document )
+ return m_outputDocument;
+ else if ( m_outputQueried != Nil ) {
+ kdWarning( 30500 ) << "You already asked for some different destination." << endl;
+ return 0;
+ }
+
+ if ( ( m_state & End ) &&
+ static_cast<KoFilterManager::Direction>( filterManagerDirection() ) == KoFilterManager::Import &&
+ filterManagerKoDocument() )
+ m_outputDocument = filterManagerKoDocument();
+ else
+ m_outputDocument = createDocument( m_chainLinks.current()->to() );
+
+ m_outputQueried = Document;
+ return m_outputDocument;
+}
+
+void KoFilterChain::dump() const
+{
+ kdDebug( 30500 ) << "########## KoFilterChain with " << m_chainLinks.count() << " members:" << endl;
+ QPtrListIterator<ChainLink> it( m_chainLinks );
+ for ( ; it.current(); ++it )
+ it.current()->dump();
+ kdDebug( 30500 ) << "########## KoFilterChain (done) ##########" << endl;
+}
+
+KoFilterChain::KoFilterChain( const KoFilterManager* manager ) :
+ m_manager( manager ), m_state( Beginning ), m_inputStorage( 0 ),
+ m_inputStorageDevice( 0 ), m_outputStorage( 0 ), m_outputStorageDevice( 0 ),
+ m_inputDocument( 0 ), m_outputDocument( 0 ), m_inputTempFile( 0 ),
+ m_outputTempFile( 0 ), m_inputQueried( Nil ), m_outputQueried( Nil ), d( 0 )
+{
+ // We "own" our chain links, the filter entries are implicitly shared
+ m_chainLinks.setAutoDelete( true );
+}
+
+void KoFilterChain::appendChainLink( KoFilterEntry::Ptr filterEntry, const QCString& from, const QCString& to )
+{
+ m_chainLinks.append( new ChainLink( this, filterEntry, from, to ) );
+}
+
+void KoFilterChain::prependChainLink( KoFilterEntry::Ptr filterEntry, const QCString& from, const QCString& to )
+{
+ m_chainLinks.prepend( new ChainLink( this, filterEntry, from, to ) );
+}
+
+void KoFilterChain::enterDirectory( const QString& directory )
+{
+ // Only a little bit of checking as we (have to :} ) trust KoEmbeddingFilter
+ // If the output storage isn't initialized yet, we perform that step(s) on init.
+ if ( m_outputStorage )
+ m_outputStorage->enterDirectory( directory );
+ m_internalEmbeddingDirectories.append( directory );
+}
+
+void KoFilterChain::leaveDirectory()
+{
+ if ( m_outputStorage )
+ m_outputStorage->leaveDirectory();
+ if ( !m_internalEmbeddingDirectories.isEmpty() )
+ m_internalEmbeddingDirectories.pop_back();
+}
+
+QString KoFilterChain::filterManagerImportFile() const
+{
+ return m_manager->importFile();
+}
+
+QString KoFilterChain::filterManagerExportFile() const
+{
+ return m_manager->exportFile();
+}
+
+KoDocument* KoFilterChain::filterManagerKoDocument() const
+{
+ return m_manager->document();
+}
+
+int KoFilterChain::filterManagerDirection() const
+{
+ return m_manager->direction();
+}
+
+KoFilterChain* const KoFilterChain::filterManagerParentChain() const
+{
+ return m_manager->parentChain();
+}
+
+void KoFilterChain::manageIO()
+{
+ m_inputQueried = Nil;
+ m_outputQueried = Nil;
+
+ delete m_inputStorageDevice;
+ m_inputStorageDevice = 0;
+ if ( m_inputStorage ) {
+ m_inputStorage->close();
+ delete m_inputStorage;
+ m_inputStorage = 0;
+ }
+ if ( m_inputTempFile ) {
+ m_inputTempFile->close();
+ delete m_inputTempFile; // autodelete
+ m_inputTempFile = 0;
+ }
+ m_inputFile = QString::null;
+
+ if ( !m_outputFile.isEmpty() ) {
+ m_inputFile = m_outputFile;
+ m_outputFile = QString::null;
+ m_inputTempFile = m_outputTempFile;
+ m_outputTempFile = 0;
+
+ delete m_outputStorageDevice;
+ m_outputStorageDevice = 0;
+ if ( m_outputStorage ) {
+ m_outputStorage->close();
+ // Don't delete the storage if we're just pointing to the
+ // storage of the parent filter chain
+ if ( !filterManagerParentChain() || m_outputStorage->mode() != KoStore::Write )
+ delete m_outputStorage;
+ m_outputStorage = 0;
+ }
+ }
+
+ if ( m_inputDocument != filterManagerKoDocument() )
+ delete m_inputDocument;
+ m_inputDocument = m_outputDocument;
+ m_outputDocument = 0;
+}
+
+void KoFilterChain::finalizeIO()
+{
+ // In case we export (to a file, of course) and the last
+ // filter chose to output a KoDocument we have to save it.
+ // Should be very rare, but well...
+ // Note: m_*input*Document as we already called manageIO()
+ if ( m_inputDocument &&
+ static_cast<KoFilterManager::Direction>( filterManagerDirection() ) == KoFilterManager::Export ) {
+ kdDebug( 30500 ) << "Saving the output document to the export file" << endl;
+ m_inputDocument->saveNativeFormat( filterManagerExportFile() );
+ m_inputFile = filterManagerExportFile();
+ }
+}
+
+bool KoFilterChain::createTempFile( KTempFile** tempFile, bool autoDelete )
+{
+ if ( *tempFile ) {
+ kdError( 30500 ) << "Ooops, why is there already a temp file???" << endl;
+ return false;
+ }
+ *tempFile = new KTempFile();
+ ( *tempFile )->setAutoDelete( autoDelete );
+ return ( *tempFile )->status() == 0;
+}
+
+void KoFilterChain::inputFileHelper( KoDocument* document, const QString& alternativeFile )
+{
+ if ( document ) {
+ if ( !createTempFile( &m_inputTempFile ) ) {
+ delete m_inputTempFile;
+ m_inputTempFile = 0;
+ m_inputFile = QString::null;
+ return;
+ }
+ if ( !document->saveNativeFormat( m_inputTempFile->name() ) ) {
+ delete m_inputTempFile;
+ m_inputTempFile = 0;
+ m_inputFile = QString::null;
+ return;
+ }
+ m_inputFile = m_inputTempFile->name();
+ }
+ else
+ m_inputFile = alternativeFile;
+}
+
+void KoFilterChain::outputFileHelper( bool autoDelete )
+{
+ if ( !createTempFile( &m_outputTempFile, autoDelete ) ) {
+ delete m_outputTempFile;
+ m_outputTempFile = 0;
+ m_outputFile = QString::null;
+ }
+ else
+ m_outputFile = m_outputTempFile->name();
+}
+
+KoStoreDevice* KoFilterChain::storageNewStreamHelper( KoStore** storage, KoStoreDevice** device,
+ const QString& name )
+{
+ delete *device;
+ *device = 0;
+ if ( ( *storage )->isOpen() )
+ ( *storage )->close();
+ if ( ( *storage )->bad() )
+ return storageCleanupHelper( storage );
+ if ( !( *storage )->open( name ) )
+ return 0;
+
+ *device = new KoStoreDevice( *storage );
+ return *device;
+}
+
+KoStoreDevice* KoFilterChain::storageHelper( const QString& file, const QString& streamName,
+ KoStore::Mode mode, KoStore** storage,
+ KoStoreDevice** device )
+{
+ if ( file.isEmpty() )
+ return 0;
+ if ( *storage ) {
+ kdDebug( 30500 ) << "Uh-oh, we forgot to clean up..." << endl;
+ return 0;
+ }
+
+ storageInit( file, mode, storage );
+
+ if ( ( *storage )->bad() )
+ return storageCleanupHelper( storage );
+
+ // Seems that we got a valid storage, at least. Even if we can't open
+ // the stream the "user" asked us to open, we nontheless change the
+ // IOState from File to Storage, as it might be possible to open other streams
+ if ( mode == KoStore::Read )
+ m_inputQueried = Storage;
+ else // KoStore::Write
+ m_outputQueried = Storage;
+
+ return storageCreateFirstStream( streamName, storage, device );
+}
+
+void KoFilterChain::storageInit( const QString& file, KoStore::Mode mode, KoStore** storage )
+{
+ QCString appIdentification( "" );
+ if ( mode == KoStore::Write ) {
+ // To create valid storages we also have to add the mimetype
+ // magic "applicationIndentifier" to the storage.
+ // As only filters with a KOffice destination should query
+ // for a storage to write to, we don't check the content of
+ // the mimetype here. It doesn't do a lot of harm if someome
+ // "abuses" this method.
+ appIdentification = m_chainLinks.current()->to();
+ }
+ *storage = KoStore::createStore( file, mode, appIdentification );
+}
+
+KoStoreDevice* KoFilterChain::storageInitEmbedding( const QString& name )
+{
+ if ( m_outputStorage ) {
+ kdWarning( 30500 ) << "Ooops! Something's really screwed here." << endl;
+ return 0;
+ }
+
+ m_outputStorage = filterManagerParentChain()->m_outputStorage;
+
+ if ( !m_outputStorage ) {
+ // If the storage of the parent hasn't been initialized yet,
+ // we have to do that here. Quite nasty...
+ storageInit( filterManagerParentChain()->outputFile(), KoStore::Write, &m_outputStorage );
+
+ // transfer the ownership
+ filterManagerParentChain()->m_outputStorage = m_outputStorage;
+ filterManagerParentChain()->m_outputQueried = Storage;
+ }
+
+ if ( m_outputStorage->isOpen() )
+ m_outputStorage->close(); // to be on the safe side, should never happen
+ if ( m_outputStorage->bad() )
+ return storageCleanupHelper( &m_outputStorage );
+
+ m_outputQueried = Storage;
+
+ // Now that we have a storage we have to change the directory
+ // and remember it for later!
+ const int lruPartIndex = filterManagerParentChain()->m_chainLinks.current()->lruPartIndex();
+ if ( lruPartIndex == -1 ) {
+ kdError( 30500 ) << "Huh! You want to use embedding features w/o inheriting KoEmbeddingFilter?" << endl;
+ return storageCleanupHelper( &m_outputStorage );
+ }
+
+ if ( !m_outputStorage->enterDirectory( QString( "part%1" ).arg( lruPartIndex ) ) )
+ return storageCleanupHelper( &m_outputStorage );
+
+ return storageCreateFirstStream( name, &m_outputStorage, &m_outputStorageDevice );
+}
+
+KoStoreDevice* KoFilterChain::storageCreateFirstStream( const QString& streamName, KoStore** storage,
+ KoStoreDevice** device )
+{
+ // Before we go and create the first stream in this storage we
+ // have to perform a little hack in case we're used by any ole-style
+ // filter which utilizes internal embedding. Ugly, but well...
+ if ( !m_internalEmbeddingDirectories.isEmpty() ) {
+ QStringList::ConstIterator it = m_internalEmbeddingDirectories.begin();
+ QStringList::ConstIterator end = m_internalEmbeddingDirectories.end();
+ for ( ; it != end && ( *storage )->enterDirectory( *it ); ++it );
+ }
+
+ if ( !( *storage )->open( streamName ) )
+ return 0;
+
+ if ( *device ) {
+ kdDebug( 30500 ) << "Uh-oh, we forgot to clean up the storage device!" << endl;
+ ( *storage )->close();
+ return storageCleanupHelper( storage );
+ }
+ *device = new KoStoreDevice( *storage );
+ return *device;
+}
+
+KoStoreDevice* KoFilterChain::storageCleanupHelper( KoStore** storage )
+{
+ // Take care not to delete the storage of the parent chain
+ if ( *storage != m_outputStorage || !filterManagerParentChain() ||
+ ( *storage )->mode() != KoStore::Write )
+ delete *storage;
+ *storage = 0;
+ return 0;
+}
+
+KoDocument* KoFilterChain::createDocument( const QString& file )
+{
+ KURL url;
+ url.setPath( file );
+ KMimeType::Ptr t = KMimeType::findByURL( url, 0, true );
+ if ( t->name() == KMimeType::defaultMimeType() ) {
+ kdError( 30500 ) << "No mimetype found for " << file << endl;
+ return 0;
+ }
+
+ KoDocument *doc = createDocument( QCString( t->name().latin1() ) );
+
+ if ( !doc || !doc->loadNativeFormat( file ) ) {
+ kdError( 30500 ) << "Couldn't load from the file" << endl;
+ delete doc;
+ return 0;
+ }
+ return doc;
+}
+
+KoDocument* KoFilterChain::createDocument( const QCString& mimeType )
+{
+ KoDocumentEntry entry = KoDocumentEntry::queryByMimeType(mimeType);
+
+ if (entry.isEmpty())
+ {
+ kdError( 30500 ) << "Couldn't find a part that can handle mimetype " << mimeType << endl;
+ }
+
+ KoDocument* doc = entry.createDoc(); /*entries.first().createDoc();*/
+ if ( !doc ) {
+ kdError( 30500 ) << "Couldn't create the document" << endl;
+ return 0;
+ }
+ return doc;
+}
+
+
+namespace KOffice {
+
+ Edge::Edge( Vertex* vertex, KoFilterEntry::Ptr filterEntry ) :
+ m_vertex( vertex ), m_filterEntry( filterEntry ), d( 0 )
+ {
+ }
+
+ void Edge::relax( const Vertex* predecessor, PriorityQueue<Vertex>& queue )
+ {
+ if ( !m_vertex || !predecessor || !m_filterEntry )
+ return;
+ if ( m_vertex->setKey( predecessor->key() + m_filterEntry->weight ) ) {
+ queue.keyDecreased( m_vertex ); // maintain the heap property
+ m_vertex->setPredecessor( predecessor );
+ }
+ }
+
+ void Edge::dump( const QCString& indent ) const
+ {
+ if ( m_vertex )
+ kdDebug( 30500 ) << indent << "Edge -> '" << m_vertex->mimeType()
+ << "' (" << m_filterEntry->weight << ")" << endl;
+ else
+ kdDebug( 30500 ) << indent << "Edge -> '(null)' ("
+ << m_filterEntry->weight << ")" << endl;
+ }
+
+
+ Vertex::Vertex( const QCString& mimeType ) : m_predecessor( 0 ), m_mimeType( mimeType ),
+ m_weight( UINT_MAX ), m_index( -1 ), d( 0 )
+ {
+ m_edges.setAutoDelete( true ); // we take ownership of added edges
+ }
+
+ bool Vertex::setKey( unsigned int key )
+ {
+ if ( m_weight > key ) {
+ m_weight = key;
+ return true;
+ }
+ return false;
+ }
+
+ void Vertex::reset()
+ {
+ m_weight = UINT_MAX;
+ m_predecessor = 0;
+ }
+
+ void Vertex::addEdge( const Edge* edge )
+ {
+ if ( !edge || edge->weight() == 0 )
+ return;
+ m_edges.append( edge );
+ }
+
+ const Edge* Vertex::findEdge( const Vertex* vertex ) const
+ {
+ if ( !vertex )
+ return 0;
+ const Edge* edge = 0;
+ QPtrListIterator<Edge> it( m_edges );
+
+ for ( ; it.current(); ++it ) {
+ if ( it.current()->vertex() == vertex &&
+ ( !edge || it.current()->weight() < edge->weight() ) )
+ edge = it.current();
+ }
+ return edge;
+ }
+
+ void Vertex::relaxVertices( PriorityQueue<Vertex>& queue )
+ {
+ for ( Edge *e = m_edges.first(); e; e = m_edges.next() )
+ e->relax( this, queue );
+ }
+
+ void Vertex::dump( const QCString& indent ) const
+ {
+ kdDebug( 30500 ) << indent << "Vertex: " << m_mimeType << " (" << m_weight << "):" << endl;
+ const QCString i( indent + " " );
+ QPtrListIterator<Edge> it( m_edges );
+ for ( ; it.current(); ++it )
+ it.current()->dump( i );
+ }
+
+
+ Graph::Graph( const QCString& from ) : m_vertices( 47 ), m_from( from ),
+ m_graphValid( false ), d( 0 )
+ {
+ m_vertices.setAutoDelete( true );
+ buildGraph();
+ shortestPaths(); // Will return after a single lookup if "from" is invalid (->no check here)
+ }
+
+ void Graph::setSourceMimeType( const QCString& from )
+ {
+ if ( from == m_from )
+ return;
+ m_from = from;
+ m_graphValid = false;
+
+ // Initialize with "infinity" ...
+ QAsciiDictIterator<Vertex> it( m_vertices );
+ for ( ; it.current(); ++it )
+ it.current()->reset();
+
+ // ...and re-run the shortest path search for the new source mime
+ shortestPaths();
+ }
+
+ KoFilterChain::Ptr Graph::chain( const KoFilterManager* manager, QCString& to ) const
+ {
+ if ( !isValid() || !manager )
+ return 0;
+
+ if ( to.isEmpty() ) { // if the destination is empty we search the closest KOffice part
+ to = findKOfficePart();
+ if ( to.isEmpty() ) // still empty? strange stuff...
+ return 0;
+ }
+
+ const Vertex* vertex = m_vertices[ to ];
+ if ( !vertex || vertex->key() == UINT_MAX )
+ return 0;
+
+ KoFilterChain::Ptr ret = new KoFilterChain( manager );
+
+ // Fill the filter chain with all filters on the path
+ const Vertex* tmp = vertex->predecessor();
+ while ( tmp ) {
+ const Edge* const edge = tmp->findEdge( vertex );
+ Q_ASSERT( edge );
+ ret->prependChainLink( edge->filterEntry(), tmp->mimeType(), vertex->mimeType() );
+ vertex = tmp;
+ tmp = tmp->predecessor();
+ }
+ return ret;
+ }
+
+ void Graph::dump() const
+ {
+ kdDebug( 30500 ) << "+++++++++ Graph::dump +++++++++" << endl;
+ kdDebug( 30500 ) << "From: " << m_from << endl;
+ QAsciiDictIterator<Vertex> it( m_vertices );
+ for ( ; it.current(); ++it )
+ it.current()->dump( " " );
+ kdDebug( 30500 ) << "+++++++++ Graph::dump (done) +++++++++" << endl;
+ }
+
+ // Query the trader and create the vertices and edges representing
+ // available mime types and filters.
+ void Graph::buildGraph()
+ {
+ // Make sure that all available parts are added to the graph
+ QValueList<KoDocumentEntry> parts( KoDocumentEntry::query() );
+ QValueList<KoDocumentEntry>::ConstIterator partIt( parts.begin() );
+ QValueList<KoDocumentEntry>::ConstIterator partEnd( parts.end() );
+
+ while ( partIt != partEnd ) {
+ QStringList nativeMimeTypes = ( *partIt ).service()->property( "X-KDE-ExtraNativeMimeTypes" ).toStringList();
+ nativeMimeTypes += ( *partIt ).service()->property( "X-KDE-NativeMimeType" ).toString();
+ QStringList::ConstIterator it = nativeMimeTypes.begin();
+ QStringList::ConstIterator end = nativeMimeTypes.end();
+ for ( ; it != end; ++it )
+ if ( !(*it).isEmpty() )
+ m_vertices.insert( (*it).latin1(), new Vertex( (*it).latin1() ) );
+ ++partIt;
+ }
+
+ QValueList<KoFilterEntry::Ptr> filters( KoFilterEntry::query() ); // no constraint here - we want *all* :)
+ QValueList<KoFilterEntry::Ptr>::ConstIterator it = filters.begin();
+ QValueList<KoFilterEntry::Ptr>::ConstIterator end = filters.end();
+
+ for ( ; it != end; ++it ) {
+ // First add the "starting points" to the dict
+ QStringList::ConstIterator importIt = ( *it )->import.begin();
+ QStringList::ConstIterator importEnd = ( *it )->import.end();
+ for ( ; importIt != importEnd; ++importIt ) {
+ const QCString key = ( *importIt ).latin1(); // latin1 is okay here (werner)
+ // already there?
+ if ( !m_vertices[ key ] )
+ m_vertices.insert( key, new Vertex( key ) );
+ }
+
+ // Are we allowed to use this filter at all?
+ if ( KoFilterManager::filterAvailable( *it ) ) {
+ QStringList::ConstIterator exportIt = ( *it )->export_.begin();
+ QStringList::ConstIterator exportEnd = ( *it )->export_.end();
+
+ for ( ; exportIt != exportEnd; ++exportIt ) {
+ // First make sure the export vertex is in place
+ const QCString key = ( *exportIt ).latin1(); // latin1 is okay here
+ Vertex* exp = m_vertices[ key ];
+ if ( !exp ) {
+ exp = new Vertex( key );
+ m_vertices.insert( key, exp );
+ }
+ // Then create the appropriate edges
+ importIt = ( *it )->import.begin();
+ for ( ; importIt != importEnd; ++importIt )
+ m_vertices[ ( *importIt ).latin1() ]->addEdge( new Edge( exp, *it ) );
+ }
+ }
+ else
+ kdDebug( 30500 ) << "Filter: " << ( *it )->service()->name() << " doesn't apply." << endl;
+ }
+ }
+
+ // As all edges (=filters) are required to have a positive weight
+ // we can use Dijkstra's shortest path algorithm from Cormen's
+ // "Introduction to Algorithms" (p. 527)
+ // Note: I did some adaptions as our data structures are slightly
+ // different from the ones used in the book. Further we simply stop
+ // the algorithm is we don't find any node with a weight != Infinity
+ // (==UINT_MAX), as this means that the remaining nodes in the queue
+ // aren't connected anyway.
+ void Graph::shortestPaths()
+ {
+ // Is the requested start mime type valid?
+ Vertex* from = m_vertices[ m_from ];
+ if ( !from )
+ return;
+
+ // Inititalize start vertex
+ from->setKey( 0 );
+
+ // Fill the priority queue with all the vertices
+ PriorityQueue<Vertex> queue( m_vertices );
+
+ while ( !queue.isEmpty() ) {
+ Vertex *min = queue.extractMinimum();
+ // Did we already relax all connected vertices?
+ if ( min->key() == UINT_MAX )
+ break;
+ min->relaxVertices( queue );
+ }
+ m_graphValid = true;
+ }
+
+ QCString Graph::findKOfficePart() const
+ {
+ // Here we simply try to find the closest KOffice mimetype
+ QValueList<KoDocumentEntry> parts( KoDocumentEntry::query() );
+ QValueList<KoDocumentEntry>::ConstIterator partIt( parts.begin() );
+ QValueList<KoDocumentEntry>::ConstIterator partEnd( parts.end() );
+
+ const Vertex *v = 0;
+
+ // Be sure that v gets initialized correctly
+ while ( !v && partIt != partEnd ) {
+ QStringList nativeMimeTypes = ( *partIt ).service()->property( "X-KDE-ExtraNativeMimeTypes" ).toStringList();
+ nativeMimeTypes += ( *partIt ).service()->property( "X-KDE-NativeMimeType" ).toString();
+ QStringList::ConstIterator it = nativeMimeTypes.begin();
+ QStringList::ConstIterator end = nativeMimeTypes.end();
+ for ( ; !v && it != end; ++it )
+ if ( !(*it).isEmpty() )
+ v = m_vertices[ ( *it ).latin1() ];
+ ++partIt;
+ }
+ if ( !v )
+ return "";
+
+ // Now we try to find the "cheapest" KOffice vertex
+ while ( partIt != partEnd ) {
+ QStringList nativeMimeTypes = ( *partIt ).service()->property( "X-KDE-ExtraNativeMimeTypes" ).toStringList();
+ nativeMimeTypes += ( *partIt ).service()->property( "X-KDE-NativeMimeType" ).toString();
+ QStringList::ConstIterator it = nativeMimeTypes.begin();
+ QStringList::ConstIterator end = nativeMimeTypes.end();
+ for ( ; !v && it != end; ++it ) {
+ QString key = *it;
+ if ( !key.isEmpty() ) {
+ Vertex* tmp = m_vertices[ key.latin1() ];
+ if ( !v || ( tmp && tmp->key() < v->key() ) )
+ v = tmp;
+ }
+ }
+ ++partIt;
+ }
+
+ // It seems it already is a KOffice part
+ if ( v->key() == 0 )
+ return "";
+
+ return v->mimeType();
+ }
+
+} // namespace KOffice
diff --git a/lib/kofficecore/KoFilterChain.h b/lib/kofficecore/KoFilterChain.h
new file mode 100644
index 00000000..5b8dd3dd
--- /dev/null
+++ b/lib/kofficecore/KoFilterChain.h
@@ -0,0 +1,410 @@
+/* This file is part of the KOffice libraries
+ Copyright (C) 2001 Werner Trobin <trobin@kde.org>
+
+ 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 __koffice_filter_chain_h__
+#define __koffice_filter_chain_h__
+
+#include <qcstring.h>
+#include <qasciidict.h>
+#include <qptrlist.h>
+#include <qstringlist.h>
+
+#include <KoFilter.h>
+#include <KoQueryTrader.h>
+#include <KoStoreDevice.h>
+#include <koffice_export.h>
+
+class KTempFile;
+class KoFilterManager;
+class KoDocument;
+class QStrList;
+
+namespace KOffice {
+ class Graph;
+}
+
+/**
+ * @brief This class represents a chain of plain @ref KoFilter instances.
+ *
+ * Instances of this class are shared, so please just hold
+ * KoFilterChain::Ptr pointers to it.
+ *
+ * @author Werner Trobin <trobin@kde.org>
+ * @todo the class has no constructor and therefore cannot initialize its private class
+ */
+class KOFFICECORE_EXPORT KoFilterChain : public KShared
+{
+ // Only KOffice::Graph is allowed to construct instances and
+ // add chain links.
+ friend class KOffice::Graph;
+ friend class KoFilterManager;
+
+public:
+ typedef KSharedPtr<KoFilterChain> Ptr;
+
+ virtual ~KoFilterChain();
+
+ /**
+ * The filter manager returned may be 0!
+ */
+ const KoFilterManager* manager() const { return m_manager; }
+
+ /**
+ * Starts the filtering process.
+ * @return The return status of the conversion. KoFilter::OK
+ * if everything is alright.
+ */
+ KoFilter::ConversionStatus invokeChain();
+
+ /**
+ * Tells the @ref KoFilterManager the output file of the
+ * filter chain in case of an import operation. If it's
+ * QString::null we directly manipulated the document.
+ */
+ QString chainOutput() const;
+
+ /**
+ * Get the current file to read from. This part of the API
+ * is for the filters in our chain.
+ */
+ QString inputFile();
+ /**
+ * Get the current file to write to. This part of the API
+ * is for the filters in our chain.
+ */
+ QString outputFile();
+
+ /**
+ * Get a file from a storage. May return 0!
+ * This part of the API is for the filters in our chain.
+ * If you call it multiple times with the same stream name
+ * the stream will be closed and re-opened.
+ * Note: @em Don't delete that @ref KoStoreDevice we return.
+ * @param name The name of the stream inside the storage
+ * @param mode Whether we want to read or write from/to the stream
+ * @return The storage device to access the stream. May be 0!
+ */
+ KoStoreDevice* storageFile( const QString& name = "root", KoStore::Mode mode = KoStore::Read );
+
+ /**
+ * This method allows your filter to work directly on the
+ * @ref KoDocument of the application.
+ * This part of the API is for the filters in our chain.
+ * @return The document containing the data. May return 0 on error.
+ */
+ KoDocument* inputDocument();
+ /**
+ * This method allows your filter to work directly on the
+ * @ref KoDocument of the application.
+ * This part of the API is for the filters in our chain.
+ * @return The document you have to write to. May return 0 on error.
+ */
+ KoDocument* outputDocument();
+
+
+ // debugging
+ void dump() const;
+
+private:
+ // ### API for KOffice::Graph:
+ // Construct a filter chain belonging to some KoFilterManager.
+ // The parent filter manager may be 0.
+ KoFilterChain( const KoFilterManager* manager );
+
+ void appendChainLink( KoFilterEntry::Ptr filterEntry, const QCString& from, const QCString& to );
+ void prependChainLink( KoFilterEntry::Ptr filterEntry, const QCString& from, const QCString& to );
+
+ // ### API for KoEmbeddingFilter
+ // This is needed as the embedding filter might have to influence
+ // the way we change directories (e.g. in the olefilter case)
+ // The ugly friend methods are needed, but I'd welcome and suggestions for
+ // better design :}
+ friend void KoEmbeddingFilter::filterChainEnterDirectory( const QString& directory ) const;
+ void enterDirectory( const QString& directory );
+ friend void KoEmbeddingFilter::filterChainLeaveDirectory() const;
+ void leaveDirectory();
+
+ // These methods are friends of KoFilterManager and provide access
+ // to a private part of its API. As I don't want to include
+ // koFilterManager.h in this header the direction is "int" here.
+ QString filterManagerImportFile() const;
+ QString filterManagerExportFile() const;
+ KoDocument* filterManagerKoDocument() const;
+ int filterManagerDirection() const;
+ KoFilterChain* const filterManagerParentChain() const;
+
+
+ // Helper methods which keep track of all the temp files, documents,
+ // storages,... and properly delete them as soon as they are not
+ // needed anymore.
+ void manageIO();
+ void finalizeIO();
+
+ bool createTempFile( KTempFile** tempFile, bool autoDelete = true );
+
+ void inputFileHelper( KoDocument* document, const QString& alternativeFile );
+ void outputFileHelper( bool autoDelete );
+ KoStoreDevice* storageNewStreamHelper( KoStore** storage, KoStoreDevice** device, const QString& name );
+ KoStoreDevice* storageHelper( const QString& file, const QString& streamName,
+ KoStore::Mode mode, KoStore** storage, KoStoreDevice** device );
+ void storageInit( const QString& file, KoStore::Mode mode, KoStore** storage );
+ KoStoreDevice* storageInitEmbedding( const QString& name );
+ KoStoreDevice* storageCreateFirstStream( const QString& streamName, KoStore** storage, KoStoreDevice** device );
+ KoStoreDevice* storageCleanupHelper( KoStore** storage );
+
+ KoDocument* createDocument( const QString& file );
+ KoDocument* createDocument( const QCString& mimeType );
+
+ /**
+ * A small private helper class with represents one single filter
+ * (one link of the chain)
+ * @internal
+ */
+ class ChainLink
+ {
+
+ public:
+ ChainLink( KoFilterChain* chain, KoFilterEntry::Ptr filterEntry,
+ const QCString& from, const QCString& to );
+
+ KoFilter::ConversionStatus invokeFilter( const ChainLink* const parentChainLink );
+
+ QCString from() const { return m_from; }
+ QCString to() const { return m_to; }
+
+ // debugging
+ void dump() const;
+
+ // This hack is only needed due to crappy Microsoft design and
+ // circular dependencies in their embedded files :}
+ int lruPartIndex() const;
+
+ private:
+ ChainLink( const ChainLink& rhs );
+ ChainLink& operator=( const ChainLink& rhs );
+
+ void setupCommunication( const KoFilter* const parentFilter ) const;
+ void setupConnections( const KoFilter* sender, const QStrList& sigs,
+ const KoFilter* receiver, const QStrList& sl0ts ) const;
+
+ KoFilterChain* m_chain;
+ KoFilterEntry::Ptr m_filterEntry;
+ QCString m_from, m_to;
+
+ // This hack is only needed due to crappy Microsoft design and
+ // circular dependencies in their embedded files :}
+ KoFilter* m_filter;
+
+ class Private;
+ Private* d;
+ };
+
+ // "A whole is that which has beginning, middle, and end" - Aristotle
+ // ...but we also need to signal "Done" state, Mr. Aristotle
+ enum Whole { Beginning = 1, Middle = 2, End = 4, Done = 8 };
+
+ // Don't copy or assign filter chains
+ KoFilterChain( const KoFilterChain& rhs );
+ KoFilterChain& operator=( const KoFilterChain& rhs );
+
+ const KoFilterManager* const m_manager;
+ QPtrList<ChainLink> m_chainLinks;
+
+ // stuff needed for bookkeeping
+ int m_state;
+
+ QString m_inputFile; // Did we pass around plain files?
+ QString m_outputFile;
+
+ KoStore* m_inputStorage; // ...or was it a storage+device?
+ KoStoreDevice* m_inputStorageDevice;
+ KoStore* m_outputStorage;
+ KoStoreDevice* m_outputStorageDevice;
+
+ KoDocument* m_inputDocument; // ...or even documents?
+ KoDocument* m_outputDocument;
+
+ KTempFile* m_inputTempFile;
+ KTempFile* m_outputTempFile;
+
+ // These two flags keep track of the input/output the
+ // filter (=user) asked for
+ enum IOState { Nil, File, Storage, Document };
+ IOState m_inputQueried, m_outputQueried;
+
+ // This stack keeps track of directories we have to enter and
+ // leave due to internal embedding a la OLE filters. This serves
+ // as a kind of "memory" even if we didn't initialize the store yet.
+ // I know that it's ugly, and I'll try to clean up that hack
+ // sooner or later (Werner)
+ QStringList m_internalEmbeddingDirectories;
+
+ class Private;
+ Private* d;
+};
+
+
+// As we use quite generic classnames...
+namespace KOffice
+{
+ class Vertex;
+ template<class T> class PriorityQueue;
+
+ /**
+ * An internal class representing a filter (=edge) in the filter graph.
+ * @internal
+ */
+ class Edge
+ {
+
+ public:
+ // creates a new edge to "vertex" with the given weight.
+ Edge( Vertex* vertex, KoFilterEntry::Ptr filterEntry );
+ ~Edge() {}
+
+ unsigned int weight() const { return m_filterEntry ? m_filterEntry->weight : 0; }
+ KoFilterEntry::Ptr filterEntry() const { return m_filterEntry; }
+ const Vertex* vertex() const { return m_vertex; }
+
+ // Relaxes the "connected" vertex (i.e. the weight of the
+ // connected vertex = "predec.->key()" (parameter) + weight of this edge
+ // As this will only be called once we calculate the weight
+ // of the edge "on the fly"
+ // Note: We have to pass the queue as we have to call keyDecreased :}
+ void relax( const Vertex* predecessor, PriorityQueue<Vertex>& queue );
+
+ // debugging
+ void dump( const QCString& indent ) const;
+
+ private:
+ Edge( const Edge& rhs );
+ Edge& operator=( const Edge& rhs );
+
+ Vertex* m_vertex;
+ KoFilterEntry::Ptr m_filterEntry;
+
+ class Private;
+ Private* d;
+ };
+
+
+ /**
+ * An internal class representing a mime type (=node, vertex) in the filter graph.
+ * @internal
+ */
+ class Vertex
+ {
+
+ public:
+ Vertex( const QCString& mimeType );
+ ~Vertex() {}
+
+ QCString mimeType() const { return m_mimeType; }
+
+ // Current "weight" of the vertex - will be "relaxed" when
+ // running the shortest path algorithm. Returns true if it
+ // really has been "relaxed"
+ bool setKey( unsigned int key );
+ unsigned int key() const { return m_weight; }
+ // Can be used to set the key back to "Infinity" (UINT_MAX)
+ // and reset the predecessor of this vertex
+ void reset();
+
+ // Position in the heap, needed for a fast keyDecreased operation
+ void setIndex( int index ) { m_index=index; }
+ int index() const { return m_index; }
+
+ // predecessor on the way from the source to the destination,
+ // needed for the shortest path algorithm
+ void setPredecessor( const Vertex* predecessor ) { m_predecessor=predecessor; }
+ const Vertex* predecessor() const { return m_predecessor; }
+
+ // Adds an outgoing edge to the vertex, transfers ownership
+ void addEdge( const Edge* edge );
+ // Finds the lightest(!) edge pointing to the given vertex, if any (0 if not found)
+ // This means it will always search the whole list of edges
+ const Edge* findEdge( const Vertex* vertex ) const;
+
+ // This method is called when we need to relax all "our" edges.
+ // We need to pass the queue as we have to notify it about key changes - ugly :(
+ void relaxVertices( PriorityQueue<Vertex>& queue );
+
+ // debugging
+ void dump( const QCString& indent ) const;
+
+ private:
+ Vertex( const Vertex& rhs );
+ Vertex& operator=( const Vertex& rhs );
+
+ QPtrList<Edge> m_edges;
+ const Vertex* m_predecessor;
+ QCString m_mimeType;
+ unsigned int m_weight; // "key" inside the queue
+ int m_index; // position inside the queue, needed for a fast keyDecreased()
+
+ class Private;
+ Private* d;
+ };
+
+
+ /**
+ * The main worker behind the scenes. Manages the creation of the graph,
+ * processing the information in it, and creating the filter chains.
+ * @internal
+ */
+ class Graph
+ {
+
+ public:
+ Graph( const QCString& from );
+ ~Graph() {}
+
+ bool isValid() const { return m_graphValid; }
+
+ QCString sourceMimeType() const { return m_from; }
+ void setSourceMimeType( const QCString& from );
+
+ // Creates a chain from "from" to the "to" mimetype
+ // If the "to" mimetype isEmpty() then we try to find the
+ // closest KOffice mimetype and use that as destination.
+ // After such a search "to" will contain the dest. mimetype (return value)
+ // if the search was successful. Might return 0!
+ KoFilterChain::Ptr chain( const KoFilterManager* manager, QCString& to ) const;
+
+ // debugging
+ void dump() const;
+
+ private:
+ Graph( const Graph& rhs );
+ Graph& operator=( const Graph& rhs );
+
+ void buildGraph();
+ void shortestPaths();
+ QCString findKOfficePart() const;
+
+ QAsciiDict<Vertex> m_vertices;
+ QCString m_from;
+ bool m_graphValid;
+
+ class Private;
+ Private* d;
+ };
+
+} // namespace KOffice
+
+#endif // __koffice_filter_chain_h__
diff --git a/lib/kofficecore/KoFilterManager.cpp b/lib/kofficecore/KoFilterManager.cpp
new file mode 100644
index 00000000..f3db5c73
--- /dev/null
+++ b/lib/kofficecore/KoFilterManager.cpp
@@ -0,0 +1,602 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ 2000, 2001 Werner Trobin <trobin@kde.org>
+ Copyright (C) 2004 Nicolas Goutte <goutte@kde.org>
+
+ 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.
+*/
+
+
+#include <KoFilterManager.h>
+#include <KoFilterManager_p.h>
+
+#include <qfile.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qptrlist.h>
+#include <qapplication.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <KoDocument.h>
+#include <klibloader.h>
+#include <klistbox.h>
+#include <kmimetype.h>
+#include <kdebug.h>
+
+#include <queue>
+
+#include <unistd.h>
+
+class KoFilterManager::Private
+{
+public:
+ bool m_batch;
+};
+
+
+KoFilterChooser::KoFilterChooser (QWidget *parent, const QStringList &mimeTypes, const QString &nativeFormat)
+ : KDialogBase (parent, "kofilterchooser", true, i18n ("Choose Filter"),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true),
+ m_mimeTypes (mimeTypes)
+{
+ setInitialSize (QSize (300, 350));
+
+ QWidget *page = new QWidget (this);
+ setMainWidget (page);
+
+ // looks too squashed together without * 2
+ QVBoxLayout *layout = new QVBoxLayout (page, marginHint (), spacingHint () * 2);
+
+ QLabel *filterLabel = new QLabel (i18n ("Select a filter:"), page, "filterlabel");
+ layout->addWidget (filterLabel);
+
+ m_filterList = new KListBox (page, "filterlist");
+ layout->addWidget (m_filterList);
+
+ Q_ASSERT (!m_mimeTypes.isEmpty ());
+ for (QStringList::ConstIterator it = m_mimeTypes.begin ();
+ it != m_mimeTypes.end ();
+ it++)
+ {
+ KMimeType::Ptr mime = KMimeType::mimeType (*it);
+ m_filterList->insertItem (mime->comment ());
+ }
+
+ if (nativeFormat == "application/x-kword")
+ {
+ const int index = m_mimeTypes.findIndex ("text/plain");
+ if (index > -1)
+ m_filterList->setCurrentItem (index);
+ }
+
+ if (m_filterList->currentItem () == -1)
+ m_filterList->setCurrentItem (0);
+
+ m_filterList->centerCurrentItem ();
+ m_filterList->setFocus ();
+
+ connect (m_filterList, SIGNAL (selected (int)), this, SLOT (slotOk ()));
+}
+
+KoFilterChooser::~KoFilterChooser ()
+{
+}
+
+QString KoFilterChooser::filterSelected ()
+{
+ const int item = m_filterList->currentItem ();
+
+ if (item > -1)
+ return m_mimeTypes [item];
+ else
+ return QString::null;
+}
+
+
+// static cache for filter availability
+QMap<QString, bool> KoFilterManager::m_filterAvailable;
+
+const int KoFilterManager::s_area = 30500;
+
+
+KoFilterManager::KoFilterManager( KoDocument* document ) :
+ m_document( document ), m_parentChain( 0 ), m_graph( "" ), d( 0 )
+{
+ d = new KoFilterManager::Private;
+ d -> m_batch = false;
+ if ( document )
+ QObject::connect( this, SIGNAL( sigProgress( int ) ),
+ document, SIGNAL( sigProgress( int ) ) );
+}
+
+
+KoFilterManager::KoFilterManager( const QString& url, const QCString& mimetypeHint,
+ KoFilterChain* const parentChain ) :
+ m_document( 0 ), m_parentChain( parentChain ), m_importUrl( url ), m_importUrlMimetypeHint( mimetypeHint ),
+ m_graph( "" ), d( 0 )
+{
+ d = new KoFilterManager::Private;
+ d -> m_batch = false;
+}
+
+KoFilterManager::~KoFilterManager()
+{
+ delete d;
+}
+
+QString KoFilterManager::import( const QString& url, KoFilter::ConversionStatus& status )
+{
+ // Find the mime type for the file to be imported.
+ KURL u;
+ u.setPath( url );
+ KMimeType::Ptr t = KMimeType::findByURL( u, 0, true );
+
+ m_graph.setSourceMimeType( t->name().latin1() ); // .latin1() is okay here (Werner)
+ if ( !m_graph.isValid() ) {
+ bool userCancelled = false;
+
+ kdWarning(s_area) << "Can't open " << t->name () << ", trying filter chooser" << endl;
+ if ( m_document )
+ {
+ if ( !m_document->isAutoErrorHandlingEnabled() )
+ {
+ status = KoFilter::BadConversionGraph;
+ return QString::null;
+ }
+ QCString nativeFormat = m_document->nativeFormatMimeType ();
+
+ QApplication::setOverrideCursor( arrowCursor );
+ KoFilterChooser chooser(0,
+ KoFilterManager::mimeFilter (nativeFormat, KoFilterManager::Import, m_document->extraNativeMimeTypes()),
+ nativeFormat);
+ if (chooser.exec ())
+ {
+ QCString f = chooser.filterSelected ().latin1();
+
+ if (f == nativeFormat)
+ {
+ status = KoFilter::OK;
+ QApplication::restoreOverrideCursor();
+ return url;
+ }
+
+ m_graph.setSourceMimeType (f);
+ }
+ else
+ userCancelled = true;
+ QApplication::restoreOverrideCursor();
+ }
+
+ if (!m_graph.isValid())
+ {
+ kdError(s_area) << "Couldn't create a valid graph for this source mimetype: "
+ << t->name() << endl;
+ importErrorHelper( t->name(), userCancelled );
+ status = KoFilter::BadConversionGraph;
+ return QString::null;
+ }
+ }
+
+ KoFilterChain::Ptr chain( 0 );
+ // Are we owned by a KoDocument?
+ if ( m_document ) {
+ QCString mimeType = m_document->nativeFormatMimeType();
+ QStringList extraMimes = m_document->extraNativeMimeTypes();
+ int i=0, n = extraMimes.count();
+ chain = m_graph.chain( this, mimeType );
+ while( !chain && i<n) {
+ mimeType = extraMimes[i].utf8();
+ chain = m_graph.chain( this, mimeType );
+ ++i;
+ }
+ }
+ else {
+ kdError(s_area) << "You aren't supposed to use import() from a filter!" << endl;
+ status = KoFilter::UsageError;
+ return QString::null;
+ }
+
+ if ( !chain ) {
+ kdError(s_area) << "Couldn't create a valid filter chain!" << endl;
+ importErrorHelper( t->name() );
+ status = KoFilter::BadConversionGraph;
+ return QString::null;
+ }
+
+ // Okay, let's invoke the filters one after the other
+ m_direction = Import; // vital information!
+ m_importUrl = url; // We want to load that file
+ m_exportUrl = QString::null; // This is null for sure, as embedded stuff isn't
+ // allowed to use that method
+ status = chain->invokeChain();
+
+ m_importUrl = QString::null; // Reset the import URL
+
+ if ( status == KoFilter::OK )
+ return chain->chainOutput();
+ return QString::null;
+}
+
+KoFilter::ConversionStatus KoFilterManager::exp0rt( const QString& url, QCString& mimeType )
+{
+ bool userCancelled = false;
+
+ // The import url should already be set correctly (null if we have a KoDocument
+ // file manager and to the correct URL if we have an embedded manager)
+ m_direction = Export; // vital information!
+ m_exportUrl = url;
+
+ KoFilterChain::Ptr chain = 0;
+ if ( m_document ) {
+ // We have to pick the right native mimetype as source.
+ QStringList nativeMimeTypes;
+ nativeMimeTypes.append( m_document->nativeFormatMimeType() );
+ nativeMimeTypes += m_document->extraNativeMimeTypes();
+ QStringList::ConstIterator it = nativeMimeTypes.begin();
+ const QStringList::ConstIterator end = nativeMimeTypes.end();
+ for ( ; !chain && it != end; ++it )
+ {
+ m_graph.setSourceMimeType( (*it).latin1() );
+ if ( m_graph.isValid() )
+ chain = m_graph.chain( this, mimeType );
+ }
+ }
+ else if ( !m_importUrlMimetypeHint.isEmpty() ) {
+ kdDebug(s_area) << "Using the mimetype hint: '" << m_importUrlMimetypeHint << "'" << endl;
+ m_graph.setSourceMimeType( m_importUrlMimetypeHint );
+ }
+ else {
+ KURL u;
+ u.setPath( m_importUrl );
+ KMimeType::Ptr t = KMimeType::findByURL( u, 0, true );
+ if ( t->name() == KMimeType::defaultMimeType() ) {
+ kdError(s_area) << "No mimetype found for " << m_importUrl << endl;
+ return KoFilter::BadMimeType;
+ }
+ m_graph.setSourceMimeType( t->name().latin1() );
+
+ if ( !m_graph.isValid() ) {
+ kdWarning(s_area) << "Can't open " << t->name () << ", trying filter chooser" << endl;
+
+ QApplication::setOverrideCursor( arrowCursor );
+ KoFilterChooser chooser(0, KoFilterManager::mimeFilter ());
+ if (chooser.exec ())
+ m_graph.setSourceMimeType (chooser.filterSelected ().latin1 ());
+ else
+ userCancelled = true;
+
+ QApplication::restoreOverrideCursor();
+ }
+ }
+
+ if (!m_graph.isValid ())
+ {
+ kdError(s_area) << "Couldn't create a valid graph for this source mimetype." << endl;
+ if (!userCancelled) KMessageBox::error( 0L, i18n("Could not export file."), i18n("Missing Export Filter") );
+ return KoFilter::BadConversionGraph;
+ }
+
+ if ( !chain ) // already set when coming from the m_document case
+ chain = m_graph.chain( this, mimeType );
+
+ if ( !chain ) {
+ kdError(s_area) << "Couldn't create a valid filter chain to " << mimeType << " !" << endl;
+ KMessageBox::error( 0L, i18n("Could not export file."), i18n("Missing Export Filter") );
+ return KoFilter::BadConversionGraph;
+ }
+
+ return chain->invokeChain();
+}
+
+namespace // in order not to mess with the global namespace ;)
+{
+ // This class is needed only for the static mimeFilter method
+ class Vertex
+ {
+ public:
+ Vertex( const QCString& mimeType ) : m_color( White ), m_mimeType( mimeType ) {}
+
+ enum Color { White, Gray, Black };
+ Color color() const { return m_color; }
+ void setColor( Color color ) { m_color = color; }
+
+ QCString mimeType() const { return m_mimeType; }
+
+ void addEdge( Vertex* vertex ) { if ( vertex ) m_edges.append( vertex ); }
+ QPtrList<Vertex> edges() const { return m_edges; }
+
+ private:
+ Color m_color;
+ QCString m_mimeType;
+ QPtrList<Vertex> m_edges;
+ };
+
+ // Some helper methods for the static stuff
+ // This method builds up the graph in the passed ascii dict
+ void buildGraph( QAsciiDict<Vertex>& vertices, KoFilterManager::Direction direction )
+ {
+ QStringList stopList; // Lists of mimetypes that are considered end of chains
+ stopList << "text/plain";
+ stopList << "text/csv";
+ stopList << "text/x-tex";
+ stopList << "text/html";
+
+ vertices.setAutoDelete( true );
+
+ // partly copied from build graph, but I don't see any other
+ // way without crude hacks, as we have to obey the direction here
+ QValueList<KoDocumentEntry> parts( KoDocumentEntry::query(false, QString::null) );
+ QValueList<KoDocumentEntry>::ConstIterator partIt( parts.begin() );
+ QValueList<KoDocumentEntry>::ConstIterator partEnd( parts.end() );
+
+ while ( partIt != partEnd ) {
+ QStringList nativeMimeTypes = ( *partIt ).service()->property( "X-KDE-ExtraNativeMimeTypes" ).toStringList();
+ nativeMimeTypes += ( *partIt ).service()->property( "X-KDE-NativeMimeType" ).toString();
+ QStringList::ConstIterator it = nativeMimeTypes.begin();
+ const QStringList::ConstIterator end = nativeMimeTypes.end();
+ for ( ; it != end; ++it )
+ if ( !(*it).isEmpty() )
+ vertices.insert( (*it).latin1(), new Vertex( (*it).latin1() ) );
+ ++partIt;
+ }
+
+ QValueList<KoFilterEntry::Ptr> filters = KoFilterEntry::query(); // no constraint here - we want *all* :)
+ QValueList<KoFilterEntry::Ptr>::ConstIterator it = filters.begin();
+ const QValueList<KoFilterEntry::Ptr>::ConstIterator end = filters.end();
+
+ for ( ; it != end; ++it ) {
+
+ QStringList impList; // Import list
+ QStringList expList; // Export list
+
+ const QStringList::Iterator stopEnd = stopList.end();
+ // Now we have to exclude the "stop" mimetypes (in the right direction!)
+ if ( direction == KoFilterManager::Import ) {
+ // Import: "stop" mime type should not appear in export
+ QStringList::ConstIterator testIt = ( *it )->export_.begin();
+ QStringList::ConstIterator testEnd = ( *it )->export_.end();
+ for ( ; testIt != testEnd ; ++testIt ) {
+ if ( stopList.find( *testIt ) == stopEnd ) {
+ expList.append( *testIt );
+ }
+ }
+ impList = ( *it )->import;
+ }
+ else {
+ // Export: "stop" mime type should not appear in import
+ QStringList::ConstIterator testIt = ( *it )->import.begin();
+ const QStringList::ConstIterator testEnd = ( *it )->import.end();
+ for ( ; testIt != testEnd ; ++testIt ) {
+ if ( stopList.find( *testIt ) == stopEnd ) {
+ impList.append( *testIt );
+ }
+ }
+ expList = ( *it )->export_;
+ }
+
+ if ( impList.empty() || expList.empty() )
+ {
+ // This filter cannot be used under these conditions
+ kdDebug( 30500 ) << "Filter: " << ( *it )->service()->name() << " ruled out" << endl;
+ continue;
+ }
+
+ // First add the "starting points" to the dict
+ QStringList::ConstIterator importIt = impList.begin();
+ const QStringList::ConstIterator importEnd = impList.end();
+ for ( ; importIt != importEnd; ++importIt ) {
+ const QCString key = ( *importIt ).latin1(); // latin1 is okay here (werner)
+ // already there?
+ if ( !vertices[ key ] )
+ vertices.insert( key, new Vertex( key ) );
+ }
+
+ // Are we allowed to use this filter at all?
+ if ( KoFilterManager::filterAvailable( *it ) ) {
+ QStringList::ConstIterator exportIt = expList.begin();
+ const QStringList::ConstIterator exportEnd = expList.end();
+
+ for ( ; exportIt != exportEnd; ++exportIt ) {
+ // First make sure the export vertex is in place
+ const QCString key = ( *exportIt ).latin1(); // latin1 is okay here
+ Vertex* exp = vertices[ key ];
+ if ( !exp ) {
+ exp = new Vertex( key );
+ vertices.insert( key, exp );
+ }
+ // Then create the appropriate edges depending on the
+ // direction (import/export)
+ // This is the chunk of code which actually differs from the
+ // graph stuff (apart from the different vertex class)
+ importIt = impList.begin(); // ### TODO: why only the first one?
+ if ( direction == KoFilterManager::Import ) {
+ for ( ; importIt != importEnd; ++importIt )
+ exp->addEdge( vertices[ ( *importIt ).latin1() ] );
+ } else {
+ for ( ; importIt != importEnd; ++importIt )
+ vertices[ ( *importIt ).latin1() ]->addEdge( exp );
+ }
+ }
+ }
+ else
+ kdDebug( 30500 ) << "Filter: " << ( *it )->service()->name() << " does not apply." << endl;
+ }
+ }
+
+ // This method runs a BFS on the graph to determine the connected
+ // nodes. Make sure that the graph is "cleared" (the colors of the
+ // nodes are all white)
+ QStringList connected( const QAsciiDict<Vertex>& vertices, const QCString& mimetype )
+ {
+ if ( mimetype.isEmpty() )
+ return QStringList();
+ Vertex *v = vertices[ mimetype ];
+ if ( !v )
+ return QStringList();
+
+ v->setColor( Vertex::Gray );
+ std::queue<Vertex*> queue;
+ queue.push( v );
+ QStringList connected;
+
+ while ( !queue.empty() ) {
+ v = queue.front();
+ queue.pop();
+ QPtrList<Vertex> edges = v->edges();
+ QPtrListIterator<Vertex> it( edges );
+ for ( ; it.current(); ++it ) {
+ if ( it.current()->color() == Vertex::White ) {
+ it.current()->setColor( Vertex::Gray );
+ queue.push( it.current() );
+ }
+ }
+ v->setColor( Vertex::Black );
+ connected.append( v->mimeType() );
+ }
+ return connected;
+ }
+}
+
+// The static method to figure out to which parts of the
+// graph this mimetype has a connection to.
+QStringList KoFilterManager::mimeFilter( const QCString& mimetype, Direction direction, const QStringList& extraNativeMimeTypes )
+{
+ //kdDebug(s_area) << "mimetype=" << mimetype << " extraNativeMimeTypes=" << extraNativeMimeTypes << endl;
+ QAsciiDict<Vertex> vertices;
+ buildGraph( vertices, direction );
+
+ // TODO maybe use the fake vertex trick from the method below, to make the search faster?
+
+ QStringList nativeMimeTypes;
+ nativeMimeTypes.append( QString::fromLatin1( mimetype ) );
+ nativeMimeTypes += extraNativeMimeTypes;
+
+ // Add the native mimetypes first so that they are on top.
+ QStringList lst = nativeMimeTypes;
+
+ // Now look for filters which output each of those natives mimetypes
+ for( QStringList::ConstIterator natit = nativeMimeTypes.begin(); natit != nativeMimeTypes.end(); ++natit ) {
+ const QStringList outMimes = connected( vertices, (*natit).latin1() );
+ //kdDebug(s_area) << k_funcinfo << "output formats connected to mime " << *natit << " : " << outMimes << endl;
+ for ( QStringList::ConstIterator mit = outMimes.begin(); mit != outMimes.end(); ++mit )
+ if ( lst.find( *mit ) == lst.end() ) // append only if not there already. Qt4: QSet<QString>?
+ lst.append( *mit );
+ }
+ return lst;
+}
+
+QStringList KoFilterManager::mimeFilter()
+{
+ QAsciiDict<Vertex> vertices;
+ buildGraph( vertices, KoFilterManager::Import );
+
+ QValueList<KoDocumentEntry> parts( KoDocumentEntry::query(false, QString::null) );
+ QValueList<KoDocumentEntry>::ConstIterator partIt( parts.begin() );
+ QValueList<KoDocumentEntry>::ConstIterator partEnd( parts.end() );
+
+ if ( partIt == partEnd )
+ return QStringList();
+
+ // To find *all* reachable mimetypes, we have to resort to
+ // a small hat trick, in order to avoid multiple searches:
+ // We introduce a fake vertex, which is connected to every
+ // single KOffice mimetype. Due to that one BFS is enough :)
+ // Now we just need an... ehrm.. unique name for our fake mimetype
+ Vertex *v = new Vertex( "supercalifragilistic/x-pialadocious" );
+ vertices.insert( "supercalifragilistic/x-pialadocious", v );
+ while ( partIt != partEnd ) {
+ QStringList nativeMimeTypes = ( *partIt ).service()->property( "X-KDE-ExtraNativeMimeTypes" ).toStringList();
+ nativeMimeTypes += ( *partIt ).service()->property( "X-KDE-NativeMimeType" ).toString();
+ QStringList::ConstIterator it = nativeMimeTypes.begin();
+ const QStringList::ConstIterator end = nativeMimeTypes.end();
+ for ( ; it != end; ++it )
+ if ( !(*it).isEmpty() )
+ v->addEdge( vertices[ (*it).latin1() ] );
+ ++partIt;
+ }
+ QStringList result = connected( vertices, "supercalifragilistic/x-pialadocious" );
+
+ // Finally we have to get rid of our fake mimetype again
+ result.remove( "supercalifragilistic/x-pialadocious" );
+ return result;
+}
+
+// Here we check whether the filter is available. This stuff is quite slow,
+// but I don't see any other convenient (for the user) way out :}
+bool KoFilterManager::filterAvailable( KoFilterEntry::Ptr entry )
+{
+ if ( !entry )
+ return false;
+ if ( entry->available != "check" )
+ return true;
+
+ //kdDebug( 30500 ) << "Checking whether " << entry->service()->name() << " applies." << endl;
+ // generate some "unique" key
+ QString key( entry->service()->name() );
+ key += " - ";
+ key += entry->service()->library();
+
+ if ( !m_filterAvailable.contains( key ) ) {
+ //kdDebug( 30500 ) << "Not cached, checking..." << endl;
+
+ KLibrary* library = KLibLoader::self()->library( QFile::encodeName( entry->service()->library() ) );
+ if ( !library ) {
+ kdWarning( 30500 ) << "Huh?? Couldn't load the lib: "
+ << KLibLoader::self()->lastErrorMessage() << endl;
+ m_filterAvailable[ key ] = false;
+ return false;
+ }
+
+ // This code is "borrowed" from klibloader ;)
+ QCString symname;
+ symname.sprintf("check_%s", library->name().latin1() );
+ void* sym = library->symbol( symname );
+ if ( !sym )
+ {
+ kdWarning( 30500 ) << "The library " << library->name()
+ << " does not offer a check_" << library->name()
+ << " function." << endl;
+ m_filterAvailable[ key ] = false;
+ }
+ else {
+ typedef int (*t_func)();
+ t_func check = (t_func)sym;
+ m_filterAvailable[ key ] = check() == 1;
+ }
+ }
+ return m_filterAvailable[ key ];
+}
+
+void KoFilterManager::importErrorHelper( const QString& mimeType, const bool suppressDialog )
+{
+ QString tmp = i18n("Could not import file of type\n%1").arg( mimeType );
+ // ###### FIXME: use KLibLoader::lastErrorMessage() here
+ if (!suppressDialog) KMessageBox::error( 0L, tmp, i18n("Missing Import Filter") );
+}
+
+void KoFilterManager::setBatchMode( const bool batch )
+{
+ d->m_batch = batch;
+}
+
+bool KoFilterManager::getBatchMode( void ) const
+{
+ return d->m_batch;
+}
+
+#include <KoFilterManager.moc>
+#include <KoFilterManager_p.moc>
diff --git a/lib/kofficecore/KoFilterManager.h b/lib/kofficecore/KoFilterManager.h
new file mode 100644
index 00000000..952c8475
--- /dev/null
+++ b/lib/kofficecore/KoFilterManager.h
@@ -0,0 +1,178 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ 2000, 2001 Werner Trobin <trobin@kde.org>
+ Copyright (C) 2004 Nicolas Goutte <goutte@kde.org>
+
+ 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 __koffice_filter_manager_h__
+#define __koffice_filter_manager_h__
+
+#include <qobject.h>
+#include <qmap.h>
+#include <KoFilterChain.h>
+#include <koffice_export.h>
+class KoDocument;
+
+/**
+ * @brief The class managing all the filters.
+ *
+ * This class manages all filters for a %KOffice application. Normally
+ * you will not have to use it, since KoMainWindow takes care of loading
+ * and saving documents.
+ *
+ * @ref KoFilter
+ *
+ * @author Kalle Dalheimer <kalle@kde.org>
+ * @author Torben Weis <weis@kde.org>
+ * @author Werner Trobin <trobin@kde.org>
+ */
+class KOFFICECORE_EXPORT KoFilterManager : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * This enum is used to distinguish the import/export cases
+ */
+ enum Direction { Import = 1, Export = 2 };
+
+ /**
+ * Create a filter manager for a document
+ */
+ KoFilterManager( KoDocument* document );
+ /**
+ * Create a filter manager for a filter which wants to embed something.
+ * The url it passes is the file to convert, obviously. You cannot use
+ * the @ref import() method -- use @ref exp0rt() to convert the file to
+ * the destination mimetype you prefer.
+ *
+ * @param url The file you want to export
+ * @param mimetypeHint The mimetype of the file you want to export. You have
+ * to specify this information only if the automatic detection will
+ * fail because e.g. you saved an embedded stream to a *.tmp file.
+ * Most likely you do not have to care about that.
+ * @param parentChain The parent filter chain of this filter manager. Used
+ * to allow embedding for filters. Most likely you do not have to care.
+ */
+ KoFilterManager( const QString& url, const QCString& mimetypeHint = "",
+ KoFilterChain* const parentChain = 0 );
+
+ virtual ~KoFilterManager();
+
+ /**
+ * Imports the passed URL and returns the resultant filename
+ * (most likely some file in /tmp).
+ * The @p status variable signals the success/error of the conversion
+ * If the QString which is returned isEmpty() and the status is OK,
+ * then we imported the file directly into the document.
+ */
+ QString import( const QString& url, KoFilter::ConversionStatus& status );
+ /**
+ * @brief Exports the given file/document to the specified URL/mimetype.
+ *
+ * If @p mimeType is empty, then the closest matching KOffice part is searched
+ * and when the method returns @p mimeType contains this mimetype.
+ * Oh, well, export is a C++ keyword ;)
+ */
+ KoFilter::ConversionStatus exp0rt( const QString& url, QCString& mimeType );
+
+
+
+ ///@name Static API
+ //@{
+ /**
+ * Suitable for passing to KFileDialog::setMimeFilter. The default mime
+ * gets set by the "users" of this method, as we do not have enough
+ * information here.
+ * Optionally, @p extraNativeMimeTypes are added after the native mimetype.
+ */
+ KOFFICECORE_EXPORT static QStringList mimeFilter( const QCString& mimetype, Direction direction,
+ const QStringList& extraNativeMimeTypes = QStringList() );
+
+ /**
+ * The same method as KoFilterManager::mimeFilter but suited for KoShell.
+ * We do not need the mimetype, as we will simply use all available
+ * %KOffice mimetypes. The Direction enum is omitted, as we only
+ * call this for importing. When saving from KoShell we already
+ * know the KOffice part we are using.
+ */
+ static QStringList mimeFilter();
+
+ /**
+ * Method used to check if that filter is available at all.
+ * @note Slow, but cached
+ */
+ static bool filterAvailable( KoFilterEntry::Ptr entry );
+
+ //@}
+
+ /**
+ * Set the filter manager is batch mode (no dialog shown)
+ * instead of the interactive mode (dialog shown)
+ * @since 1.4
+ */
+ void setBatchMode ( const bool batch );
+
+ /**
+ * Get if the filter manager is batch mode (true)
+ * or in interactive mode (true)
+ * @since 1.4
+ */
+ bool getBatchMode ( void ) const;
+
+signals:
+ void sigProgress( int );
+
+private:
+ // === API for KoFilterChains === (internal)
+ // The friend methods are private in KoFilterChain and
+ // just forward calls to the methods here. Should be
+ // pretty save.
+ friend QString KoFilterChain::filterManagerImportFile() const;
+ QString importFile() const { return m_importUrl; }
+ friend QString KoFilterChain::filterManagerExportFile() const;
+ QString exportFile() const { return m_exportUrl; }
+ friend KoDocument* KoFilterChain::filterManagerKoDocument() const;
+ KoDocument* document() const { return m_document; }
+ friend int KoFilterChain::filterManagerDirection() const;
+ int direction() const { return static_cast<int>( m_direction ); }
+ friend KoFilterChain* const KoFilterChain::filterManagerParentChain() const;
+ KoFilterChain* const parentChain() const { return m_parentChain; }
+
+ // Private API
+ KoFilterManager( const KoFilterManager& rhs );
+ KoFilterManager &operator=( const KoFilterManager& rhs );
+
+ void importErrorHelper( const QString& mimeType, const bool suppressDialog = false );
+
+ static const int s_area;
+
+ KoDocument* m_document;
+ KoFilterChain* const m_parentChain;
+ QString m_importUrl, m_exportUrl;
+ QCString m_importUrlMimetypeHint; ///< suggested mimetype
+ KOffice::Graph m_graph;
+ Direction m_direction;
+
+ /// A static cache for the availability checks of filters
+ static QMap<QString, bool> m_filterAvailable;
+
+ class Private;
+ Private *d;
+};
+
+#endif // __koffice_filter_manager_h__
diff --git a/lib/kofficecore/KoFilterManager_p.h b/lib/kofficecore/KoFilterManager_p.h
new file mode 100644
index 00000000..20c6cac8
--- /dev/null
+++ b/lib/kofficecore/KoFilterManager_p.h
@@ -0,0 +1,46 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Clarence Dang <dang@kde.org>
+
+ 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 Version 2 for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ Version 2 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 __koFilterManager_p_h__
+#define __koFilterManager_p_h__
+
+#include <qstring.h>
+#include <qstringlist.h>
+
+#include <kdialogbase.h>
+
+class KListBox;
+
+
+class KoFilterChooser : public KDialogBase
+{
+Q_OBJECT
+
+public:
+ KoFilterChooser (QWidget *parent, const QStringList &mimeTypes,
+ const QString &nativeFormat = QString::null);
+ ~KoFilterChooser ();
+
+ QString filterSelected ();
+
+private:
+ QStringList m_mimeTypes;
+ KListBox *m_filterList;
+};
+
+#endif
diff --git a/lib/kofficecore/KoFrame.cpp b/lib/kofficecore/KoFrame.cpp
new file mode 100644
index 00000000..4c07d5c1
--- /dev/null
+++ b/lib/kofficecore/KoFrame.cpp
@@ -0,0 +1,393 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
+
+ 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.
+*/
+
+#include <KoFrame.h>
+#include <KoView.h>
+
+#include <qpainter.h>
+#include <kparts/event.h>
+#include <kcursor.h>
+#include <kdebug.h>
+
+#define QMAX32767(a,b) QMAX(QMAX(32767,a),b)
+
+class KoFramePrivate
+{
+public:
+ KoFramePrivate()
+ {
+ }
+ ~KoFramePrivate()
+ {
+ }
+
+ KoView *m_view;
+
+ QPoint m_mousePressPos;
+ QPoint m_framePos;
+ int m_width;
+ int m_height;
+ int m_mode;
+
+ KoFrame::State m_state;
+};
+
+KoFrame::KoFrame( QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ d = new KoFramePrivate;
+ d->m_state = Inactive;
+ d->m_mode = -1;
+ d->m_view = 0;
+
+ setBackgroundColor( white );
+ setMouseTracking( true );
+}
+
+KoFrame::~KoFrame()
+{
+ delete d;
+}
+
+void KoFrame::setView( KoView *view )
+{
+ if ( view == d->m_view )
+ return;
+
+ if ( d->m_view )
+ d->m_view->removeEventFilter( this );
+
+ d->m_view = view;
+ if ( d->m_view )
+ d->m_view->installEventFilter( this );
+
+ resizeEvent( 0L );
+}
+
+KoView *KoFrame::view() const
+{
+ return d->m_view;
+}
+
+void KoFrame::setState( State s )
+{
+ if ( d->m_state == s )
+ return;
+
+ State old = d->m_state;
+ d->m_state = s;
+
+ if ( d->m_view )
+ {
+ /*
+ kdDebug(30003) << "KoFrame::setView setMaximumSize "
+ << "QMAX32767(" << d->m_view->maximumWidth() + 2 * border() << "," << d->m_view->maximumWidth() << "), "
+ << "QMAX32767(" << d->m_view->maximumHeight() + 2 * border() << "," << d->m_view->maximumHeight() << ")"
+ << endl;
+ */
+ setMaximumSize( QMAX32767( d->m_view->maximumWidth() + 2 * border(), d->m_view->maximumWidth() ),
+ QMAX32767( d->m_view->maximumHeight() + 2 * border(), d->m_view->maximumHeight() ) );
+ setMinimumSize( d->m_view->minimumWidth() + leftBorder() + rightBorder(),
+ d->m_view->minimumHeight() + topBorder() + bottomBorder() );
+ }
+
+ if ( d->m_state == Inactive )
+ {
+ d->m_state = old;
+ int l = leftBorder();
+ int r = rightBorder();
+ int t = topBorder();
+ int b = bottomBorder();
+ d->m_state = Inactive;
+ setGeometry( x() + l, y() + t, width() - l - r, height() - t - b );
+ }
+ else if ( ( d->m_state == Active || d->m_state == Selected ) && old == Inactive )
+ setGeometry( x() - leftBorder(), y() - topBorder(),
+ width() + leftBorder() + rightBorder(),
+ height() + topBorder() + bottomBorder() );
+ else if ( d->m_state == Active && old == Selected )
+ {
+ setGeometry( x() - leftBorder() + border(), y() - topBorder() + border(),
+ width() + leftBorder() + rightBorder() - 2 * border(),
+ height() + topBorder() + bottomBorder() - 2 * border() );
+ }
+
+ update();
+}
+
+KoFrame::State KoFrame::state() const
+{
+ return d->m_state;
+}
+
+int KoFrame::leftBorder() const
+{
+ if ( d->m_state == Inactive )
+ return 0;
+ if ( d->m_state == Selected || !d->m_view )
+ return border();
+
+ return d->m_view->leftBorder() + border();
+}
+
+int KoFrame::rightBorder() const
+{
+ if ( d->m_state == Inactive )
+ return 0;
+ if ( d->m_state == Selected || !d->m_view )
+ return border();
+
+ return d->m_view->rightBorder() + border();
+}
+
+int KoFrame::topBorder() const
+{
+ if ( d->m_state == Inactive )
+ return 0;
+ if ( d->m_state == Selected || !d->m_view )
+ return border();
+
+ return d->m_view->topBorder() + border();
+}
+
+int KoFrame::bottomBorder() const
+{
+ if ( d->m_state == Inactive )
+ return 0;
+ if ( d->m_state == Selected || !d->m_view )
+ return border();
+
+ return d->m_view->bottomBorder() + border();
+}
+
+int KoFrame::border() const
+{
+ if ( d->m_state == Inactive )
+ return 0;
+ return 5;
+}
+
+void KoFrame::paintEvent( QPaintEvent* )
+{
+ QPainter painter;
+ painter.begin( this );
+
+ painter.setPen( black );
+ painter.fillRect( 0, 0, width(), height(), BDiagPattern );
+
+ if ( d->m_state == Selected )
+ {
+ painter.fillRect( 0, 0, 5, 5, black );
+ painter.fillRect( 0, height() - 5, 5, 5, black );
+ painter.fillRect( width() - 5, height() - 5, 5, 5, black );
+ painter.fillRect( width() - 5, 0, 5, 5, black );
+ painter.fillRect( width() / 2 - 3, 0, 5, 5, black );
+ painter.fillRect( width() / 2 - 3, height() - 5, 5, 5, black );
+ painter.fillRect( 0, height() / 2 - 3, 5, 5, black );
+ painter.fillRect( width() - 5, height() / 2 - 3, 5, 5, black );
+ }
+
+ painter.end();
+}
+
+void KoFrame::mousePressEvent( QMouseEvent* ev )
+{
+ QRect r1( 0, 0, 5, 5 );
+ QRect r2( 0, height() - 5, 5, 5 );
+ QRect r3( width() - 5, height() - 5, 5, 5 );
+ QRect r4( width() - 5, 0, 5, 5 );
+ QRect r5( width() / 2 - 3, 0, 5, 5 );
+ QRect r6( width() / 2 - 3, height() - 5, 5, 5 );
+ QRect r7( 0, height() / 2 - 3, 5, 5 );
+ QRect r8( width()- 5, height() / 2 - 3, 5, 5 );
+
+ if ( r1.contains( ev->pos() ) )
+ d->m_mode = 1;
+ else if ( r2.contains( ev->pos() ) )
+ d->m_mode = 2;
+ else if ( r3.contains( ev->pos() ) )
+ d->m_mode = 3;
+ else if ( r4.contains( ev->pos() ) )
+ d->m_mode = 4;
+ else if ( r5.contains( ev->pos() ) )
+ d->m_mode = 5;
+ else if ( r6.contains( ev->pos() ) )
+ d->m_mode = 6;
+ else if ( r7.contains( ev->pos() ) )
+ d->m_mode = 7;
+ else if ( r8.contains( ev->pos() ) )
+ d->m_mode = 8;
+ else
+ d->m_mode = 0;
+
+ // if ( d->m_state == Active )
+ // d->m_mode = 0;
+
+ kdDebug(30003) << "---- MODE=" << d->m_mode << endl;
+
+ d->m_mousePressPos = mapToParent( ev->pos() );
+ d->m_framePos = mapToParent( QPoint( 0, 0 ) );
+ d->m_width = width();
+ d->m_height = height();
+}
+
+void KoFrame::mouseMoveEvent( QMouseEvent* ev )
+{
+ if ( d->m_mode == 0 )
+ {
+ QPoint p = parentWidget()->mapFromGlobal( ev->globalPos() );
+ move( QPoint( p.x() + d->m_framePos.x() - d->m_mousePressPos.x(),
+ p.y() + d->m_framePos.y() - d->m_mousePressPos.y() ) );
+ // The other modes change the geometry so they call resizeEvent.
+ // This one doesn't, so it has to emit geometryChangedby itself.
+ emit geometryChanged();
+ //kdDebug() << "KoFrame::mouseMoveEvent koFrame position is " << x() << "," << y() << endl;
+ }
+ else if ( d->m_mode == 1 )
+ {
+ QPoint p = parentWidget()->mapFromGlobal( ev->globalPos() );
+ int w = QMIN( QMAX( d->m_width + d->m_mousePressPos.x() - p.x(), minimumWidth() ), maximumWidth() );
+ int h = QMIN( QMAX( d->m_height + d->m_mousePressPos.y() - p.y(), minimumHeight() ), maximumHeight() );
+ setGeometry( d->m_framePos.x() - w + d->m_width,
+ d->m_framePos.y() - h + d->m_height, w, h );
+ }
+ else if ( d->m_mode == 2 )
+ {
+ QPoint p = parentWidget()->mapFromGlobal( ev->globalPos() );
+ int w = QMIN( QMAX( d->m_width + d->m_mousePressPos.x() - p.x(), minimumWidth() ), maximumWidth() );
+ int h = QMIN( QMAX( d->m_height - d->m_mousePressPos.y() + p.y(), minimumHeight() ), maximumHeight() );
+ setGeometry( d->m_framePos.x() - w + d->m_width,
+ d->m_framePos.y(), w, h );
+ }
+ else if ( d->m_mode == 3 )
+ {
+ QPoint p = parentWidget()->mapFromGlobal( ev->globalPos() );
+ int w = QMIN( QMAX( d->m_width - d->m_mousePressPos.x() + p.x(), minimumWidth() ), maximumWidth() );
+ int h = QMIN( QMAX( d->m_height - d->m_mousePressPos.y() + p.y(), minimumHeight() ), maximumHeight() );
+ resize( w, h );
+ }
+ else if ( d->m_mode == 4 )
+ {
+ QPoint p = parentWidget()->mapFromGlobal( ev->globalPos() );
+ int w = QMIN( QMAX( d->m_width - d->m_mousePressPos.x() + p.x(), minimumWidth() ), maximumWidth() );
+ int h = QMIN( QMAX( d->m_height + d->m_mousePressPos.y() - p.y(), minimumHeight() ), maximumHeight() );
+ setGeometry( d->m_framePos.x(),
+ d->m_framePos.y() - h + d->m_height, w, h );
+ }
+ else if ( d->m_mode == 5 )
+ {
+ QPoint p = parentWidget()->mapFromGlobal( ev->globalPos() );
+ int h = QMIN( QMAX( d->m_height + d->m_mousePressPos.y() - p.y(), minimumHeight() ), maximumHeight() );
+ setGeometry( d->m_framePos.x(),
+ d->m_framePos.y() - h + d->m_height, d->m_width, h );
+ }
+ else if ( d->m_mode == 6 )
+ {
+ QPoint p = parentWidget()->mapFromGlobal( ev->globalPos() );
+ int h = QMIN( QMAX( d->m_height - d->m_mousePressPos.y() + p.y(), minimumHeight() ), maximumHeight() );
+ resize( d->m_width, h );
+ }
+ else if ( d->m_mode == 7 )
+ {
+ QPoint p = parentWidget()->mapFromGlobal( ev->globalPos() );
+ int w = QMIN( QMAX( d->m_width + d->m_mousePressPos.x() - p.x(), minimumWidth() ), maximumWidth() );
+ setGeometry( d->m_framePos.x() - w + d->m_width,
+ d->m_framePos.y(), w, d->m_height );
+ }
+ else if ( d->m_mode == 8 )
+ {
+ QPoint p = parentWidget()->mapFromGlobal( ev->globalPos() );
+ int w = QMIN( QMAX( d->m_width - d->m_mousePressPos.x() + p.x(), minimumWidth() ), maximumWidth() );
+ resize( w, d->m_height );
+ }
+ else if ( d->m_state == Selected || d->m_state == Active )
+ {
+ QRect r1( 0, 0, 5, 5 );
+ QRect r2( 0, height() - 5, 5, 5 );
+ QRect r3( width() - 5, height() - 5, 5, 5 );
+ QRect r4( width() - 5, 0, 5, 5 );
+ QRect r5( width() / 2 - 3, 0, 5, 5 );
+ QRect r6( width() / 2 - 3, height() - 5, 5, 5 );
+ QRect r7( 0, height() / 2 - 3, 5, 5 );
+ QRect r8( width()- 5, height() / 2 - 3, 5, 5 );
+
+ if ( r1.contains( ev->pos() ) || r3.contains( ev->pos() ) )
+ setCursor( sizeFDiagCursor );
+ else if ( r2.contains( ev->pos() ) || r4.contains( ev->pos() ) )
+ setCursor( sizeBDiagCursor );
+ else if ( r5.contains( ev->pos() ) || r6.contains( ev->pos() ) )
+ setCursor( sizeVerCursor );
+ else if ( r7.contains( ev->pos() ) || r8.contains( ev->pos() ) )
+ setCursor( sizeHorCursor );
+ else
+ setCursor( KCursor::handCursor() );
+ }
+ else
+ setCursor( KCursor::handCursor() );
+}
+
+void KoFrame::mouseReleaseEvent( QMouseEvent* )
+{
+ d->m_mode = -1;
+}
+
+void KoFrame::resizeEvent( QResizeEvent* )
+{
+ if ( !d->m_view )
+ return;
+
+ if ( d->m_state == Active || d->m_state == Selected )
+ d->m_view->setGeometry( 5, 5, width() - 10, height() - 10 );
+ else
+ d->m_view->setGeometry( 0, 0, width(), height() );
+
+ emit geometryChanged();
+}
+
+bool KoFrame::eventFilter( QObject* obj, QEvent* ev )
+{
+ if ( obj == d->m_view && KParts::PartActivateEvent::test( ev ) )
+ {
+ kdDebug(30003) << "Activate event"<< endl;
+ KParts::PartActivateEvent* e = (KParts::PartActivateEvent*)ev;
+ if ( e->part() == (KParts::Part *)d->m_view->koDocument() )
+ {
+ if ( e->activated() )
+ setState( Active );
+ else
+ setState( Inactive );
+ }
+ }
+ else if ( obj == d->m_view && KParts::PartSelectEvent::test( ev ) )
+ {
+ kdDebug(30003) << "Selected event" << endl;
+ KParts::PartSelectEvent* e = (KParts::PartSelectEvent*)ev;
+ if ( e->part() == (KParts::Part *)d->m_view->koDocument() )
+ {
+ if ( e->selected() )
+ setState( Selected );
+ else
+ setState( Inactive );
+ }
+ }
+
+ return false;
+}
+
+#include <KoFrame.moc>
diff --git a/lib/kofficecore/KoFrame.h b/lib/kofficecore/KoFrame.h
new file mode 100644
index 00000000..7f729bdb
--- /dev/null
+++ b/lib/kofficecore/KoFrame.h
@@ -0,0 +1,65 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
+
+ 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 __koFrame_h__
+#define __koFrame_h__
+
+#include <qwidget.h>
+
+class KoView;
+class KoFramePrivate;
+
+class KoFrame : public QWidget
+{
+ Q_OBJECT
+public:
+ enum State { Inactive, Selected, Active };
+
+ KoFrame( QWidget *parent, const char *name = 0 );
+ virtual ~KoFrame();
+
+ virtual void setView( KoView *view );
+ virtual KoView *view() const;
+
+ virtual void setState( State s );
+ virtual State state() const;
+
+ virtual int leftBorder() const;
+ virtual int rightBorder() const;
+ virtual int topBorder() const;
+ virtual int bottomBorder() const;
+
+ virtual int border() const;
+
+signals:
+ void geometryChanged();
+
+protected:
+ virtual void paintEvent( QPaintEvent* );
+ virtual void mousePressEvent( QMouseEvent* );
+ virtual void mouseMoveEvent( QMouseEvent* );
+ virtual void mouseReleaseEvent( QMouseEvent* );
+ virtual void resizeEvent( QResizeEvent* );
+ virtual bool eventFilter( QObject*, QEvent* );
+
+private:
+ KoFramePrivate *d;
+};
+
+#endif
diff --git a/lib/kofficecore/KoGenStyles.cpp b/lib/kofficecore/KoGenStyles.cpp
new file mode 100644
index 00000000..503b6802
--- /dev/null
+++ b/lib/kofficecore/KoGenStyles.cpp
@@ -0,0 +1,405 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2006 David Faure <faure@kde.org>
+
+ 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 "KoGenStyles.h"
+#include <KoXmlWriter.h>
+#include <float.h>
+#include <kdebug.h>
+
+KoGenStyles::KoGenStyles()
+{
+}
+
+KoGenStyles::~KoGenStyles()
+{
+}
+
+QString KoGenStyles::lookup( const KoGenStyle& style, const QString& name, int flags )
+{
+ StyleMap::iterator it = m_styleMap.find( style );
+ if ( it == m_styleMap.end() ) {
+ // Not found, try if this style is in fact equal to its parent (the find above
+ // wouldn't have found it, due to m_parentName being set).
+ if ( !style.parentName().isEmpty() ) {
+ KoGenStyle testStyle( style );
+ const KoGenStyle* parentStyle = this->style( style.parentName() ); // ## linear search
+ if( !parentStyle ) {
+ kdDebug(30003) << "KoGenStyles::lookup(" << name << "): parent style '" << style.parentName() << "' not found in collection" << endl;
+ } else {
+ if ( testStyle.m_familyName != parentStyle->m_familyName )
+ {
+ kdWarning(30003) << "KoGenStyles::lookup(" << name << ", family=" << testStyle.m_familyName << ") parent style '" << style.parentName() << "' has a different family: " << parentStyle->m_familyName << endl;
+ }
+
+ testStyle.m_parentName = parentStyle->m_parentName;
+ // Exclude the type from the comparison. It's ok for an auto style
+ // to have a user style as parent; they can still be identical
+ testStyle.m_type = parentStyle->m_type;
+ // Also it's ok to not have the display name of the parent style
+ // in the auto style
+ QMap<QString, QString>::const_iterator it = parentStyle->m_attributes.find( "style:display-name" );
+ if ( it != parentStyle->m_attributes.end() )
+ testStyle.addAttribute( "style:display-name", *it );
+
+ if ( *parentStyle == testStyle )
+ return style.parentName();
+ }
+ }
+
+ QString styleName( name );
+ if ( styleName.isEmpty() ) {
+ styleName = 'A'; // for "auto".
+ flags &= ~DontForceNumbering; // i.e. force numbering
+ }
+ styleName = makeUniqueName( styleName, flags );
+ if ( style.autoStyleInStylesDotXml() )
+ m_autoStylesInStylesDotXml.insert( styleName, true /*unused*/ );
+ else
+ m_styleNames.insert( styleName, true /*unused*/ );
+ it = m_styleMap.insert( style, styleName );
+ NamedStyle s;
+ s.style = &it.key();
+ s.name = styleName;
+ m_styleArray.append( s );
+ }
+ return it.data();
+}
+
+QString KoGenStyles::makeUniqueName( const QString& base, int flags ) const
+{
+ // If this name is not used yet, and numbering isn't forced, then the given name is ok.
+ if ( ( flags & DontForceNumbering )
+ && m_autoStylesInStylesDotXml.find( base ) == m_autoStylesInStylesDotXml.end()
+ && m_styleNames.find( base ) == m_styleNames.end() )
+ return base;
+ int num = 1;
+ QString name;
+ do {
+ name = base;
+ name += QString::number( num++ );
+ } while ( m_autoStylesInStylesDotXml.find( name ) != m_autoStylesInStylesDotXml.end()
+ || m_styleNames.find( name ) != m_styleNames.end() );
+ return name;
+}
+
+QValueList<KoGenStyles::NamedStyle> KoGenStyles::styles( int type, bool markedForStylesXml ) const
+{
+ QValueList<KoGenStyles::NamedStyle> lst;
+ const NameMap& nameMap = markedForStylesXml ? m_autoStylesInStylesDotXml : m_styleNames;
+ StyleArray::const_iterator it = m_styleArray.begin();
+ const StyleArray::const_iterator end = m_styleArray.end();
+ for ( ; it != end ; ++it ) {
+ // Look up if it's marked for styles.xml or not by looking up in the corresponding style map.
+ if ( (*it).style->type() == type && nameMap.find((*it).name) != nameMap.end() ) {
+ lst.append( *it );
+ }
+ }
+ return lst;
+}
+
+const KoGenStyle* KoGenStyles::style( const QString& name ) const
+{
+ StyleArray::const_iterator it = m_styleArray.begin();
+ const StyleArray::const_iterator end = m_styleArray.end();
+ for ( ; it != end ; ++it ) {
+ if ( (*it).name == name )
+ return (*it).style;
+ }
+ return 0;
+}
+
+KoGenStyle* KoGenStyles::styleForModification( const QString& name )
+{
+ return const_cast<KoGenStyle *>( style( name ) );
+}
+
+void KoGenStyles::markStyleForStylesXml( const QString& name )
+{
+ Q_ASSERT( m_styleNames.find( name ) != m_styleNames.end() );
+ m_styleNames.remove( name );
+ m_autoStylesInStylesDotXml.insert( name, true );
+ styleForModification( name )->setAutoStyleInStylesDotXml( true );
+}
+
+void KoGenStyles::dump()
+{
+ kdDebug() << "Style array:" << endl;
+ StyleArray::const_iterator it = m_styleArray.begin();
+ const StyleArray::const_iterator end = m_styleArray.end();
+ for ( ; it != end ; ++it ) {
+ kdDebug() << (*it).name << endl;
+ }
+ for ( NameMap::const_iterator it = m_styleNames.begin(); it != m_styleNames.end(); ++it ) {
+ kdDebug() << "style: " << it.key() << endl;
+ }
+ for ( NameMap::const_iterator it = m_autoStylesInStylesDotXml.begin(); it != m_autoStylesInStylesDotXml.end(); ++it ) {
+ kdDebug() << "auto style for style.xml: " << it.key() << endl;
+ const KoGenStyle* s = style( it.key() );
+ Q_ASSERT( s );
+ Q_ASSERT( s->autoStyleInStylesDotXml() );
+ }
+}
+
+// Returns -1, 0 (equal) or 1
+static int compareMap( const QMap<QString, QString>& map1, const QMap<QString, QString>& map2 )
+{
+ QMap<QString, QString>::const_iterator it = map1.begin();
+ QMap<QString, QString>::const_iterator oit = map2.begin();
+ for ( ; it != map1.end(); ++it, ++oit ) { // both maps have been checked for size already
+ if ( it.key() != oit.key() )
+ return it.key() < oit.key() ? -1 : +1;
+ if ( it.data() != oit.data() )
+ return it.data() < oit.data() ? -1 : +1;
+ }
+ return 0; // equal
+}
+
+////
+
+
+KoGenStyle::KoGenStyle( int type, const char* familyName,
+ const QString& parentName )
+ : m_type( type ), m_familyName( familyName ), m_parentName( parentName ),
+ m_autoStyleInStylesDotXml( false ), m_defaultStyle( false )
+{
+}
+
+KoGenStyle::~KoGenStyle()
+{
+}
+
+void KoGenStyle::writeStyleProperties( KoXmlWriter* writer, PropertyType i,
+ const char* elementName, const KoGenStyle* parentStyle ) const
+{
+ if ( !m_properties[i].isEmpty() ) {
+ writer->startElement( elementName );
+ QMap<QString, QString>::const_iterator it = m_properties[i].begin();
+ const QMap<QString, QString>::const_iterator end = m_properties[i].end();
+ for ( ; it != end; ++it ) {
+ if ( !parentStyle || parentStyle->property( it.key(), i ) != it.data() )
+ writer->addAttribute( it.key().utf8(), it.data().utf8() );
+ }
+ writer->endElement();
+ }
+}
+
+void KoGenStyle::writeStyle( KoXmlWriter* writer, KoGenStyles& styles, const char* elementName, const QString& name, const char* propertiesElementName, bool closeElement, bool drawElement ) const
+{
+ //kdDebug(30003) << "writing out style " << name << " display-name=" << m_attributes["style:display-name"] << " family=" << m_familyName << endl;
+ writer->startElement( elementName );
+ const KoGenStyle* parentStyle = 0;
+ if ( !m_defaultStyle ) {
+ if ( !drawElement )
+ writer->addAttribute( "style:name", name );
+ else
+ writer->addAttribute( "draw:name", name );
+ if ( !m_parentName.isEmpty() ) {
+ parentStyle = styles.style( m_parentName );
+ if ( parentStyle && m_familyName.isEmpty() ) {
+ // get family from parent style, just in case
+ // Note: this is saving code, don't convert to attributeNS!
+ const_cast<KoGenStyle *>( this )->
+ m_familyName = parentStyle->attribute( "style:family" ).latin1();
+ //kdDebug(30003) << "Got familyname " << m_familyName << " from parent" << endl;
+ }
+ writer->addAttribute( "style:parent-style-name", m_parentName );
+ }
+ } else { // default-style
+ Q_ASSERT( qstrcmp( elementName, "style:default-style" ) == 0 );
+ Q_ASSERT( m_parentName.isEmpty() );
+ }
+ if ( !m_familyName.isEmpty() )
+ const_cast<KoGenStyle *>( this )->
+ addAttribute( "style:family", QString::fromLatin1( m_familyName ) );
+ else {
+ if ( qstrcmp( elementName, "style:style" ) == 0 )
+ kdWarning(30003) << "User style " << name << " is without family - invalid. m_type=" << m_type << endl;
+ }
+
+#if 0 // #ifndef NDEBUG
+ kdDebug() << "style: " << name << endl;
+ printDebug();
+ if ( parentStyle ) {
+ kdDebug() << " parent: " << m_parentName << endl;
+ parentStyle->printDebug();
+ }
+#endif
+
+ // Write attributes [which differ from the parent style]
+ // We only look at the direct parent style because we assume
+ // that styles are fully specified, i.e. the inheritance is
+ // only in the final file, not in the caller's code.
+ QMap<QString, QString>::const_iterator it = m_attributes.begin();
+ for ( ; it != m_attributes.end(); ++it ) {
+ bool writeit = true;
+ if ( parentStyle && it.key() != "style:family" // always write the family out
+ && parentStyle->attribute( it.key() ) == it.data() )
+ writeit = false;
+ if ( writeit )
+ writer->addAttribute( it.key().utf8(), it.data().utf8() );
+ }
+ bool createPropertiesTag = propertiesElementName && propertiesElementName[0] != '\0';
+ KoGenStyle::PropertyType i = KoGenStyle::DefaultType;
+ if ( !m_properties[i].isEmpty() ||
+ !m_properties[KoGenStyle::ChildElement].isEmpty() ) {
+ if ( createPropertiesTag )
+ writer->startElement( propertiesElementName ); // e.g. paragraph-properties
+ it = m_properties[i].begin();
+ for ( ; it != m_properties[i].end(); ++it ) {
+ if ( !parentStyle || parentStyle->property( it.key(), i ) != it.data() )
+ writer->addAttribute( it.key().utf8(), it.data().utf8() );
+ }
+ i = KoGenStyle::ChildElement;
+ it = m_properties[i].begin();
+ for ( ; it != m_properties[i].end(); ++it ) {
+ if ( !parentStyle || parentStyle->property( it.key(), i ) != it.data() ) {
+ writer->addCompleteElement( it.data().utf8() );
+ }
+ }
+ if ( createPropertiesTag )
+ writer->endElement();
+ }
+ writeStyleProperties( writer, KoGenStyle::GraphicType, "style:graphic-properties", parentStyle );
+ writeStyleProperties( writer, KoGenStyle::ParagraphType, "style:paragraph-properties", parentStyle );
+ writeStyleProperties( writer, KoGenStyle::TextType, "style:text-properties", parentStyle );
+
+ // And now the style maps
+ for ( uint i = 0; i < m_maps.count(); ++i ) {
+ bool writeit = true;
+ if ( parentStyle && compareMap( m_maps[i], parentStyle->m_maps[i] ) == 0 )
+ writeit = false;
+ if ( writeit ) {
+ writer->startElement( "style:map" );
+ QMap<QString, QString>::const_iterator it = m_maps[i].begin();
+ for ( ; it != m_maps[i].end(); ++it ) {
+ writer->addAttribute( it.key().utf8(), it.data().utf8() );
+ }
+ writer->endElement(); // style:map
+ }
+ }
+ if ( closeElement )
+ writer->endElement();
+}
+
+void KoGenStyle::addPropertyPt( const QString& propName, double propValue, PropertyType type )
+{
+ QString str;
+ str.setNum( propValue, 'g', DBL_DIG );
+ str += "pt";
+ m_properties[type].insert( propName, str );
+}
+
+void KoGenStyle::addAttributePt( const QString& attrName, double attrValue )
+{
+ QString str;
+ str.setNum( attrValue, 'g', DBL_DIG );
+ str += "pt";
+ m_attributes.insert( attrName, str );
+}
+
+#ifndef NDEBUG
+void KoGenStyle::printDebug() const
+{
+ int i = DefaultType;
+ kdDebug() << m_properties[i].count() << " properties." << endl;
+ for( QMap<QString,QString>::ConstIterator it = m_properties[i].begin(); it != m_properties[i].end(); ++it ) {
+ kdDebug() << " " << it.key() << " = " << it.data() << endl;
+ }
+ i = TextType;
+ kdDebug() << m_properties[i].count() << " text properties." << endl;
+ for( QMap<QString,QString>::ConstIterator it = m_properties[i].begin(); it != m_properties[i].end(); ++it ) {
+ kdDebug() << " " << it.key() << " = " << it.data() << endl;
+ }
+ i = ParagraphType;
+ kdDebug() << m_properties[i].count() << " paragraph properties." << endl;
+ for( QMap<QString,QString>::ConstIterator it = m_properties[i].begin(); it != m_properties[i].end(); ++it ) {
+ kdDebug() << " " << it.key() << " = " << it.data() << endl;
+ }
+ i = ChildElement;
+ kdDebug() << m_properties[i].count() << " child elements." << endl;
+ for( QMap<QString,QString>::ConstIterator it = m_properties[i].begin(); it != m_properties[i].end(); ++it ) {
+ kdDebug() << " " << it.key() << " = " << it.data() << endl;
+ }
+ kdDebug() << m_attributes.count() << " attributes." << endl;
+ for( QMap<QString,QString>::ConstIterator it = m_attributes.begin(); it != m_attributes.end(); ++it ) {
+ kdDebug() << " " << it.key() << " = " << it.data() << endl;
+ }
+ kdDebug() << m_maps.count() << " maps." << endl;
+ for ( uint i = 0; i < m_maps.count(); ++i ) {
+ kdDebug() << "map " << i << ":" << endl;
+ for( QMap<QString,QString>::ConstIterator it = m_maps[i].begin(); it != m_maps[i].end(); ++it ) {
+ kdDebug() << " " << it.key() << " = " << it.data() << endl;
+ }
+ }
+ kdDebug() << endl;
+}
+#endif
+
+bool KoGenStyle::operator<( const KoGenStyle &other ) const
+{
+ if ( m_type != other.m_type ) return m_type < other.m_type;
+ if ( m_parentName != other.m_parentName ) return m_parentName < other.m_parentName;
+ if ( m_autoStyleInStylesDotXml != other.m_autoStyleInStylesDotXml ) return m_autoStyleInStylesDotXml;
+ for ( uint i = 0 ; i < N_NumTypes ; ++i )
+ if ( m_properties[i].count() != other.m_properties[i].count() )
+ return m_properties[i].count() < other.m_properties[i].count();
+ if ( m_attributes.count() != other.m_attributes.count() ) return m_attributes.count() < other.m_attributes.count();
+ if ( m_maps.count() != other.m_maps.count() ) return m_maps.count() < other.m_maps.count();
+ // Same number of properties and attributes, no other choice than iterating
+ for ( uint i = 0 ; i < N_NumTypes ; ++i ) {
+ int comp = compareMap( m_properties[i], other.m_properties[i] );
+ if ( comp != 0 )
+ return comp < 0;
+ }
+ int comp = compareMap( m_attributes, other.m_attributes );
+ if ( comp != 0 )
+ return comp < 0;
+ for ( uint i = 0 ; i < m_maps.count() ; ++i ) {
+ int comp = compareMap( m_maps[i], other.m_maps[i] );
+ if ( comp != 0 )
+ return comp < 0;
+ }
+ return false;
+}
+
+bool KoGenStyle::operator==( const KoGenStyle &other ) const
+{
+ if ( m_type != other.m_type ) return false;
+ if ( m_parentName != other.m_parentName ) return false;
+ if ( m_autoStyleInStylesDotXml != other.m_autoStyleInStylesDotXml ) return false;
+ for ( uint i = 0 ; i < N_NumTypes ; ++i )
+ if ( m_properties[i].count() != other.m_properties[i].count() )
+ return false;
+ if ( m_attributes.count() != other.m_attributes.count() ) return false;
+ if ( m_maps.count() != other.m_maps.count() ) return false;
+ // Same number of properties and attributes, no other choice than iterating
+ for ( uint i = 0 ; i < N_NumTypes ; ++i ) {
+ int comp = compareMap( m_properties[i], other.m_properties[i] );
+ if ( comp != 0 )
+ return false;
+ }
+ int comp = compareMap( m_attributes, other.m_attributes );
+ if ( comp != 0 )
+ return false;
+ for ( uint i = 0 ; i < m_maps.count() ; ++i ) {
+ int comp = compareMap( m_maps[i], other.m_maps[i] );
+ if ( comp != 0 )
+ return false;
+ }
+ return true;
+}
diff --git a/lib/kofficecore/KoGenStyles.h b/lib/kofficecore/KoGenStyles.h
new file mode 100644
index 00000000..ad05d6e0
--- /dev/null
+++ b/lib/kofficecore/KoGenStyles.h
@@ -0,0 +1,463 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2006 David Faure <faure@kde.org>
+
+ 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 KOGENSTYLES_H
+#define KOGENSTYLES_H
+
+#include <qdict.h>
+#include <qmap.h>
+#include <qvaluevector.h>
+
+#include <koffice_export.h>
+
+class KoXmlWriter;
+class KoGenStyle;
+
+/**
+ * @brief Repository of styles used during saving of OASIS/OOo file.
+ *
+ * Each instance of KoGenStyles is a collection of styles whose names
+ * are in the same "namespace".
+ * This means there should be one instance for all styles in <office:styles>,
+ * and automatic-styles, another instance for number formats, another
+ * one for draw styles, and another one for list styles.
+ *
+ * "Style" in this context only means "a collection of properties".
+ * The "Gen" means both "Generic" and "Generated" :)
+ *
+ * The basic design principle is the flyweight pattern: if you need
+ * a style with the same properties from two different places, you
+ * get the same object. Here it means rather you get the same name for the style,
+ * and it will get saved only once to the file.
+ *
+ * KoGenStyles features sharing, creation on demand, and name generation.
+ * Since this is used for saving only, it doesn't feature refcounting, nor
+ * removal of individual styles.
+ *
+ * NOTE: the use of KoGenStyles isn't mandatory, of course. If the application
+ * is already designed with user and automatic styles in mind for a given
+ * set of properties, it can go ahead and save all styles directly (after
+ * ensuring they have unique names).
+ *
+ * @author David Faure <faure@kde.org>
+ */
+class KOFFICECORE_EXPORT KoGenStyles
+{
+public:
+ KoGenStyles();
+ ~KoGenStyles();
+
+ /**
+ * Those are flags for the lookup() call.
+ *
+ * By default, the generated style names will look like "name1", "name2".
+ * If DontForceNumbering is set, the first name that will be tried is "name", and only if
+ * that one exists, then "name1" is tried. Set DontForceNumbering if the name given as
+ * argument is supposed to be the full style name.
+ *
+ */
+ enum Flags { // bitfield
+ NoFlag = 0,
+ ForceNumbering = 0, // it's the default anyway
+ DontForceNumbering = 1
+ };
+ // KDE4 TODO: use QFlags and change the arg type in lookup
+
+ /**
+ * Look up a style in the collection, inserting it if necessary.
+ * This assigns a name to the style and returns it.
+ *
+ * @param style the style to look up.
+ * @param name proposed (base) name for the style. Note that with the OASIS format,
+ * the style name is never shown to the user (there's a separate display-name
+ * attribute for that). So there are little reasons to use named styles anyway.
+ * But this attribute can be used for clarity of the files.
+ * If this name is already in use (for another style), then a number is appended
+ * to it until unique.
+ * @param flags see Flags
+ *
+ * @return the name for this style
+ * @todo ### rename lookup to insert
+ */
+ QString lookup( const KoGenStyle& style, const QString& name = QString::null, int flags = NoFlag );
+
+ typedef QMap<KoGenStyle, QString> StyleMap;
+ /**
+ * Return the entire collection of styles
+ * Use this for saving the styles
+ */
+ const StyleMap& styles() const { return m_styleMap; }
+
+ struct NamedStyle {
+ const KoGenStyle* style; ///< @note owned by the collection
+ QString name;
+ };
+ /**
+ * Return all styles of a given type
+ * Use this for saving the styles
+ *
+ * @param type the style type, see the KoGenStyle constructor
+ * @param markedForStylesXml if true, return only style marked for styles.xml,
+ * otherwise only those NOT marked for styles.xml.
+ * @see lookup
+ */
+ QValueList<NamedStyle> styles( int type, bool markedForStylesXml = false ) const;
+
+ /**
+ * @return an existing style by name
+ */
+ const KoGenStyle* style( const QString& name ) const;
+
+ /**
+ * @return an existing style by name, which can be modified.
+ * @warning This is DANGEROUS.
+ * It basically defeats the purpose of lookup()!
+ * Only do this if you know for sure no other 'user' of that style will
+ * be affected.
+ */
+ KoGenStyle* styleForModification( const QString& name );
+
+ /**
+ * Mark a given automatic style as being needed in styles.xml.
+ * For instance styles used by headers and footers need to go there, since
+ * they are saved in styles.xml, and styles.xml must be independent from content.xml.
+ *
+ * Equivalent to using KoGenStyle::setAutoStyleInStylesDotXml() but this can be done after lookup.
+ *
+ * This operation can't be undone; once styles are promoted they can't go back
+ * to being content.xml-only.
+ *
+ * @see styles, KoGenStyle::setAutoStyleInStylesDotXml
+ */
+ void markStyleForStylesXml( const QString& name );
+
+ /**
+ * Outputs debug information
+ */
+ void dump();
+
+private:
+ QString makeUniqueName( const QString& base, int flags ) const;
+
+ /// style definition -> name
+ StyleMap m_styleMap;
+
+ /// Map with the style name as key.
+ /// This map is mainly used to check for name uniqueness
+ /// The value of the bool doesn't matter.
+ typedef QMap<QString, bool> NameMap; // KDE4: QSet
+ NameMap m_styleNames;
+ NameMap m_autoStylesInStylesDotXml;
+
+ /// List of styles (used to preserve ordering)
+ typedef QValueVector<NamedStyle> StyleArray;
+ StyleArray m_styleArray;
+
+ class Private;
+ Private *d;
+};
+
+/**
+ * A generic style, i.e. basically a collection of properties and a name.
+ * Instances of KoGenStyle can either be held in the KoGenStyles collection,
+ * or created (e.g. on the stack) and given to KoGenStyles::lookup.
+ *
+ * @author David Faure <faure@kde.org>
+ */
+class KOFFICECORE_EXPORT KoGenStyle
+{
+public:
+ /**
+ * Possible values for the "type" argument of the KoGenStyle constructor.
+ * Those values can be extended by applications (starting at number 20),
+ * it's for their own consumption anyway.
+ * (The reason for having the very common ones here, is to make it possible to
+ * use them from libkotext).
+ */
+ enum { STYLE_PAGELAYOUT = 0,
+ STYLE_USER = 1,
+ STYLE_AUTO = 2,
+ STYLE_MASTER = 3,
+ STYLE_LIST = 4,
+ STYLE_AUTO_LIST = 5,
+ STYLE_NUMERIC_NUMBER = 6,
+ STYLE_NUMERIC_DATE = 7,
+ STYLE_NUMERIC_TIME = 8,
+ STYLE_NUMERIC_FRACTION = 9,
+ STYLE_NUMERIC_PERCENTAGE = 10,
+ STYLE_NUMERIC_SCIENTIFIC = 11,
+ STYLE_NUMERIC_CURRENCY = 12,
+ STYLE_NUMERIC_TEXT = 13,
+ STYLE_HATCH = 14,
+ STYLE_GRAPHICAUTO = 15};
+
+ /**
+ * Start the definition of a new style. Its name will be set later by KoGenStyles::lookup(),
+ * but first you must define its properties and attributes.
+ *
+ * @param type this is a hook for the application to categorize styles
+ * See the STYLE_* enum. Ignored when writing out the style.
+ *
+ * @param familyName The value for style:family, e.g. text, paragraph, graphic etc.
+ * The family is for style:style elements only; number styles and list styles don't have one.
+ *
+ * @param parentName If set, name of the parent style from which this one inherits.
+ */
+ explicit KoGenStyle( int type = 0, const char* familyName = 0,
+ const QString& parentName = QString::null );
+ ~KoGenStyle();
+
+ /*
+ * setAutoStyleInStylesDotXml(true) marks a given automatic style as being needed in styles.xml.
+ * For instance styles used by headers and footers need to go there, since
+ * they are saved in styles.xml, and styles.xml must be independent from content.xml.
+ *
+ * The application should use KoGenStyles::styles( type, true ) in order to retrieve
+ * those styles and save them separately.
+ */
+ void setAutoStyleInStylesDotXml( bool b ) { m_autoStyleInStylesDotXml = b; }
+ /// @return the value passed to setAutoStyleInStylesDotXml; false by default
+ bool autoStyleInStylesDotXml() const { return m_autoStyleInStylesDotXml; }
+
+ /*
+ * setDefaultStyle(true) marks a given style as being the default style.
+ * This means we expect that you will call writeStyle( ...,"style:default-style"),
+ * and its name will be ommitted in the output.
+ */
+ void setDefaultStyle( bool b ) { m_defaultStyle = b; }
+ /// @return the value passed to setDefaultStyle; false by default
+ bool isDefaultStyle() const { return m_defaultStyle; }
+
+ /// Return the type of this style, as set in the constructor
+ int type() const { return m_type; }
+
+ /// Return the family name
+ const char* familyName() const { return m_familyName.data(); }
+
+ /// Return the name of style's parent, if set
+ QString parentName() const { return m_parentName; }
+
+ /**
+ * @brief The types of properties
+ *
+ * Simple styles only write one foo-properties tag, in which case they can just use DefaultType.
+ * However a given style might want to write several kinds of properties, in which case it would
+ * need to use other property types than the default one.
+ *
+ * For instance this style:
+ * @code
+ * <style:style style:family="chart">
+ * <style:chart-properties .../>
+ * <style:graphic-properties .../>
+ * <style:text-properties .../>
+ * </style:style>
+ * @endcode
+ * would use DefaultType for chart-properties (and would pass "style:chart-properties" to writeStyle(),
+ * and would use GraphicType and TextType.
+ */
+ enum PropertyType
+ {
+ /**
+ * DefaultType depends on family: e.g. paragraph-properties if family=paragraph
+ * or on the type of style (e.g. page-layout -> page-layout-properties).
+ * (In fact that tag name is the one passed to writeStyle)
+ */
+ DefaultType = 0,
+ /// TextType is always text-properties.
+ TextType,
+ /// ParagraphType is always paragraph-properties.
+ ParagraphType,
+ /// GraphicType is always graphic-properties.
+ GraphicType,
+ Reserved1, ///< @internal for binary compatible extensions
+ Reserved2, ///< @internal for binary compatible extensions
+ ChildElement, ///< @internal
+ N_NumTypes ///< @internal - warning, adding items here affects binary compatibility (size of KoGenStyle)
+ };
+
+ /// Add a property to the style
+ void addProperty( const QString& propName, const QString& propValue, PropertyType type = DefaultType ) {
+ m_properties[type].insert( propName, propValue );
+ }
+ /// Overloaded version of addProperty that takes a char*, usually for "..."
+ void addProperty( const QString& propName, const char* propValue, PropertyType type = DefaultType ) {
+ m_properties[type].insert( propName, QString::fromUtf8( propValue ) );
+ }
+ /// Overloaded version of addProperty that converts an int to a string
+ void addProperty( const QString& propName, int propValue, PropertyType type = DefaultType ) {
+ m_properties[type].insert( propName, QString::number( propValue ) );
+ }
+ /// Overloaded version of addProperty that converts a bool to a string (false/true)
+ void addProperty( const QString& propName, bool propValue, PropertyType type = DefaultType ) {
+ m_properties[type].insert( propName, propValue ? "true" : "false" );
+ }
+
+ /**
+ * Add a property which represents a distance, measured in pt
+ * The number is written out with the highest possible precision
+ * (unlike QString::number and setNum, which default to 6 digits),
+ * and the unit name ("pt") is appended to it.
+ */
+ void addPropertyPt( const QString& propName, double propValue, PropertyType type = DefaultType );
+
+ /**
+ * Add an attribute to the style
+ * The difference between property and attributes is a bit oasis-format-specific:
+ * attributes are for the style element itself, and properties are in the style:properties child element
+ */
+ void addAttribute( const QString& attrName, const QString& attrValue ) {
+ m_attributes.insert( attrName, attrValue );
+ }
+ /// Overloaded version of addAttribute that takes a char*, usually for "..."
+ void addAttribute( const QString& attrName, const char* attrValue ) {
+ m_attributes.insert( attrName, QString::fromUtf8( attrValue ) );
+ }
+ /// Overloaded version of addAttribute that converts an int to a string
+ void addAttribute( const QString& attrName, int attrValue ) {
+ m_attributes.insert( attrName, QString::number( attrValue ) );
+ }
+
+ /// Overloaded version of addAttribute that converts a bool to a string
+ void addAttribute( const QString& attrName, bool attrValue ) {
+ m_attributes.insert( attrName, attrValue ? "true" : "false" );
+ }
+
+ /**
+ * Add an attribute which represents a distance, measured in pt
+ * The number is written out with the highest possible precision
+ * (unlike QString::number and setNum, which default to 6 digits),
+ * and the unit name ("pt") is appended to it.
+ */
+ void addAttributePt( const QString& attrName, double attrValue );
+
+ /**
+ * @brief Add a child element to the style properties.
+ *
+ * What is meant here is that the contents of the QString
+ * will be written out literally. This means you should use
+ * KoXmlWriter to generate it:
+ * @code
+ * QBuffer buffer;
+ * buffer.open( IO_WriteOnly );
+ * KoXmlWriter elementWriter( &buffer ); // TODO pass indentation level
+ * elementWriter.startElement( "..." );
+ * ...
+ * elementWriter.endElement();
+ * QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
+ * gs.addChildElement( "...", elementContents );
+ * @endcode
+ *
+ * The value of @p elementName isn't used, except that it must be unique.
+ */
+ void addChildElement( const QString& elementName, const QString& elementContents ) {
+ m_properties[ChildElement].insert( elementName, elementContents );
+ }
+
+ /**
+ * @brief Add a style:map to the style.
+ * @param styleMap the attributes for the map, associated as (name,value).
+ */
+ void addStyleMap( const QMap<QString, QString>& styleMap ) {
+ m_maps.append( styleMap );
+ }
+
+ /**
+ * @return true if the style has no attributes, no properties, no style map etc.
+ * This can be used by applications which do not save all attributes unconditionally,
+ * but only those that differ from the parent. But note that lookup() can't find this out...
+ */
+ bool isEmpty() const {
+ if ( !m_attributes.isEmpty() || ! m_maps.isEmpty() )
+ return false;
+ for ( uint i = 0 ; i < N_NumTypes ; ++i )
+ if ( ! m_properties[i].isEmpty() )
+ return false;
+ return true;
+ }
+
+ /**
+ * Write the definition of this style to @p writer, using the OASIS format.
+ * @param writer the KoXmlWriter in which @p elementName will be created and filled in
+ * @param styles the styles collection, used to look up the parent style
+ * @param elementName the name of the XML element, e.g. "style:style". Don't forget to
+ * pass style:default-style if isDefaultStyle().
+ * @param name must come from the collection. It will be ignored if isDefaultStyle() is true.
+ * @param propertiesElementName the name of the XML element with the style properties,
+ * e.g. "style:text-properties". Can be 0 in special cases where there should be no such item,
+ * in which case the attributes and elements are added under the style itself.
+ * @param closeElement set it to false to be able to add more child elements to the style element
+ * @param drawElement set it to true to add "draw:name" (used for gradient/hatch style) otherwise add "style:name"
+ */
+ void writeStyle( KoXmlWriter* writer, KoGenStyles& styles, const char* elementName, const QString& name,
+ const char* propertiesElementName, bool closeElement = true, bool drawElement = false ) const;
+
+ /**
+ * QMap requires a complete sorting order.
+ * Another solution would have been a qdict and a key() here, a la KoTextFormat,
+ * but the key was difficult to generate.
+ * Solutions with only a hash value (not representative of the whole data)
+ * require us to write a hashtable by hand....
+ */
+ bool operator<( const KoGenStyle &other ) const;
+
+ /// Not needed for QMap, but can still be useful
+ bool operator==( const KoGenStyle &other ) const;
+
+private:
+ QString property( const QString& propName, PropertyType type ) const {
+ QMap<QString, QString>::const_iterator it = m_properties[type].find( propName );
+ if ( it != m_properties[type].end() )
+ return it.data();
+ return QString::null;
+ }
+
+ QString attribute( const QString& propName ) const {
+ QMap<QString, QString>::const_iterator it = m_attributes.find( propName );
+ if ( it != m_attributes.end() )
+ return it.data();
+ return QString::null;
+ }
+
+ void writeStyleProperties( KoXmlWriter* writer, PropertyType i,
+ const char* elementName, const KoGenStyle* parentStyle ) const;
+
+#ifndef NDEBUG
+ void printDebug() const;
+#endif
+
+private:
+ // Note that the copy constructor and assignment operator are allowed.
+ // Better not use pointers below!
+ int m_type;
+ QCString m_familyName;
+ QString m_parentName;
+ /// We use QMaps since they provide automatic sorting on the key (important for unicity!)
+ QMap<QString, QString> m_properties[N_NumTypes];
+ QMap<QString, QString> m_attributes;
+ typedef QMap<QString, QString> StyleMap;
+ QValueVector<StyleMap> m_maps; // we can't really sort the maps between themselves...
+
+ bool m_autoStyleInStylesDotXml;
+ bool m_defaultStyle;
+ short m_unused2;
+
+ // For lookup
+ friend class KoGenStyles;
+};
+
+#endif /* KOGENSTYLES_H */
diff --git a/lib/kofficecore/KoGlobal.cpp b/lib/kofficecore/KoGlobal.cpp
new file mode 100644
index 00000000..87717172
--- /dev/null
+++ b/lib/kofficecore/KoGlobal.cpp
@@ -0,0 +1,205 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 David Faure <faure@kde.org>
+ Copyright 2003 Nicolas GOUTTE <goutte@kde.org>
+
+ 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.
+*/
+
+#include "config.h"
+#include <KoGlobal.h>
+#include <kdebug.h>
+#include <qfont.h>
+#include <qfontinfo.h>
+#include <kglobalsettings.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kstaticdeleter.h>
+#include <kimageio.h>
+#include <kiconloader.h>
+#include <kstandarddirs.h>
+#include <locale.h>
+
+KoGlobal* KoGlobal::s_global = 0L;
+static KStaticDeleter<KoGlobal> sdg;
+
+KoGlobal* KoGlobal::self()
+{
+ if ( !s_global )
+ sdg.setObject( s_global, new KoGlobal );
+ return s_global;
+}
+
+KoGlobal::KoGlobal()
+ : m_pointSize( -1 ), m_kofficeConfig( 0L )
+{
+ // Make sure that QCString::setNum doesn't give us "," as decimal point, e.g. when saving to OpenDocument.
+ setlocale( LC_NUMERIC, "C" );
+
+ // Install the libkoffice* translations
+ KGlobal::locale()->insertCatalogue("koffice");
+
+ KImageIO::registerFormats();
+
+ // Tell KStandardDirs about the koffice prefix
+ KGlobal::dirs()->addPrefix(PREFIX);
+
+ // Tell the iconloader about share/apps/koffice/icons
+ KGlobal::iconLoader()->addAppDir("koffice");
+
+ // Another way to get the DPI of the display would be QPaintDeviceMetrics,
+ // but we have no widget here (and moving this to KoView wouldn't allow
+ // using this from the document easily).
+#ifdef Q_WS_X11
+ m_dpiX = QPaintDevice::x11AppDpiX();
+ m_dpiY = QPaintDevice::x11AppDpiY();
+#else
+ m_dpiX = 75;
+ m_dpiY = 75;
+#endif
+}
+
+KoGlobal::~KoGlobal()
+{
+ delete m_kofficeConfig;
+}
+
+QFont KoGlobal::_defaultFont()
+{
+ QFont font = KGlobalSettings::generalFont();
+ // we have to use QFontInfo, in case the font was specified with a pixel size
+ if ( font.pointSize() == -1 )
+ {
+ // cache size into m_pointSize, since QFontInfo loads the font -> slow
+ if ( m_pointSize == -1 )
+ m_pointSize = QFontInfo(font).pointSize();
+ Q_ASSERT( m_pointSize != -1 );
+ font.setPointSize( m_pointSize );
+ }
+ //kdDebug()<<k_funcinfo<<"QFontInfo(font).pointSize() :"<<QFontInfo(font).pointSize()<<endl;
+ //kdDebug()<<k_funcinfo<<"font.name() :"<<font.family ()<<endl;
+ return font;
+}
+
+QStringList KoGlobal::_listOfLanguageTags()
+{
+ if ( m_langMap.isEmpty() )
+ createListOfLanguages();
+ return m_langMap.values();
+}
+
+QStringList KoGlobal::_listOfLanguages()
+{
+ if ( m_langMap.empty() )
+ createListOfLanguages();
+ return m_langMap.keys();
+}
+
+void KoGlobal::createListOfLanguages()
+{
+ KConfig config( "all_languages", true, false, "locale" );
+ // Note that we could also use KLocale::allLanguagesTwoAlpha
+
+ QMap<QString, bool> seenLanguages;
+ const QStringList langlist = config.groupList();
+ for ( QStringList::ConstIterator itall = langlist.begin();
+ itall != langlist.end(); ++itall )
+ {
+ const QString tag = *itall;
+ config.setGroup( tag );
+ const QString name = config.readEntry("Name", tag);
+ // e.g. name is "French" and tag is "fr"
+
+ // The QMap does the sorting on the display-name, so that
+ // comboboxes are sorted.
+ m_langMap.insert( name, tag );
+
+ seenLanguages.insert( tag, true );
+ }
+
+ // Also take a look at the installed translations.
+ // Many of them are already in all_languages but all_languages doesn't
+ // currently have en_GB or en_US etc.
+
+ const QStringList translationList = KGlobal::dirs()->findAllResources("locale",
+ QString::fromLatin1("*/entry.desktop"));
+ for ( QStringList::ConstIterator it = translationList.begin();
+ it != translationList.end(); ++it )
+ {
+ // Extract the language tag from the directory name
+ QString tag = *it;
+ int index = tag.findRev('/');
+ tag = tag.left(index);
+ index = tag.findRev('/');
+ tag = tag.mid(index+1);
+
+ if ( seenLanguages.find( tag ) == seenLanguages.end() ) {
+ KSimpleConfig entry(*it);
+ entry.setGroup("KCM Locale");
+
+ const QString name = entry.readEntry("Name", tag);
+ // e.g. name is "US English" and tag is "en_US"
+ m_langMap.insert( name, tag );
+
+ // enable this if writing a third way of finding languages below
+ //seenLanguages.insert( tag, true );
+ }
+
+ }
+
+ // #### We also might not have an entry for a language where spellchecking is supported,
+ // but no KDE translation is available, like fr_CA.
+ // How to add them?
+}
+
+QString KoGlobal::tagOfLanguage( const QString & _lang)
+{
+ const LanguageMap& map = self()->m_langMap;
+ QMap<QString,QString>::ConstIterator it = map.find( _lang );
+ if ( it != map.end() )
+ return *it;
+ return QString::null;
+}
+
+QString KoGlobal::languageFromTag( const QString &langTag )
+{
+ const LanguageMap& map = self()->m_langMap;
+ QMap<QString,QString>::ConstIterator it = map.begin();
+ const QMap<QString,QString>::ConstIterator end = map.end();
+ for ( ; it != end; ++it )
+ if ( it.data() == langTag )
+ return it.key();
+
+ // Language code not found. Better return the code (tag) than nothing.
+ return langTag;
+}
+
+KConfig* KoGlobal::_kofficeConfig()
+{
+ if ( !m_kofficeConfig ) {
+ m_kofficeConfig = new KConfig( "kofficerc" );
+ }
+ return m_kofficeConfig;
+}
+
+void KoGlobal::setDPI( int x, int y )
+{
+ //kdDebug() << k_funcinfo << x << "," << y << endl;
+ KoGlobal* s = self();
+ s->m_dpiX = x;
+ s->m_dpiY = y;
+}
diff --git a/lib/kofficecore/KoGlobal.h b/lib/kofficecore/KoGlobal.h
new file mode 100644
index 00000000..bab9c261
--- /dev/null
+++ b/lib/kofficecore/KoGlobal.h
@@ -0,0 +1,107 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ Copyright 2002, 2003 David Faure <faure@kde.org>
+ Copyright 2003 Nicolas GOUTTE <goutte@kde.org>
+
+ 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 koGlobal_h
+#define koGlobal_h
+
+#include <qstringlist.h>
+#include <qfont.h>
+#include <qmap.h>
+class KConfig;
+
+#include <koffice_export.h>
+class KOFFICECORE_EXPORT KoGlobal
+{
+public:
+ /// For KoApplication
+ static void initialize() {
+ (void)self(); // I don't want to make KGlobal instances public, so self() is private
+ }
+ /**
+ * Return the default font for KOffice programs.
+ * This is (currently) the same as the KDE-global default font,
+ * except that it is guaranteed to have a point size set,
+ * never a pixel size (see @ref QFont).
+ */
+ static QFont defaultFont() {
+ return self()->_defaultFont();
+ }
+
+ /**
+ * @return the global KConfig object around kofficerc.
+ * kofficerc is used for KOffice-wide settings, from totally unrelated classes,
+ * so this is the centralization of the KConfig object so that the file is
+ * parsed only once
+ */
+ static KConfig* kofficeConfig() {
+ return self()->_kofficeConfig();
+ }
+
+ static int dpiX() {
+ return self()->m_dpiX;
+ }
+ static int dpiY() {
+ return self()->m_dpiY;
+ }
+ /// @internal, for KoApplication
+ static void setDPI( int x, int y );
+
+ /// Return the list of available languages, in their displayable form
+ /// (translated names)
+ static QStringList listOfLanguages() {
+ return self()->_listOfLanguages();
+ }
+ /// Return the list of available languages, in their internal form
+ /// e.g. "fr" or "en_US", here called "tag"
+ static QStringList listTagOfLanguages() { // TODO rename to listOfLanguageTags
+ return self()->_listOfLanguageTags();
+ }
+ /// For a given language display name, return its tag
+ static QString tagOfLanguage( const QString & _lang );
+ /// For a given language tag, return its display name
+ static QString languageFromTag( const QString &_lang );
+
+ ~KoGlobal();
+
+private:
+ static KoGlobal* self();
+ KoGlobal();
+ QFont _defaultFont();
+ QStringList _listOfLanguages();
+ QStringList _listOfLanguageTags();
+ KConfig* _kofficeConfig();
+ void createListOfLanguages();
+
+ int m_pointSize;
+ typedef QMap<QString, QString> LanguageMap;
+ LanguageMap m_langMap; // display-name -> language tag
+ KConfig* m_kofficeConfig;
+ int m_dpiX;
+ int m_dpiY;
+ // No BC problem here, constructor is private, feel free to add members
+
+ // Singleton pattern. Maybe this should even be refcounted, so
+ // that it gets cleaned up when closing all koffice parts in e.g. konqueror?
+ static KoGlobal* s_global;
+ friend class this_is_a_singleton; // work around gcc warning
+};
+
+#endif // koGlobal
diff --git a/lib/kofficecore/KoMainWindow.cpp b/lib/kofficecore/KoMainWindow.cpp
new file mode 100644
index 00000000..bdcfaa6a
--- /dev/null
+++ b/lib/kofficecore/KoMainWindow.cpp
@@ -0,0 +1,1701 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ Copyright (C) 2000-2006 David Faure <faure@kde.org>
+
+ 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.
+*/
+
+#include "KoMainWindow.h"
+#include "KoDocument.h"
+#include "KoView.h"
+#include "KoFilterManager.h"
+#include "KoDocumentInfo.h"
+#include "KoDocumentInfoDlg.h"
+#include "KoQueryTrader.h"
+#include "KoMainWindowIface.h"
+#include "KoFrame.h"
+#include "KoFileDialog.h"
+#include "Koversiondialog.h"
+#include "kkbdaccessextensions.h"
+#include "KoSpeaker.h"
+
+#include <kaboutdata.h>
+#include <kprinter.h>
+#include <kdeversion.h>
+#include <kstdaction.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kio/netaccess.h>
+#include <kkeydialog.h>
+#include <kedittoolbar.h>
+#include <kprogress.h>
+#include <kpushbutton.h>
+#include <kdebug.h>
+#include <ktempfile.h>
+#include <krecentdocument.h>
+#include <kparts/partmanager.h>
+#include <kparts/plugin.h>
+#include <kparts/event.h>
+#include <klocale.h>
+#include <kstatusbar.h>
+#include <kglobalsettings.h>
+#include <ksharedptr.h>
+
+#include <qobjectlist.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+
+class KoPartManager : public KParts::PartManager
+{
+public:
+ KoPartManager( QWidget * parent, const char * name = 0L )
+ : KParts::PartManager( parent, name )
+ {
+ setSelectionPolicy( KParts::PartManager::TriState );
+ setAllowNestedParts( true );
+ setIgnoreScrollBars( true );
+ // Allow right-click on embedded objects (without activating them)
+ // But beware: this means right-click on parent, from embedded object,
+ // doesn't make the parent active first...
+ setActivationButtonMask( Qt::LeftButton | Qt::MidButton );
+ }
+ virtual bool eventFilter( QObject *obj, QEvent *ev )
+ {
+ if ( !obj->isWidgetType() || ::qt_cast<KoFrame *>( obj ) )
+ return false;
+ return KParts::PartManager::eventFilter( obj, ev );
+ }
+};
+
+class KoMainWindowPrivate
+{
+public:
+ KoMainWindowPrivate()
+ {
+ m_rootDoc = 0L;
+ m_docToOpen = 0L;
+ m_manager = 0L;
+ bMainWindowGUIBuilt = false;
+ m_forQuit=false;
+ m_splitted=false;
+ m_activePart = 0L;
+ m_activeView = 0L;
+ m_splitter=0L;
+ m_orientation=0L;
+ m_removeView=0L;
+ m_toolbarList.setAutoDelete( true );
+ m_firstTime=true;
+ m_progress=0L;
+ m_paDocInfo = 0;
+ m_paSave = 0;
+ m_paSaveAs = 0;
+ m_paPrint = 0;
+ m_paPrintPreview = 0;
+ statusBarLabel = 0L;
+ m_dcopObject = 0;
+ m_sendfile = 0;
+ m_paCloseFile = 0L;
+ m_reloadfile = 0L;
+ m_versionsfile = 0L;
+ m_importFile = 0;
+ m_exportFile = 0;
+ m_isImporting = false;
+ m_isExporting = false;
+ m_windowSizeDirty = false;
+ m_lastExportSpecialOutputFlag = 0;
+
+ // TTS accessibility enhancement (only if KDE 3.4 or later and KTTSD daemon is installed.)
+ if (KoSpeaker::isKttsdInstalled()) {
+ if (kospeaker)
+ m_koSpeaker = kospeaker;
+ else
+ m_koSpeaker = new KoSpeaker();
+ } else
+ m_koSpeaker = 0;
+ }
+ ~KoMainWindowPrivate()
+ {
+ delete m_dcopObject;
+ }
+
+ KoDocument *m_rootDoc;
+ KoDocument *m_docToOpen;
+ QPtrList<KoView> m_rootViews;
+ KParts::PartManager *m_manager;
+
+ KParts::Part *m_activePart;
+ KoView *m_activeView;
+
+ QLabel * statusBarLabel;
+ KProgress *m_progress;
+
+ QPtrList<KAction> m_splitViewActionList;
+ // This additional list is needed, because we don't plug
+ // the first list, when an embedded view gets activated (Werner)
+ QPtrList<KAction> m_veryHackyActionList;
+ QSplitter *m_splitter;
+ KSelectAction *m_orientation;
+ KAction *m_removeView;
+ KoMainWindowIface *m_dcopObject;
+
+ QPtrList <KAction> m_toolbarList;
+
+ bool bMainWindowGUIBuilt;
+ bool m_splitted;
+ bool m_forQuit;
+ bool m_firstTime;
+ bool m_windowSizeDirty;
+
+ KAction *m_paDocInfo;
+ KAction *m_paSave;
+ KAction *m_paSaveAs;
+ KAction *m_paPrint;
+ KAction *m_paPrintPreview;
+ KAction *m_sendfile;
+ KAction *m_paCloseFile;
+ KAction *m_reloadfile;
+ KAction *m_versionsfile;
+ KAction *m_importFile;
+ KAction *m_exportFile;
+
+ bool m_isImporting;
+ bool m_isExporting;
+
+ KURL m_lastExportURL;
+ QCString m_lastExportFormat;
+ int m_lastExportSpecialOutputFlag;
+
+ KSharedPtr<KoSpeaker> m_koSpeaker;
+};
+
+KoMainWindow::KoMainWindow( KInstance *instance, const char* name )
+ : KParts::MainWindow( name )
+{
+ setStandardToolBarMenuEnabled(true); // should there be a check for >= 3.1 ?
+ Q_ASSERT(instance);
+ d = new KoMainWindowPrivate;
+
+ d->m_manager = new KoPartManager( this );
+
+ connect( d->m_manager, SIGNAL( activePartChanged( KParts::Part * ) ),
+ this, SLOT( slotActivePartChanged( KParts::Part * ) ) );
+
+ if ( instance )
+ setInstance( instance, false ); // don't load plugins! we don't want
+ // the part's plugins with this shell, even though we are using the
+ // part's instance! (Simon)
+
+ QString doc;
+ QStringList allFiles = KGlobal::dirs()->findAllResources( "data", "koffice/koffice_shell.rc" );
+ setXMLFile( findMostRecentXMLFile( allFiles, doc ) );
+ setLocalXMLFile( locateLocal( "data", "koffice/koffice_shell.rc" ) );
+
+ KStdAction::openNew( this, SLOT( slotFileNew() ), actionCollection(), "file_new" );
+ KStdAction::open( this, SLOT( slotFileOpen() ), actionCollection(), "file_open" );
+ m_recent = KStdAction::openRecent( this, SLOT(slotFileOpenRecent(const KURL&)), actionCollection() );
+ d->m_paSave = KStdAction::save( this, SLOT( slotFileSave() ), actionCollection(), "file_save" );
+ d->m_paSaveAs = KStdAction::saveAs( this, SLOT( slotFileSaveAs() ), actionCollection(), "file_save_as" );
+ d->m_paPrint = KStdAction::print( this, SLOT( slotFilePrint() ), actionCollection(), "file_print" );
+ d->m_paPrintPreview = KStdAction::printPreview( this, SLOT( slotFilePrintPreview() ), actionCollection(), "file_print_preview" );
+ d->m_sendfile = KStdAction::mail( this, SLOT( slotEmailFile() ), actionCollection(), "file_send_file");
+
+ d->m_paCloseFile = KStdAction::close( this, SLOT( slotFileClose() ), actionCollection(), "file_close" );
+ KStdAction::quit( this, SLOT( slotFileQuit() ), actionCollection(), "file_quit" );
+
+ d->m_reloadfile = new KAction( i18n( "Reload"), 0,
+ this, SLOT( slotReloadFile() ),
+ actionCollection(), "file_reload_file");
+
+ d->m_versionsfile = new KAction( i18n( "Versions..."), 0,
+ this, SLOT( slotVersionsFile() ),
+ actionCollection(), "file_versions_file");
+
+ d->m_importFile = new KAction( i18n( "I&mport..." ), 0, // clashing accel key :(
+ this, SLOT( slotImportFile() ),
+ actionCollection(), "file_import_file");
+ d->m_exportFile = new KAction( i18n( "E&xport..." ), 0,
+ this, SLOT( slotExportFile() ),
+ actionCollection(), "file_export_file");
+
+ /* 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->m_paDocInfo = new KAction( i18n( "&Document Information" ), "documentinfo", 0,
+ this, SLOT( slotDocumentInfo() ),
+ actionCollection(), "file_documentinfo" );
+
+ KStdAction::keyBindings( this, SLOT( slotConfigureKeys() ), actionCollection() );
+ KStdAction::configureToolbars( this, SLOT( slotConfigureToolbars() ), actionCollection() );
+
+ d->m_paDocInfo->setEnabled( false );
+ d->m_paSaveAs->setEnabled( false );
+ d->m_reloadfile->setEnabled( false );
+ d->m_versionsfile->setEnabled( false );
+ d->m_importFile->setEnabled( true ); // always enabled like File --> Open
+ d->m_exportFile->setEnabled( false );
+ d->m_paSave->setEnabled( false );
+ d->m_paPrint->setEnabled( false );
+ d->m_paPrintPreview->setEnabled( false );
+ d->m_sendfile->setEnabled( false);
+ d->m_paCloseFile->setEnabled( false);
+
+ d->m_splitter=new QSplitter(Qt::Vertical, this, "mw-splitter");
+ setCentralWidget( d->m_splitter );
+ // Keyboard accessibility enhancements.
+ new KKbdAccessExtensions(this, "mw-panelSizer");
+ // set up the action "list" for "Close all Views" (hacky :) (Werner)
+ d->m_veryHackyActionList.append(
+ new KAction(i18n("&Close All Views"), "fileclose",
+ "ctrl+shift+w", this, SLOT(slotCloseAllViews()),
+ actionCollection(), "view_closeallviews") );
+
+ // set up the action list for the splitter stuff
+ d->m_splitViewActionList.append(new KAction(i18n("&Split View"), "view_split", 0,
+ this, SLOT(slotSplitView()),
+ actionCollection(), "view_split"));
+ d->m_removeView=new KAction(i18n("&Remove View"), "view_remove", 0,
+ this, SLOT(slotRemoveView()),
+ actionCollection(), "view_rm_splitter");
+ d->m_splitViewActionList.append(d->m_removeView);
+ d->m_removeView->setEnabled(false);
+ d->m_orientation=new KSelectAction(i18n("Splitter &Orientation"), "view_orientation", 0,
+ this, SLOT(slotSetOrientation()),
+ actionCollection(), "view_splitter_orientation");
+ QStringList items;
+ items << i18n("&Vertical")
+ << i18n("&Horizontal");
+ d->m_orientation->setItems(items);
+ d->m_orientation->setCurrentItem(static_cast<int>(d->m_splitter->orientation()));
+ d->m_splitViewActionList.append(d->m_orientation);
+ d->m_splitViewActionList.append(new KActionSeparator(this));
+
+ // Load list of recent files
+ KConfig * config = instance ? instance->config() : KGlobal::config();
+ m_recent->loadEntries( config );
+
+ createShellGUI();
+ d->bMainWindowGUIBuilt = true;
+
+ if ( !initialGeometrySet() )
+ {
+ // Default size
+ const int deskWidth = KGlobalSettings::desktopGeometry(this).width();
+ if (deskWidth > 1100) // very big desktop ?
+ resize( 1000, 800 );
+ if (deskWidth > 850) // big desktop ?
+ resize( 800, 600 );
+ else // small (800x600, 640x480) desktop
+ resize( 600, 400 );
+ }
+
+ // Saved size
+ config->setGroup( "MainWindow" );
+ //kdDebug(30003) << "KoMainWindow::restoreWindowSize" << endl;
+ restoreWindowSize( config );
+}
+
+KoMainWindow::~KoMainWindow()
+{
+ // The doc and view might still exist (this is the case when closing the window)
+ if (d->m_rootDoc)
+ d->m_rootDoc->removeShell(this);
+
+ if (d->m_docToOpen) {
+ d->m_docToOpen->removeShell(this);
+ delete d->m_docToOpen;
+ }
+
+ // safety first ;)
+ d->m_manager->setActivePart(0);
+
+ if(d->m_rootViews.findRef(d->m_activeView)==-1) {
+ delete d->m_activeView;
+ d->m_activeView=0L;
+ }
+ d->m_rootViews.setAutoDelete( true );
+ d->m_rootViews.clear();
+
+ // We have to check if this was a root document.
+ // -> We aren't allowed to delete the (embedded) document!
+ // This has to be checked from queryClose, too :)
+ if ( d->m_rootDoc && d->m_rootDoc->viewCount() == 0 &&
+ !d->m_rootDoc->isEmbedded())
+ {
+ //kdDebug(30003) << "Destructor. No more views, deleting old doc " << d->m_rootDoc << endl;
+ delete d->m_rootDoc;
+ }
+
+ delete d->m_manager;
+ delete d;
+}
+
+void KoMainWindow::setRootDocument( KoDocument *doc )
+{
+ if ( d->m_rootDoc == doc )
+ return;
+
+ if (d->m_docToOpen && d->m_docToOpen != doc) {
+ d->m_docToOpen->removeShell(this);
+ delete d->m_docToOpen;
+ d->m_docToOpen = 0;
+ } else {
+ d->m_docToOpen = 0;
+ }
+
+ //kdDebug(30003) << "KoMainWindow::setRootDocument this = " << this << " doc = " << doc << endl;
+ QPtrList<KoView> oldRootViews = d->m_rootViews;
+ d->m_rootViews.clear();
+ KoDocument *oldRootDoc = d->m_rootDoc;
+
+ if ( oldRootDoc )
+ oldRootDoc->removeShell( this );
+
+ d->m_rootDoc = doc;
+
+ if ( doc )
+ {
+ doc->setSelectable( false );
+ //d->m_manager->addPart( doc, false ); // done by KoView::setPartManager
+ d->m_rootViews.append( doc->createView( d->m_splitter, "view" /*not unique, but better than unnamed*/ ) );
+ d->m_rootViews.current()->setPartManager( d->m_manager );
+
+ d->m_rootViews.current()->show();
+ // The addShell has been done already if using openURL
+ if ( !d->m_rootDoc->shells().contains( this ) )
+ d->m_rootDoc->addShell( this );
+ d->m_removeView->setEnabled(false);
+ d->m_orientation->setEnabled(false);
+ }
+
+ bool enable = d->m_rootDoc != 0 ? true : false;
+ d->m_paDocInfo->setEnabled( enable );
+ d->m_paSave->setEnabled( enable );
+ d->m_paSaveAs->setEnabled( enable );
+ d->m_importFile->setEnabled( enable );
+ d->m_exportFile->setEnabled( enable );
+ d->m_paPrint->setEnabled( enable );
+ d->m_paPrintPreview->setEnabled( enable );
+ d->m_sendfile->setEnabled( enable);
+ d->m_paCloseFile->setEnabled( enable);
+ updateCaption();
+
+ d->m_manager->setActivePart( d->m_rootDoc, d->m_rootViews.current() );
+
+ oldRootViews.setAutoDelete( true );
+ oldRootViews.clear();
+
+ if ( oldRootDoc && oldRootDoc->viewCount() == 0 )
+ {
+ //kdDebug(30003) << "No more views, deleting old doc " << oldRootDoc << endl;
+ delete oldRootDoc;
+ }
+}
+
+void KoMainWindow::updateReloadFileAction(KoDocument *doc)
+{
+ d->m_reloadfile->setEnabled( doc && !doc->url().isEmpty() );
+}
+
+void KoMainWindow::updateVersionsFileAction(KoDocument *doc)
+{
+ //TODO activate it just when we save it in oasis file format
+ d->m_versionsfile->setEnabled( doc && !doc->url().isEmpty()&&doc->isModified());
+}
+
+
+void KoMainWindow::setRootDocumentDirect( KoDocument *doc, const QPtrList<KoView> & views )
+{
+ d->m_rootDoc = doc;
+ d->m_rootViews = views;
+ bool enable = d->m_rootDoc != 0 ? true : false;
+ d->m_paDocInfo->setEnabled( enable );
+ d->m_paSave->setEnabled( enable );
+ d->m_paSaveAs->setEnabled( enable );
+ d->m_exportFile->setEnabled( enable );
+ d->m_paPrint->setEnabled( enable );
+ d->m_paPrintPreview->setEnabled( enable );
+ d->m_sendfile->setEnabled( enable);
+ d->m_paCloseFile->setEnabled( enable );
+}
+
+void KoMainWindow::addRecentURL( const KURL& url )
+{
+ kdDebug(30003) << "KoMainWindow::addRecentURL url=" << url.prettyURL() << endl;
+ // 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.path( -1 );
+ QStringList tmpDirs = KGlobal::dirs()->resourceDirs( "tmp" );
+ for ( QStringList::Iterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it )
+ if ( path.contains( *it ) )
+ ok = false; // it's in the tmp resource
+ if ( ok )
+ KRecentDocument::add(path);
+ }
+ else
+ KRecentDocument::add(url.url(-1), true);
+
+ if ( ok )
+ m_recent->addURL( url );
+ saveRecentFiles();
+ }
+}
+
+void KoMainWindow::saveRecentFiles()
+{
+ // Save list of recent files
+ KConfig * config = instance() ? instance()->config() : KGlobal::config();
+ kdDebug(30003) << this << " Saving recent files list into config. instance()=" << instance() << endl;
+ m_recent->saveEntries( config );
+ config->sync();
+ if (KMainWindow::memberList)
+ {
+ // Tell all windows to reload their list, after saving
+ // Doesn't work multi-process, but it's a start
+ KMainWindow *window = KMainWindow::memberList->first();
+ for (; window; window = KMainWindow::memberList->next())
+ static_cast<KoMainWindow *>(window)->reloadRecentFileList();
+ }
+}
+
+void KoMainWindow::reloadRecentFileList()
+{
+ KConfig * config = instance() ? instance()->config() : KGlobal::config();
+ m_recent->loadEntries( config );
+}
+
+KoDocument* KoMainWindow::createDoc() const
+{
+ KoDocumentEntry entry = KoDocumentEntry( KoDocument::readNativeService() );
+ return entry.createDoc();
+}
+
+void KoMainWindow::updateCaption()
+{
+ //kdDebug(30003) << "KoMainWindow::updateCaption()" << endl;
+ if ( !d->m_rootDoc )
+ setCaption(QString::null);
+ else if ( rootDocument()->isCurrent() )
+ {
+ QString caption;
+ // Get caption from document info (title(), in about page)
+ if ( rootDocument()->documentInfo() )
+ {
+ KoDocumentInfoPage * page = rootDocument()->documentInfo()->page( QString::fromLatin1("about") );
+ if (page)
+ caption = static_cast<KoDocumentInfoAbout *>(page)->title();
+ }
+ const QString url = rootDocument()->url().prettyURL( 0, KURL::StripFileProtocol );
+ if ( !caption.isEmpty() && !url.isEmpty() )
+ caption = QString( "%1 - %2" ).arg( caption ).arg( url );
+ else if ( caption.isEmpty() )
+ caption = url;
+
+ setCaption( caption, rootDocument()->isModified() );
+ if ( !rootDocument()->url().fileName(false).isEmpty() )
+ d->m_paSave->setToolTip( i18n("Save as %1").arg(rootDocument()->url().fileName(false)) );
+ else
+ d->m_paSave->setToolTip( i18n("Save") );
+ }
+}
+
+void KoMainWindow::updateCaption( QString caption, bool mod )
+{
+ //kdDebug(30003)<<"KoMainWindow::updateCaption("<<caption<<","<<mod<<")"<<endl;
+ setCaption( caption, mod );
+}
+
+KoDocument *KoMainWindow::rootDocument() const
+{
+ return d->m_rootDoc;
+}
+
+KoView *KoMainWindow::rootView() const
+{
+ if(d->m_rootViews.find(d->m_activeView)!=-1)
+ return d->m_activeView;
+ return d->m_rootViews.first();
+}
+
+KParts::PartManager *KoMainWindow::partManager()
+{
+ return d->m_manager;
+}
+
+bool KoMainWindow::openDocument( const KURL & url )
+{
+ if ( !KIO::NetAccess::exists(url,true,0) )
+ {
+ KMessageBox::error(0L, i18n("The file %1 does not exist.").arg(url.url()) );
+ m_recent->removeURL(url); //remove the file from the recent-opened-file-list
+ saveRecentFiles();
+ return false;
+ }
+ return openDocumentInternal( url );
+}
+
+// (not virtual)
+bool KoMainWindow::openDocument( KoDocument *newdoc, const KURL & url )
+{
+ if (!KIO::NetAccess::exists(url,true,0) )
+ {
+ if (!newdoc->checkAutoSaveFile())
+ {
+ newdoc->initEmpty(); //create an emtpy document
+ }
+
+ setRootDocument( newdoc );
+ newdoc->setURL(url);
+ QString mime = KMimeType::findByURL(url)->name();
+ if ( mime.isEmpty() || mime == KMimeType::defaultMimeType() )
+ mime = newdoc->nativeFormatMimeType();
+ if ( url.isLocalFile() ) // workaround for kde<=3.3 kparts bug, fixed for 3.4
+ newdoc->setFile(url.path());
+ newdoc->setMimeTypeAfterLoading( mime );
+ updateCaption();
+ return true;
+ }
+ return openDocumentInternal( url, newdoc );
+}
+
+// ## If you modify anything here, please check KoShellWindow::openDocumentInternal
+bool KoMainWindow::openDocumentInternal( const KURL & url, KoDocument *newdoc )
+{
+ //kdDebug(30003) << "KoMainWindow::openDocument " << url.url() << endl;
+
+ if ( !newdoc )
+ newdoc = createDoc();
+ if ( !newdoc )
+ return false;
+
+ d->m_firstTime=true;
+ connect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int)));
+ connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
+ connect(newdoc, SIGNAL(canceled( const QString & )), this, SLOT(slotLoadCanceled( const QString & )));
+ newdoc->addShell( this ); // used by openURL
+ bool openRet = (!isImporting ()) ? newdoc->openURL(url) : newdoc->import(url);
+ if(!openRet)
+ {
+ newdoc->removeShell(this);
+ delete newdoc;
+ return false;
+ }
+ updateReloadFileAction(newdoc);
+ updateVersionsFileAction( newdoc );
+ return true;
+}
+
+// Separate from openDocument to handle async loading (remote URLs)
+void KoMainWindow::slotLoadCompleted()
+{
+ kdDebug(30003) << "KoMainWindow::slotLoadCompleted" << endl;
+ KoDocument* doc = rootDocument();
+ KoDocument* newdoc = (KoDocument *)(sender());
+
+ if ( doc && doc->isEmpty() && !doc->isEmbedded() )
+ {
+ // Replace current empty document
+ setRootDocument( newdoc );
+ }
+ else if ( doc && !doc->isEmpty() )
+ {
+ // Open in a new shell
+ // (Note : could create the shell first and the doc next for this
+ // particular case, that would give a better user feedback...)
+ KoMainWindow *s = new KoMainWindow( newdoc->instance() );
+ s->show();
+ newdoc->removeShell( this );
+ s->setRootDocument( newdoc );
+ }
+ else
+ {
+ // We had no document, set the new one
+ setRootDocument( newdoc );
+ }
+ disconnect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int)));
+ disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
+ disconnect(newdoc, SIGNAL(canceled( const QString & )), this, SLOT(slotLoadCanceled( const QString & )));
+}
+
+void KoMainWindow::slotLoadCanceled( const QString & errMsg )
+{
+ kdDebug(30003) << "KoMainWindow::slotLoadCanceled" << endl;
+ 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* newdoc = (KoDocument *)(sender());
+ disconnect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int)));
+ disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
+ disconnect(newdoc, SIGNAL(canceled( const QString & )), this, SLOT(slotLoadCanceled( const QString & )));
+
+ newdoc->removeShell(this);
+ delete newdoc;
+}
+
+void KoMainWindow::slotSaveCanceled( const QString &errMsg )
+{
+ kdDebug(30003) << "KoMainWindow::slotSaveCanceled" << endl;
+ if ( !errMsg.isEmpty() ) // empty when canceled by user
+ KMessageBox::error( this, errMsg );
+ slotSaveCompleted();
+}
+
+void KoMainWindow::slotSaveCompleted()
+{
+ kdDebug(30003) << "KoMainWindow::slotSaveCompleted" << endl;
+ KoDocument* pDoc = (KoDocument *)(sender());
+ disconnect(pDoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int)));
+ disconnect(pDoc, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
+ disconnect(pDoc, SIGNAL(canceled( const QString & )),
+ this, SLOT(slotSaveCanceled( const QString & )));
+}
+
+// returns true if we should save, false otherwise.
+bool KoMainWindow::exportConfirmation( const QCString &outputFormat )
+{
+ if (!rootDocument()->wantExportConfirmation()) return true;
+ KMimeType::Ptr mime = KMimeType::mimeType( outputFormat );
+
+ const bool neverHeardOfIt = ( mime->name() == KMimeType::defaultMimeType() );
+ QString comment = neverHeardOfIt ?
+ i18n( "%1 (unknown file type)" ).arg( outputFormat )
+ : mime->comment();
+
+ // Warn the user
+ int ret;
+ if (!isExporting ()) // File --> Save
+ {
+ ret = KMessageBox::warningContinueCancel
+ (
+ this,
+ i18n( "<qt>Saving as a %1 may result in some loss of formatting."
+ "<p>Do you still want to save in this format?</qt>" )
+ .arg( QString( "<b>%1</b>" ).arg( comment ) ), // in case we want to remove the bold later
+ i18n( "Confirm Save" ),
+ KStdGuiItem::save (),
+ "NonNativeSaveConfirmation",
+ true
+ );
+ }
+ else // File --> Export
+ {
+ ret = KMessageBox::warningContinueCancel
+ (
+ this,
+ i18n( "<qt>Exporting as a %1 may result in some loss of formatting."
+ "<p>Do you still want to export to this format?</qt>" )
+ .arg( QString( "<b>%1</b>" ).arg( comment ) ), // in case we want to remove the bold later
+ i18n( "Confirm Export" ),
+ i18n ("Export"),
+ "NonNativeExportConfirmation", // different to the one used for Save (above)
+ true
+ );
+ }
+
+ return (ret == KMessageBox::Continue);
+}
+
+bool KoMainWindow::saveDocument( bool saveas, bool silent )
+{
+ KoDocument* pDoc = rootDocument();
+ if(!pDoc)
+ return true;
+
+ bool reset_url;
+ if ( pDoc->url().isEmpty() )
+ {
+ emit saveDialogShown();
+ reset_url = true;
+ saveas = true;
+ }
+ else
+ reset_url = false;
+
+ connect(pDoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int)));
+ connect(pDoc, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
+ connect(pDoc, SIGNAL(canceled( const QString & )),
+ this, SLOT(slotSaveCanceled( const QString & )));
+
+ KURL oldURL = pDoc->url();
+ QString oldFile = pDoc->file();
+ QCString _native_format = pDoc->nativeFormatMimeType();
+ QCString oldOutputFormat = pDoc->outputMimeType();
+ int oldSpecialOutputFlag = pDoc->specialOutputFlag();
+ KURL suggestedURL = pDoc->url();
+
+ QStringList mimeFilter = KoFilterManager::mimeFilter( _native_format, KoFilterManager::Export, pDoc->extraNativeMimeTypes() );
+ if (mimeFilter.findIndex (oldOutputFormat) < 0 && !isExporting())
+ {
+ kdDebug(30003) << "KoMainWindow::saveDocument no export filter for '" << oldOutputFormat << "'" << endl;
+
+ // --- 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 () ) // ".kwd" looks strange for a name
+ {
+ int c = suggestedFilename.findRev ('.');
+
+ KMimeType::Ptr mime = KMimeType::mimeType( _native_format );
+ QString ext = mime->property( "X-KDE-NativeExtension" ).toString();
+ 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.setFileName (suggestedFilename);
+ }
+
+ // force the user to choose outputMimeType
+ saveas = true;
+ }
+
+ bool ret = false;
+
+ if ( pDoc->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 = new KoFileDialog( (isExporting() && !d->m_lastExportURL.isEmpty() )? d->m_lastExportURL.url () : suggestedURL.url (),
+ QString::null, this, "file dialog", true);
+
+ if (!isExporting())
+ dialog->setCaption( i18n("Save Document As") );
+ else
+ dialog->setCaption( i18n("Export Document As") );
+
+ dialog->setOperationMode( KFileDialog::Saving );
+ dialog->setSpecialMimeFilter( mimeFilter,
+ isExporting() ? d->m_lastExportFormat : pDoc->mimeType(),
+ isExporting() ? d->m_lastExportSpecialOutputFlag : oldSpecialOutputFlag,
+ _native_format,
+ pDoc->supportedSpecialFormats() );
+
+ KURL newURL;
+ QCString outputFormat = _native_format;
+ int specialOutputFlag = 0;
+ bool bOk;
+ do {
+ bOk=true;
+ if(dialog->exec()==QDialog::Accepted) {
+ newURL=dialog->selectedURL();
+ outputFormat=dialog->currentMimeFilter().latin1();
+ specialOutputFlag = dialog->specialEntrySelected();
+ kdDebug(30003) << "KoMainWindow::saveDocument outputFormat = " << outputFormat << endl;
+
+ if (!isExporting())
+ justChangingFilterOptions = (newURL == pDoc->url()) &&
+ (outputFormat == pDoc->mimeType()) &&
+ (specialOutputFlag == oldSpecialOutputFlag);
+ else
+ justChangingFilterOptions = (newURL == d->m_lastExportURL) &&
+ (outputFormat == d->m_lastExportFormat) &&
+ (specialOutputFlag == d->m_lastExportSpecialOutputFlag);
+ }
+ else
+ {
+ bOk = false;
+ break;
+ }
+
+ if ( newURL.isEmpty() )
+ {
+ bOk = false;
+ break;
+ }
+
+ // adjust URL before doing checks on whether the file exists.
+ if ( specialOutputFlag == KoDocument::SaveAsDirectoryStore ) {
+ QString fileName = newURL.fileName();
+ if ( fileName != "content.xml" ) {
+ newURL.addPath( "content.xml" );
+ }
+ }
+
+ // this file exists and we are not just clicking "Save As" to change filter options
+ // => ask for confirmation
+ if ( KIO::NetAccess::exists( newURL, false /*will write*/, this ) && !justChangingFilterOptions )
+ {
+ bOk = KMessageBox::questionYesNo( this,
+ i18n("A document with this name already exists.\n"\
+ "Do you want to overwrite it?"),
+ i18n("Warning") ) == KMessageBox::Yes;
+ }
+ } while ( !bOk );
+
+ delete dialog;
+
+ if (bOk)
+ {
+ bool wantToSave = true;
+
+ // don't change this line unless you know what you're doing :)
+ if (!justChangingFilterOptions || pDoc->confirmNonNativeSave (isExporting ())) {
+ if ( !pDoc->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 pDoc->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
+ //
+
+
+ pDoc->setOutputMimeType( outputFormat, specialOutputFlag );
+ if (!isExporting ()) // Save As
+ {
+ ret = pDoc->saveAs( newURL );
+
+ if (ret)
+ {
+ kdDebug(30003) << "Successful Save As!" << endl;
+ addRecentURL( newURL );
+ }
+ else
+ {
+ kdDebug(30003) << "Failed Save As!" << endl;
+ pDoc->setURL( oldURL ), pDoc->setFile( oldFile );
+ pDoc->setOutputMimeType( oldOutputFormat, oldSpecialOutputFlag );
+ }
+ }
+ else // Export
+ {
+ ret = pDoc->exp0rt( newURL );
+
+ if (ret)
+ {
+ // a few file dialog convenience things
+ d->m_lastExportURL = newURL;
+ d->m_lastExportFormat = outputFormat;
+ d->m_lastExportSpecialOutputFlag = specialOutputFlag;
+ }
+
+ // always restore output format
+ pDoc->setOutputMimeType( oldOutputFormat, oldSpecialOutputFlag );
+ }
+ } // if (wantToSave)
+ else
+ ret = false;
+ } // if (bOk)
+ else
+ ret = false;
+ }
+ else { // saving
+ bool needConfirm = pDoc->confirmNonNativeSave( false ) &&
+ !pDoc->isNativeFormat( oldOutputFormat );
+ if (!needConfirm ||
+ (needConfirm && exportConfirmation( oldOutputFormat /* not so old :) */ ))
+ )
+ {
+ // be sure pDoc has the correct outputMimeType!
+ ret = pDoc->save();
+
+ if (!ret)
+ {
+ kdDebug(30003) << "Failed Save!" << endl;
+ pDoc->setURL( oldURL ), pDoc->setFile( oldFile );
+ }
+ }
+ else
+ ret = false;
+ }
+
+// Now that there's a File/Export option, this is no longer necessary.
+// If you continue to use File/Save to export to a foreign format,
+// this signals your intention to continue working in a foreign format.
+// You have already been warned by the DoNotAskAgain exportConfirmation
+// about losing formatting when you first saved so don't set modified
+// here or else it will be reported as a bug by some MSOffice user.
+// You have been warned! Do not click DoNotAskAgain!!!
+#if 0
+ if (ret && !isExporting())
+ {
+ // When exporting to a non-native format, we don't reset modified.
+ // This way the user will be reminded to save it again in the native format,
+ // if he/she doesn't want to lose formatting.
+ if ( wasModified && pDoc->outputMimeType() != _native_format )
+ pDoc->setModified( true );
+ }
+#endif
+
+ if (!ret && reset_url)
+ pDoc->resetURL(); //clean the suggested filename as the save dialog was rejected
+ else if (! silent) // don't let the document change the window caption
+ pDoc->setTitleModified();
+ return ret;
+}
+
+void KoMainWindow::closeEvent(QCloseEvent *e) {
+ if(queryClose()) {
+ saveWindowSettings();
+ setRootDocument(0L);
+ KParts::MainWindow::closeEvent(e);
+ }
+}
+
+void KoMainWindow::saveWindowSettings()
+{
+ if (d->m_windowSizeDirty && rootDocument())
+ {
+ // Save window size into the config file of our instance
+ instance()->config()->setGroup( "MainWindow" );
+ //kdDebug(30003) << "KoMainWindow::saveWindowSettings" << endl;
+ saveWindowSize( instance()->config() );
+ d->m_windowSizeDirty = false;
+ // Save toolbar position into the config file of the app, under the doc's instance name
+ //kdDebug(30003) << "KoMainWindow::closeEvent -> saveMainWindowSettings rootdoc's instance=" << rootDocument()->instance()->instanceName() << endl;
+ saveMainWindowSettings( KGlobal::config(), rootDocument()->instance()->instanceName() );
+ KGlobal::config()->sync();
+ resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down
+ }
+}
+
+void KoMainWindow::resizeEvent( QResizeEvent * e )
+{
+ d->m_windowSizeDirty = true;
+ KParts::MainWindow::resizeEvent( e );
+}
+
+bool KoMainWindow::queryClose()
+{
+ if ( rootDocument() == 0 )
+ return true;
+ //kdDebug(30003) << "KoMainWindow::queryClose() viewcount=" << rootDocument()->viewCount()
+ // << " shellcount=" << rootDocument()->shellCount() << endl;
+ if ( !d->m_forQuit && rootDocument()->shellCount() > 1 )
+ // there are more open, and we are closing just one, so no problem for closing
+ return true;
+
+ // see DTOR for a descr. of the test
+ if ( d->m_rootDoc->isEmbedded() )
+ return true;
+
+ // main doc + internally stored child documents
+ if ( d->m_rootDoc->isModified() )
+ {
+ QString name;
+ if ( rootDocument()->documentInfo() )
+ {
+ name = rootDocument()->documentInfo()->title();
+ }
+ if ( name.isEmpty() )
+ name = rootDocument()->url().fileName();
+
+ if ( name.isEmpty() )
+ name = i18n( "Untitled" );
+
+ int res = KMessageBox::warningYesNoCancel( this,
+ i18n( "<p>The document <b>'%1'</b> has been modified.</p><p>Do you want to save it?</p>" ).arg(name),
+ QString::null,
+ KStdGuiItem::save(),
+ KStdGuiItem::discard());
+
+ switch(res) {
+ case KMessageBox::Yes : {
+ d->m_rootDoc->setDoNotSaveExtDoc(); // external docs are saved later
+ bool isNative = ( d->m_rootDoc->outputMimeType() == d->m_rootDoc->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;
+ }
+ }
+
+ if ( d->m_rootDoc->queryCloseExternalChildren() == KMessageBox::Cancel )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// Helper method for slotFileNew and slotFileClose
+void KoMainWindow::chooseNewDocument( int /*KoDocument::InitDocFlags*/ initDocFlags )
+{
+ KoDocument* doc = rootDocument();
+ KoDocument *newdoc = createDoc();
+
+ if ( !newdoc )
+ return;
+
+ //FIXME: This needs to be handled differently
+ connect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int)));
+ disconnect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int)));
+
+ if ( ( !doc && ( initDocFlags == KoDocument::InitDocFileNew ) ) || ( doc && !doc->isEmpty() ) )
+ {
+ KoMainWindow *s = new KoMainWindow( newdoc->instance() );
+ s->show();
+ newdoc->addShell( s );
+ newdoc->showStartUpWidget( s, true /*Always show widget*/ );
+ return;
+ }
+
+ if( doc ) {
+ setRootDocument( 0 );
+ delete d->m_rootDoc;
+ d->m_rootDoc = 0;
+ }
+
+ newdoc->addShell( this );
+ newdoc->showStartUpWidget( this, true /*Always show widget*/ );
+}
+
+void KoMainWindow::slotFileNew()
+{
+ chooseNewDocument( KoDocument::InitDocFileNew );
+}
+
+void KoMainWindow::slotFileOpen()
+{
+ KFileDialog *dialog = new KFileDialog(":OpenDialog", QString::null, this, "file dialog", true);
+ if (!isImporting())
+ dialog->setCaption( i18n("Open Document") );
+ else
+ dialog->setCaption( i18n("Import Document") );
+
+ // The few lines below need to be kept in sync with KoTemplateChooseDia::setupFileDialog
+ const QStringList mimeFilter = KoFilterManager::mimeFilter( KoDocument::readNativeFormatMimeType(),
+ KoFilterManager::Import,
+ KoDocument::readExtraNativeMimeTypes() );
+ dialog->setMimeFilter( mimeFilter );
+ if(dialog->exec()!=QDialog::Accepted) {
+ delete dialog;
+ return;
+ }
+ KURL url( dialog->selectedURL() );
+ delete dialog;
+
+ if ( url.isEmpty() )
+ return;
+
+ (void) openDocument( url );
+}
+
+void KoMainWindow::slotFileOpenRecent( const KURL & url )
+{
+ (void) openDocument( url );
+}
+
+void KoMainWindow::slotFileSave()
+{
+ if ( saveDocument() )
+ emit documentSaved();
+}
+
+void KoMainWindow::slotFileSaveAs()
+{
+ if ( saveDocument( true ) )
+ emit documentSaved();
+}
+
+void KoMainWindow::slotDocumentInfo()
+{
+ if ( !rootDocument() )
+ return;
+
+ KoDocumentInfo *docInfo = rootDocument()->documentInfo();
+
+ if ( !docInfo )
+ return;
+
+ KoDocumentInfoDlg *dlg = new KoDocumentInfoDlg( docInfo, this, "documentInfoDlg" );
+ if ( dlg->exec() )
+ {
+ dlg->save();
+ rootDocument()->setModified( true );
+ rootDocument()->setTitleModified();
+ }
+
+ delete dlg;
+}
+
+void KoMainWindow::slotFileClose()
+{
+ if (queryClose())
+ {
+ saveWindowSettings();
+ setRootDocument( 0 ); // don't delete this shell when deleting the document
+ delete d->m_rootDoc;
+ d->m_rootDoc = 0;
+ chooseNewDocument( KoDocument::InitDocFileClose );
+ }
+}
+
+void KoMainWindow::slotFileQuit()
+{
+ close();
+}
+
+void KoMainWindow::print(bool quick) {
+ if ( !rootView() )
+ {
+ kdDebug(30003) << "KoMainWindow::slotFilePrint : No root view!" << endl;
+ return;
+ }
+
+ KPrinter printer( true /*, QPrinter::HighResolution*/ );
+ QString title = rootView()->koDocument()->documentInfo()->title();
+ QString fileName = rootView()->koDocument()->url().fileName();
+
+ // strip off the native extension (I don't want foobar.kwd.ps when printing into a file)
+ KMimeType::Ptr mime = KMimeType::mimeType( rootView()->koDocument()->outputMimeType() );
+ if ( mime ) {
+ QString extension = mime->property( "X-KDE-NativeExtension" ).toString();
+
+ if ( fileName.endsWith( extension ) )
+ fileName.truncate( fileName.length() - extension.length() );
+ }
+
+ if ( title.isEmpty() )
+ title = fileName;
+ if ( title.isEmpty() ) {
+ // #139905 - breaks message freeze though
+ //const QString programName = instance()->aboutData() ? instance()->aboutData()->programName() : instance()->instanceName();
+ //title = i18n("%1 unsaved document (%2)").arg(programName).arg(KGlobal::locale()->formatDate(QDate::currentDate(), true/*short*/));
+ }
+ printer.setDocName( title );
+ printer.setDocFileName( fileName );
+ printer.setDocDirectory( rootView()->koDocument()->url().directory() );
+
+ // ### TODO: apply global koffice settings here
+
+ rootView()->setupPrinter( printer );
+
+ if ( quick || printer.setup( this ) )
+ rootView()->print( printer );
+}
+
+
+void KoMainWindow::slotFilePrint()
+{
+ print(false);
+}
+
+void KoMainWindow::slotFilePrintPreview()
+{
+ if ( !rootView() )
+ {
+ kdWarning() << "KoMainWindow::slotFilePrint : No root view!" << endl;
+ return;
+ }
+ KPrinter printer( false );
+ KTempFile tmpFile;
+ // The temp file is deleted by KoPrintPreview
+
+ // This line has to be before setupPrinter to let the apps decide what to
+ // print and what not (if they want to :)
+ printer.setFromTo( printer.minPage(), printer.maxPage() );
+ printer.setPreviewOnly( true );
+ rootView()->setupPrinter( printer );
+
+ QString oldFileName = printer.outputFileName();
+ printer.setOutputFileName( tmpFile.name() );
+ int oldNumCopies = printer.numCopies();
+ printer.setNumCopies( 1 );
+ // Disable kdeprint's own preview, we'd get two. This shows that KPrinter needs
+ // a "don't use the previous settings" mode. The current way is really too much of a hack.
+ QString oldKDEPreview = printer.option( "kde-preview" );
+ printer.setOption( "kde-preview", "0" );
+
+ rootView()->print(printer);
+ //KoPrintPreview::preview(this, "KoPrintPreviewDialog", tmpFile.name());
+
+ // Restore previous values
+ printer.setOutputFileName( oldFileName );
+ printer.setNumCopies( oldNumCopies );
+ printer.setOption( "kde-preview", oldKDEPreview );
+}
+
+void KoMainWindow::slotConfigureKeys()
+{
+ guiFactory()->configureShortcuts();
+}
+
+void KoMainWindow::slotConfigureToolbars()
+{
+ if (rootDocument())
+ saveMainWindowSettings( KGlobal::config(), rootDocument()->instance()->instanceName() );
+ KEditToolbar edit(factory(), this);
+ connect(&edit,SIGNAL(newToolbarConfig()),this,SLOT(slotNewToolbarConfig()));
+ (void) edit.exec();
+}
+
+void KoMainWindow::slotNewToolbarConfig()
+{
+ if (rootDocument())
+ applyMainWindowSettings( KGlobal::config(), rootDocument()->instance()->instanceName() );
+ KXMLGUIFactory *factory = guiFactory();
+
+ // Check if there's an active view
+ if( !d->m_activeView )
+ return;
+
+ // This gets plugged in even for embedded views
+ factory->plugActionList(d->m_activeView, "view_closeallviews",
+ d->m_veryHackyActionList);
+
+ // This one only for root views
+ if(d->m_rootViews.findRef(d->m_activeView)!=-1)
+ factory->plugActionList(d->m_activeView, "view_split",
+ d->m_splitViewActionList );
+ plugActionList( "toolbarlist", d->m_toolbarList );
+}
+
+void KoMainWindow::slotToolbarToggled( bool toggle )
+{
+ //kdDebug(30003) << "KoMainWindow::slotToolbarToggled " << sender()->name() << " toggle=" << true << endl;
+ // The action (sender) and the toolbar have the same name
+ KToolBar * bar = toolBar( sender()->name() );
+ if (bar)
+ {
+ if (toggle)
+ bar->show();
+ else
+ bar->hide();
+
+ if (rootDocument())
+ saveMainWindowSettings( KGlobal::config(), rootDocument()->instance()->instanceName() );
+ }
+ else
+ kdWarning(30003) << "slotToolbarToggled : Toolbar " << sender()->name() << " not found!" << endl;
+}
+
+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 )
+ {
+ kdWarning(30003) << "KoMainWindow: toolbar " << tbName << " not found." << endl;
+ return;
+ }
+ if ( shown )
+ tb->show();
+ else
+ tb->hide();
+
+ // Update the action appropriately
+ QPtrListIterator<KAction> it( d->m_toolbarList );
+ for ( ; it.current() ; ++it )
+ if ( !strcmp( it.current()->name(), tbName ) )
+ {
+ //kdDebug(30003) << "KoMainWindow::showToolbar setChecked " << shown << endl;
+ static_cast<KToggleAction *>(it.current())->setChecked( shown );
+ break;
+ }
+}
+
+void KoMainWindow::slotSplitView() {
+ d->m_splitted=true;
+ d->m_rootViews.append(d->m_rootDoc->createView(d->m_splitter, "splitted-view"));
+ d->m_rootViews.current()->show();
+ d->m_rootViews.current()->setPartManager( d->m_manager );
+ d->m_manager->setActivePart( d->m_rootDoc, d->m_rootViews.current() );
+ d->m_removeView->setEnabled(true);
+ d->m_orientation->setEnabled(true);
+}
+
+void KoMainWindow::slotCloseAllViews() {
+
+ // Attention: Very touchy code... you know what you're doing? Goooood :)
+ d->m_forQuit=true;
+ if(queryClose()) {
+ // In case the document is embedded we close all open "extra-shells"
+ if(d->m_rootDoc && d->m_rootDoc->isEmbedded()) {
+ hide();
+ d->m_rootDoc->removeShell(this);
+ QPtrListIterator<KoMainWindow> it(d->m_rootDoc->shells());
+ while (it.current()) {
+ it.current()->hide();
+ delete it.current(); // this updates the lists' current pointer and thus
+ // the iterator (the shell dtor calls removeShell)
+ d->m_rootDoc=0;
+ }
+ }
+ // not embedded -> destroy the document and all shells/views ;)
+ else
+ setRootDocument( 0L );
+ close(); // close this window (and quit the app if necessary)
+ }
+ d->m_forQuit=false;
+}
+
+void KoMainWindow::slotRemoveView() {
+ KoView *view;
+ if(d->m_rootViews.findRef(d->m_activeView)!=-1)
+ view=d->m_rootViews.current();
+ else
+ view=d->m_rootViews.first();
+ view->hide();
+ if ( !d->m_rootViews.removeRef(view) )
+ kdWarning() << "view not found in d->m_rootViews!" << endl;
+
+ if(d->m_rootViews.count()==1)
+ {
+ d->m_removeView->setEnabled(false);
+ d->m_orientation->setEnabled(false);
+ }
+ // Prevent the view's destroyed() signal from triggering GUI rebuilding (too early)
+ d->m_manager->setActivePart( 0, 0 );
+
+ delete view;
+ view=0L;
+
+ d->m_rootViews.first()->setPartManager( d->m_manager );
+ d->m_manager->setActivePart( d->m_rootDoc, d->m_rootViews.first() );
+
+ if(d->m_rootViews.count()==1)
+ d->m_splitted=false;
+}
+
+void KoMainWindow::slotSetOrientation() {
+ d->m_splitter->setOrientation(static_cast<Qt::Orientation>
+ (d->m_orientation->currentItem()));
+}
+
+void KoMainWindow::slotProgress(int value) {
+ //kdDebug(30003) << "KoMainWindow::slotProgress " << value << endl;
+ if(value==-1) {
+ if ( d->m_progress )
+ {
+ statusBar()->removeWidget(d->m_progress);
+ delete d->m_progress;
+ d->m_progress=0L;
+ }
+ d->m_firstTime=true;
+ return;
+ }
+ if(d->m_firstTime)
+ {
+ // The statusbar might not even be created yet.
+ // So check for that first, and create it if necessary
+ QObjectList *l = queryList( "QStatusBar" );
+ if ( !l || !l->first() ) {
+ statusBar()->show();
+ QApplication::sendPostedEvents( this, QEvent::ChildInserted );
+ setUpLayout();
+ }
+ delete l;
+
+ if ( d->m_progress )
+ {
+ statusBar()->removeWidget(d->m_progress);
+ delete d->m_progress;
+ d->m_progress=0L;
+ }
+ statusBar()->setMaximumHeight(statusBar()->height());
+ d->m_progress=new KProgress(statusBar());
+ //d->m_progress->setMaximumHeight(statusBar()->height());
+ statusBar()->addWidget( d->m_progress, 0, true );
+ d->m_progress->show();
+ d->m_firstTime=false;
+ }
+ d->m_progress->setProgress(value);
+ kapp->processEvents();
+}
+
+
+void KoMainWindow::slotActivePartChanged( KParts::Part *newPart )
+{
+
+ // This looks very much like KParts::MainWindow::createGUI, but we have
+ // to reimplement it because it works with an active part, whereas we work
+ // with an active view _and_ an active part, depending for what.
+ // Both are KXMLGUIClients, but e.g. the plugin query needs a QObject.
+ //kdDebug(30003) << "KoMainWindow::slotActivePartChanged( Part * newPart) newPart = " << newPart << endl;
+ //kdDebug(30003) << "current active part is " << d->m_activePart << endl;
+
+ if ( d->m_activePart && d->m_activePart == newPart && !d->m_splitted )
+ {
+ //kdDebug(30003) << "no need to change the GUI" << endl;
+ return;
+ }
+
+ KXMLGUIFactory *factory = guiFactory();
+
+ setUpdatesEnabled( false );
+
+ if ( d->m_activeView )
+ {
+ KParts::GUIActivateEvent ev( false );
+ QApplication::sendEvent( d->m_activePart, &ev );
+ QApplication::sendEvent( d->m_activeView, &ev );
+
+
+ factory->removeClient( d->m_activeView );
+
+ unplugActionList( "toolbarlist" );
+ d->m_toolbarList.clear(); // deletes the actions
+ }
+
+ if ( !d->bMainWindowGUIBuilt )
+ {
+ // Load mainwindow plugins
+ KParts::Plugin::loadPlugins( this, this, instance(), true );
+ createShellGUI();
+ }
+
+ if ( newPart && d->m_manager->activeWidget() && d->m_manager->activeWidget()->inherits( "KoView" ) )
+ {
+ d->m_activeView = (KoView *)d->m_manager->activeWidget();
+ d->m_activePart = newPart;
+ //kdDebug(30003) << "new active part is " << d->m_activePart << endl;
+
+ factory->addClient( d->m_activeView );
+
+
+ // This gets plugged in even for embedded views
+ factory->plugActionList(d->m_activeView, "view_closeallviews",
+ d->m_veryHackyActionList);
+ // This one only for root views
+ if(d->m_rootViews.findRef(d->m_activeView)!=-1)
+ factory->plugActionList(d->m_activeView, "view_split", d->m_splitViewActionList );
+
+ // Position and show toolbars according to user's preference
+ setAutoSaveSettings( newPart->instance()->instanceName(), false );
+
+ // Create and plug toolbar list for Settings menu
+ //QPtrListIterator<KToolBar> it = toolBarIterator();
+ QPtrList<QWidget> toolBarList = factory->containers( "ToolBar" );
+ QPtrListIterator<QWidget> it( toolBarList );
+ for ( ; it.current() ; ++it )
+ {
+ if ( it.current()->inherits("KToolBar") )
+ {
+ KToolBar * tb = static_cast<KToolBar *>(it.current());
+ KToggleAction * act = new KToggleAction( i18n("Show %1 Toolbar").arg( tb->text() ), 0,
+ actionCollection(), tb->name() );
+ act->setCheckedState(i18n("Hide %1 Toolbar").arg( tb->text() ));
+ connect( act, SIGNAL( toggled( bool ) ), this, SLOT( slotToolbarToggled( bool ) ) );
+ act->setChecked ( !tb->isHidden() );
+ d->m_toolbarList.append( act );
+ }
+ else
+ kdWarning(30003) << "Toolbar list contains a " << it.current()->className() << " which is not a toolbar!" << endl;
+ }
+ plugActionList( "toolbarlist", d->m_toolbarList );
+
+ // Send the GUIActivateEvent only now, since it might show/hide toolbars too
+ // (and this has priority over applyMainWindowSettings)
+ KParts::GUIActivateEvent ev( true );
+ QApplication::sendEvent( d->m_activePart, &ev );
+ QApplication::sendEvent( d->m_activeView, &ev );
+ }
+ else
+ {
+ d->m_activeView = 0L;
+ d->m_activePart = 0L;
+ }
+ setUpdatesEnabled( true );
+}
+
+QLabel * KoMainWindow::statusBarLabel()
+{
+ if ( !d->statusBarLabel )
+ {
+ d->statusBarLabel = new QLabel( statusBar() );
+ statusBar()->addWidget( d->statusBarLabel, 1, true );
+ }
+ return d->statusBarLabel;
+}
+
+void KoMainWindow::setMaxRecentItems(uint _number)
+{
+ m_recent->setMaxItems( _number );
+}
+
+DCOPObject * KoMainWindow::dcopObject()
+{
+ if ( !d->m_dcopObject )
+ {
+ d->m_dcopObject = new KoMainWindowIface( this );
+ }
+
+ return d->m_dcopObject;
+}
+
+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();
+ KURL const tmp_url = rootDocument()->url();
+ QCString const tmp_mimetype = rootDocument()->outputMimeType();
+ KTempFile tmpfile; //TODO: The temorary file should be deleted when the mail program is closed
+ KURL u;
+ u.setPath(tmpfile.name());
+ rootDocument()->setURL(u);
+ rootDocument()->setModified(true);
+ rootDocument()->setOutputMimeType(rootDocument()->nativeFormatMimeType());
+
+ saveDocument(false, true);
+
+ fileURL = tmpfile.name();
+ 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").arg(rootDocument()->url().fileName(false));
+ urls.append( fileURL );
+ }
+
+ kdDebug(30003) << "(" << fileURL <<")" << endl;
+
+ if (!fileURL.isEmpty())
+ {
+ kapp->invokeMailer(QString::null, QString::null, QString::null, theSubject,
+ QString::null, //body
+ QString::null,
+ urls); // attachments
+ }
+}
+
+void KoMainWindow::slotVersionsFile()
+{
+ KoVersionDialog *dlg = new KoVersionDialog( this );
+ dlg->exec();
+ delete dlg;
+}
+
+void KoMainWindow::slotReloadFile()
+{
+ KoDocument* pDoc = rootDocument();
+ if(!pDoc || pDoc->url().isEmpty() || !pDoc->isModified())
+ return;
+
+ bool bOk = KMessageBox::questionYesNo( this,
+ i18n("You will lose all your changes!\n"
+ "Do you want to continue?"),
+ i18n("Warning") ) == KMessageBox::Yes;
+ if ( !bOk )
+ return;
+
+ KURL url = pDoc->url();
+ if ( pDoc && !pDoc->isEmpty() )
+ {
+ setRootDocument( 0L ); // don't delete this shell when deleting the document
+ delete d->m_rootDoc;
+ d->m_rootDoc = 0L;
+ }
+ openDocument( url );
+ return;
+
+}
+
+void KoMainWindow::slotImportFile()
+{
+ kdDebug(30003) << "slotImportFile()" << endl;
+
+ d->m_isImporting = true;
+ slotFileOpen();
+ d->m_isImporting = false;
+}
+
+void KoMainWindow::slotExportFile()
+{
+ kdDebug(30003) << "slotExportFile()" << endl;
+
+ d->m_isExporting = true;
+ slotFileSaveAs();
+ d->m_isExporting = false;
+}
+
+bool KoMainWindow::isImporting() const
+{
+ return d->m_isImporting;
+}
+
+bool KoMainWindow::isExporting() const
+{
+ return d->m_isExporting;
+}
+
+void KoMainWindow::setDocToOpen( KoDocument *doc )
+{
+ d->m_docToOpen = doc;
+}
+
+#include <KoMainWindow.moc>
diff --git a/lib/kofficecore/KoMainWindow.h b/lib/kofficecore/KoMainWindow.h
new file mode 100644
index 00000000..2b738db9
--- /dev/null
+++ b/lib/kofficecore/KoMainWindow.h
@@ -0,0 +1,387 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ Copyright (C) 2000-2004 David Faure <faure@kde.org>
+
+ 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 __ko_main_window_h__
+#define __ko_main_window_h__
+
+#include <kparts/mainwindow.h>
+#include <kfiledialog.h>
+#include <koffice_export.h>
+#include "KoDocument.h"
+
+class QLabel;
+class KoView;
+class KoMainWindowPrivate;
+class KURL;
+class KRecentFilesAction;
+class KoFilterManager;
+class DCOPObject;
+
+namespace KParts
+{
+ class PartManager;
+}
+
+/**
+ * @brief Main window for a KOffice application
+ *
+ * This class is used to represent a main window
+ * of a KOffice component. Each main window contains
+ * a menubar and some toolbars.
+ *
+ * @note This class does NOT need to be subclassed in your application.
+ */
+class KOFFICECORE_EXPORT KoMainWindow : public KParts::MainWindow
+{
+ Q_OBJECT
+public:
+
+ /**
+ * Constructor.
+ *
+ * Initializes a KOffice main window (with its basic GUI etc.).
+ */
+ KoMainWindow( KInstance *instance, const char *_name = 0 );
+
+ /**
+ * Destructor.
+ */
+ ~KoMainWindow();
+
+ /**
+ * Called when a document is assigned to this mainwindow.
+ * This creates a view for this document, makes it the active part, etc.
+ */
+ virtual void setRootDocument( KoDocument *doc );
+
+ /**
+ * This is used to handle the document used at start up before it actually
+ * added as root document.
+ */
+ void setDocToOpen( KoDocument *doc );
+
+ /**
+ * Update caption from document info - call when document info
+ * (title in the about page) changes.
+ */
+ virtual void updateCaption();
+
+ /**
+ * Retrieves the document that is displayed in the mainwindow.
+ */
+ virtual KoDocument* rootDocument() const;
+
+ virtual KoView *rootView() const;
+
+ virtual KParts::PartManager *partManager();
+
+ /**
+ * Prints the document
+ * @param quick whether the print setup dialog is to be displayed
+ **/
+ void print(bool quick);
+
+ /**
+ * The application should call this to show or hide a toolbar.
+ * It also takes care of the corresponding action in the settings menu.
+ */
+ void showToolbar( const char * tbName, bool shown );
+
+ /**
+ * @return TRUE if the toolbar @p tbName is visible
+ */
+ bool toolbarIsVisible(const char *tbName);
+
+ /**
+ * Get hold of the label in the statusbar, to write messages to it.
+ * You can also insert other items in the status bar by using QStatusBar::addWidget.
+ */
+ QLabel * statusBarLabel();
+
+ /**
+ * Sets the maximum number of recent documents entries.
+ */
+ void setMaxRecentItems(uint _number);
+
+ /**
+ * The document opened a URL -> store into recent documents list.
+ */
+ void addRecentURL( const KURL& url );
+
+ /**
+ * Load the desired document and show it.
+ * @param url the URL to open
+ *
+ * @return TRUE on success.
+ */
+ virtual bool openDocument( const KURL & url );
+
+ /**
+ * Load the URL into this document (and make it root doc after loading)
+ *
+ * Special method for KoApplication::start, don't use.
+ */
+ bool openDocument( KoDocument *newdoc, const KURL & url );
+
+ virtual DCOPObject * dcopObject();
+
+ /**
+ * Reloads the recent documents list.
+ */
+ void reloadRecentFileList();
+
+ /**
+ * Updates the window caption based on the document info and path.
+ */
+ virtual void updateCaption( const QString caption, bool mod );
+ void updateReloadFileAction(KoDocument *doc);
+ void updateVersionsFileAction(KoDocument *doc);
+
+signals:
+ /**
+ * This signal is emitted if the document has been saved successfully.
+ */
+ void documentSaved();
+ /// This signals is emmitted before the save dialog is shown
+ void saveDialogShown();
+
+public slots:
+
+ /**
+ * Slot for eMailing the document using KMail
+ *
+ * This is a very simple extension that will allow any document
+ * that is currently being edited to be emailed using KMail.
+ */
+ void slotEmailFile();
+
+ /**
+ * Slot for opening a new document.
+ *
+ * If the current document is empty, the new document replaces it.
+ * If not, a new shell will be opened for showing the document.
+ */
+ virtual void slotFileNew();
+
+ /**
+ * Slot for opening a saved file.
+ *
+ * If the current document is empty, the opened document replaces it.
+ * If not a new shell will be opened for showing the opened file.
+ */
+ virtual void slotFileOpen();
+
+ /**
+ * Slot for opening a file among the recently opened files.
+ *
+ * If the current document is empty, the opened document replaces it.
+ * If not a new shell will be opened for showing the opened file.
+ */
+ virtual void slotFileOpenRecent( const KURL & );
+
+ /**
+ * Saves the current document with the current name.
+ */
+ virtual void slotFileSave();
+
+ /**
+ * Saves the current document with a new name.
+ */
+ virtual void slotFileSaveAs();
+
+ /**
+ * Prints the actual document.
+ */
+ virtual void slotFilePrint();
+
+ /**
+ * Show a print preview
+ */
+ void slotFilePrintPreview(); // make virtual later
+
+ /**
+ * Show a dialog with author and document information.
+ */
+ virtual void slotDocumentInfo();
+
+ /**
+ * Closes the document.
+ */
+ virtual void slotFileClose();
+
+ /**
+ * Closes the shell.
+ */
+ virtual void slotFileQuit();
+
+ /**
+ * Configure key bindings.
+ */
+ virtual void slotConfigureKeys();
+
+ /**
+ * Configure toolbars.
+ */
+ virtual void slotConfigureToolbars();
+
+ /**
+ * Post toolbar config.
+ * (Plug action lists back in, etc.)
+ */
+ virtual void slotNewToolbarConfig();
+
+ /**
+ * Shows or hides a toolbar
+ */
+ virtual void slotToolbarToggled( bool toggle );
+
+ /**
+ * View splitting stuff
+ */
+ virtual void slotSplitView();
+ virtual void slotRemoveView();
+ virtual void slotSetOrientation();
+
+ /**
+ * Close all views
+ */
+ virtual void slotCloseAllViews();
+
+ /**
+ * Reload file
+ */
+ void slotReloadFile();
+
+ /**
+ * This will call a dialogbox to add version to list of files
+ */
+ void slotVersionsFile();
+
+ /**
+ * File --> Import
+ *
+ * This will call slotFileOpen(). To differentiate this from an ordinary
+ * call to slotFileOpen() call @ref isImporting().
+ */
+ void slotImportFile();
+
+ /**
+ * File --> Export
+ *
+ * This will call slotFileSaveAs(). To differentiate this from an ordinary
+ * call to slotFileSaveAs() call @ref isExporting().
+ */
+ void slotExportFile();
+
+protected: // protected methods are mostly for koshell, it's the only one deriving from KoMainWindow
+
+ /// Helper method for slotFileNew and slotFileClose
+ void chooseNewDocument( int /*KoDocument::InitDocFlags*/ initDocFlags );
+ /**
+ * Special method for KOShell, to allow switching the root
+ * document (and its views) among a set of them.
+ */
+ void setRootDocumentDirect( KoDocument *doc, const QPtrList<KoView> & views );
+
+ /**
+ * Create a new empty document.
+ */
+ virtual KoDocument* createDoc() const;
+
+ /**
+ * Saves the document, asking for a filename if necessary.
+ *
+ * @param saveas if set to TRUE the user is always prompted for a filename
+ *
+ * @param silent if set to TRUE rootDocument()->setTitleModified will not be called.
+ *
+ * @return TRUE on success, false on error or cancel
+ * (don't display anything in this case, the error dialog box is also implemented here
+ * but restore the original URL in slotFileSaveAs)
+ */
+ virtual bool saveDocument( bool saveas = false, bool silent = false );
+
+ virtual void closeEvent( QCloseEvent * e );
+ virtual void resizeEvent( QResizeEvent * e );
+
+ /**
+ * Ask user about saving changes to the document upon exit.
+ */
+ virtual bool queryClose();
+
+ virtual bool openDocumentInternal( const KURL & url, KoDocument * newdoc = 0L );
+
+ /**
+ * Returns whether or not the current slotFileSave[As]() or saveDocument()
+ * call is actually an export operation (like File --> Export).
+ *
+ * If this is true, you must call KoDocument::export() instead of
+ * KoDocument::save() or KoDocument::saveAs(), in any reimplementation of
+ * saveDocument().
+ */
+ bool isExporting() const;
+
+ /**
+ * Returns whether or not the current slotFileOpen() or openDocument()
+ * call is actually an import operation (like File --> Import).
+ *
+ * If this is true, you must call KoDocument::import() instead of
+ * KoDocument::openURL(), in any reimplementation of openDocument() or
+ * openDocumentInternal().
+ */
+ bool isImporting() const;
+
+ /**
+ * Save the list of recent files.
+ */
+ void saveRecentFiles();
+
+ KRecentFilesAction *recentAction() const { return m_recent; }
+
+private:
+
+ /**
+ * Asks the user if they really want to save the document.
+ * Called only if outputFormat != nativeFormat.
+ *
+ * @return true if the document should be saved
+ */
+ bool exportConfirmation( const QCString &outputFormat );
+
+ void saveWindowSettings();
+
+ KRecentFilesAction *m_recent;
+
+protected slots:
+ virtual void slotActivePartChanged( KParts::Part *newPart );
+
+private slots:
+ void slotProgress(int value);
+ void slotLoadCompleted();
+ void slotLoadCanceled (const QString &);
+ void slotSaveCompleted();
+ void slotSaveCanceled(const QString &);
+
+private:
+ KoMainWindowPrivate *d;
+
+};
+
+#endif
diff --git a/lib/kofficecore/KoMainWindowIface.cc b/lib/kofficecore/KoMainWindowIface.cc
new file mode 100644
index 00000000..356bd80b
--- /dev/null
+++ b/lib/kofficecore/KoMainWindowIface.cc
@@ -0,0 +1,65 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 David Faure <faure@kde.org>
+
+ $Id: KoMainWindowIface.cc 508787 2006-02-12 18:28:12Z ingwa $
+
+ 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.
+*/
+#include "KoMainWindowIface.h"
+
+#include "KoMainWindow.h"
+
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kdcopactionproxy.h>
+
+KoMainWindowIface::KoMainWindowIface( KoMainWindow *mainwindow )
+ : DCOPObject( mainwindow )
+{
+ m_pMainWindow = mainwindow;
+ m_actionProxy = new KDCOPActionProxy( mainwindow->actionCollection(), this );
+}
+
+KoMainWindowIface::~KoMainWindowIface()
+{
+ delete m_actionProxy;
+}
+
+DCOPRef KoMainWindowIface::action( const QCString &name )
+{
+ return DCOPRef( kapp->dcopClient()->appId(), m_actionProxy->actionObjectId( name ) );
+}
+
+QCStringList KoMainWindowIface::actions()
+{
+ QCStringList res;
+ QValueList<KAction *> lst = m_actionProxy->actions();
+ QValueList<KAction *>::ConstIterator it = lst.begin();
+ QValueList<KAction *>::ConstIterator end = lst.end();
+ for (; it != end; ++it )
+ res.append( (*it)->name() );
+
+ return res;
+}
+
+QMap<QCString,DCOPRef> KoMainWindowIface::actionMap()
+{
+ return m_actionProxy->actionMap();
+}
+
+ASYNC KoMainWindowIface::print(bool quick) {
+ m_pMainWindow->print(quick);
+}
diff --git a/lib/kofficecore/KoMainWindowIface.h b/lib/kofficecore/KoMainWindowIface.h
new file mode 100644
index 00000000..6b7c3cb6
--- /dev/null
+++ b/lib/kofficecore/KoMainWindowIface.h
@@ -0,0 +1,48 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Davud Faure <faure@kde.org>
+
+ $Id: KoMainWindowIface.h 466447 2005-10-02 17:54:10Z zander $
+
+ 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 __KoMainWindowIface_h__
+#define __KoMainWindowIface_h__
+
+#include <dcopobject.h>
+#include <dcopref.h>
+
+class KDCOPActionProxy;
+class KoMainWindow;
+
+class KoMainWindowIface : public DCOPObject
+{
+ K_DCOP
+public:
+ KoMainWindowIface( KoMainWindow *mainwindow );
+ virtual ~KoMainWindowIface();
+
+k_dcop:
+ DCOPRef action( const QCString &name );
+ QCStringList actions();
+ QMap<QCString,DCOPRef> actionMap();
+ ASYNC print(bool quick);
+
+protected:
+ KoMainWindow *m_pMainWindow;
+ KDCOPActionProxy *m_actionProxy;
+};
+
+#endif
diff --git a/lib/kofficecore/KoOasisLoadingContext.cpp b/lib/kofficecore/KoOasisLoadingContext.cpp
new file mode 100644
index 00000000..fdad0ecc
--- /dev/null
+++ b/lib/kofficecore/KoOasisLoadingContext.cpp
@@ -0,0 +1,124 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 David Faure <faure@kde.org>
+
+ 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 "KoOasisLoadingContext.h"
+#include <KoOasisStore.h>
+#include <KoOasisStyles.h>
+#include <KoStore.h>
+#include <KoXmlNS.h>
+#include <kdebug.h>
+#include <KoDom.h>
+
+KoOasisLoadingContext::KoOasisLoadingContext( KoDocument* doc,
+ KoOasisStyles& styles, KoStore* store )
+ : m_doc( doc ), m_store( store ), m_styles( styles ),
+ m_metaXmlParsed( false ), m_useStylesAutoStyles( false )
+{
+ // Ideally this should be done by KoDocument and passed as argument here...
+ KoOasisStore oasisStore( store );
+ QString dummy;
+ (void)oasisStore.loadAndParse( "tar:/META-INF/manifest.xml", m_manifestDoc, dummy );
+}
+
+
+KoOasisLoadingContext::~KoOasisLoadingContext()
+{
+
+}
+
+void KoOasisLoadingContext::fillStyleStack( const QDomElement& object, const char* nsURI, const char* attrName, const char* family )
+{
+ // find all styles associated with an object and push them on the stack
+ if ( object.hasAttributeNS( nsURI, attrName ) ) {
+ const QString styleName = object.attributeNS( nsURI, attrName, QString::null );
+ const QDomElement* style = 0;
+ bool isStyleAutoStyle = false;
+ if ( m_useStylesAutoStyles ) {
+ // When loading something from styles.xml, look into the styles.xml auto styles first
+ style = m_styles.findStyleAutoStyle( styleName, family );
+ // and fallback to looking at styles(), which includes the user styles from styles.xml
+ if ( style )
+ isStyleAutoStyle = true;
+ }
+ if ( !style )
+ style = m_styles.findStyle( styleName, family );
+ if ( style )
+ addStyles( style, family, isStyleAutoStyle );
+ else
+ kdWarning(32500) << "fillStyleStack: no style named " << styleName << " found." << endl;
+ }
+}
+
+void KoOasisLoadingContext::addStyles( const QDomElement* style, const char* family, bool usingStylesAutoStyles )
+{
+ Q_ASSERT( style );
+ if ( !style ) return;
+ // this recursive function is necessary as parent styles can have parents themselves
+ if ( style->hasAttributeNS( KoXmlNS::style, "parent-style-name" ) ) {
+ const QString parentStyleName = style->attributeNS( KoXmlNS::style, "parent-style-name", QString::null );
+ const QDomElement* parentStyle = 0;
+ if ( usingStylesAutoStyles ) {
+ // When loading something from styles.xml, look into the styles.xml auto styles first
+ parentStyle = m_styles.findStyleAutoStyle( parentStyleName, family );
+ // and fallback to looking at styles(), which includes the user styles from styles.xml
+ }
+ if ( !parentStyle )
+ parentStyle = m_styles.findStyle( parentStyleName, family );
+ if ( parentStyle )
+ addStyles( parentStyle, family, usingStylesAutoStyles );
+ else
+ kdWarning(32500) << "Parent style not found: " << parentStyleName << endl;
+ }
+ else if ( family ) {
+ const QDomElement* def = m_styles.defaultStyle( family );
+ if ( def ) { // on top of all, the default style for this family
+ //kdDebug(32500) << "pushing default style " << style->attributeNS( KoXmlNS::style, "name", QString::null ) << endl;
+ m_styleStack.push( *def );
+ }
+ }
+
+ //kdDebug(32500) << "pushing style " << style->attributeNS( KoXmlNS::style, "name", QString::null ) << endl;
+ m_styleStack.push( *style );
+}
+
+QString KoOasisLoadingContext::generator() const
+{
+ parseMeta();
+ return m_generator;
+}
+
+void KoOasisLoadingContext::parseMeta() const
+{
+ if ( !m_metaXmlParsed && m_store )
+ {
+ if ( m_store->hasFile( "meta.xml" ) )
+ {
+ QDomDocument metaDoc;
+ KoOasisStore oasisStore( m_store );
+ QString errorMsg;
+ if ( oasisStore.loadAndParse( "meta.xml", metaDoc, errorMsg ) ) {
+ QDomNode meta = KoDom::namedItemNS( metaDoc, KoXmlNS::office, "document-meta" );
+ QDomNode office = KoDom::namedItemNS( meta, KoXmlNS::office, "meta" );
+ QDomElement generator = KoDom::namedItemNS( office, KoXmlNS::meta, "generator" );
+ if ( !generator.isNull() )
+ m_generator = generator.text();
+ }
+ }
+ m_metaXmlParsed = true;
+ }
+}
diff --git a/lib/kofficecore/KoOasisLoadingContext.h b/lib/kofficecore/KoOasisLoadingContext.h
new file mode 100644
index 00000000..cac1786b
--- /dev/null
+++ b/lib/kofficecore/KoOasisLoadingContext.h
@@ -0,0 +1,122 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 David Faure <faure@kde.org>
+
+ 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 KOOASISLOADINGCONTEXT_H
+#define KOOASISLOADINGCONTEXT_H
+
+class KoXmlWriter;
+class QDomElement;
+class KoDocument;
+class KoOasisStyles;
+class KoPictureCollection;
+class KoStore;
+
+#include <qmap.h>
+#include <koffice_export.h>
+#include <qstringlist.h>
+#include <KoStyleStack.h>
+
+/**
+ * Used during loading of Oasis format (and discarded at the end of the loading).
+ *
+ * @author David Faure <faure@kde.org>
+ */
+class KOFFICECORE_EXPORT KoOasisLoadingContext
+{
+public:
+ /**
+ * Stores reference to the KoOasisStyles and stored passed by KoDocument.
+ * Make sure that the KoOasisStyles instance outlives this KoOasisLoadingContext instance.
+ * (This is the case during loading, when using the KoOasisStyles given by KoDocument)
+ *
+ * @param doc the KoDocument being loaded
+ * @param styles reference to the KoOasisStyles parsed by KoDocument
+ * @param store pointer to store, if available, for e.g. loading images.
+ */
+ KoOasisLoadingContext( KoDocument* doc, KoOasisStyles& styles, KoStore* store );
+ ~KoOasisLoadingContext();
+
+ KoDocument* koDocument() { return m_doc; }
+ KoStore* store() { return m_store; }
+
+ KoOasisStyles& oasisStyles() { return m_styles; }
+ KoStyleStack& styleStack() { return m_styleStack; }
+
+ const QDomDocument& manifestDocument() const { return m_manifestDoc; }
+
+ /// Return the <meta:generator> of the document, e.g. "KOffice/1.4.0a"
+ QString generator() const;
+
+ /**
+ * Convenience method for loading the style of an object
+ * before loading that object.
+ *
+ * Read attribute (nsURI,attrName) from the given dom element,
+ * treat that attribute as a style name, and load that style
+ * including all its parent styles.
+ * @param element the dom element to read the attribute from
+ * @param nsURI the namespace URI of the attribute to read
+ * @param attrName the name of the attribute to read
+ * @param family the style family used for this object
+ */
+ void fillStyleStack( const QDomElement& element, const char* nsURI, const char* attrName, const char* family );
+
+ /**
+ * Add @p style to the stack, as well as all its parent styles
+ * and the default style for this style family.
+ *
+ * @param style the dom element containing the style to add to the stack
+ * @param family the family to use when looking up parent styles
+ * @param usingStylesAutoStyles if true, the parent styles are looked up
+ * in the automatic styles from styles.xml, instead of looking up
+ * in the automatic styles from content.xml as we usually do.
+ * This is useful for loading headers and footers for instance.
+ * See setUseStylesAutoStyles(), which makes fillStyleStack() set this bool.
+ *
+ * Usually you would call fillStyleStack() instead.
+ */
+ void addStyles( const QDomElement* style, const char* family, bool usingStylesAutoStyles = false );
+
+ /// Set to true while loading headers and footers, to remember to use auto styles
+ /// from styles.xml
+ void setUseStylesAutoStyles( bool useStylesAutoStyles ) { m_useStylesAutoStyles = useStylesAutoStyles; }
+ //bool useStylesAutoStyles() const { return m_useStylesAutoStyles; }
+
+private:
+ void parseMeta() const;
+
+private:
+ KoDocument* m_doc;
+ KoStore* m_store;
+ KoOasisStyles& m_styles;
+ KoStyleStack m_styleStack;
+
+ mutable QString m_generator;
+ mutable bool m_metaXmlParsed;
+ bool m_useStylesAutoStyles;
+ bool m_unused1; // padding, can be used later
+ bool m_unused2; // padding, can be used later
+
+ QDomDocument m_manifestDoc;
+
+ class Private;
+ Private *d;
+};
+
+#endif /* KOOASISLOADINGCONTEXT_H */
+
diff --git a/lib/kofficecore/KoOasisSettings.cpp b/lib/kofficecore/KoOasisSettings.cpp
new file mode 100644
index 00000000..99f63524
--- /dev/null
+++ b/lib/kofficecore/KoOasisSettings.cpp
@@ -0,0 +1,224 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Laurent Montel <montel@kde.org>
+
+ 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.
+*/
+
+#include "KoOasisSettings.h"
+#include "KoXmlNS.h"
+#include "KoDom.h"
+#include <kdebug.h>
+
+KoOasisSettings::KoOasisSettings( const QDomDocument& doc )
+ : m_settingsElement( KoDom::namedItemNS( doc.documentElement(), KoXmlNS::office, "settings" ) ),
+ m_configNSURI( KoXmlNS::config )
+{
+ const QDomElement contents = doc.documentElement();
+ if ( m_settingsElement.isNull() )
+ kdDebug() << " document doesn't have tag 'office:settings'\n";
+}
+
+KoOasisSettings::KoOasisSettings( const QDomDocument& doc, const char* officeNSURI, const char* configNSURI )
+ : m_settingsElement( KoDom::namedItemNS( doc.documentElement(), officeNSURI, "settings" ) ),
+ m_configNSURI( configNSURI )
+{
+ const QDomElement contents = doc.documentElement();
+ if ( m_settingsElement.isNull() )
+ kdDebug() << " document doesn't have tag 'office:settings'\n";
+}
+
+KoOasisSettings::Items KoOasisSettings::itemSet( const QString& itemSetName ) const
+{
+ QDomElement e;
+ forEachElement( e, m_settingsElement )
+ {
+ if ( e.localName() == "config-item-set" &&
+ e.namespaceURI() == m_configNSURI &&
+ e.attributeNS( m_configNSURI, "name", QString::null ) == itemSetName )
+ {
+ return Items( e, this );
+ }
+ }
+
+ return Items( QDomElement(), this );
+}
+
+KoOasisSettings::IndexedMap KoOasisSettings::Items::indexedMap( const QString& itemMapName ) const
+{
+ QDomElement configItem;
+ forEachElement( configItem, m_element )
+ {
+ if ( configItem.localName() == "config-item-map-indexed" &&
+ configItem.namespaceURI() == m_settings->m_configNSURI &&
+ configItem.attributeNS( m_settings->m_configNSURI, "name", QString::null ) == itemMapName )
+ {
+ return IndexedMap( configItem, m_settings );
+ }
+ }
+ return IndexedMap( QDomElement(), m_settings );
+}
+
+KoOasisSettings::NamedMap KoOasisSettings::Items::namedMap( const QString& itemMapName ) const
+{
+ QDomElement configItem;
+ forEachElement( configItem, m_element )
+ {
+ if ( configItem.localName() == "config-item-map-named" &&
+ configItem.namespaceURI() == m_settings->m_configNSURI &&
+ configItem.attributeNS( m_settings->m_configNSURI, "name", QString::null ) == itemMapName )
+ {
+ return NamedMap( configItem, m_settings );
+ }
+ }
+ return NamedMap( QDomElement(), m_settings );
+}
+
+KoOasisSettings::Items KoOasisSettings::IndexedMap::entry( int entryIndex ) const
+{
+ int i = 0;
+ QDomElement entry;
+ forEachElement( entry, m_element )
+ {
+ if ( entry.localName() == "config-item-map-entry" &&
+ entry.namespaceURI() == m_settings->m_configNSURI )
+ {
+ if ( i == entryIndex )
+ return Items( entry, m_settings );
+ else
+ ++i;
+ }
+ }
+ return Items( QDomElement(), m_settings );
+}
+
+KoOasisSettings::Items KoOasisSettings::NamedMap::entry( const QString& entryName ) const
+{
+ QDomElement entry;
+ forEachElement( entry, m_element )
+ {
+ if ( entry.localName() == "config-item-map-entry" &&
+ entry.namespaceURI() == m_settings->m_configNSURI &&
+ entry.attributeNS( m_settings->m_configNSURI, "name", QString::null ) == entryName )
+ {
+ return Items( entry, m_settings );
+ }
+ }
+ return Items( QDomElement(), m_settings );
+}
+
+// helper method
+QString KoOasisSettings::Items::findConfigItem( const QDomElement& element,
+ const QString& item, bool* ok ) const
+{
+ QDomElement it;
+ forEachElement( it, element )
+ {
+ if ( it.localName() == "config-item" &&
+ it.namespaceURI() == m_settings->m_configNSURI &&
+ it.attributeNS( m_settings->m_configNSURI, "name", QString::null ) == item )
+ {
+ *ok = true;
+ return it.text();
+ }
+ }
+ *ok = false;
+ return QString::null;
+}
+
+
+QString KoOasisSettings::Items::findConfigItem( const QString& item, bool* ok ) const
+{
+ return findConfigItem( m_element, item, ok );
+}
+
+#if 0 // does anyone need this one? passing a default value does the job, too
+bool KoOasisSettings::Items::hasConfigItem( const QString& configName ) const
+{
+ bool ok;
+ (void)findConfigItem( configName, &ok );
+ return ok;
+}
+#endif
+
+QString KoOasisSettings::Items::parseConfigItemString( const QString& configName, const QString& defValue ) const
+{
+ bool ok;
+ const QString str = findConfigItem( configName, &ok );
+ return ok ? str : defValue;
+}
+
+int KoOasisSettings::Items::parseConfigItemInt( const QString& configName, int defValue ) const
+{
+ bool ok;
+ const QString str = findConfigItem( configName, &ok );
+ int value;
+ if ( ok ) {
+ value = str.toInt( &ok );
+ if ( ok )
+ return value;
+ }
+ return defValue;
+}
+
+double KoOasisSettings::Items::parseConfigItemDouble( const QString& configName, double defValue ) const
+{
+ bool ok;
+ const QString str = findConfigItem( configName, &ok );
+ double value;
+ if ( ok ) {
+ value = str.toDouble( &ok );
+ if ( ok )
+ return value;
+ }
+ return defValue;
+}
+
+bool KoOasisSettings::Items::parseConfigItemBool( const QString& configName, bool defValue ) const
+{
+ bool ok;
+ const QString str = findConfigItem( configName, &ok );
+ if ( str == "true" )
+ return true;
+ else if ( str == "false" )
+ return false;
+ return defValue;
+}
+
+short KoOasisSettings::Items::parseConfigItemShort( const QString& configName, short defValue ) const
+{
+ bool ok;
+ const QString str = findConfigItem( configName, &ok );
+ short value;
+ if ( ok ) {
+ value = str.toShort( &ok );
+ if ( ok )
+ return value;
+ }
+ return defValue;
+}
+
+long KoOasisSettings::Items::parseConfigItemLong( const QString& configName, long defValue ) const
+{
+ bool ok;
+ const QString str = findConfigItem( configName, &ok );
+ long value;
+ if ( ok ) {
+ value = str.toLong( &ok );
+ if ( ok )
+ return value;
+ }
+ return defValue;
+}
diff --git a/lib/kofficecore/KoOasisSettings.h b/lib/kofficecore/KoOasisSettings.h
new file mode 100644
index 00000000..cd1ae112
--- /dev/null
+++ b/lib/kofficecore/KoOasisSettings.h
@@ -0,0 +1,179 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Laurent Montel <montel@kde.org>
+ David Faure <faure@kde.org>
+
+ 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 KOOASISSETTINGS_H
+#define KOOASISSETTINGS_H
+
+#include <qdom.h>
+#include <koffice_export.h>
+
+/**
+ * @brief Parse settings.xml file.
+ *
+ * This class helps parsing the settings.xml file of an OASIS document.
+ *
+ * For reference, the structure of settings.xml looks like:
+ * <pre>
+ * \<office:settings\>
+ * \<config:config-item-set config:name="configure-settings"\>
+ * ....
+ * \</config:config-item-set\>
+ * \<config:config-item-set config:name="view-settings"\>
+ * \<config:config-item-map-indexed config:name="Views"\>
+ * \<config:config-item-map-entry\>
+ * \<config:config-item config:name="SnapLinesDrawing" config:type="string"\>value\</config:config-item\>
+ * ....
+ * \<config:config-item-map-named config:name="Tables"\>
+ * \<config:config-item-map-entry config:name="Sheet1"\>
+ * \<config:config-item config:name="CursorPositionX"\>
+ * ......
+ * \</config:config-item-map-entry\>
+ * \<config:config-item-map-entry config:name="Sheet2"\>
+ * ....
+ * \</config:config-item-map-entry\>
+ * \</config:config-item-map-named\>
+ * .....
+ * \</config:config-item-map-entry\>
+ * \</config:config-item-map-indexed\>
+ * \<config:config-item-map-indexed config:name="Interface"\>
+ * .......
+ * \</config:config-item-map-indexed\>
+ * \</config:config-item-set\>
+ * \</office:settings\>
+ * </pre>
+ * Basically, an item-set is a set of named \<config-item\>s and/or maps.
+ * There are two kinds of maps (by-index or by-name), and entries in the
+ * maps contain \<config-item\>s too, or nested maps.
+ *
+ * The API of KoOasisSettings allows the caller to look for a given item-set
+ * or item-map once, and then lookup multiple items inside it.
+ * It also allows "drilling down" inside the tree in case of nesting.
+ */
+class KOFFICECORE_EXPORT KoOasisSettings
+{
+public:
+ /**
+ * Normal KoOasisSettings constructor, for an OASIS settings.xml
+ */
+ explicit KoOasisSettings( const QDomDocument& doc );
+
+ /**
+ * KoOasisSettings constructor for an OpenOffice-1.1 file
+ */
+ KoOasisSettings( const QDomDocument& doc, const char* officeNSURI, const char* configNSURI );
+
+ class Items;
+
+ /**
+ * Returns the toplevel item-set named @p itemSetName.
+ * If not found, the returned items instance is null.
+ */
+ Items itemSet( const QString& itemSetName ) const;
+
+ class IndexedMap;
+ class NamedMap;
+ /// Represents a collection of items (config-item or maps).
+ class KOFFICECORE_EXPORT Items
+ {
+ friend class KoOasisSettings;
+ friend class IndexedMap;
+ friend class NamedMap;
+ Items( const QDomElement& elem, const KoOasisSettings* settings )
+ : m_element( elem ), m_settings( settings ) {}
+ public:
+ bool isNull() const { return m_element.isNull(); }
+
+ /**
+ * Look for the config-item-map-indexed named @p itemMapName and return it.
+ *
+ * An indexed map is an array (or sequence), i.e. items are supposed to
+ * be retrieved by index. This is useful for e.g. "view 0", "view 1" etc.
+ */
+ IndexedMap indexedMap( const QString& itemMapName ) const;
+
+ /**
+ * Look for the config-item-map-named named @p mapItemName and return it.
+ *
+ * A named map is a map where items are retrieved by entry name, @see selectItemMapEntry
+ * @return false if no such map was found
+ */
+ NamedMap namedMap( const QString& itemMapName ) const;
+
+ int parseConfigItemInt( const QString& configName, int defValue = 0 ) const;
+ double parseConfigItemDouble( const QString& configName, double defValue = 0 ) const;
+ QString parseConfigItemString( const QString& configName, const QString& defValue = QString::null ) const;
+ bool parseConfigItemBool( const QString& configName, bool defValue = false ) const;
+ short parseConfigItemShort( const QString& configName, short defValue = 0 ) const;
+ long parseConfigItemLong( const QString& configName, long defValue = 0 ) const;
+ private:
+ /// @internal
+ QString findConfigItem( const QString& item, bool* ok ) const;
+ /// @internal
+ QString findConfigItem( const QDomElement& element, const QString& item, bool* ok ) const;
+
+ QDomElement m_element;
+ const KoOasisSettings* m_settings;
+ };
+
+ /// Internal base class for IndexedMap and NamedMap
+ class Map
+ {
+ public:
+ bool isNull() const { return m_element.isNull(); }
+ protected:
+ Map( const QDomElement& elem, const KoOasisSettings* settings )
+ : m_element( elem ), m_settings( settings ) {}
+ const QDomElement m_element;
+ const KoOasisSettings* m_settings;
+ };
+
+ class KOFFICECORE_EXPORT IndexedMap : public Map
+ {
+ friend class Items;
+ IndexedMap( const QDomElement& elem, const KoOasisSettings* settings )
+ : Map( elem, settings ) {}
+ public:
+ /// Returns an entry in an indexed map
+ Items entry( int entryIndex ) const;
+ };
+
+ class KOFFICECORE_EXPORT NamedMap : public Map
+ {
+ friend class Items;
+ NamedMap( const QDomElement& elem, const KoOasisSettings* settings )
+ : Map( elem, settings ) {}
+ public:
+ /// Returns an entry in a named map
+ Items entry( const QString& entryName ) const;
+ };
+
+private:
+ friend class Items;
+ friend class IndexedMap;
+ friend class NamedMap;
+ const QDomElement m_settingsElement;
+ const char* m_configNSURI;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/lib/kofficecore/KoOasisStore.cpp b/lib/kofficecore/KoOasisStore.cpp
new file mode 100644
index 00000000..1b7537ae
--- /dev/null
+++ b/lib/kofficecore/KoOasisStore.cpp
@@ -0,0 +1,194 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 David Faure <faure@kde.org>
+
+ 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 "KoOasisStore.h"
+
+#include "KoDocument.h"
+#include "KoXmlNS.h"
+#include "KoDom.h"
+#include <KoStore.h>
+#include <KoStoreDevice.h>
+#include <KoXmlWriter.h>
+
+#include <ktempfile.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qfile.h>
+#include <qxml.h>
+#include <qbuffer.h>
+
+KoOasisStore::KoOasisStore( KoStore* store )
+ : m_store( store ),
+ m_storeDevice( 0 ),
+ m_contentWriter( 0 ),
+ m_bodyWriter( 0 ),
+ m_manifestWriter( 0 ),
+ m_contentTmpFile( 0 )
+{
+}
+
+KoOasisStore::~KoOasisStore()
+{
+ // If all the right close methods were called, nothing should remain,
+ // so those deletes are really just in case.
+ Q_ASSERT( !m_contentWriter );
+ delete m_contentWriter;
+ Q_ASSERT( !m_bodyWriter );
+ delete m_bodyWriter;
+ Q_ASSERT( !m_storeDevice );
+ delete m_storeDevice;
+ Q_ASSERT( !m_contentTmpFile );
+ delete m_contentTmpFile;
+ Q_ASSERT( !m_manifestWriter );
+ delete m_manifestWriter;
+}
+
+KoXmlWriter* KoOasisStore::contentWriter()
+{
+ if ( !m_contentWriter )
+ {
+ if ( !m_store->open( "content.xml" ) )
+ return 0;
+ m_storeDevice = new KoStoreDevice( m_store );
+ m_contentWriter = KoDocument::createOasisXmlWriter( m_storeDevice, "office:document-content" );
+ }
+ return m_contentWriter;
+}
+
+KoXmlWriter* KoOasisStore::bodyWriter()
+{
+ if ( !m_bodyWriter )
+ {
+ Q_ASSERT( !m_contentTmpFile );
+ m_contentTmpFile = new KTempFile;
+ m_contentTmpFile->setAutoDelete( true );
+ m_bodyWriter = new KoXmlWriter( m_contentTmpFile->file(), 1 );
+ }
+ return m_bodyWriter;
+}
+
+bool KoOasisStore::closeContentWriter()
+{
+ Q_ASSERT( m_bodyWriter );
+ Q_ASSERT( m_contentTmpFile );
+
+ delete m_bodyWriter; m_bodyWriter = 0;
+ // copy over the contents from the tempfile to the real one
+ QFile* tmpFile = m_contentTmpFile->file();
+ tmpFile->close();
+ m_contentWriter->addCompleteElement( tmpFile );
+ m_contentTmpFile->close();
+ delete m_contentTmpFile; m_contentTmpFile = 0;
+
+ Q_ASSERT( m_contentWriter );
+ m_contentWriter->endElement(); // document-content
+ m_contentWriter->endDocument();
+ delete m_contentWriter; m_contentWriter = 0;
+ delete m_storeDevice; m_storeDevice = 0;
+ if ( !m_store->close() ) // done with content.xml
+ return false;
+ return true;
+}
+
+KoXmlWriter* KoOasisStore::manifestWriter( const char* mimeType )
+{
+ if ( !m_manifestWriter )
+ {
+ // the pointer to the buffer is already stored in the KoXmlWriter, no need to store it here as well
+ QBuffer *manifestBuffer = new QBuffer;
+ manifestBuffer->open( IO_WriteOnly );
+ m_manifestWriter = new KoXmlWriter( manifestBuffer );
+ m_manifestWriter->startDocument( "manifest:manifest" );
+ m_manifestWriter->startElement( "manifest:manifest" );
+ m_manifestWriter->addAttribute( "xmlns:manifest", KoXmlNS::manifest );
+ m_manifestWriter->addManifestEntry( "/", mimeType );
+ }
+ return m_manifestWriter;
+}
+
+bool KoOasisStore::closeManifestWriter()
+{
+ m_manifestWriter->endElement();
+ m_manifestWriter->endDocument();
+ QBuffer* buffer = static_cast<QBuffer *>( m_manifestWriter->device() );
+ delete m_manifestWriter; m_manifestWriter = 0;
+ bool ok = false;
+ if ( m_store->open( "META-INF/manifest.xml" ) )
+ {
+ Q_LONG written = m_store->write( buffer->buffer() );
+ ok = ( written == (Q_LONG)buffer->buffer().size() && m_store->close() );
+ }
+ delete buffer;
+ return ok;
+}
+
+bool KoOasisStore::loadAndParse( const QString& fileName, QDomDocument& doc, QString& errorMessage )
+{
+ //kdDebug(30003) << "loadAndParse: Trying to open " << fileName << endl;
+
+ if (!m_store->open(fileName))
+ {
+ kdWarning(30003) << "Entry " << fileName << " not found!" << endl;
+ errorMessage = i18n( "Could not find %1" ).arg( fileName );
+ return false;
+ }
+ // Error variables for QDomDocument::setContent
+ QString errorMsg;
+ int errorLine, errorColumn;
+
+ // We need to be able to see the space in <text:span> </text:span>, this is why
+ // we activate the "report-whitespace-only-CharData" feature.
+ // Unfortunately this leads to lots of whitespace text nodes in between real
+ // elements in the rest of the document, watch out for that.
+ QXmlInputSource source( m_store->device() );
+ // Copied from QDomDocumentPrivate::setContent, to change the whitespace thing
+ QXmlSimpleReader reader;
+ KoDocument::setupXmlReader( reader, true /*namespaceProcessing*/ );
+
+ bool ok = doc.setContent( &source, &reader, &errorMsg, &errorLine, &errorColumn );
+ if ( !ok )
+ {
+ kdError(30003) << "Parsing error in " << fileName << "! Aborting!" << endl
+ << " In line: " << errorLine << ", column: " << errorColumn << endl
+ << " Error message: " << errorMsg << endl;
+ errorMessage = i18n( "Parsing error in the main document at line %1, column %2\nError message: %3" )
+ .arg( errorLine ).arg( errorColumn ).arg( i18n ( "QXml", errorMsg.utf8() ) );
+ }
+ else
+ {
+ kdDebug(30003) << "File " << fileName << " loaded and parsed" << endl;
+ }
+ m_store->close();
+ return ok;
+}
+
+QString KoOasisStore::mimeForPath( const QDomDocument& doc, const QString& fullPath )
+{
+ QDomElement docElem = doc.documentElement();
+ QDomElement elem;
+ forEachElement( elem, docElem )
+ {
+ if ( elem.localName() == "file-entry" && elem.namespaceURI() == KoXmlNS::manifest )
+ {
+ if ( elem.attributeNS( KoXmlNS::manifest, "full-path", QString::null ) == fullPath )
+ return elem.attributeNS( KoXmlNS::manifest, "media-type", QString::null );
+ }
+ }
+ return QString::null;
+}
diff --git a/lib/kofficecore/KoOasisStore.h b/lib/kofficecore/KoOasisStore.h
new file mode 100644
index 00000000..506700b0
--- /dev/null
+++ b/lib/kofficecore/KoOasisStore.h
@@ -0,0 +1,96 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 David Faure <faure@kde.org>
+
+ 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 KOOASISSTORE_H
+#define KOOASISSTORE_H
+
+class QString;
+class QDomDocument;
+class KTempFile;
+class KoXmlWriter;
+class KoStore;
+class KoStoreDevice;
+class QIODevice;
+
+/**
+ * Helper class around KoStore for writing out OASIS files.
+ * This class helps solving the problem that automatic styles must be before
+ * the body, but it's easier to iterate over the application's objects only
+ * once. So we open a KoXmlWriter into a memory buffer, write the body into it,
+ * collect automatic styles while doing that, write out automatic styles,
+ * and then copy the body XML from the buffer into the real KoXmlWriter.
+ *
+ * The typical use of this class is therefore:
+ * - write body into bodyWriter() and collect auto styles
+ * - write auto styles into contentWriter()
+ * - call closeContentWriter()
+ * - write other files into the store (styles.xml, settings.xml etc.)
+ *
+ *
+ * TODO: maybe we could encapsulate a bit more things, to e.g. handle
+ * adding manifest entries automatically.
+ *
+ * @author: David Faure <faure@kde.org>
+ */
+#include <koffice_export.h>
+
+class KOFFICECORE_EXPORT KoOasisStore
+{
+public:
+ /// @param store recontents the property of the caller
+ KoOasisStore( KoStore* store );
+
+ ~KoOasisStore();
+
+ KoStore* store() const { return m_store; }
+
+ /// Open contents.xml for writing and return the KoXmlWriter
+ KoXmlWriter* contentWriter();
+
+ /// Open another KoXmlWriter for writing out the contents
+ /// into a temporary file, to collect automatic styles while doing that.
+ KoXmlWriter* bodyWriter();
+
+ /// This will copy the body into the content writer,
+ /// delete the bodyWriter and the contentWriter, and then
+ /// close contents.xml.
+ bool closeContentWriter();
+
+ // For other files in the store, use open/addManifestEntry/KoStoreDevice/createOasisXmlWriter/close
+
+ /// Create and return a manifest writer. It will write to a memory buffer.
+ KoXmlWriter* manifestWriter( const char* mimeType );
+
+ /// Close the manifest writer, writing its contents to manifest.xml
+ bool closeManifestWriter();
+
+ /// A completely unrelated method, for loading a file from an oasis store
+ bool loadAndParse( const QString& fileName, QDomDocument& doc, QString& errorMessage );
+
+ /// Another method for loading: get mimetype from full path, using the manifest
+ static QString mimeForPath( const QDomDocument& doc, const QString& fullPath );
+
+private:
+ KoStore* m_store;
+ KoStoreDevice* m_storeDevice;
+ KoXmlWriter* m_contentWriter;
+ KoXmlWriter* m_bodyWriter;
+ KoXmlWriter* m_manifestWriter;
+ KTempFile* m_contentTmpFile;
+};
+
+#endif /* KOOASISSTORE_H */
diff --git a/lib/kofficecore/KoOasisStyles.cpp b/lib/kofficecore/KoOasisStyles.cpp
new file mode 100644
index 00000000..3a018767
--- /dev/null
+++ b/lib/kofficecore/KoOasisStyles.cpp
@@ -0,0 +1,1604 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2006 David Faure <faure@kde.org>
+
+ 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 "KoOasisStyles.h"
+#include <KoXmlWriter.h>
+#include <kdebug.h>
+#include <qdom.h>
+#include "KoDom.h"
+#include "KoXmlNS.h"
+#include "KoGenStyles.h"
+#include <qbuffer.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <qbrush.h>
+#include <KoStyleStack.h>
+
+class KoOasisStyles::Private
+{
+public:
+ // The key of the map is the family
+ QMap<QString, QDict<QDomElement> > m_styles;
+ QMap<QString, QDict<QDomElement> > m_stylesAutoStyles;
+};
+
+KoOasisStyles::KoOasisStyles()
+ : d( new Private )
+{
+ m_defaultStyle.setAutoDelete( true );
+ m_masterPages.setAutoDelete( true );
+ m_listStyles.setAutoDelete( true );
+ m_drawStyles.setAutoDelete( true );
+}
+
+KoOasisStyles::~KoOasisStyles()
+{
+ delete d;
+}
+
+void KoOasisStyles::createStyleMap( const QDomDocument& doc, bool stylesDotXml )
+{
+ const QDomElement docElement = doc.documentElement();
+ // We used to have the office:version check here, but better let the apps do that
+ QDomElement fontStyles = KoDom::namedItemNS( docElement, KoXmlNS::office, "font-decls" );
+
+ if ( !fontStyles.isNull() ) {
+ //kdDebug(30003) << "Starting reading in font-decl..." << endl;
+ insertStyles( fontStyles, false );
+ }// else
+ // kdDebug(30003) << "No items found" << endl;
+
+ //kdDebug(30003) << "Starting reading in office:automatic-styles. stylesDotXml=" << stylesDotXml << endl;
+
+ QDomElement autoStyles = KoDom::namedItemNS( docElement, KoXmlNS::office, "automatic-styles" );
+ if ( !autoStyles.isNull() ) {
+ insertStyles( autoStyles, stylesDotXml );
+ }// else
+ // kdDebug(30003) << "No items found" << endl;
+
+
+ //kdDebug(30003) << "Reading in master styles" << endl;
+
+ QDomNode masterStyles = KoDom::namedItemNS( docElement, KoXmlNS::office, "master-styles" );
+
+ if ( !masterStyles.isNull() ) {
+ QDomElement master;
+ forEachElement( master, masterStyles )
+ {
+ if ( master.localName() == "master-page" &&
+ master.namespaceURI() == KoXmlNS::style ) {
+ const QString name = master.attributeNS( KoXmlNS::style, "name", QString::null );
+ kdDebug(30003) << "Master style: '" << name << "' loaded " << endl;
+ m_masterPages.insert( name, new QDomElement( master ) );
+ } else
+ // OASIS docu mentions style:handout-master and draw:layer-set here
+ kdWarning(30003) << "Unknown tag " << master.tagName() << " in office:master-styles" << endl;
+ }
+ }
+
+
+ kdDebug(30003) << "Starting reading in office:styles" << endl;
+
+ const QDomElement officeStyle = KoDom::namedItemNS( docElement, KoXmlNS::office, "styles" );
+
+ if ( !officeStyle.isNull() ) {
+ m_officeStyle = officeStyle;
+ insertOfficeStyles( m_officeStyle );
+
+ }
+
+ //kdDebug(30003) << "Styles read in." << endl;
+}
+
+QValueVector<QDomElement> KoOasisStyles::userStyles() const
+{
+ QValueVector<QDomElement> vec;
+ // Collect user styles
+ unsigned int i = 0;
+ QDomElement e;
+ forEachElement( e, m_officeStyle )
+ {
+ if ( e.localName() == "style" &&
+ e.namespaceURI() == KoXmlNS::style )
+ {
+ vec.resize( i+1 );
+ vec[i++] = e;
+ }
+ }
+ return vec;
+}
+
+const QDict<QDomElement>& KoOasisStyles::styles(const QString& family) const
+{
+ // hmm this can create an empty item in the map, but otherwise we couldn't
+ // return a const reference.
+ return d->m_styles[family];
+}
+
+void KoOasisStyles::insertOfficeStyles( const QDomElement& styles )
+{
+ QDomElement e;
+ forEachElement( e, styles )
+ {
+ const QString localName = e.localName();
+ const QString ns = e.namespaceURI();
+ if ( ( ns == KoXmlNS::svg && (
+ localName == "linearGradient"
+ || localName == "radialGradient" ) )
+ || ( ns == KoXmlNS::draw && (
+ localName == "gradient"
+ || localName == "hatch"
+ || localName == "fill-image"
+ || localName == "marker"
+ || localName == "stroke-dash"
+ || localName == "opacity" ) )
+ )
+ {
+ const QString name = e.attributeNS( KoXmlNS::draw, "name", QString::null );
+ Q_ASSERT( !name.isEmpty() );
+ QDomElement* ep = new QDomElement( e );
+ m_drawStyles.insert( name, ep );
+ }
+ else
+ insertStyle( e, false );
+ }
+}
+
+
+void KoOasisStyles::insertStyles( const QDomElement& styles, bool styleAutoStyles )
+{
+ //kdDebug(30003) << "Inserting styles from " << styles.tagName() << endl;
+ QDomElement e;
+ forEachElement( e, styles )
+ insertStyle( e, styleAutoStyles );
+}
+
+void KoOasisStyles::insertStyle( const QDomElement& e, bool styleAutoStyles )
+{
+ const QString localName = e.localName();
+ const QString ns = e.namespaceURI();
+
+ const QString name = e.attributeNS( KoXmlNS::style, "name", QString::null );
+ if ( ns == KoXmlNS::style && localName == "style" ) {
+ const QString family = e.attributeNS( KoXmlNS::style, "family", QString::null );
+
+ if ( styleAutoStyles ) {
+ QDict<QDomElement>& dict = d->m_stylesAutoStyles[ family ];
+ dict.setAutoDelete( true );
+ if ( dict.find( name ) != 0 )
+ kdDebug(30003) << "Auto-style: '" << name << "' already exists" << endl;
+ dict.insert( name, new QDomElement( e ) );
+ //kdDebug(30003) << "Style: '" << name << "' loaded as a style auto style" << endl;
+ } else {
+ QDict<QDomElement>& dict = d->m_styles[ family ];
+ dict.setAutoDelete( true );
+
+ if ( dict.find( name ) != 0 )
+ kdDebug(30003) << "Style: '" << name << "' already exists" << endl;
+ dict.insert( name, new QDomElement( e ) );
+ //kdDebug(30003) << "Style: '" << name << "' loaded " << endl;
+ }
+ } else if ( ns == KoXmlNS::style && (
+ localName == "page-layout"
+ || localName == "font-decl"
+ || localName == "presentation-page-layout" ) )
+ {
+ if ( m_styles.find( name ) != 0 )
+ kdDebug(30003) << "Style: '" << name << "' already exists" << endl;
+ m_styles.insert( name, new QDomElement( e ) );
+ } else if ( localName == "default-style" && ns == KoXmlNS::style ) {
+ const QString family = e.attributeNS( KoXmlNS::style, "family", QString::null );
+ if ( !family.isEmpty() )
+ m_defaultStyle.insert( family, new QDomElement( e ) );
+ } else if ( localName == "list-style" && ns == KoXmlNS::text ) {
+ m_listStyles.insert( name, new QDomElement( e ) );
+ //kdDebug(30003) << "List style: '" << name << "' loaded " << endl;
+ } else if ( ns == KoXmlNS::number && (
+ localName == "number-style"
+ || localName == "currency-style"
+ || localName == "percentage-style"
+ || localName == "boolean-style"
+ || localName == "text-style"
+ || localName == "date-style"
+ || localName == "time-style" ) ) {
+ importDataStyle( e );
+ }
+ // The rest (text:*-configuration and text:outline-style) is to be done by the apps.
+}
+
+// OO spec 2.5.4. p68. Conversion to Qt format: see qdate.html
+// OpenCalcImport::loadFormat has similar code, but slower, intermixed with other stuff,
+// lacking long-textual forms.
+void KoOasisStyles::importDataStyle( const QDomElement& parent )
+{
+ NumericStyleFormat dataStyle;
+
+ const QString localName = parent.localName();
+ if (localName == "number-style")
+ dataStyle.type = NumericStyleFormat::Number;
+ else if (localName == "currency-style")
+ dataStyle.type = NumericStyleFormat::Currency;
+ else if (localName == "percentage-style")
+ dataStyle.type = NumericStyleFormat::Percentage;
+ else if (localName == "boolean-style")
+ dataStyle.type = NumericStyleFormat::Boolean;
+ else if (localName == "text-style")
+ dataStyle.type = NumericStyleFormat::Text;
+ else if (localName == "date-style")
+ dataStyle.type = NumericStyleFormat::Date;
+ else if (localName == "time-style")
+ dataStyle.type = NumericStyleFormat::Time;
+
+ QString format;
+ int precision = -1;
+ int leadingZ = 1;
+ bool thousandsSep = false;
+ //todo negred
+ //bool negRed = false;
+ bool ok = false;
+ int i = 0;
+ QDomElement e;
+ QString prefix;
+ QString suffix;
+ forEachElement( e, parent )
+ {
+ if ( e.namespaceURI() != KoXmlNS::number )
+ continue;
+ QString localName = e.localName();
+ const QString numberStyle = e.attributeNS( KoXmlNS::number, "style", QString::null );
+ const bool shortForm = numberStyle == "short" || numberStyle.isEmpty();
+ if ( localName == "day" ) {
+ format += shortForm ? "d" : "dd";
+ } else if ( localName == "day-of-week" ) {
+ format += shortForm ? "ddd" : "dddd";
+ } else if ( localName == "month" ) {
+ if ( e.attributeNS( KoXmlNS::number, "possessive-form", QString::null ) == "true" ) {
+ format += shortForm ? "PPP" : "PPPP";
+ }
+ // TODO the spec has a strange mention of number:format-source
+ else if ( e.attributeNS( KoXmlNS::number, "textual", QString::null ) == "true" ) {
+ format += shortForm ? "MMM" : "MMMM";
+ } else { // month number
+ format += shortForm ? "M" : "MM";
+ }
+ } else if ( localName == "year" ) {
+ format += shortForm ? "yy" : "yyyy";
+ } else if ( localName == "era" ) {
+ //TODO I don't know what is it... (define into oo spec)
+ } else if ( localName == "week-of-year" || localName == "quarter") {
+ // ### not supported in Qt
+ } else if ( localName == "hours" ) {
+ format += shortForm ? "h" : "hh";
+ } else if ( localName == "minutes" ) {
+ format += shortForm ? "m" : "mm";
+ } else if ( localName == "seconds" ) {
+ format += shortForm ? "s" : "ss";
+ } else if ( localName == "am-pm" ) {
+ format += "ap";
+ } else if ( localName == "text" ) { // litteral
+ format += e.text();
+ } else if ( localName == "suffix" ) {
+ suffix = e.text();
+ kdDebug()<<" suffix :"<<suffix<<endl;
+ } else if ( localName == "prefix" ) {
+ prefix = e.text();
+ kdDebug()<<" prefix :"<<prefix<<endl;
+ } else if ( localName == "currency-symbol" ) {
+ dataStyle.currencySymbol = e.text();
+ kdDebug()<<" currency-symbol: "<<dataStyle.currencySymbol<<endl;
+ format += e.text();
+ //TODO
+ // number:language="de" number:country="DE">€</number:currency-symbol>
+ // Stefan: localization of the symbol?
+ } else if ( localName == "number" ) {
+ // TODO: number:grouping="true"
+ if ( e.hasAttributeNS( KoXmlNS::number, "decimal-places" ) )
+ {
+ int d = e.attributeNS( KoXmlNS::number, "decimal-places", QString::null ).toInt( &ok );
+ if ( ok )
+ precision = d;
+ }
+ if ( e.hasAttributeNS( KoXmlNS::number, "min-integer-digits" ) )
+ {
+ int d = e.attributeNS( KoXmlNS::number, "min-integer-digits", QString::null ).toInt( &ok );
+ if ( ok )
+ leadingZ = d;
+ }
+ if ( thousandsSep && leadingZ <= 3 )
+ {
+ format += "#,";
+ for ( i = leadingZ; i <= 3; ++i )
+ format += '#';
+ }
+ for ( i = 1; i <= leadingZ; ++i )
+ {
+ format += '0';
+ if ( ( i % 3 == 0 ) && thousandsSep )
+ format =+ ',' ;
+ }
+ if (precision > -1)
+ {
+ format += '.';
+ for ( i = 0; i < precision; ++i )
+ format += '0';
+ }
+ }
+ else if ( localName == "scientific-number" ) {
+ if (dataStyle.type == NumericStyleFormat::Number)
+ dataStyle.type = NumericStyleFormat::Scientific;
+ int exp = 2;
+
+ if ( e.hasAttributeNS( KoXmlNS::number, "decimal-places" ) )
+ {
+ int d = e.attributeNS( KoXmlNS::number, "decimal-places", QString::null ).toInt( &ok );
+ if ( ok )
+ precision = d;
+ }
+
+ if ( e.hasAttributeNS( KoXmlNS::number, "min-integer-digits" ) )
+ {
+ int d = e.attributeNS( KoXmlNS::number, "min-integer-digits", QString::null ).toInt( &ok );
+ if ( ok )
+ leadingZ = d;
+ }
+
+ if ( e.hasAttributeNS( KoXmlNS::number, "min-exponent-digits" ) )
+ {
+ int d = e.attributeNS( KoXmlNS::number, "min-exponent-digits", QString::null ).toInt( &ok );
+ if ( ok )
+ exp = d;
+ if ( exp <= 0 )
+ exp = 1;
+ }
+
+ if ( thousandsSep && leadingZ <= 3 )
+ {
+ format += "#,";
+ for ( i = leadingZ; i <= 3; ++i )
+ format += '#';
+ }
+
+ for ( i = 1; i <= leadingZ; ++i )
+ {
+ format+='0';
+ if ( ( i % 3 == 0 ) && thousandsSep )
+ format+=',';
+ }
+
+ if (precision > -1)
+ {
+ format += '.';
+ for ( i = 0; i < precision; ++i )
+ format += '0';
+ }
+
+ format+="E+";
+ for ( i = 0; i < exp; ++i )
+ format+='0';
+ } else if ( localName == "fraction" ) {
+ if (dataStyle.type == NumericStyleFormat::Number)
+ dataStyle.type = NumericStyleFormat::Fraction;
+ int integer = 0;
+ int numerator = 1;
+ int denominator = 1;
+ int denominatorValue=0;
+ if ( e.hasAttributeNS( KoXmlNS::number, "min-integer-digits" ) )
+ {
+ int d = e.attributeNS( KoXmlNS::number, "min-integer-digits", QString::null ).toInt( &ok );
+ if ( ok )
+ integer = d;
+ }
+ if ( e.hasAttributeNS( KoXmlNS::number, "min-numerator-digits" ) )
+ {
+ int d = e.attributeNS( KoXmlNS::number, "min-numerator-digits", QString::null ).toInt( &ok );
+ if ( ok )
+ numerator = d;
+ }
+ if ( e.hasAttributeNS( KoXmlNS::number, "min-denominator-digits" ) )
+ {
+ int d = e.attributeNS( KoXmlNS::number, "min-denominator-digits", QString::null ).toInt( &ok );
+ if ( ok )
+ denominator = d;
+ }
+ if ( e.hasAttributeNS( KoXmlNS::number, "denominator-value" ) )
+ {
+ int d = e.attributeNS( KoXmlNS::number, "denominator-value", QString::null ).toInt( &ok );
+ if ( ok )
+ denominatorValue = d;
+ }
+
+ for ( i = 0; i < integer; ++i )
+ format+='#';
+
+ format+=' ';
+
+ for ( i = 0; i < numerator; ++i )
+ format+='?';
+
+ format+='/';
+
+ if ( denominatorValue != 0 )
+ format+=QString::number( denominatorValue );
+ else
+ {
+ for ( i = 0; i < denominator; ++i )
+ format+='?';
+ }
+ }
+ // Not needed:
+ // <style:map style:condition="value()&gt;=0" style:apply-style-name="N106P0"/>
+ // we handle painting negative numbers in red differently
+
+ }
+
+ const QString styleName = parent.attributeNS( KoXmlNS::style, "name", QString::null );
+ kdDebug(30003) << "data style: " << styleName << " qt format=" << format << endl;
+ if ( !prefix.isEmpty() )
+ {
+ kdDebug(30003)<<" format.left( prefix.length() ) :"<<format.left( prefix.length() )<<" prefix :"<<prefix<<endl;
+ if ( format.left( prefix.length() )==prefix )
+ {
+ format = format.right( format.length()-prefix.length() );
+ }
+ else
+ prefix = QString::null;
+ }
+ if ( !suffix.isEmpty() )
+ {
+ kdDebug(30003)<<"format.right( suffix.length() ) :"<<format.right( suffix.length() )<<" suffix :"<<suffix<<endl;
+ if ( format.right( suffix.length() )==suffix )
+ {
+ format = format.left( format.length()-suffix.length() );
+ }
+ else
+ suffix = QString::null;
+ }
+
+ dataStyle.formatStr = format;
+ dataStyle.prefix = prefix;
+ dataStyle.suffix = suffix;
+ dataStyle.precision = precision;
+ kdDebug()<<" finish insert format :"<<format<<" prefix :"<<prefix<<" suffix :"<<suffix<<endl;
+ m_dataFormats.insert( styleName, dataStyle );
+}
+
+#define addTextNumber( text, elementWriter ) { \
+ if ( !text.isEmpty() ) \
+ { \
+ elementWriter.startElement( "number:text" ); \
+ elementWriter.addTextNode( text ); \
+ elementWriter.endElement(); \
+ text=""; \
+ } \
+}
+
+void KoOasisStyles::parseOasisTimeKlocale(KoXmlWriter &elementWriter, QString & format, QString & text )
+{
+ kdDebug(30003)<<"parseOasisTimeKlocale(KoXmlWriter &elementWriter, QString & format, QString & text ) :"<<format<<endl;
+ do
+ {
+ if ( !saveOasisKlocaleTimeFormat( elementWriter, format, text ) )
+ {
+ text += format[0];
+ format = format.remove( 0, 1 );
+ }
+ }
+ while ( format.length() > 0 );
+ addTextNumber( text, elementWriter );
+}
+
+bool KoOasisStyles::saveOasisKlocaleTimeFormat( KoXmlWriter &elementWriter, QString & format, QString & text )
+{
+ bool changed = false;
+ if ( format.startsWith( "%H" ) ) //hh
+ {
+ //hour in 24h
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:hours" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ changed = true;
+ }
+ else if ( format.startsWith( "%k" ) )//h
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:hours" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ changed = true;
+ }
+ else if ( format.startsWith( "%I" ) )// ?????
+ {
+ //TODO hour in 12h
+ changed = true;
+ }
+ else if ( format.startsWith( "%l" ) )
+ {
+ //TODO hour in 12h with 1 digit
+ changed = true;
+ }
+ else if ( format.startsWith( "%M" ) )// mm
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:minutes" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ changed = true;
+
+ }
+ else if ( format.startsWith( "%S" ) ) //ss
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:seconds" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ changed = true;
+ }
+ else if ( format.startsWith( "%p" ) )
+ {
+ //TODO am or pm
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:am-pm" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ changed = true;
+ }
+ return changed;
+}
+
+
+bool KoOasisStyles::saveOasisTimeFormat( KoXmlWriter &elementWriter, QString & format, QString & text, bool &antislash )
+{
+ bool changed = false;
+ //we can also add time to date.
+ if ( antislash )
+ {
+ text+=format[0];
+ format = format.remove( 0, 1 );
+ antislash = false;
+ changed = true;
+ }
+ else if ( format.startsWith( "hh" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:hours" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ changed = true;
+ }
+ else if ( format.startsWith( "h" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:hours" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.endElement();
+ format = format.remove( 0, 1 );
+ changed = true;
+ }
+ else if ( format.startsWith( "mm" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:minutes" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ changed = true;
+ }
+ else if ( format.startsWith( "m" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:minutes" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.endElement();
+ format = format.remove( 0, 1 );
+ changed = true;
+ }
+ else if ( format.startsWith( "ss" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:seconds" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ changed = true;
+ }
+ else if ( format.startsWith( "s" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:seconds" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.endElement();
+ format = format.remove( 0, 1 );
+ changed = true;
+ }
+ else if ( format.startsWith( "ap" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:am-pm" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ changed = true;
+ }
+ return changed;
+}
+
+QString KoOasisStyles::saveOasisTimeStyle( KoGenStyles &mainStyles, const QString & _format, bool klocaleFormat,
+ const QString & _prefix, const QString & _suffix )
+{
+ Q_UNUSED(_prefix);
+ Q_UNUSED(_suffix);
+ kdDebug(30003)<<"QString KoOasisStyles::saveOasisTimeStyle( KoGenStyles &mainStyles, const QString & _format ) :"<<_format<<endl;
+ QString format( _format );
+ KoGenStyle currentStyle( KoGenStyle::STYLE_NUMERIC_TIME );
+ QBuffer buffer;
+ buffer.open( IO_WriteOnly );
+ KoXmlWriter elementWriter( &buffer ); // TODO pass indentation level
+ QString text;
+ if ( klocaleFormat )
+ {
+ parseOasisTimeKlocale( elementWriter, format, text );
+ }
+ else
+ {
+ bool antislash = false;
+ do
+ {
+ if ( !saveOasisTimeFormat( elementWriter, format, text, antislash ) )
+ {
+ QString elem( format[0] );
+ format = format.remove( 0, 1 );
+ if ( elem == "\\" )
+ {
+ antislash = true;
+ }
+ else
+ {
+ text += elem;
+ antislash = false;
+ }
+ }
+ }
+ while ( format.length() > 0 );
+ addTextNumber( text, elementWriter );
+ }
+ QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
+ currentStyle.addChildElement( "number", elementContents );
+ return mainStyles.lookup( currentStyle, "N" );
+}
+
+//convert klocale string to good format
+void KoOasisStyles::parseOasisDateKlocale(KoXmlWriter &elementWriter, QString & format, QString & text )
+{
+ kdDebug(30003)<<"KoOasisStyles::parseOasisDateKlocale(KoXmlWriter &elementWriter, QString & format, QString & text ) :"<<format<<endl;
+ do
+ {
+ if ( format.startsWith( "%Y" ) )
+ {
+ addTextNumber( text, elementWriter );
+ elementWriter.startElement( "number:year" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ }
+ else if ( format.startsWith( "%y" ) )
+ {
+
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:year" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ }
+ else if ( format.startsWith( "%n" ) )
+ {
+ addTextNumber( text, elementWriter );
+ elementWriter.startElement( "number:month" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.addAttribute( "number:textual", "false");
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ }
+ else if ( format.startsWith( "%m" ) )
+ {
+ addTextNumber( text, elementWriter );
+ elementWriter.startElement( "number:month" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.addAttribute( "number:textual", "false"); //not necessary remove it
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ }
+ else if ( format.startsWith( "%e" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:day" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ }
+ else if ( format.startsWith( "%d" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:day" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ }
+ else if ( format.startsWith( "%b" ) )
+ {
+ addTextNumber( text, elementWriter );
+ elementWriter.startElement( "number:month" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.addAttribute( "number:textual", "true");
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ }
+ else if ( format.startsWith( "%B" ) )
+ {
+ addTextNumber( text, elementWriter );
+ elementWriter.startElement( "number:month" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.addAttribute( "number:textual", "true");
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ }
+ else if ( format.startsWith( "%a" ) )
+ {
+ addTextNumber( text, elementWriter );
+ elementWriter.startElement( "number:day-of-week" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.endElement();
+
+ format = format.remove( 0, 2 );
+ }
+ else if ( format.startsWith( "%A" ) )
+ {
+ addTextNumber( text, elementWriter );
+ elementWriter.startElement( "number:day-of-week" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ }
+ else
+ {
+ if ( !saveOasisKlocaleTimeFormat( elementWriter, format, text ) )
+ {
+ text += format[0];
+ format = format.remove( 0, 1 );
+ }
+ }
+ }
+ while ( format.length() > 0 );
+ addTextNumber( text, elementWriter );
+}
+
+QString KoOasisStyles::saveOasisDateStyle( KoGenStyles &mainStyles, const QString & _format, bool klocaleFormat,
+ const QString & _prefix, const QString & _suffix )
+{
+ Q_UNUSED(_prefix);
+ Q_UNUSED(_suffix);
+ kdDebug(30003)<<"QString KoOasisStyles::saveOasisDateStyle( KoGenStyles &mainStyles, const QString & _format ) :"<<_format<<endl;
+ QString format( _format );
+
+ // Not supported into Qt: "era" "week-of-year" "quarter"
+
+ KoGenStyle currentStyle( KoGenStyle::STYLE_NUMERIC_DATE );
+ QBuffer buffer;
+ buffer.open( IO_WriteOnly );
+ KoXmlWriter elementWriter( &buffer ); // TODO pass indentation level
+ QString text;
+ if ( klocaleFormat )
+ {
+ parseOasisDateKlocale( elementWriter, format, text );
+ }
+ else
+ {
+ bool antislash = false;
+ do
+ {
+ if ( antislash )
+ {
+ text+=format[0];
+ format = format.remove( 0, 1 );
+ }
+ //TODO implement loading ! What is it ?
+ else if ( format.startsWith( "MMMMM" ) )
+ {
+ addTextNumber( text, elementWriter );
+ elementWriter.startElement( "number:month" );
+ elementWriter.addAttribute( "number:textual", "true");
+ elementWriter.endElement();
+ format = format.remove( 0, 5 );
+ }
+ else if ( format.startsWith( "MMMM" ) )
+ {
+ addTextNumber( text, elementWriter );
+ elementWriter.startElement( "number:month" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.addAttribute( "number:textual", "true");
+ elementWriter.endElement();
+ format = format.remove( 0, 4 );
+ }
+ else if ( format.startsWith( "MMM" ) )
+ {
+ addTextNumber( text, elementWriter );
+ elementWriter.startElement( "number:month" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.addAttribute( "number:textual", "true");
+ elementWriter.endElement();
+ format = format.remove( 0, 3 );
+ }
+ else if ( format.startsWith( "MM" ) )
+ {
+ addTextNumber( text, elementWriter );
+ elementWriter.startElement( "number:month" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.addAttribute( "number:textual", "false"); //not necessary remove it
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ }
+ else if ( format.startsWith( "M" ) )
+ {
+ addTextNumber( text, elementWriter );
+ elementWriter.startElement( "number:month" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.addAttribute( "number:textual", "false");
+ elementWriter.endElement();
+ format = format.remove( 0, 1 );
+ }
+ else if ( format.startsWith( "PPPP" ) )
+ {
+ addTextNumber( text, elementWriter );
+ //<number:month number:possessive-form="true" number:textual="true" number:style="long"/>
+ elementWriter.startElement( "number:month" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.addAttribute( "number:textual", "false");
+ elementWriter.addAttribute( "number:possessive-form", "true" );
+ elementWriter.endElement();
+ format = format.remove( 0, 4 );
+ }
+ else if ( format.startsWith( "PPP" ) )
+ {
+ addTextNumber( text, elementWriter );
+ //<number:month number:possessive-form="true" number:textual="true" number:style="short"/>
+ elementWriter.startElement( "number:month" );
+ elementWriter.addAttribute( "number:possessive-form", "true" );
+
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.addAttribute( "number:textual", "false");
+ elementWriter.endElement();
+ format = format.remove( 0, 3 );
+ }
+ else if ( format.startsWith( "dddd" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:day-of-week" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.endElement();
+ format = format.remove( 0, 4 );
+ }
+ else if ( format.startsWith( "ddd" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:day-of-week" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.endElement();
+ format = format.remove( 0, 3 );
+ }
+ else if ( format.startsWith( "dd" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:day" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ }
+ else if ( format.startsWith( "d" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:day" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.endElement();
+ format = format.remove( 0, 1 );
+ }
+ else if ( format.startsWith( "yyyy" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:year" );
+ elementWriter.addAttribute( "number:style", "long" );
+ elementWriter.endElement();
+ format = format.remove( 0, 4 );
+ }
+ else if ( format.startsWith( "yy" ) )
+ {
+ addTextNumber( text, elementWriter );
+
+ elementWriter.startElement( "number:year" );
+ elementWriter.addAttribute( "number:style", "short" );
+ elementWriter.endElement();
+ format = format.remove( 0, 2 );
+ }
+ else
+ {
+ if ( !saveOasisTimeFormat( elementWriter, format, text,antislash ) )
+ {
+ QString elem( format[0] );
+ format = format.remove( 0, 1 );
+ if ( elem == "\\" )
+ {
+ antislash = true;
+ }
+ else
+ {
+ text += elem;
+ antislash = false;
+ }
+ }
+ }
+ }
+ while ( format.length() > 0 );
+ addTextNumber( text, elementWriter );
+ }
+
+ QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
+ currentStyle.addChildElement( "number", elementContents );
+ return mainStyles.lookup( currentStyle, "N" );
+}
+
+
+QString KoOasisStyles::saveOasisFractionStyle( KoGenStyles &mainStyles, const QString & _format, const QString &_prefix, const QString &_suffix )
+{
+ kdDebug(30003)<<"QString saveOasisFractionStyle( KoGenStyles &mainStyles, const QString & _format ) :"<<_format<<endl;
+ QString format( _format );
+
+ KoGenStyle currentStyle( KoGenStyle::STYLE_NUMERIC_FRACTION );
+ QBuffer buffer;
+ buffer.open( IO_WriteOnly );
+ KoXmlWriter elementWriter( &buffer ); // TODO pass indentation level
+ QString text;
+ int integer = 0;
+ int numerator = 0;
+ int denominator = 0;
+ int denominatorValue = 0;
+ bool beforeSlash = true;
+ do
+ {
+ if ( format[0]=='#' )
+ integer++;
+ else if ( format[0]=='/' )
+ beforeSlash = false;
+ else if ( format[0]=='?' )
+ {
+ if ( beforeSlash )
+ numerator++;
+ else
+ denominator++;
+ }
+ else
+ {
+ bool ok;
+ int value = format.toInt( &ok );
+ if ( ok )
+ {
+ denominatorValue=value;
+ break;
+ }
+ }
+ format.remove( 0,1 );
+ }
+ while ( format.length() > 0 );
+
+ text= _prefix;
+ addTextNumber(text, elementWriter );
+
+ elementWriter.startElement( "number:fraction" );
+ elementWriter.addAttribute( "number:min-integer-digits", integer );
+ elementWriter.addAttribute( "number:min-numerator-digits",numerator );
+ elementWriter.addAttribute( "number:min-denominator-digits",denominator );
+ if ( denominatorValue != 0 )
+ elementWriter.addAttribute( "number:denominator-value",denominatorValue );
+ elementWriter.endElement();
+
+ addKofficeNumericStyleExtension( elementWriter, _suffix, _prefix );
+
+ text=_suffix;
+ addTextNumber(text, elementWriter );
+
+ QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
+ currentStyle.addChildElement( "number", elementContents );
+ return mainStyles.lookup( currentStyle, "N" );
+}
+
+
+QString KoOasisStyles::saveOasisNumberStyle( KoGenStyles &mainStyles, const QString & _format, const QString &_prefix, const QString &_suffix )
+{
+ kdDebug(30003)<<"QString saveOasisNumberStyle( KoGenStyles &mainStyles, const QString & _format ) :"<<_format<<endl;
+ QString format( _format );
+
+ KoGenStyle currentStyle( KoGenStyle::STYLE_NUMERIC_NUMBER );
+ QBuffer buffer;
+ buffer.open( IO_WriteOnly );
+ KoXmlWriter elementWriter( &buffer ); // TODO pass indentation level
+ QString text;
+ int decimalplaces = 0;
+ int integerdigits = 0;
+ bool beforeSeparator = true;
+ do
+ {
+ if ( format[0]=='.' || format[0]==',' )
+ beforeSeparator = false;
+ else if ( format[0]=='0' && beforeSeparator )
+ integerdigits++;
+ else if ( format[0]=='0' && !beforeSeparator )
+ decimalplaces++;
+ else
+ kdDebug(30003)<<" error format 0 \n";
+ format.remove( 0,1 );
+ }
+ while ( format.length() > 0 );
+ text= _prefix ;
+ addTextNumber(text, elementWriter );
+ elementWriter.startElement( "number:number" );
+ kdDebug(30003)<<" decimalplaces :"<<decimalplaces<<" integerdigits :"<<integerdigits<<endl;
+ if (!beforeSeparator)
+ elementWriter.addAttribute( "number:decimal-places", decimalplaces );
+ elementWriter.addAttribute( "number:min-integer-digits", integerdigits );
+ elementWriter.endElement();
+
+ text =_suffix ;
+ addTextNumber(text, elementWriter );
+ addKofficeNumericStyleExtension( elementWriter, _suffix,_prefix );
+
+ QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
+ currentStyle.addChildElement( "number", elementContents );
+ return mainStyles.lookup( currentStyle, "N" );
+}
+
+QString KoOasisStyles::saveOasisPercentageStyle( KoGenStyles &mainStyles, const QString & _format, const QString &_prefix, const QString &_suffix )
+{
+ //<number:percentage-style style:name="N11">
+ //<number:number number:decimal-places="2" number:min-integer-digits="1"/>
+ //<number:text>%</number:text>
+ //</number:percentage-style>
+
+ kdDebug(30003)<<"QString saveOasisPercentageStyle( KoGenStyles &mainStyles, const QString & _format ) :"<<_format<<endl;
+ QString format( _format );
+
+ KoGenStyle currentStyle( KoGenStyle::STYLE_NUMERIC_PERCENTAGE );
+ QBuffer buffer;
+ buffer.open( IO_WriteOnly );
+ KoXmlWriter elementWriter( &buffer ); // TODO pass indentation level
+ QString text;
+ int decimalplaces = 0;
+ int integerdigits = 0;
+ bool beforeSeparator = true;
+ do
+ {
+ if ( format[0]=='.' || format[0]==',' )
+ beforeSeparator = false;
+ else if ( format[0]=='0' && beforeSeparator )
+ integerdigits++;
+ else if ( format[0]=='0' && !beforeSeparator )
+ decimalplaces++;
+ else
+ kdDebug(30003)<<" error format 0 \n";
+ format.remove( 0,1 );
+ }
+ while ( format.length() > 0 );
+ text= _prefix ;
+ addTextNumber(text, elementWriter );
+ elementWriter.startElement( "number:number" );
+ if (!beforeSeparator)
+ elementWriter.addAttribute( "number:decimal-places", decimalplaces );
+ elementWriter.addAttribute( "number:min-integer-digits", integerdigits );
+ elementWriter.endElement();
+
+ addTextNumber(QString( "%" ), elementWriter );
+
+ text =_suffix ;
+ addTextNumber(text, elementWriter );
+ addKofficeNumericStyleExtension( elementWriter, _suffix,_prefix );
+
+ QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
+ currentStyle.addChildElement( "number", elementContents );
+ return mainStyles.lookup( currentStyle, "N" );
+
+}
+
+QString KoOasisStyles::saveOasisScientificStyle( KoGenStyles &mainStyles, const QString & _format, const QString &_prefix, const QString &_suffix )
+{
+ //<number:number-style style:name="N60">
+ //<number:scientific-number number:decimal-places="2" number:min-integer-digits="1" number:min-exponent-digits="3"/>
+ //</number:number-style>
+
+ //example 000,000e+0000
+ kdDebug(30003)<<"QString saveOasisScientificStyle( KoGenStyles &mainStyles, const QString & _format ) :"<<_format<<endl;
+ QString format( _format );
+
+ KoGenStyle currentStyle( KoGenStyle::STYLE_NUMERIC_SCIENTIFIC );
+ QBuffer buffer;
+ buffer.open( IO_WriteOnly );
+ int decimalplace = 0;
+ int integerdigits = 0;
+ int exponentdigits = 0;
+ KoXmlWriter elementWriter( &buffer ); // TODO pass indentation level
+ QString text;
+ bool beforeSeparator = true;
+ bool exponential = false;
+ bool positive = true;
+ do
+ {
+ if ( !exponential )
+ {
+ if ( format[0]=='0' && beforeSeparator )
+ integerdigits++;
+ else if ( format[0]==',' || format[0]=='.' )
+ beforeSeparator = false;
+ else if ( format[0]=='0' && !beforeSeparator )
+ decimalplace++;
+ else if ( format[0].lower()=='e' )
+ {
+ format.remove( 0, 1 );
+ if ( format[0]=='+' )
+ positive = true;
+ else if ( format[0]=='-' )
+ positive = false;
+ else
+ kdDebug(30003)<<"Error into scientific number\n";
+ exponential = true;
+ }
+ }
+ else
+ {
+ if ( format[0]=='0' && positive )
+ exponentdigits++;
+ else if ( format[0]=='0' && !positive )
+ exponentdigits--;
+ else
+ kdDebug(30003)<<" error into scientific number exponential value\n";
+ }
+ format.remove( 0,1 );
+ }
+ while ( format.length() > 0 );
+ text = _prefix ;
+ addTextNumber(text, elementWriter );
+
+ elementWriter.startElement( "number:scientific-number" );
+ kdDebug(30003)<<" decimalplace :"<<decimalplace<<" integerdigits :"<<integerdigits<<" exponentdigits :"<<exponentdigits<<endl;
+ if (!beforeSeparator)
+ elementWriter.addAttribute( "number:decimal-places", decimalplace );
+ elementWriter.addAttribute( "number:min-integer-digits",integerdigits );
+ elementWriter.addAttribute( "number:min-exponent-digits",exponentdigits );
+ elementWriter.endElement();
+
+ text = _suffix;
+ addTextNumber(text, elementWriter );
+ addKofficeNumericStyleExtension( elementWriter, _suffix,_prefix );
+
+ QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
+ currentStyle.addChildElement( "number", elementContents );
+ return mainStyles.lookup( currentStyle, "N" );
+}
+
+QString KoOasisStyles::saveOasisCurrencyStyle( KoGenStyles &mainStyles,
+ const QString & _format, const QString &symbol,
+ const QString &_prefix, const QString &_suffix )
+{
+
+ //<number:currency-style style:name="N107P0" style:volatile="true">
+ //<number:number number:decimal-places="2" number:min-integer-digits="1" number:grouping="true"/>
+ //<number:text> </number:text>
+ //<number:currency-symbol>VEB</number:currency-symbol>
+ //</number:currency-style>
+
+ kdDebug(30003)<<"QString saveOasisCurrencyStyle( KoGenStyles &mainStyles, const QString & _format ) :"<<_format<<endl;
+ QString format( _format );
+
+ KoGenStyle currentStyle( KoGenStyle::STYLE_NUMERIC_CURRENCY );
+ QBuffer buffer;
+ buffer.open( IO_WriteOnly );
+ KoXmlWriter elementWriter( &buffer ); // TODO pass indentation level
+ QString text;
+ int decimalplaces = 0;
+ int integerdigits = 0;
+ bool beforeSeparator = true;
+ do
+ {
+ if ( format[0]=='.' || format[0]==',' )
+ beforeSeparator = false;
+ else if ( format[0]=='0' && beforeSeparator )
+ integerdigits++;
+ else if ( format[0]=='0' && !beforeSeparator )
+ decimalplaces++;
+ else
+ kdDebug(30003)<<" error format 0 \n";
+ format.remove( 0,1 );
+ }
+ while ( format.length() > 0 );
+
+ text = _prefix ;
+ addTextNumber(text, elementWriter );
+
+ elementWriter.startElement( "number:number" );
+ kdDebug(30003)<<" decimalplaces :"<<decimalplaces<<" integerdigits :"<<integerdigits<<endl;
+ if (!beforeSeparator)
+ elementWriter.addAttribute( "number:decimal-places", decimalplaces );
+ elementWriter.addAttribute( "number:min-integer-digits", integerdigits );
+ elementWriter.endElement();
+
+ text = _suffix ;
+ addTextNumber(text, elementWriter );
+ addKofficeNumericStyleExtension( elementWriter, _suffix,_prefix );
+
+ elementWriter.startElement( "number:currency-symbol" );
+ kdDebug(30003) << " currency-symbol: " << symbol << endl;
+ elementWriter.addTextNode( symbol.utf8() );
+ elementWriter.endElement();
+
+ QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
+ currentStyle.addChildElement( "number", elementContents );
+ return mainStyles.lookup( currentStyle, "N" );
+}
+
+QString KoOasisStyles::saveOasisTextStyle( KoGenStyles &mainStyles, const QString & _format, const QString &_prefix, const QString &_suffix )
+{
+
+ //<number:text-style style:name="N100">
+ //<number:text-content/>
+ ///</number:text-style>
+
+ kdDebug(30003)<<"QString saveOasisTextStyle( KoGenStyles &mainStyles, const QString & _format ) :"<<_format<<endl;
+ QString format( _format );
+
+ KoGenStyle currentStyle( KoGenStyle::STYLE_NUMERIC_TEXT );
+ QBuffer buffer;
+ buffer.open( IO_WriteOnly );
+ KoXmlWriter elementWriter( &buffer ); // TODO pass indentation level
+ QString text;
+ do
+ {
+ format.remove( 0,1 );
+ }
+ while ( format.length() > 0 );
+ text = _prefix ;
+ addTextNumber(text, elementWriter );
+
+ elementWriter.startElement( "number:text-style" );
+
+ text = _suffix ;
+ addTextNumber(text, elementWriter );
+ addKofficeNumericStyleExtension( elementWriter, _suffix,_prefix );
+ elementWriter.endElement();
+
+ QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
+ currentStyle.addChildElement( "number", elementContents );
+ return mainStyles.lookup( currentStyle, "N" );
+}
+
+//This is an extension of numeric style. For the moment we used namespace of oasis format for specific koffice extention. change it for the futur.
+void KoOasisStyles::addKofficeNumericStyleExtension( KoXmlWriter & elementWriter, const QString &_suffix, const QString &_prefix )
+ {
+ if ( !_suffix.isEmpty() )
+ {
+ elementWriter.startElement( "number:suffix" );
+ elementWriter.addTextNode( _suffix );
+ elementWriter.endElement();
+ }
+ if ( !_prefix.isEmpty() )
+ {
+ elementWriter.startElement( "number:prefix" );
+ elementWriter.addTextNode( _prefix );
+ elementWriter.endElement();
+ }
+}
+
+void KoOasisStyles::saveOasisFillStyle( KoGenStyle &styleFill, KoGenStyles& mainStyles, const QBrush & brush )
+{
+ if ( brush.style() == Qt::SolidPattern )
+ {
+ styleFill.addProperty( "draw:fill","solid" );
+ styleFill.addProperty( "draw:fill-color", brush.color().name() );
+ }
+ else if ( brush.style() == Qt::Dense1Pattern )
+ {
+ styleFill.addProperty( "draw:transparency", "94%" );
+ styleFill.addProperty( "draw:fill","solid" );
+ styleFill.addProperty( "draw:fill-color", brush.color().name() );
+ }
+ else if ( brush.style() == Qt::Dense2Pattern )
+ {
+ styleFill.addProperty( "draw:transparency", "88%" );
+ styleFill.addProperty( "draw:fill","solid" );
+ styleFill.addProperty( "draw:fill-color", brush.color().name() );
+ }
+ else if ( brush.style() == Qt::Dense3Pattern )
+ {
+ styleFill.addProperty( "draw:transparency", "63%" );
+ styleFill.addProperty( "draw:fill","solid" );
+ styleFill.addProperty( "draw:fill-color", brush.color().name() );
+ }
+ else if ( brush.style() == Qt::Dense4Pattern )
+ {
+ styleFill.addProperty( "draw:transparency", "50%" );
+ styleFill.addProperty( "draw:fill","solid" );
+ styleFill.addProperty( "draw:fill-color", brush.color().name() );
+ }
+ else if ( brush.style() == Qt::Dense5Pattern )
+ {
+ styleFill.addProperty( "draw:transparency", "37%" );
+ styleFill.addProperty( "draw:fill","solid" );
+ styleFill.addProperty( "draw:fill-color", brush.color().name() );
+ }
+ else if ( brush.style() == Qt::Dense6Pattern )
+ {
+ styleFill.addProperty( "draw:transparency", "12%" );
+ styleFill.addProperty( "draw:fill","solid" );
+ styleFill.addProperty( "draw:fill-color", brush.color().name() );
+ }
+ else if ( brush.style() == Qt::Dense7Pattern )
+ {
+ styleFill.addProperty( "draw:transparency", "6%" );
+ styleFill.addProperty( "draw:fill","solid" );
+ styleFill.addProperty( "draw:fill-color", brush.color().name() );
+ }
+ else //otherstyle
+ {
+ styleFill.addProperty( "draw:fill","hatch" );
+ styleFill.addProperty( "draw:fill-hatch-name", saveOasisHatchStyle( mainStyles,brush ) );
+ }
+
+}
+
+QString KoOasisStyles::saveOasisHatchStyle( KoGenStyles& mainStyles, const QBrush &brush )
+{
+ KoGenStyle hatchStyle( KoGenStyle::STYLE_HATCH /*no family name*/);
+ hatchStyle.addAttribute( "draw:color", brush.color().name() );
+ //hatchStyle.addAttribute( "draw:distance", m_distance ); not implemented into kpresenter
+ switch( brush.style() )
+ {
+ case Qt::HorPattern:
+ hatchStyle.addAttribute( "draw:style", "single" );
+ hatchStyle.addAttribute( "draw:rotation", 0);
+ break;
+ case Qt::BDiagPattern:
+ hatchStyle.addAttribute( "draw:style", "single" );
+ hatchStyle.addAttribute( "draw:rotation", 450);
+ break;
+ case Qt::VerPattern:
+ hatchStyle.addAttribute( "draw:style", "single" );
+ hatchStyle.addAttribute( "draw:rotation", 900);
+ break;
+ case Qt::FDiagPattern:
+ hatchStyle.addAttribute( "draw:style", "single" );
+ hatchStyle.addAttribute( "draw:rotation", 1350);
+ break;
+ case Qt::CrossPattern:
+ hatchStyle.addAttribute( "draw:style", "double" );
+ hatchStyle.addAttribute( "draw:rotation", 0);
+ break;
+ case Qt::DiagCrossPattern:
+ hatchStyle.addAttribute( "draw:style", "double" );
+ hatchStyle.addAttribute( "draw:rotation", 450);
+ break;
+ default:
+ break;
+ }
+
+ return mainStyles.lookup( hatchStyle, "hatch" );
+}
+
+QBrush KoOasisStyles::loadOasisFillStyle( const KoStyleStack &styleStack, const QString & fill, const KoOasisStyles & oasisStyles )
+{
+ QBrush tmpBrush;
+ if ( fill == "solid" )
+ {
+ tmpBrush.setStyle(static_cast<Qt::BrushStyle>( 1 ) );
+ if ( styleStack.hasAttributeNS( KoXmlNS::draw, "fill-color" ) )
+ tmpBrush.setColor(styleStack.attributeNS( KoXmlNS::draw, "fill-color" ) );
+ if ( styleStack.hasAttributeNS( KoXmlNS::draw, "transparency" ) )
+ {
+ QString transparency = styleStack.attributeNS( KoXmlNS::draw, "transparency" );
+ if ( transparency == "94%" )
+ {
+ tmpBrush.setStyle(Qt::Dense1Pattern);
+ }
+ else if ( transparency == "88%" )
+ {
+ tmpBrush.setStyle(Qt::Dense2Pattern);
+ }
+ else if ( transparency == "63%" )
+ {
+ tmpBrush.setStyle(Qt::Dense3Pattern);
+
+ }
+ else if ( transparency == "50%" )
+ {
+ tmpBrush.setStyle(Qt::Dense4Pattern);
+
+ }
+ else if ( transparency == "37%" )
+ {
+ tmpBrush.setStyle(Qt::Dense5Pattern);
+
+ }
+ else if ( transparency == "12%" )
+ {
+ tmpBrush.setStyle(Qt::Dense6Pattern);
+
+ }
+ else if ( transparency == "6%" )
+ {
+ tmpBrush.setStyle(Qt::Dense7Pattern);
+
+ }
+ else
+ kdDebug()<<" transparency is not defined into kpresenter :"<<transparency<<endl;
+ }
+ }
+ else if ( fill == "hatch" )
+ {
+ QString style = styleStack.attributeNS( KoXmlNS::draw, "fill-hatch-name" );
+ kdDebug()<<" hatch style is : "<<style<<endl;
+
+ //type not defined by default
+ //try to use style.
+ QDomElement* draw = oasisStyles.drawStyles()[style];
+ if ( draw)
+ {
+ kdDebug()<<"We have a style\n";
+ int angle = 0;
+ if( draw->hasAttributeNS( KoXmlNS::draw, "rotation" ))
+ {
+ angle = (draw->attributeNS( KoXmlNS::draw, "rotation", QString::null ).toInt())/10;
+ kdDebug()<<"angle :"<<angle<<endl;
+ }
+ if(draw->hasAttributeNS( KoXmlNS::draw, "color" ) )
+ {
+ //kdDebug()<<" draw:color :"<<draw->attributeNS( KoXmlNS::draw, "color", QString::null )<<endl;
+ tmpBrush.setColor(draw->attributeNS( KoXmlNS::draw, "color", QString::null ) );
+ }
+ if( draw->hasAttributeNS( KoXmlNS::draw, "distance" ))
+ {
+ //todo implemente it into kpresenter
+ }
+ if( draw->hasAttributeNS( KoXmlNS::draw, "display-name"))
+ {
+ //todo implement it into kpresenter
+ }
+ if( draw->hasAttributeNS( KoXmlNS::draw, "style" ))
+ {
+ //todo implemente it into kpresenter
+ QString styleHash = draw->attributeNS( KoXmlNS::draw, "style", QString::null );
+ if( styleHash == "single")
+ {
+ switch( angle )
+ {
+ case 0:
+ case 180:
+ tmpBrush.setStyle(Qt::HorPattern );
+ break;
+ case 45:
+ case 225:
+ tmpBrush.setStyle(Qt::BDiagPattern );
+ break;
+ case 90:
+ case 270:
+ tmpBrush.setStyle(Qt::VerPattern );
+ break;
+ case 135:
+ case 315:
+ tmpBrush.setStyle(Qt::FDiagPattern );
+ break;
+ default:
+ //todo fixme when we will have a kopaint
+ kdDebug()<<" draw:rotation 'angle' : "<<angle<<endl;
+ break;
+ }
+ }
+ else if( styleHash == "double")
+ {
+ switch( angle )
+ {
+ case 0:
+ case 180:
+ case 90:
+ case 270:
+ tmpBrush.setStyle(Qt::CrossPattern );
+ break;
+ case 45:
+ case 135:
+ case 225:
+ case 315:
+ tmpBrush.setStyle(Qt::DiagCrossPattern );
+ break;
+ default:
+ //todo fixme when we will have a kopaint
+ kdDebug()<<" draw:rotation 'angle' : "<<angle<<endl;
+ break;
+ }
+
+ }
+ else if( styleHash == "triple")
+ {
+ kdDebug()<<" it is not implemented :( \n";
+ }
+ }
+ }
+ }
+ return tmpBrush;
+}
+
+const QDomElement* KoOasisStyles::defaultStyle( const QString& family ) const
+{
+ return m_defaultStyle[family];
+}
+
+const QDomElement* KoOasisStyles::findStyle( const QString& name ) const
+{
+ return m_styles[ name ];
+}
+
+const QDomElement* KoOasisStyles::findStyle( const QString& styleName, const QString& family ) const
+{
+ const QDomElement* style = d->m_styles[ family ][ styleName ];
+ if ( style && !family.isEmpty() ) {
+ const QString styleFamily = style->attributeNS( KoXmlNS::style, "family", QString::null );
+ if ( styleFamily != family ) {
+ kdWarning() << "KoOasisStyles: was looking for style " << styleName
+ << " in family " << family << " but got " << styleFamily << endl;
+ }
+ }
+ return style;
+}
+
+const QDomElement* KoOasisStyles::findStyleAutoStyle( const QString& styleName, const QString& family ) const
+{
+ const QDomElement* style = d->m_stylesAutoStyles[ family ][ styleName ];
+ if ( style ) {
+ const QString styleFamily = style->attributeNS( KoXmlNS::style, "family", QString::null );
+ if ( styleFamily != family ) {
+ kdWarning() << "KoOasisStyles: was looking for style " << styleName
+ << " in family " << family << " but got " << styleFamily << endl;
+ }
+ }
+ return style;
+}
diff --git a/lib/kofficecore/KoOasisStyles.h b/lib/kofficecore/KoOasisStyles.h
new file mode 100644
index 00000000..caa8d513
--- /dev/null
+++ b/lib/kofficecore/KoOasisStyles.h
@@ -0,0 +1,165 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2006 David Faure <faure@kde.org>
+
+ 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 KOOASISSTYLES_H
+#define KOOASISSTYLES_H
+
+#include <qdom.h>
+#include <qdict.h>
+#include <qvaluevector.h>
+#include <qmap.h>
+#include <koffice_export.h>
+
+class KoGenStyles;
+class KoXmlWriter;
+class QBrush;
+class KoGenStyle;
+class KoStyleStack;
+
+/**
+ * Repository of styles used during loading of OASIS/OOo file
+ */
+class KOFFICECORE_EXPORT KoOasisStyles
+{
+public:
+ KoOasisStyles();
+ ~KoOasisStyles();
+
+ /// Look into @p doc for styles and remember them
+ /// @param doc document to look into
+ /// @param stylesDotXml true when loading styles.xml, false otherwise
+ void createStyleMap( const QDomDocument& doc, bool stylesDotXml );
+
+ /**
+ * Look up a style by name.
+ * This method can find styles defined by the tags "style:page-layout",
+ * "style:presentation-page-layout", or "style:font-decl".
+ * Do NOT use this method for style:style styles.
+ *
+ * @param name the style name
+ * @return the dom element representing the style, or QString::null if it wasn't found.
+ */
+ const QDomElement* findStyle( const QString& name ) const;
+
+ /**
+ * Look up a style:style by name.
+ * @param name the style name
+ * @param family the style family (for a style:style, use 0 otherwise)
+ * @return the dom element representing the style, or QString::null if it wasn't found.
+ */
+ const QDomElement* findStyle( const QString& name, const QString& family ) const;
+
+ /// Similar to findStyle but for auto-styles in styles.xml only.
+ const QDomElement* findStyleAutoStyle( const QString& name, const QString& family ) const;
+
+ /// @return the style:styles that are "user styles", i.e. those from office:styles
+ /// findStyle() is used for lookup. userStyles() is used to load all user styles upfront.
+ QValueVector<QDomElement> userStyles() const;
+
+ /// @return the default style for a given family ("graphic","paragraph","table" etc.)
+ /// Returns 0 if no default style for this family is available
+ const QDomElement* defaultStyle( const QString& family ) const;
+
+ /// @return the office:style element
+ const QDomElement& officeStyle() const { return m_officeStyle; }
+
+ /// @return all list styles ("text:list-style" elements), hashed by name
+ const QDict<QDomElement>& listStyles() const { return m_listStyles; }
+
+ /// @return master pages ("style:master-page" elements), hashed by name
+ const QDict<QDomElement>& masterPages() const { return m_masterPages; }
+
+ /// @return draw styles, hashed by name
+ const QDict<QDomElement>& drawStyles() const { return m_drawStyles; }
+
+ /// @return all styles ("style:style" elements) for a given family, hashed by name
+ const QDict<QDomElement>& styles(const QString& family) const;
+
+ /// Prefix and suffix are always included into formatStr. Having them as separate fields simply
+ /// allows to extract them from formatStr, to display them in separate widgets.
+ struct NumericStyleFormat
+ {
+ QString formatStr;
+ QString prefix;
+ QString suffix;
+ enum { Number, Scientific, Fraction, Currency, Percentage,
+ Date, Time, Boolean, Text } type;
+ int precision;
+ QString currencySymbol;
+ };
+
+ typedef QMap<QString, NumericStyleFormat> DataFormatsMap;
+ /// Value (date/time/number...) formats found while parsing styles. Used e.g. for fields.
+ /// Key: format name. Value:
+ const DataFormatsMap& dataFormats() const { return m_dataFormats; }
+
+ static QString saveOasisDateStyle( KoGenStyles &mainStyles, const QString & _format, bool klocaleFormat,
+ const QString &_prefix = QString::null , const QString &_suffix= QString::null );
+ static QString saveOasisTimeStyle( KoGenStyles &mainStyles, const QString & _format, bool klocaleFormat,
+ const QString &_prefix = QString::null , const QString &_suffix= QString::null );
+ static QString saveOasisFractionStyle( KoGenStyles &mainStyles, const QString & _format,
+ const QString &_prefix = QString::null , const QString &_suffix= QString::null );
+ static QString saveOasisScientificStyle( KoGenStyles &mainStyles, const QString & _format,
+ const QString &_prefix = QString::null , const QString &_suffix= QString::null );
+ static QString saveOasisNumberStyle( KoGenStyles &mainStyles, const QString & _format,
+ const QString &_prefix = QString::null , const QString &_suffix= QString::null );
+ static QString saveOasisPercentageStyle( KoGenStyles &mainStyles, const QString & _format,
+ const QString &_prefix = QString::null , const QString &_suffix= QString::null );
+ static QString saveOasisCurrencyStyle( KoGenStyles &mainStyles, const QString & _format, const QString &symbol,
+ const QString &_prefix = QString::null , const QString &_suffix= QString::null );
+ static QString saveOasisTextStyle( KoGenStyles &mainStyles, const QString & _format,
+ const QString &_prefix = QString::null , const QString &_suffix= QString::null );
+
+ static void saveOasisFillStyle( KoGenStyle &styleFill, KoGenStyles& mainStyles, const QBrush & brush );
+ static QString saveOasisHatchStyle( KoGenStyles& mainStyles, const QBrush &brush );
+
+ static QBrush loadOasisFillStyle( const KoStyleStack &styleStack, const QString & fill, const KoOasisStyles & oasisStyles );
+
+private:
+ /// Add styles to styles map
+ void insertStyles( const QDomElement& styles, bool styleAutoStyles = false );
+
+private:
+ void insertOfficeStyles( const QDomElement& styles );
+ void insertStyle( const QDomElement& style, bool styleAutoStyles );
+ void importDataStyle( const QDomElement& parent );
+ static bool saveOasisTimeFormat( KoXmlWriter &elementWriter, QString & format, QString & text, bool &antislash );
+ static void parseOasisDateKlocale(KoXmlWriter &elementWriter, QString & format, QString & text );
+ static bool saveOasisKlocaleTimeFormat( KoXmlWriter &elementWriter, QString & format, QString & text );
+ static void parseOasisTimeKlocale(KoXmlWriter &elementWriter, QString & format, QString & text );
+ static void addKofficeNumericStyleExtension( KoXmlWriter & elementWriter, const QString &_suffix, const QString &_prefix );
+
+ KoOasisStyles( const KoOasisStyles & ); // forbidden
+ KoOasisStyles& operator=( const KoOasisStyles & ); // forbidden
+
+private:
+ QDict<QDomElement> m_styles; // page-layout, font-decl etc.
+ QDict<QDomElement> m_defaultStyle;
+ QDomElement m_officeStyle;
+
+ QDict<QDomElement> m_masterPages;
+ QDict<QDomElement> m_listStyles;
+
+ QDict<QDomElement> m_drawStyles;
+ DataFormatsMap m_dataFormats;
+
+ class Private;
+ Private * const d;
+};
+
+#endif /* KOOASISSTYLES_H */
diff --git a/lib/kofficecore/KoOpenPane.cpp b/lib/kofficecore/KoOpenPane.cpp
new file mode 100644
index 00000000..07c9580d
--- /dev/null
+++ b/lib/kofficecore/KoOpenPane.cpp
@@ -0,0 +1,315 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+
+ 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.
+*/
+
+#include "KoOpenPane.h"
+
+#include <qvbox.h>
+#include <qlayout.h>
+#include <qheader.h>
+#include <qwidgetstack.h>
+#include <qlabel.h>
+#include <qvaluelist.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpen.h>
+
+#include <klocale.h>
+#include <kfiledialog.h>
+#include <kinstance.h>
+#include <kpushbutton.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <klistview.h>
+
+#include "KoFilterManager.h"
+#include "KoTemplates.h"
+#include "KoDocument.h"
+#include "KoDetailsPane.h"
+#include "koDetailsPaneBase.h"
+
+#include <limits.h>
+
+class KoSectionListItem : public QListViewItem
+{
+ public:
+ KoSectionListItem(KListView* listView, const QString& name, int sortWeight, int widgetIndex = -1)
+ : QListViewItem(listView, name), m_sortWeight(sortWeight), m_widgetIndex(widgetIndex)
+ {
+ }
+
+ virtual int compare(QListViewItem* i, int, bool) const
+ {
+ KoSectionListItem* item = dynamic_cast<KoSectionListItem*>(i);
+
+ if(!item)
+ return 0;
+
+ return sortWeight() - item->sortWeight();
+ }
+
+ virtual void paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align)
+ {
+ if(widgetIndex() >= 0) {
+ QListViewItem::paintCell(p, cg, column, width, align);
+ } else {
+ int ypos = (height() - 2) / 2;
+ QPen pen(cg.foreground(), 2);
+ p->setPen(pen);
+ p->drawLine(0, ypos, width, ypos);
+ }
+ }
+
+ int sortWeight() const { return m_sortWeight; }
+ int widgetIndex() const { return m_widgetIndex; }
+
+ private:
+ int m_sortWeight;
+ int m_widgetIndex;
+};
+
+class KoOpenPanePrivate
+{
+ public:
+ KoOpenPanePrivate() :
+ m_instance(0)
+ {
+ }
+
+ KInstance* m_instance;
+};
+
+KoOpenPane::KoOpenPane(QWidget *parent, KInstance* instance, const QString& templateType)
+ : KoOpenPaneBase(parent, "OpenPane")
+{
+ d = new KoOpenPanePrivate;
+ d->m_instance = instance;
+
+ m_sectionList->header()->hide();
+ m_sectionList->setSorting(0);
+#if KDE_IS_VERSION(3,4,0)
+ m_sectionList->setShadeSortColumn(false);
+#endif
+ connect(m_sectionList, SIGNAL(selectionChanged(QListViewItem*)),
+ this, SLOT(selectionChanged(QListViewItem*)));
+ connect(m_sectionList, SIGNAL(pressed(QListViewItem*)),
+ this, SLOT(itemClicked(QListViewItem*)));
+ connect(m_sectionList, SIGNAL(spacePressed(QListViewItem*)),
+ this, SLOT(itemClicked(QListViewItem*)));
+ connect(m_sectionList, SIGNAL(returnPressed(QListViewItem*)),
+ this, SLOT(itemClicked(QListViewItem*)));
+
+ KGuiItem openExistingGItem(i18n("Open Existing Document..."), "fileopen");
+ m_openExistingButton->setGuiItem(openExistingGItem);
+ connect(m_openExistingButton, SIGNAL(clicked()), this, SLOT(showOpenFileDialog()));
+
+ initRecentDocs();
+ initTemplates(templateType);
+
+ KoSectionListItem* selectedItem = static_cast<KoSectionListItem*>(m_sectionList->selectedItem());
+
+ if(selectedItem) {
+ m_widgetStack->widget(selectedItem->widgetIndex())->setFocus();
+ }
+
+ QValueList<int> sizes;
+ sizes << 20 << width() - 20;
+ m_splitter->setSizes(sizes);
+
+ // Set the sizes of the details pane splitters
+ KConfigGroup cfgGrp(d->m_instance->config(), "TemplateChooserDialog");
+ sizes = cfgGrp.readIntListEntry("DetailsPaneSplitterSizes");
+ emit splitterResized(0, sizes);
+
+ connect(this, SIGNAL(splitterResized(KoDetailsPaneBase*, const QValueList<int>&)),
+ this, SLOT(saveSplitterSizes(KoDetailsPaneBase*, const QValueList<int>&)));
+}
+
+KoOpenPane::~KoOpenPane()
+{
+ KoSectionListItem* item = dynamic_cast<KoSectionListItem*>(m_sectionList->selectedItem());
+
+ if(item) {
+ if(!dynamic_cast<KoDetailsPaneBase*>(m_widgetStack->widget(item->widgetIndex()))) {
+ KConfigGroup cfgGrp(d->m_instance->config(), "TemplateChooserDialog");
+ cfgGrp.writeEntry("LastReturnType", "Custom");
+ }
+ }
+
+ delete d;
+}
+
+void KoOpenPane::showOpenFileDialog()
+{
+ const QStringList mimeFilter = KoFilterManager::mimeFilter(KoDocument::readNativeFormatMimeType(),
+ KoFilterManager::Import, KoDocument::readExtraNativeMimeTypes());
+
+ KURL url = KFileDialog::getOpenURL(":OpenDialog", mimeFilter.join(" "), this);
+
+ if(!url.isEmpty()) {
+ KConfigGroup cfgGrp(d->m_instance->config(), "TemplateChooserDialog");
+ cfgGrp.writeEntry("LastReturnType", "File");
+ emit openExistingFile(url.url());
+ }
+}
+
+void KoOpenPane::initRecentDocs()
+{
+ KoRecentDocumentsPane* recentDocPane = new KoRecentDocumentsPane(this, d->m_instance);
+ connect(recentDocPane, SIGNAL(openFile(const QString&)), this, SIGNAL(openExistingFile(const QString&)));
+ QListViewItem* item = addPane(i18n("Recent Documents"), "fileopen", recentDocPane, 0);
+ connect(recentDocPane, SIGNAL(splitterResized(KoDetailsPaneBase*, const QValueList<int>&)),
+ this, SIGNAL(splitterResized(KoDetailsPaneBase*, const QValueList<int>&)));
+ connect(this, SIGNAL(splitterResized(KoDetailsPaneBase*, const QValueList<int>&)),
+ recentDocPane, SLOT(resizeSplitter(KoDetailsPaneBase*, const QValueList<int>&)));
+
+ KoSectionListItem* separator = new KoSectionListItem(m_sectionList, "", 1);
+ separator->setEnabled(false);
+
+ if(d->m_instance->config()->hasGroup("RecentFiles")) {
+ m_sectionList->setSelected(item, true);
+ }
+}
+
+void KoOpenPane::initTemplates(const QString& templateType)
+{
+ QListViewItem* selectItem = 0;
+ QListViewItem* firstItem = 0;
+ const int templateOffset = 1000;
+
+ if(!templateType.isEmpty())
+ {
+ KoTemplateTree templateTree(templateType.local8Bit(), d->m_instance, true);
+
+ for (KoTemplateGroup *group = templateTree.first(); group != 0L; group = templateTree.next()) {
+ if (group->isHidden()) {
+ continue;
+ }
+
+ KoTemplatesPane* pane = new KoTemplatesPane(this, d->m_instance,
+ group, templateTree.defaultTemplate());
+ connect(pane, SIGNAL(openTemplate(const QString&)), this, SIGNAL(openTemplate(const QString&)));
+ connect(pane, SIGNAL(alwaysUseChanged(KoTemplatesPane*, const QString&)),
+ this, SIGNAL(alwaysUseChanged(KoTemplatesPane*, const QString&)));
+ connect(this, SIGNAL(alwaysUseChanged(KoTemplatesPane*, const QString&)),
+ pane, SLOT(changeAlwaysUseTemplate(KoTemplatesPane*, const QString&)));
+ connect(pane, SIGNAL(splitterResized(KoDetailsPaneBase*, const QValueList<int>&)),
+ this, SIGNAL(splitterResized(KoDetailsPaneBase*, const QValueList<int>&)));
+ connect(this, SIGNAL(splitterResized(KoDetailsPaneBase*, const QValueList<int>&)),
+ pane, SLOT(resizeSplitter(KoDetailsPaneBase*, const QValueList<int>&)));
+ QListViewItem* item = addPane(group->name(), group->first()->loadPicture(d->m_instance),
+ pane, group->sortingWeight() + templateOffset);
+
+ if(!firstItem) {
+ firstItem = item;
+ }
+
+ if(group == templateTree.defaultGroup()) {
+ firstItem = item;
+ }
+
+ if(pane->isSelected()) {
+ selectItem = item;
+ }
+ }
+ } else {
+ firstItem = m_sectionList->firstChild();
+ }
+
+ KConfigGroup cfgGrp(d->m_instance->config(), "TemplateChooserDialog");
+
+ if(selectItem && (cfgGrp.readEntry("LastReturnType") == "Template")) {
+ m_sectionList->setSelected(selectItem, true);
+ } else if(!m_sectionList->selectedItem() && firstItem) {
+ m_sectionList->setSelected(firstItem, true);
+ }
+}
+
+void KoOpenPane::setCustomDocumentWidget(QWidget *widget) {
+ Q_ASSERT(widget);
+ KoSectionListItem* separator = new KoSectionListItem(m_sectionList, "", INT_MAX-1);
+ separator->setEnabled(false);
+
+ QListViewItem* item = addPane(i18n("Custom Document"), QString::null, widget, INT_MAX);
+
+ KConfigGroup cfgGrp(d->m_instance->config(), "TemplateChooserDialog");
+
+ if(cfgGrp.readEntry("LastReturnType") == "Custom") {
+ m_sectionList->setSelected(item, true);
+ KoSectionListItem* selectedItem = static_cast<KoSectionListItem*>(item);
+ m_widgetStack->widget(selectedItem->widgetIndex())->setFocus();
+ }
+}
+
+QListViewItem* KoOpenPane::addPane(const QString& title, const QString& icon, QWidget* widget, int sortWeight)
+{
+ return addPane(title, SmallIcon(icon, KIcon::SizeLarge, KIcon::DefaultState, d->m_instance),
+ widget, sortWeight);
+}
+
+QListViewItem* KoOpenPane::addPane(const QString& title, const QPixmap& icon, QWidget* widget, int sortWeight)
+{
+ if(!widget) {
+ return 0;
+ }
+
+ int id = m_widgetStack->addWidget(widget);
+ KoSectionListItem* listItem = new KoSectionListItem(m_sectionList, title, sortWeight, id);
+
+ if(!icon.isNull()) {
+ QImage image = icon.convertToImage();
+
+ if((image.width() > 48) || (image.height() > 48)) {
+ image = image.smoothScale(48, 48, QImage::ScaleMin);
+ }
+
+ image.setAlphaBuffer(true);
+ image = image.copy((image.width() - 48) / 2, (image.height() - 48) / 2, 48, 48);
+ listItem->setPixmap(0, QPixmap(image));
+ }
+
+ return listItem;
+}
+
+void KoOpenPane::selectionChanged(QListViewItem* item)
+{
+ KoSectionListItem* section = dynamic_cast<KoSectionListItem*>(item);
+
+ if(!item)
+ return;
+
+ m_headerLabel->setText(section->text(0));
+ m_widgetStack->raiseWidget(section->widgetIndex());
+}
+
+void KoOpenPane::saveSplitterSizes(KoDetailsPaneBase* /*sender*/, const QValueList<int>& sizes)
+{
+ KConfigGroup cfgGrp(d->m_instance->config(), "TemplateChooserDialog");
+ cfgGrp.writeEntry("DetailsPaneSplitterSizes", sizes);
+}
+
+void KoOpenPane::itemClicked(QListViewItem* item)
+{
+ KoSectionListItem* selectedItem = static_cast<KoSectionListItem*>(item);
+
+ if(selectedItem) {
+ m_widgetStack->widget(selectedItem->widgetIndex())->setFocus();
+ }
+}
+
+#include "KoOpenPane.moc"
diff --git a/lib/kofficecore/KoOpenPane.h b/lib/kofficecore/KoOpenPane.h
new file mode 100644
index 00000000..20bd425d
--- /dev/null
+++ b/lib/kofficecore/KoOpenPane.h
@@ -0,0 +1,90 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Peter Simonsson <psn@linux.se>
+
+ 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 KOOPENPANE_H
+#define KOOPENPANE_H
+
+#include <koOpenPaneBase.h>
+
+class KoCustomDocumentCreator;
+class KConfig;
+class KoTemplateGroup;
+class KoOpenPanePrivate;
+class KInstance;
+class QPixmap;
+class KListViewItem;
+class KoTemplatesPane;
+class KoDetailsPaneBase;
+
+class KoOpenPane : public KoOpenPaneBase
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor
+ * @param parent the parent widget
+ * @param instance the KInstance to be used for KConfig data
+ * @param templateType the template-type (group) that should be selected on creation.
+ */
+ KoOpenPane(QWidget *parent, KInstance* instance, const QString& templateType = QString::null);
+ virtual ~KoOpenPane();
+
+ QListViewItem* addPane(const QString& title, const QString& icon, QWidget* widget, int sortWeight);
+ QListViewItem* addPane(const QString& title, const QPixmap& icon, QWidget* widget, int sortWeight);
+
+ /**
+ * If the application has a way to create a document not based on a template, but on user
+ * provided settings, the widget showing these gets set here.
+ * @see KoDocument::createCustomDocumentWidget()
+ * @param widget the widget.
+ */
+ void setCustomDocumentWidget(QWidget *widget);
+
+ protected slots:
+ void showOpenFileDialog();
+
+ void selectionChanged(QListViewItem* item);
+ void itemClicked(QListViewItem* item);
+
+ /// Saves the splitter sizes for KoDetailsPaneBase based panes
+ void saveSplitterSizes(KoDetailsPaneBase* sender, const QValueList<int>& sizes);
+
+ signals:
+ void openExistingFile(const QString&);
+ void openTemplate(const QString&);
+
+ /// Emitted when the always use template has changed
+ void alwaysUseChanged(KoTemplatesPane* sender, const QString& alwaysUse);
+
+ /// Emitted when one of the detail panes have changed it's splitter
+ void splitterResized(KoDetailsPaneBase* sender, const QValueList<int>& sizes);
+
+ protected:
+ void initRecentDocs();
+ /**
+ * Populate the list with all templates the user can choose.
+ * @param templateType the template-type (group) that should be selected on creation.
+ */
+ void initTemplates(const QString& templateType);
+
+ private:
+ KoOpenPanePrivate* d;
+};
+
+#endif //KOOPENPANE_H
diff --git a/lib/kofficecore/KoPageLayout.cpp b/lib/kofficecore/KoPageLayout.cpp
new file mode 100644
index 00000000..36e447e9
--- /dev/null
+++ b/lib/kofficecore/KoPageLayout.cpp
@@ -0,0 +1,242 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ Copyright 2002, 2003 David Faure <faure@kde.org>
+ Copyright 2003 Nicolas GOUTTE <goutte@kde.org>
+
+ 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.
+*/
+#include "KoPageLayout.h"
+#include <KoUnit.h>
+#include <klocale.h>
+#include <kprinter.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <KoDom.h>
+#include <KoXmlNS.h>
+#include <qdom.h>
+
+// paper formats ( mm )
+#define PG_A3_WIDTH 297.0
+#define PG_A3_HEIGHT 420.0
+#define PG_A4_WIDTH 210.0
+#define PG_A4_HEIGHT 297.0
+#define PG_A5_WIDTH 148.0
+#define PG_A5_HEIGHT 210.0
+#define PG_B5_WIDTH 182.0
+#define PG_B5_HEIGHT 257.0
+#define PG_US_LETTER_WIDTH 216.0
+#define PG_US_LETTER_HEIGHT 279.0
+#define PG_US_LEGAL_WIDTH 216.0
+#define PG_US_LEGAL_HEIGHT 356.0
+#define PG_US_EXECUTIVE_WIDTH 191.0
+#define PG_US_EXECUTIVE_HEIGHT 254.0
+
+KoGenStyle KoPageLayout::saveOasis() const
+{
+ KoGenStyle style(KoGenStyle::STYLE_PAGELAYOUT);
+ style.addPropertyPt("fo:page-width", ptWidth);
+ style.addPropertyPt("fo:page-height", ptHeight);
+ style.addPropertyPt("fo:margin-left", ptLeft);
+ style.addPropertyPt("fo:margin-right", ptRight);
+ style.addPropertyPt("fo:margin-top", ptTop);
+ style.addPropertyPt("fo:margin-bottom", ptBottom);
+ style.addProperty("style:print-orientation", (orientation == PG_LANDSCAPE ? "landscape" : "portrait"));
+ return style;
+}
+
+void KoPageLayout::loadOasis(const QDomElement &style)
+{
+ QDomElement properties( KoDom::namedItemNS( style, KoXmlNS::style, "page-layout-properties" ) );
+ if ( !properties.isNull() )
+ {
+ ptWidth = KoUnit::parseValue(properties.attributeNS( KoXmlNS::fo, "page-width", QString::null ) );
+ ptHeight = KoUnit::parseValue(properties.attributeNS( KoXmlNS::fo, "page-height", QString::null ) );
+ if (properties.attributeNS( KoXmlNS::style, "print-orientation", QString::null)=="portrait")
+ orientation=PG_PORTRAIT;
+ else
+ orientation=PG_LANDSCAPE;
+ ptRight = KoUnit::parseValue( properties.attributeNS( KoXmlNS::fo, "margin-right", QString::null ) );
+ ptBottom = KoUnit::parseValue( properties.attributeNS( KoXmlNS::fo, "margin-bottom", QString::null ) );
+ ptLeft = KoUnit::parseValue( properties.attributeNS( KoXmlNS::fo, "margin-left", QString::null ) );
+ ptTop = KoUnit::parseValue( properties.attributeNS( KoXmlNS::fo, "margin-top", QString::null ) );
+ // guessFormat takes millimeters
+ if ( orientation == PG_LANDSCAPE )
+ format = KoPageFormat::guessFormat( POINT_TO_MM(ptHeight), POINT_TO_MM(ptWidth) );
+ else
+ format = KoPageFormat::guessFormat( POINT_TO_MM(ptWidth), POINT_TO_MM(ptHeight) );
+ }
+}
+
+KoPageLayout KoPageLayout::standardLayout()
+{
+ KoPageLayout layout;
+ layout.format = KoPageFormat::defaultFormat();
+ layout.orientation = PG_PORTRAIT;
+ layout.ptWidth = MM_TO_POINT( KoPageFormat::width( layout.format, layout.orientation ) );
+ layout.ptHeight = MM_TO_POINT( KoPageFormat::height( layout.format, layout.orientation ) );
+ layout.ptLeft = MM_TO_POINT( 20.0 );
+ layout.ptRight = MM_TO_POINT( 20.0 );
+ layout.ptTop = MM_TO_POINT( 20.0 );
+ layout.ptBottom = MM_TO_POINT( 20.0 );
+ layout.ptPageEdge = -1;
+ layout.ptBindingSide = -1;
+ return layout;
+}
+
+struct PageFormatInfo
+{
+ KoFormat format;
+ KPrinter::PageSize kprinter;
+ const char* shortName; // Short name
+ const char* descriptiveName; // Full name, which will be translated
+ double width; // in mm
+ double height; // in mm
+};
+
+// NOTES:
+// - the width and height of non-ISO formats are rounded
+// http://en.wikipedia.org/wiki/Paper_size can help
+// - the comments "should be..." indicates the exact values if the inch sizes would be multiplied by 25.4 mm/inch
+
+const PageFormatInfo pageFormatInfo[]=
+{
+ { PG_DIN_A3, KPrinter::A3, "A3", I18N_NOOP("ISO A3"), 297.0, 420.0 },
+ { PG_DIN_A4, KPrinter::A4, "A4", I18N_NOOP("ISO A4"), 210.0, 297.0 },
+ { PG_DIN_A5, KPrinter::A5, "A5", I18N_NOOP("ISO A5"), 148.0, 210.0 },
+ { PG_US_LETTER, KPrinter::Letter, "Letter", I18N_NOOP("US Letter"), 215.9, 279.4 },
+ { PG_US_LEGAL, KPrinter::Legal, "Legal", I18N_NOOP("US Legal"), 215.9, 355.6 },
+ { PG_SCREEN, KPrinter::A4, "Screen", I18N_NOOP("Screen"), PG_A4_HEIGHT, PG_A4_WIDTH }, // Custom, so fall back to A4
+ { PG_CUSTOM, KPrinter::A4, "Custom", I18N_NOOP2("Custom size", "Custom"), PG_A4_WIDTH, PG_A4_HEIGHT }, // Custom, so fall back to A4
+ { PG_DIN_B5, KPrinter::B5, "B5", I18N_NOOP("ISO B5"), 182.0, 257.0 },
+ // Hmm, wikipedia says 184.15 * 266.7 for executive !
+ { PG_US_EXECUTIVE, KPrinter::Executive, "Executive", I18N_NOOP("US Executive"), 191.0, 254.0 }, // should be 190.5 mm x 254.0 mm
+ { PG_DIN_A0, KPrinter::A0, "A0", I18N_NOOP("ISO A0"), 841.0, 1189.0 },
+ { PG_DIN_A1, KPrinter::A1, "A1", I18N_NOOP("ISO A1"), 594.0, 841.0 },
+ { PG_DIN_A2, KPrinter::A2, "A2", I18N_NOOP("ISO A2"), 420.0, 594.0 },
+ { PG_DIN_A6, KPrinter::A6, "A6", I18N_NOOP("ISO A6"), 105.0, 148.0 },
+ { PG_DIN_A7, KPrinter::A7, "A7", I18N_NOOP("ISO A7"), 74.0, 105.0 },
+ { PG_DIN_A8, KPrinter::A8, "A8", I18N_NOOP("ISO A8"), 52.0, 74.0 },
+ { PG_DIN_A9, KPrinter::A9, "A9", I18N_NOOP("ISO A9"), 37.0, 52.0 },
+ { PG_DIN_B0, KPrinter::B0, "B0", I18N_NOOP("ISO B0"), 1030.0, 1456.0 },
+ { PG_DIN_B1, KPrinter::B1, "B1", I18N_NOOP("ISO B1"), 728.0, 1030.0 },
+ { PG_DIN_B10, KPrinter::B10, "B10", I18N_NOOP("ISO B10"), 32.0, 45.0 },
+ { PG_DIN_B2, KPrinter::B2, "B2", I18N_NOOP("ISO B2"), 515.0, 728.0 },
+ { PG_DIN_B3, KPrinter::B3, "B3", I18N_NOOP("ISO B3"), 364.0, 515.0 },
+ { PG_DIN_B4, KPrinter::B4, "B4", I18N_NOOP("ISO B4"), 257.0, 364.0 },
+ { PG_DIN_B6, KPrinter::B6, "B6", I18N_NOOP("ISO B6"), 128.0, 182.0 },
+ { PG_ISO_C5, KPrinter::C5E, "C5", I18N_NOOP("ISO C5"), 163.0, 229.0 }, // Some sources tells: 162 mm x 228 mm
+ { PG_US_COMM10, KPrinter::Comm10E, "Comm10", I18N_NOOP("US Common 10"), 105.0, 241.0 }, // should be 104.775 mm x 241.3 mm
+ { PG_ISO_DL, KPrinter::DLE, "DL", I18N_NOOP("ISO DL"), 110.0, 220.0 },
+ { PG_US_FOLIO, KPrinter::Folio, "Folio", I18N_NOOP("US Folio"), 210.0, 330.0 }, // should be 209.54 mm x 330.2 mm
+ { PG_US_LEDGER, KPrinter::Ledger, "Ledger", I18N_NOOP("US Ledger"), 432.0, 279.0 }, // should be 431.8 mm x 297.4 mm
+ { PG_US_TABLOID, KPrinter::Tabloid, "Tabloid", I18N_NOOP("US Tabloid"), 279.0, 432.0 } // should be 297.4 mm x 431.8 mm
+};
+
+int KoPageFormat::printerPageSize( KoFormat format )
+{
+ if ( format == PG_SCREEN )
+ {
+ kdWarning() << "You use the page layout SCREEN. Printing in DIN A4 LANDSCAPE." << endl;
+ return KPrinter::A4;
+ }
+ else if ( format == PG_CUSTOM )
+ {
+ kdWarning() << "The used page layout (CUSTOM) is not supported by KPrinter. Printing in A4." << endl;
+ return KPrinter::A4;
+ }
+ else if ( format <= PG_LAST_FORMAT )
+ return pageFormatInfo[ format ].kprinter;
+ else
+ return KPrinter::A4;
+}
+
+double KoPageFormat::width( KoFormat format, KoOrientation orientation )
+{
+ if ( orientation == PG_LANDSCAPE )
+ return height( format, PG_PORTRAIT );
+ if ( format <= PG_LAST_FORMAT )
+ return pageFormatInfo[ format ].width;
+ return PG_A4_WIDTH; // should never happen
+}
+
+double KoPageFormat::height( KoFormat format, KoOrientation orientation )
+{
+ if ( orientation == PG_LANDSCAPE )
+ return width( format, PG_PORTRAIT );
+ if ( format <= PG_LAST_FORMAT )
+ return pageFormatInfo[ format ].height;
+ return PG_A4_HEIGHT;
+}
+
+KoFormat KoPageFormat::guessFormat( double width, double height )
+{
+ for ( int i = 0 ; i <= PG_LAST_FORMAT ; ++i )
+ {
+ // We have some tolerance. 1pt is a third of a mm, this is
+ // barely noticeable for a page size.
+ if ( i != PG_CUSTOM
+ && kAbs( width - pageFormatInfo[i].width ) < 1.0
+ && kAbs( height - pageFormatInfo[i].height ) < 1.0 )
+ return static_cast<KoFormat>(i);
+ }
+ return PG_CUSTOM;
+}
+
+QString KoPageFormat::formatString( KoFormat format )
+{
+ if ( format <= PG_LAST_FORMAT )
+ return QString::fromLatin1( pageFormatInfo[ format ].shortName );
+ return QString::fromLatin1( "A4" );
+}
+
+KoFormat KoPageFormat::formatFromString( const QString & string )
+{
+ for ( int i = 0 ; i <= PG_LAST_FORMAT ; ++i )
+ {
+ if (string == QString::fromLatin1( pageFormatInfo[ i ].shortName ))
+ return pageFormatInfo[ i ].format;
+ }
+ // We do not know the format name, so we have a custom format
+ return PG_CUSTOM;
+}
+
+KoFormat KoPageFormat::defaultFormat()
+{
+ int kprinter = KGlobal::locale()->pageSize();
+ for ( int i = 0 ; i <= PG_LAST_FORMAT ; ++i )
+ {
+ if ( pageFormatInfo[ i ].kprinter == kprinter )
+ return static_cast<KoFormat>(i);
+ }
+ return PG_DIN_A4;
+}
+
+QString KoPageFormat::name( KoFormat format )
+{
+ if ( format <= PG_LAST_FORMAT )
+ return i18n( pageFormatInfo[ format ].descriptiveName );
+ return i18n( pageFormatInfo[ PG_DIN_A4 ].descriptiveName );
+}
+
+QStringList KoPageFormat::allFormats()
+{
+ QStringList lst;
+ for ( int i = 0 ; i <= PG_LAST_FORMAT ; ++i )
+ {
+ lst << i18n( pageFormatInfo[ i ].descriptiveName );
+ }
+ return lst;
+}
diff --git a/lib/kofficecore/KoPageLayout.h b/lib/kofficecore/KoPageLayout.h
new file mode 100644
index 00000000..f9f98fc6
--- /dev/null
+++ b/lib/kofficecore/KoPageLayout.h
@@ -0,0 +1,260 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ Copyright 2002, 2003 David Faure <faure@kde.org>
+ Copyright 2003 Nicolas GOUTTE <goutte@kde.org>
+
+ 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 KOPAGELAYOUT_H
+#define KOPAGELAYOUT_H
+
+#include <KoGenStyles.h>
+#include <qstringlist.h>
+#include <koffice_export.h>
+class QDomElement;
+
+/**
+ * @brief Represents the paper format a document shall be printed on.
+ *
+ * For compatibility reasons, and because of screen and custom,
+ * this enum doesn't map to QPrinter::PageSize but KoPageFormat::printerPageSize
+ * does the conversion.
+ *
+ * @todo convert DIN to ISO in the names
+ */
+enum KoFormat {
+ PG_DIN_A3 = 0,
+ PG_DIN_A4 = 1,
+ PG_DIN_A5 = 2,
+ PG_US_LETTER = 3,
+ PG_US_LEGAL = 4,
+ PG_SCREEN = 5,
+ PG_CUSTOM = 6,
+ PG_DIN_B5 = 7,
+ PG_US_EXECUTIVE = 8,
+ PG_DIN_A0 = 9,
+ PG_DIN_A1 = 10,
+ PG_DIN_A2 = 11,
+ PG_DIN_A6 = 12,
+ PG_DIN_A7 = 13,
+ PG_DIN_A8 = 14,
+ PG_DIN_A9 = 15,
+ PG_DIN_B0 = 16,
+ PG_DIN_B1 = 17,
+ PG_DIN_B10 = 18,
+ PG_DIN_B2 = 19,
+ PG_DIN_B3 = 20,
+ PG_DIN_B4 = 21,
+ PG_DIN_B6 = 22,
+ PG_ISO_C5 = 23,
+ PG_US_COMM10 = 24,
+ PG_ISO_DL = 25,
+ PG_US_FOLIO = 26,
+ PG_US_LEDGER = 27,
+ PG_US_TABLOID = 28,
+ // update the number below and the static arrays if you add more values to the enum
+ PG_LAST_FORMAT = PG_US_TABLOID // used by koPageLayout.cpp
+};
+
+/**
+ * Represents the orientation of a printed document.
+ */
+enum KoOrientation {
+ PG_PORTRAIT = 0,
+ PG_LANDSCAPE = 1
+};
+
+namespace KoPageFormat
+{
+ /**
+ * @brief Convert a KoFormat into a KPrinter::PageSize.
+ *
+ * If format is 'screen' it will use A4 landscape.
+ * If format is 'custom' it will use A4 portrait.
+ * (you may want to take care of those cases separately).
+ * Usually passed to KPrinter::setPageSize().
+ *
+ * @note We return int instead of the enum to avoid including kprinter.h
+ */
+ KOFFICECORE_EXPORT int /*KPrinter::PageSize*/ printerPageSize( KoFormat format );
+
+ /**
+ * Returns the width (in mm) for a given page format and orientation
+ * 'Custom' isn't supported by this function, obviously.
+ */
+ KOFFICECORE_EXPORT double width( KoFormat format, KoOrientation orientation );
+
+ /**
+ * Returns the height (in mm) for a given page format and orientation
+ * 'Custom' isn't supported by this function, obviously.
+ */
+ KOFFICECORE_EXPORT double height( KoFormat format, KoOrientation orientation );
+
+ /**
+ * Returns the internal name of the given page format.
+ * Use for saving.
+ */
+ KOFFICECORE_EXPORT QString formatString( KoFormat format );
+
+ /**
+ * Convert a format string (internal name) to a page format value.
+ * Use for loading.
+ */
+ KOFFICECORE_EXPORT KoFormat formatFromString( const QString & string );
+
+ /**
+ * Returns the default format (based on the KControl settings)
+ */
+ KOFFICECORE_EXPORT KoFormat defaultFormat();
+
+ /**
+ * Returns the translated name of the given page format.
+ * Use for showing the user.
+ */
+ KOFFICECORE_EXPORT QString name( KoFormat format );
+
+ /**
+ * Lists the translated names of all the available formats
+ */
+ KOFFICECORE_EXPORT QStringList allFormats();
+
+ /**
+ * Try to find the paper format for the given width and height (in mm).
+ * Useful to some import filters.
+ */
+ KOFFICECORE_EXPORT KoFormat guessFormat( double width, double height );
+}
+
+
+/**
+ * @brief Header/Footer type.
+ *
+ * @note Yes, this should have been a bitfield, but there was only 0, 2, 3 in koffice-1.0. Don't ask why.
+ * In the long run this should be replaced with a more flexible repetition/section concept.
+ */
+enum KoHFType {
+ HF_SAME = 0, ///< 0: Header/Footer is the same on all pages
+ HF_FIRST_EO_DIFF = 1, ///< 1: Header/Footer is different on first, even and odd pages (2&3)
+ HF_FIRST_DIFF = 2, ///< 2: Header/Footer for the first page differs
+ HF_EO_DIFF = 3 ///< 3: Header/Footer for even - odd pages are different
+};
+
+/**
+ * This structure defines the page layout, including
+ * its size in pt, its format (e.g. A4), orientation, unit, margins etc.
+ */
+struct KoPageLayout
+{
+ /** Page format */
+ KoFormat format;
+ /** Page orientation */
+ KoOrientation orientation;
+
+ /** Page width in pt */
+ double ptWidth;
+ /** Page height in pt */
+ double ptHeight;
+ /** Left margin in pt */
+ double ptLeft;
+ /** Right margin in pt */
+ double ptRight;
+ /** Top margin in pt */
+ double ptTop;
+ /** Bottom margin in pt */
+ double ptBottom;
+ double ptPageEdge;
+ double ptBindingSide;
+
+ bool operator==( const KoPageLayout& l ) const {
+ return ( ptWidth == l.ptWidth &&
+ ptHeight == l.ptHeight &&
+ ptLeft == l.ptLeft &&
+ ptRight == l.ptRight &&
+ ptTop == l.ptTop &&
+ ptBottom == l.ptBottom &&
+ ptPageEdge == l.ptPageEdge &&
+ ptBindingSide == l.ptBindingSide);
+ }
+ bool operator!=( const KoPageLayout& l ) const {
+ return !( (*this) == l );
+ }
+
+ /**
+ * Save this page layout to OASIS.
+ */
+ KOFFICECORE_EXPORT KoGenStyle saveOasis() const;
+
+ /**
+ * Load this page layout from OASIS
+ */
+ KOFFICECORE_EXPORT void loadOasis(const QDomElement &style);
+
+ /**
+ * @return a page layout with the default page size depending on the locale settings,
+ * default margins (2 cm), and portrait orientation.
+ * @since 1.4
+ */
+ static KOFFICECORE_EXPORT KoPageLayout standardLayout();
+};
+
+/** structure for header-footer */
+struct KoHeadFoot
+{
+ QString headLeft;
+ QString headMid;
+ QString headRight;
+ QString footLeft;
+ QString footMid;
+ QString footRight;
+};
+
+/** structure for columns */
+struct KoColumns
+{
+ int columns;
+ double ptColumnSpacing;
+ bool operator==( const KoColumns& rhs ) const {
+ return columns == rhs.columns &&
+ QABS(ptColumnSpacing - rhs.ptColumnSpacing) <= 1E-10;
+ }
+ bool operator!=( const KoColumns& rhs ) const {
+ return columns != rhs.columns ||
+ QABS(ptColumnSpacing - rhs.ptColumnSpacing) > 1E-10;
+ }
+};
+
+/** structure for KWord header-footer */
+struct KoKWHeaderFooter
+{
+ KoHFType header;
+ KoHFType footer;
+ double ptHeaderBodySpacing;
+ double ptFooterBodySpacing;
+ double ptFootNoteBodySpacing;
+ bool operator==( const KoKWHeaderFooter& rhs ) const {
+ return header == rhs.header && footer == rhs.footer &&
+ QABS(ptHeaderBodySpacing - rhs.ptHeaderBodySpacing) <= 1E-10 &&
+ QABS(ptFooterBodySpacing - rhs.ptFooterBodySpacing) <= 1E-10 &&
+ QABS(ptFootNoteBodySpacing - rhs.ptFootNoteBodySpacing) <= 1E-10;
+ }
+ bool operator!=( const KoKWHeaderFooter& rhs ) const {
+ return !( *this == rhs );
+ }
+};
+
+#endif /* KOPAGELAYOUT_H */
+
diff --git a/lib/kofficecore/KoPicture.cpp b/lib/kofficecore/KoPicture.cpp
new file mode 100644
index 00000000..11bb89dd
--- /dev/null
+++ b/lib/kofficecore/KoPicture.cpp
@@ -0,0 +1,290 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002, 2003 Nicolas GOUTTE <goutte@kde.org>
+
+ 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.
+*/
+
+#include <qpainter.h>
+#include <qfile.h>
+
+#include <kdebug.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+
+#include "KoPictureKey.h"
+#include "KoPictureBase.h"
+#include "KoPictureShared.h"
+#include "KoPicture.h"
+
+uint KoPicture::uniqueValue = 0;
+
+
+KoPicture::KoPicture(void) : m_sharedData(NULL)
+{
+ m_uniqueName = "Pictures"+ QString::number(uniqueValue++);
+}
+
+KoPicture::~KoPicture(void)
+{
+ unlinkSharedData();
+}
+
+QString KoPicture::uniqueName() const
+{
+ return m_uniqueName;
+}
+
+KoPicture::KoPicture(const KoPicture &other)
+{
+ m_sharedData=NULL;
+ (*this)=other;
+}
+
+void KoPicture::assignPictureId( uint _id)
+{
+ if ( m_sharedData )
+ m_sharedData->assignPictureId(_id);
+}
+
+QString KoPicture::uniquePictureId() const
+{
+ if ( m_sharedData )
+ return m_sharedData->uniquePictureId();
+ else
+ return QString::null;
+}
+
+KoPicture& KoPicture::operator=( const KoPicture &other )
+{
+ //kdDebug(30003) << "KoPicture::= before" << endl;
+ if (other.m_sharedData)
+ other.linkSharedData();
+ if (m_sharedData)
+ unlinkSharedData();
+ m_sharedData=other.m_sharedData;
+ m_key=other.m_key;
+ //kdDebug(30003) << "KoPicture::= after" << endl;
+ return *this;
+}
+
+void KoPicture::unlinkSharedData(void)
+{
+ if (m_sharedData && m_sharedData->deref())
+ delete m_sharedData;
+
+ m_sharedData=NULL;
+}
+
+void KoPicture::linkSharedData(void) const
+{
+ if (m_sharedData)
+ m_sharedData->ref();
+}
+
+void KoPicture::createSharedData(void)
+{
+ if (!m_sharedData)
+ {
+ m_sharedData=new KoPictureShared();
+ // Do not call m_sharedData->ref()
+ }
+}
+
+KoPictureType::Type KoPicture::getType(void) const
+{
+ if (m_sharedData)
+ return m_sharedData->getType();
+ return KoPictureType::TypeUnknown;
+}
+
+KoPictureKey KoPicture::getKey(void) const
+{
+ return m_key;
+}
+
+void KoPicture::setKey(const KoPictureKey& key)
+{
+ m_key=key;
+}
+
+
+bool KoPicture::isNull(void) const
+{
+ if (m_sharedData)
+ return m_sharedData->isNull();
+ return true;
+}
+
+void KoPicture::draw(QPainter& painter, int x, int y, int width, int height, int sx, int sy, int sw, int sh, bool fastMode)
+{
+ if (m_sharedData)
+ m_sharedData->draw(painter, x, y, width, height, sx, sy, sw, sh, fastMode);
+ else
+ {
+ // Draw a white box
+ kdWarning(30003) << "Drawing white rectangle! (KoPicture::draw)" << endl;
+ painter.save();
+ painter.setBrush(QColor(255, 255, 255));
+ painter.drawRect(x,y,width,height);
+ painter.restore();
+ }
+}
+
+bool KoPicture::loadXpm(QIODevice* io)
+{
+ kdDebug(30003) << "KoPicture::loadXpm" << endl;
+ if (!io)
+ {
+ kdError(30003) << "No QIODevice!" << endl;
+ return false;
+ }
+ createSharedData();
+ return m_sharedData->loadXpm(io);
+}
+
+bool KoPicture::save(QIODevice* io) const
+{
+ if (!io)
+ return false;
+ if (m_sharedData)
+ return m_sharedData->save(io);
+ return false;
+}
+
+bool KoPicture::saveAsBase64( KoXmlWriter& writer ) const
+{
+ if ( m_sharedData )
+ return m_sharedData->saveAsBase64( writer );
+ return false;
+}
+
+void KoPicture::clear(void)
+{
+ unlinkSharedData();
+}
+
+void KoPicture::clearAndSetMode(const QString& newMode)
+{
+ createSharedData();
+ m_sharedData->clearAndSetMode(newMode);
+}
+
+QString KoPicture::getExtension(void) const
+{
+ if (m_sharedData)
+ return m_sharedData->getExtension();
+ return "null"; // Just a dummy
+}
+
+QString KoPicture::getMimeType(void) const
+{
+ if (m_sharedData)
+ return m_sharedData->getMimeType();
+ return QString(NULL_MIME_TYPE);
+}
+
+bool KoPicture::load(QIODevice* io, const QString& extension)
+{
+ kdDebug(30003) << "KoPicture::load(QIODevice*, const QString&) " << extension << endl;
+ createSharedData();
+
+ return m_sharedData->load(io,extension);
+}
+
+bool KoPicture::loadFromFile(const QString& fileName)
+{
+ kdDebug(30003) << "KoPicture::loadFromFile " << fileName << endl;
+ createSharedData();
+ return m_sharedData->loadFromFile(fileName);
+}
+
+bool KoPicture::loadFromBase64( const QCString& str )
+{
+ createSharedData();
+ return m_sharedData->loadFromBase64( str );
+}
+
+QSize KoPicture::getOriginalSize(void) const
+{
+ if (m_sharedData)
+ return m_sharedData->getOriginalSize();
+ return QSize(0,0);
+}
+
+QPixmap KoPicture::generatePixmap(const QSize& size, bool smoothScale)
+{
+ if (m_sharedData)
+ return m_sharedData->generatePixmap(size, smoothScale);
+ return QPixmap();
+}
+
+bool KoPicture::setKeyAndDownloadPicture(const KURL& url, QWidget *window)
+{
+ bool result=false;
+
+ QString tmpFileName;
+ if ( KIO::NetAccess::download(url, tmpFileName, window) )
+ {
+ KoPictureKey key;
+ key.setKeyFromFile( tmpFileName );
+ setKey( key );
+ result=loadFromFile( tmpFileName );
+ KIO::NetAccess::removeTempFile( tmpFileName );
+ }
+
+ return result;
+}
+
+QDragObject* KoPicture::dragObject( QWidget *dragSource, const char *name )
+{
+ if (m_sharedData)
+ return m_sharedData->dragObject( dragSource, name );
+ return 0L;
+}
+
+QImage KoPicture::generateImage(const QSize& size)
+{
+ if (m_sharedData)
+ return m_sharedData->generateImage( size );
+ return QImage();
+}
+
+bool KoPicture::hasAlphaBuffer() const
+{
+ if (m_sharedData)
+ return m_sharedData->hasAlphaBuffer();
+ return false;
+}
+
+void KoPicture::setAlphaBuffer(bool enable)
+{
+ if (m_sharedData)
+ m_sharedData->setAlphaBuffer(enable);
+}
+
+QImage KoPicture::createAlphaMask(int conversion_flags) const
+{
+ if (m_sharedData)
+ return m_sharedData->createAlphaMask(conversion_flags);
+ return QImage();
+}
+
+void KoPicture::clearCache(void)
+{
+ if (m_sharedData)
+ m_sharedData->clearCache();
+}
diff --git a/lib/kofficecore/KoPicture.h b/lib/kofficecore/KoPicture.h
new file mode 100644
index 00000000..e4ed1d5d
--- /dev/null
+++ b/lib/kofficecore/KoPicture.h
@@ -0,0 +1,259 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
+
+ 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 __koPicture_h__
+#define __koPicture_h__
+
+#include <qstring.h>
+#include <qiodevice.h>
+#include <qpixmap.h>
+#include <koffice_export.h>
+
+#include "KoPictureKey.h"
+
+class KoXmlWriter;
+class QPainter;
+class QSize;
+class QDragObject;
+class KURL;
+
+class KoPictureShared;
+
+/**
+ * KoPicture is a container class for various types of pictures supported by %KOffice.
+ *
+ * @short A picture container class
+ */
+class KOFFICECORE_EXPORT KoPicture
+{
+public:
+ /**
+ * Default constructor.
+ */
+ KoPicture(void);
+
+ /**
+ * Destructor.
+ */
+ ~KoPicture(void);
+
+ /**
+ * Copy constructor
+ */
+ KoPicture(const KoPicture &other);
+
+ /**
+ * Assignment operator
+ */
+ KoPicture& operator=(const KoPicture& other);
+
+ KoPictureType::Type getType(void) const;
+
+ /**
+ * Retrieve the key structure describing the picture in a unique way.
+ */
+ KoPictureKey getKey(void) const;
+
+ /**
+ * Set the key structure describing the picture in a unique way
+ */
+ void setKey(const KoPictureKey& key);
+
+ /**
+ * Returns true if the picture is null.
+ */
+ bool isNull(void) const;
+
+ /**
+ * @brief Draw the picture in a painter.
+ *
+ * The parameter @p fastMode allows the picture to be re-sized and drawn quicker if possible
+ *
+ * The parameters @p width, @p height define the desired size for the picture
+ *
+ * The other parameters are very similar to QPainter::drawPixmap :
+ * (@p x, @p y) define the position in the painter,
+ * (@p sx, @p sy) specify the top-left point in picture that is to be drawn. The default is (0, 0).
+ * (@p sw, @p sh) specify the size of the picture that is to be drawn. The default, (-1, -1), means all the way to the bottom
+ * right of the pixmap.
+ *
+ */
+ void draw(QPainter& painter, int x, int y, int width, int height, int sx = 0, int sy = 0,
+ int sw = -1, int sh = -1, bool fastMode = false);
+
+ /**
+ * Create a dragobject containing this picture.
+ * @param dragSource must be 0 when copying to the clipboard
+ * @param name name for the QDragObject
+ * @return 0L if the picture is null, or if a dragobject for it isn't implemented [yet]
+ */
+ QDragObject* dragObject( QWidget *dragSource = 0L, const char *name = 0L );
+
+ bool load(QIODevice* io, const QString& extension);
+
+ /**
+ * Save picture into a QIODevice
+ * @param io QIODevice used for saving
+ */
+ bool save(QIODevice* io) const;
+
+ /**
+ * OASIS FlatXML support:
+ * Save picture as base64-encoded data into an XML writer.
+ * The caller will usually do something like
+ * @code
+ * writer.startElement( "office:binary-data" );
+ * m_picture.saveAsBase64( writer );
+ * writer.endElement();
+ * @endcode
+ */
+ bool saveAsBase64( KoXmlWriter& writer ) const;
+
+ /**
+ * @return the image extension (e.g. png)
+ */
+ QString getExtension(void) const;
+
+ /**
+ * @return the image MIME type
+ */
+ QString getMimeType(void) const;
+
+ /**
+ * @return the original image size
+ */
+ QSize getOriginalSize(void) const;
+
+ /**
+ * Clear and set the mode of this KoPicture
+ *
+ * @param newMode a file extension (like "png") giving the wanted mode
+ */
+ void clearAndSetMode(const QString& newMode);
+
+ /**
+ * Reset the KoPicture (but not the key!)
+ */
+ void clear(void);
+
+ /**
+ * Load the picture from a file named @p fileName
+ */
+ bool loadFromFile(const QString& fileName);
+
+ /**
+ * Load the picture from base64-encoded data
+ */
+ bool loadFromBase64(const QCString& str);
+
+ /**
+ * Load a potentially broken XPM file (for old files of KPresenter)
+ */
+ bool loadXpm(QIODevice* io);
+
+ /**
+ * @deprecated To be replaced by @ref KoPicture::draw
+ *
+ * Returns a QPixmap from an image
+ * Returns an empty QPixmap if the KoPicture is not an image.
+ */
+ QPixmap generatePixmap(const QSize& size, bool smoothScale = false);
+
+ /**
+ * Download and set the key for a possibly remote file.
+ *
+ * @param url the url to download from
+ * @param window the parent widget for the download. You can pass
+ * NULL (0) if you absolutely cannot find a parent
+ * widget to use.
+ */
+ bool setKeyAndDownloadPicture(const KURL& url, QWidget *window);
+
+ /**
+ * Generate a QImage
+ * (always in slow mode)
+ *
+ * @param size the wanted size for the QImage
+ */
+ QImage generateImage(const QSize& size);
+
+ /**
+ * @return TRUE if the alpha channel processing has been enabled
+ */
+ bool hasAlphaBuffer() const;
+
+ /**
+ * Respect the image alpha buffer
+ */
+ void setAlphaBuffer(bool enable);
+
+ /**
+ * Creates an alpha mask for the picture
+ * (first you have to call @ref #setAlphaBuffer).
+ *
+ * @see hasAlphaBuffer() setAlphaBuffer()
+ */
+ QImage createAlphaMask(int conversion_flags = 0) const;
+
+ /**
+ * @brief Clear any cache
+ *
+ * This is used to avoid using too much memory
+ * especially if the application somehow also caches the KoPicture's output
+ */
+ void clearCache(void);
+
+ QString uniquePictureId() const;
+ void assignPictureId( uint _id);
+
+protected:
+ /**
+ * @internal
+ * Unregister shared data
+ */
+ void unlinkSharedData(void);
+ /**
+ * @internal
+ * Register shared data
+ */
+ void linkSharedData(void) const;
+ /**
+ * @internal
+ * Creare the shared data if needed
+ */
+ void createSharedData(void);
+ QString uniqueName() const;
+
+protected:
+ /**
+ * @internal
+ * The key
+ */
+ KoPictureKey m_key;
+ /**
+ * @internal
+ * The shared data
+ */
+ KoPictureShared* m_sharedData;
+ static uint uniqueValue;
+
+ QString m_uniqueName;
+};
+
+#endif /* __koPicture_h__ */
diff --git a/lib/kofficecore/KoPictureBase.cpp b/lib/kofficecore/KoPictureBase.cpp
new file mode 100644
index 00000000..e608abc2
--- /dev/null
+++ b/lib/kofficecore/KoPictureBase.cpp
@@ -0,0 +1,142 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002, 2003 Nicolas GOUTTE <goutte@kde.org>
+
+ 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.
+*/
+
+#include "KoPictureBase.h"
+
+#include <KoXmlWriter.h>
+
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kglobal.h>
+
+#include <kmdcodec.h>
+#include <qpainter.h>
+#include <qpicture.h>
+#include <qpixmap.h>
+#include <qdragobject.h>
+
+static int s_useSlowResizeMode = -1; // unset
+
+KoPictureBase::KoPictureBase(void)
+{
+ // Slow mode can be very slow, especially at high zoom levels -> configurable
+ if ( s_useSlowResizeMode == -1 )
+ {
+ KConfigGroup group( KGlobal::config(), "KOfficeImage" );
+ s_useSlowResizeMode = group.readNumEntry( "HighResolution", 1 );
+ kdDebug(30003) << "HighResolution = " << s_useSlowResizeMode << endl;
+ }
+}
+
+KoPictureBase::~KoPictureBase(void)
+{
+}
+
+KoPictureBase* KoPictureBase::newCopy(void) const
+{
+ return new KoPictureBase(*this);
+}
+
+KoPictureType::Type KoPictureBase::getType(void) const
+{
+ return KoPictureType::TypeUnknown;
+}
+
+bool KoPictureBase::isNull(void) const
+{
+ return true; // A KoPictureBase is always null.
+}
+
+void KoPictureBase::draw(QPainter& painter, int x, int y, int width, int height, int, int, int, int, bool /*fastMode*/)
+{
+ // Draw a light red box (easier DEBUG)
+ kdWarning(30003) << "Drawing light red rectangle! (KoPictureBase::draw)" << endl;
+ painter.save();
+ painter.setBrush(QColor(128,0,0));
+ painter.drawRect(x,y,width,height);
+ painter.restore();
+}
+
+bool KoPictureBase::load(QIODevice* io, const QString& extension)
+{
+ return loadData(io->readAll(), extension);
+}
+
+bool KoPictureBase::loadData(const QByteArray&, const QString&)
+{
+ // Nothing to load!
+ return false;
+}
+
+bool KoPictureBase::save(QIODevice*) const
+{
+ // Nothing to save!
+ return false;
+}
+
+bool KoPictureBase::saveAsBase64( KoXmlWriter& writer ) const
+{
+ QBuffer buffer;
+ buffer.open(IO_ReadWrite);
+ if ( !save( &buffer ) )
+ return false;
+ QCString encoded = KCodecs::base64Encode( buffer.buffer() );
+ writer.addTextNode( encoded );
+ return true;
+}
+
+QSize KoPictureBase::getOriginalSize(void) const
+{
+ return QSize(0,0);
+}
+
+QPixmap KoPictureBase::generatePixmap(const QSize&, bool /*smoothScale*/)
+{
+ return QPixmap();
+}
+
+QString KoPictureBase::getMimeType(const QString&) const
+{
+ return QString(NULL_MIME_TYPE);
+}
+
+bool KoPictureBase::isSlowResizeModeAllowed(void) const
+{
+ return s_useSlowResizeMode != 0;
+}
+
+QDragObject* KoPictureBase::dragObject( QWidget * dragSource, const char * name )
+{
+ QImage image (generateImage(getOriginalSize()));
+ if (image.isNull())
+ return 0L;
+ else
+ return new QImageDrag( image, dragSource, name );
+}
+
+QImage KoPictureBase::generateImage(const QSize& size)
+{
+ return generatePixmap(size,true).convertToImage();
+}
+
+void KoPictureBase::clearCache(void)
+{
+ // Nothign to do!
+}
diff --git a/lib/kofficecore/KoPictureBase.h b/lib/kofficecore/KoPictureBase.h
new file mode 100644
index 00000000..17a7df96
--- /dev/null
+++ b/lib/kofficecore/KoPictureBase.h
@@ -0,0 +1,128 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
+
+ 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 __koPictureBase_h__
+#define __koPictureBase_h__
+
+#include "KoPictureKey.h" // for KoPictureType
+
+#include <qstring.h>
+#include <qimage.h>
+
+class KoXmlWriter;
+class QPainter;
+class QSize;
+class QIODevice;
+class QDragObject;
+
+const char NULL_MIME_TYPE[]="application/x-zerosize";
+
+// TODO: fix documentation
+
+/**
+ * @internal
+ * Base class for KoPictureImage, KoPictureClipart, KoPictureEps, KoPictureWmf...
+ */
+class KoPictureBase
+{
+public:
+ /**
+ * Default constructor.
+ */
+ KoPictureBase();
+
+ /**
+ * Destructor.
+ */
+ virtual ~KoPictureBase();
+
+ virtual KoPictureType::Type getType(void) const;
+
+ virtual KoPictureBase* newCopy(void) const;
+
+ /**
+ * Returns true if the picture is null.
+ */
+ virtual bool isNull(void) const;
+
+ /**
+ * @brief Draw the picture in a painter.
+ *
+ * The parameter @p fastMode allows the picture to be re-sized and drawn quicker if possible
+ *
+ * The parameters @p width, @p height define the desired size for the image
+ * Note that the image is being scaled to that size using scale() - except when printing.
+ * This avoids scaling the image at each paint event.
+ *
+ * The other parameters are very similar to QPainter::drawPixmap :
+ * (@p x, @p y) define the position in the painter,
+ * (@p sx, @p sy) specify the top-left point in pixmap that is to be drawn. The default is (0, 0).
+ * (@p sw, @p sh) specify the size of the pixmap that is to be drawn. The default, (-1, -1), means all the way to the bottom
+ * right of the pixmap.
+ */
+ virtual void draw(QPainter& painter, int x, int y, int width, int height, int sx = 0, int sy = 0, int sw = -1, int sh = -1, bool fastMode = false);
+
+ /**
+ * Create a dragobject containing this picture.
+ * @param dragSource must be 0 when copying to the clipboard
+ * @return 0L if the picture is null!
+ */
+ virtual QDragObject* dragObject( QWidget *dragSource = 0L, const char *name = 0L );
+
+ virtual bool load(QIODevice* io, const QString& extension);
+
+ virtual bool loadData(const QByteArray& array, const QString& extension);
+
+ /**
+ * save file
+ * @param io QIODevice used for saving
+ */
+ virtual bool save(QIODevice* io) const;
+
+ /**
+ * OASIS FlatXML support:
+ * Save picture as base64-encoded data into an XML writer.
+ */
+ virtual bool saveAsBase64( KoXmlWriter& writer ) const;
+
+ virtual QSize getOriginalSize(void) const;
+
+ virtual QPixmap generatePixmap(const QSize& size, bool smoothScale = false);
+
+ virtual QString getMimeType(const QString& extension) const;
+
+ bool isSlowResizeModeAllowed(void) const;
+
+ /**
+ * Generate a QImage
+ * (always in slow mode)
+ */
+ virtual QImage generateImage(const QSize& size);
+
+ virtual bool hasAlphaBuffer() const
+ { return false; }
+ virtual void setAlphaBuffer(bool /*enable*/)
+ { }
+ virtual QImage createAlphaMask(int /*conversion_flags*/ = 0) const
+ { return QImage(); }
+
+ virtual void clearCache(void);
+};
+
+#endif /* __koPictureBase_h__ */
diff --git a/lib/kofficecore/KoPictureClipart.cpp b/lib/kofficecore/KoPictureClipart.cpp
new file mode 100644
index 00000000..f4d94fd2
--- /dev/null
+++ b/lib/kofficecore/KoPictureClipart.cpp
@@ -0,0 +1,150 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (c) 2001 David Faure <faure@kde.org>
+ Copyright (C) 2002 Nicolas GOUTTE <goutte@kde.org>
+
+ 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.
+*/
+
+#include <qbuffer.h>
+#include <qpainter.h>
+#include <qpicture.h>
+#include <qpixmap.h>
+
+#include <kdebug.h>
+#include <kdeversion.h>
+#if ! KDE_IS_VERSION( 3,1,90 )
+#include <kdebugclasses.h>
+#endif
+
+#include "KoPictureKey.h"
+#include "KoPictureBase.h"
+#include "KoPictureClipart.h"
+
+KoPictureClipart::KoPictureClipart(void) : m_clipart(KoPictureType::formatVersionQPicture)
+{
+}
+
+KoPictureClipart::~KoPictureClipart(void)
+{
+}
+
+KoPictureBase* KoPictureClipart::newCopy(void) const
+{
+ return new KoPictureClipart(*this);
+}
+
+KoPictureType::Type KoPictureClipart::getType(void) const
+{
+ return KoPictureType::TypeClipart;
+}
+
+bool KoPictureClipart::isNull(void) const
+{
+ return m_clipart.isNull();
+}
+
+void KoPictureClipart::drawQPicture(QPicture& clipart, QPainter& painter,
+ int x, int y, int width, int height, int sx, int sy, int sw, int sh)
+{
+ kdDebug(30003) << "Drawing KoPictureClipart " << this << endl;
+ kdDebug(30003) << " x=" << x << " y=" << y << " width=" << width << " height=" << height << endl;
+ kdDebug(30003) << " sx=" << sx << " sy=" << sy << " sw=" << sw << " sh=" << sh << endl;
+ painter.save();
+ // Thanks to Harri, Qt3 makes it much easier than Qt2 ;)
+ QRect br = clipart.boundingRect();
+ kdDebug(30003) << " Bounding rect. " << br << endl;
+
+ painter.translate(x,y); // Translating must be done before scaling!
+ if ( br.width() && br.height() )
+ painter.scale(double(width)/double(br.width()),double(height)/double(br.height()));
+ else
+ kdWarning(30003) << "Null bounding rectangle: " << br.width() << " x " << br.height() << endl;
+ painter.drawPicture(0,0,clipart);
+ painter.restore();
+}
+
+void KoPictureClipart::draw(QPainter& painter, int x, int y, int width, int height, int sx, int sy, int sw, int sh, bool /*fastMode*/)
+{
+ drawQPicture(m_clipart, painter, x, y, width, height, sx, sy, sw, sh);
+}
+
+bool KoPictureClipart::loadData(const QByteArray& array, const QString& extension)
+{
+ // Second, create the original clipart
+ kdDebug(30003) << "Trying to load clipart... (Size:" << m_rawData.size() << ")" << endl;
+ m_rawData=array;
+ QBuffer buffer(m_rawData);
+ buffer.open(IO_ReadOnly);
+ bool check = true;
+ if (extension=="svg")
+ {
+ if (!m_clipart.load(&buffer, "svg"))
+ {
+ kdWarning(30003) << "Loading SVG has failed! (KoPictureClipart::load)" << endl;
+ check = false;
+ }
+ }
+ else
+ {
+ if (!m_clipart.load(&buffer, NULL))
+ {
+ kdWarning(30003) << "Loading QPicture has failed! (KoPictureClipart::load)" << endl;
+ check = false;
+ }
+ }
+ buffer.close();
+ return check;
+}
+
+bool KoPictureClipart::save(QIODevice* io) const
+{
+ // We save the raw data, as the SVG supposrt in QPicture is poor
+ Q_ULONG size=io->writeBlock(m_rawData); // WARNING: writeBlock returns Q_LONG but size() Q_ULONG!
+ return (size==m_rawData.size());
+}
+
+QSize KoPictureClipart::getOriginalSize(void) const
+{
+ return m_clipart.boundingRect().size();
+}
+
+QPixmap KoPictureClipart::generatePixmap(const QSize& size, bool /*smoothScale*/)
+{
+ // Not sure if it works, but it worked for KoPictureFilePreviewWidget::setClipart
+ QPixmap pixmap(size);
+ QPainter p;
+
+ p.begin( &pixmap );
+ p.setBackgroundColor( Qt::white );
+ pixmap.fill( Qt::white );
+
+ QRect br = m_clipart.boundingRect();
+ if ( br.width() && br.height() )
+ p.scale( (double)pixmap.width() / (double)br.width(), (double)pixmap.height() / (double)br.height() );
+ p.drawPicture( m_clipart );
+ p.end();
+ return pixmap;
+}
+
+QString KoPictureClipart::getMimeType(const QString& extension) const
+{
+ if (extension=="svg")
+ return "image/svg+xml";
+ else
+ return "image/x-vnd.trolltech.qpicture";
+}
+
diff --git a/lib/kofficecore/KoPictureClipart.h b/lib/kofficecore/KoPictureClipart.h
new file mode 100644
index 00000000..9784c673
--- /dev/null
+++ b/lib/kofficecore/KoPictureClipart.h
@@ -0,0 +1,102 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002, 2004 Nicolas GOUTTE <goutte@kde.org>
+
+ 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 __koPictureClipart_h__
+#define __koPictureClipart_h__
+
+#include <qstring.h>
+#include <qpicture.h>
+
+class QPainter;
+class QSize;
+
+// TODO: fix documentation
+
+/**
+ * @internal
+ * KoPictureClipart is a container class for cliparts
+ */
+class KoPictureClipart : public KoPictureBase
+{
+public:
+ /**
+ * Default constructor.
+ */
+ KoPictureClipart();
+
+ /**
+ * Destructor.
+ */
+ virtual ~KoPictureClipart();
+
+ virtual KoPictureType::Type getType(void) const;
+
+ virtual KoPictureBase* newCopy(void) const;
+
+
+ /**
+ * Returns true if the picture is null.
+ */
+ virtual bool isNull(void) const;
+
+ /**
+ * @brief Draw the image in a painter.
+ *
+ * The parameter @p fastMode allows the picture to be re-sized and drawn quicker if possible
+ *
+ * The parameters @p width, @p height define the desired size for the image
+ * Note that the image is being scaled to that size using scale() - except when printing.
+ * This avoids scaling the image at each paint event.
+ *
+ * The other parameters are very similar to QPainter::drawPixmap :
+ * (@p x, @p y) define the position in the painter,
+ * (@p sx, @p sy) specify the top-left point in pixmap that is to be drawn. The default is (0, 0).
+ * (@p sw, @p sh) specify the size of the pixmap that is to be drawn. The default, (-1, -1), means all the way to the bottom
+ * right of the pixmap.
+ */
+ virtual void draw(QPainter& painter, int x, int y, int width, int height, int sx = 0, int sy = 0, int sw = -1, int sh = -1, bool fastMode = false);
+
+ virtual bool loadData(const QByteArray& array, const QString& extension);
+
+ /**
+ * save file
+ * @param io QIODevice used for saving
+ */
+ virtual bool save(QIODevice* io) const;
+
+ virtual QSize getOriginalSize(void) const;
+
+ virtual QPixmap generatePixmap(const QSize& size, bool smoothScale = false);
+
+ virtual QString getMimeType(const QString& extension) const;
+
+protected:
+ QPixmap getPixmap(QImage& image);
+ /**
+ * @internal
+ * Draw a QPicture
+ */
+ void drawQPicture(QPicture& clipart, QPainter& painter,
+ int x, int y, int width, int height, int sx, int sy, int sw, int sh);
+protected:
+ QPicture m_clipart; ///< The clipart as QPicture
+ QByteArray m_rawData; ///< Copy of the loaded image file
+};
+
+#endif /* __koPictureClipart_h__ */
diff --git a/lib/kofficecore/KoPictureCollection.cpp b/lib/kofficecore/KoPictureCollection.cpp
new file mode 100644
index 00000000..8941f32e
--- /dev/null
+++ b/lib/kofficecore/KoPictureCollection.cpp
@@ -0,0 +1,324 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
+ Copyright (c) 2001, 2004 David Faure <faure@kde.org>
+ Copyright (C) 2002, 2004 Nicolas GOUTTE <goutte@kde.org>
+
+ 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.
+*/
+
+#include <qdom.h>
+
+#include <kdebug.h>
+#include <kurl.h>
+
+#include <KoStoreDevice.h>
+#include <KoXmlWriter.h>
+
+#include "KoPicture.h"
+#include "KoPictureCollection.h"
+
+//#define DEBUG_PICTURES
+
+KoPicture KoPictureCollection::findPicture(const KoPictureKey& key) const
+{
+#ifdef DEBUG_PICTURES
+ kdDebug(30003) << "KoPictureCollection::findPicture " << key.toString() << endl;
+#endif
+ ConstIterator it = find( key );
+ if ( it == end() )
+ {
+ KoPicture picture;
+ picture.setKey(key);
+ return picture;
+ }
+
+ return *it;
+}
+
+
+KoPicture KoPictureCollection::insertPicture(const KoPictureKey& key, const KoPicture& picture)
+{
+#ifdef DEBUG_PICTURES
+ kdDebug(30003) << "KoPictureCollection::insertPicture " << key.toString() << endl;
+#endif
+ KoPicture c = findPicture(key);
+ if (c.isNull())
+ {
+#ifdef DEBUG_PICTURES
+ kdDebug(30003) << "KoPictureCollection::insertPicture not found -> inserting" << endl;
+#endif
+ c=picture;
+ c.setKey(key); // Be sure that the key is correctly set in the KoPicture!
+ insert(key, c);
+ }
+ return c;
+}
+
+KoPicture KoPictureCollection::insertPicture(const KoPicture& picture)
+{
+ return insertPicture(picture.getKey(), picture);
+}
+
+KoPicture KoPictureCollection::downloadPicture(const KURL& url, QWidget *window)
+{
+#ifdef DEBUG_PICTURES
+ kdDebug(30003) << "KoPictureCollection::downloadPicture " << url.prettyURL() << endl;
+#endif
+
+ // If it is a local file, we can check the last modification date, so we should better use loadPicture
+ if (url.isLocalFile())
+ return loadPicture(url.path());
+
+
+ // We have really a remote file, so we cannot check the last modification date
+ // Therefore we have to always download the file
+
+ KoPicture pic;
+#ifdef DEBUG_PICTURES
+ kdDebug(30003) << "Trying to download picture from file " << url.prettyURL() << endl;
+#endif
+
+ if (pic.setKeyAndDownloadPicture(url, window))
+ insertPicture(pic.getKey(), pic);
+ else
+ kdWarning(30003) << "Could not download KoPicture from " << url.prettyURL() << endl;
+
+ return pic;
+}
+
+KoPicture KoPictureCollection::loadPicture(const QString& fileName)
+{
+#ifdef DEBUG_PICTURES
+ kdDebug(30003) << "KoPictureCollection::loadPicture " << fileName << endl;
+#endif
+ // Check the last modified date of the file, as it is now.
+ KoPictureKey key;
+ key.setKeyFromFile(fileName);
+
+ KoPicture c = findPicture(key);
+ if (c.isNull())
+ {
+#ifdef DEBUG_PICTURES
+ kdDebug(30003) << "Trying to load picture from file " << fileName << endl;
+#endif
+ if (c.loadFromFile(fileName))
+ insertPicture(key, c);
+ else
+ kdWarning(30003) << "Could not load KoPicture from " << fileName << endl;
+ }
+ return c;
+}
+
+QString KoPictureCollection::getFileName(const Type pictureType, KoPicture& picture, int& counter)
+{
+ QString storeURL;
+ // ### TODO: remove "cliparts" when KPresenter is ready for it
+ if (pictureType==CollectionClipart)
+ storeURL="cliparts/clipart";
+ else
+ storeURL="pictures/picture";
+ storeURL+=QString::number(++counter);
+ storeURL+='.';
+ storeURL+=picture.getExtension();
+ return storeURL;
+}
+
+QString KoPictureCollection::getOasisFileName(const KoPicture& picture) const
+{
+ QString storeURL( "Pictures/");
+ if ( !picture.uniquePictureId().isEmpty() )
+ storeURL+=picture.uniquePictureId();
+ else
+ storeURL+=picture.getKey().toString();
+ storeURL+='.';
+ storeURL+=picture.getExtension();
+ return storeURL;
+}
+
+bool KoPictureCollection::saveToStore(const Type pictureType, KoStore *store, const QValueList<KoPictureKey>& keys)
+{
+ int counter=0;
+ QValueList<KoPictureKey>::const_iterator it = keys.begin();
+ for ( ; it != keys.end(); ++it )
+ {
+ KoPicture c = findPicture( *it );
+ if (c.isNull())
+ kdWarning(30003) << "Picture " << (*it).toString() << " not found in collection !" << endl;
+ else
+ {
+ QString storeURL=getFileName(pictureType, c, counter);
+
+ if (store->open(storeURL))
+ {
+ KoStoreDevice dev(store);
+ if ( ! c.save(&dev) )
+ return false; // e.g. bad image?
+ if ( !store->close() )
+ return false; // e.g. disk full
+ }
+ }
+ }
+ return true;
+}
+
+bool KoPictureCollection::saveOasisToStore( KoStore *store, QValueList<KoPictureKey> keys, KoXmlWriter* manifestWriter )
+{
+ QValueList<KoPictureKey>::Iterator it = keys.begin();
+ for ( ; it != keys.end(); ++it )
+ {
+ KoPicture c = findPicture( *it );
+ if (c.isNull())
+ kdWarning(30003) << "Picture " << (*it).toString() << " not found in collection !" << endl;
+ else
+ {
+ QString storeURL( getOasisFileName(c) );
+ if (store->open(storeURL))
+ {
+ KoStoreDevice dev(store);
+ if ( ! c.save(&dev) )
+ return false; // e.g. bad image?
+ if ( !store->close() )
+ return false; // e.g. disk full
+ manifestWriter->addManifestEntry( storeURL, c.getMimeType() );
+ }
+ }
+ }
+ return true;
+}
+
+QDomElement KoPictureCollection::saveXML(const Type pictureType, QDomDocument &doc, QValueList<KoPictureKey> keys)
+{
+ QString strElementName("PICTURES");
+ if (pictureType==CollectionImage)
+ strElementName="PIXMAPS";
+ else if (pictureType==CollectionClipart)
+ strElementName="CLIPARTS";
+ QDomElement cliparts = doc.createElement( strElementName );
+ int counter=0;
+ QValueList<KoPictureKey>::Iterator it = keys.begin();
+ for ( ; it != keys.end(); ++it )
+ {
+ KoPicture picture = findPicture( *it );
+ if ( picture.isNull() )
+ kdWarning(30003) << "Picture " << (*it).toString() << " not found in collection !" << endl;
+ else
+ {
+ QString pictureName=getFileName(pictureType, picture, counter);
+ QDomElement keyElem = doc.createElement( "KEY" );
+ cliparts.appendChild(keyElem);
+ (*it).saveAttributes(keyElem);
+ keyElem.setAttribute("name", pictureName);
+ }
+ }
+ return cliparts;
+}
+
+void KoPictureCollection::readXML( QDomElement& pixmapsElem, QMap <KoPictureKey, QString>& map )
+{
+ for( QDomNode n = pixmapsElem.firstChild();
+ !n.isNull();
+ n = n.nextSibling() )
+ {
+ QDomElement keyElement = n.toElement();
+ if (keyElement.isNull()) continue;
+ if (keyElement.tagName()=="KEY")
+ {
+ KoPictureKey key;
+ key.loadAttributes(keyElement);
+ map.insert(key, keyElement.attribute("name"));
+ }
+ }
+}
+
+
+KoPictureCollection::StoreMap KoPictureCollection::readXML( QDomElement& pixmapsElem )
+{
+ StoreMap map;
+ readXML(pixmapsElem, map);
+ return map;
+}
+
+void KoPictureCollection::readFromStore( KoStore * store, const StoreMap & storeMap )
+{
+#ifdef DEBUG_PICTURES
+ kdDebug(30003) << "KoPictureCollection::readFromStore " << store << endl;
+#endif
+ StoreMap::ConstIterator it = storeMap.begin();
+ for ( ; it != storeMap.end(); ++it )
+ {
+ KoPicture c = findPicture(it.key());
+ if (!c.isNull())
+ {
+ // Do not load a file that we already have!
+ //kdDebug(30003) << store << endl;
+ continue;
+ }
+ QString u(it.data());
+ if (u.isEmpty())
+ {
+ // old naming I suppose ?
+ u=it.key().toString();
+ }
+
+ KoPicture picture;
+
+ if (!store->open( u ))
+ {
+ u.prepend( "file:" );
+ if (!store->open( u ))
+ {
+ kdWarning(30003) << "Could load neither from store nor from file: " << it.data() << endl;
+ return;
+ }
+ }
+
+ const int pos=u.findRev('.');
+ if (pos==-1)
+ {
+ kdError(30003) << "File with no extension! Not supported!" << endl;
+ return;
+ }
+ const QString extension(u.mid(pos+1));
+
+ KoStoreDevice dev(store);
+ picture.load(&dev, extension);
+ store->close();
+
+ if (!picture.isNull())
+ insertPicture(it.key(), picture);
+ }
+}
+
+KoPicture KoPictureCollection::findOrLoad(const QString& fileName, const QDateTime& dateTime)
+{
+ // As now all KoPictureKey objects have a valid QDateTime, we must do it without a date/time check.
+ ConstIterator it = find( KoPictureKey ( fileName, dateTime ) );
+ if ( it == end() )
+ {
+ return loadPicture( fileName );
+ }
+ return *it;
+}
+
+void KoPictureCollection::assignUniqueIds()
+{
+ uint idx = 0;
+ Iterator it;
+ for ( it = begin(); it != end(); ++it, ++idx )
+ {
+ it.data().assignPictureId(idx);
+ }
+}
diff --git a/lib/kofficecore/KoPictureCollection.h b/lib/kofficecore/KoPictureCollection.h
new file mode 100644
index 00000000..22277bdd
--- /dev/null
+++ b/lib/kofficecore/KoPictureCollection.h
@@ -0,0 +1,171 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
+ Copyright (c) 2001 David Faure <faure@kde.org>
+ Copyright (C) 2002 Nicolas GOUTTE <goutte@kde.org>
+
+ 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 koPictureCollection_h
+#define koPictureCollection_h
+
+#include <qmap.h>
+#include <qdom.h>
+#include <qvaluelist.h>
+
+#include "KoPicture.h"
+
+class KURL;
+
+class KoStore;
+class KoXmlWriter;
+
+/**
+ * A collection of pictures (a key and the picture itself).
+ *
+ */
+class KOFFICECORE_EXPORT KoPictureCollection : public QMap<KoPictureKey, KoPicture>
+{
+public:
+ enum Type {
+ /// collection with mixed pictures
+ CollectionPicture=0,
+ /// collection with images only
+ CollectionImage,
+ /// collection with cliparts only
+ CollectionClipart
+ };
+
+ /**
+ * Looks for a clipart in the collection, returns a new KoPicture with that key if not found.
+ */
+ KoPicture findPicture( const KoPictureKey &key ) const;
+
+ /**
+ * Inserts a picture into the collection, if not already there
+ */
+ KoPicture insertPicture( const KoPictureKey& key, const KoPicture& picture );
+
+ /**
+ * Inserts a picture into the collection, if not already there
+ * Same as above, but takes the key from the @p picture
+ */
+ KoPicture insertPicture( const KoPicture& picture );
+
+ /**
+ * @brief Download a possibly remote file
+ *
+ * If this file is really remote, it is always downloaded.
+ * If the file is local, it acts as @ref #loadPicture.
+ * @param url the URL to download from
+ * @param window the parent window for the download. You can pass NULL (0)
+ * if you absolutely cannot find anything to use.
+ */
+ KoPicture downloadPicture(const KURL& url, QWidget *window);
+
+ /**
+ * @brief Load a clipart from a file (and insert into the collection).
+ *
+ * The modification date of the file is checked, to create the key
+ * for this clipart. If this key maps to an existing clipart in the
+ * collection, then this picture is returned, otherwise the file is loaded.
+ */
+ KoPicture loadPicture( const QString &fileName );
+
+ /**
+ * Save the used picturess from the collection into the store
+ * Usually called from completeSaving().
+ *
+ * @param pictureType type for the stored picture
+ * @param store the store in which to save the pictures
+ * @param keys the list of keys corresponding to the pictures to save
+ * @return true on success, false on failure (e.g. disk full)
+ *
+ * @todo Reduce lameness of dox for pictureType.
+ */
+ bool saveToStore(const Type pictureType, KoStore * store, const QValueList<KoPictureKey>& keys );
+
+ /**
+ * Generate the &lt;PICTURES&gt;, &lt;PIXMAPS> or &lt;CLIPARTS> tag, that saves the key and the related
+ * relative path in the store (e.g. pictures/picture1.png) for each picture.
+ *
+ * @param pictureType the type of the collection
+ * @param doc the DOM document in which the tags are to be generated
+ * @param keys the list of keys
+ */
+ QDomElement saveXML(const Type pictureType, QDomDocument &doc,
+ QValueList<KoPictureKey> keys );
+
+ bool saveOasisToStore( KoStore *store, QValueList<KoPictureKey> keys, KoXmlWriter* manifestWriter );
+
+
+ typedef QMap<KoPictureKey, QString> StoreMap;
+ /**
+ * Read the &lt;PICTURES&gt;, &lt;PIXMAPS> or &lt;CLIPARTS> tag, and save the result (key<->store-filename associations)
+ * into the QMap. You may want to 'new' a QMap in loadXML, and to use and then delete
+ * it in completeLoading (to save memory).
+ *
+ * @param pixmapsElem the &lt;PICTURES&gt;, &lt;PIXMAPS> or &lt;CLIPARTS> element
+ */
+ StoreMap readXML( QDomElement &pixmapsElem );
+
+ /**
+ * Helper method for @ref #readFromStore
+ */
+ void readXML( QDomElement& pixmapsElem, QMap <KoPictureKey, QString>& map );
+
+ /**
+ * Read all pictures from the store, into this collection
+ * The map comes from @ref #readXML, and is used to find which pictures
+ * to load, and which key to associate them.
+ */
+ void readFromStore( KoStore * store, const StoreMap & storeMap );
+
+ /**
+ * @deprecated
+ * KPresenter needs to use the same code for loading images from a collection and
+ * for loading images from disk.
+ *
+ * @param fileName the name of the file to read from disk if needed
+ * @param dateTime the date and time
+ *
+ * Formerly, an invalid date/time meant to read the file from disk. This is not the case anymore.
+ */
+ KoPicture findOrLoad(const QString& fileName, const QDateTime& dateTime);
+
+ /**
+ * Return filename as url for picture
+ *
+ */
+ QString getOasisFileName(const KoPicture& picture) const;
+
+ /**
+ * Call it before to save Oasis file
+ */
+ void assignUniqueIds();
+
+
+private:
+ /**
+ * @internal
+ */
+ QString getFileName(const Type pictureType, KoPicture& picture, int& counter);
+
+ class Private;
+ Private* d;
+};
+
+#endif /* __koPictureCollection_h_- */
diff --git a/lib/kofficecore/KoPictureEps.cpp b/lib/kofficecore/KoPictureEps.cpp
new file mode 100644
index 00000000..752e2a45
--- /dev/null
+++ b/lib/kofficecore/KoPictureEps.cpp
@@ -0,0 +1,447 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
+
+ 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.
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+
+#include <qbuffer.h>
+#include <qpainter.h>
+#include <qpaintdevicemetrics.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qapplication.h>
+#include <qdragobject.h>
+
+#include <kglobal.h>
+#include <kdebug.h>
+#include <kdeversion.h>
+#if ! KDE_IS_VERSION( 3,1,90 )
+#include <kdebugclasses.h>
+#endif
+#include <ktempfile.h>
+#include <kprocess.h>
+
+#include "KoPictureKey.h"
+#include "KoPictureBase.h"
+#include "KoPictureEps.h"
+
+
+KoPictureEps::KoPictureEps(void) : m_psStreamStart(0), m_psStreamLength(0), m_cacheIsInFastMode(true)
+{
+ // Forbid QPixmap to cache the X-Window resources (Yes, it is slower!)
+ m_cachedPixmap.setOptimization(QPixmap::MemoryOptim);
+}
+
+KoPictureEps::~KoPictureEps(void)
+{
+}
+
+KoPictureBase* KoPictureEps::newCopy(void) const
+{
+ return new KoPictureEps(*this);
+}
+
+KoPictureType::Type KoPictureEps::getType(void) const
+{
+ return KoPictureType::TypeEps;
+}
+
+bool KoPictureEps::isNull(void) const
+{
+ return m_rawData.isNull();
+}
+
+QImage KoPictureEps::scaleWithGhostScript(const QSize& size, const int resolutionx, const int resolutiony )
+{
+ if (!m_boundingBox.width() || !m_boundingBox.height())
+ {
+ kdDebug(30003) << "EPS image has a null size! (in KoPictureEps::scaleWithGhostScript)" << endl;
+ return QImage();
+ }
+
+ // ### TODO: do not call GhostScript up to three times for each re-scaling (one call of GhostScript should be enough to know which device is available: gs --help)
+ // png16m is better, but not always available -> fallback to bmp16m, then fallback to ppm (256 colors)
+ // ### TODO: pcx24b is also a true colour format
+ // ### TODO: support alpha (other gs devices needed)
+
+ const char* deviceTable[] = { "png16m", "bmp16m", "ppm", 0 };
+
+ QImage img;
+
+ for ( int i = 0; deviceTable[i]; ++i)
+ {
+ if ( tryScaleWithGhostScript( img, size, resolutionx, resolutiony, deviceTable[i] ) != -1 )
+ {
+ return img;
+ }
+
+ }
+
+ kdError(30003) << "Image from GhostScript cannot be loaded (in KoPictureEps::scaleWithGhostScript)" << endl;
+ return img;
+}
+
+// Helper method for scaleWithGhostScript. Returns 1 on success, 0 on error, -1 if nothing generated
+// (in which case another 'output device' can be tried)
+int KoPictureEps::tryScaleWithGhostScript(QImage &image, const QSize& size, const int resolutionx, const int resolutiony, const char* device )
+// Based on the code of the file kdelibs/kimgio/eps.cpp
+{
+ kdDebug(30003) << "Sampling with GhostScript, using device \"" << device << "\" (in KoPictureEps::tryScaleWithGhostScript)" << endl;
+
+ KTempFile tmpFile;
+ tmpFile.setAutoDelete(true);
+
+ if ( tmpFile.status() )
+ {
+ kdError(30003) << "No KTempFile! (in KoPictureEps::tryScaleWithGhostScript)" << endl;
+ return 0; // error
+ }
+
+ const int wantedWidth = size.width();
+ const int wantedHeight = size.height();
+ const double xScale = double(size.width()) / double(m_boundingBox.width());
+ const double yScale = double(size.height()) / double(m_boundingBox.height());
+
+ // create GS command line
+
+ QString cmdBuf ( "gs -sOutputFile=" );
+ cmdBuf += KProcess::quote(tmpFile.name());
+ cmdBuf += " -q -g";
+ cmdBuf += QString::number( wantedWidth );
+ cmdBuf += "x";
+ cmdBuf += QString::number( wantedHeight );
+
+ if ( ( resolutionx > 0) && ( resolutiony > 0) )
+ {
+#if 0
+ // Do not play with resolution for now.
+ // It brings more problems at print than solutions
+ cmdBuf += " -r";
+ cmdBuf += QString::number( resolutionx );
+ cmdBuf += "x";
+ cmdBuf += QString::number( resolutiony );
+#endif
+ }
+
+ cmdBuf += " -dSAFER -dPARANOIDSAFER -dNOPAUSE -sDEVICE=";
+ cmdBuf += device;
+ //cmdBuf += " -c 255 255 255 setrgbcolor fill 0 0 0 setrgbcolor";
+ cmdBuf += " -";
+ cmdBuf += " -c showpage quit";
+
+ // run ghostview
+
+ FILE* ghostfd = popen (QFile::encodeName(cmdBuf), "w");
+
+ if ( ghostfd == 0 )
+ {
+ kdError(30003) << "No connection to GhostScript (in KoPictureEps::tryScaleWithGhostScript)" << endl;
+ return 0; // error
+ }
+
+ // The translation is needed as GhostScript (7.07) cannot handle negative values in the bounding box otherwise.
+ fprintf (ghostfd, "\n%d %d translate\n", -qRound(m_boundingBox.left()*xScale), -qRound(m_boundingBox.top()*yScale));
+ fprintf (ghostfd, "%g %g scale\n", xScale, yScale);
+
+ // write image to gs
+
+ fwrite( m_rawData.data() + m_psStreamStart, sizeof(char), m_psStreamLength, ghostfd);
+
+ pclose ( ghostfd );
+
+ // load image
+ if( !image.load (tmpFile.name()) )
+ {
+ // It failed - maybe the device isn't supported by gs
+ return -1;
+ }
+ if ( image.size() != size ) // this can happen due to rounding problems
+ {
+ //kdDebug(30003) << "fixing size to " << size.width() << "x" << size.height()
+ // << " (was " << image.width() << "x" << image.height() << ")" << endl;
+ image = image.scale( size ); // hmm, smoothScale instead?
+ }
+ kdDebug(30003) << "Image parameters: " << image.width() << "x" << image.height() << "x" << image.depth() << endl;
+ return 1; // success
+}
+
+void KoPictureEps::scaleAndCreatePixmap(const QSize& size, bool fastMode, const int resolutionx, const int resolutiony )
+{
+ kdDebug(30003) << "KoPictureEps::scaleAndCreatePixmap " << size << " " << (fastMode?QString("fast"):QString("slow"))
+ << " resolutionx: " << resolutionx << " resolutiony: " << resolutiony << endl;
+ if ((size==m_cachedSize)
+ && ((fastMode) || (!m_cacheIsInFastMode)))
+ {
+ // The cached pixmap has already the right size
+ // and:
+ // - we are in fast mode (We do not care if the re-size was done slowly previously)
+ // - the re-size was already done in slow mode
+ kdDebug(30003) << "Already cached!" << endl;
+ return;
+ }
+
+ // Slow mode can be very slow, especially at high zoom levels -> configurable
+ if ( !isSlowResizeModeAllowed() )
+ {
+ kdDebug(30003) << "User has disallowed slow mode!" << endl;
+ fastMode = true;
+ }
+
+ // We cannot use fast mode, if nothing was ever cached.
+ if ( fastMode && !m_cachedSize.isEmpty())
+ {
+ kdDebug(30003) << "Fast scaling!" << endl;
+ // Slower than caching a QImage, but faster than re-sampling!
+ QImage image( m_cachedPixmap.convertToImage() );
+ m_cachedPixmap=image.scale( size );
+ m_cacheIsInFastMode=true;
+ m_cachedSize=size;
+ }
+ else
+ {
+ QTime time;
+ time.start();
+
+ QApplication::setOverrideCursor( Qt::waitCursor );
+ m_cachedPixmap = scaleWithGhostScript( size, resolutionx, resolutiony );
+ QApplication::restoreOverrideCursor();
+ m_cacheIsInFastMode=false;
+ m_cachedSize=size;
+
+ kdDebug(30003) << "Time: " << (time.elapsed()/1000.0) << " s" << endl;
+ }
+ kdDebug(30003) << "New size: " << size << endl;
+}
+
+void KoPictureEps::draw(QPainter& painter, int x, int y, int width, int height, int sx, int sy, int sw, int sh, bool fastMode)
+{
+ if ( !width || !height )
+ return;
+
+ QSize screenSize( width, height );
+ //kdDebug() << "KoPictureEps::draw screenSize=" << screenSize.width() << "x" << screenSize.height() << endl;
+
+ QPaintDeviceMetrics metrics (painter.device());
+ kdDebug(30003) << "Metrics: X: " << metrics.logicalDpiX() << " x Y: " << metrics.logicalDpiX() << " (in KoPictureEps::draw)" << endl;
+
+ if ( painter.device()->isExtDev() ) // Is it an external device (i.e. printer)
+ {
+ kdDebug(30003) << "Drawing for a printer (in KoPictureEps::draw)" << endl;
+ // For printing, always re-sample the image, as a printer has never the same resolution than a display.
+ QImage image( scaleWithGhostScript( screenSize, metrics.logicalDpiX(), metrics.logicalDpiY() ) );
+ // sx,sy,sw,sh is meant to be used as a cliprect on the pixmap, but drawImage
+ // translates it to the (x,y) point -> we need (x+sx, y+sy).
+ painter.drawImage( x + sx, y + sy, image, sx, sy, sw, sh );
+ }
+ else // No, it is simply a display
+ {
+ scaleAndCreatePixmap(screenSize, fastMode, metrics.logicalDpiX(), metrics.logicalDpiY() );
+
+ // sx,sy,sw,sh is meant to be used as a cliprect on the pixmap, but drawPixmap
+ // translates it to the (x,y) point -> we need (x+sx, y+sy).
+ painter.drawPixmap( x + sx, y + sy, m_cachedPixmap, sx, sy, sw, sh );
+ }
+}
+
+bool KoPictureEps::extractPostScriptStream( void )
+{
+ kdDebug(30003) << "KoPictureEps::extractPostScriptStream" << endl;
+ QDataStream data( m_rawData, IO_ReadOnly );
+ data.setByteOrder( QDataStream::LittleEndian );
+ Q_UINT32 magic, offset, length;
+ data >> magic;
+ data >> offset;
+ data >> length;
+ if ( !length )
+ {
+ kdError(30003) << "Length of PS stream is zero!" << endl;
+ return false;
+ }
+ if ( offset+length>m_rawData.size() )
+ {
+ kdError(30003) << "Data stream of the EPSF file is longer than file: " << offset << "+" << length << ">" << m_rawData.size() << endl;
+ return false;
+ }
+ m_psStreamStart = offset;
+ m_psStreamLength = length;
+ return true;
+}
+
+QString KoPictureEps::readLine( const QByteArray& array, const uint start, const uint length, uint& pos, bool& lastCharWasCr )
+{
+ QString strLine;
+ const uint finish = kMin( start + length, array.size() );
+ for ( ; pos < finish; ++pos ) // We are starting at pos
+ {
+ const char ch = array[ pos ]; // Read one character
+ if ( ch == '\n' )
+ {
+ if ( lastCharWasCr )
+ {
+ // We have a line feed following a Carriage Return
+ // As the Carriage Return has already ended the previous line,
+ // discard this Line Feed.
+ lastCharWasCr = false;
+ }
+ else
+ {
+ // We have a normal Line Feed, therefore we end the line
+ break;
+ }
+ }
+ else if ( ch == '\r' )
+ {
+ // We have a Carriage Return, therefore we end the line
+ lastCharWasCr = true;
+ break;
+ }
+ else if ( ch == char(12) ) // Form Feed
+ { // ### TODO: can a FF happen in PostScript?
+ // Ignore the form feed
+ continue;
+ }
+ else
+ {
+ strLine += ch;
+ lastCharWasCr = false;
+ }
+ }
+ return strLine;
+}
+
+
+bool KoPictureEps::loadData(const QByteArray& array, const QString& /* extension */ )
+{
+
+ kdDebug(30003) << "KoPictureEps::load" << endl;
+ // First, read the raw data
+ m_rawData=array;
+
+ if (m_rawData.isNull())
+ {
+ kdError(30003) << "No data was loaded!" << endl;
+ return false;
+ }
+
+ if ( ( m_rawData[0]==char(0xc5) ) && ( m_rawData[1]==char(0xd0) )
+ && ( m_rawData[2]==char(0xd3) ) && ( m_rawData[3]==char(0xc6) ) )
+ {
+ // We have a so-called "MS-DOS EPS file", we have to extract the PostScript stream
+ if (!extractPostScriptStream()) // Changes m_rawData
+ return false;
+ }
+ else
+ {
+ m_psStreamStart = 0;
+ m_psStreamLength = m_rawData.size();
+ }
+
+ QString lineBox; // Line with the bounding box
+ bool lastWasCr = false; // Was the last character of the line a carriage return?
+ uint pos = m_psStreamStart; // We start to search the bounding box at the start of the PostScript stream
+ QString line( readLine( m_rawData, m_psStreamStart, m_psStreamLength, pos, lastWasCr ) );
+ kdDebug(30003) << "Header: " << line << endl;
+ if (!line.startsWith("%!"))
+ {
+ kdError(30003) << "Not a PostScript file!" << endl;
+ return false;
+ }
+ QRect rect;
+ bool lineIsBoundingBox = false; // Does "line" has a %%BoundingBox line?
+ for(;;)
+ {
+ ++pos; // Get over the previous line end (CR or LF)
+ line = readLine( m_rawData, m_psStreamStart, m_psStreamLength, pos, lastWasCr );
+ kdDebug(30003) << "Checking line: " << line << endl;
+ // ### TODO: it seems that the bounding box can be delayed with "(atend)" in the trailer (GhostScript 7.07 does not support it either.)
+ if (line.startsWith("%%BoundingBox:"))
+ {
+ lineIsBoundingBox = true;
+ break;
+ }
+ // ### TODO: also abort on %%EndComments
+ // ### TODO: %? , where ? is non-white-space printable, does not end the comment!
+ else if (!line.startsWith("%%"))
+ break; // Not a EPS comment anymore, so abort as we are not in the EPS header anymore
+ }
+ if ( !lineIsBoundingBox )
+ {
+ kdError(30003) << "KoPictureEps::load: could not find a bounding box!" << endl;
+ return false;
+ }
+ // Floating point values are not allowed in a Bounding Box, but ther are many such files out there...
+ QRegExp exp("(\\-?[0-9]+\\.?[0-9]*)\\s(\\-?[0-9]+\\.?[0-9]*)\\s(\\-?[0-9]+\\.?[0-9]*)\\s(\\-?[0-9]+\\.?[0-9]*)");
+ if ( exp.search(line) == -1 )
+ {
+ // ### TODO: it might be an "(atend)" and the bounding box is in the trailer
+ // (but GhostScript 7.07 does not support a bounding box in the trailer.)
+ // Note: in Trailer, it is the last BoundingBox that counts not the first!
+ kdError(30003) << "Not standard bounding box: " << line << endl;
+ return false;
+ }
+ kdDebug(30003) << "Reg. Exp. Found: " << exp.capturedTexts() << endl;
+ rect.setLeft((int)exp.cap(1).toDouble());
+ rect.setTop((int)exp.cap(2).toDouble());
+ rect.setRight((int)exp.cap(3).toDouble());
+ rect.setBottom((int)exp.cap(4).toDouble());
+ m_boundingBox=rect;
+ m_originalSize=rect.size();
+ kdDebug(30003) << "Rect: " << rect << " Size: " << m_originalSize << endl;
+ return true;
+}
+
+bool KoPictureEps::save(QIODevice* io) const
+{
+ // We save the raw data, to avoid damaging the file by many load/save cycles
+ Q_ULONG size=io->writeBlock(m_rawData); // WARNING: writeBlock returns Q_LONG but size() Q_ULONG!
+ return (size==m_rawData.size());
+}
+
+QSize KoPictureEps::getOriginalSize(void) const
+{
+ return m_originalSize;
+}
+
+QPixmap KoPictureEps::generatePixmap(const QSize& size, bool smoothScale)
+{
+ scaleAndCreatePixmap(size,!smoothScale, 0, 0);
+ return m_cachedPixmap;
+}
+
+QString KoPictureEps::getMimeType(const QString&) const
+{
+ return "image/x-eps";
+}
+
+QImage KoPictureEps::generateImage(const QSize& size)
+{
+ // 0, 0 == resolution unknown
+ return scaleWithGhostScript(size, 0, 0);
+}
+
+void KoPictureEps::clearCache(void)
+{
+ m_cachedPixmap.resize(0, 0);
+ m_cacheIsInFastMode=true;
+ m_cachedSize=QSize();
+}
diff --git a/lib/kofficecore/KoPictureEps.h b/lib/kofficecore/KoPictureEps.h
new file mode 100644
index 00000000..ba2a4cb9
--- /dev/null
+++ b/lib/kofficecore/KoPictureEps.h
@@ -0,0 +1,136 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
+
+ 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 __koPictureEps_h__
+#define __koPictureEps_h__
+
+#include <qstring.h>
+
+class QPainter;
+class QSize;
+class KoPictureEpsPrivate;
+
+// TODO: fix documentation
+
+/**
+ * @internal
+ * KoPictureEps is a container class for a EPS picture
+ */
+class KoPictureEps : public KoPictureBase
+{
+public:
+ /**
+ * Default constructor.
+ */
+ KoPictureEps();
+
+ /**
+ * Destructor.
+ */
+ virtual ~KoPictureEps();
+
+ KoPictureType::Type getType(void) const;
+
+ KoPictureBase* newCopy(void) const;
+
+ /**
+ * Returns true if the picture is null.
+ */
+ virtual bool isNull(void) const;
+
+ /**
+ * @brief Draw the image in a painter.
+ *
+ * No, this isn't as simple as painter.drawPixmap().
+ * This method ensures that the best quality is used when printing, scaling the painter.
+ *
+ * The parameter @p fastMode allows the picture to be re-sized and drawn quicker if possible
+ *
+ * The parameters @p width, @p height define the desired size for the image
+ * Note that the image is being scaled to that size using scale() - except when printing.
+ * This avoids scaling the image at each paint event.
+ *
+ * The other parameters are very similar to QPainter::drawPixmap :
+ * (@p x, @p y) define the position in the painter,
+ * (@p sx, @p sy) specify the top-left point in pixmap that is to be drawn. The default is (0, 0).
+ * (@p sw, @p sh) specify the size of the pixmap that is to be drawn. The default, (-1, -1), means all the way to the bottom
+ * right of the pixmap.
+ */
+ virtual void draw(QPainter& painter, int x, int y, int width, int height, int sx = 0, int sy = 0, int sw = -1, int sh = -1, bool fastMode = false);
+
+ virtual bool loadData(const QByteArray& array, const QString& extension);
+
+ virtual bool save(QIODevice* io) const;
+
+ virtual QSize getOriginalSize(void) const;
+
+ virtual QPixmap generatePixmap(const QSize& size, bool smoothScale = false);
+
+ virtual QString getMimeType(const QString& extension) const;
+
+ /**
+ * Generate a QImage
+ * (always in slow mode)
+ */
+ virtual QImage generateImage(const QSize& size);
+
+ virtual void clearCache(void);
+protected:
+ QPixmap getPixmap(QImage& image);
+ void scaleAndCreatePixmap(const QSize& size, bool fastMode, const int resolutionx, const int resolutiony );
+ QImage scaleWithGhostScript( const QSize& size, const int resolutionx, const int resolutiony );
+ bool extractPostScriptStream( void );
+
+private:
+ int tryScaleWithGhostScript(QImage &image, const QSize& size, const int resolutionx, const int resolutiony, const char* device );
+ /**
+ * @brief Read a line from a PostScript stream in a QByteArray
+ *
+ * The PostScript stream is somewhere in the QByteArray and therefore has to be
+ * determined by the start and length of this stream and the current position
+ *
+ * @param array the array which is read
+ * @param start start position of the PostScript stream
+ * @param lenght lenght of the PostScript stream
+ * @param pos current position (which is also returned)
+ * @param lastCharWasCr defines if the previous line ended with a Carriage Return,
+ * to skip an eventual Line Feed at the start of this line
+ * @return the read line
+ */
+ QString readLine( const QByteArray& array, const uint start, const uint length, uint& pos, bool& lastCharWasCr );
+
+private:
+ /**
+ * Copy of the loaded EPS file
+ */
+ QByteArray m_rawData;
+ QPixmap m_cachedPixmap; ///< Cached pixmap
+ QSize m_originalSize; ///< Original size of the EPS picture
+ QSize m_cachedSize; ///< size of the currently cached pixmap @see m_cachedPixmap
+ QRect m_boundingBox; ///< Bounding box, as read from the EPS file
+ uint m_psStreamStart; ///< Start position of the PostScript stream (like if it was a MS-DOS EPS file)
+ uint m_psStreamLength; ///< Lenght of the PostScript stream (like if it was a MS-DOS EPS file)
+ /**
+ * true, if the last cached image was done using fast mode.
+ * false, if the last cached image was done using slow mode.
+ */
+ bool m_cacheIsInFastMode;
+};
+
+#endif /* __koPictureEps_h__ */
diff --git a/lib/kofficecore/KoPictureImage.cpp b/lib/kofficecore/KoPictureImage.cpp
new file mode 100644
index 00000000..02110760
--- /dev/null
+++ b/lib/kofficecore/KoPictureImage.cpp
@@ -0,0 +1,194 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002, 2003 Nicolas GOUTTE <goutte@kde.org>
+
+ 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.
+*/
+
+#include "KoPictureImage.h"
+#include "KoPictureKey.h"
+
+#include <kdebug.h>
+#include <kmimetype.h>
+
+#include <qbuffer.h>
+#include <qpainter.h>
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qapplication.h>
+#include <qdragobject.h>
+
+KoPictureImage::KoPictureImage(void) : m_cacheIsInFastMode(true)
+{
+ // Forbid QPixmap to cache the X-Window resources (Yes, it is slower!)
+ m_cachedPixmap.setOptimization(QPixmap::MemoryOptim);
+}
+
+KoPictureImage::~KoPictureImage(void)
+{
+}
+
+KoPictureBase* KoPictureImage::newCopy(void) const
+{
+ return new KoPictureImage(*this);
+}
+
+KoPictureType::Type KoPictureImage::getType(void) const
+{
+ return KoPictureType::TypeImage;
+}
+
+bool KoPictureImage::isNull(void) const
+{
+ return m_originalImage.isNull();
+}
+
+void KoPictureImage::scaleAndCreatePixmap(const QSize& size, bool fastMode)
+{
+ if ((size==m_cachedSize)
+ && ((fastMode) || (!m_cacheIsInFastMode)))
+ {
+ // The cached pixmap has already the right size
+ // and:
+ // - we are in fast mode (We do not care if the re-size was done slowly previously)
+ // - the re-size was already done in slow mode
+ return;
+ }
+
+ // Slow mode can be very slow, especially at high zoom levels -> configurable
+ if ( !isSlowResizeModeAllowed() )
+ {
+ kdDebug(30003) << "User has disallowed slow mode!" << endl;
+ fastMode = true;
+ }
+
+ // Use QImage::scale if we have fastMode==true
+ if ( fastMode )
+ {
+ m_cachedPixmap.convertFromImage(m_originalImage.scale( size ), QPixmap::Color); // Always color or else B/W can be reversed
+ m_cacheIsInFastMode=true;
+ }
+ else
+ {
+ m_cachedPixmap.convertFromImage(m_originalImage.smoothScale( size ), QPixmap::Color); // Always color or else B/W can be reversed
+ m_cacheIsInFastMode=false;
+ }
+ m_cachedSize=size;
+}
+
+void KoPictureImage::draw(QPainter& painter, int x, int y, int width, int height, int sx, int sy, int sw, int sh, bool fastMode)
+{
+ //kdDebug() << "KoImage::draw currentSize:" << currentSize.width() << "x" << currentSize.height() << endl;
+ if ( !width || !height )
+ return;
+ QSize origSize = getOriginalSize();
+ const bool scaleImage = painter.device()->isExtDev() // we are printing
+ && ((width <= origSize.width()) || (height <= origSize.height()));
+ if( scaleImage )
+ {
+ // use full resolution of image
+ double xScale = double(width) / double(origSize.width());
+ double yScale = double(height) / double(origSize.height());
+
+ painter.save();
+ painter.translate( x, y );
+ painter.scale( xScale, yScale );
+ // Note that sx, sy, sw and sh are unused in this case. Not a problem, since it's about printing.
+ // Note 2: we do not cache the QPixmap. As we are printing, the next time we will probably
+ // need again the screen version.
+ painter.drawImage(0, 0, m_originalImage);
+ painter.restore();
+ }
+ else
+ {
+ QSize screenSize( width, height );
+ //kdDebug() << "KoPictureImage::draw screenSize=" << screenSize.width() << "x" << screenSize.height() << endl;
+
+ scaleAndCreatePixmap(screenSize, fastMode);
+
+ // sx,sy,sw,sh is meant to be used as a cliprect on the pixmap, but drawPixmap
+ // translates it to the (x,y) point -> we need (x+sx, y+sy).
+ painter.drawPixmap( x + sx, y + sy, m_cachedPixmap, sx, sy, sw, sh );
+ }
+}
+
+bool KoPictureImage::loadData(const QByteArray& array, const QString& /* extension*/ )
+{
+ m_rawData=array;
+ // Second, create the original image
+ QBuffer buffer(m_rawData);
+ buffer.open(IO_ReadWrite);
+ QImageIO imageIO(&buffer,NULL);
+
+ if (!imageIO.read())
+ {
+ buffer.close();
+ kdError(30003) << "Image could not be loaded!" << endl;
+ return false;
+ }
+ buffer.close();
+ m_originalImage=imageIO.image();
+
+ return true;
+}
+
+bool KoPictureImage::save(QIODevice* io) const
+{
+ kdDebug() << k_funcinfo << "writing raw data. size=" << m_rawData.size() << endl;
+ // We save the raw data, to avoid damaging the file by many load/save cycles (especially for JPEG)
+ Q_ULONG size=io->writeBlock(m_rawData); // WARNING: writeBlock returns Q_LONG but size() Q_ULONG!
+ return (size==m_rawData.size());
+}
+
+QSize KoPictureImage::getOriginalSize(void) const
+{
+ return m_originalImage.size();
+}
+
+QPixmap KoPictureImage::generatePixmap(const QSize& size, bool smoothScale)
+{
+ scaleAndCreatePixmap(size,!smoothScale);
+ return m_cachedPixmap;
+}
+
+QString KoPictureImage::getMimeType(const QString& extension) const
+{
+ QString fileName("/tmp/temp.");
+ fileName+=extension;
+ // Find the mimetype only by the extension, not by file content (as the file is empty!)
+ const QString mimetype( KMimeType::findByPath( fileName, 0 ,true )->name() );
+ // ### TODO: use KMimeType::findByContent (but then the mimetype probably need to be cached)
+ kdDebug(30003) << "Image is mime type: " << mimetype << endl;
+ return mimetype;
+}
+
+QDragObject* KoPictureImage::dragObject( QWidget *dragSource, const char *name )
+{
+ return new QImageDrag( m_originalImage, dragSource, name );
+}
+
+QImage KoPictureImage::generateImage(const QSize& size)
+{
+ // We do not cache the image, as we will seldom need it again.
+ return m_originalImage.smoothScale( size );
+}
+
+void KoPictureImage::clearCache(void)
+{
+ m_cachedPixmap.resize(0, 0);
+ m_cacheIsInFastMode=true;
+ m_cachedSize=QSize();
+}
diff --git a/lib/kofficecore/KoPictureImage.h b/lib/kofficecore/KoPictureImage.h
new file mode 100644
index 00000000..98d7d588
--- /dev/null
+++ b/lib/kofficecore/KoPictureImage.h
@@ -0,0 +1,123 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
+
+ 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 __koPictureImage_h__
+#define __koPictureImage_h__
+
+#include "KoPictureBase.h"
+#include <qstring.h>
+
+class KoPictureImagePrivate;
+// TODO: fix documentation
+
+/**
+ * @internal
+ * KoPictureImage is a container class for a QImage-based picture
+ * \todo remove private class, as the header is not installed
+ */
+class KoPictureImage : public KoPictureBase
+{
+public:
+ /**
+ * Default constructor.
+ */
+ KoPictureImage();
+
+ /**
+ * Destructor.
+ */
+ virtual ~KoPictureImage();
+
+ KoPictureType::Type getType(void) const;
+
+ KoPictureBase* newCopy(void) const;
+
+ /**
+ * Returns true if the picture is null.
+ */
+ virtual bool isNull(void) const;
+
+ /**
+ * @brief Draw the image in a painter.
+ *
+ * No, this isn't as simple as painter.drawPixmap().
+ * This method ensures that the best quality is used when printing, scaling the painter.
+ *
+ * The parameter @p fastMode allows the picture to be re-sized and drawn quicker if possible
+ *
+ * The parameters @p width, @p height define the desired size for the image
+ * Note that the image is being scaled to that size using scale() - except when printing.
+ * This avoids scaling the image at each paint event.
+ *
+ * The other parameters are very similar to QPainter::drawPixmap :
+ * (@p x, @p y) define the position in the painter,
+ * (@p sx, @p sy) specify the top-left point in pixmap that is to be drawn. The default is (0, 0).
+ * (@p sw, @p sh) specify the size of the pixmap that is to be drawn. The default, (-1, -1), means all the way to the bottom
+ * right of the pixmap.
+ */
+ virtual void draw(QPainter& painter, int x, int y, int width, int height, int sx = 0, int sy = 0, int sw = -1, int sh = -1, bool fastMode = false);
+
+ virtual QDragObject* dragObject( QWidget *dragSource = 0L, const char *name = 0L );
+
+ virtual bool loadData(const QByteArray& array, const QString& extension);
+
+ virtual bool save(QIODevice* io) const;
+
+ virtual QSize getOriginalSize(void) const;
+
+ virtual QPixmap generatePixmap(const QSize& size, bool smoothScale = false);
+
+ virtual QString getMimeType(const QString& extension) const;
+
+ /**
+ * Generate a QImage
+ * (always in slow mode)
+ */
+ virtual QImage generateImage(const QSize& size);
+
+ virtual bool hasAlphaBuffer() const
+ { return m_originalImage.hasAlphaBuffer(); }
+
+ virtual void setAlphaBuffer(bool enable)
+ { m_originalImage.setAlphaBuffer(enable); }
+
+ virtual QImage createAlphaMask(int conversion_flags = 0) const
+ { return m_originalImage.createAlphaMask(conversion_flags); }
+
+ virtual void clearCache(void);
+
+protected:
+ QPixmap getPixmap(QImage& image);
+ void scaleAndCreatePixmap(const QSize& size, bool fastMode=false);
+
+private:
+ QImage m_originalImage; ///< Image as Qimage
+ QByteArray m_rawData; ///< Binary copy of the loaded image file
+ QPixmap m_cachedPixmap; ///< Cached pixmap
+ QSize m_cachedSize; ///< size of the currently cached pixmap @see m_cachedPixmap
+ /**
+ * true, if the last cached image was done using fast mode.
+ * false, if the last cached image was done using slow mode.
+ */
+ bool m_cacheIsInFastMode;
+ class Private;
+ Private* d;
+};
+
+#endif /* __koPictureImage_h__ */
diff --git a/lib/kofficecore/KoPictureKey.cpp b/lib/kofficecore/KoPictureKey.cpp
new file mode 100644
index 00000000..ef60cfed
--- /dev/null
+++ b/lib/kofficecore/KoPictureKey.cpp
@@ -0,0 +1,151 @@
+
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright 2002 Nicolas GOUTTE <goutte@kde.org>
+
+ 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.
+*/
+
+#include <qdatetime.h>
+#include <qfileinfo.h>
+#include <qdom.h>
+
+#include <kdebug.h>
+
+#include "KoPictureKey.h"
+
+static void resetDateTimeToEpoch(QDateTime& dt)
+{
+ // set the time point to 1970-01-01
+ dt.setDate(QDate(1970,1,1));
+ dt.setTime(QTime(0,0));
+ // Note: we cannot use QDateTime;;setTime_t as it makes a local time correction! (### TODO: not true anymore with recent Qt versions)
+}
+
+KoPictureKey::KoPictureKey()
+{
+ resetDateTimeToEpoch(m_lastModified);
+}
+
+KoPictureKey::KoPictureKey( const QString &fn, const QDateTime &mod )
+ : m_filename( fn ), m_lastModified( mod )
+{
+ if (!m_lastModified.isValid())
+ {
+ // As we have an invalid date, set the time point to 1970-01-01
+ resetDateTimeToEpoch(m_lastModified);
+ }
+}
+
+KoPictureKey::KoPictureKey( const QString &fn )
+ : m_filename( fn )
+{
+ resetDateTimeToEpoch(m_lastModified);
+}
+
+KoPictureKey::KoPictureKey( const KoPictureKey &key )
+ : m_filename( key.m_filename ), m_lastModified( key.m_lastModified )
+{
+}
+
+KoPictureKey& KoPictureKey::operator=( const KoPictureKey &key )
+{
+ m_filename = key.m_filename;
+ m_lastModified = key.m_lastModified;
+ return *this;
+}
+
+bool KoPictureKey::operator==( const KoPictureKey &key ) const
+{
+ return ( key.m_filename == m_filename &&
+ key.m_lastModified == m_lastModified );
+}
+
+bool KoPictureKey::operator<( const KoPictureKey &key ) const
+{
+ return key.toString() < toString();
+}
+
+void KoPictureKey::saveAttributes( QDomElement &elem ) const
+{
+ QDate date = m_lastModified.date();
+ QTime time = m_lastModified.time();
+ elem.setAttribute( "filename", m_filename );
+ elem.setAttribute( "year", date.year() );
+ elem.setAttribute( "month", date.month() );
+ elem.setAttribute( "day", date.day() );
+ elem.setAttribute( "hour", time.hour() );
+ elem.setAttribute( "minute", time.minute() );
+ elem.setAttribute( "second", time.second() );
+ elem.setAttribute( "msec", time.msec() );
+}
+
+void KoPictureKey::loadAttributes( const QDomElement &elem )
+{
+ // Default date/time is the *nix epoch: 1970-01-01 00:00:00,000
+ int year=1970, month=1, day=1;
+ int hour=0, minute=0, second=0, msec=0; // We must initialize to zero, as not all compilers are C99-compliant
+
+ if( elem.hasAttribute( "key" ) )
+ {
+ // Note: the old KWord format (up to 1.1-beta2) has no date/time
+ m_filename=elem.attribute( "key" );
+ }
+ else
+ {
+ // ### TODO: document which format is this?
+ m_filename=elem.attribute( "filename" );
+ }
+
+ if( elem.hasAttribute( "year" ) )
+ year=elem.attribute( "year" ).toInt();
+ if( elem.hasAttribute( "month" ) )
+ month=elem.attribute( "month" ).toInt();
+ if( elem.hasAttribute( "day" ) )
+ day=elem.attribute( "day" ).toInt();
+ if( elem.hasAttribute( "hour" ) )
+ hour=elem.attribute( "hour" ).toInt();
+ if( elem.hasAttribute( "minute" ) )
+ minute=elem.attribute( "minute" ).toInt();
+ if( elem.hasAttribute( "second" ) )
+ second=elem.attribute( "second" ).toInt();
+ if( elem.hasAttribute( "msec" ) )
+ msec=elem.attribute( "msec" ).toInt();
+
+ m_lastModified.setDate( QDate( year, month, day ) );
+ m_lastModified.setTime( QTime( hour, minute, second, msec ) );
+
+ if (!m_lastModified.isValid())
+ {
+ // If the date/time is not valid, make it valid by force!
+ kdWarning(30003) << "Correcting invalid date/time: " << toString() << " (in KoPictureKey::loadAttributes)" << endl;
+ resetDateTimeToEpoch(m_lastModified);
+ }
+}
+
+QString KoPictureKey::toString() const
+{
+ // We do not use the default QDateTime::toString has it does not show microseconds
+ return QString::fromLatin1( "%1 %2" )
+ .arg( m_filename, m_lastModified.toString("yyyy-MM-dd hh:mm:ss.zzz") );
+}
+
+void KoPictureKey::setKeyFromFile (const QString& filename)
+{
+ QFileInfo inf(filename);
+ m_filename = filename;
+ m_lastModified = inf.lastModified();
+}
diff --git a/lib/kofficecore/KoPictureKey.h b/lib/kofficecore/KoPictureKey.h
new file mode 100644
index 00000000..d336215b
--- /dev/null
+++ b/lib/kofficecore/KoPictureKey.h
@@ -0,0 +1,154 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002, 2004 Nicolas GOUTTE <goutte@kde.org>
+
+ 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 __koPictureKey_h__
+#define __koPictureKey_h__
+
+#include <qstring.h>
+#include <qdatetime.h>
+#include <koffice_export.h>
+/**
+ * \file koPictureKey.h
+ * \todo correct documentation (for example: sed "s/image/picture/g")
+ */
+
+class QDomElement;
+
+namespace KoPictureType
+{
+ /**
+ * QPicture version used by KoPictureClipart
+ *
+ * Possible values:
+ * \li 3 for Qt 2.1.x and later Qt 2.x
+ * \li 4 for Qt 3.0
+ * \li 5 for Qt 3.1 and later Qt 3.x
+ * \li -1 for current Qt
+ */
+ const int formatVersionQPicture=-1;
+
+ enum Type
+ {
+ TypeUnknown = 0, ///< Unknown or not-an-image @see KoPictureBase
+ TypeImage, ///< Image, QImage-based @see KoPictureImage
+ TypeEps, ///< Encapsulated Postscript @see KoPictureEps
+ TypeClipart, ///< Clipart, QPicture-based @see KoPictureClipart
+ TypeWmf ///< WMF (Windows Meta File) @see KoPictureWmf
+ };
+}
+
+/**
+ * KoPictureKey is the structure describing a picture in a unique way.
+ * It currently includes the original path to the picture and the modification
+ * date.
+ *
+ * @short Structure describing a picture on disk
+ *
+ * @note We use the *nix epoch (1970-01-01) as a time base because it is a valid date.
+ * That way we do not depend on a behaviour of the current QDateTime that might change in future versions of Qt
+ * and we are also nice to non-Qt programs wanting to read KOffice's files.
+ *
+ * @note This behaviour is also needed for re-saving KWord files having \<FORMAT id="2"\>. When saving again,
+ * these files get a \<KEY\> element as child of \<PIXMAPS\> but not one as child of \<FORMAT\> and \<IMAGE\>.
+ * Therefore we need to be careful that the key remains compatible to default values
+ * (another good reason for the *NIX epoch)
+ *
+ * @note In case of a remote path, the "original path" is the name of the temporary file that was
+ * used to download the file.
+ */
+class KOFFICEUI_EXPORT KoPictureKey
+{
+public:
+ /**
+ * Default constructor. Creates a null key
+ */
+ KoPictureKey();
+
+ /**
+ * @brief Constructs a key, from a filename and a modification date
+ *
+ * Storing the modification date as part of the key allows the user
+ * to update the file and import it into the application again, without
+ * the application reusing the old copy from the collection.
+ */
+ KoPictureKey( const QString &fn, const QDateTime &mod );
+
+ /**
+ * Constructs a key from a filename
+ * @note The modification date is set to 1970-01-01
+ */
+ KoPictureKey( const QString &fn );
+
+ /**
+ * Copy constructor
+ */
+ KoPictureKey( const KoPictureKey &key );
+
+ /**
+ * Assignment operator
+ */
+ KoPictureKey &operator=( const KoPictureKey &key );
+
+ /**
+ * Comparison operator
+ */
+ bool operator==( const KoPictureKey &key ) const;
+
+ /**
+ * Comparison operator
+ * @note Used for sorting in the collection's map
+ */
+ bool operator<( const KoPictureKey &key ) const;
+
+ /**
+ * Convert this key into a string representation of it
+ */
+ QString toString() const;
+
+ /**
+ * Save this key in XML (as %KOffice 1.3)
+ */
+ void saveAttributes( QDomElement &elem ) const;
+
+ /**
+ * Load this key from XML (as %KOffice 1.3)
+ */
+ void loadAttributes( const QDomElement &elem );
+
+ /**
+ * First part of the key: the filename
+ */
+ QString filename() const { return m_filename; }
+
+ /**
+ * Second part of the key: the modification date
+ */
+ QDateTime lastModified() const { return m_lastModified; }
+
+ /**
+ * Sets the key according to @p filename, including modification time
+ */
+ void setKeyFromFile (const QString& filename);
+
+protected:
+ QString m_filename;
+ QDateTime m_lastModified;
+};
+
+#endif /* __koPictureKey_h__ */
diff --git a/lib/kofficecore/KoPictureShared.cpp b/lib/kofficecore/KoPictureShared.cpp
new file mode 100644
index 00000000..5c2ff2a2
--- /dev/null
+++ b/lib/kofficecore/KoPictureShared.cpp
@@ -0,0 +1,535 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
+
+ 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.
+*/
+
+#include <qpainter.h>
+#include <qfile.h>
+
+#include <kdebug.h>
+#include <kurl.h>
+#include <kfilterdev.h>
+#include <kio/netaccess.h>
+
+#include "KoPictureKey.h"
+#include "KoPictureBase.h"
+#include "KoPictureImage.h"
+#include "KoPictureEps.h"
+#include "KoPictureClipart.h"
+#include "KoPictureWmf.h"
+#include "KoPictureShared.h"
+#include <kmdcodec.h>
+
+
+KoPictureShared::KoPictureShared(void) : m_base(NULL)
+{
+}
+
+void KoPictureShared::assignPictureId( uint _id)
+{
+ m_pictureId = _id;
+}
+
+QString KoPictureShared::uniquePictureId() const
+{
+ return "Pictures"+ QString::number(m_pictureId);
+}
+
+KoPictureShared::~KoPictureShared(void)
+{
+ delete m_base;
+}
+
+KoPictureShared::KoPictureShared(const KoPictureShared &other)
+ : QShared() // Some compilers want it explicitly!
+{
+ // We need to use newCopy, because we want a real copy, not just a copy of the part of KoPictureBase
+ if (other.m_base)
+ m_base=other.m_base->newCopy();
+ else
+ m_base=NULL;
+}
+
+KoPictureShared& KoPictureShared::operator=( const KoPictureShared &other )
+{
+ clear();
+ kdDebug(30003) << "KoPictureShared::= before" << endl;
+ if (other.m_base)
+ m_base=other.m_base->newCopy();
+ kdDebug(30003) << "KoPictureShared::= after" << endl;
+ return *this;
+}
+
+KoPictureType::Type KoPictureShared::getType(void) const
+{
+ if (m_base)
+ return m_base->getType();
+ return KoPictureType::TypeUnknown;
+}
+
+bool KoPictureShared::isNull(void) const
+{
+ if (m_base)
+ return m_base->isNull();
+ return true;
+}
+
+void KoPictureShared::draw(QPainter& painter, int x, int y, int width, int height, int sx, int sy, int sw, int sh, bool fastMode)
+{
+ if (m_base)
+ m_base->draw(painter, x, y, width, height, sx, sy, sw, sh, fastMode);
+ else
+ {
+ // Draw a red box (easier DEBUG)
+ kdWarning(30003) << "Drawing red rectangle! (KoPictureShared::draw)" << endl;
+ painter.save();
+ painter.setBrush(QColor(255,0,0));
+ painter.drawRect(x,y,width,height);
+ painter.restore();
+ }
+}
+
+bool KoPictureShared::loadWmf(QIODevice* io)
+{
+ kdDebug(30003) << "KoPictureShared::loadWmf" << endl;
+ if (!io)
+ {
+ kdError(30003) << "No QIODevice!" << endl;
+ return false;
+ }
+
+ clear();
+
+ // The extension .wmf was used (KOffice 1.1.x) for QPicture files
+ // For an extern file or in the storage, .wmf can mean a real Windows Meta File.
+
+ QByteArray array ( io->readAll() );
+
+ if ((array[0]=='Q') && (array[1]=='P') &&(array[2]=='I') && (array[3]=='C'))
+ {
+ m_base=new KoPictureClipart();
+ setExtension("qpic");
+ }
+ else
+ {
+ m_base=new KoPictureWmf();
+ setExtension("wmf");
+ }
+ return m_base->loadData(array, m_extension);
+}
+
+bool KoPictureShared::loadTmp(QIODevice* io)
+// We have a temp file, probably from a downloaded file
+// We must check the file type
+{
+ kdDebug(30003) << "KoPictureShared::loadTmp" << endl;
+ if (!io)
+ {
+ kdError(30003) << "No QIODevice!" << endl;
+ return false;
+ }
+
+ // The extension .wmf was used (KOffice 1.1.x) for QPicture files
+ // For an extern file or in the storage, .wmf can mean a real Windows Meta File.
+
+ QByteArray array ( io->readAll() );
+ return identifyAndLoad( array );
+}
+
+bool KoPictureShared::identifyAndLoad( QByteArray array )
+{
+ if ( array.size() < 5 )
+ {
+ kdError(30003) << "Picture is less than 5 bytes long!" << endl;
+ return false;
+ }
+
+ QString strExtension;
+ bool flag=false;
+
+ // Try to find the file type by comparing magic on the first few bytes!
+ // ### TODO: could not QImageIO::imageFormat do it too? (At least most of them?)
+ if ((array[0]==char(0x89)) && (array[1]=='P') &&(array[2]=='N') && (array[3]=='G'))
+ {
+ strExtension="png";
+ }
+ else if ((array[0]==char(0xff)) && (array[1]==char(0xd8)) &&(array[2]==char(0xff)) && (array[3]==char(0xe0)))
+ {
+ strExtension="jpeg";
+ }
+ else if ((array[0]=='B') && (array[1]=='M'))
+ {
+ strExtension="bmp";
+ }
+ else if ((array[0]==char(0xd7)) && (array[1]==char(0xcd)) &&(array[2]==char(0xc6)) && (array[3]==char(0x9a)))
+ {
+ strExtension="wmf";
+ }
+ else if ((array[0]=='<') && (array[1]=='?') && ( array[2]=='x' ) && (array[3]=='m') && ( array[4]=='l' ) )
+ {
+ strExtension="svg";
+ }
+ else if ((array[0]=='Q') && (array[1]=='P') &&(array[2]=='I') && (array[3]=='C'))
+ {
+ strExtension="qpic";
+ }
+ else if ((array[0]=='%') && (array[1]=='!') &&(array[2]=='P') && (array[3]=='S'))
+ {
+ strExtension="eps";
+ }
+ else if ((array[0]==char(0xc5)) && (array[1]==char(0xd0)) && (array[2]==char(0xd3)) && (array[3]==char(0xc6)))
+ {
+ // So called "MS-DOS EPS file"
+ strExtension="eps";
+ }
+ else if ((array[0]=='G') && (array[1]=='I') && (array[2]=='F') && (array[3]=='8'))
+ {
+ // GIF (87a or 89a)
+ strExtension="gif";
+ }
+ else if ( ( array[0] == char( 0037 ) ) && ( array[1] == char( 0213 ) ) )
+ {
+ // Gzip
+ QBuffer buffer(array);
+ buffer.open(IO_ReadOnly);
+
+ const bool flag = loadCompressed( &buffer, "application/x-gzip", "tmp" );
+ buffer.close();
+ return flag;
+ }
+ else if ( ( array[0] == 'B' ) && ( array[1] == 'Z' ) && ( array[2] == 'h') )
+ {
+ // BZip2
+ QBuffer buffer(array);
+ buffer.open(IO_ReadOnly);
+ const bool flag = loadCompressed( &buffer, "application/x-bzip2", "tmp" );
+ buffer.close();
+ return flag;
+ }
+ else
+ {
+ kdDebug(30003) << "Cannot identify the type of temp file!"
+ << " Trying to convert to PNG! (in KoPictureShared::loadTmp" << endl;
+
+ // Do not trust QBuffer and do not work directly on the QByteArray array
+ // DF: It would be faster to work on array here, and to create a completely
+ // different QBuffer for the writing code!
+ QBuffer buf( array.copy() );
+ if (!buf.open(IO_ReadOnly))
+ {
+ kdError(30003) << "Could not open read buffer!" << endl;
+ return false;
+ }
+
+ QImageIO imageIO(&buf,NULL);
+
+ if (!imageIO.read())
+ {
+ kdError(30003) << "Could not read image!" << endl;
+ return false;
+ }
+
+ buf.close();
+
+ if ( !buf.open( IO_WriteOnly | IO_Truncate ) )
+ {
+ kdError(30003) << "Could not open write buffer!" << endl;
+ return false;
+ }
+
+ imageIO.setIODevice(&buf);
+ imageIO.setFormat("PNG");
+
+ if (!imageIO.write())
+ {
+ kdError(30003) << "Could not write converted image!" << endl;
+ return false;
+ }
+ buf.close();
+
+ array = buf.buffer();
+
+ strExtension="png";
+ }
+
+ kdDebug(30003) << "Temp file considered to be " << strExtension << endl;
+
+ clearAndSetMode(strExtension);
+ if (m_base)
+ flag = m_base->loadData(array,strExtension);
+ setExtension(strExtension);
+
+ return flag;
+}
+
+
+
+bool KoPictureShared::loadXpm(QIODevice* io)
+{
+ kdDebug(30003) << "KoPictureShared::loadXpm" << endl;
+ if (!io)
+ {
+ kdError(30003) << "No QIODevice!" << endl;
+ return false;
+ }
+
+ clear();
+
+ // Old KPresenter XPM files have char(1) instead of some "
+ // Therefore we need to treat XPM separately
+
+ QByteArray array=io->readAll();
+
+ // As XPM files are normally only ASCII files, we can replace it without problems
+
+ int pos=0;
+
+ while ((pos=array.find(char(1),pos))!=-1)
+ {
+ array[pos]='"';
+ }
+
+ // Now that the XPM file is corrected, we need to load it.
+
+ m_base=new KoPictureImage();
+
+ QBuffer buffer(array);
+ bool check = m_base->load(&buffer,"xpm");
+ setExtension("xpm");
+ return check;
+}
+
+bool KoPictureShared::save(QIODevice* io) const
+{
+ if (!io)
+ return false;
+ if (m_base)
+ return m_base->save(io);
+ return false;
+}
+
+bool KoPictureShared::saveAsBase64( KoXmlWriter& writer ) const
+{
+ if ( m_base )
+ m_base->saveAsBase64( writer );
+ return false;
+}
+
+void KoPictureShared::clear(void)
+{
+ // Clear does not reset the key m_key!
+ delete m_base;
+ m_base=NULL;
+}
+
+void KoPictureShared::clearAndSetMode(const QString& newMode)
+{
+ delete m_base;
+ m_base=NULL;
+
+ const QString mode=newMode.lower();
+
+ if ((mode=="svg") || (mode=="qpic"))
+ {
+ m_base=new KoPictureClipart();
+ }
+ else if (mode=="wmf")
+ {
+ m_base=new KoPictureWmf();
+ }
+ else if ( (mode=="eps") || (mode=="epsi") || (mode=="epsf") )
+ {
+ m_base=new KoPictureEps();
+ }
+ else
+ { // TODO: test if QImageIO really knows the file format
+ m_base=new KoPictureImage();
+ }
+}
+
+QString KoPictureShared::getExtension(void) const
+{
+ return m_extension;
+}
+
+void KoPictureShared::setExtension(const QString& extension)
+{
+ m_extension = extension;
+}
+
+QString KoPictureShared::getMimeType(void) const
+{
+ if (m_base)
+ return m_base->getMimeType(m_extension);
+ return QString(NULL_MIME_TYPE);
+}
+
+
+bool KoPictureShared::loadFromBase64( const QCString& str )
+{
+ clear();
+ QByteArray data;
+ KCodecs::base64Decode( str, data );
+ return identifyAndLoad( data );
+}
+
+bool KoPictureShared::load(QIODevice* io, const QString& extension)
+{
+ kdDebug(30003) << "KoPictureShared::load(QIODevice*, const QString&) " << extension << endl;
+ bool flag=false;
+ QString ext(extension.lower());
+ if (ext=="wmf")
+ flag=loadWmf(io);
+ else if (ext=="tmp") // ### TODO: also remote scripts need this, don't they?
+ flag=loadTmp(io);
+ else if ( ext == "bz2" )
+ {
+ flag = loadCompressed( io, "application/x-bzip2", "tmp" );
+ }
+ else if ( ext == "gz" )
+ {
+ flag = loadCompressed( io, "application/x-gzip", "tmp" );
+ }
+ else if ( ext == "svgz" )
+ {
+ flag = loadCompressed( io, "application/x-gzip", "svg" );
+ }
+ else
+ {
+ clearAndSetMode(ext);
+ if (m_base)
+ flag = m_base->load(io, ext);
+ setExtension(ext);
+ }
+ if (!flag)
+ {
+ kdError(30003) << "File was not loaded! (KoPictureShared::load)" << endl;
+ }
+ return flag;
+}
+
+bool KoPictureShared::loadFromFile(const QString& fileName)
+{
+ kdDebug(30003) << "KoPictureShared::loadFromFile " << fileName << endl;
+ if ( fileName.isEmpty() )
+ {
+ kdError(30003) << "Cannot load file with empty name!" << endl;
+ return false;
+ }
+ QFile file(fileName);
+ if (!file.open(IO_ReadOnly))
+ return false;
+
+ bool flag = false;
+ const int pos=fileName.findRev('.');
+ if (pos==-1)
+ {
+ kdDebug(30003) << "File with no extension!" << endl;
+ // As we have no extension, consider it like a temporary file
+ flag = loadTmp( &file );
+ }
+ else
+ {
+ const QString extension( fileName.mid( pos+1 ) );
+ // ### TODO: check if the extension if gz or bz2 and find the previous extension
+ flag = load( &file, extension );
+ }
+ file.close();
+ return flag;
+}
+
+QSize KoPictureShared::getOriginalSize(void) const
+{
+ if (m_base)
+ return m_base->getOriginalSize();
+ return QSize(0,0);
+}
+
+QPixmap KoPictureShared::generatePixmap(const QSize& size, bool smoothScale)
+{
+ if (m_base)
+ return m_base->generatePixmap(size, smoothScale);
+ return QPixmap();
+}
+
+QDragObject* KoPictureShared::dragObject( QWidget *dragSource, const char *name )
+{
+ if (m_base)
+ return m_base->dragObject( dragSource, name );
+ return 0L;
+}
+
+QImage KoPictureShared::generateImage(const QSize& size)
+{
+ if (m_base)
+ return m_base->generateImage( size );
+ return QImage();
+}
+
+bool KoPictureShared::hasAlphaBuffer() const
+{
+ if (m_base)
+ return m_base->hasAlphaBuffer();
+ return false;
+}
+
+void KoPictureShared::setAlphaBuffer(bool enable)
+{
+ if (m_base)
+ m_base->setAlphaBuffer(enable);
+}
+
+QImage KoPictureShared::createAlphaMask(int conversion_flags) const
+{
+ if (m_base)
+ return m_base->createAlphaMask(conversion_flags);
+ return QImage();
+}
+
+void KoPictureShared::clearCache(void)
+{
+ if (m_base)
+ m_base->clearCache();
+}
+
+bool KoPictureShared::loadCompressed( QIODevice* io, const QString& mimeType, const QString& extension )
+{
+ // ### TODO: check that we do not have an endless recursion
+ QIODevice* in = KFilterDev::device( io, mimeType, false);
+
+ if ( !in )
+ {
+ kdError(30003) << "Cannot create device for uncompressing! Aborting!" << endl;
+ return false;
+ }
+
+
+ if ( !in->open( IO_ReadOnly ) )
+ {
+ kdError(30003) << "Cannot open file for uncompressing! Aborting!" << endl;
+ delete in;
+ return false;
+ }
+
+ const bool flag = load( in, extension );
+
+ in->close();
+ delete in;
+
+ return flag;
+}
diff --git a/lib/kofficecore/KoPictureShared.h b/lib/kofficecore/KoPictureShared.h
new file mode 100644
index 00000000..da286368
--- /dev/null
+++ b/lib/kofficecore/KoPictureShared.h
@@ -0,0 +1,214 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
+
+ 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 __koPictureShared_h__
+#define __koPictureShared_h__
+
+#include <qshared.h>
+#include <qstring.h>
+#include <qiodevice.h>
+#include <qpixmap.h>
+
+#include "KoPictureKey.h"
+
+class KoXmlWriter;
+class QPainter;
+class QSize;
+
+class KURL;
+
+class KoPictureBase;
+
+/**
+ * @internal
+ * KoPictureShared is the class that contains the shared part for KoPicture
+ *
+ * @warning As with all QShared objects, the sharing is neither automatic nor transparent!
+ */
+class KoPictureShared : public QShared
+{
+public:
+ /**
+ * Default constructor.
+ */
+ KoPictureShared(void);
+
+ /**
+ * Destructor.
+ */
+ ~KoPictureShared(void);
+
+ /**
+ * @brief Copy constructor
+ *
+ * This makes a deep copy. Do not use if you want to share!
+ */
+ KoPictureShared(const KoPictureShared &other);
+
+ /**
+ * @brief Assignment operator
+ *
+ * This makes a deep copy. Do not use if you want to share!
+ */
+ KoPictureShared& operator=(const KoPictureShared& other);
+
+ KoPictureType::Type getType(void) const;
+
+ /**
+ * Returns true if the picture is null.
+ */
+ bool isNull(void) const;
+
+ /**
+ * @brief Draw the image in a painter.
+ *
+ * The parameter @p fastMode allows the picture to be re-sized and drawn quicker if possible
+ *
+ * The parameters @p width, @p height define the desired size for the picture
+ *
+ * The other parameters are very similar to QPainter::drawPixmap :
+ * (@p x, @p y) define the position in the painter,
+ * (@p sx, @p sy) specify the top-left point in pixmap that is to be drawn. The default is (0, 0).
+ * (@p sw, @p sh) specify the size of the pixmap that is to be drawn. The default, (-1, -1), means all the way to the bottom
+ * right of the pixmap.
+ */
+ void draw(QPainter& painter, int x, int y, int width, int height, int sx = 0, int sy = 0, int sw = -1, int sh = -1, bool fastMode = false);
+
+ /**
+ * Create a dragobject containing this picture.
+ * @param dragSource must be 0 when copying to the clipboard
+ * @return 0L if the picture is null!
+ */
+ QDragObject* dragObject( QWidget *dragSource = 0L, const char *name = 0L );
+
+ bool load(QIODevice* io, const QString& extension);
+ bool loadFromBase64( const QCString& str );
+
+ /**
+ * Save picture into a QIODevice
+ * @param io QIODevice used for saving
+ */
+ bool save(QIODevice* io) const;
+
+ /**
+ * OASIS FlatXML support:
+ * Save picture as base64-encoded data into an XML writer.
+ */
+ bool saveAsBase64( KoXmlWriter& writer ) const;
+
+ void setExtension(const QString& extension);
+
+ QString getExtension(void) const;
+
+ QSize getOriginalSize(void) const;
+
+ /**
+ * Clear and set the mode of this KoPictureShared
+ *
+ * @param newMode file extension (like "png") giving the wanted mode
+ */
+ void clearAndSetMode(const QString& newMode);
+
+ /**
+ * Reset the KoPictureShared (but not the key!)
+ */
+ void clear(void);
+
+ /**
+ * Load a file
+ *
+ * @param fileName the name of the file to load
+ */
+ bool loadFromFile(const QString& fileName);
+
+ /**
+ * Load a potentially broken XPM file (for KPresenter)
+ */
+ bool loadXpm(QIODevice* io);
+
+ /**
+ * @deprecated
+ * Returns a QPixmap from an image
+ *
+ * @param size the wanted size for the QPixmap
+ */
+ QPixmap generatePixmap(const QSize& size, bool smoothScale = false);
+
+ QString getMimeType(void) const;
+
+ /**
+ * Generate a QImage
+ *
+ * (always in slow mode)
+ *
+ * @param size the wanted size for the QImage
+ */
+ QImage generateImage(const QSize& size);
+
+ bool hasAlphaBuffer() const;
+ void setAlphaBuffer(bool enable);
+ QImage createAlphaMask(int conversion_flags = 0) const;
+
+ /**
+ * Clear any cache
+ *
+ * It is used to avoid using too much memory
+ * especially if the application somehow caches the KoPicture too.
+ */
+ void clearCache(void);
+
+ QString uniquePictureId() const;
+ void assignPictureId( uint _id);
+
+protected:
+ /**
+ * @internal
+ * Load a WMF file
+ * \note In %KOffice 1.1, a .wmf file was a QPicture file
+ */
+ bool loadWmf(QIODevice* io);
+
+ /**
+ * @internal
+ * Loads a temporary file, probably from a downloaded file
+ */
+ bool loadTmp(QIODevice* io);
+
+ /// Find type of image, create base accordingly, and load data
+ bool identifyAndLoad( QByteArray data );
+
+ /**
+ * @internal
+ * Loads a compressed file
+ *
+ * @warning risk of endless recurision, be careful when it is called from @see load
+ *
+ * @param io QIODevice of the compressed file/strea,
+ * @param mimeType mimetype of the (de-)compressor
+ * @param extension extension of the uncompressed file
+ */
+ bool loadCompressed( QIODevice* io, const QString& mimeType, const QString& extension );
+
+protected:
+ KoPictureBase* m_base;
+ QString m_extension;
+ uint m_pictureId;
+};
+
+#endif /* __koPictureShared_h__ */
diff --git a/lib/kofficecore/KoPictureWmf.cpp b/lib/kofficecore/KoPictureWmf.cpp
new file mode 100644
index 00000000..96872af7
--- /dev/null
+++ b/lib/kofficecore/KoPictureWmf.cpp
@@ -0,0 +1,137 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (c) 2001 David Faure <faure@kde.org>
+ Copyright (C) 2002 Nicolas GOUTTE <goutte@kde.org>
+
+ 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.
+*/
+
+#include <qbuffer.h>
+#include <qpainter.h>
+#include <qpicture.h>
+#include <qpixmap.h>
+
+#include <kdebug.h>
+#include <kdeversion.h>
+#if ! KDE_IS_VERSION( 3,1,90 )
+#include <kdebugclasses.h>
+#endif
+
+#include <kowmfpaint.h>
+#include "KoPictureKey.h"
+#include "KoPictureBase.h"
+#include "KoPictureWmf.h"
+
+KoPictureWmf::KoPictureWmf(void) : m_clipart(KoPictureType::formatVersionQPicture)
+{
+}
+
+KoPictureWmf::~KoPictureWmf(void)
+{
+}
+
+KoPictureBase* KoPictureWmf::newCopy(void) const
+{
+ return new KoPictureWmf(*this);
+}
+
+KoPictureType::Type KoPictureWmf::getType(void) const
+{
+ return KoPictureType::TypeWmf;
+}
+
+bool KoPictureWmf::isNull(void) const
+{
+ return m_clipart.isNull();
+}
+
+void KoPictureWmf::drawQPicture(QPicture& clipart, QPainter& painter,
+ int x, int y, int width, int height, int sx, int sy, int sw, int sh)
+{
+ kdDebug(30003) << "Drawing KoPictureWmf " << this << endl;
+ kdDebug(30003) << " x=" << x << " y=" << y << " width=" << width << " height=" << height << endl;
+ kdDebug(30003) << " sx=" << sx << " sy=" << sy << " sw=" << sw << " sh=" << sh << endl;
+ painter.save();
+ // Thanks to Harri, Qt3 makes it much easier than Qt2 ;)
+ QRect br = clipart.boundingRect();
+ kdDebug(30003) << " Bounding rect. " << br << endl;
+
+ painter.translate(x,y); // Translating must be done before scaling!
+ if ( br.width() && br.height() )
+ painter.scale(double(width)/double(br.width()),double(height)/double(br.height()));
+ else
+ kdWarning(30003) << "Null bounding rectangle: " << br.width() << " x " << br.height() << endl;
+ painter.drawPicture(0,0,clipart);
+ painter.restore();
+}
+
+void KoPictureWmf::draw(QPainter& painter, int x, int y, int width, int height, int sx, int sy, int sw, int sh, bool /*fastMode*/)
+{
+ drawQPicture(m_clipart, painter, x, y, width, height, sx, sy, sw, sh);
+}
+
+bool KoPictureWmf::loadData(const QByteArray& array, const QString& /* extension */)
+{
+ // Second, create the original clipart
+ kdDebug(30003) << "Trying to load clipart... (Size:" << array.size() << ")" << endl;
+ m_rawData=array;
+
+ KoWmfPaint wmf;
+ if (!wmf.load(array))
+ {
+ kdWarning(30003) << "Loading WMF has failed! (KoPictureWmf::load)" << endl;
+ return false;
+ }
+ m_originalSize = wmf.boundingRect().size();
+ // draw wmf file with relative coordinate
+ wmf.play(m_clipart, true);
+
+ return true;
+}
+
+bool KoPictureWmf::save(QIODevice* io) const
+{
+ // We save the raw data, as the SVG supposrt in QPicture is poor
+ Q_ULONG size=io->writeBlock(m_rawData); // WARNING: writeBlock returns Q_LONG but size() Q_ULONG!
+ return (size==m_rawData.size());
+}
+
+QSize KoPictureWmf::getOriginalSize(void) const
+{
+ return m_originalSize;
+}
+
+QPixmap KoPictureWmf::generatePixmap(const QSize& size, bool /*smoothScale*/)
+{
+ // Not sure if it works, but it worked for KoPictureFilePreviewWidget::setClipart
+ QPixmap pixmap(size);
+ QPainter p;
+
+ p.begin( &pixmap );
+ p.setBackgroundColor( Qt::white );
+ pixmap.fill( Qt::white );
+
+ if ( m_originalSize.width() && m_originalSize.height() )
+ p.scale( (double)pixmap.width() / (double)m_originalSize.width(), (double)pixmap.height() / (double)m_originalSize.height() );
+ p.drawPicture( m_clipart );
+ p.end();
+ return pixmap;
+}
+
+QString KoPictureWmf::getMimeType(const QString& /* extension */) const
+{
+ return "image/x-wmf";
+}
diff --git a/lib/kofficecore/KoPictureWmf.h b/lib/kofficecore/KoPictureWmf.h
new file mode 100644
index 00000000..f1924d9c
--- /dev/null
+++ b/lib/kofficecore/KoPictureWmf.h
@@ -0,0 +1,108 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2002 Nicolas GOUTTE <goutte@kde.org>
+
+ 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 __koPictureWmf_h__
+#define __koPictureWmf_h__
+
+#include <qstring.h>
+#include <qpicture.h>
+#include <koffice_export.h>
+
+class QPainter;
+class QSize;
+
+/**
+ * \file koPictureWmf.h
+ * \todo fix documentation
+ */
+
+/**
+ * @internal
+ * KoPictureWmf is a container class for WMF pictures.
+ */
+class KOFFICECORE_EXPORT KoPictureWmf : public KoPictureBase
+{
+public:
+ /**
+ * Default constructor.
+ */
+ KoPictureWmf();
+
+ /**
+ * Destructor.
+ */
+ virtual ~KoPictureWmf();
+
+ virtual KoPictureType::Type getType(void) const;
+
+ virtual KoPictureBase* newCopy(void) const;
+
+
+ /**
+ * Returns true if the picture is null.
+ */
+ virtual bool isNull(void) const;
+
+ /**
+ * @brief Draw the image in a painter.
+ *
+ * The parameter @p fastMode allows the picture to be re-sized and drawn quicker if possible
+ *
+ * The parameters @p width, @p height define the desired size for the image
+ * Note that the image is being scaled to that size using scale() - except when printing.
+ * This avoids scaling the image at each paint event.
+ *
+ * The other parameters are very similar to QPainter::drawPixmap :
+ * (@p x, @p y) define the position in the painter,
+ * (@p sx, @p sy) specify the top-left point in pixmap that is to be drawn. The default is (0, 0).
+ * (@p sw, @p sh) specify the size of the pixmap that is to be drawn. The default, (-1, -1), means all the way to the bottom
+ * right of the pixmap.
+ */
+ virtual void draw(QPainter& painter, int x, int y, int width, int height, int sx = 0, int sy = 0, int sw = -1, int sh = -1, bool fastMode = false);
+
+ virtual bool loadData(const QByteArray& array, const QString& extension);
+
+ /**
+ * save file
+ * @param io QIODevice used for saving
+ */
+ virtual bool save(QIODevice* io) const;
+
+ virtual QSize getOriginalSize(void) const;
+
+ virtual QPixmap generatePixmap(const QSize& size, bool smoothScale = false);
+
+ virtual QString getMimeType(const QString& extension) const;
+
+protected:
+ QPixmap getPixmap(QImage& image);
+ /**
+ * @internal
+ * Draw a QPicture
+ */
+ void drawQPicture(QPicture& clipart, QPainter& painter,
+ int x, int y, int width, int height, int sx, int sy, int sw, int sh);
+protected:
+ QPicture m_clipart;
+ QByteArray m_rawData;
+ QSize m_size;
+ QSize m_originalSize;
+};
+
+#endif /* __koPictureWmf_h__ */
diff --git a/lib/kofficecore/KoPoint.h b/lib/kofficecore/KoPoint.h
new file mode 100644
index 00000000..5f39e6c6
--- /dev/null
+++ b/lib/kofficecore/KoPoint.h
@@ -0,0 +1,128 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 David Faure <faure@kde.org>
+
+ 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 koPoint_h
+#define koPoint_h
+
+#include <qwmatrix.h>
+#include <math.h>
+
+/**
+ * A point whose coordinates are floating-point values ( "double"s ).
+ * The API isn't documented, it's a perfect mirror of QPoint.
+ */
+class KoPoint {
+
+public:
+ KoPoint() { m_x = 0; m_y = 0; }
+ KoPoint(const double &x, const double &y) : m_x(x), m_y(y) {}
+ explicit KoPoint(const QPoint & p) : m_x(p.x()), m_y(p.y()) {}
+ ~KoPoint() {}
+
+ bool operator==(const KoPoint &rhs) const { return QABS(m_x-rhs.x()) < 1E-10 && QABS(m_y-rhs.y()) < 1E-10; }
+ bool operator!=(const KoPoint &rhs) const { return QABS(m_x-rhs.x()) > 1E-10 || QABS(m_y-rhs.y()) > 1E-10; }
+
+ bool isNull() const { return m_x == 0 && m_y == 0; }
+
+ double x() const { return m_x; }
+ double y() const { return m_y; }
+ void setX(const double &x) { m_x = x; }
+ void setY(const double &y) { m_y = y; }
+
+ double &rx() { return m_x; }
+ double &ry() { return m_y; }
+
+ KoPoint &operator=(const KoPoint &rhs) { m_x = rhs.x(); m_y = rhs.y(); return *this; }
+ KoPoint &operator+=( const KoPoint &rhs ) { m_x += rhs.x(); m_y += rhs.y(); return *this; }
+ KoPoint &operator-=( const KoPoint &rhs ) { m_x -= rhs.x(); m_y -= rhs.y(); return *this; }
+ KoPoint &operator*=( const double &c ) { m_x *= c; m_y *= c; return *this; }
+
+ friend inline KoPoint operator+( const KoPoint &, const KoPoint & );
+ friend inline KoPoint operator-( const KoPoint &, const KoPoint & );
+ friend inline KoPoint operator*( const KoPoint &, const double & );
+ friend inline KoPoint operator*( const double &, const KoPoint & );
+ friend inline double operator*( const KoPoint &a, const KoPoint &b );
+
+ // Not in QPoint:
+ void setCoords(const double &x, const double &y) { m_x = x; m_y = y; }
+ KoPoint transform (const QWMatrix &m) const
+ {
+ double x, y;
+ m.map(m_x, m_y, &x, &y);
+ return KoPoint(x, y);
+ };
+
+ bool isNear(const KoPoint &p, double range) const
+ {
+ return (p.x() >= m_x - range && p.x() <= m_x + range && p.y() >= m_y - range && p.y() <= m_y + range);
+ }
+
+ static double getAngle( const KoPoint& p1, const KoPoint& p2 ) {
+ double a = atan2( p2.x() - p1.x(), p2.y() - p1.y() ) + M_PI;
+ return ( ( - ( a * 360 ) / ( 2 * M_PI ) - 90 ) - 180 );
+ }
+
+ double manhattanLength() const
+ {
+ return QABS( m_x ) + QABS( m_y );
+ }
+
+ /// Convert to a QPoint - with precision loss!
+ QPoint toQPoint() const
+ {
+ return QPoint( qRound( m_x ), qRound( m_y ) );
+ }
+
+private:
+ double m_x, m_y;
+};
+
+inline KoPoint operator+( const KoPoint &p1, const KoPoint &p2 )
+{ return KoPoint( p1.m_x+p2.m_x, p1.m_y+p2.m_y ); }
+
+inline KoPoint operator-( const KoPoint &p1, const KoPoint &p2 )
+{ return KoPoint( p1.m_x-p2.m_x, p1.m_y-p2.m_y ); }
+
+inline KoPoint operator*( const KoPoint &p, const double &c )
+{ return KoPoint( p.m_x*c, p.m_y*c ); }
+
+inline KoPoint operator*( const double &c, const KoPoint &p )
+{ return KoPoint( p.m_x*c, p.m_y*c ); }
+
+inline double operator*( const KoPoint &a, const KoPoint &b )
+{ return a.m_x * b.m_x + a.m_y * b.m_y; }
+
+/******************************
+ kdDebug support
+*******************************/
+
+#include <kdebug.h>
+
+/** Show a floating point value with great precision (use within kdDebug) */
+#define DEBUGDOUBLE(d) QString::number( (d), 'g', 20 )
+
+inline kdbgstream operator<<( kdbgstream str, const KoPoint & r ) {
+ // should this use DEBUGDOUBLE?
+ str << "(" << r.x() << ", " << r.y() << ")";
+ return str;
+}
+
+inline kndbgstream operator<<( kndbgstream str, const KoPoint & ) { return str; }
+
+#endif
diff --git a/lib/kofficecore/KoQueryTrader.cpp b/lib/kofficecore/KoQueryTrader.cpp
new file mode 100644
index 00000000..00f4379b
--- /dev/null
+++ b/lib/kofficecore/KoQueryTrader.cpp
@@ -0,0 +1,214 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ 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.
+*/
+
+#include <kparts/factory.h>
+
+#include <KoQueryTrader.h>
+#include <KoDocument.h>
+#include <KoFilter.h>
+#include <ktrader.h>
+#include <kservicetype.h>
+#include <kdebug.h>
+
+#include <qfile.h>
+
+#include <limits.h> // UINT_MAX
+
+/**
+ * Port from KOffice Trader to KTrader/KActivator (kded) by Simon Hausmann
+ * (c) 1999 Simon Hausmann <hausmann@kde.org>
+ * Port to KService and simplifications by David Faure <faure@kde.org>
+ */
+
+
+/*******************************************************************
+ *
+ * KoDocumentEntry
+ *
+ *******************************************************************/
+
+KoDocumentEntry::KoDocumentEntry( KService::Ptr service )
+ : m_service( service )
+{
+}
+
+KoDocument* KoDocumentEntry::createDoc( KoDocument* parent, const char* name ) const
+{
+ KLibFactory* factory = KLibLoader::self()->factory( QFile::encodeName(m_service->library()) );
+
+ if( !factory ) {
+ kdWarning(30003) << KLibLoader::self()->lastErrorMessage() << endl;
+ return 0;
+ }
+
+ QObject* obj;
+ if ( factory->inherits( "KParts::Factory" ) )
+ obj = static_cast<KParts::Factory *>(factory)->createPart( 0L, "", parent, name, "KoDocument" );
+ else {
+ kdWarning(30003) << "factory doesn't inherit KParts::Factory ! It is a " << factory->className() << endl; // This shouldn't happen...
+ obj = factory->create( parent, name, "KoDocument" );
+ }
+
+ if ( !obj || !obj->inherits( "KoDocument" ) )
+ {
+ delete obj;
+ return 0;
+ }
+
+ return static_cast<KoDocument*>(obj);
+}
+
+KoDocumentEntry KoDocumentEntry::queryByMimeType( const QString & mimetype )
+{
+ QString constr = QString::fromLatin1( "[X-KDE-NativeMimeType] == '%1' or '%2' in [X-KDE-ExtraNativeMimeTypes]" ).arg( mimetype ).arg( mimetype );
+
+ QValueList<KoDocumentEntry> vec = query( false,constr );
+ if ( vec.isEmpty() )
+ {
+ kdWarning(30003) << "Got no results with " << constr << endl;
+ // Fallback to the old way (which was probably wrong, but better be safe)
+ QString constr = QString::fromLatin1( "'%1' in ServiceTypes" ).arg( mimetype );
+ vec = query( constr );
+ if ( vec.isEmpty() )
+ {
+ // Still no match. Either the mimetype itself is unknown, or we have no service for it.
+ // Help the user debugging stuff by providing some more diagnostics
+ if ( KServiceType::serviceType( mimetype ) == 0L )
+ {
+ kdError(30003) << "Unknown KOffice MimeType " << mimetype << "." << endl;
+ kdError(30003) << "Check your installation (for instance, run 'kde-config --path mime' and check the result)." << endl;
+ } else
+ {
+ kdError(30003) << "Found no KOffice part able to handle " << mimetype << "!" << endl;
+ kdError(30003) << "Check your installation (does the desktop file have X-KDE-NativeMimeType and KOfficePart, did you install KOffice in a different prefix than KDE, without adding the prefix to /etc/kderc ?)" << endl;
+ }
+ return KoDocumentEntry();
+ }
+ }
+
+ return vec[0];
+}
+
+QValueList<KoDocumentEntry> KoDocumentEntry::query( const QString & _constr )
+{
+ return query(true,_constr);
+}
+
+QValueList<KoDocumentEntry> KoDocumentEntry::query( bool _onlyDocEmb, const QString & _constr )
+{
+
+ QValueList<KoDocumentEntry> lst;
+ QString constr;
+ if ( !_constr.isEmpty() ) {
+ constr = "(";
+ constr += _constr;
+ constr += ") and ";
+ }
+ constr += " exist Library";
+
+ // Query the trader
+ KTrader::OfferList offers = KTrader::self()->query( "KOfficePart", constr );
+
+ KTrader::OfferList::ConstIterator it = offers.begin();
+ unsigned int max = offers.count();
+ for( unsigned int i = 0; i < max; i++, ++it )
+ {
+ //kdDebug(30003) << " desktopEntryPath=" << (*it)->desktopEntryPath()
+ // << " library=" << (*it)->library() << endl;
+
+ if ( (*it)->noDisplay() )
+ continue;
+ // Maybe this could be done as a trader constraint too.
+ if ( (!_onlyDocEmb) || ((*it)->property("X-KDE-NOTKoDocumentEmbeddable").toString()!="1") )
+ {
+ KoDocumentEntry d( *it );
+ // Append converted offer
+ lst.append( d );
+ // Next service
+ }
+ }
+
+ if ( lst.count() > 1 && !_constr.isEmpty() )
+ kdWarning(30003) << "KoDocumentEntry::query " << constr << " got " << max << " offers!" << endl;
+
+ return lst;
+}
+
+
+
+
+/*******************************************************************
+ *
+ * KoFilterEntry
+ *
+ *******************************************************************/
+
+KoFilterEntry::KoFilterEntry( KService::Ptr service )
+ : m_service( service )
+{
+ import = service->property( "X-KDE-Import" ).toStringList();
+ export_ = service->property( "X-KDE-Export" ).toStringList();
+ int w = service->property( "X-KDE-Weight" ).toInt();
+ weight = w < 0 ? UINT_MAX : static_cast<unsigned int>( w );
+ available = service->property( "X-KDE-Available" ).toString();
+}
+
+QValueList<KoFilterEntry::Ptr> KoFilterEntry::query( const QString & _constr )
+{
+ kdDebug(30500) << "KoFilterEntry::query( " << _constr << " )" << endl;
+ QValueList<KoFilterEntry::Ptr> lst;
+
+ KTrader::OfferList offers = KTrader::self()->query( "KOfficeFilter", _constr );
+
+ KTrader::OfferList::ConstIterator it = offers.begin();
+ unsigned int max = offers.count();
+ //kdDebug(30500) << "Query returned " << max << " offers" << endl;
+ for( unsigned int i = 0; i < max; i++ )
+ {
+ //kdDebug(30500) << " desktopEntryPath=" << (*it)->desktopEntryPath()
+ // << " library=" << (*it)->library() << endl;
+ // Append converted offer
+ lst.append( new KoFilterEntry( *it ) );
+ // Next service
+ it++;
+ }
+
+ return lst;
+}
+
+KoFilter* KoFilterEntry::createFilter( KoFilterChain* chain, QObject* parent, const char* name )
+{
+ KLibFactory* factory = KLibLoader::self()->factory( QFile::encodeName( m_service->library() ) );
+
+ if ( !factory ) {
+ kdWarning(30003) << KLibLoader::self()->lastErrorMessage() << endl;
+ return 0;
+ }
+
+ QObject* obj = factory->create( parent, name, "KoFilter" );
+ if ( !obj || !obj->inherits( "KoFilter" ) )
+ {
+ delete obj;
+ return 0;
+ }
+
+ KoFilter* filter = static_cast<KoFilter*>( obj );
+ filter->m_chain = chain;
+ return filter;
+}
diff --git a/lib/kofficecore/KoQueryTrader.h b/lib/kofficecore/KoQueryTrader.h
new file mode 100644
index 00000000..55ab8dbe
--- /dev/null
+++ b/lib/kofficecore/KoQueryTrader.h
@@ -0,0 +1,178 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ 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 __ko_query_trader_h__
+#define __ko_query_trader_h__
+
+#include <kservice.h>
+#include <ksharedptr.h>
+#include <qvaluelist.h>
+#include <koffice_export.h>
+
+class QObject;
+class QStringList;
+class KoDocument;
+class KoFilter;
+class KoFilterChain;
+
+/**
+ * Represents an available KOffice component
+ * that supports the document interface.
+ */
+class KOFFICECORE_EXPORT KoDocumentEntry
+{
+
+public:
+ KoDocumentEntry() { m_service = 0L; } // for QValueList
+ KoDocumentEntry( KService::Ptr service );
+ ~KoDocumentEntry() { }
+
+ KService::Ptr service() const { return m_service; }
+
+ /**
+ * @return TRUE if the service pointer is null
+ */
+ bool isEmpty() const { return m_service == 0L; }
+
+ /**
+ * @return name of the associated service
+ */
+ QString name() const { return m_service->name(); }
+
+ /**
+ * Mimetypes (and other service types) which this document can handle.
+ */
+ QStringList mimeTypes() const { return m_service->serviceTypes(); }
+
+ /**
+ * @return TRUE if the document can handle the requested mimetype.
+ */
+ bool supportsMimeType( const QString & _mimetype ) const
+ { return mimeTypes().contains( _mimetype ); }
+
+ /**
+ * Uses the factory of the component to create
+ * a document. If that is not possible, 0 is returned.
+ */
+ KoDocument* createDoc( KoDocument* parent = 0, const char* name = 0 ) const;
+
+ /**
+ * This function will query ksycoca to find all available components.
+ * The result will only contain parts, which are embeddable into a document
+ *
+ * @param _constr is a constraint expression as used by KTrader.
+ * You can use it to set additional restrictions on the available
+ * components.
+ */
+ static QValueList<KoDocumentEntry> query( const QString & _constr = QString::null );
+
+ /**
+ * This function will query the system to find all available filters.
+ *
+ * @param _onlyDocEmb specifies if only KOffice Parts should be listed which are
+ * embeddable into other koDocuments, or all (if false)
+ * (eg.: it makes no sense to embed Kexi into KWord,
+ * but it makes sense to embed it into KoShell)
+ * @param _constr is a constraint expression as used by KDEDs trader interface.
+ * You can use it to set additional restrictions on the available
+ * components.
+ */
+ // ### TODO: MERGE WITH ABOVE METHODE WHEN BIC+SIC CHANGES ARE ALLOWED
+ static QValueList<KoDocumentEntry> query( bool _onlyDocEmb,const QString& _constr);
+ /* this is how the signature should be looking after merging
+ static QValueList<KoDocumentEntry> query( bool _onlyDocEmb =true, const QString& _constr = QString::null );
+ or better: use an enum for the first arg.
+ */
+
+
+ /**
+ * This is a convenience function.
+ *
+ * @return a document entry for the KOffice component that supports
+ * the requested mimetype and fits the user best.
+ */
+ static KoDocumentEntry queryByMimeType( const QString & mimetype );
+
+private:
+ KService::Ptr m_service;
+};
+
+/**
+ * Represents an available filter.
+ */
+class KoFilterEntry : public KShared
+{
+
+public:
+ typedef KSharedPtr<KoFilterEntry> Ptr;
+
+ KoFilterEntry() : weight( 0 ) { m_service = 0L; } // for QValueList
+ KoFilterEntry( KService::Ptr service );
+ ~KoFilterEntry() { }
+
+ KoFilter* createFilter( KoFilterChain* chain, QObject* parent = 0, const char* name = 0 );
+
+ /**
+ * The imported mimetype(s).
+ */
+ QStringList import;
+
+ /**
+ * The exported mimetype(s).
+ */
+ QStringList export_;
+
+ /**
+ * The "weight" of this filter path. Has to be > 0 to be valid.
+ */
+ unsigned int weight;
+
+ /**
+ * Do we have to check during runtime?
+ */
+ QString available;
+
+ /**
+ * @return TRUE if the filter can import the requested mimetype.
+ */
+ bool imports( const QString& _mimetype ) const
+ { return ( import.contains( _mimetype ) ); }
+
+ /**
+ * @return TRUE if the filter can export the requested mimetype.
+ */
+ bool exports( const QString& _m ) const
+ { return ( export_.contains( _m ) ); }
+
+ /**
+ * This function will query KDED to find all available filters.
+ *
+ * @param _constr is a constraint expression as used by KDEDs trader interface.
+ * You can use it to set additional restrictions on the available
+ * components.
+ */
+ static QValueList<KoFilterEntry::Ptr> query( const QString& _constr = QString::null );
+
+ KService::Ptr service() const { return m_service; }
+
+private:
+ KService::Ptr m_service;
+};
+
+#endif
diff --git a/lib/kofficecore/KoRect.cpp b/lib/kofficecore/KoRect.cpp
new file mode 100644
index 00000000..b93a577d
--- /dev/null
+++ b/lib/kofficecore/KoRect.cpp
@@ -0,0 +1,274 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 David Faure <faure@kde.org>
+
+ 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 "KoRect.h"
+
+KoRect KoRect::normalize() const
+{
+ KoRect r;
+ if ( right() < left() ) { // swap bad x values
+ r.m_tl.setX( right() );
+ r.m_br.setX( left() );
+ } else {
+ r.m_tl.setX( left() );
+ r.m_br.setX( right() );
+ }
+ if ( bottom() < top() ) { // swap bad y values
+ r.m_tl.setY( bottom() );
+ r.m_br.setY( top() );
+ } else {
+ r.m_tl.setY( top() );
+ r.m_br.setY( bottom() );
+ }
+ return r;
+}
+
+void KoRect::setTopLeft(const KoPoint &topleft)
+{
+ m_tl = topleft;
+}
+
+void KoRect::setBottomRight(const KoPoint &bottomright)
+{
+ m_br = bottomright;
+}
+
+void KoRect::setTopRight(const KoPoint &topright)
+{
+ m_br.rx() = topright.x();
+ m_tl.ry() = topright.y();
+}
+
+void KoRect::setBottomLeft(const KoPoint &bottomleft)
+{
+ m_tl.rx() = bottomleft.x();
+ m_br.ry() = bottomleft.y();
+}
+
+
+KoPoint KoRect::center() const
+{
+ return KoPoint((left() + right()) / 2, (top() + bottom()) / 2);
+}
+
+void KoRect::moveTopLeft(const KoPoint &topleft)
+{
+ m_br.rx() += topleft.x() - m_tl.x();
+ m_br.ry() += topleft.y() - m_tl.y();
+ m_tl = topleft;
+}
+
+void KoRect::moveBottomRight(const KoPoint &bottomright)
+{
+ m_tl.rx() += bottomright.x() - m_br.x();
+ m_tl.ry() += bottomright.y() - m_br.y();
+ m_br = bottomright;
+}
+
+void KoRect::moveTopRight(const KoPoint &topright)
+{
+ m_tl.rx() += topright.x() - m_br.x();
+ m_br.ry() += topright.y() - m_tl.y();
+ m_br.rx() = topright.x();
+ m_tl.ry() = topright.y();
+}
+
+void KoRect::moveBottomLeft(const KoPoint &bottomleft)
+{
+ m_br.rx() += bottomleft.x() - m_tl.x();
+ m_tl.ry() += bottomleft.y() - m_br.y();
+ m_tl.rx() = bottomleft.x();
+ m_br.ry() = bottomleft.y();
+}
+
+void KoRect::moveBy(const double &dx, const double &dy)
+{
+ m_tl.rx() += dx;
+ m_tl.ry() += dy;
+ m_br.rx() += dx;
+ m_br.ry() += dy;
+}
+
+void KoRect::setRect(const double &x, const double &y, const double &width, const double &height)
+{
+ m_tl.setCoords( x, y );
+ m_br.setCoords( x + width, y + height );
+}
+
+void KoRect::setRect(const KoRect &rect)
+{
+ m_tl = rect.m_tl;
+ m_br = rect.m_br;
+}
+
+void KoRect::setCoords(const double &x1, const double &y1, const double &x2, const double &y2)
+{
+ m_tl.setCoords( x1, y1 );
+ m_br.setCoords( x2, y2 );
+}
+
+void KoRect::setSize(const KoSize &size)
+{
+ setWidth(size.width());
+ setHeight(size.height());
+}
+
+KoSize KoRect::size() const
+{
+ return KoSize(width(), height());
+}
+
+KoRect &KoRect::operator|=(const KoRect &rhs) {
+
+ if(rhs.isEmpty())
+ return *this;
+ if(isEmpty())
+ {
+ *this = rhs;
+ return *this;
+ }
+ if(m_tl.x() > rhs.left())
+ m_tl.setX(rhs.left());
+ if(m_tl.y() > rhs.top())
+ m_tl.setY(rhs.top());
+ if(m_br.x() < rhs.right())
+ m_br.setX(rhs.right());
+ if(m_br.y() < rhs.bottom())
+ m_br.setY(rhs.bottom());
+ return *this;
+}
+
+KoRect &KoRect::operator&=(const KoRect &rhs) {
+
+ if(m_tl.x() < rhs.left())
+ m_tl.setX(rhs.left());
+ if(m_tl.y() < rhs.top())
+ m_tl.setY(rhs.top());
+ if(m_br.x() > rhs.right())
+ m_br.setX(rhs.right());
+ if(m_br.y() > rhs.bottom())
+ m_br.setY(rhs.bottom());
+ return *this;
+}
+
+bool KoRect::contains(const KoPoint &p) const {
+ return (p.x() >= m_tl.x() && p.x() <= m_br.x() && p.y() >= m_tl.y() && p.y() <= m_br.y());
+}
+
+bool KoRect::contains(const double &x, const double &y) const {
+ return (x >= m_tl.x() && x <= m_br.x() && y >= m_tl.y() && y <= m_br.y());
+}
+
+bool KoRect::contains(const KoRect &r) const {
+ return (r.left() >= m_tl.x() && r.right() <= m_br.x() && r.top() >= m_tl.y() && r.bottom() <= m_br.y());
+}
+
+
+KoRect KoRect::unite(const KoRect &r) const {
+ return *this | r;
+}
+
+KoRect KoRect::intersect(const KoRect &r) const {
+ return *this & r;
+}
+
+bool KoRect::intersects(const KoRect &r) const {
+ return ( QMAX(m_tl.x(), r.left()) <= QMIN(m_br.x(), r.right()) &&
+ QMAX(m_tl.y(), r.top()) <= QMIN(m_br.y(), r.bottom()) );
+}
+
+KoRect operator|(const KoRect &lhs, const KoRect &rhs) {
+
+ if(lhs.isEmpty())
+ return rhs;
+ if(rhs.isEmpty())
+ return lhs;
+ KoRect tmp;
+ tmp.setCoords( (lhs.left() < rhs.left() ? lhs.left() : rhs.left()),
+ (lhs.top() < rhs.top() ? lhs.top() : rhs.top()),
+ (lhs.right() > rhs.right() ? lhs.right() : rhs.right()),
+ (lhs.bottom() > rhs.bottom() ? lhs.bottom() : rhs.bottom()) );
+ return tmp;
+}
+
+KoRect operator&(const KoRect &lhs, const KoRect &rhs) {
+
+ KoRect tmp;
+ tmp.setCoords( (lhs.left() > rhs.left() ? lhs.left() : rhs.left()),
+ (lhs.top() > rhs.top() ? lhs.top() : rhs.top()),
+ (lhs.right() < rhs.right() ? lhs.right() : rhs.right()),
+ (lhs.bottom() < rhs.bottom() ? lhs.bottom() : rhs.bottom()) );
+ return tmp;
+}
+
+bool operator==(const KoRect &lhs, const KoRect &rhs) {
+ return ( lhs.topLeft()==rhs.topLeft() &&
+ lhs.bottomRight()==rhs.bottomRight() );
+}
+
+bool operator!=(const KoRect &lhs, const KoRect &rhs) {
+ return ( lhs.topLeft()!=rhs.topLeft() ||
+ lhs.bottomRight()!=rhs.bottomRight() );
+}
+
+KoRect KoRect::transform(const QWMatrix &m) const
+{
+ KoRect result;
+ if(m.m12() == 0.0F && m.m21() == 0.0F)
+ {
+ result = KoRect(topLeft().transform(m), bottomRight().transform(m));
+ }
+ else
+ {
+ int i;
+ KoPoint p[4] = { KoPoint(m_tl.x(), m_tl.y()), KoPoint(m_tl.x(), m_br.x()),
+ KoPoint(m_br.x(), m_br.x()), KoPoint(m_br.x(), m_tl.y()) };
+ for(i = 0; i < 4; i++)
+ p[i] = p[i].transform(m);
+
+ result.setLeft(p[0].x());
+ result.setTop(p[0].y());
+ result.setRight(p[0].x());
+ result.setBottom(p[0].y());
+
+ for(int i = 1; i < 4; i++)
+ {
+ result.setLeft(QMIN(p[i].x(), result.left()));
+ result.setTop(QMIN(p[i].y(), result.top()));
+ result.setRight(QMAX(p[i].x(), result.right()));
+ result.setBottom(QMAX(p[i].y(), result.bottom()));
+ }
+ }
+ return result;
+}
+
+KoRect KoRect::translate(double dx, double dy) const
+{
+ return KoRect(left() + dx, top() + dy, width(), height());
+}
+
+QRect KoRect::toQRect() const
+{
+ return QRect( qRound( left() ), qRound( top() ), qRound( width() ), qRound( height() ) );
+}
+
+//static
+KoRect KoRect::fromQRect( const QRect &rect )
+{
+ return KoRect( rect.left(), rect.top(), rect.width(), rect.height() );
+}
diff --git a/lib/kofficecore/KoRect.h b/lib/kofficecore/KoRect.h
new file mode 100644
index 00000000..dfc63946
--- /dev/null
+++ b/lib/kofficecore/KoRect.h
@@ -0,0 +1,148 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 David Faure <faure@kde.org>
+
+ 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 koRect_h
+#define koRect_h
+
+#include "KoPoint.h"
+#include "KoSize.h"
+#include <qrect.h>
+#include "koffice_export.h"
+
+/**
+ * A rect whose coordinates are floating-point values ( "double"s ).
+ * The API isn't documented, it's a perfect mirror of QRect.
+ */
+class KOFFICEUI_EXPORT KoRect {
+
+public:
+ KoRect()
+ : m_tl(), m_br() {}
+ KoRect(const KoPoint &topleft, const KoPoint &bottomright)
+ : m_tl(topleft), m_br(bottomright) {}
+ KoRect(const KoPoint &topleft, const KoSize &size)
+ {m_tl = topleft; setSize(size);}
+ KoRect(const double &left, const double &top, const double &width, const double &height)
+ : m_tl(left,top), m_br(left+width,top+height) {}
+ ~KoRect() {}
+
+ bool isNull() const { return m_tl == m_br; }
+ // Like QRect, a null KoRect is empty.
+ bool isEmpty() const { return m_tl.x() > m_br.x() || m_tl.y() > m_br.y() || isNull(); }
+ // Unlike QRect, a null KoRect is valid (0-sized).
+ bool isValid() const { return m_tl.x() <= m_br.x() && m_tl.y() <= m_br.y(); }
+ KoRect normalize() const;
+
+ double left() const { return m_tl.x(); }
+ double top() const { return m_tl.y(); }
+ double right() const { return m_br.x(); }
+ double bottom() const { return m_br.y(); }
+
+ double& rLeft() { return m_tl.rx(); }
+ double& rTop() { return m_tl.ry(); }
+ double& rRight() { return m_br.rx(); }
+ double& rBottom() { return m_br.ry(); }
+
+ double x() const { return left(); }
+ double y() const { return top(); }
+
+ void setLeft(const double &left) { m_tl.setX(left); }
+ void setTop(const double &top) { m_tl.setY(top); }
+ void setRight(const double &right) { m_br.setX(right); }
+ void setBottom(const double &bottom) { m_br.setY(bottom); }
+
+ void setX(const double &x) { m_tl.setX(x); } //same as setLeft()
+ void setY(const double &y) { m_tl.setY(y); } //same as setTop()
+
+ KoPoint topLeft() const { return m_tl; }
+ KoPoint bottomRight() const { return m_br; }
+ KoPoint topRight() const { return KoPoint(m_br.x(), m_tl.y()); }
+ KoPoint bottomLeft() const { return KoPoint(m_tl.x(), m_br.y()); }
+ KoPoint center() const;
+
+ void setTopLeft(const KoPoint &topleft);
+ void setBottomRight(const KoPoint &bottomright);
+ void setTopRight(const KoPoint &topright);
+ void setBottomLeft(const KoPoint &bottomleft);
+
+ void moveTopLeft(const KoPoint &topleft);
+ void moveBottomRight(const KoPoint &bottomright);
+ void moveTopRight(const KoPoint &topright);
+ void moveBottomLeft(const KoPoint &bottomleft);
+ //void moveCenter(const KoPoint &center);
+ void moveBy(const double &dx, const double &dy);
+
+ void setRect(const double &x, const double &y, const double &width, const double &height);
+ void setRect(const KoRect &rect);
+ void setCoords(const double &x1, const double &y1, const double &x2, const double &y2);
+
+ KoSize size() const;
+ double width() const { return m_br.x()-m_tl.x(); }
+ double height() const { return m_br.y()-m_tl.y(); }
+ void setWidth(const double &width) { m_br.setX(m_tl.x()+width); }
+ void setHeight(const double &height) { m_br.setY(m_tl.y()+height); }
+ void setSize(const KoSize &size);
+
+ KoRect &operator|=(const KoRect &rhs);
+ KoRect &operator&=(const KoRect &rhs);
+ /**
+ * Returns if the point is contained in the rect.
+ * @param p the point to test
+ * Will return true if the point is contained in the 2d area this rect represents, this means
+ * that it will return true on everthing from the topleft() to the bottomright();
+ * Note that for KoRect(0, 0, 100, 100) the KoPoint(0, 0) as well as KoPoint(100, 100) are
+ * mathmatically contained in the rect, this in contrary to pixel based rectangles.
+ */
+ bool contains(const KoPoint &p) const;
+ /// Helper function for the above function.
+ bool contains(const double &x, const double &y) const;
+ /// Helper function for the above function.
+ bool contains(const KoRect &r) const;
+ KoRect unite(const KoRect &r) const;
+ KoRect intersect(const KoRect &r) const;
+ bool intersects(const KoRect &r) const;
+
+ KoRect transform(const QWMatrix &m) const;
+ KoRect translate(double dx, double dy) const;
+
+ QRect toQRect() const;
+ static KoRect fromQRect( const QRect &rect );
+
+private:
+ KoPoint m_tl, m_br;
+};
+
+KoRect operator|(const KoRect &lhs, const KoRect &rhs);
+KoRect operator&(const KoRect &lhs, const KoRect &rhs);
+KOFFICEUI_EXPORT bool operator==(const KoRect &lhs, const KoRect &rhs);
+KOFFICEUI_EXPORT bool operator!=(const KoRect &lhs, const KoRect &rhs);
+
+
+/** Show the position and size of a rectangle (use within kdDebug) */
+#define DEBUGRECT(rc) (rc).x() << "," << (rc).y() << " " << (rc).width() << "x" << (rc).height()
+
+//inline kdbgstream operator<<( kdbgstream str, const KoRect & r ) { str << "[" << r.left() << ", " << r.top() << " - " << r.right() << ", " << r.bottom() << "]"; return str; }
+inline kdbgstream operator<<( kdbgstream str, const KoRect & r ) { str << "[" << r.left() << "," << r.top() << " " << r.width() << "x" << r.height() << "]"; return str; }
+inline kndbgstream operator<<( kndbgstream str, const KoRect & ) { return str; }
+
+/** Show the rectangles that form a region (use as a standalone statement) */
+#define DEBUGREGION(reg) { QMemArray<QRect>rs=reg.rects(); for (int i=0;i<rs.size();++i) \
+ kdDebug()<<" "<<DEBUGRECT(rs[i] )<<endl; }
+// You can now use kdDebug() << theregion << endl; (kdebug.h)
+
+#endif
diff --git a/lib/kofficecore/KoSetPropCommand.h b/lib/kofficecore/KoSetPropCommand.h
new file mode 100644
index 00000000..53ee73bf
--- /dev/null
+++ b/lib/kofficecore/KoSetPropCommand.h
@@ -0,0 +1,86 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Werner Trobin <trobin@kde.org>
+
+ 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 KOGENCOMMAND_H
+#define KOGENCOMMAND_H
+
+#include <kcommand.h>
+
+/**
+ * Generic command to set a property on an object.
+ * This variant is for simple types, where the setter method takes a value.
+ */
+template<class Property, class Object, void (Object::* Function) (Property)> class KoSetBasicPropCommand : public KNamedCommand {
+
+public:
+ KoSetBasicPropCommand(Object *object, const QString &name) : KNamedCommand(name), m_object(object) {}
+ KoSetBasicPropCommand(Object *object, const QString &name,
+ const Property &oldProperty, const Property &newProperty) : KNamedCommand(name),
+ m_object(object), m_oldProperty(oldProperty), m_newProperty(newProperty) {}
+ virtual ~KoSetBasicPropCommand() {}
+
+ virtual void execute() { if(m_object) (m_object->*Function)(m_newProperty); }
+ virtual void unexecute() { if(m_object) (m_object->*Function)(m_oldProperty); }
+
+ void setOldProperty(const Property &oldProperty) { m_oldProperty=oldProperty; }
+ const Property &oldProperty() const { return m_oldProperty; }
+ void setNewProperty(const Property &newProperty) { m_newProperty=newProperty; }
+ const Property &newProperty() const { return m_newProperty; }
+
+private:
+ // Don't copy or assign this stuff
+ KoSetBasicPropCommand(const KoSetBasicPropCommand<Property, Object, Function> &rhs);
+ KoSetBasicPropCommand &operator=(const KoSetBasicPropCommand<Property, Object, Function> &rhs);
+
+ Object *m_object;
+ Property m_oldProperty, m_newProperty;
+};
+
+/**
+ * Generic command to set a property on an object.
+ * This variant is for non-trivial types, where the setter method takes a const reference.
+ */
+template<class Property, class Object, void (Object::* Function) (const Property &)> class KoSetPropCommand : public KNamedCommand {
+
+public:
+ KoSetPropCommand(Object *object, const QString &name) : KNamedCommand(name), m_object(object) {}
+ KoSetPropCommand(Object *object, const QString &name,
+ const Property &oldProperty, const Property &newProperty) : KNamedCommand(name),
+ m_object(object), m_oldProperty(oldProperty), m_newProperty(newProperty) {}
+ virtual ~KoSetPropCommand() {}
+
+ virtual void execute() { if(m_object) (m_object->*Function)(m_newProperty); }
+ virtual void unexecute() { if(m_object) (m_object->*Function)(m_oldProperty); }
+
+ void setOldProperty(const Property &oldProperty) { m_oldProperty=oldProperty; }
+ const Property &oldProperty() const { return m_oldProperty; }
+ void setNewProperty(const Property &newProperty) { m_newProperty=newProperty; }
+ const Property &newProperty() const { return m_newProperty; }
+
+private:
+ // Don't copy or assign this stuff
+ KoSetPropCommand(const KoSetPropCommand<Property, Object, Function> &rhs);
+ KoSetPropCommand &operator=(const KoSetPropCommand<Property, Object, Function> &rhs);
+
+ Object *m_object;
+ Property m_oldProperty, m_newProperty;
+};
+
+#endif /* KOGENCOMMAND_H */
+
diff --git a/lib/kofficecore/KoSize.h b/lib/kofficecore/KoSize.h
new file mode 100644
index 00000000..3cdc30bc
--- /dev/null
+++ b/lib/kofficecore/KoSize.h
@@ -0,0 +1,230 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Lukas Tinkl <lukas@kde.org>
+
+ 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.
+
+ This file borrows from the QSize class;
+ Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
+*/
+
+#ifndef koSize_h
+#define koSize_h
+
+#include <qsize.h>
+#include <qtl.h>
+
+/**
+ * A size whose coordinates are floating-point values ( "double"s ).
+ * The API isn't documented, it's a perfect mirror of QSize.
+ */
+class KoSize
+{
+public:
+ KoSize();
+ KoSize( double w, double h );
+
+ bool isNull() const;
+ bool isEmpty() const;
+ bool isValid() const;
+
+ double width() const;
+ double height() const;
+ void setWidth( double w );
+ void setHeight( double h );
+
+ KoSize expandedTo( const KoSize & ) const;
+ KoSize boundedTo( const KoSize & ) const;
+
+ double &rwidth();
+ double &rheight();
+
+ KoSize &operator+=( const KoSize & );
+ KoSize &operator-=( const KoSize & );
+ KoSize &operator*=( int c );
+ KoSize &operator*=( double c );
+ KoSize &operator/=( int c );
+ KoSize &operator/=( double c );
+
+ friend inline bool operator==( const KoSize &, const KoSize & );
+ friend inline bool operator!=( const KoSize &, const KoSize & );
+ friend inline const KoSize operator+( const KoSize &, const KoSize & );
+ friend inline const KoSize operator-( const KoSize &, const KoSize & );
+ friend inline const KoSize operator*( const KoSize &, int );
+ friend inline const KoSize operator*( int, const KoSize & );
+ friend inline const KoSize operator*( const KoSize &, double );
+ friend inline const KoSize operator*( double, const KoSize & );
+ friend inline const KoSize operator/( const KoSize &, int );
+ friend inline const KoSize operator/( const KoSize &, double );
+
+ inline QSize toQSize() const;
+ static KoSize fromQSize( const QSize &size )
+ {
+ return KoSize(size.width(), size.height());
+ }
+
+ void transpose()
+ {
+ qSwap(wd, ht);
+ }
+
+private:
+ static void warningDivByZero()
+ {
+#if defined(QT_CHECK_MATH)
+ qWarning( "KoSize: Division by zero error" );
+#endif
+ }
+
+ double wd;
+ double ht;
+};
+
+
+/*****************************************************************************
+ KoSize inline functions
+ *****************************************************************************/
+
+inline KoSize::KoSize()
+{ wd = ht = -1.0; }
+
+inline KoSize::KoSize( double w, double h )
+{ wd=w; ht=h; }
+
+inline bool KoSize::isNull() const
+{ return wd==0.0 && ht==0.0; }
+
+inline bool KoSize::isEmpty() const
+{ return wd<=0.0 || ht<=0.0; }
+
+inline bool KoSize::isValid() const
+{ return wd>=0.0 && ht>=0.0; }
+
+inline double KoSize::width() const
+{ return wd; }
+
+inline double KoSize::height() const
+{ return ht; }
+
+inline void KoSize::setWidth( double w )
+{ wd=w; }
+
+inline void KoSize::setHeight( double h )
+{ ht=h; }
+
+inline double &KoSize::rwidth()
+{ return wd; }
+
+inline double &KoSize::rheight()
+{ return ht; }
+
+inline KoSize &KoSize::operator+=( const KoSize &s )
+{ wd+=s.wd; ht+=s.ht; return *this; }
+
+inline KoSize &KoSize::operator-=( const KoSize &s )
+{ wd-=s.wd; ht-=s.ht; return *this; }
+
+inline KoSize &KoSize::operator*=( int c )
+{ wd*=c; ht*=c; return *this; }
+
+inline KoSize &KoSize::operator*=( double c )
+{ wd=wd*c; ht=ht*c; return *this; }
+
+inline bool operator==( const KoSize &s1, const KoSize &s2 )
+{ return s1.wd == s2.wd && s1.ht == s2.ht; }
+
+inline bool operator!=( const KoSize &s1, const KoSize &s2 )
+{ return s1.wd != s2.wd || s1.ht != s2.ht; }
+
+inline const KoSize operator+( const KoSize & s1, const KoSize & s2 )
+{ return KoSize(s1.wd+s2.wd, s1.ht+s2.ht); }
+
+inline const KoSize operator-( const KoSize &s1, const KoSize &s2 )
+{ return KoSize(s1.wd-s2.wd, s1.ht-s2.ht); }
+
+inline const KoSize operator*( const KoSize &s, int c )
+{ return KoSize(s.wd*c, s.ht*c); }
+
+inline const KoSize operator*( int c, const KoSize &s )
+{ return KoSize(s.wd*c, s.ht*c); }
+
+inline const KoSize operator*( const KoSize &s, double c )
+{ return KoSize(s.wd*c, s.ht*c); }
+
+inline const KoSize operator*( double c, const KoSize &s )
+{ return KoSize(s.wd*c, s.ht*c); }
+
+inline KoSize &KoSize::operator/=( int c )
+{
+#if defined(QT_CHECK_MATH)
+ if ( c == 0 )
+ warningDivByZero();
+#endif
+ wd/=c; ht/=c;
+ return *this;
+}
+
+inline KoSize &KoSize::operator/=( double c )
+{
+#if defined(QT_CHECK_MATH)
+ if ( c == 0.0 )
+ warningDivByZero();
+#endif
+ wd=wd/c; ht=ht/c;
+ return *this;
+}
+
+inline const KoSize operator/( const KoSize &s, int c )
+{
+#if defined(QT_CHECK_MATH)
+ if ( c == 0 )
+ KoSize::warningDivByZero();
+#endif
+ return KoSize(s.wd/c, s.ht/c);
+}
+
+inline const KoSize operator/( const KoSize &s, double c )
+{
+#if defined(QT_CHECK_MATH)
+ if ( c == 0.0 )
+ KoSize::warningDivByZero();
+#endif
+ return KoSize(s.wd/c, s.ht/c);
+}
+
+inline KoSize KoSize::expandedTo( const KoSize & otherSize ) const
+{
+ return KoSize( QMAX(wd,otherSize.wd), QMAX(ht,otherSize.ht) );
+}
+
+inline KoSize KoSize::boundedTo( const KoSize & otherSize ) const
+{
+ return KoSize( QMIN(wd,otherSize.wd), QMIN(ht,otherSize.ht) );
+}
+
+inline QSize KoSize::toQSize() const
+{
+ return QSize(qRound(wd), qRound(ht));
+}
+
+/******************************
+ kdDebug support
+*******************************/
+#include <kdebug.h>
+
+inline kdbgstream operator<<( kdbgstream str, const KoSize & sz ) { str << "[" << sz.width() << "x" << sz.height() << "]"; return str; }
+inline kndbgstream operator<<( kndbgstream str, const KoSize & ) { return str; }
+
+#endif
diff --git a/lib/kofficecore/KoSpeaker.cpp b/lib/kofficecore/KoSpeaker.cpp
new file mode 100644
index 00000000..db975623
--- /dev/null
+++ b/lib/kofficecore/KoSpeaker.cpp
@@ -0,0 +1,602 @@
+/*
+* This file is part of the KDE/KOffice project.
+* Copyright (C) 2005, Gary Cramblitt <garycramblitt@comcast.net>
+*
+* @author Gary Cramblitt <garycramblitt@comcast.net>
+* @since KOffice 1.5
+*
+* 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.
+*/
+
+// Qt includes.
+#include <qtimer.h>
+#include <qcursor.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+#include <qmenubar.h>
+#include <qlabel.h>
+#include <qbutton.h>
+#include <qcombobox.h>
+#include <qtabbar.h>
+#include <qgroupbox.h>
+#include <qlineedit.h>
+#include <qtextedit.h>
+#include <qlistview.h>
+#include <qlistbox.h>
+#include <qiconview.h>
+#include <qtable.h>
+#include <qgridview.h>
+#include <qregexp.h>
+#include <qstylesheet.h>
+
+// KDE includes.
+#include <kapplication.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <dcopclient.h>
+#include <kconfig.h>
+#include <ktrader.h>
+#include <kdebug.h>
+
+// KoSpeaker includes.
+#include "KoSpeaker.h"
+#include "KoSpeaker.moc"
+
+// ------------------ KoSpeakerPrivate ------------------------
+
+class KoSpeakerPrivate
+{
+public:
+ KoSpeakerPrivate() :
+ m_versionChecked(false),
+ m_enabled(false),
+ m_speakFlags(0),
+ m_timeout(600),
+ m_timer(0),
+ m_prevPointerWidget(0),
+ m_prevPointerId(-1),
+ m_prevFocusWidget(0),
+ m_prevFocusId(-1),
+ m_prevWidget(0),
+ m_prevId(-1),
+ m_cancelSpeakWidget(false)
+ {}
+
+ // List of text jobs.
+ QValueList<uint> m_jobNums;
+ // Whether the version of KTTSD has been requested from the daemon.
+ bool m_versionChecked;
+ // KTTSD version string.
+ QString m_kttsdVersion;
+ // Language code of last spoken text.
+ QString m_langCode;
+ // Word used before speaking an accelerator letter.
+ QString m_acceleratorPrefix;
+ // Whether TTS service is available or not.
+ bool m_enabled;
+ // TTS options.
+ uint m_speakFlags;
+ // Timer which implements the polling interval.
+ int m_timeout;
+ QTimer* m_timer;
+ // Widget and part of widget for 1) last widget under mouse pointer, 2) last widget with focus, and
+ // last widget spoken.
+ QWidget* m_prevPointerWidget;
+ int m_prevPointerId;
+ QWidget* m_prevFocusWidget;
+ int m_prevFocusId;
+ QWidget* m_prevWidget;
+ int m_prevId;
+ // True when cancelSpeakWidget has been called in response to customSpeakWidget signal.
+ bool m_cancelSpeakWidget;
+};
+
+// ------------------ KoSpeaker -------------------------------
+
+KoSpeaker* KoSpeaker::KSpkr = 0L;
+
+KoSpeaker::KoSpeaker()
+{
+ Q_ASSERT(!KSpkr);
+ KSpkr = this;
+ d = new KoSpeakerPrivate();
+ readConfig(KGlobal::config());
+}
+
+KoSpeaker::~KoSpeaker()
+{
+ if (d->m_jobNums.count() > 0) {
+ for (int i = d->m_jobNums.count() - 1; i >= 0; i--)
+ removeText(d->m_jobNums[i]);
+ d->m_jobNums.clear();
+ }
+ delete d;
+ KSpkr = 0;
+}
+
+bool KoSpeaker::isEnabled() const { return d->m_enabled; }
+
+void KoSpeaker::probe()
+{
+ d->m_timer->stop();
+ QWidget* w;
+ QPoint pos;
+ bool spoke = false;
+ if ( d->m_speakFlags & SpeakFocusWidget ) {
+ w = kapp->focusWidget();
+ if (w) {
+ spoke = maybeSayWidget(w);
+ if (!spoke)
+ emit customSpeakWidget(w, pos, d->m_speakFlags);
+ }
+ }
+ if ( !spoke && d->m_speakFlags & SpeakPointerWidget ) {
+ pos = QCursor::pos();
+ w = kapp->widgetAt(pos, true);
+ if (w) {
+ if (!maybeSayWidget(w, pos))
+ emit customSpeakWidget(w, pos, d->m_speakFlags);
+ }
+ }
+ d->m_timer->start(d->m_timeout);
+}
+
+void KoSpeaker::queueSpeech(const QString& msg, const QString& langCode /*= QString()*/, bool first /*= true*/)
+{
+ if (!startKttsd()) return;
+ int jobCount = d->m_jobNums.count();
+ if (first && jobCount > 0) {
+ for (int i = jobCount - 1; i >= 0; i--)
+ removeText(d->m_jobNums[i]);
+ d->m_jobNums.clear();
+ jobCount = 0;
+ }
+ QString s = msg.stripWhiteSpace();
+ if (s.isEmpty()) return;
+ // kdDebug() << "KoSpeaker::queueSpeech: s = [" << s << "]" << endl;
+ // If no language code given, assume desktop setting.
+ QString languageCode = langCode;
+ if (langCode.isEmpty())
+ languageCode = KGlobal::locale()->language();
+ // kdDebug() << "KoSpeaker::queueSpeech:languageCode = " << languageCode << endl;
+ // If KTTSD version is 0.3.5 or later, we can use the appendText method to submit a
+ // single, multi-part text job. Otherwise, must submit separate text jobs.
+ // If language code changes, then must also start a new text job so that it will
+ // be spoken in correct talker.
+ if (getKttsdVersion().isEmpty())
+ d->m_jobNums.append(setText(s, languageCode));
+ else {
+ if ((jobCount == 0) || (languageCode != d->m_langCode))
+ d->m_jobNums.append(setText(s, languageCode));
+ else
+ appendText(s, d->m_jobNums[jobCount-1]);
+ }
+ d->m_langCode = languageCode;
+}
+
+void KoSpeaker::startSpeech()
+{
+ for (uint i = 0; i < d->m_jobNums.count(); i++)
+ startText(d->m_jobNums[i]);
+}
+
+void KoSpeaker::readConfig(KConfig* config)
+{
+ delete d->m_timer;
+ d->m_timer = 0;
+ config->setGroup("TTS");
+ d->m_speakFlags = 0;
+ if (config->readBoolEntry("SpeakPointerWidget", false)) d->m_speakFlags |= SpeakPointerWidget;
+ if (config->readBoolEntry("SpeakFocusWidget", false)) d->m_speakFlags |= SpeakFocusWidget;
+ if (config->readBoolEntry("SpeakTooltips", true)) d->m_speakFlags |= SpeakTooltip;
+ if (config->readBoolEntry("SpeakWhatsThis", false)) d->m_speakFlags |= SpeakWhatsThis;
+ if (config->readBoolEntry("SpeakDisabled", true)) d->m_speakFlags |= SpeakDisabled;
+ if (config->readBoolEntry("SpeakAccelerators", true)) d->m_speakFlags |= SpeakAccelerator;
+ d->m_timeout = config->readNumEntry("PollingInterval", 600);
+ d->m_acceleratorPrefix = config->readEntry("AcceleratorPrefixWord", i18n("Accelerator"));
+ if (d->m_speakFlags & (SpeakPointerWidget | SpeakFocusWidget)) {
+ if (startKttsd()) {
+ d->m_timer = new QTimer( this );
+ connect( d->m_timer, SIGNAL(timeout()), this, SLOT(probe()) );
+ d->m_timer->start( d->m_timeout );
+ }
+ }
+}
+
+bool KoSpeaker::maybeSayWidget(QWidget* w, const QPoint& pos /*=QPoint()*/)
+{
+ if (!w) return false;
+
+ int id = -1;
+ QString text;
+
+ if (w->inherits("QViewportWidget")) {
+ w = w->parentWidget();
+ if (!w) return false;
+ }
+
+ // Handle widgets that have multiple parts.
+
+ if ( w->inherits("QMenuBar") ) {
+ QMenuBar* menuBar = dynamic_cast<QMenuBar *>(w);
+ if (pos == QPoint()) {
+ for (uint i = 0; i < menuBar->count(); ++i)
+ if (menuBar->isItemActive(menuBar->idAt(i))) {
+ id = menuBar->idAt(i);
+ break;
+ }
+ }
+ // TODO: This doesn't work. Need way to figure out the QMenuItem underneath mouse pointer.
+ // id = menuBarItemAt(menuBar, pos);
+ if ( id != -1 )
+ text = menuBar->text(id);
+ }
+ else
+ if (w->inherits("QPopupMenu")) {
+ QPopupMenu* popupMenu = dynamic_cast<QPopupMenu *>(w);
+ if (pos == QPoint()) {
+ for (uint i = 0; i < popupMenu->count(); ++i)
+ if (popupMenu->isItemActive(popupMenu->idAt(i))) {
+ id = popupMenu->idAt(i);
+ break;
+ }
+ } else
+ id = popupMenu->idAt(popupMenu->mapFromGlobal(pos));
+ if ( id != -1 )
+ text = popupMenu->text(id);
+ }
+ else
+ if (w->inherits("QTabBar")) {
+ QTabBar* tabBar = dynamic_cast<QTabBar *>(w);
+ QTab* tab = 0;
+ if (pos == QPoint())
+ tab = tabBar->tabAt(tabBar->currentTab());
+ else
+ tab = tabBar->selectTab(tabBar->mapFromGlobal(pos));
+ if (tab) {
+ id = tab->identifier();
+ text = tab->text();
+ }
+ }
+ else
+ if (w->inherits("QListView")) {
+ QListView* lv = dynamic_cast<QListView *>(w);
+ QListViewItem* item = 0;
+ if (pos == QPoint())
+ item = lv->currentItem();
+ else
+ item = lv->itemAt(lv->viewport()->mapFromGlobal(pos));
+ if (item) {
+ id = lv->itemPos(item);
+ text = item->text(0);
+ for (int col = 1; col < lv->columns(); ++col)
+ if (!item->text(col).isEmpty()) text += ". " + item->text(col);
+ }
+ }
+ else
+ if (w->inherits("QListBox")) {
+ QListBox* lb = dynamic_cast<QListBox *>(w);
+ // qt docs say coordinates are in "on-screen" coordinates. What does that mean?
+ QListBoxItem* item = 0;
+ if (pos == QPoint())
+ item = lb->item(lb->currentItem());
+ else
+ item = lb->itemAt(lb->mapFromGlobal(pos));
+ if (item) {
+ id = lb->index(item);
+ text = item->text();
+ }
+ }
+ else
+ if (w->inherits("QIconView")) {
+ QIconView* iv = dynamic_cast<QIconView *>(w);
+ QIconViewItem* item = 0;
+ if (pos == QPoint())
+ item = iv->currentItem();
+ else
+ item = iv->findItem(iv->viewportToContents(iv->viewport()->mapFromGlobal(pos)));
+ if (item) {
+ id = item->index();
+ text = item->text();
+ }
+ }
+ else
+ if (w->inherits("QTable")) {
+ QTable* tbl = dynamic_cast<QTable *>(w);
+ int row = -1;
+ int col = -1;
+ if (pos == QPoint()) {
+ row = tbl->currentRow();
+ col = tbl->currentColumn();
+ } else {
+ QPoint p = tbl->viewportToContents(tbl->viewport()->mapFromGlobal(pos));
+ row = tbl->rowAt(p.y());
+ col = tbl->columnAt(p.x());
+ }
+ if (row >= 0 && col >= 0) {
+ id = (row * tbl->numCols()) + col;
+ text = tbl->text(row, col);
+ }
+ }
+ else
+ if (w->inherits("QGridView")) {
+ QGridView* gv = dynamic_cast<QGridView *>(w);
+ // TODO: QGridView does not have a "current" row or column. Don't think they can even get focus?
+ int row = -1;
+ int col = -1;
+ if (pos != QPoint()) {
+ QPoint p = gv->viewportToContents(gv->viewport()->mapFromGlobal(pos));
+ row = gv->rowAt(p.y());
+ col = gv->columnAt(p.x());
+ }
+ if (row >= 0 && col >= 0)
+ id = (row * gv->numCols()) + col;
+ }
+
+ if (pos == QPoint()) {
+ if ( w == d->m_prevFocusWidget && id == d->m_prevFocusId) return false;
+ d->m_prevFocusWidget = w;
+ d->m_prevFocusId = id;
+ } else {
+ if ( w == d->m_prevPointerWidget && id == d->m_prevPointerId) return false;
+ d->m_prevPointerWidget = w;
+ d->m_prevPointerId = id;
+ }
+ if (w == d->m_prevWidget && id == d->m_prevId) return false;
+ d->m_prevWidget = w;
+ d->m_prevId = id;
+
+ // kdDebug() << " w = " << w << endl;
+
+ d->m_cancelSpeakWidget = false;
+ emit customSpeakNewWidget(w, pos, d->m_speakFlags);
+ if (d->m_cancelSpeakWidget) return true;
+
+ // Handle simple, single-part widgets.
+ if ( w->inherits("QButton") )
+ text = dynamic_cast<QButton *>(w)->text();
+ else
+ if (w->inherits("QComboBox"))
+ text = dynamic_cast<QComboBox *>(w)->currentText();
+ else
+ if (w->inherits("QLineEdit"))
+ text = dynamic_cast<QLineEdit *>(w)->text();
+ else
+ if (w->inherits("QTextEdit"))
+ text = dynamic_cast<QTextEdit *>(w)->text();
+ else
+ if (w->inherits("QLabel"))
+ text = dynamic_cast<QLabel *>(w)->text();
+ else
+ if (w->inherits("QGroupBox")) {
+ // TODO: Should calculate this number from font size?
+ if (w->mapFromGlobal(pos).y() < 30)
+ text = dynamic_cast<QGroupBox *>(w)->title();
+ }
+// else
+// if (w->inherits("QWhatsThat")) {
+// text = dynamic_cast<QWhatsThat *>(w)->text();
+// }
+
+ text = text.stripWhiteSpace();
+ if (!text.isEmpty()) {
+ if (text.right(1) == ".")
+ text += " ";
+ else
+ text += ". ";
+ }
+ if (d->m_speakFlags & SpeakTooltip || text.isEmpty()) {
+ // kdDebug() << "pos = " << pos << endl;
+ // QPoint p = w->mapFromGlobal(pos);
+ // kdDebug() << "p = " << p << endl;
+ QString t = QToolTip::textFor(w, pos);
+ t = t.stripWhiteSpace();
+ if (!t.isEmpty()) {
+ if (t.right(1) != ".") t += ".";
+ text += t + " ";
+ }
+ }
+
+ if (d->m_speakFlags & SpeakWhatsThis || text.isEmpty()) {
+ QString t = QWhatsThis::textFor(w, pos);
+ t = t.stripWhiteSpace();
+ if (!t.isEmpty()) {
+ if (t.right(1) != ".") t += ".";
+ text += t + " ";
+ }
+ }
+
+ if (d->m_speakFlags & SpeakDisabled) {
+ if (!w->isEnabled())
+ text += i18n("A grayed widget", "Disabled. ");
+ }
+
+ return sayWidget(text);
+}
+
+bool KoSpeaker::sayWidget(const QString& msg)
+{
+ QString s = msg;
+ if (d->m_speakFlags & SpeakAccelerator) {
+ int amp = s.find("&");
+ if (amp >= 0) {
+ QString acc = s.mid(++amp,1);
+ acc = acc.stripWhiteSpace();
+ if (!acc.isEmpty())
+ s += ". " + d->m_acceleratorPrefix + " " + acc + ".";
+ }
+ }
+ s.remove("&");
+ if (QStyleSheet::mightBeRichText(s)) {
+ // kdDebug() << "richtext" << endl;
+ s.replace(QRegExp("</?[pbius]>"), "");
+ s.replace(QRegExp("</?h\\d>"), "");
+ s.replace(QRegExp("<(br|hr)>"), " ");
+ s.replace(QRegExp(
+ "</?(qt|center|li|pre|div|span|em|strong|big|small|sub|sup|code|tt|font|nobr|ul|ol|dl|dt)>"), "");
+ s.replace(QRegExp("</?(table|tr|th|td).*>"), "");
+ s.replace(QRegExp("</?a\\s.+>"), "");
+ // Replace <img source="small|frame_text"> with "small frame_text image. "
+ s.replace(QRegExp("<img\\s.*(?:source=|src=)\"([^|\"]+)[|]?([^|\"]*)\">"), "\\1 \\2 image. ");
+ }
+ if (s.isEmpty()) return false;
+ s.replace("Ctrl+", i18n("control plus "));
+ s.replace("Alt+", i18n("alt plus "));
+ s.replace("+", i18n(" plus "));
+ sayScreenReaderOutput(s, "");
+ return true;
+}
+
+// This doesn't work. Anybody know how to find the menu item underneath mouse pointer
+// in a QMenuBar?
+// int KoSpeaker::menuBarItemAt(QMenuBar* m, const QPoint& p)
+// {
+// for (uint i = 0; i < m->count(); i++) {
+// int id = m->idAt(i);
+// QMenuItem* mi = m->findItem(id);
+// QWidget* w = mi->widget();
+// if (w->rect().contains(w->mapFromGlobal(p))) return id;
+// }
+// return -1;
+// }
+
+/*static*/ bool KoSpeaker::isKttsdInstalled()
+{
+ KTrader::OfferList offers = KTrader::self()->query("DCOP/Text-to-Speech", "Name == 'KTTSD'");
+ return (offers.count() > 0);
+}
+
+bool KoSpeaker::startKttsd()
+{
+ DCOPClient *client = kapp->dcopClient();
+ // If KTTSD not running, start it.
+ if (!client->isApplicationRegistered("kttsd"))
+ {
+ QString error;
+ if (kapp->startServiceByDesktopName("kttsd", QStringList(), &error)) {
+ kdDebug() << "KoSpeaker::startKttsd: error starting KTTSD service: " << error << endl;
+ d->m_enabled = false;
+ } else
+ d->m_enabled = true;
+ } else
+ d->m_enabled = true;
+ return d->m_enabled;
+}
+
+QString KoSpeaker::getKttsdVersion()
+{
+ // Determine which version of KTTSD is running. Note that earlier versions of KSpeech interface
+ // did not support version() method, so we must manually marshall this call ourselves.
+ if (d->m_enabled) {
+ if (!d->m_versionChecked) {
+ DCOPClient *client = kapp->dcopClient();
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ if ( client->call("kttsd", "KSpeech", "version()", data, replyType, replyData, true) ) {
+ QDataStream arg(replyData, IO_ReadOnly);
+ arg >> d->m_kttsdVersion;
+ kdDebug() << "KoSpeaker::startKttsd: KTTSD version = " << d->m_kttsdVersion << endl;
+ }
+ d->m_versionChecked = true;
+ }
+ }
+ return d->m_kttsdVersion;
+}
+
+void KoSpeaker::sayScreenReaderOutput(const QString &msg, const QString &talker)
+{
+ if (msg.isEmpty()) return;
+ DCOPClient *client = kapp->dcopClient();
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << msg << talker;
+ if ( !client->call("kttsd", "KSpeech", "sayScreenReaderOutput(QString,QString)",
+ data, replyType, replyData, true) ) {
+ kdDebug() << "KoSpeaker::sayScreenReaderOutput: failed" << endl;
+ }
+}
+
+uint KoSpeaker::setText(const QString &text, const QString &talker)
+{
+ if (text.isEmpty()) return 0;
+ DCOPClient *client = kapp->dcopClient();
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << text << talker;
+ uint jobNum = 0;
+ if ( !client->call("kttsd", "KSpeech", "setText(QString,QString)",
+ data, replyType, replyData, true) ) {
+ kdDebug() << "KoSpeaker::sayText: failed" << endl;
+ } else {
+ QDataStream arg2(replyData, IO_ReadOnly);
+ arg2 >> jobNum;
+ }
+ return jobNum;
+}
+
+int KoSpeaker::appendText(const QString &text, uint jobNum /*=0*/)
+{
+ if (text.isEmpty()) return 0;
+ DCOPClient *client = kapp->dcopClient();
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << text << jobNum;
+ int partNum = 0;
+ if ( !client->call("kttsd", "KSpeech", "appendText(QString,uint)",
+ data, replyType, replyData, true) ) {
+ kdDebug() << "KoSpeaker::appendText: failed" << endl;
+ } else {
+ QDataStream arg2(replyData, IO_ReadOnly);
+ arg2 >> partNum;
+ }
+ return partNum;
+}
+
+void KoSpeaker::startText(uint jobNum /*=0*/)
+{
+ DCOPClient *client = kapp->dcopClient();
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << jobNum;
+ if ( !client->call("kttsd", "KSpeech", "startText(uint)",
+ data, replyType, replyData, true) ) {
+ kdDebug() << "KoSpeaker::startText: failed" << endl;
+ }
+}
+
+void KoSpeaker::removeText(uint jobNum /*=0*/)
+{
+ DCOPClient *client = kapp->dcopClient();
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << jobNum;
+ if ( !client->call("kttsd", "KSpeech", "removeText(uint)",
+ data, replyType, replyData, true) ) {
+ kdDebug() << "KoSpeaker::removeText: failed" << endl;
+ }
+}
+
diff --git a/lib/kofficecore/KoSpeaker.h b/lib/kofficecore/KoSpeaker.h
new file mode 100644
index 00000000..6f8b430f
--- /dev/null
+++ b/lib/kofficecore/KoSpeaker.h
@@ -0,0 +1,204 @@
+/** @file
+* This file is part of the KDE/KOffice project.
+* Copyright (C) 2005, Gary Cramblitt <garycramblitt@comcast.net>
+*
+* @author Gary Cramblitt <garycramblitt@comcast.net>
+* @since KOffice 1.5
+*
+* 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 KOSPEAKER_H
+#define KOSPEAKER_H
+
+// Qt includes.
+#include <qobject.h>
+#include <qstring.h>
+
+// KDE includes.
+#include <ksharedptr.h>
+
+// KOffice includes.
+#include <koffice_export.h>
+
+class QWidget;
+class QPoint;
+class KConfig;
+class KoSpeakerPrivate;
+
+#define kospeaker KoSpeaker::koSpeaker()
+
+/** KoSpeaker is a singleton object that provides Text-to-Speech services for KOffice applications.
+ * When activated, it will speak the text of widgets under the mouse pointer and/or the
+ * the widget with focus.
+ *
+ * It also provides some methods for speaking text from documents.
+ *
+ * IMPORTANT: This class will be removed from KOffice when KOffice is converted to KDE4.
+ * It will be replaced with a proper screen reading capability using the AT-SPI.
+ *
+ * This is quite a hack and doesn't work reliably. The following are current problems:
+ * 1. Cannot speak menu items in a QMenuBar (top menu of app).
+ * 2. Doesn't understand every possible widget.
+ *
+ * This capability is @em not intended for completely blind users. Such users cannot use
+ * KDE 3.x anyway, since it lacks a screen reader. Instead, this capability is intended as
+ * an aid to users with other vision disabilities.
+ *
+ * KOffice applications can access this object using the kospeaker global.
+ */
+class KOFFICECORE_EXPORT KoSpeaker : public QObject, public KShared
+{
+ Q_OBJECT
+public:
+ KoSpeaker();
+ ~KoSpeaker();
+
+ /** Speech Options */
+ enum SpeakFlags {
+ SpeakFocusWidget = 0x0001 /**< Speak widget with focus */,
+ SpeakPointerWidget = 0x0002 /**< Speak widget under mouse pointer */,
+ SpeakWhatsThis = 0x0004 /**< Speak Whats This if available */,
+ SpeakTooltip = 0x0008 /**< Speak tooltip if available */,
+ SpeakAccelerator = 0x0010 /**< Speak accelerator */,
+ SpeakDisabled = 0x0020 /**< Say 'disabled' if not enabled */
+ };
+
+ /**
+ * Returns true if TTS services are available. If KTTSD daemon is not running, it is started.
+ * Will return false if:
+ * -- KTTSD daemon is not installed, or
+ * -- Was not able to start KTTSD daemon for some reason.
+ */
+ bool isEnabled() const;
+
+ /**
+ * Reads configuration options from @p config object and starts TTS if screen reader
+ * capability is requested.
+ * If KTTSD daemon is not installed, @ref isEnabled will return false.
+ * If screen reader is requested and KTTSD is installed, but not running, it will be started.
+ */
+ void readConfig(KConfig* config);
+
+ /**
+ * Given a widget @p w and its @p pos screen coordinates, tries to extract the text of the widget
+ * and speak it. If @p pos is not specified, and the widget has multiple parts (such as
+ * a QListView), uses the current part.
+ * Call @ref isEnabled to ensure TTS is available before calling this method.
+ */
+ bool maybeSayWidget(QWidget* w, const QPoint& pos = QPoint());
+
+ /**
+ * Speak a @p msg that came from a widget, such as the widget's text label, tool tip, etc.
+ * Speaks using ScreenReaderOutput, which has highest priority, and therefore, should only be
+ * be used in very time-sensitive contexts and for short messages.
+ * Certain standard substitutions are performed on the message. For example, "Ctrl+" becomes
+ * "control plus". "Qt" markup is stripped.
+ * @returns true if anything is actually spoken.
+ * Call @ref isEnabled to ensure TTS is available before calling this method.
+ */
+ bool sayWidget(const QString& msg);
+
+ /**
+ * Cancels speaking of widget. Usually called by slots that receive @ref customSpeakNewWidget
+ * signal when they wish to speak the widget themselves.
+ */
+ void cancelSpeakWidget();
+
+ /**
+ * Queue a @p msg as a speech text job. The text is encoded in the @p langCode language.
+ * Examples "en", "es", "en_US". If not specified, defaults to current desktop setting.
+ * If @p first is true and a job is already speaking, cancel it.
+ * If @p first is false, appends to the already queued job.
+ * If the KTTSD daemon is not already running, it is started.
+ */
+ void queueSpeech(const QString& msg, const QString& langCode = QString(), bool first = true);
+
+ /**
+ * Start speaking queued text job (if any).
+ */
+ void startSpeech();
+
+ /**
+ * Returns whether the KTTSD deamon is installed in the system. If not, apps should disable
+ * or hide options/commands to speak.
+ */
+ static bool isKttsdInstalled();
+
+ /**
+ * Returns the KoSpeaker object singleton. Apps should use "kospeaker" rather than this function
+ * directly.
+ */
+ static KoSpeaker* koSpeaker() { return KSpkr; }
+
+signals:
+ /**
+ * This signal is emitted whenever a new widget has received focus or the mouse pointer
+ * has moved to a new widget. If a receiver wishes to handle speaking of the widget itself,
+ * it should call @ref cancelSpeakWidget() .
+ * @param w The widget.
+ * @param p Mouse pointer global coordinates, or in the case of a focus change (0,0).
+ * @param flags Speech options. @ref SpeakFlags.
+ *
+ * IMPORTANT: This signal is emitted from the @ref maybeSayWidget method. Slots who
+ * call maybeSayWidget should take care to avoid infinite recursion.
+ */
+ void customSpeakNewWidget(QWidget* w, const QPoint& p, uint flags);
+
+ /**
+ * This signal is emitted each polling interval when KoSpeaker did not speak the widget
+ * (either because it did not think the widget was a new one or because it did not
+ * understand the widget). If both mouse pointer and focus flags are set, it may
+ * emit twice per polling interval.
+ * @param w The widget.
+ * @param p Mouse pointer global coordinates, or in the case of a focus change (0,0).
+ * @param flags Speech options. @ref SpeakFlags.
+ *
+ * IMPORTANT: This signal is emitted frequently. Receivers should be coded efficiently.
+ */
+ void customSpeakWidget(QWidget* w, const QPoint& p, uint flags);
+
+protected:
+ static KoSpeaker* KSpkr;
+
+private slots:
+ /**
+ * Tells the class to do it's stuff - ie. figure out
+ * which widget is under the mouse pointer or which has focus and speak it.
+ */
+ void probe();
+
+private:
+ // int menuBarItemAt(QMenuBar* m, const QPoint& p);
+
+ // Start the KTTSD daemon if not already running.
+ bool startKttsd();
+ // Return the KTTSD daemon version string.
+ QString getKttsdVersion();
+
+ // These methods correspond to dcop interface in kdelibs/interfaces/kspeech/kspeech.h.
+ // They use manual marshalling, instead of using kspeech_stub, because KOffice
+ // supports KDE 3.3 and above and kspeech.h didn't appear until 3.4.
+ void sayScreenReaderOutput(const QString &msg, const QString &talker);
+ uint setText(const QString &text, const QString &talker);
+ int appendText(const QString &text, uint jobNum=0);
+ void startText(uint jobNum=0);
+ void removeText(uint jobNum=0);
+
+ KoSpeakerPrivate* d;
+};
+
+#endif // H_KOSPEAKER
diff --git a/lib/kofficecore/KoStyleStack.cpp b/lib/kofficecore/KoStyleStack.cpp
new file mode 100644
index 00000000..4b5b807b
--- /dev/null
+++ b/lib/kofficecore/KoStyleStack.cpp
@@ -0,0 +1,296 @@
+/* This file is part of the KDE project
+ Copyright (c) 2003 Lukas Tinkl <lukas@kde.org>
+ Copyright (c) 2003 David Faure <faure@kde.org>
+
+ 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.
+*/
+
+#include "KoStyleStack.h"
+#include "KoUnit.h"
+#include "KoDom.h"
+#include "KoXmlNS.h"
+
+#include <kdebug.h>
+
+//#define DEBUG_STYLESTACK
+
+KoStyleStack::KoStyleStack()
+ : m_styleNSURI( KoXmlNS::style ), m_foNSURI( KoXmlNS::fo )
+{
+ clear();
+}
+
+KoStyleStack::KoStyleStack( const char* styleNSURI, const char* foNSURI )
+ : m_propertiesTagName( "properties" ), m_styleNSURI( styleNSURI ), m_foNSURI( foNSURI )
+{
+ clear();
+}
+
+KoStyleStack::~KoStyleStack()
+{
+}
+
+void KoStyleStack::clear()
+{
+ m_stack.clear();
+#ifdef DEBUG_STYLESTACK
+ kdDebug(30003) << "clear!" << endl;
+#endif
+}
+
+void KoStyleStack::save()
+{
+ m_marks.push( m_stack.count() );
+#ifdef DEBUG_STYLESTACK
+ kdDebug(30003) << "save (level " << m_marks.count() << ") -> index " << m_stack.count() << endl;
+#endif
+}
+
+void KoStyleStack::restore()
+{
+ Q_ASSERT( !m_marks.isEmpty() );
+ int toIndex = m_marks.pop();
+#ifdef DEBUG_STYLESTACK
+ kdDebug(30003) << "restore (level " << m_marks.count()+1 << ") -> to index " << toIndex << endl;
+#endif
+ Q_ASSERT( toIndex > -1 );
+ Q_ASSERT( toIndex <= (int)m_stack.count() ); // If equal, nothing to remove. If greater, bug.
+ for ( int index = (int)m_stack.count() - 1; index >= toIndex; --index )
+ m_stack.pop_back();
+}
+
+void KoStyleStack::pop()
+{
+ Q_ASSERT( !m_stack.isEmpty() );
+ m_stack.pop_back();
+#ifdef DEBUG_STYLESTACK
+ kdDebug(30003) << "pop -> count=" << m_stack.count() << endl;
+#endif
+}
+
+void KoStyleStack::push( const QDomElement& style )
+{
+ m_stack.append( style );
+#ifdef DEBUG_STYLESTACK
+ kdDebug(30003) << "pushed " << style.attributeNS( m_styleNSURI, "name", QString::null ) << " -> count=" << m_stack.count() << endl;
+#endif
+}
+
+bool KoStyleStack::hasAttribute( const QString& name, const QString& detail ) const
+{
+ QString fullName( name );
+ if ( !detail.isEmpty() )
+ {
+ fullName += '-';
+ fullName += detail;
+ }
+ QValueList<QDomElement>::ConstIterator it = m_stack.end();
+ while ( it != m_stack.begin() )
+ {
+ --it;
+ QDomElement properties = (*it).namedItem( "style:"+m_propertiesTagName ).toElement();
+ if ( properties.hasAttribute( name ) ||
+ ( !detail.isEmpty() && properties.hasAttribute( fullName ) ) )
+ return true;
+ }
+ return false;
+}
+
+QString KoStyleStack::attribute( const QString& name, const QString& detail ) const
+{
+ QString fullName( name );
+ if ( !detail.isEmpty() )
+ {
+ fullName += '-';
+ fullName += detail;
+ }
+ QValueList<QDomElement>::ConstIterator it = m_stack.end();
+ while ( it != m_stack.begin() )
+ {
+ --it;
+ QDomElement properties = (*it).namedItem( "style:"+m_propertiesTagName ).toElement();
+ if ( properties.hasAttribute( name ) )
+ return properties.attribute( name );
+ if ( !detail.isEmpty() && properties.hasAttribute( fullName ) )
+ return properties.attribute( fullName );
+ }
+ return QString::null;
+}
+
+QString KoStyleStack::attributeNS( const char* nsURI, const char* name, const char* detail ) const
+{
+ QString fullName( name );
+ if ( detail )
+ {
+ fullName += '-';
+ fullName += detail;
+ }
+ QValueList<QDomElement>::ConstIterator it = m_stack.end();
+ while ( it != m_stack.begin() )
+ {
+ --it;
+ QDomElement properties = KoDom::namedItemNS( *it, m_styleNSURI, m_propertiesTagName );
+ if ( properties.hasAttributeNS( nsURI, name ) )
+ return properties.attributeNS( nsURI, name, QString::null );
+ if ( detail && properties.hasAttributeNS( nsURI, fullName ) )
+ return properties.attributeNS( nsURI, fullName, QString::null );
+ }
+ return QString::null;
+}
+
+bool KoStyleStack::hasAttributeNS( const char* nsURI, const char* name, const char* detail ) const
+{
+ QString fullName( name );
+ if ( detail )
+ {
+ fullName += '-';
+ fullName += detail;
+ }
+ QValueList<QDomElement>::ConstIterator it = m_stack.end();
+ while ( it != m_stack.begin() )
+ {
+ --it;
+ QDomElement properties = KoDom::namedItemNS( *it, m_styleNSURI, m_propertiesTagName );
+ if ( properties.hasAttributeNS( nsURI, name ) ||
+ ( detail && properties.hasAttributeNS( nsURI, fullName ) ) )
+ return true;
+ }
+ return false;
+}
+
+// Font size is a bit special. "115%" applies to "the fontsize of the parent style".
+// This can be generalized though (hasAttributeThatCanBePercentOfParent() ? :)
+// Although, if we also add support for fo:font-size-rel here then it's not general anymore.
+double KoStyleStack::fontSize() const
+{
+ const QString name = "font-size";
+ double percent = 1;
+ QValueList<QDomElement>::ConstIterator it = m_stack.end(); // reverse iterator
+
+ while ( it != m_stack.begin() )
+ {
+ --it;
+ QDomElement properties = KoDom::namedItemNS( *it, m_styleNSURI, m_propertiesTagName ).toElement();
+ if ( properties.hasAttributeNS( m_foNSURI, name ) ) {
+ const QString value = properties.attributeNS( m_foNSURI, name, QString::null );
+ if ( value.endsWith( "%" ) )
+ percent *= value.left( value.length() - 1 ).toDouble() / 100.0;
+ else
+ return percent * KoUnit::parseValue( value ); // e.g. 12pt
+ }
+ }
+ return 0;
+}
+
+bool KoStyleStack::hasChildNode(const QString & name) const
+{
+ QValueList<QDomElement>::ConstIterator it = m_stack.end();
+ while ( it != m_stack.begin() )
+ {
+ --it;
+ QDomElement properties = (*it).namedItem( "style:"+m_propertiesTagName ).toElement();
+ if ( !properties.namedItem( name ).isNull() )
+ return true;
+ }
+
+ return false;
+}
+
+QDomElement KoStyleStack::childNode(const QString & name) const
+{
+ QValueList<QDomElement>::ConstIterator it = m_stack.end();
+
+ while ( it != m_stack.begin() )
+ {
+ --it;
+ QDomElement properties = (*it).namedItem( "style:"+m_propertiesTagName ).toElement();
+ if ( !properties.namedItem( name ).isNull() )
+ return properties.namedItem( name ).toElement();
+ }
+
+ return QDomElement(); // a null element
+}
+
+bool KoStyleStack::hasChildNodeNS( const char* nsURI, const char* localName ) const
+{
+ QValueList<QDomElement>::ConstIterator it = m_stack.end();
+ while ( it != m_stack.begin() )
+ {
+ --it;
+ QDomElement properties = KoDom::namedItemNS( *it, m_styleNSURI, m_propertiesTagName );
+ if ( !KoDom::namedItemNS( properties, nsURI, localName ).isNull() )
+ return true;
+ }
+
+ return false;
+}
+
+QDomElement KoStyleStack::childNodeNS( const char* nsURI, const char* localName) const
+{
+ QValueList<QDomElement>::ConstIterator it = m_stack.end();
+
+ while ( it != m_stack.begin() )
+ {
+ --it;
+ QDomElement properties = KoDom::namedItemNS( *it, m_styleNSURI, m_propertiesTagName );
+ QDomElement e = KoDom::namedItemNS( properties, nsURI, localName );
+ if ( !e.isNull() )
+ return e;
+ }
+
+ return QDomElement(); // a null element
+}
+
+bool KoStyleStack::isUserStyle( const QDomElement& e, const QString& family ) const
+{
+ if ( e.attributeNS( m_styleNSURI, "family", QString::null ) != family )
+ return false;
+ const QDomElement parent = e.parentNode().toElement();
+ //kdDebug(30003) << k_funcinfo << "tagName=" << e.tagName() << " parent-tagName=" << parent.tagName() << endl;
+ return parent.localName() == "styles" /*&& parent.namespaceURI() == KoXmlNS::office*/;
+}
+
+QString KoStyleStack::userStyleName( const QString& family ) const
+{
+ QValueList<QDomElement>::ConstIterator it = m_stack.end();
+ while ( it != m_stack.begin() )
+ {
+ --it;
+ //kdDebug(30003) << k_funcinfo << (*it).attributeNS( m_styleNSURI, "name", QString::null) << endl;
+ if ( isUserStyle( *it, family ) )
+ return (*it).attributeNS( m_styleNSURI, "name", QString::null );
+ }
+ // Can this ever happen?
+ return "Standard";
+}
+
+QString KoStyleStack::userStyleDisplayName( const QString& family ) const
+{
+ QValueList<QDomElement>::ConstIterator it = m_stack.end();
+ while ( it != m_stack.begin() )
+ {
+ --it;
+ //kdDebug(30003) << k_funcinfo << (*it).attributeNS( m_styleNSURI, "display-name") << endl;
+ if ( isUserStyle( *it, family ) )
+ return (*it).attributeNS( m_styleNSURI, "display-name", QString::null );
+ }
+ return QString::null; // no display name, this can happen since it's optional
+}
+
+void KoStyleStack::setTypeProperties( const char* typeProperties )
+{
+ m_propertiesTagName = typeProperties == 0 ? QCString( "properties" ) : ( QCString( typeProperties ) + "-properties" );
+}
diff --git a/lib/kofficecore/KoStyleStack.h b/lib/kofficecore/KoStyleStack.h
new file mode 100644
index 00000000..f3c5faf1
--- /dev/null
+++ b/lib/kofficecore/KoStyleStack.h
@@ -0,0 +1,201 @@
+/* This file is part of the KDE project
+ Copyright (c) 2003 Lukas Tinkl <lukas@kde.org>
+ Copyright (c) 2003 David Faure <faure@kde.org>
+
+ 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 KOSTYLESTACK_H
+#define KOSTYLESTACK_H
+
+
+#include <qvaluelist.h>
+#include <qdom.h>
+#include <qvaluestack.h>
+
+#include <kdemacros.h>
+
+#include "koffice_export.h"
+
+/**
+ * @brief This class implements a stack for the different styles of an object.
+ *
+ * There can be several styles that are valid for one object. For example
+ * a textobject on a page has styles 'pr3' and 'P7' and a paragraph in
+ * that textobject has styles 'P1' and 'T3'. And some styles even have
+ * parent-styles...
+ *
+ * If you want to know if there is, for example, the attribute 'fo:font-family'
+ * for this paragraph, you have to look into style 'T3', 'P1', 'P7' and 'pr3'.
+ * When you find this attribute in one style you have to stop processing the list
+ * and take the found attribute for this object.
+ *
+ * This is what this class does. You can push styles on the stack while walking
+ * through the xml-tree to your object and then ask the stack if any of the styles
+ * provides a certain attribute. The stack will search from top to bottom, i.e.
+ * in our example from 'T3' to 'pr3' and return the first occurrence of the wanted
+ * attribute.
+ *
+ * So this is some sort of inheritance where the styles on top of the stack overwrite
+ * the same attribute of a lower style on the stack.
+ *
+ * In general though, you wouldn't use push/pop directly, but KoOasisLoadingContext::fillStyleStack
+ * or KoOasisLoadingContext::addStyles to automatically push a style and all its
+ * parent styles onto the stack.
+ */
+class KOFFICECORE_EXPORT KoStyleStack
+{
+public:
+ /**
+ * Create a OASIS style stack
+ */
+ KoStyleStack();
+ /**
+ * Create a style stack based on other namespaces than OASIS - used for OOo-1.1 import.
+ */
+ KoStyleStack( const char* styleNSURI, const char* foNSURI );
+ virtual ~KoStyleStack();
+
+ /**
+ * Clears the complete stack.
+ */
+ void clear();
+
+ /**
+ * Save the current state of the stack. Any items added between
+ * this call and its corresponding restore() will be removed when calling restore().
+ */
+ void save();
+
+ /**
+ * Restore the stack to the state it was at the corresponding save() call.
+ */
+ void restore();
+
+ /**
+ * Removes the style on top of the stack.
+ */
+ void pop();
+
+ /**
+ * Pushes the new style onto the stack.
+ */
+ void push( const QDomElement& style );
+
+ /**
+ * Check if any of the styles on the stack has an attribute called 'name'-'detail'
+ * where detail is e.g. left, right, top or bottom.
+ * This allows to also find 'name' alone (e.g. padding implies padding-left, padding-right etc.)
+ */
+ bool hasAttribute( const QString& name, const QString& detail = QString::null ) const KDE_DEPRECATED;
+
+ /**
+ * Search for the attribute called 'name', starting on top of the stack,
+ * and return it.
+ */
+ QString attribute( const QString& name, const QString& detail = QString::null ) const KDE_DEPRECATED;
+
+ /**
+ * Check if any of the styles on the stack has an attribute called 'name'-'detail'
+ * where detail is e.g. left, right, top or bottom.
+ * This allows to also find 'name' alone (e.g. padding implies padding-left, padding-right etc.)
+ */
+ bool hasAttributeNS( const char* nsURI, const char* localName, const char* detail = 0 ) const;
+
+ /**
+ * Search for the attribute called 'name', starting on top of the stack,
+ * and return it.
+ */
+ QString attributeNS( const char* nsURI, const char* localName, const char* detail = 0 ) const;
+
+ /**
+ * Check if any of the styles on the stack has a child node called 'name'.
+ */
+ bool hasChildNode( const QString & name ) const KDE_DEPRECATED;
+
+ /**
+ * Search for a child node called 'name', starting on top of the stack,
+ * and return it.
+ */
+ QDomElement childNode( const QString & name ) const KDE_DEPRECATED;
+
+ /**
+ * Check if any of the styles on the stack has a child element called 'localName' in the namespace 'nsURI'.
+ */
+ bool hasChildNodeNS( const char* nsURI, const char* localName ) const;
+
+ /**
+ * Search for a child element which has a child element called 'localName'
+ * in the namespace 'nsURI' starting on top of the stack,
+ * and return it.
+ */
+ QDomElement childNodeNS( const char* nsURI, const char* localName ) const;
+
+ /**
+ * Special case for the current font size, due to special handling of fo:font-size="115%".
+ */
+ double fontSize() const;
+
+ /**
+ * Return the name of the style specified by the user,
+ * i.e. not an auto style.
+ * This is used to know e.g. which user-style is associated with the current paragraph.
+ * There could be none though.
+ */
+ QString userStyleName( const QString& family ) const;
+
+ /**
+ * Return the display name of the style specified by the user,
+ * i.e. not an auto style
+ */
+ QString userStyleDisplayName( const QString& family ) const;
+
+ /**
+ * Set the type of properties that will be looked for.
+ * For instance setTypeProperties("paragraph") will make hasAttribute() and attribute()
+ * look into "paragraph-properties".
+ * If @p typeProperties is 0, the stylestack is resetted to look for "properties"
+ * as it does by default.
+ */
+ void setTypeProperties( const char* typeProperties );
+
+private:
+ bool isUserStyle( const QDomElement& e, const QString& family ) const;
+
+private:
+ /// For save/restore: stack of "marks". Each mark is an index in m_stack.
+ QValueStack<int> m_marks;
+
+ /**
+ * We use QValueList instead of QValueStack because we need access to all styles
+ * not only the top one.
+ */
+ QValueList<QDomElement> m_stack;
+
+ QCString m_propertiesTagName;
+
+ const char* m_styleNSURI;
+ const char* m_foNSURI;
+
+ class KoStyleStackPrivate;
+ KoStyleStackPrivate *d;
+
+ // forbidden
+ void operator=( const KoStyleStack& );
+ KoStyleStack( const KoStyleStack& );
+};
+
+#endif /* KOSTYLESTACK_H */
diff --git a/lib/kofficecore/KoTemplates.cpp b/lib/kofficecore/KoTemplates.cpp
new file mode 100644
index 00000000..7fbf3a99
--- /dev/null
+++ b/lib/kofficecore/KoTemplates.cpp
@@ -0,0 +1,379 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Werner Trobin <trobin@kde.org>
+
+ 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.
+*/
+
+#include <KoTemplates.h>
+
+#include <qdir.h>
+#include <qimage.h>
+#include <qprinter.h>
+
+#include <kdesktopfile.h>
+#include <ksimpleconfig.h>
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <kinstance.h>
+#include <ksavefile.h>
+#include <kstandarddirs.h>
+#include <kiconloader.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+
+#include <stdlib.h>
+
+
+KoTemplate::KoTemplate(const QString &name, const QString &description, const QString &file,
+ const QString &picture, const QString &fileName, const QString &_measureSystem,
+ bool hidden, bool touched) :
+ m_name(name), m_descr(description), m_file(file), m_picture(picture), m_fileName(fileName),
+ m_hidden(hidden), m_touched(touched), m_cached(false), m_measureSystem(_measureSystem)
+{
+}
+
+const QPixmap &KoTemplate::loadPicture( KInstance* instance ) {
+
+ if(m_cached)
+ return m_pixmap;
+ m_cached=true;
+ if ( m_picture[ 0 ] == '/' )
+ {
+ // ### TODO: use the class KoPicture instead of QImage to support non-image pictures
+ QImage img( m_picture );
+ if (img.isNull()) {
+ kdWarning() << "Couldn't find icon " << m_picture << endl;
+ m_pixmap=QPixmap();
+ return m_pixmap;
+ }
+ const int maxHeightWidth = 128; // ### TODO: some people would surely like to have 128x128
+ if (img.width() > maxHeightWidth || img.height() > maxHeightWidth) {
+ img = img.smoothScale( maxHeightWidth, maxHeightWidth, QImage::ScaleMax );
+ }
+ m_pixmap.convertFromImage(img);
+ return m_pixmap;
+ } else { // relative path
+ m_pixmap = instance->iconLoader()->loadIcon( m_picture, KIcon::Desktop, 128 );
+ return m_pixmap;
+ }
+}
+
+
+KoTemplateGroup::KoTemplateGroup(const QString &name, const QString &dir,
+ int _sortingWeight, bool touched) :
+ m_name(name), m_touched(touched), m_sortingWeight(_sortingWeight)
+{
+ m_dirs.append(dir);
+ m_templates.setAutoDelete(true);
+}
+
+bool KoTemplateGroup::isHidden() const {
+
+ QPtrListIterator<KoTemplate> it(m_templates);
+ bool hidden=true;
+ while(it.current()!=0L && hidden) {
+ hidden=it.current()->isHidden();
+ ++it;
+ }
+ return hidden;
+}
+
+void KoTemplateGroup::setHidden(bool hidden) const {
+
+ QPtrListIterator<KoTemplate> it(m_templates);
+ for( ; it.current()!=0L; ++it)
+ it.current()->setHidden(hidden);
+ m_touched=true;
+}
+
+bool KoTemplateGroup::add(KoTemplate *t, bool force, bool touch) {
+
+ KoTemplate *myTemplate=find(t->name());
+ if(myTemplate==0L) {
+ m_templates.append(t);
+ m_touched=touch;
+ return true;
+ }
+ else if(myTemplate && force) {
+ //kdDebug() << "removing :" << myTemplate->fileName() << endl;
+ QFile::remove( myTemplate->fileName() );
+ QFile::remove( myTemplate->picture() );
+ QFile::remove( myTemplate->file() );
+ m_templates.removeRef(myTemplate);
+ m_templates.append(t);
+ m_touched=touch;
+ return true;
+ }
+ return false;
+}
+
+KoTemplate *KoTemplateGroup::find(const QString &name) const {
+
+ QPtrListIterator<KoTemplate> it(m_templates);
+ while(it.current() && it.current()->name()!=name)
+ ++it;
+ return it.current();
+}
+
+
+KoTemplateTree::KoTemplateTree(const QCString &templateType,
+ KInstance *instance, bool readTree) :
+ m_templateType(templateType), m_instance(instance), m_defaultGroup(0L),
+ m_defaultTemplate(0L) {
+
+ m_groups.setAutoDelete(true);
+ if(readTree)
+ readTemplateTree();
+}
+
+void KoTemplateTree::readTemplateTree() {
+
+ readGroups();
+ readTemplates();
+}
+
+void KoTemplateTree::writeTemplateTree() {
+ QString localDir=m_instance->dirs()->saveLocation(m_templateType);
+
+ for(KoTemplateGroup *group=m_groups.first(); group!=0L; group=m_groups.next()) {
+ //kdDebug() << "---------------------------------" << endl;
+ //kdDebug() << "group: " << group->name() << endl;
+
+ bool touched=false;
+ for(KoTemplate *t=group->first(); t!=0L && !touched && !group->touched(); t=group->next())
+ touched=t->touched();
+
+ if(group->touched() || touched) {
+ //kdDebug() << "touched" << endl;
+ if(!group->isHidden()) {
+ //kdDebug() << "not hidden" << endl;
+ KStandardDirs::makeDir(localDir+group->name()); // create the local group dir
+ }
+ else {
+ //kdDebug() << "hidden" << endl;
+ if(group->dirs().count()==1 && !group->dirs().grep(localDir).isEmpty()) {
+ //kdDebug() << "local only" << endl;
+ KIO::NetAccess::del(group->dirs().first(), 0);
+ //kdDebug() << "removing: " << group->dirs().first() << endl;
+ }
+ else {
+ //kdDebug() << "global" << endl;
+ KStandardDirs::makeDir(localDir+group->name());
+ }
+ }
+ }
+ for(KoTemplate *t=group->first(); t!=0L; t=group->next()) {
+ if(t->touched()) {
+ //kdDebug() << "++template: " << t->name() << endl;
+ writeTemplate(t, group, localDir);
+ }
+ if(t->isHidden() && t->touched() ) {
+ //kdDebug() << "+++ delete local template ##############" << endl;
+ writeTemplate(t, group, localDir);
+ QFile::remove(t->file());
+ QFile::remove(t->picture());
+ }
+ }
+ }
+}
+
+void KoTemplateTree::add(KoTemplateGroup *g) {
+
+ KoTemplateGroup *group=find(g->name());
+ if(group==0L)
+ m_groups.append(g);
+ else
+ group->addDir(g->dirs().first()); // "...there can be only one..." (Queen)
+}
+
+KoTemplateGroup *KoTemplateTree::find(const QString &name) const {
+
+ QPtrListIterator<KoTemplateGroup> it(m_groups);
+ while(it.current() && it.current()->name()!=name)
+ ++it;
+ return it.current();
+}
+
+void KoTemplateTree::readGroups() {
+
+ QStringList dirs = m_instance->dirs()->resourceDirs(m_templateType);
+ for(QStringList::ConstIterator it=dirs.begin(); it!=dirs.end(); ++it) {
+ //kdDebug() << "dir: " << *it << endl;
+ QDir dir(*it);
+ // avoid the annoying warning
+ if(!dir.exists())
+ continue;
+ dir.setFilter(QDir::Dirs);
+ QStringList templateDirs=dir.entryList();
+ for(QStringList::ConstIterator tdirIt=templateDirs.begin(); tdirIt!=templateDirs.end(); ++tdirIt) {
+ if(*tdirIt=="." || *tdirIt=="..") // we don't want to check those dirs :)
+ continue;
+ QDir templateDir(*it+*tdirIt);
+ QString name=*tdirIt;
+ QString defaultTab;
+ int sortingWeight = 1000;
+ if(templateDir.exists(".directory")) {
+ KSimpleConfig config(templateDir.absPath()+"/.directory", true);
+ config.setDesktopGroup();
+ name=config.readEntry("Name");
+ defaultTab=config.readEntry("X-KDE-DefaultTab");
+ sortingWeight=config.readNumEntry("X-KDE-SortingWeight", 1000);
+ //kdDebug() << "name: " << name <<endl;
+ }
+ KoTemplateGroup *g=new KoTemplateGroup(name, *it+*tdirIt+QChar('/'), sortingWeight);
+ add(g);
+ if(defaultTab=="true")
+ m_defaultGroup=g;
+ }
+ }
+}
+
+void KoTemplateTree::readTemplates() {
+ QString dontShow = "imperial";
+
+ if(KGlobal::locale()->pageSize() == QPrinter::Letter) {
+ dontShow = "metric";
+ }
+
+ QPtrListIterator<KoTemplateGroup> groupIt(m_groups);
+ for( ; groupIt.current()!=0L; ++groupIt) {
+ QStringList dirs=groupIt.current()->dirs();
+ for(QStringList::ConstIterator it=dirs.begin(); it!=dirs.end(); ++it) {
+ QDir d(*it);
+ if( !d.exists() )
+ continue;
+ QStringList files=d.entryList( QDir::Files | QDir::Readable, QDir::Name );
+ for(unsigned int i=0; i<files.count(); ++i) {
+ QString filePath = *it + files[i];
+ //kdDebug() << "filePath: " << filePath << endl;
+ QString icon;
+ QString text;
+ QString description;
+ QString hidden_str;
+ QString fileName;
+ bool hidden=false;
+ bool defaultTemplate = false;
+ QString templatePath;
+ QString measureSystem;
+ // If a desktop file, then read the name from it.
+ // Otherwise (or if no name in it?) use file name
+ if (KDesktopFile::isDesktopFile(filePath)) {
+ KSimpleConfig config(filePath, true);
+ config.setDesktopGroup();
+ if (config.readEntry("Type")=="Link") {
+ text=config.readEntry("Name");
+ fileName=filePath;
+ description=config.readEntry("Comment");
+ //kdDebug() << "name: " << text << endl;
+ icon=config.readEntry("Icon");
+ if(icon[0]!='/' && // allow absolute paths for icons
+ QFile::exists(*it+icon)) // allow icons from icontheme
+ icon=*it+icon;
+ //kdDebug() << "icon2: " << icon << endl;
+ hidden=config.readBoolEntry("X-KDE-Hidden", false);
+ defaultTemplate = config.readBoolEntry("X-KDE-DefaultTemplate", false);
+ measureSystem=config.readEntry("X-KDE-MeasureSystem").lower();
+
+ // Don't add a template that is for the wrong measure system
+ if(measureSystem == dontShow)
+ continue;
+
+ //kdDebug() << "hidden: " << hidden_str << endl;
+ templatePath=config.readPathEntry("URL");
+ //kdDebug() << "Link to : " << templatePath << endl;
+ if(templatePath[0]!='/') {
+ if(templatePath.left(6)=="file:/") // I doubt this will happen
+ templatePath=templatePath.right(templatePath.length()-6);
+ //else
+ // kdDebug() << "dirname=" << *it << endl;
+ templatePath=*it+templatePath;
+ //kdDebug() << "templatePath: " << templatePath << endl;
+ }
+ } else
+ continue; // Invalid
+ }
+ // The else if and the else branch are here for compat. with the old system
+ else if ( files[i].right(4) != ".png" )
+ // Ignore everything that is not a PNG file
+ continue;
+ else {
+ // Found a PNG file - the template must be here in the same dir.
+ icon = filePath;
+ QFileInfo fi(filePath);
+ text = fi.baseName();
+ templatePath = filePath; // Note that we store the .png file as the template !
+ // That's the way it's always been done. Then the app replaces the extension...
+ }
+ KoTemplate *t=new KoTemplate(text, description, templatePath, icon, fileName,
+ measureSystem, hidden);
+ groupIt.current()->add(t, false, false); // false -> we aren't a "user", false -> don't
+ // "touch" the group to avoid useless
+ // creation of dirs in .kde/blah/...
+ if ( defaultTemplate )
+ m_defaultTemplate = t;
+ }
+ }
+ }
+}
+
+void KoTemplateTree::writeTemplate(KoTemplate *t, KoTemplateGroup *group,
+ const QString &localDir) {
+ QString fileName;
+ if ( t->isHidden() )
+ {
+ fileName = t->fileName();
+ // try to remove the file
+ if ( QFile::remove(fileName) || !QFile::exists(fileName) )
+ {
+ QFile::remove( t->name() );
+ QFile::remove( t->picture() );
+ return;
+ }
+ }
+ // be sure that the template's file name is unique so we don't overwrite an other
+ QString const path = localDir + group->name() + '/';
+ QString const name = KoTemplates::stripWhiteSpace( t->name() );
+ fileName = path + name + ".desktop";
+ if ( t->isHidden() && QFile::exists(fileName) )
+ return;
+ QString fill;
+ while ( KIO::NetAccess::exists( fileName, true, 0 ) )
+ {
+ fill += '_';
+ fileName = path + fill + name + ".desktop";
+ }
+
+ KSimpleConfig config( fileName );
+ config.setDesktopGroup();
+ config.writeEntry("Type", "Link");
+ config.writePathEntry("URL", t->file());
+ config.writeEntry("Name", t->name());
+ config.writeEntry("Icon", t->picture());
+ config.writeEntry("X-KDE-Hidden", t->isHidden());
+}
+
+namespace KoTemplates {
+QString stripWhiteSpace(const QString &string) {
+
+ QString ret;
+ for(unsigned int i=0; i<string.length(); ++i) {
+ QChar tmp(string[i]);
+ if(!tmp.isSpace())
+ ret+=tmp;
+ }
+ return ret;
+}
+}
diff --git a/lib/kofficecore/KoTemplates.h b/lib/kofficecore/KoTemplates.h
new file mode 100644
index 00000000..4c9e3133
--- /dev/null
+++ b/lib/kofficecore/KoTemplates.h
@@ -0,0 +1,150 @@
+/*
+ This file is part of the KDE project
+ Copyright (C) 2000 Werner Trobin <trobin@kde.org>
+
+ 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 koTemplates_h
+#define koTemplates_h
+
+#include <qptrlist.h>
+#include <qstringlist.h>
+#include <qpixmap.h>
+#include <koffice_export.h>
+
+class KInstance;
+
+/** @internal */
+class KOFFICECORE_EXPORT KoTemplate {
+
+public:
+ KoTemplate(const QString &name,
+ const QString &description=QString::null,
+ const QString &file=QString::null,
+ const QString &picture=QString::null,
+ const QString &fileName=QString::null,
+ const QString &_measureSystem=QString::null,
+ bool hidden=false, bool touched=false);
+ ~KoTemplate() {}
+
+ QString name() const { return m_name; }
+ QString description() const { return m_descr; };
+ QString file() const { return m_file; }
+ QString picture() const { return m_picture; }
+ QString fileName() const { return m_fileName; }
+ const QPixmap &loadPicture( KInstance* instance );
+
+ bool isHidden() const { return m_hidden; }
+ void setHidden(bool hidden=true) { m_hidden=hidden; m_touched=true; }
+
+ bool touched() const { return m_touched; }
+
+ QString measureSystem() const { return m_measureSystem; }
+ void setMeasureSystem(const QString& system) { m_measureSystem = system; }
+
+private:
+ QString m_name, m_descr, m_file, m_picture, m_fileName;
+ bool m_hidden;
+ mutable bool m_touched;
+ bool m_cached;
+ QPixmap m_pixmap;
+ QString m_measureSystem;
+};
+
+
+class KOFFICECORE_EXPORT KoTemplateGroup {
+
+public:
+ KoTemplateGroup(const QString &name,
+ const QString &dir=QString::null,
+ int _sortingWeight=0,
+ bool touched=false);
+ ~KoTemplateGroup() {}
+
+ QString name() const { return m_name; }
+ QStringList dirs() const { return m_dirs; }
+ void addDir(const QString &dir) { m_dirs.append(dir); m_touched=true; }
+ int sortingWeight() const { return m_sortingWeight; }
+ void setSortingWeight(int weight) { m_sortingWeight = weight; }
+ /// If all children are hidden, we are hidden too
+ bool isHidden() const;
+ /// if we should hide, we hide all the children
+ void setHidden(bool hidden=true) const;
+
+ KoTemplate *first() { return m_templates.first(); }
+ KoTemplate *next() { return m_templates.next(); }
+ KoTemplate *last() { return m_templates.last(); }
+ KoTemplate *prev() { return m_templates.prev(); }
+ KoTemplate *current() { return m_templates.current(); }
+
+ bool add(KoTemplate *t, bool force=false, bool touch=true);
+ KoTemplate *find(const QString &name) const;
+
+ bool touched() const { return m_touched; }
+
+private:
+ QString m_name;
+ QStringList m_dirs;
+ QPtrList<KoTemplate> m_templates;
+ mutable bool m_touched;
+ int m_sortingWeight;
+};
+
+
+class KoTemplateTree {
+
+public:
+ KoTemplateTree(const QCString &templateType, KInstance *instance,
+ bool readTree=false);
+ ~KoTemplateTree() {}
+
+ QCString templateType() const { return m_templateType; }
+ KInstance *instance() const { return m_instance; }
+ void readTemplateTree();
+ void writeTemplateTree();
+
+ KoTemplateGroup *first() { return m_groups.first(); }
+ KoTemplateGroup *next() { return m_groups.next(); }
+ KoTemplateGroup *last() { return m_groups.last(); }
+ KoTemplateGroup *prev() { return m_groups.prev(); }
+ KoTemplateGroup *current() { return m_groups.current(); }
+
+ void add(KoTemplateGroup *g);
+ KoTemplateGroup *find(const QString &name) const;
+
+ KoTemplateGroup *defaultGroup() const { return m_defaultGroup; }
+ KoTemplate *defaultTemplate() const { return m_defaultTemplate; }
+
+private:
+ void readGroups();
+ void readTemplates();
+ void writeTemplate(KoTemplate *t, KoTemplateGroup *group,
+ const QString &localDir);
+
+ QCString m_templateType;
+ KInstance *m_instance;
+ QPtrList<KoTemplateGroup> m_groups;
+ KoTemplateGroup *m_defaultGroup;
+ KoTemplate *m_defaultTemplate;
+};
+
+
+namespace KoTemplates {
+QString stripWhiteSpace(const QString &string);
+}
+
+#endif
diff --git a/lib/kofficecore/KoUnit.cpp b/lib/kofficecore/KoUnit.cpp
new file mode 100644
index 00000000..fc459c51
--- /dev/null
+++ b/lib/kofficecore/KoUnit.cpp
@@ -0,0 +1,215 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 David Faure <faure@kde.org>
+ Copyright (C) 2004, Nicolas GOUTTE <goutte@kde.org>
+
+ 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.
+*/
+
+//#include <KoGlobal.h>
+#include "KoUnit.h"
+#include <KoXmlWriter.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kdebug.h>
+
+#include <qregexp.h>
+#include <qdom.h>
+
+QStringList KoUnit::listOfUnitName()
+{
+ QStringList lst;
+ for ( uint i = 0 ; i <= KoUnit::U_LASTUNIT ; ++i )
+ {
+ KoUnit::Unit unit = static_cast<KoUnit::Unit>( i );
+ lst.append( KoUnit::unitDescription( unit ) );
+ }
+ return lst;
+}
+
+QString KoUnit::unitDescription( Unit _unit )
+{
+ switch ( _unit )
+ {
+ case KoUnit::U_MM:
+ return i18n("Millimeters (mm)");
+ case KoUnit::U_CM:
+ return i18n("Centimeters (cm)");
+ case KoUnit::U_DM:
+ return i18n("Decimeters (dm)");
+ case KoUnit::U_INCH:
+ return i18n("Inches (in)");
+ case KoUnit::U_PI:
+ return i18n("Pica (pi)");
+ case KoUnit::U_DD:
+ return i18n("Didot (dd)");
+ case KoUnit::U_CC:
+ return i18n("Cicero (cc)");
+ case KoUnit::U_PT:
+ return i18n("Points (pt)" );
+ default:
+ return i18n("Error!");
+ }
+}
+
+double KoUnit::toUserValue( double ptValue, Unit unit )
+{
+ switch ( unit ) {
+ case U_MM:
+ return toMM( ptValue );
+ case U_CM:
+ return toCM( ptValue );
+ case U_DM:
+ return toDM( ptValue );
+ case U_INCH:
+ return toInch( ptValue );
+ case U_PI:
+ return toPI( ptValue );
+ case U_DD:
+ return toDD( ptValue );
+ case U_CC:
+ return toCC( ptValue );
+ case U_PT:
+ default:
+ return toPoint( ptValue );
+ }
+}
+
+double KoUnit::ptToUnit( const double ptValue, const Unit unit )
+{
+ switch ( unit )
+ {
+ case U_MM:
+ return POINT_TO_MM( ptValue );
+ case U_CM:
+ return POINT_TO_CM( ptValue );
+ case U_DM:
+ return POINT_TO_DM( ptValue );
+ case U_INCH:
+ return POINT_TO_INCH( ptValue );
+ case U_PI:
+ return POINT_TO_PI( ptValue );
+ case U_DD:
+ return POINT_TO_DD( ptValue );
+ case U_CC:
+ return POINT_TO_CC( ptValue );
+ case U_PT:
+ default:
+ return ptValue;
+ }
+}
+
+QString KoUnit::toUserStringValue( double ptValue, Unit unit )
+{
+ return KGlobal::locale()->formatNumber( toUserValue( ptValue, unit ) );
+}
+
+double KoUnit::fromUserValue( double value, Unit unit )
+{
+ switch ( unit ) {
+ case U_MM:
+ return MM_TO_POINT( value );
+ case U_CM:
+ return CM_TO_POINT( value );
+ case U_DM:
+ return DM_TO_POINT( value );
+ case U_INCH:
+ return INCH_TO_POINT( value );
+ case U_PI:
+ return PI_TO_POINT( value );
+ case U_DD:
+ return DD_TO_POINT( value );
+ case U_CC:
+ return CC_TO_POINT( value );
+ case U_PT:
+ default:
+ return value;
+ }
+}
+
+double KoUnit::fromUserValue( const QString& value, Unit unit, bool* ok )
+{
+ return fromUserValue( KGlobal::locale()->readNumber( value, ok ), unit );
+}
+
+double KoUnit::parseValue( QString value, double defaultVal )
+{
+ value.simplifyWhiteSpace();
+ value.remove( ' ' );
+
+ if( value.isEmpty() )
+ return defaultVal;
+
+ int index = value.find( QRegExp( "[a-z]+$" ) );
+ if ( index == -1 )
+ return value.toDouble();
+
+ QString unit = value.mid( index );
+ value.truncate ( index );
+ double val = value.toDouble();
+
+ if ( unit == "pt" )
+ return val;
+
+ bool ok;
+ Unit u = KoUnit::unit( unit, &ok );
+ if( ok )
+ return fromUserValue( val, u );
+
+ if( unit == "m" )
+ return fromUserValue( val * 10.0, U_DM );
+ else if( unit == "km" )
+ return fromUserValue( val * 10000.0, U_DM );
+ kdWarning() << "KoUnit::parseValue: Unit " << unit << " is not supported, please report." << endl;
+
+ // TODO : add support for mi/ft ?
+ return defaultVal;
+}
+
+KoUnit::Unit KoUnit::unit( const QString &_unitName, bool* ok )
+{
+ if ( ok )
+ *ok = true;
+ if ( _unitName == QString::fromLatin1( "mm" ) ) return U_MM;
+ if ( _unitName == QString::fromLatin1( "cm" ) ) return U_CM;
+ if ( _unitName == QString::fromLatin1( "dm" ) ) return U_DM;
+ if ( _unitName == QString::fromLatin1( "in" )
+ || _unitName == QString::fromLatin1("inch") /*compat*/ ) return U_INCH;
+ if ( _unitName == QString::fromLatin1( "pi" ) ) return U_PI;
+ if ( _unitName == QString::fromLatin1( "dd" ) ) return U_DD;
+ if ( _unitName == QString::fromLatin1( "cc" ) ) return U_CC;
+ if ( _unitName == QString::fromLatin1( "pt" ) ) return U_PT;
+ if ( ok )
+ *ok = false;
+ return U_PT;
+}
+
+QString KoUnit::unitName( Unit _unit )
+{
+ if ( _unit == U_MM ) return QString::fromLatin1( "mm" );
+ if ( _unit == U_CM ) return QString::fromLatin1( "cm" );
+ if ( _unit == U_DM ) return QString::fromLatin1( "dm" );
+ if ( _unit == U_INCH ) return QString::fromLatin1( "in" );
+ if ( _unit == U_PI ) return QString::fromLatin1( "pi" );
+ if ( _unit == U_DD ) return QString::fromLatin1( "dd" );
+ if ( _unit == U_CC ) return QString::fromLatin1( "cc" );
+ return QString::fromLatin1( "pt" );
+}
+
+void KoUnit::saveOasis(KoXmlWriter* settingsWriter, Unit _unit)
+{
+ settingsWriter->addConfigItem( "unit", unitName(_unit) );
+}
diff --git a/lib/kofficecore/KoUnit.h b/lib/kofficecore/KoUnit.h
new file mode 100644
index 00000000..07638215
--- /dev/null
+++ b/lib/kofficecore/KoUnit.h
@@ -0,0 +1,175 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>, Torben Weis <weis@kde.org>
+ Copyright (C) 2004, Nicolas GOUTTE <goutte@kde.org>
+
+ 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 kounit_h
+#define kounit_h
+#include <qstring.h>
+#include <qstringlist.h>
+#include <math.h> // for floor
+#include <koffice_export.h>
+
+class KoXmlWriter;
+class QDomElement;
+
+// 1 inch ^= 72 pt
+// 1 inch ^= 25.399956 mm (-pedantic ;p)
+// 1 pt = 1/12 pi
+// 1 pt ^= 0.0077880997 cc
+// 1 cc = 12 dd
+// Note: I don't use division but multiplication with the inverse value
+// because it's faster ;p (Werner)
+#define POINT_TO_MM(px) ((px)*0.352777167)
+#define MM_TO_POINT(mm) ((mm)*2.83465058)
+#define POINT_TO_CM(px) ((px)*0.0352777167)
+#define CM_TO_POINT(cm) ((cm)*28.3465058)
+#define POINT_TO_DM(px) ((px)*0.00352777167)
+#define DM_TO_POINT(dm) ((dm)*283.465058)
+#define POINT_TO_INCH(px) ((px)*0.01388888888889)
+#define INCH_TO_POINT(inch) ((inch)*72.0)
+#define MM_TO_INCH(mm) ((mm)*0.039370147)
+#define INCH_TO_MM(inch) ((inch)*25.399956)
+#define POINT_TO_PI(px)((px)*0.083333333)
+#define POINT_TO_DD(px)((px)*0.006490083)
+#define POINT_TO_CC(px)((px)*0.077880997)
+#define PI_TO_POINT(pi)((pi)*12)
+#define DD_TO_POINT(dd)((dd)*154.08124)
+#define CC_TO_POINT(cc)((cc)*12.840103)
+/**
+ * %KOffice stores everything in pt (using "double") internally.
+ * When displaying a value to the user, the value is converted to the user's unit
+ * of choice, and rounded to a reasonable precision to avoid 0.999999
+ */
+class KOFFICECORE_EXPORT KoUnit
+{
+public:
+ /** Length units supported by KOffice. */
+ enum Unit {
+ U_MM = 0,
+ U_PT = 1,
+ U_INCH = 2,
+ U_CM = 3,
+ U_DM = 4,
+ U_PI = 5, // pica
+ U_DD = 6, // didot
+ U_CC = 7, // cicero
+ U_LASTUNIT = U_CC // update when adding a new unit
+ // when adding a new unit, make sure to implement support for it in koRuler too
+ };
+
+ /// Prepare ptValue to be displayed in pt
+ static double toPoint( double ptValue ) {
+ // No conversion, only rounding (to 0.001 precision)
+ return floor( ptValue * 1000.0 ) / 1000.0;
+ }
+
+ /// Prepare ptValue to be displayed in mm
+ static double toMM( double ptValue ) {
+ // "mm" values are rounded to 0.0001 millimeters
+ return floor( POINT_TO_MM( ptValue ) * 10000.0 ) / 10000.0;
+ }
+
+ /// Prepare ptValue to be displayed in cm
+ static double toCM( double ptValue ) {
+ return floor( POINT_TO_CM( ptValue ) * 10000.0 ) / 10000.0;
+ }
+
+ /// Prepare ptValue to be displayed in dm
+ static double toDM( double ptValue ) {
+ return floor( POINT_TO_DM( ptValue ) * 10000.0 ) / 10000.0;
+ }
+
+ /// Prepare ptValue to be displayed in inch
+ static double toInch( double ptValue ) {
+ // "in" values are rounded to 0.00001 inches
+ return floor( POINT_TO_INCH( ptValue ) * 100000.0 ) / 100000.0;
+ }
+
+ /// Prepare ptValue to be displayed in pica
+ static double toPI( double ptValue ) {
+ // "pi" values are rounded to 0.00001 inches
+ return floor( POINT_TO_PI( ptValue ) * 100000.0 ) / 100000.0;
+ }
+
+ /// Prepare ptValue to be displayed in didot
+ static double toDD( double ptValue ) {
+ // "dd" values are rounded to 0.00001 inches
+ return floor( POINT_TO_DD( ptValue ) * 100000.0 ) / 100000.0;
+ }
+
+ /// Prepare ptValue to be displayed in cicero
+ static double toCC( double ptValue ) {
+ // "cc" values are rounded to 0.00001 inches
+ return floor( POINT_TO_CC( ptValue ) * 100000.0 ) / 100000.0;
+ }
+
+ /**
+ * This method is the one to use to display a value in a dialog
+ * \return the value @p ptValue converted to @p unit and rounded, ready to be displayed
+ * Old name: ptToUnit
+ */
+ static double toUserValue( double ptValue, Unit unit );
+
+ /**
+ * Convert the value @p ptValue to a given unit @p unit
+ * Unlike KoUnit::ptToUnit the return value remains unrounded, so that it can be used in complex calculation
+ * \return the converted value
+ * Old name: ptToUnitUnrounded
+ */
+ static double ptToUnit( const double ptValue, const Unit unit );
+
+ /// This method is the one to use to display a value in a dialog
+ /// @return the value @p ptValue converted to @p unit and rounded, ready to be displayed
+ /// Old name: userValue
+ static QString toUserStringValue( double ptValue, Unit unit );
+
+ /// This method is the one to use to read a value from a dialog
+ /// @return the value in @p unit, converted to points for internal use
+ /// Old name: ptFromUnit
+ static double fromUserValue( double value, Unit unit );
+
+ /// This method is the one to use to read a value from a dialog
+ /// @param value value entered by the user
+ /// @param unit unit type selected by the user
+ /// @param ok if set, the pointed bool is set to true if the value could be
+ /// converted to a double, and to false otherwise.
+ /// @return the value in @p unit, converted to points for internal use
+ static double fromUserValue( const QString& value, Unit unit, bool* ok = 0 );
+
+ /// Convert a unit name into a Unit enum
+ /// @param _unitName name to convert
+ /// @param ok if set, it will be true if the unit was known, false if unknown
+ static Unit unit( const QString &_unitName, bool* ok = 0 );
+ /// Get the name of a unit
+ static QString unitName( Unit _unit );
+ /// Get the full (translated) description of a unit
+ static QString unitDescription( Unit _unit );
+ static QStringList listOfUnitName();
+
+ /// parse common %KOffice and OO values, like "10cm", "5mm" to pt
+ static double parseValue( QString value, double defaultVal = 0.0 );
+ // Note: the above method doesn't take a const ref, since it modifies the arg.
+
+ /// Save a unit in OASIS format
+ static void saveOasis(KoXmlWriter* settingsWriter, Unit _unit);
+
+};
+
+
+#endif
diff --git a/lib/kofficecore/KoView.cpp b/lib/kofficecore/KoView.cpp
new file mode 100644
index 00000000..9650192c
--- /dev/null
+++ b/lib/kofficecore/KoView.cpp
@@ -0,0 +1,868 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ 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.
+*/
+
+#include <KoView.h>
+#include <KoDocument.h>
+#include <KoMainWindow.h>
+#include <KoFrame.h>
+#include <KoViewIface.h>
+#include <KoDocumentChild.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <kparts/partmanager.h>
+#include <kparts/event.h>
+#include <kcursor.h>
+#include <assert.h>
+#include <kstatusbar.h>
+#include <kapplication.h>
+#include <qtimer.h>
+
+class KoViewPrivate
+{
+public:
+ KoViewPrivate()
+ {
+ m_inOperation = false;
+ m_zoom = 1.0;
+ m_children.setAutoDelete( true );
+ m_manager = 0L;
+ m_tempActiveWidget = 0L;
+ m_dcopObject = 0;
+ m_registered=false;
+ m_documentDeleted=false;
+ }
+ ~KoViewPrivate()
+ {
+ }
+
+ QGuardedPtr<KoDocument> m_doc; // our KoDocument
+ QGuardedPtr<KParts::PartManager> m_manager;
+ double m_zoom;
+ QPtrList<KoViewChild> m_children;
+ QWidget *m_tempActiveWidget;
+ KoViewIface *m_dcopObject;
+ bool m_registered; // are we registered at the part manager?
+ bool m_documentDeleted; // true when m_doc gets deleted [can't use m_doc==0
+ // since this only happens in ~QObject, and views
+ // get deleted by ~KoDocument].
+ QTimer *m_scrollTimer;
+
+ // Hmm sorry for polluting the private class with such a big inner class.
+ // At the beginning it was a little struct :)
+ class StatusBarItem {
+ public:
+ StatusBarItem() // for QValueList
+ : m_widget(0), m_visible(false)
+ {}
+ StatusBarItem( QWidget * widget, int stretch, bool permanent )
+ : m_widget(widget), m_stretch(stretch), m_permanent(permanent), m_visible(false)
+ {}
+
+ QWidget * widget() const { return m_widget; }
+
+ void ensureItemShown( KStatusBar * sb )
+ {
+ if ( !m_visible )
+ {
+ sb->addWidget( m_widget, m_stretch, m_permanent );
+ m_visible = true;
+ m_widget->show();
+ }
+ }
+ void ensureItemHidden( KStatusBar * sb )
+ {
+ if ( m_visible )
+ {
+ sb->removeWidget( m_widget );
+ m_visible = false;
+ m_widget->hide();
+ }
+ }
+ private:
+ QWidget * m_widget;
+ int m_stretch;
+ bool m_permanent;
+ bool m_visible; // true when the item has been added to the statusbar
+ };
+ QValueList<StatusBarItem> m_statusBarItems; // Our statusbar items
+ bool m_inOperation; //in the middle of an operation (no screen refreshing)?
+};
+
+KoView::KoView( KoDocument *document, QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ Q_ASSERT( document );
+
+ //kdDebug(30003) << "KoView::KoView " << this << endl;
+ d = new KoViewPrivate;
+ d->m_doc = document;
+ KParts::PartBase::setPartObject( this );
+
+ setFocusPolicy( StrongFocus );
+
+ setMouseTracking( true );
+
+ connect( d->m_doc, SIGNAL( childChanged( KoDocumentChild * ) ),
+ this, SLOT( slotChildChanged( KoDocumentChild * ) ) );
+
+ connect( d->m_doc, SIGNAL( sigBeginOperation() ),
+ this, SLOT( beginOperation() ) );
+
+ connect( d->m_doc, SIGNAL( sigEndOperation() ),
+ this, SLOT( endOperation() ) );
+
+
+ actionCollection()->setWidget( this );
+ setupGlobalActions();
+ KActionCollection *coll = actionCollection();
+ /**** not needed anymore, according to David (Werner)
+ QValueList<KAction*> docActions = document->actionCollection()->actions();
+ QValueList<KAction*>::ConstIterator it = docActions.begin();
+ QValueList<KAction*>::ConstIterator end = docActions.end();
+ for (; it != end; ++it )
+ coll->insert( *it );
+ */
+
+ KStatusBar * sb = statusBar();
+ if ( sb ) // No statusbar in e.g. konqueror
+ {
+ coll->setHighlightingEnabled( true );
+ connect( coll, SIGNAL( actionStatusText( const QString & ) ),
+ this, SLOT( slotActionStatusText( const QString & ) ) );
+ connect( coll, SIGNAL( clearStatusText() ),
+ this, SLOT( slotClearStatusText() ) );
+
+ connect( d->m_doc, SIGNAL( sigStatusBarMessage( const QString& ) ),
+ this, SLOT( slotActionStatusText( const QString& ) ) );
+ connect( d->m_doc, SIGNAL( sigClearStatusBarMessage() ),
+ this, SLOT( slotClearStatusText() ) );
+ }
+ d->m_doc->setCurrent();
+
+ d->m_scrollTimer = new QTimer( this );
+ connect (d->m_scrollTimer, SIGNAL( timeout() ), this, SLOT( slotAutoScroll() ) );
+}
+
+KoView::~KoView()
+{
+ kdDebug(30003) << "KoView::~KoView " << this << endl;
+ delete d->m_scrollTimer;
+ delete d->m_dcopObject;
+ if (!d->m_documentDeleted)
+ {
+ if ( koDocument() && !koDocument()->isSingleViewMode() )
+ {
+ if ( d->m_manager && d->m_registered ) // if we aren't registered we mustn't unregister :)
+ d->m_manager->removePart( koDocument() );
+ d->m_doc->removeView(this);
+ d->m_doc->setCurrent( false );
+ }
+ }
+ delete d;
+}
+
+KoDocument *KoView::koDocument() const
+{
+ return d->m_doc;
+}
+
+void KoView::setDocumentDeleted()
+{
+ d->m_documentDeleted = true;
+}
+
+bool KoView::documentDeleted() const
+{
+ return d->m_documentDeleted;
+}
+
+bool KoView::hasDocumentInWindow( KoDocument *doc )
+{
+ return child( doc ) != 0L;
+}
+
+void KoView::setPartManager( KParts::PartManager *manager )
+{
+ d->m_manager = manager;
+ if ( !koDocument()->isSingleViewMode() &&
+ manager->parts()->containsRef( koDocument() ) == 0 ) // is there another view registered?
+ {
+ d->m_registered = true; // no, so we have to register now and ungregister again in the DTOR
+ manager->addPart( koDocument(), false );
+ }
+ else
+ d->m_registered = false; // There is already another view registered for that part...
+}
+
+KParts::PartManager *KoView::partManager() const
+{
+ return d->m_manager;
+}
+
+KAction *KoView::action( const QDomElement &element ) const
+{
+ static const QString &attrName = KGlobal::staticQString( "name" );
+ QString name = element.attribute( attrName );
+
+ KAction *act = KXMLGUIClient::action( name.utf8() );
+
+ if ( !act )
+ act = d->m_doc->KXMLGUIClient::action( name.utf8() );
+
+ return act;
+}
+
+KoDocument *KoView::hitTest( const QPoint &viewPos )
+{
+ KoViewChild *viewChild;
+
+ QPoint pos = reverseViewTransformations( viewPos );
+
+ KoDocumentChild *docChild = selectedChild();
+ if ( docChild )
+ {
+ if ( ( viewChild = child( docChild->document() ) ) )
+ {
+ if ( viewChild->frameRegion().contains( pos ) )
+ return 0;
+ }
+ else
+ if ( docChild->frameRegion().contains( pos ) )
+ return 0;
+ }
+
+ docChild = activeChild();
+ if ( docChild )
+ {
+ if ( ( viewChild = child( docChild->document() ) ) )
+ {
+ if ( viewChild->frameRegion().contains( pos ) )
+ return 0;
+ }
+ else
+ if ( docChild->frameRegion().contains( pos ) )
+ return 0;
+ }
+
+ return koDocument()->hitTest( pos );
+}
+
+int KoView::leftBorder() const
+{
+ return 0;
+}
+
+int KoView::rightBorder() const
+{
+ return 0;
+}
+
+int KoView::topBorder() const
+{
+ return 0;
+}
+
+int KoView::bottomBorder() const
+{
+ return 0;
+}
+
+void KoView::setZoom( double zoom )
+{
+ d->m_zoom = zoom;
+ update();
+}
+
+double KoView::zoom() const
+{
+ return d->m_zoom;
+}
+
+QWidget *KoView::canvas() const
+{
+ //dfaure: since the view plays two roles in this method (the const means "you can modify the canvas
+ // but not the view", it's just coincidence that the view is the canvas by default ;)
+ return const_cast<KoView *>(this);
+}
+
+int KoView::canvasXOffset() const
+{
+ return 0;
+}
+
+int KoView::canvasYOffset() const
+{
+ return 0;
+}
+
+void KoView::canvasAddChild( KoViewChild * )
+{
+}
+
+void KoView::customEvent( QCustomEvent *ev )
+{
+ if ( KParts::PartActivateEvent::test( ev ) )
+ partActivateEvent( (KParts::PartActivateEvent *)ev );
+ else if ( KParts::PartSelectEvent::test( ev ) )
+ partSelectEvent( (KParts::PartSelectEvent *)ev );
+ else if( KParts::GUIActivateEvent::test( ev ) )
+ guiActivateEvent( (KParts::GUIActivateEvent*)ev );
+}
+
+void KoView::partActivateEvent( KParts::PartActivateEvent *event )
+{
+ if ( event->part() != (KParts::Part *)koDocument() )
+ {
+ assert( event->part()->inherits( "KoDocument" ) );
+
+ KoDocumentChild *child = koDocument()->child( (KoDocument *)event->part() );
+ if ( child && event->activated() )
+ {
+ if ( child->isRectangle() && !child->isTransparent() )
+ {
+ KoViewChild *viewChild = new KoViewChild( child, this );
+ d->m_children.append( viewChild );
+
+ QApplication::setOverrideCursor(waitCursor);
+ // This is the long operation (due to toolbar layout stuff)
+ d->m_manager->setActivePart( child->document(), viewChild->frame()->view() );
+ QApplication::restoreOverrideCursor();
+
+ // Now we can move the frame to the right place
+ viewChild->setInitialFrameGeometry();
+ }
+ else
+ {
+ emit regionInvalidated( child->frameRegion( matrix() ), true );
+ }
+ emit childActivated( child );
+ }
+ else if ( child )
+ {
+ emit regionInvalidated( child->frameRegion( matrix() ), true );
+ emit childDeactivated( child );
+ }
+ else
+ emit invalidated();
+ }
+ else
+ emit activated( event->activated() );
+}
+
+void KoView::partSelectEvent( KParts::PartSelectEvent *event )
+{
+ if ( event->part() != (KParts::Part *)koDocument() )
+ {
+ assert( event->part()->inherits( "KoDocument" ) );
+
+ KoDocumentChild *child = koDocument()->child( (KoDocument *)event->part() );
+
+ if ( child && event->selected() )
+ {
+ QRegion r = child->frameRegion( matrix() );
+ r.translate( - canvasXOffset(), - canvasYOffset() );
+ emit regionInvalidated( r, true );
+ emit childSelected( child );
+ }
+ else if ( child )
+ {
+ QRegion r = child->frameRegion( matrix() );
+ r.translate( - canvasXOffset(), - canvasYOffset() );
+ emit regionInvalidated( r, true );
+ emit childUnselected( child );
+ }
+ else
+ emit invalidated();
+ }
+ else
+ emit selected( event->selected() );
+}
+
+void KoView::guiActivateEvent( KParts::GUIActivateEvent * ev )
+{
+ showAllStatusBarItems( ev->activated() );
+}
+
+void KoView::showAllStatusBarItems( bool show )
+{
+ KStatusBar * sb = statusBar();
+ if ( !sb )
+ return;
+ QValueListIterator<KoViewPrivate::StatusBarItem> it = d->m_statusBarItems.begin();
+ for ( ; it != d->m_statusBarItems.end() ; ++it )
+ if ( show )
+ (*it).ensureItemShown( sb );
+ else
+ (*it).ensureItemHidden( sb );
+}
+
+void KoView::addStatusBarItem( QWidget * widget, int stretch, bool permanent )
+{
+ KoViewPrivate::StatusBarItem item( widget, stretch, permanent );
+ d->m_statusBarItems.append(item);
+ QValueListIterator<KoViewPrivate::StatusBarItem> it = d->m_statusBarItems.fromLast();
+ KStatusBar * sb = statusBar();
+ Q_ASSERT(sb);
+ if (sb)
+ (*it).ensureItemShown( sb );
+}
+
+void KoView::removeStatusBarItem( QWidget * widget )
+{
+ KStatusBar * sb = statusBar();
+ QValueListIterator<KoViewPrivate::StatusBarItem> it = d->m_statusBarItems.begin();
+ for ( ; it != d->m_statusBarItems.end() ; ++it )
+ if ( (*it).widget() == widget )
+ {
+ if ( sb )
+ (*it).ensureItemHidden( sb );
+ d->m_statusBarItems.remove( it );
+ break;
+ }
+ if ( it == d->m_statusBarItems.end() )
+ kdWarning() << "KoView::removeStatusBarItem. Widget not found : " << widget << endl;
+}
+
+KoDocumentChild *KoView::selectedChild()
+{
+ if ( !d->m_manager )
+ return 0L;
+
+ KParts::Part *selectedPart = d->m_manager->selectedPart();
+
+ if ( !selectedPart || !selectedPart->inherits( "KoDocument" ) )
+ return 0L;
+
+ return koDocument()->child( (KoDocument *)selectedPart );
+}
+
+KoDocumentChild *KoView::activeChild()
+{
+ if ( !d->m_manager )
+ return 0L;
+
+ KParts::Part *activePart = d->m_manager->activePart();
+
+ if ( !activePart || !activePart->inherits( "KoDocument" ) )
+ return 0L;
+
+ return koDocument()->child( (KoDocument *)activePart );
+}
+
+void KoView::enableAutoScroll( )
+{
+ d->m_scrollTimer->start( 50 );
+}
+
+void KoView::disableAutoScroll( )
+{
+ d->m_scrollTimer->stop();
+}
+
+void KoView::paintEverything( QPainter &painter, const QRect &rect, bool transparent )
+{
+ koDocument()->paintEverything( painter, rect, transparent, this );
+}
+
+KoViewChild *KoView::child( KoView *view )
+{
+ QPtrListIterator<KoViewChild> it( d->m_children );
+ for (; it.current(); ++it )
+ if ( it.current()->frame()->view() == view )
+ return it.current();
+
+ return 0L;
+}
+
+KoViewChild *KoView::child( KoDocument *doc )
+{
+ QPtrListIterator<KoViewChild> it( d->m_children );
+ for (; it.current(); ++it )
+ if ( it.current()->documentChild()->document() == doc )
+ return it.current();
+
+ return 0L;
+}
+
+QWMatrix KoView::matrix() const
+{
+ QWMatrix m;
+ m.scale( zoom(), zoom() );
+ //m.translate( canvasXOffset() , canvasYOffset() );
+ return m;
+}
+
+void KoView::slotChildActivated( bool a )
+{
+ // Only interested in deactivate events
+ if ( a )
+ return;
+
+ KoViewChild* ch = child( (KoView*)sender() );
+ if ( !ch )
+ return;
+
+ KoView* view = ch->frame()->view();
+
+ QWidget *activeWidget = view->d->m_tempActiveWidget;
+
+ if ( d->m_manager->activeWidget() )
+ activeWidget = d->m_manager->activeWidget();
+
+ if ( !activeWidget || !activeWidget->inherits( "KoView" ) )
+ return;
+
+ // Is the new active view a child of this one ?
+ // In this case we may not delete!
+ // QObject *n = d->m_manager->activeWidget();
+ QObject *n = activeWidget;
+ while( n )
+ if ( n == (QObject *)view )
+ return;
+ else
+ n = n->parent();
+
+
+ d->m_tempActiveWidget = activeWidget;
+ QApplication::setOverrideCursor(waitCursor);
+ d->m_manager->setActivePart( 0L );
+
+ QGuardedPtr<KoDocumentChild> docChild = ch->documentChild();
+ QGuardedPtr<KoFrame> chFrame = ch->frame();
+ if ( docChild && chFrame && chFrame->view() )
+ {
+ docChild->setContentsPos( chFrame->view()->canvasXOffset(),
+ chFrame->view()->canvasYOffset() );
+ docChild->document()->setViewBuildDocument( chFrame->view(), chFrame->view()->xmlguiBuildDocument() );
+ }
+
+ d->m_children.remove( ch );
+
+ d->m_manager->addPart( docChild->document(), false ); // the destruction of the view removed the part from the partmanager. re-add it :)
+
+ QApplication::restoreOverrideCursor();
+
+ // #### HACK
+ // We want to delete as many views as possible and this
+ // trick is used to go upwards in the view-tree.
+ emit activated( FALSE );
+}
+
+void KoView::slotChildChanged( KoDocumentChild *child )
+{
+ QRegion region( child->oldPointArray( matrix() ) );
+ emit regionInvalidated( child->frameRegion( matrix(), true ).unite( region ), true );
+}
+
+int KoView::autoScrollAcceleration( int offset ) const
+{
+ if(offset < 40)
+ return offset;
+ else
+ return offset*offset/40;
+}
+
+void KoView::slotAutoScroll( )
+{
+ QPoint scrollDistance;
+ bool actuallyDoScroll = false;
+ QPoint pos( mapFromGlobal( QCursor::pos() ) );
+
+ //Provide progressive scrolling depending on the mouse position
+ if ( pos.y() < topBorder() )
+ {
+ scrollDistance.setY ((int) - autoScrollAcceleration( - pos.y() + topBorder() ));
+ actuallyDoScroll = true;
+ }
+ else if ( pos.y() > height() - bottomBorder() )
+ {
+ scrollDistance.setY ((int) autoScrollAcceleration(pos.y() - height() + bottomBorder() ));
+ actuallyDoScroll = true;
+ }
+
+ if ( pos.x() < leftBorder() )
+ {
+ scrollDistance.setX ((int) - autoScrollAcceleration( - pos.x() + leftBorder() ));
+ actuallyDoScroll = true;
+ }
+ else if ( pos.x() > width() - rightBorder() )
+ {
+ scrollDistance.setX ((int) autoScrollAcceleration( pos.x() - width() + rightBorder() ));
+ actuallyDoScroll = true;
+ }
+
+ if ( actuallyDoScroll )
+ {
+ int state=0;
+#if KDE_IS_VERSION(3,4,0)
+ state = kapp->keyboardMouseState();
+#endif
+
+ pos = canvas()->mapFrom(this, pos);
+ QMouseEvent * event = new QMouseEvent(QEvent::MouseMove, pos, 0, state);
+
+ QApplication::postEvent( canvas(), event );
+ emit autoScroll( scrollDistance );
+ }
+}
+
+
+void KoView::setupGlobalActions() {
+ actionNewView = new KAction( i18n( "&New View" ), "window_new", 0,
+ this, SLOT( newView() ),
+ actionCollection(), "view_newview" );
+}
+
+void KoView::setupPrinter( KPrinter & )
+{
+ kdDebug() << "KoView::setupPrinter not implemented by the application!" << endl;
+}
+
+void KoView::print( KPrinter & )
+{
+ kdDebug() << "KoView::print not implemented by the application!" << endl;
+}
+
+void KoView::newView() {
+ assert( ( d!=0L && d->m_doc ) );
+
+ KoDocument *thisDocument = d->m_doc;
+ KoMainWindow *shell = new KoMainWindow( thisDocument->instance() );
+ shell->setRootDocument(thisDocument);
+ shell->show();
+}
+
+bool KoView::isInOperation() const
+{
+ return d->m_inOperation;
+}
+
+void KoView::beginOperation()
+{
+ d->m_inOperation = true;
+ canvas()->setUpdatesEnabled(FALSE);
+}
+
+void KoView::endOperation()
+{
+ canvas()->setUpdatesEnabled(TRUE);
+ d->m_inOperation = false;
+
+// canvas()->update();
+}
+
+KoMainWindow * KoView::shell() const
+{
+ return dynamic_cast<KoMainWindow *>( topLevelWidget() );
+}
+
+KMainWindow * KoView::mainWindow() const
+{
+ return dynamic_cast<KMainWindow *>( topLevelWidget() );
+}
+
+KStatusBar * KoView::statusBar() const
+{
+ KoMainWindow *mw = shell();
+ return mw ? mw->statusBar() : 0L;
+}
+
+void KoView::slotActionStatusText( const QString &text )
+{
+ KStatusBar *sb = statusBar();
+ if ( sb )
+ sb->message( text );
+}
+
+void KoView::slotClearStatusText()
+{
+ KStatusBar *sb = statusBar();
+ if ( sb )
+ sb->clear();
+}
+
+DCOPObject *KoView::dcopObject()
+{
+ if ( !d->m_dcopObject )
+ d->m_dcopObject = new KoViewIface( this );
+ return d->m_dcopObject;
+}
+
+class KoViewChild::KoViewChildPrivate
+{
+public:
+ KoViewChildPrivate()
+ {
+ }
+ ~KoViewChildPrivate()
+ {
+ }
+};
+
+KoViewChild::KoViewChild( KoDocumentChild *child, KoView *_parentView )
+{
+ d = new KoViewChildPrivate;
+ m_parentView = _parentView;
+ m_child = child;
+
+ m_frame = new KoFrame( parentView()->canvas() );
+ KoView *view = child->document()->createView( m_frame );
+ view->setXMLGUIBuildDocument( child->document()->viewBuildDocument( view ) );
+
+ view->setPartManager( parentView()->partManager() );
+
+ // hack? (Werner)
+ view->setZoom( parentView()->zoom() * QMAX(child->xScaling(), child->yScaling()) );
+
+ m_frame->setView( view );
+ m_frame->show();
+ m_frame->raise();
+
+ parentView()->canvasAddChild( this );
+
+
+ /*
+ KoViewChild has basically three geometries to keep in sync.
+ - The KoDocumentChild geometry (i.e. the embedded object's geometry, unzoomed)
+ - Its own geometry (used for hit-test etc.)
+ - The KoFrame geometry (the graphical widget for moving the object when active)
+
+ So we need to subtract the scrollview's offset for the frame geometry, since it's a widget.
+
+ The rules are
+ (R1) frameGeometry = viewGeometry(childGeometry) "+" m_frame->{left|right|top|bottom}Border() - scrollview offset,
+ (R2) frameGeometry = myGeometry "+" active_frame_border - scrollview offset.
+
+ So: (R3, unused) myGeometry = viewGeometry(childGeometry) "+" m_frame->{left|right|top|bottom}Border() "-" active_frame_border
+
+ Notes: active_frame_border is m_frame->border() (0 when inactive, 5 when active).
+ {left|right|top|bottom}Border are the borders used in kspread (0 when inactive, big when active).
+ "+" border means we add a border, so it's a subtraction on x, y and an addition on width, height.
+
+ viewGeometry() applies the zoom as well as any other translation the app might want to do
+ */
+
+ // Setting the frameGeometry is done in setInitialFrameGeometry, which is
+ // also called right after activation.
+
+ connect( view, SIGNAL( activated( bool ) ),
+ parentView(), SLOT( slotChildActivated( bool ) ) );
+}
+
+KoViewChild::~KoViewChild()
+{
+ if ( m_frame )
+ {
+ slotFrameGeometryChanged();
+ delete static_cast<KoFrame *>( m_frame );
+ }
+ delete d;
+}
+
+void KoViewChild::slotFrameGeometryChanged()
+{
+ // Set our geometry from the frame geometry (R2 reversed)
+ QRect geom = m_frame->geometry();
+ int b = m_frame->border();
+ QRect borderRect( geom.x() + b + parentView()->canvasXOffset(),
+ geom.y() + b + parentView()->canvasYOffset(),
+ geom.width() - b * 2,
+ geom.height() - b * 2 );
+ setGeometry( borderRect );
+
+ if(m_child)
+ {
+ // Set the child geometry from the frame geometry (R1 reversed)
+ QRect borderLessRect( geom.x() + m_frame->leftBorder() + parentView()->canvasXOffset(),
+ geom.y() + m_frame->topBorder() + parentView()->canvasYOffset(),
+ geom.width() - m_frame->leftBorder() - m_frame->rightBorder(),
+ geom.height() - m_frame->topBorder() - m_frame->bottomBorder() );
+
+ // We don't want to trigger slotDocGeometryChanged again
+ lock();
+ QRect childGeom = parentView()->reverseViewTransformations( borderLessRect );
+ kdDebug() << "KoChild::slotFrameGeometryChanged child geometry "
+ << ( geometry() == childGeom ? "already " : "set to " )
+ << childGeom << endl;
+ m_child->setGeometry( childGeom );
+ unlock();
+ }
+}
+
+void KoViewChild::slotDocGeometryChanged()
+{
+ if ( locked() )
+ return;
+ // Set frame geometry from child geometry (R1)
+ // The frame's resizeEvent will call slotFrameGeometryChanged.
+ QRect geom = parentView()->applyViewTransformations( m_child->geometry() );
+ QRect borderRect( geom.x() - m_frame->leftBorder() - parentView()->canvasXOffset(),
+ geom.y() - m_frame->topBorder() - parentView()->canvasYOffset(),
+ geom.width() + m_frame->leftBorder() + m_frame->rightBorder(),
+ geom.height() + m_frame->topBorder() + m_frame->bottomBorder() );
+ kdDebug() << "KoViewChild::slotDocGeometryChanged frame geometry "
+ << ( m_frame->geometry() == borderRect ? "already " : "set to " )
+ << borderRect << endl;
+
+ m_frame->setGeometry( borderRect );
+}
+
+QPoint KoView::applyViewTransformations( const QPoint& p ) const
+{
+ return QPoint( qRound( p.x() * zoom() ), qRound( p.y() * zoom() ) );
+}
+
+QPoint KoView::reverseViewTransformations( const QPoint& v ) const
+{
+ return QPoint( qRound( v.x() / zoom() ), qRound( v.y() / zoom() ) );
+}
+
+QRect KoView::applyViewTransformations( const QRect& r ) const
+{
+ return QRect( applyViewTransformations( r.topLeft() ),
+ applyViewTransformations( r.bottomRight() ) );
+}
+
+QRect KoView::reverseViewTransformations( const QRect& r ) const
+{
+ return QRect( reverseViewTransformations( r.topLeft() ),
+ reverseViewTransformations( r.bottomRight() ) );
+}
+
+void KoViewChild::setInitialFrameGeometry()
+{
+ kdDebug() << k_funcinfo << endl;
+
+ // Connect only now, so that the GUI building doesn't move us around.
+ connect( m_frame, SIGNAL( geometryChanged() ),
+ this, SLOT( slotFrameGeometryChanged() ) );
+ connect( m_child, SIGNAL( changed( KoChild * ) ),
+ this, SLOT( slotDocGeometryChanged() ) );
+
+ // Set frameGeometry from childGeometry
+ slotDocGeometryChanged();
+ // Set myGeometry from frameGeometry
+ slotFrameGeometryChanged();
+
+}
+
+#include "KoView.moc"
+
diff --git a/lib/kofficecore/KoView.h b/lib/kofficecore/KoView.h
new file mode 100644
index 00000000..c2be52ec
--- /dev/null
+++ b/lib/kofficecore/KoView.h
@@ -0,0 +1,502 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ 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 __koView_h__
+#define __koView_h__
+
+#include <qwidget.h>
+#include <qguardedptr.h>
+
+#include <kparts/part.h>
+
+#include <KoChild.h>
+#include <koffice_export.h>
+
+class KAction;
+class KActionCollection;
+class QCustomEvent;
+class KoDocument;
+class KoMainWindow;
+class KMainWindow;
+class KoViewPrivate;
+class KoViewChild;
+class KoFrame;
+class KStatusBar;
+class KInstance;
+class KPrinter;
+class KoDocumentChild;
+class DCOPObject;
+
+namespace KParts
+{
+ class PartManager;
+ class PartActivateEvent;
+ class PartSelectEvent;
+}
+
+/**
+ * This class is used to display a @ref KoDocument.
+ *
+ * Multiple views can be attached to one document at a time.
+ */
+class KOFFICECORE_EXPORT KoView : public QWidget, public KParts::PartBase
+{
+ Q_OBJECT
+public:
+ /**
+ * Creates a new view for the document. Usually you don't create views yourself
+ * since the KOffice components come with their own view classes which inherit
+ * KoView.
+ *
+ * The standard way to retrieve a KoView is to call @ref KoDocument::createView.
+ *
+ * @param document is the document which should be displayed in this view. This pointer
+ * must not be zero.
+ * @param parent parent widget for this view.
+ * @param name Name of the view. The name is used in DCOP, so the name should
+ * match the pattern [A-Za-z_][A-Za-z_0-9]*.
+ *
+ */
+ KoView( KoDocument *document, QWidget *parent = 0, const char *name = 0 );
+ /**
+ * Destroys the view and unregisters at the document.
+ */
+ virtual ~KoView();
+
+ /**
+ * Retrieves the document object of this view.
+ */
+ KoDocument *koDocument() const;
+
+ /**
+ * Tells this view that its document has got deleted (called internally)
+ */
+ void setDocumentDeleted();
+ /**
+ * @return true if the document has already got deleted.
+ * This can be useful for the view destructor to know if it can
+ * access the document or not.
+ */
+ bool documentDeleted() const;
+
+ virtual void setPartManager( KParts::PartManager *manager );
+ virtual KParts::PartManager *partManager() const;
+
+ /**
+ * Returns the action described action object. In fact only the "name" attribute
+ * of @p element is of interest here. The method searches in the
+ * KActionCollection of this view.
+ *
+ * Please notice that KoView indirectly inherits KXMLGUIClient.
+ *
+ * @see KXMLGUIClient
+ * @see KXMLGUIClient::actionCollection
+ * @see KoDocument::action
+ */
+ virtual KAction *action( const QDomElement &element ) const;
+
+ /**
+ * Retrieves the document that is hit. This can be an embedded document.
+ *
+ * The default implementation asks @ref KoDocument::hitTest. This
+ * will iterate over all child documents to detect a hit.
+ *
+ * If your koffice component has multiple pages, like for example KSpread, then the hittest
+ * may not succeed for a child that is not on the visible page. In those
+ * cases you need to reimplement this method.
+ */
+ virtual KoDocument *hitTest( const QPoint &pos );
+
+ /**
+ * Retrieves the left border width that is displayed around the content if
+ * the view is active.
+ *
+ * In a spread sheet this border is for example used to display the
+ * rows, while a top border is used to display the names of the cells
+ * and a right and bottom border is used to display scrollbars. If the view
+ * becomes inactive, then this stuff is not displayed anymore.
+ *
+ * @ref KoFrame uses this border information. If an embedded document becomes active
+ * then it is resized so that it has enough space to display the borders and to
+ * display the same content as before the activation.
+ * So if for example all of your borders are 20 pixels, then activating the embedded
+ * document causes the KoView to move 20 pixels up/left and the size and width increases
+ * by 20+20 pixels each.
+ *
+ * The default border is 0.
+ */
+ virtual int leftBorder() const;
+ /**
+ * @see #leftBorder
+ */
+ virtual int rightBorder() const;
+ /**
+ * @see #leftBorder
+ */
+ virtual int topBorder() const;
+ /**
+ * @see #leftBorder
+ */
+ virtual int bottomBorder() const;
+
+ /**
+ * Scales the view on the content. This does not affect the contents
+ * data structures. You can use this mechanism to implement a zoom
+ * for example.
+ *
+ * The method calls QWidget::update so that the scaled content
+ * is automatically displayed.
+ *
+ * The default scaling is 1.0 in both orientations.
+ */
+ virtual void setZoom( double zoom );
+ /**
+ * @return the current scaling factor (zoom level)
+ *
+ * @see #setZoom
+ */
+ virtual double zoom() const;
+
+ /**
+ * Overload this function if the content will be displayed
+ * on some child widget instead of the view directly.
+ *
+ * By default this function returns a pointer to the view.
+ */
+ virtual QWidget *canvas() const;
+
+ /**
+ * Overload this function if the content will be displayed
+ * with an offset relative to the upper left corner
+ * of the canvas widget.
+ *
+ * By default this function returns 0.
+ */
+ virtual int canvasXOffset() const;
+
+ /**
+ * Overload this function if the content will be displayed
+ * with an offset relative to the upper left corner
+ * of the canvas widget.
+ *
+ * By default this function returns 0.
+ */
+ virtual int canvasYOffset() const;
+
+ /**
+ * Overload this function if you need to perform some actions
+ * after KoView (the part widget) is inserted into canvas.
+ * You should call for example addChild(QWidget*) method
+ * of QScrollView here, if canvas is a viewport of QScrollView.
+ *
+ * By default this function does nothing.
+ */
+ virtual void canvasAddChild( KoViewChild *child );
+
+ /**
+ * @return the selected child. The function returns 0 if
+ * no direct child is currently selected.
+ */
+ virtual KoDocumentChild *selectedChild();
+
+ /**
+ * @return the active child. The function returns 0 if
+ * no direct child is currently active.
+ */
+ virtual KoDocumentChild *activeChild();
+
+ /**
+ * Sets up so that autoScroll signals are emitted when the mouse pointer is outside the view
+ */
+ void enableAutoScroll();
+
+ /**
+ * Stops the emitting of autoScroll signals
+ */
+ void disableAutoScroll();
+
+ /**
+ * calls KoDocument::paintEverything()
+ */
+ virtual void paintEverything( QPainter &painter, const QRect &rect, bool transparent = false );
+
+ /**
+ * @return TRUE if the document @p doc is represented in this view by
+ * some KoViewChild.
+ *
+ * This is just a convenience function for @ref #child.
+ */
+ bool hasDocumentInWindow( KoDocument *doc );
+
+ /**
+ * Returns the matrix which is used by the view to transform the content.
+ * Currently only scaling is supported.
+ *
+ * The matrix changes when calling @ref #setZoom.
+ *
+ * @deprecated, use applyViewTransformations / reverseViewTransformations instead.
+ */
+ virtual QWMatrix matrix() const KDE_DEPRECATED;
+
+ /**
+ * Apply the transformations that the view makes to its contents.
+ * This is used for embedded objects.
+ * By default this simply applies the zoom().
+ * Reimplement to add some translation if needed (e.g. to center the page)
+ */
+ virtual QPoint applyViewTransformations( const QPoint& ) const;
+
+ /**
+ * Reverse the transformations that the view makes to its contents,
+ * i.e. undo the transformations done by applyViewTransformations().
+ * This is used for embedded objects.
+ * By default this simply unzooms the point.
+ * Reimplement to add some translation if needed (e.g. to center the page)
+ */
+ virtual QPoint reverseViewTransformations( const QPoint& ) const;
+
+ /**
+ * Overload for QRect, usually it's not needed to reimplement this one.
+ */
+ virtual QRect applyViewTransformations( const QRect& ) const;
+
+ /**
+ * Overload for QRect, usually it's not needed to reimplement this one.
+ */
+ virtual QRect reverseViewTransformations( const QRect& ) const;
+
+ /**
+ * @return the KoViewChild which is responsible for the @p view or 0.
+ *
+ * This method does no recursion.
+ */
+ KoViewChild *child( KoView *view );
+ /**
+ * A convenience function which returns the KoViewChild which in turn holds the
+ * @ref KoView that in turn holds the @p document.
+ */
+ KoViewChild *child( KoDocument *document );
+
+ /**
+ * Return a DCOP interface for this view
+ * KOffice Applications are strongly recommended to reimplement this method,
+ * so that their dcop interface provides more functionality than the basic KoViewIface
+ */
+ virtual DCOPObject * dcopObject();
+
+ /**
+ * Overload this method to setup KPrinter before the actual printing.
+ *
+ * @see #print
+ */
+ virtual void setupPrinter( KPrinter &printer );
+
+ // BCI: make it return a bool, so that aborting doesn't still fire up the print preview afterwards
+ /**
+ * Overload this method with your own printing code.
+ */
+ virtual void print( KPrinter &printer );
+
+ /**
+ * @return the KoMainWindow in which this view is currently.
+ * WARNING: this could be 0L, if the main window isn't a koffice main window.
+ * (e.g. it can be any KParts application).
+ */
+ KoMainWindow * shell() const;
+
+ /**
+ * @return the KMainWindow in which this view is currently.
+ * This one should never return 0L, in a KDE app.
+ */
+ KMainWindow* mainWindow() const;
+
+ /**
+ * @return the statusbar of the KoMainWindow in which this view is currently.
+ * WARNING: this could be 0L, if the main window isn't a koffice main window.
+ * (e.g. it can be any KParts application).
+ */
+ KStatusBar * statusBar() const;
+
+ /**
+ * This adds a widget to the statusbar for this view.
+ * If you use this method instead of using statusBar() directly,
+ * KoView will take care of removing the items when the view GUI is deactivated
+ * and readding them when it is reactivated.
+ * The parameters are the same as QStatusBar::addWidget().
+ *
+ * Note that you can't use KStatusBar methods (inserting text items by id).
+ * But you can create a KStatusBarLabel with a dummy id instead, and use
+ * it directly, to get the same look and feel.
+ */
+ void addStatusBarItem( QWidget * widget, int stretch = 0, bool permanent = false );
+
+ /**
+ * Remove a widget from the statusbar for this view.
+ */
+ void removeStatusBarItem( QWidget * widget );
+
+ /**
+ * Show or hide all statusbar items. Used by KoMainWindow during saving.
+ */
+ void showAllStatusBarItems( bool show );
+
+ /**
+ * You have to implement this method and disable/enable certain functionality (actions for example) in
+ * your view to allow/disallow editing of the document.
+ */
+ virtual void updateReadWrite( bool readwrite ) = 0;
+
+ /**
+ * Check to see if the view is currently in the middle of an operation which means
+ * that there will be no screen refreshes until a signal from the document hits
+ * the @ref #endOperation slot
+ */
+ bool isInOperation() const;
+
+public slots:
+ /**
+ * Slot to create a new view around the contained @ref #koDocument.
+ */
+ virtual void newView();
+
+ /**
+ * Slot to allow code to signal the beginning of an operation where the screen should
+ * not update until it is done.
+ *
+ * @see #endOperation
+ */
+ virtual void beginOperation();
+
+ /**
+ * Slot to allow code to signal the end of an operation where the screen should
+ * not have been updating. So now it will update.
+ *
+ * @see #beginOperation
+ */
+ virtual void endOperation();
+
+ /**
+ * Display a message in the status bar (calls QStatusBar::message())
+ * @todo rename to something more generic
+ */
+ void slotActionStatusText( const QString &text );
+
+ /**
+ * End of the message in the status bar (calls QStatusBar::clear())
+ * @todo rename to something more generic
+ */
+ void slotClearStatusText();
+
+protected:
+ /**
+ * This method handles three events: KParts::PartActivateEvent, KParts::PartSelectEvent
+ * and KParts::GUIActivateEvent.
+ * The respective handlers are called if such an event is found.
+ */
+ virtual void customEvent( QCustomEvent *ev );
+
+ /**
+ * Handles the event KParts::PartActivateEvent.
+ */
+ virtual void partActivateEvent( KParts::PartActivateEvent *event );
+ /**
+ * Handles the event KParts::PartSelectEvent.
+ */
+ virtual void partSelectEvent( KParts::PartSelectEvent *event );
+ /**
+ * Handles the event KParts::GUIActivateEvent.
+ */
+ virtual void guiActivateEvent( KParts::GUIActivateEvent * );
+
+signals:
+ void activated( bool active );
+ void selected( bool select );
+
+ void autoScroll(const QPoint &scrollDistance);
+
+ void childSelected( KoDocumentChild *child );
+ void childUnselected( KoDocumentChild *child );
+
+ void childActivated( KoDocumentChild *child );
+ void childDeactivated( KoDocumentChild *child );
+
+ void regionInvalidated( const QRegion &region, bool erase );
+
+ void invalidated();
+
+// KDE invents public signals :)
+#undef signals
+#define signals public
+signals:
+
+ /**
+ * Make it possible for plugins to request
+ * the embedding of an image into the current
+ * document. Used e.g. by the scan-plugin
+ */
+ void embeddImage(const QString &filename);
+
+#undef signals
+#define signals protected
+
+protected slots:
+ virtual void slotChildActivated( bool a );
+ virtual void slotChildChanged( KoDocumentChild *child );
+ virtual void slotAutoScroll( );
+
+private:
+ KAction *actionNewView;
+ virtual void setupGlobalActions( void );
+ KoViewPrivate *d;
+ int autoScrollAcceleration( int offset ) const;
+};
+
+/**
+ * This class represents an active embedded document.
+ */
+class KoViewChild : public KoChild
+{
+ Q_OBJECT
+public:
+ KoViewChild( KoDocumentChild *child, KoView *_parentView );
+ virtual ~KoViewChild();
+
+ KoDocumentChild *documentChild() const { return m_child; }
+ KoView *parentView() const { return m_parentView; }
+ KoFrame *frame() const { return m_frame; }
+
+ void setInitialFrameGeometry();
+
+public slots:
+
+ // Call this when the view transformations change
+ void reposition() { slotDocGeometryChanged(); }
+
+private slots:
+ void slotFrameGeometryChanged();
+ void slotDocGeometryChanged();
+
+private:
+ QGuardedPtr<KoDocumentChild> m_child;
+ QGuardedPtr<KoFrame> m_frame;
+ QGuardedPtr<KoView> m_parentView;
+ class KoViewChildPrivate;
+ KoViewChildPrivate *d;
+};
+
+#endif
diff --git a/lib/kofficecore/KoViewIface.cc b/lib/kofficecore/KoViewIface.cc
new file mode 100644
index 00000000..378db1e6
--- /dev/null
+++ b/lib/kofficecore/KoViewIface.cc
@@ -0,0 +1,76 @@
+/* This file is part of the KDE project
+ Copyright (c) 2000 Simon Hausmann <hausmann@kde.org>
+
+ $Id: KoViewIface.cc 508787 2006-02-12 18:28:12Z ingwa $
+
+ 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.
+*/
+#include "KoViewIface.h"
+
+#include "KoView.h"
+
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kdcopactionproxy.h>
+#include <kaction.h>
+
+//static
+QCString KoViewIface::newIfaceName()
+{
+ static int s_viewIFNumber = 0;
+ QCString name; name.setNum( s_viewIFNumber++ ); name.prepend("View-");
+ return name;
+}
+
+KoViewIface::KoViewIface( KoView *view )
+ : DCOPObject( newIfaceName() )
+{
+ m_actionProxy = new KDCOPActionProxy( view->actionCollection(), this );
+}
+
+KoViewIface::KoViewIface( const char *name, KoView *view )
+ : DCOPObject( name )
+{
+ m_pView = view;
+ m_actionProxy = new KDCOPActionProxy( view->actionCollection(), this );
+}
+
+KoViewIface::~KoViewIface()
+{
+ delete m_actionProxy;
+}
+
+DCOPRef KoViewIface::action( const QCString &name )
+{
+ return DCOPRef( kapp->dcopClient()->appId(), m_actionProxy->actionObjectId( name ) );
+}
+
+QCStringList KoViewIface::actions()
+{
+ QCStringList res;
+ QValueList<KAction *> lst = m_actionProxy->actions();
+ QValueList<KAction *>::ConstIterator it = lst.begin();
+ QValueList<KAction *>::ConstIterator end = lst.end();
+ for (; it != end; ++it )
+ res.append( (*it)->name() );
+
+ return res;
+}
+
+QMap<QCString,DCOPRef> KoViewIface::actionMap()
+{
+ return m_actionProxy->actionMap();
+}
diff --git a/lib/kofficecore/KoViewIface.h b/lib/kofficecore/KoViewIface.h
new file mode 100644
index 00000000..1d01af64
--- /dev/null
+++ b/lib/kofficecore/KoViewIface.h
@@ -0,0 +1,56 @@
+/* This file is part of the KDE project
+ Copyright (c) 2000 Simon Hausmann <hausmann@kde.org>
+
+ $Id: KoViewIface.h 466447 2005-10-02 17:54:10Z zander $
+
+ 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 __KoViewIface_h__
+#define __KoViewIface_h__
+
+#include <dcopobject.h>
+#include <dcopref.h>
+
+#include "koffice_export.h"
+
+class KDCOPActionProxy;
+class KoView;
+
+class KOFFICECORE_EXPORT KoViewIface : public DCOPObject
+{
+ K_DCOP
+public:
+ KoViewIface( KoView *view );
+ KoViewIface( const char *name, KoView *view );
+ // TODO same args order as KoDocumentIface
+
+ virtual ~KoViewIface();
+
+ // Generate a name for this interface. Automatically used if
+ // the first constructor is used.
+ static QCString newIfaceName();
+
+k_dcop:
+ DCOPRef action( const QCString &name );
+ QCStringList actions();
+ QMap<QCString,DCOPRef> actionMap();
+
+protected:
+ KoView *m_pView;
+ KDCOPActionProxy *m_actionProxy;
+};
+
+#endif
diff --git a/lib/kofficecore/KoXmlNS.cpp b/lib/kofficecore/KoXmlNS.cpp
new file mode 100644
index 00000000..769ee653
--- /dev/null
+++ b/lib/kofficecore/KoXmlNS.cpp
@@ -0,0 +1,44 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 David Faure <faure@kde.org>
+
+ 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.
+*/
+
+#include "KoXmlNS.h"
+
+const char* const KoXmlNS::office = "urn:oasis:names:tc:opendocument:xmlns:office:1.0";
+const char* const KoXmlNS::meta = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0";
+const char* const KoXmlNS::config = "urn:oasis:names:tc:opendocument:xmlns:config:1.0";
+const char* const KoXmlNS::text = "urn:oasis:names:tc:opendocument:xmlns:text:1.0";
+const char* const KoXmlNS::table = "urn:oasis:names:tc:opendocument:xmlns:table:1.0";
+const char* const KoXmlNS::draw = "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0";
+const char* const KoXmlNS::presentation = "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0";
+const char* const KoXmlNS::dr3d = "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0";
+const char* const KoXmlNS::chart = "urn:oasis:names:tc:opendocument:xmlns:chart:1.0";
+const char* const KoXmlNS::form = "urn:oasis:names:tc:opendocument:xmlns:form:1.0";
+const char* const KoXmlNS::script = "urn:oasis:names:tc:opendocument:xmlns:script:1.0";
+const char* const KoXmlNS::style = "urn:oasis:names:tc:opendocument:xmlns:style:1.0";
+const char* const KoXmlNS::number = "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0";
+const char* const KoXmlNS::manifest = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0";
+
+const char* const KoXmlNS::math = "http://www.w3.org/1998/Math/MathML";
+const char* const KoXmlNS::svg = "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0";
+const char* const KoXmlNS::fo = "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0";
+const char* const KoXmlNS::dc = "http://purl.org/dc/elements/1.1/";
+const char* const KoXmlNS::xlink = "http://www.w3.org/1999/xlink";
+// const char* const KoXmlNS::smil = "urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0";
+
+const char* const KoXmlNS::koffice = "http://www.koffice.org/2005/";
diff --git a/lib/kofficecore/KoXmlNS.h b/lib/kofficecore/KoXmlNS.h
new file mode 100644
index 00000000..410519b8
--- /dev/null
+++ b/lib/kofficecore/KoXmlNS.h
@@ -0,0 +1,55 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 David Faure <faure@kde.org>
+
+ 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 KOXMLNS_H
+#define KOXMLNS_H
+
+#include <koffice_export.h>
+/**
+ * Repository of XML namespaces used for OASIS documents.
+ * Note: if we have code which needs often those as QStrings, then maybe
+ * we need static const QString& versions of them too. Needs an init() though.
+ */
+class KOFFICECORE_EXPORT KoXmlNS {
+public:
+ static const char* const office;
+ static const char* const meta;
+ static const char* const config;
+ static const char* const text;
+ static const char* const table;
+ static const char* const draw;
+ static const char* const presentation;
+ static const char* const dr3d;
+ static const char* const chart;
+ static const char* const form;
+ static const char* const script;
+ static const char* const style;
+ static const char* const number;
+ static const char* const manifest;
+
+ static const char* const math;
+ static const char* const svg;
+ static const char* const fo;
+ static const char* const dc;
+ static const char* const xlink;
+
+ static const char* const koffice;
+};
+
+#endif /* KOXMLNS_H */
diff --git a/lib/kofficecore/KoXmlReader.cpp b/lib/kofficecore/KoXmlReader.cpp
new file mode 100644
index 00000000..ae1e2ae6
--- /dev/null
+++ b/lib/kofficecore/KoXmlReader.cpp
@@ -0,0 +1,1568 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Ariya Hidayat <ariya@kde.org>
+
+ 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.
+*/
+
+/*
+ This is a memory-efficient DOM implementation for KOffice. See the API
+ documentation for details.
+
+ IMPORTANT !
+
+ * When you change this stuff, make sure it DOES NOT BREAK the test suite.
+ Build tests/koxmlreadertest.cpp and verify it. Many sleepless nights
+ have been sacrificed for this piece of code, do not let those precious
+ hours wasted!
+
+ * Run testdom.cpp WITH Valgrind's memcheck tool and make sure NO illegal
+ memory read/write and any type of leak occurs. If you are not familiar
+ with Valgrind then RTFM first and come back again later on.
+
+ * The public API shall remain as compatible as QDom.
+
+ * All QDom-compatible methods should behave the same. All QDom-compatible
+ functions should return the same result. In case of doubt, run
+ koxmlreadertest.cpp but uncomment KOXML_USE_QDOM in koxmlreader.h
+ so that the tests are performed with standard QDom.
+
+ Some differences compared to QDom:
+
+ - DOM tree in KoXmlDocument is read-only, you can not modify it. This is
+ sufficient for KOffice since the tree is only accessed when loading
+ a document to the application. For saving the document to XML file,
+ use KoXmlWriter.
+
+ - Comment node (like QDomComment) is not implemented as comments are
+ simply ignored.
+
+ - DTD, entity and entity reference are not handled. Thus, the associated
+ nodes (like QDomDocumentType, QDomEntity, QDomEntityReference) are also
+ not implemented.
+
+ - Attribute mapping node is not implemented. But of course, functions to
+ query attributes of an element are available.
+
+
+ */
+
+#include "KoXmlReader.h"
+
+#ifndef KOXML_USE_QDOM
+
+#include <qxml.h>
+#include <qdom.h>
+
+#include <qmap.h>
+#include <qcstring.h>
+
+// double QString, used for hashing against namespace and qualified name pair
+class DQString
+{
+public:
+ DQString() { }
+ DQString( const QString& s1, const QString& s2 ){ str1 = s1; str2 = s2; }
+ DQString( const DQString& b ) { str1 = b.str1; str2 = b.str2; }
+ DQString& operator=( const DQString& b ){ str1 = b.str1; str2 = b.str2; return *this; }
+ bool operator==( const DQString& b ) const { return (str1==b.str1) && (str2==b.str2); }
+ bool operator!=( const DQString& b ) const { return (str1!=b.str1) || (str2!=b.str2); }
+ bool operator<( const DQString& b ) const
+ { return ( str1 < b.str1 ) ? true : ( str1==b.str1 ) ? str2<b.str2 : false; }
+ QString s1() const { return str1; }
+ QString s2() const { return str2; }
+private:
+ QString str1, str2;
+};
+
+// just for completeness, this is the self-test for DQString above
+#if 0
+ DQString b1;
+ DQString b2;
+ CHECK( b1==b2, true );
+ CHECK( b1!=b2, false );
+
+ b1 = DQString( "sweet","princess" );
+ b2 = DQString( "sweet","princess" );
+ CHECK( b1==b2, true );
+ CHECK( b1!=b2, false );
+
+ b1 = DQString( "sweet","princess" );
+ b2 = DQString( "bad","prince" );
+ CHECK( b1==b2, false );
+ CHECK( b1!=b2, true );
+#endif
+
+class KoXmlStream
+{
+public:
+ KoXmlStream(){ saveData = true; data.reserve(1024); pos = 0; }
+ QString stringData() const { return data; }
+ void setSaveData( bool s ){ saveData = s; }
+ int at() const { return pos; }
+ KoXmlStream& operator<<( const QString& str )
+ { if(saveData) data.append(str); pos+=str.length(); return *this; }
+ KoXmlStream& appendEscape( const QString& str );
+
+private:
+ bool saveData;
+ QString data;
+ int pos;
+};
+
+KoXmlStream& KoXmlStream::appendEscape( const QString& str )
+{
+ unsigned len = str.length();
+
+ if( saveData )
+ {
+ data.reserve( data.length() + len );
+ for( unsigned c=0; c<len; c++ )
+ if( str[c]=='<' ){ data.append( "&lt;"); pos += 4; } else
+ if( str[c]=='>'){ data.append( "&gt;"); pos+= 4; } else
+ if( str[c]=='"'){ data.append( "&quot;"); pos += 6; } else
+ if( str[c]=='&'){ data.append( "&amp;"); pos += 5; } else
+ { data.append( str[c] ); pos++; }
+ }
+ else
+ {
+ pos += len;
+ for( unsigned c=0; c<len; c++ )
+ if( str[c]=='<' ) pos += 3; else // "&lt;"
+ if( str[c]=='>') pos+= 3; else // "&gt;"
+ if( str[c]=='"') pos += 5; else // "&quot;"
+ if( str[c]=='&') pos += 4; // "&amp;"
+ }
+
+ return *this;
+}
+
+class KoXmlNodeData
+{
+public:
+
+ KoXmlNodeData();
+ virtual ~KoXmlNodeData();
+
+ // generic properties
+ KoXmlNode::NodeType nodeType;
+ QString tagName;
+ QString namespaceURI;
+ QString prefix;
+ QString localName;
+
+ // reference counting
+ unsigned long count;
+ void ref() { count++; }
+ void unref() { --count; if( !count ) delete this; }
+
+ // type information
+ virtual const char* typeInfo() const { return "Node"; }
+ QString nodeName() const;
+
+ // for tree and linked-list
+ KoXmlNodeData* parent;
+ KoXmlNodeData* prev;
+ KoXmlNodeData* next;
+ KoXmlNodeData* first;
+ KoXmlNodeData* last;
+
+ QString text();
+
+ // node manipulation
+ void appendChild( KoXmlNodeData* child );
+ virtual void clear();
+ KoXmlNodeData* ownerDocument();
+
+ // attributes
+ void setAttribute( const QString& name, const QString& value );
+ QString attribute( const QString& name );
+ bool hasAttribute( const QString& name );
+ void setAttributeNS( const QString& nsURI, const QString& name, const QString& value );
+ QString attributeNS( const QString& nsURI, const QString& name );
+ bool hasAttributeNS( const QString& nsURI, const QString& name );
+
+ // for text and CDATA
+ QString data() const;
+ void setData( const QString& data );
+
+ // for document node
+ QXmlSimpleReader* xmlReader;
+ QString buffer;
+ bool setContent( QXmlInputSource* source, QXmlReader* reader,
+ QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0 );
+
+ // used when doing on-demand (re)parse
+ bool loaded;
+ unsigned startPos, endPos;
+ void loadChildren( int depth=1 );
+ void unloadChildren();
+ bool fastLoading;
+
+private:
+ QMap<QString,QString> attr;
+ QMap<DQString,QString> attrNS;
+ QString textData;
+ friend class KoXmlHandler;
+};
+
+class KoXmlHandler : public QXmlDefaultHandler
+{
+public:
+ KoXmlHandler( KoXmlNodeData*, bool processNamespace );
+ ~KoXmlHandler();
+
+ void setMaxDepth( int d ){ maxDepth = d; }
+ void setInitialOffset( int ofs ){ parseOffset = ofs; }
+
+ // content handler
+ bool startDocument();
+ bool endDocument();
+
+ bool startElement( const QString& nsURI, const QString& localName,
+ const QString& qName, const QXmlAttributes& atts );
+ bool endElement( const QString& nsURI, const QString& localName,
+ const QString& qName );
+
+ bool characters( const QString& ch );
+ bool processingInstruction( const QString& target, const QString& data );
+ bool skippedEntity( const QString& name );
+
+ // lexical handler
+ bool startCDATA();
+ bool endCDATA();
+ bool startEntity( const QString & );
+ bool endEntity( const QString & );
+ bool startDTD( const QString& name, const QString& publicId,
+ const QString& systemId );
+ bool comment( const QString& ch );
+
+ // decl handler
+ bool externalEntityDecl( const QString &name, const QString &publicId,
+ const QString &systemId ) ;
+
+ // DTD handler
+ bool notationDecl( const QString & name, const QString & publicId,
+ const QString & systemId );
+ bool unparsedEntityDecl( const QString &name, const QString &publicId,
+ const QString &systemId, const QString &notationName ) ;
+
+ // error handler
+ bool fatalError( const QXmlParseException& exception );
+
+ QString errorMsg;
+ int errorLine;
+ int errorColumn;
+
+private:
+ bool processNamespace;
+ KoXmlNodeData* rootNode;
+ KoXmlNodeData* currentNode;
+ QString entityName;
+ bool cdata;
+ int parseOffset;
+ KoXmlStream bufferStream;
+ int elementDepth;
+ int maxDepth;
+};
+
+// ==================================================================
+//
+// KoXmlNodeData
+//
+// ==================================================================
+
+KoXmlNodeData::KoXmlNodeData()
+{
+ nodeType = KoXmlNode::NullNode;
+
+ tagName = QString::null;
+ prefix = QString::null;
+ localName = QString::null;
+ namespaceURI = QString::null;
+ textData = QString::null;
+
+ count = 1;
+ parent = 0;
+ prev = next = 0;
+ first = last = 0;
+
+ xmlReader = 0;
+ startPos = endPos = 0;
+
+ fastLoading = false;
+
+ // assume true, it will be set to false by XML parser when this node
+ // apparently has children AND the children are not loaded
+ loaded = true;
+}
+
+KoXmlNodeData::~KoXmlNodeData()
+{
+ clear();
+}
+
+void KoXmlNodeData::clear()
+{
+ if( first )
+ for( KoXmlNodeData* node = first; node ; )
+ {
+ KoXmlNodeData* next = node->next;
+ node->unref();
+ node = next;
+ }
+
+ nodeType = KoXmlNode::NullNode;
+ tagName = QString::null;
+ prefix = QString::null;
+ namespaceURI = QString::null;
+ textData = QString::null;
+
+ attr.clear();
+ attrNS.clear();
+
+ parent = 0;
+ prev = next = 0;
+ first = last = 0;
+
+ delete xmlReader;
+ xmlReader = 0;
+ buffer = QString::null;
+}
+
+QString KoXmlNodeData::text()
+{
+ QString t( "" );
+
+ loadChildren();
+
+ KoXmlNodeData* node = first;
+ while ( node )
+ {
+ switch( node->nodeType )
+ {
+ case KoXmlNode::ElementNode:
+ t += node->text(); break;
+ case KoXmlNode::TextNode:
+ t += node->data(); break;
+ case KoXmlNode::CDATASectionNode:
+ t += node->data(); break;
+ default: break;
+ }
+ node = node->next;
+ }
+
+ return t;
+}
+
+QString KoXmlNodeData::nodeName() const
+{
+ QString n;
+
+ switch( nodeType )
+ {
+ case KoXmlNode::ElementNode:
+ n = tagName;
+ if (!prefix.isEmpty()) n.prepend(":").prepend(prefix); break;
+ case KoXmlNode::TextNode: return QString("#text");
+ case KoXmlNode::CDATASectionNode: return QString("#cdata-section");
+ case KoXmlNode::DocumentNode: return QString("#document");
+ default: break;
+ }
+
+ return n;
+}
+
+KoXmlNodeData* KoXmlNodeData::ownerDocument()
+{
+ KoXmlNodeData* owner = this;
+
+ while( owner->parent )
+ owner = owner->parent;
+
+ return (owner->nodeType==KoXmlNode::DocumentNode) ? owner : 0;
+}
+
+void KoXmlNodeData::appendChild( KoXmlNodeData* node )
+{
+ node->parent = this;
+ if( !last )
+ first = last = node;
+ else
+ {
+ last->next = node;
+ node->prev = last;
+ node->next = 0;
+ last = node;
+ }
+}
+
+void KoXmlNodeData::setAttribute( const QString& name, const QString& value )
+{
+ attr[ name ] = value;
+}
+
+QString KoXmlNodeData::attribute( const QString& name )
+{
+ return attr[ name ];
+}
+
+bool KoXmlNodeData::hasAttribute( const QString& name )
+{
+ return attr.contains( name );
+}
+
+void KoXmlNodeData::setAttributeNS( const QString& nsURI,
+const QString& name, const QString& value )
+{
+ QString prefix;
+ QString localName = name;
+ int i = name.find( ':' );
+ if( i != -1 )
+ {
+ localName = name.mid( i + 1 );
+ prefix = name.left( i );
+ }
+
+ if( prefix.isNull() ) return;
+
+ DQString key( nsURI, localName );
+ attrNS[ key ] = value;
+}
+
+QString KoXmlNodeData::attributeNS( const QString& nsURI, const QString& name )
+{
+ DQString key( nsURI, name );
+ return attrNS[ key ];
+}
+
+bool KoXmlNodeData::hasAttributeNS( const QString& nsURI, const QString& name )
+{
+ DQString key( nsURI, name );
+ return attrNS.contains( key );
+}
+
+QString KoXmlNodeData::data() const
+{
+ return textData;
+}
+
+void KoXmlNodeData::setData( const QString& d )
+{
+ textData = d;
+}
+
+bool KoXmlNodeData::setContent( QXmlInputSource* source,
+QXmlReader* reader, QString* errorMsg, int* errorLine, int* errorColumn )
+{
+ if( nodeType != KoXmlNode::DocumentNode )
+ return false;
+
+ clear();
+ nodeType = KoXmlNode::DocumentNode;
+
+ // sanity checks
+ if( !source ) return false;
+ if( !reader ) return false;
+
+ // copy the reader for later on-demand loading
+ // FIXME this is a workaround because no copy is possible with QXmlReader
+ char* features[] =
+ {
+ "http://xml.org/sax/features/namespaces",
+ "http://xml.org/sax/features/namespace-prefixes",
+ "http://trolltech.com/xml/features/report-whitespace-only-CharData",
+ "http://trolltech.com/xml/features/report-start-end-entity"
+ };
+ xmlReader = new QXmlSimpleReader;
+ for( int fi=0; fi<4; fi++ )
+ xmlReader->setFeature( features[fi], reader->feature( features[fi] ) );
+
+ bool processNamespace =
+ reader->feature( "http://xml.org/sax/features/namespaces" ) &&
+ !reader->feature( "http://xml.org/sax/features/namespace-prefixes" );
+
+ KoXmlHandler handler( this, processNamespace );
+ reader->setContentHandler( &handler );
+ reader->setErrorHandler( &handler );
+ reader->setLexicalHandler( &handler );
+ reader->setDeclHandler( &handler );
+ reader->setDTDHandler( &handler );
+
+ if( !fastLoading )
+ handler.setMaxDepth( 4 );
+
+ if( !reader->parse( source ) )
+ {
+ // parsing error has occurred
+ if( errorMsg ) *errorMsg = handler.errorMsg;
+ if( errorLine ) *errorLine = handler.errorLine;
+ if( errorColumn ) *errorColumn = handler.errorColumn;
+ return false;
+ }
+
+ return true;
+}
+
+void KoXmlNodeData::loadChildren( int depth )
+{
+ // for more than 1 level, force reloading anyway
+ if( ( depth== 1 ) && loaded ) return;
+
+ KoXmlNodeData* doc = ownerDocument();
+ if( !doc ) return;
+
+ // fast loading? then this on-demand loading makes no sense
+ if( doc->fastLoading ) return;
+
+ unloadChildren();
+
+ bool nsProcess =
+ doc->xmlReader->feature( "http://xml.org/sax/features/namespaces" ) &&
+ !doc->xmlReader->feature( "http://xml.org/sax/features/namespace-prefixes" );
+
+
+ // XML snippet for the children, including this element
+ QString snippet = doc->buffer.mid( startPos, endPos-startPos+1 );
+
+ // now parse all subnodes
+ KoXmlHandler handler( this, nsProcess );
+ handler.setMaxDepth( depth );
+ handler.setInitialOffset( startPos );
+ doc->xmlReader->setContentHandler( &handler );
+ doc->xmlReader->setErrorHandler( &handler );
+ doc->xmlReader->setLexicalHandler( &handler );
+ doc->xmlReader->setDeclHandler( &handler );
+ doc->xmlReader->setDTDHandler( &handler );
+
+ QXmlInputSource source;
+ source.setData( snippet );
+ if( !doc->xmlReader->parse( source ) )
+ {
+ // parsing error has occurred, which should not happen
+ // nothing we can do except...
+ loaded = false;
+ qWarning( "On-demand loading triggers parse error!" );
+ }
+ else
+ loaded = true;
+}
+
+void KoXmlNodeData::unloadChildren()
+{
+ if( !loaded ) return;
+
+ if( first )
+ for( KoXmlNodeData* node = first; node ; )
+ {
+ KoXmlNodeData* next = node->next;
+ node->unloadChildren();
+ node->unref();
+ node = next;
+ }
+
+ loaded = false;
+ first = last = 0;
+}
+
+
+// ==================================================================
+//
+// KoXmlHandler
+//
+// ==================================================================
+
+KoXmlHandler::KoXmlHandler( KoXmlNodeData* n, bool ns ):
+QXmlDefaultHandler()
+{
+ processNamespace = ns;
+
+ rootNode = n;
+ currentNode = n;
+ cdata = false;
+ entityName = QString::null;
+
+ errorMsg = QString::null;
+ errorLine = 0;
+ errorColumn = 0;
+
+ parseOffset = 0;
+ elementDepth = -1;
+ maxDepth = 999;
+
+ bufferStream.setSaveData( rootNode->nodeType == KoXmlNode::DocumentNode );
+}
+
+KoXmlHandler::~KoXmlHandler()
+{
+}
+
+bool KoXmlHandler::startDocument()
+{
+ // just for sanity
+ currentNode = rootNode;
+ cdata = false;
+ entityName = QString::null;
+ elementDepth = -1;
+
+ return true;
+}
+
+bool KoXmlHandler::endDocument()
+{
+ // just for sanity
+ if( rootNode->nodeType == KoXmlNode::DocumentNode )
+ if( currentNode!=rootNode )
+ return false;
+
+ if( rootNode->nodeType == KoXmlNode::DocumentNode )
+ rootNode->buffer = bufferStream.stringData();
+
+ return true;
+}
+
+bool KoXmlHandler::startDTD( const QString& name, const QString& publicId,
+const QString& systemId )
+{
+ Q_UNUSED( name );
+ Q_UNUSED( publicId );
+ Q_UNUSED( systemId );
+
+ // we skip DTD
+ return true;
+}
+
+bool KoXmlHandler::startElement( const QString& nsURI, const QString& localName,
+const QString& name, const QXmlAttributes& atts )
+{
+ Q_UNUSED( localName );
+
+ // sanity check
+ if( !currentNode )
+ return false;
+
+ // we are going one level deeper
+ elementDepth++;
+
+ QString nodePrefix, nodeLocalName, nodeTagName;
+ KoXmlNodeData* element = 0;
+
+ if( processNamespace )
+ {
+ // parse, using namespace
+ nodeTagName = name;
+ nodeLocalName = name;
+ nodePrefix = nsURI.isNull() ? QString::null : QString("");
+ int i = name.find( ':' );
+ if( i != -1 )
+ {
+ nodeTagName = name.mid( i + 1 );
+ nodeLocalName = nodeTagName;
+ nodePrefix = name.left( i );
+ }
+
+ if( elementDepth <= maxDepth )
+ {
+ // construct a new element
+ element = new KoXmlNodeData;
+ element->nodeType = KoXmlNode::ElementNode;
+ element->parent = currentNode;
+ element->namespaceURI = nsURI;
+ element->prefix = nodePrefix;
+ element->localName = nodeLocalName;
+ element->tagName = nodeTagName;
+
+ // Note: endPos will be later fixed in endElement
+ element->endPos = element->startPos = parseOffset + bufferStream.at();
+
+ // handle the attributes
+ for( int c=0; c<atts.length(); c++ )
+ {
+ QString prefix;
+ QString qName; // with prefix
+ QString name; // without prefix, i.e. local name
+
+ name = qName = atts.qName(c);
+ int i = qName.find( ':' );
+ if( i != -1 ) prefix = qName.left( i );
+ if( i != -1 ) name = qName.mid( i + 1 );
+ element->setAttributeNS( atts.uri(c), qName, atts.value(c) );
+ element->setAttribute( name, atts.value(c) );
+ }
+ }
+
+ // save in buffer for later on-demand loading
+ if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
+ {
+ bufferStream << "<";
+ if( !nodePrefix.isEmpty() )
+ bufferStream << nodePrefix << ":";
+ bufferStream << localName;
+ bufferStream << " xmlns";
+ if( !nodePrefix.isEmpty() )
+ bufferStream << ":" << nodePrefix;
+ bufferStream << "=\"";
+ bufferStream.appendEscape( nsURI );
+ bufferStream << "\"";
+ for( int c=0; c<atts.length(); c++ )
+ {
+ QString prefix;
+ QString name = atts.qName(c); // qName contains the prefix
+ int i = name.find( ':' );
+ if( i != -1 ) prefix = name.left( i );
+ if( i != -1 ) name = atts.qName(c).mid( i + 1 );
+ if( !atts.uri(c).isEmpty() )
+ bufferStream << " xmlns:" << prefix << "=\"" << atts.uri(c) << "\"";
+ bufferStream << " ";
+ if( !prefix.isEmpty() ) bufferStream << prefix << ":";
+ bufferStream << name << "=\"";
+ bufferStream.appendEscape( atts.value(c) );
+ bufferStream << "\"";
+ }
+ bufferStream << ">";
+ }
+ }
+ else
+ {
+ // parse, without using namespace
+ nodeTagName = name;
+
+ if( elementDepth <= maxDepth )
+ {
+ // construct a new element
+ element = new KoXmlNodeData;
+ element->nodeType = KoXmlNode::ElementNode;
+ element->parent = currentNode;
+ element->namespaceURI = QString::null;
+ element->prefix = QString::null;
+ element->localName = QString::null;
+ element->tagName = nodeTagName;
+
+ if( rootNode->nodeType == KoXmlNode::DocumentNode )
+ element->fastLoading = rootNode->fastLoading;
+
+ // Note: endPos will be later fixed in endElement
+ element->endPos = element->startPos = parseOffset + bufferStream.at();
+
+ // handle the attributes
+ for( int c=0; c<atts.length(); c++ )
+ element->setAttribute( atts.qName(c), atts.value(c) );
+ }
+
+ // save in buffer for later on-demand loading
+ if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
+ {
+ bufferStream << "<";
+ bufferStream << nodeTagName;
+ for( int c=0; c<atts.length(); c++ )
+ {
+ bufferStream << " " << atts.qName(c) << "=\"";
+ bufferStream.appendEscape( atts.value(c) );
+ bufferStream << "\"";
+ }
+ bufferStream << ">";
+ }
+
+ }
+
+ // if we do not parse a complete document, the first element is ignored
+ // this feature is used in on-demand loading
+ // e.g. "<ul><li><b>bold items</b></li></ul>", if root node points to
+ // <ul> tag, then the first <ul> is skipped so that next <li> element
+ // becomes the child of it.
+ if( elementDepth == 0 )
+ if( rootNode->nodeType != KoXmlNode::DocumentNode )
+ {
+ delete element;
+ return true;
+ }
+
+ if( element )
+ {
+ // add as the child and traverse to it
+ currentNode->loaded = true;
+ currentNode->appendChild( element );
+ currentNode = element;
+ }
+ else
+ currentNode->loaded = false;
+
+ return true;
+}
+
+bool KoXmlHandler::endElement( const QString& nsURI, const QString& localName,
+const QString& qName )
+{
+ Q_UNUSED( nsURI );
+ Q_UNUSED( localName );
+
+ // sanity check
+ if( !currentNode ) return false;
+ if( !currentNode->parent ) return false;
+
+ // see comments in startElement about first element and on-demand loading
+ if( rootNode->nodeType == KoXmlNode::DocumentNode )
+ if( currentNode == rootNode )
+ return false;
+
+ // buffer for on-demand loading
+ if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
+ bufferStream << "</" << qName << ">";
+
+ // fix up the pointer
+ currentNode->endPos = parseOffset + bufferStream.at() - 1;
+
+ // go up one level
+ if( elementDepth <= maxDepth )
+ currentNode = currentNode->parent;
+
+ // we are going up one level
+ elementDepth--;
+
+ return true;
+}
+
+bool KoXmlHandler::characters( const QString& str )
+{
+ // sanity check
+ if( rootNode->nodeType == KoXmlNode::DocumentNode )
+ if( currentNode == rootNode )
+ return false;
+
+ // are we inside entity ?
+ if( !entityName.isEmpty() )
+ {
+ // we do not handle entity but need to keep track of it
+ // because we want to skip it alltogether
+ return true;
+ }
+
+ if( cdata )
+ {
+ // are we inside CDATA section ?
+ if( elementDepth <= maxDepth )
+ {
+ KoXmlNodeData* cdata = new KoXmlNodeData;
+ cdata->nodeType = KoXmlNode::CDATASectionNode;
+ cdata->parent = currentNode;
+ cdata->setData( str );
+ currentNode->appendChild( cdata );
+ }
+
+ if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
+ bufferStream << "<![CDATA[" << str << "]]>"; // no escape for CDATA
+ }
+ else
+ {
+ // this must be normal text node
+ if( elementDepth <= maxDepth )
+ {
+ KoXmlNodeData* text = new KoXmlNodeData;
+ text->nodeType = KoXmlNode::TextNode;
+ text->parent = currentNode;
+ text->setData( str );
+ currentNode->appendChild( text );
+ }
+
+ // save it for later use
+ if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
+ bufferStream.appendEscape( str );
+ }
+
+ return true;
+}
+
+bool KoXmlHandler::processingInstruction( const QString& target,
+const QString& data )
+{
+ // sanity check
+ if( !currentNode )
+ return false;
+
+ KoXmlNodeData* instruction = new KoXmlNodeData;
+ instruction->nodeType = KoXmlNode::ProcessingInstructionNode;
+ instruction->parent = currentNode;
+ instruction->tagName = target;
+ instruction->setData( data );
+
+ currentNode->appendChild( instruction );
+
+ return true;
+}
+
+bool KoXmlHandler::skippedEntity( const QString& name )
+{
+ Q_UNUSED( name );
+
+ // we skip entity
+ return true;
+}
+
+bool KoXmlHandler::startCDATA()
+{
+ cdata = true;
+ return true;
+}
+
+bool KoXmlHandler::endCDATA()
+{
+ cdata = false;
+ return true;
+}
+
+bool KoXmlHandler::startEntity( const QString& name )
+{
+ entityName = name;
+ return true;
+}
+
+bool KoXmlHandler::endEntity( const QString& name )
+{
+ Q_UNUSED( name );
+ entityName = QString::null;
+ return true;
+}
+
+bool KoXmlHandler::comment( const QString& comment )
+{
+ Q_UNUSED( comment );
+
+ // we skip comment
+ return true;
+}
+
+bool KoXmlHandler::unparsedEntityDecl( const QString &name,
+const QString &publicId, const QString &systemId, const QString &notationName )
+{
+ Q_UNUSED( name );
+ Q_UNUSED( publicId );
+ Q_UNUSED( systemId );
+ Q_UNUSED( notationName );
+
+ // we skip entity
+ return true;
+}
+
+bool KoXmlHandler::externalEntityDecl( const QString &name,
+const QString &publicId, const QString &systemId )
+{
+ Q_UNUSED( name );
+ Q_UNUSED( publicId );
+ Q_UNUSED( systemId );
+
+ // we skip entity
+ return true;
+}
+
+bool KoXmlHandler::notationDecl( const QString & name,
+const QString & publicId, const QString & systemId )
+{
+ Q_UNUSED( name );
+ Q_UNUSED( publicId );
+ Q_UNUSED( systemId );
+
+ // we skip notation node
+ return true;
+}
+
+bool KoXmlHandler::fatalError( const QXmlParseException& exception )
+{
+ errorMsg = exception.message();
+ errorLine = exception.lineNumber();
+ errorColumn = exception.columnNumber();
+ return QXmlDefaultHandler::fatalError( exception );
+}
+
+// ==================================================================
+//
+// KoXmlNode
+//
+// ==================================================================
+
+// Creates a null node
+KoXmlNode::KoXmlNode()
+{
+ d = new KoXmlNodeData;
+}
+
+// Destroys this node
+KoXmlNode::~KoXmlNode()
+{
+ if( d ) d->unref();
+}
+
+// Creates a copy of another node
+KoXmlNode::KoXmlNode( const KoXmlNode& node )
+{
+ d = node.d;
+ d->ref();
+}
+
+// Creates a node for specific implementation
+KoXmlNode::KoXmlNode( KoXmlNodeData* data )
+{
+ d = data;
+ data->ref();
+}
+
+// Creates a shallow copy of another node
+KoXmlNode& KoXmlNode::operator=( const KoXmlNode& node )
+{
+ d->unref();
+ d = node.d;
+ d->ref();
+ return *this;
+}
+
+// Note: two null nodes are always equal
+bool KoXmlNode::operator==( const KoXmlNode& node ) const
+{
+ if( isNull() && node.isNull() ) return true;
+ return( d==node.d );
+}
+
+// Note: two null nodes are always equal
+bool KoXmlNode::operator!=( const KoXmlNode& node ) const
+{
+ if( isNull() && !node.isNull() ) return true;
+ if( !isNull() && node.isNull() ) return true;
+ if( isNull() && node.isNull() ) return false;
+ return( d!=node.d );
+}
+
+KoXmlNode::NodeType KoXmlNode::nodeType() const
+{
+ return d->nodeType;
+}
+
+bool KoXmlNode::isElement() const
+{
+ return d->nodeType == ElementNode;
+}
+
+bool KoXmlNode::isText() const
+{
+ return (d->nodeType == TextNode) || isCDATASection();
+}
+
+bool KoXmlNode::isCDATASection() const
+{
+ return d->nodeType == CDATASectionNode;
+}
+
+bool KoXmlNode::isDocument() const
+{
+ return d->nodeType == DocumentNode;
+}
+
+bool KoXmlNode::isNull() const
+{
+ return d->nodeType == NullNode;
+}
+
+void KoXmlNode::clear()
+{
+ d->unref();
+ d = new KoXmlNodeData;
+}
+
+QString KoXmlNode::nodeName() const
+{
+ return d->nodeName();
+}
+
+QString KoXmlNode::prefix() const
+{
+ return isElement() ? d->prefix : QString::null;
+}
+
+QString KoXmlNode::namespaceURI() const
+{
+ return isElement() ? d->namespaceURI : QString::null;
+}
+
+QString KoXmlNode::localName() const
+{
+ return isElement() ? d->localName : QString::null;
+}
+
+KoXmlDocument KoXmlNode::ownerDocument() const
+{
+ KoXmlNodeData* node = d;
+ while( node->parent ) node = node->parent;
+
+ if( node->nodeType != DocumentNode ) return KoXmlDocument();
+ return KoXmlDocument( node );
+}
+
+KoXmlNode KoXmlNode::parentNode() const
+{
+ return d->parent ? KoXmlNode( d->parent ) : KoXmlNode();
+}
+
+bool KoXmlNode::hasChildNodes() const
+{
+ d->loadChildren();
+ return d->first!=0 ;
+}
+
+KoXmlNode KoXmlNode::firstChild() const
+{
+ if( !d->fastLoading )
+ d->loadChildren();
+ return d->first ? KoXmlNode( d->first ) : KoXmlNode();
+}
+
+KoXmlNode KoXmlNode::lastChild() const
+{
+ if( !d->fastLoading )
+ d->loadChildren();
+ return d->last ? KoXmlNode( d->last ) : KoXmlNode();
+}
+
+KoXmlNode KoXmlNode::nextSibling() const
+{
+ return d->next ? KoXmlNode( d->next ) : KoXmlNode();
+}
+
+KoXmlNode KoXmlNode::previousSibling() const
+{
+ return d->prev ? KoXmlNode( d->prev ) : KoXmlNode();
+}
+
+KoXmlNode KoXmlNode::namedItem( const QString& name ) const
+{
+ if( !d->fastLoading )
+ d->loadChildren();
+
+ KoXmlNodeData* node = d->first;
+ while ( node )
+ {
+ if( node->nodeName() == name )
+ return KoXmlNode( node );
+ node = node->next;
+ }
+
+ // not found
+ return KoXmlNode();
+}
+
+KoXmlNode KoXmlNode::namedItemNS( const QString& nsURI, const QString& name ) const
+{
+ if( !d->fastLoading )
+ d->loadChildren();
+
+ KoXmlNodeData* node = d->first;
+ while ( node )
+ {
+ if( !node->prefix.isNull() )
+ if( node->namespaceURI == nsURI )
+ if( node->localName == name )
+ return KoXmlNode( node );
+ node = node->next;
+ }
+
+ // not found
+ return KoXmlNode();
+}
+
+KoXmlElement KoXmlNode::toElement()
+{
+ return isElement() ? KoXmlElement( d ) : KoXmlElement();
+}
+
+KoXmlText KoXmlNode::toText()
+{
+ return isText() ? KoXmlText( d ) : KoXmlText();
+}
+
+KoXmlCDATASection KoXmlNode::toCDATASection()
+{
+ return isCDATASection() ? KoXmlCDATASection( (KoXmlNodeData*)d ) :
+ KoXmlCDATASection();
+}
+
+KoXmlDocument KoXmlNode::toDocument()
+{
+ return isDocument() ? KoXmlDocument( d ) : KoXmlDocument();
+}
+
+void KoXmlNode::load( int depth )
+{
+ d->loadChildren( depth );
+}
+
+void KoXmlNode::unload()
+{
+ d->unloadChildren();
+}
+
+// ==================================================================
+//
+// KoXmlElement
+//
+// ==================================================================
+
+// Creates an empty element
+KoXmlElement::KoXmlElement(): KoXmlNode()
+{
+ d->unref();
+ d = new KoXmlNodeData;
+}
+
+KoXmlElement::~KoXmlElement()
+{
+ d->unref();
+ d = 0;
+}
+
+// Creates a shallow copy of another element
+KoXmlElement::KoXmlElement( const KoXmlElement& element ): KoXmlNode()
+{
+ d->unref();
+ d = element.d;
+ d->ref();
+}
+
+KoXmlElement::KoXmlElement( KoXmlNodeData* data ): KoXmlNode()
+{
+ d->unref();
+ d = data;
+ d->ref();
+}
+
+// Copies another element
+KoXmlElement& KoXmlElement::operator=( const KoXmlElement& element )
+{
+ KoXmlNode::operator=( element );
+ return *this;
+}
+
+bool KoXmlElement::operator== ( const KoXmlElement& element ) const
+{
+ if( isNull() || element.isNull() ) return false;
+ return (d==element.d);
+}
+
+bool KoXmlElement::operator!= ( const KoXmlElement& element ) const
+{
+ if( isNull() && element.isNull() ) return false;
+ if( isNull() || element.isNull() ) return true;
+ return (d!=element.d);
+}
+
+QString KoXmlElement::tagName() const
+{
+ return isElement() ? ((KoXmlNodeData*)d)->tagName: QString::null;
+}
+
+QString KoXmlElement::text() const
+{
+ return d->text();
+}
+
+bool KoXmlElement::isElement() const
+{
+ return true;
+}
+
+QString KoXmlElement::attribute( const QString& name ) const
+{
+ return attribute( name, QString::null );
+}
+
+QString KoXmlElement::attribute( const QString& name,
+const QString& defaultValue ) const
+{
+ if( !isElement() )
+ return defaultValue;
+
+ if( !hasAttribute( name ) )
+ return defaultValue;
+
+ return ((KoXmlNodeData*)d)->attribute( name );
+}
+
+QString KoXmlElement::attributeNS( const QString& namespaceURI,
+const QString& localName, const QString& defaultValue ) const
+{
+ if( !isElement() )
+ return defaultValue;
+
+ if( !hasAttributeNS( namespaceURI,localName ) )
+ return defaultValue;
+
+ return ((KoXmlNodeData*)d)->attributeNS( namespaceURI,localName );
+}
+
+bool KoXmlElement::hasAttribute( const QString& name ) const
+{
+ return isElement() ? ((KoXmlNodeData*)d)->hasAttribute( name ) : false;
+}
+
+bool KoXmlElement::hasAttributeNS( const QString& namespaceURI,
+const QString& localName ) const
+{
+ return isElement() ? ((KoXmlNodeData*)d)->hasAttributeNS(
+ namespaceURI, localName ) : false;;
+}
+
+// ==================================================================
+//
+// KoXmlText
+//
+// ==================================================================
+
+KoXmlText::KoXmlText(): KoXmlNode()
+{
+ d->unref();
+ d = new KoXmlNodeData;
+ d->nodeType = TextNode;
+}
+
+KoXmlText::~KoXmlText()
+{
+ if( d ) d->unref();
+ d = 0;
+}
+
+KoXmlText::KoXmlText( const KoXmlText& text ): KoXmlNode()
+{
+ d->unref();
+ d = (KoXmlNodeData*) text.d;
+ d->ref();
+}
+
+KoXmlText::KoXmlText( KoXmlNodeData* data ): KoXmlNode()
+{
+ d->unref();
+ d = data;
+ d->ref();
+}
+
+bool KoXmlText::isText() const
+{
+ return true;
+}
+
+QString KoXmlText::data() const
+{
+ return ((KoXmlNodeData*)d)->data();
+}
+
+KoXmlText& KoXmlText::operator=( const KoXmlText& element )
+{
+ KoXmlNode::operator=( element );
+ return *this;
+}
+
+// ==================================================================
+//
+// KoXmlCDATASection
+//
+// ==================================================================
+
+KoXmlCDATASection::KoXmlCDATASection(): KoXmlText()
+{
+ d->unref();
+ d = new KoXmlNodeData;
+ d->nodeType = KoXmlNode::CDATASectionNode;
+}
+
+KoXmlCDATASection::~KoXmlCDATASection()
+{
+ d->unref();
+ d = 0;
+}
+
+KoXmlCDATASection::KoXmlCDATASection( const KoXmlCDATASection& cdata ):
+KoXmlText()
+{
+ d->unref();
+ d = (KoXmlNodeData*) cdata.d;
+ d->ref();
+}
+
+KoXmlCDATASection::KoXmlCDATASection( KoXmlNodeData* cdata ):
+KoXmlText()
+{
+ d->unref();
+ d = cdata;
+ d->ref();
+}
+
+bool KoXmlCDATASection::isCDATASection() const
+{
+ return true;
+}
+
+KoXmlCDATASection& KoXmlCDATASection::operator=( const KoXmlCDATASection& cdata )
+{
+ KoXmlNode::operator=( cdata );
+ return *this;
+}
+
+// ==================================================================
+//
+// KoXmlDocument
+//
+// ==================================================================
+
+KoXmlDocument::KoXmlDocument(): KoXmlNode()
+{
+ d->unref();
+ d = new KoXmlNodeData;
+ d->nodeType = KoXmlNode::DocumentNode;
+}
+
+KoXmlDocument::~KoXmlDocument()
+{
+ d->unref();
+ d = 0;
+}
+
+KoXmlDocument::KoXmlDocument( KoXmlNodeData* data ): KoXmlNode()
+{
+ d->unref();
+ d = data;
+ d->ref();
+}
+
+// Creates a copy of another document
+KoXmlDocument::KoXmlDocument( const KoXmlDocument& doc ): KoXmlNode()
+{
+ d->unref();
+ d = doc.d;
+ d->ref();
+}
+
+// Creates a shallow copy of another document
+KoXmlDocument& KoXmlDocument::operator=( const KoXmlDocument& doc )
+{
+ KoXmlNode::operator=( doc );
+ return *this;
+}
+
+// Checks if this document and doc are equals
+bool KoXmlDocument::operator==( const KoXmlDocument& doc ) const
+{
+ return( d==doc.d );
+}
+
+// Checks if this document and doc are not equals
+bool KoXmlDocument::operator!=( const KoXmlDocument& doc ) const
+{
+ return( d!=doc.d );
+}
+
+bool KoXmlDocument::isDocument() const
+{
+ return true;
+}
+
+KoXmlElement KoXmlDocument::documentElement() const
+{
+ for( KoXmlNodeData* node=d->first; node; )
+ if( node->nodeType==KoXmlNode::ElementNode )
+ return KoXmlElement( node );
+ else node = node->next;
+
+ return KoXmlElement();
+}
+
+void KoXmlDocument::setFastLoading( bool f )
+{
+ d->fastLoading = f;
+}
+
+bool KoXmlDocument::fastLoading() const
+{
+ return d->fastLoading;
+}
+
+bool KoXmlDocument::setContent( QXmlInputSource *source, QXmlReader *reader,
+ QString* errorMsg, int* errorLine, int* errorColumn )
+{
+ if( d->nodeType != KoXmlNode::DocumentNode )
+ return false;
+
+ return d->setContent( source, reader, errorMsg, errorLine, errorColumn );
+}
+
+// no namespace processing
+bool KoXmlDocument::setContent( QIODevice* device, QString* errorMsg,
+int* errorLine, int* errorColumn )
+{
+ return setContent( device, false, errorMsg, errorLine, errorColumn );
+}
+
+bool KoXmlDocument::setContent( QIODevice* device, bool namespaceProcessing,
+QString* errorMsg, int* errorLine, int* errorColumn )
+{
+ if( d->nodeType != KoXmlNode::DocumentNode )
+ return false;
+
+ QXmlSimpleReader reader;
+ reader.setFeature( "http://xml.org/sax/features/namespaces", namespaceProcessing );
+ reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", !namespaceProcessing );
+ reader.setFeature( "http://trolltech.com/xml/features/report-whitespace-only-CharData", false );
+
+ // FIXME this hack is apparently private
+ //reader.setUndefEntityInAttrHack(true);
+
+ QXmlInputSource source( device );
+ return d->setContent( &source, &reader, errorMsg, errorLine, errorColumn );
+}
+
+#endif
+
+KoXmlElement KoXml::namedItemNS( const KoXmlNode& node, const char* nsURI,
+const char* localName )
+{
+#ifdef KOXML_USE_QDOM
+ // David's solution for namedItemNS, only for QDom stuff
+ KoXmlNode n = node.firstChild();
+ for ( ; !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isElement() && n.localName() == localName &&
+ n.namespaceURI() == nsURI )
+ return n.toElement();
+ }
+ return KoXmlElement();
+#else
+ return node.namedItemNS( nsURI, localName).toElement();
+#endif
+}
+
+void KoXml::load( KoXmlNode& node, int depth )
+{
+#ifdef KOXML_USE_QDOM
+ // do nothing, QDom has no on-demand loading
+ Q_UNUSED( node );
+ Q_UNUSED( depth );
+#else
+ node.load( depth );
+#endif
+}
+
+
+void KoXml::unload( KoXmlNode& node )
+{
+#ifdef KOXML_USE_QDOM
+ // do nothing, QDom has no on-demand unloading
+ Q_UNUSED( node );
+#else
+ node.unload();
+#endif
+}
diff --git a/lib/kofficecore/KoXmlReader.h b/lib/kofficecore/KoXmlReader.h
new file mode 100644
index 00000000..df2f10c9
--- /dev/null
+++ b/lib/kofficecore/KoXmlReader.h
@@ -0,0 +1,320 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Ariya Hidayat <ariya@kde.org>
+
+ 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 KOFFICE_XMLREADER
+#define KOFFICE_XMLREADER
+
+// use standard QDom, useful to test KoXml classes against Qt's QDom
+//#define KOXML_USE_QDOM
+
+#include <qdom.h>
+#include <koffice_export.h>
+
+#ifdef KOXML_USE_QDOM
+
+#define KoXmlNode QDomNode
+#define KoXmlElement QDomElement
+#define KoXmlText QDomText
+#define KoXmlCDATASection QDomCDATASection
+#define KoXmlDocument QDomDocument
+
+#else
+
+class QIODevice;
+class QString;
+class QTextStream;
+class QXmlReader;
+class QXmlInputSource;
+
+class KoXmlElement;
+class KoXmlText;
+class KoXmlCDATASection;
+class KoXmlDocument;
+class KoXmlNodeData;
+
+/**
+ * KoXmlNode represents a node in a DOM tree.
+ *
+ * KoXmlNode is a base class for KoXmlElement, KoXmlText.
+ * Often, these subclasses are used for getting the data instead of KoXmlNode.
+ * However, as base class, KoXmlNode is very helpful when for example iterating
+ * all child nodes within one parent node.
+ *
+ * KoXmlNode implements an explicit sharing, a node shares its data with
+ * other copies (if exist).
+ *
+ * @author Ariya Hidayat <ariya@kde.org>
+ */
+class KOFFICECORE_EXPORT KoXmlNode
+{
+public:
+
+ enum NodeType
+ {
+ NullNode = 0,
+ ElementNode,
+ TextNode,
+ CDATASectionNode,
+ ProcessingInstructionNode,
+ DocumentNode
+ };
+
+ KoXmlNode();
+ KoXmlNode( const KoXmlNode& node );
+ KoXmlNode& operator=( const KoXmlNode& node );
+ bool operator== ( const KoXmlNode& ) const;
+ bool operator!= ( const KoXmlNode& ) const;
+ virtual ~KoXmlNode();
+
+ virtual KoXmlNode::NodeType nodeType() const;
+ virtual bool isNull() const;
+ virtual bool isElement() const;
+ virtual bool isText() const;
+ virtual bool isCDATASection() const;
+ virtual bool isDocument() const;
+
+ void clear();
+ KoXmlElement toElement();
+ KoXmlText toText();
+ KoXmlCDATASection toCDATASection();
+ KoXmlDocument toDocument();
+
+ virtual QString nodeName() const;
+ virtual QString namespaceURI() const;
+ virtual QString prefix() const;
+ virtual QString localName() const;
+
+ KoXmlDocument ownerDocument() const;
+ KoXmlNode parentNode() const;
+
+ bool hasChildNodes() const;
+ KoXmlNode firstChild() const;
+ KoXmlNode lastChild() const;
+ KoXmlNode nextSibling() const;
+ KoXmlNode previousSibling() const;
+
+ KoXmlNode namedItem( const QString& name ) const;
+ KoXmlNode namedItemNS( const QString& nsURI, const QString& name ) const;
+
+ /**
+ * Loads all child nodes (if any) of this node. Normally you do not need
+ * to call this function as the child nodes will be automatically
+ * loaded when necessary.
+ */
+ void load( int depth=1 );
+
+ /**
+ * Releases all child nodes of this node.
+ */
+ void unload();
+
+protected:
+ KoXmlNodeData* d;
+ KoXmlNode( KoXmlNodeData* );
+};
+
+/**
+ * KoXmlElement represents a tag element in a DOM tree.
+ *
+ * KoXmlElement holds information about an XML tag, along with its attributes.
+ *
+ * @author Ariya Hidayat <ariya@kde.org>
+ */
+
+class KOFFICECORE_EXPORT KoXmlElement: public KoXmlNode
+{
+public:
+ KoXmlElement();
+ KoXmlElement( const KoXmlElement& element );
+ KoXmlElement& operator=( const KoXmlElement& element );
+ virtual ~KoXmlElement();
+ bool operator== ( const KoXmlElement& ) const;
+ bool operator!= ( const KoXmlElement& ) const;
+
+ QString tagName() const;
+ QString text() const;
+ virtual bool isElement() const;
+
+ QString attribute( const QString& name ) const;
+ QString attribute( const QString& name, const QString& defaultValue ) const;
+ QString attributeNS( const QString& namespaceURI, const QString& localName,
+ const QString& defaultValue ) const;
+ bool hasAttribute( const QString& name ) const;
+ bool hasAttributeNS( const QString& namespaceURI, const QString& localName ) const;
+
+private:
+ friend class KoXmlNode;
+ friend class KoXmlDocument;
+ KoXmlElement( KoXmlNodeData* );
+};
+
+/**
+ * KoXmlText represents a text in a DOM tree.
+ * @author Ariya Hidayat <ariya@kde.org>
+ */
+class KOFFICECORE_EXPORT KoXmlText: public KoXmlNode
+{
+public:
+ KoXmlText();
+ KoXmlText( const KoXmlText& text );
+ KoXmlText& operator=( const KoXmlText& text );
+ virtual ~KoXmlText();
+
+ QString data() const;
+ void setData( const QString& data );
+ virtual bool isText() const;
+
+private:
+ friend class KoXmlNode;
+ friend class KoXmlDocument;
+ KoXmlText( KoXmlNodeData* );
+};
+
+/**
+ * KoXmlCDATASection represents a CDATA section in a DOM tree.
+ * @author Ariya Hidayat <ariya@kde.org>
+ */
+class KOFFICECORE_EXPORT KoXmlCDATASection: public KoXmlText
+{
+public:
+ KoXmlCDATASection();
+ KoXmlCDATASection( const KoXmlCDATASection& cdata );
+ KoXmlCDATASection& operator=( const KoXmlCDATASection& cdata );
+ virtual ~KoXmlCDATASection();
+
+ virtual bool isCDATASection() const;
+
+private:
+ friend class KoXmlNode;
+ friend class KoXmlDocument;
+ KoXmlCDATASection( KoXmlNodeData* );
+};
+
+/**
+ * KoXmlDocument represents an XML document, structured in a DOM tree.
+ *
+ * KoXmlDocument is designed to be memory efficient. Unlike QDomDocument from
+ * Qt's XML module, KoXmlDocument does not store all nodes in the DOM tree.
+ * Some nodes will be loaded and parsed on-demand only.
+ *
+ * KoXmlDocument is read-only, you can not modify its content.
+ *
+ * @author Ariya Hidayat <ariya@kde.org>
+ */
+
+class KOFFICECORE_EXPORT KoXmlDocument: public KoXmlNode
+{
+public:
+ KoXmlDocument();
+ KoXmlDocument( const KoXmlDocument& node );
+ KoXmlDocument& operator=( const KoXmlDocument& node );
+ bool operator==( const KoXmlDocument& ) const;
+ bool operator!=( const KoXmlDocument& ) const;
+ virtual ~KoXmlDocument();
+
+ virtual bool isDocument() const;
+
+ KoXmlElement documentElement() const;
+
+ void setFastLoading( bool f );
+ bool fastLoading() const;
+
+ bool setContent( QIODevice* device, bool namespaceProcessing,
+ QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0 );
+ bool setContent( QIODevice* device,
+ QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0 );
+ bool setContent( QXmlInputSource *source, QXmlReader *reader,
+ QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0 );
+
+// bool setContent( const QCString& text, bool namespaceProcessing, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0 );
+// bool setContent( const QByteArray& text, bool namespaceProcessing, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0 );
+// bool setContent( const QString& text, bool namespaceProcessing, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0 );
+// bool setContent( QIODevice* dev, bool namespaceProcessing, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0 );
+// bool setContent( const QCString& text, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0 );
+// bool setContent( const QByteArray& text, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0 );
+// bool setContent( const QString& text, QString *errorMsg=0, int *errorLine=0,
+// int *errorColumn=0 );
+
+
+private:
+ friend class KoXmlNode;
+ KoXmlDocument( KoXmlNodeData* );
+};
+
+#endif // KOXML_USE_QDOM
+
+/**
+ * This namespace contains a few convenience functions to simplify code using QDom
+ * (when loading OASIS documents, in particular).
+ *
+ * To find the child element with a given name, use KoXml::namedItemNS.
+ *
+ * To find all child elements with a given name, use
+ * QDomElement e;
+ * forEachElement( e, parent )
+ * {
+ * if ( e.localName() == "..." && e.namespaceURI() == KoXmlNS::... )
+ * {
+ * ...
+ * }
+ * }
+ * Note that this means you don't ever need to use QDomNode nor toElement anymore!
+ * Also note that localName is the part without the prefix, this is the whole point
+ * of namespace-aware methods.
+ *
+ * To find the attribute with a given name, use QDomElement::attributeNS.
+ *
+ * Do not use getElementsByTagNameNS, it's recursive (which is never needed in KOffice).
+ * Do not use tagName() or nodeName() or prefix(), since the prefix isn't fixed.
+ *
+ * @author David Faure <faure@kde.org>
+ */
+namespace KoXml {
+
+ /**
+ * A namespace-aware version of QDomNode::namedItem(),
+ * which also takes care of casting to a QDomElement.
+ * Use this when a domelement is known to have only *one* child element
+ * with a given tagname.
+ *
+ * Note: do *NOT* use getElementsByTagNameNS, it's recursive!
+ */
+ KOFFICECORE_EXPORT KoXmlElement namedItemNS( const KoXmlNode& node,
+ const char* nsURI, const char* localName );
+
+ /**
+ * Explicitly load child nodes of specified node, up to given depth.
+ * This function has no effect if QDom is used.
+ */
+ KOFFICECORE_EXPORT void load( KoXmlNode& node, int depth = 1 );
+
+ /**
+ * Unload child nodes of specified node.
+ * This function has no effect if QDom is used.
+ */
+ KOFFICECORE_EXPORT void unload( KoXmlNode& node );
+
+}
+
+#define forEachElement( elem, parent ) \
+ for ( KoXmlNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling() ) \
+ if ( !( elem = _node.toElement() ).isNull() )
+
+
+#endif // KOFFICE_XMLREADER
diff --git a/lib/kofficecore/Koversiondialog.cpp b/lib/kofficecore/Koversiondialog.cpp
new file mode 100644
index 00000000..b21ae52f
--- /dev/null
+++ b/lib/kofficecore/Koversiondialog.cpp
@@ -0,0 +1,143 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Laurent Montel <montel@kde.org>
+
+ 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.
+*/
+
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qmultilineedit.h>
+#include <qpushbutton.h>
+#include <qtoolbutton.h>
+#include <qapplication.h>
+#include <qlayout.h>
+#include <kiconloader.h>
+#include <kbuttonbox.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <klistview.h>
+#include <kdebug.h>
+
+#include <qmultilineedit.h>
+
+#include "Koversiondialog.h"
+
+
+KoVersionDialog::KoVersionDialog( QWidget* parent, const char* name )
+ : KDialogBase( parent, name, true, i18n("Version"), Ok|Cancel )
+{
+ QWidget* page = new QWidget( this );
+ setMainWidget( page );
+
+ QGridLayout *grid1 = new QGridLayout( page,10,3,KDialog::marginHint(), KDialog::spacingHint());
+
+ list=new KListView(page, "versionlist");
+ list->addColumn(i18n("Date & Time"));
+ list->addColumn(i18n("Saved By"));
+ list->addColumn(i18n("Comment"));
+
+ grid1->addMultiCellWidget(list,0,8,0,0);
+
+ m_pAdd=new QPushButton(i18n("&Add"),page);
+ grid1->addWidget(m_pAdd,1,2);
+
+ m_pRemove=new QPushButton(i18n("&Remove"),page);
+ grid1->addWidget(m_pRemove,2,2);
+
+ m_pModify=new QPushButton(i18n("&Modify"),page);
+ grid1->addWidget(m_pModify,3,2);
+
+ m_pOpen=new QPushButton(i18n("&Open"),page);
+ grid1->addWidget(m_pOpen,4,2);
+
+
+ connect( m_pRemove, SIGNAL( clicked() ), this, SLOT( slotRemove() ) );
+ connect( m_pAdd, SIGNAL( clicked() ), this, SLOT( slotAdd() ) );
+ connect( m_pOpen, SIGNAL( clicked() ), this, SLOT( slotOpen() ) );
+ connect( m_pModify, SIGNAL( clicked() ), this, SLOT( slotModify() ) );
+
+ updateButton();
+
+ resize( 600, 250 );
+
+}
+
+KoVersionDialog::~KoVersionDialog()
+{
+}
+
+void KoVersionDialog::updateButton()
+{
+#if 0
+ bool state = ( list->currentItem() >= 0 );
+ m_pRemove->setEnabled( state );
+#endif
+}
+
+void KoVersionDialog::slotAdd()
+{
+ //TODO create entry
+}
+
+void KoVersionDialog::slotRemove()
+{
+ //TODO remove entry
+}
+
+void KoVersionDialog::slotModify()
+{
+ KoVersionModifyDialog * dlg = new KoVersionModifyDialog( this /*, const QString &_comment*/ /*TODO add*/ );
+ if ( dlg->exec() )
+ {
+ //TODO
+ kdDebug()<<" comment :"<<dlg->comment()<<endl;
+ }
+ delete dlg;
+
+}
+
+void KoVersionDialog::slotOpen()
+{
+ //TODO open file
+}
+
+void KoVersionDialog::slotOk()
+{
+ accept();
+}
+
+KoVersionModifyDialog::KoVersionModifyDialog( QWidget* parent, const QString &/*comment*/, const char* name )
+ : KDialogBase( parent, name, true, i18n("Comment"), Ok|Cancel )
+{
+ QWidget* page = new QWidget( this );
+ setMainWidget( page );
+
+ QHBoxLayout *grid1 = new QHBoxLayout( page,KDialog::marginHint(), KDialog::spacingHint());
+
+ m_multiline=new QMultiLineEdit(page, "multiline");
+ grid1->addWidget( m_multiline );
+
+}
+
+QString KoVersionModifyDialog::comment() const
+{
+ return m_multiline->text();
+}
+
+
+#include "Koversiondialog.moc"
diff --git a/lib/kofficecore/Koversiondialog.h b/lib/kofficecore/Koversiondialog.h
new file mode 100644
index 00000000..6a8c7fb6
--- /dev/null
+++ b/lib/kofficecore/Koversiondialog.h
@@ -0,0 +1,68 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Laurent Montel <montel@kde.org>
+
+ 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 __VERSION_DIALOG__
+#define __VERSION_DIALOG__
+
+#include <kdialogbase.h>
+
+class QPushButton;
+class QToolButton;
+class KListView;
+class QMultiLineEdit;
+
+class KoVersionDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ KoVersionDialog( QWidget* parent, const char* name = 0L );
+ ~KoVersionDialog();
+
+public slots:
+ virtual void slotOk();
+ void slotRemove();
+ void slotAdd();
+ void slotOpen();
+ void slotModify();
+
+protected:
+
+ void init();
+ void updateButton();
+
+ KListView * list;
+ QPushButton* m_pRemove;
+ QPushButton* m_pAdd;
+ QPushButton* m_pOpen;
+ QPushButton* m_pModify;
+};
+
+class KoVersionModifyDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ KoVersionModifyDialog( QWidget* parent, const QString &_comment = QString::null , const char* name = 0L );
+
+ QString comment() const;
+
+private:
+ QMultiLineEdit *m_multiline;
+};
+
+#endif
diff --git a/lib/kofficecore/Makefile.am b/lib/kofficecore/Makefile.am
new file mode 100644
index 00000000..df7cdfa8
--- /dev/null
+++ b/lib/kofficecore/Makefile.am
@@ -0,0 +1,70 @@
+####### General stuff
+
+SUBDIRS = . tests
+KDE_CXXFLAGS = $(USE_RTTI) $(WOVERLOADED_VIRTUAL)
+INCLUDES= $(KSTORE_INCLUDES) $(KWMF_INCLUDES) $(all_includes)
+libkofficecore_la_LIBADD = $(LIB_KSTORE) $(LIB_KOWMF) $(LIB_KPARTS) \
+ $(LIB_KDEPRINT) $(LIB_KABC) $(LIB_KWMF)
+
+####### Files
+
+lib_LTLIBRARIES = libkofficecore.la
+kde_module_LTLIBRARIES = kodocinfopropspage.la
+
+libkofficecore_la_SOURCES = KoDocument.cpp \
+ KoGlobal.cpp KoUnit.cpp KoFilterManager.cpp \
+ KoMainWindow.cpp \
+ KoApplication.cpp KoQueryTrader.cpp \
+ KoFilter.cpp KoFilterChain.cpp \
+ KoPictureKey.cpp KoPictureBase.cpp KoPicture.cpp KoPictureShared.cpp \
+ KoPictureImage.cpp KoPictureClipart.cpp KoPictureCollection.cpp \
+ KoPictureEps.cpp KoPictureWmf.cpp \
+ KoDocumentInfo.cpp \
+ KoView.cpp KoFrame.cpp KoContainerHandler.cpp KoDocumentChild.cpp \
+ KoDocumentInfoDlg.cpp KoFactory.cpp KoChild.cpp \
+ koDocumentInfoAboutWidget.ui koDocumentInfoAuthorWidget.ui \
+ koDocumentInfoUserMetadataWidget.ui \
+ KoApplicationIface.cc KoApplicationIface.skel \
+ KoDocumentIface.cc KoDocumentIface.skel KoViewIface.cc KoViewIface.skel \
+ KoMainWindowIface.cc KoMainWindowIface.skel kofficeversion.cc KoOasisStyles.cpp \
+ KoStyleStack.cpp KoGenStyles.cpp KoOasisSettings.cpp KoPageLayout.cpp \
+ KoFileDialog.cpp KoXmlNS.cpp KoDom.cpp Koversiondialog.cpp KoOasisStore.cpp \
+ kkbdaccessextensions.cpp koDetailsPaneBase.ui koOpenPaneBase.ui KoOpenPane.cpp \
+ KoTemplates.cpp KoDetailsPane.cpp \
+ KoSpeaker.cpp KoOasisLoadingContext.cpp \
+ KoRect.cpp
+
+libkofficecore_la_LDFLAGS = $(all_libraries) -version-info 3:0:0 -no-undefined
+
+include_HEADERS = KoContainerHandler.h \
+ KoFilter.h KoFilterChain.h \
+ KoGlobal.h KoUnit.h KoDocument.h \
+ KoMainWindow.h \
+ KoApplication.h KoQueryTrader.h \
+ KoFilterManager.h \
+ KoDocumentInfo.h \
+ KoView.h KoFrame.h KoDocumentChild.h \
+ KoDocumentInfoDlg.h KoFactory.h KoChild.h \
+ KoApplicationIface.h KoDocumentIface.h KoViewIface.h KoMainWindowIface.h \
+ KoPictureKey.h KoPicture.h KoPictureCollection.h kofficeversion.h KoOasisStyles.h \
+ KoStyleStack.h KoGenStyles.h KoOasisSettings.h KoPageLayout.h KoXmlNS.h KoDom.h Koversiondialog.h \
+ kkbdaccessextensions.h \
+ koffice_export.h KoOpenPane.h \
+ KoSpeaker.h KoOasisLoadingContext.h \
+ KoPoint.h
+
+noinst_HEADERS = KoDocument_p.h KoFilterManager_p.h \
+ KoPictureShared.h KoPictureBase.h KoPictureImage.h KoPictureClipart.h \
+ KoPictureEps.h KoPictureWmf.h
+
+kodocinfopropspage_la_SOURCES = KoDocInfoPropsFactory.cpp
+kodocinfopropspage_la_LIBADD = ./libkofficecore.la
+kodocinfopropspage_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+
+METASOURCES = AUTO
+
+rcdir = $(kde_datadir)/koffice
+rc_DATA = koffice_shell.rc
+
+service_DATA = kodocinfopropspage.desktop
+servicedir = $(kde_servicesdir)
diff --git a/lib/kofficecore/THOUGHTS b/lib/kofficecore/THOUGHTS
new file mode 100644
index 00000000..f0121aa9
--- /dev/null
+++ b/lib/kofficecore/THOUGHTS
@@ -0,0 +1,164 @@
+------------------------------------------------------------------------------
+- Bugs and missing features in the KOffice Library -
+- some initial brainstroming... comments? flames? -
+------------------------------------------------------------------------------
+
+1) Accurate Coordinates:
+IMHO it's a must for KOffice applications to store reasonably accurate
+coordinates. If zooming is supported I think we should try to make it work
+up to zoom factors of ten (1000%) or so. This means you need some kind
+of "spare precision". Another related issue is getting KOffice applications
+support 1:1 sizes on the screen (i.e. that an A4 paper is A4 also on the
+screen). Here the problem is that in X we can have a wide range of
+resolutions (from 75 or so up to 120 dpi). We also can have different
+resolutions in X and Y direction, but as Qt only honors one of them (the
+Y resolution) I think it's safe to ignore the X resolution (see
+QPaintDevice::x11AppDpiY()).
+In lib/kofficeui we have some classes replacing QPoint/QRect (they use double
+precision floating point numbers to store coordinates).
+
+2) Embedding:
+2.1) "Plain" KOffice embedding (paintContent):
+We finally added two double values for the X and Y zoom factor. This means
+using these values and the QRect argument we can "ask" the part to draw
+certain areas of itself. One problem I still see with this method is, that we
+only have a QRect there. It's not that problematic there because we can
+agree on some hacks if it's not accurate enough (e.g. values * 100.0), or we
+just agree that this QRect represents screen pixels at the given zoom value(s)
+and the app can calculate the requested area to be painted itself. This should
+solve zooming and "scrolling" problems.
+One ugly problem also is what we will do about performance problems on
+repainting a part. Some time ago we got a mail on KOffice where some guy
+asked what to do about making that embedded painting faster. The reason is
+that we *always* repaint the whole area, no matter what. This can easily be
+optimized to a WNorthWestGravity like behavior, because you can't edit those
+embedded parts anyway, so even kchart and other non-WNG apps support that.
+( we have to translate the painter, of course).
+
+### Zooming Special (copy from DESIGN_zooming in kofficecore):
+There are two kinds of zooming:
+
+1) View Zooming:
+This kind of zooming is only done by full KoViews, regardless
+whether they are the root views (not embedded), or they are
+*active* embedded views. There only has to be a KoView object.
+
+There is *one* zoom factor for both axes (x/y) and the whole
+content (text, objects, children,...) is zoomed accordingly.
+
+This kind of zooming is like the zoom support you know from
+other applications.
+
+
+2) Child Zooming:
+Imagine you'd like to show a certain part of an embedded
+document, but you have to fill a specified area. At the
+moment this is "impossible" (we know that it's kind of
+possible, but it's not straightforward) because when you
+resize the child's frame, the contents resize, too. Due
+to that you see more of the child's content :)
+
+The solution will be: If you simply resize a frame, the
+child document will resize, too, and show more contents.
+If you press Alt/Meta during resizing, the child's
+content will stay the same, but it will be zoomed to
+fit the rectangle you specify.
+Pressing Shift gives you a constant width/height ratio
+during resizing.
+Pressing Alt+Shift is allowed, too, if you can do that :p
+
+As you already know we'll support a zoom factor for
+each axe here. (i.e. x/y zoom factors)
+
+Related to that is child-panning. Indepenent of the zoom factor
+the child shouldn't just show the left/top corner, but it should
+"remember" the position inside the document when it's repainted
+as inactive child.
+###
+
+If a part doesn't support zooming natively we have to fall back to WMartix
+hacks (don't know how we can "ask" a part about that, but I'm sure we find
+some BC way).
+
+2.2) Widget embedding:
+That's a tough topic... but let's see... There are a few different possibilities
+we have to treat differently:
+a) plain KParts:
+We can only embed the widgets obviously. Printing will look horribly, but
+I think we should support that nonetheless. Imagine a KPresenter (screen)
+presentation with an embedded video player part on some page :))
+I'd say when printing those files the parts aren't printed at all by default...
+makes more sense to me than redirecting an ugly 96dpi printout to some
+QPrinter... well. It should at least be possible to print that stuff and get
+some crappy output (but at least some output, e.g. an empty frame with some
+text information about the part in it). Of course the user should be warned
+before embedding such parts :)
+b) embedding special KOffice parts as widgets:
+Here we can simply add one entry "X-KDE-EmbedAsWidget" or so to our
+.desktop files for KOffice parts. This will guarantee then that this special
+part wants to be embedded as a widget. This surely makes sense, but still
+we have some problems on printing... no idea how to solve that one. Maybe
+we should use the widget for viewing on the screen and fall back to a plain
+paintContents when printing.
+
+General embedding stuff:
+Do we support the "transparent" flag in paintContent with all parts we have?
+IMHO it's a nice feature and we sould keep it, but if no part supports it...
+well :]
+Maybe we'll have to add a X-KDE-DoNotEmbed flag to the .desktop files
+at some point. These parts should be excluded in the part select dia, then.
+
+3) Handling of embedded parts:
+This is one of the most annoying things in KOffice. Every application
+handles embedded parts different. Of course this makes sense for most
+of the cases, but maybe we can make that a bit more consistent at least
+among "object-based" applications like KPresenter, Kivio, and KIllustrator.
+
+4) Printing:
+Here the problem is that even Qt has enough problems with printing :(
+We definitely need some magic here, because right now we don't take
+any advantage of the better resolution of the printer. This will be a hard
+job (and an ugly hack) even if we have accurate coordinates and so
+on. Lars told me that QPainter will have some setResolution call for
+printing in 3.0... let's see. As a temporary solution he suggested to scale
+the painter by 0.1 and print 10 times as big... don't know if that works.
+
+5) Shell menus:
+The Help -> About entry should be the one for the active part.
+
+6) Image handling:
+We need one class (IMVHO a part is too much overhead) which properly
+handles all kinds of images. Internal ones, external ones, maybe thumbnails,
+proper rescaling w/o any quality loss on rescaling again and again (read:
+keep the original around),... Fortunately Simon already implemented most of it.
+He also suggested that we need a very tiny KOffice part which just can
+display images. That way we can finally support images in KSpread and other
+non-object based apps.
+
+7) Colors:
+What about RGB-CMYK-HSV-LAB-... should this be solved in the KOffice libs?
+I see some code for this in krayon, and maybe Matthias Elter or gis can help
+us with that one.
+
+8) General configuration options:
+Do we need a KOffice kcm? What to configure there?
+e.g.:
+- start default template (which one :) (start without any template dialog)
+- default page size
+- default unit
+- ...
+
+9) Make it possible for components to provide their own about dialog.
+ What's currently needed to acomplish this is an awful addShell()
+ hack, like in kivio_doc.cpp .
+
+ Possible solution:
+ Make a slot in KoMainWindow call a virtual method in KoDocument which
+ calls back the real show-about method. That would give components the
+ ability to re-implement that very method and be done.
+
+ Disadvantage: breaks binary compatibility.
+ Possible workaround for the BC breakage: Don't call a virtual method
+ but call/connect-to a slot in the document only if it exists and
+ call the default show-about method otherwise.
+
diff --git a/lib/kofficecore/document-info.dtd b/lib/kofficecore/document-info.dtd
new file mode 100644
index 00000000..bff7a00b
--- /dev/null
+++ b/lib/kofficecore/document-info.dtd
@@ -0,0 +1,80 @@
+<!-- $Id: document-info.dtd 466019 2005-10-01 11:07:55Z mlaurent $
+
+This is an XML document type definition (DTD) for the documentinfo.xml files which
+are used by all KOffice-1.1 and 1.1.1 programs, to store information about the
+document (e.g. title and abstract) and its author.
+
+ChangeLog:
+Initial version (for koffice-1.1.1) written by David Faure <faure@kde.org>.
+-->
+
+<!--
+ The document-info for a KOffice document contains one main element for each tab of
+the "Document Info" dialog. As of KOffice-1.1, this includes: author and about.
+A third page, "log" is planned for the future.
+-->
+<!ELEMENT document-info
+ (author?, about?, log?)>
+
+<!--
+ The "author" element stores information about the author of the document.
+-->
+<!ELEMENT author
+ (full-name?, initial?, title?, company?, email?, telephone?, telephone-work?, fax?, country?, postal-code?, city?, street?, position?)>
+
+<!ELEMENT full-name
+ (#PCDATA)>
+<!ELEMENT initial
+ (#PCDATA)>
+<!ELEMENT title
+ (#PCDATA)>
+<!ELEMENT company
+ (#PCDATA)>
+<!ELEMENT email
+ (#PCDATA)>
+<!ELEMENT telephone
+ (#PCDATA)>
+<!ELEMENT telephone-work
+ (#PCDATA)>
+<!ELEMENT fax
+ (#PCDATA)>
+<!ELEMENT country
+ (#PCDATA)>
+<!ELEMENT postal-code
+ (#PCDATA)>
+<!ELEMENT city
+ (#PCDATA)>
+<!ELEMENT street
+ (#PCDATA)>
+<!ELEMENT position
+ (#PCDATA)>
+
+<!--
+ The "about" element contains information about the document itself.
+ This includes an optional title for the document, and an optional abstract.
+-->
+<!ELEMENT about
+ (abstract?, title?, keyword?, subject?, initial-creator?,
+ editing-cycles?, creation-date?, date?)>
+<!ELEMENT abstract
+ (#PCDATA)>
+<!--title already defined above-->
+<!ELEMENT keyword
+ (#PCDATA)>
+<!ELEMENT subject
+ (#PCDATA)>
+<!ELEMENT initial-creator
+ (#PCDATA)>
+<!ELEMENT editing-cycles
+ (#PCDATA)>
+<!ELEMENT creation-date
+ (#PCDATA)>
+<!ELEMENT date
+ (#PCDATA)>
+
+<!--
+ The "log" element is unused at the moment.
+-->
+<!ELEMENT log
+ EMPTY>
+
diff --git a/lib/kofficecore/kkbdaccessextensions.cpp b/lib/kofficecore/kkbdaccessextensions.cpp
new file mode 100644
index 00000000..aae37203
--- /dev/null
+++ b/lib/kofficecore/kkbdaccessextensions.cpp
@@ -0,0 +1,667 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005, Gary Cramblitt <garycramblitt@comcast.net>
+
+ 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.
+*/
+
+// Qt includes
+#include <qsplitter.h>
+#include <qdockwindow.h>
+#include <qdockarea.h>
+#include <qevent.h>
+#include <qcursor.h>
+#include <qobjectlist.h>
+#include <qwidgetlist.h>
+#include <qlabel.h>
+#include <qtooltip.h>
+
+// KDE includes
+#include <klocale.h>
+#include <kglobal.h>
+#include <kapplication.h>
+#include <kmainwindow.h>
+#include <kaction.h>
+#include <kdebug.h>
+
+// KKbdAccessExtensions includes
+#include "kkbdaccessextensions.h"
+// TODO: See eventFilter method.
+//#include "kkbdaccessextensions.moc"
+
+class KPanelKbdSizerIcon : public QCursor
+{
+ public:
+ KPanelKbdSizerIcon() :
+ QCursor(Qt::SizeAllCursor),
+ isActive(false)
+ {
+ currentPos = QPoint(-1, -1);
+ }
+
+ ~KPanelKbdSizerIcon()
+ {
+ hide();
+ }
+
+ void show(const QPoint p) {
+ if (!isActive) {
+ originalPos = QCursor::pos();
+ kapp->setOverrideCursor(*this);
+ isActive = true;
+ }
+ if (p != pos())
+ setPos(p);
+ currentPos = p;
+ }
+
+ void hide() {
+ if (isActive) {
+ kapp->restoreOverrideCursor();
+ QCursor::setPos(originalPos);
+ }
+ isActive = false;
+ }
+
+ void setShape(int shayp)
+ {
+ if (shayp != shape()) {
+ // Must restore and override to get the icon to refresh.
+ if (isActive) kapp->restoreOverrideCursor();
+ QCursor::setShape(shayp);
+ if (isActive) kapp->setOverrideCursor(*this);
+ }
+ }
+
+ // Return the difference between a position and where icon is supposed to be.
+ QSize delta(const QPoint p)
+ {
+ QPoint d = p - currentPos;
+ return QSize(d.x(), d.y());
+ }
+
+ // Return the difference between where the icon is currently positioned and where
+ // it is supposed to be.
+ QSize delta() { return delta(pos()); }
+
+ // True if the sizing icon is visible.
+ bool isActive;
+
+ private:
+ // Icon's current position.
+ QPoint currentPos;
+ // Mouse cursor's original position when icon is shown.
+ QPoint originalPos;
+};
+
+class KKbdAccessExtensionsPrivate
+{
+ public:
+ KKbdAccessExtensionsPrivate() :
+ fwdAction(0),
+ revAction(0),
+ accessKeysAction(0),
+ panel(0),
+ handleNdx(0),
+ icon(0),
+ stepSize(10),
+ accessKeyLabels(0) {};
+
+ ~KKbdAccessExtensionsPrivate()
+ {
+ delete icon;
+ // TODO: This crashes, but should delete in the event that KMainWindow is not deleted.
+ if (accessKeyLabels) {
+ accessKeyLabels->setAutoDelete(false);
+ delete accessKeyLabels;
+ }
+ }
+
+ // Action that starts panel sizing (defaults to F8), forward and reverse;
+ KAction* fwdAction;
+ KAction* revAction;
+
+ // Action that starts access keys.
+ KAction* accessKeysAction;
+
+ // The splitter or dockwindow currently being sized. If 0, sizing is not in progress.
+ QWidget* panel;
+
+ // Index of current handle of the panel. When panel is a QDockWindow:
+ // 1 = size horizontally
+ // 2 = size vertically
+ uint handleNdx;
+
+ // Sizing icon.
+ KPanelKbdSizerIcon* icon;
+
+ // Sizing increment.
+ int stepSize;
+
+ // List of the access key QLabels. If not 0, access keys are onscreen.
+ QPtrList<QLabel>* accessKeyLabels;
+
+ // Pointer to the KMainWindow.
+ KMainWindow* mainWindow;
+};
+
+KKbdAccessExtensions::KKbdAccessExtensions(KMainWindow* parent, const char* name) :
+ QObject(parent, name)
+{
+ // kdDebug() << "KKbdAccessExtensions::KKbdAccessExtensions: running." << endl;
+ d = new KKbdAccessExtensionsPrivate;
+ d->mainWindow = parent;
+ d->fwdAction = new KAction(i18n("Resize Panel Forward"), KShortcut("F8"),
+ 0, 0, parent->actionCollection(), "resize_panel_forward");
+ d->revAction = new KAction(i18n("Resize Panel Reverse"), KShortcut("Shift+F8"),
+ 0, 0, parent->actionCollection(), "resize_panel_reverse");
+ d->accessKeysAction = new KAction(i18n("Access Keys"), KShortcut("Alt+F8"),
+ 0, 0, parent->actionCollection(), "access_keys");
+ // "Disable" the shortcuts so we can see them in eventFilter.
+ d->fwdAction->setEnabled(false);
+ d->revAction->setEnabled(false);
+ d->accessKeysAction->setEnabled(false);
+ d->icon = new KPanelKbdSizerIcon();
+ kapp->installEventFilter(this);
+}
+
+KKbdAccessExtensions::~KKbdAccessExtensions()
+{
+ kapp->removeEventFilter(this);
+ if (d->panel) exitSizing();
+ delete d;
+}
+
+int KKbdAccessExtensions::stepSize() const { return d->stepSize; }
+
+void KKbdAccessExtensions::setStepSize(int s) { d->stepSize = s; }
+
+bool KKbdAccessExtensions::eventFilter( QObject *o, QEvent *e )
+{
+ if ( e->type() == QEvent::KeyPress ) {
+ // TODO: This permits only a single-key shortcut. For example, Alt+S,R would not work.
+ // If user configures a multi-key shortcut, it is undefined what will happen here.
+ // It would be better to handle these as KShortcut activate() signals, but the problem
+ // is that once a QDockWindow is undocked and has focus, the KShortcut activate() signals
+ // don't fire anymore.
+ KShortcut fwdSc = d->fwdAction->shortcut();
+ KShortcut revSc = d->revAction->shortcut();
+ KShortcut accessKeysSc = d->accessKeysAction->shortcut();
+ QKeyEvent* kev = dynamic_cast<QKeyEvent *>(e);
+ KKey k = KKey(kev);
+ KShortcut sc = KShortcut(k);
+ // kdDebug() << "KKbdAccessExtensions::eventFilter: Key press " << sc << endl;
+ if (!d->accessKeyLabels) {
+ if (sc == fwdSc) {
+ nextHandle();
+ return true;
+ }
+ if (sc == revSc) {
+ prevHandle();
+ return true;
+ }
+ }
+ if (d->panel) {
+ if (k == KKey(Key_Escape))
+ exitSizing();
+ else
+ resizePanelFromKey(kev->key(), kev->state());
+ // Eat the key.
+ return true;
+ }
+ if (sc == accessKeysSc && !d->panel) {
+ if (d->accessKeyLabels) {
+ delete d->accessKeyLabels;
+ d->accessKeyLabels = 0;
+ } else
+ displayAccessKeys();
+ return true;
+ }
+ if (d->accessKeyLabels) {
+ if (k == KKey(Key_Escape)) {
+ delete d->accessKeyLabels;
+ d->accessKeyLabels = 0;
+ } else
+ handleAccessKey(kev);
+ return true;
+ }
+ return false;
+ }
+ else if (d->icon->isActive && e->type() == QEvent::MouseButtonPress) {
+ exitSizing();
+ return true;
+ }
+ else if (d->accessKeyLabels && e->type() == QEvent::MouseButtonPress) {
+ delete d->accessKeyLabels;
+ d->accessKeyLabels = 0;
+ return true;
+ }
+/* else if (e->type() == QEvent::MouseMove && d->icon->isActive) {
+ // Lock mouse cursor down.
+ showIcon();
+ dynamic_cast<QMouseEvent *>(e)->accept();
+ return true;
+ }*/
+ else if (e->type() == QEvent::MouseMove && d->icon->isActive && d->panel) {
+ // Resize according to mouse movement.
+ QMouseEvent* me = dynamic_cast<QMouseEvent *>(e);
+ QSize s = d->icon->delta();
+ int dx = s.width();
+ int dy = s.height();
+ resizePanel(dx, dy, me->state());
+ me->accept();
+ showIcon();
+ return true;
+ }
+ else if (e->type() == QEvent::Resize && d->panel && o == d->panel) {
+ // TODO: This doesn't always work.
+ showIcon();
+ }
+ return false;
+}
+
+QWidgetList* KKbdAccessExtensions::getAllPanels()
+{
+ QWidgetList* allWidgets = kapp->allWidgets();
+ QWidgetList* allPanels = new QWidgetList;
+ QWidget* widget = allWidgets->first();
+ while (widget) {
+ if (widget->isVisible()) {
+ if (::qt_cast<QSplitter*>( widget )) {
+ // Only size QSplitters with at least two handles (there is always one hidden).
+ if (dynamic_cast<QSplitter *>(widget)->sizes().count() >= 2)
+ allPanels->append(widget);
+ } else if (::qt_cast<QDockWindow*>( widget )) {
+ if (dynamic_cast<QDockWindow *>(widget)->isResizeEnabled()) {
+ // kdDebug() << "KKbdAccessExtensions::getAllPanels: QDockWindow = " << widget->name() << endl;
+ allPanels->append(widget);
+ }
+ }
+ }
+ widget = allWidgets->next();
+ }
+ delete allWidgets;
+ return allPanels;
+}
+
+void KKbdAccessExtensions::nextHandle()
+{
+ QWidget* panel = d->panel;
+ // See if current panel has another handle. If not, find next panel.
+ if (panel) {
+ bool advance = true;
+ d->handleNdx++;
+ if (::qt_cast<QSplitter*>( panel ))
+ advance = (d->handleNdx >= dynamic_cast<QSplitter *>(panel)->sizes().count());
+ else
+ // Undocked windows have only one "handle" (center).
+ advance = (d->handleNdx > 2 || !dynamic_cast<QDockWindow *>(panel)->area());
+ if (advance) {
+ QWidgetList* allWidgets = getAllPanels();
+ allWidgets->findRef(panel);
+ panel = 0;
+ if (allWidgets->current()) panel = allWidgets->next();
+ delete allWidgets;
+ d->handleNdx = 1;
+ }
+ } else {
+ // Find first panel.
+ QWidgetList* allWidgets = getAllPanels();
+ panel = allWidgets->first();
+ delete allWidgets;
+ d->handleNdx = 1;
+ }
+ d->panel = panel;
+ if (panel)
+ showIcon();
+ else
+ exitSizing();
+}
+
+void KKbdAccessExtensions::prevHandle()
+{
+ QWidget* panel = d->panel;
+ // See if current panel has another handle. If not, find previous panel.
+ if (panel) {
+ bool rewind = true;
+ d->handleNdx--;
+ rewind = (d->handleNdx < 1);
+ if (rewind) {
+ QWidgetList* allWidgets = getAllPanels();
+ allWidgets->findRef(panel);
+ panel = 0;
+ if (allWidgets->current()) panel = allWidgets->prev();
+ delete allWidgets;
+ if (panel) {
+ if (::qt_cast<QSplitter*>( panel ))
+ d->handleNdx = dynamic_cast<QSplitter *>(panel)->sizes().count() - 1;
+ else {
+ if (dynamic_cast<QDockWindow *>(panel)->area())
+ d->handleNdx = 2;
+ else
+ d->handleNdx = 1;
+ }
+ }
+ }
+ } else {
+ // Find last panel.
+ QWidgetList* allWidgets = getAllPanels();
+ panel = allWidgets->last();
+ delete allWidgets;
+ if (panel) {
+ if (::qt_cast<QSplitter*>( panel ))
+ d->handleNdx = dynamic_cast<QSplitter *>(panel)->sizes().count() - 1;
+ else {
+ if (dynamic_cast<QDockWindow *>(panel)->area())
+ d->handleNdx = 2;
+ else
+ d->handleNdx = 1;
+ }
+ }
+ }
+ d->panel = panel;
+ if (panel)
+ showIcon();
+ else
+ exitSizing();
+}
+
+void KKbdAccessExtensions::exitSizing()
+{
+ // kdDebug() << "KKbdAccessExtensions::exiting sizing mode." << endl;
+ hideIcon();
+ d->handleNdx = 0;
+ d->panel = 0;
+}
+
+void KKbdAccessExtensions::showIcon()
+{
+ if (!d->panel) return;
+ QPoint p;
+ // kdDebug() << "KKbdAccessExtensions::showIcon: topLevelWidget = " << d->panel->topLevelWidget()->name() << endl;
+ if (::qt_cast<QSplitter*>( d->panel )) {
+ QSplitter* splitter = dynamic_cast<QSplitter *>(d->panel);
+ int handleNdx = d->handleNdx - 1;
+ QValueList<int> sizes = splitter->sizes();
+ // kdDebug() << "KKbdAccessExtensions::showIcon: sizes = " << sizes << endl;
+ if (splitter->orientation() == Qt::Horizontal) {
+ d->icon->setShape(Qt::SizeHorCursor);
+ p.setX(sizes[handleNdx] + (splitter->handleWidth() / 2));
+ p.setY(splitter->height() / 2);
+ } else {
+ d->icon->setShape(Qt::SizeVerCursor);
+ p.setX(splitter->width() / 2);
+ p.setY(sizes[handleNdx] + (splitter->handleWidth() / 2));
+ }
+ // kdDebug() << "KKbdAccessExtensions::showIcon: p = " << p << endl;
+ p = splitter->mapToGlobal(p);
+ // kdDebug() << "KKbdAccessExtensions::showIcon: mapToGlobal = " << p << endl;
+ } else {
+ QDockWindow* dockWindow = dynamic_cast<QDockWindow *>(d->panel);
+ p = dockWindow->pos();
+ if (dockWindow->area()) {
+ // kdDebug() << "KKbdAccessExtensions::showIcon: pos = " << p << " of window = " << dockWindow->parentWidget()->name() << endl;
+ p = dockWindow->parentWidget()->mapTo(dockWindow->topLevelWidget(), p);
+ // kdDebug() << "KKbdAccessExtensions::showIcon: mapTo = " << p << " of window = " << dockWindow->topLevelWidget()->name() << endl;
+ // TODO: How to get the handle width?
+ if (d->handleNdx == 1) {
+ d->icon->setShape(Qt::SizeHorCursor);
+ if (dockWindow->area()->orientation() == Qt::Vertical) {
+ if (dockWindow->area()->handlePosition() == QDockArea::Normal)
+ // Handle is to the right of the dock window.
+ p.setX(p.x() + dockWindow->width());
+ // else Handle is to the left of the dock window.
+ } else
+ // Handle is to the right of the dock window.
+ p.setX(p.x() + dockWindow->width());
+ p.setY(p.y() + (dockWindow->height() / 2));
+ } else {
+ d->icon->setShape(Qt::SizeVerCursor);
+ p.setX(p.x() + (dockWindow->width() / 2));
+ if (dockWindow->area()->orientation() == Qt::Vertical)
+ // Handle is below the dock window.
+ p.setY(p.y() + dockWindow->height());
+ else {
+ if (dockWindow->area()->handlePosition() == QDockArea::Normal)
+ // Handle is below the dock window.
+ p.setY(p.y() + dockWindow->height());
+ // else Handle is above the dock window.
+ }
+ }
+ p = dockWindow->topLevelWidget()->mapToGlobal(p);
+ } else {
+ d->icon->setShape(Qt::SizeAllCursor);
+ p = QPoint(dockWindow->width() / 2, dockWindow->height() / 2);
+ p = dockWindow->mapToGlobal(p); // Undocked. Position in center of window.
+ }
+ }
+ // kdDebug() << "KKbdAccessExtensions::showIcon: show(p) = " << p << endl;
+ d->icon->show(p);
+}
+
+void KKbdAccessExtensions::hideIcon()
+{
+ d->icon->hide();
+}
+
+void KKbdAccessExtensions::resizePanel(int dx, int dy, int state)
+{
+ int adj = dx + dy;
+ if (adj == 0) return;
+ // kdDebug() << "KKbdAccessExtensions::resizePanel: panel = " << d->panel->name() << endl;
+ if (::qt_cast<QSplitter*>( d->panel )) {
+ QSplitter* splitter = dynamic_cast<QSplitter *>(d->panel);
+ int handleNdx = d->handleNdx - 1;
+ QValueList<int> sizes = splitter->sizes();
+ // kdDebug() << "KKbdAccessExtensions::resizePanel: before sizes = " << sizes << endl;
+ sizes[handleNdx] = sizes[handleNdx] + adj;
+ // kdDebug() << "KKbdAccessExtensions::resizePanel: setSizes = " << sizes << endl;
+ splitter->setSizes(sizes);
+ QApplication::postEvent(splitter, new QEvent(QEvent::LayoutHint));
+ } else {
+ // TODO: How to get the handle width?
+ QDockWindow* dockWindow = dynamic_cast<QDockWindow *>(d->panel);
+ if (dockWindow->area()) {
+ // kdDebug() << "KKbdAccessExtensions::resizePanel: fixedExtent = " << dockWindow->fixedExtent() << endl;
+ QSize fe = dockWindow->fixedExtent();
+ if (d->handleNdx == 1) {
+ // When vertically oriented and dock area is on right side of screen, pressing
+ // left arrow increases size.
+ if (dockWindow->area()->orientation() == Qt::Vertical &&
+ dockWindow->area()->handlePosition() == QDockArea::Reverse) adj = -adj;
+ int w = fe.width();
+ if (w < 0) w = dockWindow->width();
+ w = w + adj;
+ if (w > 0 ) dockWindow->setFixedExtentWidth(w);
+ } else {
+ // When horizontally oriented and dock area is at bottom of screen,
+ // pressing up arrow increases size.
+ if (dockWindow->area()->orientation() == Qt::Horizontal &&
+ dockWindow->area()->handlePosition() == QDockArea::Reverse) adj = -adj;
+ int h = fe.height();
+ if (h < 0) h = dockWindow->height();
+ h = h + adj;
+ if (h > 0) dockWindow->setFixedExtentHeight(h);
+ }
+ dockWindow->updateGeometry();
+ QApplication::postEvent(dockWindow->area(), new QEvent(QEvent::LayoutHint));
+ // kdDebug() << "KKbdAccessExtensions::resizePanel: fixedExtent = " << dockWindow->fixedExtent() << endl;
+ } else {
+ if (state == Qt::ShiftButton) {
+ QSize s = dockWindow->size();
+ s.setWidth(s.width() + dx);
+ s.setHeight(s.height() + dy);
+ dockWindow->resize(s);
+ } else {
+ QPoint p = dockWindow->pos();
+ p.setX(p.x() + dx);
+ p.setY(p.y() + dy);
+ dockWindow->move(p);
+ }
+ }
+ }
+}
+
+void KKbdAccessExtensions::resizePanelFromKey(int key, int state)
+{
+ // kdDebug() << "KPanelKdbSizer::resizePanelFromKey: key = " << key << " state = " << state << endl;
+ if (!d->panel) return;
+ int dx = 0;
+ int dy = 0;
+ int stepSize = d->stepSize;
+ switch (key) {
+ case Qt::Key_Left: dx = -stepSize; break;
+ case Qt::Key_Right: dx = stepSize; break;
+ case Qt::Key_Up: dy = -stepSize; break;
+ case Qt::Key_Down: dy = stepSize; break;
+ case Qt::Key_Prior: dy = -5 * stepSize; break;
+ case Qt::Key_Next: dy = 5 * stepSize; break;
+ }
+ int adj = dx + dy;
+ // kdDebug() << "KKbdAccessExtensions::resizePanelFromKey: adj = " << adj << endl;
+ if (adj != 0)
+ resizePanel(dx, dy, state);
+ else {
+ if (key == Qt::Key_Enter && ::qt_cast<QDockWindow*>( d->panel )) {
+ QDockWindow* dockWindow = dynamic_cast<QDockWindow *>(d->panel);
+ if (dockWindow->area())
+ dockWindow->undock();
+ else
+ dockWindow->dock();
+ }
+ }
+ showIcon();
+}
+
+void KKbdAccessExtensions::displayAccessKeys()
+{
+ // Build a list of valid access keys that don't collide with shortcuts.
+ QString availableAccessKeys = "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890";
+ QPtrList<KXMLGUIClient> allClients = d->mainWindow->factory()->clients();
+ QPtrListIterator<KXMLGUIClient> it( allClients );
+ KXMLGUIClient *client;
+ while( (client=it.current()) !=0 )
+ {
+ ++it;
+ KActionPtrList actions = client->actionCollection()->actions();
+ for (int j = 0; j < (int)actions.count(); j++) {
+ KAction* action = actions[j];
+ KShortcut sc = action->shortcut();
+ for (int i = 0; i < (int)sc.count(); i++) {
+ KKeySequence seq = sc.seq(i);
+ if (seq.count() == 1) {
+ QString s = seq.toString();
+ if (availableAccessKeys.contains(s))
+ availableAccessKeys.remove(s);
+ }
+ }
+ }
+ }
+ // Find all visible, focusable widgets and create a QLabel for each. Don't exceed
+ // available list of access keys.
+ QWidgetList* allWidgets = kapp->allWidgets();
+ QWidget* widget = allWidgets->first();
+ int accessCount = 0;
+ int maxAccessCount = availableAccessKeys.length();
+ int overlap = 20;
+ QPoint prevGlobalPos = QPoint(-overlap, -overlap);
+ while (widget && (accessCount < maxAccessCount)) {
+ if (widget->isVisible() && widget->isFocusEnabled() ) {
+ QRect r = widget->rect();
+ QPoint p(r.x(), r.y());
+ // Don't display an access key if within overlap pixels of previous one.
+ QPoint globalPos = widget->mapToGlobal(p);
+ QPoint diffPos = globalPos - prevGlobalPos;
+ if (diffPos.manhattanLength() > overlap) {
+ accessCount++;
+ QLabel* lab=new QLabel(widget, "", widget, 0, Qt::WDestructiveClose);
+ lab->setPalette(QToolTip::palette());
+ lab->setLineWidth(2);
+ lab->setFrameStyle(QFrame::Box | QFrame::Plain);
+ lab->setMargin(3);
+ lab->adjustSize();
+ lab->move(p);
+ if (!d->accessKeyLabels) {
+ d->accessKeyLabels = new QPtrList<QLabel>;
+ d->accessKeyLabels->setAutoDelete(true);
+ }
+ d->accessKeyLabels->append(lab);
+ prevGlobalPos = globalPos;
+ }
+ }
+ widget = allWidgets->next();
+ }
+ if (accessCount > 0) {
+ // Sort the access keys from left to right and down the screen.
+ QValueList<KSortedLabel> sortedLabels;
+ for (int i = 0; i < accessCount; i++)
+ sortedLabels.append(KSortedLabel(d->accessKeyLabels->at(i)));
+ qHeapSort( sortedLabels );
+ // Assign access key labels.
+ for (int i = 0; i < accessCount; i++) {
+ QLabel* lab = sortedLabels[i].label();
+ QChar s = availableAccessKeys[i];
+ lab->setText(s);
+ lab->adjustSize();
+ lab->show();
+ }
+ }
+}
+
+// Handling of the HTML accesskey attribute.
+bool KKbdAccessExtensions::handleAccessKey( const QKeyEvent* ev )
+{
+// Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
+// but this code must act as if the modifiers weren't pressed
+ if (!d->accessKeyLabels) return false;
+ QChar c;
+ if( ev->key() >= Key_A && ev->key() <= Key_Z )
+ c = 'A' + ev->key() - Key_A;
+ else if( ev->key() >= Key_0 && ev->key() <= Key_9 )
+ c = '0' + ev->key() - Key_0;
+ else {
+ // TODO fake XKeyEvent and XLookupString ?
+ // This below seems to work e.g. for eacute though.
+ if( ev->text().length() == 1 )
+ c = ev->text()[ 0 ];
+ }
+ if( c.isNull())
+ return false;
+
+ QLabel* lab = d->accessKeyLabels->first();
+ while (lab) {
+ if (lab->text() == c) {
+ lab->buddy()->setFocus();
+ delete d->accessKeyLabels;
+ d->accessKeyLabels = 0;
+ return true;
+ }
+ lab = d->accessKeyLabels->next();
+ }
+ return false;
+}
+
+KSortedLabel::KSortedLabel(QLabel* l) :
+ m_l(l) { }
+
+KSortedLabel::KSortedLabel() :
+ m_l(0) { }
+
+bool KSortedLabel::operator<( KSortedLabel l )
+{
+ QPoint p1 = m_l->mapToGlobal(m_l->pos());
+ QPoint p2 = l.label()->mapToGlobal(l.label()->pos());
+ return (p1.y() < p2.y() || (p1.y() == p2.y() && p1.x() < p2.x()));
+}
diff --git a/lib/kofficecore/kkbdaccessextensions.h b/lib/kofficecore/kkbdaccessextensions.h
new file mode 100644
index 00000000..acca057c
--- /dev/null
+++ b/lib/kofficecore/kkbdaccessextensions.h
@@ -0,0 +1,175 @@
+/** @file
+* This file is part of the KDE/KOffice project.
+* Copyright (C) 2005, Gary Cramblitt <garycramblitt@comcast.net>
+*
+* @author Gary Cramblitt <garycramblitt@comcast.net>
+* @since KOffice 1.5
+*
+* 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 __KKBDACCESSEXTENSIONS_H__
+#define __KKBDACCESSEXTENSIONS_H__
+
+// Qt includes.
+#include <qobject.h>
+
+// KOffice includes.
+#include <koffice_export.h>
+
+class KKbdAccessExtensionsPrivate;
+class QWidgetList;
+class KMainWindow;
+
+/** KKbdAccessExtensions is an object that improves accessibility for motor impaired users
+* who may not be able to easily use a mouse. It adds two new capabilities using the keyboard:
+*
+* - Resizing and positioning of panel widgets derived from QSplitter and QDockWindow.
+* - Setting focus to any widget that accepts focus.
+*
+* @section sizing_mode Sizing Mode
+*
+* Users may press F8 or Shift-F8 (defaults) to enter sizing mode. A sizing icon appears on the first
+* QSplitter or QDockWindow handle found in the application (F8) or the last such handle (Shift+F8).
+* (A "handle" is the divider bar that appears to the left, right, above, or below each panel
+* of a QSplitter or QDockArea.)
+*
+* Once in sizing mode, the following functions are available via the keyboard:
+*
+* - F8 Moves to the next sizing handle. After the last handle, exits sizing mode.
+* - Shift+F8 Moves to the previous sizing handle. After the first handle, exits sizing mode.
+* - Esc Exits sizing mode.
+* - LeftArrow When on a vertical sizing handle, moves the handle to the left.
+* When on a horizontal sizing handle, moves the handle up.
+* - RightArrow When on a vertical sizing handle, moves the handle to the right.
+* When on a horizontal sizing handle, moves the handle down.
+* - UpArrow When on a vertical sizing handle, moves the handle to the left.
+* When on a horizontal sizing handle, moves the handle up.
+* - DownArrow When on a vertical sizing handle, moves the handle to the right.
+* When on a horizontal sizing handle, moves the handle down.
+* - PgUp Like LeftArrow or UpArrow, but moves the handle 5X farther to the left or up.
+* - PgDn Like RightArrow or DownArrow, but moves the handle 5X farther to the right or down.
+* - Enter (On numeric keypad). When on the handle of a QDockWindow, undocks or docks
+* the widget. Ignored when on the handle of a QSplitter.
+*
+* The default step size for each arrow key press is 10 pixels.
+*
+* When a QDockWindow is undocked, the sizing icon appears in the center of the window.
+* The arrow keys and PgUp/PgDn move the undocked window on the screen. Shifted arrow keys
+* and PgUp/PgDn decrease/increase the size of the undocked window.
+*
+* When the sizing icon is on a sizing handle, the mouse may also be used to move the handle
+* without having to click and drag. When moving the mouse while sizing icon is on an undocked
+* QDockWindow, the window moves with the mouse. Holding Shift down while moving the mouse
+* sizes the QDockWindow.
+*
+* @note Users can also move and size undocked windows using the Window Operations Menu (Alt+F3).
+*
+* Clicking any mouse button exits sizing mode.
+*
+* When entering sizing mode, the position of the mouse cursor is saved and restored when
+* exiting sizing mode.
+*
+* For a QSplitter or QDockWindow to be found, it must be in the kapp::allWidgets() list.
+*
+* @section focus_setting Focus Setting
+*
+* Users can press Alt-F8. A small box appears in the upperleft corner of each visible widget
+* on the screen that can accept focus. Each box is assigned a single letter or digit.
+* User can press the corresponding key to set focus to the widget.
+*
+* At most 36 such shortcuts are possible. If any application shortcuts are single letters
+* or digits, those shortcuts are not in any of the boxes.
+*
+* Clicking any mouse button exits Focus Setting mode.
+*
+* @section notes Notes
+*
+* The F8, Shift+F8, and Alt+F8 keys are KShortcuts and therefore user may choose different keys in
+* the application's Configure Shortcuts dialog.
+*
+* @note At present, these shortcuts may not be multi-key. If user sets multi-key
+* shortcuts, they will not work.
+*
+* F8/Shift+F8 are the default shortcuts because these are the keys used for similar
+* functionality in GNOME and Java SWT.
+*/
+class KOFFICECORE_EXPORT KKbdAccessExtensions : public QObject
+{
+ // TODO: A .moc isn't really needed right now, but see TODO in eventFilter method.
+ // Q_PROPERTY(int stepSize READ stepSize WRITE setStepSize)
+
+ public:
+ /** Constructor.
+ * @param parent KMainWindow of the application. Required.
+ * @param name (optional) Name of this object.
+ */
+ KKbdAccessExtensions(KMainWindow* parent, const char* name = 0);
+
+ /** Destructor. */
+ virtual ~KKbdAccessExtensions();
+
+ /** Returns number of pixels panel is sized for each arrow key pressed. Default is 10. */
+ int stepSize() const;
+ /** Sets number of pixels panel is sized for each arrow key pressed. */
+ void setStepSize(int s);
+
+ protected:
+ /** Event filter installed on kapp object. */
+ bool eventFilter( QObject *o, QEvent *e );
+
+ /** Retrieves a list of all Splitter and DockArea widgets in the application. */
+ QWidgetList* getAllPanels();
+ /** Advances to the next Panel handle. If not currently in resizing mode,
+ turns it on. */
+ void nextHandle();
+ /** Moves to the previous Panel handle. If not currently in resizing mode,
+ turns it on. */
+ void prevHandle();
+ /** Exits Sizing mode. */
+ void exitSizing();
+ /** Moves panel handle based on key pressed. */
+ void resizePanelFromKey(int key, int state);
+ /** Moves panel handle based on deltaX and deltaY and state of keyboard modifier keys. */
+ void resizePanel(int dx, int dy, int state);
+ /** Displays the sizer icon. */
+ void showIcon();
+ /** Hides the sizer icon. */
+ void hideIcon();
+
+ /** Displays the access keys. */
+ void displayAccessKeys();
+ /** Handles an access keypress. */
+ bool handleAccessKey( const QKeyEvent* ev );
+
+ private:
+ KKbdAccessExtensionsPrivate* d;
+};
+
+/** Provides a way to sort QLabelss using a QValueList based on their screen position. */
+class KSortedLabel
+{
+public:
+ KSortedLabel(QLabel* l);
+ KSortedLabel(); // default constructor
+ bool operator<( KSortedLabel l);
+ QLabel* label() { return m_l; }
+
+private:
+ QLabel* m_l;
+};
+
+#endif // __KKBDACCESSEXTENSIONS_H__
diff --git a/lib/kofficecore/koDetailsPaneBase.ui b/lib/kofficecore/koDetailsPaneBase.ui
new file mode 100644
index 00000000..282e8bb6
--- /dev/null
+++ b/lib/kofficecore/koDetailsPaneBase.ui
@@ -0,0 +1,238 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KoDetailsPaneBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KoDetailsPaneBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>528</width>
+ <height>549</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>DetailsPaneBase</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QSplitter" row="0" column="0">
+ <property name="name">
+ <cstring>m_splitter</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_documentList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>30</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="resizeMode">
+ <enum>LastColumn</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KTextBrowser" row="5" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>m_detailsLabel</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>m_previewLabel</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ <spacer row="3" column="0">
+ <property name="name">
+ <cstring>spacer3_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="Line" row="4" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="2" column="2">
+ <property name="name">
+ <cstring>m_openButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>m_alwaysUseCheckBox</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Always use this template</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Always use this template at application start up</string>
+ </property>
+ </widget>
+ <spacer row="3" column="4">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>m_titleLabel</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ <spacer row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="3" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer8_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+ <tabstop>m_documentList</tabstop>
+ <tabstop>m_openButton</tabstop>
+ <tabstop>m_alwaysUseCheckBox</tabstop>
+ <tabstop>m_detailsLabel</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>ktextbrowser.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/lib/kofficecore/koDocumentInfoAboutWidget.ui b/lib/kofficecore/koDocumentInfoAboutWidget.ui
new file mode 100644
index 00000000..29070e54
--- /dev/null
+++ b/lib/kofficecore/koDocumentInfoAboutWidget.ui
@@ -0,0 +1,352 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KoDocumentInfoAboutWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KoDocumentInfoAboutWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>448</width>
+ <height>557</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>pixmapLabel</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>56</width>
+ <height>56</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>leDocFile</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>leDocKeywords</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Subject:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Keywords:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout14</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Abstract:</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>31</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>leDocSubject</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>leDocTitle</cstring>
+ </property>
+ </widget>
+ <widget class="KTextEdit" row="3" column="1">
+ <property name="name">
+ <cstring>meDocAbstract</cstring>
+ </property>
+ <property name="tabChangesFocus">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Title:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line2</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="0" column="2" rowspan="4" colspan="2">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>90</width>
+ <height>70</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="4" column="2">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Modified:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="1">
+ <property name="name">
+ <cstring>labelLastPrinted</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>labelModified</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Created:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Last printed:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="1">
+ <property name="name">
+ <cstring>labelRevision</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Revision number:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="1">
+ <property name="name">
+ <cstring>labelEditing</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Total editing time:</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="4" column="3">
+ <property name="name">
+ <cstring>pbReset</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>24</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>24</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Reset</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>labelCreated</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>labelType</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Type:</string>
+ </property>
+ </widget>
+ <spacer row="5" column="3">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>leDocFile</tabstop>
+ <tabstop>leDocTitle</tabstop>
+ <tabstop>leDocSubject</tabstop>
+ <tabstop>leDocKeywords</tabstop>
+ <tabstop>meDocAbstract</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>ktextedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/lib/kofficecore/koDocumentInfoAuthorWidget.ui b/lib/kofficecore/koDocumentInfoAuthorWidget.ui
new file mode 100644
index 00000000..e442a1e8
--- /dev/null
+++ b/lib/kofficecore/koDocumentInfoAuthorWidget.ui
@@ -0,0 +1,311 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KoDocumentInfoAuthorWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KoDocumentInfoAuthorWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>363</width>
+ <height>492</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>labelAuthor</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>56</width>
+ <height>56</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>leFullName</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout16</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="9" column="0">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="text">
+ <string>Postal code:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Telephone (work):</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Email:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>leCompany</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="9" column="1">
+ <property name="name">
+ <cstring>lePostalCode</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="10" column="0">
+ <property name="name">
+ <cstring>textLabel11</cstring>
+ </property>
+ <property name="text">
+ <string>City:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>leAuthorPosition</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="8" column="1">
+ <property name="name">
+ <cstring>leStreet</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="7" column="1">
+ <property name="name">
+ <cstring>leFax</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>leTelephoneHome</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Initials:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="0">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>Fax:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>leEmail</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>leAuthorTitle</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Company:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="6" column="1">
+ <property name="name">
+ <cstring>leTelephoneWork</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>leInitial</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="11" column="0">
+ <property name="name">
+ <cstring>textLabel12</cstring>
+ </property>
+ <property name="text">
+ <string>Country:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Title:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Position:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="8" column="0">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="text">
+ <string>Street:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Telephone (home):</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="11" column="1">
+ <property name="name">
+ <cstring>leCountry</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="10" column="1">
+ <property name="name">
+ <cstring>leCity</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>pbLoadKABC</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Load From Address Book</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>pbDelete</cstring>
+ </property>
+ <property name="text">
+ <string>Delete Personal Data</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>leFullName</tabstop>
+ <tabstop>leInitial</tabstop>
+ <tabstop>leAuthorTitle</tabstop>
+ <tabstop>leAuthorPosition</tabstop>
+ <tabstop>leCompany</tabstop>
+ <tabstop>leEmail</tabstop>
+ <tabstop>leTelephoneHome</tabstop>
+ <tabstop>leTelephoneWork</tabstop>
+ <tabstop>leFax</tabstop>
+ <tabstop>leStreet</tabstop>
+ <tabstop>lePostalCode</tabstop>
+ <tabstop>leCity</tabstop>
+ <tabstop>leCountry</tabstop>
+ <tabstop>pbLoadKABC</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/lib/kofficecore/koDocumentInfoUserMetadataWidget.ui b/lib/kofficecore/koDocumentInfoUserMetadataWidget.ui
new file mode 100644
index 00000000..fd91dd5e
--- /dev/null
+++ b/lib/kofficecore/koDocumentInfoUserMetadataWidget.ui
@@ -0,0 +1,233 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KoDocumentInfoUserMetadataWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KoDocumentInfoUserMetadataWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>558</width>
+ <height>484</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KListView" row="0" column="0">
+ <property name="name">
+ <cstring>metaListView</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>kPushButton2</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>130</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Add...</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>kPushButton4</cstring>
+ </property>
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>101</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Type</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton" row="1" column="0">
+ <property name="name">
+ <cstring>rbNumber</cstring>
+ </property>
+ <property name="text">
+ <string>Number:</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="KDoubleSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>wNumber</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="0">
+ <property name="name">
+ <cstring>rString</cstring>
+ </property>
+ <property name="text">
+ <string>String:</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>wString</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="2" column="0">
+ <property name="name">
+ <cstring>rbBoolean</cstring>
+ </property>
+ <property name="text">
+ <string>Boolean:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="2" column="1">
+ <item>
+ <property name="text">
+ <string>true</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>false</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>wBoolean</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="3" column="0">
+ <property name="name">
+ <cstring>rbTime</cstring>
+ </property>
+ <property name="text">
+ <string>Time:</string>
+ </property>
+ </widget>
+ <widget class="KTimeWidget" row="3" column="1">
+ <property name="name">
+ <cstring>wTime</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="4" column="0">
+ <property name="name">
+ <cstring>rbDate</cstring>
+ </property>
+ <property name="text">
+ <string>Date:</string>
+ </property>
+ </widget>
+ <widget class="KDateTimeWidget" row="4" column="1">
+ <property name="name">
+ <cstring>wDate</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>rbNumber</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>wNumber</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>rbDate</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>wDate</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>rbTime</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>wTime</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>rbBoolean</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>wBoolean</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>rString</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>wString</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>ktimewidget.h</includehint>
+ <includehint>kdatetimewidget.h</includehint>
+ <includehint>kdatewidget.h</includehint>
+ <includehint>ktimewidget.h</includehint>
+</includehints>
+</UI>
diff --git a/lib/kofficecore/koOpenPaneBase.ui b/lib/kofficecore/koOpenPaneBase.ui
new file mode 100644
index 00000000..64a083a5
--- /dev/null
+++ b/lib/kofficecore/koOpenPaneBase.ui
@@ -0,0 +1,144 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KoOpenPaneBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KoOpenPaneBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>534</width>
+ <height>482</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSplitter">
+ <property name="name">
+ <cstring>m_splitter</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_sectionList</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>LastColumn</enum>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_openExistingButton</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_headerLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>No Header</string>
+ </property>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line2</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QWidgetStack">
+ <property name="name">
+ <cstring>m_widgetStack</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>WStackPage</cstring>
+ </property>
+ <attribute name="id">
+ <number>0</number>
+ </attribute>
+ </widget>
+ </widget>
+ </vbox>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/lib/kofficecore/kodocinfopropspage.desktop b/lib/kofficecore/kodocinfopropspage.desktop
new file mode 100644
index 00000000..8ef8c958
--- /dev/null
+++ b/lib/kofficecore/kodocinfopropspage.desktop
@@ -0,0 +1,68 @@
+[Desktop Entry]
+Type=Service
+Name=KOffice Document Info Properties Page
+Name[af]=Koffice Dokument Inligting Eienskappe Bladsy
+Name[ar]=صفحة الخصائص المعلوماتيّة الخاصة بمُستند KOffice
+Name[az]=KOffice Sənəd Xüsusiyyətləri Səhifəsi
+Name[bg]=Информация за документ на KOffice
+Name[br]=Pajenn Titouriñ Perzhioù Teul KOffice
+Name[bs]=Stranica sa osobinama KOffice dokumenta
+Name[ca]=Pàgina d'informació de propietats de documents KOffice
+Name[cs]=Stránka s informacemi o dokumentu KOffice
+Name[cy]=Tudalen Priodweddau Gwybodaeth Dogfen KWord
+Name[da]=Egenskabsside for KOffice-dokumentinformation
+Name[de]=KOffice Dokumentinfo-Eigenschaftenseite
+Name[el]=Σελίδα ιδιοτήτων των πληροφοριών εγγράφου του KOffice
+Name[eo]=KOficeja Dokumentinformo-eco-paĝo
+Name[es]=Página de propiedades de información de documentos de KOffice
+Name[et]=KOffice'i dokumendiinfo omaduste lehekülg
+Name[eu]=KOffice-en dokumentuen informazio-propietateen orria
+Name[fa]=صفحۀ ویژگیهای اطلاعات سند KOffice
+Name[fr]=Page des propriétés d'un document KOffice
+Name[fy]=KOffice dokumint ynfo eigenskippen side
+Name[gl]=Páxina de Información das Propiedades do Documento de KOffice
+Name[he]=דף מאפיינים לגבי מידע מסמך של KOffice
+Name[hi]=के-ऑफ़िस दस्तावेज़ जानकारी गुण पृष्ठ
+Name[hr]=Stranica s podacima o svojstvima KOffice dokumenta
+Name[hu]=KOffice dokumentumjellemzők tulajdonságlap
+Name[id]=Halaman Informasi Properties KOffice
+Name[is]=Eiginleikar KOffice skjals
+Name[it]=Pagina proprietà informazioni documento KOffice
+Name[ja]=KOffice ドキュメント情報プロパティページ
+Name[km]=ទំព័រ​លក្ខណៈសម្បត្តិ​ព័ត៌មាន​ឯកសារ KOffice
+Name[lo]=ຫນ້າຂໍ້ມູນຄຸນສົມບັດຂອງເອກະສານ ໂປຣແກມຊຸດສໍານັກງານ K
+Name[lt]=KOffice Dokumento informacijos savybių puslapis
+Name[lv]=KOffice dokumenta informācijas īpašību lapa
+Name[mk]=Информации и параметри на KOffice документ
+Name[ms]=Halaman Ciri Maklumat Dokumen KOffice
+Name[mt]=Paġna ta' Informazzjoni dwar Dokumenti KOffice
+Name[nb]=KOffice dokumentinformasjon
+Name[nds]=Dokmentinformatschonen-Egenschappensiet för KOffice
+Name[ne]=केडीई कार्यालय कागजात सूचना गुणहरूको पृष्ठ
+Name[nl]=KOffice Document Info Eigenschappen Pagina
+Name[nn]=KOffice Dokumentinformasjon
+Name[pl]=Strona właściwości dokumentu KOffice
+Name[pt]=Página de Informações sobre um Documento do KOffice
+Name[pt_BR]=Página de Propriedades de Informações do Documento do KOffice
+Name[ro]=Pagină proprietăţi document KOffice
+Name[ru]=Страница сведений о документе KOffice
+Name[se]=KOffice-dokumeantadieđut
+Name[sk]=Popis vlastností dokumentu KOffice
+Name[sl]=Stran z lastnostmi dokumenta za KOffice
+Name[sr]=KOffice-ова страна са информацијама о документу
+Name[sr@Latn]=KOffice-ova strana sa informacijama o dokumentu
+Name[sv]=Koffice-sida med dokumentinformation
+Name[tg]=KOffice Ҳуҷҷати Ахбороти Саҳифаи Ҳосиятҳо
+Name[th]=หน้าข้อมูลคุณสมบัติของเอกสารโปรแกรมชุดสำนักงาน K
+Name[tr]=KOffice Belge Özellikler Sayfası
+Name[uk]=Сторінка властивостей документу KOffice
+Name[uz]=KOffice hujjati haqida maʼlumot
+Name[uz@cyrillic]=KOffice ҳужжати ҳақида маълумот
+Name[ven]=Siatari la mafhungo a manwalwa a ofisi ya K
+Name[wa]=Pådje des prôpietés des informåcions do documint KOffice
+Name[xh]=Iphepha Lezinto zobumnini Zexwebhu lwe KOffice
+Name[zh_CN]=KOffice 文档基本信息和属性
+Name[zh_TW]=KOffice 文件資訊和屬性
+Name[zu]=KOffice iphepha ledokhumenti Iphepha Eliqukethe Ulwazi
+X-KDE-Library=kodocinfopropspage
+ServiceTypes=KPropsDlg/Plugin,application/x-kspread,application/x-kword,application/x-kpresenter,application/x-kchart
diff --git a/lib/kofficecore/koffice_export.h b/lib/kofficecore/koffice_export.h
new file mode 100644
index 00000000..37a9e8f7
--- /dev/null
+++ b/lib/kofficecore/koffice_export.h
@@ -0,0 +1,210 @@
+/*
+ This file is part of kofficecore
+ Copyright (c) 2005 KOffice Team
+
+ 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 _KOFFICE_EXPORT_H
+#define _KOFFICE_EXPORT_H
+
+#include <kdeversion.h>
+
+#ifdef Q_WS_WIN
+
+/* workaround for KDElibs < 3.2 on !win32 */
+#ifndef KDE_EXPORT
+# define KDE_EXPORT
+#endif
+
+#ifndef KOFFICECORE_EXPORT
+# ifdef MAKE_KOFFICECORE_LIB
+# define KOFFICECORE_EXPORT KDE_EXPORT
+# elif KDE_MAKE_LIB
+# define KOFFICECORE_EXPORT KDE_IMPORT
+# else
+# define KOFFICECORE_EXPORT
+# endif
+#endif
+
+#ifndef KOFFICEUI_EXPORT
+# ifdef MAKE_KOFFICEUI_LIB
+# define KOFFICEUI_EXPORT KDE_EXPORT
+# elif KDE_MAKE_LIB
+# define KOFFICEUI_EXPORT KDE_IMPORT
+# else
+# define KOFFICEUI_EXPORT
+# endif
+#endif
+
+#ifdef MAKE_KOSTORE_LIB
+# define KOSTORE_EXPORT KDE_EXPORT
+#elif KDE_MAKE_LIB
+# define KOSTORE_EXPORT KDE_IMPORT
+#else
+# define KOSTORE_EXPORT
+#endif
+
+#ifdef MAKE_KOPALETTE_LIB
+# define KOPALETTE_EXPORT KDE_EXPORT
+#elif KDE_MAKE_LIB
+# define KOPALETTE_EXPORT KDE_IMPORT
+#else
+# define KOPALETTE_EXPORT
+#endif
+
+#ifdef MAKE_KOWMF_LIB
+# define KOWMF_EXPORT KDE_EXPORT
+#elif KDE_MAKE_LIB
+# define KOWMF_EXPORT KDE_IMPORT
+#else
+# define KOWMF_EXPORT
+#endif
+
+#ifdef MAKE_KWMF_LIB
+# define KWMF_EXPORT KDE_EXPORT
+#elif KDE_MAKE_LIB
+# define KWMF_EXPORT KDE_IMPORT
+#else
+# define KWMF_EXPORT
+#endif
+
+#ifdef MAKE_KOTEXT_LIB
+# define KOTEXT_EXPORT KDE_EXPORT
+#elif KDE_MAKE_LIB
+# define KOTEXT_EXPORT KDE_IMPORT
+#else
+# define KOTEXT_EXPORT
+#endif
+
+#ifdef MAKE_KOFORMULA_LIB
+# define KOFORMULA_EXPORT KDE_EXPORT
+#elif KDE_MAKE_LIB
+# define KOFORMULA_EXPORT KDE_IMPORT
+#else
+# define KOFORMULA_EXPORT
+#endif
+
+#ifdef MAKE_KOPAINTER_LIB
+# define KOPAINTER_EXPORT KDE_EXPORT
+#elif KDE_MAKE_LIB
+# define KOPAINTER_EXPORT KDE_IMPORT
+#else
+# define KOPAINTER_EXPORT
+#endif
+
+#ifdef MAKE_KWORD_LIB
+# define KWORD_EXPORT KDE_EXPORT
+#elif KDE_MAKE_LIB
+# define KWORD_EXPORT KDE_IMPORT
+#else
+# define KWORD_EXPORT
+#endif
+
+#ifdef MAKE_KWMAILMERGE_LIB
+# define KWMAILMERGE_EXPORT KDE_EXPORT
+#elif KDE_MAKE_LIB
+# define KWMAILMERGE_EXPORT KDE_IMPORT
+#else
+# define KWMAILMERGE_EXPORT
+#endif
+
+#ifndef KOPROPERTY_EXPORT
+# ifdef MAKE_KOPROPERTY_LIB
+# define KOPROPERTY_EXPORT KDE_EXPORT
+# elif KDE_MAKE_LIB
+# define KOPROPERTY_EXPORT KDE_IMPORT
+# else
+# define KOPROPERTY_EXPORT
+# endif
+#endif
+
+#define KPRESENTER_EXPORT KDE_EXPORT
+#define KCHART_EXPORT KDE_EXPORT
+#define KDCHART_EXPORT KDE_EXPORT
+#define KARBONCOMMON_EXPORT KDE_EXPORT
+#define KARBONBASE_EXPORT KDE_EXPORT
+#define KARBONCOMMAND_EXPORT KDE_EXPORT
+#define KSPREAD_EXPORT KDE_EXPORT
+#define KOSHELL_EXPORT KDE_EXPORT
+#define KPLATO_EXPORT KDE_EXPORT
+#define KPLATOCHART_EXPORT KDE_EXPORT
+#define KUGAR_EXPORT KDE_EXPORT
+#define KUGARDESIGNER_EXPORT KDE_EXPORT
+#define KOFFICETOOLS_EXPORT KDE_EXPORT
+#define KOFFICEFILTER_EXPORT KDE_EXPORT
+#define KOCHARTINTERFACE_EXPORT KDE_EXPORT
+#define KIVIOPLUGINS_EXPORT KDE_EXPORT
+#define KIVIO_EXPORT KDE_EXPORT
+#define KRITA_EXPORT KDE_EXPORT
+#define KRITAUI_EXPORT KDE_EXPORT
+#define KRITACORE_EXPORT KDE_EXPORT
+#define KRITATOOL_EXPORT KDE_EXPORT
+#define KRITAPAINT_EXPORT KDE_EXPORT
+#define EXAMPLE_EXPORT KDE_EXPORT
+#else // not windows
+
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,3,90)
+#define KOFFICE_EXPORT KDE_EXPORT
+#else
+#define KOFFICE_EXPORT
+#endif
+
+/* kdemacros is OK, we can use gcc visibility macros */
+#define KOFFICECORE_EXPORT KOFFICE_EXPORT
+#define KOFFICEUI_EXPORT KOFFICE_EXPORT
+#define KOPALETTE_EXPORT KOFFICE_EXPORT
+#define KOTEXT_EXPORT KOFFICE_EXPORT
+#define KOFORMULA_EXPORT KOFFICE_EXPORT
+#define KOSTORE_EXPORT KOFFICE_EXPORT
+#define KOWMF_EXPORT KOFFICE_EXPORT
+#define KOSCRIPT_EXPORT KOFFICE_EXPORT
+#define KOPAINTER_EXPORT KOFFICE_EXPORT
+#define KSPREAD_EXPORT KOFFICE_EXPORT
+#define KFORMULA_EXPORT KOFFICE_EXPORT
+#define KWORD_EXPORT KOFFICE_EXPORT
+#define KWORD_MAILMERGE_EXPORT KOFFICE_EXPORT
+#define KPRESENTER_EXPORT KOFFICE_EXPORT
+#define KCHART_EXPORT KOFFICE_EXPORT
+#define KDCHART_EXPORT KOFFICE_EXPORT
+#define KARBONCOMMON_EXPORT KOFFICE_EXPORT
+#define KARBONBASE_EXPORT KOFFICE_EXPORT
+#define KARBONCOMMAND_EXPORT KOFFICE_EXPORT
+#define KOSHELL_EXPORT KOFFICE_EXPORT
+#define KPLATO_EXPORT KOFFICE_EXPORT
+#define KPLATOCHART_EXPORT KOFFICE_EXPORT
+#define KUGAR_EXPORT KOFFICE_EXPORT
+#define KUGARDESIGNER_EXPORT KOFFICE_EXPORT
+#define KOFFICETOOLS_EXPORT KOFFICE_EXPORT
+#define KOFFICEFILTER_EXPORT KOFFICE_EXPORT
+#define KOCHARTINTERFACE_EXPORT KOFFICE_EXPORT
+#define KIVIOPLUGINS_EXPORT KOFFICE_EXPORT
+#define KIVIO_EXPORT KOFFICE_EXPORT
+#define KRITA_EXPORT KOFFICE_EXPORT
+#define KRITAUI_EXPORT KOFFICE_EXPORT
+#define KRITACORE_EXPORT KOFFICE_EXPORT
+#define KRITATOOL_EXPORT KOFFICE_EXPORT
+#define KRITAPAINT_EXPORT KOFFICE_EXPORT
+#ifndef KOPROPERTY_EXPORT
+# define KOPROPERTY_EXPORT KOFFICE_EXPORT
+#endif
+#define EXAMPLE_EXPORT KOFFICE_EXPORT
+
+#endif /* not windows */
+
+#endif /* _KOFFICE_EXPORT_H */
diff --git a/lib/kofficecore/koffice_shell.rc b/lib/kofficecore/koffice_shell.rc
new file mode 100644
index 00000000..a619390b
--- /dev/null
+++ b/lib/kofficecore/koffice_shell.rc
@@ -0,0 +1,58 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="KOffice" version="17">
+<MenuBar>
+ <Menu name="file" noMerge="1"><text>&amp;File</text>
+ <Action name="file_new" />
+ <Action name="file_open" />
+ <Action name="file_open_recent" />
+ <Separator />
+ <Action name="file_save" />
+ <Action name="file_save_as" />
+ <Action name="file_reload_file" />
+<!-- <Action name="file_versions_file"/> -->
+ <Separator />
+ <Action name="file_import_file" />
+ <Action name="file_export_file" />
+ <Action name="file_send_file" />
+ <Separator />
+ <Merge />
+ <Separator />
+ <Action name="file_print" />
+ <Action name="file_print_preview" />
+ <Separator />
+ <Action name="file_documentinfo" />
+ <Separator />
+ <Action name="file_close" />
+ <Action name="file_quit" />
+ </Menu>
+ <Merge />
+ <Menu name="settings" noMerge="1"><text>&amp;Settings</text>
+ <DefineGroup name="settings_top"/>
+ <Merge/>
+<!-- <ActionList name="toolbarlist"/> -->
+ <Separator/>
+ <DefineGroup name="settings_show"/>
+ <Separator/>
+ <Action name="options_configure_keybinding"/>
+ <Action name="options_configure_toolbars"/>
+ <DefineGroup name="settings_configure"/>
+ </Menu>
+ <Menu name="help" noMerge="1"><text>&amp;Help</text>
+ <Merge />
+ <Action name="help_contents"/>
+ <Action name="help_whats_this"/>
+ <Separator/>
+ <Action name="help_report_bug"/>
+ <Separator/>
+ <Action name="help_about_app"/>
+ <Action name="help_about_kde"/>
+ </Menu>
+</MenuBar>
+<ToolBar name="mainToolBar" fullWidth="false" noMerge="1"><Text>File</Text>
+ <Action name="file_new" />
+ <Action name="file_open" />
+ <Action name="file_save" />
+ <Action name="file_print" />
+ <Action name="file_print_preview" />
+</ToolBar>
+</kpartgui>
diff --git a/lib/kofficecore/kofficeversion.cc b/lib/kofficecore/kofficeversion.cc
new file mode 100644
index 00000000..262664fc
--- /dev/null
+++ b/lib/kofficecore/kofficeversion.cc
@@ -0,0 +1,46 @@
+/* This file is part of the KOffice libraries
+ Copyright (c) 2003 David Faure <faure@kde.org>
+
+ 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.
+*/
+
+#include "kofficeversion.h"
+
+unsigned int KOffice::version()
+{
+ return KOFFICE_VERSION;
+}
+
+unsigned int KOffice::versionMajor()
+{
+ return KOFFICE_VERSION_MAJOR;
+}
+
+unsigned int KOffice::versionMinor()
+{
+ return KOFFICE_VERSION_MINOR;
+}
+
+unsigned int KOffice::versionRelease()
+{
+ return KOFFICE_VERSION_RELEASE;
+}
+
+const char *KOffice::versionString()
+{
+ return KOFFICE_VERSION_STRING;
+}
+
diff --git a/lib/kofficecore/kofficeversion.h b/lib/kofficecore/kofficeversion.h
new file mode 100644
index 00000000..89022330
--- /dev/null
+++ b/lib/kofficecore/kofficeversion.h
@@ -0,0 +1,86 @@
+/* This file is part of the KOffice libraries
+ Copyright (c) 2003 David Faure <faure@kde.org>
+ Copyright (c) 2003 Lukas Tinkl <lukas@kde.org>
+ Copyright (c) 2004 Nicolas Goutte <goutte@kde.org>
+
+ 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 _KOFFICE_VERSION_H_
+#define _KOFFICE_VERSION_H_
+
+// Remmber to synchronize the version number with the file(s):
+// - koffice/configure.in.in
+//
+// NOT synchronized are:
+// - karbon/karbon_aboutdata.h
+//
+
+#define KOFFICE_VERSION_STRING "1.6.3"
+#define KOFFICE_VERSION_MAJOR 1
+#define KOFFICE_VERSION_MINOR 6
+#define KOFFICE_VERSION_RELEASE 3
+#define KOFFICE_MAKE_VERSION( a,b,c ) (((a) << 16) | ((b) << 8) | (c))
+
+#define KOFFICE_VERSION \
+ KOFFICE_MAKE_VERSION(KOFFICE_VERSION_MAJOR,KOFFICE_VERSION_MINOR,KOFFICE_VERSION_RELEASE)
+
+#define KOFFICE_IS_VERSION(a,b,c) ( KOFFICE_VERSION >= KOFFICE_MAKE_VERSION(a,b,c) )
+
+/**
+ * Namespace for general KOFFICE functions.
+ */
+namespace KOffice
+{
+ /**
+ * Returns the encoded number of KOffice's version, see the KOFFICE_VERSION macro.
+ * In contrary to that macro this function returns the number of the actually
+ * installed KOffice version, not the number of the KOffice version that was
+ * installed when the program was compiled.
+ * @return the version number, encoded in a single uint
+ * @since 1.3
+ */
+ unsigned int version();
+ /**
+ * Returns the major number of KOffice's version, e.g.
+ * 1 for KOffice 1.2.3.
+ * @return the major version number
+ * @since 1.3
+ */
+ unsigned int versionMajor();
+ /**
+ * Returns the minor number of KOffice's version, e.g.
+ * 2 for KOffice 1.2.3.
+ * @return the minor version number
+ * @since 1.3
+ */
+ unsigned int versionMinor();
+ /**
+ * Returns the release of KOffice's version, e.g.
+ * 3 for KOffice 1.2.3.
+ * @return the release number
+ * @since 1.3
+ */
+ unsigned int versionRelease();
+ /**
+ * Returns the KOffice version as string, e.g. "1.2.3".
+ * @return the KOffice version. You can keep the string forever
+ * @since 1.3
+ */
+ const char *versionString();
+}
+
+#endif // _KOFFICE_VERSION_H_
diff --git a/lib/kofficecore/priorityqueue.h b/lib/kofficecore/priorityqueue.h
new file mode 100644
index 00000000..68019143
--- /dev/null
+++ b/lib/kofficecore/priorityqueue.h
@@ -0,0 +1,216 @@
+/* This file is part of the KOffice libraries
+ Copyright (C) 2001 Werner Trobin <trobin@kde.org>
+
+ 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 priority_queue_h
+#define priority_queue_h
+
+#include <vector>
+#include <qstring.h>
+#include <qasciidict.h>
+#include <kdebug.h>
+
+// Better put all those internal classes in some namespace to avoid clashes
+// as the names are quite generic
+namespace KOffice {
+
+ /**
+ * This PriorityQueue class is implemented as "upside down" heap, i.e. the item
+ * with the \b smallest key is at the root of the heap.
+ * If you feel like using that class your template parameter has to have a public
+ * method "unsigned int key() const" which returns - surprise - the key. The
+ * supplied key must not be "negative." We use UINT_MAX as value to represent
+ * "infinity" for the shortest path algorithm.
+ * As this is a very specialized class we also demand a "void setIndex(int i)"
+ * method in order to tell nodes where they are located inside the heap. This is
+ * a very ugly approach, but it's the only way I see if you want to avoid a O(n)
+ * search for the item where you decreased the key :}
+ * Just to make it even worse we also use a "int index() const" method
+ * to fetch the index... well, you most likely would need one anyway ;)
+ * Note: This class is pointer based (like QPtr*) - if you create PriorityQueue<X>
+ * we actually operate on X* ! We don't care about deleting your pointers at all.
+ * We don't copy them, we don't create new ones,... - you own them, you have
+ * to delete them :)
+ *
+ * In case you change a key value make sure to call keyDecreased and pass the
+ * item you touched. This is running in O(log n) according to Cormen...
+ *
+ * All the ideas are stol^H^H^H^Hborrowed from "Introduction to Algorithms",
+ * Cormen et al
+ *
+ * @author Werner Trobin <trobin@kde.org>
+ */
+ template<class T> class PriorityQueue {
+
+ public:
+ PriorityQueue() {}
+ PriorityQueue( const PriorityQueue<T>& rhs ) : m_vector( rhs.m_vector ) {}
+ PriorityQueue( const QAsciiDict<T>& items );
+ ~PriorityQueue() {}
+
+ PriorityQueue<T> &operator=( const PriorityQueue<T>& rhs ) { m_vector = rhs.m_vector; return *this; }
+ bool operator==( const PriorityQueue<T>& rhs ) { return m_vector == rhs.m_vector; }
+
+ unsigned int count() const { return m_vector.size(); }
+ bool isEmpty() const { return m_vector.empty(); }
+
+ void insert( T* item );
+
+ /**
+ * Call this method after decreasing the key of the ith item. The heap
+ * properties will no longer be valid if you either forget to call that
+ * method, or if you \b increase the key.
+ */
+ void keyDecreased( T* item );
+
+ T* extractMinimum();
+
+ /**
+ * For debugging
+ */
+ void dump() const;
+
+ private:
+ // Note: We have to use a 1-based index here, and we get/return 0-based ones
+ int parent( int i ) { return ( ( i + 1 ) >> 1 ) - 1; }
+ int left( int i ) { return ( ( i + 1 ) << 1 ) - 1; }
+ int right( int i ) { return ( i + 1 ) << 1; }
+
+ void heapify( int i );
+ // item is !=0 for sure
+ void bubbleUp( T* item, int i );
+ // Builds the heap in the vector in O(n)
+ void buildHeap();
+
+ std::vector<T*> m_vector;
+ };
+
+ template<class T>
+ PriorityQueue<T>::PriorityQueue( const QAsciiDict<T>& items ) : m_vector( items.count() )
+ {
+ // First put all items into the vector
+ QAsciiDictIterator<T> it( items );
+ for ( int i = 0; it.current(); ++it, ++i ) {
+ it.current()->setIndex( i );
+ m_vector[ i ] = it.current();
+ }
+ // Then build a heap in that vector
+ buildHeap();
+ }
+
+ template<class T>
+ void PriorityQueue<T>::insert( T* item )
+ {
+ if ( !item )
+ return;
+ int i = static_cast<int>( m_vector.size() );
+ m_vector.push_back( 0 ); // extend the vector by one item. i == index to the last item
+ bubbleUp( item, i );
+ }
+
+ template<class T>
+ void PriorityQueue<T>::keyDecreased( T* item )
+ {
+ if ( !item )
+ return;
+ bubbleUp( item, item->index() );
+ }
+
+ template<class T>
+ T* PriorityQueue<T>::extractMinimum()
+ {
+ if ( m_vector.size() < 1 )
+ return 0;
+ T *min = m_vector[ 0 ];
+ m_vector[ 0 ] = m_vector[ m_vector.size() - 1 ];
+ // update the index
+ m_vector[ 0 ]->setIndex( 0 );
+ m_vector.pop_back();
+ heapify( 0 );
+ return min;
+ }
+
+ template<class T>
+ void PriorityQueue<T>::dump() const
+ {
+ kdDebug( 30500 ) << "++++++++++ PriorityQueue::dump ++++++++++" << endl;
+ QString out;
+ int size = static_cast<int>( m_vector.size() );
+ for ( int i = 0; i < size; ++i ) {
+ if ( m_vector[ i ]->index() != i )
+ out += " ERROR: index out of sync. Should be " + QString::number( i ) + ", is " +
+ QString::number( m_vector[ i ]->index() ) + ". ";
+ out += QString::number( m_vector[ i ]->key() );
+ out += ", ";
+ }
+ if ( out.isEmpty() )
+ out = "(empty)";
+ kdDebug( 30500 ) << out << endl;
+ kdDebug( 30500 ) << "++++++++++ PriorityQueue::dump (done) ++++++++++" << endl;
+ }
+
+ template<class T>
+ void PriorityQueue<T>::heapify( int i )
+ {
+ int l = left( i ), r = right( i ), size = static_cast<int>( m_vector.size() );
+ int smallest;
+
+ if ( l < size && m_vector[ l ]->key() < m_vector[ i ]->key() )
+ smallest = l;
+ else
+ smallest = i;
+ if ( r < size && m_vector[ r ]->key() < m_vector[ smallest ]->key() )
+ smallest = r;
+
+ if ( smallest != i ) {
+ T* tmp = m_vector[ i ];
+ m_vector[ i ] = m_vector[ smallest ];
+ // update indices
+ m_vector[ i ]->setIndex( i );
+ tmp->setIndex( smallest );
+ m_vector[ smallest ] = tmp;
+ heapify( smallest );
+ }
+ }
+
+ template<class T>
+ void PriorityQueue<T>::bubbleUp( T* item, int i )
+ {
+ int p = parent( i );
+ while ( i > 0 && m_vector[ p ]->key() > item->key() ) {
+ // update the index first
+ m_vector[ p ]->setIndex( i );
+ // then move it there
+ m_vector[ i ] = m_vector[ p ];
+ i = p;
+ p = parent( i );
+ }
+ item->setIndex( i );
+ m_vector[ i ] = item;
+ }
+
+ template<class T>
+ void PriorityQueue<T>::buildHeap()
+ {
+ // from size() / 2 down to 1
+ for ( int i = ( m_vector.size() >> 1 ) - 1; i >= 0; --i )
+ heapify( i );
+ }
+
+} // namespace KOffice
+
+#endif // priority_queue_h
diff --git a/lib/kofficecore/tests/Makefile.am b/lib/kofficecore/tests/Makefile.am
new file mode 100644
index 00000000..3cc6827a
--- /dev/null
+++ b/lib/kofficecore/tests/Makefile.am
@@ -0,0 +1,40 @@
+####### General stuff
+
+INCLUDES= -I$(srcdir)/.. $(KSTORE_INCLUDES) $(all_includes)
+
+####### Files
+
+check_PROGRAMS = priorityqueue_test filterchain_test filter_graph kogenstylestest \
+ kodomtest kooasissettingstest kooasisstoretest kopointtest korecttest
+
+TESTS = kogenstylestest kodomtest kooasissettingstest kooasisstoretest kopointtest korecttest
+
+priorityqueue_test_SOURCES = priorityqueue_test.cpp
+priorityqueue_test_LDADD = ../libkofficecore.la
+
+filterchain_test_SOURCES = filterchain_test.cpp
+filterchain_test_LDADD = ../libkofficecore.la
+
+filter_graph_SOURCES = filter_graph.cpp
+filter_graph_LDADD = ../libkofficecore.la
+
+kogenstylestest_SOURCES = kogenstylestest.cpp
+kogenstylestest_LDADD = ../libkofficecore.la
+
+kodomtest_SOURCES = kodomtest.cpp
+kodomtest_LDADD = ../libkofficecore.la
+
+kooasissettingstest_SOURCES = kooasissettingstest.cpp
+kooasissettingstest_LDADD = ../libkofficecore.la
+
+kooasisstoretest_SOURCES = kooasisstoretest.cpp
+kooasisstoretest_LDADD = ../libkofficecore.la
+
+kopointtest_SOURCES = kopointtest.cpp
+kopointtest_LDADD = ../libkofficecore.la
+
+korecttest_SOURCES = korecttest.cpp
+korecttest_LDADD = ../libkofficecore.la
+
+dot:
+ dot -Tpng -o graph.png graph.dot
diff --git a/lib/kofficecore/tests/filter_graph.cpp b/lib/kofficecore/tests/filter_graph.cpp
new file mode 100644
index 00000000..22323355
--- /dev/null
+++ b/lib/kofficecore/tests/filter_graph.cpp
@@ -0,0 +1,115 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Werner Trobin <trobin@kde.org>
+
+ 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.
+*/
+
+#include <qfile.h>
+#include <KoQueryTrader.h>
+#include <KoFilterManager.h>
+#include <kinstance.h>
+#include <kdebug.h>
+
+int main( int /*argc*/, char ** /*argv*/ )
+{
+ KInstance instance( "filter_graph" ); // we need an instance when using the trader
+
+ QCString output = "digraph filters {\n";
+
+ // The following code is shamelessly copied over from KOffice::Graph::buildGraph
+ // It wasn't feasible to do some serious changes in the lib for that tiny bit
+ // of duplicated code in a test file.
+
+ QValueList<QString> vertices; // to keep track of already inserted values, not performance critical
+
+ // Make sure that all available parts are added to the graph
+ QValueList<KoDocumentEntry> parts( KoDocumentEntry::query() );
+ QValueList<KoDocumentEntry>::ConstIterator partIt( parts.begin() );
+ QValueList<KoDocumentEntry>::ConstIterator partEnd( parts.end() );
+
+ while ( partIt != partEnd ) {
+ //kdDebug() << ( *partIt ).service()->desktopEntryName() << endl;
+ QStringList nativeMimeTypes = ( *partIt ).service()->property( "X-KDE-ExtraNativeMimeTypes" ).toStringList();
+ nativeMimeTypes += ( *partIt ).service()->property( "X-KDE-NativeMimeType" ).toString();
+ QStringList::ConstIterator it = nativeMimeTypes.begin();
+ QStringList::ConstIterator end = nativeMimeTypes.end();
+ for ( ; it != end; ++it ) {
+ QString key = *it;
+ //kdDebug() << " " << key << endl;
+ if ( !key.isEmpty() ) {
+ output += " \"";
+ output += key.latin1();
+ output += "\" [shape=box, style=filled, fillcolor=lightblue];\n";
+ if ( vertices.find( key ) == vertices.end() )
+ vertices.append( key );
+ }
+ }
+ ++partIt;
+ }
+
+ QValueList<KoFilterEntry::Ptr> filters( KoFilterEntry::query() ); // no constraint here - we want *all* :)
+ QValueList<KoFilterEntry::Ptr>::ConstIterator it = filters.begin();
+ QValueList<KoFilterEntry::Ptr>::ConstIterator end = filters.end();
+
+ for ( ; it != end; ++it ) {
+ kdDebug() << "import " << ( *it )->import << " export " << ( *it )->export_ << endl;
+ // First add the "starting points"
+ QStringList::ConstIterator importIt = ( *it )->import.begin();
+ QStringList::ConstIterator importEnd = ( *it )->import.end();
+ for ( ; importIt != importEnd; ++importIt ) {
+ // already there?
+ if ( vertices.find( *importIt ) == vertices.end() ) {
+ vertices.append( *importIt );
+ output += " \"";
+ output += ( *importIt ).latin1();
+ output += "\";\n";
+ }
+ }
+
+ QStringList::ConstIterator exportIt = ( *it )->export_.begin();
+ QStringList::ConstIterator exportEnd = ( *it )->export_.end();
+
+ for ( ; exportIt != exportEnd; ++exportIt ) {
+ // First make sure the export vertex is in place
+ if ( vertices.find( *exportIt ) == vertices.end() ) {
+ output += " \"";
+ output += ( *exportIt ).latin1();
+ output += "\";\n";
+ vertices.append( *exportIt );
+ }
+ // Then create the appropriate edges
+ importIt = ( *it )->import.begin();
+ for ( ; importIt != importEnd; ++importIt ) {
+ output += " \"";
+ output += ( *importIt ).latin1();
+ output += "\" -> \"";
+ output += ( *exportIt ).latin1();
+ if ( KoFilterManager::filterAvailable( *it ) )
+ output += "\";\n";
+ else
+ output += "\" [style=dotted];\n";
+ }
+ }
+ }
+
+ output += "}\n";
+
+ QFile f( "graph.dot" );
+ if ( f.open( IO_WriteOnly ) )
+ f.writeBlock( output.data(), output.size() - 1 );
+ f.close();
+ return 0;
+}
diff --git a/lib/kofficecore/tests/filterchain_test.cpp b/lib/kofficecore/tests/filterchain_test.cpp
new file mode 100644
index 00000000..23f620a1
--- /dev/null
+++ b/lib/kofficecore/tests/filterchain_test.cpp
@@ -0,0 +1,107 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Werner Trobin <trobin@kde.org>
+
+ 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.
+*/
+
+#include <KoFilterChain.h>
+#include <KoFilterManager.h>
+#include <kinstance.h>
+#include <kdebug.h>
+
+int main( int /*argc*/, char ** /*argv*/ )
+{
+ KInstance instance( "filterchain_test" ); // we need an instance when using the trader
+ KOffice::Graph g( "application/x-kspread" );
+ g.dump();
+ g.setSourceMimeType( "application/x-kword" );
+ g.dump();
+
+ KoFilterManager *manager = new KoFilterManager( 0 );
+ kdDebug() << "Trying to build some filter chains..." << endl;
+ QCString mimeType( "foo/bar" );
+ KoFilterChain::Ptr chain = g.chain( manager, mimeType );
+ if ( !chain )
+ kdDebug() << "Chain for 'foo/bar' is not available, OK" << endl;
+ else {
+ kdError() << "Chain for 'foo/bar' is available!" << endl;
+ chain->dump();
+ }
+
+ mimeType = "application/x-krita";
+ chain = g.chain( manager, mimeType );
+ if ( !chain )
+ kdDebug() << "Chain for 'application/x-krita' is not available, OK" << endl;
+ else {
+ kdError() << "Chain 'application/x-krita' is available!" << endl;
+ chain->dump();
+ }
+
+ mimeType = "text/x-csv";
+ chain = g.chain( manager, mimeType );
+ if ( !chain )
+ kdError() << "Chain for 'text/x-csv' is not available!" << endl;
+ else {
+ kdDebug() << "Chain for 'text/x-csv' is available, OK" << endl;
+ chain->dump();
+ }
+
+ // Try to find the closest KOffice part
+ mimeType = "";
+ chain = g.chain( manager, mimeType );
+ if ( !chain )
+ kdDebug() << "It was already a KOffice part, OK" << endl;
+ else
+ kdError() << "We really got a chain? ugh :}" << endl;
+
+ g.setSourceMimeType( "text/x-csv" );
+ mimeType = "";
+ chain = g.chain( manager, mimeType );
+ if ( !chain )
+ kdError() << "Hmm... why didn't we find a chain?" << endl;
+ else {
+ kdDebug() << "Chain for 'text/x-csv' -> closest part is available ("
+ << mimeType << "), OK" << endl;
+ chain->dump();
+ }
+
+ kdDebug() << "Checking mimeFilter() for Import:" << endl;
+ QStringList list = KoFilterManager::mimeFilter( "application/x-kword", KoFilterManager::Import );
+ QStringList::ConstIterator it = list.begin();
+ QStringList::ConstIterator end = list.end();
+ for ( ; it != end; ++it )
+ kdDebug() << " " << *it << endl;
+ kdDebug() << " " << list.count() << " entries." << endl;
+
+ kdDebug() << "Checking mimeFilter() for Export:" << endl;
+ list = KoFilterManager::mimeFilter( "application/x-kword", KoFilterManager::Export );
+ it = list.begin();
+ end = list.end();
+ for ( ; it != end; ++it )
+ kdDebug() << " " << *it << endl;
+ kdDebug() << " " << list.count() << " entries." << endl;
+
+ kdDebug() << "Checking KoShell's mimeFilter():" << endl;
+ list = KoFilterManager::mimeFilter();
+ it = list.begin();
+ end = list.end();
+ for ( ; it != end; ++it )
+ kdDebug() << " " << *it << endl;
+ kdDebug() << " " << list.count() << " entries." << endl;
+
+ delete manager;
+ return 0;
+}
diff --git a/lib/kofficecore/tests/kodomtest.cpp b/lib/kofficecore/tests/kodomtest.cpp
new file mode 100644
index 00000000..c8e3c539
--- /dev/null
+++ b/lib/kofficecore/tests/kodomtest.cpp
@@ -0,0 +1,129 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 David Faure <faure@kde.org>
+
+ 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.
+*/
+
+#include "KoDom.h"
+#include "KoXmlNS.h"
+
+#include <qapplication.h>
+#include <assert.h>
+
+//static void debugElemNS( const QDomElement& elem )
+//{
+// qDebug( "nodeName=%s tagName=%s localName=%s prefix=%s namespaceURI=%s", elem.nodeName().latin1(), elem.tagName().latin1(), elem.localName().latin1(), elem.prefix().latin1(), elem.namespaceURI().latin1() );
+//}
+
+void testQDom( const QDomDocument& doc )
+{
+ QDomElement docElem = doc.documentElement();
+ //debugElemNS( docElem );
+ assert( docElem.nodeName() == "o:document-content" );
+ assert( docElem.tagName() == "document-content" );
+ assert( docElem.localName() == "document-content" );
+ assert( docElem.prefix() == "o" );
+ assert( docElem.namespaceURI() == KoXmlNS::office );
+
+ // WARNING, elementsByTagNameNS is recursive!!!
+ QDomNodeList docElemChildren = docElem.elementsByTagNameNS( KoXmlNS::office, "body" );
+ assert( docElemChildren.length() == 1 );
+
+ QDomElement elem = docElemChildren.item( 0 ).toElement();
+ //debugElemNS( elem );
+ assert( elem.tagName() == "body" );
+ assert( elem.localName() == "body" );
+ assert( elem.prefix() == "o" );
+ assert( elem.namespaceURI() == KoXmlNS::office );
+
+ QDomNode n = elem.firstChild();
+ for ( ; !n.isNull() ; n = n.nextSibling() ) {
+ if ( !n.isElement() )
+ continue;
+ QDomElement e = n.toElement();
+ //debugElemNS( e );
+ assert( e.tagName() == "p" );
+ assert( e.localName() == "p" );
+ assert( e.prefix().isEmpty() );
+ assert( e.namespaceURI() == KoXmlNS::text );
+ }
+
+ qDebug("testQDom... ok");
+}
+
+void testKoDom( const QDomDocument& doc )
+{
+ QDomElement docElem = KoDom::namedItemNS( doc, KoXmlNS::office, "document-content" );
+ assert( !docElem.isNull() );
+ assert( docElem.localName() == "document-content" );
+ assert( docElem.namespaceURI() == KoXmlNS::office );
+
+ QDomElement body = KoDom::namedItemNS( docElem, KoXmlNS::office, "body" );
+ assert( !body.isNull() );
+ assert( body.localName() == "body" );
+ assert( body.namespaceURI() == KoXmlNS::office );
+
+ QDomElement p = KoDom::namedItemNS( body, KoXmlNS::text, "p" );
+ assert( !p.isNull() );
+ assert( p.localName() == "p" );
+ assert( p.namespaceURI() == KoXmlNS::text );
+
+ const QDomElement officeStyle = KoDom::namedItemNS( docElem, KoXmlNS::office, "styles" );
+ assert( !officeStyle.isNull() );
+
+ // Look for a non-existing element
+ QDomElement notexist = KoDom::namedItemNS( body, KoXmlNS::text, "notexist" );
+ assert( notexist.isNull() );
+
+ int count = 0;
+ QDomElement elem;
+ forEachElement( elem, body ) {
+ assert( elem.localName() == "p" );
+ assert( elem.namespaceURI() == KoXmlNS::text );
+ ++count;
+ }
+ assert( count == 2 );
+
+ // Attributes
+ // ### Qt bug: it doesn't work if using style-name instead of text:style-name in the XML
+ const QString styleName = p.attributeNS( KoXmlNS::text, "style-name", QString::null );
+ qDebug( "%s", styleName.latin1() );
+ assert( styleName == "L1" );
+
+ qDebug("testKoDom... ok");
+}
+
+int main( int argc, char** argv ) {
+ QApplication app( argc, argv, QApplication::Tty );
+
+ const QCString xml = QCString( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<o:document-content xmlns:o=\"" )
+ + KoXmlNS::office
+ + "\" xmlns=\"" + KoXmlNS::text
+ + "\" xmlns:text=\"" + KoXmlNS::text
+ + "\">\n"
+ "<o:body><p text:style-name=\"L1\">foobar</p><p>2nd</p></o:body><o:styles></o:styles>\n"
+ "</o:document-content>\n";
+ QDomDocument doc;
+ //QXmlSimpleReader reader;
+ //reader.setFeature( "http://xml.org/sax/features/namespaces", TRUE );
+ //reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", FALSE );
+ bool ok = doc.setContent( xml, true /* namespace processing */ );
+ assert( ok );
+
+ testQDom(doc);
+ testKoDom(doc);
+}
diff --git a/lib/kofficecore/tests/kogenstylestest.cpp b/lib/kofficecore/tests/kogenstylestest.cpp
new file mode 100644
index 00000000..ddfac1f6
--- /dev/null
+++ b/lib/kofficecore/tests/kogenstylestest.cpp
@@ -0,0 +1,270 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2006 David Faure <faure@kde.org>
+
+ 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 <KoGenStyles.h>
+#include <KoXmlWriter.h>
+#include "../../store/tests/xmlwritertest.h"
+#include <kdebug.h>
+#include <assert.h>
+
+int testLookup()
+{
+ kdDebug() << k_funcinfo << endl;
+ KoGenStyles coll;
+
+ QMap<QString, QString> map1;
+ map1.insert( "map1key", "map1value" );
+ QMap<QString, QString> map2;
+ map2.insert( "map2key1", "map2value1" );
+ map2.insert( "map2key2", "map2value2" );
+
+ KoGenStyle first( KoGenStyle::STYLE_AUTO, "paragraph" );
+ first.addAttribute( "style:master-page-name", "Standard" );
+ first.addProperty( "style:page-number", "0" );
+ first.addProperty( "style:foobar", "2", KoGenStyle::TextType );
+ first.addStyleMap( map1 );
+ first.addStyleMap( map2 );
+
+ QString firstName = coll.lookup( first );
+ kdDebug() << "The first style got assigned the name " << firstName << endl;
+ assert( firstName == "A1" ); // it's fine if it's something else, but the koxmlwriter tests require a known name
+ assert( first.type() == KoGenStyle::STYLE_AUTO );
+
+ KoGenStyle second( KoGenStyle::STYLE_AUTO, "paragraph" );
+ second.addAttribute( "style:master-page-name", "Standard" );
+ second.addProperty( "style:page-number", "0" );
+ second.addProperty( "style:foobar", "2", KoGenStyle::TextType );
+ second.addStyleMap( map1 );
+ second.addStyleMap( map2 );
+
+ QString secondName = coll.lookup( second );
+ kdDebug() << "The second style got assigned the name " << secondName << endl;
+
+ assert( firstName == secondName ); // check that sharing works
+ assert( first == second ); // check that operator== works :)
+
+ const KoGenStyle* s = coll.style( firstName ); // check lookup of existing style
+ assert( s );
+ assert( *s == first );
+ s = coll.style( "foobarblah" ); // check lookup of non-existing style
+ assert( !s );
+
+ KoGenStyle third( KoGenStyle::STYLE_AUTO, "paragraph", secondName ); // inherited style
+ third.addProperty( "style:margin-left", "1.249cm" );
+ third.addProperty( "style:page-number", "0" ); // same as parent
+ third.addProperty( "style:foobar", "3", KoGenStyle::TextType ); // different from parent
+ assert( third.parentName() == secondName );
+
+ QString thirdName = coll.lookup( third, "P" );
+ kdDebug() << "The third style got assigned the name " << thirdName << endl;
+ assert( thirdName == "P1" );
+
+ KoGenStyle user( KoGenStyle::STYLE_USER, "paragraph" ); // differs from third since it doesn't inherit second, and has a different type
+ user.addProperty( "style:margin-left", "1.249cm" );
+
+ QString userStyleName = coll.lookup( user, "User", KoGenStyles::DontForceNumbering );
+ kdDebug() << "The user style got assigned the name " << userStyleName << endl;
+ assert( userStyleName == "User" );
+
+ KoGenStyle sameAsParent( KoGenStyle::STYLE_AUTO, "paragraph", secondName ); // inherited style
+ sameAsParent.addAttribute( "style:master-page-name", "Standard" );
+ sameAsParent.addProperty( "style:page-number", "0" );
+ sameAsParent.addProperty( "style:foobar", "2", KoGenStyle::TextType );
+ sameAsParent.addStyleMap( map1 );
+ sameAsParent.addStyleMap( map2 );
+ QString sapName = coll.lookup( sameAsParent, "foobar" );
+ kdDebug() << "The 'same as parent' style got assigned the name " << sapName << endl;
+
+ assert( sapName == secondName );
+ assert( coll.styles().count() == 3 );
+
+ // OK, now add a style marked as for styles.xml; it looks like the above style, but
+ // since it's marked for styles.xml it shouldn't be shared with it.
+ KoGenStyle headerStyle( KoGenStyle::STYLE_AUTO, "paragraph" );
+ headerStyle.addAttribute( "style:master-page-name", "Standard" );
+ headerStyle.addProperty( "style:page-number", "0" );
+ headerStyle.addProperty( "style:foobar", "2", KoGenStyle::TextType );
+ headerStyle.addStyleMap( map1 );
+ headerStyle.addStyleMap( map2 );
+ headerStyle.setAutoStyleInStylesDotXml( true );
+ QString headerStyleName = coll.lookup( headerStyle, "foobar" );
+
+ assert( coll.styles().count() == 4 );
+ assert( coll.styles( KoGenStyle::STYLE_AUTO ).count() == 2 );
+ assert( coll.styles( KoGenStyle::STYLE_USER ).count() == 1 );
+
+ QValueList<KoGenStyles::NamedStyle> stylesXmlStyles = coll.styles( KoGenStyle::STYLE_AUTO, true );
+ assert( stylesXmlStyles.count() == 1 );
+ KoGenStyles::NamedStyle firstStyle = stylesXmlStyles.first();
+ assert( firstStyle.name == headerStyleName );
+
+ TEST_BEGIN( 0, 0 );
+ first.writeStyle( &writer, coll, "style:style", firstName, "style:paragraph-properties" );
+ TEST_END( "XML for first/second style", "<r>\n <style:style style:name=\"A1\" style:family=\"paragraph\" style:master-page-name=\"Standard\">\n <style:paragraph-properties style:page-number=\"0\"/>\n <style:text-properties style:foobar=\"2\"/>\n <style:map map1key=\"map1value\"/>\n <style:map map2key1=\"map2value1\" map2key2=\"map2value2\"/>\n </style:style>\n</r>\n" );
+
+ TEST_BEGIN( 0, 0 );
+ third.writeStyle( &writer, coll, "style:style", thirdName, "style:paragraph-properties" );
+ TEST_END( "XML for third style", "<r>\n <style:style style:name=\"P1\" style:parent-style-name=\"A1\" style:family=\"paragraph\">\n <style:paragraph-properties style:margin-left=\"1.249cm\"/>\n <style:text-properties style:foobar=\"3\"/>\n </style:style>\n</r>\n" );
+
+ coll.markStyleForStylesXml( firstName );
+ {
+ QValueList<KoGenStyles::NamedStyle> stylesXmlStyles = coll.styles( KoGenStyle::STYLE_AUTO, true );
+ assert( stylesXmlStyles.count() == 2 );
+ QValueList<KoGenStyles::NamedStyle> contentXmlStyles = coll.styles( KoGenStyle::STYLE_AUTO, false );
+ assert( contentXmlStyles.count() == 1 );
+ }
+
+ return 0;
+}
+
+int testDefaultStyle()
+{
+ kdDebug() << k_funcinfo << endl;
+ /* Create a default style,
+ * and then an auto style with exactly the same attributes
+ * -> the lookup gives the default style.
+ *
+ * Also checks how the default style gets written out to XML.
+ */
+ KoGenStyles coll;
+
+ KoGenStyle defaultStyle( KoGenStyle::STYLE_AUTO, "paragraph" );
+ defaultStyle.addAttribute( "style:master-page-name", "Standard" );
+ defaultStyle.addProperty( "myfont", "isBold" );
+ defaultStyle.setDefaultStyle( true );
+ QString defaultStyleName = coll.lookup( defaultStyle );
+ assert( !defaultStyleName.isEmpty() ); // whatever, but not empty
+ assert( defaultStyle.type() == KoGenStyle::STYLE_AUTO );
+ assert( defaultStyle.isDefaultStyle() );
+
+ KoGenStyle anotherStyle( KoGenStyle::STYLE_AUTO, "paragraph" );
+ anotherStyle.addAttribute( "style:master-page-name", "Standard" );
+ anotherStyle.addProperty( "myfont", "isBold" );
+ QString anotherStyleName = coll.lookup( anotherStyle );
+ assert( anotherStyleName == defaultStyleName );
+
+ assert( coll.styles().count() == 1 );
+
+ TEST_BEGIN( 0, 0 );
+ defaultStyle.writeStyle( &writer, coll, "style:default-style", defaultStyleName, "style:paragraph-properties" );
+ TEST_END( "XML for default style", "<r>\n <style:default-style style:family=\"paragraph\" style:master-page-name=\"Standard\">\n <style:paragraph-properties myfont=\"isBold\"/>\n </style:default-style>\n</r>\n" );
+
+ // The kspread case: not writing out all properties, only if they differ
+ // from the default style.
+ // KoGenStyles doesn't fetch info from the parent style when testing
+ // for equality, so KSpread uses isEmpty() to check for equality-to-parent.
+ KoGenStyle dataStyle( KoGenStyle::STYLE_AUTO, "paragraph", defaultStyleName );
+ assert( dataStyle.isEmpty() );
+ // and then it doesn't look up the auto style, but rather uses the parent style directly.
+
+ return 0;
+}
+
+int testUserStyles()
+{
+ kdDebug() << k_funcinfo << endl;
+ /* Two user styles with exactly the same attributes+properties will not get merged, since
+ * they don't have exactly the same attributes after all: the display-name obviously differs :)
+ */
+ KoGenStyles coll;
+
+ KoGenStyle user1( KoGenStyle::STYLE_USER, "paragraph" );
+ user1.addAttribute( "style:display-name", "User 1" );
+ user1.addProperty( "myfont", "isBold" );
+
+ QString user1StyleName = coll.lookup( user1, "User1", KoGenStyles::DontForceNumbering );
+ kdDebug() << "The user style got assigned the name " << user1StyleName << endl;
+ assert( user1StyleName == "User1" );
+
+ KoGenStyle user2( KoGenStyle::STYLE_USER, "paragraph" );
+ user2.addAttribute( "style:display-name", "User 2" );
+ user2.addProperty( "myfont", "isBold" );
+
+ QString user2StyleName = coll.lookup( user2, "User2", KoGenStyles::DontForceNumbering );
+ kdDebug() << "The user style got assigned the name " << user2StyleName << endl;
+ assert( user2StyleName == "User2" );
+
+ // And now, what if the data uses that style?
+ // This is like sameAsParent in the other test, but this time the
+ // parent is a STYLE_USER...
+ KoGenStyle dataStyle( KoGenStyle::STYLE_AUTO, "paragraph", user2StyleName );
+ dataStyle.addProperty( "myfont", "isBold" );
+
+ QString dataStyleName = coll.lookup( dataStyle, "DataStyle" );
+ kdDebug() << "The auto style got assigned the name " << dataStyleName << endl;
+ assert( dataStyleName == "User2" ); // it found the parent as equal
+
+ // Let's do the opposite test, just to make sure
+ KoGenStyle dataStyle2( KoGenStyle::STYLE_AUTO, "paragraph", user2StyleName );
+ dataStyle2.addProperty( "myfont", "isNotBold" );
+
+ QString dataStyle2Name = coll.lookup( dataStyle2, "DataStyle" );
+ kdDebug() << "The different auto style got assigned the name " << dataStyle2Name << endl;
+ assert( dataStyle2Name == "DataStyle1" );
+
+ assert( coll.styles().count() == 3 );
+
+ TEST_BEGIN( 0, 0 );
+ user1.writeStyle( &writer, coll, "style:style", user1StyleName, "style:paragraph-properties" );
+ TEST_END( "XML for user style 1", "<r>\n <style:style style:name=\"User1\" style:display-name=\"User 1\" style:family=\"paragraph\">\n <style:paragraph-properties myfont=\"isBold\"/>\n </style:style>\n</r>\n" );
+
+ TEST_BEGIN( 0, 0 );
+ user2.writeStyle( &writer, coll, "style:style", user2StyleName, "style:paragraph-properties" );
+ TEST_END( "XML for user style 2", "<r>\n <style:style style:name=\"User2\" style:display-name=\"User 2\" style:family=\"paragraph\">\n <style:paragraph-properties myfont=\"isBold\"/>\n </style:style>\n</r>\n" );
+
+ return 0;
+}
+
+int testStylesDotXml()
+{
+ kdDebug() << k_funcinfo << endl;
+ KoGenStyles coll;
+
+ // Check that an autostyle-in-style.xml and an autostyle-in-content.xml
+ // don't get the same name. It confuses KoGenStyle's named-based maps.
+ KoGenStyle headerStyle( KoGenStyle::STYLE_AUTO, "paragraph" );
+ headerStyle.addAttribute( "style:master-page-name", "Standard" );
+ headerStyle.addProperty( "style:page-number", "0" );
+ headerStyle.setAutoStyleInStylesDotXml( true );
+ QString headerStyleName = coll.lookup( headerStyle, "P" );
+ assert( headerStyleName == "P1" );
+
+ //coll.dump();
+
+ KoGenStyle first( KoGenStyle::STYLE_AUTO, "paragraph" );
+ first.addAttribute( "style:master-page-name", "Standard" );
+ QString firstName = coll.lookup( first, "P" );
+ kdDebug() << "The auto style got assigned the name " << firstName << endl;
+ assert( firstName == "P2" ); // anything but not P1.
+ return 0;
+}
+
+int main( int, char** ) {
+ fprintf( stderr, "OK\n" );
+
+ if ( testLookup() )
+ return 1;
+ if ( testDefaultStyle() )
+ return 1;
+ if ( testUserStyles() )
+ return 1;
+ if ( testStylesDotXml() )
+ return 1;
+
+ return 0;
+}
diff --git a/lib/kofficecore/tests/kooasissettingstest.cpp b/lib/kofficecore/tests/kooasissettingstest.cpp
new file mode 100644
index 00000000..ef29536d
--- /dev/null
+++ b/lib/kofficecore/tests/kooasissettingstest.cpp
@@ -0,0 +1,106 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 David Faure <faure@kde.org>
+
+ 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 <KoOasisSettings.h>
+#include <KoDom.h>
+#include <kdebug.h>
+#include <assert.h>
+
+void testSelectItemSet( KoOasisSettings& settings )
+{
+ KoOasisSettings::Items items = settings.itemSet( "notexist" );
+ assert( items.isNull() );
+ items = settings.itemSet( "view-settings" );
+ assert( !items.isNull() );
+ kdDebug() << "testSelectItemSet OK" << endl;
+}
+
+void testParseConfigItemString( KoOasisSettings& settings )
+{
+ KoOasisSettings::Items viewSettings = settings.itemSet( "view-settings" );
+ const QString unit = viewSettings.parseConfigItemString( "unit" );
+ qDebug( "%s", unit.latin1() );
+ assert( unit == "mm" );
+ kdDebug() << "testParseConfigItemString OK" << endl;
+}
+
+void testIndexedMap( KoOasisSettings& settings )
+{
+ KoOasisSettings::Items viewSettings = settings.itemSet( "view-settings" );
+ assert( !viewSettings.isNull() );
+ KoOasisSettings::IndexedMap viewMap = viewSettings.indexedMap( "Views" );
+ assert( !viewMap.isNull() );
+ KoOasisSettings::Items firstView = viewMap.entry( 0 );
+ assert( !firstView.isNull() );
+ const short zoomFactor = firstView.parseConfigItemShort( "ZoomFactor" );
+ assert( zoomFactor == 100 );
+ KoOasisSettings::Items secondView = viewMap.entry( 1 );
+ assert( secondView.isNull() );
+ kdDebug() << "testIndexedMap OK" << endl;
+}
+
+void testNamedMap( KoOasisSettings& settings )
+{
+ KoOasisSettings::Items viewSettings = settings.itemSet( "view-settings" );
+ assert( !viewSettings.isNull() );
+ KoOasisSettings::NamedMap viewMap = viewSettings.namedMap( "NamedMap" );
+ assert( !viewMap.isNull() );
+ KoOasisSettings::Items foo = viewMap.entry( "foo" );
+ assert( !foo.isNull() );
+ const int zoomFactor = foo.parseConfigItemShort( "ZoomFactor" );
+ assert( zoomFactor == 100 );
+ KoOasisSettings::Items secondView = viewMap.entry( "foobar" );
+ assert( secondView.isNull() );
+ kdDebug() << "testNamedMap OK" << endl;
+}
+
+int main( int, char** ) {
+
+ const QCString xml = "\
+<?xml version=\"1.0\" encoding=\"UTF-8\"?> \
+<office:document-settings xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" xmlns:config=\"urn:oasis:names:tc:opendocument:xmlns:config:1.0\"> \
+ <office:settings> \
+ <config:config-item-set config:name=\"view-settings\"> \
+ <config:config-item config:name=\"unit\" config:type=\"string\">mm</config:config-item> \
+ <config:config-item-map-indexed config:name=\"Views\"> \
+ <config:config-item-map-entry> \
+ <config:config-item config:name=\"ZoomFactor\" config:type=\"short\">100</config:config-item> \
+ </config:config-item-map-entry> \
+ </config:config-item-map-indexed> \
+ <config:config-item-map-named config:name=\"NamedMap\"> \
+ <config:config-item-map-entry config:name=\"foo\"> \
+ <config:config-item config:name=\"ZoomFactor\" config:type=\"int\">100</config:config-item> \
+ </config:config-item-map-entry> \
+ </config:config-item-map-named> \
+ </config:config-item-set> \
+ </office:settings> \
+</office:document-settings> \
+";
+
+ QDomDocument doc;
+ bool ok = doc.setContent( xml, true /* namespace processing */ );
+ assert( ok );
+
+ KoOasisSettings settings( doc );
+
+ testSelectItemSet( settings );
+ testParseConfigItemString( settings );
+ testIndexedMap( settings );
+ testNamedMap( settings );
+ return 0;
+}
diff --git a/lib/kofficecore/tests/kooasisstoretest.cpp b/lib/kofficecore/tests/kooasisstoretest.cpp
new file mode 100644
index 00000000..6750904b
--- /dev/null
+++ b/lib/kofficecore/tests/kooasisstoretest.cpp
@@ -0,0 +1,58 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 David Faure <faure@kde.org>
+
+ 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 <KoOasisStore.h>
+#include <KoDom.h>
+#include <kdebug.h>
+#include <assert.h>
+
+void testMimeForPath( QDomDocument& doc )
+{
+ QString mime = KoOasisStore::mimeForPath( doc, "Object 1" );
+ kdDebug() << k_funcinfo << mime << endl;
+ assert( !mime.isNull() );
+ assert( !mime.isEmpty() );
+ assert( mime == "application/vnd.oasis.opendocument.text" );
+ kdDebug() << "testMimeForPath OK" << endl;
+}
+
+int main( int, char** ) {
+
+ const QCString xml = "\
+<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
+<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n\
+ <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.text\" manifest:full-path=\"/\"/>\n\
+ <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n\
+ <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.text\" manifest:full-path=\"Object 1\"/>\n\
+</manifest:manifest> \
+";
+
+ QDomDocument doc;
+ QString errorMsg;
+ int errorLine, errorColumn;
+ bool ok = doc.setContent( xml, true /* namespace processing */, &errorMsg, &errorLine, &errorColumn );
+ if ( !ok ) {
+ kdError() << "Parsing error! Aborting!" << endl
+ << " In line: " << errorLine << ", column: " << errorColumn << endl
+ << " Error message: " << errorMsg << endl;
+ return 1;
+ }
+
+ testMimeForPath( doc );
+ return 0;
+}
diff --git a/lib/kofficecore/tests/kopointtest.cpp b/lib/kofficecore/tests/kopointtest.cpp
new file mode 100644
index 00000000..25286a5d
--- /dev/null
+++ b/lib/kofficecore/tests/kopointtest.cpp
@@ -0,0 +1,89 @@
+#include <KoPoint.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <stdlib.h>
+#include <math.h>
+
+bool check( QString txt, bool res, bool expected )
+{
+ if ( res == expected ) {
+ kdDebug() << txt << " : checking '" << res << "' against expected value '" << expected << "'... " << "ok" << endl;
+ } else {
+ kdDebug() << txt << " : checking '" << res << "' against expected value '" << expected << "'... " << "KO !" << endl;
+ exit( 1 );
+ }
+ return true;
+}
+
+bool check( QString txt, double res, double expected )
+{
+ if ( kAbs(res - expected) < 0.000001 ) {
+ kdDebug() << txt << " : checking '" << res << "' against expected value '" << expected << "'... " << "ok" << endl;
+ } else {
+ kdDebug() << txt << " : checking '" << res << "' against expected value '" << expected << "'... " << "KO !" << endl;
+ exit( 1 );
+ }
+ return true;
+}
+
+int main()
+{
+ KoPoint p0;
+ check( "KoPoint() is null", p0.isNull(), true );
+ KoPoint p1( 10.1, 20.2 );
+ check( "KoPoint(...) is not null", p1.isNull(), false );
+ check( "KoPoint::x()", p1.x(), 10.1 );
+ check( "KoPoint::y()", p1.y(), 20.2 );
+ p1.setX( 2.1 );
+ p1.setY( 3.2 );
+ check( "KoPoint::setX()", p1.x(), 2.1 );
+ check( "KoPoint::setY()", p1.y(), 3.2 );
+
+ KoPoint p2( QPoint( -30, -40 ) );
+ check( "KoPoint(const QPoint&)", p2.x(), -30.0 );
+ check( "KoPoint(const QPoint&)", p2.y(), -40.0 );
+ p2.rx() = 1.5;
+ p2.ry() = 2.5;
+ check( "KoPoint::rx()", p2.x(), 1.5 );
+ check( "KoPoint::ry()", p2.y(), 2.5 );
+ p2.setCoords( -1.6, -1.7 );
+ check( "KoPoint::setCoords(double, double)", p2.x(), -1.6 );
+ check( "KoPoint::setCoords(double, double)", p2.y(), -1.7 );
+
+ check( "KoPoint::operator==(const KoPoint&)", p1 == p1, true );
+ check( "KoPoint::operator!=(const KoPoint&)", p1 != p2, true );
+
+ KoPoint p4( 10.2, 20.2 );
+ KoPoint p5( 30.4, 40.4 );
+ p4 += p5;
+ check( "KoPoint::operator+=(const KoPoint&)", p4.x(), 40.6 );
+ check( "KoPoint::operator+=(const KoPoint&)", p4.y(), 60.6 );
+ p4 -= p5;
+ check( "KoPoint::operator-=(const KoPoint&)", p4.x(), 10.2 );
+ check( "KoPoint::operator-=(const KoPoint&)", p4.y(), 20.2 );
+ p4 *= 2.0;
+ check( "KoPoint::operator*=(double)", p4.x(), 20.4 );
+ check( "KoPoint::operator*=(double)", p4.y(), 40.4 );
+ p4 = p5;
+ check( "KoPoint::operator=(const KoPoint&)", p4.x(), 30.4 );
+ check( "KoPoint::operator=(const KoPoint&)", p4.y(), 40.4 );
+
+ // ### global operator+, operator- and operator*
+ // ### transform
+
+ KoPoint p6( 1.0, 2.0 );
+ check( "KoPoint::isNear()", p6.isNear( p6, 0 ), true );
+ check( "KoPoint::isNear()", p6.isNear( KoPoint( 2, 10 ), 1 ), false );
+ check( "KoPoint::isNear()", p6.isNear( KoPoint( 2, 1 ), 1 ), true );
+
+ KoPoint p7( 10, 10 );
+ check( "KoPoint::getAngle()",
+ fmod( KoPoint::getAngle( p7, p7 * -1 ) + 10*360.0, 360.0 ), 45.0 );
+ // ### ??? check( "KoPoint::getAngle()",
+ // fmod( KoPoint::getAngle( -1 * p7, p7 ) + 10*360.0, 360.0 ), 45.0 );
+ // can we define a behaviour ?
+ // ### check( "KoPoint::getAngle()", KoPoint::getAngle( p7, p7 ), ??? );
+
+ kdDebug() << endl << "Test OK !" << endl;
+}
+
diff --git a/lib/kofficecore/tests/korecttest.cpp b/lib/kofficecore/tests/korecttest.cpp
new file mode 100644
index 00000000..9c2cb8b0
--- /dev/null
+++ b/lib/kofficecore/tests/korecttest.cpp
@@ -0,0 +1,53 @@
+#include <KoRect.h>
+#include <stdio.h>
+#include <kapplication.h>
+#include <stdlib.h>
+#include <kdebug.h>
+#include <kglobal.h>
+
+bool check(QString txt, bool res, bool expected)
+{
+ if (res == expected) {
+ kdDebug() << txt << " : checking '" << res << "' against expected value '" << expected << "'... " << "ok" << endl;
+ }
+ else {
+ kdDebug() << txt << " : checking '" << res << "' against expected value '" << expected << "'... " << "KO !" << endl;
+ exit(1);
+ }
+ return true;
+}
+
+bool check(QString txt, QString a, QString b)
+{
+ if (a.isEmpty())
+ a = QString::null;
+ if (b.isEmpty())
+ b = QString::null;
+ if (a == b) {
+ kdDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'... " << "ok" << endl;
+ }
+ else {
+ kdDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'... " << "KO !" << endl;
+ exit(1);
+ }
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ KApplication app(argc,argv,"korecttest",false,false);
+
+ KoRect emptyRect;
+ check( "KoRect() is null", emptyRect.isNull(), true );
+ check( "KoRect() is empty", emptyRect.isEmpty(), true );
+ KoRect rect( 1, 15, 250, 156.14 );
+ check( "KoRect(...) is not null", rect.isNull(), false );
+ check( "KoRect(...) is not empty", rect.isEmpty(), false );
+ KoRect unionRect = rect | emptyRect;
+ check( "Union is not null", unionRect.isNull(), false );
+ check( "Union is not empty", unionRect.isEmpty(), false );
+ kdDebug() << unionRect << endl;
+
+ printf("\nTest OK !\n");
+}
+
diff --git a/lib/kofficecore/tests/koxmlreadertest.cpp b/lib/kofficecore/tests/koxmlreadertest.cpp
new file mode 100644
index 00000000..213fe0db
--- /dev/null
+++ b/lib/kofficecore/tests/koxmlreadertest.cpp
@@ -0,0 +1,2074 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Ariya Hidayat <ariya@kde.org>
+
+ 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,
+*/
+
+// xmlreadertest.cpp - test KoXml classes
+// Ariya Hidayat, November 2005
+
+#include <qstring.h>
+#include <qcstring.h>
+#include <qbuffer.h>
+#include <qtextstream.h>
+#include <qdatetime.h>
+#include <qfile.h>
+
+#include "KoXmlReader.h"
+#include <qxml.h>
+
+#define CHECK(x,y) check(__FILE__,__LINE__,#x,x,y)
+
+static int testCount = 0;
+static int testFailed = 0;
+
+template<typename T>
+void check( const char *file, int line, const char* msg,
+const T& result, const T& expected )
+{
+ testCount++;
+ if( result != expected )
+ {
+ testFailed++;
+ QString message;
+ QTextStream ts( &message, IO_WriteOnly );
+ ts << msg;
+ ts << " Result:";
+ ts << result;
+ ts << ", ";
+ ts << "Expected:";
+ ts << expected;
+ printf( "%s [%d]: %s\n", file, line, message.latin1() );
+ }
+}
+
+void check( const char *file, int line, const char* msg, bool result,
+bool expected )
+{
+ testCount++;
+ if( result != expected )
+ {
+ testFailed++;
+ QString message;
+ QTextStream ts( &message, IO_WriteOnly );
+ ts << msg;
+ ts << " Result: ";
+ if( result ) ts << "True"; else ts << "False";
+ ts << ", ";
+ ts << "Expected: ";
+ if( expected ) ts << "True"; else ts << "False";
+ printf( "%s [%d]: %s\n", file, line, message.latin1() );
+ }
+}
+
+void testNode()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+ xmlstream << "<earth>";
+ xmlstream << "<continents>";
+ xmlstream << "<asia/>";
+ xmlstream << "<africa/>";
+ xmlstream << "<europe/>";
+ xmlstream << "<america/>";
+ xmlstream << "<australia/>";
+ xmlstream << "<antartic/>";
+ xmlstream << "</continents>";
+ xmlstream << "<oceans>";
+ xmlstream << "<pacific/>";
+ xmlstream << "<atlantic/>";
+ xmlstream << "</oceans>";
+ xmlstream << "</earth>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent( &xmldevice, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ // null node
+ KoXmlNode node1;
+ CHECK( node1.nodeName(), QString::null );
+ CHECK( node1.isNull(), true );
+ CHECK( node1.isElement(), false );
+ CHECK( node1.isElement(), false );
+ CHECK( node1.isDocument(), false );
+ CHECK( node1.ownerDocument().isNull(), false );
+ CHECK( node1.parentNode().isNull(), true );
+ CHECK( node1.hasChildNodes(), false );
+ CHECK( node1.firstChild().isNull(), true );
+ CHECK( node1.lastChild().isNull(), true );
+ CHECK( node1.previousSibling().isNull(), true );
+ CHECK( node1.nextSibling().isNull(), true );
+
+ // compare with another null node
+ KoXmlNode node2;
+ CHECK( node2.isNull(), true );
+ CHECK( node1==node2, true );
+ CHECK( node1!=node2, false );
+
+ // a node which is a document
+ KoXmlNode node3 = doc;
+ CHECK( node3.nodeName(), QString("#document") );
+ CHECK( node3.isNull(), false );
+ CHECK( node3.isElement(), false );
+ CHECK( node3.isText(), false );
+ CHECK( node3.isDocument(), true );
+ CHECK( node3.ownerDocument().isNull(), false );
+ CHECK( node3.ownerDocument()==doc, true );
+ CHECK( node3.toDocument()==doc, true );
+
+ // convert to document and the compare
+ KoXmlDocument doc2 = node3.toDocument();
+ CHECK( doc2.nodeName(), QString("#document") );
+ CHECK( doc2.isNull(), false );
+ CHECK( doc2.isDocument(), true );
+ CHECK( node3==doc2, true );
+
+ // a document is of course can't be converted to element
+ KoXmlElement invalidElement = node3.toElement();
+ CHECK( invalidElement.nodeName(), QString::null );
+ CHECK( invalidElement.isNull(), true );
+ CHECK( invalidElement.isElement(), true );
+ CHECK( invalidElement.isText(), false );
+ CHECK( invalidElement.isDocument(), false );
+
+ // clear() makes it a null node again
+ node3.clear();
+ CHECK( node3.isNull(), true );
+ CHECK( node3.nodeName(), QString::null );
+ CHECK( node3.isElement(), false );
+ CHECK( node3.isText(), false );
+ CHECK( node3.isDocument(), false );
+ CHECK( node3.ownerDocument().isNull(), false );
+ CHECK( node1==node3, true );
+ CHECK( node1!=node3, false );
+
+ // a node which is an element for <earth>
+ KoXmlNode node4 = doc.firstChild();
+ CHECK( node4.isNull(), false );
+ CHECK( node4.isElement(), true );
+ CHECK( node4.isText(), false );
+ CHECK( node4.isDocument(), false );
+ CHECK( node4.hasChildNodes(), true );
+ CHECK( node4.ownerDocument()==doc, true );
+ CHECK( node4.toElement()==doc.firstChild().toElement(), true );
+
+ // clear() makes it a null node again
+ node4.clear();
+ CHECK( node4.isNull(), true );
+ CHECK( node4.isElement(), false );
+ CHECK( node4.isText(), false );
+ CHECK( node4.isDocument(), false );
+ CHECK( node4==node1, true );
+ CHECK( node4!=node1, false );
+
+ // a node which is an element for <continents>
+ KoXmlNode node5 = doc.firstChild().firstChild();
+ CHECK( node5.nodeName(), QString("continents") );
+ CHECK( node5.isNull(), false );
+ CHECK( node5.isElement(), true );
+ CHECK( node5.isText(), false );
+ CHECK( node5.isDocument(), false );
+ CHECK( node5.hasChildNodes(), true );
+ CHECK( node5.ownerDocument()==doc, true );
+
+ // convert to element and the compare
+ KoXmlElement continentsElement = node5.toElement();
+ CHECK( node5==continentsElement, true );
+ CHECK( continentsElement.isNull(), false );
+ CHECK( continentsElement.isElement(), true );
+ CHECK( continentsElement.isText(), false );
+ CHECK( continentsElement.hasChildNodes(), true );
+ CHECK( continentsElement.ownerDocument()==doc, true );
+
+ // and it doesn't make sense to convert that node to document
+ // (instead a brand new document is created, i.e. not a null node)
+ KoXmlDocument invalidDoc = node5.toDocument();
+ CHECK( invalidDoc.isNull(), false );
+ CHECK( invalidDoc.isElement(), false );
+ CHECK( invalidDoc.isText(), false );
+ CHECK( invalidDoc.isDocument(), true );
+
+ // node for <europe> using namedItem() function
+ KoXmlNode europeNode = continentsElement.namedItem( QString("europe") );
+ CHECK( europeNode.nodeName(), QString("europe") );
+ CHECK( europeNode.isNull(), false );
+ CHECK( europeNode.isElement(), true );
+ CHECK( europeNode.isText(), false );
+ CHECK( europeNode.hasChildNodes(), false );
+ CHECK( europeNode.ownerDocument()==doc, true );
+
+ // search non-existing node
+ KoXmlNode fooNode = continentsElement.namedItem( QString("foobar") );
+ CHECK( fooNode.isNull(), true );
+ CHECK( fooNode.isElement(), false );
+ CHECK( fooNode.isText(), false );
+ CHECK( fooNode.isCDATASection(), false );
+}
+
+void testElement()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+ xmlstream << "<html>";
+ xmlstream << "<body bgcolor=\"#000\">";
+ xmlstream << "<p>";
+ xmlstream << "Hello, world!";
+ xmlstream << "</p>";
+ xmlstream << "</body>";
+ xmlstream << "</html>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent( &xmldevice, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ // element for <html>
+ KoXmlElement rootElement;
+ rootElement = doc.documentElement();
+ CHECK( rootElement.nodeName(), QString("html") );
+ CHECK( rootElement.isNull(), false );
+ CHECK( rootElement.isElement(), true );
+ CHECK( rootElement.isDocument(), false );
+ CHECK( rootElement.ownerDocument().isNull(), false );
+ CHECK( rootElement.ownerDocument()==doc, true );
+ CHECK( rootElement.parentNode().isNull(), false );
+ CHECK( rootElement.parentNode().toDocument()==doc, true );
+ CHECK( rootElement.hasChildNodes(), true );
+ CHECK( rootElement.tagName(), QString("html") );
+ CHECK( rootElement.prefix().isNull(), true );
+
+ // element for <body>
+ KoXmlElement bodyElement;
+ bodyElement = rootElement.firstChild().toElement();
+ CHECK( bodyElement.nodeName(), QString("body") );
+ CHECK( bodyElement.isNull(), false );
+ CHECK( bodyElement.isElement(), true );
+ CHECK( bodyElement.isDocument(), false );
+ CHECK( bodyElement.ownerDocument().isNull(), false );
+ CHECK( bodyElement.ownerDocument()==doc, true );
+ CHECK( bodyElement.parentNode().isNull(), false );
+ CHECK( bodyElement.parentNode()==rootElement, true );
+ CHECK( bodyElement.hasChildNodes(), true );
+ CHECK( bodyElement.tagName(), QString("body") );
+ CHECK( bodyElement.prefix().isNull(), true );
+ CHECK( bodyElement.hasAttribute("bgcolor"), true );
+ CHECK( bodyElement.attribute("bgcolor"), QString("#000") );
+
+ // a shared copy of <body>, will still have access to attribute bgcolor
+ KoXmlElement body2Element;
+ body2Element = bodyElement;
+ CHECK( body2Element.nodeName(), QString("body") );
+ CHECK( body2Element.isNull(), false );
+ CHECK( body2Element.isElement(), true );
+ CHECK( body2Element.isDocument(), false );
+ CHECK( body2Element.ownerDocument().isNull(), false );
+ CHECK( body2Element.ownerDocument()==doc, true );
+ CHECK( body2Element==bodyElement, true );
+ CHECK( body2Element!=bodyElement, false );
+ CHECK( body2Element.hasChildNodes(), true );
+ CHECK( body2Element.tagName(), QString("body") );
+ CHECK( body2Element.prefix().isNull(), true );
+ CHECK( body2Element.hasAttribute("bgcolor"), true );
+ CHECK( body2Element.attribute("bgcolor"), QString("#000") );
+
+ // empty element, by default constructor
+ KoXmlElement testElement;
+ CHECK( testElement.nodeName(), QString::null );
+ CHECK( testElement.isNull(), true );
+ CHECK( testElement.isElement(), true );
+ CHECK( testElement.isDocument(), false );
+ CHECK( testElement.ownerDocument().isNull(), false );
+ CHECK( testElement.ownerDocument()!=doc, true );
+ CHECK( testElement==rootElement, false );
+ CHECK( testElement!=rootElement, true );
+ CHECK( testElement.parentNode().isNull(), true );
+ CHECK( testElement.hasChildNodes(), false );
+
+ // check assignment operator
+ testElement = rootElement;
+ CHECK( testElement.nodeName(), QString("html") );
+ CHECK( testElement.isNull(), false );
+ CHECK( testElement.isElement(), true );
+ CHECK( testElement.isDocument(), false );
+ CHECK( testElement==rootElement, true );
+ CHECK( testElement!=rootElement, false );
+ CHECK( testElement.parentNode().isNull(), false );
+ CHECK( testElement.parentNode().toDocument()==doc, true );
+ CHECK( testElement.tagName(), QString("html") );
+ CHECK( testElement.prefix().isNull(), true );
+
+ // assigned from another empty element
+ testElement = KoXmlElement();
+ CHECK( testElement.isNull(), true );
+ CHECK( testElement!=rootElement, true );
+
+ // assigned from <body>
+ testElement = bodyElement;
+ CHECK( testElement.isNull(), false );
+ CHECK( testElement.isElement(), true );
+ CHECK( testElement.isDocument(), false );
+ CHECK( testElement.ownerDocument().isNull(), false );
+ CHECK( testElement.ownerDocument()==doc, true );
+ CHECK( testElement==bodyElement, true );
+ CHECK( testElement.parentNode().isNull(), false );
+ CHECK( testElement.tagName(), QString("body") );
+ CHECK( testElement.prefix().isNull(), true );
+ CHECK( testElement.hasChildNodes(), true );
+
+ // copy constructor
+ KoXmlElement dummyElement( rootElement );
+ CHECK( dummyElement.isNull(), false );
+ CHECK( dummyElement.isElement(), true );
+ CHECK( dummyElement.isDocument(), false );
+ CHECK( dummyElement.ownerDocument().isNull(), false );
+ CHECK( dummyElement.ownerDocument()==doc, true );
+ CHECK( dummyElement==rootElement, true );
+ CHECK( dummyElement.parentNode().isNull(), false );
+ CHECK( dummyElement.hasChildNodes(), true );
+ CHECK( dummyElement.tagName(), QString("html") );
+ CHECK( dummyElement.prefix().isNull(), true );
+
+ // clear() turns element to null node
+ dummyElement.clear();
+ CHECK( dummyElement.isNull(), true );
+ CHECK( dummyElement.isElement(), true );
+ CHECK( dummyElement.isDocument(), false );
+ CHECK( dummyElement.ownerDocument().isNull(), false );
+ CHECK( dummyElement.ownerDocument()==doc, false );
+ CHECK( dummyElement.hasChildNodes(), false );
+ CHECK( dummyElement==rootElement, false );
+ CHECK( dummyElement!=rootElement, true );
+
+ // check for plain null node converted to element
+ KoXmlNode dummyNode;
+ dummyElement = dummyNode.toElement();
+ CHECK( dummyElement.isNull(), true );
+ CHECK( dummyElement.isElement(), true );
+ CHECK( dummyElement.isDocument(), false );
+ CHECK( dummyElement.ownerDocument().isNull(), false );
+ CHECK( dummyElement.hasChildNodes(), false );
+ CHECK( dummyElement.ownerDocument()==doc, false );
+}
+
+void testAttributes()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+ xmlstream << "<p>";
+ xmlstream << "<img src=\"foo.png\" width=\"300\" height=\"150\"/>";
+ xmlstream << "</p>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent( &xmldevice, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ KoXmlElement rootElement;
+ rootElement = doc.documentElement();
+ CHECK( rootElement.isNull(), false );
+ CHECK( rootElement.isElement(), true );
+ CHECK( rootElement.parentNode().isNull(), false );
+ CHECK( rootElement.parentNode().toDocument()==doc, true );
+ CHECK( rootElement.tagName(), QString("p") );
+ CHECK( rootElement.prefix().isNull(), true );
+
+ KoXmlElement imgElement;
+ imgElement = rootElement.firstChild().toElement();
+ CHECK( imgElement.isNull(), false );
+ CHECK( imgElement.isElement(), true );
+ CHECK( imgElement.tagName(), QString("img") );
+ CHECK( imgElement.prefix().isNull(), true );
+ CHECK( imgElement.hasAttribute("src"), true );
+ CHECK( imgElement.hasAttribute("width"), true );
+ CHECK( imgElement.hasAttribute("height"), true );
+ CHECK( imgElement.hasAttribute("non-exist"), false );
+ CHECK( imgElement.hasAttribute("SRC"), false );
+ CHECK( imgElement.attribute("src"), QString("foo.png") );
+ CHECK( imgElement.attribute("width"), QString("300") );
+ CHECK( imgElement.attribute("width").toInt(), 300 );
+ CHECK( imgElement.attribute("height"), QString("150") );
+ CHECK( imgElement.attribute("height").toInt(), 150 );
+ CHECK( imgElement.attribute("border").isEmpty(), true );
+ CHECK( imgElement.attribute("border","0").toInt(), 0 );
+ CHECK( imgElement.attribute("border","-1").toInt(), -1 );
+}
+
+void testText()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+ xmlstream << "<p>";
+ xmlstream << "Hello ";
+ xmlstream << "<b>world</b>";
+ xmlstream << "</p>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent( &xmldevice, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ // element for <p>
+ KoXmlElement parElement;
+ parElement = doc.documentElement();
+ CHECK( parElement.isNull(), false );
+ CHECK( parElement.isElement(), true );
+ CHECK( parElement.isText(), false );
+ CHECK( parElement.isDocument(), false );
+ CHECK( parElement.ownerDocument().isNull(), false );
+ CHECK( parElement.ownerDocument()==doc, true );
+ CHECK( parElement.parentNode().isNull(), false );
+ CHECK( parElement.parentNode().toDocument()==doc, true );
+ CHECK( parElement.hasChildNodes(), true );
+ CHECK( parElement.tagName(), QString("p") );
+ CHECK( parElement.prefix().isNull(), true );
+ CHECK( parElement.text(), QString("Hello world") );
+
+ // node for "Hello"
+ KoXmlNode helloNode;
+ helloNode = parElement.firstChild();
+ CHECK( helloNode.nodeName(), QString("#text") );
+ CHECK( helloNode.isNull(), false );
+ CHECK( helloNode.isElement(), false );
+ CHECK( helloNode.isText(), true );
+ CHECK( helloNode.isDocument(), false );
+
+ // "Hello" text
+ KoXmlText helloText;
+ helloText = helloNode.toText();
+ CHECK( helloText.nodeName(), QString("#text") );
+ CHECK( helloText.isNull(), false );
+ CHECK( helloText.isElement(), false );
+ CHECK( helloText.isText(), true );
+ CHECK( helloText.isDocument(), false );
+ CHECK( helloText.data(), QString("Hello ") );
+
+ // shared copy of the text
+ KoXmlText hello2Text;
+ hello2Text = helloText;
+ CHECK( hello2Text.isNull(), false );
+ CHECK( hello2Text.isElement(), false );
+ CHECK( hello2Text.isText(), true );
+ CHECK( hello2Text.isDocument(), false );
+ CHECK( hello2Text.data(), QString("Hello ") );
+
+ // element for <b>
+ KoXmlElement boldElement;
+ boldElement = helloNode.nextSibling().toElement();
+ CHECK( boldElement.isNull(), false );
+ CHECK( boldElement.isElement(), true );
+ CHECK( boldElement.isText(), false );
+ CHECK( boldElement.isDocument(), false );
+ CHECK( boldElement.ownerDocument().isNull(), false );
+ CHECK( boldElement.ownerDocument()==doc, true );
+ CHECK( boldElement.parentNode().isNull(), false );
+ CHECK( boldElement.hasChildNodes(), true );
+ CHECK( boldElement.tagName(), QString("b") );
+ CHECK( boldElement.prefix().isNull(), true );
+
+ // "world" text
+ KoXmlText worldText;
+ worldText = boldElement.firstChild().toText();
+ CHECK( worldText.isNull(), false );
+ CHECK( worldText.isElement(), false );
+ CHECK( worldText.isText(), true );
+ CHECK( worldText.isDocument(), false );
+ CHECK( worldText.data(), QString("world") );
+}
+
+void testCDATA()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+ xmlstream << "<p>";
+ xmlstream << "Hello ";
+ xmlstream << "<![CDATA[world]]>";
+ xmlstream << "</p>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent( &xmldevice, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ // element for <p>
+ KoXmlElement parElement;
+ parElement = doc.documentElement();
+ CHECK( parElement.isNull(), false );
+ CHECK( parElement.isElement(), true );
+ CHECK( parElement.isText(), false );
+ CHECK( parElement.isDocument(), false );
+ CHECK( parElement.ownerDocument().isNull(), false );
+ CHECK( parElement.ownerDocument()==doc, true );
+ CHECK( parElement.parentNode().isNull(), false );
+ CHECK( parElement.parentNode().toDocument()==doc, true );
+ CHECK( parElement.hasChildNodes(), true );
+ CHECK( parElement.tagName(), QString("p") );
+ CHECK( parElement.prefix().isNull(), true );
+ CHECK( parElement.text(), QString("Hello world") );
+
+ // node for "Hello"
+ KoXmlNode helloNode;
+ helloNode = parElement.firstChild();
+ CHECK( helloNode.isNull(), false );
+ CHECK( helloNode.isElement(), false );
+ CHECK( helloNode.isText(), true );
+ CHECK( helloNode.isDocument(), false );
+
+ // "Hello" text
+ KoXmlText helloText;
+ helloText = helloNode.toText();
+ CHECK( helloText.isNull(), false );
+ CHECK( helloText.isElement(), false );
+ CHECK( helloText.isText(), true );
+ CHECK( helloText.isDocument(), false );
+ CHECK( helloText.data(), QString("Hello ") );
+
+ // node for CDATA "world!"
+ // Note: isText() is also true for CDATA
+ KoXmlNode worldNode;
+ worldNode = helloNode.nextSibling();
+ CHECK( worldNode.nodeName(), QString("#cdata-section") );
+ CHECK( worldNode.isNull(), false );
+ CHECK( worldNode.isElement(), false );
+ CHECK( worldNode.isText(), true );
+ CHECK( worldNode.isCDATASection(), true );
+ CHECK( worldNode.isDocument(), false );
+
+ // CDATA section for "world!"
+ // Note: isText() is also true for CDATA
+ KoXmlCDATASection worldCDATA;
+ worldCDATA = worldNode.toCDATASection();
+ CHECK( worldCDATA.nodeName(), QString("#cdata-section") );
+ CHECK( worldCDATA.isNull(), false );
+ CHECK( worldCDATA.isElement(), false );
+ CHECK( worldCDATA.isText(), true );
+ CHECK( worldCDATA.isCDATASection(), true );
+ CHECK( worldCDATA.isDocument(), false );
+ CHECK( worldCDATA.data(), QString("world") );
+}
+
+void testDocument()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+ xmlstream << "<koffice>";
+ xmlstream << " <kword/>\n";
+ xmlstream << " <kpresenter/>\n";
+ xmlstream << " <krita/>\n";
+ xmlstream << "</koffice>";
+
+ KoXmlDocument doc;
+
+ // empty document
+ CHECK( doc.nodeName(), QString("#document") );
+ CHECK( doc.isNull(), false );
+ CHECK( doc.isElement(), false );
+ CHECK( doc.isDocument(), true );
+ CHECK( doc.parentNode().isNull(), true );
+ CHECK( doc.firstChild().isNull(), true );
+ CHECK( doc.lastChild().isNull(), true );
+ CHECK( doc.previousSibling().isNull(), true );
+ CHECK( doc.nextSibling().isNull(), true );
+
+ // now give something as the content
+ CHECK( doc.setContent(&xmldevice,&errorMsg,&errorLine,&errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ // this document has something already
+ CHECK( doc.nodeName(), QString("#document") );
+ CHECK( doc.isNull(), false );
+ CHECK( doc.isElement(), false );
+ CHECK( doc.isDocument(), true );
+ CHECK( doc.parentNode().isNull(), true );
+ CHECK( doc.firstChild().isNull(), false );
+ CHECK( doc.lastChild().isNull(), false );
+ CHECK( doc.previousSibling().isNull(), true );
+ CHECK( doc.nextSibling().isNull(), true );
+
+ // make sure its children are fine
+ KoXmlElement rootElement;
+ rootElement = doc.firstChild().toElement();
+ CHECK( rootElement.isNull(), false );
+ CHECK( rootElement.isElement(), true );
+ CHECK( rootElement.isDocument(), false );
+ CHECK( rootElement.parentNode().isNull(), false );
+ CHECK( rootElement.parentNode().toDocument()==doc, true );
+ rootElement = doc.lastChild().toElement();
+ CHECK( rootElement.isNull(), false );
+ CHECK( rootElement.isElement(), true );
+ CHECK( rootElement.isDocument(), false );
+ CHECK( rootElement.parentNode().isNull(), false );
+ CHECK( rootElement.parentNode().toDocument()==doc, true );
+
+ // clear() converts it into null node
+ doc.clear();
+ CHECK( doc.nodeName(), QString::null );
+ CHECK( doc.isNull(), true );
+ CHECK( doc.isElement(), false );
+ CHECK( doc.isDocument(), true );
+ CHECK( doc.parentNode().isNull(), true );
+ CHECK( doc.firstChild().isNull(), true );
+ CHECK( doc.lastChild().isNull(), true );
+ CHECK( doc.previousSibling().isNull(), true );
+ CHECK( doc.nextSibling().isNull(), true );
+
+ // assigned from another empty document
+ doc = KoXmlDocument();
+ CHECK( doc.nodeName(), QString("#document") );
+ CHECK( doc.isNull(), false );
+ CHECK( doc.isElement(), false );
+ CHECK( doc.isDocument(), true );
+ CHECK( doc.parentNode().isNull(), true );
+}
+
+void testNamespace()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+
+ // taken from example in Qt documentation (xml.html)
+ xmlstream << "<document xmlns:book = \"http://trolltech.com/fnord/book/\"";
+ xmlstream << " xmlns = \"http://trolltech.com/fnord/\" >";
+ xmlstream << "<book>";
+ xmlstream << " <book:title>Practical XML</book:title>";
+ xmlstream << " <book:author xmlns:fnord = \"http://trolltech.com/fnord/\"";
+ xmlstream << " title=\"Ms\"";
+ xmlstream << " fnord:title=\"Goddess\"";
+ xmlstream << " name=\"Eris Kallisti\"/>";
+ xmlstream << " <chapter>";
+ xmlstream << " <title>A Namespace Called fnord</title>";
+ xmlstream << " </chapter>";
+ xmlstream << "</book>";
+ xmlstream << "</document>";
+
+ KoXmlDocument doc;
+ KoXmlElement rootElement;
+ KoXmlElement bookElement;
+ KoXmlElement bookTitleElement;
+ KoXmlElement bookAuthorElement;
+
+ // ------------- first without any namespace processing -----------
+ CHECK( doc.setContent( &xmldevice, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ rootElement = doc.documentElement();
+ CHECK( rootElement.isNull(), false );
+ CHECK( rootElement.isElement(), true );
+ CHECK( rootElement.tagName(), QString("document") );
+ CHECK( rootElement.prefix().isNull(), true );
+
+ bookElement = rootElement.firstChild().toElement();
+ CHECK( bookElement.isNull(), false );
+ CHECK( bookElement.isElement(), true );
+ CHECK( bookElement.tagName(), QString("book") );
+ CHECK( bookElement.prefix().isNull(), true );
+ CHECK( bookElement.localName(), QString::null );
+
+ bookTitleElement = bookElement.firstChild().toElement();
+ CHECK( bookTitleElement.isNull(), false );
+ CHECK( bookTitleElement.isElement(), true );
+ CHECK( bookTitleElement.tagName(), QString("book:title") );
+ CHECK( bookTitleElement.prefix().isNull(), true );
+ CHECK( bookTitleElement.localName(), QString::null );
+
+ bookAuthorElement = bookTitleElement.nextSibling().toElement();
+ CHECK( bookAuthorElement.isNull(), false );
+ CHECK( bookAuthorElement.isElement(), true );
+ CHECK( bookAuthorElement.tagName(), QString("book:author") );
+ CHECK( bookAuthorElement.prefix().isNull(), true );
+ CHECK( bookAuthorElement.attribute("title"), QString("Ms") );
+ CHECK( bookAuthorElement.attribute("fnord:title"), QString("Goddess") );
+ CHECK( bookAuthorElement.attribute("name"), QString("Eris Kallisti") );
+
+ // ------------- now with namespace processing -----------
+ xmldevice.at(0); // just to rewind
+
+ CHECK( doc.setContent( &xmldevice, true, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ char* defaultNS = "http://trolltech.com/fnord/";
+ char* bookNS = "http://trolltech.com/fnord/book/";
+ char* fnordNS = "http://trolltech.com/fnord/";
+
+ // <document>
+ rootElement = doc.documentElement();
+ CHECK( rootElement.isNull(), false );
+ CHECK( rootElement.isElement(), true );
+ CHECK( rootElement.tagName(), QString("document") );
+ CHECK( rootElement.prefix().isEmpty(), true );
+ CHECK( rootElement.namespaceURI(), QString( defaultNS ) );
+ CHECK( rootElement.localName(), QString("document") );
+
+ // <book>
+ bookElement = rootElement.firstChild().toElement();
+ CHECK( bookElement.isNull(), false );
+ CHECK( bookElement.isElement(), true );
+ CHECK( bookElement.tagName(), QString("book") );
+ CHECK( bookElement.prefix().isEmpty(), true );
+ CHECK( bookElement.namespaceURI(), QString( defaultNS ) );
+ CHECK( bookElement.localName(), QString("book") );
+
+ // <book:title>
+ bookTitleElement = bookElement.firstChild().toElement();
+ CHECK( bookTitleElement.isNull(), false );
+ CHECK( bookTitleElement.isElement(), true );
+ CHECK( bookTitleElement.tagName(), QString("title") );
+ CHECK( bookTitleElement.prefix(), QString("book") );
+ CHECK( bookTitleElement.namespaceURI(), QString(bookNS) );
+ CHECK( bookTitleElement.localName(), QString("title") );
+
+ // another way, find it using namedItemNS()
+ KoXmlElement book2TitleElement;
+ book2TitleElement = KoXml::namedItemNS( rootElement.firstChild(), bookNS, "title" );
+ //book2TitleElement = bookElement.namedItemNS( bookNS, "title" ).toElement();
+ CHECK( book2TitleElement==bookTitleElement, true );
+ CHECK( book2TitleElement.isNull(), false );
+ CHECK( book2TitleElement.isElement(), true );
+ CHECK( book2TitleElement.tagName(), QString("title") );
+
+ // <book:author>
+ bookAuthorElement = bookTitleElement.nextSibling().toElement();
+ CHECK( bookAuthorElement.isNull(), false );
+ CHECK( bookAuthorElement.isElement(), true );
+ CHECK( bookAuthorElement.tagName(), QString("author") );
+ CHECK( bookAuthorElement.prefix(), QString("book") );
+ CHECK( bookAuthorElement.namespaceURI(), QString(bookNS) );
+ CHECK( bookAuthorElement.localName(), QString("author") );
+
+ // another way, find it using namedItemNS()
+ KoXmlElement book2AuthorElement;
+ book2AuthorElement = KoXml::namedItemNS( bookElement, bookNS, "author" );
+ //book2AuthorElement = bookElement.namedItemNS( bookNS, "author" ).toElement();
+ CHECK( book2AuthorElement==bookAuthorElement, true );
+ CHECK( book2AuthorElement.isNull(), false );
+ CHECK( book2AuthorElement.isElement(), true );
+ CHECK( book2AuthorElement.tagName(), QString("author") );
+
+ // attributes in <book:author>
+ // Note: with namespace processing, attribute's prefix is taken out and
+ // hence "fnord:title" will simply override "title"
+ // and searching attribute with prefix will give no result
+ CHECK( bookAuthorElement.hasAttribute("title"), true );
+ CHECK( bookAuthorElement.hasAttribute("fnord:title"), false );
+ CHECK( bookAuthorElement.hasAttribute("name"), true );
+ CHECK( bookAuthorElement.attribute("title"), QString("Goddess") );
+ CHECK( bookAuthorElement.attribute("fnord:title").isEmpty(), true );
+ CHECK( bookAuthorElement.attribute("name"), QString("Eris Kallisti") );
+
+ // attributes in <book:author>, with NS family of functions
+ // those without prefix are not accessible at all, because they do not belong
+ // to any namespace at all.
+ // Note: default namespace does not apply to attribute names!
+ CHECK( bookAuthorElement.hasAttributeNS(defaultNS,"title"), true );
+ CHECK( bookAuthorElement.hasAttributeNS(bookNS,"title"), false );
+ CHECK( bookAuthorElement.hasAttributeNS(fnordNS,"title"), true );
+
+ CHECK( bookAuthorElement.attributeNS(defaultNS,"title",""), QString("Goddess") );
+ CHECK( bookAuthorElement.attributeNS(bookNS,"title",""), QString("") );
+ CHECK( bookAuthorElement.attributeNS(fnordNS,"title",""), QString("Goddess") );
+
+ CHECK( bookAuthorElement.hasAttributeNS(defaultNS,"fnord:title"), false );
+ CHECK( bookAuthorElement.hasAttributeNS(bookNS,"fnord:title"), false );
+ CHECK( bookAuthorElement.hasAttributeNS(fnordNS,"fnord:title"), false );
+
+ CHECK( bookAuthorElement.hasAttributeNS(defaultNS,"name"), false );
+ CHECK( bookAuthorElement.hasAttributeNS(bookNS,"name"), false );
+ CHECK( bookAuthorElement.hasAttributeNS(fnordNS,"name"), false );
+
+ CHECK( bookAuthorElement.attributeNS(defaultNS,"name",QString::null).isEmpty(), true );
+ CHECK( bookAuthorElement.attributeNS(bookNS,"name",QString::null).isEmpty(), true );
+ CHECK( bookAuthorElement.attributeNS(fnordNS,"name",QString::null).isEmpty(), true );
+}
+
+void testUnload()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+ xmlstream << "<earth>";
+ xmlstream << "<continents>";
+ xmlstream << "<asia/>";
+ xmlstream << "<africa/>";
+ xmlstream << "<europe/>";
+ xmlstream << "<america/>";
+ xmlstream << "<australia/>";
+ xmlstream << "<antartic/>";
+ xmlstream << "</continents>";
+ xmlstream << "<oceans>";
+ xmlstream << "<pacific/>";
+ xmlstream << "<atlantic/>";
+ xmlstream << "</oceans>";
+ xmlstream << "</earth>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent( &xmldevice, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ KoXmlElement earthElement;
+ earthElement = doc.documentElement().toElement();
+ CHECK( earthElement.isNull(), false );
+ CHECK( earthElement.isElement(), true );
+ CHECK( earthElement.parentNode().isNull(), false );
+ CHECK( earthElement.hasChildNodes(), true );
+ CHECK( earthElement.tagName(), QString("earth") );
+ CHECK( earthElement.prefix().isNull(), true );
+
+ // this ensures that all child nodes of <earth> are loaded
+ earthElement.firstChild();
+
+ // explicitly unload all child nodes of <earth>
+ KoXml::unload( earthElement );
+
+ // we should get the correct first child
+ KoXmlElement continentsElement = earthElement.firstChild().toElement();
+ CHECK( continentsElement.nodeName(), QString("continents") );
+ CHECK( continentsElement.isNull(), false );
+ CHECK( continentsElement.isElement(), true );
+ CHECK( continentsElement.isText(), false );
+ CHECK( continentsElement.isDocument(), false );
+ CHECK( continentsElement.hasChildNodes(), true );
+
+ // let us unload everything again
+ KoXml::unload( earthElement );
+
+ // we should get the correct last child
+ KoXmlElement oceansElement = earthElement.lastChild().toElement();
+ CHECK( oceansElement.nodeName(), QString("oceans") );
+ CHECK( oceansElement.isNull(), false );
+ CHECK( oceansElement.isElement(), true );
+ CHECK( oceansElement.isText(), false );
+ CHECK( oceansElement.isDocument(), false );
+ CHECK( oceansElement.hasChildNodes(), true );
+}
+
+void testSimpleXML()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+ xmlstream << "<solarsystem>";
+ xmlstream << " <mercurius/>\n";
+ xmlstream << " <venus/>\n";
+ xmlstream << " <earth>\n";
+ xmlstream << " <moon/>\n";
+ xmlstream << " </earth>\n";
+ xmlstream << " <mars/>\n";
+ xmlstream << " <jupiter/>\n";
+ xmlstream << "</solarsystem>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent(&xmldevice,&errorMsg,&errorLine,&errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ // <solarsystem>
+ KoXmlElement rootElement;
+ rootElement = doc.documentElement();
+ CHECK( rootElement.isNull(), false );
+ CHECK( rootElement.isElement(), true );
+ CHECK( rootElement.parentNode().isNull(), false );
+ CHECK( rootElement.hasChildNodes(), true );
+ CHECK( rootElement.tagName(), QString("solarsystem") );
+ CHECK( rootElement.prefix().isNull(), true );
+
+ // node <mercurius>
+ KoXmlNode firstPlanetNode;
+ firstPlanetNode = rootElement.firstChild();
+ CHECK( firstPlanetNode.isNull(), false );
+ CHECK( firstPlanetNode.isElement(), true );
+ CHECK( firstPlanetNode.nextSibling().isNull(), false );
+ CHECK( firstPlanetNode.previousSibling().isNull(), true );
+ CHECK( firstPlanetNode.parentNode().isNull(), false );
+ CHECK( firstPlanetNode.parentNode()==rootElement, true );
+ CHECK( firstPlanetNode.parentNode()!=rootElement, false );
+ CHECK( firstPlanetNode.hasChildNodes(), false );
+ CHECK( firstPlanetNode.firstChild().isNull(), true );
+ CHECK( firstPlanetNode.lastChild().isNull(), true );
+
+ // element <mercurius>
+ KoXmlElement firstPlanetElement;
+ firstPlanetElement = firstPlanetNode.toElement();
+ CHECK( firstPlanetElement.isNull(), false );
+ CHECK( firstPlanetElement.isElement(), true );
+ CHECK( firstPlanetElement.parentNode().isNull(), false );
+ CHECK( firstPlanetElement.parentNode()==rootElement, true );
+ CHECK( firstPlanetElement.hasChildNodes(), false );
+ CHECK( firstPlanetElement.firstChild().isNull(), true );
+ CHECK( firstPlanetElement.lastChild().isNull(), true );
+ CHECK( firstPlanetElement.tagName(), QString("mercurius") );
+ CHECK( firstPlanetElement.prefix().isNull(), true );
+
+ // node <venus>
+ KoXmlNode secondPlanetNode;
+ secondPlanetNode = firstPlanetNode.nextSibling();
+ CHECK( secondPlanetNode.isNull(), false );
+ CHECK( secondPlanetNode.isElement(), true );
+ CHECK( secondPlanetNode.nextSibling().isNull(), false );
+ CHECK( secondPlanetNode.previousSibling().isNull(), false );
+ CHECK( secondPlanetNode.previousSibling()==firstPlanetNode, true );
+ CHECK( secondPlanetNode.previousSibling()==firstPlanetElement, true );
+ CHECK( secondPlanetNode.parentNode().isNull(), false );
+ CHECK( secondPlanetNode.parentNode()==rootElement, true );
+ CHECK( secondPlanetNode.parentNode()!=rootElement, false );
+ CHECK( secondPlanetNode.hasChildNodes(), false );
+ CHECK( secondPlanetNode.firstChild().isNull(), true );
+ CHECK( secondPlanetNode.lastChild().isNull(), true );
+
+ // element <venus>
+ KoXmlElement secondPlanetElement;
+ secondPlanetElement = secondPlanetNode.toElement();
+ CHECK( secondPlanetElement.isNull(), false );
+ CHECK( secondPlanetElement.isElement(), true );
+ CHECK( secondPlanetElement.nextSibling().isNull(), false );
+ CHECK( secondPlanetElement.previousSibling().isNull(), false );
+ CHECK( secondPlanetElement.previousSibling()==firstPlanetNode, true );
+ CHECK( secondPlanetElement.previousSibling()==firstPlanetElement, true );
+ CHECK( secondPlanetElement.parentNode().isNull(), false );
+ CHECK( secondPlanetElement.parentNode()==rootElement, true );
+ CHECK( secondPlanetElement.parentNode()!=rootElement, false );
+ CHECK( secondPlanetElement.hasChildNodes(), false );
+ CHECK( secondPlanetElement.firstChild().isNull(), true );
+ CHECK( secondPlanetElement.lastChild().isNull(), true );
+ CHECK( secondPlanetElement.tagName(), QString("venus") );
+ CHECK( secondPlanetElement.prefix().isNull(), true );
+}
+
+void testRootError()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ // multiple root nodes are not valid !
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+ xmlstream << "<earth></earth><moon></moon>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent( &xmldevice, &errorMsg, &errorLine, &errorColumn ), false );
+ CHECK( errorMsg.isEmpty(), false );
+ CHECK( errorMsg, QString("unexpected character") );
+ CHECK( errorLine, 1 );
+ CHECK( errorColumn, 17 );
+}
+
+void testMismatchedTag()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+ xmlstream << "<earth></e>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent( &xmldevice, &errorMsg, &errorLine, &errorColumn ), false );
+ CHECK( errorMsg.isEmpty(), false );
+ CHECK( errorMsg, QString("tag mismatch") );
+ CHECK( errorLine, 1 );
+ CHECK( errorColumn, 11 );
+}
+
+void testSimpleOpenDocumentText()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+
+ // content.xml from a simple OpenDocument text
+ // it has only paragraph "Hello, world!"
+ // automatic styles, declarations and unnecessary namespaces are omitted.
+ xmlstream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ xmlstream << "<office:document-content ";
+ xmlstream << " xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"";
+ xmlstream << " xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"";
+ xmlstream << " xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" ";
+ xmlstream << " office:version=\"1.0\">";
+ xmlstream << " <office:automatic-styles/>";
+ xmlstream << " <office:body>";
+ xmlstream << " <office:text>";
+ xmlstream << " <text:p text:style-name=\"Standard\">Hello, world!</text:p>";
+ xmlstream << " </office:text>";
+ xmlstream << " </office:body>";
+ xmlstream << "</office:document-content>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent( &xmldevice, true, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ char* officeNS = "urn:oasis:names:tc:opendocument:xmlns:office:1.0";
+ char* textNS = "urn:oasis:names:tc:opendocument:xmlns:text:1.0";
+
+ // <office:document-content>
+ KoXmlElement contentElement;
+ contentElement = doc.documentElement();
+ CHECK( contentElement.isNull(), false );
+ CHECK( contentElement.isElement(), true );
+ CHECK( contentElement.parentNode().isNull(), false );
+ CHECK( contentElement.parentNode().toDocument()==doc, true );
+ CHECK( contentElement.firstChild().isNull(), false );
+ CHECK( contentElement.lastChild().isNull(), false );
+ CHECK( contentElement.previousSibling().isNull(), false );
+ CHECK( contentElement.nextSibling().isNull(), true );
+ CHECK( contentElement.localName(), QString("document-content") );
+ CHECK( contentElement.hasAttributeNS(officeNS,"version"), true );
+ CHECK( contentElement.attributeNS(officeNS,"version",""), QString("1.0") );
+
+ // <office:automatic-styles>
+ KoXmlElement stylesElement;
+ stylesElement = KoXml::namedItemNS( contentElement, officeNS, "automatic-styles" );
+ CHECK( stylesElement.isNull(), false );
+ CHECK( stylesElement.isElement(), true );
+ CHECK( stylesElement.parentNode().isNull(), false );
+ CHECK( stylesElement.parentNode()==contentElement, true );
+ CHECK( stylesElement.firstChild().isNull(), true );
+ CHECK( stylesElement.lastChild().isNull(), true );
+ CHECK( stylesElement.previousSibling().isNull(), true );
+ CHECK( stylesElement.nextSibling().isNull(), false );
+ CHECK( stylesElement.localName(), QString("automatic-styles") );
+
+ // also same <office:automatic-styles>, but without namedItemNS
+ KoXmlNode styles2Element;
+ styles2Element = contentElement.firstChild().toElement();
+ CHECK( styles2Element.isNull(), false );
+ CHECK( styles2Element.isElement(), true );
+ CHECK( styles2Element.parentNode().isNull(), false );
+ CHECK( styles2Element.parentNode()==contentElement, true );
+ CHECK( styles2Element.firstChild().isNull(), true );
+ CHECK( styles2Element.lastChild().isNull(), true );
+ CHECK( styles2Element.previousSibling().isNull(), true );
+ CHECK( styles2Element.nextSibling().isNull(), false );
+ CHECK( styles2Element.localName(), QString("automatic-styles") );
+
+ // <office:body>
+ KoXmlElement bodyElement;
+ bodyElement = KoXml::namedItemNS( contentElement, officeNS, "body" );
+ CHECK( bodyElement.isNull(), false );
+ CHECK( bodyElement.isElement(), true );
+ CHECK( bodyElement.parentNode().isNull(), false );
+ CHECK( bodyElement.parentNode()==contentElement, true );
+ CHECK( bodyElement.firstChild().isNull(), false );
+ CHECK( bodyElement.lastChild().isNull(), false );
+ CHECK( bodyElement.previousSibling().isNull(), false );
+ CHECK( bodyElement.nextSibling().isNull(), true );
+ CHECK( bodyElement.localName(), QString("body") );
+
+ // also same <office:body>, but without namedItemNS
+ KoXmlElement body2Element;
+ body2Element = stylesElement.nextSibling().toElement();
+ CHECK( body2Element.isNull(), false );
+ CHECK( body2Element.isElement(), true );
+ CHECK( body2Element.parentNode().isNull(), false );
+ CHECK( body2Element.parentNode()==contentElement, true );
+ CHECK( body2Element.firstChild().isNull(), false );
+ CHECK( body2Element.lastChild().isNull(), false );
+ CHECK( body2Element.previousSibling().isNull(), false );
+ CHECK( body2Element.nextSibling().isNull(), true );
+ CHECK( body2Element.localName(), QString("body") );
+
+ // <office:text>
+ KoXmlElement textElement;
+ textElement = KoXml::namedItemNS( bodyElement, officeNS, "text" );
+ CHECK( textElement.isNull(), false );
+ CHECK( textElement.isElement(), true );
+ CHECK( textElement.parentNode().isNull(), false );
+ CHECK( textElement.parentNode()==bodyElement, true );
+ CHECK( textElement.firstChild().isNull(), false );
+ CHECK( textElement.lastChild().isNull(), false );
+ CHECK( textElement.previousSibling().isNull(), true );
+ CHECK( textElement.nextSibling().isNull(), true );
+ CHECK( textElement.localName(), QString("text") );
+
+ // the same <office:text>, but without namedItemNS
+ KoXmlElement text2Element;
+ text2Element = bodyElement.firstChild().toElement();
+ CHECK( text2Element.isNull(), false );
+ CHECK( text2Element.isElement(), true );
+ CHECK( text2Element.parentNode().isNull(), false );
+ CHECK( text2Element.parentNode()==bodyElement, true );
+ CHECK( text2Element.firstChild().isNull(), false );
+ CHECK( text2Element.lastChild().isNull(), false );
+ CHECK( text2Element.previousSibling().isNull(), true );
+ CHECK( text2Element.nextSibling().isNull(), true );
+ CHECK( text2Element.localName(), QString("text") );
+
+ // <text:p>
+ KoXmlElement parElement;
+ parElement = textElement.firstChild().toElement();
+ CHECK( parElement.isNull(), false );
+ CHECK( parElement.isElement(), true );
+ CHECK( parElement.parentNode().isNull(), false );
+ CHECK( parElement.parentNode()==textElement, true );
+ CHECK( parElement.firstChild().isNull(), false );
+ CHECK( parElement.lastChild().isNull(), false );
+ CHECK( parElement.previousSibling().isNull(), true );
+ CHECK( parElement.nextSibling().isNull(), true );
+ CHECK( parElement.tagName(), QString("p") );
+ CHECK( parElement.text(), QString("Hello, world!") );
+ CHECK( parElement.attributeNS( QString(textNS),"style-name",""), QString("Standard") );
+}
+
+void testSimpleOpenDocumentSpreadsheet()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+
+ // content.xml from a simple OpenDocument spreadsheet
+ // the document has three worksheets, the last two are empty.
+ // on the first sheet, cell A1 contains the text "Hello, world".
+ // automatic styles, font declarations and unnecessary namespaces are omitted.
+
+ xmlstream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ xmlstream << "<office:document-content ";
+ xmlstream << "xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"";
+ xmlstream << "xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" ";
+ xmlstream << "xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\">";
+ xmlstream << "<office:body>";
+ xmlstream << "<office:spreadsheet>";
+ xmlstream << "<table:table table:name=\"Sheet1\" table:style-name=\"ta1\" table:print=\"false\">";
+ xmlstream << "<table:table-column table:style-name=\"co1\" table:default-cell-style-name=\"Default\"/>";
+ xmlstream << "<table:table-row table:style-name=\"ro1\">";
+ xmlstream << "<table:table-cell office:value-type=\"string\">";
+ xmlstream << "<text:p>Hello, world</text:p>";
+ xmlstream << "</table:table-cell>";
+ xmlstream << "</table:table-row>";
+ xmlstream << "</table:table>";
+ xmlstream << "<table:table table:name=\"Sheet2\" table:style-name=\"ta1\" table:print=\"false\">";
+ xmlstream << "<table:table-column table:style-name=\"co1\" table:default-cell-style-name=\"Default\"/>";
+ xmlstream << "<table:table-row table:style-name=\"ro1\">";
+ xmlstream << "<table:table-cell/>";
+ xmlstream << "</table:table-row>";
+ xmlstream << "</table:table>";
+ xmlstream << "<table:table table:name=\"Sheet3\" table:style-name=\"ta1\" table:print=\"false\">";
+ xmlstream << "<table:table-column table:style-name=\"co1\" table:default-cell-style-name=\"Default\"/>";
+ xmlstream << "<table:table-row table:style-name=\"ro1\">";
+ xmlstream << "<table:table-cell/>";
+ xmlstream << "</table:table-row>";
+ xmlstream << "</table:table>";
+ xmlstream << "</office:spreadsheet>";
+ xmlstream << "</office:body>";
+ xmlstream << "</office:document-content>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent( &xmldevice, true, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ QString officeNS = "urn:oasis:names:tc:opendocument:xmlns:office:1.0";
+ QString tableNS = "urn:oasis:names:tc:opendocument:xmlns:table:1.0";
+ QString textNS = "urn:oasis:names:tc:opendocument:xmlns:text:1.0";
+
+ // <office:document-content>
+ KoXmlElement contentElement;
+ contentElement = doc.documentElement();
+ CHECK( contentElement.isNull(), false );
+ CHECK( contentElement.isElement(), true );
+ CHECK( contentElement.parentNode().isNull(), false );
+ CHECK( contentElement.parentNode().toDocument()==doc, true );
+ CHECK( contentElement.firstChild().isNull(), false );
+ CHECK( contentElement.lastChild().isNull(), false );
+ CHECK( contentElement.previousSibling().isNull(), false );
+ CHECK( contentElement.nextSibling().isNull(), true );
+ CHECK( contentElement.localName(), QString("document-content") );
+
+ // <office:body>
+ KoXmlElement bodyElement;
+ bodyElement = contentElement.firstChild().toElement();
+ CHECK( bodyElement.isNull(), false );
+ CHECK( bodyElement.isElement(), true );
+ CHECK( bodyElement.parentNode().isNull(), false );
+ CHECK( bodyElement.parentNode()==contentElement, true );
+ CHECK( bodyElement.firstChild().isNull(), false );
+ CHECK( bodyElement.lastChild().isNull(), false );
+ CHECK( bodyElement.previousSibling().isNull(), true );
+ CHECK( bodyElement.nextSibling().isNull(), true );
+ CHECK( bodyElement.localName(), QString("body") );
+
+ // <office:spreadsheet>
+ KoXmlElement spreadsheetElement;
+ spreadsheetElement = bodyElement.firstChild().toElement();
+ CHECK( spreadsheetElement.isNull(), false );
+ CHECK( spreadsheetElement.isElement(), true );
+ CHECK( spreadsheetElement.parentNode().isNull(), false );
+ CHECK( spreadsheetElement.parentNode()==bodyElement, true );
+ CHECK( spreadsheetElement.firstChild().isNull(), false );
+ CHECK( spreadsheetElement.lastChild().isNull(), false );
+ CHECK( spreadsheetElement.previousSibling().isNull(), true );
+ CHECK( spreadsheetElement.nextSibling().isNull(), true );
+ CHECK( spreadsheetElement.localName(), QString("spreadsheet") );
+
+ // <table:table> for Sheet1
+ KoXmlElement sheet1Element;
+ sheet1Element = spreadsheetElement.firstChild().toElement();
+ CHECK( sheet1Element.isNull(), false );
+ CHECK( sheet1Element.isElement(), true );
+ CHECK( sheet1Element.parentNode().isNull(), false );
+ CHECK( sheet1Element.parentNode()==spreadsheetElement, true );
+ CHECK( sheet1Element.firstChild().isNull(), false );
+ CHECK( sheet1Element.lastChild().isNull(), false );
+ CHECK( sheet1Element.previousSibling().isNull(), true );
+ CHECK( sheet1Element.nextSibling().isNull(), false );
+ CHECK( sheet1Element.tagName(), QString("table") );
+ CHECK( sheet1Element.hasAttributeNS(tableNS,"name"), true );
+ CHECK( sheet1Element.attributeNS(tableNS,"name",""), QString("Sheet1") );
+ CHECK( sheet1Element.attributeNS(tableNS,"style-name",""), QString("ta1") );
+ CHECK( sheet1Element.attributeNS(tableNS,"print",""), QString("false") );
+
+ KoXml::load( sheet1Element, 100 );
+
+ // <table:table-column>
+ KoXmlElement columnElement;
+ columnElement = sheet1Element.firstChild().toElement();
+ CHECK( columnElement.isNull(), false );
+ CHECK( columnElement.isElement(), true );
+ CHECK( columnElement.parentNode().isNull(), false );
+ CHECK( columnElement.parentNode()==sheet1Element, true );
+ CHECK( columnElement.firstChild().isNull(), true );
+ CHECK( columnElement.lastChild().isNull(), true );
+ CHECK( columnElement.previousSibling().isNull(), true );
+ CHECK( columnElement.nextSibling().isNull(), false );
+ CHECK( columnElement.tagName(), QString("table-column") );
+ CHECK( columnElement.attributeNS(tableNS,"style-name",""), QString("co1") );
+ CHECK( columnElement.attributeNS(tableNS,"default-cell-style-name",""), QString("Default") );
+
+ // <table:table-row>
+ KoXmlElement rowElement;
+ rowElement = columnElement.nextSibling().toElement();
+ CHECK( rowElement.isNull(), false );
+ CHECK( rowElement.isElement(), true );
+ CHECK( rowElement.parentNode().isNull(), false );
+ CHECK( rowElement.parentNode()==sheet1Element, true );
+ CHECK( rowElement.firstChild().isNull(), false );
+ CHECK( rowElement.lastChild().isNull(), false );
+ CHECK( rowElement.previousSibling().isNull(), false );
+ CHECK( rowElement.nextSibling().isNull(), true );
+ CHECK( rowElement.tagName(), QString("table-row") );
+ CHECK( rowElement.attributeNS(tableNS,"style-name",""), QString("ro1") );
+
+ // <table:table-cell>
+ KoXmlElement cellElement;
+ cellElement = rowElement.firstChild().toElement();
+ CHECK( cellElement.isNull(), false );
+ CHECK( cellElement.isElement(), true );
+ CHECK( cellElement.parentNode().isNull(), false );
+ CHECK( cellElement.parentNode()==rowElement, true );
+ CHECK( cellElement.firstChild().isNull(), false );
+ CHECK( cellElement.lastChild().isNull(), false );
+ CHECK( cellElement.previousSibling().isNull(), true );
+ CHECK( cellElement.nextSibling().isNull(), true );
+ CHECK( cellElement.tagName(), QString("table-cell") );
+ CHECK( cellElement.attributeNS(officeNS,"value-type",""), QString("string") );
+
+ // <text:p>
+ KoXmlElement parElement;
+ parElement = cellElement.firstChild().toElement();
+ CHECK( parElement.isNull(), false );
+ CHECK( parElement.isElement(), true );
+ CHECK( parElement.parentNode().isNull(), false );
+ CHECK( parElement.parentNode()==cellElement, true );
+ CHECK( parElement.firstChild().isNull(), false );
+ CHECK( parElement.lastChild().isNull(), false );
+ CHECK( parElement.previousSibling().isNull(), true );
+ CHECK( parElement.nextSibling().isNull(), true );
+ CHECK( parElement.tagName(), QString("p") );
+ CHECK( parElement.text(), QString("Hello, world") );
+
+ // <table:table> for Sheet2
+ KoXmlElement sheet2Element;
+ sheet2Element = sheet1Element.nextSibling().toElement();
+ CHECK( sheet2Element.isNull(), false );
+ CHECK( sheet2Element.isElement(), true );
+ CHECK( sheet2Element.parentNode().isNull(), false );
+ CHECK( sheet2Element.parentNode()==spreadsheetElement, true );
+ CHECK( sheet2Element.firstChild().isNull(), false );
+ CHECK( sheet2Element.lastChild().isNull(), false );
+ CHECK( sheet2Element.previousSibling().isNull(), false );
+ CHECK( sheet2Element.nextSibling().isNull(), false );
+ CHECK( sheet2Element.tagName(), QString("table") );
+
+ // </table:table> for Sheet3
+ KoXmlElement sheet3Element;
+ sheet3Element = sheet2Element.nextSibling().toElement();
+ CHECK( sheet3Element.isNull(), false );
+ CHECK( sheet3Element.isElement(), true );
+ CHECK( sheet3Element.parentNode().isNull(), false );
+ CHECK( sheet3Element.parentNode()==spreadsheetElement, true );
+ CHECK( sheet3Element.firstChild().isNull(), false );
+ CHECK( sheet3Element.lastChild().isNull(), false );
+ CHECK( sheet3Element.previousSibling().isNull(), false );
+ CHECK( sheet3Element.nextSibling().isNull(), true );
+ CHECK( sheet3Element.tagName(), QString("table") );
+}
+
+void testSimpleOpenDocumentPresentation()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+
+ // content.xml from a simple OpenDocument presentation
+ // styles, declarations and unnecessary namespaces are omitted
+ // the first page is "Title" and has two text boxes
+ // the second page is
+
+ xmlstream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ xmlstream << "<office:document-content ";
+ xmlstream << " xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" ";
+ xmlstream << " xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" ";
+ xmlstream << " xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\" ";
+ xmlstream << " xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\" ";
+ xmlstream << " xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\" ";
+ xmlstream << " office:version=\"1.0\">";
+ xmlstream << " <office:scripts/>";
+ xmlstream << " <office:automatic-styles/>";
+ xmlstream << " <office:body>";
+ xmlstream << " <office:presentation>";
+ xmlstream << " <draw:page draw:name=\"Title\" draw:style-name=\"dp1\" ";
+ xmlstream << " draw:master-page-name=\"lyt-cool\" ";
+ xmlstream << " presentation:presentation-page-layout-name=\"AL1T0\">";
+ xmlstream << " <draw:frame presentation:style-name=\"pr1\" ";
+ xmlstream << " draw:text-style-name=\"P2\" draw:layer=\"layout\" ";
+ xmlstream << " svg:width=\"23.912cm\" svg:height=\"3.508cm\" ";
+ xmlstream << " svg:x=\"2.058cm\" svg:y=\"1.543cm\" ";
+ xmlstream << " presentation:class=\"title\" ";
+ xmlstream << " presentation:user-transformed=\"true\">";
+ xmlstream << " <draw:text-box>";
+ xmlstream << " <text:p text:style-name=\"P1\">Foobar</text:p>";
+ xmlstream << " </draw:text-box>";
+ xmlstream << " </draw:frame>";
+ xmlstream << " <draw:frame presentation:style-name=\"pr2\" ";
+ xmlstream << " draw:text-style-name=\"P3\" draw:layer=\"layout\"";
+ xmlstream << " svg:width=\"23.912cm\" svg:height=\"13.231cm\"";
+ xmlstream << " svg:x=\"2.058cm\" svg:y=\"5.838cm\" ";
+ xmlstream << " presentation:class=\"subtitle\">";
+ xmlstream << " <draw:text-box>";
+ xmlstream << " <text:p text:style-name=\"P3\">Foo</text:p>";
+ xmlstream << " </draw:text-box>";
+ xmlstream << " </draw:frame>";
+ xmlstream << " <presentation:notes draw:style-name=\"dp2\">";
+ xmlstream << " <draw:page-thumbnail draw:style-name=\"gr1\" draw:layer=\"layout\" svg:width=\"13.706cm\" svg:height=\"10.28cm\" svg:x=\"3.647cm\" svg:y=\"2.853cm\" draw:page-number=\"1\" presentation:class=\"page\"/>";
+ xmlstream << " <draw:frame presentation:style-name=\"pr3\" draw:text-style-name=\"P1\" draw:layer=\"layout\" svg:width=\"14.518cm\" svg:height=\"11.411cm\" svg:x=\"3.249cm\" svg:y=\"14.13cm\" presentation:class=\"notes\" presentation:placeholder=\"true\">";
+ xmlstream << " <draw:text-box/>";
+ xmlstream << " </draw:frame>";
+ xmlstream << " </presentation:notes>";
+ xmlstream << " </draw:page>";
+ xmlstream << " <presentation:settings presentation:stay-on-top=\"true\"/>";
+ xmlstream << " </office:presentation>";
+ xmlstream << " </office:body>";
+ xmlstream << "</office:document-content>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent( &xmldevice, true, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ char* officeNS = "urn:oasis:names:tc:opendocument:xmlns:office:1.0";
+ char* drawNS = "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0";
+ char* textNS = "urn:oasis:names:tc:opendocument:xmlns:text:1.0";
+ char* presentationNS = "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0";
+ char* svgNS = "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0";
+
+ // <office:document-content>
+ KoXmlElement contentElement;
+ contentElement = doc.documentElement();
+ CHECK( contentElement.isNull(), false );
+
+ CHECK( contentElement.isElement(), true );
+ CHECK( contentElement.parentNode().isNull(), false );
+ CHECK( contentElement.parentNode().toDocument()==doc, true );
+ CHECK( contentElement.firstChild().isNull(), false );
+ CHECK( contentElement.lastChild().isNull(), false );
+ CHECK( contentElement.previousSibling().isNull(), false );
+
+ CHECK( contentElement.nextSibling().isNull(), true );
+ CHECK( contentElement.localName(), QString("document-content") );
+ CHECK( contentElement.hasAttributeNS(officeNS,"version"), true );
+ CHECK( contentElement.attributeNS(officeNS,"version",""), QString("1.0") );
+
+ // <office:scripts>
+ KoXmlElement scriptsElement;
+ scriptsElement = KoXml::namedItemNS( contentElement, officeNS, "scripts" );
+ CHECK( scriptsElement.isNull(), false );
+ CHECK( scriptsElement.isElement(), true );
+ CHECK( scriptsElement.parentNode().isNull(), false );
+ CHECK( scriptsElement.parentNode()==contentElement, true );
+ CHECK( scriptsElement.firstChild().isNull(), true );
+ CHECK( scriptsElement.lastChild().isNull(), true );
+ CHECK( scriptsElement.previousSibling().isNull(), true );
+ CHECK( scriptsElement.nextSibling().isNull(), false );
+ CHECK( scriptsElement.localName(), QString("scripts") );
+
+ // <office:automatic-styles>
+ KoXmlElement stylesElement;
+ stylesElement = KoXml::namedItemNS( contentElement, officeNS, "automatic-styles" );
+ CHECK( stylesElement.isNull(), false );
+ CHECK( stylesElement.isElement(), true );
+ CHECK( stylesElement.parentNode().isNull(), false );
+ CHECK( stylesElement.parentNode()==contentElement, true );
+ CHECK( stylesElement.firstChild().isNull(), true );
+ CHECK( stylesElement.lastChild().isNull(), true );
+ CHECK( stylesElement.previousSibling().isNull(), false );
+ CHECK( stylesElement.nextSibling().isNull(), false );
+ CHECK( stylesElement.localName(), QString("automatic-styles") );
+
+ // also same <office:automatic-styles>, but without namedItemNS
+ KoXmlNode styles2Element;
+ styles2Element = scriptsElement.nextSibling().toElement();
+ CHECK( styles2Element.isNull(), false );
+ CHECK( styles2Element.isElement(), true );
+ CHECK( styles2Element.parentNode().isNull(), false );
+ CHECK( styles2Element.parentNode()==contentElement, true );
+ CHECK( styles2Element.firstChild().isNull(), true );
+ CHECK( styles2Element.lastChild().isNull(), true );
+ CHECK( styles2Element.previousSibling().isNull(), false );
+ CHECK( styles2Element.nextSibling().isNull(), false );
+ CHECK( styles2Element.localName(), QString("automatic-styles") );
+
+ // <office:body>
+ KoXmlElement bodyElement;
+ bodyElement = KoXml::namedItemNS( contentElement, officeNS, "body" );
+ CHECK( bodyElement.isNull(), false );
+ CHECK( bodyElement.isElement(), true );
+ CHECK( bodyElement.parentNode().isNull(), false );
+ CHECK( bodyElement.parentNode()==contentElement, true );
+ CHECK( bodyElement.firstChild().isNull(), false );
+ CHECK( bodyElement.lastChild().isNull(), false );
+ CHECK( bodyElement.previousSibling().isNull(), false );
+ CHECK( bodyElement.nextSibling().isNull(), true );
+ CHECK( bodyElement.localName(), QString("body") );
+
+ // also same <office:body>, but without namedItemNS
+ KoXmlElement body2Element;
+ body2Element = stylesElement.nextSibling().toElement();
+ CHECK( body2Element.isNull(), false );
+ CHECK( body2Element.isElement(), true );
+ CHECK( body2Element.parentNode().isNull(), false );
+ CHECK( body2Element.parentNode()==contentElement, true );
+ CHECK( body2Element.firstChild().isNull(), false );
+ CHECK( body2Element.lastChild().isNull(), false );
+ CHECK( body2Element.previousSibling().isNull(), false );
+ CHECK( body2Element.nextSibling().isNull(), true );
+ CHECK( body2Element.localName(), QString("body") );
+
+ // <office:presentation>
+ KoXmlElement presentationElement;
+ presentationElement = KoXml::namedItemNS( bodyElement, officeNS, "presentation" );
+ CHECK( presentationElement.isNull(), false );
+ CHECK( presentationElement.isElement(), true );
+ CHECK( presentationElement.parentNode().isNull(), false );
+ CHECK( presentationElement.parentNode()==bodyElement, true );
+ CHECK( presentationElement.firstChild().isNull(), false );
+ CHECK( presentationElement.lastChild().isNull(), false );
+ CHECK( presentationElement.previousSibling().isNull(), true );
+ CHECK( presentationElement.nextSibling().isNull(), true );
+ CHECK( presentationElement.localName(), QString("presentation") );
+
+ // the same <office:presentation>, but without namedItemNS
+ KoXmlElement presentation2Element;
+ presentation2Element = bodyElement.firstChild().toElement();
+ CHECK( presentation2Element.isNull(), false );
+ CHECK( presentation2Element.isElement(), true );
+ CHECK( presentation2Element.parentNode().isNull(), false );
+ CHECK( presentation2Element.parentNode()==bodyElement, true );
+ CHECK( presentation2Element.firstChild().isNull(), false );
+ CHECK( presentation2Element.lastChild().isNull(), false );
+ CHECK( presentation2Element.previousSibling().isNull(), true );
+ CHECK( presentation2Element.nextSibling().isNull(), true );
+ CHECK( presentation2Element.localName(), QString("presentation") );
+
+ // <draw:page> for "Title"
+ KoXmlElement titlePageElement;
+ titlePageElement = presentationElement.firstChild().toElement();
+ CHECK( titlePageElement.isNull(), false );
+ CHECK( titlePageElement.isElement(), true );
+ CHECK( titlePageElement.parentNode().isNull(), false );
+ CHECK( titlePageElement.parentNode()==presentationElement, true );
+ CHECK( titlePageElement.firstChild().isNull(), false );
+ CHECK( titlePageElement.lastChild().isNull(), false );
+ CHECK( titlePageElement.previousSibling().isNull(), true );
+ CHECK( titlePageElement.nextSibling().isNull(), false );
+ CHECK( titlePageElement.localName(), QString("page") );
+ CHECK( titlePageElement.attributeNS(drawNS,"name",""), QString("Title") );
+ CHECK( titlePageElement.attributeNS(drawNS,"style-name",""), QString("dp1") );
+ CHECK( titlePageElement.attributeNS(drawNS,"master-page-name",""), QString("lyt-cool") );
+ CHECK( titlePageElement.attributeNS(presentationNS,
+ "presentation-page-layout-name",""), QString("AL1T0") );
+
+ // <draw:frame> for the title frame
+ KoXmlElement titleFrameElement;
+ titleFrameElement = titlePageElement.firstChild().toElement();
+ CHECK( titleFrameElement.isNull(), false );
+ CHECK( titleFrameElement.isElement(), true );
+ CHECK( titleFrameElement.parentNode().isNull(), false );
+ CHECK( titleFrameElement.parentNode()==titlePageElement, true );
+ CHECK( titleFrameElement.firstChild().isNull(), false );
+ CHECK( titleFrameElement.lastChild().isNull(), false );
+ CHECK( titleFrameElement.previousSibling().isNull(), true );
+ CHECK( titleFrameElement.nextSibling().isNull(), false );
+ CHECK( titleFrameElement.localName(), QString("frame") );
+ CHECK( titleFrameElement.attributeNS(presentationNS,"style-name",""), QString("pr1") );
+ CHECK( titleFrameElement.attributeNS(presentationNS,"class",""), QString("title") );
+ CHECK( titleFrameElement.attributeNS(presentationNS,"user-transformed",""), QString("true") );
+ CHECK( titleFrameElement.attributeNS(drawNS,"text-style-name",""), QString("P2") );
+ CHECK( titleFrameElement.attributeNS(drawNS,"layer",""), QString("layout") );
+ CHECK( titleFrameElement.attributeNS(svgNS,"width",""), QString("23.912cm") );
+ CHECK( titleFrameElement.attributeNS(svgNS,"height",""), QString("3.508cm") );
+ CHECK( titleFrameElement.attributeNS(svgNS,"x",""), QString("2.058cm") );
+ CHECK( titleFrameElement.attributeNS(svgNS,"y",""), QString("1.543cm") );
+
+ // <draw:text-box> of the title frame
+ KoXmlElement titleBoxElement;
+ titleBoxElement = titleFrameElement.firstChild().toElement();
+ CHECK( titleBoxElement.isNull(), false );
+ CHECK( titleBoxElement.isElement(), true );
+ CHECK( titleBoxElement.parentNode().isNull(), false );
+ CHECK( titleBoxElement.parentNode()==titleFrameElement, true );
+ CHECK( titleBoxElement.firstChild().isNull(), false );
+ CHECK( titleBoxElement.lastChild().isNull(), false );
+ CHECK( titleBoxElement.previousSibling().isNull(), true );
+ CHECK( titleBoxElement.nextSibling().isNull(), true );
+ CHECK( titleBoxElement.localName(), QString("text-box") );
+
+ // <text:p> for the title text-box
+ KoXmlElement titleParElement;
+ titleParElement = titleBoxElement.firstChild().toElement();
+ CHECK( titleParElement.isNull(), false );
+ CHECK( titleParElement.isElement(), true );
+ CHECK( titleParElement.parentNode().isNull(), false );
+ CHECK( titleParElement.parentNode()==titleBoxElement, true );
+ CHECK( titleParElement.firstChild().isNull(), false );
+ CHECK( titleParElement.lastChild().isNull(), false );
+ CHECK( titleParElement.previousSibling().isNull(), true );
+ CHECK( titleParElement.nextSibling().isNull(), true );
+ CHECK( titleParElement.localName(), QString("p") );
+ CHECK( titleParElement.attributeNS(textNS,"style-name",""), QString("P1") );
+ CHECK( titleParElement.text(), QString("Foobar") );
+
+ // <draw:frame> for the subtitle frame
+ KoXmlElement subtitleFrameElement;
+ subtitleFrameElement = titleFrameElement.nextSibling().toElement();
+ CHECK( subtitleFrameElement.isNull(), false );
+ CHECK( subtitleFrameElement.isElement(), true );
+ CHECK( subtitleFrameElement.parentNode().isNull(), false );
+ CHECK( subtitleFrameElement.parentNode()==titlePageElement, true );
+ CHECK( subtitleFrameElement.firstChild().isNull(), false );
+ CHECK( subtitleFrameElement.lastChild().isNull(), false );
+ CHECK( subtitleFrameElement.previousSibling().isNull(), false );
+ CHECK( subtitleFrameElement.nextSibling().isNull(), false );
+ CHECK( subtitleFrameElement.localName(), QString("frame") );
+ CHECK( subtitleFrameElement.attributeNS(presentationNS,"style-name",""), QString("pr2") );
+ CHECK( subtitleFrameElement.attributeNS(presentationNS,"class",""), QString("subtitle") );
+ CHECK( subtitleFrameElement.hasAttributeNS(presentationNS,"user-transformed"), false );
+ CHECK( subtitleFrameElement.attributeNS(drawNS,"text-style-name",""), QString("P3") );
+ CHECK( subtitleFrameElement.attributeNS(drawNS,"layer",""), QString("layout") );
+ CHECK( subtitleFrameElement.attributeNS(svgNS,"width",""), QString("23.912cm") );
+ CHECK( subtitleFrameElement.attributeNS(svgNS,"height",""), QString("13.231cm") );
+ CHECK( subtitleFrameElement.attributeNS(svgNS,"x",""), QString("2.058cm") );
+ CHECK( subtitleFrameElement.attributeNS(svgNS,"y",""), QString("5.838cm") );
+
+ // <draw:text-box> of the subtitle frame
+ KoXmlElement subtitleBoxElement;
+ subtitleBoxElement = subtitleFrameElement.firstChild().toElement();
+ CHECK( subtitleBoxElement.isNull(), false );
+ CHECK( subtitleBoxElement.isElement(), true );
+ CHECK( subtitleBoxElement.parentNode().isNull(), false );
+ CHECK( subtitleBoxElement.parentNode()==subtitleFrameElement, true );
+ CHECK( subtitleBoxElement.firstChild().isNull(), false );
+ CHECK( subtitleBoxElement.lastChild().isNull(), false );
+ CHECK( subtitleBoxElement.previousSibling().isNull(), true );
+ CHECK( subtitleBoxElement.nextSibling().isNull(), true );
+ CHECK( subtitleBoxElement.localName(), QString("text-box") );
+
+ // <text:p> for the subtitle text-box
+ KoXmlElement subtitleParElement;
+ subtitleParElement = subtitleBoxElement.firstChild().toElement();
+ CHECK( subtitleParElement.isNull(), false );
+ CHECK( subtitleParElement.isElement(), true );
+ CHECK( subtitleParElement.parentNode().isNull(), false );
+ CHECK( subtitleParElement.parentNode()==subtitleBoxElement, true );
+ CHECK( subtitleParElement.firstChild().isNull(), false );
+ CHECK( subtitleParElement.lastChild().isNull(), false );
+ CHECK( subtitleParElement.previousSibling().isNull(), true );
+ CHECK( subtitleParElement.nextSibling().isNull(), true );
+ CHECK( subtitleParElement.localName(), QString("p") );
+ CHECK( subtitleParElement.attributeNS(textNS,"style-name",""), QString("P3") );
+ CHECK( subtitleParElement.text(), QString("Foo") );
+}
+
+void testSimpleOpenDocumentFormula()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+
+ // content.xml from a simple OpenDocument formula
+ // this is essentially MathML
+ xmlstream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ xmlstream << "<!DOCTYPE math:math PUBLIC \"-//OpenOffice.org//DTD Modified W3C MathML 1.01//EN\" \"math.dtd\">";
+ xmlstream << "<math:math xmlns:math=\"http://www.w3.org/1998/Math/MathML\">";
+ xmlstream << " <math:semantics>";
+ xmlstream << " <math:mrow>";
+ xmlstream << " <math:mi>E</math:mi>";
+ xmlstream << " <math:mo math:stretchy=\"false\">=</math:mo>";
+ xmlstream << " <math:msup>";
+ xmlstream << " <math:mi math:fontstyle=\"italic\">mc</math:mi>";
+ xmlstream << " <math:mn>2</math:mn>";
+ xmlstream << " </math:msup>";
+ xmlstream << " </math:mrow>";
+ xmlstream << " <math:annotation math:encoding=\"StarMath 5.0\">E = mc^2 </math:annotation>";
+ xmlstream << " </math:semantics>";
+ xmlstream << "</math:math>";
+
+ KoXmlDocument doc;
+ CHECK( doc.setContent( &xmldevice, true, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ char* mathNS = "http://www.w3.org/1998/Math/MathML";
+
+ // <math:math>
+ KoXmlElement mathElement;
+ mathElement = doc.documentElement();
+ CHECK( mathElement.isNull(), false );
+ CHECK( mathElement.isElement(), true );
+ CHECK( mathElement.parentNode().isNull(), false );
+ CHECK( mathElement.parentNode().toDocument()==doc, true );
+ CHECK( mathElement.firstChild().isNull(), false );
+ CHECK( mathElement.lastChild().isNull(), false );
+ CHECK( mathElement.previousSibling().isNull(), false );
+ CHECK( mathElement.nextSibling().isNull(), true );
+ CHECK( mathElement.localName(), QString("math") );
+
+ // <math:semantics>
+ KoXmlElement semanticsElement;
+ semanticsElement = KoXml::namedItemNS( mathElement, mathNS, "semantics" );
+ CHECK( semanticsElement.isNull(), false );
+ CHECK( semanticsElement.isElement(), true );
+ CHECK( semanticsElement.parentNode().isNull(), false );
+ CHECK( semanticsElement.parentNode().toElement()==mathElement, true );
+ CHECK( semanticsElement.firstChild().isNull(), false );
+ CHECK( semanticsElement.lastChild().isNull(), false );
+ CHECK( semanticsElement.previousSibling().isNull(), true );
+ CHECK( semanticsElement.nextSibling().isNull(), true );
+ CHECK( semanticsElement.localName(), QString("semantics") );
+
+ // the same <math:semantics> but without namedItemNS
+ KoXmlElement semantics2Element;
+ semantics2Element = mathElement.firstChild().toElement();
+ CHECK( semantics2Element.isNull(), false );
+ CHECK( semantics2Element.isElement(), true );
+ CHECK( semantics2Element.parentNode().isNull(), false );
+ CHECK( semantics2Element.parentNode().toElement()==mathElement, true );
+ CHECK( semantics2Element.firstChild().isNull(), false );
+ CHECK( semantics2Element.lastChild().isNull(), false );
+ CHECK( semantics2Element.previousSibling().isNull(), true );
+ CHECK( semantics2Element.nextSibling().isNull(), true );
+ CHECK( semantics2Element.localName(), QString("semantics") );
+
+ // <math:mrow>
+ KoXmlElement mrowElement;
+ mrowElement = semanticsElement.firstChild().toElement();
+ CHECK( mrowElement.isNull(), false );
+ CHECK( mrowElement.isElement(), true );
+ CHECK( mrowElement.parentNode().isNull(), false );
+ CHECK( mrowElement.parentNode().toElement()==semanticsElement, true );
+ CHECK( mrowElement.firstChild().isNull(), false );
+ CHECK( mrowElement.lastChild().isNull(), false );
+ CHECK( mrowElement.previousSibling().isNull(), true );
+ CHECK( mrowElement.nextSibling().isNull(), false );
+ CHECK( mrowElement.localName(), QString("mrow") );
+
+ // <math:mi> for "E"
+ KoXmlElement miElement;
+ miElement = mrowElement.firstChild().toElement();
+ CHECK( miElement.isNull(), false );
+ CHECK( miElement.isElement(), true );
+ CHECK( miElement.parentNode().isNull(), false );
+ CHECK( miElement.parentNode().toElement()==mrowElement, true );
+ CHECK( miElement.firstChild().isNull(), false );
+ CHECK( miElement.lastChild().isNull(), false );
+ CHECK( miElement.previousSibling().isNull(), true );
+ CHECK( miElement.nextSibling().isNull(), false );
+ CHECK( miElement.localName(), QString("mi") );
+
+ // <math:mo> for "="
+ KoXmlElement moElement;
+ moElement = miElement.nextSibling().toElement();
+ CHECK( moElement.isNull(), false );
+ CHECK( moElement.isElement(), true );
+ CHECK( moElement.parentNode().isNull(), false );
+ CHECK( moElement.parentNode().toElement()==mrowElement, true );
+ CHECK( moElement.firstChild().isNull(), false );
+ CHECK( moElement.lastChild().isNull(), false );
+ CHECK( moElement.previousSibling().isNull(), false );
+ CHECK( moElement.nextSibling().isNull(), false );
+ CHECK( moElement.localName(), QString("mo") );
+ CHECK( moElement.attributeNS(mathNS,"stretchy",""), QString("false") );
+
+ // <math:msup> for "mc" and superscripted "2"
+ KoXmlElement msupElement;
+ msupElement = moElement.nextSibling().toElement();
+ CHECK( msupElement.isNull(), false );
+ CHECK( msupElement.isElement(), true );
+ CHECK( msupElement.parentNode().isNull(), false );
+ CHECK( msupElement.parentNode().toElement()==mrowElement, true );
+ CHECK( msupElement.firstChild().isNull(), false );
+ CHECK( msupElement.lastChild().isNull(), false );
+ CHECK( msupElement.previousSibling().isNull(), false );
+ CHECK( msupElement.nextSibling().isNull(), true );
+ CHECK( msupElement.localName(), QString("msup") );
+
+ // <math:mi> inside the <math:msup> for "mc"
+ KoXmlElement mcElement;
+ mcElement = msupElement.firstChild().toElement();
+ CHECK( mcElement.isNull(), false );
+ CHECK( mcElement.isElement(), true );
+ CHECK( mcElement.parentNode().isNull(), false );
+ CHECK( mcElement.parentNode().toElement()==msupElement, true );
+ CHECK( mcElement.firstChild().isNull(), false );
+ CHECK( mcElement.lastChild().isNull(), false );
+ CHECK( mcElement.previousSibling().isNull(), true );
+ CHECK( mcElement.nextSibling().isNull(), false );
+ CHECK( mcElement.localName(), QString("mi") );
+ CHECK( mcElement.text(), QString("mc") );
+ CHECK( mcElement.attributeNS(mathNS,"fontstyle",""), QString("italic") );
+
+ // <math:mn> inside the <math:msup> for "2" (superscript)
+ KoXmlElement mnElement;
+ mnElement = mcElement.nextSibling().toElement();
+ CHECK( mnElement.isNull(), false );
+ CHECK( mnElement.isElement(), true );
+ CHECK( mnElement.parentNode().isNull(), false );
+ CHECK( mnElement.parentNode().toElement()==msupElement, true );
+ CHECK( mnElement.firstChild().isNull(), false );
+ CHECK( mnElement.lastChild().isNull(), false );
+ CHECK( mnElement.previousSibling().isNull(), false );
+ CHECK( mnElement.nextSibling().isNull(), true );
+ CHECK( mnElement.localName(), QString("mn") );
+ CHECK( mnElement.text(), QString("2") );
+
+ // <math:annotation>
+ KoXmlElement annotationElement;
+ annotationElement = semanticsElement.lastChild().toElement();
+ CHECK( annotationElement.isNull(), false );
+ CHECK( annotationElement.isElement(), true );
+ CHECK( annotationElement.parentNode().isNull(), false );
+ CHECK( annotationElement.parentNode().toElement()==semanticsElement, true );
+ CHECK( annotationElement.firstChild().isNull(), false );
+ CHECK( annotationElement.lastChild().isNull(), false );
+ CHECK( annotationElement.previousSibling().isNull(), false );
+ CHECK( annotationElement.nextSibling().isNull(), true );
+ CHECK( annotationElement.localName(), QString("annotation") );
+ CHECK( annotationElement.text(), QString("E = mc^2 ") );
+ CHECK( annotationElement.attributeNS(mathNS,"encoding",""), QString("StarMath 5.0") );
+}
+
+void testLargeOpenDocumentSpreadsheet()
+{
+ QString errorMsg;
+ int errorLine = 0;
+ int errorColumn = 0;
+
+ int sheetCount = 4;
+ int rowCount = 200;
+ int colCount = 200*2;
+
+ QByteArray xmlbuf;
+ QBuffer xmldevice( xmlbuf );
+ QTextStream xmlstream( xmlbuf, IO_WriteOnly );
+
+ // content.xml
+ xmlstream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ xmlstream << "<office:document-content ";
+ xmlstream << "xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" ";
+ xmlstream << "xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" ";
+ xmlstream << "xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\" >";
+ xmlstream << "<office:body>";
+ xmlstream << "<office:spreadsheet>";
+ for( int i = 0; i < sheetCount; i++ )
+ {
+ QString sheetName = QString("Sheet%1").arg(i+1);
+ xmlstream << "<table:table table:name=\"" << sheetName;
+ xmlstream << "\" table:print=\"false\">";
+ for( int j = 0; j < rowCount; j++ )
+ {
+ xmlstream << "<table:table-row>";
+ for( int k = 0; k < colCount; k++ )
+ {
+ xmlstream << "<table:table-cell office:value-type=\"string\">";
+ xmlstream << "<text:p>Hello, world</text:p>";
+ xmlstream << "</table:table-cell>";
+ }
+ xmlstream << "</table:table-row>";
+ }
+ xmlstream << "</table:table>";
+ }
+ xmlstream << "</office:spreadsheet>";
+ xmlstream << "</office:body>";
+ xmlstream << "</office:document-content>";
+
+ printf("Raw XML size: %d KB\n", xmlbuf.size()/1024 );
+
+ QTime timer;
+ timer.start();
+
+#if 1
+ // just to test parsing speed with plain dumb handler
+ QXmlSimpleReader* reader = new QXmlSimpleReader;
+ reader->setFeature( "http://xml.org/sax/features/namespaces", true );
+ QXmlDefaultHandler handler;
+ reader->setContentHandler( &handler );
+ reader->setErrorHandler( &handler );
+ reader->setLexicalHandler( &handler );
+ reader->setDeclHandler( &handler );
+ reader->setDTDHandler( &handler );
+ QXmlInputSource xmlSource;
+ xmlSource.setData( QString::fromUtf8( xmlbuf.data(), xmlbuf.size() ) );
+ timer.start();
+ reader->parse( &xmlSource );
+ printf("Large spreadsheet: QXmlDefaultHandler parsing time is %d ms\n", timer.elapsed() );
+ delete reader;
+#endif
+
+ timer.start();
+ KoXmlDocument doc;
+
+ // uncomment to see the performance if on-demand/lazy loading is not used
+ // will definitely eat more memory
+#ifndef KOXML_USE_QDOM
+ doc.setFastLoading( true );
+#endif
+
+ CHECK( doc.setContent( &xmldevice, true, &errorMsg, &errorLine, &errorColumn ), true );
+ CHECK( errorMsg.isEmpty(), true );
+ CHECK( errorLine, 0 );
+ CHECK( errorColumn, 0 );
+
+ printf("Large spreadsheet: parsing time is %d ms\n", timer.elapsed() );
+
+ // release memory taken by the XML document content
+ xmlbuf.resize( 0 );
+
+ // namespaces that will be used
+ QString officeNS = "urn:oasis:names:tc:opendocument:xmlns:office:1.0";
+ QString tableNS = "urn:oasis:names:tc:opendocument:xmlns:table:1.0";
+ QString textNS = "urn:oasis:names:tc:opendocument:xmlns:text:1.0";
+
+ // <office:document-content>
+ KoXmlElement contentElement;
+ contentElement = doc.documentElement();
+ CHECK( contentElement.isNull(), false );
+ CHECK( contentElement.isElement(), true );
+ CHECK( contentElement.localName(), QString("document-content") );
+
+ // <office:body>
+ KoXmlElement bodyElement;
+ bodyElement = contentElement.firstChild().toElement();
+ CHECK( bodyElement.isNull(), false );
+ CHECK( bodyElement.isElement(), true );
+ CHECK( bodyElement.localName(), QString("body") );
+
+ // <office:spreadsheet>
+ KoXmlElement spreadsheetElement;
+ spreadsheetElement = bodyElement.firstChild().toElement();
+ CHECK( spreadsheetElement.isNull(), false );
+ CHECK( spreadsheetElement.isElement(), true );
+ CHECK( spreadsheetElement.localName(), QString("spreadsheet") );
+
+ // now we visit every sheet, every row, every cell
+ timer.start();
+ KoXmlElement tableElement;
+ tableElement = spreadsheetElement.firstChild().toElement();
+ for( int table=0; table < sheetCount; table++ )
+ {
+ QString tableName = QString("Sheet%1").arg(table+1);
+ CHECK( tableElement.isNull(), false );
+ CHECK( tableElement.isElement(), true );
+ CHECK( tableElement.localName(), QString("table") );
+ CHECK( tableElement.hasAttributeNS(tableNS,"name"), true );
+ CHECK( tableElement.attributeNS(tableNS,"name",""), tableName );
+ CHECK( tableElement.attributeNS(tableNS,"print",""), QString("false") );
+
+ // load everything for this table
+ KoXml::load( tableElement, 99 );
+
+ CHECK( tableElement.parentNode().isNull(), false );
+ CHECK( tableElement.parentNode()==spreadsheetElement, true );
+ CHECK( tableElement.firstChild().isNull(), false );
+ CHECK( tableElement.lastChild().isNull(), false );
+
+ KoXmlElement rowElement;
+ rowElement = tableElement.firstChild().toElement();
+ for( int row=0; row < rowCount; row++ )
+ {
+ CHECK( rowElement.isNull(), false );
+ CHECK( rowElement.isElement(), true );
+ CHECK( rowElement.localName(), QString("table-row") );
+ CHECK( rowElement.parentNode().isNull(), false );
+ CHECK( rowElement.parentNode()==tableElement, true );
+ CHECK( rowElement.firstChild().isNull(), false );
+ CHECK( rowElement.lastChild().isNull(), false );
+
+ KoXmlElement cellElement;
+ cellElement = rowElement.firstChild().toElement();
+ for( int col=0; col < colCount; col++ )
+ {
+ CHECK( cellElement.isNull(), false );
+ CHECK( cellElement.isElement(), true );
+ CHECK( cellElement.localName(), QString("table-cell") );
+ CHECK( cellElement.text(), QString("Hello, world") );
+ CHECK( cellElement.hasAttributeNS(officeNS,"value-type"), true );
+ CHECK( cellElement.attributeNS(officeNS,"value-type",""), QString("string" ) );
+ CHECK( cellElement.parentNode().isNull(), false );
+ CHECK( cellElement.parentNode()==rowElement, true );
+ CHECK( cellElement.firstChild().isNull(), false );
+ CHECK( cellElement.lastChild().isNull(), false );
+ cellElement = cellElement.nextSibling().toElement();
+ }
+
+ //KoXml::unload( rowElement );
+ rowElement = rowElement.nextSibling().toElement();
+ }
+
+ KoXml::unload( tableElement );
+ tableElement = tableElement.nextSibling().toElement();
+ }
+
+ printf("Large spreadsheet: iterating time is %d ms\n", timer.elapsed() );
+}
+
+int main( int argc, char** argv )
+{
+ Q_UNUSED( argc );
+ Q_UNUSED( argv );
+
+ testNode();
+ testElement();
+ testAttributes();
+ testText();
+ testCDATA();
+ testDocument();
+ testNamespace();
+
+ testUnload();
+
+ testSimpleXML();
+ testRootError();
+ testMismatchedTag();
+
+ testSimpleOpenDocumentText();
+ testSimpleOpenDocumentSpreadsheet();
+ testSimpleOpenDocumentPresentation();
+ testSimpleOpenDocumentFormula();
+
+// testLargeOpenDocumentSpreadsheet();
+
+#ifdef KOXML_USE_QDOM
+ printf("Using QDom: ");
+#else
+ printf("Using KoXml: ");
+#endif
+ printf("%d tests, %d failed\n", testCount, testFailed );
+
+ return testFailed;
+}
diff --git a/lib/kofficecore/tests/priorityqueue_test.cpp b/lib/kofficecore/tests/priorityqueue_test.cpp
new file mode 100644
index 00000000..d2c7eafd
--- /dev/null
+++ b/lib/kofficecore/tests/priorityqueue_test.cpp
@@ -0,0 +1,87 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Werner Trobin <trobin@kde.org>
+
+ 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.
+*/
+
+#include <priorityqueue.h>
+#include <kdebug.h>
+#include <qptrlist.h>
+#include <qasciidict.h>
+#include <stdlib.h>
+#include <time.h>
+
+struct Node {
+ Node( unsigned int key ) : m_key( key ), m_index( 0 ) {}
+
+ unsigned int key() const { return m_key; }
+ void setKey( unsigned int key ) { m_key = key; }
+
+ int index() const { return m_index; }
+ void setIndex( int i ) { m_index = i; }
+private:
+ unsigned int m_key;
+ int m_index;
+};
+
+static const char* const keys[] = { "one", "two", "three", "four", "five",
+ "six", "seven", "eight", "nine", "ten",
+ "eleven", "twelve", 0 };
+
+int main( int /*argc*/, char **/*argv*/ )
+{
+ QPtrList<Node> list;
+ list.setAutoDelete( true );
+ QAsciiDict<Node> dict;
+
+ KOffice::PriorityQueue<Node> queue;
+
+ srand( time( 0 ) );
+ for ( int i = 0; i < 12; ++i ) {
+ Node *n = new Node( rand() % 20 );
+ list.append( n );
+ queue.insert( n );
+ // Check whether the AsciiDict CTOR is okay
+ Node *n2 = new Node( *n );
+ dict.insert( keys[ i ], n2 );
+ }
+
+ kdDebug() << "##### Queue 1: " << endl;
+ queue.dump();
+
+ kdDebug() << "##### Queue 2: " << endl;
+ KOffice::PriorityQueue<Node> queue2( dict );
+ queue2.dump();
+
+ Node *n = list.at( 6 );
+ kdDebug() << "##### Decreasing node: " << n->key() << " at " << n->index() << endl;
+ n->setKey( 2 );
+ queue.keyDecreased( n );
+ queue.dump();
+
+ n = list.at( 2 );
+ kdDebug() << "##### Decreasing node: " << n->key() << " at " << n->index() << endl;
+ n->setKey( 0 );
+ queue.keyDecreased( n );
+ queue.dump();
+
+ n = queue.extractMinimum();
+ while ( n ) {
+ queue.dump();
+ n = queue.extractMinimum();
+ }
+ return 0;
+}