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

itemfactory.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 <qstring.h>
#include <kurl.h>
#include <qpixmap.h>
#include <qcolor.h>
#include <qregexp.h>
#include <kcolordrag.h>
#include <kurldrag.h>
#include <qstylesheet.h>
#include <qdir.h>
#include <kmimetype.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kdesktopfile.h>
#include <kapplication.h>
#include <qfile.h>
#include <kfilemetainfo.h>
#include <kio/jobclasses.h>
#include <qtextcodec.h>
#include <kopenwith.h>
#include <kfiledialog.h>
#include <kicondialog.h>
#include <kiconloader.h>
#include <qfileinfo.h>
#include <kpopupmenu.h>
#include <kstandarddirs.h>
#include <kurifilter.h>

#include "basket.h"
#include "item.h"
#include "itemfactory.h"
#include "itemdrag.h"
#include "linklabel.h"
#include "global.h"
#include "settings.h"
#include "keyboard.h"

#include "debugwindow.h"

/** Create items from scratch (just a content) */

00061 Item* ItemFactory::createItemText(const QString &text, Basket *parent, const QString &annotations)
{
      Item *item = new Item(createFileForNewItem(parent, "txt"), Settings::defTextFont(), Settings::defTextColor(),
                            annotations, false, parent);
      item->setText(text);
      parent->insertItem(item);
      parent->save();

      return item;
}

Item* ItemFactory::createItemHtml(const QString &html, Basket *parent, const QString &annotations)
{
      Item *item = new Item(createFileForNewItem(parent, "html"), //Settings::defHtmlShowSource(),
                            annotations, false, parent);
      item->setHtml(html);
      parent->insertItem(item);
      parent->save();

      return item;
}

Item* ItemFactory::createItemLink(const KURL &url, Basket *parent, const QString &annotations)
{
      Item *item = new Item(url, titleForURL(url), iconForURL(url), true, true,
                            annotations, false, parent);
      parent->insertItem(item);
      parent->save();

      return item;
}

// FIXME: , title = QString(), ???

// The same as ItemFactory::createItemLink but with a defined title :
Item* ItemFactory::createItemLink(const KURL &url, const QString &title, Basket *parent, const QString &annotations)
{
      Item *item = new Item(url, title, iconForURL(url), /*autoTitle=*/false, true,
                            annotations, false, parent);
      parent->insertItem(item);
      parent->save();

      return item;
}

Item* ItemFactory::createItemImage(const QPixmap &image, Basket *parent, const QString &annotations)
{
      Item *item = new Item(createFileForNewItem(parent, "png"), Item::Image, // Take care of image format
                            annotations, false, parent);
      item->setPixmap(image);
      parent->insertItem(item);
      parent->save();

      return item;
}

Item* ItemFactory::createItemColor(const QColor &color, Basket *parent, const QString &annotations)
{
      Item *item = new Item(color,
                            annotations, false, parent);
      parent->insertItem(item);
      parent->save();

      return item;
}

/** Return a string list containing {url1, title1, url2, title2, url3, title3...}
  */
00129 QStringList ItemFactory::textToURLList(const QString &text)
{
      // List to return:
      QStringList list;

      // Split lines:
      QStringList texts = QStringList::split('\n', text);

      // For each lines:
      QStringList::iterator it;
      for (it = texts.begin(); it != texts.end(); ++it) {
            // Strip white spaces:
            (*it) = (*it).stripWhiteSpace();

            // Don't care of empty entries:
            if ((*it).isEmpty())
                  continue;

            // Compute lower case equivalent:
            QString ltext = (*it).lower();

            /* Search for mail address ("*@*.*" ; "*" can contain '_', '-', or '.') and add protocol to it */
            QString mailExpString = "[\\w-\\.]+@[\\w-\\.]+\\.[\\w]+";
            QRegExp mailExp("^"+mailExpString+"$");
            if (mailExp.search(ltext) != -1) {
                  ltext.insert(0, "mailto:");
                  (*it).insert(0, "mailto:");
            }

            // TODO: Recognize "<link>" (link between '<' and '>')
            // TODO: Replace " at " by "@" and " dot " by "." to look for e-mail addresses

            /* Search for mail address like "Name <address@provider.net>" */
            QRegExp namedMailExp("^([\\w\\s]+)\\s<("+mailExpString+")>$");
            //namedMailExp.setCaseSensitive(true); // For the name to be keeped with uppercases // DOESN'T WORK !
            if (namedMailExp.search(ltext) != -1) {
                  QString name    = namedMailExp.cap(1);
                  QString address = "mailto:" + namedMailExp.cap(2);
                  // Threat it NOW, as it's an exception (it have a title):
                  list.append(address);
                  list.append(name);
                  continue;
            }

            /* Search for an url and create an URL item */
            if ( ltext.startsWith("/") && ltext[1] != '/' && ltext[1] != '*' || // Take files but not C/C++/... comments !
                   ltext.startsWith("file:")   ||
                   ltext.startsWith("http://") ||
                   ltext.startsWith("www.")    ||
                   ltext.startsWith("ftp")     ||
                   ltext.startsWith("mailto:")    ) {

                  // First, correct the text to use the good format for the url
                  if (ltext.startsWith( "/"))
                        (*it).insert(0, "file:");
                  if (ltext.startsWith("www."))
                        (*it).insert(0, "http://");
                  if (ltext.startsWith("ftp."))
                        (*it).insert(0, "ftp://");

                  // And create the Url item (or launcher if URL point a .desktop file)
                  list.append(*it);
                  list.append(""); // We don't have any title
            } else
                  return QStringList(); // FAILED: treat the text as a text, and not as a URL list!
      }
      return list;
}

