Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/skin/legacyskinparser.cpp

Go to the documentation of this file.
00001 // legacyskinparser.cpp
00002 // Created 9/19/2010 by RJ Ryan (rryan@mit.edu)
00003 
00004 #include <QtGlobal>
00005 #include <QtDebug>
00006 #include <QDir>
00007 #include <QStackedWidget>
00008 #include <QVBoxLayout>
00009 #include <QLabel>
00010 #include <QGridLayout>
00011 #include <QMutexLocker>
00012 
00013 #include "controlobject.h"
00014 #include "controlobjectthreadwidget.h"
00015 
00016 #include "mixxxkeyboard.h"
00017 #include "playermanager.h"
00018 #include "basetrackplayer.h"
00019 #include "library/library.h"
00020 #include "waveformviewerfactory.h"
00021 #include "xmlparse.h"
00022 
00023 #include "skin/legacyskinparser.h"
00024 #include "skin/colorschemeparser.h"
00025 #include "skin/propertybinder.h"
00026 
00027 #include "widget/wwidget.h"
00028 #include "widget/wabstractcontrol.h"
00029 #include "widget/wknob.h"
00030 #include "widget/wslidercomposed.h"
00031 #include "widget/wpushbutton.h"
00032 #include "widget/wdisplay.h"
00033 #include "widget/wvumeter.h"
00034 #include "widget/wstatuslight.h"
00035 #include "widget/wlabel.h"
00036 #include "widget/wtime.h"
00037 #include "widget/wtracktext.h"
00038 #include "widget/wtrackproperty.h"
00039 #include "widget/wnumber.h"
00040 #include "widget/wnumberpos.h"
00041 #include "widget/wnumberrate.h"
00042 #include "widget/woverview.h"
00043 #include "widget/wspinny.h"
00044 
00045 #include "widget/wvisualsimple.h"
00046 #include "widget/wglwaveformviewer.h"
00047 #include "widget/wwaveformviewer.h"
00048 
00049 #include "widget/wsearchlineedit.h"
00050 #include "widget/wlibrary.h"
00051 #include "widget/wlibrarysidebar.h"
00052 
00053 #include "widget/wskincolor.h"
00054 #include "widget/wpixmapstore.h"
00055 
00056 QList<const char*> LegacySkinParser::s_channelStrs;
00057 QMutex LegacySkinParser::s_safeStringMutex;
00058 
00059 LegacySkinParser::LegacySkinParser(ConfigObject<ConfigValue>* pConfig,
00060                                    MixxxKeyboard* pKeyboard,
00061                                    PlayerManager* pPlayerManager,
00062                                    Library* pLibrary,
00063                                    VinylControlManager* pVCMan)
00064         : m_pConfig(pConfig),
00065           m_pKeyboard(pKeyboard),
00066           m_pPlayerManager(pPlayerManager),
00067           m_pLibrary(pLibrary),
00068           m_pVCManager(pVCMan),
00069           m_pParent(NULL) {
00070 
00071 }
00072 
00073 LegacySkinParser::~LegacySkinParser() {
00074 }
00075 
00076 bool LegacySkinParser::canParse(QString skinPath) {
00077     QDir skinDir(skinPath);
00078 
00079     if (!skinDir.exists()) {
00080         return false;
00081     }
00082 
00083     if (!skinDir.exists("skin.xml"))
00084         return false;
00085 
00086     // TODO check skin.xml for compliance
00087 
00088     return true;
00089 }
00090 
00091 // static
00092 QDomElement LegacySkinParser::openSkin(QString skinPath) {
00093     QDir skinDir(skinPath);
00094 
00095     if (!skinDir.exists()) {
00096         return QDomElement();
00097     }
00098 
00099     QString skinXmlPath = skinDir.filePath("skin.xml");
00100     QFile skinXmlFile(skinXmlPath);
00101 
00102     if (!skinXmlFile.open(QIODevice::ReadOnly)) {
00103         return QDomElement();
00104     }
00105 
00106     QDomDocument skin("skin");
00107 
00108     if (!skin.setContent(&skinXmlFile)) {
00109         return QDomElement();
00110     }
00111 
00112     // TODO(XXX) legacy
00113     WWidget::setPixmapPath(skinPath.append("/"));
00114 
00115     skinXmlFile.close();
00116     return skin.documentElement();
00117 }
00118 
00119 // static
00120 QList<QString> LegacySkinParser::getSchemeList(QString qSkinPath) {
00121 
00122     QDomElement docElem = openSkin(qSkinPath);
00123     QList<QString> schlist;
00124 
00125     QDomNode colsch = docElem.namedItem("Schemes");
00126     if (!colsch.isNull() && colsch.isElement()) {
00127         QDomNode sch = colsch.firstChild();
00128 
00129         while (!sch.isNull()) {
00130             QString thisname = XmlParse::selectNodeQString(sch, "Name");
00131             schlist.append(thisname);
00132             sch = sch.nextSibling();
00133         }
00134     }
00135 
00136     return schlist;
00137 }
00138 
00139 // static
00140 void LegacySkinParser::freeChannelStrings() {
00141     QMutexLocker lock(&s_safeStringMutex);
00142     for (int i = 0; i < s_channelStrs.length(); ++i) {
00143         if (s_channelStrs[i]) {
00144             delete [] s_channelStrs[i];
00145         }
00146         s_channelStrs[i] = NULL;
00147     }
00148 }
00149 
00150 bool LegacySkinParser::compareConfigKeys(QDomNode node, QString key)
00151 {
00152     QDomNode n = node;
00153 
00154     // Loop over each <Connection>, check if it's ConfigKey matches key
00155     while (!n.isNull())
00156     {
00157         n = XmlParse::selectNode(n, "Connection");
00158         if (!n.isNull())
00159         {
00160             if  (XmlParse::selectNodeQString(n, "ConfigKey").contains(key))
00161                 return true;
00162         }
00163     }
00164     return false;
00165 }
00166 
00167 void LegacySkinParser::setControlDefaults(QDomNode node, WAbstractControl* pControl) {
00168     if (compareConfigKeys(node, "[Master],headMix"))
00169     {
00170         pControl->setDefaultValue(0.);
00171     }
00172     else if (compareConfigKeys(node, "],volume") && // Matches [ChannelX],volume
00173              !compareConfigKeys(node, "[Master],volume"))
00174     {
00175         pControl->setDefaultValue(127.);
00176     }
00177 }
00178 
00179 QWidget* LegacySkinParser::parseSkin(QString skinPath, QWidget* pParent) {
00180     Q_ASSERT(!m_pParent);
00181     /*
00182      * Long explaination because this took too long to figure:
00183      * Parent all the mixxx widgets (subclasses of wwidget) to
00184      * some anonymous QWidget (this was MixxxView in <=1.8, MixxxView
00185      * was a subclass of QWidget), and then feed its parent to the background
00186      * tag parser. The background tag parser does stuff to the anon widget, as
00187      * everything else does, but then it colors the parent widget with some
00188      * color that shows itself in fullscreen. Having all these mixxx widgets
00189      * with their own means they're easy to move to boot -- bkgood
00190      */
00191     m_pParent = new QWidget; // this'll get deleted with pParent
00192     QDomElement skinDocument = openSkin(skinPath);
00193 
00194     if (skinDocument.isNull()) {
00195         // TODO error message
00196         qDebug() << "Could not load skin.";
00197         return NULL;
00198     }
00199 
00200     ColorSchemeParser::setupLegacyColorSchemes(skinDocument, m_pConfig);
00201 
00202     QStringList skinPaths(skinPath);
00203     QDir::setSearchPaths("skin", skinPaths);
00204 
00205     // don't parent till here so the first opengl waveform doesn't screw
00206     // up --bkgood
00207     // I'm disregarding this return value because I want to return the
00208     // created parent so MixxxApp can use it for nefarious purposes (
00209     // fullscreen mostly) --bkgood
00210     parseNode(skinDocument, pParent);
00211     m_pParent->setParent(pParent);
00212     return m_pParent;
00213 }
00214 
00215 QWidget* LegacySkinParser::parseNode(QDomElement node, QWidget *pGrandparent) {
00216     QString nodeName = node.nodeName();
00217     //qDebug() << "parseNode" << node.nodeName();
00218 
00219     // TODO(rryan) replace with a map to function pointers?
00220 
00221     // Root of the document
00222     if (nodeName == "skin") {
00223         // Descend chilren, should only happen for the root node
00224         QDomNodeList children = node.childNodes();
00225 
00226         for (int i = 0; i < children.count(); ++i) {
00227             QDomNode node = children.at(i);
00228 
00229             if (node.isElement()) {
00230                 parseNode(node.toElement(), pGrandparent);
00231             }
00232         }
00233         return pGrandparent;
00234     } else if (nodeName == "Background") {
00235         return parseBackground(node, pGrandparent);
00236     } else if (nodeName == "SliderComposed") {
00237         return parseSliderComposed(node);
00238     } else if (nodeName == "PushButton") {
00239         return parsePushButton(node);
00240     } else if (nodeName == "Overview") {
00241         return parseOverview(node);
00242     } else if (nodeName == "Visual") {
00243         return parseVisual(node);
00244     } else if (nodeName == "Text") {
00245         return parseText(node);
00246     } else if (nodeName == "TrackProperty") {
00247         return parseTrackProperty(node);
00248     } else if (nodeName == "VuMeter") {
00249         return parseVuMeter(node);
00250     } else if (nodeName == "StatusLight") {
00251         return parseStatusLight(node);
00252     } else if (nodeName == "Display") {
00253         return parseDisplay(node);
00254     } else if (nodeName == "NumberRate") {
00255         return parseNumberRate(node);
00256     } else if (nodeName == "NumberPos") {
00257         return parseNumberPos(node);
00258     } else if (nodeName == "Number" || nodeName == "NumberBpm") {
00259         // NumberBpm is deprecated, and is now the same as a Number
00260         return parseNumber(node);
00261     } else if (nodeName == "Label") {
00262         return parseLabel(node);
00263     } else if (nodeName == "Knob") {
00264         return parseKnob(node);
00265     } else if (nodeName == "TableView") {
00266         return parseTableView(node);
00267     } else if (nodeName == "WidgetGroup") {
00268         return parseWidgetGroup(node);
00269     } else if (nodeName == "Style") {
00270         return parseStyle(node);
00271     } else if (nodeName == "Spinny") {
00272         return parseSpinny(node);
00273     } else if (nodeName == "Time") {
00274         return parseTime(node);
00275     } else {
00276         qDebug() << "Invalid node name in skin:" << nodeName;
00277     }
00278 
00279     return NULL;
00280 }
00281 
00282 QWidget* LegacySkinParser::parseWidgetGroup(QDomElement node) {
00283     QWidget* pGroup = new QGroupBox(m_pParent);
00284     pGroup->setContentsMargins(0, 0, 0, 0);
00285     setupWidget(node, pGroup);
00286     setupConnections(node, pGroup);
00287 
00288     QBoxLayout* pLayout = NULL;
00289     if (!XmlParse::selectNode(node, "Layout").isNull()) {
00290         QString layout = XmlParse::selectNodeQString(node, "Layout");
00291         if (layout == "vertical") {
00292             pLayout = new QVBoxLayout();
00293             pLayout->setSpacing(0);
00294             pLayout->setContentsMargins(0, 0, 0, 0);
00295             pLayout->setAlignment(Qt::AlignCenter);
00296         } else if (layout == "horizontal") {
00297             pLayout = new QHBoxLayout();
00298             pLayout->setSpacing(0);
00299             pLayout->setContentsMargins(0, 0, 0, 0);
00300             pLayout->setAlignment(Qt::AlignCenter);
00301         }
00302     }
00303 
00304     QDomNode childrenNode = XmlParse::selectNode(node, "Children");
00305 
00306     QWidget* pOldParent = m_pParent;
00307     m_pParent = pGroup;
00308 
00309     if (!childrenNode.isNull()) {
00310         // Descend chilren
00311         QDomNodeList children = childrenNode.childNodes();
00312 
00313         for (int i = 0; i < children.count(); ++i) {
00314             QDomNode node = children.at(i);
00315 
00316             if (node.isElement()) {
00317                 QWidget* pChild = parseNode(node.toElement(), pGroup);
00318 
00319                 if (pChild == NULL)
00320                     continue;
00321 
00322                 if (pLayout) {
00323                     pLayout->addWidget(pChild);
00324                 }
00325             }
00326         }
00327     }
00328     m_pParent = pOldParent;
00329 
00330     if (pLayout) {
00331         pGroup->setLayout(pLayout);
00332     }
00333 
00334     return pGroup;
00335 }
00336 
00337 QWidget* LegacySkinParser::parseBackground(QDomElement node, QWidget* pGrandparent) {
00338     QLabel* bg = new QLabel(m_pParent);
00339 
00340     QString filename = XmlParse::selectNodeQString(node, "Path");
00341     QPixmap* background = WPixmapStore::getPixmapNoCache(WWidget::getPath(filename));
00342 
00343     bg->move(0, 0);
00344     if (background != NULL) {
00345         bg->setPixmap(*background);
00346     }
00347 
00348     bg->lower();
00349 
00350     // yes, this is confusing. Sorry. See ::parseSkin.
00351     m_pParent->move(0,0);
00352     if (background != NULL) {
00353         m_pParent->setFixedSize(background->width(), background->height());
00354         pGrandparent->setMinimumSize(background->width(), background->height());
00355     }
00356 
00357     QColor c(0,0,0); // Default background color is now black, if people want to do <invert/> filters they'll have to figure something out for this.
00358     if (!XmlParse::selectNode(node, "BgColor").isNull()) {
00359         c.setNamedColor(XmlParse::selectNodeQString(node, "BgColor"));
00360     }
00361 
00362     QPalette palette;
00363     palette.setBrush(QPalette::Window, WSkinColor::getCorrectColor(c));
00364     pGrandparent->setBackgroundRole(QPalette::Window);
00365     pGrandparent->setPalette(palette);
00366     pGrandparent->setAutoFillBackground(true);
00367 
00368     // WPixmapStore::getPixmapNoCache() allocated background and gave us
00369     // ownership. QLabel::setPixmap makes a copy, so we have to delete this.
00370     delete background;
00371 
00372     return bg;
00373 }
00374 
00375 QWidget* LegacySkinParser::parsePushButton(QDomElement node) {
00376     WPushButton* p = new WPushButton(m_pParent);
00377     setupWidget(node, p);
00378     p->setup(node);
00379     setupConnections(node, p);
00380     p->installEventFilter(m_pKeyboard);
00381     return p;
00382 }
00383 
00384 QWidget* LegacySkinParser::parseSliderComposed(QDomElement node) {
00385     WSliderComposed* p = new WSliderComposed(m_pParent);
00386     setupWidget(node, p);
00387     p->setup(node);
00388     setupConnections(node, p);
00389     p->installEventFilter(m_pKeyboard);
00390     setControlDefaults(node, p);
00391     return p;
00392 }
00393 
00394 QWidget* LegacySkinParser::parseOverview(QDomElement node) {
00395     QString channelStr = lookupNodeGroup(node);
00396 
00397     const char* pSafeChannelStr = safeChannelString(channelStr);
00398 
00399     BaseTrackPlayer* pPlayer = m_pPlayerManager->getPlayer(channelStr);
00400 
00401     if (pPlayer == NULL)
00402         return NULL;
00403 
00404     WOverview* p = new WOverview(pSafeChannelStr, m_pParent);
00405 
00406     connect(p, SIGNAL(trackDropped(QString, QString)),
00407             m_pPlayerManager, SLOT(slotLoadToPlayer(QString, QString)));
00408 
00409     setupWidget(node, p);
00410     p->setup(node);
00411     setupConnections(node, p);
00412     p->installEventFilter(m_pKeyboard);
00413 
00414     // Connect the player's load and unload signals to the overview widget.
00415     connect(pPlayer, SIGNAL(newTrackLoaded(TrackPointer)),
00416             p, SLOT(slotTrackLoaded(TrackPointer)));
00417     connect(pPlayer, SIGNAL(unloadingTrack(TrackPointer)),
00418             p, SLOT(slotUnloadTrack(TrackPointer)));
00419 
00420     // Connect the load progress of waveforms signals so that we can use this
00421     // widget as a progress bar.
00422     AnalyserQueue* pAnalyserQueue = pPlayer->getAnalyserQueue();
00423     if (pAnalyserQueue) {
00424         connect(pAnalyserQueue, SIGNAL(trackProgress(TrackPointer, int)),
00425                 p, SLOT(slotTrackProgress(TrackPointer, int)));
00426     }
00427 
00428     TrackPointer pTrack = pPlayer->getLoadedTrack();
00429     if (pTrack) {
00430         p->slotTrackLoaded(pTrack);
00431     }
00432 
00433     return p;
00434 }
00435 
00436 QWidget* LegacySkinParser::parseVisual(QDomElement node) {
00437     QString channelStr = lookupNodeGroup(node);
00438     BaseTrackPlayer* pPlayer = m_pPlayerManager->getPlayer(channelStr);
00439 
00440     const char* pSafeChannelStr = safeChannelString(channelStr);
00441 
00442     if (pPlayer == NULL)
00443         return NULL;
00444 
00445     WaveformRenderer* pWaveformRenderer = pPlayer->getWaveformRenderer();
00446 
00447     WaveformViewerType type;
00448     QWidget* widget = NULL;
00449     type = WaveformViewerFactory::createWaveformViewer(pSafeChannelStr, m_pParent,
00450                                                        m_pConfig, &widget, pWaveformRenderer);
00451     widget->installEventFilter(m_pKeyboard);
00452 
00453     // Hook up the wheel Control Object to the Visual Controller
00454 
00455     // Connect control proxy to widget, so delete can be handled by the QT object tree
00456     ControlObjectThreadWidget * p = new ControlObjectThreadWidget(
00457         ControlObject::getControl(ConfigKey(channelStr, "wheel")), widget);
00458 
00459     p->setWidget((QWidget *)widget, true, true,
00460                  ControlObjectThreadWidget::EMIT_ON_PRESS, Qt::RightButton);
00461 
00462     setupWidget(node, widget);
00463     if (type == WAVEFORM_GL) {
00464         ((WGLWaveformViewer*)widget)->setup(node);
00465     } else if (type == WAVEFORM_WIDGET) {
00466         ((WWaveformViewer *)widget)->setup(node);
00467     } else if (type == WAVEFORM_SIMPLE) {
00468         ((WVisualSimple*)widget)->setup(node);
00469     }
00470     setupConnections(node, widget);
00471 
00472     connect(widget, SIGNAL(trackDropped(QString, QString)),
00473             m_pPlayerManager, SLOT(slotLoadToPlayer(QString, QString)));
00474 
00475     return widget;
00476 }
00477 
00478 QWidget* LegacySkinParser::parseText(QDomElement node) {
00479     QString channelStr = lookupNodeGroup(node);
00480 
00481     BaseTrackPlayer* pPlayer = m_pPlayerManager->getPlayer(channelStr);
00482 
00483     if (!pPlayer)
00484         return NULL;
00485 
00486     WTrackText* p = new WTrackText(m_pParent);
00487     setupWidget(node, p);
00488     p->setup(node);
00489     setupConnections(node, p);
00490     p->installEventFilter(m_pKeyboard);
00491 
00492     connect(pPlayer, SIGNAL(newTrackLoaded(TrackPointer)),
00493             p, SLOT(slotTrackLoaded(TrackPointer)));
00494     connect(pPlayer, SIGNAL(unloadingTrack(TrackPointer)),
00495             p, SLOT(slotTrackUnloaded(TrackPointer)));
00496 
00497     TrackPointer pTrack = pPlayer->getLoadedTrack();
00498     if (pTrack) {
00499         p->slotTrackLoaded(pTrack);
00500     }
00501 
00502     return p;
00503 }
00504 
00505 QWidget* LegacySkinParser::parseTrackProperty(QDomElement node) {
00506     QString channelStr = lookupNodeGroup(node);
00507 
00508 
00509     BaseTrackPlayer* pPlayer = m_pPlayerManager->getPlayer(channelStr);
00510 
00511     if (!pPlayer)
00512         return NULL;
00513 
00514     WTrackProperty* p = new WTrackProperty(m_pParent);
00515     setupWidget(node, p);
00516     p->setup(node);
00517     setupConnections(node, p);
00518     p->installEventFilter(m_pKeyboard);
00519 
00520     connect(pPlayer, SIGNAL(newTrackLoaded(TrackPointer)),
00521             p, SLOT(slotTrackLoaded(TrackPointer)));
00522     connect(pPlayer, SIGNAL(unloadingTrack(TrackPointer)),
00523             p, SLOT(slotTrackUnloaded(TrackPointer)));
00524 
00525     TrackPointer pTrack = pPlayer->getLoadedTrack();
00526     if (pTrack) {
00527         p->slotTrackLoaded(pTrack);
00528     }
00529 
00530     return p;
00531 }
00532 
00533 QWidget* LegacySkinParser::parseVuMeter(QDomElement node) {
00534     WVuMeter * p = new WVuMeter(m_pParent);
00535     setupWidget(node, p);
00536     p->setup(node);
00537     setupConnections(node, p);
00538     p->installEventFilter(m_pKeyboard);
00539     return p;
00540 }
00541 
00542 QWidget* LegacySkinParser::parseStatusLight(QDomElement node) {
00543     WStatusLight * p = new WStatusLight(m_pParent);
00544     setupWidget(node, p);
00545     p->setup(node);
00546     setupConnections(node, p);
00547     p->installEventFilter(m_pKeyboard);
00548     return p;
00549 }
00550 
00551 QWidget* LegacySkinParser::parseDisplay(QDomElement node) {
00552     WDisplay * p = new WDisplay(m_pParent);
00553     setupWidget(node, p);
00554     p->setup(node);
00555     setupConnections(node, p);
00556     p->installEventFilter(m_pKeyboard);
00557     return p;
00558 }
00559 
00560 QWidget* LegacySkinParser::parseNumberRate(QDomElement node) {
00561     QString channelStr = lookupNodeGroup(node);
00562 
00563     const char* pSafeChannelStr = safeChannelString(channelStr);
00564 
00565     QColor c(255,255,255);
00566     if (!XmlParse::selectNode(node, "BgColor").isNull()) {
00567         c.setNamedColor(XmlParse::selectNodeQString(node, "BgColor"));
00568     }
00569 
00570     QPalette palette;
00571     //palette.setBrush(QPalette::Background, WSkinColor::getCorrectColor(c));
00572     palette.setBrush(QPalette::Button, Qt::NoBrush);
00573 
00574 
00575     WNumberRate * p = new WNumberRate(pSafeChannelStr, m_pParent);
00576     setupWidget(node, p);
00577     p->setup(node);
00578     setupConnections(node, p);
00579     p->installEventFilter(m_pKeyboard);
00580     p->setPalette(palette);
00581 
00582     return p;
00583 }
00584 
00585 QWidget* LegacySkinParser::parseNumberPos(QDomElement node) {
00586     QString channelStr = lookupNodeGroup(node);
00587 
00588     const char* pSafeChannelStr = safeChannelString(channelStr);
00589 
00590     WNumberPos* p = new WNumberPos(pSafeChannelStr, m_pParent);
00591     p->installEventFilter(m_pKeyboard);
00592     setupWidget(node, p);
00593     p->setup(node);
00594     setupConnections(node, p);
00595     return p;
00596 }
00597 
00598 QWidget* LegacySkinParser::parseNumber(QDomElement node) {
00599     WNumber* p = new WNumber(m_pParent);
00600     setupWidget(node, p);
00601     p->setup(node);
00602     setupConnections(node, p);
00603     p->installEventFilter(m_pKeyboard);
00604     return p;
00605 }
00606 
00607 QWidget* LegacySkinParser::parseLabel(QDomElement node) {
00608     WLabel * p = new WLabel(m_pParent);
00609     setupWidget(node, p);
00610     p->setup(node);
00611     setupConnections(node, p);
00612     p->installEventFilter(m_pKeyboard);
00613     return p;
00614 }
00615 
00616 QWidget* LegacySkinParser::parseTime(QDomElement node) {
00617    WTime *p = new WTime(m_pParent);
00618    setupWidget(node, p);
00619    p->setup(node);
00620    setupConnections(node, p);
00621    p->installEventFilter(m_pKeyboard);
00622    return p;
00623 }
00624 
00625 QWidget* LegacySkinParser::parseKnob(QDomElement node) {
00626     WKnob * p = new WKnob(m_pParent);
00627     setupWidget(node, p);
00628     p->setup(node);
00629     setupConnections(node, p);
00630     p->installEventFilter(m_pKeyboard);
00631     setControlDefaults(node, p);
00632     return p;
00633 }
00634 
00635 QWidget* LegacySkinParser::parseSpinny(QDomElement node) {
00636     QString channelStr = lookupNodeGroup(node);
00637     const char* pSafeChannelStr = safeChannelString(channelStr);
00638     WSpinny* p = new WSpinny(m_pParent, m_pVCManager);
00639     setupWidget(node, p);
00640 
00641     connect(p, SIGNAL(trackDropped(QString, QString)),
00642             m_pPlayerManager, SLOT(slotLoadToPlayer(QString, QString)));
00643 
00644     p->setup(node, pSafeChannelStr);
00645     setupConnections(node, p);
00646     p->installEventFilter(m_pKeyboard);
00647     return p;
00648 }
00649 
00650 
00651 QWidget* LegacySkinParser::parseTableView(QDomElement node) {
00652     QStackedWidget* pTabWidget = new QStackedWidget(m_pParent);
00653 
00654     setupPosition(node, pTabWidget);
00655     setupSize(node, pTabWidget);
00656 
00657     // set maximum width to prevent growing to qSplitter->sizeHint()
00658     // Note: sizeHint() may be greater in skins for tiny screens
00659     int width = pTabWidget->minimumWidth();
00660     if (width == 0) {
00661         width = m_pParent->minimumWidth();
00662     }
00663     pTabWidget->setMaximumWidth(width);
00664 
00665     QWidget* pLibraryPage = new QWidget(pTabWidget);
00666 
00667     QGridLayout* pLibraryPageLayout = new QGridLayout(pLibraryPage);
00668     pLibraryPageLayout->setContentsMargins(0, 0, 0, 0);
00669     pLibraryPage->setLayout(pLibraryPageLayout);
00670 
00671     QSplitter* pSplitter = new QSplitter(pLibraryPage);
00672 
00673     WLibrary* pLibraryWidget = new WLibrary(pSplitter);
00674     pLibraryWidget->installEventFilter(m_pKeyboard);
00675 
00676     QWidget* pLibrarySidebarPage = new QWidget(pSplitter);
00677 
00678     WLibrarySidebar* pLibrarySidebar = new WLibrarySidebar(pLibrarySidebarPage);
00679     pLibrarySidebar->installEventFilter(m_pKeyboard);
00680 
00681     WSearchLineEdit* pLineEditSearch = new WSearchLineEdit(m_pConfig,
00682                                                            pLibrarySidebarPage);
00683     pLineEditSearch->setup(node);
00684 
00685     QVBoxLayout* vl = new QVBoxLayout(pLibrarySidebarPage);
00686     vl->setContentsMargins(0,0,0,0); //Fill entire space
00687     vl->addWidget(pLineEditSearch);
00688     vl->addWidget(pLibrarySidebar);
00689     pLibrarySidebarPage->setLayout(vl);
00690 
00691     // Connect search box signals to the library
00692     connect(pLineEditSearch, SIGNAL(search(const QString&)),
00693             pLibraryWidget, SLOT(search(const QString&)));
00694     connect(pLineEditSearch, SIGNAL(searchCleared()),
00695             pLibraryWidget, SLOT(searchCleared()));
00696     connect(pLineEditSearch, SIGNAL(searchStarting()),
00697             pLibraryWidget, SLOT(searchStarting()));
00698     connect(m_pLibrary, SIGNAL(restoreSearch(const QString&)),
00699             pLineEditSearch, SLOT(restoreSearch(const QString&)));
00700 
00701     m_pLibrary->bindWidget(pLibrarySidebar,
00702                            pLibraryWidget,
00703                            m_pKeyboard);
00704     // This must come after the bindWidget or we will not style any of the
00705     // LibraryView's because they have not been added yet.
00706     pLibraryWidget->setup(node);
00707 
00708     pSplitter->addWidget(pLibrarySidebarPage);
00709     pSplitter->addWidget(pLibraryWidget);
00710 
00711     // TODO(rryan) can we make this more elegant?
00712     QList<int> splitterSizes;
00713     splitterSizes.push_back(50);
00714     splitterSizes.push_back(500);
00715     pSplitter->setSizes(splitterSizes);
00716 
00717     // Add the splitter to the library page's layout, so it's
00718     // positioned/sized automatically
00719     pLibraryPageLayout->addWidget(pSplitter,
00720                                     1, 0, //From row 1, col 0,
00721                                     1,    //Span 1 row
00722                                     3,    //Span 3 cols
00723                                     0);   //Default alignment
00724 
00725     pTabWidget->addWidget(pLibraryPage);
00726 
00727     QString style = XmlParse::selectNodeQString(node, "Style");
00728 
00729     // Workaround to support legacy color styling
00730     QColor color(0,0,0);
00731 
00732 
00733     // Qt 4.7.0's GTK style is broken.
00734     bool hasQtKickedUsInTheNuts = false;
00735 
00736 #ifdef __LINUX__
00737 #define ohyesithas true
00738     QString QtVersion = qVersion();
00739     if (QtVersion == "4.7.0") {
00740         hasQtKickedUsInTheNuts = ohyesithas;
00741     }
00742 #undef ohyesithas
00743 #endif
00744 
00745     QString styleHack = "";
00746 
00747     if (!XmlParse::selectNode(node, "FgColor").isNull()) {
00748         color.setNamedColor(XmlParse::selectNodeQString(node, "FgColor"));
00749         color = WSkinColor::getCorrectColor(color);
00750 
00751         if (hasQtKickedUsInTheNuts) {
00752             styleHack.append(QString("QTreeView { color: %1; }\n ").arg(color.name()));
00753             styleHack.append(QString("QTableView { color: %1; }\n ").arg(color.name()));
00754             styleHack.append(QString("QTableView::item:!selected { color: %1; }\n ").arg(color.name()));
00755             styleHack.append(QString("QTreeView::item:!selected { color: %1; }\n ").arg(color.name()));
00756         } else {
00757             styleHack.append(QString("WLibraryTableView { color: %1; }\n ").arg(color.name()));
00758             styleHack.append(QString("WLibrarySidebar { color: %1; }\n ").arg(color.name()));
00759         }
00760         styleHack.append(QString("WSearchLineEdit { color: %1; }\n ").arg(color.name()));
00761         styleHack.append(QString("QTextBrowser { color: %1; }\n ").arg(color.name()));
00762         styleHack.append(QString("QLabel { color: %1; }\n ").arg(color.name()));
00763         styleHack.append(QString("QRadioButton { color: %1; }\n ").arg(color.name()));
00764     }
00765 
00766     if (!XmlParse::selectNode(node, "BgColor").isNull()) {
00767         color.setNamedColor(XmlParse::selectNodeQString(node, "BgColor"));
00768         color = WSkinColor::getCorrectColor(color);
00769         if (hasQtKickedUsInTheNuts) {
00770             styleHack.append(QString("QTreeView {  background-color: %1; }\n ").arg(color.name()));
00771             styleHack.append(QString("QTableView {  background-color: %1; }\n ").arg(color.name()));
00772 
00773             // Required for styling the item backgrounds, need to pick !selected
00774             styleHack.append(QString("QTreeView::item:!selected {  background-color: %1; }\n ").arg(color.name()));
00775             styleHack.append(QString("QTableView::item:!selected {  background-color: %1; }\n ").arg(color.name()));
00776 
00777             // Styles the sidebar triangle area where there is no triangle
00778             styleHack.append(QString("QTreeView::branch:!has-children {  background-color: %1; }\n ").arg(color.name()));
00779 
00780             // We can't style the triangle portions because the triangle
00781             // disappears when we do background-color. I suspect they use
00782             // background-image instead of border-image, against their own
00783             // documentation's recommendation.
00784             // EDIT: Un-commented next line cause it works if we use custom images as triangle,
00785             // see https://bugs.launchpad.net/mixxx/+bug/690280 --jus 12/2010
00786             styleHack.append(QString("QTreeView::branch:has-children {  background-color: %1; }\n ").arg(color.name()));
00787         } else {
00788             styleHack.append(QString("WLibraryTableView {  background-color: %1; }\n ").arg(color.name()));
00789             styleHack.append(QString("WLibrarySidebar {  background-color: %1; }\n ").arg(color.name()));
00790         }
00791 
00792         styleHack.append(QString("WSearchLineEdit {  background-color: %1; }\n ").arg(color.name()));
00793         styleHack.append(QString("QTextBrowser {  background-color: %1; }\n ").arg(color.name()));
00794     }
00795 
00796     if (!XmlParse::selectNode(node, "BgColorRowEven").isNull()) {
00797         color.setNamedColor(XmlParse::selectNodeQString(node, "BgColorRowEven"));
00798         color = WSkinColor::getCorrectColor(color);
00799 
00800         if (hasQtKickedUsInTheNuts) {
00801             styleHack.append(QString("QTableView::item:!selected { background-color: %1; }\n ").arg(color.name()));
00802         } else {
00803             styleHack.append(QString("WLibraryTableView { background: %1; }\n ").arg(color.name()));
00804         }
00805     }
00806 
00807     if (!XmlParse::selectNode(node, "BgColorRowUneven").isNull()) {
00808         color.setNamedColor(XmlParse::selectNodeQString(node, "BgColorRowUneven"));
00809         color = WSkinColor::getCorrectColor(color);
00810 
00811         if (hasQtKickedUsInTheNuts) {
00812             styleHack.append(QString("QTableView::item:alternate:!selected { background-color: %1; }\n ").arg(color.name()));
00813         } else {
00814             styleHack.append(QString("WLibraryTableView { alternate-background-color: %1; }\n ").arg(color.name()));
00815         }
00816     }
00817 
00818     style.prepend(styleHack);
00819 
00820     pTabWidget->setStyleSheet(style);
00821 
00822     return pTabWidget;
00823 }
00824 
00825 QString LegacySkinParser::lookupNodeGroup(QDomElement node) {
00826     QString group = XmlParse::selectNodeQString(node, "Group");
00827 
00828     // If the group is not present, then check for a Channel, since legacy skins
00829     // will specify the channel as either 1 or 2.
00830     if (group.size() == 0) {
00831         int channel = XmlParse::selectNodeInt(node, "Channel");
00832         group = QString("[Channel%1]").arg(channel);
00833     }
00834 
00835     return group;
00836 }
00837 
00838 // static
00839 const char* LegacySkinParser::safeChannelString(QString channelStr) {
00840     QMutexLocker lock(&s_safeStringMutex);
00841     foreach (const char *s, s_channelStrs) {
00842         if (channelStr == s) { // calls QString::operator==(const char*)
00843             return s;
00844         }
00845     }
00846     QByteArray qba(channelStr.toAscii());
00847     char *safe = new char[qba.size() + 1]; // +1 for \0
00848     int i = 0;
00849     while (safe[i] = qba[i]) ++i;
00850     s_channelStrs.append(safe);
00851     return safe;
00852 }
00853 
00854 QWidget* LegacySkinParser::parseStyle(QDomElement node) {
00855     QString style = node.text();
00856     m_pParent->setStyleSheet(style);
00857     return m_pParent;
00858 }
00859 
00860 void LegacySkinParser::setupPosition(QDomNode node, QWidget* pWidget) {
00861     if (!XmlParse::selectNode(node, "Pos").isNull()) {
00862         QString pos = XmlParse::selectNodeQString(node, "Pos");
00863         int x = pos.left(pos.indexOf(",")).toInt();
00864         int y = pos.mid(pos.indexOf(",")+1).toInt();
00865         pWidget->move(x,y);
00866     }
00867 }
00868 
00869 void LegacySkinParser::setupSize(QDomNode node, QWidget* pWidget) {
00870     if (!XmlParse::selectNode(node, "Size").isNull()) {
00871         QString size = XmlParse::selectNodeQString(node, "Size");
00872         int comma = size.indexOf(",");
00873         QString xs = size.left(comma);
00874         QString ys = size.mid(comma+1);
00875         QSizePolicy sizePolicy;
00876 
00877         if (xs.endsWith("e")) {
00878             //qDebug() << "horizontal expanding";
00879             sizePolicy.setHorizontalPolicy(QSizePolicy::Expanding);
00880             xs = xs.left(xs.size()-1);
00881         } else if (xs.endsWith("me")) {
00882             //qDebug() << "horizontal minimum expanding";
00883             sizePolicy.setHorizontalPolicy(QSizePolicy::MinimumExpanding);
00884             xs = xs.left(xs.size()-2);
00885         } else if (xs.endsWith("i")) {
00886             //qDebug() << "horizontal ignored";
00887             sizePolicy.setHorizontalPolicy(QSizePolicy::Ignored);
00888             xs = xs.left(xs.size()-1);
00889         } else {
00890             sizePolicy.setHorizontalPolicy(QSizePolicy::Fixed);
00891         }
00892 
00893         bool ok;
00894         int x = xs.toInt(&ok);
00895         if (ok) {
00896             //qDebug() << "setting width to" << x;
00897             pWidget->setMinimumWidth(x);
00898         }
00899 
00900         if (ys.endsWith("e")) {
00901             //qDebug() << "vertical expanding";
00902             sizePolicy.setVerticalPolicy(QSizePolicy::Expanding);
00903             ys = ys.left(ys.size()-1);
00904         } else if (ys.endsWith("me")) {
00905             //qDebug() << "vertical minimum expanding";
00906             sizePolicy.setVerticalPolicy(QSizePolicy::MinimumExpanding);
00907             ys = ys.left(ys.size()-2);
00908         } else if (ys.endsWith("i")) {
00909             //qDebug() << "vertical ignored";
00910             sizePolicy.setVerticalPolicy(QSizePolicy::Ignored);
00911             ys = ys.left(ys.size()-1);
00912         } else {
00913             sizePolicy.setVerticalPolicy(QSizePolicy::Fixed);
00914         }
00915 
00916         int y = ys.toInt(&ok);
00917         if (ok) {
00918             //qDebug() << "setting height to" << y;
00919             pWidget->setMinimumHeight(y);
00920         }
00921         pWidget->setSizePolicy(sizePolicy);
00922     }
00923 }
00924 
00925 void LegacySkinParser::setupWidget(QDomNode node, QWidget* pWidget) {
00926     setupPosition(node, pWidget);
00927     setupSize(node, pWidget);
00928 
00929     // Tooltip
00930     if (!XmlParse::selectNode(node, "Tooltip").isNull()) {
00931         QString toolTip = XmlParse::selectNodeQString(node, "Tooltip");
00932         pWidget->setToolTip(toolTip);
00933     }
00934 
00935     QString style = XmlParse::selectNodeQString(node, "Style");
00936     if (style != "") {
00937         pWidget->setStyleSheet(style);
00938     }
00939 }
00940 
00941 void LegacySkinParser::setupConnections(QDomNode node, QWidget* pWidget) {
00942     // For each connection
00943     QDomNode con = XmlParse::selectNode(node, "Connection");
00944 
00945     while (!con.isNull())
00946     {
00947         // Get ConfigKey
00948         QString key = XmlParse::selectNodeQString(con, "ConfigKey");
00949 
00950         ConfigKey configKey = ConfigKey::parseCommaSeparated(key);
00951 
00952         // Check that the control exists
00953         ControlObject * control = ControlObject::getControl(configKey);
00954 
00955         if (control == NULL) {
00956             qWarning() << "Requested control does not exist:" << key << ". Creating it.";
00957             control = new ControlObject(configKey);
00958         }
00959 
00960         QString property = XmlParse::selectNodeQString(con, "BindProperty");
00961         if (property != "") {
00962             qDebug() << "Making property binder for" << property;
00963             // Bind this control to a property. Not leaked because it is
00964             // parented to the widget and so it dies with it.
00965             new PropertyBinder(pWidget, property, control);
00966         } else if (!XmlParse::selectNode(con, "OnOff").isNull() &&
00967             XmlParse::selectNodeQString(con, "OnOff")=="true") {
00968             // Connect control proxy to widget. Parented to pWidget so it is not
00969             // leaked.
00970             (new ControlObjectThreadWidget(control, pWidget))->setWidgetOnOff(pWidget);
00971         } else {
00972             // Default to emit on press
00973             ControlObjectThreadWidget::EmitOption emitOption = ControlObjectThreadWidget::EMIT_ON_PRESS;
00974 
00975             // Get properties from XML, or use defaults
00976             if (XmlParse::selectNodeQString(con, "EmitOnPressAndRelease").contains("true", Qt::CaseInsensitive))
00977                 emitOption = ControlObjectThreadWidget::EMIT_ON_PRESS_AND_RELEASE;
00978 
00979             if (XmlParse::selectNodeQString(con, "EmitOnDownPress").contains("false", Qt::CaseInsensitive))
00980                 emitOption = ControlObjectThreadWidget::EMIT_ON_RELEASE;
00981 
00982             bool connectValueFromWidget = true;
00983             if (XmlParse::selectNodeQString(con, "ConnectValueFromWidget").contains("false", Qt::CaseInsensitive))
00984                 connectValueFromWidget = false;
00985 
00986             bool connectValueToWidget = true;
00987             if (XmlParse::selectNodeQString(con, "ConnectValueToWidget").contains("false", Qt::CaseInsensitive))
00988                 connectValueToWidget = false;
00989 
00990             Qt::MouseButton state = Qt::NoButton;
00991             if (!XmlParse::selectNode(con, "ButtonState").isNull())
00992             {
00993                 if (XmlParse::selectNodeQString(con, "ButtonState").contains("LeftButton", Qt::CaseInsensitive))
00994                     state = Qt::LeftButton;
00995                 else if (XmlParse::selectNodeQString(con, "ButtonState").contains("RightButton", Qt::CaseInsensitive))
00996                     state = Qt::RightButton;
00997             }
00998 
00999             // Connect control proxy to widget. Parented to pWidget so it is not
01000             // leaked.
01001             (new ControlObjectThreadWidget(control, pWidget))->setWidget(
01002                 pWidget, connectValueFromWidget, connectValueToWidget,
01003                 emitOption, state);
01004 
01005             // Add keyboard shortcut info to tooltip string
01006             QString tooltip = pWidget->toolTip();
01007             QString shortcut = m_pKeyboard->getKeyboardConfig()->getValueString(configKey);
01008             if (!shortcut.isEmpty() && !tooltip.contains(shortcut, Qt::CaseInsensitive)) {
01009                 tooltip.append(QString("\nShortcut: %1").arg(shortcut));
01010                 pWidget->setToolTip(tooltip);
01011             }
01012         }
01013         con = con.nextSibling();
01014     }
01015 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines