<commit_before>/*
  This file is part of KDE Kontact.

  Copyright (C) 2003 Cornelius Schumacher <schumacher@kde.org>
  Copyright (C) 2008 Rafael Fernández López <ereslibre@kde.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; see the file COPYING.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#include "iconsidepane.h"
#include "mainwindow.h"
#include "plugin.h"
#include "prefs.h"

#include <QtGui/QStringListModel>
#include <QtGui/QSortFilterProxyModel>
#include <QtGui/QDragEnterEvent>
#include <QtGui/QDragMoveEvent>
#include <QtGui/QStyledItemDelegate>
#include <QtGui/QScrollBar>

#include <KLocalizedString>
#include <KDialog>
#include <KIcon>

namespace Kontact
{

class Navigator;

class Model : public QStringListModel
{
  public:
    enum SpecialRoles {
      PluginName = Qt::UserRole
    };

    Model( Navigator *parentNavigator = 0 )
      : QStringListModel( parentNavigator ), mNavigator(parentNavigator)
    {
    }

    void setPluginList( const QList<Kontact::Plugin*> &list ) {
      pluginList = list;
    }

    virtual Qt::ItemFlags flags( const QModelIndex &index ) const
    {
      Qt::ItemFlags flags = QStringListModel::flags( index );

      if ( index.isValid() ) {
        if ( static_cast<Kontact::Plugin*>( index.internalPointer() )->disabled() ) {
          flags &= ~Qt::ItemIsEnabled;
          flags &= ~Qt::ItemIsSelectable;
          flags &= ~Qt::ItemIsDropEnabled;
        } else {
          flags |= Qt::ItemIsDropEnabled;
        }
      } else {
        flags &= ~Qt::ItemIsDropEnabled;
      }

      return flags;
    }

    virtual QModelIndex index( int row, int column,
                               const QModelIndex &parent = QModelIndex() ) const
    {
      Q_UNUSED( parent );
      if ( row < 0 || row >= pluginList.count() ) {
        return QModelIndex();
      }
      return createIndex( row, column, pluginList[row] );
    }

    virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const
    {
      if ( !index.isValid() || !index.internalPointer() ) {
        return QVariant();
      }

      if ( role == Qt::DisplayRole ) {
        if ( !mNavigator->showText() ) {
          return QVariant();
        }
        return static_cast<Kontact::Plugin*>( index.internalPointer() )->title();
      } else if ( role == Qt::DecorationRole ) {
        if ( !mNavigator->showIcons() ) {
          return QVariant();
        }
        return KIcon( static_cast<Kontact::Plugin*>( index.internalPointer() )->icon() );
      } else if ( role == Qt::TextAlignmentRole ) {
        return Qt::AlignCenter;
      } else if ( role == Qt::ToolTipRole ) {
        if ( !mNavigator->showText() ) {
          return static_cast<Kontact::Plugin*>( index.internalPointer() )->title();
        }
        return QVariant();
      } else if ( role == PluginName ) {
        return static_cast<Kontact::Plugin*>( index.internalPointer() )->identifier();
      }
      return QStringListModel::data( index, role );
    }

  private:
    QList<Kontact::Plugin*> pluginList;
    Navigator *mNavigator;
};

class SortFilterProxyModel
  : public QSortFilterProxyModel
{
  public:
    SortFilterProxyModel( QObject *parent = 0 ): QSortFilterProxyModel( parent )
    {
      setDynamicSortFilter( true );
      sort ( 0 );
    }
  protected:
    bool lessThan( const QModelIndex &left, const QModelIndex &right ) const
    {
      Kontact::Plugin *leftPlugin = static_cast<Kontact::Plugin*>( left.internalPointer() );
      Kontact::Plugin *rightPlugin = static_cast<Kontact::Plugin*>( right.internalPointer() );
      return leftPlugin->weight() < rightPlugin->weight();
    }
};

class Delegate : public QStyledItemDelegate
{
  public:
    Delegate( Navigator *parentNavigator = 0 )
      : QStyledItemDelegate( parentNavigator ), mNavigator( parentNavigator )
    {
    }

    void paint( QPainter *painter, const QStyleOptionViewItem &option,
                const QModelIndex &index ) const
    {
      if ( !index.isValid() || !index.internalPointer() ) {
        return;
      }

      QStyleOptionViewItemV4 optionCopy( *static_cast<const QStyleOptionViewItemV4*>( &option ) );
      optionCopy.decorationPosition = QStyleOptionViewItem::Top;
      optionCopy.decorationSize = QSize( mNavigator->iconSize(), mNavigator->iconSize() );
      optionCopy.textElideMode = Qt::ElideNone;
      QStyledItemDelegate::paint( painter, optionCopy, index );
    }

    QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
    {
      if ( !index.isValid() || !index.internalPointer() ) {
        return QSize();
      }

      QStyleOptionViewItemV4 optionCopy( *static_cast<const QStyleOptionViewItemV4*>( &option ) );
      optionCopy.decorationPosition = QStyleOptionViewItem::Top;
      optionCopy.decorationSize =
        mNavigator->showIcons() ? QSize( mNavigator->iconSize(), mNavigator->iconSize() ) : QSize();
      optionCopy.textElideMode = Qt::ElideNone;
      return QStyledItemDelegate::sizeHint( optionCopy, index );
    }

  private:
    Navigator *mNavigator;
};

Navigator::Navigator( SidePaneBase *parent )
  : QListView( parent ), mSidePane( parent )
{
  setViewport( new QWidget( this ) );

  setVerticalScrollMode( ScrollPerPixel );
  setHorizontalScrollMode( ScrollPerPixel );

  mIconSize = Prefs::self()->sidePaneIconSize();
  mShowIcons = Prefs::self()->sidePaneShowIcons();
  mShowText = Prefs::self()->sidePaneShowText();

  QActionGroup *viewMode = new QActionGroup( this );

  mShowIconsAction = new KAction( i18n( "Show Icons Only" ), this );
  mShowIconsAction->setCheckable( true );
  mShowIconsAction->setActionGroup( viewMode );
  mShowIconsAction->setChecked( !mShowText && mShowIcons );
  connect( mShowIconsAction, SIGNAL(triggered(bool)), this, SLOT(slotActionTriggered(bool)) );

  mShowTextAction = new KAction( i18n( "Show Text Only" ), this );
  mShowTextAction->setCheckable( true );
  mShowTextAction->setActionGroup( viewMode );
  mShowTextAction->setChecked( mShowText && !mShowIcons );
  connect( mShowTextAction, SIGNAL(triggered(bool)), this, SLOT(slotActionTriggered(bool)) );

  mShowBothAction = new KAction( i18n( "Show Icons and Text" ), this );
  mShowBothAction->setCheckable( true );
  mShowBothAction->setActionGroup( viewMode );
  mShowBothAction->setChecked( mShowText && mShowIcons );
  connect( mShowBothAction, SIGNAL(triggered(bool)), this, SLOT(slotActionTriggered(bool)) );

  KAction *sep = new KAction( this );
  sep->setSeparator( true );

  QActionGroup *iconSize = new QActionGroup( this );

  mBigIconsAction = new KAction( i18n( "Big Icons" ), this );
  mBigIconsAction->setCheckable( iconSize );
  mBigIconsAction->setActionGroup( iconSize );
  mBigIconsAction->setChecked( mIconSize == KIconLoader::SizeLarge );
  connect( mBigIconsAction, SIGNAL(triggered(bool)), this, SLOT(slotActionTriggered(bool)) );

  mNormalIconsAction = new KAction( i18n( "Normal Icons" ), this );
  mNormalIconsAction->setCheckable( true );
  mNormalIconsAction->setActionGroup( iconSize );
  mNormalIconsAction->setChecked( mIconSize == KIconLoader::SizeMedium );
  connect( mNormalIconsAction, SIGNAL(triggered(bool)), this, SLOT(slotActionTriggered(bool)) );

  mSmallIconsAction = new KAction( i18n( "Small Icons" ), this );
  mSmallIconsAction->setCheckable( true );
  mSmallIconsAction->setActionGroup( iconSize );
  mSmallIconsAction->setChecked( mIconSize == KIconLoader::SizeSmall );
  connect( mSmallIconsAction, SIGNAL(triggered(bool)), this, SLOT(slotActionTriggered(bool)) );

  QList<QAction*> actionList;
  actionList << mShowIconsAction << mShowTextAction << mShowBothAction << sep
             << mBigIconsAction << mNormalIconsAction << mSmallIconsAction;

  insertActions( 0, actionList );

  setContextMenuPolicy( Qt::ActionsContextMenu );
  setViewMode( ListMode );
  setItemDelegate( new Delegate( this ) );
  mModel = new Model( this );
  SortFilterProxyModel *sortFilterProxyModel = new SortFilterProxyModel;
  sortFilterProxyModel->setSourceModel( mModel );
  setModel( sortFilterProxyModel );

  setDragDropMode( DropOnly );
  viewport()->setAcceptDrops( true );
  setDropIndicatorShown( true );

  connect( selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
           this, SLOT(slotCurrentChanged(QModelIndex)) );
}

void Navigator::updatePlugins( QList<Kontact::Plugin*> plugins_ )
{
  QString currentPlugin;
  if ( currentIndex().isValid() ) {
    currentPlugin = currentIndex().model()->data( currentIndex(), Model::PluginName ).toString();
  }

  QList<Kontact::Plugin*> pluginsToShow;
  foreach ( Kontact::Plugin *plugin, plugins_ ) {
    if ( plugin->showInSideBar() ) {
      pluginsToShow << plugin;
    }
  }

  mModel->setPluginList( pluginsToShow );

  mModel->removeRows( 0, mModel->rowCount() );
  mModel->insertRows( 0, pluginsToShow.count() );

  // Restore the previous selected index, if any
  if ( !currentPlugin.isEmpty() ) {
    setCurrentPlugin(currentPlugin);
  }
}

void Navigator::setCurrentPlugin( const QString &plugin )
{
  for ( int i = 0; i < model()->rowCount(); i++ ) {
    const QModelIndex index = model()->index( i, 0 );
    const QString pluginName = model()->data( index, Model::PluginName ).toString();

    if ( plugin == pluginName ) {
      selectionModel()->setCurrentIndex( index, QItemSelectionModel::SelectCurrent );
      break;
    }
  }
}

QSize Navigator::sizeHint() const
{
  //### TODO: We can cache this value, so this reply is faster. Since here we won't
  //          have too many elements, it is not that important. When caching this value
  //          make sure it is updated correctly when new rows have been added or
  //          removed. (ereslibre)

  int maxWidth = 0;
  for ( int i = 0; i < model()->rowCount(); i++ ) {
    const QModelIndex index = model()->index( i, 0 );
    maxWidth = qMax( maxWidth, sizeHintForIndex( index ).width() );
  }

  if ( !verticalScrollBar()->isVisible() ) {
    maxWidth += style()->pixelMetric( QStyle::PM_ScrollBarExtent ) * 2;
  }

  int viewHeight = QListView::sizeHint().height();

  return QSize( maxWidth + rect().width() - contentsRect().width(), viewHeight );
}

void Navigator::dragEnterEvent( QDragEnterEvent *event )
{
  if ( event->proposedAction() == Qt::IgnoreAction ) {
    return;
  }
  event->acceptProposedAction();
}

void Navigator::dragMoveEvent( QDragMoveEvent *event )
{
  if ( event->proposedAction() == Qt::IgnoreAction ) {
    return;
  }

  const QModelIndex dropIndex = indexAt( event->pos() );

  if ( !dropIndex.isValid() ||
       !( dropIndex.model()->flags( dropIndex ) & Qt::ItemIsEnabled ) ) {
    event->setAccepted( false );
    return;
  } else {
    const QModelIndex sourceIndex =
      static_cast<const QSortFilterProxyModel*>( model() )->mapToSource( dropIndex );
    Kontact::Plugin *plugin =
      static_cast<Kontact::Plugin*>( sourceIndex.internalPointer() );
    if ( !plugin->canDecodeMimeData( event->mimeData() ) ) {
      event->setAccepted( false );
      return;
    }
  }

  event->acceptProposedAction();
}

void Navigator::dropEvent( QDropEvent *event )
{
  if ( event->proposedAction() == Qt::IgnoreAction ) {
    return;
  }

  const QModelIndex dropIndex = indexAt( event->pos() );

  if ( !dropIndex.isValid() ) {
    return;
  } else {
    const QModelIndex sourceIndex =
      static_cast<const QSortFilterProxyModel*>( model() )->mapToSource( dropIndex );
    Kontact::Plugin *plugin =
      static_cast<Kontact::Plugin*>( sourceIndex.internalPointer() );
    plugin->processDropEvent( event );
  }
}

void Navigator::slotCurrentChanged( const QModelIndex &current )
{
  if ( !current.isValid() || !current.internalPointer() ||
       !( current.model()->flags( current ) & Qt::ItemIsEnabled ) ) {
    return;
  }

  QModelIndex source =
    static_cast<const QSortFilterProxyModel*>( current.model() )->mapToSource( current );

  emit pluginActivated( static_cast<Kontact::Plugin*>( source.internalPointer() ) );
}

void Navigator::slotActionTriggered( bool checked )
{
  QObject *object = sender();

  if ( object == mShowIconsAction ) {
    mShowIcons = checked;
    mShowText = !checked;
  } else if ( object == mShowTextAction ) {
    mShowIcons = !checked;
    mShowText = checked;
  } else if ( object == mShowBothAction ) {
    mShowIcons = checked;
    mShowText = checked;
  } else if ( object == mBigIconsAction ) {
    mIconSize = KIconLoader::SizeLarge;
  } else if ( object == mNormalIconsAction ) {
    mIconSize = KIconLoader::SizeMedium;
  } else if ( object == mSmallIconsAction ) {
    mIconSize = KIconLoader::SizeSmallMedium;
  }

  Prefs::self()->setSidePaneIconSize( mIconSize );
  Prefs::self()->setSidePaneShowIcons( mShowIcons );
  Prefs::self()->setSidePaneShowText( mShowText );

  scheduleDelayedItemsLayout();

  parentWidget()->setMaximumWidth( sizeHint().width() );
  parentWidget()->setMinimumWidth( sizeHint().width() );
}

IconSidePane::IconSidePane( Core *core, QWidget *parent )
  : SidePaneBase( core, parent )
{
  mNavigator = new Navigator( this );
  connect( mNavigator, SIGNAL(pluginActivated(Kontact::Plugin*)),
           SIGNAL(pluginSelected(Kontact::Plugin*)) );
}

IconSidePane::~IconSidePane()
{
}

void IconSidePane::setCurrentPlugin( const QString &plugin )
{
  mNavigator->setCurrentPlugin( plugin );
}

void IconSidePane::updatePlugins()
{
  mNavigator->updatePlugins( core()->pluginList() );
}

}

#include "iconsidepane.moc"

// vim: sw=2 sts=2 et tw=80
<commit_msg>Ooops, coding style issue<commit_after>/*
  This file is part of KDE Kontact.

  Copyright (C) 2003 Cornelius Schumacher <schumacher@kde.org>
  Copyright (C) 2008 Rafael Fernández López <ereslibre@kde.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; see the file COPYING.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#include "iconsidepane.h"
#include "mainwindow.h"
#include "plugin.h"
#include "prefs.h"

#include <QtGui/QStringListModel>
#include <QtGui/QSortFilterProxyModel>
#include <QtGui/QDragEnterEvent>
#include <QtGui/QDragMoveEvent>
#include <QtGui/QStyledItemDelegate>
#include <QtGui/QScrollBar>

#include <KLocalizedString>
#include <KDialog>
#include <KIcon>

namespace Kontact
{

class Navigator;

class Model : public QStringListModel
{
  public:
    enum SpecialRoles {
      PluginName = Qt::UserRole
    };

    Model( Navigator *parentNavigator = 0 )
      : QStringListModel( parentNavigator ), mNavigator(parentNavigator)
    {
    }

    void setPluginList( const QList<Kontact::Plugin*> &list ) {
      pluginList = list;
    }

    virtual Qt::ItemFlags flags( const QModelIndex &index ) const
    {
      Qt::ItemFlags flags = QStringListModel::flags( index );

      if ( index.isValid() ) {
        if ( static_cast<Kontact::Plugin*>( index.internalPointer() )->disabled() ) {
          flags &= ~Qt::ItemIsEnabled;
          flags &= ~Qt::ItemIsSelectable;
          flags &= ~Qt::ItemIsDropEnabled;
        } else {
          flags |= Qt::ItemIsDropEnabled;
        }
      } else {
        flags &= ~Qt::ItemIsDropEnabled;
      }

      return flags;
    }

    virtual QModelIndex index( int row, int column,
                               const QModelIndex &parent = QModelIndex() ) const
    {
      Q_UNUSED( parent );
      if ( row < 0 || row >= pluginList.count() ) {
        return QModelIndex();
      }
      return createIndex( row, column, pluginList[row] );
    }

    virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const
    {
      if ( !index.isValid() || !index.internalPointer() ) {
        return QVariant();
      }

      if ( role == Qt::DisplayRole ) {
        if ( !mNavigator->showText() ) {
          return QVariant();
        }
        return static_cast<Kontact::Plugin*>( index.internalPointer() )->title();
      } else if ( role == Qt::DecorationRole ) {
        if ( !mNavigator->showIcons() ) {
          return QVariant();
        }
        return KIcon( static_cast<Kontact::Plugin*>( index.internalPointer() )->icon() );
      } else if ( role == Qt::TextAlignmentRole ) {
        return Qt::AlignCenter;
      } else if ( role == Qt::ToolTipRole ) {
        if ( !mNavigator->showText() ) {
          return static_cast<Kontact::Plugin*>( index.internalPointer() )->title();
        }
        return QVariant();
      } else if ( role == PluginName ) {
        return static_cast<Kontact::Plugin*>( index.internalPointer() )->identifier();
      }
      return QStringListModel::data( index, role );
    }

  private:
    QList<Kontact::Plugin*> pluginList;
    Navigator *mNavigator;
};

class SortFilterProxyModel
  : public QSortFilterProxyModel
{
  public:
    SortFilterProxyModel( QObject *parent = 0 ): QSortFilterProxyModel( parent )
    {
      setDynamicSortFilter( true );
      sort ( 0 );
    }
  protected:
    bool lessThan( const QModelIndex &left, const QModelIndex &right ) const
    {
      Kontact::Plugin *leftPlugin = static_cast<Kontact::Plugin*>( left.internalPointer() );
      Kontact::Plugin *rightPlugin = static_cast<Kontact::Plugin*>( right.internalPointer() );
      return leftPlugin->weight() < rightPlugin->weight();
    }
};

class Delegate : public QStyledItemDelegate
{
  public:
    Delegate( Navigator *parentNavigator = 0 )
      : QStyledItemDelegate( parentNavigator ), mNavigator( parentNavigator )
    {
    }

    void paint( QPainter *painter, const QStyleOptionViewItem &option,
                const QModelIndex &index ) const
    {
      if ( !index.isValid() || !index.internalPointer() ) {
        return;
      }

      QStyleOptionViewItemV4 optionCopy( *static_cast<const QStyleOptionViewItemV4*>( &option ) );
      optionCopy.decorationPosition = QStyleOptionViewItem::Top;
      optionCopy.decorationSize = QSize( mNavigator->iconSize(), mNavigator->iconSize() );
      optionCopy.textElideMode = Qt::ElideNone;
      QStyledItemDelegate::paint( painter, optionCopy, index );
    }

    QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
    {
      if ( !index.isValid() || !index.internalPointer() ) {
        return QSize();
      }

      QStyleOptionViewItemV4 optionCopy( *static_cast<const QStyleOptionViewItemV4*>( &option ) );
      optionCopy.decorationPosition = QStyleOptionViewItem::Top;
      optionCopy.decorationSize =
        mNavigator->showIcons() ? QSize( mNavigator->iconSize(), mNavigator->iconSize() ) : QSize();
      optionCopy.textElideMode = Qt::ElideNone;
      return QStyledItemDelegate::sizeHint( optionCopy, index );
    }

  private:
    Navigator *mNavigator;
};

Navigator::Navigator( SidePaneBase *parent )
  : QListView( parent ), mSidePane( parent )
{
  setViewport( new QWidget( this ) );

  setVerticalScrollMode( ScrollPerPixel );
  setHorizontalScrollMode( ScrollPerPixel );

  mIconSize = Prefs::self()->sidePaneIconSize();
  mShowIcons = Prefs::self()->sidePaneShowIcons();
  mShowText = Prefs::self()->sidePaneShowText();

  QActionGroup *viewMode = new QActionGroup( this );

  mShowIconsAction = new KAction( i18n( "Show Icons Only" ), this );
  mShowIconsAction->setCheckable( true );
  mShowIconsAction->setActionGroup( viewMode );
  mShowIconsAction->setChecked( !mShowText && mShowIcons );
  connect( mShowIconsAction, SIGNAL(triggered(bool)), this, SLOT(slotActionTriggered(bool)) );

  mShowTextAction = new KAction( i18n( "Show Text Only" ), this );
  mShowTextAction->setCheckable( true );
  mShowTextAction->setActionGroup( viewMode );
  mShowTextAction->setChecked( mShowText && !mShowIcons );
  connect( mShowTextAction, SIGNAL(triggered(bool)), this, SLOT(slotActionTriggered(bool)) );

  mShowBothAction = new KAction( i18n( "Show Icons and Text" ), this );
  mShowBothAction->setCheckable( true );
  mShowBothAction->setActionGroup( viewMode );
  mShowBothAction->setChecked( mShowText && mShowIcons );
  connect( mShowBothAction, SIGNAL(triggered(bool)), this, SLOT(slotActionTriggered(bool)) );

  KAction *sep = new KAction( this );
  sep->setSeparator( true );

  QActionGroup *iconSize = new QActionGroup( this );

  mBigIconsAction = new KAction( i18n( "Big Icons" ), this );
  mBigIconsAction->setCheckable( iconSize );
  mBigIconsAction->setActionGroup( iconSize );
  mBigIconsAction->setChecked( mIconSize == KIconLoader::SizeLarge );
  connect( mBigIconsAction, SIGNAL(triggered(bool)), this, SLOT(slotActionTriggered(bool)) );

  mNormalIconsAction = new KAction( i18n( "Normal Icons" ), this );
  mNormalIconsAction->setCheckable( true );
  mNormalIconsAction->setActionGroup( iconSize );
  mNormalIconsAction->setChecked( mIconSize == KIconLoader::SizeMedium );
  connect( mNormalIconsAction, SIGNAL(triggered(bool)), this, SLOT(slotActionTriggered(bool)) );

  mSmallIconsAction = new KAction( i18n( "Small Icons" ), this );
  mSmallIconsAction->setCheckable( true );
  mSmallIconsAction->setActionGroup( iconSize );
  mSmallIconsAction->setChecked( mIconSize == KIconLoader::SizeSmall );
  connect( mSmallIconsAction, SIGNAL(triggered(bool)), this, SLOT(slotActionTriggered(bool)) );

  QList<QAction*> actionList;
  actionList << mShowIconsAction << mShowTextAction << mShowBothAction << sep
             << mBigIconsAction << mNormalIconsAction << mSmallIconsAction;

  insertActions( 0, actionList );

  setContextMenuPolicy( Qt::ActionsContextMenu );
  setViewMode( ListMode );
  setItemDelegate( new Delegate( this ) );
  mModel = new Model( this );
  SortFilterProxyModel *sortFilterProxyModel = new SortFilterProxyModel;
  sortFilterProxyModel->setSourceModel( mModel );
  setModel( sortFilterProxyModel );

  setDragDropMode( DropOnly );
  viewport()->setAcceptDrops( true );
  setDropIndicatorShown( true );

  connect( selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
           this, SLOT(slotCurrentChanged(QModelIndex)) );
}

void Navigator::updatePlugins( QList<Kontact::Plugin*> plugins_ )
{
  QString currentPlugin;
  if ( currentIndex().isValid() ) {
    currentPlugin = currentIndex().model()->data( currentIndex(), Model::PluginName ).toString();
  }

  QList<Kontact::Plugin*> pluginsToShow;
  foreach ( Kontact::Plugin *plugin, plugins_ ) {
    if ( plugin->showInSideBar() ) {
      pluginsToShow << plugin;
    }
  }

  mModel->setPluginList( pluginsToShow );

  mModel->removeRows( 0, mModel->rowCount() );
  mModel->insertRows( 0, pluginsToShow.count() );

  // Restore the previous selected index, if any
  if ( !currentPlugin.isEmpty() ) {
    setCurrentPlugin( currentPlugin );
  }
}

void Navigator::setCurrentPlugin( const QString &plugin )
{
  for ( int i = 0; i < model()->rowCount(); i++ ) {
    const QModelIndex index = model()->index( i, 0 );
    const QString pluginName = model()->data( index, Model::PluginName ).toString();

    if ( plugin == pluginName ) {
      selectionModel()->setCurrentIndex( index, QItemSelectionModel::SelectCurrent );
      break;
    }
  }
}

QSize Navigator::sizeHint() const
{
  //### TODO: We can cache this value, so this reply is faster. Since here we won't
  //          have too many elements, it is not that important. When caching this value
  //          make sure it is updated correctly when new rows have been added or
  //          removed. (ereslibre)

  int maxWidth = 0;
  for ( int i = 0; i < model()->rowCount(); i++ ) {
    const QModelIndex index = model()->index( i, 0 );
    maxWidth = qMax( maxWidth, sizeHintForIndex( index ).width() );
  }

  if ( !verticalScrollBar()->isVisible() ) {
    maxWidth += style()->pixelMetric( QStyle::PM_ScrollBarExtent ) * 2;
  }

  int viewHeight = QListView::sizeHint().height();

  return QSize( maxWidth + rect().width() - contentsRect().width(), viewHeight );
}

void Navigator::dragEnterEvent( QDragEnterEvent *event )
{
  if ( event->proposedAction() == Qt::IgnoreAction ) {
    return;
  }
  event->acceptProposedAction();
}

void Navigator::dragMoveEvent( QDragMoveEvent *event )
{
  if ( event->proposedAction() == Qt::IgnoreAction ) {
    return;
  }

  const QModelIndex dropIndex = indexAt( event->pos() );

  if ( !dropIndex.isValid() ||
       !( dropIndex.model()->flags( dropIndex ) & Qt::ItemIsEnabled ) ) {
    event->setAccepted( false );
    return;
  } else {
    const QModelIndex sourceIndex =
      static_cast<const QSortFilterProxyModel*>( model() )->mapToSource( dropIndex );
    Kontact::Plugin *plugin =
      static_cast<Kontact::Plugin*>( sourceIndex.internalPointer() );
    if ( !plugin->canDecodeMimeData( event->mimeData() ) ) {
      event->setAccepted( false );
      return;
    }
  }

  event->acceptProposedAction();
}

void Navigator::dropEvent( QDropEvent *event )
{
  if ( event->proposedAction() == Qt::IgnoreAction ) {
    return;
  }

  const QModelIndex dropIndex = indexAt( event->pos() );

  if ( !dropIndex.isValid() ) {
    return;
  } else {
    const QModelIndex sourceIndex =
      static_cast<const QSortFilterProxyModel*>( model() )->mapToSource( dropIndex );
    Kontact::Plugin *plugin =
      static_cast<Kontact::Plugin*>( sourceIndex.internalPointer() );
    plugin->processDropEvent( event );
  }
}

void Navigator::slotCurrentChanged( const QModelIndex &current )
{
  if ( !current.isValid() || !current.internalPointer() ||
       !( current.model()->flags( current ) & Qt::ItemIsEnabled ) ) {
    return;
  }

  QModelIndex source =
    static_cast<const QSortFilterProxyModel*>( current.model() )->mapToSource( current );

  emit pluginActivated( static_cast<Kontact::Plugin*>( source.internalPointer() ) );
}

void Navigator::slotActionTriggered( bool checked )
{
  QObject *object = sender();

  if ( object == mShowIconsAction ) {
    mShowIcons = checked;
    mShowText = !checked;
  } else if ( object == mShowTextAction ) {
    mShowIcons = !checked;
    mShowText = checked;
  } else if ( object == mShowBothAction ) {
    mShowIcons = checked;
    mShowText = checked;
  } else if ( object == mBigIconsAction ) {
    mIconSize = KIconLoader::SizeLarge;
  } else if ( object == mNormalIconsAction ) {
    mIconSize = KIconLoader::SizeMedium;
  } else if ( object == mSmallIconsAction ) {
    mIconSize = KIconLoader::SizeSmallMedium;
  }

  Prefs::self()->setSidePaneIconSize( mIconSize );
  Prefs::self()->setSidePaneShowIcons( mShowIcons );
  Prefs::self()->setSidePaneShowText( mShowText );

  scheduleDelayedItemsLayout();

  parentWidget()->setMaximumWidth( sizeHint().width() );
  parentWidget()->setMinimumWidth( sizeHint().width() );
}

IconSidePane::IconSidePane( Core *core, QWidget *parent )
  : SidePaneBase( core, parent )
{
  mNavigator = new Navigator( this );
  connect( mNavigator, SIGNAL(pluginActivated(Kontact::Plugin*)),
           SIGNAL(pluginSelected(Kontact::Plugin*)) );
}

IconSidePane::~IconSidePane()
{
}

void IconSidePane::setCurrentPlugin( const QString &plugin )
{
  mNavigator->setCurrentPlugin( plugin );
}

void IconSidePane::updatePlugins()
{
  mNavigator->updatePlugins( core()->pluginList() );
}

}

#include "iconsidepane.moc"

// vim: sw=2 sts=2 et tw=80
<|endoftext|>


<commit_before>/*
  This file is part of KOrganizer.

  Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org>
  Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>

  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.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

  As a special exception, permission is given to link this program
  with any edition of Qt, and distribute the resulting executable,
  without including the source code for Qt in the source distribution.
*/

