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