Item* ItemFactory::createItem(const QString &text, Basket *parent, const QString &annotations)
{
      /* Search for a color (#RGB , #RRGGBB , #RRRGGGBBB , #RRRRGGGGBBBB) and create a color item */
      QRegExp exp("^#(?:[a-fA-F\\d]{3}){1,4}$");
      if ( exp.search(text) != -1 )
            return createItemColor(QColor(text), parent, annotations);

      /* Try to convert the text as a URL or a list of URLs */
      QStringList uriList = textToURLList(text);
      if ( ! uriList.isEmpty() ) {
            Item *lastItem = 0L;
            //bool insertAfter  = parent->m_insertAfter; // To past all URLS at the same place (and not one in the right place
                                                       //  and the others at begin or end)
            QStringList::iterator it;
            for (it = uriList.begin(); it != uriList.end(); ++it) {
                  QString url = (*it);
                  ++it;
                  QString title = (*it);
                  if (title.isEmpty())
                        lastItem = createItemLinkOrLauncher(KURL(url), parent);
                  else
                        lastItem = createItemLink(KURL(url), title, parent);
                  // After last item was inserted, the insert position is reseted : re-set it
                  parent->m_insertAtItem = lastItem;
                  parent->m_insertAfter  = true;
            }
            return lastItem; // It don't return ALL inserted items !
      }

      QString newText = text.stripWhiteSpace(); // The text for a new item, without useless spaces
      /* Else, it's a text or an HTML item, so, create it */
      if (QStyleSheet::mightBeRichText(newText))   // ISITUSEFULL: Verify all lines ?
            return createItemHtml(newText, parent, annotations);
      else
            return createItemText(newText, parent, annotations);
}

Item* ItemFactory::createItemLauncher(const KURL &url, Basket *parent)
{
      if (url.isEmpty())
            return createItemLauncher("", "", "", parent);
      else
            return copyFileAndLoad(url, parent);
}

Item* ItemFactory::createItemLauncher(const QString &command, const QString &name, const QString &icon, Basket *parent)
{
      QString fileName = createItemLauncherFile(command, name, icon, parent);
      if (fileName.isEmpty())
            return 0L;
      else
            return loadFile(fileName, parent);
}

QString ItemFactory::createItemLauncherFile(const QString &command, const QString &name, const QString &icon, Basket *parent)
{
      QString content = QString(
            "[Desktop Entry]\n"
            "Exec=%1\n"
            "Name=%2\n"
            "Icon=%3\n"
            "Encoding=UTF-8\n"
            "Type=Application\n").arg(command, name, icon.isEmpty() ? "exec" : icon);
      QString fileName = fileNameForNewItem(parent, "launcher.desktop");
      QString fullPath = parent->fullPathForFileName(fileName);
      parent->dontCareOfCreation(fullPath);
      QFile file(fullPath);
      if ( file.open(IO_WriteOnly) ) {
            QTextStream stream(&file);
            stream.setEncoding(QTextStream::UnicodeUTF8);
            stream << content;
            file.close();
            return fileName;
      } else
            return QString();
}

00275 Item* ItemFactory::createItemLinkOrLauncher(const KURL &url, Basket *parent)
{
      // IMPORTANT: we create the service ONLY if the extension is ".desktop".
      //            Otherwise, KService take a long time to analyse all the file
      //            and output such things to stdout:
      //            "Invalid entry (missing '=') at /my/file.ogg:11984"
      //            "Invalid entry (missing ']') at /my/file.ogg:11984"...
      KService::Ptr service;
      if (url.fileName().endsWith(".desktop"))
            service = new KService(Item::urlWithoutProtocol(url));

      // If link point to a .desktop file then add a launcher, otherwise it's a link
      if (service && service->isValid())
            return createItemLauncher(url, parent);
      else
            return createItemLink(url, parent);
}

#include <qstrlist.h>
#include <qimage.h>

