• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KFile

kurlnavigatorbutton.cpp

Go to the documentation of this file.
00001 /*****************************************************************************
00002  * Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at>                      *
00003  * Copyright (C) 2006 by Aaron J. Seigo <aseigo@kde.org>                     *
00004  *                                                                           *
00005  * This library is free software; you can redistribute it and/or             *
00006  * modify it under the terms of the GNU Library General Public               *
00007  * License version 2 as published by the Free Software Foundation.           *
00008  *                                                                           *
00009  * This library is distributed in the hope that it will be useful,           *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         *
00012  * Library General Public License for more details.                          *
00013  *                                                                           *
00014  * You should have received a copy of the GNU Library General Public License *
00015  * along with this library; see the file COPYING.LIB.  If not, write to      *
00016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,      *
00017  * Boston, MA 02110-1301, USA.                                               *
00018  *****************************************************************************/
00019 
00020 #include "kurlnavigatorbutton_p.h"
00021 
00022 #include "kurlnavigator.h"
00023 #include "kurlnavigatormenu_p.h"
00024 #include "kdirsortfilterproxymodel.h"
00025 
00026 #include <kio/job.h>
00027 #include <kio/jobclasses.h>
00028 #include <kglobalsettings.h>
00029 #include <kstringhandler.h>
00030 
00031 #include <QtCore/QTimer>
00032 #include <QtGui/QPainter>
00033 #include <QtGui/QKeyEvent>
00034 #include <QtGui/QStyleOption>
00035 
00036 QPointer<KUrlNavigatorMenu> KUrlNavigatorButton::m_dirsMenu;
00037 
00038 KUrlNavigatorButton::KUrlNavigatorButton(int index, KUrlNavigator* parent) :
00039     KUrlButton(parent),
00040     m_index(-1),
00041     m_hoverArrow(false),
00042     m_popupDelay(0),
00043     m_listJob(0)
00044 {
00045     setAcceptDrops(true);
00046     setIndex(index);
00047     setMouseTracking(true);
00048     connect(this, SIGNAL(clicked()), this, SLOT(updateNavigatorUrl()));
00049 
00050     m_popupDelay = new QTimer(this);
00051     m_popupDelay->setSingleShot(true);
00052     connect(m_popupDelay, SIGNAL(timeout()), this, SLOT(startListJob()));
00053     connect(this, SIGNAL(pressed()), this, SLOT(startPopupDelay()));
00054 }
00055 
00056 KUrlNavigatorButton::~KUrlNavigatorButton()
00057 {
00058 }
00059 
00060 void KUrlNavigatorButton::setIndex(int index)
00061 {
00062     m_index = index;
00063     const QString path = urlNavigator()->url().pathOrUrl();
00064     setText(path.section('/', index, index));
00065 }
00066 
00067 void KUrlNavigatorButton::setActive(bool active)
00068 {
00069     QFont adjustedFont(font());
00070     if (active) {
00071         setDisplayHintEnabled(ActivatedHint, true);
00072         adjustedFont.setBold(true);
00073     } else {
00074         setDisplayHintEnabled(ActivatedHint, false);
00075         adjustedFont.setBold(false);
00076     }
00077 
00078     setFont(adjustedFont);
00079     updateMinimumWidth();
00080     update();
00081 }
00082 
00083 bool KUrlNavigatorButton::isActive() const
00084 {
00085     return isDisplayHintEnabled(ActivatedHint);
00086 }
00087 
00088 void KUrlNavigatorButton::setText(const QString& text)
00089 {
00090     KUrlButton::setText(text);
00091     updateMinimumWidth();
00092 }
00093 
00094 QSize KUrlNavigatorButton::sizeHint() const
00095 {
00096     // the minimum size is textWidth + arrowWidth() + 2 * BorderWidth; for the
00097     // preferred size we add the BorderWidth 2 times again for having an uncluttered look
00098     const int width = fontMetrics().width(text()) + arrowWidth() + 4 * BorderWidth;
00099     return QSize(width, KUrlButton::sizeHint().height());
00100 }
00101 
00102 void KUrlNavigatorButton::paintEvent(QPaintEvent* event)
00103 {
00104     Q_UNUSED(event);
00105 
00106     QPainter painter(this);
00107 
00108     int buttonWidth  = width();
00109     int preferredWidth = sizeHint().width();
00110     if (preferredWidth < minimumWidth()) {
00111         preferredWidth = minimumWidth();
00112     }
00113     if (buttonWidth > preferredWidth) {
00114         buttonWidth = preferredWidth;
00115     }
00116     const int buttonHeight = height();
00117 
00118     const QColor fgColor = foregroundColor();
00119     drawHoverBackground(&painter);
00120 
00121     int textLeft = 0;
00122     int textWidth = buttonWidth;
00123 
00124     const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
00125 
00126     if (!isDisplayHintEnabled(ActivatedHint)) {
00127         // draw arrow
00128         const int arrowSize = arrowWidth();
00129         const int arrowX = leftToRight ? (buttonWidth - arrowSize) - BorderWidth : BorderWidth;
00130         const int arrowY = (buttonHeight - arrowSize) / 2;
00131 
00132         QStyleOption option;
00133         option.initFrom(this);
00134         option.rect = QRect(arrowX, arrowY, arrowSize, arrowSize);
00135         option.palette = palette();
00136         option.palette.setColor(QPalette::Text, fgColor);
00137         option.palette.setColor(QPalette::WindowText, fgColor);
00138         option.palette.setColor(QPalette::ButtonText, fgColor);
00139 
00140         if (m_hoverArrow) {
00141             // highlight the background of the arrow to indicate that the directories
00142             // popup can be opened by a mouse click
00143             QColor hoverColor = palette().color(QPalette::HighlightedText);
00144             hoverColor.setAlpha(96);
00145             painter.setPen(Qt::NoPen);
00146             painter.setBrush(hoverColor);
00147 
00148             int hoverX = arrowX;
00149             if (!leftToRight) {
00150                 hoverX -= BorderWidth;
00151             }
00152             painter.drawRect(QRect(hoverX, 0, arrowSize + BorderWidth, buttonHeight));
00153         }
00154 
00155         if (leftToRight) {
00156             style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &option, &painter, this);
00157         } else {
00158             style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &option, &painter, this);
00159             textLeft += arrowSize + 2 * BorderWidth;
00160         }
00161 
00162         textWidth -= arrowSize + 2 * BorderWidth;
00163     }
00164 
00165     painter.setPen(fgColor);
00166     const bool clipped = isTextClipped();
00167     const int align = clipped ? Qt::AlignVCenter : Qt::AlignCenter;
00168     const QRect textRect(textLeft, 0, textWidth, buttonHeight);
00169     if (clipped) {
00170         QColor bgColor = fgColor;
00171         bgColor.setAlpha(0);
00172         QLinearGradient gradient(textRect.topLeft(), textRect.topRight());
00173         if (leftToRight) {
00174             gradient.setColorAt(0.8, fgColor);
00175             gradient.setColorAt(1.0, bgColor);
00176         } else {
00177             gradient.setColorAt(0.0, bgColor);
00178             gradient.setColorAt(0.2, fgColor);
00179         }
00180 
00181         QPen pen;
00182         pen.setBrush(QBrush(gradient));
00183         painter.setPen(pen);
00184     }
00185     painter.drawText(textRect, align, text());
00186 }
00187 
00188 void KUrlNavigatorButton::enterEvent(QEvent* event)
00189 {
00190     KUrlButton::enterEvent(event);
00191 
00192     // if the text is clipped due to a small window width, the text should
00193     // be shown as tooltip
00194     if (isTextClipped()) {
00195         setToolTip(text());
00196     }
00197 }
00198 
00199 void KUrlNavigatorButton::leaveEvent(QEvent* event)
00200 {
00201     KUrlButton::leaveEvent(event);
00202     setToolTip(QString());
00203 
00204     if (m_hoverArrow) {
00205         m_hoverArrow = false;
00206         update();
00207     }
00208 }
00209 
00210 void KUrlNavigatorButton::dropEvent(QDropEvent* event)
00211 {
00212     if (m_index < 0) {
00213         return;
00214     }
00215 
00216     const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
00217     if (!urls.isEmpty()) {
00218         setDisplayHintEnabled(DraggedHint, true);
00219 
00220         QString path = urlNavigator()->url().prettyUrl();
00221         path = path.section('/', 0, m_index + 2);
00222 
00223         emit urlsDropped(KUrl(path), event);
00224 
00225         setDisplayHintEnabled(DraggedHint, false);
00226         update();
00227     }
00228 }
00229 
00230 void KUrlNavigatorButton::dragEnterEvent(QDragEnterEvent* event)
00231 {
00232     if (event->mimeData()->hasUrls()) {
00233         setDisplayHintEnabled(DraggedHint, true);
00234         event->acceptProposedAction();
00235 
00236         update();
00237     }
00238 }
00239 
00240 void KUrlNavigatorButton::dragMoveEvent(QDragMoveEvent* event)
00241 {
00242     QRect rect = event->answerRect();
00243     if (isAboveArrow(rect.center().x())) {
00244         m_hoverArrow = true;
00245         update();
00246 
00247         if (m_dirsMenu == 0) {
00248             startPopupDelay();
00249         } else if (m_dirsMenu->parent() != this) {
00250             m_dirsMenu->close();
00251             m_dirsMenu->deleteLater();
00252             m_dirsMenu = 0;
00253 
00254             startPopupDelay();
00255         }
00256     } else {
00257         if (m_popupDelay->isActive()) {
00258             stopPopupDelay();
00259         }
00260         delete m_dirsMenu;
00261         m_dirsMenu = 0;
00262         m_hoverArrow = false;
00263         update();
00264     }
00265 }
00266 
00267 void KUrlNavigatorButton::dragLeaveEvent(QDragLeaveEvent* event)
00268 {
00269     KUrlButton::dragLeaveEvent(event);
00270 
00271     m_hoverArrow = false;
00272     setDisplayHintEnabled(DraggedHint, false);
00273     update();
00274 }
00275 
00276 void KUrlNavigatorButton::mousePressEvent(QMouseEvent* event)
00277 {
00278     if (isAboveArrow(event->x()) && (event->button() == Qt::LeftButton)) {
00279         urlNavigator()->requestActivation();
00280         startListJob();
00281     } else {
00282         // the mouse is pressed above the text area
00283         KUrlButton::mousePressEvent(event);
00284     }
00285 }
00286 
00287 void KUrlNavigatorButton::mouseReleaseEvent(QMouseEvent* event)
00288 {
00289     if (!isAboveArrow(event->x()) || (event->button() != Qt::LeftButton)) {
00290         // the mouse is released above the text area
00291         KUrlButton::mouseReleaseEvent(event);
00292     }
00293 }
00294 
00295 void KUrlNavigatorButton::mouseMoveEvent(QMouseEvent* event)
00296 {
00297     KUrlButton::mouseMoveEvent(event);
00298 
00299     const bool hoverArrow = isAboveArrow(event->x());
00300     if (hoverArrow != m_hoverArrow) {
00301         m_hoverArrow = hoverArrow;
00302         update();
00303     }
00304 }
00305 
00306 void KUrlNavigatorButton::updateNavigatorUrl()
00307 {
00308     stopPopupDelay();
00309 
00310     if (m_index < 0) {
00311         return;
00312     }
00313 
00314     urlNavigator()->setUrl(urlNavigator()->url(m_index));
00315 }
00316 
00317 void KUrlNavigatorButton::startPopupDelay()
00318 {
00319     if (m_popupDelay->isActive() || (m_listJob != 0) || (m_index < 0)) {
00320         return;
00321     }
00322 
00323     m_popupDelay->start(300);
00324 }
00325 
00326 void KUrlNavigatorButton::stopPopupDelay()
00327 {
00328     m_popupDelay->stop();
00329     if (m_listJob != 0) {
00330         m_listJob->kill();
00331         m_listJob = 0;
00332     }
00333 }
00334 
00335 void KUrlNavigatorButton::startListJob()
00336 {
00337     if (m_listJob != 0) {
00338         return;
00339     }
00340 
00341     const KUrl& url = urlNavigator()->url(m_index);
00342     m_listJob = KIO::listDir(url, KIO::HideProgressInfo, false /*no hidden files*/);
00343     m_subdirs.clear(); // just to be ++safe
00344 
00345     connect(m_listJob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList &)),
00346             this, SLOT(entriesList(KIO::Job*, const KIO::UDSEntryList&)));
00347     connect(m_listJob, SIGNAL(result(KJob*)), this, SLOT(listJobFinished(KJob*)));
00348 }
00349 
00350 void KUrlNavigatorButton::entriesList(KIO::Job* job, const KIO::UDSEntryList& entries)
00351 {
00352     if (job != m_listJob) {
00353         return;
00354     }
00355 
00356     foreach (const KIO::UDSEntry& entry, entries) {
00357         if (entry.isDir()) {
00358             const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME);
00359             if ((name != ".") && (name != "..")) {
00360                 m_subdirs.append(name);
00361             }
00362         }
00363     }
00364 }
00365 
00366 void KUrlNavigatorButton::urlsDropped(QAction* action, QDropEvent* event)
00367 {
00368     const int result = action->data().toInt();
00369     KUrl url = urlNavigator()->url(m_index);
00370     url.addPath(m_subdirs.at(result));
00371     urlsDropped(url, event);
00372 }
00373 
00377 static bool naturalLessThan(const QString& s1, const QString& s2)
00378 {
00379     return KStringHandler::naturalCompare(s1, s2, Qt::CaseInsensitive) < 0;
00380 }
00381 
00382 void KUrlNavigatorButton::listJobFinished(KJob* job)
00383 {
00384     if (job != m_listJob) {
00385         return;
00386     }
00387 
00388     m_listJob = 0;
00389     if (job->error() || m_subdirs.isEmpty()) {
00390         // clear listing
00391         return;
00392     }
00393 
00394     qSort(m_subdirs.begin(), m_subdirs.end(), naturalLessThan);
00395     setDisplayHintEnabled(PopupActiveHint, true);
00396     update(); // ensure the button is drawn highlighted
00397 
00398     if (m_dirsMenu != 0) {
00399         m_dirsMenu->close();
00400         m_dirsMenu->deleteLater();
00401         m_dirsMenu = 0;
00402     }
00403 
00404     m_dirsMenu = new KUrlNavigatorMenu(this);
00405     connect(m_dirsMenu, SIGNAL(urlsDropped(QAction*, QDropEvent*)),
00406             this, SLOT(urlsDropped(QAction*, QDropEvent*)));
00407 
00408     m_dirsMenu->setLayoutDirection(Qt::LeftToRight);
00409     int i = 0;
00410     const QString selectedSubdir = urlNavigator()->url(m_index + 1).fileName();
00411     foreach (const QString& subdir, m_subdirs) {
00412         QString text = KStringHandler::csqueeze(subdir, 60);
00413         text.replace('&', "&&");
00414         QAction* action = new QAction(text, this);
00415         if (selectedSubdir == subdir) {
00416             QFont font(action->font());
00417             font.setBold(true);
00418             action->setFont(font);
00419         }
00420         action->setData(i);
00421         m_dirsMenu->addAction(action);
00422         ++i;
00423 
00424         if (i > 100) {
00425             // Opening a menu with several 100 items makes no sense from
00426             // a usability view. Also there are implementation issues in
00427             // QMenu if the number of menu items don't fit into the available
00428             // screen -> skip remaining items
00429             QAction* limitReached = new QAction("...", this);
00430             limitReached->setEnabled(false);
00431             m_dirsMenu->addAction(limitReached);
00432             break;
00433         }
00434     }
00435 
00436     const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
00437     const int popupX = leftToRight ? width() - arrowWidth() - BorderWidth : 0;
00438     const QPoint popupPos  = urlNavigator()->mapToGlobal(geometry().bottomLeft() + QPoint(popupX, 0));
00439 
00440     const QAction* action = m_dirsMenu->exec(popupPos);
00441     if (action != 0) {
00442         const int result = action->data().toInt();
00443         KUrl url = urlNavigator()->url(m_index);
00444         url.addPath(m_subdirs[result]);
00445         urlNavigator()->setUrl(url);
00446     }
00447 
00448     m_subdirs.clear();
00449     delete m_dirsMenu;
00450     m_dirsMenu = 0;
00451 
00452     setDisplayHintEnabled(PopupActiveHint, false);
00453 }
00454 
00455 int KUrlNavigatorButton::arrowWidth() const
00456 {
00457     // if there isn't arrow then return 0
00458     int width = 0;
00459     if (!isDisplayHintEnabled(ActivatedHint)) {
00460         width = height() / 2;
00461         if (width < 4) {
00462             width = 4;
00463         }
00464     }
00465 
00466     return width;
00467 }
00468 
00469 bool KUrlNavigatorButton::isAboveArrow(int x) const
00470 {
00471     const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
00472     return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth());
00473 }
00474 
00475 bool KUrlNavigatorButton::isTextClipped() const
00476 {
00477     int availableWidth = width() - 2 * BorderWidth;
00478     if (!isDisplayHintEnabled(ActivatedHint)) {
00479         availableWidth -= arrowWidth() - BorderWidth;
00480     }
00481 
00482     QFontMetrics fontMetrics(font());
00483     return fontMetrics.width(text()) >= availableWidth;
00484 }
00485 
00486 void KUrlNavigatorButton::updateMinimumWidth()
00487 {
00488     const int oldMinWidth = minimumWidth();
00489 
00490     int minWidth = sizeHint().width();
00491     if (minWidth < 40) {
00492         minWidth = 40;
00493     }
00494     else if (minWidth > 150) {
00495         // don't let an overlong path name waste all the URL navigator space
00496         minWidth = 150;
00497     }
00498     if (oldMinWidth != minWidth) {
00499         setMinimumWidth(minWidth);
00500     }
00501 }
00502 
00503 #include "kurlnavigatorbutton_p.moc"

KFile

Skip menu "KFile"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal