Logo Search packages:      
Sourcecode: basket version File versions  Download package

itemdrag.cpp

/***************************************************************************
 *   Copyright (C) 2003 by Sébastien Laoût                                 *
 *   sebastien.laout@tuxfamily.org                                         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <qbuffer.h>
#include <kcolordrag.h>
#include <kapplication.h>
#include <kiconloader.h>
#include <qimage.h>
#include <kurldrag.h>
#include <qtimer.h>
#include <qvaluestack.h>
#include <qfile.h>
#include <qdir.h>
#include <qtextcodec.h>

#include "itemdrag.h"
#include "itemfactory.h"
#include "basket.h"
#include "global.h"

/** ItemDrag */

const char * ItemDrag::item_mime_string  = "application/x-basket-item";

ItemDrag::ItemDrag(Item *item, bool cutting, QWidget *parent, const char *name)
 : QDragObject(parent, name)
{
      /* Init item */
      m_type        = item->type();
      m_isChecked   = item->isChecked();
      m_annotations = item->annotations();
      m_fileName    = item->fileName();
      m_fullPath    = item->fullPath();

      /* Init item content */
      switch (m_type) {
            case Item::Text:
                  m_text         = item->text();
                  m_textFontType = item->textFontType();
                  m_color        = item->textColor();
                  break;
            case Item::Html:
                  m_text         = item->html();
//                m_showSource   = item->showSource();
                  break;
            case Item::Image:
                  m_pixmap       = QPixmap(*(item->pixmap()));
                  break;
            case Item::Link:
                  m_url          = item->url();
                  m_title        = item->title();
                  m_icon         = item->icon();
                  m_autoTitle    = item->autoTitle();
                  m_autoIcon     = item->autoIcon();
                  break;
            case Item::Color:
                  m_color        = item->color();
                  break;
            case Item::Animation:
            case Item::Sound:
            case Item::File:
            case Item::Launcher:
            case Item::Unknow:
                  // Process is done below:
                  break;
      }

      if (cutting && item->useFile() && item->type() != Item::Unknow) {
            // Make sure the temporary folder exists and is empty (we delete previously moved file(s) (if exists)
            // since we override the content of the clipboard and previous file willn't be accessable anymore):
            QString tmpFolder = createAndEmptyCuttingTmpDir();
            // Move file in a temporary place:
            m_fullPath = tmpFolder + ItemFactory::fileNameForNewFile(item->fileName(), tmpFolder);
            KIO::move(KURL(item->fullPath()), KURL(m_fullPath), /*showProgressInfo=*/false);
      }
}

QString ItemDrag::createAndEmptyCuttingTmpDir()
{
      // Create the temp folder (if not exists):
      QString tmpFolder = Global::basketsFolder() + ".tmp/";
      QDir dir(tmpFolder, QString::null, QDir::Name | QDir::IgnoreCase, QDir::All | QDir::Hidden);
      dir.mkdir(tmpFolder);
      // Empty the folder:
      QStringList list = dir.entryList();
      for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
            if ( *it != "." && *it != ".." )
                  QFile::remove(tmpFolder + *it);
      // Return the tmp folder we've created:
      return tmpFolder;
}

ItemDrag::~ItemDrag()
{
}

/** Return the full path of the item drag, that can be different of the full path
  * of the item in case of cutting.
  */
00117 QString ItemDrag::fullPath() const
{
      return m_fullPath;
}

const char* ItemDrag::format(int i) const
{
      if (i == 0)
            return item_mime_string;
      else
            return 0;
}