// ArchiveDialog -- archive/delete past events.

#include "archivedialog.h"
#include "koprefs.h"
#include "eventarchiver.h"

#include <libkdepim/kdateedit.h>

#include <kdebug.h>
#include <klocale.h>
#include <knuminput.h>
#include <kurlrequester.h>
#include <kmessagebox.h>
#include <kfiledialog.h>
#include <kurl.h>
#include <klineedit.h>
#include <kvbox.h>
#include <KComboBox>
#include <KTextBrowser>

#include <QLabel>
#include <QLayout>
#include <QDateTime>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QFrame>
#include <QHBoxLayout>
#include <QButtonGroup>
#include <QRadioButton>

#include "archivedialog.moc"

ArchiveDialog::ArchiveDialog(Calendar *cal,QWidget *parent)
  : KDialog (parent)
{
  setCaption( i18nc( "@title:window", "Archive/Delete Past Events and To-dos" ) );
  setButtons( User1|Cancel );
  setDefaultButton( User1 );
  setModal( false );
  showButtonSeparator( true );
  setButtonText( User1, i18nc( "@action:button", "&Archive" ) );
  mCalendar = cal;

  QFrame *topFrame = new QFrame( this );
  setMainWidget( topFrame );
  QVBoxLayout *topLayout = new QVBoxLayout(topFrame);
  topLayout->setSpacing(spacingHint());

  KTextBrowser *descLabel = new KTextBrowser(topFrame);
  descLabel->setText(
    i18nc( "@info:whatsthis",
           "Archiving saves old items into the given file and "
           "then deletes them in the current calendar. If the archive file "
           "already exists they will be added. "
           "(<link url=\"whatsthis:In order to add an archive "
           "to your calendar, use the "Merge Calendar" function. "
           "You can view an archive by opening it in KOrganizer like any "
           "other calendar. It is not saved in a special format, but as "
           "vCalendar.\">How to restore</link>)" ) );
  descLabel->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard );
  topLayout->addWidget(descLabel);

  QButtonGroup* radioBG = new QButtonGroup( this );
  connect( radioBG, SIGNAL( buttonClicked( int ) ), SLOT( slotActionChanged() ) );

  QHBoxLayout *dateLayout = new QHBoxLayout();
  dateLayout->setMargin( 0 );
  mArchiveOnceRB = new QRadioButton( i18nc( "@option:radio", "Archive now items older than:" ),topFrame );
  dateLayout->addWidget(mArchiveOnceRB);
  radioBG->addButton(mArchiveOnceRB);
  mDateEdit = new KPIM::KDateEdit(topFrame);
  mDateEdit->setWhatsThis(
    i18nc( "@info:whatsthis",
           "The date before which items should be archived. All older events "
           "and to-dos will be saved and deleted, the newer (and events "
           "exactly on that date) will be kept." ) );
  dateLayout->addWidget(mDateEdit);
  topLayout->addLayout(dateLayout);

  // Checkbox, numinput and combo for auto-archiving
  // (similar to kmail's mExpireFolderCheckBox/mReadExpiryTimeNumInput in kmfolderdia.cpp)
  KHBox* autoArchiveHBox = new KHBox(topFrame);
  topLayout->addWidget(autoArchiveHBox);
  mAutoArchiveRB = new QRadioButton( i18nc( "@option:radio", "Automaticall&y archive items older than:" ), autoArchiveHBox );
  radioBG->addButton(mAutoArchiveRB);
  mAutoArchiveRB->setWhatsThis(
    i18nc( "@info:whatsthis",
           "If this feature is enabled, KOrganizer will regularly check if "
           "events and to-dos have to be archived; this means you will not "
           "need to use this dialog box again, except to change the settings." ) );

  mExpiryTimeNumInput = new KIntNumInput(autoArchiveHBox);
  mExpiryTimeNumInput->setRange(1, 500, 1);
  mExpiryTimeNumInput->setSliderEnabled(false);
  mExpiryTimeNumInput->setEnabled(false);
  mExpiryTimeNumInput->setValue(7);
  mExpiryTimeNumInput->setWhatsThis(
    i18nc( "@info:whatsthis",
           "The age of the events and to-dos to archive. All older items "
           "will be saved and deleted, the newer will be kept." ) );

  mExpiryUnitsComboBox = new KComboBox(autoArchiveHBox);
  // Those items must match the "Expiry Unit" enum in the kcfg file!
  mExpiryUnitsComboBox->addItem( i18nc( "@item:inlistbox expires in daily units", "Day(s)" ) );
  mExpiryUnitsComboBox->addItem( i18nc( "@item:inlistbox expiration in weekly units", "Week(s)" ) );
  mExpiryUnitsComboBox->addItem( i18nc( "@item:inlistbox expiration in monthly units", "Month(s)" ) );
  mExpiryUnitsComboBox->setEnabled( false );

  QHBoxLayout *fileLayout = new QHBoxLayout();
  fileLayout->setMargin( 0 );
  fileLayout->setSpacing(spacingHint());
  QLabel *l = new QLabel( i18nc( "@label", "Archive &file:" ),topFrame );
  fileLayout->addWidget(l);
  mArchiveFile = new KUrlRequester(KOPrefs::instance()->mArchiveFile,topFrame);
  mArchiveFile->setMode(KFile::File);
  mArchiveFile->setFilter( i18nc( "@label filter for KUrlRequester", "*.ics|iCalendar Files" ) );
  mArchiveFile->setWhatsThis(
    i18nc( "@info:whatsthis",
           "The path of the archive. The events and to-dos will be added to "
           "the archive file, so any events that are already in the file "
           "will not be modified or deleted. You can later load or merge the "
           "file like any other calendar. It is not saved in a special "
           "format, it uses the iCalendar format." ) );
  l->setBuddy(mArchiveFile->lineEdit());
  fileLayout->addWidget(mArchiveFile);
  topLayout->addLayout(fileLayout);

  QGroupBox *typeBox = new QGroupBox( i18nc( "@title:group", "Type of Items to Archive" ) );
  topLayout->addWidget( typeBox );

  QBoxLayout *typeLayout = new QVBoxLayout( typeBox );

  mEvents = new QCheckBox( i18nc( "@option:check", "&Events" ) );
  typeLayout->addWidget( mEvents );
  mTodos = new QCheckBox( i18nc( "@option:check", "&To-dos")  );
  typeLayout->addWidget( mTodos );
  typeBox->setWhatsThis(
    i18nc( "@info:whatsthis",
           "Here you can select which items "
           "should be archived. Events are archived if they "
           "ended before the date given above; to-dos are archived if "
           "they were finished before the date.") );

  mDeleteCb = new QCheckBox( i18nc( "@option:check", "&Delete only, do not save" ), topFrame );
  mDeleteCb->setWhatsThis(
    i18nc(
      "@info:whatsthis",
      "Select this option to delete old events and to-dos without saving them. "
      "It is not possible to recover the events later." ) );
  topLayout->addWidget(mDeleteCb);
  connect(mDeleteCb, SIGNAL(toggled(bool)), mArchiveFile, SLOT(setDisabled(bool)));
  connect(mDeleteCb, SIGNAL(toggled(bool)), this, SLOT(slotEnableUser1()));
  connect(mArchiveFile->lineEdit(),SIGNAL(textChanged ( const QString & )),
          this,SLOT(slotEnableUser1()));

  // Load settings from KOPrefs
  mExpiryTimeNumInput->setValue( KOPrefs::instance()->mExpiryTime );
  mExpiryUnitsComboBox->setCurrentIndex( KOPrefs::instance()->mExpiryUnit );
  mDeleteCb->setChecked( KOPrefs::instance()->mArchiveAction == KOPrefs::actionDelete );
  mEvents->setChecked( KOPrefs::instance()->mArchiveEvents );
  mTodos->setChecked( KOPrefs::instance()->mArchiveTodos );

  slotEnableUser1();

  // The focus should go to a useful field by default, not to the top richtext-label
  if ( KOPrefs::instance()->mAutoArchive ) {
    mAutoArchiveRB->setChecked( true );
    mAutoArchiveRB->setFocus();
  } else {
    mArchiveOnceRB->setChecked( true );
    mArchiveOnceRB->setFocus();
  }
  slotActionChanged();
  connect(this,SIGNAL(user1Clicked()),this,SLOT(slotUser1()));
}

