/** This file is part of Kig, a KDE program for Interactive Geometry... Copyright (C) 2002 Dominique Devriese 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 **/ #include "typesdialog.h" #include "typesdialog.moc" #include "edittype.h" #include "ui_typeswidget.h" #include "../kig/kig_part.h" #include "../misc/guiaction.h" #include "../misc/object_constructor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include static QString wrapAt( const QString& str, int col = 50 ) { QStringList ret; int delta = 0; while ( delta + col < str.length() ) { int pos = delta + col; while ( !str.at( pos ).isSpace() ) --pos; ret << str.mid( delta, pos - delta ); delta = pos + 1; } ret << str.mid( delta ); return ret.join( "
" ); } class BaseListElement { protected: BaseListElement(); public: virtual ~BaseListElement(); virtual bool isMacro() const { return false; } virtual QString name() const = 0; virtual QString description() const = 0; virtual QString icon( bool canNull = false ) const = 0; virtual QString type() const = 0; }; BaseListElement::BaseListElement() { } BaseListElement::~BaseListElement() { } class MacroListElement : public BaseListElement { Macro* mmacro; public: MacroListElement( Macro* m ); virtual ~MacroListElement(); Macro* getMacro() const { return mmacro; } virtual bool isMacro() const { return true; } virtual QString name() const; virtual QString description() const; virtual QString icon( bool canNull = false ) const; virtual QString type() const; }; MacroListElement::MacroListElement( Macro* m ) : BaseListElement(), mmacro( m ) { } MacroListElement::~MacroListElement() { // do NOT delete the associated macro, but instead safely release the // pointer, as it's kept elsewhere mmacro = 0; } QString MacroListElement::name() const { return mmacro->action->descriptiveName(); } QString MacroListElement::description() const { return mmacro->action->description(); } QString MacroListElement::icon( bool canNull ) const { return mmacro->ctor->iconFileName( canNull ); } QString MacroListElement::type() const { return i18n( "Macro" ); } TypesModel::TypesModel( QObject* parent ) : QAbstractTableModel( parent ) { } TypesModel::~TypesModel() { } void TypesModel::addMacros( const std::vector& macros ) { if ( macros.size() < 1 ) return; beginInsertRows( QModelIndex(), melems.size(), melems.size() + macros.size() - 1 ); for ( std::vector::const_iterator it = macros.begin(); it != macros.end(); ++it ) { melems.push_back( new MacroListElement( *it ) ); } endInsertRows(); } void TypesModel::removeElements( const QModelIndexList& elems ) { // this way of deleting needs some explanation: the std::vector.erase needs // an iterator to the element to remove from the list, while the // beginRemoveRows() of Qt needs the index(es). so, for each element to // delete, we search it into melems (keeping a count of the id), and when we // find it, we free the memory of the BaseListElement and remove the element // from the list. in the meanwhile, we notify the model structure of Qt that // we're removing a row. for ( int i = elems.count(); i > 0; --i ) { QModelIndex index = elems.at( i - 1 ); if ( !index.isValid() || index.row() < 0 || index.row() >= static_cast( melems.size() ) || index.column() < 0 || index.column() > 3 ) continue; BaseListElement* element = melems[ index.row() ]; bool found = false; int id = 0; for ( std::vector::iterator mit = melems.begin(); mit != melems.end() && !found; ) { if ( *mit == element ) { found = true; beginRemoveRows( QModelIndex(), id, id ); delete (*mit); mit = melems.erase( mit ); element = 0; endRemoveRows(); } else ++mit; ++id; } } } void TypesModel::clear() { for ( std::vector::const_iterator it = melems.begin(); it != melems.end(); ++it ) delete *it; melems.clear(); } void TypesModel::elementChanged( const QModelIndex& index ) { if ( !index.isValid() || index.row() < 0 || index.row() >= static_cast( melems.size() ) || index.column() < 0 || index.column() > 3 ) return; QModelIndex left = createIndex( index.row(), 1 ); QModelIndex right = createIndex( index.row(), 2 ); emit dataChanged( left, right ); } bool TypesModel::isMacro( const QModelIndex& index ) const { if ( !index.isValid() || index.row() < 0 || index.row() >= static_cast( melems.size() ) ) return false; return melems[ index.row() ]->isMacro(); } Macro* TypesModel::macroFromIndex( const QModelIndex& index ) const { if ( !index.isValid() || index.row() < 0 || index.row() >= static_cast( melems.size() ) ) return 0; BaseListElement* el = melems[ index.row() ]; if ( !el->isMacro() ) return 0; return static_cast( el )->getMacro(); } int TypesModel::columnCount( const QModelIndex& parent ) const { return parent.isValid() ? 0 : 3; } QVariant TypesModel::data( const QModelIndex& index, int role ) const { if ( !index.isValid() ) return QVariant(); if ( ( index.row() < 0 ) || ( index.row() >= static_cast( melems.size() ) ) ) return QVariant(); switch ( role ) { case Qt::DecorationRole: { if ( index.column() == 1 ) return KIcon( melems[ index.row() ]->icon() ); else return QVariant(); break; } case Qt::DisplayRole: { switch ( index.column() ) { case 0: return melems[ index.row() ]->type(); break; case 1: return melems[ index.row() ]->name(); break; case 2: return melems[ index.row() ]->description(); break; default: return QVariant(); } break; } case Qt::ToolTipRole: { static QString macro_with_image( "" "" "
%1 (%4)
%2
" ); static QString macro_no_image( "%1 (%3)
%2
" ); if ( melems[ index.row() ]->icon( true ).isEmpty() ) return macro_no_image .arg( melems[ index.row() ]->name() ) .arg( wrapAt( melems[ index.row() ]->description() ) ) .arg( melems[ index.row() ]->type() ); else return macro_with_image .arg( melems[ index.row() ]->name() ) .arg( wrapAt( melems[ index.row() ]->description() ) ) .arg( KIconLoader::global()->iconPath( melems[ index.row() ]->icon(), - KIconLoader::SizeMedium ) ) .arg( melems[ index.row() ]->type() ); } default: return QVariant(); } } QVariant TypesModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation != Qt::Horizontal ) return QVariant(); if ( role == Qt::TextAlignmentRole ) return QVariant( Qt::AlignLeft ); if ( role != Qt::DisplayRole ) return QVariant(); switch ( section ) { case 0: return i18n( "Type" ); break; case 1: return i18n( "Name" ); break; case 2: return i18n( "Description" ); break; default: return QVariant(); } } QModelIndex TypesModel::index( int row, int column, const QModelIndex& parent ) const { if ( parent.isValid() || row < 0 || row >= static_cast( melems.size() ) || column < 0 || column > 3 ) return QModelIndex(); return createIndex( row, column, melems[row] ); } int TypesModel::rowCount( const QModelIndex& parent ) const { return parent.isValid() ? 0 : melems.size(); } TypesDialog::TypesDialog( QWidget* parent, KigPart& part ) : KDialog( parent ), mpart( part ) { setCaption( i18n( "Manage Types" ) ); setButtons( Help | Ok | Cancel ); QWidget* base = new QWidget( this ); setMainWidget( base ); mtypeswidget = new Ui_TypesWidget(); mtypeswidget->setupUi( base ); base->layout()->setMargin( 0 ); // model creation and usage mmodel = new TypesModel( mtypeswidget->typeList ); mtypeswidget->typeList->setModel( mmodel ); mtypeswidget->typeList->setContextMenuPolicy( Qt::CustomContextMenu ); // improving GUI look'n'feel... mtypeswidget->buttonEdit->setIcon( KIcon( "document-properties" ) ); mtypeswidget->buttonRemove->setIcon( KIcon( "edit-delete" ) ); mtypeswidget->buttonExport->setIcon( KIcon( "document-export" ) ); mtypeswidget->buttonImport->setIcon( KIcon( "document-import" ) ); // loading macros... mmodel->addMacros( MacroList::instance()->macros() ); // mtypeswidget->typeList->sortItems( 2, Qt::AscendingOrder ); mtypeswidget->typeList->resizeColumnToContents( 0 ); popup = new QMenu( this ); popup->addAction( KIcon( "document-properties" ), i18n( "&Edit..." ), this, SLOT( editType() ) ); popup->addAction( KIcon( "edit-delete" ), i18n( "&Delete" ), this, SLOT( deleteType() ) ); popup->addSeparator(); popup->addAction( KIcon( "document-export" ), i18n( "E&xport..." ), this, SLOT( exportType() ) ); // saving types mpart.saveTypes(); connect( mtypeswidget->buttonExport, SIGNAL( clicked() ), this, SLOT( exportType() ) ); connect( mtypeswidget->buttonImport, SIGNAL( clicked() ), this, SLOT( importTypes() ) ); connect( mtypeswidget->buttonRemove, SIGNAL( clicked() ), this, SLOT( deleteType() ) ); connect( mtypeswidget->buttonEdit, SIGNAL( clicked() ), this, SLOT( editType() ) ); connect( mtypeswidget->typeList, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( typeListContextMenu( const QPoint& ) ) ); connect( this, SIGNAL( helpClicked() ), this, SLOT( slotHelp() ) ); connect( this, SIGNAL( okClicked() ), this, SLOT( slotOk() ) ); connect( this, SIGNAL( cancelClicked() ), this, SLOT( slotCancel() ) ); resize( 460, 270 ); } TypesDialog::~TypesDialog() { delete mtypeswidget; } void TypesDialog::slotHelp() { KToolInvocation::invokeHelp( "working-with-types", "kig" ); } void TypesDialog::slotOk() { mpart.saveTypes(); mpart.deleteTypes(); mpart.loadTypes(); accept(); } void TypesDialog::deleteType() { std::vector selectedTypes; QModelIndexList indexes = selectedRows(); for ( QModelIndexList::const_iterator it = indexes.constBegin(); it != indexes.constEnd(); ++it ) { Macro* macro = mmodel->macroFromIndex( *it ); if ( macro ) { selectedTypes.push_back( macro ); } } if (selectedTypes.empty()) return; QStringList types; for ( std::vector::iterator j = selectedTypes.begin(); j != selectedTypes.end(); ++j ) types << ( *j )->action->descriptiveName(); types.sort(); if ( KMessageBox::warningContinueCancelList( this, i18np( "Are you sure you want to delete this type?", "Are you sure you want to delete these %1 types?", selectedTypes.size() ), types, i18n("Are You Sure?"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "deleteTypeWarning") == KMessageBox::Cancel ) return; bool updates = mtypeswidget->typeList->updatesEnabled(); mtypeswidget->typeList->setUpdatesEnabled( false ); mmodel->removeElements( indexes ); mtypeswidget->typeList->setUpdatesEnabled( updates ); for ( std::vector::iterator j = selectedTypes.begin(); j != selectedTypes.end(); ++j) MacroList::instance()->remove( *j ); } void TypesDialog::exportType() { std::vector types; QModelIndexList indexes = selectedRows(); for ( QModelIndexList::const_iterator it = indexes.constBegin(); it != indexes.constEnd(); ++it ) { Macro* macro = mmodel->macroFromIndex( *it ); if ( macro ) types.push_back( macro ); } if (types.empty()) return; QString file_name = KFileDialog::getSaveFileName( KUrl( "kfiledialog:///macro" ), i18n("*.kigt|Kig Types Files\n*|All Files"), this, i18n( "Export Types" ) ); if ( file_name.isNull() ) return; QFile fi( file_name ); if ( fi.exists() ) if ( KMessageBox::warningContinueCancel( this, i18n( "The file \"%1\" already exists. " "Do you wish to overwrite it?", fi.fileName() ), i18n( "Overwrite File?" ), KStandardGuiItem::overwrite() ) == KMessageBox::Cancel ) return; MacroList::instance()->save( types, file_name ); } void TypesDialog::importTypes() { QStringList file_names = KFileDialog::getOpenFileNames( KUrl( "kfiledialog:///importTypes" ), i18n("*.kigt|Kig Types Files\n*|All Files"), this, i18n( "Import Types" )); std::vector macros; for ( QStringList::const_iterator i = file_names.constBegin(); i != file_names.constEnd(); ++i) { std::vector nmacros; bool ok = MacroList::instance()->load( *i, nmacros, mpart ); if ( ! ok ) continue; std::copy( nmacros.begin(), nmacros.end(), std::back_inserter( macros ) ); }; MacroList::instance()->add( macros ); mmodel->addMacros( macros ); mtypeswidget->typeList->resizeColumnToContents( 0 ); } void TypesDialog::editType() { QModelIndexList indexes = selectedRows(); if ( indexes.isEmpty() ) return; if ( indexes.count() > 1 ) { KMessageBox::sorry( this, i18n( "There is more than one type selected. You can " "only edit one type at a time. Please select " "only the type you want to edit and try again." ), i18n( "More Than One Type Selected" ) ); return; } bool refresh = false; QModelIndex index = indexes.first(); if ( mmodel->isMacro( index ) ) { Macro* oldmacro = mmodel->macroFromIndex( index ); EditType editdialog( this, oldmacro->action->descriptiveName(), oldmacro->action->description(), oldmacro->ctor->iconFileName( false ) ); if ( editdialog.exec() ) { QString newname = editdialog.name(); QString newdesc = editdialog.description(); QString newicon = editdialog.icon(); // mpart.unplugActionLists(); oldmacro->ctor->setName( newname ); oldmacro->ctor->setDescription( newdesc ); QByteArray ncicon( newicon.toUtf8() ); oldmacro->ctor->setIcon( ncicon ); // mpart.plugActionLists(); refresh = true; } } if ( refresh ) { mmodel->elementChanged( index ); } } void TypesDialog::slotCancel() { mpart.deleteTypes(); mpart.loadTypes(); reject(); } QModelIndexList TypesDialog::selectedRows() const { QModelIndexList indexes = mtypeswidget->typeList->selectionModel()->selectedRows(); qSort( indexes ); return indexes; } void TypesDialog::typeListContextMenu( const QPoint& pos ) { QModelIndexList indexes = mtypeswidget->typeList->selectionModel()->selectedRows(); if ( indexes.isEmpty() ) return; popup->exec( mtypeswidget->typeList->viewport()->mapToGlobal( pos ) ); }