KDEUI
ktip.cpp
Go to the documentation of this file.
00001 /***************************************************************** 00002 00003 Copyright (c) 2000-2003 Matthias Hoelzer-Kluepfel <mhk@kde.org> 00004 Tobias Koenig <tokoe@kde.org> 00005 Daniel Molkentin <molkentin@kde.org> 00006 Copyright (c) 2008 Urs Wolfer <uwolfer @ kde.org> 00007 00008 Permission is hereby granted, free of charge, to any person obtaining a copy 00009 of this software and associated documentation files (the "Software"), to deal 00010 in the Software without restriction, including without limitation the rights 00011 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 00012 copies of the Software, and to permit persons to whom the Software is 00013 furnished to do so, subject to the following conditions: 00014 00015 The above copyright notice and this permission notice shall be included in 00016 all copies or substantial portions of the Software. 00017 00018 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00019 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00020 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00021 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 00022 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 00023 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00024 00025 ******************************************************************/ 00026 00027 #include "ktip.h" 00028 00029 #include <QtCore/QFile> 00030 #include <QtGui/QCheckBox> 00031 #include <QtGui/QKeyEvent> 00032 #include <QtGui/QLabel> 00033 #include <QtGui/QLayout> 00034 00035 #include <kaboutdata.h> 00036 #include <kconfig.h> 00037 #include <kdebug.h> 00038 #include <kglobalsettings.h> 00039 #include <kcomponentdata.h> 00040 #include <klocale.h> 00041 #include <kpushbutton.h> 00042 #include <krandom.h> 00043 #include <kseparator.h> 00044 #include <kstandarddirs.h> 00045 #include <ktextbrowser.h> 00046 00047 class KTipDatabase::Private 00048 { 00049 public: 00050 void loadTips( const QString &tipFile ); 00051 void addTips( const QString &tipFile ); 00052 00053 QStringList tips; 00054 int currentTip; 00055 }; 00056 00057 void KTipDatabase::Private::loadTips( const QString &tipFile ) 00058 { 00059 tips.clear(); 00060 addTips( tipFile ); 00061 } 00062 00068 void KTipDatabase::Private::addTips( const QString &tipFile ) 00069 { 00070 QString fileName = KStandardDirs::locate( "data", tipFile ); 00071 00072 if ( fileName.isEmpty() ) { 00073 kDebug() << "KTipDatabase::addTips: can't find '" << tipFile << "' in standard dirs"; 00074 return; 00075 } 00076 00077 QFile file( fileName ); 00078 if ( !file.open( QIODevice::ReadOnly ) ) { 00079 kDebug() << "KTipDatabase::addTips: can't open '" << fileName << "' for reading"; 00080 return; 00081 } 00082 00083 QByteArray data = file.readAll(); 00084 QString content = QString::fromUtf8( data.constData(), data.size() ); 00085 const QRegExp rx( "\\n+" ); 00086 00087 int pos = -1; 00088 while ( ( pos = content.indexOf( "<html>", pos + 1, Qt::CaseInsensitive ) ) != -1 ) { 00093 QString tip = content 00094 .mid( pos + 6, content.indexOf( "</html>", pos, Qt::CaseInsensitive ) - pos - 6 ) 00095 .replace( rx, "\n" ); 00096 00097 if ( !tip.endsWith( '\n' ) ) 00098 tip += '\n'; 00099 00100 if ( tip.startsWith( '\n' ) ) 00101 tip = tip.mid( 1 ); 00102 00103 if ( tip.isEmpty() ) { 00104 kDebug() << "Empty tip found! Skipping! " << pos; 00105 continue; 00106 } 00107 00108 tips.append( tip ); 00109 } 00110 00111 file.close(); 00112 } 00113 00114 00115 KTipDatabase::KTipDatabase( const QString &_tipFile ) 00116 : d( new Private ) 00117 { 00118 QString tipFile = _tipFile; 00119 00120 if ( tipFile.isEmpty() ) 00121 tipFile = KGlobal::mainComponent().aboutData()->appName() + "/tips"; 00122 00123 d->loadTips( tipFile ); 00124 00125 if ( !d->tips.isEmpty() ) 00126 d->currentTip = KRandom::random() % d->tips.count(); 00127 } 00128 00129 KTipDatabase::KTipDatabase( const QStringList& tipsFiles ) 00130 : d( new Private ) 00131 { 00132 if ( tipsFiles.isEmpty() || ( ( tipsFiles.count() == 1 ) && tipsFiles.first().isEmpty() ) ) { 00133 d->addTips( KGlobal::mainComponent().aboutData()->appName() + "/tips" ); 00134 } else { 00135 for ( QStringList::ConstIterator it = tipsFiles.begin(); it != tipsFiles.end(); ++it ) 00136 d->addTips( *it ); 00137 } 00138 00139 if ( !d->tips.isEmpty() ) 00140 d->currentTip = KRandom::random() % d->tips.count(); 00141 } 00142 00143 KTipDatabase::~KTipDatabase() 00144 { 00145 delete d; 00146 } 00147 00148 void KTipDatabase::nextTip() 00149 { 00150 if ( d->tips.isEmpty() ) 00151 return; 00152 00153 d->currentTip += 1; 00154 00155 if ( d->currentTip >= (int) d->tips.count() ) 00156 d->currentTip = 0; 00157 } 00158 00159 void KTipDatabase::prevTip() 00160 { 00161 if ( d->tips.isEmpty() ) 00162 return; 00163 00164 d->currentTip -= 1; 00165 00166 if ( d->currentTip < 0 ) 00167 d->currentTip = d->tips.count() - 1; 00168 } 00169 00170 QString KTipDatabase::tip() const 00171 { 00172 if ( d->tips.isEmpty() ) 00173 return QString(); 00174 00175 return d->tips[ d->currentTip ]; 00176 } 00177 00178 00179 class KTipDialog::Private 00180 { 00181 public: 00182 Private( KTipDialog *_parent ) 00183 : parent( _parent ) 00184 { 00185 } 00186 ~Private() 00187 { 00188 delete database; 00189 } 00190 00191 void _k_nextTip(); 00192 void _k_prevTip(); 00193 void _k_showOnStart( bool ); 00194 00195 KTipDialog *parent; 00196 KTipDatabase *database; 00197 QCheckBox *tipOnStart; 00198 KTextBrowser *tipText; 00199 00200 static KTipDialog *mInstance; 00201 }; 00202 00203 KTipDialog *KTipDialog::Private::mInstance = 0; 00204 00205 void KTipDialog::Private::_k_prevTip() 00206 { 00207 database->prevTip(); 00208 tipText->setHtml( QString::fromLatin1( "<html><body>%1</body></html>" ) 00209 .arg( i18n( database->tip().toUtf8() ) ) ); 00210 } 00211 00212 void KTipDialog::Private::_k_nextTip() 00213 { 00214 database->nextTip(); 00215 tipText->setHtml( QString::fromLatin1( "<html><body>%1</body></html>" ) 00216 .arg( i18n( database->tip().toUtf8() ) ) ); 00217 } 00218 00219 void KTipDialog::Private::_k_showOnStart( bool on ) 00220 { 00221 parent->setShowOnStart( on ); 00222 } 00223 00224 00225 KTipDialog::KTipDialog( KTipDatabase *database, QWidget *parent ) 00226 : KDialog( parent ), 00227 d( new Private( this ) ) 00228 { 00229 setButtons( KDialog::None ); 00230 setCaption( i18n( "Tip of the Day" ) ); 00231 00236 bool isTipDialog = (parent != 0); 00237 00238 d->database = database; 00239 00240 setWindowIcon(KIcon("ktip")); 00241 00242 QWidget *widget = new QWidget( this ); 00243 setMainWidget( widget ); 00244 QVBoxLayout *mainLayout = new QVBoxLayout( widget ); 00245 mainLayout->setMargin( 0 ); 00246 00247 if ( isTipDialog ) { 00248 QLabel *titleLabel = new QLabel( this ); 00249 titleLabel->setText( i18n( "Did you know...?\n" ) ); 00250 titleLabel->setFont( QFont( KGlobalSettings::generalFont().family(), 20, QFont::Bold ) ); 00251 titleLabel->setAlignment( Qt::AlignCenter ); 00252 mainLayout->addWidget( titleLabel ); 00253 } 00254 00255 QHBoxLayout *browserLayout = new QHBoxLayout(); 00256 mainLayout->addLayout( browserLayout ); 00257 00258 d->tipText = new KTextBrowser( this ); 00259 00260 d->tipText->setOpenExternalLinks( true ); 00261 00262 d->tipText->setWordWrapMode( QTextOption::WrapAtWordBoundaryOrAnywhere ); 00263 00264 QStringList paths; 00265 paths << KGlobal::dirs()->resourceDirs( "icon" ) 00266 << KGlobal::dirs()->findResourceDir( "data", "kdewizard/pics" ) + "kdewizard/pics/"; 00267 00268 d->tipText->setSearchPaths( paths ); 00269 00270 d->tipText->setFrameStyle( QFrame::NoFrame ); 00271 d->tipText->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); 00272 QPalette tipPal(d->tipText->palette()); 00273 tipPal.setColor(QPalette::Base, Qt::transparent); 00274 tipPal.setColor(QPalette::Text, tipPal.color(QPalette::WindowText)); 00275 d->tipText->setPalette(tipPal); 00276 00277 browserLayout->addWidget( d->tipText ); 00278 00279 QLabel *label = new QLabel( this ); 00280 label->setPixmap( KStandardDirs::locate( "data", "kdeui/pics/ktip-bulb.png" ) ); 00281 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter ); 00282 browserLayout->addWidget( label ); 00283 00284 if ( !isTipDialog ) { 00285 resize( 520, 280 ); 00286 QSize sh = size(); 00287 00288 QRect rect = KGlobalSettings::splashScreenDesktopGeometry(); 00289 00290 move( rect.x() + ( rect.width() - sh.width() ) / 2, 00291 rect.y() + ( rect.height() - sh.height() ) / 2 ); 00292 } 00293 00294 KSeparator* sep = new KSeparator( Qt::Horizontal ); 00295 mainLayout->addWidget( sep ); 00296 00297 QHBoxLayout *buttonLayout = new QHBoxLayout(); 00298 00299 mainLayout->addLayout( buttonLayout ); 00300 00301 d->tipOnStart = new QCheckBox( i18n( "&Show tips on startup" ) ); 00302 buttonLayout->addWidget( d->tipOnStart, 1 ); 00303 00304 KPushButton *prev = new KPushButton( KStandardGuiItem::back( KStandardGuiItem::UseRTL ) ); 00305 prev->setText( i18n( "&Previous" ) ); 00306 buttonLayout->addWidget( prev ); 00307 00308 KPushButton *next = new KPushButton( KStandardGuiItem::forward( KStandardGuiItem::UseRTL )); 00309 next->setText( i18nc( "Opposite to Previous", "&Next" ) ); 00310 buttonLayout->addWidget( next ); 00311 00312 KPushButton *ok = new KPushButton( KStandardGuiItem::close()); 00313 ok->setDefault( true ); 00314 buttonLayout->addWidget( ok ); 00315 00316 KConfigGroup config( KGlobal::config(), "TipOfDay" ); 00317 d->tipOnStart->setChecked( config.readEntry( "RunOnStart", true ) ); 00318 00319 connect( next, SIGNAL( clicked() ), this, SLOT( _k_nextTip() ) ); 00320 connect( prev, SIGNAL( clicked() ), this, SLOT( _k_prevTip() ) ); 00321 connect( ok, SIGNAL( clicked() ), this, SLOT( accept() ) ); 00322 connect( d->tipOnStart, SIGNAL( toggled( bool ) ), this, SLOT( _k_showOnStart( bool ) ) ); 00323 00324 ok->setFocus(); 00325 00326 d->_k_nextTip(); 00327 } 00328 00329 KTipDialog::~KTipDialog() 00330 { 00331 if ( Private::mInstance == this ) 00332 Private::mInstance = 0L; 00333 delete d; 00334 } 00335 00340 void KTipDialog::showTip( const QString &tipFile, bool force ) 00341 { 00342 showTip( 0, tipFile, force ); 00343 } 00344 00345 void KTipDialog::showTip( QWidget *parent, const QString &tipFile, bool force ) 00346 { 00347 showMultiTip( parent, QStringList( tipFile ), force ); 00348 } 00349 00350 void KTipDialog::showMultiTip( QWidget *parent, const QStringList &tipFiles, bool force ) 00351 { 00352 KConfigGroup configGroup( KGlobal::config(), "TipOfDay" ); 00353 00354 const bool runOnStart = configGroup.readEntry( "RunOnStart", true ); 00355 00356 if ( !force ) { 00357 if ( !runOnStart ) 00358 return; 00359 00360 // showing the tooltips on startup suggests the tooltip 00361 // will be shown *each time* on startup, not $random days later 00362 // TODO either remove or uncomment this code, but make the situation clear 00363 /*bool hasLastShown = configGroup.hasKey( "TipLastShown" ); 00364 if ( hasLastShown ) { 00365 const int oneDay = 24 * 60 * 60; 00366 QDateTime lastShown = configGroup.readEntry( "TipLastShown", QDateTime() ); 00367 00368 // Show tip roughly once a week 00369 if ( lastShown.secsTo( QDateTime::currentDateTime() ) < (oneDay + (KRandom::random() % (10 * oneDay))) ) 00370 return; 00371 } 00372 00373 configGroup.writeEntry( "TipLastShown", QDateTime::currentDateTime() ); 00374 00375 if ( !hasLastShown ) 00376 return; // Don't show tip on first start*/ 00377 } 00378 00379 if ( !Private::mInstance ) 00380 Private::mInstance = new KTipDialog( new KTipDatabase( tipFiles ), parent ); 00381 else 00382 // The application might have changed the RunOnStart option in its own 00383 // configuration dialog, so we should update the checkbox. 00384 Private::mInstance->d->tipOnStart->setChecked( runOnStart ); 00385 00386 Private::mInstance->show(); 00387 Private::mInstance->raise(); 00388 } 00389 00390 void KTipDialog::setShowOnStart( bool on ) 00391 { 00392 KConfigGroup config( KGlobal::config(), "TipOfDay" ); 00393 config.writeEntry( "RunOnStart", on ); 00394 } 00395 00396 bool KTipDialog::eventFilter( QObject *object, QEvent *event ) 00397 { 00398 if ( object == d->tipText && event->type() == QEvent::KeyPress && 00399 (((QKeyEvent *)event)->key() == Qt::Key_Return || 00400 ((QKeyEvent *)event)->key() == Qt::Key_Space )) 00401 accept(); 00402 00411 return QWidget::eventFilter( object, event ); 00412 } 00413 00414 00415 #include "ktip.moc"
KDE 4.6 API Reference