00296 void ItemFactory::dropItem(QMimeSource *source, Basket *parent, bool fromDrop, QDropEvent::Action action, Item *itemSource)
{
      Item *item = 0L;

      /* No data */
      if (source->format(0) == 0L) {
            // TODO: add a parameter to say if it's from a clipboard paste, a selection paste, or a drop
            //       To be able to say "The clipboard/selection/drop is empty".
//          KMessageBox::error(parent, i18n("There is no data to insert."), i18n("No Data"));
            return;
      }

      /* Debug */
      if (Global::debugWindow) {
            *Global::debugWindow << "<b>Drop :</b>";
            for(int i = 0; source->format(i); ++i)
                  if ( *(source->format(i)) )
                        *Global::debugWindow << "\t[" + QString::number(i) + "] " + QString(source->format(i));
            switch (action) { // The source want that we:
                  case QDropEvent::Copy:       *Global::debugWindow << ">> Drop action: Copy";       break;
                  case QDropEvent::Move:       *Global::debugWindow << ">> Drop action: Move";       break;
                  case QDropEvent::Link:       *Global::debugWindow << ">> Drop action: Link";       break;
                  case QDropEvent::Private:    *Global::debugWindow << ">> Drop action: Private";    break; // What is it? (Copy?)
                  case QDropEvent::UserAction: *Global::debugWindow << ">> Drop action: UserAction"; break; // Not currently
                  default:                     *Global::debugWindow << ">> Drop action: Unknown";           //  supported by QT!
            }
      }

      /* Copy or move an Item */
      if (ItemDrag::canDecode(source)) {
            if (Global::debugWindow)
                  *Global::debugWindow << ">> Drop an item";

            if ( action == QDropEvent::Move && itemSource != 0 &&     // We *move* an *item* : just reinsert it to well place
                 ! itemSource->parentBasket()->isLocked()    ) { //  Unless parent source is locked : then it's a copy
                                                                      //  TODO: Copy the file ??? Not for the moment !
                  if (itemSource->parentBasket() == parent) { // If drop in the same basket : keep the file and just move the item
                        if (Global::debugWindow)
                              *Global::debugWindow << ">> DROP : Just move the item to its new position";
                        parent->changeItemPlace(itemSource); //Just remove and reinsert
                        return;
                  } else { // Drop to another basket
                         // Another way to do the thing : move the file (eventually rename it) : will be deleted
                         //  for the original basket (so, old item deleted) and insert the new item.
                        if (Global::debugWindow)
                              *Global::debugWindow << ">> DROP : Move to another basket";
                        item = ItemDrag::decode(source, parent, /*move=*/true); // Filename will be kept
                        parent->insertItem(item);                               // (eventualy with a number added)
                        parent->save();
                        //if (!item->useFile()) // If useFile(), ItemDrag::decode() will move the file!
                        // FIXME: text/html/image items aren't managed the same way other useFile() items are :-/
                        if (item->type() != Item::Animation && item->type() != Item::Sound    &&
                            item->type() != Item::File      && item->type() != Item::Launcher &&
                            item->type() != Item::Unknow)
                              ((Item*)itemSource)->parentBasket()->delItem(itemSource, false); // Delete the item, & file whereas it's a mirror
                        return;
                  }
            } // Else, at this point it's a copy (link an item not supported : act as a copy !)

            // Do not try to copy the file, because if it is a cut and paste, item file could be deleted and then re-created
            item = ItemDrag::decode(source, parent, /*move=*/false);
            parent->insertItem(item);
            parent->save();
            return;
      }

      /* Else : Drop object to item */

      QPixmap pixmap;
      if ( QImageDrag::decode(source, pixmap) ) {
            createItemImage(pixmap, parent);
            return;
      }

      // KColorDrag::decode() is buggy and can trheat strings like "#include <foo.h>" as a black color
      // The correct "ideal" code:
      /*QColor color;
      if ( KColorDrag::decode(source, color) ) {
            createItemColor(color, parent);
            return;
      }*/
      // And then the hack (if provide color MIME type or a text that contains color), using createItem Color RegExp:
      QString hack;
      QRegExp exp("^#(?:[a-fA-F\\d]{3}){1,4}$");
      if (source->provides("application/x-color") ||
           (QTextDrag::decode(source, hack) && (exp.search(hack) != -1)) ) {
            QColor color;
            if ( KColorDrag::decode(source, color) )
                  if (createItemColor(color, parent))
                        return;
                  // Theorically it should be returned. If not, continue by dropping other things
      }

      KURL::List urls;
      if ( KURLDrag::decode(source, urls) ) {
            if (!fromDrop) // If it's a Paste, we should know if files should be copied (copy&paste) or moved (cut&paste)
                  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' )
                              action = QDropEvent::Move;
                  }
            dropURLs(urls, parent, action, fromDrop);
            return;
      }

      // FIXME: use dropURLs() also from Mozilla?

      /*
       * Mozilla's stuff sometimes uses utf-16-le - little-endian UTF-16.
       *
       * This has the property that for the ASCII subset case (And indeed, the
       * ISO-8859-1 subset, I think), if you treat it as a C-style string,
       * it'll come out to one character long in most cases, since it looks
       * like:
       *
       * "<\0H\0T\0M\0L\0>\0"
       *
       * A strlen() call on that will give you 1, which simply isn't correct.
       * That might, I suppose, be the answer, or something close.
       *
       * Also, Mozilla's drag/drop code predates the use of MIME types in XDnD
       * - hence it'll throw about STRING and UTF8_STRING quite happily, hence
       * the odd named types.
       *
       * Thanks to Dave Cridland for having said me that.
       */
      if (source->provides("text/x-moz-url")) { // FOR MOZILLA
            // Get the array and create a QChar array of 1/2 of the size
            QByteArray mozilla = source->encodedData("text/x-moz-url");
            QMemArray<QChar> chars( mozilla.count() / 2 );
            // A small debug work to know the value of each bytes
            if (Global::debugWindow)
                  for (uint i = 0; i < mozilla.count(); i++)
                        *Global::debugWindow << QString("'") + QChar(mozilla[i]) + "' " + QString::number(int(mozilla[i]));
            // text/x-moz-url give the URL followed by the link title and separed by OxOA (10 decimal: new line?)
            uint size   = 0;
            QChar *name = 0L;
            // For each little endian mozilla chars, copy it to the array of QChars
            for (uint i = 0; i < mozilla.count(); i += 2) {
                  chars[i/2] = QChar(mozilla[i], mozilla[i+1]);
                  if (mozilla[i] == 0x0A) {
                        size = i/2;
                        name = &(chars[i/2+1]);
                  }
            }
            // Create a QString that take the address of the first QChar and a length
            if (name == 0L) { // We haven't found name (FIXME: Is it possible ?)
                  QString normalHtml(&(chars[0]), chars.size());
                  createItemLink(normalHtml, parent);
            } else {
                  QString normalHtml(  &(chars[0]), size               );
                  QString normalTitle( name,        chars.size()-size-1);
                  createItemLink(normalHtml, normalTitle, parent);
            }
            return;
      }

      if (source->provides("text/html")) {
            QString html;
            QCString subtype("html");
            // If the text/html comes from Mozilla or GNOME it can be UTF-16 encoded: we need ExtendedTextDrag to check that
            ExtendedTextDrag::decode(source, html, subtype);
            createItemHtml(html, parent);
            return;
      }

      QString text;
      // If the text/plain comes from GEdit or GNOME it can be empty: we need ExtendedTextDrag to check other MIME types
      if ( ExtendedTextDrag::decode(source, text) ) {
            createItem(text, parent);
            return;
      }

      /* Unsucceful drop */
      createItemUnknow(source, parent, "");
      QString message = i18n("<p>BasKet doesn't support the data you've dropped.<br>"
                             "It however created a generic item, allowing you to drag or copy "
                             "them to an application that understand them.</p>"
                             "If you want BasKet to support them, please contact developer or visit the "
                             "<a href=\"http://basket.kde.org/dropdb.php\">BasKet Drop Database</a>.</p>");
      KMessageBox::information(parent, message, i18n("Unsupported MIME Type(s)"),
                               "unsupportedDropInfo", KMessageBox::AllowLink);
      return;
}

