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

item.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.             *
 ***************************************************************************/

/** Interface */
#include <qlayout.h>
#include <qlabel.h>
#include <qmovie.h>
#include <qfont.h>
#include <qinputdialog.h>
#include <kiconloader.h>
#include <ktempfile.h>
#include <qcursor.h>
#include <klocale.h>
#include <kapplication.h>
#include <qdragobject.h>
#include <kpopupmenu.h>
#include <qtooltip.h>
#include <kurldrag.h>
#include <qaction.h>
#include <kio/jobclasses.h>
#include <qpainter.h>
#include <kmessagebox.h>

/** Open URLs */
#include <kstandarddirs.h>
#include <krun.h>
#include <qfile.h>
#include <qdir.h>
#include <kfiledialog.h>
#include <qfile.h>
#include <kurifilter.h>
#include <arts/kartsdispatcher.h>
//#include <arts/kplayobjectfactory.h>
//#include <arts/kartsserver.h>

#include <kaction.h>
#include <kglobalsettings.h>

#include "basket.h"
#include "item.h"
#include "itemdrag.h"
#include "linklabel.h"
#include "itemfactory.h"
#include "settings.h"
#include "global.h"
#include "onclickaction.h"
#include "search.h"
#include "debugwindow.h"

#include "keyboard.h"

/** AlignableCheckBox */

00071 AlignableCheckBox::AlignableCheckBox(const QString &text, QWidget *parent, const char *name)
 : QWidget(parent, name != 0 ? name : "AlignableCheckBox"), m_check(this)
{
      m_layout = new QVBoxLayout(this);
      m_spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
      m_check.setText(text);
      m_layout->addWidget(&m_check);
}

AlignableCheckBox::~AlignableCheckBox()
{
}

void AlignableCheckBox::setAlignment(int align)
{
      m_layout->remove(&m_check);
      m_layout->removeItem(m_spacer);

      switch (align) {
            case 0:
                  m_layout->addWidget(&m_check);
                  m_layout->addItem(m_spacer);
                  break;
            case 1:
                  m_layout->addWidget(&m_check);
                  break;
            case 2:
                  m_layout->addItem(m_spacer);
                  m_layout->addWidget(&m_check);
                  break;
      }
}

void AlignableCheckBox::setFocusPolicy(QWidget::FocusPolicy policy)
{
      m_check.setFocusPolicy(policy);
}

void AlignableCheckBox::setCheckCursor(const QCursor &cursor)
{
      m_check.setCursor(cursor);
}

/** Item */

00116 Item::Item(const QString &fileName, int fontType, const QColor &fontColor,
           const QString &annotations, bool checked, Basket *parent)
 : QFrame(parent), m_parentBasket(parent), m_fileName(fileName)
{
      m_type = Text;
      initItem(checked);
      m_item->setTextFormat(Qt::PlainText);
      setTextStyle(fontType, fontColor);
      setAnnotations(annotations);
}

Item::Item(const QString &fileName, //bool showSource,
           const QString &annotations, bool checked, Basket *parent)
 : QFrame(parent), m_parentBasket(parent), m_fileName(fileName)
{
      m_type = Html;
      initItem(checked);
      //setShowSource(showSource);
      setAnnotations(annotations);
}

Item::Item(const KURL &url, const QString &title, const QString &icon,
           bool autoTitle, bool autoIcon,
           const QString &annotations, bool checked, Basket *parent)
 : QFrame(parent), m_parentBasket(parent), m_url(0L), m_title(0L), m_icon(0L)
{
      m_type = Link;
      initItem(checked);
      setUrl(url, title, icon);
      setAuto(autoTitle, autoIcon);
      setAnnotations(annotations);
}

Item::Item(const QString &fileName, Type type,
           const QString &annotations, bool checked, Basket *parent)
 : QFrame(parent), m_parentBasket(parent), m_fileName(fileName)
{
//    if (type != Image && type != Animation && type != Sound && type != File && type != Launcher && type != Unknow)
//          ASSERT it shouldnt appears!

      m_type = type;
      initItem(checked);
      // I now allow Text, Html and File items to be loaded through this constructor
      // But it's quite special (uggly?): from ItemFactory
      if (m_type == Text) {
            m_item->setTextFormat(Qt::PlainText);
            setTextStyle(Settings::defTextFont(), Settings::defTextColor());
      }// else if (m_type == Html)
      //    setShowSource(false);
      else if (m_type == Sound || m_type == File || m_type == Launcher)
            //setFile(); // Because after a LinkLabel creation, setLook SHOULD be called (if not, hightForWidth will cause crash)
            m_linkLabel->setLook(LinkLook::fileLook);
      setAnnotations(annotations);
}

Item::Item(const QColor  &color,
           const QString &annotations, bool checked, Basket *parent)
 : QFrame(parent), m_parentBasket(parent), m_color(0L)
{
      m_type = Color;
      initItem(checked);
      setColor(color);
      setAnnotations(annotations);
}

Item::~Item()
{
}

/** Create needed objects / widgets and initialize them with common properties (including isChecked)
  *  m_type must be initialized before call initItem
  */