ArchiveDialog::~ArchiveDialog()
{
}

void ArchiveDialog::slotEnableUser1()
{
  bool state = ( mDeleteCb->isChecked() ||
                 !mArchiveFile->lineEdit()->text().isEmpty() );
  enableButton(KDialog::User1,state);
}

void ArchiveDialog::slotActionChanged()
{
  mDateEdit->setEnabled( mArchiveOnceRB->isChecked() );
  mExpiryTimeNumInput->setEnabled( mAutoArchiveRB->isChecked() );
  mExpiryUnitsComboBox->setEnabled( mAutoArchiveRB->isChecked() );
}

// Archive old events
void ArchiveDialog::slotUser1()
{
  EventArchiver archiver;
  connect( &archiver, SIGNAL( eventsDeleted() ), this, SLOT( slotEventsDeleted() ) );

  KOPrefs::instance()->mAutoArchive = mAutoArchiveRB->isChecked();
  KOPrefs::instance()->mExpiryTime = mExpiryTimeNumInput->value();
  KOPrefs::instance()->mExpiryUnit = mExpiryUnitsComboBox->currentIndex();

  if (mDeleteCb->isChecked()) {
    KOPrefs::instance()->mArchiveAction = KOPrefs::actionDelete;
  } else {
    KOPrefs::instance()->mArchiveAction = KOPrefs::actionArchive;

    // Get destination URL
    KUrl destUrl( mArchiveFile->url() );
    if ( !destUrl.isValid() ) {
      KMessageBox::sorry( this, i18nc( "@info", "The archive file name is not valid." ) );
      return;
    }
    // Force filename to be ending with vCalendar extension
    QString filename = destUrl.fileName();
    if (!filename.endsWith(".vcs") && !filename.endsWith(".ics")) {
      filename.append(".ics");
      destUrl.setFileName(filename);
    }

    KOPrefs::instance()->mArchiveFile = destUrl.url();
  }
  if ( KOPrefs::instance()->mAutoArchive ) {
    archiver.runAuto( mCalendar, this, true /*with gui*/ );
    emit autoArchivingSettingsModified();
    accept();
  }
  else
    archiver.runOnce( mCalendar, mDateEdit->date(), this );
}