Item* ItemFactory::createItemUnknow(QMimeSource *source, Basket *parent, const QString &annotations)
{
      // Save the MimeSource in a file: create and open the file:
      QString fileName = createFileForNewItem(parent, "unknow");
      QFile file(parent->fullPath() + fileName);
      if ( ! file.open(IO_WriteOnly) )
            return 0L;
      QDataStream stream(&file);

      // Echo MIME types:
      for (int i = 0; source->format(i); ++i)
            if ( *(source->format(i)) )
                  stream << QString(source->format(i)); // Output the '\0'-terminated format name string

      // Echo end of MIME types list delimiter:
      stream << "";

      // Echo the length (in bytes) and then the data, and then same for next MIME type:
      for (int i = 0; source->format(i); ++i)
            if ( *(source->format(i)) ) {
                  QByteArray data = source->encodedData(source->format(i));
                  stream << (Q_UINT32)data.count();
                  stream.writeRawBytes(data.data(), data.count());
            }
      file.close();

      Item *item = new Item(fileName, Item::Unknow, annotations, false, parent);
      item->loadContent(); // FIXME: Why not loadContent() in the contructor ???????????? I think I should
      parent->insertItem(item);
      parent->save();

      return item;
}

void ItemFactory::dropURLs(KURL::List urls, Basket *parent, QDropEvent::Action action, bool fromDrop)
{
      Item *item;

      // Drop ONLY LINKS for this type of basket!
      // This prevent cutting a file in Konqueror to be immediatly pasted into BasKet!!
      if (parent->isAClipboard()) {
            for ( KURL::List::iterator it = urls.begin(); it != urls.end(); ++it ) {
                  item = createItemLinkOrLauncher(*it, parent);
                  // After last item was inserted, the insert position is reseted : re-set it
                  parent->m_insertAtItem = item;
                  parent->m_insertAfter  = true;
            }
            return;
      }

      // TODO: Basket::insertItems(QPtrList<Item> items) or Basket::insertItemAndDon'tReset(Item *item)
      //bool insertAfter  = parent->m_insertAfter; // To past all URLS at the same place (and not one in the right place
                                                 //  and the others at begin or end)
      // BEGIN Insertion work
      int shouldAsk = 0; // shouldAsk==0: don't ask ; shouldAsk==1: ask for "file" ; shouldAsk>=2: ask for "files"
      bool shiftPressed = Keyboard::shiftPressed();
      bool ctrlPressed  = Keyboard::controlPressed();
      bool modified = fromDrop && (shiftPressed || ctrlPressed);

      if (modified) // Then no menu + modified action
            ; // action is already set: no work to do
      else if (fromDrop) { // Compute if user should be asked or not
            for ( KURL::List::iterator it = urls.begin(); it != urls.end(); ++it )
                  if ((*it).protocol() != "mailto") { // Do not ask when dropping mail address :-)
                        shouldAsk++;
                        if (shouldAsk == 1/*2*/) // Sufficient
                              break;
                  }
            if (shouldAsk) {
                  KPopupMenu menu(parent);
                  menu.insertItem( SmallIconSet("goto"),     i18n("&Move Here"), 0 );
                  menu.insertItem( SmallIconSet("editcopy"), i18n("&Copy Here"), 1 );
                  menu.insertItem( SmallIconSet("html"),     i18n("&Link Here"), 2 );
                  menu.insertSeparator();
                  menu.insertItem( SmallIconSet("cancel"),   i18n("C&ancel"),    3 );
                  int id = menu.exec(QCursor::pos());
                  switch (id) {
                        case 0: action = QDropEvent::Move; break;
                        case 1: action = QDropEvent::Copy; break;
                        case 2: action = QDropEvent::Link; break;
                        default: return;
                  }
                  modified = true;
            }
      } else { // fromPaste
            ;
      }

      /* Policy of drops of URL:
       *   Email: [Modifier keys: Useless]
       +    - Link mail address
       *   Remote URL: [Modifier keys: {Copy,Link}]
       +    - Download as Image, Animation and Launcher
       +    - Link other URLs
       *   Local URL: [Modifier keys: {Copy,Move,Link}]
       *    - Copy as Image, Animation and Launcher (or mirror?) [Modifier keys: {Copy,Move,Link}]
       *    - Link directory [Modifier keys: Useless]
       *    - Make Launcher of executable [Modifier keys: {Copy_exec,Move_exec,Link_Launcher}]
       *    - Ask for file (if use want to copy and it is a sound: make Sound)
       * Policy of pastes of URL: [NO modifier keys]
       *   - Same as drops
       *   - But copy when ask should be done
       *   - Unless cut-selection is true: move files instead
       * Policy of file created in the basket dir: [NO modifier keys]
       *   - View as Image, Animation, Sound, Launcher
       *   - View as File
       */
      for ( KURL::List::iterator it = urls.begin(); it != urls.end(); ++it ) {
            if ( ((*it).protocol() == "mailto") ||
                 (action == QDropEvent::Link)    )
                  item = createItemLinkOrLauncher(*it, parent);
            else if (!(*it).isLocalFile()) {
                  if ( action != QDropEvent::Link && (maybeImageOrAnimation(*it)/* || maybeSound(*it)*/) )
                        item = copyFileAndLoad(*it, parent);
                  else
                        item = createItemLinkOrLauncher(*it, parent);
            } else {
                  if (action == QDropEvent::Copy)
                        item = copyFileAndLoad(*it, parent);
                  else if (action == QDropEvent::Move)
                        item = moveFileAndLoad(*it, parent);
                  else
                        item = createItemLinkOrLauncher(*it, parent);
            }

            // END Insertion work
            // After last item was inserted, the insert position is reseted : re-set it
            parent->m_insertAtItem = item;
            parent->m_insertAfter  = true;
      }
}

