tools/shared/deviceskin/deviceskin.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     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 &parameters,  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