void ArchiveDialog::slotEventsDeleted()
{
  emit eventsDeleted();
  if ( !KOPrefs::instance()->mAutoArchive )
    accept();
}
<commit_msg>minor coding style fixes SVN_SILENT:<commit_after>/*
  This file is part of KOrganizer.

  Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org>
  Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>

  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.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

  As a special exception, permission is given to link this program
  with any edition of Qt, and distribute the resulting executable,
  without including the source code for Qt in the source distribution.
*/

// ArchiveDialog -- archive/delete past events.

#include "archivedialog.h"
#include "koprefs.h"
#include "eventarchiver.h"

#include <libkdepim/kdateedit.h>

#include <kdebug.h>
#include <klocale.h>
#include <knuminput.h>
#include <kurlrequester.h>
#include <kmessagebox.h>
#include <kfiledialog.h>
#include <kurl.h>
#include <klineedit.h>
#include <kvbox.h>
#include <KComboBox>
#include <KTextBrowser>

#include <QLabel>
#include <QLayout>
#include <QDateTime>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QFrame>
#include <QHBoxLayout>
#include <QButtonGroup>
#include <QRadioButton>

#include "archivedialog.moc"

ArchiveDialog::ArchiveDialog( Calendar *cal, QWidget *parent )
  : KDialog (parent)
{
  setCaption( i18nc( "@title:window", "Archive/Delete Past Events and To-dos" ) );
  setButtons( User1|Cancel );
  setDefaultButton( User1 );
  setModal( false );
  showButtonSeparator( true );
  setButtonText( User1, i18nc( "@action:button", "&Archive" ) );
  mCalendar = cal;

  QFrame *topFrame = new QFrame( this );
  setMainWidget( topFrame );
  QVBoxLayout *topLayout = new QVBoxLayout( topFrame );
  topLayout->setSpacing( spacingHint() );

  KTextBrowser *descLabel = new KTextBrowser( topFrame );
  descLabel->setText(
    i18nc( "@info:whatsthis",
           "Archiving saves old items into the given file and "
           "then deletes them in the current calendar. If the archive file "
           "already exists they will be added. "
           "(<link url=\"whatsthis:In order to add an archive "
           "to your calendar, use the "Merge Calendar" function. "
           "You can view an archive by opening it in KOrganizer like any "
           "other calendar. It is not saved in a special format, but as "
           "vCalendar.\">How to restore</link>)" ) );
  descLabel->setTextInteractionFlags(
    Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard |
    Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard );
  topLayout->addWidget( descLabel );

  QButtonGroup *radioBG = new QButtonGroup( this );
  connect( radioBG, SIGNAL(buttonClicked(int)), SLOT(slotActionChanged()) );

  QHBoxLayout *dateLayout = new QHBoxLayout();
  dateLayout->setMargin( 0 );
  mArchiveOnceRB =
    new QRadioButton( i18nc( "@option:radio", "Archive now items older than:" ),
                      topFrame );
  dateLayout->addWidget( mArchiveOnceRB );
  radioBG->addButton( mArchiveOnceRB );
  mDateEdit = new KPIM::KDateEdit( topFrame );
  mDateEdit->setWhatsThis(
    i18nc( "@info:whatsthis",
           "The date before which items should be archived. All older events "
           "and to-dos will be saved and deleted, the newer (and events "
           "exactly on that date) will be kept." ) );
  dateLayout->addWidget( mDateEdit );
  topLayout->addLayout( dateLayout );

  // Checkbox, numinput and combo for auto-archiving (similar to kmail's
  // mExpireFolderCheckBox/mReadExpiryTimeNumInput in kmfolderdia.cpp)
  KHBox *autoArchiveHBox = new KHBox( topFrame );
  topLayout->addWidget( autoArchiveHBox );
  mAutoArchiveRB =
    new QRadioButton( i18nc( "@option:radio", "Automaticall&y archive items older than:" ),
                      autoArchiveHBox );
  radioBG->addButton( mAutoArchiveRB );
  mAutoArchiveRB->setWhatsThis(
    i18nc( "@info:whatsthis",
           "If this feature is enabled, KOrganizer will regularly check if "
           "events and to-dos have to be archived; this means you will not "
           "need to use this dialog box again, except to change the settings." ) );

  mExpiryTimeNumInput = new KIntNumInput( autoArchiveHBox );
  mExpiryTimeNumInput->setRange( 1, 500, 1 );
  mExpiryTimeNumInput->setSliderEnabled( false );
  mExpiryTimeNumInput->setEnabled( false );
  mExpiryTimeNumInput->setValue( 7 );
  mExpiryTimeNumInput->setWhatsThis(
    i18nc( "@info:whatsthis",
           "The age of the events and to-dos to archive. All older items "
           "will be saved and deleted, the newer will be kept." ) );

  mExpiryUnitsComboBox = new KComboBox( autoArchiveHBox );
  // Those items must match the "Expiry Unit" enum in the kcfg file!
  mExpiryUnitsComboBox->addItem(
    i18nc( "@item:inlistbox expires in daily units", "Day(s)" ) );
  mExpiryUnitsComboBox->addItem(
    i18nc( "@item:inlistbox expiration in weekly units", "Week(s)" ) );
  mExpiryUnitsComboBox->addItem(
    i18nc( "@item:inlistbox expiration in monthly units", "Month(s)" ) );
  mExpiryUnitsComboBox->setEnabled( false );

  QHBoxLayout *fileLayout = new QHBoxLayout();
  fileLayout->setMargin( 0 );
  fileLayout->setSpacing( spacingHint() );
  QLabel *l = new QLabel( i18nc( "@label", "Archive &file:" ), topFrame );
  fileLayout->addWidget(l);
  mArchiveFile = new KUrlRequester( KOPrefs::instance()->mArchiveFile, topFrame );
  mArchiveFile->setMode( KFile::File );
  mArchiveFile->setFilter( i18nc( "@label filter for KUrlRequester", "*.ics|iCalendar Files" ) );
  mArchiveFile->setWhatsThis(
    i18nc( "@info:whatsthis",
           "The path of the archive. The events and to-dos will be added to "
           "the archive file, so any events that are already in the file "
           "will not be modified or deleted. You can later load or merge the "
           "file like any other calendar. It is not saved in a special "
           "format, it uses the iCalendar format." ) );
  l->setBuddy( mArchiveFile->lineEdit() );
  fileLayout->addWidget( mArchiveFile );
  topLayout->addLayout( fileLayout );

  QGroupBox *typeBox = new QGroupBox( i18nc( "@title:group", "Type of Items to Archive" ) );
  topLayout->addWidget( typeBox );

  QBoxLayout *typeLayout = new QVBoxLayout( typeBox );

  mEvents = new QCheckBox( i18nc( "@option:check", "&Events" ) );
  typeLayout->addWidget( mEvents );
  mTodos = new QCheckBox( i18nc( "@option:check", "&To-dos" ) );
  typeLayout->addWidget( mTodos );
  typeBox->setWhatsThis(
    i18nc( "@info:whatsthis",
           "Here you can select which items "
           "should be archived. Events are archived if they "
           "ended before the date given above; to-dos are archived if "
           "they were finished before the date." ) );

  mDeleteCb = new QCheckBox( i18nc( "@option:check", "&Delete only, do not save" ), topFrame );
  mDeleteCb->setWhatsThis(
    i18nc( "@info:whatsthis",
           "Select this option to delete old events and to-dos without saving "
           "them. It is not possible to recover the events later." ) );
  topLayout->addWidget(mDeleteCb);
  connect( mDeleteCb, SIGNAL(toggled(bool)), mArchiveFile, SLOT(setDisabled(bool)) );
  connect( mDeleteCb, SIGNAL(toggled(bool)), this, SLOT(slotEnableUser1()) );
  connect( mArchiveFile->lineEdit(), SIGNAL(textChanged(const QString &)),
           this, SLOT(slotEnableUser1()) );

  // Load settings from KOPrefs
  mExpiryTimeNumInput->setValue( KOPrefs::instance()->mExpiryTime );
  mExpiryUnitsComboBox->setCurrentIndex( KOPrefs::instance()->mExpiryUnit );
  mDeleteCb->setChecked( KOPrefs::instance()->mArchiveAction == KOPrefs::actionDelete );
  mEvents->setChecked( KOPrefs::instance()->mArchiveEvents );
  mTodos->setChecked( KOPrefs::instance()->mArchiveTodos );

  slotEnableUser1();

  // The focus should go to a useful field by default, not to the top richtext-label
  if ( KOPrefs::instance()->mAutoArchive ) {
    mAutoArchiveRB->setChecked( true );
    mAutoArchiveRB->setFocus();
  } else {
    mArchiveOnceRB->setChecked( true );
    mArchiveOnceRB->setFocus();
  }
  slotActionChanged();
  connect( this, SIGNAL(user1Clicked()), this, SLOT(slotUser1()) );
}