// mayBeLauncher: url.url().endsWith(".desktop");

00615 bool ItemFactory::maybeText(const KURL &url)
{
      QString path = url.url().lower();
      return path.endsWith(".txt");
}

bool ItemFactory::maybeHtml(const KURL &url)
{
      QString path = url.url().lower();
      return path.endsWith(".html") || path.endsWith(".htm");
}

bool ItemFactory::maybeImageOrAnimation(const KURL &url)
{
      /* Examples on my machine:
            QImageDrag can understands
                  {"image/png", "image/bmp", "image/jpeg", "image/pgm", "image/ppm", "image/xbm", "image/xpm"}
            QImageIO::inputFormats() returns
                  {"BMP", "GIF", "JPEG", "MNG", "PBM", "PGM", "PNG", "PPM", "XBM", "XPM"}
            QImageDecoder::inputFormats():
                  {"GIF", "MNG", "PNG"} */
      QStrList list = QImageIO::inputFormats();
      list.prepend("jpg"); // Since QImageDrag return only "JPEG" and extensions can be "JPG"; preprend for heuristic optim.
      char *s;
      QString path = url.url().lower();
      for (s = list.first(); s; s = list.next())
            if (path.endsWith(QString(".") + QString(s).lower()))
                  return true;
      // TODO: Search real MIME type for local files?
      return false;
}

bool ItemFactory::maybeAnimation(const KURL &url)
{
      QString path = url.url().lower();
      return path.endsWith(".mng") || path.endsWith(".gif");
}

bool ItemFactory::maybeSound(const KURL &url)
{
      QString path = url.url().lower();
      return path.endsWith(".mp3") || path.endsWith(".ogg");
}

