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_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::dragLeaveEvent(QDragLeaveEvent* event)
00241 {
00242 KUrlButton::dragLeaveEvent(event);
00243
00244 setDisplayHintEnabled(DraggedHint, false);
00245 update();
00246 }
00247
00248 void KUrlNavigatorButton::mousePressEvent(QMouseEvent* event)
00249 {
00250 if (isAboveArrow(event->x()) && (event->button() == Qt::LeftButton)) {
00251 urlNavigator()->requestActivation();
00252 startListJob();
00253 } else {
00254
00255 KUrlButton::mousePressEvent(event);
00256 }
00257 }
00258
00259 void KUrlNavigatorButton::mouseReleaseEvent(QMouseEvent* event)
00260 {
00261 if (!isAboveArrow(event->x()) || (event->button() != Qt::LeftButton)) {
00262
00263 KUrlButton::mouseReleaseEvent(event);
00264 }
00265 }
00266
00267 void KUrlNavigatorButton::mouseMoveEvent(QMouseEvent* event)
00268 {
00269 KUrlButton::mouseMoveEvent(event);
00270
00271 const bool hoverArrow = isAboveArrow(event->x());
00272 if (hoverArrow != m_hoverArrow) {
00273 m_hoverArrow = hoverArrow;
00274 update();
00275 }
00276 }
00277
00278 void KUrlNavigatorButton::updateNavigatorUrl()
00279 {
00280 stopPopupDelay();
00281
00282 if (m_index < 0) {
00283 return;
00284 }
00285
00286 urlNavigator()->setUrl(urlNavigator()->url(m_index));
00287 }
00288
00289 void KUrlNavigatorButton::startPopupDelay()
00290 {
00291 if (m_popupDelay->isActive() || (m_listJob != 0) || (m_index < 0)) {
00292 return;
00293 }
00294
00295 m_popupDelay->start(300);
00296 }
00297
00298 void KUrlNavigatorButton::stopPopupDelay()
00299 {
00300 m_popupDelay->stop();
00301 if (m_listJob != 0) {
00302 m_listJob->kill();
00303 m_listJob = 0;
00304 }
00305 }
00306
00307 void KUrlNavigatorButton::startListJob()
00308 {
00309 if (m_listJob != 0) {
00310 return;
00311 }
00312
00313 const KUrl& url = urlNavigator()->url(m_index);
00314 m_listJob = KIO::listDir(url, KIO::HideProgressInfo, false );
00315 m_subdirs.clear();
00316
00317 connect(m_listJob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList &)),
00318 this, SLOT(entriesList(KIO::Job*, const KIO::UDSEntryList&)));
00319 connect(m_listJob, SIGNAL(result(KJob*)), this, SLOT(listJobFinished(KJob*)));
00320 }
00321
00322 void KUrlNavigatorButton::entriesList(KIO::Job* job, const KIO::UDSEntryList& entries)
00323 {
00324 if (job != m_listJob) {
00325 return;
00326 }
00327
00328 foreach (const KIO::UDSEntry& entry, entries) {
00329 if (entry.isDir()) {
00330 const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME);
00331 if ((name != ".") && (name != "..")) {
00332 m_subdirs.append(name);
00333 }
00334 }
00335 }
00336 }
00337
00341 static bool naturalLessThan(const QString& s1, const QString& s2)
00342 {
00343 return KStringHandler::naturalCompare(s1, s2, Qt::CaseInsensitive) < 0;
00344 }
00345
00346 void KUrlNavigatorButton::listJobFinished(KJob* job)
00347 {
00348 if (job != m_listJob) {
00349 return;
00350 }
00351
00352 m_listJob = 0;
00353 if (job->error() || m_subdirs.isEmpty()) {
00354
00355 return;
00356 }
00357
00358 qSort(m_subdirs.begin(), m_subdirs.end(), naturalLessThan);
00359 setDisplayHintEnabled(PopupActiveHint, true);
00360 update();
00361
00362 KMenu* dirsMenu = new KMenu(this);
00363 dirsMenu->setLayoutDirection(Qt::LeftToRight);
00364 int i = 0;
00365 const QString selectedSubdir = urlNavigator()->url(m_index + 1).fileName();
00366 foreach (const QString& subdir, m_subdirs) {
00367 QString text = KStringHandler::csqueeze(subdir, 60);
00368 text.replace('&', "&&");
00369 QAction* action = new QAction(text, this);
00370 if (selectedSubdir == subdir) {
00371 QFont font(action->font());
00372 font.setBold(true);
00373 action->setFont(font);
00374 }
00375 action->setData(i);
00376 dirsMenu->addAction(action);
00377 ++i;
00378 }
00379
00380 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
00381 const int popupX = leftToRight ? width() - arrowWidth() - BorderWidth : 0;
00382 const QPoint popupPos = urlNavigator()->mapToGlobal(geometry().bottomLeft() + QPoint(popupX, 0));
00383
00384 const QAction* action = dirsMenu->exec(popupPos);
00385 if (action != 0) {
00386 const int result = action->data().toInt();
00387 KUrl url = urlNavigator()->url(m_index);
00388 url.addPath(m_subdirs[result]);
00389 urlNavigator()->setUrl(url);
00390 }
00391
00392 m_subdirs.clear();
00393 delete dirsMenu;
00394 dirsMenu = 0;
00395
00396 setDisplayHintEnabled(PopupActiveHint, false);
00397 }
00398
00399 int KUrlNavigatorButton::arrowWidth() const
00400 {
00401
00402 int width = 0;
00403 if (!isDisplayHintEnabled(ActivatedHint)) {
00404 width = height() / 2;
00405 if (width < 4) {
00406 width = 4;
00407 }
00408 }
00409
00410 return width;
00411 }
00412
00413 bool KUrlNavigatorButton::isAboveArrow(int x) const
00414 {
00415 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
00416 return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth());
00417 }
00418
00419 bool KUrlNavigatorButton::isTextClipped() const
00420 {
00421 int availableWidth = width() - 2 * BorderWidth;
00422 if (!isDisplayHintEnabled(ActivatedHint)) {
00423 availableWidth -= arrowWidth() - BorderWidth;
00424 }
00425
00426 QFontMetrics fontMetrics(font());
00427 return fontMetrics.width(text()) >= availableWidth;
00428 }
00429
00430 void KUrlNavigatorButton::updateMinimumWidth()
00431 {
00432 const int oldMinWidth = minimumWidth();
00433
00434 int minWidth = sizeHint().width();
00435 if (minWidth < 40) {
00436 minWidth = 40;
00437 }
00438 else if (minWidth > 150) {
00439
00440 minWidth = 150;
00441 }
00442 if (oldMinWidth != minWidth) {
00443 setMinimumWidth(minWidth);
00444 }
00445 }
00446
00447 #include "kurlnavigatorbutton_p.moc"