ArchiveDialog::~ArchiveDialog()
{
}

void ArchiveDialog::slotEnableUser1()
{
  bool state = ( mDeleteCb->isChecked() || !mArchiveFile->lineEdit()->text().isEmpty() );
  enableButton( KDialog::User1, state );
}

void ArchiveDialog::slotActionChanged()
{
  mDateEdit->setEnabled( mArchiveOnceRB->isChecked() );
  mExpiryTimeNumInput->setEnabled( mAutoArchiveRB->isChecked() );
  mExpiryUnitsComboBox->setEnabled( mAutoArchiveRB->isChecked() );
}

// Archive old events
void ArchiveDialog::slotUser1()
{
  EventArchiver archiver;
  connect( &archiver, SIGNAL(eventsDeleted()), this, SLOT(slotEventsDeleted()) );

  KOPrefs::instance()->mAutoArchive = mAutoArchiveRB->isChecked();
  KOPrefs::instance()->mExpiryTime = mExpiryTimeNumInput->value();
  KOPrefs::instance()->mExpiryUnit = mExpiryUnitsComboBox->currentIndex();

  if ( mDeleteCb->isChecked() ) {
    KOPrefs::instance()->mArchiveAction = KOPrefs::actionDelete;
  } else {
    KOPrefs::instance()->mArchiveAction = KOPrefs::actionArchive;

    // Get destination URL
    KUrl destUrl( mArchiveFile->url() );
    if ( !destUrl.isValid() ) {
      KMessageBox::sorry( this, i18nc( "@info", "The archive file name is not valid." ) );
      return;
    }
    // Force filename to be ending with vCalendar extension
    QString filename = destUrl.fileName();
    if ( !filename.endsWith( ".vcs" ) && !filename.endsWith( ".ics" ) ) {
      filename.append( ".ics" );
      destUrl.setFileName( filename );
    }

    KOPrefs::instance()->mArchiveFile = destUrl.url();
  }
  if ( KOPrefs::instance()->mAutoArchive ) {
    archiver.runAuto( mCalendar, this, true /*with gui*/);
    emit autoArchivingSettingsModified();
    accept();
  } else {
    archiver.runOnce( mCalendar, mDateEdit->date(), this );
  }
}

void ArchiveDialog::slotEventsDeleted()
{
  emit eventsDeleted();
  if ( !KOPrefs::instance()->mAutoArchive ) {
    accept();
  }
}
<|endoftext|>


<commit_before>/*
  This file is part of KOrganizer.
  Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org>
  Copyright (c) 2004 David Faure <faure@kde.org>
  Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>

  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.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

  As a special exception, permission is given to link this program
  with any edition of Qt, and distribute the resulting executable,
  without including the source code for Qt in the source distribution.
*/

#include "eventarchiver.h"
#include "koprefs.h"

#include <kio/netaccess.h>
#include <kcal/filestorage.h>
#include <kcal/calendarlocal.h>
#include <kcal/calendar.h>

#include <kdebug.h>
#include <kglobal.h>
#include <klocale.h>
#include <ktemporaryfile.h>
#include <kmessagebox.h>

EventArchiver::EventArchiver( QObject* parent, const char* name )
 : QObject( parent )
{
  setObjectName( name );
}

EventArchiver::~EventArchiver()
{
}

void EventArchiver::runOnce( Calendar* calendar, const QDate& limitDate, QWidget* widget )
{
  run( calendar, limitDate, widget, true, true );
}

void EventArchiver::runAuto( Calendar* calendar, QWidget* widget, bool withGUI )
{
  QDate limitDate( QDate::currentDate() );
  int expiryTime = KOPrefs::instance()->mExpiryTime;
  switch (KOPrefs::instance()->mExpiryUnit) {
  case KOPrefs::UnitDays: // Days
    limitDate = limitDate.addDays( -expiryTime );
    break;
  case KOPrefs::UnitWeeks: // Weeks
    limitDate = limitDate.addDays( -expiryTime*7 );
    break;
  case KOPrefs::UnitMonths: // Months
    limitDate = limitDate.addMonths( -expiryTime );
    break;
  default:
    return;
  }
  run( calendar, limitDate, widget, withGUI, false );
}

void EventArchiver::run( Calendar* calendar, const QDate& limitDate, QWidget* widget, bool withGUI, bool errorIfNone )
{
  // We need to use rawEvents, otherwise events hidden by filters will not be archived.
  Incidence::List incidences;
  Event::List events;
  Todo::List todos;
  Journal::List journals;

  if ( KOPrefs::instance()->mArchiveEvents ) {
    events = calendar->rawEvents(
      QDate( 1769, 12, 1 ),
      // #29555, also advertised by the "limitDate not included" in the class docu
      limitDate.addDays( -1 ),
      true );
  }
  if ( KOPrefs::instance()->mArchiveTodos ) {
    Todo::List t = calendar->rawTodos();
    Todo::List::ConstIterator it;
    for( it = t.begin(); it != t.end(); ++it ) {
      if ( (*it) && ( (*it)->isCompleted() ) &&  ( (*it)->completed().date() < limitDate ) ) {
        todos.append( *it );
      }
    }
  }

  incidences = Calendar::mergeIncidenceList( events, todos, journals );

  kDebug(5850) << "EventArchiver: archiving incidences before " << limitDate << " -> " << incidences.count() << " incidences found." << endl;
  if ( incidences.isEmpty() ) {
    if ( withGUI && errorIfNone )
      KMessageBox::information( widget, i18n("There are no items before %1",
                           KGlobal::locale()->formatDate(limitDate)),
                          "ArchiverNoIncidences" );
    return;
  }

  switch ( KOPrefs::instance()->mArchiveAction ) {
  case KOPrefs::actionDelete:
    deleteIncidences( calendar, limitDate, widget, incidences, withGUI );
    break;
  case KOPrefs::actionArchive:
    archiveIncidences( calendar, limitDate, widget, incidences, withGUI );
    break;
  }
}

void EventArchiver::deleteIncidences( Calendar* calendar, const QDate& limitDate, QWidget* widget, const Incidence::List& incidences, bool withGUI )
{
  QStringList incidenceStrs;
  Incidence::List::ConstIterator it;
  for( it = incidences.begin(); it != incidences.end(); ++it ) {
    incidenceStrs.append( (*it)->summary() );
  }

  if ( withGUI ) {
    int result = KMessageBox::warningContinueCancelList(
      widget, i18n("Delete all items before %1 without saving?\n"
                 "The following items will be deleted:",
       KGlobal::locale()->formatDate(limitDate)), incidenceStrs,
		 i18n("Delete Old Items"),KStandardGuiItem::del());
    if (result != KMessageBox::Continue)
      return;
  }
  for( it = incidences.begin(); it != incidences.end(); ++it ) {
    calendar->deleteIncidence( *it );
  }
  emit eventsDeleted();
}