// FIXME: To avoid errors, it's better to do:
//        void Item::initItem(Type type, bool checked)
00190 void Item::initItem(bool checked)
{
      if (Global::debugWindow)
            *Global::debugWindow << "Item[" + fullPath() +"] Created";

      m_layout = new QHBoxLayout(this, margins, margins);

      m_check = new AlignableCheckBox(QString(), this);
      m_check->setShown( m_parentBasket->showCheckBoxes() );
      m_check->setSizePolicy( QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding, 0, 0, false) );
      m_check->setEnabled( ! m_parentBasket->isLocked() );
      m_check->checkBox()->setChecked( checked );
      m_check->setCheckCursor(QCursor(Qt::ArrowCursor)); // Since a click to open just check/uncheck
      m_layout->addWidget(m_check);                      //   m_check, assign "normal" cursor
      m_check->setFocusPolicy(QWidget::NoFocus);

      if (useLinkLabel()) {
            m_linkLabel = new LinkLabel(m_parentBasket->hAlign(), m_parentBasket->vAlign(), this);
            m_layout->addWidget(m_linkLabel);
            m_item = 0L;
      } else {
            m_item = new QLabel(this);
            m_item->setScaledContents(false); // I could speed-up draws (not timered it : perhapse not so important)
            m_layout->addWidget(m_item);
            m_linkLabel = 0L;
      }

      alignChanged( m_parentBasket->hAlign(), m_parentBasket->vAlign() );

      connect( m_check->checkBox(), SIGNAL(clicked()), this, SLOT(slotChecked()) );
      connect( &m_selectTimer,      SIGNAL(timeout()), this, SLOT(select())      );

      m_playObj    = 0L;
      m_playServer = 0L;
      m_previous   = 0L;
      m_next       = 0L;
      m_isSelected = false;
      m_isFocused  = false;
      m_canDrag    = false;
      m_wasPressed = false;
}

QString Item::typeName()
{
      switch (m_type) {
            case Text:      return i18n("Text");
            case Html:      return i18n("Rich Text");
            case Image:     return i18n("Image");
            case Animation: return i18n("Animation");
            case Sound:     return i18n("Sound");
            case File:      return i18n("File");
            case Link:      return i18n("Link");
            case Launcher:  return i18n("Launcher");
            case Color:     return i18n("Color");
            case Unknow:    return i18n("Unknown");
      }
      return QString();
}

QString Item::lowerTypeName()
{
      switch (m_type) {
            case Text:      return "text";
            case Html:      return "html";
            case Image:     return "image";
            case Animation: return "animation";
            case Sound:     return "sound";
            case File:      return "file";
            case Link:      return "link";
            case Launcher:  return "launcher";
            case Color:     return "color";
            case Unknow:    return "unknow";
      }
      return QString();
}

QString Item::toString()
{
      switch (m_type) {
            case Text:     return text();
            case Html:     return html();
//          case Image:    return QString();
//          case Animtion: return QString(); //
//          case Sound:    return QString(); //
//          case File:     return QString(); //
            case Link:     return url().prettyURL();
//          case Launcher: return QString(); //
            case Color:    return color().name();
//          case Unknow:   return QString(); //
            default:       return QString();
      }
      return QString();
}

00284 QString Item::toHtml(const QString &imageName)
{
      switch (m_type) {
            case Text:
                  {
                        QString font;
                        switch (textFontType()) {
                              case 1:  font = " face=sans-serif"; break;
                              case 2:  font = " face=serif";      break;
                              case 3:  font = " face=monospace";  break;
                        }
                        return "<font color=" + textColor().name() + font + ">" +
                               m_parentBasket->textToHTMLWithoutP(text()) + "</font>";
                        //return m_parentBasket->textToHTMLWithoutP(text());
                  }
            case Html:
                  return Basket::htmlToParagraph(html());
            case Image:
            case Animation:
                  {
                        if ( (m_type == Image     && pixmap() == 0L) ||
                             (m_type == Animation && movie()  == 0L)    ) {
                              QMimeSourceFactory::defaultFactory()->setData(imageName, 0L);
                              return i18n("(Image)"); // Image or animation not yet loaded!!
                        }

                        QImage image;
                        if (m_type == Image)
                              image = pixmap()->convertToImage();
                        else
                              image = movie()->framePixmap().convertToImage();
                        image = image.smoothScale(200, 150, QImage::ScaleMin);
                        QPixmap pixmap = QPixmap(image);
                        QMimeSourceFactory::defaultFactory()->setPixmap(imageName, pixmap);
                        return "<img src=" + imageName + ">"; ///

/*                      // FIXME: movie isn't loaded yet: CRASH!
                        return i18n("(Image)");
                        // Not executed, because don't work:
                        QImage image;
                        if (m_type == Image)
                              image = pixmap()->convertToImage();
                        else
                              image = movie()->framePixmap().convertToImage();
                        image = image.smoothScale(200, 150, QImage::ScaleMin);
                        QPixmap pixmap = QPixmap(image);
                        QMimeSourceFactory::defaultFactory()->setPixmap(imageName, pixmap);
                        return "<img src=" + imageName + ">"; ///
      */                //TODO?: QMimeSourceFactory::defaultFactory()->setData(imageName, 0L);
                  }
            case Sound:
            case File:
                  {
                        /// FIXME: Since fullPath() doesn't exist yet, the icon rely on the extension.
                        ///        Bad if there isn't one or if it's a wrong one.
                        /*QPixmap icon = DesktopIcon(
                              ItemFactory::iconForURL(fullPath()),
                              (m_type == Sound ? LinkLook::soundLook : LinkLook::fileLook)->iconSize());
                        QMimeSourceFactory::defaultFactory()->setPixmap(imageName, icon);
                        return "<img src=" + imageName + "> " + fileName(); */ ///
                        return m_linkLabel->toHtml(imageName);
                  }
            case Link:
                  {
                        QString link = m_linkLabel->toHtml(imageName);
                        if (!autoTitle() && title() != ItemFactory::titleForURL(url().prettyURL()))
                              link += "<br><i>" + url().prettyURL() + "</i>"; ///
                        return link;
                  }
            case Launcher:
                  {
                        return m_linkLabel->toHtml(imageName);
                        //KService service(fullPath()); // service.icon()
                        //return service.name() + "<br><i>" + service.exec() + "</i>"; ///
                  }
            case Color:
                  return "<b><font color=" + color().name() + ">" + color().name() + "</font></b>";
            case Unknow:
                  return text();
      }
      return QString();
}