QByteArray ItemDrag::encodedData(const char *mimeType) const
{
      if ( ::qstrcmp(mimeType, item_mime_string) != 0 )
            return QByteArray(); // Return if it isn't the BasketItem MIME string

      QBuffer buffer;

      if (buffer.open(IO_WriteOnly)) {
            QDataStream stream(&buffer);
            stream << (Q_INT32)m_type
                   << (Q_INT32)m_isChecked // Basket::trueOrFalse() : "true" / "false" ? Because I don't know how to store bools
                   << m_annotations
                   << m_fileName
                   << m_fullPath;
            switch (m_type) {
                  case Item::Text:
                        stream << m_text
                               << m_textFontType
                               << m_color;
                        break;
                  case Item::Html:
                        stream << m_text/*
                               << (Q_INT32)m_showSource*/;
                        break;
                  case Item::Image:
                        stream << m_pixmap;
                        break;
                  case Item::Link:
                        stream << m_url
                               << m_title
                               << m_icon
                               << (Q_INT32)m_autoTitle
                               << (Q_INT32)m_autoIcon;
                        break;
                  case Item::Color:
                        stream << m_color;
                        break;
                  case Item::Animation:
                  case Item::Sound:
                  case Item::File:
                  case Item::Launcher:
                  case Item::Unknow:
                        // We just need fileName (and fullPath) is those cases!
                        break;
            }
            buffer.close();
      }

      return buffer.buffer();
}

bool ItemDrag::canDecode(QMimeSource *source)
{
      return (source != 0) && (source->provides(item_mime_string));
}

Item* ItemDrag::decode(QMimeSource *source, Basket *parent, bool move)
{
      /* Variables to handle item type and global properties */
      Item::Type type;
      Q_INT32    tmp_isChecked;
      bool       isChecked;
      QString    annotations;
      QString    fileName;
      QString    fullPath;

      /* Special variables to handle contents by type */
      QString text;
      int     textFontType;
//    Q_INT32 showSource;
      QPixmap pixmap;
      QColor  color;
      KURL    url;
      QString title;
      QString icon;
      Q_INT32 autoTitle;
      Q_INT32 autoIcon;

      bool shouldCopy = false;

      /* Decode the item */
      QBuffer buffer(source->encodedData(item_mime_string));
      Item *item = 0;

      if (buffer.open(IO_ReadOnly)) {
            QDataStream stream(&buffer);
            stream >> (Q_INT32&)type // FIXME: Alwayse Q_INT32 ?
                   >> tmp_isChecked
                   >> annotations
                   >> fileName
                   >> fullPath;
            isChecked = tmp_isChecked != 0;
            if ( Item::isAMirror(fileName) && parent->itemForFullPath(fileName) ) {
                  shouldCopy = true;
                  // Notice it to the user as soon as the drop is processed :
                  parent->m_mirrorOnlyOnceFileName = fileName;
                  QTimer::singleShot( 0, parent, SLOT(showMirrorOnlyOnceInfo()) ); // TODO: Show also in statusBar ?
            }
            switch (type) {
                  case Item::Text:
                        stream >> text
                               >> textFontType
                               >> color;
                        if (Item::isAMirror(fileName)) {
                              item = new Item( fileName, textFontType, color,
                                               annotations, isChecked, parent );
                              item->loadContent();
                              if (shouldCopy)       // Hey : I'm lazy and 0.5.0alpha(s) are a lot of works !
                                    item->unmirror(); // But it works ;-)
                        } else {
                              item = new Item( ItemFactory::createFileForNewItem(parent, "txt", fileName), textFontType, color,
                                               annotations, isChecked, parent );
                              item->setText(text);
                        }
                        break;
                  case Item::Html:
                        stream >> text/*
                               >> showSource*/;
                        if (Item::isAMirror(fileName)) {
                              item = new Item( fileName, //showSource != 0,
                                               annotations, isChecked, parent );
                              item->loadContent();
                              if (shouldCopy)
                                    item->unmirror();
                        } else {
                              item = new Item( ItemFactory::createFileForNewItem(parent, "html", fileName), //showSource != 0,
                                               annotations, isChecked, parent );
                              item->setHtml(text);
                        }
                        break;
                  case Item::Image:
                        if (Item::isAMirror(fileName)) {
                              item = new Item( fileName, Item::Image,
                                               annotations, isChecked, parent );
                              item->loadContent();
                              if (shouldCopy)
                                    item->unmirror();
                        } else {
                              stream >> pixmap;
                              item = new Item( ItemFactory::createFileForNewItem(parent, "png", fileName), Item::Image,
                                               annotations, isChecked, parent );
                              item->setPixmap(pixmap);
                        }
                        break;
                  case Item::Link:
                        stream >> url
                               >> title
                               >> icon
                               >> autoTitle
                               >> autoIcon;
                        item = new Item( url, title, icon,
                                         autoTitle != 0, autoIcon != 0,
                                         annotations, isChecked, parent );
                        break;
                  case Item::Color:
                        stream >> color;
                        item = new Item( color,
                                         annotations, isChecked, parent );
                        break;
                  case Item::Animation:
                  case Item::Sound:
                  case Item::File:
                  case Item::Launcher:
                  case Item::Unknow:
                        if (source->provides("application/x-kde-cutselection")) {
                              QByteArray array = source->encodedData("application/x-kde-cutselection");
                              if ( !array.isEmpty() && QCString(array.data(), array.size() + 1).at(0) == '1' )
                                    move = true;
                        }
                        QString newFileName = ItemFactory::createFileForNewItem(parent, "", fileName);
                        item = new Item(newFileName, type, annotations, isChecked, parent);
                        if (move)
                              KIO::file_move(KURL(fullPath), KURL(parent->fullPath() + newFileName),
                                             /*perms=*/-1, /*override=*/true, /*resume=*/false, /*showProgressInfo=*/false);
                        else
                              KIO::file_copy(KURL(fullPath), KURL(parent->fullPath() + newFileName),
                                             /*perms=*/-1, /*override=*/true, /*resume=*/false, /*showProgressInfo=*/false);
                        break;
            }
            buffer.close();
      }

      return item;
}

