![]() |
Mixxx
|
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 }