00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
00097
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
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
00142
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
00193
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
00283 KUrlButton::mousePressEvent(event);
00284 }
00285 }
00286
00287 void KUrlNavigatorButton::mouseReleaseEvent(QMouseEvent* event)
00288 {
00289 if (!isAboveArrow(event->x()) || (event->button() != Qt::LeftButton)) {
00290
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 );
00343 m_subdirs.clear();
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
00391 return;
00392 }
00393
00394 qSort(m_subdirs.begin(), m_subdirs.end(), naturalLessThan);
00395 setDisplayHintEnabled(PopupActiveHint, true);
00396 update();
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
00426
00427
00428
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
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
00496 minWidth = 150;
00497 }
00498 if (oldMinWidth != minWidth) {
00499 setMinimumWidth(minWidth);
00500 }
00501 }
00502
00503 #include "kurlnavigatorbutton_p.moc"