libyui-qt  2.46.21
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  foreach( QWidget *registered_widget, _registered_widgets )
115  registered_widget->setStyleSheet( _style );
116 }
117 
118 
119 void QY2Styler::processUrls( QString & text )
120 {
121  QString result;
122  QStringList lines = text.split( '\n' );
123  QRegExp urlRegex( ": *url\\((.*)\\)" );
124  QRegExp backgroundRegex( "^ */\\* *Background: *([^ ]*) *([^ ]*) *\\*/$" );
125  QRegExp richTextRegex( "^ */\\* *Richtext: *([^ ]*) *\\*/$" );
126 
127  _backgrounds.clear();
128 
129  for ( QStringList::const_iterator it = lines.begin(); it != lines.end(); ++it )
130  {
131  QString line = *it;
132 
133  // Replace file name inside url() with full path (from themeDir() )
134 
135  if ( urlRegex.indexIn( line ) >= 0 )
136  {
137  QString fileName = urlRegex.cap( 1 );
138  QString fullPath = themeDir() + fileName;
139  yuiDebug() << "Expanding " << fileName << "\tto " << fullPath << std::endl;
140  line.replace( urlRegex, ": url(" + fullPath + ")");
141  }
142 
143  if ( backgroundRegex.exactMatch( line ) )
144  {
145  QStringList name = backgroundRegex.cap( 1 ).split( '#' );
146  QString fullPath = themeDir() + backgroundRegex.cap( 2 );
147  yuiDebug() << "Expanding background " << name[0] << "\tto " << fullPath << std::endl;
148 
149  _backgrounds[ name[0] ].filename = fullPath;
150  _backgrounds[ name[0] ].full = false;
151 
152  if ( name.size() > 1 )
153  _backgrounds[ name[0] ].full = ( name[1] == "full" );
154  }
155 
156  if ( richTextRegex.exactMatch( line ) )
157  {
158  QString filename = richTextRegex.cap( 1 );
159  QFile file( themeDir() + "/" + filename );
160 
161  if ( file.open( QIODevice::ReadOnly ) )
162  {
163  yuiDebug() << "Reading " << file.fileName();
164  _textStyle = file.readAll();
165  }
166  else
167  {
168  yuiError() << "Can't read " << file.fileName();
169  }
170  }
171 
172  result += line;
173  }
174 
175  text = result;
176 }
177 
178 
179 QString
180 QY2Styler::themeDir() const
181 {
182  return QString(YSettings::themeDir().c_str());
183 }
184 
185 
186 void QY2Styler::registerWidget( QWidget * widget )
187 {
188  widget->installEventFilter( this );
189  widget->setAutoFillBackground( true );
190  widget->setStyleSheet( _style );
191  _registered_widgets.push_back( widget );
192 }
193 
194 
195 void QY2Styler::unregisterWidget( QWidget *widget )
196 {
197  _children.remove( widget );
198  _registered_widgets.removeOne( widget );
199 }
200 
201 
202 void QY2Styler::registerChildWidget( QWidget * parent, QWidget * widget )
203 {
204  // Don't use yuiDebug() here - deadlock (reason unknown so far) in thread handling!
205 
206  qDebug() << "Registering " << widget << " for parent " << parent << endl;
207  widget->installEventFilter( this );
208  _children[parent].push_back( widget );
209 }
210 
211 
212 QImage
213 QY2Styler::getScaled( const QString name, const QSize & size )
214 {
215  QImage image = _backgrounds[name].pix;
216 
217  if ( size != image.size() )
218  image = image.scaled( size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
219  else
220  image = image.convertToFormat( QImage::Format_ARGB32 );
221 
222  if ( image.isNull() )
223  yuiError() << "Can't load pixmap from " << name << std::endl;
224 #if 1
225  else
226  yuiMilestone() << "Loaded pixmap from \"" << name
227  << "\" size: " << image.size().width() << "x" << image.size().height()
228  << std::endl;
229 #endif
230 
231  return image;
232 }
233 
234 
235 void QY2Styler::renderParent( QWidget * wid )
236 {
237  // yuiDebug() << "Rendering " << wid << std::endl;
238  QString name = wid->objectName();
239 
240  // TODO
241  wid->setPalette( QApplication::palette() );
242 
243  // if the parent does not have a background, this does not make sense
244  if ( _backgrounds[name].pix.isNull() )
245  return;
246 
247  QRect fillRect = wid->contentsRect();
248  if ( _backgrounds[name].full )
249  fillRect = wid->rect();
250 
251  QImage back;
252 
253  if ( _backgrounds[name].lastscale != fillRect.size() )
254  {
255  _backgrounds[name].scaled = getScaled( name, fillRect.size() );
256  _backgrounds[name].lastscale = fillRect.size();
257  }
258 
259  back = _backgrounds[name].scaled;
260 
261  QPainter pain( &back );
262  QWidget *child;
263 
264 
265  foreach( child, _children[wid] )
266  {
267  // yuiDebug() << "foreach " << child << " " << wid << std::endl;
268  QString name = child->objectName();
269 
270  if (! child->isVisible() || _backgrounds[name].pix.isNull() )
271  continue;
272 
273  QRect fillRect = child->contentsRect();
274  if ( _backgrounds[name].full )
275  fillRect = child->rect();
276 
277  QString key = QString( "style_%1_%2_%3" ).arg( name ).arg( fillRect.width() ).arg( fillRect.height() );
278  QPixmap scaled;
279 
280  if ( QPixmapCache::find( key, scaled ) )
281  {
282  // yuiDebug() << "found " << key << std::endl;
283  }
284  else
285  {
286  scaled = QPixmap::fromImage( getScaled( name, fillRect.size() ) );
287  QPixmapCache::insert( key, scaled );
288  }
289  pain.drawPixmap( wid->mapFromGlobal( child->mapToGlobal( fillRect.topLeft() ) ), scaled );
290  }
291 
292  QPixmap result = QPixmap::fromImage( back );
293 
294  QPalette p = wid->palette();
295  p.setBrush(QPalette::Window, result );
296  wid->setPalette( p );
297 }
298 
299 
300 bool
301 QY2Styler::updateRendering( QWidget *wid )
302 {
303  if (!wid)
304  return false;
305 
306  QString name = wid->objectName();
307 
308  if (! wid->isVisible() || !wid->updatesEnabled() )
309  return false;
310 
311  if ( _backgrounds[name].pix.isNull() )
312  {
313  QString back = _backgrounds[ name ].filename;
314 
315  if ( back.isEmpty() )
316  {
317  _backgrounds[ name ].pix = QImage();
318  }
319  else
320  {
321  QImage image ( back );
322  _backgrounds[ name ].pix = image;
323 
324  if ( image.isNull() )
325  {
326  yuiError() << "Couldn't load background image \"" << back
327  << "\" for \"" << name << "\""
328  << std::endl;
329  }
330  else
331  {
332  yuiDebug() << "Loading background image \"" << back
333  << "\" for " << name << "\""
334  << std::endl;
335  }
336  }
337  }
338 
339 
340  // if it's a child itself, we have to do the full blow action
341 
342  if ( !_children.contains( wid ) )
343  {
344  QWidget *parent = wid->parentWidget();
345  while ( parent && !_children.contains( parent ) )
346  parent = parent->parentWidget();
347  if (!parent)
348  return false;
349  renderParent( parent );
350  }
351  else
352  {
353  renderParent( wid );
354  }
355 
356  return true;
357 }
358 
359 
360 bool
361 QY2Styler::eventFilter( QObject * obj, QEvent * ev )
362 {
363  if ( ev->type() == QEvent::Resize ||
364  ev->type() == QEvent::Show ||
365  ev->type() == QEvent::LayoutRequest ||
366  ev->type() == QEvent::UpdateRequest )
367  updateRendering( qobject_cast<QWidget*>( obj ) );
368 
369  return QObject::eventFilter( obj, ev );
370 }
371 
372 
373 
374 
375 std::ostream & operator<<( std::ostream & stream, const QString & str )
376 {
377  return stream << qPrintable( str );
378 }
379 
380 
381 std::ostream & operator<<( std::ostream & stream, const QStringList & strList )
382 {
383  stream << "[ ";
384 
385  for ( QStringList::const_iterator it = strList.begin();
386  it != strList.end();
387  ++it )
388  {
389  stream << qPrintable( *it ) << " ";
390  }
391 
392  stream << " ]";
393 
394  return stream;
395 }
396 
397 
398 std::ostream & operator<<( std::ostream & stream, const QWidget * widget )
399 {
400 #if LOGGING_CAUSES_QT4_THREADING_PROBLEMS
401 
402  // QObject::metaObject()->className() and QObject::objectName() can cause
403  // YQUI to hang, probably due to threading problems.
404 
405  stream << "QWidget at " << hex << (void *) widget << dec;
406 #else
407  if ( widget )
408  {
409  if ( widget->metaObject() )
410  stream << widget->metaObject()->className();
411  else
412  stream << "<QWidget>";
413 
414  if ( ! widget->objectName().isEmpty() )
415  stream << " \"" << qPrintable( widget->objectName() ) << "\"";
416 
417  stream << " at " << hex << widget << dec;
418  }
419  else // ! widget
420  {
421  stream << "<NULL QWidget>";
422  }
423 #endif
424 
425 
426  return stream;
427 }
428 
429 
430 #include "QY2Styler.moc"
STL namespace.
void processUrls(QString &text)
Search and replace some self-defined macros in the style sheet.
Definition: QY2Styler.cc:119
QY2Styler(QObject *parent)
Constructor.
Definition: QY2Styler.cc:53