/** ItemMultipleDrag */

00317 ItemMultipleDrag::ItemMultipleDrag(Item *item, bool cutting, QWidget *parent, const char *name)
 : KMultipleDrag(parent, name)
{
      KColorDrag *cDrag = 0;

      // We do it now because we're not sure when the file moving (in new ItemDrag) will be done:
      QString iconOfFile = ItemFactory::iconForURL(item->fullPath());

      /* Main format for item content */
      ItemDrag *itemDrag = new ItemDrag(item, cutting, 0);
      addDragObject(itemDrag);

      /* Alternative formats for item content */
      QTextDrag   *text;
      QTextDrag   *html;
      // To process Unknow item:
      QFile       *file;
      QString      line;
      QValueList<QString> *mimes;
      uint         i;
      Q_UINT32     size;
      QByteArray  *array = 0L;
      QStoredDrag *storedDrag = 0L;
      switch (item->type()) {
            case Item::Text:
                  addDragObject(new QTextDrag( item->text(), 0 ));
                  break;
            case Item::Html:
                  text = new QTextDrag(htmlToText(item->html()), 0);
                  html = new QTextDrag(item->html(), 0);
                  html->setSubtype("html");
                  addDragObject(text);
                  addDragObject(html);
                  // FIXME: Is it useful to also provie "application/x-qrichtext" MIME type to dorp in Rich Text editors?
                  break;
            case Item::Image:
                  addDragObject(new QImageDrag( item->pixmap()->convertToImage(), 0 ));
                  break;
            case Item::Link:
                  {
                        // Html version of the link:
                        html = new QTextDrag( QString("<a href=\"%1\">%2</a>").arg(item->url().prettyURL(), item->title()), 0 );
                        html->setSubtype("html");
                        // A version for Mozilla applications (convert "theUrl\ntheTitle" into UTF-16):
                        QString xMozUrl = item->title() + "\n" + item->url().prettyURL();
                        QByteArray baMozUrl;
                        QTextStream stream(baMozUrl, IO_WriteOnly);
                        stream.setEncoding(QTextStream::RawUnicode); // It's UTF16 (aka UCS2), but with the first two order bytes
                        stream << xMozUrl;
                        QStoredDrag *xMozUrlDrag = new QStoredDrag("text/x-moz-url");
                        xMozUrlDrag->setEncodedData(baMozUrl);
                        // OK: add them:
                        addDragObject(new KURLDrag( KURL::List(item->url()), 0, "" ));
                        addDragObject(html);
                        addDragObject(xMozUrlDrag);
                  }
                  break;
            case Item::Color:
                  addDragObject(cDrag = new KColorDrag( item->color(), 0 ));
                  addDragObject(new QTextDrag( item->color().name(), 0 ));
                  break;
            case Item::Animation:
            case Item::Sound:
            case Item::File:
            case Item::Launcher:
                  // Will be done below
                  break;
            case Item::Unknow:
                  file = new QFile(itemDrag->fullPath());
                  if (file->open(IO_ReadOnly)) {
                        QDataStream stream(file);
                        mimes = new QValueList<QString>;
                        // Get the MIME types names:
                        do {
                              stream >> line;
                              if (!line.isEmpty())
                                    mimes->append(line);
                        } while (!line.isEmpty());
                        // Add the streams:
                        for (i = 0; i < mimes->count(); ++i) {
                              // Get the size:
                              stream >> size;
                              // Allocate memory to retreive size bytes and store them:
                              array = new QByteArray(size);
                              stream.readRawBytes(array->data(), size);
                              // Creata and add the QDragObject:
                              storedDrag = new QStoredDrag(*(mimes->at(i)));
                              storedDrag->setEncodedData(*array);
                              addDragObject(storedDrag);
                              delete array; // FIXME: Should we?
                        }
                        file->close();
                  }
                  delete file;
                  break;
      }

      /* Alternative format (the path) for items stored in files */
      if (item->useFile() && item->type() != Item::Unknow) { // ... but not for Unknow objects since they are stored in an
            addDragObject( new KURLDrag(KURL(itemDrag->fullPath()), 0, "") );                          // internal format only

            if (cutting) {
                  // Warning: We're reusing variables...
                  QByteArray  arrayCut(2);
                  QStoredDrag *storedDragCut = new QStoredDrag("application/x-kde-cutselection");
                  arrayCut[0] = '1';
                  arrayCut[1] = 0;
                  storedDragCut->setEncodedData(arrayCut);
                  addDragObject(storedDragCut);
            }
      }

      /* Init icon that is shown during drag */
      QPixmap      icon;
      QImage       image; // For temporary scale purpose
      const int    iconSize = 22;
      const QPoint iconHotPoint = QPoint(-5, -7); //iconSize / 2;

      switch (item->type()) {
            case Item::Text:
                  icon = kapp->iconLoader()->loadIcon("text", KIcon::NoGroup, iconSize);
                  setPixmap(icon, iconHotPoint);
                  break;
            case Item::Html:
                  icon = kapp->iconLoader()->loadIcon("html", KIcon::NoGroup, iconSize);
                  setPixmap(icon, iconHotPoint);
                  break;
            case Item::Image:
                  image = item->pixmap()->convertToImage();
                  image = image.smoothScale(40, 30, QImage::ScaleMin);
                  icon = QPixmap(image);
                  setPixmap(icon, iconHotPoint);
                  break;
            case Item::Animation:
                  image = item->movie()->frameImage();
                  image = image.smoothScale(40, 30, QImage::ScaleMin);
                  icon = QPixmap(image);
                  setPixmap(icon, iconHotPoint);
                  break;
            case Item::Link:
                  icon = kapp->iconLoader()->loadIcon(item->icon(), KIcon::NoGroup, iconSize);
                  setPixmap(icon, iconHotPoint);
                  break;
            case Item::Color:
                  setPixmap(cDrag->pixmap(), cDrag->pixmapHotSpot());
                  break;
            case Item::Sound:
            case Item::File:
            case Item::Launcher:
                  icon = kapp->iconLoader()->loadIcon(iconOfFile, KIcon::NoGroup, iconSize);
                  setPixmap(icon, iconHotPoint);
                  break;
            case Item::Unknow:
                  icon = kapp->iconLoader()->loadIcon("unknown", KIcon::NoGroup, iconSize);
                  setPixmap(icon, iconHotPoint);
                  break;
      }
}