bool ItemFactory::maybeLauncher(const KURL &url)
{
      QString path = url.url().lower();
      return path.endsWith(".desktop");
}

////////////// NEW:

Item* ItemFactory::copyFileAndLoad(const KURL &url, Basket *parent)
{
      QString fileName = fileNameForNewItem(parent, url.fileName());
      QString fullPath = parent->fullPathForFileName(fileName);

      if (Global::debugWindow)
            *Global::debugWindow << "copyFileAndLoad: " + url.prettyURL() + " to " + fullPath;

      QString annotations = i18n("Original file: %1").arg(url.prettyURL());
      parent->dontCareOfCreation(fullPath);
      KIO::CopyJob *copyJob = KIO::copy(url, KURL(fullPath));
      parent->connect( copyJob,  SIGNAL(copyingDone(KIO::Job *, const KURL &, const KURL &, bool, bool)),
                       parent, SLOT(slotCopyingDone(KIO::Job *, const KURL &, const KURL &, bool, bool)) );

      Item::Type type = typeForURL(url, parent); // Use the type of the original file because the target doesn't exist yet
      Item *item = new Item(fileName, type, annotations, false, parent);
      parent->insertItem(item);
      parent->save();

      return item;
}

Item* ItemFactory::moveFileAndLoad(const KURL &url, Basket *parent)
{
      // Globally the same as copyFileAndLoad() but move instead of copy (KIO::move())
      QString fileName = fileNameForNewItem(parent, url.fileName());
      QString fullPath = parent->fullPathForFileName(fileName);

      if (Global::debugWindow)
            *Global::debugWindow << "moveFileAndLoad: " + url.prettyURL() + " to " + fullPath;

      QString annotations = i18n("Original file: %1").arg(url.prettyURL());
      parent->dontCareOfCreation(fullPath);
      KIO::CopyJob *copyJob = KIO::move(url, KURL(fullPath));
      parent->connect( copyJob,  SIGNAL(copyingDone(KIO::Job *, const KURL &, const KURL &, bool, bool)),
                       parent, SLOT(slotCopyingDone(KIO::Job *, const KURL &, const KURL &, bool, bool)) );

      Item::Type type = typeForURL(url, parent); // Use the type of the original file because the target doesn't exist yet
      Item *item = new Item(fileName, type, annotations, false, parent);
      parent->insertItem(item);
      parent->save();

      return item;
}

Item* ItemFactory::loadFile(const QString &fileName, Basket *parent, const QString &annotations)
{
      Item *item = 0L;

      // The file MUST exists
      QFileInfo file( Item::urlWithoutProtocol(parent->fullPathForFileName(fileName)) );
      if ( ! file.exists() )
            return 0L;

      Item::Type type = typeForURL(parent->fullPathForFileName(fileName), parent);
      item = new Item(fileName, type, annotations, false, parent);
      item->loadContent();
      parent->insertItem(item);
      parent->save();

      return item;
}

Item::Type ItemFactory::typeForURL(const KURL &url, Basket *parent)
{
/*    KMimeType::Ptr kMimeType = KMimeType::findByURL(url);
      if (Global::debugWindow)
            *Global::debugWindow << "typeForURL: " + kMimeType->parentMimeType();//property("MimeType").toString();*/
      bool viewText  = parent->viewFileContent(Basket::FileText);
      bool viewHTML  = parent->viewFileContent(Basket::FileHTML);
      bool viewImage = parent->viewFileContent(Basket::FileImage);
      bool viewSound = parent->viewFileContent(Basket::FileSound);

      KFileMetaInfo metaInfo(url);
      if (Global::debugWindow && metaInfo.isEmpty())
            *Global::debugWindow << "typeForURL: metaInfo is empty for " + url.prettyURL();
      if (metaInfo.isEmpty()) { // metaInfo is empty for GIF files on my machine !
            if      (viewText  && maybeText(url))             return Item::Text;
            else if (viewHTML  && (maybeHtml(url)))           return Item::Html;
            else if (viewImage && maybeAnimation(url))        return Item::Animation; // See Item::movieStatus(int)
            else if (viewImage && maybeImageOrAnimation(url)) return Item::Image;     //  for more explanations
            else if (viewSound && maybeSound(url))            return Item::Sound;
            else if (maybeLauncher(url))                      return Item::Launcher;
            else                                              return Item::File;
      }
      QString mimeType = metaInfo.mimeType();

      if (Global::debugWindow)
            *Global::debugWindow << "typeForURL: " + url.prettyURL() + " ; MIME type = " + mimeType;

      if      (mimeType == "application/x-desktop")            return Item::Launcher;
      else if (viewText  && mimeType.startsWith("text/plain")) return Item::Text;
      else if (viewHTML  && mimeType.startsWith("text/html"))  return Item::Html;
      else if (viewImage && mimeType == "movie/x-mng")         return Item::Animation;
      else if (viewImage && mimeType == "image/gif")           return Item::Animation;
      else if (viewImage && mimeType.startsWith("image/"))     return Item::Image;
      else if (viewSound && mimeType.startsWith("audio/"))     return Item::Sound;
      else                                                     return Item::File;
}

