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 <tqcheckbox.h> 00027 #include <tqfile.h> 00028 #include <tqhbox.h> 00029 #include <tqlabel.h> 00030 #include <tqlayout.h> 00031 #include <tqpushbutton.h> 00032 #include <tqregexp.h> 00033 #include <tqtextstream.h> 00034 #include <tqimage.h> 00035 00036 #include <tdeaboutdata.h> 00037 #include <tdeapplication.h> 00038 #include <tdeconfig.h> 00039 #include <kdebug.h> 00040 #include <tdeglobal.h> 00041 #include <kiconloader.h> 00042 #include <tdelocale.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 <tdeglobalsettings.h> 00050 00051 #ifdef Q_WS_X11 00052 #include <twin.h> 00053 #endif 00054 00055 #include "ktip.h" 00056 00057 00058 KTipDatabase::KTipDatabase(const TQString &_tipFile) 00059 { 00060 TQString tipFile = _tipFile; 00061 if (tipFile.isEmpty()) 00062 tipFile = TQString::fromLatin1(TDEGlobal::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 TQStringList& tipsFiles ) 00072 { 00073 if ( tipsFiles.isEmpty() || ( ( tipsFiles.count() == 1 ) && tipsFiles.first().isEmpty() ) ) 00074 { 00075 addTips(TQString::fromLatin1(TDEGlobal::instance()->aboutData()->appName()) + "/tips"); 00076 } 00077 else 00078 { 00079 for (TQStringList::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 TQString &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 TQString& tipFile ) 00097 { 00098 TQString 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 TQFile 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 TQByteArray data = file.readAll(); 00114 TQString content = TQString::fromUtf8(data.data(), data.size()); 00115 const TQRegExp 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 TQString 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 TQString KTipDatabase::tip() const 00162 { 00163 if (mTips.isEmpty()) 00164 return TQString::null; 00165 return mTips[mCurrent]; 00166 } 00167 00168 KTipDialog *KTipDialog::mInstance = 0; 00169 00170 00171 KTipDialog::KTipDialog(KTipDatabase *db, TQWidget *parent, const char *name) 00172 : KDialog(parent, name) 00173 { 00178 bool isTipDialog = (parent); 00179 00180 TQImage img; 00181 int h,s,v; 00182 00183 mBlendedColor = TDEGlobalSettings::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 = TQImage(locate("data", "tdewizard/pics/wizard_small.png")); 00190 // colorize and check to figure the correct color 00191 TDEIconEffect::colorize(img, mBlendedColor, 1.0); 00192 QRgb colPixel( img.pixel(0,0) ); 00193 00194 mBlendedColor = TQColor(tqRed(colPixel),tqGreen(colPixel),tqBlue(colPixel)); 00195 } 00196 00197 mBaseColor = TDEGlobalSettings::alternateBackgroundColor(); 00198 mBaseColor.hsv(&h,&s,&v); 00199 mBaseColor.setHsv(h, int(s*(10/6.0)), int(v*(93/99.0))); 00200 00201 mTextColor = TDEGlobalSettings::textColor(); 00202 00203 00204 mDatabase = db; 00205 00206 setCaption(i18n("Tip of the Day")); 00207 #ifdef Q_WS_X11 00208 KWin::setIcons( winId(), 00209 TDEGlobal::iconLoader()->loadIcon( "idea", TDEIcon::NoGroup, 32 ), 00210 TDEGlobal::iconLoader()->loadIcon( "idea", TDEIcon::NoGroup, 16 ) ); 00211 #endif 00212 TQVBoxLayout *vbox = new TQVBoxLayout(this, marginHint(), spacingHint()); 00213 00214 if (isTipDialog) 00215 { 00216 TQHBoxLayout *pl = new TQHBoxLayout(vbox, 0, 0); 00217 00218 TQLabel *bulb = new TQLabel(this); 00219 bulb->setPixmap(locate("data", "tdeui/pics/ktip-bulb.png")); 00220 pl->addWidget(bulb); 00221 00222 TQLabel *titlePane = new TQLabel(this); 00223 titlePane->setBackgroundPixmap(locate("data", "tdeui/pics/ktip-background.png")); 00224 titlePane->setText(i18n("Did you know...?\n")); 00225 titlePane->setFont(TQFont(TDEGlobalSettings::generalFont().family(), 20, TQFont::Bold)); 00226 titlePane->setAlignment(TQLabel::AlignCenter); 00227 pl->addWidget(titlePane, 100); 00228 } 00229 00230 TQHBox *hbox = new TQHBox(this); 00231 hbox->setSpacing(0); 00232 hbox->setFrameStyle(TQFrame::Panel | TQFrame::Sunken); 00233 vbox->addWidget(hbox); 00234 00235 TQHBox *tl = new TQHBox(hbox); 00236 tl->setMargin(7); 00237 tl->setBackgroundColor(mBlendedColor); 00238 00239 TQHBox *topLeft = new TQHBox(tl); 00240 topLeft->setMargin(15); 00241 topLeft->setBackgroundColor(mBaseColor); 00242 00243 mTipText = new KTextBrowser(topLeft); 00244 00245 mTipText->setWrapPolicy( TQTextEdit::AtWordOrDocumentBoundary ); 00246 mTipText->mimeSourceFactory()->addFilePath( 00247 TDEGlobal::dirs()->findResourceDir("data", "tdewizard/pics")+"tdewizard/pics/"); 00248 mTipText->setFrameStyle(TQFrame::NoFrame | TQFrame::Plain); 00249 mTipText->setHScrollBarMode(TQScrollView::AlwaysOff); 00250 mTipText->setLinkUnderline(false); 00251 00252 TQStyleSheet *sheet = mTipText->styleSheet(); 00253 TQStyleSheetItem *item = sheet->item("a"); 00254 item->setFontWeight(TQFont::Bold); 00255 mTipText->setStyleSheet(sheet); 00256 TQPalette pal = mTipText->palette(); 00257 pal.setColor( TQPalette::Active, TQColorGroup::Link, mBlendedColor ); 00258 pal.setColor( TQPalette::Inactive, TQColorGroup::Link, mBlendedColor ); 00259 mTipText->setPalette(pal); 00260 00261 TQStringList icons = TDEGlobal::dirs()->resourceDirs("icon"); 00262 TQStringList::Iterator it; 00263 for (it = icons.begin(); it != icons.end(); ++it) 00264 mTipText->mimeSourceFactory()->addFilePath(*it); 00265 00266 if (!isTipDialog) 00267 { 00268 TQLabel *l = new TQLabel(hbox); 00269 l->setPixmap(img); 00270 l->setBackgroundColor(mBlendedColor); 00271 l->setAlignment(Qt::AlignRight | Qt::AlignBottom); 00272 00273 resize(550, 230); 00274 TQSize sh = size(); 00275 00276 TQRect rect = TDEGlobalSettings::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 TQHBoxLayout *hbox2 = new TQHBoxLayout(vbox, 4); 00286 00287 mTipOnStart = new TQCheckBox(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 TDEConfigGroup config(kapp->config(), "TipOfDay"); 00305 mTipOnStart->setChecked(config.readBoolEntry("RunOnStart", true)); 00306 00307 connect(next, TQT_SIGNAL(clicked()), this, TQT_SLOT(nextTip())); 00308 connect(prev, TQT_SIGNAL(clicked()), this, TQT_SLOT(prevTip())); 00309 connect(ok, TQT_SIGNAL(clicked()), this, TQT_SLOT(accept())); 00310 connect(mTipOnStart, TQT_SIGNAL(toggled(bool)), this, TQT_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 TQString &tipFile, bool force) 00324 { 00325 showTip(kapp->mainWidget(), tipFile, force); 00326 } 00327 00328 void KTipDialog::showTip(TQWidget *parent, const TQString &tipFile, bool force) 00329 { 00330 showMultiTip( parent, TQStringList(tipFile), force ); 00331 } 00332 00333 void KTipDialog::showMultiTip(TQWidget *parent, const TQStringList &tipFiles, bool force) 00334 { 00335 TDEConfigGroup 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 TQDateTime lastShown = configGroup.readDateTimeEntry("TipLastShown"); 00349 // Show tip roughly once a week 00350 if (lastShown.secsTo(TQDateTime::currentDateTime()) < (oneDay + (kapp->random() % (10*oneDay)))) 00351 return; 00352 } 00353 configGroup.writeEntry("TipLastShown", TQDateTime::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 static TQString fixTip(TQString tip) 00371 { 00372 TQRegExp iconRegExp("<img src=\"(.*)\">"); 00373 iconRegExp.setMinimal(true); 00374 if (iconRegExp.search(tip)>-1) { 00375 TQString iconName = iconRegExp.cap(1); 00376 if (!iconName.isEmpty()) 00377 if (TDEGlobal::dirs()->findResource("icon", iconName).isEmpty()) 00378 tip.replace("crystalsvg","hicolor"); 00379 } 00380 00381 return tip; 00382 } 00383 00384 void KTipDialog::prevTip() 00385 { 00386 mDatabase->prevTip(); 00387 TQString currentTip = TQString::fromLatin1( 00388 "<qt text=\"%1\" bgcolor=\"%2\">%3</qt>") 00389 .arg(mTextColor.name()) 00390 .arg(mBaseColor.name()) 00391 .arg(i18n(mDatabase->tip().utf8())); 00392 00393 00394 currentTip = fixTip(currentTip); 00395 mTipText->setText(currentTip); 00396 mTipText->setContentsPos(0, 0); 00397 } 00398 00399 void KTipDialog::nextTip() 00400 { 00401 mDatabase->nextTip(); 00402 TQString currentTip = TQString::fromLatin1( 00403 "<qt text=\"%1\" bgcolor=\"%2\">%3</qt>") 00404 .arg(mTextColor.name()) 00405 .arg(mBaseColor.name()) 00406 .arg(i18n(mDatabase->tip().utf8())); 00407 00408 00409 currentTip = fixTip(currentTip); 00410 mTipText->setText(currentTip); 00411 mTipText->setContentsPos(0, 0); 00412 } 00413 00414 void KTipDialog::showOnStart(bool on) 00415 { 00416 setShowOnStart(on); 00417 } 00418 00419 void KTipDialog::setShowOnStart(bool on) 00420 { 00421 TDEConfigGroup config(kapp->config(), "TipOfDay"); 00422 config.writeEntry("RunOnStart", on); 00423 config.sync(); 00424 } 00425 00426 bool KTipDialog::eventFilter(TQObject *o, TQEvent *e) 00427 { 00428 if (TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(mTipText) && e->type()== TQEvent::KeyPress && 00429 (((TQKeyEvent *)e)->key() == Key_Return || 00430 ((TQKeyEvent *)e)->key() == Key_Space )) 00431 accept(); 00432 00433 // If the user presses Return or Space, we close the dialog as if the 00434 // default button was pressed even if the KTextBrowser has the keyboard 00435 // focus. This could have the bad side-effect that the user cannot use the 00436 // keyboard to open urls in the KTextBrowser, so we just let it handle 00437 // the key event _additionally_. (Antonio) 00438 00439 return TQWidget::eventFilter( o, e ); 00440 } 00441 00442 void KTipDialog::virtual_hook( int id, void* data ) 00443 { 00444 KDialog::virtual_hook( id, data ); 00445 } 00446 00447 #include "ktip.moc"