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