src/qt3support/other/q3mimefactory.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt3Support module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "q3mimefactory.h"
       
    43 
       
    44 #ifndef QT_NO_MIMEFACTORY
       
    45 
       
    46 #include "qmap.h"
       
    47 #include "qmime.h"
       
    48 #include "qstringlist.h"
       
    49 #include "qfileinfo.h"
       
    50 #include "qdir.h"
       
    51 #include "q3dragobject.h"
       
    52 #include "qpixmap.h"
       
    53 #include "qimagereader.h"
       
    54 #include "q3cleanuphandler.h"
       
    55 #include "private/qtextimagehandler_p.h"
       
    56 
       
    57 QT_BEGIN_NAMESPACE
       
    58 
       
    59 static Q3MimeSourceFactory* defaultfactory = 0;
       
    60 static Q3SingleCleanupHandler<Q3MimeSourceFactory> qmime_cleanup_factory;
       
    61 
       
    62 class Q3MimeSourceFactoryData {
       
    63 public:
       
    64     Q3MimeSourceFactoryData() :
       
    65         last(0)
       
    66     {
       
    67     }
       
    68 
       
    69     ~Q3MimeSourceFactoryData()
       
    70     {
       
    71         QMap<QString, QMimeSource*>::Iterator it = stored.begin();
       
    72         while (it != stored.end()) {
       
    73             delete *it;
       
    74             ++it;
       
    75         }
       
    76         delete last;
       
    77     }
       
    78 
       
    79     QMap<QString, QMimeSource*> stored;
       
    80     QMap<QString, QString> extensions;
       
    81     QStringList path;
       
    82     QMimeSource* last;
       
    83     QList<Q3MimeSourceFactory*> factories;
       
    84 };
       
    85 
       
    86 static QImage richTextImageLoader(const QString &name, const QString &context)
       
    87 {
       
    88     QImage img;
       
    89 
       
    90     const QMimeSource *src = Q3MimeSourceFactory::defaultFactory()->data(name, context);
       
    91     if (src && Q3ImageDrag::decode(src, img))
       
    92         return img;
       
    93 
       
    94     return QImage();
       
    95 }
       
    96 
       
    97 /*!
       
    98     \class Q3MimeSourceFactory
       
    99     \brief The Q3MimeSourceFactory class is an extensible provider of mime-typed data.
       
   100 
       
   101     \compat
       
   102 
       
   103     A Q3MimeSourceFactory provides an abstract interface to a
       
   104     collection of information. Each piece of information is
       
   105     represented by a QMimeSource object which can be examined and
       
   106     converted to concrete data types by functions such as
       
   107     Q3ImageDrag::canDecode() and Q3ImageDrag::decode().
       
   108 
       
   109     The base Q3MimeSourceFactory can be used in two ways: as an
       
   110     abstraction of a collection of files or as specifically stored
       
   111     data. For it to access files, call setFilePath() before accessing
       
   112     data. For stored data, call setData() for each item (there are
       
   113     also convenience functions, e.g. setText(), setImage() and
       
   114     setPixmap(), that simply call setData() with appropriate
       
   115     parameters).
       
   116 
       
   117     The rich text widgets, QTextEdit and QTextBrowser, use
       
   118     Q3MimeSourceFactory to resolve references such as images or links
       
   119     within rich text documents. They either access the default factory
       
   120     (see \l{defaultFactory()}) or their own. Other classes that are
       
   121     capable of displaying rich text (such as QLabel, QWhatsThis or
       
   122     QMessageBox) always use the default factory.
       
   123 
       
   124     A factory can also be used as a container to store data associated
       
   125     with a name. This technique is useful whenever rich text contains
       
   126     images that are stored in the program itself, not loaded from the
       
   127     hard disk. Your program may, for example, define some image data
       
   128     as:
       
   129     \snippet doc/src/snippets/code/src_qt3support_other_q3mimefactory.cpp 0
       
   130 
       
   131     To be able to use this image within some rich text, for example
       
   132     inside a QLabel, you must create a QImage from the raw data and
       
   133     insert it into the factory with a unique name:
       
   134     \snippet doc/src/snippets/code/src_qt3support_other_q3mimefactory.cpp 1
       
   135 
       
   136     Now you can create a rich text QLabel with
       
   137 
       
   138     \snippet doc/src/snippets/code/src_qt3support_other_q3mimefactory.cpp 2
       
   139 
       
   140     When no longer needed, you can clear the data from the factory:
       
   141 
       
   142     \snippet doc/src/snippets/code/src_qt3support_other_q3mimefactory.cpp 3
       
   143 */
       
   144 
       
   145 
       
   146 /*!
       
   147     Constructs a Q3MimeSourceFactory that has no file path and no
       
   148     stored content.
       
   149 */
       
   150 Q3MimeSourceFactory::Q3MimeSourceFactory() :
       
   151     d(new Q3MimeSourceFactoryData)
       
   152 {
       
   153     addFilePath(QLatin1String(":/qt/q3mimesourcefactory/")); //to get from the resources
       
   154     // add some reasonable defaults
       
   155     setExtensionType(QLatin1String("htm"), "text/html;charset=iso8859-1");
       
   156     setExtensionType(QLatin1String("html"), "text/html;charset=iso8859-1");
       
   157     setExtensionType(QLatin1String("txt"), "text/plain");
       
   158     setExtensionType(QLatin1String("xml"), "text/xml;charset=UTF-8");
       
   159     setExtensionType(QLatin1String("jpg"), "image/jpeg"); // support misspelled jpeg files
       
   160 }
       
   161 
       
   162 /*!
       
   163     Destroys the Q3MimeSourceFactory, deleting all stored content.
       
   164 */
       
   165 Q3MimeSourceFactory::~Q3MimeSourceFactory()
       
   166 {
       
   167     if (defaultFactory() == this)
       
   168         defaultfactory = 0;
       
   169     delete d;
       
   170 }
       
   171 
       
   172 QMimeSource* Q3MimeSourceFactory::dataInternal(const QString& abs_name, const QMap<QString, QString> &extensions) const
       
   173 {
       
   174     QMimeSource* r = 0;
       
   175     QStringList attempted_names(abs_name);
       
   176     QFileInfo fi(abs_name);
       
   177     if (fi.isReadable()) {
       
   178         // get the right mimetype
       
   179         QString e = fi.extension(false);
       
   180         QByteArray mimetype("application/octet-stream");
       
   181         if (extensions.contains(e))
       
   182             mimetype = extensions[e].latin1();
       
   183         if (!QImageReader::imageFormat(abs_name).isEmpty())
       
   184             mimetype = "application/x-qt-image";
       
   185 
       
   186         QFile f(abs_name);
       
   187         if (f.open(QIODevice::ReadOnly) && f.size()) {
       
   188             QByteArray ba;
       
   189             ba.resize(f.size());
       
   190             f.readBlock(ba.data(), ba.size());
       
   191             Q3StoredDrag* sr = new Q3StoredDrag(mimetype);
       
   192             sr->setEncodedData(ba);
       
   193             delete d->last;
       
   194             d->last = r = sr;
       
   195         }
       
   196     }
       
   197 
       
   198     // we didn't find the mime-source, so ask the default factory for
       
   199     // the mime-source (this one will iterate over all installed ones)
       
   200     //
       
   201     // this looks dangerous, as this dataInternal() function will be
       
   202     // called again when the default factory loops over all installed
       
   203     // factories (including this), but the static bool looping in
       
   204     // data() avoids endless recursions
       
   205     if (!r && this != defaultFactory())
       
   206         r = (QMimeSource*)defaultFactory()->data(abs_name);
       
   207 
       
   208     return r;
       
   209 }
       
   210 
       
   211 
       
   212 /*!
       
   213     Returns a reference to the data associated with \a abs_name. The
       
   214     return value remains valid only until the next data() or setData()
       
   215     call, so you should immediately decode the result.
       
   216 
       
   217     If there is no data associated with \a abs_name in the factory's
       
   218     store, the factory tries to access the local filesystem. If \a
       
   219     abs_name isn't an absolute file name, the factory will search for
       
   220     it in all defined paths (see \l{setFilePath()}).
       
   221 
       
   222     The factory understands all the image formats supported by
       
   223     QImageReader. Any other mime types are determined by the file name
       
   224     extension. The default settings are
       
   225     \snippet doc/src/snippets/code/src_qt3support_other_q3mimefactory.cpp 4
       
   226     The effect of these is that file names ending in "txt" will be
       
   227     treated as text encoded in the local encoding; those ending in
       
   228     "xml" will be treated as text encoded in Unicode UTF-8 encoding.
       
   229     The text/html type is treated specially, since the encoding can be
       
   230     specified in the html file itself. "html" or "htm" will be treated
       
   231     as text encoded in the encoding specified by the html meta tag, if
       
   232     none could be found, the charset of the mime type will be used.
       
   233     The text subtype ("html", "plain", or "xml") does not affect the
       
   234     factory, but users of the factory may behave differently. We
       
   235     recommend creating "xml" files where practical. These files can be
       
   236     viewed regardless of the runtime encoding and can encode any
       
   237     Unicode characters without resorting to encoding definitions
       
   238     inside the file.
       
   239 
       
   240     Any file data that is not recognized will be retrieved as a
       
   241     QMimeSource providing the "application/octet-stream" mime type,
       
   242     meaning uninterpreted binary data.
       
   243 
       
   244     You can add further extensions or change existing ones with
       
   245     subsequent calls to setExtensionType(). If the extension mechanism
       
   246     is not sufficient for your problem domain, you can inherit
       
   247     Q3MimeSourceFactory and reimplement this function to perform some
       
   248     more specialized mime-type detection. The same applies if you want
       
   249     to use the mime source factory to access URL referenced data over
       
   250     a network.
       
   251 */
       
   252 const QMimeSource *Q3MimeSourceFactory::data(const QString& abs_name) const
       
   253 {
       
   254     if (d->stored.contains(abs_name))
       
   255         return d->stored[abs_name];
       
   256 
       
   257     const QMimeSource *r = 0;
       
   258     if (abs_name.isEmpty())
       
   259         return r;
       
   260     QStringList::Iterator it;
       
   261     if (abs_name[0] == QLatin1Char('/')
       
   262 #ifdef Q_WS_WIN
       
   263             || (abs_name[0].isLetter() && abs_name[1] == QLatin1Char(':')) || abs_name.startsWith(QLatin1String("\\\\"))
       
   264 #endif
       
   265    )
       
   266     {
       
   267         // handle absolute file names directly
       
   268         r = dataInternal(abs_name, d->extensions);
       
   269     }
       
   270     else { // check list of paths
       
   271         for (it = d->path.begin(); !r && it != d->path.end(); ++it) {
       
   272             QString filename = *it;
       
   273             if (filename[(int)filename.length()-1] != QLatin1Char('/'))
       
   274                 filename += QLatin1Char('/');
       
   275             filename += abs_name;
       
   276             r = dataInternal(filename, d->extensions);
       
   277         }
       
   278     }
       
   279 
       
   280     static bool looping = false;
       
   281     if (!r && this == defaultFactory()) {
       
   282         // we found no mime-source and we are the default factory, so
       
   283         // we know all the other installed mime-source factories, so
       
   284         // ask them
       
   285         if (!looping) {
       
   286             // to avoid endless recustions, don't enter the loop below
       
   287             // if data() got called from within the loop below
       
   288             looping = true;
       
   289             for (int i = 0; i < d->factories.size(); ++i) {
       
   290                 const Q3MimeSourceFactory *f = d->factories.at(i);
       
   291                 if (f == this)
       
   292                     continue;
       
   293                 r = static_cast<const QMimeSource *>(f->data(abs_name));
       
   294                 if (r) {
       
   295                     looping = false;
       
   296                     return r;
       
   297                 }
       
   298             }
       
   299             looping = false;
       
   300         }
       
   301     } else if (!r) {
       
   302         // we are not the default mime-source factory, so ask the
       
   303         // default one for the mime-source, as this one will loop over
       
   304         // all installed mime-source factories and ask these
       
   305         r = static_cast<const QMimeSource *>(defaultFactory()->data(abs_name));
       
   306     }
       
   307     return r;
       
   308 }
       
   309 
       
   310 /*!
       
   311     \fn void Q3MimeSourceFactory::setFilePath(const QStringList &path)
       
   312     \fn void Q3MimeSourceFactory::setFilePath(const QString &path)
       
   313 
       
   314     Sets the list of directories that will be searched when named data
       
   315     is requested to those given in the string list \a path.
       
   316 
       
   317     \sa filePath()
       
   318 */
       
   319 void Q3MimeSourceFactory::setFilePath(const QStringList& path)
       
   320 {
       
   321     d->path = path;
       
   322 }
       
   323 
       
   324 /*!
       
   325     Returns the currently set search paths.
       
   326 */
       
   327 QStringList Q3MimeSourceFactory::filePath() const
       
   328 {
       
   329     return d->path;
       
   330 }
       
   331 
       
   332 /*!
       
   333     Adds another search path, \a p to the existing search paths.
       
   334 
       
   335     \sa setFilePath()
       
   336 */
       
   337 void Q3MimeSourceFactory::addFilePath(const QString& p)
       
   338 {
       
   339     d->path += p;
       
   340 }
       
   341 
       
   342 /*!
       
   343     Sets the mime-type to be associated with the file name extension,
       
   344     \a ext to \a mimetype. This determines the mime-type for files
       
   345     found via the paths set by setFilePath().
       
   346 */
       
   347 void Q3MimeSourceFactory::setExtensionType(const QString& ext, const char* mimetype)
       
   348 {
       
   349     d->extensions.insert(ext, QLatin1String(mimetype));
       
   350 }
       
   351 
       
   352 /*!
       
   353     Converts the absolute or relative data item name \a
       
   354     abs_or_rel_name to an absolute name, interpreted within the
       
   355     context (path) of the data item named \a context (this must be an
       
   356     absolute name).
       
   357 */
       
   358 QString Q3MimeSourceFactory::makeAbsolute(const QString& abs_or_rel_name, const QString& context) const
       
   359 {
       
   360     if (context.isNull() ||
       
   361          !(context[0] == QLatin1Char('/')
       
   362 #ifdef Q_WS_WIN
       
   363          || (context[0].isLetter() && context[1] == QLatin1Char(':'))
       
   364 #endif
       
   365           ))
       
   366         return abs_or_rel_name;
       
   367     if (abs_or_rel_name.isEmpty())
       
   368         return context;
       
   369     QFileInfo c(context);
       
   370     if (!c.isDir()) {
       
   371         QFileInfo r(c.dir(true), abs_or_rel_name);
       
   372         return r.absFilePath();
       
   373     } else {
       
   374         QDir d(context);
       
   375         QFileInfo r(d, abs_or_rel_name);
       
   376         return r.absFilePath();
       
   377     }
       
   378 }
       
   379 
       
   380 /*!
       
   381     \overload
       
   382     A convenience function. See data(const QString& abs_name). The
       
   383     file name is given in \a abs_or_rel_name and the path is in \a
       
   384     context.
       
   385 */
       
   386 const QMimeSource* Q3MimeSourceFactory::data(const QString& abs_or_rel_name, const QString& context) const
       
   387 {
       
   388     const QMimeSource* r = data(makeAbsolute(abs_or_rel_name,context));
       
   389     if (!r && !d->path.isEmpty())
       
   390         r = data(abs_or_rel_name);
       
   391     return r;
       
   392 }
       
   393 
       
   394 
       
   395 /*!
       
   396     Sets \a text to be the data item associated with the absolute name
       
   397     \a abs_name.
       
   398 
       
   399     Equivalent to setData(abs_name, new Q3TextDrag(text)).
       
   400 */
       
   401 void Q3MimeSourceFactory::setText(const QString& abs_name, const QString& text)
       
   402 {
       
   403     setData(abs_name, new Q3TextDrag(text));
       
   404 }
       
   405 
       
   406 /*!
       
   407     Sets \a image to be the data item associated with the absolute
       
   408     name \a abs_name.
       
   409 
       
   410     Equivalent to setData(abs_name, new Q3ImageDrag(image)).
       
   411 */
       
   412 void Q3MimeSourceFactory::setImage(const QString& abs_name, const QImage& image)
       
   413 {
       
   414     setData(abs_name, new Q3ImageDrag(image));
       
   415 }
       
   416 
       
   417 /*!
       
   418     Sets \a pixmap to be the data item associated with the absolute
       
   419     name \a abs_name.
       
   420 */
       
   421 void Q3MimeSourceFactory::setPixmap(const QString& abs_name, const QPixmap& pixmap)
       
   422 {
       
   423     setData(abs_name, new Q3ImageDrag(pixmap.convertToImage()));
       
   424 }
       
   425 
       
   426 /*!
       
   427   Sets \a data to be the data item associated with
       
   428   the absolute name \a abs_name. Note that the ownership of \a data is
       
   429   transferred to the factory: do not delete or access the pointer after
       
   430   passing it to this function.
       
   431 
       
   432   Passing 0 for data removes previously stored data.
       
   433 */
       
   434 void Q3MimeSourceFactory::setData(const QString& abs_name, QMimeSource* data)
       
   435 {
       
   436     if (d->stored.contains(abs_name))
       
   437         delete d->stored[abs_name];
       
   438     d->stored.insert(abs_name,data);
       
   439 }
       
   440 
       
   441 
       
   442 /*!
       
   443     Returns the application-wide default mime source factory. This
       
   444     factory is used by rich text rendering classes such as
       
   445     QSimpleRichText, QWhatsThis and QMessageBox to resolve named
       
   446     references within rich text documents. It serves also as the
       
   447     initial factory for the more complex render widgets, QTextEdit and
       
   448     QTextBrowser.
       
   449 
       
   450     \sa setDefaultFactory()
       
   451 */
       
   452 Q3MimeSourceFactory* Q3MimeSourceFactory::defaultFactory()
       
   453 {
       
   454     if (!defaultfactory)
       
   455     {
       
   456         defaultfactory = new Q3MimeSourceFactory();
       
   457         qmime_cleanup_factory.set(&defaultfactory);
       
   458         QTextImageHandler::externalLoader = richTextImageLoader;
       
   459     }
       
   460     return defaultfactory;
       
   461 }
       
   462 
       
   463 /*!
       
   464     Sets the default \a factory, destroying any previously set mime
       
   465     source provider. The ownership of the factory is transferred to
       
   466     Qt.
       
   467 
       
   468     \sa defaultFactory()
       
   469 */
       
   470 void Q3MimeSourceFactory::setDefaultFactory(Q3MimeSourceFactory* factory)
       
   471 {
       
   472     if (!defaultfactory)
       
   473         qmime_cleanup_factory.set(&defaultfactory);
       
   474     else if (defaultfactory != factory)
       
   475         delete defaultfactory;
       
   476     defaultfactory = factory;
       
   477 }
       
   478 
       
   479 /*!
       
   480     Sets the defaultFactory() to 0 and returns the previous one.
       
   481 */
       
   482 
       
   483 Q3MimeSourceFactory* Q3MimeSourceFactory::takeDefaultFactory()
       
   484 {
       
   485     Q3MimeSourceFactory *f = defaultfactory;
       
   486     defaultfactory = 0;
       
   487     return f;
       
   488 }
       
   489 
       
   490 /*!
       
   491     Adds the Q3MimeSourceFactory \a f to the list of available
       
   492     mimesource factories. If the defaultFactory() can't resolve a
       
   493     data() it iterates over the list of installed mimesource factories
       
   494     until the data can be resolved.
       
   495 
       
   496     \sa removeFactory()
       
   497 */
       
   498 
       
   499 void Q3MimeSourceFactory::addFactory(Q3MimeSourceFactory *f)
       
   500 {
       
   501     Q3MimeSourceFactory::defaultFactory()->d->factories.append(f);
       
   502 }
       
   503 
       
   504 /*!
       
   505     Removes the mimesource factory \a f from the list of available
       
   506     mimesource factories.
       
   507 
       
   508     \sa addFactory()
       
   509 */
       
   510 
       
   511 void Q3MimeSourceFactory::removeFactory(Q3MimeSourceFactory *f)
       
   512 {
       
   513     Q3MimeSourceFactory::defaultFactory()->d->factories.removeAll(f);
       
   514 }
       
   515 
       
   516 QPixmap qPixmapFromMimeSource(const QString &abs_name)
       
   517 {
       
   518     const QMimeSource *m = Q3MimeSourceFactory::defaultFactory()->data(abs_name);
       
   519     if (!m) {
       
   520         if (QFile::exists(abs_name))
       
   521             return QPixmap(abs_name);
       
   522         if (!abs_name.isEmpty())
       
   523             qWarning("QPixmap::fromMimeSource: Cannot find pixmap \"%s\" in the mime source factory",
       
   524                       abs_name.latin1());
       
   525         return QPixmap();
       
   526     }
       
   527     QPixmap pix;
       
   528     Q3ImageDrag::decode(m, pix);
       
   529     return pix;
       
   530 }
       
   531 
       
   532 QImage qImageFromMimeSource(const QString &abs_name)
       
   533 {
       
   534     const QMimeSource *m = Q3MimeSourceFactory::defaultFactory()->data(abs_name);
       
   535     if (!m) {
       
   536         qWarning("QImage::fromMimeSource: Cannot find image \"%s\" in the mime source factory", abs_name.latin1());
       
   537         return QImage();
       
   538     }
       
   539     QImage img;
       
   540     Q3ImageDrag::decode(m, img);
       
   541     return img;
       
   542 }
       
   543 
       
   544 QT_END_NAMESPACE
       
   545 
       
   546 #endif // QT_NO_MIMEFACTORY