libyui-qt  2.46.13
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  if ( back.isEmpty() )
311  {
312  _backgrounds[ name ].pix = QImage();
313  }
314  else
315  {
316  QImage image ( back );
317  _backgrounds[ name ].pix = image;
318 
319  if ( image.isNull() )
320  {
321  yuiError() << "Couldn't load background image \"" << back
322  << "\" for \"" << name << "\""
323  << std::endl;
324  }
325  else
326  {
327  yuiDebug() << "Loading background image \"" << back
328  << "\" for " << name << "\""
329  << std::endl;
330  }
331  }
332  }
333 
334 
335  // if it's a child itself, we have to do the full blow action
336 
337  if ( !_children.contains( wid ) )
338  {
339  QWidget *parent = wid->parentWidget();
340  while ( parent && !_children.contains( parent ) )
341  parent = parent->parentWidget();
342  if (!parent)
343  return false;
344  renderParent( parent );
345  }
346  else
347  {
348  renderParent( wid );
349  }
350 
351  return true;
352 }
353 
354 
355 bool
356 QY2Styler::eventFilter( QObject * obj, QEvent * ev )
357 {
358  if ( ev->type() == QEvent::Resize ||
359  ev->type() == QEvent::Show ||
360  ev->type() == QEvent::LayoutRequest ||
361  ev->type() == QEvent::UpdateRequest )
362  updateRendering( qobject_cast<QWidget*>( obj ) );
363 
364  return QObject::eventFilter( obj, ev );
365 }
366 
367 
368 
369 
370 std::ostream & operator<<( std::ostream & stream, const QString & str )
371 {
372  return stream << qPrintable( str );
373 }
374 
375 
376 std::ostream & operator<<( std::ostream & stream, const QStringList & strList )
377 {
378  stream << "[ ";
379 
380  for ( QStringList::const_iterator it = strList.begin();
381  it != strList.end();
382  ++it )
383  {
384  stream << qPrintable( *it ) << " ";
385  }
386 
387  stream << " ]";
388 
389  return stream;
390 }
391 
392 
393 std::ostream & operator<<( std::ostream & stream, const QWidget * widget )
394 {
395 #if LOGGING_CAUSES_QT4_THREADING_PROBLEMS
396 
397  // QObject::metaObject()->className() and QObject::objectName() can cause
398  // YQUI to hang, probably due to threading problems.
399 
400  stream << "QWidget at " << hex << (void *) widget << dec;
401 #else
402  if ( widget )
403  {
404  if ( widget->metaObject() )
405  stream << widget->metaObject()->className();
406  else
407  stream << "<QWidget>";
408 
409  if ( ! widget->objectName().isEmpty() )
410  stream << " \"" << qPrintable( widget->objectName() ) << "\"";
411 
412  stream << " at " << hex << widget << dec;
413  }
414  else // ! widget
415  {
416  stream << "<NULL QWidget>";
417  }
418 #endif
419 
420 
421  return stream;
422 }
423 
424 
425 #include "QY2Styler.moc"
STL namespace.
void processUrls(QString &text)
Search and replace some self-defined macros in the style sheet.
Definition: QY2Styler.cc:116
QY2Styler(QObject *parent)
Constructor.
Definition: QY2Styler.cc:53