00367 int Item::heightForWidth(int w) const
{
      // Remove the margins size
      w -= 2 * margins;
      // If visible, remove checkBox size + spacing size (equals to margins)
      w -= (m_parentBasket->showCheckBoxes()) ? m_check->sizeHint().width() + margins : 0;

      // Now, w contains the wanted width for m_item or m_linkLabel, and not teh wanted size for this
      int h;
      if (m_item)
            h = m_item->heightForWidth(w);
      else
            h = m_linkLabel->heightForWidth(w);

      // Have a minimum size: not yet loaded items temporary get 0px (in fact 2+2 px) size:
      if (h < 16)
            h = 16;

      if (m_parentBasket->showCheckBoxes() && m_check->sizeHint().height() > h)
            return m_check->sizeHint().height() + 2 * margins; // top and bottom margins
      else
            return h + 2 * margins;
}

int Item::realXWithCheckbox() const
{
      if (m_parentBasket->showCheckBoxes())
            return (m_parentBasket->hAlign() == 2 ? 0 : m_check->sizeHint().width() + margins);
      else
            return 0;
}

int Item::realWidthWithCheckbox() const
{
      if (m_parentBasket->showCheckBoxes())
            return width() - m_check->sizeHint().width() - margins;
      else
            return width();
}

/** Events */

00409 int Item::onClickActionPolicy()
{
      if (m_type == Sound || m_type == File || m_type == Link || m_type == Launcher)
            return m_parentBasket->fileOnClickAction();
      else
            return m_parentBasket->contentOnClickAction();
}

void Item::setSelected(bool selected)
{
      if (!isShown()) // Don't select a not visible item!
            selected = false;

      if (selected == m_isSelected)
            return;

      DEBUG_WIN << QString(selected ? "Add" : "Remove") + " selected item " + QString::number((int)this) +
                   "; b.lastIn=" + QString::number((int)(parentBasket()->lastInsertedItem()));

      m_isSelected = selected;
      if (selected) {
            m_parentBasket->addSelectedItem();
            if (m_type != Color)
                  setPaletteForegroundColor( KApplication::palette().active().highlightedText() );
            if (useLinkLabel())
                  m_linkLabel->setSelected(true);
      } else {
            m_parentBasket->removeSelectedItem();
            if (m_type == Text)
                  setPaletteForegroundColor(m_textColor);
            else if (m_type != Color)
                  setPaletteForegroundColor( KApplication::palette().active().foreground() );
            if (useLinkLabel())
                  m_linkLabel->setSelected(false);
      }
}

void Item::setFocused(bool focused)
{
      if (focused == m_isFocused)
            return;

      m_isFocused = focused;
      update();
}

bool Item::isSelected()
{
      return m_isSelected;
}

00460 bool Item::match(const SearchData &data)
{
      if ( ! data.isSearching )
            return true;

      switch (m_type) {
            case Text:
                  if (text() .find(data.string, 0, false) != -1)
                        return true;
                  break;
            case Html:
                  if (html() .find(data.string, 0, false) != -1) // TODO: it will also search in HTML tags !!
                        return true;
                  break;
            case Image:
                  break;
            case Animation:
                  break;
            case Sound:
                  if (fileName().find(data.string, 0, false) != -1)
                        return true;
                  break;
            case File:
                  if (fileName().find(data.string, 0, false) != -1)
                        return true;
                  break;
            case Link:
                  if (title().find(data.string, 0, false) != -1 ||
                      url().prettyURL().find(data.string, 0, false) != -1)
                        return true;
                  break;
            case Launcher:
                  if (service()->exec().find(data.string, 0, false) != -1 ||
                      service()->name().find(data.string, 0, false) != -1   )
                        return true;
                  break;
            case Color:
                  if (color().name().find(data.string, 0, false) != -1)
                        return true;
                  break;
            case Unknow:
                  if (text() .find(data.string, 0, false) != -1)
                        return true;
                  break;
      }
      return annotations().find(data.string, 0, false) != -1;
}

bool Item::isDuplicateOf(Item *item)
{
      if (item->type() != type())
            return false;

      switch (m_type) {
            case Text:
                  return item->text() == text();
            case Html:
                  return item->html() == html();
            case Image:
                  return item->fileName() == fileName(); // FIXME: Too lazy...
            case Animation:
                  return item->fileName() == fileName(); // FIXME: Too lazy...
            case Sound:
                  return item->fileName() == fileName(); // FIXME: I'm afraid it doesn't work
            case File:                                 //        because of the copy of the file
                  return item->fileName() == fileName(); //        !!!
            case Link:
                  return item->url() == url();
            case Color:
                  return item->color() == color();
            default:
                  return false;
      }
}

void Item::setPaletteBackgroundColor(const QColor &color)
{
      QFrame::setPaletteBackgroundColor(color);
      if (useLinkLabel())
            m_linkLabel->setPaletteBackgroundColor(color);
      else
            m_item->setPaletteBackgroundColor(color);
}

void Item::setPaletteForegroundColor(const QColor &color)
{
      // Don't apply setPaletteForegroundColor() to myself, because checkboxes would also be colored
      if (useLinkLabel())
            m_linkLabel->setPaletteForegroundColor(color);
      else
            m_item->setPaletteForegroundColor(color);
}


void Item::select() // m_selectTimer expired OR clicked
{
      if (m_parentBasket->isDuringEdit() || kapp->activePopupWidget() != 0L)
            return;

      bool shiftPressed   = false;
      bool controlPressed = false;

      Keyboard::pressedKeys(shiftPressed, controlPressed);

      m_parentBasket->setFocus(); // Same as clicked in a single click config
      m_parentBasket->clicked( this, controlPressed, shiftPressed );
}

void Item::execAction()
{
      bool shiftPressed   = false;
      bool controlPressed = false;

      Keyboard::pressedKeys(shiftPressed, controlPressed);

      int state = 0;
      if (shiftPressed)
            state =  Qt::ShiftButton;
      if (controlPressed)
            state |= Qt::ControlButton;
      execAction( OnClickAction::actionForButton((Qt::ButtonState)state, onClickActionPolicy()) );
}


/*                'Single click' VS 'Double click' modes
 *              events and actions to do on those events :
 *
 *          | enter       | mousePress        | mouseRelease | doubleClick
 *  --------+-------------+-------------------+--------------+--------------
 *   Single | selectLater | select+startDrag  | launchAction |
 *   Double |             | select+startDrag  |              | launchAction
 */


