|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the tools applications of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "deviceskin.h" |
|
43 |
|
44 #include <QtCore/qnamespace.h> |
|
45 #include <QtGui/QApplication> |
|
46 #include <QtGui/QBitmap> |
|
47 #include <QtGui/QPixmap> |
|
48 #include <QtGui/QPainter> |
|
49 #include <QtCore/QTextStream> |
|
50 #include <QtCore/QFile> |
|
51 #include <QtCore/QFileInfo> |
|
52 #include <QtGui/QImage> |
|
53 #include <QtCore/QTimer> |
|
54 #include <QtCore/QDir> |
|
55 #include <QtCore/QRegExp> |
|
56 #include <QtGui/QMouseEvent> |
|
57 #include <QtCore/QDebug> |
|
58 |
|
59 #ifdef TEST_SKIN |
|
60 # include <QtGui/QMainWindow> |
|
61 # include <QtGui/QDialog> |
|
62 # include <QtGui/QDialogButtonBox> |
|
63 # include <QtGui/QHBoxLayout> |
|
64 #endif |
|
65 |
|
66 QT_BEGIN_NAMESPACE |
|
67 |
|
68 namespace { |
|
69 enum { joydistance = 10, key_repeat_period = 50, key_repeat_delay = 500 }; |
|
70 enum { debugDeviceSkin = 0 }; |
|
71 } |
|
72 |
|
73 static void parseRect(const QString &value, QRect *rect) { |
|
74 const QStringList l = value.split(QLatin1Char(' ')); |
|
75 rect->setRect(l[0].toInt(), l[1].toInt(), l[2].toInt(), l[3].toInt()); |
|
76 } |
|
77 |
|
78 static QString msgImageNotLoaded(const QString &f) { |
|
79 return DeviceSkin::tr("The image file '%1' could not be loaded.").arg(f); |
|
80 } |
|
81 |
|
82 // ------------ DeviceSkinButtonArea |
|
83 DeviceSkinButtonArea::DeviceSkinButtonArea() : |
|
84 keyCode(0), |
|
85 activeWhenClosed(0) |
|
86 { |
|
87 } |
|
88 |
|
89 QDebug &operator<<(QDebug &str, const DeviceSkinButtonArea &a) |
|
90 { |
|
91 |
|
92 str << "Area: " << a.name << " keyCode=" << a.keyCode << " area=" << a.area |
|
93 << " text=" << a.text << " activeWhenClosed=" << a.activeWhenClosed; |
|
94 return str; |
|
95 } |
|
96 |
|
97 // ------------ DeviceSkinParameters |
|
98 |
|
99 QDebug operator<<(QDebug str, const DeviceSkinParameters &p) |
|
100 { |
|
101 str << "Images " << p.skinImageUpFileName << ',' |
|
102 << p.skinImageDownFileName<< ',' << p.skinImageClosedFileName |
|
103 << ',' << p.skinCursorFileName <<"\nScreen: " << p.screenRect |
|
104 << " back: " << p.backScreenRect << " closed: " << p.closedScreenRect |
|
105 << " cursor: " << p.cursorHot << " Prefix: " << p.prefix |
|
106 << " Joystick: " << p.joystick << " MouseHover" << p.hasMouseHover; |
|
107 const int numAreas = p.buttonAreas.size(); |
|
108 for (int i = 0; i < numAreas; i++) |
|
109 str << p.buttonAreas[i]; |
|
110 return str; |
|
111 } |
|
112 |
|
113 QSize DeviceSkinParameters::secondaryScreenSize() const |
|
114 { |
|
115 return backScreenRect.isNull() ? closedScreenRect .size(): backScreenRect.size(); |
|
116 } |
|
117 |
|
118 bool DeviceSkinParameters::hasSecondaryScreen() const |
|
119 { |
|
120 return secondaryScreenSize() != QSize(0, 0); |
|
121 } |
|
122 |
|
123 bool DeviceSkinParameters::read(const QString &skinDirectory, ReadMode rm, QString *errorMessage) |
|
124 { |
|
125 // Figure out the name. remove ending '/' if present |
|
126 QString skinFile = skinDirectory; |
|
127 if (skinFile.endsWith(QLatin1Char('/'))) |
|
128 skinFile.truncate(skinFile.length() - 1); |
|
129 |
|
130 QFileInfo fi(skinFile); |
|
131 QString fn; |
|
132 if ( fi.isDir() ) { |
|
133 prefix = skinFile; |
|
134 prefix += QLatin1Char('/'); |
|
135 fn = prefix; |
|
136 fn += fi.baseName(); |
|
137 fn += QLatin1String(".skin"); |
|
138 } else if (fi.isFile()){ |
|
139 fn = skinFile; |
|
140 prefix = fi.path(); |
|
141 prefix += QLatin1Char('/'); |
|
142 } else { |
|
143 *errorMessage = DeviceSkin::tr("The skin directory '%1' does not contain a configuration file.").arg(skinDirectory); |
|
144 return false; |
|
145 } |
|
146 QFile f(fn); |
|
147 if (!f.open(QIODevice::ReadOnly )) { |
|
148 *errorMessage = DeviceSkin::tr("The skin configuration file '%1' could not be opened.").arg(fn); |
|
149 return false; |
|
150 } |
|
151 QTextStream ts(&f); |
|
152 const bool rc = read(ts, rm, errorMessage); |
|
153 if (!rc) |
|
154 *errorMessage = DeviceSkin::tr("The skin configuration file '%1' could not be read: %2").arg(fn).arg(*errorMessage); |
|
155 return rc; |
|
156 } |
|
157 bool DeviceSkinParameters::read(QTextStream &ts, ReadMode rm, QString *errorMessage) |
|
158 { |
|
159 QStringList closedAreas; |
|
160 QStringList toggleAreas; |
|
161 QStringList toggleActiveAreas; |
|
162 int nareas = 0; |
|
163 screenDepth = 0; |
|
164 QString mark; |
|
165 ts >> mark; |
|
166 hasMouseHover = true; // historical default |
|
167 if ( mark == QLatin1String("[SkinFile]") ) { |
|
168 const QString UpKey = QLatin1String("Up"); |
|
169 const QString DownKey = QLatin1String("Down"); |
|
170 const QString ClosedKey = QLatin1String("Closed"); |
|
171 const QString ClosedAreasKey = QLatin1String("ClosedAreas"); |
|
172 const QString ScreenKey = QLatin1String("Screen"); |
|
173 const QString ScreenDepthKey = QLatin1String("ScreenDepth"); |
|
174 const QString BackScreenKey = QLatin1String("BackScreen"); |
|
175 const QString ClosedScreenKey = QLatin1String("ClosedScreen"); |
|
176 const QString CursorKey = QLatin1String("Cursor"); |
|
177 const QString AreasKey = QLatin1String("Areas"); |
|
178 const QString ToggleAreasKey = QLatin1String("ToggleAreas"); |
|
179 const QString ToggleActiveAreasKey = QLatin1String("ToggleActiveAreas"); |
|
180 const QString HasMouseHoverKey = QLatin1String("HasMouseHover"); |
|
181 // New |
|
182 while (!nareas) { |
|
183 QString line = ts.readLine(); |
|
184 if ( line.isNull() ) |
|
185 break; |
|
186 if ( line[0] != QLatin1Char('#') && !line.isEmpty() ) { |
|
187 int eq = line.indexOf(QLatin1Char('=')); |
|
188 if ( eq >= 0 ) { |
|
189 const QString key = line.left(eq); |
|
190 eq++; |
|
191 while (eq<line.length()-1 && line[eq].isSpace()) |
|
192 eq++; |
|
193 const QString value = line.mid(eq); |
|
194 if ( key == UpKey ) { |
|
195 skinImageUpFileName = value; |
|
196 } else if ( key == DownKey ) { |
|
197 skinImageDownFileName = value; |
|
198 } else if ( key == ClosedKey ) { |
|
199 skinImageClosedFileName = value; |
|
200 } else if ( key == ClosedAreasKey ) { |
|
201 closedAreas = value.split(QLatin1Char(' ')); |
|
202 } else if ( key == ScreenKey ) { |
|
203 parseRect( value, &screenRect); |
|
204 } else if ( key == ScreenDepthKey ) { |
|
205 screenDepth = value.toInt(); |
|
206 } else if ( key == BackScreenKey ) { |
|
207 parseRect(value, &backScreenRect); |
|
208 } else if ( key == ClosedScreenKey ) { |
|
209 parseRect( value, &closedScreenRect ); |
|
210 } else if ( key == CursorKey ) { |
|
211 QStringList l = value.split(QLatin1Char(' ')); |
|
212 skinCursorFileName = l[0]; |
|
213 cursorHot = QPoint(l[1].toInt(),l[2].toInt()); |
|
214 } else if ( key == AreasKey ) { |
|
215 nareas = value.toInt(); |
|
216 } else if ( key == ToggleAreasKey ) { |
|
217 toggleAreas = value.split(QLatin1Char(' ')); |
|
218 } else if ( key == ToggleActiveAreasKey ) { |
|
219 toggleActiveAreas = value.split(QLatin1Char(' ')); |
|
220 } else if ( key == HasMouseHoverKey ) { |
|
221 hasMouseHover = value == QLatin1String("true") || value == QLatin1String("1"); |
|
222 } |
|
223 } else { |
|
224 *errorMessage = DeviceSkin::tr("Syntax error: %1").arg(line); |
|
225 return false; |
|
226 } |
|
227 } |
|
228 } |
|
229 } else { |
|
230 // Old |
|
231 skinImageUpFileName = mark; |
|
232 QString s; |
|
233 int x,y,w,h,na; |
|
234 ts >> s >> x >> y >> w >> h >> na; |
|
235 skinImageDownFileName = s; |
|
236 screenRect.setRect(x, y, w, h); |
|
237 nareas = na; |
|
238 } |
|
239 // Done for short mode |
|
240 if (rm == ReadSizeOnly) |
|
241 return true; |
|
242 // verify skin files exist |
|
243 skinImageUpFileName.insert(0, prefix); |
|
244 if (!QFile(skinImageUpFileName).exists()) { |
|
245 *errorMessage = DeviceSkin::tr("The skin \"up\" image file '%1' does not exist.").arg(skinImageUpFileName); |
|
246 return false; |
|
247 } |
|
248 if (!skinImageUp.load(skinImageUpFileName)) { |
|
249 *errorMessage = msgImageNotLoaded(skinImageUpFileName); |
|
250 return false; |
|
251 } |
|
252 |
|
253 skinImageDownFileName.insert(0, prefix); |
|
254 if (!QFile(skinImageDownFileName).exists()) { |
|
255 *errorMessage = DeviceSkin::tr("The skin \"down\" image file '%1' does not exist.").arg(skinImageDownFileName); |
|
256 return false; |
|
257 } |
|
258 if (!skinImageDown.load(skinImageDownFileName)) { |
|
259 *errorMessage = msgImageNotLoaded(skinImageDownFileName); |
|
260 return false; |
|
261 } |
|
262 |
|
263 if (!skinImageClosedFileName.isEmpty()) { |
|
264 skinImageClosedFileName.insert(0, prefix); |
|
265 if (!QFile(skinImageClosedFileName).exists()) { |
|
266 *errorMessage = DeviceSkin::tr("The skin \"closed\" image file '%1' does not exist.").arg(skinImageClosedFileName); |
|
267 return false; |
|
268 } |
|
269 if (!skinImageClosed.load(skinImageClosedFileName)) { |
|
270 *errorMessage = msgImageNotLoaded(skinImageClosedFileName); |
|
271 return false; |
|
272 } |
|
273 } |
|
274 |
|
275 if (!skinCursorFileName.isEmpty()) { |
|
276 skinCursorFileName.insert(0, prefix); |
|
277 if (!QFile(skinCursorFileName).exists()) { |
|
278 *errorMessage = DeviceSkin::tr("The skin cursor image file '%1' does not exist.").arg(skinCursorFileName); |
|
279 return false; |
|
280 } |
|
281 if (!skinCursor.load(skinCursorFileName)) { |
|
282 *errorMessage = msgImageNotLoaded(skinCursorFileName); |
|
283 return false; |
|
284 } |
|
285 } |
|
286 |
|
287 // read areas |
|
288 if (!nareas) |
|
289 return true; |
|
290 buttonAreas.reserve(nareas); |
|
291 |
|
292 int i = 0; |
|
293 ts.readLine(); // eol |
|
294 joystick = -1; |
|
295 const QString Joystick = QLatin1String("Joystick"); |
|
296 while (i < nareas && !ts.atEnd() ) { |
|
297 buttonAreas.push_back(DeviceSkinButtonArea()); |
|
298 DeviceSkinButtonArea &area = buttonAreas.back(); |
|
299 const QString line = ts.readLine(); |
|
300 if ( !line.isEmpty() && line[0] != QLatin1Char('#') ) { |
|
301 const QStringList tok = line.split(QRegExp(QLatin1String("[ \t][ \t]*"))); |
|
302 if ( tok.count()<6 ) { |
|
303 *errorMessage = DeviceSkin::tr("Syntax error in area definition: %1").arg(line); |
|
304 return false; |
|
305 } else { |
|
306 area.name = tok[0]; |
|
307 QString k = tok[1]; |
|
308 if ( k.left(2).toLower() == QLatin1String("0x")) { |
|
309 area.keyCode = k.mid(2).toInt(0,16); |
|
310 } else { |
|
311 area.keyCode = k.toInt(); |
|
312 } |
|
313 |
|
314 int p=0; |
|
315 for (int j=2; j < tok.count() - 1; ) { |
|
316 const int x = tok[j++].toInt(); |
|
317 const int y = tok[j++].toInt(); |
|
318 area.area.putPoints(p++,1,x,y); |
|
319 } |
|
320 |
|
321 const QChar doubleQuote = QLatin1Char('"'); |
|
322 if ( area.name[0] == doubleQuote && area.name.endsWith(doubleQuote)) { |
|
323 area.name.truncate(area.name.size() - 1); |
|
324 area.name.remove(0, 1); |
|
325 } |
|
326 if ( area.name.length() == 1 ) |
|
327 area.text = area.name; |
|
328 if ( area.name == Joystick) |
|
329 joystick = i; |
|
330 area.activeWhenClosed = closedAreas.contains(area.name) |
|
331 || area.keyCode == Qt::Key_Flip; // must be to work |
|
332 area.toggleArea = toggleAreas.contains(area.name); |
|
333 area.toggleActiveArea = toggleActiveAreas.contains(area.name); |
|
334 if ( area.toggleArea ) |
|
335 toggleAreaList += i; |
|
336 i++; |
|
337 } |
|
338 } |
|
339 } |
|
340 if (i != nareas) { |
|
341 qWarning() << DeviceSkin::tr("Mismatch in number of areas, expected %1, got %2.") |
|
342 .arg(nareas).arg(i); |
|
343 } |
|
344 if (debugDeviceSkin) |
|
345 qDebug() << *this; |
|
346 return true; |
|
347 } |
|
348 |
|
349 // --------- CursorWindow declaration |
|
350 |
|
351 namespace qvfb_internal { |
|
352 |
|
353 class CursorWindow : public QWidget |
|
354 { |
|
355 public: |
|
356 explicit CursorWindow(const QImage &cursor, QPoint hot, QWidget *sk); |
|
357 |
|
358 void setView(QWidget*); |
|
359 void setPos(QPoint); |
|
360 bool handleMouseEvent(QEvent *ev); |
|
361 |
|
362 protected: |
|
363 bool event( QEvent *); |
|
364 bool eventFilter( QObject*, QEvent *); |
|
365 |
|
366 private: |
|
367 QWidget *mouseRecipient; |
|
368 QWidget *m_view; |
|
369 QWidget *skin; |
|
370 QPoint hotspot; |
|
371 }; |
|
372 } |
|
373 |
|
374 // --------- Skin |
|
375 |
|
376 DeviceSkin::DeviceSkin(const DeviceSkinParameters ¶meters, QWidget *p ) : |
|
377 QWidget(p), |
|
378 m_parameters(parameters), |
|
379 buttonRegions(parameters.buttonAreas.size(), QRegion()), |
|
380 parent(p), |
|
381 m_view(0), |
|
382 m_secondaryView(0), |
|
383 buttonPressed(false), |
|
384 buttonIndex(0), |
|
385 cursorw(0), |
|
386 joydown(0), |
|
387 t_skinkey(new QTimer(this)), |
|
388 t_parentmove(new QTimer(this)), |
|
389 flipped_open(true) |
|
390 { |
|
391 Q_ASSERT(p); |
|
392 setMouseTracking(true); |
|
393 setAttribute(Qt::WA_NoSystemBackground); |
|
394 |
|
395 setZoom(1.0); |
|
396 connect( t_skinkey, SIGNAL(timeout()), this, SLOT(skinKeyRepeat()) ); |
|
397 t_parentmove->setSingleShot( true ); |
|
398 connect( t_parentmove, SIGNAL(timeout()), this, SLOT(moveParent()) ); |
|
399 } |
|
400 |
|
401 void DeviceSkin::skinKeyRepeat() |
|
402 { |
|
403 if ( m_view ) { |
|
404 const DeviceSkinButtonArea &area = m_parameters.buttonAreas[buttonIndex]; |
|
405 emit skinKeyReleaseEvent( area.keyCode,area.text, true ); |
|
406 emit skinKeyPressEvent( area.keyCode, area.text, true ); |
|
407 t_skinkey->start(key_repeat_period); |
|
408 } |
|
409 } |
|
410 |
|
411 void DeviceSkin::calcRegions() |
|
412 { |
|
413 const int numAreas = m_parameters.buttonAreas.size(); |
|
414 for (int i=0; i<numAreas; i++) { |
|
415 QPolygon xa(m_parameters.buttonAreas[i].area.count()); |
|
416 int n = m_parameters.buttonAreas[i].area.count(); |
|
417 for (int p=0; p<n; p++) { |
|
418 xa.setPoint(p,transform.map(m_parameters.buttonAreas[i].area[p])); |
|
419 } |
|
420 if ( n == 2 ) { |
|
421 buttonRegions[i] = QRegion(xa.boundingRect()); |
|
422 } else { |
|
423 buttonRegions[i] = QRegion(xa); |
|
424 } |
|
425 } |
|
426 } |
|
427 |
|
428 void DeviceSkin::loadImages() |
|
429 { |
|
430 QImage iup = m_parameters.skinImageUp; |
|
431 QImage idown = m_parameters.skinImageDown; |
|
432 |
|
433 QImage iclosed; |
|
434 const bool hasClosedImage = !m_parameters.skinImageClosed.isNull(); |
|
435 |
|
436 if (hasClosedImage) |
|
437 iclosed = m_parameters.skinImageClosed; |
|
438 QImage icurs; |
|
439 const bool hasCursorImage = !m_parameters.skinCursor.isNull(); |
|
440 if (hasCursorImage) |
|
441 icurs = m_parameters.skinCursor; |
|
442 |
|
443 if (!transform.isIdentity()) { |
|
444 iup = iup.transformed(transform, Qt::SmoothTransformation); |
|
445 idown = idown.transformed(transform, Qt::SmoothTransformation); |
|
446 if (hasClosedImage) |
|
447 iclosed = iclosed.transformed(transform, Qt::SmoothTransformation); |
|
448 if (hasCursorImage) |
|
449 icurs = icurs.transformed(transform, Qt::SmoothTransformation); |
|
450 } |
|
451 const Qt::ImageConversionFlags conv = Qt::ThresholdAlphaDither|Qt::AvoidDither; |
|
452 skinImageUp = QPixmap::fromImage(iup); |
|
453 skinImageDown = QPixmap::fromImage(idown, conv); |
|
454 if (hasClosedImage) |
|
455 skinImageClosed = QPixmap::fromImage(iclosed, conv); |
|
456 if (hasCursorImage) |
|
457 skinCursor = QPixmap::fromImage(icurs, conv); |
|
458 |
|
459 setFixedSize( skinImageUp.size() ); |
|
460 if (!skinImageUp.mask()) |
|
461 skinImageUp.setMask(skinImageUp.createHeuristicMask()); |
|
462 if (!skinImageClosed.mask()) |
|
463 skinImageClosed.setMask(skinImageClosed.createHeuristicMask()); |
|
464 |
|
465 QWidget* parent = parentWidget(); |
|
466 parent->setMask( skinImageUp.mask() ); |
|
467 parent->setFixedSize( skinImageUp.size() ); |
|
468 |
|
469 delete cursorw; |
|
470 cursorw = 0; |
|
471 if (hasCursorImage) { |
|
472 cursorw = new qvfb_internal::CursorWindow(m_parameters.skinCursor, m_parameters.cursorHot, this); |
|
473 if ( m_view ) |
|
474 cursorw->setView(m_view); |
|
475 } |
|
476 } |
|
477 |
|
478 DeviceSkin::~DeviceSkin( ) |
|
479 { |
|
480 delete cursorw; |
|
481 } |
|
482 |
|
483 void DeviceSkin::setTransform( const QMatrix& wm ) |
|
484 { |
|
485 transform = QImage::trueMatrix(wm,m_parameters.skinImageUp.width(),m_parameters.skinImageUp.height()); |
|
486 calcRegions(); |
|
487 loadImages(); |
|
488 if ( m_view ) { |
|
489 QPoint p = transform.map(QPolygon(m_parameters.screenRect)).boundingRect().topLeft(); |
|
490 m_view->move(p); |
|
491 } |
|
492 updateSecondaryScreen(); |
|
493 } |
|
494 |
|
495 void DeviceSkin::setZoom( double z ) |
|
496 { |
|
497 setTransform(QMatrix().scale(z,z)); |
|
498 } |
|
499 |
|
500 void DeviceSkin::updateSecondaryScreen() |
|
501 { |
|
502 if (!m_secondaryView) |
|
503 return; |
|
504 if (flipped_open) { |
|
505 if (m_parameters.backScreenRect.isNull()) { |
|
506 m_secondaryView->hide(); |
|
507 } else { |
|
508 m_secondaryView->move(transform.map(QPolygon(m_parameters.backScreenRect)).boundingRect().topLeft()); |
|
509 m_secondaryView->show(); |
|
510 } |
|
511 } else { |
|
512 if (m_parameters.closedScreenRect.isNull()) { |
|
513 m_secondaryView->hide(); |
|
514 } else { |
|
515 m_secondaryView->move(transform.map(QPolygon(m_parameters.closedScreenRect)).boundingRect().topLeft()); |
|
516 m_secondaryView->show(); |
|
517 } |
|
518 } |
|
519 } |
|
520 |
|
521 void DeviceSkin::setView( QWidget *v ) |
|
522 { |
|
523 m_view = v; |
|
524 m_view->setFocus(); |
|
525 m_view->move(transform.map(QPolygon(m_parameters.screenRect)).boundingRect().topLeft()); |
|
526 if ( cursorw ) |
|
527 cursorw->setView(v); |
|
528 } |
|
529 |
|
530 void DeviceSkin::setSecondaryView( QWidget *v ) |
|
531 { |
|
532 m_secondaryView = v; |
|
533 updateSecondaryScreen(); |
|
534 } |
|
535 |
|
536 void DeviceSkin::paintEvent( QPaintEvent *) |
|
537 { |
|
538 QPainter p( this ); |
|
539 if ( flipped_open ) { |
|
540 p.drawPixmap( 0, 0, skinImageUp ); |
|
541 } else { |
|
542 p.drawPixmap( 0, 0, skinImageClosed ); |
|
543 } |
|
544 QList<int> toDraw; |
|
545 if ( buttonPressed == true ) { |
|
546 toDraw += buttonIndex; |
|
547 } |
|
548 foreach (int toggle, m_parameters.toggleAreaList) { |
|
549 const DeviceSkinButtonArea &ba = m_parameters.buttonAreas[toggle]; |
|
550 if ( flipped_open || ba.activeWhenClosed ) { |
|
551 if ( ba.toggleArea && ba.toggleActiveArea ) |
|
552 toDraw += toggle; |
|
553 } |
|
554 } |
|
555 foreach (int button, toDraw ) { |
|
556 const DeviceSkinButtonArea &ba = m_parameters.buttonAreas[button]; |
|
557 const QRect r = buttonRegions[button].boundingRect(); |
|
558 if ( ba.area.count() > 2 ) |
|
559 p.setClipRegion(buttonRegions[button]); |
|
560 p.drawPixmap( r.topLeft(), skinImageDown, r); |
|
561 } |
|
562 } |
|
563 |
|
564 void DeviceSkin::mousePressEvent( QMouseEvent *e ) |
|
565 { |
|
566 if (e->button() == Qt::RightButton) { |
|
567 emit popupMenu(); |
|
568 } else { |
|
569 buttonPressed = false; |
|
570 |
|
571 onjoyrelease = -1; |
|
572 const int numAreas = m_parameters.buttonAreas.size(); |
|
573 for (int i = 0; i < numAreas ; i++) { |
|
574 const DeviceSkinButtonArea &ba = m_parameters.buttonAreas[i]; |
|
575 if ( buttonRegions[i].contains( e->pos() ) ) { |
|
576 if ( flipped_open || ba.activeWhenClosed ) { |
|
577 if ( m_parameters.joystick == i ) { |
|
578 joydown = true; |
|
579 } else { |
|
580 if ( joydown ) |
|
581 onjoyrelease = i; |
|
582 else |
|
583 startPress(i); |
|
584 break; |
|
585 if (debugDeviceSkin)// Debug message to be sure we are clicking the right areas |
|
586 qDebug()<< m_parameters.buttonAreas[i].name << " clicked"; |
|
587 } |
|
588 } |
|
589 } |
|
590 } |
|
591 clickPos = e->pos(); |
|
592 // This is handy for finding the areas to define rectangles for new skins |
|
593 if (debugDeviceSkin) |
|
594 qDebug()<< "Clicked in " << e->pos().x() << ',' << e->pos().y(); |
|
595 clickPos = e->pos(); |
|
596 } |
|
597 } |
|
598 |
|
599 void DeviceSkin::flip(bool open) |
|
600 { |
|
601 if ( flipped_open == open ) |
|
602 return; |
|
603 if ( open ) { |
|
604 parent->setMask( skinImageUp.mask() ); |
|
605 emit skinKeyReleaseEvent( Qt::Key(Qt::Key_Flip), QString(), false); |
|
606 } else { |
|
607 parent->setMask( skinImageClosed.mask() ); |
|
608 emit skinKeyPressEvent( Qt::Key(Qt::Key_Flip), QString(), false); |
|
609 } |
|
610 flipped_open = open; |
|
611 updateSecondaryScreen(); |
|
612 repaint(); |
|
613 } |
|
614 |
|
615 void DeviceSkin::startPress(int i) |
|
616 { |
|
617 buttonPressed = true; |
|
618 buttonIndex = i; |
|
619 if (m_view) { |
|
620 const DeviceSkinButtonArea &ba = m_parameters.buttonAreas[buttonIndex]; |
|
621 if ( ba.keyCode == Qt::Key_Flip ) { |
|
622 flip(!flipped_open); |
|
623 } else if ( ba.toggleArea ) { |
|
624 bool active = !ba.toggleActiveArea; |
|
625 const_cast<DeviceSkinButtonArea &>(ba).toggleActiveArea = active; |
|
626 if ( active ) |
|
627 emit skinKeyPressEvent( ba.keyCode, ba.text, false); |
|
628 else |
|
629 emit skinKeyReleaseEvent( ba.keyCode, ba.text, false); |
|
630 } else { |
|
631 emit skinKeyPressEvent( ba.keyCode, ba.text, false); |
|
632 t_skinkey->start(key_repeat_delay); |
|
633 } |
|
634 repaint( buttonRegions[buttonIndex].boundingRect() ); |
|
635 } |
|
636 } |
|
637 |
|
638 void DeviceSkin::endPress() |
|
639 { |
|
640 const DeviceSkinButtonArea &ba = m_parameters.buttonAreas[buttonIndex]; |
|
641 if (m_view && ba.keyCode != Qt::Key_Flip && !ba.toggleArea ) |
|
642 emit skinKeyReleaseEvent(ba.keyCode, ba.text, false ); |
|
643 t_skinkey->stop(); |
|
644 buttonPressed = false; |
|
645 repaint( buttonRegions[buttonIndex].boundingRect() ); |
|
646 } |
|
647 |
|
648 void DeviceSkin::mouseMoveEvent( QMouseEvent *e ) |
|
649 { |
|
650 if ( e->buttons() & Qt::LeftButton ) { |
|
651 const int joystick = m_parameters.joystick; |
|
652 QPoint newpos = e->globalPos() - clickPos; |
|
653 if ( joydown ) { |
|
654 int k1=0, k2=0; |
|
655 if ( newpos.x() < -joydistance ) { |
|
656 k1 = joystick+1; |
|
657 } else if ( newpos.x() > +joydistance ) { |
|
658 k1 = joystick+3; |
|
659 } |
|
660 if ( newpos.y() < -joydistance ) { |
|
661 k2 = joystick+2; |
|
662 } else if ( newpos.y() > +joydistance ) { |
|
663 k2 = joystick+4; |
|
664 } |
|
665 if ( k1 || k2 ) { |
|
666 if ( !buttonPressed ) { |
|
667 onjoyrelease = -1; |
|
668 if ( k1 && k2 ) { |
|
669 startPress(k2); |
|
670 endPress(); |
|
671 } |
|
672 startPress(k1 ? k1 : k2); |
|
673 } |
|
674 } else if ( buttonPressed ) { |
|
675 endPress(); |
|
676 } |
|
677 } else if ( buttonPressed == false ) { |
|
678 parentpos = newpos; |
|
679 if ( !t_parentmove->isActive() ) |
|
680 t_parentmove->start(50); |
|
681 } |
|
682 } |
|
683 if ( cursorw ) |
|
684 cursorw->setPos(e->globalPos()); |
|
685 } |
|
686 |
|
687 void DeviceSkin::moveParent() |
|
688 { |
|
689 parent->move( parentpos ); |
|
690 } |
|
691 |
|
692 void DeviceSkin::mouseReleaseEvent( QMouseEvent * ) |
|
693 { |
|
694 if ( buttonPressed ) |
|
695 endPress(); |
|
696 if ( joydown ) { |
|
697 joydown = false; |
|
698 if ( onjoyrelease >= 0 ) { |
|
699 startPress(onjoyrelease); |
|
700 endPress(); |
|
701 } |
|
702 } |
|
703 } |
|
704 |
|
705 bool DeviceSkin::hasCursor() const |
|
706 { |
|
707 return !skinCursor.isNull(); |
|
708 } |
|
709 |
|
710 // ------------------ CursorWindow implementation |
|
711 |
|
712 namespace qvfb_internal { |
|
713 |
|
714 bool CursorWindow::eventFilter( QObject *, QEvent *ev) |
|
715 { |
|
716 handleMouseEvent(ev); |
|
717 return false; |
|
718 } |
|
719 |
|
720 bool CursorWindow::event( QEvent *ev ) |
|
721 { |
|
722 if (handleMouseEvent(ev)) |
|
723 return true; |
|
724 return QWidget::event(ev); |
|
725 } |
|
726 |
|
727 bool CursorWindow::handleMouseEvent(QEvent *ev) |
|
728 { |
|
729 bool handledEvent = false; |
|
730 static int inhere=0; |
|
731 if ( !inhere ) { |
|
732 inhere++; |
|
733 if ( m_view ) { |
|
734 if ( ev->type() >= QEvent::MouseButtonPress && ev->type() <= QEvent::MouseMove ) { |
|
735 QMouseEvent *e = (QMouseEvent*)ev; |
|
736 QPoint gp = e->globalPos(); |
|
737 QPoint vp = m_view->mapFromGlobal(gp); |
|
738 QPoint sp = skin->mapFromGlobal(gp); |
|
739 if ( e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonDblClick ) { |
|
740 if ( m_view->rect().contains(vp) ) |
|
741 mouseRecipient = m_view; |
|
742 else if ( skin->parentWidget()->geometry().contains(gp) ) |
|
743 mouseRecipient = skin; |
|
744 else |
|
745 mouseRecipient = 0; |
|
746 } |
|
747 if ( mouseRecipient ) { |
|
748 setPos(gp); |
|
749 QMouseEvent me(e->type(),mouseRecipient==skin ? sp : vp,gp,e->button(),e->buttons(),e->modifiers()); |
|
750 QApplication::sendEvent(mouseRecipient, &me); |
|
751 } else if ( !skin->parentWidget()->geometry().contains(gp) ) { |
|
752 hide(); |
|
753 } else { |
|
754 setPos(gp); |
|
755 } |
|
756 if ( e->type() == QEvent::MouseButtonRelease ) |
|
757 mouseRecipient = 0; |
|
758 handledEvent = true; |
|
759 } |
|
760 } |
|
761 inhere--; |
|
762 } |
|
763 return handledEvent; |
|
764 } |
|
765 |
|
766 void CursorWindow::setView(QWidget* v) |
|
767 { |
|
768 if ( m_view ) { |
|
769 m_view->removeEventFilter(this); |
|
770 m_view->removeEventFilter(this); |
|
771 } |
|
772 m_view = v; |
|
773 m_view->installEventFilter(this); |
|
774 m_view->installEventFilter(this); |
|
775 mouseRecipient = 0; |
|
776 } |
|
777 |
|
778 CursorWindow::CursorWindow(const QImage &img, QPoint hot, QWidget* sk) |
|
779 :QWidget(0), |
|
780 m_view(0), skin(sk), |
|
781 hotspot(hot) |
|
782 { |
|
783 setWindowFlags( Qt::FramelessWindowHint ); |
|
784 mouseRecipient = 0; |
|
785 setMouseTracking(true); |
|
786 #ifndef QT_NO_CURSOR |
|
787 setCursor(Qt::BlankCursor); |
|
788 #endif |
|
789 QPixmap p; |
|
790 p = QPixmap::fromImage(img); |
|
791 if (!p.mask()) { |
|
792 if ( img.hasAlphaChannel() ) { |
|
793 QBitmap bm; |
|
794 bm = QPixmap::fromImage(img.createAlphaMask()); |
|
795 p.setMask( bm ); |
|
796 } else { |
|
797 QBitmap bm; |
|
798 bm = QPixmap::fromImage(img.createHeuristicMask()); |
|
799 p.setMask( bm ); |
|
800 } |
|
801 } |
|
802 QPalette palette; |
|
803 palette.setBrush(backgroundRole(), QBrush(p)); |
|
804 setPalette(palette); |
|
805 setFixedSize( p.size() ); |
|
806 if ( !p.mask().isNull() ) |
|
807 setMask( p.mask() ); |
|
808 } |
|
809 |
|
810 void CursorWindow::setPos(QPoint p) |
|
811 { |
|
812 move(p-hotspot); |
|
813 show(); |
|
814 raise(); |
|
815 } |
|
816 } |
|
817 |
|
818 #ifdef TEST_SKIN |
|
819 |
|
820 int main(int argc,char *argv[]) |
|
821 { |
|
822 if (argc < 1) |
|
823 return 1; |
|
824 const QString skinFile = QString::fromUtf8(argv[1]); |
|
825 QApplication app(argc,argv); |
|
826 QMainWindow mw; |
|
827 |
|
828 DeviceSkinParameters params; |
|
829 QString errorMessage; |
|
830 if (!params.read(skinFile, DeviceSkinParameters::ReadAll, &errorMessage)) { |
|
831 qWarning() << errorMessage; |
|
832 return 1; |
|
833 } |
|
834 DeviceSkin ds(params, &mw); |
|
835 // View Dialog |
|
836 QDialog *dialog = new QDialog(); |
|
837 QHBoxLayout *dialogLayout = new QHBoxLayout(); |
|
838 dialog->setLayout(dialogLayout); |
|
839 QDialogButtonBox *dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); |
|
840 QObject::connect(dialogButtonBox, SIGNAL(rejected()), dialog, SLOT(reject())); |
|
841 QObject::connect(dialogButtonBox, SIGNAL(accepted()), dialog, SLOT(accept())); |
|
842 dialogLayout->addWidget(dialogButtonBox); |
|
843 dialog->setFixedSize(params.screenSize()); |
|
844 dialog->setParent(&ds, Qt::SubWindow); |
|
845 dialog->setAutoFillBackground(true); |
|
846 ds.setView(dialog); |
|
847 |
|
848 QObject::connect(&ds, SIGNAL(popupMenu()), &mw, SLOT(close())); |
|
849 QObject::connect(&ds, SIGNAL(skinKeyPressEvent(int,QString,bool)), &mw, SLOT(close())); |
|
850 mw.show(); |
|
851 return app.exec(); |
|
852 } |
|
853 |
|
854 #endif |
|
855 |
|
856 QT_END_NAMESPACE |
|
857 |