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