libkdepim

kcmdesignerfields.cpp

00001 /*
00002     This file is part of libkdepim.
00003 
00004     Copyright (c) 2004 Tobias Koenig <tokoe@kde.org>
00005     Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Library 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     This library is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Library General Public License for more details.
00016 
00017     You should have received a copy of the GNU Library General Public License
00018     along with this library; see the file COPYING.LIB.  If not, write to
00019     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020     Boston, MA 02110-1301, USA.
00021 */
00022 
00023 #include <unistd.h>
00024 
00025 #include <qimage.h>
00026 #include <qlabel.h>
00027 #include <qlayout.h>
00028 #include <qobjectlist.h>
00029 #include <qpixmap.h>
00030 #include <qpushbutton.h>
00031 #include <qwhatsthis.h>
00032 #include <qgroupbox.h>
00033 #include <qwidgetfactory.h>
00034 #include <qregexp.h>
00035 #include <qtimer.h>
00036 
00037 #include <kaboutdata.h>
00038 #include <kdebug.h>
00039 #include <kdialog.h>
00040 #include <kglobal.h>
00041 #include <klistview.h>
00042 #include <klocale.h>
00043 #include <krun.h>
00044 #include <kstandarddirs.h>
00045 #include <kactivelabel.h>
00046 #include <kdirwatch.h>
00047 #include <kfiledialog.h>
00048 #include <kmessagebox.h>
00049 #include <kprocess.h>
00050 #include <kio/netaccess.h>
00051 
00052 #include "kcmdesignerfields.h"
00053 
00054 using namespace KPIM;
00055 
00056 class PageItem : public QCheckListItem
00057 {
00058   public:
00059     PageItem( QListView *parent, const QString &path )
00060       : QCheckListItem( parent, "", QCheckListItem::CheckBox ),
00061         mPath( path ), mIsActive( false )
00062     {
00063       mName = path.mid( path.findRev( '/' ) + 1 );
00064 
00065       QWidget *wdg = QWidgetFactory::create( mPath, 0, 0 );
00066       if ( wdg ) {
00067         setText( 0, wdg->caption() );
00068 
00069         QPixmap pm = QPixmap::grabWidget( wdg );
00070         QImage img = pm.convertToImage().smoothScale( 300, 300, QImage::ScaleMin );
00071         mPreview = img;
00072 
00073         QObjectList *list = wdg->queryList( "QWidget" );
00074         QObjectListIt it( *list );
00075 
00076         QMap<QString, QString> allowedTypes;
00077         allowedTypes.insert( "QLineEdit", i18n( "Text" ) );
00078         allowedTypes.insert( "QTextEdit", i18n( "Text" ) );
00079         allowedTypes.insert( "QSpinBox", i18n( "Numeric Value" ) );
00080         allowedTypes.insert( "QCheckBox", i18n( "Boolean" ) );
00081         allowedTypes.insert( "QComboBox", i18n( "Selection" ) );
00082         allowedTypes.insert( "QDateTimeEdit", i18n( "Date & Time" ) );
00083         allowedTypes.insert( "KLineEdit", i18n( "Text" ) );
00084         allowedTypes.insert( "KDateTimeWidget", i18n( "Date & Time" ) );
00085         allowedTypes.insert( "KDatePicker", i18n( "Date" ) );
00086 
00087         while ( it.current() ) {
00088           if ( allowedTypes.find( it.current()->className() ) != allowedTypes.end() ) {
00089             QString name = it.current()->name();
00090             if ( name.startsWith( "X_" ) ) {
00091               new QListViewItem( this, name,
00092                                  allowedTypes[ it.current()->className() ],
00093                                  it.current()->className(),
00094                                  QWhatsThis::textFor( static_cast<QWidget*>( it.current() ) ) );
00095             }
00096           }
00097 
00098           ++it;
00099         }
00100 
00101         delete list;
00102       } 
00103     }
00104 
00105     QString name() const { return mName; }
00106     QString path() const { return mPath; }
00107 
00108     QPixmap preview()
00109     {
00110       return mPreview;
00111     }
00112 
00113     void setIsActive( bool isActive ) { mIsActive = isActive; }
00114     bool isActive() const { return mIsActive; }
00115 
00116   protected:
00117     void paintBranches( QPainter *p, const QColorGroup & cg, int w, int y, int h )
00118     {
00119       QListViewItem::paintBranches( p, cg, w, y, h );
00120     }
00121 
00122   private:
00123     QString mName;
00124     QString mPath;
00125     QPixmap mPreview;
00126     bool mIsActive;
00127 };
00128 
00129 KCMDesignerFields::KCMDesignerFields( QWidget *parent, const char *name )
00130   : KCModule( parent, name )
00131 {
00132   QTimer::singleShot( 0, this, SLOT( delayedInit() ) );
00133   
00134   KAboutData *about = new KAboutData( I18N_NOOP( "KCMDesignerfields" ),
00135                                       I18N_NOOP( "Qt Designer Fields Dialog" ),
00136                                       0, 0, KAboutData::License_LGPL,
00137                                       I18N_NOOP( "(c), 2004 Tobias Koenig" ) );
00138 
00139   about->addAuthor( "Tobias Koenig", 0, "tokoe@kde.org" );
00140   about->addAuthor( "Cornelius Schumacher", 0, "schumacher@kde.org" );
00141   setAboutData( about );
00142 }
00143 
00144 void KCMDesignerFields::delayedInit()
00145 {
00146   kdDebug() << "KCMDesignerFields::delayedInit()" << endl;
00147 
00148   initGUI();
00149 
00150   connect( mPageView, SIGNAL( selectionChanged( QListViewItem* ) ),
00151            this, SLOT( updatePreview( QListViewItem* ) ) );
00152   connect( mPageView, SIGNAL( clicked( QListViewItem* ) ),
00153            this, SLOT( itemClicked( QListViewItem* ) ) );
00154 
00155   connect( mDeleteButton, SIGNAL( clicked() ),
00156            this, SLOT( deleteFile() ) );
00157   connect( mImportButton, SIGNAL( clicked() ),
00158            this, SLOT( importFile() ) );
00159   connect( mDesignerButton, SIGNAL( clicked() ),
00160            this, SLOT( startDesigner() ) );
00161 
00162   load();
00163 
00164   // Install a dirwatcher that will detect newly created or removed designer files
00165   KDirWatch *dw = new KDirWatch( this );
00166   dw->addDir( localUiDir(), true );
00167   connect( dw, SIGNAL( created(const QString&) ), SLOT( rebuildList() ) );
00168   connect( dw, SIGNAL( deleted(const QString&) ), SLOT( rebuildList() ) );
00169   connect( dw, SIGNAL( dirty(const QString&) ),   SLOT( rebuildList() ) );
00170 }
00171 
00172 void KCMDesignerFields::deleteFile()
00173 {
00174   QListViewItem *item = mPageView->selectedItem();
00175   if ( item ) {
00176     PageItem *pageItem = static_cast<PageItem*>( item->parent() ? item->parent() : item );
00177     if (KMessageBox::warningContinueCancel(this,
00178     i18n( "<qt>Do you really want to delete '<b>%1</b>'?</qt>").arg( pageItem->text(0) ), "", KStdGuiItem::del() )
00179          == KMessageBox::Continue)
00180       KIO::NetAccess::del( pageItem->path(), 0 );
00181   }
00182   // The actual view refresh will be done automagically by the slots connected to kdirwatch
00183 }
00184 
00185 void KCMDesignerFields::importFile()
00186 {
00187   KURL src = KFileDialog::getOpenFileName( QDir::homeDirPath(), i18n("*.ui|Designer Files"),
00188                                               this, i18n("Import Page") );
00189   KURL dest = localUiDir();
00190   dest.setFileName(src.fileName());
00191   KIO::NetAccess::file_copy( src, dest, -1, true, false, this );
00192   // The actual view refresh will be done automagically by the slots connected to kdirwatch
00193 }
00194 
00195 
00196 void KCMDesignerFields::loadUiFiles()
00197 {
00198   QStringList list = KGlobal::dirs()->findAllResources( "data", uiPath() + "/*.ui", true, true );
00199   for ( QStringList::iterator it = list.begin(); it != list.end(); ++it ) {
00200     new PageItem( mPageView, *it );
00201   }
00202 }
00203 
00204 void KCMDesignerFields::rebuildList()
00205 {
00206   QStringList ai = saveActivePages();
00207   updatePreview( 0 );
00208   mPageView->clear();
00209   loadUiFiles();
00210   loadActivePages(ai);
00211 }
00212 
00213 void KCMDesignerFields::loadActivePages(const QStringList& ai)
00214 {
00215   QListViewItemIterator it( mPageView );
00216   while ( it.current() ) {
00217     if ( it.current()->parent() == 0 ) {
00218       PageItem *item = static_cast<PageItem*>( it.current() );
00219       if ( ai.find( item->name() ) != ai.end() ) {
00220         item->setOn( true );
00221         item->setIsActive( true );
00222       }
00223     }
00224 
00225     ++it;
00226   }
00227 }
00228 
00229 void KCMDesignerFields::load()
00230 {
00231   loadActivePages( readActivePages() );
00232 }
00233 
00234 QStringList KCMDesignerFields::saveActivePages()
00235 {
00236   QListViewItemIterator it( mPageView, QListViewItemIterator::Checked |
00237                             QListViewItemIterator::Selectable );
00238 
00239   QStringList activePages;
00240   while ( it.current() ) {
00241     if ( it.current()->parent() == 0 ) {
00242       PageItem *item = static_cast<PageItem*>( it.current() );
00243       activePages.append( item->name() );
00244     }
00245 
00246     ++it;
00247   }
00248 
00249   return activePages;
00250 }
00251 
00252 void KCMDesignerFields::save()
00253 {
00254   writeActivePages( saveActivePages() );
00255 }
00256 
00257 void KCMDesignerFields::defaults()
00258 {
00259 }
00260 
00261 void KCMDesignerFields::initGUI()
00262 {
00263   QVBoxLayout *layout = new QVBoxLayout( this, KDialog::marginHint(),
00264                                          KDialog::spacingHint() );
00265 
00266   bool noDesigner = KStandardDirs::findExe("designer").isEmpty();
00267 
00268   if ( noDesigner )
00269   {
00270     QString txt =
00271       i18n("<qt><b>Warning:</b> Qt Designer could not be found. It is probably not "
00272          "installed. You will only be able to import existing designer files.</qt>");
00273     QLabel *lbl = new QLabel( txt, this );
00274     layout->addWidget( lbl );
00275   }
00276 
00277   QHBoxLayout *hbox = new QHBoxLayout( layout, KDialog::spacingHint() );
00278 
00279   mPageView = new KListView( this );
00280   mPageView->addColumn( i18n( "Available Pages" ) );
00281   mPageView->setRootIsDecorated( true );
00282   mPageView->setAllColumnsShowFocus( true );
00283   mPageView->setFullWidth( true );
00284   hbox->addWidget( mPageView );
00285 
00286   QGroupBox *box = new QGroupBox(1, Qt::Horizontal, i18n("Preview of Selected Page"), this );
00287 
00288   mPagePreview = new QLabel( box );
00289   mPagePreview->setMinimumWidth( 300 );
00290 
00291   mPageDetails = new QLabel( box );
00292 
00293   hbox->addWidget( box );
00294 
00295   loadUiFiles();
00296 
00297   hbox = new QHBoxLayout( layout, KDialog::spacingHint() );
00298 
00299   QString cwHowto = i18n("<qt><p>This section allows you to add your own GUI"
00300                          "  Elements ('<i>Widgets</i>') to store your own values"
00301                          " into %1. Proceed as described below:</p>"
00302                          "<ol>"
00303                          "<li>Click on '<i>Edit with Qt Designer</i>'"
00304                          "<li>In the dialog, select '<i>Widget</i>', then click <i>OK</i>"
00305                          "<li>Add your widgets to the form"
00306                          "<li>Save the file in the directory proposed by Qt Designer"
00307                          "<li>Close Qt Designer"
00308                          "</ol>"
00309                          "<p>In case you already have a designer file (*.ui) located"
00310                          " somewhere on your hard disk, simply choose '<i>Import Page</i>'</p>"
00311                          "<p><b>Important:</b> The name of each input widget you place within"
00312                          " the form must start with '<i>X_</i>'; so if you want the widget to"
00313                          " correspond to your custom entry '<i>X-Foo</i>', set the widget's"
00314                          " <i>name</i> property to '<i>X_Foo</i>'.</p>"
00315                          "<p><b>Important:</b> The widget will edit custom fields with an"
00316                          " application name of %2.  To change the application name"
00317                          " to be edited, set the widget name in Qt Designer.</p></qt>" )
00318                          .arg( applicationName(), applicationName() );
00319 
00320   KActiveLabel *activeLabel = new KActiveLabel(
00321       i18n( "<a href=\"whatsthis:%1\">How does this work?</a>" ).arg(cwHowto), this );
00322   hbox->addWidget( activeLabel );
00323 
00324   // ### why is this needed? Looks like a KActiveLabel bug...
00325   activeLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum );
00326 
00327   hbox->addStretch( 1 );
00328 
00329   mDeleteButton = new QPushButton( i18n( "Delete Page" ), this);
00330   mDeleteButton->setEnabled( false );
00331   hbox->addWidget( mDeleteButton );
00332   mImportButton = new QPushButton( i18n( "Import Page..." ), this);
00333   hbox->addWidget( mImportButton );
00334   mDesignerButton = new QPushButton( i18n( "Edit with Qt Designer..." ), this );
00335   hbox->addWidget( mDesignerButton );
00336 
00337   if ( noDesigner )
00338     mDesignerButton->setEnabled( false );
00339 
00340   // FIXME: Why do I have to call show() for all widgets? A this->show() doesn't
00341   // seem to work.
00342   mPageView->show();
00343   box->show();
00344   activeLabel->show();
00345   mDeleteButton->show();
00346   mImportButton->show();
00347   mDesignerButton->show();
00348 }
00349 
00350 void KCMDesignerFields::updatePreview( QListViewItem *item )
00351 {
00352   bool widgetItemSelected = false;
00353 
00354   if ( item ) {
00355     if ( item->parent() ) {
00356       QString details = QString( "<qt><table>"
00357                                  "<tr><td align=\"right\"><b>%1</b></td><td>%2</td></tr>"
00358                                  "<tr><td align=\"right\"><b>%3</b></td><td>%4</td></tr>"
00359                                  "<tr><td align=\"right\"><b>%5</b></td><td>%6</td></tr>"
00360                                  "<tr><td align=\"right\"><b>%7</b></td><td>%8</td></tr>"
00361                                  "</table></qt>" )
00362                                 .arg( i18n( "Key:" ) )
00363                                 .arg( item->text( 0 ).replace("X_","X-") )
00364                                 .arg( i18n( "Type:" ) )
00365                                 .arg( item->text( 1 ) )
00366                                 .arg( i18n( "Classname:" ) )
00367                                 .arg( item->text( 2 ) )
00368                                 .arg( i18n( "Description:" ) )
00369                                 .arg( item->text( 3 ) );
00370 
00371       mPageDetails->setText( details );
00372 
00373       PageItem *pageItem = static_cast<PageItem*>( item->parent() );
00374       mPagePreview->setPixmap( pageItem->preview() );
00375     } else {
00376       mPageDetails->setText( QString::null );
00377 
00378       PageItem *pageItem = static_cast<PageItem*>( item );
00379       mPagePreview->setPixmap( pageItem->preview() );
00380 
00381       widgetItemSelected = true;
00382     }
00383 
00384     mPagePreview->setFrameStyle( QFrame::Panel | QFrame::Sunken );
00385   } else {
00386     mPagePreview->setPixmap( QPixmap() );
00387     mPagePreview->setFrameStyle( 0 );
00388     mPageDetails->setText( QString::null );
00389   }
00390 
00391   mDeleteButton->setEnabled( widgetItemSelected );
00392 }
00393 
00394 void KCMDesignerFields::itemClicked( QListViewItem *item )
00395 {
00396   if ( !item || item->parent() != 0 )
00397     return;
00398 
00399   PageItem *pageItem = static_cast<PageItem*>( item );
00400 
00401   if ( pageItem->isOn() != pageItem->isActive() ) {
00402     emit changed( true );
00403     pageItem->setIsActive( pageItem->isOn() );
00404   }
00405 }
00406 
00407 void KCMDesignerFields::startDesigner()
00408 {
00409   QString cmdLine = "designer";
00410 
00411   // check if path exists and create one if not.
00412   QString cepPath = localUiDir();
00413   if( !KGlobal::dirs()->exists(cepPath) ) {
00414     KIO::NetAccess::mkdir( cepPath, this );
00415   }
00416 
00417   // finally jump there
00418   chdir(cepPath.local8Bit());
00419 
00420   QListViewItem *item = mPageView->selectedItem();
00421   if ( item ) {
00422     PageItem *pageItem = static_cast<PageItem*>( item->parent() ? item->parent() : item );
00423     cmdLine += " " + KProcess::quote( pageItem->path() );
00424   }
00425 
00426   KRun::runCommand( cmdLine );
00427 }
00428 
00429 #include "kcmdesignerfields.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys