File indexing completed on 2022-11-29 20:22:31

0001 /**
0002  * SPDX-FileCopyrightText: (C) 2003 Sébastien Laoût <slaout@linux62.org>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "notedrag.h"
0008 
0009 #include <QApplication>
0010 #include <QDesktopWidget> //For qApp->desktop()
0011 #include <QtCore/QBuffer>
0012 #include <QtCore/QDir>
0013 #include <QtCore/QList>
0014 #include <QtCore/QMimeData>
0015 #include <QtCore/QTextCodec>
0016 #include <QtCore/QTextStream>
0017 #include <QtGui/QDragEnterEvent>
0018 #include <QtGui/QPainter>
0019 #include <QtGui/QPixmap>
0020 
0021 #include <KIO/CopyJob>
0022 
0023 #include "basketscene.h"
0024 #include "global.h"
0025 #include "notefactory.h"
0026 #include "noteselection.h"
0027 #include "tools.h"
0028 
0029 /** NoteDrag */
0030 
0031 const char *NoteDrag::NOTE_MIME_STRING = "application/x-basket-note";
0032 QList<Note *> NoteDrag::selectedNotes;
0033 
0034 void NoteDrag::createAndEmptyCuttingTmpFolder()
0035 {
0036     Tools::deleteRecursively(Global::tempCutFolder());
0037     QDir dir;
0038     dir.mkdir(Global::tempCutFolder());
0039 }
0040 
0041 QDrag *NoteDrag::dragObject(NoteSelection *noteList, bool cutting, QWidget *source)
0042 {
0043     if (noteList->count() <= 0)
0044         return nullptr;
0045 
0046     QDrag *multipleDrag = new QDrag(source);
0047 
0048     // The MimeSource:
0049     QMimeData *mimeData = new QMimeData;
0050 
0051     // Make sure the temporary folder exists and is empty (we delete previously moved file(s) (if exists)
0052     // since we override the content of the clipboard and previous file willn't be accessable anymore):
0053     createAndEmptyCuttingTmpFolder();
0054 
0055     // The "Native Format" Serialization:
0056     QBuffer buffer;
0057     if (buffer.open(QIODevice::WriteOnly)) {
0058         QDataStream stream(&buffer);
0059         // First append a pointer to the basket:
0060         stream << (quint64)(noteList->firstStacked()->note->basket());
0061 
0062         // And finally the notes themselves:
0063         serializeNotes(noteList, stream, cutting);
0064         // Append the object:
0065         buffer.close();
0066         mimeData->setData(NOTE_MIME_STRING, buffer.buffer());
0067     }
0068 
0069     // The "Other Flavors" Serialization:
0070     serializeText(noteList, mimeData);
0071     serializeHtml(noteList, mimeData);
0072     serializeImage(noteList, mimeData);
0073     serializeLinks(noteList, mimeData, cutting);
0074 
0075     // The Alternate Flavors:
0076     if (noteList->count() == 1)
0077         noteList->firstStacked()->note->content()->addAlternateDragObjects(mimeData);
0078 
0079     multipleDrag->setMimeData(mimeData);
0080 
0081     // If it is a drag, and not a copy/cut, add the feedback pixmap:
0082     if (source)
0083         setFeedbackPixmap(noteList, multipleDrag);
0084 
0085     return multipleDrag;
0086 }
0087 
0088 void NoteDrag::serializeNotes(NoteSelection *noteList, QDataStream &stream, bool cutting)
0089 {
0090     for (NoteSelection *node = noteList; node; node = node->next) {
0091         stream << (quint64)(node->note);
0092         if (node->firstChild) {
0093             stream << (quint64)(NoteType::Group) << (quint64)(node->note->groupWidth()) << (quint64)(node->note->isFolded());
0094             serializeNotes(node->firstChild, stream, cutting);
0095         } else {
0096             NoteContent *content = node->note->content();
0097             stream << (quint64)(content->type()) << (quint64)(node->note->groupWidth());
0098             // Serialize file name, and move the file to a temporary place if the note is to be cut.
0099             // If note does not have file name, we append empty string to be able to easily decode the notes later:
0100             stream << content->fileName();
0101             if (content->shouldSerializeFile()) {
0102                 if (cutting) {
0103                     // Move file in a temporary place:
0104                     QString fullPath = Global::tempCutFolder() + Tools::fileNameForNewFile(content->fileName(), Global::tempCutFolder());
0105                     KIO::move(QUrl::fromLocalFile(content->fullPath()), QUrl::fromLocalFile(fullPath), KIO::HideProgressInfo);
0106                     node->fullPath = fullPath;
0107                     stream << fullPath;
0108                 } else
0109                     stream << content->fullPath();
0110             } else
0111                 stream << QString(QString());
0112             stream << content->note()->addedDate() << content->note()->lastModificationDate();
0113             content->serialize(stream);
0114             State::List states = node->note->states();
0115             for (State::List::Iterator it = states.begin(); it != states.end(); ++it)
0116                 stream << (quint64)(*it);
0117             stream << (quint64)0;
0118         }
0119     }
0120     stream << (quint64)0; // Mark the end of the notes in this group/hierarchy.
0121 }
0122 
0123 void NoteDrag::serializeText(NoteSelection *noteList, QMimeData* mimeData)
0124 {
0125     QString textEquivalent;
0126     QString text;
0127     for (NoteSelection *node = noteList->firstStacked(); node; node = node->nextStacked()) {
0128         text = node->note->toText(node->fullPath); // note->toText() and not note->content()->toText() because the first one will also export the tags as text.
0129         if (!text.isEmpty())
0130             textEquivalent += (!textEquivalent.isEmpty() ? "\n" : QString()) + text;
0131     }
0132     if (!textEquivalent.isEmpty()) {
0133         mimeData->setText(textEquivalent);
0134     }
0135 }
0136 
0137 void NoteDrag::serializeHtml(NoteSelection *noteList, QMimeData* mimeData)
0138 {
0139     QString htmlEquivalent;
0140     QString html;
0141     for (NoteSelection *node = noteList->firstStacked(); node; node = node->nextStacked()) {
0142         html = node->note->content()->toHtml(QString(), node->fullPath);
0143         if (!html.isEmpty())
0144             htmlEquivalent += (!htmlEquivalent.isEmpty() ? "<br>\n" : QString()) + html;
0145     }
0146     if (!htmlEquivalent.isEmpty()) {
0147         // Add HTML flavour:
0148         mimeData->setHtml(htmlEquivalent);
0149 
0150         // But also QTextEdit flavour, to be able to paste several notes to a text edit:
0151         QByteArray byteArray = ("<!--StartFragment--><p>" + htmlEquivalent).toLocal8Bit();
0152         mimeData->setData("application/x-qrichtext", byteArray);
0153     }
0154 }
0155 
0156 void NoteDrag::serializeImage(NoteSelection *noteList, QMimeData* mimeData)
0157 {
0158     QList<QPixmap> pixmaps;
0159     QPixmap pixmap;
0160     for (NoteSelection *node = noteList->firstStacked(); node; node = node->nextStacked()) {
0161         pixmap = node->note->content()->toPixmap();
0162         if (!pixmap.isNull())
0163             pixmaps.append(pixmap);
0164     }
0165     if (!pixmaps.isEmpty()) {
0166         QPixmap pixmapEquivalent;
0167         if (pixmaps.count() == 1)
0168             pixmapEquivalent = pixmaps.first();
0169         else {
0170             // Search the total size:
0171             int height = 0;
0172             int width = 0;
0173             for (QList<QPixmap>::iterator it = pixmaps.begin(); it != pixmaps.end(); ++it) {
0174                 height += (*it).height();
0175                 if ((*it).width() > width)
0176                     width = (*it).width();
0177             }
0178             // Create the image by painting all image into one big image:
0179             pixmapEquivalent = QPixmap(width, height);
0180             pixmapEquivalent.fill(Qt::white);
0181             QPainter painter(&pixmapEquivalent);
0182             height = 0;
0183             for (QList<QPixmap>::iterator it = pixmaps.begin(); it != pixmaps.end(); ++it) {
0184                 painter.drawPixmap(0, height, *it);
0185                 height += (*it).height();
0186             }
0187         }
0188         mimeData->setImageData(pixmapEquivalent.toImage());
0189     }
0190 }
0191 
0192 void NoteDrag::serializeLinks(NoteSelection *noteList, QMimeData* mimeData, bool cutting)
0193 {
0194     QList<QUrl> urls;
0195     QStringList titles;
0196     QUrl url;
0197     QString title;
0198     for (NoteSelection *node = noteList->firstStacked(); node; node = node->nextStacked()) {
0199         node->note->content()->toLink(&url, &title, node->fullPath);
0200         if (!url.isEmpty()) {
0201             urls.append(url);
0202             titles.append(title);
0203         }
0204     }
0205     if (!urls.isEmpty()) {
0206         // First, the standard text/uri-list MIME format:
0207         mimeData->setUrls(urls);
0208 
0209         // Then, also provide it in the Mozilla proprietary format (that also allow to add titles to URLs):
0210         // A version for Mozilla applications (convert to "theUrl\ntheTitle", into UTF-16):
0211         // FIXME: Does Mozilla support the drag of several URLs at once?
0212         // FIXME: If no, only provide that if there is only ONE URL.
0213         QString xMozUrl;
0214         for (int i = 0; i < urls.count(); ++i)
0215             xMozUrl += (xMozUrl.isEmpty() ? QString() : "\n") + urls[i].toDisplayString() + '\n' + titles[i];
0216         /*      Code for only one: ===============
0217                 xMozUrl = note->title() + "\n" + note->url().toDisplayString();*/
0218         QByteArray baMozUrl;
0219         QTextStream stream(baMozUrl, QIODevice::WriteOnly);
0220 
0221         // It's UTF16 (aka UCS2), but with the first two order bytes
0222         // stream.setEncoding(QTextStream::RawUnicode); // It's UTF16 (aka UCS2), but with the first two order bytes
0223         // FIXME: find out if this is really equivalent, as https://doc.qt.io/archives/3.3/qtextstream.html pretends
0224         stream.setCodec("UTF-16");
0225 
0226         stream << xMozUrl;
0227 
0228         mimeData->setData("text/x-moz-url", baMozUrl);
0229 
0230         if (cutting) {
0231             QByteArray arrayCut;
0232             arrayCut.resize(2);
0233             arrayCut[0] = '1';
0234             arrayCut[1] = 0;
0235             mimeData->setData("application/x-kde-cutselection", arrayCut);
0236         }
0237     }
0238 }
0239 
0240 void NoteDrag::setFeedbackPixmap(NoteSelection *noteList, QDrag *multipleDrag)
0241 {
0242     QPixmap pixmap = feedbackPixmap(noteList);
0243     if (!pixmap.isNull()) {
0244         multipleDrag->setPixmap(pixmap);
0245         multipleDrag->setHotSpot(QPoint(-8, -8));
0246     }
0247 }
0248 
0249 QPixmap NoteDrag::feedbackPixmap(NoteSelection *noteList)
0250 {
0251     if (noteList == nullptr)
0252         return QPixmap();
0253 
0254     static const int MARGIN = 2;
0255     static const int SPACING = 1;
0256 
0257     QColor textColor = noteList->firstStacked()->note->basket()->textColor();
0258     QColor backgroundColor = noteList->firstStacked()->note->basket()->backgroundColor().darker(NoteContent::FEEDBACK_DARKING);
0259 
0260     QList<QPixmap> pixmaps;
0261     QList<QColor> backgrounds;
0262     QList<bool> spaces;
0263     QPixmap pixmap;
0264     int height = 0;
0265     int width = 0;
0266     int i = 0;
0267     bool elipsisImage = false;
0268     QColor bgColor;
0269     bool needSpace;
0270     for (NoteSelection *node = noteList->firstStacked(); node; node = node->nextStacked(), ++i) {
0271         if (elipsisImage) {
0272             pixmap = QPixmap(7, 2);
0273             pixmap.fill(backgroundColor);
0274             QPainter painter(&pixmap);
0275             painter.setPen(textColor);
0276             painter.drawPoint(1, 1);
0277             painter.drawPoint(3, 1);
0278             painter.drawPoint(5, 1);
0279             painter.end();
0280             bgColor = node->note->basket()->backgroundColor();
0281             needSpace = false;
0282         } else {
0283             pixmap = node->note->content()->feedbackPixmap(/*maxWidth=*/qApp->desktop()->width() / 2, /*maxHeight=*/96);
0284             bgColor = node->note->backgroundColor();
0285             needSpace = node->note->content()->needSpaceForFeedbackPixmap();
0286         }
0287         if (!pixmap.isNull()) {
0288             if (pixmap.width() > width)
0289                 width = pixmap.width();
0290             pixmaps.append(pixmap);
0291             backgrounds.append(bgColor);
0292             spaces.append(needSpace);
0293             height += (i > 0 && needSpace ? 1 : 0) + pixmap.height() + SPACING + (needSpace ? 1 : 0);
0294             if (elipsisImage)
0295                 break;
0296             if (height > qApp->desktop()->height() / 2)
0297                 elipsisImage = true;
0298         }
0299     }
0300     if (!pixmaps.isEmpty()) {
0301         QPixmap result(MARGIN + width + MARGIN, MARGIN + height - SPACING + MARGIN - (spaces.last() ? 1 : 0));
0302         QPainter painter(&result);
0303         // Draw all the images:
0304         height = MARGIN;
0305         QList<QPixmap>::iterator it;
0306         QList<QColor>::iterator it2;
0307         QList<bool>::iterator it3;
0308         int i = 0;
0309         for (it = pixmaps.begin(), it2 = backgrounds.begin(), it3 = spaces.begin(); it != pixmaps.end(); ++it, ++it2, ++it3, ++i) {
0310             if (i != 0 && (*it3)) {
0311                 painter.fillRect(MARGIN, height, result.width() - 2 * MARGIN, SPACING, (*it2).darker(NoteContent::FEEDBACK_DARKING));
0312                 ++height;
0313             }
0314             painter.drawPixmap(MARGIN, height, *it);
0315             if ((*it).width() < width)
0316                 painter.fillRect(MARGIN + (*it).width(), height, width - (*it).width(), (*it).height(), (*it2).darker(NoteContent::FEEDBACK_DARKING));
0317             if (*it3) {
0318                 painter.fillRect(MARGIN, height + (*it).height(), result.width() - 2 * MARGIN, SPACING, (*it2).darker(NoteContent::FEEDBACK_DARKING));
0319                 ++height;
0320             }
0321             painter.fillRect(MARGIN, height + (*it).height(), result.width() - 2 * MARGIN, SPACING, Tools::mixColor(textColor, backgroundColor));
0322             height += (*it).height() + SPACING;
0323         }
0324         // Draw the border:
0325         painter.setPen(textColor);
0326         painter.drawLine(0, 0, result.width() - 1, 0);
0327         painter.drawLine(0, 0, 0, result.height() - 1);
0328         painter.drawLine(0, result.height() - 1, result.width() - 1, result.height() - 1);
0329         painter.drawLine(result.width() - 1, 0, result.width() - 1, result.height() - 1);
0330         // Draw the "lightly rounded" border:
0331         painter.setPen(Tools::mixColor(textColor, backgroundColor));
0332         painter.drawPoint(0, 0);
0333         painter.drawPoint(0, result.height() - 1);
0334         painter.drawPoint(result.width() - 1, result.height() - 1);
0335         painter.drawPoint(result.width() - 1, 0);
0336         // Draw the background in the margin (the inside will be painted above, anyway):
0337         painter.setPen(backgroundColor);
0338         painter.drawLine(1, 1, result.width() - 2, 1);
0339         painter.drawLine(1, 1, 1, result.height() - 2);
0340         painter.drawLine(1, result.height() - 2, result.width() - 2, result.height() - 2);
0341         painter.drawLine(result.width() - 2, 1, result.width() - 2, result.height() - 2);
0342         // And assign the feedback pixmap to the drag object:
0343         // multipleDrag->setPixmap(result, QPoint(-8, -8));
0344         return result;
0345     }
0346     return QPixmap();
0347 }
0348 
0349 bool NoteDrag::canDecode(const QMimeData *source)
0350 {
0351     return source->hasFormat(NOTE_MIME_STRING);
0352 }
0353 
0354 BasketScene *NoteDrag::basketOf(const QMimeData *source)
0355 {
0356     QByteArray srcData = source->data(NOTE_MIME_STRING);
0357     QBuffer buffer(&srcData);
0358     if (buffer.open(QIODevice::ReadOnly)) {
0359         QDataStream stream(&buffer);
0360         // Get the parent basket:
0361         quint64 basketPointer;
0362         stream >> (quint64 &)basketPointer;
0363         return (BasketScene *)basketPointer;
0364     } else
0365         return nullptr;
0366 }
0367 
0368 QList<Note *> NoteDrag::notesOf(QGraphicsSceneDragDropEvent *source)
0369 {
0370     /* FIXME: this code does not parse the stream properly (see NoteDrag::decode).
0371        Thus m_draggedNotes will contain many invalid pointer values.
0372        As a workaround, we use NoteDrag::selectedNotes now. */
0373 
0374     QByteArray srcData = source->mimeData()->data(NOTE_MIME_STRING);
0375     QBuffer buffer(&srcData);
0376     if (buffer.open(QIODevice::ReadOnly)) {
0377         QDataStream stream(&buffer);
0378         // Get the parent basket:
0379         quint64 basketPointer;
0380         stream >> (quint64 &)basketPointer;
0381         // Get the note list:
0382         quint64 notePointer;
0383         QList<Note *> notes;
0384         do {
0385             stream >> notePointer;
0386             if (notePointer != 0)
0387                 notes.append((Note *)notePointer);
0388         } while (notePointer);
0389         // Done:
0390         return notes;
0391     } else
0392         return QList<Note *>();
0393 }
0394 
0395 void NoteDrag::saveNoteSelectionToList(NoteSelection *selection)
0396 {
0397     for (NoteSelection *sel = selection->firstStacked(); sel != nullptr; sel = sel->nextStacked()) {
0398         if (sel->note->isGroup())
0399             saveNoteSelectionToList(sel);
0400         else
0401             selectedNotes.append(sel->note);
0402     }
0403 }
0404 
0405 Note *NoteDrag::decode(const QMimeData *source, BasketScene *parent, bool moveFiles, bool moveNotes)
0406 {
0407     QByteArray srcData = source->data(NOTE_MIME_STRING);
0408     QBuffer buffer(&srcData);
0409     if (buffer.open(QIODevice::ReadOnly)) {
0410         QDataStream stream(&buffer);
0411         // Get the parent basket:
0412         quint64 basketPointer;
0413         stream >> (quint64 &)basketPointer;
0414         BasketScene *basket = (BasketScene *)basketPointer;
0415         // Decode the note hierarchy:
0416         Note *hierarchy = decodeHierarchy(stream, parent, moveFiles, moveNotes, basket);
0417         // In case we moved notes from one basket to another, save the source basket where notes were removed:
0418         basket->filterAgainDelayed(); // Delayed, because if a note is moved to the same basket, the note is not at its
0419         basket->save();               //  new position yet, and the call to ensureNoteVisible would make the interface flicker!!
0420         return hierarchy;
0421     } else
0422         return nullptr;
0423 }
0424 
0425 Note *NoteDrag::decodeHierarchy(QDataStream &stream, BasketScene *parent, bool moveFiles, bool moveNotes, BasketScene *originalBasket)
0426 {
0427     quint64 notePointer;
0428     quint64 type;
0429     QString fileName;
0430     QString fullPath;
0431     QDateTime addedDate;
0432     QDateTime lastModificationDate;
0433 
0434     Note *firstNote = nullptr; // TODO: class NoteTreeChunk
0435     Note *lastInserted = nullptr;
0436 
0437     do {
0438         stream >> notePointer;
0439         if (notePointer == 0)
0440             return firstNote;
0441         Note *oldNote = (Note *)notePointer;
0442 
0443         Note *note = nullptr;
0444         quint64 groupWidth;
0445         stream >> type >> groupWidth;
0446         if (type == NoteType::Group) {
0447             note = new Note(parent);
0448             note->setZValue(-1);
0449             note->setGroupWidth(groupWidth);
0450             quint64 isFolded;
0451             stream >> isFolded;
0452             if (isFolded)
0453                 note->toggleFolded();
0454             if (moveNotes) {
0455                 note->setX(oldNote->x());           // We don't move groups but re-create them (every children can to not be selected)
0456                 note->setY(oldNote->y());           // We just set the position of the copied group so the animation seems as if the group is the same as (or a copy of) the old.
0457                 note->setHeight(oldNote->height()); // Idem: the only use of Note::setHeight()
0458                 parent->removeItem(oldNote);
0459             }
0460             Note *children = decodeHierarchy(stream, parent, moveFiles, moveNotes, originalBasket);
0461             if (children) {
0462                 for (Note *n = children; n; n = n->next())
0463                     n->setParentNote(note);
0464                 note->setFirstChild(children);
0465             }
0466         } else {
0467             stream >> fileName >> fullPath >> addedDate >> lastModificationDate;
0468             if (moveNotes) {
0469                 originalBasket->unplugNote(oldNote);
0470                 note = oldNote;
0471                 if (note->basket() != parent && (!fileName.isEmpty() && !fullPath.isEmpty())) {
0472                     QString newFileName = Tools::fileNameForNewFile(fileName, parent->fullPath());
0473                     note->content()->setFileName(newFileName);
0474 
0475                     KIO::CopyJob *copyJob = KIO::move(QUrl::fromLocalFile(fullPath), QUrl::fromLocalFile(parent->fullPath() + newFileName), KIO::Overwrite | KIO::Resume | KIO::HideProgressInfo);
0476                     parent->connect(copyJob, &KIO::CopyJob::copyingDone, parent, &BasketScene::slotCopyingDone2);
0477                 }
0478                 note->setGroupWidth(groupWidth);
0479                 note->setParentNote(nullptr);
0480                 note->setPrev(nullptr);
0481                 note->setNext(nullptr);
0482                 note->setParentBasket(parent);
0483                 NoteFactory::consumeContent(stream, (NoteType::Id)type);
0484             } else if ((note = NoteFactory::decodeContent(stream, (NoteType::Id)type, parent))) {
0485                 note->setGroupWidth(groupWidth);
0486                 note->setAddedDate(addedDate);
0487                 note->setLastModificationDate(lastModificationDate);
0488             } else if (!fileName.isEmpty()) {
0489                 // Here we are CREATING a new EMPTY file, so the name is RESERVED
0490                 // (while dropping several files at once a filename cannot be used by two of them).
0491                 // Later on, file_copy/file_move will copy/move the file to the new location.
0492                 QString newFileName = Tools::fileNameForNewFile(fileName, parent->fullPath());
0493                 // NoteFactory::createFileForNewNote(parent, QString(), fileName);
0494                 KIO::CopyJob *copyJob;
0495                 if (moveFiles) {
0496                     copyJob = KIO::move(QUrl::fromLocalFile(fullPath), QUrl::fromLocalFile(parent->fullPath() + newFileName), KIO::Overwrite | KIO::Resume | KIO::HideProgressInfo);
0497                 } else {
0498                     copyJob = KIO::copy(QUrl::fromLocalFile(fullPath), QUrl::fromLocalFile(parent->fullPath() + newFileName), KIO::Overwrite | KIO::Resume | KIO::HideProgressInfo);
0499                 }
0500                 parent->connect(copyJob, &KIO::CopyJob::copyingDone, parent, &BasketScene::slotCopyingDone2);
0501 
0502                 note = NoteFactory::loadFile(newFileName, (NoteType::Id)type, parent);
0503                 note->setGroupWidth(groupWidth);
0504                 note->setAddedDate(addedDate);
0505                 note->setLastModificationDate(lastModificationDate);
0506             }
0507         }
0508         // Retrieve the states (tags) and assign them to the note:
0509         if (note && note->content()) {
0510             quint64 statePointer;
0511             do {
0512                 stream >> statePointer;
0513                 if (statePointer)
0514                     note->addState((State *)statePointer);
0515             } while (statePointer);
0516         }
0517         // Now that we have created the note, insert it:
0518         if (note) {
0519             if (!firstNote)
0520                 firstNote = note;
0521             else {
0522                 lastInserted->setNext(note);
0523                 note->setPrev(lastInserted);
0524             }
0525             lastInserted = note;
0526         }
0527     } while (true);
0528 
0529     // We've done: return!
0530     return firstNote;
0531 }
0532 
0533 /** ExtendedTextDrag */
0534 
0535 bool ExtendedTextDrag::decode(const QMimeData *e, QString &str)
0536 {
0537     QString subtype("plain");
0538     return decode(e, str, subtype);
0539 }
0540 
0541 bool ExtendedTextDrag::decode(const QMimeData *e, QString &str, QString &subtype)
0542 {
0543     // Get the string:
0544     bool ok;
0545     str = e->text();
0546     ok = !str.isNull();
0547 
0548     // Test if it was a UTF-16 string (from eg. Mozilla):
0549     if (str.length() >= 2) {
0550         if ((str[0] == 0xFF && str[1] == 0xFE) || (str[0] == 0xFE && str[1] == 0xFF)) {
0551             QByteArray utf16 = e->data(QString("text/" + subtype).toLocal8Bit());
0552             str = QTextCodec::codecForName("utf16")->toUnicode(utf16);
0553             return true;
0554         }
0555     }
0556 
0557     // Test if it was empty (sometimes, from GNOME or Mozilla)
0558     if (str.length() == 0 && subtype == "plain") {
0559         if (e->hasFormat("UTF8_STRING")) {
0560             QByteArray utf8 = e->data("UTF8_STRING");
0561             str = QTextCodec::codecForName("utf8")->toUnicode(utf8);
0562             return true;
0563         }
0564         if (e->hasFormat("text/unicode")) { // FIXME: It's UTF-16 without order bytes!!!
0565             QByteArray utf16 = e->data("text/unicode");
0566             str = QTextCodec::codecForName("utf16")->toUnicode(utf16);
0567             return true;
0568         }
0569         if (e->hasFormat("TEXT")) { // local encoding
0570             QByteArray text = e->data("TEXT");
0571             str = QString(text);
0572             return true;
0573         }
0574         if (e->hasFormat("COMPOUND_TEXT")) { // local encoding
0575             QByteArray text = e->data("COMPOUND_TEXT");
0576             str = QString(text);
0577             return true;
0578         }
0579     }
0580     return ok;
0581 }