void EventArchiver::archiveIncidences( Calendar* calendar, const QDate& /*limitDate*/, QWidget* widget, const Incidence::List& incidences, bool /*withGUI*/)
{
  FileStorage storage( calendar );

  // Save current calendar to disk
  KTemporaryFile tmpFile;
  tmpFile.open();
  storage.setFileName( tmpFile.fileName() );
  if ( !storage.save() ) {
    kDebug(5850) << "EventArchiver::archiveEvents(): Can't save calendar to temp file" << endl;
    return;
  }

  // Duplicate current calendar by loading in new calendar object
#ifdef __GNUC__
#warning Does the time zone specification serve any purpose?
#endif
  CalendarLocal archiveCalendar( KOPrefs::instance()->timeSpec() );

  FileStorage archiveStore( &archiveCalendar );
  archiveStore.setFileName( tmpFile.fileName() );
  if (!archiveStore.load()) {
    kDebug(5850) << "EventArchiver::archiveEvents(): Can't load calendar from temp file" << endl;
    return;
  }

  // Strip active events from calendar so that only events to be archived
  // remain. This is not really efficient, but there is no other easy way.
  QStringList uids;
  Incidence::List allIncidences = archiveCalendar.rawIncidences();
  Incidence::List::ConstIterator it;
  for( it = incidences.begin(); it != incidences.end(); ++it ) {
    uids << (*it)->uid();
  }
  for( it = allIncidences.begin(); it != allIncidences.end(); ++it ) {
    if ( !uids.contains( (*it)->uid() ) ) {
      archiveCalendar.deleteIncidence( *it );
    }
  }

  // Get or create the archive file
  KUrl archiveURL( KOPrefs::instance()->mArchiveFile );
  QString archiveFile;

  if ( KIO::NetAccess::exists( archiveURL, true, widget ) ) {
    if( !KIO::NetAccess::download( archiveURL, archiveFile, widget ) ) {
      kDebug(5850) << "EventArchiver::archiveEvents(): Can't download archive file" << endl;
      return;
    }
    // Merge with events to be archived.
    archiveStore.setFileName( archiveFile );
    if ( !archiveStore.load() ) {
      kDebug(5850) << "EventArchiver::archiveEvents(): Can't merge with archive file" << endl;
      return;
    }
  } else {
    archiveFile = tmpFile.fileName();
  }

  // Save archive calendar
  if ( !archiveStore.save() ) {
    KMessageBox::error(widget,i18n("Cannot write archive file %1.", archiveStore.fileName() ));
    return;
  }

  // Upload if necessary
  KUrl srcUrl;
  srcUrl.setPath(archiveFile);
  if (srcUrl != archiveURL) {
    if ( !KIO::NetAccess::upload( archiveFile, archiveURL, widget ) ) {
      KMessageBox::error(widget,i18n("Cannot write archive to final destination."));
      return;
    }
  }

  KIO::NetAccess::removeTempFile(archiveFile);

  // Delete archived events from calendar
  for( it = incidences.begin(); it != incidences.end(); ++it ) {
    calendar->deleteIncidence( *it );
  }
  emit eventsDeleted();
}

#include "eventarchiver.moc"
<commit_msg>Follow KCal::Calendar::rawEvents time zone changes<commit_after>/*
  This file is part of KOrganizer.
  Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org>
  Copyright (c) 2004 David Faure <faure@kde.org>
  Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>

  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.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

  As a special exception, permission is given to link this program
  with any edition of Qt, and distribute the resulting executable,
  without including the source code for Qt in the source distribution.
*/

#include "eventarchiver.h"
#include "koprefs.h"

#include <kio/netaccess.h>
#include <kcal/filestorage.h>
#include <kcal/calendarlocal.h>
#include <kcal/calendar.h>

#include <kdebug.h>
#include <kglobal.h>
#include <klocale.h>
#include <ktemporaryfile.h>
#include <kmessagebox.h>

EventArchiver::EventArchiver( QObject* parent, const char* name )
 : QObject( parent )
{
  setObjectName( name );
}

EventArchiver::~EventArchiver()
{
}

void EventArchiver::runOnce( Calendar* calendar, const QDate& limitDate, QWidget* widget )
{
  run( calendar, limitDate, widget, true, true );
}

void EventArchiver::runAuto( Calendar* calendar, QWidget* widget, bool withGUI )
{
  QDate limitDate( QDate::currentDate() );
  int expiryTime = KOPrefs::instance()->mExpiryTime;
  switch (KOPrefs::instance()->mExpiryUnit) {
  case KOPrefs::UnitDays: // Days
    limitDate = limitDate.addDays( -expiryTime );
    break;
  case KOPrefs::UnitWeeks: // Weeks
    limitDate = limitDate.addDays( -expiryTime*7 );
    break;
  case KOPrefs::UnitMonths: // Months
    limitDate = limitDate.addMonths( -expiryTime );
    break;
  default:
    return;
  }
  run( calendar, limitDate, widget, withGUI, false );
}

void EventArchiver::run( Calendar* calendar, const QDate& limitDate, QWidget* widget, bool withGUI, bool errorIfNone )
{
  // We need to use rawEvents, otherwise events hidden by filters will not be archived.
  Incidence::List incidences;
  Event::List events;
  Todo::List todos;
  Journal::List journals;

  if ( KOPrefs::instance()->mArchiveEvents ) {
    events = calendar->rawEvents(
      QDate( 1769, 12, 1 ),
      // #29555, also advertised by the "limitDate not included" in the class docu
      limitDate.addDays( -1 ),
      KOPrefs::instance()->timeSpec(),
      true );
  }
  if ( KOPrefs::instance()->mArchiveTodos ) {
    Todo::List t = calendar->rawTodos();
    Todo::List::ConstIterator it;
    for( it = t.begin(); it != t.end(); ++it ) {
      if ( (*it) && ( (*it)->isCompleted() ) &&  ( (*it)->completed().date() < limitDate ) ) {
        todos.append( *it );
      }
    }
  }

  incidences = Calendar::mergeIncidenceList( events, todos, journals );

  kDebug(5850) << "EventArchiver: archiving incidences before " << limitDate << " -> " << incidences.count() << " incidences found." << endl;
  if ( incidences.isEmpty() ) {
    if ( withGUI && errorIfNone )
      KMessageBox::information( widget, i18n("There are no items before %1",
                           KGlobal::locale()->formatDate(limitDate)),
                          "ArchiverNoIncidences" );
    return;
  }

  switch ( KOPrefs::instance()->mArchiveAction ) {
  case KOPrefs::actionDelete:
    deleteIncidences( calendar, limitDate, widget, incidences, withGUI );
    break;
  case KOPrefs::actionArchive:
    archiveIncidences( calendar, limitDate, widget, incidences, withGUI );
    break;
  }
}

void EventArchiver::deleteIncidences( Calendar* calendar, const QDate& limitDate, QWidget* widget, const Incidence::List& incidences, bool withGUI )
{
  QStringList incidenceStrs;
  Incidence::List::ConstIterator it;
  for( it = incidences.begin(); it != incidences.end(); ++it ) {
    incidenceStrs.append( (*it)->summary() );
  }

  if ( withGUI ) {
    int result = KMessageBox::warningContinueCancelList(
      widget, i18n("Delete all items before %1 without saving?\n"
                 "The following items will be deleted:",
       KGlobal::locale()->formatDate(limitDate)), incidenceStrs,
		 i18n("Delete Old Items"),KStandardGuiItem::del());
    if (result != KMessageBox::Continue)
      return;
  }
  for( it = incidences.begin(); it != incidences.end(); ++it ) {
    calendar->deleteIncidence( *it );
  }
  emit eventsDeleted();
}

void EventArchiver::archiveIncidences( Calendar* calendar, const QDate& /*limitDate*/, QWidget* widget, const Incidence::List& incidences, bool /*withGUI*/)
{
  FileStorage storage( calendar );

  // Save current calendar to disk
  KTemporaryFile tmpFile;
  tmpFile.open();
  storage.setFileName( tmpFile.fileName() );
  if ( !storage.save() ) {
    kDebug(5850) << "EventArchiver::archiveEvents(): Can't save calendar to temp file" << endl;
    return;
  }

  // Duplicate current calendar by loading in new calendar object
#ifdef __GNUC__
#warning Does the time zone specification serve any purpose?
#endif
  CalendarLocal archiveCalendar( KOPrefs::instance()->timeSpec() );

  FileStorage archiveStore( &archiveCalendar );
  archiveStore.setFileName( tmpFile.fileName() );
  if (!archiveStore.load()) {
    kDebug(5850) << "EventArchiver::archiveEvents(): Can't load calendar from temp file" << endl;
    return;
  }

  // Strip active events from calendar so that only events to be archived
  // remain. This is not really efficient, but there is no other easy way.
  QStringList uids;
  Incidence::List allIncidences = archiveCalendar.rawIncidences();
  Incidence::List::ConstIterator it;
  for( it = incidences.begin(); it != incidences.end(); ++it ) {
    uids << (*it)->uid();
  }
  for( it = allIncidences.begin(); it != allIncidences.end(); ++it ) {
    if ( !uids.contains( (*it)->uid() ) ) {
      archiveCalendar.deleteIncidence( *it );
    }
  }

  // Get or create the archive file
  KUrl archiveURL( KOPrefs::instance()->mArchiveFile );
  QString archiveFile;

  if ( KIO::NetAccess::exists( archiveURL, true, widget ) ) {
    if( !KIO::NetAccess::download( archiveURL, archiveFile, widget ) ) {
      kDebug(5850) << "EventArchiver::archiveEvents(): Can't download archive file" << endl;
      return;
    }
    // Merge with events to be archived.
    archiveStore.setFileName( archiveFile );
    if ( !archiveStore.load() ) {
      kDebug(5850) << "EventArchiver::archiveEvents(): Can't merge with archive file" << endl;
      return;
    }
  } else {
    archiveFile = tmpFile.fileName();
  }

  // Save archive calendar
  if ( !archiveStore.save() ) {
    KMessageBox::error(widget,i18n("Cannot write archive file %1.", archiveStore.fileName() ));
    return;
  }

  // Upload if necessary
  KUrl srcUrl;
  srcUrl.setPath(archiveFile);
  if (srcUrl != archiveURL) {
    if ( !KIO::NetAccess::upload( archiveFile, archiveURL, widget ) ) {
      KMessageBox::error(widget,i18n("Cannot write archive to final destination."));
      return;
    }
  }

  KIO::NetAccess::removeTempFile(archiveFile);

  // Delete archived events from calendar
  for( it = incidences.begin(); it != incidences.end(); ++it ) {
    calendar->deleteIncidence( *it );
  }
  emit eventsDeleted();
}

#include "eventarchiver.moc"
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "iosprobe.h"

#include <QDebug>
#include <QFileInfo>
#include <QProcess>
#include <QDir>
#include <QFileInfoList>

static const bool debugProbe = false;