QString ItemFactory::fileNameForNewFile(const QString &wantedName, const QString &destFolder)
{
      QString fileName  = wantedName;
      QString fullName  = destFolder + fileName;
      QString extension = "";
      int     number    = 2;
      QDir    dir;

      // First check if the file do not exists yet (simplier and more often case)
      dir = QDir(fullName);
      if ( ! dir.exists(fullName) )
            return fileName;

      // Find the file extension, if it exists : Split fileName in fileName and extension
      // Example : fileName == "item5-3.txt" => fileName = "item5-3" and extension = ".txt"
      int extIndex = fileName.findRev('.');
      if (extIndex != -1 && extIndex != int(fileName.length()-1))  { // Extension found and fileName do not ends with '.' !
            extension = fileName.mid(extIndex);
            fileName.truncate(extIndex);
      } // else fileName = fileName and extension = ""

      // Find the file number, if it exists : Split fileName in fileName and number
      // Example : fileName == "item5-3" => fileName = "item5" and number = 3
      int extNumber = fileName.findRev('-');
      if (extNumber != -1 && extNumber != int(fileName.length()-1))  { // Number found and fileName do not ends with '-' !
            bool isANumber;
            int  theNumber = fileName.mid(extNumber + 1).toInt(&isANumber);
            if (isANumber) {
                  number = theNumber;
                  fileName.truncate(extNumber);
            } // else :
      } // else fileName = fileName and number = 2 (because if the file already exists, the genereated name is at last the 2nd)

      QString finalName;
      for (/*int number = 2*/; ; ++number) { // TODO: FIXME: If overflow ???
            finalName = fileName + "-" + QString::number(number) + extension;
            fullName = destFolder + finalName;
            dir = QDir(fullName);
            if ( ! dir.exists(fullName) )
                  break;
      }

      return finalName;
}

QString ItemFactory::fileNameForNewItem(Basket *parent, const QString &wantedName)
{
      return fileNameForNewFile(wantedName, parent->fullPath());
}

// Create a file to store a new item in Basket parent and with extension extension.
// If wantedName is provided, the function will first try to use this file name, or derive it if it's impossible
//  (extension willn't be used for that case)
QString ItemFactory::createFileForNewItem(Basket *parent, const QString &extension, const QString &wantedName)
{
      static int nb = 1;

      QString fileName;
      QString fullName;

      if (wantedName.isEmpty()) {
            QDir dir;
            for (/*int nb = 1*/; ; ++nb) { // TODO: FIXME: If overflow ???
                  fileName = "item" + QString::number(nb)/*.rightJustify(5, '0')*/ + "." + extension;
                  fullName = parent->fullPath() + fileName;
                  dir = QDir(fullName);
                  if ( ! dir.exists(fullName) )
                        break;
            }
      } else {
            fileName = fileNameForNewItem(parent, wantedName);
            fullName = parent->fullPath() + fileName;
      }

      // Create the file
      parent->dontCareOfCreation(fullName);
      QFile file(fullName);
      file.open(IO_WriteOnly);
      file.close();

      return fileName;
}

QFont ItemFactory::fontForFontType(int type)
{
      QFont font;

      switch (type) {
            case 1:  font.setFamily("SansSerif"); break;
            case 2:  font.setFamily("Serif");     break;
            case 3:  font.setFamily("Courier");   break;
            default: font.setFamily("System");           // case O or another illegal type...
      }

      return font;
}

QString ItemFactory::titleForURL(const KURL &url)
{
      QString title = url.prettyURL();
      QString home  = "file:" + QDir::homeDirPath() + "/";

      if (title.startsWith("mailto:"))
            return title.remove(0, 7);

      if (title.startsWith(home))
            title = "~/" + title.remove(0, home.length());

      if (title.startsWith("file:/"))
            title = title.remove(0, 5); // 5 == QString("file:").length() - 1
      else if (title.startsWith("http://"))
            title = title.remove(0, 7); // 7 == QString("http://").length() - 1

      if ( ! url.isLocalFile() ) {
            if (title.endsWith("/index.html") && title.length() > 11)
                  title.truncate(title.length() - 11); // 11 == QString("/index.html").length()
            else if (title.endsWith("/index.htm") && title.length() > 10)
                  title.truncate(title.length() - 10); // 10 == QString("/index.htm").length()
            else if (title.endsWith("/index.xhtml") && title.length() > 12)
                  title.truncate(title.length() - 12); // 12 == QString("/index.xhtml").length()
            else if (title.endsWith("/index.php") && title.length() > 10)
                  title.truncate(title.length() - 10); // 10 == QString("/index.php").length()
            else if (title.endsWith("/index.php3") && title.length() > 11)
                  title.truncate(title.length() - 11); // 11 == QString("/index.php3").length()
            else if (title.endsWith("/index.php4") && title.length() > 11)
                  title.truncate(title.length() - 11); // 11 == QString("/index.php4").length()
            else if (title.endsWith("/index.php5") && title.length() > 11)
                  title.truncate(title.length() - 11); // 11 == QString("/index.php5").length()
      }

      if (title.length() > 2 && title.endsWith("/")) // length > 2 because "/" and "~/" shouldn't be transformed to "" and "~"
            title.truncate(title.length() - 1); // eg. transform "www.kde.org/" to "www.kde.org"

      return title;
}