void Item::enterEvent(QEvent*)
{
      if ( !m_parentBasket->isDuringDrag() && !m_parentBasket->isDuringEdit()) {
            OnClickAction::startHoverFeedback(this);
            if ( Settings::singleClick() && kapp->activePopupWidget() == 0L )
                  m_selectTimer.start(KGlobalSettings::autoSelectDelay(), true);
      }
      if (m_type == Sound) {
            KArtsDispatcher *dispatcher = new KArtsDispatcher(); // Needed for m_playObj
            dispatcher = dispatcher; // To avoid "warning: unused variable `KArtsDispatcher*dispatcher'" warning
            if (!m_playServer)
                  m_playServer = new KArtsServer();
            m_playFactory = new KDE::PlayObjectFactory(m_playServer);
            m_playObj = m_playFactory->createPlayObject(fullPath(), true);
            m_playObj->play();
      }
}

void Item::leaveEvent(QEvent*)
{
      m_selectTimer.stop();
      OnClickAction::stopHoverFeedback();

      if (m_playObj) {
            m_playObj->halt();
            delete m_playObj;
            delete m_playFactory;
            m_playObj = 0L;
      }
}

void Item::mousePressEvent(QMouseEvent *event)
{
      if (m_parentBasket->isDuringEdit()) {
//          m_wasPressed = false; // The below setFocus() will exit editing and action willn't be launched
            m_parentBasket->m_clickedToExitEdit = true; // THIS item will receive mouseReleaseEvent(), even if the
            m_parentBasket->setFocus();
            return;
      }                                               //  mouse cursor is outside of it (item has moved)

      m_parentBasket->setFocus(); // Since the basket is covered by Items we should focus it manually

      // Prepare dragging if the user will drag :
      m_wasPressed = true;        // To avoid mouseReleaseEvent just after canceled dragging
      m_pressPos   = event->globalPos();
      m_canDrag    = true;

      if (event->button() & Qt::LeftButton) {
            m_parentBasket->clicked( this, (event->state() & Qt::ControlButton), (event->state() & Qt::ShiftButton) );
            event->accept();
      } else
            event->ignore(); // Basket will receive the event to paste selection
}

void Item::mouseReleaseEvent(QMouseEvent *event)
{
      m_canDrag = false;

      if (m_parentBasket->m_clickedToExitEdit == true) { // Do not launch action if clicked an item to exit editing mode
            m_parentBasket->m_clickedToExitEdit = false;
            return;
      }

      if ( ! m_wasPressed ) // m_wasPressed is set during mousePressEvent
            return;           // When a drag begin, it is unset. Because if Escape is pressed
      m_wasPressed = false; // during drag, mouseReleaseEvent is called anyway ! Not with this variable

      if (m_parentBasket->isDuringEdit()) // No action during edit
            return;                         // This test is needed by Shift+middleClick with inline editor!

      // Trigger action only if single click setting AND the pressed button is a button to trigger actions
      if ( Settings::singleClick() && tiggerableActions(event) ) {
            execAction( OnClickAction::actionForButton(event->state(), onClickActionPolicy()) );
            event->accept();
      }
}

void Item::mouseDoubleClickEvent(QMouseEvent *event)
{
      // Trigger action only if double click setting AND the pressed button is a button to trigger actions
      if ( Settings::singleClick() || !tiggerableActions(event) )
            return;

      execAction( OnClickAction::actionForButton(event->state(), onClickActionPolicy()) );
}

bool Item::tiggerableActions(QMouseEvent *event)
{
      // Exec an action only if left button is pressed
      //  BUT ALSO IF ALT PRESSED to allow Alt+middleClick to launch actions
      //  (because KWin handle Alt+click).
      //  So it's a temporary hack to provide an alternative to alt+click !
      if ( ! (event->button() & Qt::LeftButton) &&   // If button is different of LeftButton
           ! (event->button() & Qt::MidButton)     ) //  and also different of MiddleButton
            return false;                              // => No action to do
      if ( (event->button() & Qt::MidButton) && !(event->state() & Qt::KeyButtonMask) ) // If button is Middle and without keys
            return false;                              // => It's a paste selection => No action to do
      if ( (Settings::middleAction() != 0) &&        // If there is a configured action for Shift+middleClick
           (event->button() & Qt::MidButton) && (event->state() == Qt::ShiftButton) ) // => Do nothing too
            return false;

      return true;
}

void Item::paintEvent(QPaintEvent*)
{
#if 1 // Bleeding edges: eye candy rounded selections
      QPainter paint(this);
      if (m_isSelected) {
            QColor c(paletteBackgroundColor());
            QColor b(isAlternate() ? m_parentBasket->altColor() : m_parentBasket->color());
            // Draw the 4 pixel coreners with background color
            paint.setPen(b);
            paint.drawPoint( 0,         0          );
            paint.drawPoint( 0,         height()-1 );
            paint.drawPoint( width()-1, height()-1 );
            paint.drawPoint( width()-1, 0          );
            // Draw 2 pixels rounded (mid of background color and parent background color)
            c.setRgb( (c.red()+b.red())/2, (c.green()+b.green())/2, (c.blue()+b.blue())/2 );
            paint.setPen(c);
            paint.drawPoint( 1,         0          );
            paint.drawPoint( 0,         1          );
            paint.drawPoint( 0,         height()-2 );
            paint.drawPoint( 1,         height()-1 );
            paint.drawPoint( width()-2, 0          );
            paint.drawPoint( width()-1, 1          );
            paint.drawPoint( width()-2, height()-1 );
            paint.drawPoint( width()-1, height()-2 );
      }
      /* BUGGY AND NOT SO BEAUTIFUL
        else { // Eye candy rounded items
            QColor c(paletteBackgroundColor());
            QColor b(isAlternate() ? m_parentBasket->altColor() : m_parentBasket->color());
            c.setRgb( (c.red()+b.red())/2, (c.green()+b.green())/2, (c.blue()+b.blue())/2 );
            if (previous()) {
                  paint.setPen(previous()->paletteBackgroundColor());
                  paint.drawPoint( width()-1, 0          );
                  paint.setPen(c);
                  paint.drawPoint( width()-2, 0          );
                  paint.drawPoint( width()-1, 1          );
            }
            if (next()) {
                  paint.setPen(next()->paletteBackgroundColor());
                  paint.drawPoint( 0,         height()-1 );
                  paint.setPen(c);
                  paint.drawPoint( 0,         height()-2 );
                  paint.drawPoint( 1,         height()-1 );
            }
      }*/
      if (m_isFocused) {
            paint.setPen(paletteBackgroundColor());
            QRect inRect(1, 1, width()-2, height()-2);
            paint.drawRect(inRect);
            paint.drawWinFocusRect(inRect);
      }
#else // Normal draw
      if (m_isFocused) {
            QPainter paint(this);
            paint.setPen(paletteBackgroundColor());
            paint.drawRect(rect());
            paint.drawWinFocusRect(rect());
      }
#endif
}