namespace Ios {

static QString qsystem(const QString &exe, const QStringList &args = QStringList())
{
    QProcess p;
    p.setProcessChannelMode(QProcess::MergedChannels);
    p.start(exe, args);
    p.waitForFinished();
    return QString::fromLocal8Bit(p.readAll());
}

QMap<QString, Platform> IosProbe::detectPlatforms(const QString &devPath)
{
    IosProbe probe;
    probe.addDeveloperPath(devPath);
    probe.detectFirst();
    return probe.detectedPlatforms();
}

static int compareVersions(const QString &v1, const QString &v2)
{
    QStringList v1L = v1.split(QLatin1Char('.'));
    QStringList v2L = v2.split(QLatin1Char('.'));
    int i = 0;
    while (v1.length() > i && v1.length() > i) {
        bool n1Ok, n2Ok;
        int n1 = v1L.value(i).toInt(&n1Ok);
        int n2 = v2L.value(i).toInt(&n2Ok);
        if (!(n1Ok && n2Ok)) {
            qDebug() << QString::fromLatin1("Failed to compare version %1 and %2").arg(v1, v2);
            return 0;
        }
        if (n1 > n2)
            return -1;
        else if (n1 < n2)
            return 1;
        ++i;
    }
    if (v1.length() > v2.length())
        return -1;
    if (v1.length() < v2.length())
        return 1;
    return 0;
}

void IosProbe::addDeveloperPath(const QString &path)
{
    if (path.isEmpty())
        return;
    QFileInfo pInfo(path);
    if (!pInfo.exists() || !pInfo.isDir())
        return;
    if (m_developerPaths.contains(path))
        return;
    m_developerPaths.append(path);
    if (debugProbe)
        qDebug() << QString::fromLatin1("Added developer path %1").arg(path);
}

void IosProbe::detectDeveloperPaths()
{
    QProcess selectedXcode;
    QString program = QLatin1String("/usr/bin/xcode-select");
    QStringList arguments(QLatin1String("--print-path"));
    selectedXcode.start(program, arguments, QProcess::ReadOnly);
    if (!selectedXcode.waitForFinished() || selectedXcode.exitCode()) {
        qDebug() << QString::fromLatin1("Could not detect selected xcode with /usr/bin/xcode-select");
    } else {
        QString path = QString::fromLocal8Bit(selectedXcode.readAllStandardOutput());
        addDeveloperPath(path);
    }
    addDeveloperPath(QLatin1String("/Applications/Xcode.app/Contents/Developer"));
}

void IosProbe::setupDefaultToolchains(const QString &devPath, const QString &xcodeName)
{
    if (debugProbe)
        qDebug() << QString::fromLatin1("Setting up platform '%1'.").arg(xcodeName);
    QString indent = QLatin1String("  ");

    // detect clang (default toolchain)
    QFileInfo clangFileInfo(devPath
                            + QLatin1String("/Toolchains/XcodeDefault.xctoolchain/usr/bin")
                            + QLatin1String("/clang++"));
    bool hasClang = clangFileInfo.exists();
    if (!hasClang)
        qDebug() << indent << QString::fromLatin1("Default toolchain %1 not found.")
                     .arg(clangFileInfo.canonicalFilePath());
    // Platforms
    QDir platformsDir(devPath + QLatin1String("/Platforms"));
    QFileInfoList platforms = platformsDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
    foreach (const QFileInfo &fInfo, platforms) {
        if (fInfo.isDir() && fInfo.suffix() == QLatin1String("platform")) {
            if (debugProbe)
                qDebug() << indent << QString::fromLatin1("Setting up %1").arg(fInfo.fileName());
            QSettingsPtr infoSettings(new QSettings(
                                   fInfo.absoluteFilePath() + QLatin1String("/Info.plist"),
                                   QSettings::NativeFormat));
            if (!infoSettings->contains(QLatin1String("Name"))) {
                qDebug() << indent << QString::fromLatin1("Missing platform name in Info.plist of %1")
                             .arg(fInfo.absoluteFilePath());
                continue;
            }
            QString name = infoSettings->value(QLatin1String("Name")).toString();
            if (name != QLatin1String("macosx") && name != QLatin1String("iphoneos")
                    && name != QLatin1String("iphonesimulator"))
            {
                qDebug() << indent << QString::fromLatin1("Skipping unknown platform %1").arg(name);
                continue;
            }

            // prepare default platform properties
            QVariantMap defaultProp = infoSettings->value(QLatin1String("DefaultProperties"))
                    .toMap();
            QVariantMap overrideProp = infoSettings->value(QLatin1String("OverrideProperties"))
                    .toMap();
            QMapIterator<QString, QVariant> i(overrideProp);
            while (i.hasNext()) {
                i.next();
                // use unite? might lead to double insertions...
                defaultProp[i.key()] = i.value();
            }

            QString clangFullName = name + QLatin1String("-clang") + xcodeName;
            QString clang11FullName = name + QLatin1String("-clang11") + xcodeName;
            // detect gcc
            QFileInfo gccFileInfo(fInfo.absoluteFilePath() + QLatin1String("/Developer/usr/bin/g++"));
            QString gccFullName = name + QLatin1String("-gcc") + xcodeName;
            if (!gccFileInfo.exists())
                gccFileInfo = QFileInfo(devPath + QLatin1String("/usr/bin/g++"));
            bool hasGcc = gccFileInfo.exists();

            QStringList extraFlags;
            if (defaultProp.contains(QLatin1String("NATIVE_ARCH"))) {
                QString arch = defaultProp.value(QLatin1String("NATIVE_ARCH")).toString();
                if (!arch.startsWith(QLatin1String("arm")))
                    qDebug() << indent << QString::fromLatin1("Expected arm architecture, not %1").arg(arch);
                extraFlags << QLatin1String("-arch") << arch;
            } else if (name == QLatin1String("iphonesimulator")) {
                QString arch = defaultProp.value(QLatin1String("ARCHS")).toString();
                // don't generate a toolchain for 64 bit (to fix when we support that)
                extraFlags << QLatin1String("-arch") << QLatin1String("i386");
            }
            if (hasClang) {
                Platform clangProfile;
                clangProfile.developerPath = Utils::FileName::fromString(devPath);
                clangProfile.platformKind = 0;
                clangProfile.name = clangFullName;
                clangProfile.platformPath = Utils::FileName(fInfo);
                clangProfile.platformInfo = infoSettings;
                clangProfile.compilerPath = Utils::FileName(clangFileInfo);
                QStringList flags = extraFlags;
                flags << QLatin1String("-dumpmachine");
                QString compilerTriplet = qsystem(clangFileInfo.canonicalFilePath(), flags)
                        .simplified();
                QStringList compilerTripletl = compilerTriplet.split(QLatin1Char('-'));
                clangProfile.architecture = compilerTripletl.value(0);
                clangProfile.backendFlags = extraFlags;
                if (debugProbe)
                    qDebug() << indent << QString::fromLatin1("* adding profile %1").arg(clangProfile.name);
                m_platforms[clangProfile.name] = clangProfile;
                clangProfile.platformKind |= Platform::Cxx11Support;
                clangProfile.backendFlags.append(QLatin1String("-std=c++11"));
                clangProfile.backendFlags.append(QLatin1String("-stdlib=libc++"));
                clangProfile.name = clang11FullName;
                m_platforms[clangProfile.name] = clangProfile;
            }
            if (hasGcc) {
                Platform gccProfile;
                gccProfile.developerPath = Utils::FileName::fromString(devPath);
                gccProfile.name = gccFullName;
                gccProfile.platformKind = 0;
                // use the arm-apple-darwin10-llvm-* variant and avoid the extraFlags if available???
                gccProfile.platformPath = Utils::FileName(fInfo);
                gccProfile.platformInfo = infoSettings;
                gccProfile.compilerPath = Utils::FileName(gccFileInfo);
                QStringList flags = extraFlags;
                flags << QLatin1String("-dumpmachine");
                QString compilerTriplet = qsystem(gccFileInfo.canonicalFilePath(), flags)
                        .simplified();
                QStringList compilerTripletl = compilerTriplet.split(QLatin1Char('-'));
                gccProfile.architecture = compilerTripletl.value(0);
                gccProfile.backendFlags = extraFlags;
                if (debugProbe)
                    qDebug() << indent << QString::fromLatin1("* adding profile %1").arg(gccProfile.name);
                m_platforms[gccProfile.name] = gccProfile;
            }

            // set SDKs/sysroot
            QString sysRoot;
            QSettingsPtr sdkSettings;
            {
                QString sdkName;
                if (defaultProp.contains(QLatin1String("SDKROOT")))
                    sdkName = defaultProp.value(QLatin1String("SDKROOT")).toString();
                QString sdkPath;
                QDir sdks(fInfo.absoluteFilePath() + QLatin1String("/Developer/SDKs"));
                QString maxVersion;
                foreach (const QFileInfo &sdkDirInfo, sdks.entryInfoList(QDir::Dirs
                                                                         | QDir::NoDotAndDotDot)) {
                    indent = QLatin1String("    ");
                    QSettingsPtr sdkInfo(new QSettings(sdkDirInfo.absoluteFilePath()
                                                       + QLatin1String("/SDKSettings.plist"),
                                                       QSettings::NativeFormat));
                    QString versionStr = sdkInfo->value(QLatin1String("Version")).toString();
                    QVariant currentSdkName = sdkInfo->value(QLatin1String("CanonicalName"));
                    bool isBaseSdk = sdkInfo->value((QLatin1String("isBaseSDK"))).toString()
                            .toLower() != QLatin1String("no");
                    if (!isBaseSdk) {
                        if (debugProbe)
                            qDebug() << indent << QString::fromLatin1("Skipping non base Sdk %1")
                                        .arg(currentSdkName.toString());
                        continue;
                    }
                    QString safeName = currentSdkName.toString().replace(QLatin1Char('-'), QLatin1Char('_'))
                            .replace(QRegExp(QLatin1String("[^-a-zA-Z0-9]")), QLatin1String("-"));
                    if (sdkName.isEmpty()) {
                        if (compareVersions(maxVersion, versionStr) > 0) {
                            maxVersion = versionStr;
                            sdkPath = sdkDirInfo.canonicalFilePath();
                            sdkSettings = sdkInfo;
                        }
                    } else if (currentSdkName == sdkName) {
                        sdkPath = sdkDirInfo.canonicalFilePath();
                        sdkSettings = sdkInfo;
                    }
                }
                if (!sdkPath.isEmpty())
                    sysRoot = sdkPath;
                else if (!sdkName.isEmpty())
                    qDebug() << indent << QString::fromLatin1("Failed to find sysroot %1").arg(sdkName);
            }
            if (hasClang && !sysRoot.isEmpty()) {
                m_platforms[clangFullName].platformKind |= Platform::BasePlatform;
                m_platforms[clangFullName].sdkPath = Utils::FileName::fromString(sysRoot);
                m_platforms[clangFullName].sdkSettings = sdkSettings;
                m_platforms[clang11FullName].platformKind |= Platform::BasePlatform;
                m_platforms[clang11FullName].sdkPath = Utils::FileName::fromString(sysRoot);
                m_platforms[clang11FullName].sdkSettings = sdkSettings;
            }
            if (hasGcc && !sysRoot.isEmpty()) {
                m_platforms[gccFullName].platformKind |= Platform::BasePlatform;
                m_platforms[gccFullName].sdkPath = Utils::FileName::fromString(sysRoot);
                m_platforms[gccFullName].sdkSettings = sdkSettings;
            }
        }
        indent = QLatin1String("  ");
    }
}

void IosProbe::detectFirst()
{
    detectDeveloperPaths();
    if (!m_developerPaths.isEmpty())
        setupDefaultToolchains(m_developerPaths.value(0),QLatin1String(""));
}

QMap<QString, Platform> IosProbe::detectedPlatforms()
{
    return m_platforms;
}

}
<commit_msg>ios: fix Xcode path detection<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "iosprobe.h"

#include <QDebug>
#include <QFileInfo>
#include <QProcess>
#include <QDir>
#include <QFileInfoList>

static const bool debugProbe = false;