ItemMultipleDrag::~ItemMultipleDrag()
{
}

/* TODO: A class 'General' to sore all those general purpose functions
 *       (htmlToText, htmlToParagraph, textToHtml...)
 */
QString ItemMultipleDrag::htmlToText(const QString &html)
{
      QString text = Basket::htmlToParagraph(html);
      text.remove("\n");
      text.replace("</h1>", "\n");
      text.replace("</h2>", "\n");
      text.replace("</h3>", "\n");
      text.replace("</h4>", "\n");
      text.replace("</h5>", "\n");
      text.replace("</h6>", "\n");
      text.replace("</li>", "\n");
      text.replace("</dt>", "\n");
      text.replace("</dd>", "\n");
      text.replace("<dd>",  "   ");
      text.replace("</div>","\n");
      text.replace("</blockquote>","\n");
      text.replace("</caption>","\n");
      text.replace("</tr>", "\n");
      text.replace("</th>", "  ");
      text.replace("</td>", "  ");
      text.replace("<br>",  "\n");
      text.replace("<br />","\n");
      // FIXME: Format <table> tags better, if possible
      // TODO: Replace &eacute; and co. by theire equivalent!

      // To manage tags:
      int pos = 0;
      int pos2;
      QString tag, tag3;
      // To manage lists:
      int deep = 0;            // The deep of the current line in imbriqued lists
      QValueStack<bool> ul;    // true if current list is a <ul> one, false if it's an <ol> one
      QValueStack<int>  lines; // The line number if it is an <ol> list
      // We're removing every other tags, or replace them in the case of li:
      while ( (pos = text.find("<"), pos) != -1 ) {
            // What is the current tag?
            tag  = text.mid(pos + 1, 2);
            tag3 = text.mid(pos + 1, 3);
            // Lists work:
            if (tag == "ul") {
                  deep++;
                  ul.push(true);
                  lines.push(-1);
            } else if (tag == "ol") {
                  deep++;
                  ul.push(false);
                  lines.push(0);
            } else if (tag3 == "/ul" || tag3 == "/ol") {
                  deep--;
                  ul.pop();
                  lines.pop();
            }
            // Where the tag closes?
            pos2 = text.find(">");
            if (pos2 != -1) {
                  // Remove the tag:
                  text.remove(pos, pos2 - pos + 1);
                  // And replace li with "* ", "x. "... without forbidding to indent that:
                  if (tag == "li") {
                        // How many spaces before the line (indentation):
                        QString spaces = "";
                        for (int i = 1; i < deep; i++)
                              spaces += "  ";
                        // The bullet or number of the line:
                        QString bullet = "* ";
                        if (ul.top() == false) {
                              lines.push(lines.pop() + 1);
                              bullet = QString::number(lines.top()) + ". ";
                        }
                        // Insertion:
                        text.insert(pos, spaces + bullet);
                  }
                  if ( (tag3 == "/ul" || tag3 == "/ol") && deep == 0 )
                        text.insert(pos, "\n"); // Empty line before and after a set of lists
            }
            ++pos;
      }

      return text;
}

