/* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "UBDockPalette.h" #include "core/UBSettings.h" #include "frameworks/UBPlatformUtils.h" #include "core/UBApplication.h" #include "core/UBPreferencesController.h" #include "core/UBDownloadManager.h" #include "core/memcheck.h" /** * \brief The constructor */ UBDockPalette::UBDockPalette(QWidget *parent, const char *name) :QWidget(parent, Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint) , mOrientation(eUBDockOrientation_Left) , mPreferredWidth(100) , mPreferredHeight(100) , mCanResize(false) , mResized(false) , mCollapseWidth(150) , mLastWidth(-1) , mHTab(0) , mpStackWidget(NULL) , mpLayout(NULL) , mCurrentTab(0) , mTabPalette(new UBTabDockPalete(this, parent)) { setObjectName(name); mpLayout = new QVBoxLayout(); setLayout(mpLayout); mpStackWidget = new QStackedWidget(this); mpLayout->addWidget(mpStackWidget); // clear the tab widgets mTabWidgets.clear(); // We let 2 pixels in order to keep a small border for the resizing setMinimumWidth(/*2*border() + 2*/0); if (parent) { setAttribute(Qt::WA_NoMousePropagation); setAttribute(Qt::WA_TranslucentBackground); } else { // standalone window setAttribute(Qt::WA_TranslucentBackground); } mBackgroundBrush = QBrush(UBSettings::paletteColor); // This is the only way to set the background as transparent! setStyleSheet("QWidget {background-color: transparent}"); // Set the position of the tab onToolbarPosUpdated(); connect(UBSettings::settings()->appToolBarPositionedAtTop, SIGNAL(changed(QVariant)), this, SLOT(onToolbarPosUpdated())); connect(UBDownloadManager::downloadManager(), SIGNAL(allDownloadsFinished()), this, SLOT(onAllDownloadsFinished())); } /** * \brief The destructor */ UBDockPalette::~UBDockPalette() { if(NULL != mpStackWidget) { delete mpStackWidget; mpStackWidget = NULL; } if(NULL != mpLayout) { delete mpLayout; mpLayout = NULL; } if (NULL != mTabPalette) { delete mTabPalette; mTabPalette = NULL; } } /** * \brief Get the current orientation * @return the current orientation */ eUBDockOrientation UBDockPalette::orientation() { return mOrientation; } /** * \brief Set the current orientation * @param orientation as the given orientation */ void UBDockPalette::setOrientation(eUBDockOrientation orientation) { // Set the size mOrientation = orientation; if(orientation == eUBDockOrientation_Left || orientation == eUBDockOrientation_Right) { setMaximumHeight(parentWidget()->height()); setMinimumHeight(maximumHeight()); } else if(orientation == eUBDockOrientation_Top || orientation == eUBDockOrientation_Bottom) { setMaximumWidth(parentWidget()->width()); setMinimumWidth(maximumWidth()); } } /** * \brief Handle the mouse move event * @param as the mouse event */ //void UBDockPalette::mouseMoveEvent(QMouseEvent *event) //{ // QPoint p = event->pos(); // if(mCanResize && ((mMousePressPos - p).manhattanLength() > QApplication::startDragDistance())) // { // switch(mOrientation) // { // case eUBDockOrientation_Left: // if(p.x() < collapseWidth() && p.x() >= minimumWidth()) // { // resize(border(), height()); // mLastWidth = collapseWidth() + 1; // mResized = true; // } // else if(p.x() <= maximumWidth() && p.x() >= minimumWidth()) // { // resize(p.x(), height()); // mResized = true; // } // break; // case eUBDockOrientation_Right: // if((this->x() + p.x() > parentWidget()->width() - collapseWidth()) && (this->x() + p.x() < parentWidget()->width())) // { // resize(border(), height()); // mLastWidth = collapseWidth() + 1; // mResized = true; // } // else if((this->x() + p.x() >= parentWidget()->width() - maximumWidth()) && (this->x() + p.x() <= parentWidget()->width() - this->minimumWidth())) // { // resize(parentWidget()->width() - (this->x() + p.x()), height()); // mResized = true; // } // break; // case eUBDockOrientation_Top: // case eUBDockOrientation_Bottom: // if(p.y() <= maximumHeight()) // { // resize(width(), p.y()); // mResized = true; // } // break; // default: // break; // } // } //} /** * \brief Handle the mouse press event * @param as the mouse event */ //void UBDockPalette::mousePressEvent(QMouseEvent *event) //{ // mClickTime = QTime::currentTime(); // // The goal here is to verify if the user can resize the widget. // // It is only possible to resize it if the border is selected // QPoint p = event->pos(); // mMousePressPos = p; // mResized = false; // switch(mOrientation) // { // case eUBDockOrientation_Left: // if((p.x() >= width() - 2 * border()) && (p.x() <= width())) // { // mCanResize = true; // } // break; // case eUBDockOrientation_Right: // if((p.x() >= 0) && (p.x() <= 2 * border())) // { // mCanResize = true; // } // break; // case eUBDockOrientation_Top: // // Not supported yet // break; // case eUBDockOrientation_Bottom: // // Not supported yet // break; // default: // break; // } //} /** * \brief Handle the mouse release event * @param event as the mouse event */ //void UBDockPalette::mouseReleaseEvent(QMouseEvent *event) //{ // Q_UNUSED(event); // if(!mResized && mClickTime.elapsed() < CLICKTIME) // { // int nbTabs = mTabWidgets.size(); // int clickedTab = 0; // // If the clicked position is in the tab, perform the related action // if(eUBDockOrientation_Left == mOrientation) // { // if(mMousePressPos.x() >= width() - 2*border() && // mMousePressPos.y() >= mHTab && // mMousePressPos.x() <= width() && // mMousePressPos.y() <= mHTab + nbTabs*TABSIZE + (nbTabs -1)*tabSpacing()) // { // clickedTab = (mMousePressPos.y() - mHTab)/(TABSIZE+tabSpacing()); // tabClicked(clickedTab); // } // } // else if(eUBDockOrientation_Right == mOrientation) // { // if(mMousePressPos.x() >= 0 && // mMousePressPos.x() <= 2*border() && // mMousePressPos.y() >= mHTab && // mMousePressPos.y() <= mHTab + nbTabs*TABSIZE + (nbTabs -1)*tabSpacing()) // { // clickedTab = (mMousePressPos.y() - mHTab)/(TABSIZE+tabSpacing()); // tabClicked(clickedTab); // } // } // } // mCanResize = false; //} /** * \brief Handle the resize event * @param event as the resize event */ void UBDockPalette::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); updateMaxWidth(); if(parentWidget()) { setMinimumHeight(parentWidget()->height()); } // Set the position QPoint origin; switch(mOrientation) { case eUBDockOrientation_Right: origin.setX(parentWidget()->width() - this->width()); origin.setY(0); break; case eUBDockOrientation_Bottom: // Not supported yet case eUBDockOrientation_Top: // Not supported yet case eUBDockOrientation_Left: default: origin.setX(0); origin.setY(0); break; } move(origin.x(), origin.y()); moveTabs(); } /** * \brief Handle the mouse enter event * @param event as the mouse event */ void UBDockPalette::enterEvent(QEvent *event) { Q_UNUSED(event); // We want to set the cursor as an arrow everytime it enters the palette setCursor(Qt::ArrowCursor); } /** * \brief Handle the mouse leave event * @param event as the mouse event */ void UBDockPalette::leaveEvent(QEvent *event) { Q_UNUSED(event); // Restore the cursor to its previous shape unsetCursor(); } /** * \brief Draw the palette * @param event as the paint event */ void UBDockPalette::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::NoPen); painter.setBrush(mBackgroundBrush); QPainterPath path; path.setFillRule(Qt::WindingFill); int nbTabs = mTabWidgets.size(); if(0 < nbTabs) { // First draw the BIG RECTANGLE (I write it big because the rectangle is big...) if(mOrientation == eUBDockOrientation_Left) { path.addRect(0.0, 0.0, width(), height()); } else if(mOrientation == eUBDockOrientation_Right) { path.addRect(0.0, 0.0, width(), height()); } // THEN DRAW THE small tabs (yes, the tabs are small...) if(eUBDockTabOrientation_Up == mTabsOrientation) { mHTab = border(); } else { mHTab = height() - border() - nbTabs*TABSIZE - (nbTabs-1)*tabSpacing(); } painter.drawPath(path); // for(int i = 0; i < mTabWidgets.size(); i++) // { // UBDockPaletteWidget* pCrntWidget = mTabWidgets.at(i); // if(NULL != pCrntWidget) // { // if(mOrientation == eUBDockOrientation_Left) // { // path.addRoundedRect(width()-4*border(), mHTab + i*TABSIZE + i*tabSpacing(), 4*border(), TABSIZE, radius(), radius()); // painter.drawPath(path); // QPixmap iconPixmap; // if(mCollapseWidth >= width()) // { // // Get the collapsed icon // iconPixmap = pCrntWidget->iconToRight(); // } // else // { // // Get the expanded icon // iconPixmap = pCrntWidget->iconToLeft(); // } // painter.drawPixmap(width() - 2*border() + 1, mHTab + i*TABSIZE + i*tabSpacing() + 1 , 2*border() - 4, TABSIZE - 2, iconPixmap); // } // else if(mOrientation == eUBDockOrientation_Right) // { // path.addRoundedRect(0.0, mHTab + i*TABSIZE + i*tabSpacing(), 4*border(), TABSIZE, radius(), radius()); // painter.drawPath(path); // QPixmap iconPixmap; // if(mCollapseWidth >= width()) // { // // Get the collapsed icon // iconPixmap = pCrntWidget->iconToLeft(); // } // else // { // // Get the expanded icon // iconPixmap = pCrntWidget->iconToRight(); // } // painter.drawPixmap(2, mHTab + i*TABSIZE + i*tabSpacing() + 1, 2*border() - 4, TABSIZE - 2, iconPixmap); // } // else // { // painter.drawRoundedRect(border(), border(), width() - 2 * border(), height() - 2 * border(), radius(), radius()); // } // } // } } } /** * \brief Set the background brush * @param brush as the given brush */ void UBDockPalette::setBackgroundBrush(const QBrush &brush) { if (mBackgroundBrush != brush) { mBackgroundBrush = brush; update(); } } /** * Get the border size * @return the border size */ int UBDockPalette::border() { return 15; } /** * \brief Get the radius size * @return the radius size */ int UBDockPalette::radius() { return 5; } /** * \brief Set the maximum width */ void UBDockPalette::updateMaxWidth() { // Only the inherited class will overload this method } /** * \brief Get the collapse width value * @return the collapse widht value */ int UBDockPalette::collapseWidth() { return mCollapseWidth; } /** * \brief collapse/expand automatically the palette after a click on its tab */ void UBDockPalette::tabClicked(int tabIndex) { // If the current tab is not the clicked one, show its content if(mCurrentTab != tabIndex) { showTabWidget(tabIndex); } // else collapse the palette else { toggleCollapseExpand(); } mTabPalette->update(); } /** * \brief Show the tab widget related to the given index * @param tabIndex as the given index */ void UBDockPalette::showTabWidget(int tabIndex) { mpStackWidget->setCurrentIndex(tabIndex); mCurrentTab = tabIndex; // Expand it if collapsed if(mLastWidth != -1) { toggleCollapseExpand(); } // Update the current tab index if(NULL != (dynamic_cast(mpStackWidget->widget(tabIndex)))){ mCrntTabWidget = dynamic_cast(mpStackWidget->widget(tabIndex))->name(); } } /** * \brief Toggle the collapse / expand state */ void UBDockPalette::toggleCollapseExpand() { if(mLastWidth == -1) { // The palette must be collapsed mLastWidth = width(); resize(0, height()); } else { // The palette will be expanded resize(mLastWidth, height()); mLastWidth = -1; } } /** * \brief Set the tabs orientation * @param orientation as the given tabs orientation */ void UBDockPalette::setTabsOrientation(eUBDockTabOrientation orientation) { mTabsOrientation = orientation; } /** * \brief Update the tab position regarding the toolbar position (up or down) */ void UBDockPalette::onToolbarPosUpdated() { // Get the position of the tab if(UBSettings::settings()->appToolBarPositionedAtTop->get().toBool()) { setTabsOrientation(eUBDockTabOrientation_Up); } else { setTabsOrientation(eUBDockTabOrientation_Down); } update(); } /** * \brief Get the custom margin * @return the custom margin value */ int UBDockPalette::customMargin() { return 5; } /** * \brief Add the given tab widget * @param widget as the given widget */ void UBDockPalette::addTab(UBDockPaletteWidget *widget) { if(!mTabWidgets.contains(widget) && widget->visibleState()) { widget->setVisible(true); mTabWidgets.append(widget); mpStackWidget->addWidget(widget); mpStackWidget->setCurrentWidget(widget); resizeTabs(); update(); } } /** * \brief Remove the given tab * @param widgetName as the tab widget name */ void UBDockPalette::removeTab(const QString &widgetName) { for(int i = 0; i < mTabWidgets.size(); i++) { UBDockPaletteWidget* pCrntWidget = mTabWidgets.at(i); if(NULL != pCrntWidget && (pCrntWidget->name() == widgetName)) { mpStackWidget->removeWidget(pCrntWidget); mTabWidgets.remove(i); pCrntWidget->hide(); update(); break; } } resizeTabs(); mCurrentTab = qMax(mCurrentTab - 1, 0); } /** * \brief Handle the resize request * @param event as the given resize request */ void UBDockPalette::onResizeRequest(QResizeEvent *event) { resizeEvent(event); } /** * \brief Get the tab spacing * @return the tab spacing */ int UBDockPalette::tabSpacing() { return 2; } /** * \brief Show the given widget * @param widgetName as the given widget name */ void UBDockPalette::onShowTabWidget(const QString &widgetName) { for(int i = 0; i < mRegisteredWidgets.size(); i++) { UBDockPaletteWidget* pCrntWidget = mRegisteredWidgets.at(i); if(NULL != pCrntWidget && (pCrntWidget->name() == widgetName)) { pCrntWidget->setVisibleState(true); addTab(pCrntWidget); break; } } } /** * \brief Hide the given widget * @param widgetName as the given widget name */ void UBDockPalette::onHideTabWidget(const QString &widgetName) { for(int i = 0; i < mRegisteredWidgets.size(); i++){ UBDockPaletteWidget* pCrntWidget = mRegisteredWidgets.at(i); if(NULL != pCrntWidget && (pCrntWidget->name() == widgetName)){ pCrntWidget->setVisibleState(false); break; } } removeTab(widgetName); } /** * \brief Connect the show / hide signals of the widget related to this dock palette */ void UBDockPalette::connectSignals() { for(int i=0; i < mRegisteredWidgets.size(); i++) { connect(mRegisteredWidgets.at(i), SIGNAL(showTab(QString)), this, SLOT(onShowTabWidget(QString))); connect(mRegisteredWidgets.at(i), SIGNAL(hideTab(QString)), this, SLOT(onHideTabWidget(QString))); } } /** * \brief Register the given widget * @param widget as the given widget */ void UBDockPalette::registerWidget(UBDockPaletteWidget *widget) { if(!mRegisteredWidgets.contains(widget)) { // Update the parent of this widget widget->setParent(this); mRegisteredWidgets.append(widget); // By default, the widget is hidden widget->hide(); } } /** * \brief Handles the 'all download finished' notification */ void UBDockPalette::onAllDownloadsFinished() { for(int i=0; iname()){ mpStackWidget->setCurrentWidget(pW); } } } void UBDockPalette::moveTabs() { if (!mHTab) { if(eUBDockTabOrientation_Up == mTabsOrientation) { mHTab = border(); } else { mHTab = height() - border() - mTabWidgets.size() * TABSIZE - (mTabWidgets.size() - 1) * tabSpacing(); } } QPoint origin(width(), mHTab + mTabPalette->mVerticalOffset); switch (mOrientation) { case eUBDockOrientation_Left: origin.setX(width()); break; case eUBDockOrientation_Right: if (parentWidget()) { origin.setX(parentWidget()->width() - width() - mTabPalette->width()); } break; case eUBDockOrientation_Top: ; case eUBDockOrientation_Bottom: ; } mTabPalette->move(origin.x(), origin.y()); } void UBDockPalette::resizeTabs() { int numTabs = mTabWidgets.size(); mTabPalette->resize(2 * border(), (numTabs * TABSIZE) + qMax(numTabs - 1, 0) * tabSpacing()); } QRect UBDockPalette::getTabPaletteRect() { QRect tabsRect = mTabPalette->rect(); QPoint topLeft = mTabPalette->mapToParent(tabsRect.topLeft()); QPoint bottomRight = mTabPalette->mapToParent(tabsRect.bottomRight()); return QRect(topLeft, bottomRight); } void UBDockPalette::assignParent(QWidget *widget) { setParent(widget); mTabPalette->setParent(widget); } void UBDockPalette::setVisible(bool visible) { QWidget::setVisible(visible); mTabPalette->setVisible(visible); } UBTabDockPalete::UBTabDockPalete(UBDockPalette *dockPalette, QWidget *parent) : QWidget(parent) , dock(dockPalette) , mVerticalOffset(0) , mFlotable(false) { int numTabs = dock->mTabWidgets.size(); resize(2 * dock->border(), (numTabs * TABSIZE) + qMax(numTabs - 1, 0) * dock->tabSpacing()); // move(dock->width(), 20); setAttribute(Qt::WA_TranslucentBackground); } void UBTabDockPalete::paintEvent(QPaintEvent */*event*/) { int nTabs = dock->mTabWidgets.size(); if (nTabs <= 0) { qDebug() << "not enough tabs"; return; } QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::NoPen); painter.setBrush(dock->mBackgroundBrush); int yFrom = 0; for (int i = 0; i < nTabs; i++) { UBDockPaletteWidget* pCrntWidget = dock->mTabWidgets.at(i); QPainterPath path; path.setFillRule(Qt::WindingFill); QPixmap iconPixmap; switch (dock->mOrientation) { case eUBDockOrientation_Left: path.addRect(0, yFrom, width() / 2, TABSIZE); path.addRoundedRect(0, yFrom, width(), TABSIZE, dock->radius(), dock->radius()); if (pCrntWidget) { if(dock->mCollapseWidth >= dock->width()) { // Get the collapsed icon iconPixmap = pCrntWidget->iconToRight(); } else { // Get the expanded icon iconPixmap = pCrntWidget->iconToLeft(); } } break; case eUBDockOrientation_Right: path.addRect(width() /2, yFrom, width() / 2, TABSIZE); path.addRoundedRect(0, yFrom, width(), TABSIZE, dock->radius(), dock->radius()); if (pCrntWidget) { if(dock->mCollapseWidth >= dock->width()) { // Get the collapsed icon iconPixmap = pCrntWidget->iconToLeft(); } else { // Get the expanded icon iconPixmap = pCrntWidget->iconToRight(); } } break; case eUBDockOrientation_Top: ; case eUBDockOrientation_Bottom: ; default: break; } painter.save(); QPixmap transparencyPix(":/images/tab_mask.png"); if (dock->mCurrentTab != i) { iconPixmap.setAlphaChannel(transparencyPix); QColor color(0x7F, 0x7F, 0x7F, 0x3F); painter.setBrush(QBrush(color)); } painter.drawPath(path); painter.drawPixmap(2, yFrom + 2, width() - 4, TABSIZE - 4, iconPixmap); yFrom += (TABSIZE + dock->tabSpacing()); painter.restore(); } } UBTabDockPalete::~UBTabDockPalete() { } void UBTabDockPalete::mousePressEvent(QMouseEvent *event) { dock->mClickTime = QTime::currentTime(); // The goal here is to verify if the user can resize the widget. // It is only possible to resize it if the border is selected QPoint p = event->pos(); dock->mMousePressPos = p; dock->mResized = false; switch(dock->mOrientation) { case eUBDockOrientation_Left: dock->mCanResize = true; break; case eUBDockOrientation_Right: dock->mCanResize = true; break; case eUBDockOrientation_Top: // Not supported yet break; case eUBDockOrientation_Bottom: // Not supported yet break; default: break; } } void UBTabDockPalete::mouseMoveEvent(QMouseEvent *event) { QPoint p = event->pos(); if(dock->mCanResize && ((dock->mMousePressPos - p).manhattanLength() > QApplication::startDragDistance())) { if (qAbs(dock->mMousePressPos.y() - p.y()) > 10 && mFlotable) { qDebug() << "y differences" << dock->mMousePressPos.y() << p.y(); mVerticalOffset += p.y() - dock->mMousePressPos.y(); move(this->pos().x(), p.y()); } switch(dock->mOrientation) { case eUBDockOrientation_Left: p.setX(p.x() + dock->width()); if(p.x() < dock->collapseWidth() && p.x() >= dock->minimumWidth()) { dock->resize(0, dock->height()); dock->mLastWidth = dock->collapseWidth() + 1; dock->mResized = true; } else if (p.x() <= dock->maximumWidth() && p.x() >= dock->minimumWidth()) { dock->resize(p.x(), dock->height()); dock->mResized = true; } break; case eUBDockOrientation_Right: p.setX(p.x() - 2 * dock->border()); if((dock->x() + p.x() > dock->parentWidget()->width() - dock->collapseWidth()) && (dock->x() + p.x() < dock->parentWidget()->width())) { dock->resize(0, dock->height()); dock->mLastWidth = dock->collapseWidth() + 1; dock->mResized = true; } else if((dock->x() + p.x() >= dock->parentWidget()->width() - dock->maximumWidth()) && (dock->x() + p.x() <= dock->parentWidget()->width() - dock->minimumWidth())) { dock->resize(dock->parentWidget()->width() - (dock->x() + p.x()), dock->height()); dock->mResized = true; } break; case eUBDockOrientation_Top: case eUBDockOrientation_Bottom: if(p.y() <= dock->maximumHeight()) { dock->resize(width(), p.y()); dock->mResized = true; } break; default: break; } } } void UBTabDockPalete::mouseReleaseEvent(QMouseEvent *event) { Q_UNUSED(event); if(!dock->mResized && dock->mClickTime.elapsed() < CLICKTIME) { int nbTabs = dock->mTabWidgets.size(); int clickedTab = 0; // If the clicked position is in the tab, perform the related action if(eUBDockOrientation_Left == dock->mOrientation) { if(dock->mMousePressPos.y() >= dock->mHTab && dock->mMousePressPos.x() <= width() && dock->mMousePressPos.y() <= dock->mHTab + nbTabs * TABSIZE + (nbTabs -1)*dock->tabSpacing()) { clickedTab = (dock->mMousePressPos.y() - dock->mHTab) / (TABSIZE + dock->tabSpacing()); dock->tabClicked(clickedTab); } } else if (eUBDockOrientation_Right == dock->mOrientation) { if(dock->mMousePressPos.x() >= 0 && dock->mMousePressPos.x() <= width() && dock->mMousePressPos.y() >= 0 && dock->mMousePressPos.y() <= nbTabs * TABSIZE + (nbTabs -1) * dock->tabSpacing()) { clickedTab = (dock->mMousePressPos.y())/(TABSIZE+dock->tabSpacing()); dock->tabClicked(clickedTab); } } } dock->mCanResize = false; }