void Item::resizeEvent(QResizeEvent*)
{
      m_parentBasket->itemSizeChanged(this);
}

void Item::execAction(OnClickAction::Action action)
{
      // Firstly select only the clicked item :
      m_parentBasket->unselectAllBut(this);
      m_parentBasket->setFocusedItem(this); // In case of Alt+middleClick the item isn't focused (workaround for KWin bug)

      switch (action) {
            case OnClickAction::Copy:      slotCopy();            break;
            case OnClickAction::CopyToSel: slotCopySelection();   break;
            case OnClickAction::Edit:      slotEdit();            break;
            case OnClickAction::EditAnnot: slotEditAnnotations(); break;
            case OnClickAction::Open:      slotOpen();            break;
            case OnClickAction::OpenWith:  slotOpenWith();        break;
            default: Global::mainContainer->postStatusbarMessage( i18n("No action performed.") );
      }
}

void Item::mouseMoveEvent(QMouseEvent *event)
{
      if ( m_canDrag && (m_pressPos - event->globalPos()).manhattanLength() > KApplication::startDragDistance() ) {
            m_canDrag    = false;
            m_wasPressed = false;
            dragItem();
      }
}

void Item::dragItem()
{
      OnClickAction::stopHoverFeedback();

      QDragObject *d = new ItemMultipleDrag(this, false, this); // d will be deleted by QT

      /*bool shouldRemove = */d->drag();

      // Never delete because URL is dragged and the file must be available for the extern appliation
//    if (shouldRemove && d->target() == 0) // If target is another application that request to remove the item
//          emit wantDelete(this);
}

/** Slots */

00805 void Item::slotEdit(bool editAnnotations)
{
      m_parentBasket->editItem(this, editAnnotations);
}

void Item::slotEditAnnotations()
{
      slotEdit(true);
}

void Item::slotDelete()
{
      emit wantDelete(this);
}

void Item::slotCut()
{
      int countDeleteds = m_parentBasket->countSelecteds();

      slotCopy(/*toSelection=*/false, /*cutting=*/true);
      if (!useFile()) // If useFile(), slotCopy() (and more precisly new ItemMultipleDrag()) will delete it
            slotDelete();

      Global::mainContainer->postStatusbarMessage(
            i18n("Cutted item to clipboard.", "Cutted items to clipboard.", countDeleteds) );
}

void Item::slotCopy(bool toSelection, bool cutting)
{
      QClipboard *cb = KApplication::clipboard();
      QClipboard::Mode mode = (toSelection ? QClipboard::Selection : QClipboard::Clipboard);

      cb->setData( new ItemMultipleDrag(this, cutting, 0), mode ); // ItemMultipleDrag will be deleted by QT

      if (cutting)
            return; // Do not display feedback since slotCut() will do it

      if (toSelection)
            Global::mainContainer->postStatusbarMessage(
                  i18n("Copied item to selection.", "Copied items to selection.", m_parentBasket->countSelecteds()) );
      else
            Global::mainContainer->postStatusbarMessage(
                  i18n("Copied item to clipboard.", "Copied items to clipboard.", m_parentBasket->countSelecteds()) );
}

void Item::slotCopySelection()
{
      slotCopy(true);
}

void Item::slotPaste()
{
      emit wantPaste(QClipboard::Clipboard);
}

void Item::slotOpen()
{
      if (m_type == Link) {
            if ( ! m_url->isEmpty() ) {
                  Global::mainContainer->postStatusbarMessage( i18n("Openning link target...") ); // FIXME: Plural?
                  KRun *run = new KRun( KURIFilter::self()->filteredURI(*m_url) ); //  open the URL.
                  run->setAutoDelete(true);
            } else {
                  QString warning = i18n("The link have no URL to open."); // FIXME: Plural?
                  int edit = -10;
                  if (m_parentBasket->isLocked())
                        KMessageBox::error(this, warning, QString::null);
                  else
                        edit = KMessageBox::warningContinueCancel(
                              this, warning, QString::null, KGuiItem(i18n("&Edit"), "edit"));
                  if (edit == KMessageBox::Continue)
                        slotEdit();
                  return;
            }
      } else if (useFile()) {
            if (m_type == Launcher) {
                  KService service(fullPath());
                  if (service.exec().isEmpty()) {
                        QString warning = i18n("The launcher have no application to open.");
                        int edit = -10;
                        if (m_parentBasket->isLocked())
                              KMessageBox::error(this, warning, QString::null);
                        else
                              edit = KMessageBox::warningContinueCancel(
                                    this, warning, QString::null, KGuiItem(i18n("&Edit"), "edit"));
                        if (edit == KMessageBox::Continue)
                              slotEdit();
                        return;
                  } else
                        Global::mainContainer->postStatusbarMessage( i18n("Launching application...") ); // FIXME: Plural?
            } else
                  Global::mainContainer->postStatusbarMessage( i18n("Openning item file...") ); // FIXME: Plural?
            if (m_type == Text           && Settings::isTextUseProg()      && ! Settings::textProg().isEmpty()  )
                  KRun::run( Settings::textProg(),  KURL(fullPath()) );
            else if (m_type == Html      && Settings::isHtmlUseProg()      && ! Settings::htmlProg().isEmpty()  )
                  KRun::run( Settings::htmlProg(),  KURL(fullPath()) );
            else if (m_type == Image     && Settings::isImageUseProg()     && ! Settings::imageProg().isEmpty() )
                  KRun::run( Settings::imageProg(), KURL(fullPath()) );
            else if (m_type == Animation && Settings::isAnimationUseProg() && ! Settings::animationProg().isEmpty() )
                  KRun::run( Settings::animationProg(), KURL(fullPath()) );
            else if (m_type == Sound     && Settings::isSoundUseProg()     && ! Settings::soundProg().isEmpty() )
                  KRun::run( Settings::soundProg(), KURL(fullPath()) );
            else {
                  KRun *run = new KRun(fullPath()); //  open the URL.
                  run->setAutoDelete(true);
            }
      } else
            Global::mainContainer->postStatusbarMessage( i18n("Non opennable item.") ); // FIXME: Plural?
}

