krop's commit fixes my problem in a better way, reverting
[kdepim.git] / libkdepim / kcmdesignerfields.cpp
blob81beb02892225bc67605feff4b13c46d6605743f
1 /*
2 This file is part of libkdepim.
4 Copyright (c) 2004 Tobias Koenig <tokoe@kde.org>
5 Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
23 #include "kcmdesignerfields.h"
25 #include <KAboutData>
26 #include <KDebug>
27 #include <KDialog>
28 #include <KDirWatch>
29 #include <KFileDialog>
30 #include <KGlobal>
31 #include <KLocale>
32 #include <KMessageBox>
33 #include <KRun>
34 #include <KShell>
35 #include <KStandardDirs>
36 #include <K3ListView>
37 #include <kio/job.h>
38 #include <kio/netaccess.h>
40 #include <QImage>
41 #include <QLabel>
42 #include <QLayout>
43 #include <QObject>
44 #include <QPixmap>
45 #include <QPushButton>
46 #include <QRegExp>
47 #include <QTimer>
48 #include <QtDesigner/QFormBuilder>
49 #include <QHBoxLayout>
50 #include <QFrame>
51 #include <QVBoxLayout>
52 #include <QWhatsThis>
53 #include <Q3GroupBox>
55 #include <unistd.h>
57 using namespace KPIM;
59 namespace KPIM {
61 class PageItem : public Q3CheckListItem
63 public:
64 PageItem( Q3ListView *parent, const QString &path )
65 : Q3CheckListItem( parent, "", Q3CheckListItem::CheckBox ),
66 mPath( path ), mIsActive( false )
68 mName = path.mid( path.lastIndexOf( '/' ) + 1 );
70 QFile f( mPath );
71 f.open(QFile::ReadOnly);
72 QFormBuilder builder;
73 QWidget *wdg = builder.load( &f, 0 );
74 f.close();
75 if ( wdg ) {
76 setText( 0, wdg->windowTitle() );
78 QPixmap pm = QPixmap::grabWidget( wdg );
79 QImage img = pm.toImage().scaled( 300, 300, Qt::KeepAspectRatio, Qt::SmoothTransformation );
80 mPreview = QPixmap::fromImage(img);
82 QMap<QString, QString> allowedTypes;
83 allowedTypes.insert( "QLineEdit", i18n( "Text" ) );
84 allowedTypes.insert( "QTextEdit", i18n( "Text" ) );
85 allowedTypes.insert( "QSpinBox", i18n( "Numeric Value" ) );
86 allowedTypes.insert( "QCheckBox", i18n( "Boolean" ) );
87 allowedTypes.insert( "QComboBox", i18n( "Selection" ) );
88 allowedTypes.insert( "QDateTimeEdit", i18n( "Date & Time" ) );
89 allowedTypes.insert( "KLineEdit", i18n( "Text" ) );
90 allowedTypes.insert( "KTextEdit", i18n( "Text" ) );
91 allowedTypes.insert( "KDateTimeWidget", i18n( "Date & Time" ) );
92 allowedTypes.insert( "KDatePicker", i18n( "Date" ) );
94 QList<QWidget*> list = wdg->findChildren<QWidget*>();
95 QWidget *it;
96 Q_FOREACH( it, list ) {
97 if ( allowedTypes.contains( it->metaObject()->className() ) ) {
98 QString name = it->objectName();
99 if ( name.startsWith( "X_" ) ) {
100 new Q3ListViewItem( this, name,
101 allowedTypes[ it->metaObject()->className() ],
102 it->metaObject()->className(),
103 it->whatsThis() );
111 QString name() const { return mName; }
112 QString path() const { return mPath; }
114 QPixmap preview()
116 return mPreview;
119 void setIsActive( bool isActive ) { mIsActive = isActive; }
120 bool isActive() const { return mIsActive; }
122 protected:
123 void paintBranches( QPainter *p, const QColorGroup & cg, int w, int y, int h )
125 Q3ListViewItem::paintBranches( p, cg, w, y, h );
128 private:
129 QString mName;
130 QString mPath;
131 QPixmap mPreview;
132 bool mIsActive;
138 KCMDesignerFields::KCMDesignerFields( const KComponentData &instance, QWidget *parent, const QStringList &args )
139 : KCModule( instance, parent, args )
140 ,mPageView(NULL)
141 ,mPagePreview(NULL)
142 ,mPageDetails(NULL)
143 ,mDeleteButton(NULL)
144 ,mImportButton(NULL)
145 ,mDesignerButton(NULL)
147 KAboutData *about = new KAboutData( I18N_NOOP( "KCMDesignerfields" ), 0,
148 ki18n( "Qt Designer Fields Dialog" ),
149 0, KLocalizedString(), KAboutData::License_LGPL,
150 ki18n( "(c), 2004 Tobias Koenig" ) );
152 about->addAuthor( ki18n("Tobias Koenig"), KLocalizedString(), "tokoe@kde.org" );
153 about->addAuthor( ki18n("Cornelius Schumacher"), KLocalizedString(), "schumacher@kde.org" );
154 setAboutData( about );
157 void KCMDesignerFields::delayedInit()
159 kDebug() <<"KCMDesignerFields::delayedInit()";
161 initGUI();
163 connect( mPageView, SIGNAL( selectionChanged( Q3ListViewItem* ) ),
164 this, SLOT( updatePreview( Q3ListViewItem* ) ) );
165 connect( mPageView, SIGNAL( clicked( Q3ListViewItem* ) ),
166 this, SLOT( itemClicked( Q3ListViewItem* ) ) );
168 connect( mDeleteButton, SIGNAL( clicked() ),
169 this, SLOT( deleteFile() ) );
170 connect( mImportButton, SIGNAL( clicked() ),
171 this, SLOT( importFile() ) );
172 connect( mDesignerButton, SIGNAL( clicked() ),
173 this, SLOT( startDesigner() ) );
175 load();
177 // Install a dirwatcher that will detect newly created or removed designer files
178 KDirWatch *dw = new KDirWatch( this );
179 KStandardDirs::makeDir( localUiDir() );
180 dw->addDir( localUiDir(), KDirWatch::WatchFiles );
181 connect( dw, SIGNAL( created(const QString&) ), SLOT( rebuildList() ) );
182 connect( dw, SIGNAL( deleted(const QString&) ), SLOT( rebuildList() ) );
183 connect( dw, SIGNAL( dirty(const QString&) ), SLOT( rebuildList() ) );
186 void KCMDesignerFields::deleteFile()
188 Q3ListViewItem *item = mPageView->selectedItem();
189 if ( item ) {
190 PageItem *pageItem = static_cast<PageItem*>( item->parent() ? item->parent() : item );
191 if (KMessageBox::warningContinueCancel(this,
192 i18n( "<qt>Do you really want to delete '<b>%1</b>'?</qt>", pageItem->text(0) ), "", KStandardGuiItem::del() )
193 == KMessageBox::Continue)
194 KIO::NetAccess::del( pageItem->path(), 0 );
196 // The actual view refresh will be done automagically by the slots connected to kdirwatch
199 void KCMDesignerFields::importFile()
201 KUrl src = KFileDialog::getOpenFileName( QDir::homePath(), i18n("*.ui|Designer Files"),
202 this, i18n("Import Page") );
203 KUrl dest = localUiDir();
204 QDir().mkpath( localUiDir() );
205 dest.setFileName(src.fileName());
206 KIO::Job *job = KIO::file_copy( src, dest, -1, KIO::Overwrite );
207 KIO::NetAccess::synchronousRun( job, this );
209 // The actual view refresh will be done automagically by the slots connected to kdirwatch
213 void KCMDesignerFields::loadUiFiles()
215 const QStringList list = KGlobal::dirs()->findAllResources( "data", uiPath() + "/*.ui",
216 KStandardDirs::Recursive |
217 KStandardDirs::NoDuplicates );
218 for ( QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it ) {
219 new PageItem( mPageView, *it );
223 void KCMDesignerFields::rebuildList()
225 // If nothing is initialized there is no need to do something
226 if (mPageView) {
227 QStringList ai = saveActivePages();
228 updatePreview( 0 );
229 mPageView->clear();
230 loadUiFiles();
231 loadActivePages(ai);
235 void KCMDesignerFields::loadActivePages(const QStringList& ai)
237 Q3ListViewItemIterator it( mPageView );
238 while ( it.current() ) {
239 if ( it.current()->parent() == 0 ) {
240 PageItem *item = static_cast<PageItem*>( it.current() );
241 if ( ai.contains( item->name() ) ) {
242 item->setOn( true );
243 item->setIsActive( true );
247 ++it;
251 void KCMDesignerFields::load()
253 // see KCModule::showEvent()
254 if (!mPageView) {
255 delayedInit();
257 loadActivePages( readActivePages() );
260 QStringList KCMDesignerFields::saveActivePages()
262 Q3ListViewItemIterator it( mPageView, Q3ListViewItemIterator::Checked |
263 Q3ListViewItemIterator::Selectable );
265 QStringList activePages;
266 while ( it.current() ) {
267 if ( it.current()->parent() == 0 ) {
268 PageItem *item = static_cast<PageItem*>( it.current() );
269 activePages.append( item->name() );
272 ++it;
275 return activePages;
278 void KCMDesignerFields::save()
280 writeActivePages( saveActivePages() );
283 void KCMDesignerFields::defaults()
287 void KCMDesignerFields::initGUI()
289 QVBoxLayout *layout = new QVBoxLayout( this );
290 layout->setSpacing( KDialog::spacingHint() );
291 layout->setMargin( KDialog::marginHint() );
293 bool noDesigner = KStandardDirs::findExe("designer").isEmpty();
295 if ( noDesigner )
297 QString txt =
298 i18n("<qt><b>Warning:</b> Qt Designer could not be found. It is probably not "
299 "installed. You will only be able to import existing designer files.</qt>");
300 QLabel *lbl = new QLabel( txt, this );
301 layout->addWidget( lbl );
304 QHBoxLayout *hbox = new QHBoxLayout();
305 layout->addLayout( hbox );
306 hbox->setSpacing( KDialog::spacingHint() );
308 mPageView = new K3ListView( this );
309 mPageView->addColumn( i18n( "Available Pages" ) );
310 mPageView->setRootIsDecorated( true );
311 mPageView->setAllColumnsShowFocus( true );
312 mPageView->setFullWidth( true );
313 hbox->addWidget( mPageView );
314 QTimer::singleShot( 0, mPageView, SLOT(triggerUpdate()) );
316 Q3GroupBox *box = new Q3GroupBox(1, Qt::Horizontal, i18n("Preview of Selected Page"), this );
318 mPagePreview = new QLabel( box );
319 mPagePreview->setMinimumWidth( 300 );
321 mPageDetails = new QLabel( box );
323 hbox->addWidget( box );
325 loadUiFiles();
327 hbox = new QHBoxLayout();
328 layout->addLayout( hbox );
329 hbox->setSpacing( KDialog::spacingHint() );
331 QString cwHowto = i18n("<qt><p>This section allows you to add your own GUI"
332 " Elements ('<i>Widgets</i>') to store your own values"
333 " into %1. Proceed as described below:</p>"
334 "<ol>"
335 "<li>Click on '<i>Edit with Qt Designer</i>'</li>"
336 "<li>In the dialog, select '<i>Widget</i>', then click <i>OK</i></li>"
337 "<li>Add your widgets to the form</li>"
338 "<li>Save the file in the directory proposed by Qt Designer</li>"
339 "<li>Close Qt Designer</li>"
340 "</ol>"
341 "<p>In case you already have a designer file (*.ui) located"
342 " somewhere on your hard disk, simply choose '<i>Import Page</i>'</p>"
343 "<p><b>Important:</b> The name of each input widget you place within"
344 " the form must start with '<i>X_</i>'; so if you want the widget to"
345 " correspond to your custom entry '<i>X-Foo</i>', set the widget's"
346 " <i>name</i> property to '<i>X_Foo</i>'.</p>"
347 "<p><b>Important:</b> The widget will edit custom fields with an"
348 " application name of %2. To change the application name"
349 " to be edited, set the widget name in Qt Designer.</p></qt>",
350 applicationName(), applicationName() );
352 QLabel *activeLabel = new QLabel(
353 i18n( "<a href=\"whatsthis:%1\">How does this work?</a>" , cwHowto), this );
354 activeLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::LinksAccessibleByKeyboard);
355 connect( activeLabel, SIGNAL(linkActivated(QString)),
356 this, SLOT(showWhatsThis(QString)) );
357 hbox->addWidget( activeLabel );
360 // ### why is this needed? Looks like a KActiveLabel bug...
361 activeLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum );
363 hbox->addStretch( 1 );
365 mDeleteButton = new QPushButton( i18n( "Delete Page" ), this);
366 mDeleteButton->setEnabled( false );
367 hbox->addWidget( mDeleteButton );
368 mImportButton = new QPushButton( i18n( "Import Page..." ), this);
369 hbox->addWidget( mImportButton );
370 mDesignerButton = new QPushButton( i18n( "Edit with Qt Designer..." ), this );
371 hbox->addWidget( mDesignerButton );
373 if ( noDesigner )
374 mDesignerButton->setEnabled( false );
377 void KCMDesignerFields::updatePreview( Q3ListViewItem *item )
379 bool widgetItemSelected = false;
381 if ( item ) {
382 if ( item->parent() ) {
383 QString details = QString( "<qt><table>"
384 "<tr><td align=\"right\"><b>%1</b></td><td>%2</td></tr>"
385 "<tr><td align=\"right\"><b>%3</b></td><td>%4</td></tr>"
386 "<tr><td align=\"right\"><b>%5</b></td><td>%6</td></tr>"
387 "<tr><td align=\"right\"><b>%7</b></td><td>%8</td></tr>"
388 "</table></qt>" )
389 .arg( i18n( "Key:" ) )
390 .arg( item->text( 0 ).replace("X_","X-") )
391 .arg( i18n( "Type:" ) )
392 .arg( item->text( 1 ) )
393 .arg( i18n( "Classname:" ) )
394 .arg( item->text( 2 ) )
395 .arg( i18n( "Description:" ) )
396 .arg( item->text( 3 ) );
398 mPageDetails->setText( details );
400 PageItem *pageItem = static_cast<PageItem*>( item->parent() );
401 mPagePreview->setWindowIcon( pageItem->preview() );
402 } else {
403 mPageDetails->setText( QString() );
405 PageItem *pageItem = static_cast<PageItem*>( item );
406 mPagePreview->setWindowIcon( pageItem->preview() );
408 widgetItemSelected = true;
411 mPagePreview->setFrameStyle( QFrame::Panel | QFrame::Sunken );
412 } else {
413 mPagePreview->setWindowIcon( QPixmap() );
414 mPagePreview->setFrameStyle( 0 );
415 mPageDetails->setText( QString() );
418 mDeleteButton->setEnabled( widgetItemSelected );
421 void KCMDesignerFields::itemClicked( Q3ListViewItem *item )
423 if ( !item || item->parent() != 0 )
424 return;
426 PageItem *pageItem = static_cast<PageItem*>( item );
428 if ( pageItem->isOn() != pageItem->isActive() ) {
429 emit changed( true );
430 pageItem->setIsActive( pageItem->isOn() );
434 void KCMDesignerFields::startDesigner()
436 QString cmdLine = "designer";
438 // check if path exists and create one if not.
439 QString cepPath = localUiDir();
440 if( !KGlobal::dirs()->exists(cepPath) ) {
441 KIO::NetAccess::mkdir( cepPath, this );
444 // finally jump there
445 chdir(cepPath.toLocal8Bit());
447 Q3ListViewItem *item = mPageView->selectedItem();
448 if ( item ) {
449 PageItem *pageItem = static_cast<PageItem*>( item->parent() ? item->parent() : item );
450 cmdLine += ' ' + KShell::quoteArg( pageItem->path() );
453 KRun::runCommand( cmdLine, topLevelWidget());
456 void KCMDesignerFields::showWhatsThis(const QString &href)
458 if (href.startsWith("whatsthis:")) {
459 QPoint pos = QCursor::pos();
460 QWhatsThis::showText(pos, href.mid(10), this);
464 #include "kcmdesignerfields.moc"