QString ItemFactory::iconForURL(const KURL &url)
{
      QString icon = KMimeType::iconForURL(url.url());
      if ( url.protocol() == "mailto" )
            icon = "message";
      return icon;
}

// TODO: Can I add "autoTitle" and "autoIcon" entries to .desktop files? or just store them in basket, as now...

/* Try our better to find an icon suited to the command line
 * eg. "/usr/bin/kwrite-3.2 ~/myfile.txt /home/other/file.xml"
 * will give the "kwrite" icon!
 */
QString ItemFactory::iconForCommand(const QString &command)
{
      QString icon;

      // 1. Use first word as icon (typically the program without argument)
      icon = QStringList::split(' ', command).first();
      // 2. If the command is a full path, take only the program file name
      icon = icon.mid(icon.findRev('/') + 1); // strip path if given [But it doesn't care of such
                                                         // "myprogram /my/path/argument" -> return "argument". Would
                                                         // must first strip first word and then strip path... Useful ??
      // 3. Use characters before any '-' (e.g. use "gimp" icon if run command is "gimp-1.3")
      if ( ! isIconExist(icon) )
            icon = QStringList::split('-', icon).first();
      // 4. If the icon still not findable, use a generic icon
      if ( ! isIconExist(icon) )
            icon = "exec";

      return icon;
}

bool ItemFactory::isIconExist(const QString &icon)
{
      return ! kapp->iconLoader()->loadIcon(icon, KIcon::NoGroup, 16, KIcon::DefaultState, 0L, true).isNull();
}

00942 void ItemFactory::insertEmpty(int type, Basket *parent)
{
      Item    *item = 0L;
      QPixmap *pixmap;

      switch (type) {
            case Item::Text:
                  item = ItemFactory::createItemText("", parent);
                  break;
            case Item::Html:
                  item = ItemFactory::createItemHtml("", parent);
                  break;
            case Item::Image:
                  pixmap = new QPixmap( QSize(Settings::defImageX(), Settings::defImageY()) );
                  pixmap->fill();
                  pixmap->setMask(pixmap->createHeuristicMask());
                  item = ItemFactory::createItemImage(*pixmap, parent);
                  break;
            case Item::Link:
                  item = ItemFactory::createItemLink(KURL(), parent);
                  break;
            case Item::Launcher:
                  item = ItemFactory::createItemLauncher(KURL(), parent);
                  break;
            case Item::Color:
                  item = ItemFactory::createItemColor(Qt::black, parent);
                  break;
      }
      if (item) {
            parent->ensureVisibleItem(item);
            parent->unselectAllBut(item);
            parent->setFocusedItem(item);
            item->slotEdit();
      }
}

void ItemFactory::insertWizard(int type, Basket *parent)
{
      KOpenWithDlg *dlg = 0L;
      QString       iconName;
      KURL          url;
      Item         *item = 0L;

      switch (type) {
            case 1: // importKmenuLauncher
                  dlg = new KOpenWithDlg(parent);
                  dlg->setSaveNewApplications(true); // To create temp file, needed by createItemLauncher()
                  dlg->exec();
                  if (dlg->service()) {
                        // * locateLocal() return a local file even if it is a system wide one (local one doesn't exists)
                        // * desktopEntryPath() returns the full path for system wide ressources, but relative path if in home
                        QString serviceUrl = dlg->service()->desktopEntryPath();
                        if ( ! serviceUrl.startsWith("/") )
                              serviceUrl = dlg->service()->locateLocal(); //locateLocal("xdgdata-apps", serviceUrl);
                        item = createItemLauncher(serviceUrl, parent);
                  }
                  break;
            case 2: // importIcon
                  iconName = KIconDialog::getIcon( KIcon::Desktop, KIcon::Application, false, Settings::defIconSize() );
                  if ( ! iconName.isEmpty() )
                        item = createItemImage( DesktopIcon(iconName, Settings::defIconSize()),
                                                parent, i18n("Icon name: %1").arg(iconName) );
                                                // TODO: wantedName = iconName !!!!!!!!!!!!!!
                  break;
            case 3: //loadFromFile
                  url = KFileDialog::getOpenURL( QString::null, QString::null, parent,
                                                 i18n("Load a File Content into an Item") );
                  if ( ! url.isEmpty() )
                        item = copyFileAndLoad(url, parent);
                  break;
            case 4: // mirrorFile
                  url = KFileDialog::getOpenURL( QString::null, QString::null, parent,
                                                 i18n("Mirror a File Content into an Item") );
                  if ( ! url.isEmpty() )
                        item = loadFile(Item::urlWithoutProtocol(url), parent);
                  break;
      }
      if (item) {
            parent->ensureVisibleItem(item);
            parent->unselectAllBut(item);
            parent->setFocusedItem(item);
      }
}

Generated by  Doxygen 1.6.0   Back to index