libyui-qt
2.43.5
|
00001 /* 00002 Copyright (C) 2000-2012 Novell, Inc 00003 This library is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU Lesser General Public License as 00005 published by the Free Software Foundation; either version 2.1 of the 00006 License, or (at your option) version 3.0 of the License. This library 00007 is distributed in the hope that it will be useful, but WITHOUT ANY 00008 WARRANTY; without even the implied warranty of MERCHANTABILITY or 00009 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 00010 License for more details. You should have received a copy of the GNU 00011 Lesser General Public License along with this library; if not, write 00012 to the Free Software Foundation, Inc., 51 Franklin Street, Fifth 00013 Floor, Boston, MA 02110-1301 USA 00014 */ 00015 00016 00017 /*-/ 00018 00019 File: QY2Styler.cc 00020 00021 Author: Stefan Kulow <coolo@suse.de> 00022 00023 /-*/ 00024 00025 00026 #define YUILogComponent "qt-styler" 00027 #include <yui/YUILog.h> 00028 #include <yui/YUIException.h> 00029 #include <yui/Libyui_config.h> 00030 #include <YSettings.h> 00031 00032 #include "QY2Styler.h" 00033 #include <QDebug> 00034 #include <QFile> 00035 #include <QString> 00036 #include <QStringList> 00037 #include <QApplication> 00038 #include <QWidget> 00039 #include <QPainter> 00040 #include <QSvgRenderer> 00041 #include <iostream> 00042 #include <QPixmapCache> 00043 00044 #define LOGGING_CAUSES_QT4_THREADING_PROBLEMS 1 00045 00046 std::ostream & operator<<( std::ostream & stream, const QString & str ); 00047 std::ostream & operator<<( std::ostream & stream, const QStringList & strList ); 00048 std::ostream & operator<<( std::ostream & stream, const QWidget * widget ); 00049 00050 using namespace std; 00051 00052 00053 QY2Styler::QY2Styler( QObject * parent ) 00054 : QObject( parent ) 00055 { 00056 QPixmapCache::setCacheLimit( 5 * 1024 ); 00057 yuiDebug() << "Styler created" << std::endl; 00058 } 00059 00060 00061 QY2Styler * 00062 QY2Styler::styler() 00063 { 00064 static QY2Styler * styler = 0; 00065 00066 if ( ! styler ) 00067 { 00068 yuiDebug() << "Creating QY2Styler singleton" << std::endl; 00069 00070 styler = new QY2Styler( qApp ); 00071 YUI_CHECK_NEW( styler ); 00072 00073 QString style = getenv("Y2STYLE"); 00074 00075 if ( ! style.isEmpty() ) 00076 styler->loadStyleSheet( style ); 00077 else 00078 styler->loadStyleSheet( "style.qss" ); 00079 } 00080 00081 return styler; 00082 } 00083 00084 00085 void QY2Styler::loadStyleSheet( const QString & filename ) 00086 { 00087 QFile file( themeDir() + filename ); 00088 00089 if ( file.open( QIODevice::ReadOnly ) ) 00090 { 00091 yuiMilestone() << "Using style sheet \"" << file.fileName() << "\"" << std::endl; 00092 QString text = file.readAll(); 00093 setStyleSheet( text ); 00094 } 00095 else 00096 { 00097 yuiMilestone() << "Couldn't open style sheet \"" << file.fileName() << "\"" << std::endl; 00098 } 00099 00100 } 00101 00102 void QY2Styler::setStyleSheet( const QString & text ) 00103 { 00104 _style = text; 00105 processUrls( _style ); 00106 00107 QWidget *child; 00108 QList< QWidget* > childlist; 00109 00110 foreach( childlist, _children ) 00111 foreach( child, childlist ) 00112 child->setStyleSheet( _style ); 00113 } 00114 00115 00116 void QY2Styler::processUrls( QString & text ) 00117 { 00118 QString result; 00119 QStringList lines = text.split( '\n' ); 00120 QRegExp urlRegex( ": *url\\((.*)\\)" ); 00121 QRegExp backgroundRegex( "^ */\\* *Background: *([^ ]*) *([^ ]*) *\\*/$" ); 00122 QRegExp richTextRegex( "^ */\\* *Richtext: *([^ ]*) *\\*/$" ); 00123 00124 _backgrounds.clear(); 00125 00126 for ( QStringList::const_iterator it = lines.begin(); it != lines.end(); ++it ) 00127 { 00128 QString line = *it; 00129 00130 // Replace file name inside url() with full path (from themeDir() ) 00131 00132 if ( urlRegex.indexIn( line ) >= 0 ) 00133 { 00134 QString fileName = urlRegex.cap( 1 ); 00135 QString fullPath = themeDir() + fileName; 00136 yuiDebug() << "Expanding " << fileName << "\tto " << fullPath << std::endl; 00137 line.replace( urlRegex, ": url(" + fullPath + ")"); 00138 } 00139 00140 if ( backgroundRegex.exactMatch( line ) ) 00141 { 00142 QStringList name = backgroundRegex.cap( 1 ).split( '#' ); 00143 QString fullPath = themeDir() + backgroundRegex.cap( 2 ); 00144 yuiDebug() << "Expanding background " << name[0] << "\tto " << fullPath << std::endl; 00145 00146 _backgrounds[ name[0] ].filename = fullPath; 00147 _backgrounds[ name[0] ].full = false; 00148 00149 if ( name.size() > 1 ) 00150 _backgrounds[ name[0] ].full = ( name[1] == "full" ); 00151 } 00152 00153 if ( richTextRegex.exactMatch( line ) ) 00154 { 00155 QString filename = richTextRegex.cap( 1 ); 00156 QFile file( themeDir() + "/" + filename ); 00157 00158 if ( file.open( QIODevice::ReadOnly ) ) 00159 { 00160 yuiDebug() << "Reading " << file.fileName(); 00161 _textStyle = file.readAll(); 00162 } 00163 else 00164 { 00165 yuiError() << "Can't read " << file.fileName(); 00166 } 00167 } 00168 00169 result += line; 00170 } 00171 00172 text = result; 00173 } 00174 00175 00176 QString 00177 QY2Styler::themeDir() const 00178 { 00179 return QString(YSettings::themeDir().c_str()); 00180 } 00181 00182 00183 void QY2Styler::registerWidget( QWidget * widget ) 00184 { 00185 widget->installEventFilter( this ); 00186 widget->setAutoFillBackground( true ); 00187 widget->setStyleSheet( _style ); 00188 } 00189 00190 00191 void QY2Styler::unregisterWidget( QWidget *widget ) 00192 { 00193 _children.remove( widget ); 00194 } 00195 00196 00197 void QY2Styler::registerChildWidget( QWidget * parent, QWidget * widget ) 00198 { 00199 // Don't use yuiDebug() here - deadlock (reason unknown so far) in thread handling! 00200 00201 qDebug() << "Registering " << widget << " for parent " << parent << endl; 00202 widget->installEventFilter( this ); 00203 _children[parent].push_back( widget ); 00204 } 00205 00206 00207 QImage 00208 QY2Styler::getScaled( const QString name, const QSize & size ) 00209 { 00210 QImage image = _backgrounds[name].pix; 00211 00212 if ( size != image.size() ) 00213 image = image.scaled( size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); 00214 else 00215 image = image.convertToFormat( QImage::Format_ARGB32 ); 00216 00217 if ( image.isNull() ) 00218 yuiError() << "Can't load pixmap from " << name << std::endl; 00219 #if 1 00220 else 00221 yuiMilestone() << "Loaded pixmap from \"" << name 00222 << "\" size: " << image.size().width() << "x" << image.size().height() 00223 << std::endl; 00224 #endif 00225 00226 return image; 00227 } 00228 00229 00230 void QY2Styler::renderParent( QWidget * wid ) 00231 { 00232 // yuiDebug() << "Rendering " << wid << std::endl; 00233 QString name = wid->objectName(); 00234 00235 // TODO 00236 wid->setPalette( QApplication::palette() ); 00237 00238 // if the parent does not have a background, this does not make sense 00239 if ( _backgrounds[name].pix.isNull() ) 00240 return; 00241 00242 QRect fillRect = wid->contentsRect(); 00243 if ( _backgrounds[name].full ) 00244 fillRect = wid->rect(); 00245 00246 QImage back; 00247 00248 if ( _backgrounds[name].lastscale != fillRect.size() ) 00249 { 00250 _backgrounds[name].scaled = getScaled( name, fillRect.size() ); 00251 _backgrounds[name].lastscale = fillRect.size(); 00252 } 00253 00254 back = _backgrounds[name].scaled; 00255 00256 QPainter pain( &back ); 00257 QWidget *child; 00258 00259 00260 foreach( child, _children[wid] ) 00261 { 00262 // yuiDebug() << "foreach " << child << " " << wid << std::endl; 00263 QString name = child->objectName(); 00264 00265 if (! child->isVisible() || _backgrounds[name].pix.isNull() ) 00266 continue; 00267 00268 QRect fillRect = child->contentsRect(); 00269 if ( _backgrounds[name].full ) 00270 fillRect = child->rect(); 00271 00272 QString key = QString( "style_%1_%2_%3" ).arg( name ).arg( fillRect.width() ).arg( fillRect.height() ); 00273 QPixmap scaled; 00274 00275 if ( QPixmapCache::find( key, scaled ) ) 00276 { 00277 // yuiDebug() << "found " << key << std::endl; 00278 } 00279 else 00280 { 00281 scaled = QPixmap::fromImage( getScaled( name, fillRect.size() ) ); 00282 QPixmapCache::insert( key, scaled ); 00283 } 00284 pain.drawPixmap( wid->mapFromGlobal( child->mapToGlobal( fillRect.topLeft() ) ), scaled ); 00285 } 00286 00287 QPixmap result = QPixmap::fromImage( back ); 00288 00289 QPalette p = wid->palette(); 00290 p.setBrush(QPalette::Window, result ); 00291 wid->setPalette( p ); 00292 } 00293 00294 00295 bool 00296 QY2Styler::updateRendering( QWidget *wid ) 00297 { 00298 if (!wid) 00299 return false; 00300 00301 QString name = wid->objectName(); 00302 00303 if (! wid->isVisible() || !wid->updatesEnabled() ) 00304 return false; 00305 00306 if ( _backgrounds[name].pix.isNull() ) 00307 { 00308 QString back = _backgrounds[ name ].filename; 00309 00310 QImage image( back ); 00311 _backgrounds[ name ].pix = image; 00312 00313 if ( ! back.isEmpty() ) // Prevent misleading error messages 00314 { 00315 if ( image.isNull() ) 00316 { 00317 yuiError() << "Couldn't load background image \"" << back 00318 << "\" for \"" << name << "\"" 00319 << std::endl; 00320 } 00321 else 00322 { 00323 yuiDebug() << "Loading background image \"" << back 00324 << "\" for " << name << "\"" 00325 << std::endl; 00326 } 00327 } 00328 } 00329 00330 00331 // if it's a child itself, we have to do the full blow action 00332 00333 if ( !_children.contains( wid ) ) 00334 { 00335 QWidget *parent = wid->parentWidget(); 00336 while ( parent && !_children.contains( parent ) ) 00337 parent = parent->parentWidget(); 00338 if (!parent) 00339 return false; 00340 renderParent( parent ); 00341 } 00342 else 00343 { 00344 renderParent( wid ); 00345 } 00346 00347 return true; 00348 } 00349 00350 00351 bool 00352 QY2Styler::eventFilter( QObject * obj, QEvent * ev ) 00353 { 00354 if ( ev->type() == QEvent::Resize || 00355 ev->type() == QEvent::Show || 00356 ev->type() == QEvent::LayoutRequest || 00357 ev->type() == QEvent::UpdateRequest ) 00358 updateRendering( qobject_cast<QWidget*>( obj ) ); 00359 00360 return QObject::eventFilter( obj, ev ); 00361 } 00362 00363 00364 00365 00366 std::ostream & operator<<( std::ostream & stream, const QString & str ) 00367 { 00368 return stream << qPrintable( str ); 00369 } 00370 00371 00372 std::ostream & operator<<( std::ostream & stream, const QStringList & strList ) 00373 { 00374 stream << "[ "; 00375 00376 for ( QStringList::const_iterator it = strList.begin(); 00377 it != strList.end(); 00378 ++it ) 00379 { 00380 stream << qPrintable( *it ) << " "; 00381 } 00382 00383 stream << " ]"; 00384 00385 return stream; 00386 } 00387 00388 00389 std::ostream & operator<<( std::ostream & stream, const QWidget * widget ) 00390 { 00391 #if LOGGING_CAUSES_QT4_THREADING_PROBLEMS 00392 00393 // QObject::metaObject()->className() and QObject::objectName() can cause 00394 // YQUI to hang, probably due to threading problems. 00395 00396 stream << "QWidget at " << hex << (void *) widget << dec; 00397 #else 00398 if ( widget ) 00399 { 00400 if ( widget->metaObject() ) 00401 stream << widget->metaObject()->className(); 00402 else 00403 stream << "<QWidget>"; 00404 00405 if ( ! widget->objectName().isEmpty() ) 00406 stream << " \"" << qPrintable( widget->objectName() ) << "\""; 00407 00408 stream << " at " << hex << widget << dec; 00409 } 00410 else // ! widget 00411 { 00412 stream << "<NULL QWidget>"; 00413 } 00414 #endif 00415 00416 00417 return stream; 00418 } 00419 00420 00421 #include "QY2Styler.moc"