void Item::slotOpenWith()
{
      if (m_type == Link) {
            if ( ! m_url->isEmpty() )        // If URL contain a data,
                  if ( KRun::displayOpenWithDialog(*m_url) )
                        Global::mainContainer->postStatusbarMessage( i18n("Openning link target with...") );
      } else if (useFile())
            if ( KRun::displayOpenWithDialog(KURL(fullPath())) )
                  Global::mainContainer->postStatusbarMessage( i18n("Openning item file with...") );
}

void Item::slotSaveAs()
{
      QString fileName;
      QString filters;
      QString caption;
      QString defName;

      switch (m_type) {
            case Text:      filters = "text/plain";            break;
            case Html:      filters = "text/html";             break;
            case Image:     filters = "image/png";             break; // TODO: Offer more types
            case Animation: filters = "image/gif";             break; // TODO: MNG...
            case Sound:     filters = "audio/x-mp3";           break; // TODO: OGG...
            case File:      filters = "*";                     break; // TODO: Get MIME type of the url target
            case Link:      filters = "*";                     break; // TODO: idem File + If isDir(): return
            case Launcher:  filters = "application/x-desktop"; break;
            case Color:     return;
            case Unknow:    return;
      }

      caption  = (m_type == Link ? i18n("Save Target") : i18n("Save a Copy"));
      defName  = (m_type == Link ? QString::null : KURL(fullPath()).fileName());
      fileName = KFileDialog::getSaveFileName(defName, filters, this, caption);
      // TODO: Ask to overwrite !
      if (fileName.isEmpty())
            return;

      saveAs(fileName);
}

void Item::slotChecked()
{
      m_parentBasket->save(); // The most time, save is performed by editors but the embedded checkbox is an exception
}

void Item::saveProperties()
{
      m_parentBasket->save();
}

void Item::loadContent()
{
      if ( ! useFile() )
            return;

      if (Global::debugWindow)
            *Global::debugWindow << "Item[" + fullPath() + "] Load content";

      if (m_type == Text || m_type == Html) {
            QFile file(fullPath());
            if ( file.open(IO_ReadOnly) ) {
                  QTextStream stream( &file );
                  QString content, tmp;
                  while ( tmp = stream.readLine() ) {
                        if (content.isEmpty())
                              content = tmp;
                        else
                              content += "\n" + tmp;
                  }
                  if (m_type == Text)
                        m_item->setText(content); // Directly, to not re-save it to the file (case of setText() or setHtml()) ;-)
                  if (m_type == Html)
                        m_item->setText(content);
                  file.close();
            }
      } else if (m_type == Image) {
            QPixmap pixmap(fullPath());
            m_item->setPixmap(pixmap);
      } else if (m_type == Animation) {
            QMovie movie(fullPath());
            m_item->setMovie(movie);
            movie.connectStatus(this, SLOT(movieStatus(int))); // See movieStatus(int) below
      } else if (m_type == Sound) {
            setFile();
      } else if (m_type == File) {
            setFile();
      } else if (m_type == Launcher) {
            KService service(fullPath());
            setLauncher(service.name(), service.icon());
      } else if (m_type == Unknow) {
            QFile file(fullPath());
            if (file.open(IO_ReadOnly)) {
                  QDataStream stream(&file);
                  QString mimes;
                  QString line;
                  // Get the MIME types names:
                  do {
                        stream >> line;
                        if (!line.isEmpty()) {
                              if (mimes.isEmpty())
                                    mimes += line;
                              else
                                    mimes += QString("<br>") + line;
                        }
                  } while (!line.isEmpty());
                  file.close();
                  m_item->setText(QString("<i>") + mimes + "</i>");
            }
      }

//    m_parentBasket->itemSizeChanged(this);
}

/** When a user drop a .gif file, for instance, we don't know if it is an image
  * or an animtion (gif file contain multiple images).
  * To determin that, we assume this is an animation and count the number of images.
  * QMovie send, in this order:
  * - For a unique image: QMovie::EndOfFrame, QMovie::EndOfLoop, QMovie::EndOfMovie.
  * - For animation:      QMovie::EndOfFrame... (for each image), QMovie::EndOfLoop,
  *                       and it then restart that for each loop.
  */
