ktip.cpp
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 00007 Permission is hereby granted, free of charge, to any person obtaining a copy 00008 of this software and associated documentation files (the "Software"), to deal 00009 in the Software without restriction, including without limitation the rights 00010 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 00011 copies of the Software, and to permit persons to whom the Software is 00012 furnished to do so, subject to the following conditions: 00013 00014 The above copyright notice and this permission notice shall be included in 00015 all copies or substantial portions of the Software. 00016 00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00020 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 00021 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 00022 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00023 00024 ******************************************************************/ 00025 00026 #include <qcheckbox.h> 00027 #include <qfile.h> 00028 #include <qhbox.h> 00029 #include <qlabel.h> 00030 #include <qlayout.h> 00031 #include <qpushbutton.h> 00032 #include <qregexp.h> 00033 #include <qtextstream.h> 00034 #include <qimage.h> 00035 00036 #include <kaboutdata.h> 00037 #include <kapplication.h> 00038 #include <kconfig.h> 00039 #include <kdebug.h> 00040 #include <kglobal.h> 00041 #include <kiconloader.h> 00042 #include <klocale.h> 00043 #include <kpushbutton.h> 00044 #include <kseparator.h> 00045 #include <kstandarddirs.h> 00046 #include <kstdguiitem.h> 00047 #include <ktextbrowser.h> 00048 #include <kiconeffect.h> 00049 #include <kglobalsettings.h> 00050 00051 #ifdef Q_WS_X11 00052 #include <kwin.h> 00053 #endif 00054 00055 #include "ktip.h" 00056 00057 00058 KTipDatabase::KTipDatabase(const QString &_tipFile) 00059 { 00060 QString tipFile = _tipFile; 00061 if (tipFile.isEmpty()) 00062 tipFile = QString::fromLatin1(KGlobal::instance()->aboutData()->appName()) + "/tips"; 00063 00064 loadTips(tipFile); 00065 00066 if (!mTips.isEmpty()) 00067 mCurrent = kapp->random() % mTips.count(); 00068 } 00069 00070 00071 KTipDatabase::KTipDatabase( const QStringList& tipsFiles ) 00072 { 00073 if ( tipsFiles.isEmpty() || ( ( tipsFiles.count() == 1 ) && tipsFiles.first().isEmpty() ) ) 00074 { 00075 addTips(QString::fromLatin1(KGlobal::instance()->aboutData()->appName()) + "/tips"); 00076 } 00077 else 00078 { 00079 for (QStringList::ConstIterator it = tipsFiles.begin(); it != tipsFiles.end(); ++it) 00080 addTips( *it ); 00081 } 00082 if (!mTips.isEmpty()) 00083 mCurrent = kapp->random() % mTips.count(); 00084 00085 } 00086 00087 void KTipDatabase::loadTips(const QString &tipFile) 00088 { 00089 mTips.clear(); 00090 addTips(tipFile); 00091 } 00092 00093 // if you change something here, please update the script 00094 // preparetips, which depends on extracting exactly the same 00095 // text as done here. 00096 void KTipDatabase::addTips(const QString& tipFile ) 00097 { 00098 QString fileName = locate("data", tipFile); 00099 00100 if (fileName.isEmpty()) 00101 { 00102 kdDebug() << "KTipDatabase::addTips: can't find '" << tipFile << "' in standard dirs" << endl; 00103 return; 00104 } 00105 00106 QFile file(fileName); 00107 if (!file.open(IO_ReadOnly)) 00108 { 00109 kdDebug() << "KTipDatabase::addTips: can't open '" << fileName << "' for reading" << endl; 00110 return; 00111 } 00112 00113 QByteArray data = file.readAll(); 00114 QString content = QString::fromUtf8(data.data(), data.size()); 00115 const QRegExp rx("\\n+"); 00116 00117 int pos = -1; 00118 while ((pos = content.find("<html>", pos + 1, false)) != -1) 00119 { 00120 // to make translations work, tip extraction here must exactly 00121 // match what is done by the preparetips script 00122 QString tip = content 00123 .mid(pos + 6, content.find("</html>", pos, false) - pos - 6) 00124 .replace(rx, "\n"); 00125 if (!tip.endsWith("\n")) 00126 tip += "\n"; 00127 if (tip.startsWith("\n")) 00128 tip = tip.mid(1); 00129 if (tip.isEmpty()) 00130 { 00131 kdDebug() << "Empty tip found! Skipping! " << pos << endl; 00132 continue; 00133 } 00134 mTips.append(tip); 00135 } 00136 00137 file.close(); 00138 00139 } 00140 00141 void KTipDatabase::nextTip() 00142 { 00143 if (mTips.isEmpty()) 00144 return ; 00145 mCurrent += 1; 00146 if (mCurrent >= (int) mTips.count()) 00147 mCurrent = 0; 00148 } 00149 00150 00151 void KTipDatabase::prevTip() 00152 { 00153 if (mTips.isEmpty()) 00154 return ; 00155 mCurrent -= 1; 00156 if (mCurrent < 0) 00157 mCurrent = mTips.count() - 1; 00158 } 00159 00160 00161 QString KTipDatabase::tip() const 00162 { 00163 if (mTips.isEmpty()) 00164 return QString::null; 00165 return mTips[mCurrent]; 00166 } 00167 00168 KTipDialog *KTipDialog::mInstance = 0; 00169 00170 00171 KTipDialog::KTipDialog(KTipDatabase *db, QWidget *parent, const char *name) 00172 : KDialog(parent, name) 00173 { 00178 bool isTipDialog = (parent); 00179 00180 QImage img; 00181 int h,s,v; 00182 00183 mBlendedColor = KGlobalSettings::activeTitleColor(); 00184 mBlendedColor.hsv(&h,&s,&v); 00185 mBlendedColor.setHsv(h, int(s*(71/76.0)), int(v*(67/93.0))); 00186 00187 if (!isTipDialog) 00188 { 00189 img = QImage(locate("data", "kdewizard/pics/wizard_small.png")); 00190 // colorize and check to figure the correct color 00191 KIconEffect::colorize(img, mBlendedColor, 1.0); 00192 QRgb colPixel( img.pixel(0,0) ); 00193 00194 mBlendedColor = QColor(qRed(colPixel),qGreen(colPixel),qBlue(colPixel)); 00195 } 00196 00197 mBaseColor = KGlobalSettings::alternateBackgroundColor(); 00198 mBaseColor.hsv(&h,&s,&v); 00199 mBaseColor.setHsv(h, int(s*(10/6.0)), int(v*(93/99.0))); 00200 00201 mTextColor = KGlobalSettings::textColor(); 00202 00203 00204 mDatabase = db; 00205 00206 setCaption(i18n("Tip of the Day")); 00207 #ifdef Q_WS_X11 00208 KWin::setIcons( winId(), 00209 KGlobal::iconLoader()->loadIcon( "idea", KIcon::NoGroup, 32 ), 00210 KGlobal::iconLoader()->loadIcon( "idea", KIcon::NoGroup, 16 ) ); 00211 #endif 00212 QVBoxLayout *vbox = new QVBoxLayout(this, marginHint(), spacingHint()); 00213 00214 if (isTipDialog) 00215 { 00216 QHBoxLayout *pl = new QHBoxLayout(vbox, 0, 0); 00217 00218 QLabel *bulb = new QLabel(this); 00219 bulb->setPixmap(locate("data", "kdeui/pics/ktip-bulb.png")); 00220 pl->addWidget(bulb); 00221 00222 QLabel *titlePane = new QLabel(this); 00223 titlePane->setBackgroundPixmap(locate("data", "kdeui/pics/ktip-background.png")); 00224 titlePane->setText(i18n("Did you know...?\n")); 00225 titlePane->setFont(QFont(KGlobalSettings::generalFont().family(), 20, QFont::Bold)); 00226 titlePane->setAlignment(QLabel::AlignCenter); 00227 pl->addWidget(titlePane, 100); 00228 } 00229 00230 QHBox *hbox = new QHBox(this); 00231 hbox->setSpacing(0); 00232 hbox->setFrameStyle(QFrame::Panel | QFrame::Sunken); 00233 vbox->addWidget(hbox); 00234 00235 QHBox *tl = new QHBox(hbox); 00236 tl->setMargin(7); 00237 tl->setBackgroundColor(mBlendedColor); 00238 00239 QHBox *topLeft = new QHBox(tl); 00240 topLeft->setMargin(15); 00241 topLeft->setBackgroundColor(mBaseColor); 00242 00243 mTipText = new KTextBrowser(topLeft); 00244 00245 mTipText->setWrapPolicy( QTextEdit::AtWordOrDocumentBoundary ); 00246 mTipText->mimeSourceFactory()->addFilePath( 00247 KGlobal::dirs()->findResourceDir("data", "kdewizard/pics")+"kdewizard/pics/"); 00248 mTipText->setFrameStyle(QFrame::NoFrame | QFrame::Plain); 00249 mTipText->setHScrollBarMode(QScrollView::AlwaysOff); 00250 mTipText->setLinkUnderline(false); 00251 00252 QStyleSheet *sheet = mTipText->styleSheet(); 00253 QStyleSheetItem *item = sheet->item("a"); 00254 item->setFontWeight(QFont::Bold); 00255 mTipText->setStyleSheet(sheet); 00256 QPalette pal = mTipText->palette(); 00257 pal.setColor( QPalette::Active, QColorGroup::Link, mBlendedColor ); 00258 pal.setColor( QPalette::Inactive, QColorGroup::Link, mBlendedColor ); 00259 mTipText->setPalette(pal); 00260 00261 QStringList icons = KGlobal::dirs()->resourceDirs("icon"); 00262 QStringList::Iterator it; 00263 for (it = icons.begin(); it != icons.end(); ++it) 00264 mTipText->mimeSourceFactory()->addFilePath(*it); 00265 00266 if (!isTipDialog) 00267 { 00268 QLabel *l = new QLabel(hbox); 00269 l->setPixmap(img); 00270 l->setBackgroundColor(mBlendedColor); 00271 l->setAlignment(Qt::AlignRight | Qt::AlignBottom); 00272 00273 resize(550, 230); 00274 QSize sh = size(); 00275 00276 QRect rect = KGlobalSettings::splashScreenDesktopGeometry(); 00277 00278 move(rect.x() + (rect.width() - sh.width())/2, 00279 rect.y() + (rect.height() - sh.height())/2); 00280 } 00281 00282 KSeparator* sep = new KSeparator( KSeparator::HLine, this); 00283 vbox->addWidget(sep); 00284 00285 QHBoxLayout *hbox2 = new QHBoxLayout(vbox, 4); 00286 00287 mTipOnStart = new QCheckBox(i18n("&Show tips on startup"), this); 00288 hbox2->addWidget(mTipOnStart, 1); 00289 00290 KPushButton *prev = new KPushButton( KStdGuiItem::back( 00291 KStdGuiItem::UseRTL ), this ); 00292 prev->setText( i18n("&Previous") ); 00293 hbox2->addWidget(prev); 00294 00295 KPushButton *next = new KPushButton( KStdGuiItem::forward( 00296 KStdGuiItem::UseRTL ), this ); 00297 next->setText( i18n("Opposite to Previous","&Next") ); 00298 hbox2->addWidget(next); 00299 00300 KPushButton *ok = new KPushButton(KStdGuiItem::close(), this); 00301 ok->setDefault(true); 00302 hbox2->addWidget(ok); 00303 00304 KConfigGroup config(kapp->config(), "TipOfDay"); 00305 mTipOnStart->setChecked(config.readBoolEntry("RunOnStart", true)); 00306 00307 connect(next, SIGNAL(clicked()), this, SLOT(nextTip())); 00308 connect(prev, SIGNAL(clicked()), this, SLOT(prevTip())); 00309 connect(ok, SIGNAL(clicked()), this, SLOT(accept())); 00310 connect(mTipOnStart, SIGNAL(toggled(bool)), this, SLOT(showOnStart(bool))); 00311 00312 ok->setFocus(); 00313 00314 nextTip(); 00315 } 00316 00317 KTipDialog::~KTipDialog() 00318 { 00319 if( mInstance==this ) 00320 mInstance = 0L; 00321 } 00322 00323 void KTipDialog::showTip(const QString &tipFile, bool force) 00324 { 00325 showTip(kapp->mainWidget(), tipFile, force); 00326 } 00327 00328 void KTipDialog::showTip(QWidget *parent, const QString &tipFile, bool force) 00329 { 00330 showMultiTip( parent, QStringList(tipFile), force ); 00331 } 00332 00333 void KTipDialog::showMultiTip(QWidget *parent, const QStringList &tipFiles, bool force) 00334 { 00335 KConfigGroup configGroup(kapp->config(), "TipOfDay"); 00336 00337 const bool runOnStart = configGroup.readBoolEntry("RunOnStart", true); 00338 00339 if (!force) 00340 { 00341 if (!runOnStart) 00342 return; 00343 00344 bool hasLastShown = configGroup.hasKey("TipLastShown"); 00345 if (hasLastShown) 00346 { 00347 const int oneDay = 24*60*60; 00348 QDateTime lastShown = configGroup.readDateTimeEntry("TipLastShown"); 00349 // Show tip roughly once a week 00350 if (lastShown.secsTo(QDateTime::currentDateTime()) < (oneDay + (kapp->random() % (10*oneDay)))) 00351 return; 00352 } 00353 configGroup.writeEntry("TipLastShown", QDateTime::currentDateTime()); 00354 kapp->config()->sync(); 00355 if (!hasLastShown) 00356 return; // Don't show tip on first start 00357 } 00358 00359 if (!mInstance) 00360 mInstance = new KTipDialog(new KTipDatabase(tipFiles), parent); 00361 else 00362 // The application might have changed the RunOnStart option in its own 00363 // configuration dialog, so we should update the checkbox. 00364 mInstance->mTipOnStart->setChecked(runOnStart); 00365 00366 mInstance->show(); 00367 mInstance->raise(); 00368 } 00369 00370 void KTipDialog::prevTip() 00371 { 00372 mDatabase->prevTip(); 00373 mTipText->setText(QString::fromLatin1( 00374 "<qt text=\"%1\" bgcolor=\"%2\">%3</qt>") 00375 .arg(mTextColor.name()) 00376 .arg(mBaseColor.name()) 00377 .arg(i18n(mDatabase->tip().utf8()))); 00378 mTipText->setContentsPos(0, 0); 00379 } 00380 00381 void KTipDialog::nextTip() 00382 { 00383 mDatabase->nextTip(); 00384 mTipText->setText(QString::fromLatin1("<qt text=\"%1\" bgcolor=\"%2\">%3</qt>") 00385 .arg(mTextColor.name()) 00386 .arg(mBaseColor.name()) 00387 .arg(i18n(mDatabase->tip().utf8()))); 00388 mTipText->setContentsPos(0, 0); 00389 } 00390 00391 void KTipDialog::showOnStart(bool on) 00392 { 00393 setShowOnStart(on); 00394 } 00395 00396 void KTipDialog::setShowOnStart(bool on) 00397 { 00398 KConfigGroup config(kapp->config(), "TipOfDay"); 00399 config.writeEntry("RunOnStart", on); 00400 config.sync(); 00401 } 00402 00403 bool KTipDialog::eventFilter(QObject *o, QEvent *e) 00404 { 00405 if (o == mTipText && e->type()== QEvent::KeyPress && 00406 (((QKeyEvent *)e)->key() == Key_Return || 00407 ((QKeyEvent *)e)->key() == Key_Space )) 00408 accept(); 00409 00410 // If the user presses Return or Space, we close the dialog as if the 00411 // default button was pressed even if the KTextBrowser has the keyboard 00412 // focus. This could have the bad side-effect that the user cannot use the 00413 // keyboard to open urls in the KTextBrowser, so we just let it handle 00414 // the key event _additionally_. (Antonio) 00415 00416 return QWidget::eventFilter( o, e ); 00417 } 00418 00419 void KTipDialog::virtual_hook( int id, void* data ) 00420 { 00421 KDialog::virtual_hook( id, data ); 00422 } 00423 00424 #include "ktip.moc"