profile.cpp

00001 /*
00002     KExtProcess::Profile
00003     Copyright (c) 2005 by Martijn Klingens <klingens@kde.org>
00004 
00005     *************************************************************************
00006     *                                                                       *
00007     * This library is free software; you can redistribute it and/or         *
00008     * modify it under the terms of the GNU Lesser General Public            *
00009     * License as published by the Free Software Foundation; either          *
00010     * version 2 of the License, or (at your option) any later version.      *
00011     *                                                                       *
00012     *************************************************************************
00013 */
00014 
00015 #include "profile.h"
00016 
00017 #include <qfile.h>
00018 #include <qfileinfo.h>
00019 
00020 #include <kdebug.h>
00021 #include <klocale.h>
00022 #include <ksimpleconfig.h>
00023 #include <kstandarddirs.h>
00024 
00025 namespace KExtProcess
00026 {
00027 
00028 class Profile::Private
00029 {
00030 public:
00031     QString filename;
00032     ProfileStepList steps;
00033 };
00034 
00035 Profile::Profile( QObject *parent, const char *name )
00036 : QObject( parent, name )
00037 {
00038     d = new Private;
00039 }
00040 
00041 Profile::~Profile()
00042 {
00043     delete d;
00044 }
00045 
00046 void Profile::cloneFrom( Profile *originalProfile )
00047 {
00048     if ( originalProfile != 0 )
00049         d->steps = originalProfile->d->steps;
00050     else
00051         d->steps.clear();
00052 
00053     emit profileChanged();
00054 }
00055 
00056 void Profile::addStep( const ProfileStep & step )
00057 {
00058     d->steps.append( step );
00059     emit stepAdded( step, d->steps.count() - 1 );
00060 }
00061 
00062 ProfileStepList Profile::steps() const
00063 {
00064     return d->steps;
00065 }
00066 
00067 ProfileStep Profile::step( int index ) const
00068 {
00069     return d->steps[ index ];
00070 }
00071 
00072 void Profile::removeStep( int index )
00073 {
00074     if ( index >= 0 && uint( index ) < d->steps.count() )
00075     {
00076         ProfileStep oldStep = d->steps[ index ];
00077         d->steps.remove( d->steps.at( index ) );
00078         emit stepRemoved( oldStep, index );
00079     }
00080 }
00081 
00082 void Profile::moveStepUp( int index )
00083 {
00084     if ( index < 1 || index > int( d->steps.count() - 1 ) )
00085         return;
00086 
00087     // Take item at original location
00088     ProfileStepList::iterator it = d->steps.at( index );
00089     ProfileStep step = *it;
00090 
00091     d->steps.remove( it );
00092 
00093     // Insert at new location
00094     it = d->steps.at( index - 1 );
00095     d->steps.insert( it, step );
00096 
00097     emit stepMoved( step, index, index - 1 );
00098 }
00099 
00100 void Profile::moveStepDown( int index )
00101 {
00102     if ( index < 0 || index > int( d->steps.count() - 2 ) )
00103         return;
00104 
00105     // Take item at original location
00106     ProfileStepList::iterator it = d->steps.at( index );
00107     ProfileStep step = *it;
00108 
00109     d->steps.remove( it );
00110 
00111     // Insert at new location (append if moving to the end)
00112     if ( index + 1 >= int( d->steps.count() ) )
00113     {
00114         d->steps.append( step );
00115     }
00116     else
00117     {
00118         it = d->steps.at( index + 1 );
00119 
00120         // 'it' now points to the item AFTER our insertion point,
00121         // which is correct due to the semantics of QValueList::insert:
00122         d->steps.insert( it, step );
00123     }
00124 
00125     emit stepMoved( step, index, index + 1 );
00126 }
00127 
00128 QStringList Profile::profileList()
00129 {
00130     // Return relative paths, so they can be displayed in a GUI
00131     KStandardDirs *dirs = KGlobal::dirs();
00132     QStringList absPaths = dirs->findAllResources( "data", "kextprocess/*.kextprocess" );
00133     QStringList relPaths;
00134     for ( QStringList::Iterator it = absPaths.begin(); it != absPaths.end(); ++it )
00135     {
00136         QString rel = dirs->relativeLocation( "data", *it ).replace(
00137             "kextprocess/", QString::null ).replace( ".kextprocess", QString::null );
00138         relPaths.append( rel );
00139     }
00140 
00141     return relPaths;
00142 }
00143 
00144 void Profile::load( const QString &filename )
00145 {
00146     QString baseName = QFileInfo( filename ).baseName();
00147     if ( baseName != filename )
00148     {
00149         kdWarning() << k_funcinfo << "Filename '" << filename <<
00150             "' contains a directory portion! Truncated to '" << baseName << "'." << endl;
00151     }
00152 
00153     QString path = KGlobal::dirs()->findResource( "data", "kextprocess/" + filename + ".kextprocess" );
00154 
00155     d->steps.clear();
00156     if ( path.isNull() )
00157     {
00158         // File not found
00159         kdWarning() << k_funcinfo << "Filename '" << filename <<
00160             ".kextprocess' not found. Resetting to empty profile." << endl;
00161     }
00162     else
00163     {
00164         d->filename = path;
00165 
00166         KSimpleConfig config( path );
00167         config.setGroup( "Process Startup Profile" );
00168 
00169         // The file format is a number that is increased every time the format
00170         // changes in a non-compatible way. The loader can thus adjust its parsing
00171         // to be downwards compatible.
00172         // Subsequent changes between KEP releases don't necessarily bump the
00173         // version, because the added overhead of writing compatibility code
00174         // doesn't outweigh the inconvenience for the brave people who use
00175         // development versions of the code. :)
00176         int version = config.readNumEntry( "fileFormatVersion", 0 );
00177         if ( version < 1 )
00178         {
00179             kdWarning() << k_funcinfo << "Profile '" << filename <<
00180                 "' has an invalid or missing version number. "
00181                 "The file is probably corrupted. Resetting to empty profile." << endl;
00182         }
00183         else
00184         {
00185             if ( version > 1 )
00186             {
00187                 kdWarning() << k_funcinfo << "Profile '" << filename <<
00188                     "' was created with a newer version of the KExtProcess library. "
00189                     "Trying to read the profile, but data may be read incorrectly." << endl;
00190             }
00191 
00192             // Extract the groups, putting them in a QMap, which is sorted by index
00193             QStringList groups = config.groupList();
00194             QMap<uint, QString> indexes;
00195             for ( QStringList::ConstIterator groupIt = groups.begin(); groupIt != groups.end(); ++groupIt )
00196             {
00197                 if ( ( *groupIt ).startsWith( "ProfileStep_" ) )
00198                 {
00199                     bool ok;
00200                     QString groupSuffix = *groupIt;
00201                     groupSuffix.replace( "ProfileStep_", QString::null );
00202                     uint groupIndex = groupSuffix.toUInt( &ok );
00203                     if ( ok )
00204                         indexes.insert( groupIndex, *groupIt );
00205                     else
00206                         kdDebug() << k_funcinfo << "Ignoring group '" << *groupIt << "'" << endl;
00207                 }
00208                 else
00209                 {
00210                     if ( *groupIt != "Process Startup Profile" )
00211                         kdDebug() << k_funcinfo << "Ignoring group '" << *groupIt << "'" << endl;
00212                 }
00213             }
00214 
00215             // Extract all groups
00216             for ( QMap<uint, QString>::ConstIterator groupIt = indexes.begin(); groupIt != indexes.end(); ++groupIt )
00217             {
00218                 QMap<QString, QString> entries = config.entryMap( groupIt.data() );
00219                 config.setGroup( groupIt.data() );
00220 
00221                 // processType is needed in the ctor, so read it ahead of our loop:
00222                 QString processType = config.readEntry( "processType" );
00223                 ProfileStep newStep( processType );
00224 
00225                 // FIXME: I'm not totally sure whether the values that
00226                 //        entryMap() returns are parsed and stripped off of
00227                 //        whitespace and stuff, so use explicit readEntry
00228                 //        calls instead. Might be overkill though - Martijn
00229                 for ( QMap<QString, QString>::ConstIterator entryIt = entries.begin(); entryIt != entries.end(); ++entryIt )
00230                 {
00231                     if ( entryIt.key() == "stepCaption" )
00232                     {
00233                         newStep.setCaption( config.readEntry( entryIt.key() ) );
00234                     }
00235                     else if ( entryIt.key() == "processType" )
00236                     {
00237                         /* Nothing, we already read it above */ ;
00238                     }
00239                     else if ( entryIt.key().startsWith( "property_" ) )
00240                     {
00241                         QString property = entryIt.key();
00242                         newStep.setProperty( property.replace( "property_", QString::null ), config.readEntry( entryIt.key() ) );
00243                     }
00244                     else
00245                     {
00246                         kdDebug() << k_funcinfo << "Ignoring entry '" << entryIt.key() <<
00247                             "' in group '" << groupIt.data() << "'" << endl;
00248                     }
00249                 } // for entries
00250 
00251                 d->steps.append( newStep );
00252             } // for groups
00253         } // if version
00254     } // if path
00255 
00256     emit profileChanged();
00257 }
00258 
00259 void Profile::save( const QString &filename )
00260 {
00261     QString baseName = QFileInfo( filename ).baseName();
00262     if ( baseName != filename )
00263     {
00264         kdWarning() << k_funcinfo << "Filename '" << filename <<
00265             "' contains a directory portion! Truncated to '" << baseName << "'." << endl;
00266     }
00267 
00268     QString fileName = KGlobal::dirs()->saveLocation( "data", "kextprocess/", true ) + baseName + ".kextprocess";
00269 
00270     // Make sure we don't have an existing file. Since the file might not
00271     // exist yet, don't assume 'false' is an error and don't check for it.
00272     QFile::remove( fileName );
00273 
00274     KSimpleConfig config( fileName );
00275     config.setGroup( "Process Startup Profile" );
00276 
00277     // The file format is a number that is increased every time the format
00278     // changes in a non-compatible way. The loader can thus adjust its parsing
00279     // to be downwards compatible.
00280     // Subsequent changes between KEP releases don't necessarily bump the
00281     // version, because the added overhead of writing compatibility code
00282     // doesn't outweigh the inconvenience for the brave people who use
00283     // development versions of the code. :)
00284     config.writeEntry( "fileFormatVersion", 1 );
00285 
00286     ProfileStepList::iterator stepIt;
00287     uint index = 0;
00288     for ( stepIt = d->steps.begin(); stepIt != d->steps.end(); ++stepIt )
00289     {
00290         ProfileStep step = *stepIt;
00291         config.setGroup( QString( "ProfileStep_" ) + QString::number( index ) );
00292         config.writeEntry( "stepCaption", step.caption() );
00293         config.writeEntry( "processType", step.processType() );
00294 
00295         QMap<QString, QString> properties = step.properties();
00296         QMap<QString, QString>::Iterator it;
00297         for ( it = properties.begin(); it != properties.end(); ++it )
00298             config.writeEntry( QString( "property_" ) + it.key(), it.data() );
00299 
00300         index++;
00301     }
00302 
00303     config.sync();
00304     d->filename = fileName;
00305 }
00306 
00307 QString Profile::filename() const
00308 {
00309     return d->filename;
00310 }
00311 
00312 QString Profile::caption() const
00313 {
00314     QString baseName = QFileInfo( d->filename ).baseName();
00315     return baseName.replace( ".kextprocess", QString::null );
00316 }
00317 
00318 } // namespace
00319 
00320 #include "profile.moc"
00321 
KDE Home | KDE Accessibility Home | Description of Access Keys