01037 void Item::movieStatus(int status)
{
      static int oldStatus = -100;

      // At least two frames: it's an animation, everything is OK
      if (oldStatus == QMovie::EndOfFrame && status == QMovie::EndOfFrame) {
            m_item->movie()->disconnectStatus(this);
            oldStatus = -100;
            if (m_isFocused)   // When inserting a new item we ensure it visble
                  m_parentBasket->ensureVisibleItem(this); //  But after loading it has certainly grown and if it was
      }
      // Only one image: it's an image, change item's type
      else if (oldStatus == QMovie::EndOfFrame && status == QMovie::EndOfLoop) {
            m_item->movie()->disconnectStatus(this);
            oldStatus = -100;
            m_type = Image;
            QTimer::singleShot(0,   this, SLOT(loadContent()));    // Delayed to avoid crash!
            QTimer::singleShot(100, this, SLOT(saveProperties())); // We should save it's an image and not an animation
            if (m_isFocused)
                  QTimer::singleShot(25, this, SLOT(delayedEnsureVisible()));
      }
      else
            oldStatus = status;
}

void Item::delayedEnsureVisible()
{
      m_parentBasket->ensureVisibleItem(this);
}

01067 void Item::saveAs(const KURL &dest)
{
      if (useFile()) {
            if (Global::debugWindow)
                  *Global::debugWindow << "Item[" + dest.prettyURL() + "] Save content";
            KIO::Job *fileCopy = new KIO::FileCopyJob( fullPath(), dest, 0666, false, true, true, true );
            connect( fileCopy, SIGNAL(result(KIO::Job *)), this, SLOT(slotResult(KIO::Job *)) );
            // TODO: for image, do not keep file format => CONVERT
      }
      if (m_type == Link) { // Copy file insteid of saving as
            if (Global::debugWindow)
                  *Global::debugWindow << "Item[" + url().prettyURL() + "] Save to " + dest.prettyURL();
            KIO::Job *fileCopy = new KIO::FileCopyJob( url(),      dest, 0666, false, true, true, true );
            connect( fileCopy, SIGNAL(result(KIO::Job *)), this, SLOT(slotResult(KIO::Job *)) );
      }
}

void Item::slotResult(KIO::Job *job)
{
      if (job->error())
            if (Global::debugWindow)
                  *Global::debugWindow << "ERROR during File copy : " + job->errorText();
}

01091 QString Item::fileName()
{
      return m_fileName;
}

/* Return the full path of the item, by taking care if it's a normal item or a mirrored file */
QString Item::fullPath()
{
      if ( ! useFile() )
            return "";

      return m_parentBasket->fullPathForFileName(m_fileName);
}

/** Change the item file name to fileName (rename item file) and return true
  *  But, if a file fileName already exists, the old file name is keeped (nothing changed)
  *  and the method return false
  */
01109 bool Item::setFileName(const QString &fileName)
{
      if (useFile()) {
            /* Re-save content (move file) */
            QDir dir;
            if ( !  dir.exists(m_parentBasket->fullPathForFileName(fileName)) // Do not erase existing target file
                 && dir.rename(fullPath(), m_parentBasket->fullPathForFileName(fileName)) ) { // Move file
                  fileNameChanged(fileName);                                    // Success
                  return true;
            }
      }

      return false; // !useFile() or unsuccesful rename
}

// TODO:     ^^ bool rename()     vv setFileName()

void Item::fileNameChanged(const QString &fileName)
{
      m_fileName = fileName;
      if (type() == File)
            loadContent();
}

void Item::unmirror()
{
      if ( ! isAMirror() )
            return;

      QString newName = ItemFactory::fileNameForNewItem( m_parentBasket, KURL(fullPath()).fileName() );
      m_parentBasket->dontCareOfCreation(m_parentBasket->fullPath() + newName);
      KIO::copy( KURL(fullPath()), KURL(m_parentBasket->fullPath() + newName)); // No reload because it's the same !!
      m_fileName = newName;
      m_parentBasket->save();
}

bool Item::isAMirror()
{
      return m_fileName.startsWith("/");
}

// Static version :
bool Item::isAMirror(const QString &fileName)
{
      return fileName.startsWith("/");
}

bool Item::isChecked()
{
      return m_check->checkBox()->isChecked();
}

void Item::setChecked(bool check)
{
      m_check->checkBox()->setChecked(check);
}

01166 QString Item::annotations()
{
      return m_annotations;
}

void Item::setAnnotations(const QString &annotations)
{
      m_annotations = annotations;
      updateToolTip();
}

01177 void Item::updateToolTip()
{
      if (Settings::showItemsToolTip())
            QToolTip::add(this, m_annotations);
      else
            QToolTip::remove(this);
}

void Item::showCheckBoxesChanged(bool show)
{
      m_check->setShown(show);
}

void Item::alignChanged(int hAlign, int vAlign)
{
      int hFlag, vFlag;

      switch (hAlign) {
            default:
            case 0: hFlag = Qt::AlignLeft;    break;
            case 1: hFlag = Qt::AlignHCenter; break;
            case 2: hFlag = Qt::AlignRight;   break;
      }
      switch (vAlign) {
            case 0: vFlag = Qt::AlignTop;     break;
            default:
            case 1: vFlag = Qt::AlignVCenter; break;
            case 2: vFlag = Qt::AlignBottom;  break;
      }

      m_layout->remove(m_check);
      m_layout->remove(m_item);
      m_layout->remove(m_linkLabel);
      m_layout->setResizeMode(QLayout::Minimum);

      if (useLinkLabel()) {
            m_layout->insertWidget(0, m_linkLabel);
            m_linkLabel->setAlign(hAlign, vAlign);
      } else {
            m_layout->insertWidget(0, m_item);
            m_item->setAlignment( hFlag | vFlag | Qt::WordBreak );
      }
      m_layout->insertWidget( -(hFlag == Qt::AlignRight), m_check );  // insert to index 0 or -1
      m_check->setAlignment(vAlign);
}

