libyui-qt  2.44.0
 All Classes Functions Variables
QY2Styler.cc
1 /*
2  Copyright (C) 2000-2012 Novell, Inc
3  This library is free software; you can redistribute it and/or modify
4  it under the terms of the GNU Lesser General Public License as
5  published by the Free Software Foundation; either version 2.1 of the
6  License, or (at your option) version 3.0 of the License. This library
7  is distributed in the hope that it will be useful, but WITHOUT ANY
8  WARRANTY; without even the implied warranty of MERCHANTABILITY or
9  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10  License for more details. You should have received a copy of the GNU
11  Lesser General Public License along with this library; if not, write
12  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
13  Floor, Boston, MA 02110-1301 USA
14 */
15 
16 
17 /*-/
18 
19  File: QY2Styler.cc
20 
21  Author: Stefan Kulow <coolo@suse.de>
22 
23 /-*/
24 
25 
26 #define YUILogComponent "qt-styler"
27 #include <yui/YUILog.h>
28 #include <yui/YUIException.h>
29 #include <yui/Libyui_config.h>
30 #include <YSettings.h>
31 
32 #include "QY2Styler.h"
33 #include <QDebug>
34 #include <QFile>
35 #include <QString>
36 #include <QStringList>
37 #include <QApplication>
38 #include <QWidget>
39 #include <QPainter>
40 #include <QSvgRenderer>
41 #include <iostream>
42 #include <QPixmapCache>
43 
44 #define LOGGING_CAUSES_QT4_THREADING_PROBLEMS 1
45 
46 std::ostream & operator<<( std::ostream & stream, const QString & str );
47 std::ostream & operator<<( std::ostream & stream, const QStringList & strList );
48 std::ostream & operator<<( std::ostream & stream, const QWidget * widget );
49 
50 using namespace std;
51 
52 
53 QY2Styler::QY2Styler( QObject * parent )
54  : QObject( parent )
55 {
56  QPixmapCache::setCacheLimit( 5 * 1024 );
57  yuiDebug() << "Styler created" << std::endl;
58 }
59 
60 
61 QY2Styler *
62 QY2Styler::styler()
63 {
64  static QY2Styler * styler = 0;
65 
66  if ( ! styler )
67  {
68  yuiDebug() << "Creating QY2Styler singleton" << std::endl;
69 
70  styler = new QY2Styler( qApp );
71  YUI_CHECK_NEW( styler );
72 
73  QString style = getenv("Y2STYLE");
74 
75  if ( ! style.isEmpty() )
76  styler->loadStyleSheet( style );
77  else
78  styler->loadStyleSheet( "style.qss" );
79  }
80 
81  return styler;
82 }
83 
84 
85 void QY2Styler::loadStyleSheet( const QString & filename )
86 {
87  QFile file( themeDir() + filename );
88 
89  if ( file.open( QIODevice::ReadOnly ) )
90  {
91  yuiMilestone() << "Using style sheet \"" << file.fileName() << "\"" << std::endl;
92  QString text = file.readAll();
93  setStyleSheet( text );
94  }
95  else
96  {
97  yuiMilestone() << "Couldn't open style sheet \"" << file.fileName() << "\"" << std::endl;
98  }
99 
100 }
101 
102 void QY2Styler::setStyleSheet( const QString & text )
103 {
104  _style = text;
105  processUrls( _style );
106 
107  QWidget *child;
108  QList< QWidget* > childlist;
109 
110  foreach( childlist, _children )
111  foreach( child, childlist )
112  child->setStyleSheet( _style );
113 }
114 
115 
116 void QY2Styler::processUrls( QString & text )
117 {
118  QString result;
119  QStringList lines = text.split( '\n' );
120  QRegExp urlRegex( ": *url\\((.*)\\)" );
121  QRegExp backgroundRegex( "^ */\\* *Background: *([^ ]*) *([^ ]*) *\\*/$" );
122  QRegExp richTextRegex( "^ */\\* *Richtext: *([^ ]*) *\\*/$" );
123 
124  _backgrounds.clear();
125 
126  for ( QStringList::const_iterator it = lines.begin(); it != lines.end(); ++it )
127  {
128  QString line = *it;
129 
130  // Replace file name inside url() with full path (from themeDir() )
131 
132  if ( urlRegex.indexIn( line ) >= 0 )
133  {
134  QString fileName = urlRegex.cap( 1 );
135  QString fullPath = themeDir() + fileName;
136  yuiDebug() << "Expanding " << fileName << "\tto " << fullPath << std::endl;
137  line.replace( urlRegex, ": url(" + fullPath + ")");
138  }
139 
140  if ( backgroundRegex.exactMatch( line ) )
141  {
142  QStringList name = backgroundRegex.cap( 1 ).split( '#' );
143  QString fullPath = themeDir() + backgroundRegex.cap( 2 );
144  yuiDebug() << "Expanding background " << name[0] << "\tto " << fullPath << std::endl;
145 
146  _backgrounds[ name[0] ].filename = fullPath;
147  _backgrounds[ name[0] ].full = false;
148 
149  if ( name.size() > 1 )
150  _backgrounds[ name[0] ].full = ( name[1] == "full" );
151  }
152 
153  if ( richTextRegex.exactMatch( line ) )
154  {
155  QString filename = richTextRegex.cap( 1 );
156  QFile file( themeDir() + "/" + filename );
157 
158  if ( file.open( QIODevice::ReadOnly ) )
159  {
160  yuiDebug() << "Reading " << file.fileName();
161  _textStyle = file.readAll();
162  }
163  else
164  {
165  yuiError() << "Can't read " << file.fileName();
166  }
167  }
168 
169  result += line;
170  }
171 
172  text = result;
173 }
174 
175 
176 QString
177 QY2Styler::themeDir() const
178 {
179  return QString(YSettings::themeDir().c_str());
180 }
181 
182 
183 void QY2Styler::registerWidget( QWidget * widget )
184 {
185  widget->installEventFilter( this );
186  widget->setAutoFillBackground( true );
187  widget->setStyleSheet( _style );
188 }
189 
190 
191 void QY2Styler::unregisterWidget( QWidget *widget )
192 {
193  _children.remove( widget );
194 }
195 
196 
197 void QY2Styler::registerChildWidget( QWidget * parent, QWidget * widget )
198 {
199  // Don't use yuiDebug() here - deadlock (reason unknown so far) in thread handling!
200 
201  qDebug() << "Registering " << widget << " for parent " << parent << endl;
202  widget->installEventFilter( this );
203  _children[parent].push_back( widget );
204 }
205 
206 
207 QImage
208 QY2Styler::getScaled( const QString name, const QSize & size )
209 {
210  QImage image = _backgrounds[name].pix;
211 
212  if ( size != image.size() )
213  image = image.scaled( size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
214  else
215  image = image.convertToFormat( QImage::Format_ARGB32 );
216 
217  if ( image.isNull() )
218  yuiError() << "Can't load pixmap from " << name << std::endl;
219 #if 1
220  else
221  yuiMilestone() << "Loaded pixmap from \"" << name
222  << "\" size: " << image.size().width() << "x" << image.size().height()
223  << std::endl;
224 #endif
225 
226  return image;
227 }
228 
229 
230 void QY2Styler::renderParent( QWidget * wid )
231 {
232  // yuiDebug() << "Rendering " << wid << std::endl;
233  QString name = wid->objectName();
234 
235  // TODO
236  wid->setPalette( QApplication::palette() );
237 
238  // if the parent does not have a background, this does not make sense
239  if ( _backgrounds[name].pix.isNull() )
240  return;
241 
242  QRect fillRect = wid->contentsRect();
243  if ( _backgrounds[name].full )
244  fillRect = wid->rect();
245 
246  QImage back;
247 
248  if ( _backgrounds[name].lastscale != fillRect.size() )
249  {
250  _backgrounds[name].scaled = getScaled( name, fillRect.size() );
251  _backgrounds[name].lastscale = fillRect.size();
252  }
253 
254  back = _backgrounds[name].scaled;
255 
256  QPainter pain( &back );
257  QWidget *child;
258 
259 
260  foreach( child, _children[wid] )
261  {
262  // yuiDebug() << "foreach " << child << " " << wid << std::endl;
263  QString name = child->objectName();
264 
265  if (! child->isVisible() || _backgrounds[name].pix.isNull() )
266  continue;
267 
268  QRect fillRect = child->contentsRect();
269  if ( _backgrounds[name].full )
270  fillRect = child->rect();
271 
272  QString key = QString( "style_%1_%2_%3" ).arg( name ).arg( fillRect.width() ).arg( fillRect.height() );
273  QPixmap scaled;
274 
275  if ( QPixmapCache::find( key, scaled ) )
276  {
277  // yuiDebug() << "found " << key << std::endl;
278  }
279  else
280  {
281  scaled = QPixmap::fromImage( getScaled( name, fillRect.size() ) );
282  QPixmapCache::insert( key, scaled );
283  }
284  pain.drawPixmap( wid->mapFromGlobal( child->mapToGlobal( fillRect.topLeft() ) ), scaled );
285  }
286 
287  QPixmap result = QPixmap::fromImage( back );
288 
289  QPalette p = wid->palette();
290  p.setBrush(QPalette::Window, result );
291  wid->setPalette( p );
292 }
293 
294 
295 bool
296 QY2Styler::updateRendering( QWidget *wid )
297 {
298  if (!wid)
299  return false;
300 
301  QString name = wid->objectName();
302 
303  if (! wid->isVisible() || !wid->updatesEnabled() )
304  return false;
305 
306  if ( _backgrounds[name].pix.isNull() )
307  {
308  QString back = _backgrounds[ name ].filename;
309 
310  QImage image( back );
311  _backgrounds[ name ].pix = image;
312 
313  if ( ! back.isEmpty() ) // Prevent misleading error messages
314  {
315  if ( image.isNull() )
316  {
317  yuiError() << "Couldn't load background image \"" << back
318  << "\" for \"" << name << "\""
319  << std::endl;
320  }
321  else
322  {
323  yuiDebug() << "Loading background image \"" << back
324  << "\" for " << name << "\""
325  << std::endl;
326  }
327  }
328  }
329 
330 
331  // if it's a child itself, we have to do the full blow action
332 
333  if ( !_children.contains( wid ) )
334  {
335  QWidget *parent = wid->parentWidget();
336  while ( parent && !_children.contains( parent ) )
337  parent = parent->parentWidget();
338  if (!parent)
339  return false;
340  renderParent( parent );
341  }
342  else
343  {
344  renderParent( wid );
345  }
346 
347  return true;
348 }
349 
350 
351 bool
352 QY2Styler::eventFilter( QObject * obj, QEvent * ev )
353 {
354  if ( ev->type() == QEvent::Resize ||
355  ev->type() == QEvent::Show ||
356  ev->type() == QEvent::LayoutRequest ||
357  ev->type() == QEvent::UpdateRequest )
358  updateRendering( qobject_cast<QWidget*>( obj ) );
359 
360  return QObject::eventFilter( obj, ev );
361 }
362 
363 
364 
365 
366 std::ostream & operator<<( std::ostream & stream, const QString & str )
367 {
368  return stream << qPrintable( str );
369 }
370 
371 
372 std::ostream & operator<<( std::ostream & stream, const QStringList & strList )
373 {
374  stream << "[ ";
375 
376  for ( QStringList::const_iterator it = strList.begin();
377  it != strList.end();
378  ++it )
379  {
380  stream << qPrintable( *it ) << " ";
381  }
382 
383  stream << " ]";
384 
385  return stream;
386 }
387 
388 
389 std::ostream & operator<<( std::ostream & stream, const QWidget * widget )
390 {
391 #if LOGGING_CAUSES_QT4_THREADING_PROBLEMS
392 
393  // QObject::metaObject()->className() and QObject::objectName() can cause
394  // YQUI to hang, probably due to threading problems.
395 
396  stream << "QWidget at " << hex << (void *) widget << dec;
397 #else
398  if ( widget )
399  {
400  if ( widget->metaObject() )
401  stream << widget->metaObject()->className();
402  else
403  stream << "<QWidget>";
404 
405  if ( ! widget->objectName().isEmpty() )
406  stream << " \"" << qPrintable( widget->objectName() ) << "\"";
407 
408  stream << " at " << hex << widget << dec;
409  }
410  else // ! widget
411  {
412  stream << "<NULL QWidget>";
413  }
414 #endif
415 
416 
417  return stream;
418 }
419 
420 
421 #include "QY2Styler.moc"
void processUrls(QString &text)
Definition: QY2Styler.cc:116
QY2Styler(QObject *parent)
Definition: QY2Styler.cc:53