/** ExtendedTextDrag */

00566 bool ExtendedTextDrag::decode(const QMimeSource *e, QString &str)
{
      QCString subtype("plain");
      return decode(e, str, subtype);
}

bool ExtendedTextDrag::decode(const QMimeSource *e, QString &str, QCString &subtype)
{
      // Get the string:
      bool ok = QTextDrag::decode(e, str, subtype);

      // Test if it was a UTF-16 string (from eg. Mozilla):
      if (str.length() >= 2) {
            if ((str[0] == 0xFF && str[1] == 0xFE) || (str[0] == 0xFE && str[1] == 0xFF)) {
                  QByteArray utf16 = e->encodedData(QString("text/" + subtype).local8Bit());
                  str = QTextCodec::codecForName("utf16")->toUnicode(utf16);
                  return true;
            }
      }

      // Test if it was empty (sometimes, from GNOME or Mozilla)
      if (str.length() == 0 && subtype == "plain") {
            if (e->provides("UTF8_STRING")) {
                  QByteArray utf8 = e->encodedData("UTF8_STRING");
                  str = QTextCodec::codecForName("utf8")->toUnicode(utf8);
                  return true;
            }
            if (e->provides("text/unicode")) { // FIXME: It's UTF-16 without order bytes!!!
                  QByteArray utf16 = e->encodedData("text/unicode");
                  str = QTextCodec::codecForName("utf16")->toUnicode(utf16);
                  return true;
            }
            if (e->provides("TEXT")) { // local encoding
                  QByteArray text = e->encodedData("TEXT");
                  str = QString(text);
                  return true;
            }
            if (e->provides("COMPOUND_TEXT")) { // local encoding
                  QByteArray text = e->encodedData("COMPOUND_TEXT");
                  str = QString(text);
                  return true;
            }
      }
      return ok;
}

#include "itemdrag.moc"

Generated by  Doxygen 1.6.0   Back to index