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
64QT_BEGIN_NAMESPACE
65
66namespace {
67 enum { joydistance = 10, key_repeat_period = 50, key_repeat_delay = 500 };
68 enum { debugDeviceSkin = 0 };
69}
70
71static void parseRect(const QString &value, QRect *rect) {
72 const auto l = value.splitRef(sep: QLatin1Char(' '));
73 rect->setRect(ax: l[0].toInt(), ay: l[1].toInt(), aw: l[2].toInt(), ah: l[3].toInt());
74}
75
76static QString msgImageNotLoaded(const QString &f) {
77 return DeviceSkin::tr(s: "The image file '%1' could not be loaded.").arg(a: f);
78}
79
80// ------------ DeviceSkinButtonArea
81QDebug &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
91QDebug 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
105QSize DeviceSkinParameters::secondaryScreenSize() const
106{
107 return backScreenRect.isNull() ? closedScreenRect .size(): backScreenRect.size();
108}
109
110bool DeviceSkinParameters::hasSecondaryScreen() const
111{
112 return secondaryScreenSize() != QSize(0, 0);
113}
114
115bool 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(c: QLatin1Char('/')))
120 skinFile.truncate(pos: 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(s: "The skin directory '%1' does not contain a configuration file.").arg(a: skinDirectory);
136 return false;
137 }
138 QFile f(fn);
139 if (!f.open(flags: QIODevice::ReadOnly )) {
140 *errorMessage = DeviceSkin::tr(s: "The skin configuration file '%1' could not be opened.").arg(a: fn);
141 return false;
142 }
143 QTextStream ts(&f);
144 const bool rc = read(ts, rm, errorMessage);
145 if (!rc)
146 *errorMessage = DeviceSkin::tr(s: "The skin configuration file '%1' could not be read: %2").arg(a: fn).arg(a: *errorMessage);
147 return rc;
148}
149bool 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(c: QLatin1Char('='));
180 if ( eq >= 0 ) {
181 const QString key = line.left(n: eq);
182 eq++;
183 while (eq<line.length()-1 && line[eq].isSpace())
184 eq++;
185 const QString value = line.mid(position: 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(sep: QLatin1Char(' '));
194 } else if ( key == ScreenKey ) {
195 parseRect( value, rect: &screenRect);
196 } else if ( key == ScreenDepthKey ) {
197 screenDepth = value.toInt();
198 } else if ( key == BackScreenKey ) {
199 parseRect(value, rect: &backScreenRect);
200 } else if ( key == ClosedScreenKey ) {
201 parseRect( value, rect: &closedScreenRect );
202 } else if ( key == CursorKey ) {
203 QStringList l = value.split(sep: 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(sep: QLatin1Char(' '));
210 } else if ( key == ToggleActiveAreasKey ) {
211 toggleActiveAreas = value.split(sep: QLatin1Char(' '));
212 } else if ( key == HasMouseHoverKey ) {
213 hasMouseHover = value == QLatin1String("true") || value == QLatin1String("1");
214 }
215 } else {
216 *errorMessage = DeviceSkin::tr(s: "Syntax error: %1").arg(a: 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(ax: x, ay: y, aw: w, ah: 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(i: 0, s: prefix);
236 if (!QFile(skinImageUpFileName).exists()) {
237 *errorMessage = DeviceSkin::tr(s: "The skin \"up\" image file '%1' does not exist.").arg(a: skinImageUpFileName);
238 return false;
239 }
240 if (!skinImageUp.load(fileName: skinImageUpFileName)) {
241 *errorMessage = msgImageNotLoaded(f: skinImageUpFileName);
242 return false;
243 }
244
245 skinImageDownFileName.insert(i: 0, s: prefix);
246 if (!QFile(skinImageDownFileName).exists()) {
247 *errorMessage = DeviceSkin::tr(s: "The skin \"down\" image file '%1' does not exist.").arg(a: skinImageDownFileName);
248 return false;
249 }
250 if (!skinImageDown.load(fileName: skinImageDownFileName)) {
251 *errorMessage = msgImageNotLoaded(f: skinImageDownFileName);
252 return false;
253 }
254
255 if (!skinImageClosedFileName.isEmpty()) {
256 skinImageClosedFileName.insert(i: 0, s: prefix);
257 if (!QFile(skinImageClosedFileName).exists()) {
258 *errorMessage = DeviceSkin::tr(s: "The skin \"closed\" image file '%1' does not exist.").arg(a: skinImageClosedFileName);
259 return false;
260 }
261 if (!skinImageClosed.load(fileName: skinImageClosedFileName)) {
262 *errorMessage = msgImageNotLoaded(f: skinImageClosedFileName);
263 return false;
264 }
265 }
266
267 if (!skinCursorFileName.isEmpty()) {
268 skinCursorFileName.insert(i: 0, s: prefix);
269 if (!QFile(skinCursorFileName).exists()) {
270 *errorMessage = DeviceSkin::tr(s: "The skin cursor image file '%1' does not exist.").arg(a: skinCursorFileName);
271 return false;
272 }
273 if (!skinCursor.load(fileName: skinCursorFileName)) {
274 *errorMessage = msgImageNotLoaded(f: skinCursorFileName);
275 return false;
276 }
277 }
278
279 // read areas
280 if (!nareas)
281 return true;
282 buttonAreas.reserve(asize: 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(t: 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(sep: splitRe);
296 if ( tok.count()<6 ) {
297 *errorMessage = DeviceSkin::tr(s: "Syntax error in area definition: %1").arg(a: line);
298 return false;
299 } else {
300 area.name = tok[0];
301 QString k = tok[1];
302 if ( k.left(n: 2).toLower() == QLatin1String("0x")) {
303 area.keyCode = k.mid(position: 2).toInt(ok: 0,base: 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(index: p++,nPoints: 1,firstx: x,firsty: y);
313 }
314
315 const QChar doubleQuote = QLatin1Char('"');
316 if ( area.name[0] == doubleQuote && area.name.endsWith(c: doubleQuote)) {
317 area.name.truncate(pos: area.name.size() - 1);
318 area.name.remove(i: 0, len: 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(str: area.name)
325 || area.keyCode == Qt::Key_Flip; // must be to work
326 area.toggleArea = toggleAreas.contains(str: area.name);
327 area.toggleActiveArea = toggleActiveAreas.contains(str: area.name);
328 if (area.toggleArea)
329 toggleAreaList += i;
330 i++;
331 }
332 }
333 }
334 if (i != nareas) {
335 qWarning() << DeviceSkin::tr(s: "Mismatch in number of areas, expected %1, got %2.")
336 .arg(a: nareas).arg(a: i);
337 }
338 if (debugDeviceSkin)
339 qDebug() << *this;
340 return true;
341}
342
343// --------- CursorWindow declaration
344
345namespace qvfb_internal {
346
347class CursorWindow : public QWidget
348{
349public:
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
356protected:
357 bool event( QEvent *);
358 bool eventFilter( QObject*, QEvent *);
359
360private:
361 QWidget *mouseRecipient;
362 QWidget *m_view;
363 QWidget *skin;
364 QPoint hotspot;
365};
366}
367
368// --------- Skin
369
370DeviceSkin::DeviceSkin(const DeviceSkinParameters &parameters, 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(sender: t_skinkey, signal: &QTimer::timeout, receiver: this, slot: &DeviceSkin::skinKeyRepeat );
391 t_parentmove->setSingleShot( true );
392 connect(sender: t_parentmove, signal: &QTimer::timeout, receiver: this, slot: &DeviceSkin::moveParent );
393}
394
395void DeviceSkin::skinKeyRepeat()
396{
397 if ( m_view ) {
398 const DeviceSkinButtonArea &area = m_parameters.buttonAreas[buttonIndex];
399 emit skinKeyReleaseEvent(code: area.keyCode,text: area.text, autorep: true);
400 emit skinKeyPressEvent(code: area.keyCode, text: area.text, autorep: true);
401 t_skinkey->start(msec: key_repeat_period);
402 }
403}
404
405void 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(index: p,pt: transform.map(p: 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
422void 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(matrix: transform, mode: Qt::SmoothTransformation);
439 idown = idown.transformed(matrix: transform, mode: Qt::SmoothTransformation);
440 if (hasClosedImage)
441 iclosed = iclosed.transformed(matrix: transform, mode: Qt::SmoothTransformation);
442 if (hasCursorImage)
443 icurs = icurs.transformed(matrix: transform, mode: Qt::SmoothTransformation);
444 }
445 const Qt::ImageConversionFlags conv = Qt::ThresholdAlphaDither|Qt::AvoidDither;
446 skinImageUp = QPixmap::fromImage(image: iup);
447 skinImageDown = QPixmap::fromImage(image: idown, flags: conv);
448 if (hasClosedImage)
449 skinImageClosed = QPixmap::fromImage(image: iclosed, flags: conv);
450 if (hasCursorImage)
451 skinCursor = QPixmap::fromImage(image: icurs, flags: 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
472DeviceSkin::~DeviceSkin( )
473{
474 delete cursorw;
475}
476
477void DeviceSkin::setTransform(const QTransform &wm)
478{
479 transform = QImage::trueMatrix(wm,w: m_parameters.skinImageUp.width(),h: m_parameters.skinImageUp.height());
480 calcRegions();
481 loadImages();
482 if ( m_view ) {
483 QPoint p = transform.map(a: QPolygon(m_parameters.screenRect)).boundingRect().topLeft();
484 m_view->move(p);
485 }
486 updateSecondaryScreen();
487}
488
489void DeviceSkin::setZoom( double z )
490{
491 setTransform(QTransform().scale(sx: z,sy: z));
492}
493
494void 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(a: 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(a: QPolygon(m_parameters.closedScreenRect)).boundingRect().topLeft());
510 m_secondaryView->show();
511 }
512 }
513}
514
515void DeviceSkin::setView( QWidget *v )
516{
517 m_view = v;
518 m_view->setFocus();
519 m_view->move(transform.map(a: QPolygon(m_parameters.screenRect)).boundingRect().topLeft());
520 if ( cursorw )
521 cursorw->setView(v);
522}
523
524void DeviceSkin::setSecondaryView( QWidget *v )
525{
526 m_secondaryView = v;
527 updateSecondaryScreen();
528}
529
530void DeviceSkin::paintEvent( QPaintEvent *)
531{
532 QPainter p( this );
533 if ( flipped_open ) {
534 p.drawPixmap(x: 0, y: 0, pm: skinImageUp);
535 } else {
536 p.drawPixmap(x: 0, y: 0, pm: skinImageClosed);
537 }
538 QVector<int> toDraw;
539 if ( buttonPressed == true ) {
540 toDraw += buttonIndex;
541 }
542 for (int toggle : qAsConst(t: 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(t&: 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( p: r.topLeft(), pm: skinImageDown, sr: r);
555 }
556}
557
558void 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( p: 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
593void DeviceSkin::flip(bool open)
594{
595 if ( flipped_open == open )
596 return;
597 if ( open ) {
598 parent->setMask(skinImageUp.mask());
599 emit skinKeyReleaseEvent(code: Qt::Key(Qt::Key_Flip), text: QString(), autorep: false);
600 } else {
601 parent->setMask(skinImageClosed.mask());
602 emit skinKeyPressEvent(code: Qt::Key(Qt::Key_Flip), text: QString(), autorep: false);
603 }
604 flipped_open = open;
605 updateSecondaryScreen();
606 repaint();
607}
608
609void 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(open: !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(code: ba.keyCode, text: ba.text, autorep: false);
622 else
623 emit skinKeyReleaseEvent(code: ba.keyCode, text: ba.text, autorep: false);
624 } else {
625 emit skinKeyPressEvent(code: ba.keyCode, text: ba.text, autorep: false);
626 t_skinkey->start(msec: key_repeat_delay);
627 }
628 repaint(buttonRegions[buttonIndex].boundingRect());
629 }
630}
631
632void 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(code: ba.keyCode, text: ba.text, autorep: false);
637 t_skinkey->stop();
638 buttonPressed = false;
639 repaint( buttonRegions[buttonIndex].boundingRect() );
640}
641
642void 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(i: k2);
664 endPress();
665 }
666 startPress(i: 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(msec: 50);
675 }
676 }
677 if ( cursorw )
678 cursorw->setPos(e->globalPos());
679}
680
681void DeviceSkin::moveParent()
682{
683 parent->move( parentpos );
684}
685
686void DeviceSkin::mouseReleaseEvent( QMouseEvent * )
687{
688 if ( buttonPressed )
689 endPress();
690 if ( joydown ) {
691 joydown = false;
692 if (onjoyrelease >= 0) {
693 startPress(i: onjoyrelease);
694 endPress();
695 }
696 }
697}
698
699bool DeviceSkin::hasCursor() const
700{
701 return !skinCursor.isNull();
702}
703
704// ------------------ CursorWindow implementation
705
706namespace qvfb_internal {
707
708bool CursorWindow::eventFilter( QObject *, QEvent *ev)
709{
710 handleMouseEvent(ev);
711 return false;
712}
713
714bool CursorWindow::event( QEvent *ev )
715{
716 if (handleMouseEvent(ev))
717 return true;
718 return QWidget::event(event: ev);
719}
720
721bool 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(p: vp))
735 mouseRecipient = m_view;
736 else if (skin->parentWidget()->geometry().contains(p: 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(receiver: mouseRecipient, event: &me);
745 } else if (!skin->parentWidget()->geometry().contains(p: 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
760void CursorWindow::setView(QWidget* v)
761{
762 if ( m_view ) {
763 m_view->removeEventFilter(obj: this);
764 m_view->removeEventFilter(obj: this);
765 }
766 m_view = v;
767 m_view->installEventFilter(filterObj: this);
768 m_view->installEventFilter(filterObj: this);
769 mouseRecipient = 0;
770}
771
772CursorWindow::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(image: img);
786 if (!p.mask()) {
787 if (img.hasAlphaChannel()) {
788 QBitmap bm;
789 bm = QPixmap::fromImage(image: img.createAlphaMask());
790 p.setMask(bm);
791 } else {
792 QBitmap bm;
793 bm = QPixmap::fromImage(image: img.createHeuristicMask());
794 p.setMask(bm);
795 }
796 }
797 QPalette palette;
798 palette.setBrush(acr: backgroundRole(), abrush: QBrush(p));
799 setPalette(palette);
800 setFixedSize( p.size() );
801 if ( !p.mask().isNull() )
802 setMask(p.mask());
803}
804
805void CursorWindow::setPos(QPoint p)
806{
807 move(p-hotspot);
808 show();
809 raise();
810}
811}
812
813#ifdef TEST_SKIN
814
815int 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
851QT_END_NAMESPACE
852
853

source code of qttools/src/shared/deviceskin/deviceskin.cpp