namespace Ios {

static QString qsystem(const QString &exe, const QStringList &args = QStringList())
{
    QProcess p;
    p.setProcessChannelMode(QProcess::MergedChannels);
    p.start(exe, args);
    p.waitForFinished();
    return QString::fromLocal8Bit(p.readAll());
}

QMap<QString, Platform> IosProbe::detectPlatforms(const QString &devPath)
{
    IosProbe probe;
    probe.addDeveloperPath(devPath);
    probe.detectFirst();
    return probe.detectedPlatforms();
}

static int compareVersions(const QString &v1, const QString &v2)
{
    QStringList v1L = v1.split(QLatin1Char('.'));
    QStringList v2L = v2.split(QLatin1Char('.'));
    int i = 0;
    while (v1.length() > i && v1.length() > i) {
        bool n1Ok, n2Ok;
        int n1 = v1L.value(i).toInt(&n1Ok);
        int n2 = v2L.value(i).toInt(&n2Ok);
        if (!(n1Ok && n2Ok)) {
            qDebug() << QString::fromLatin1("Failed to compare version %1 and %2").arg(v1, v2);
            return 0;
        }
        if (n1 > n2)
            return -1;
        else if (n1 < n2)
            return 1;
        ++i;
    }
    if (v1.length() > v2.length())
        return -1;
    if (v1.length() < v2.length())
        return 1;
    return 0;
}

void IosProbe::addDeveloperPath(const QString &path)
{
    if (path.isEmpty())
        return;
    QFileInfo pInfo(path);
    if (!pInfo.exists() || !pInfo.isDir())
        return;
    if (m_developerPaths.contains(path))
        return;
    m_developerPaths.append(path);
    if (debugProbe)
        qDebug() << QString::fromLatin1("Added developer path %1").arg(path);
}

void IosProbe::detectDeveloperPaths()
{
    QProcess selectedXcode;
    QString program = QLatin1String("/usr/bin/xcode-select");
    QStringList arguments(QLatin1String("--print-path"));
    selectedXcode.start(program, arguments, QProcess::ReadOnly);
    if (!selectedXcode.waitForFinished() || selectedXcode.exitCode()) {
        qDebug() << QString::fromLatin1("Could not detect selected xcode with /usr/bin/xcode-select");
    } else {
        QString path = QString::fromLocal8Bit(selectedXcode.readAllStandardOutput());
        path.chop(1);
        addDeveloperPath(path);
    }
    addDeveloperPath(QLatin1String("/Applications/Xcode.app/Contents/Developer"));
}

void IosProbe::setupDefaultToolchains(const QString &devPath, const QString &xcodeName)
{
    if (debugProbe)
        qDebug() << QString::fromLatin1("Setting up platform '%1'.").arg(xcodeName);
    QString indent = QLatin1String("  ");

    // detect clang (default toolchain)
    QFileInfo clangFileInfo(devPath
                            + QLatin1String("/Toolchains/XcodeDefault.xctoolchain/usr/bin")
                            + QLatin1String("/clang++"));
    bool hasClang = clangFileInfo.exists();
    if (!hasClang)
        qDebug() << indent << QString::fromLatin1("Default toolchain %1 not found.")
                     .arg(clangFileInfo.canonicalFilePath());
    // Platforms
    QDir platformsDir(devPath + QLatin1String("/Platforms"));
    QFileInfoList platforms = platformsDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
    foreach (const QFileInfo &fInfo, platforms) {
        if (fInfo.isDir() && fInfo.suffix() == QLatin1String("platform")) {
            if (debugProbe)
                qDebug() << indent << QString::fromLatin1("Setting up %1").arg(fInfo.fileName());
            QSettingsPtr infoSettings(new QSettings(
                                   fInfo.absoluteFilePath() + QLatin1String("/Info.plist"),
                                   QSettings::NativeFormat));
            if (!infoSettings->contains(QLatin1String("Name"))) {
                qDebug() << indent << QString::fromLatin1("Missing platform name in Info.plist of %1")
                             .arg(fInfo.absoluteFilePath());
                continue;
            }
            QString name = infoSettings->value(QLatin1String("Name")).toString();
            if (name != QLatin1String("macosx") && name != QLatin1String("iphoneos")
                    && name != QLatin1String("iphonesimulator"))
            {
                qDebug() << indent << QString::fromLatin1("Skipping unknown platform %1").arg(name);
                continue;
            }

            // prepare default platform properties
            QVariantMap defaultProp = infoSettings->value(QLatin1String("DefaultProperties"))
                    .toMap();
            QVariantMap overrideProp = infoSettings->value(QLatin1String("OverrideProperties"))
                    .toMap();
            QMapIterator<QString, QVariant> i(overrideProp);
            while (i.hasNext()) {
                i.next();
                // use unite? might lead to double insertions...
                defaultProp[i.key()] = i.value();
            }

            QString clangFullName = name + QLatin1String("-clang") + xcodeName;
            QString clang11FullName = name + QLatin1String("-clang11") + xcodeName;
            // detect gcc
            QFileInfo gccFileInfo(fInfo.absoluteFilePath() + QLatin1String("/Developer/usr/bin/g++"));
            QString gccFullName = name + QLatin1String("-gcc") + xcodeName;
            if (!gccFileInfo.exists())
                gccFileInfo = QFileInfo(devPath + QLatin1String("/usr/bin/g++"));
            bool hasGcc = gccFileInfo.exists();

            QStringList extraFlags;
            if (defaultProp.contains(QLatin1String("NATIVE_ARCH"))) {
                QString arch = defaultProp.value(QLatin1String("NATIVE_ARCH")).toString();
                if (!arch.startsWith(QLatin1String("arm")))
                    qDebug() << indent << QString::fromLatin1("Expected arm architecture, not %1").arg(arch);
                extraFlags << QLatin1String("-arch") << arch;
            } else if (name == QLatin1String("iphonesimulator")) {
                QString arch = defaultProp.value(QLatin1String("ARCHS")).toString();
                // don't generate a toolchain for 64 bit (to fix when we support that)
                extraFlags << QLatin1String("-arch") << QLatin1String("i386");
            }
            if (hasClang) {
                Platform clangProfile;
                clangProfile.developerPath = Utils::FileName::fromString(devPath);
                clangProfile.platformKind = 0;
                clangProfile.name = clangFullName;
                clangProfile.platformPath = Utils::FileName(fInfo);
                clangProfile.platformInfo = infoSettings;
                clangProfile.compilerPath = Utils::FileName(clangFileInfo);
                QStringList flags = extraFlags;
                flags << QLatin1String("-dumpmachine");
                QString compilerTriplet = qsystem(clangFileInfo.canonicalFilePath(), flags)
                        .simplified();
                QStringList compilerTripletl = compilerTriplet.split(QLatin1Char('-'));
                clangProfile.architecture = compilerTripletl.value(0);
                clangProfile.backendFlags = extraFlags;
                if (debugProbe)
                    qDebug() << indent << QString::fromLatin1("* adding profile %1").arg(clangProfile.name);
                m_platforms[clangProfile.name] = clangProfile;
                clangProfile.platformKind |= Platform::Cxx11Support;
                clangProfile.backendFlags.append(QLatin1String("-std=c++11"));
                clangProfile.backendFlags.append(QLatin1String("-stdlib=libc++"));
                clangProfile.name = clang11FullName;
                m_platforms[clangProfile.name] = clangProfile;
            }
            if (hasGcc) {
                Platform gccProfile;
                gccProfile.developerPath = Utils::FileName::fromString(devPath);
                gccProfile.name = gccFullName;
                gccProfile.platformKind = 0;
                // use the arm-apple-darwin10-llvm-* variant and avoid the extraFlags if available???
                gccProfile.platformPath = Utils::FileName(fInfo);
                gccProfile.platformInfo = infoSettings;
                gccProfile.compilerPath = Utils::FileName(gccFileInfo);
                QStringList flags = extraFlags;
                flags << QLatin1String("-dumpmachine");
                QString compilerTriplet = qsystem(gccFileInfo.canonicalFilePath(), flags)
                        .simplified();
                QStringList compilerTripletl = compilerTriplet.split(QLatin1Char('-'));
                gccProfile.architecture = compilerTripletl.value(0);
                gccProfile.backendFlags = extraFlags;
                if (debugProbe)
                    qDebug() << indent << QString::fromLatin1("* adding profile %1").arg(gccProfile.name);
                m_platforms[gccProfile.name] = gccProfile;
            }

            // set SDKs/sysroot
            QString sysRoot;
            QSettingsPtr sdkSettings;
            {
                QString sdkName;
                if (defaultProp.contains(QLatin1String("SDKROOT")))
                    sdkName = defaultProp.value(QLatin1String("SDKROOT")).toString();
                QString sdkPath;
                QDir sdks(fInfo.absoluteFilePath() + QLatin1String("/Developer/SDKs"));
                QString maxVersion;
                foreach (const QFileInfo &sdkDirInfo, sdks.entryInfoList(QDir::Dirs
                                                                         | QDir::NoDotAndDotDot)) {
                    indent = QLatin1String("    ");
                    QSettingsPtr sdkInfo(new QSettings(sdkDirInfo.absoluteFilePath()
                                                       + QLatin1String("/SDKSettings.plist"),
                                                       QSettings::NativeFormat));
                    QString versionStr = sdkInfo->value(QLatin1String("Version")).toString();
                    QVariant currentSdkName = sdkInfo->value(QLatin1String("CanonicalName"));
                    bool isBaseSdk = sdkInfo->value((QLatin1String("isBaseSDK"))).toString()
                            .toLower() != QLatin1String("no");
                    if (!isBaseSdk) {
                        if (debugProbe)
                            qDebug() << indent << QString::fromLatin1("Skipping non base Sdk %1")
                                        .arg(currentSdkName.toString());
                        continue;
                    }
                    QString safeName = currentSdkName.toString().replace(QLatin1Char('-'), QLatin1Char('_'))
                            .replace(QRegExp(QLatin1String("[^-a-zA-Z0-9]")), QLatin1String("-"));
                    if (sdkName.isEmpty()) {
                        if (compareVersions(maxVersion, versionStr) > 0) {
                            maxVersion = versionStr;
                            sdkPath = sdkDirInfo.canonicalFilePath();
                            sdkSettings = sdkInfo;
                        }
                    } else if (currentSdkName == sdkName) {
                        sdkPath = sdkDirInfo.canonicalFilePath();
                        sdkSettings = sdkInfo;
                    }
                }
                if (!sdkPath.isEmpty())
                    sysRoot = sdkPath;
                else if (!sdkName.isEmpty())
                    qDebug() << indent << QString::fromLatin1("Failed to find sysroot %1").arg(sdkName);
            }
            if (hasClang && !sysRoot.isEmpty()) {
                m_platforms[clangFullName].platformKind |= Platform::BasePlatform;
                m_platforms[clangFullName].sdkPath = Utils::FileName::fromString(sysRoot);
                m_platforms[clangFullName].sdkSettings = sdkSettings;
                m_platforms[clang11FullName].platformKind |= Platform::BasePlatform;
                m_platforms[clang11FullName].sdkPath = Utils::FileName::fromString(sysRoot);
                m_platforms[clang11FullName].sdkSettings = sdkSettings;
            }
            if (hasGcc && !sysRoot.isEmpty()) {
                m_platforms[gccFullName].platformKind |= Platform::BasePlatform;
                m_platforms[gccFullName].sdkPath = Utils::FileName::fromString(sysRoot);
                m_platforms[gccFullName].sdkSettings = sdkSettings;
            }
        }
        indent = QLatin1String("  ");
    }
}

void IosProbe::detectFirst()
{
    detectDeveloperPaths();
    if (!m_developerPaths.isEmpty())
        setupDefaultToolchains(m_developerPaths.value(0),QLatin1String(""));
}

QMap<QString, Platform> IosProbe::detectedPlatforms()
{
    return m_platforms;
}

}
<|endoftext|>