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 <assert.h>
00023
00024 #include "kurlnavigator.h"
00025 #include "kdirsortfilterproxymodel.h"
00026
00027 #include <kio/job.h>
00028 #include <kio/jobclasses.h>
00029 #include <kglobalsettings.h>
00030 #include <kmenu.h>
00031
00032 #include <QtCore/QTimer>
00033 #include <QtGui/QPainter>
00034 #include <QtGui/QKeyEvent>
00035 #include <QtGui/QStyleOption>
00036
00037 KUrlNavigatorButton::KUrlNavigatorButton(int index, KUrlNavigator* parent) :
00038 KUrlButton(parent),
00039 m_index(-1),
00040 m_hoverArrow(false),
00041 m_popupPosition(0, 0),
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
00064 if (m_index < 0) {
00065 return;
00066 }
00067
00068 QString path(urlNavigator()->url().pathOrUrl());
00069 const QString buttonText = path.section('/', index, index);
00070 setText(buttonText);
00071
00072
00073
00074 ++index;
00075 QFont adjustedFont(font());
00076 if (path.section('/', index, index).isEmpty()) {
00077 setDisplayHintEnabled(ActivatedHint, true);
00078 adjustedFont.setBold(true);
00079 } else {
00080 setDisplayHintEnabled(ActivatedHint, false);
00081 adjustedFont.setBold(false);
00082 }
00083
00084 setFont(adjustedFont);
00085 updateMinimumWidth();
00086 update();
00087 }
00088
00089 QSize KUrlNavigatorButton::sizeHint() const
00090 {
00091
00092
00093 int width = fontMetrics().width(text()) + arrowWidth() + 4 * BorderWidth;
00094 if (width < minimumWidth()) {
00095 width = minimumWidth();
00096 }
00097 return QSize(width, KUrlButton::sizeHint().height());
00098 }
00099
00100 void KUrlNavigatorButton::updateMinimumWidth()
00101 {
00102 QFontMetrics fontMetrics(font());
00103 int minWidth = fontMetrics.width(text()) + arrowWidth() + 2 * BorderWidth;
00104 if (minWidth < 50) {
00105 minWidth = 50;
00106 }
00107 else if (minWidth > 150) {
00108
00109 minWidth = 150;
00110 }
00111 setMinimumWidth(minWidth);
00112 }
00113
00114 void KUrlNavigatorButton::paintEvent(QPaintEvent* event)
00115 {
00116 QPainter painter(this);
00117 painter.setClipRect(event->rect());
00118 const int buttonWidth = width();
00119 const int buttonHeight = height();
00120
00121 const QColor bgColor = backgroundColor();
00122 const QColor fgColor = foregroundColor();
00123
00124
00125 if (bgColor != Qt::transparent)
00126 painter.fillRect(rect(), bgColor);
00127
00128 int textLeft = 0;
00129 int textWidth = buttonWidth;
00130
00131 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
00132
00133 if (!isDisplayHintEnabled(ActivatedHint)) {
00134
00135 const int arrowSize = arrowWidth();
00136 const int arrowX = leftToRight ? (buttonWidth - arrowSize) - BorderWidth : BorderWidth;
00137 const int arrowY = (buttonHeight - arrowSize) / 2;
00138
00139 QStyleOption option;
00140 option.initFrom(this);
00141 option.rect = QRect(arrowX, arrowY, arrowSize, arrowSize);
00142 option.palette = palette();
00143 option.palette.setColor(QPalette::Text, fgColor);
00144 option.palette.setColor(QPalette::WindowText, fgColor);
00145 option.palette.setColor(QPalette::ButtonText, fgColor);
00146
00147 if (m_hoverArrow) {
00148
00149
00150 QColor hoverColor = fgColor;
00151 hoverColor.setAlpha(96);
00152 painter.setPen(Qt::NoPen);
00153 painter.setBrush(hoverColor);
00154
00155 int hoverX = arrowX;
00156 if (!leftToRight) {
00157 hoverX -= BorderWidth;
00158 }
00159 painter.drawRect(QRect(hoverX, 0, arrowSize + BorderWidth, buttonHeight));
00160 }
00161
00162 if (leftToRight) {
00163 style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &option, &painter, this);
00164 } else {
00165 style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &option, &painter, this);
00166 textLeft += arrowSize + 2 * BorderWidth;
00167 }
00168
00169 textWidth -= arrowSize + 2 * BorderWidth;
00170 }
00171
00172 painter.setPen(fgColor);
00173 const bool clipped = isTextClipped();
00174 const int align = clipped ? Qt::AlignVCenter : Qt::AlignCenter;
00175 const QRect textRect(textLeft, 0, textWidth, buttonHeight);
00176 if (clipped) {
00177 QLinearGradient gradient(textRect.topLeft(), textRect.topRight());
00178 if (leftToRight) {
00179 gradient.setColorAt(0.8, fgColor);
00180 gradient.setColorAt(1.0, bgColor);
00181 } else {
00182 gradient.setColorAt(0.0, bgColor);
00183 gradient.setColorAt(0.2, fgColor);
00184 }
00185
00186 QPen pen;
00187 pen.setBrush(QBrush(gradient));
00188 painter.setPen(pen);
00189 }
00190 painter.drawText(textRect, align, text());
00191 }
00192
00193 void KUrlNavigatorButton::enterEvent(QEvent* event)
00194 {
00195 KUrlButton::enterEvent(event);
00196
00197
00198
00199 if (isTextClipped()) {
00200 setToolTip(text());
00201 }
00202 }
00203
00204 void KUrlNavigatorButton::leaveEvent(QEvent* event)
00205 {
00206 KUrlButton::leaveEvent(event);
00207 setToolTip(QString());
00208
00209 if (m_hoverArrow) {
00210 m_hoverArrow = false;
00211 update();
00212 }
00213 }
00214
00215 void KUrlNavigatorButton::dropEvent(QDropEvent* event)
00216 {
00217 if (m_index < 0) {
00218 return;
00219 }
00220
00221 const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
00222 if (!urls.isEmpty()) {
00223 event->acceptProposedAction();
00224
00225 setDisplayHintEnabled(DraggedHint, true);
00226
00227 QString path(urlNavigator()->url().prettyUrl());
00228 path = path.section('/', 0, m_index + 2);
00229
00230 emit urlsDropped(urls, KUrl(path));
00231
00232 setDisplayHintEnabled(DraggedHint, false);
00233 update();
00234 }
00235 }
00236
00237 void KUrlNavigatorButton::dragEnterEvent(QDragEnterEvent* event)
00238 {
00239 if (event->mimeData()->hasUrls()) {
00240 setDisplayHintEnabled(DraggedHint, true);
00241 event->acceptProposedAction();
00242
00243 update();
00244 }
00245 }
00246
00247 void KUrlNavigatorButton::dragLeaveEvent(QDragLeaveEvent* event)
00248 {
00249 KUrlButton::dragLeaveEvent(event);
00250
00251 setDisplayHintEnabled(DraggedHint, false);
00252 update();
00253 }
00254
00255 void KUrlNavigatorButton::mousePressEvent(QMouseEvent* event)
00256 {
00257 if (isAboveArrow(event->x()) && (event->button() == Qt::LeftButton)) {
00258
00259 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
00260 const int popupX = leftToRight ? width() - arrowWidth() - BorderWidth : 0;
00261 m_popupPosition = urlNavigator()->mapToGlobal(geometry().bottomLeft() + QPoint(popupX, 0));
00262 startListJob();
00263 } else {
00264
00265 KUrlButton::mousePressEvent(event);
00266 }
00267 }
00268
00269 void KUrlNavigatorButton::mouseReleaseEvent(QMouseEvent* event)
00270 {
00271 if (!isAboveArrow(event->x()) || (event->button() != Qt::LeftButton)) {
00272
00273 KUrlButton::mouseReleaseEvent(event);
00274 }
00275 }
00276
00277 void KUrlNavigatorButton::mouseMoveEvent(QMouseEvent* event)
00278 {
00279 KUrlButton::mouseMoveEvent(event);
00280
00281 const bool hoverArrow = isAboveArrow(event->x());
00282 if (hoverArrow != m_hoverArrow) {
00283 m_hoverArrow = hoverArrow;
00284 update();
00285 }
00286 }
00287
00288 void KUrlNavigatorButton::updateNavigatorUrl()
00289 {
00290 stopPopupDelay();
00291
00292 if (m_index < 0) {
00293 return;
00294 }
00295
00296 urlNavigator()->setUrl(urlNavigator()->url(m_index));
00297 }
00298
00299 void KUrlNavigatorButton::startPopupDelay()
00300 {
00301 if (m_popupDelay->isActive() || (m_listJob != 0) || (m_index < 0)) {
00302 return;
00303 }
00304
00305 m_popupPosition = urlNavigator()->mapToGlobal(geometry().bottomLeft());
00306 m_popupDelay->start(300);
00307 }
00308
00309 void KUrlNavigatorButton::stopPopupDelay()
00310 {
00311 m_popupDelay->stop();
00312 if (m_listJob != 0) {
00313 m_listJob->kill();
00314 m_listJob = 0;
00315 }
00316 }
00317
00318 void KUrlNavigatorButton::startListJob()
00319 {
00320 if (m_listJob != 0) {
00321 return;
00322 }
00323
00324 const KUrl& url = urlNavigator()->url(m_index);
00325 m_listJob = KIO::listDir(url, KIO::HideProgressInfo, false );
00326 m_subdirs.clear();
00327
00328 connect(m_listJob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList &)),
00329 this, SLOT(entriesList(KIO::Job*, const KIO::UDSEntryList&)));
00330 connect(m_listJob, SIGNAL(result(KJob*)), this, SLOT(listJobFinished(KJob*)));
00331 }
00332
00333 void KUrlNavigatorButton::entriesList(KIO::Job* job, const KIO::UDSEntryList& entries)
00334 {
00335 if (job != m_listJob) {
00336 return;
00337 }
00338
00339 KIO::UDSEntryList::const_iterator it = entries.constBegin();
00340 const KIO::UDSEntryList::const_iterator itEnd = entries.constEnd();
00341
00342 while (it != itEnd) {
00343 const KIO::UDSEntry entry = *it;
00344 if (entry.isDir()) {
00345 const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME);
00346 if ((name != ".") && (name != "..")) {
00347 m_subdirs.append(name);
00348 }
00349 }
00350
00351 ++it;
00352 }
00353 }
00354
00358 static bool naturalLessThan(const QString& s1, const QString& s2)
00359 {
00360 return KDirSortFilterProxyModel::naturalCompare(s1.toLower(), s2.toLower()) < 0;
00361 }
00362
00363 void KUrlNavigatorButton::listJobFinished(KJob* job)
00364 {
00365 if (job != m_listJob) {
00366 return;
00367 }
00368
00369 m_listJob = 0;
00370 if (job->error() || m_subdirs.isEmpty()) {
00371
00372 return;
00373 }
00374
00375 qSort(m_subdirs.begin(), m_subdirs.end(), naturalLessThan);
00376 setDisplayHintEnabled(PopupActiveHint, true);
00377 update();
00378
00379 KMenu* dirsMenu = new KMenu(this);
00380 QStringList::const_iterator it = m_subdirs.constBegin();
00381 QStringList::const_iterator itEnd = m_subdirs.constEnd();
00382 int i = 0;
00383 while (it != itEnd) {
00384 QAction* action = new QAction(*it, this);
00385 action->setData(i);
00386 dirsMenu->addAction(action);
00387 ++it;
00388 ++i;
00389 }
00390
00391 const QAction* action = dirsMenu->exec(m_popupPosition);
00392 if (action != 0) {
00393 const int result = action->data().toInt();
00394 KUrl url = urlNavigator()->url(m_index);
00395 url.addPath(m_subdirs[result]);
00396 urlNavigator()->setUrl(url);
00397 }
00398
00399 m_subdirs.clear();
00400 delete dirsMenu;
00401 dirsMenu = 0;
00402
00403 setDisplayHintEnabled(PopupActiveHint, false);
00404 }
00405
00406 int KUrlNavigatorButton::arrowWidth() const
00407 {
00408
00409 int width = 0;
00410 if (!isDisplayHintEnabled(ActivatedHint)) {
00411 width = height() / 2;
00412 if (width < 4) {
00413 width = 4;
00414 }
00415 }
00416
00417 return width;
00418 }
00419
00420 bool KUrlNavigatorButton::isAboveArrow(int x) const
00421 {
00422 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
00423 return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth());
00424 }
00425
00426 bool KUrlNavigatorButton::isTextClipped() const
00427 {
00428 int availableWidth = width() - 2 * BorderWidth;
00429 if (!isDisplayHintEnabled(ActivatedHint)) {
00430 availableWidth -= arrowWidth() - BorderWidth;
00431 }
00432
00433 QFontMetrics fontMetrics(font());
00434 return fontMetrics.width(text()) >= availableWidth;
00435 }
00436
00437 #include "kurlnavigatorbutton_p.moc"