void Item::linkLookChanged()
{
      // Tehorically, it should be that:
/*    if (useLinkLabel()) {
            if (m_type == Launcher)
                  m_linkLabel->setLook(LinkLook::noUrlLook);
            else
                  m_linkLabel->setLook(LinkLook::lookForURL(*m_url));
            m_parentBasket->itemSizeChanged(this); // To resize item if growed or not smaller (FIXME: Too heavy?)
      }*/
      // FIXME: When resizing down/up the icon, the linkLabel do not resize its icon !!!!!!!!!!!

      // But m_linkLabel->setLook() is buggy (hide the icon and re-show it do not works)
      //  and item resize isn't well took in account, so it's that:
      if (useLinkLabel())
            if (m_type == Link)
                  m_linkLabel->setLink( *m_title, *m_icon, LinkLook::lookForURL(*m_url) );
            else //if (m_type == Launcher) + File + Sound
                  // Impossible because name and icon not saved:
                  //m_linkLabel->setLink(name, icon, LinkLook::noUrlLook);
                  loadContent(); // FIXME: That's too overloading!
            //else
            //    setFile();
}

void Item::lockedChanged(bool lock)
{
      m_check->setEnabled( ! lock);
}

/** Various functions to change contents (according to the type of the item) */

// Text

01257 QString Item::text()
{
      // TODO : load it from file
      return m_item->text();
}

int Item::textFontType()
{
      return m_textFontType;
}

QColor Item::textColor()
{
      return m_textColor;
}

void Item::setText(const QString &text)
{
      if (Global::debugWindow)
            *Global::debugWindow << "Item[" + fullPath() + "] New text : Save content";
      QFile file(fullPath());
      if ( file.open(IO_WriteOnly) ) {
            QTextStream stream( &file );
            stream << text;
            file.close();
      }

      m_item->setText(text);
      m_parentBasket->itemSizeChanged(this);
}

void Item::setText(const QString &text, int type, QColor fontColor)
{
      setText(text);
      setTextStyle(type, fontColor);
}


void Item::setTextStyle(int type, QColor fontColor)
{
      m_textFontType = type;
      m_textColor    = fontColor;
      m_item->setFont( ItemFactory::fontForFontType(type) );
      if ( ! m_isSelected )
            setPaletteForegroundColor(fontColor);
//    m_parentBasket->itemSizeChanged(this); // Not two times ! But it's an error
}

// Html

QString Item::html()
{
      // TODO : load it from file
      return m_item->text();
}

/*bool Item::showSource()
{
      return m_showSource;
}*/

void Item::setHtml(const QString &html)
{
// Added when removed showSource property:
      m_item->setTextFormat(Qt::RichText);
      // For the moment... But after, text could be cuttable if longuer than a defined number of lines
      setText(html);

//    m_item->setText(html);
//    m_parentBasket->itemSizeChanged(this);
}

/*void Item::setHtml(const QString &html, bool showSource)
{
      m_showSource = showSource;
      m_item->setTextFormat( showSource ? Qt::PlainText : Qt::RichText );
      setHtml(html);
}

void Item::setShowSource(bool show)
{
      m_showSource = show;
      m_item->setTextFormat( show ? Qt::PlainText : Qt::RichText );
}*/

// Sound
// File

void Item::setFile()
{
      m_linkLabel->setLink( fileName(), ItemFactory::iconForURL(KURL(fullPath())),
                            m_type == File ? LinkLook::fileLook : LinkLook::soundLook );
}

// Link

KURL Item::url()
{
      return *m_url;
}

QString Item::title()
{
      return *m_title;
}

QString Item::icon()
{
      return *m_icon;
}

void Item::setUrl(const KURL &url, const QString &title, const QString &icon)
{
      delete m_url;     m_url   = new KURL(url);
      delete m_title;   m_title = new QString(title);
      delete m_icon;    m_icon  = new QString(icon);

      m_linkLabel->setLink(*m_title, *m_icon, LinkLook::lookForURL(*m_url));
      m_parentBasket->itemSizeChanged(this);
}

// Image

QPixmap* Item::pixmap()
{
      // TODO : load it from file
      return m_item->pixmap();
}

void Item::setPixmap(const QPixmap &pixmap)
{
      if (Global::debugWindow)
            *Global::debugWindow << "Item[" + fullPath() + "] New image : Save content";

      m_item->setPixmap(pixmap);
      m_parentBasket->itemSizeChanged(this);

      pixmap.save(fullPath(), "PNG"); // TODO: FIXME: URGENT: Keep file format and not only PNG...
}

// Animation

QMovie* Item::movie()
{
      // TODO : load it from file
      return m_item->movie();
}

// Launcher

void Item::setLauncher(const QString &name, const QString &icon)
{
      m_linkLabel->setLink(name, icon, LinkLook::noUrlLook);
      m_parentBasket->itemSizeChanged(this);
}

KService* Item::service()
{
      return new KService(fullPath());
}

// Color

QColor Item::color()
{
      return QColor(*m_color);
}

void Item::setColor(QColor color)
{
      delete m_color;
      m_color = new QColor(color);

      setPaletteForegroundColor(color);
      QFont *font = new QFont();
      font->setBold(true);
      setFont(*font);

      m_item->setText(color.name());
      m_parentBasket->itemSizeChanged(this);
}

// Item::ColorFormat :
/*QString( "%1 \"%2\" (%3,%4,%5)%6 {%7 %8 %9}" ).
arg( name ).
arg( color.name().upper() ).
arg( r ).arg( g ).arg( b ).
arg( isWebColor( color ) ? " web" : "" ).
arg( r / 255.0, 1, 'f', 3 ).
arg( g / 255.0, 1, 'f', 3 ).
arg( b / 255.0, 1, 'f', 3 )
);*/

/*bool MainForm::isWebColor( QColor color ) // From QT examples
{
      int r = color.red();            // The 216 web colors are those colors whose RGB (Red, Green, Blue)
      int g = color.green();          //  values are all in the set (0, 51, 102, 153, 204, 255).
      int b = color.blue();

      return ( ( r ==   0 || r ==  51 || r == 102 ||
                     r == 153 || r == 204 || r == 255    ) &&
                   ( g ==   0 || g ==  51 || g == 102 ||
                     g == 153 || g == 204 || g == 255    ) &&
                   ( b ==   0 || b ==  51 || b == 102 ||
                     b == 153 || b == 204 || b == 255    )    );
}*/

#include "item.moc"

Generated by  Doxygen 1.6.0   Back to index