33 #include <QtCore/QTimer>
34 #include <QtGui/QPainter>
35 #include <QtGui/QKeyEvent>
36 #include <QtGui/QStyleOption>
41 QPointer<KUrlNavigatorMenu> KUrlNavigatorButton::m_subDirsMenu;
44 KUrlNavigatorButtonBase(parent),
46 m_pendingTextChange(false),
47 m_replaceButton(false),
48 m_showMnemonic(false),
52 m_openSubDirsTimer(0),
57 setMouseTracking(
true);
59 m_openSubDirsTimer =
new QTimer(
this);
60 m_openSubDirsTimer->setSingleShot(
true);
61 m_openSubDirsTimer->setInterval(300);
62 connect(m_openSubDirsTimer, SIGNAL(
timeout()),
this, SLOT(startSubDirsJob()));
64 connect(
this, SIGNAL(pressed()),
this, SLOT(requestSubDirs()));
67 KUrlNavigatorButton::~KUrlNavigatorButton()
71 void KUrlNavigatorButton::setUrl(
const KUrl& url)
76 if (startTextResolving) {
82 if (protocols.isEmpty()) {
83 protocols <<
"fish" <<
"ftp" <<
"nfs" <<
"sftp" <<
"smb" <<
"webdav";
85 startTextResolving = !protocols.contains(m_url.protocol());
88 if (startTextResolving) {
89 m_pendingTextChange =
true;
91 connect(job, SIGNAL(result(
KJob*)),
92 this, SLOT(statFinished(
KJob*)));
93 emit startedTextResolving();
95 setText(m_url.fileName().replace(
'&',
"&&"));
99 KUrl KUrlNavigatorButton::url()
const
104 void KUrlNavigatorButton::setText(
const QString& text)
106 QString adjustedText = text;
107 if (adjustedText.isEmpty()) {
111 adjustedText.remove(QLatin1Char(
'\n'));
113 KUrlNavigatorButtonBase::setText(adjustedText);
114 updateMinimumWidth();
118 m_pendingTextChange =
false;
121 void KUrlNavigatorButton::setActiveSubDirectory(
const QString& subDir)
130 QString KUrlNavigatorButton::activeSubDirectory()
const
135 QSize KUrlNavigatorButton::sizeHint()
const
137 QFont adjustedFont(font());
138 adjustedFont.setBold(m_subDir.isEmpty());
141 const int width = QFontMetrics(adjustedFont).width(plainText()) + arrowWidth() + 4 * BorderWidth;
142 return QSize(width, KUrlNavigatorButtonBase::sizeHint().height());
145 void KUrlNavigatorButton::setShowMnemonic(
bool show)
147 if (m_showMnemonic != show) {
148 m_showMnemonic = show;
153 bool KUrlNavigatorButton::showMnemonic()
const
155 return m_showMnemonic;
158 void KUrlNavigatorButton::paintEvent(QPaintEvent* event)
162 QPainter painter(
this);
164 QFont adjustedFont(font());
165 adjustedFont.setBold(m_subDir.isEmpty());
166 painter.setFont(adjustedFont);
168 int buttonWidth = width();
169 int preferredWidth = sizeHint().width();
170 if (preferredWidth < minimumWidth()) {
171 preferredWidth = minimumWidth();
173 if (buttonWidth > preferredWidth) {
174 buttonWidth = preferredWidth;
176 const int buttonHeight = height();
178 const QColor fgColor = foregroundColor();
179 drawHoverBackground(&painter);
182 int textWidth = buttonWidth;
184 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
186 if (!m_subDir.isEmpty()) {
188 const int arrowSize = arrowWidth();
189 const int arrowX = leftToRight ? (buttonWidth - arrowSize) - BorderWidth : BorderWidth;
190 const int arrowY = (buttonHeight - arrowSize) / 2;
193 option.initFrom(
this);
194 option.rect = QRect(arrowX, arrowY, arrowSize, arrowSize);
195 option.palette = palette();
196 option.palette.setColor(QPalette::Text, fgColor);
197 option.palette.setColor(QPalette::WindowText, fgColor);
198 option.palette.setColor(QPalette::ButtonText, fgColor);
203 QColor hoverColor = palette().color(QPalette::HighlightedText);
204 hoverColor.setAlpha(96);
205 painter.setPen(Qt::NoPen);
206 painter.setBrush(hoverColor);
210 hoverX -= BorderWidth;
212 painter.drawRect(QRect(hoverX, 0, arrowSize + BorderWidth, buttonHeight));
216 style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &option, &painter,
this);
218 style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &option, &painter,
this);
219 textLeft += arrowSize + 2 * BorderWidth;
222 textWidth -= arrowSize + 2 * BorderWidth;
225 painter.setPen(fgColor);
226 const bool clipped = isTextClipped();
227 const QRect textRect(textLeft, 0, textWidth, buttonHeight);
229 QColor bgColor = fgColor;
231 QLinearGradient gradient(textRect.topLeft(), textRect.topRight());
233 gradient.setColorAt(0.8, fgColor);
234 gradient.setColorAt(1.0, bgColor);
236 gradient.setColorAt(0.0, bgColor);
237 gradient.setColorAt(0.2, fgColor);
241 pen.setBrush(QBrush(gradient));
245 int textFlags = clipped ? Qt::AlignVCenter : Qt::AlignCenter;
246 if (m_showMnemonic) {
247 textFlags |= Qt::TextShowMnemonic;
248 painter.drawText(textRect, textFlags, text());
250 painter.drawText(textRect, textFlags, plainText());
254 void KUrlNavigatorButton::enterEvent(QEvent* event)
256 KUrlNavigatorButtonBase::enterEvent(event);
260 if (isTextClipped()) {
261 setToolTip(plainText());
265 void KUrlNavigatorButton::leaveEvent(QEvent* event)
267 KUrlNavigatorButtonBase::leaveEvent(event);
268 setToolTip(QString());
271 m_hoverArrow =
false;
276 void KUrlNavigatorButton::keyPressEvent(QKeyEvent* event)
278 switch (event->key()) {
281 emit clicked(m_url, Qt::LeftButton);
288 KUrlNavigatorButtonBase::keyPressEvent(event);
292 void KUrlNavigatorButton::dropEvent(QDropEvent* event)
295 if (!urls.isEmpty()) {
296 setDisplayHintEnabled(DraggedHint,
true);
298 emit urlsDropped(m_url, event);
300 setDisplayHintEnabled(DraggedHint,
false);
305 void KUrlNavigatorButton::dragEnterEvent(QDragEnterEvent* event)
307 if (event->mimeData()->hasUrls()) {
308 setDisplayHintEnabled(DraggedHint,
true);
309 event->acceptProposedAction();
315 void KUrlNavigatorButton::dragMoveEvent(QDragMoveEvent* event)
317 QRect rect =
event->answerRect();
318 if (isAboveArrow(rect.center().x())) {
322 if (m_subDirsMenu == 0) {
324 }
else if (m_subDirsMenu->parent() !=
this) {
325 m_subDirsMenu->close();
326 m_subDirsMenu->deleteLater();
332 if (m_openSubDirsTimer->isActive()) {
333 cancelSubDirsRequest();
335 delete m_subDirsMenu;
337 m_hoverArrow =
false;
342 void KUrlNavigatorButton::dragLeaveEvent(QDragLeaveEvent* event)
344 KUrlNavigatorButtonBase::dragLeaveEvent(event);
346 m_hoverArrow =
false;
347 setDisplayHintEnabled(DraggedHint,
false);
351 void KUrlNavigatorButton::mousePressEvent(QMouseEvent* event)
353 if (isAboveArrow(event->x()) && (event->button() == Qt::LeftButton)) {
357 KUrlNavigatorButtonBase::mousePressEvent(event);
360 void KUrlNavigatorButton::mouseReleaseEvent(QMouseEvent* event)
362 if (!isAboveArrow(event->x()) || (event->button() != Qt::LeftButton)) {
365 emit clicked(m_url, event->button());
366 cancelSubDirsRequest();
368 KUrlNavigatorButtonBase::mouseReleaseEvent(event);
371 void KUrlNavigatorButton::mouseMoveEvent(QMouseEvent* event)
373 KUrlNavigatorButtonBase::mouseMoveEvent(event);
375 const bool hoverArrow = isAboveArrow(event->x());
376 if (hoverArrow != m_hoverArrow) {
377 m_hoverArrow = hoverArrow;
382 void KUrlNavigatorButton::wheelEvent(QWheelEvent* event)
384 if (event->orientation() == Qt::Vertical) {
385 m_wheelSteps =
event->delta() / 120;
386 m_replaceButton =
true;
390 KUrlNavigatorButtonBase::wheelEvent(event);
393 void KUrlNavigatorButton::requestSubDirs()
395 if (!m_openSubDirsTimer->isActive() && (m_subDirsJob == 0)) {
396 m_openSubDirsTimer->start();
400 void KUrlNavigatorButton::startSubDirsJob()
402 if (m_subDirsJob != 0) {
406 const KUrl url = m_replaceButton ? m_url.
upUrl() : m_url;
413 if (m_replaceButton) {
414 connect(m_subDirsJob, SIGNAL(result(
KJob*)),
this, SLOT(replaceButton(
KJob*)));
416 connect(m_subDirsJob, SIGNAL(result(
KJob*)),
this, SLOT(openSubDirsMenu(
KJob*)));
422 Q_ASSERT(job == m_subDirsJob);
429 if (displayName.isEmpty()) {
432 if ((name != QLatin1String(
".")) && (name != QLatin1String(
".."))) {
433 m_subDirs.append(qMakePair(name, displayName));
439 void KUrlNavigatorButton::urlsDropped(QAction* action, QDropEvent* event)
441 const int result = action->data().toInt();
443 url.
addPath(m_subDirs.at(result).first);
444 urlsDropped(url, event);
447 void KUrlNavigatorButton::slotMenuActionClicked(QAction* action)
449 const int result = action->data().toInt();
451 url.
addPath(m_subDirs.at(result).first);
452 emit clicked(url, Qt::MidButton);
455 void KUrlNavigatorButton::statFinished(
KJob* job)
457 if (m_pendingTextChange) {
458 m_pendingTextChange =
false;
462 if (
name.isEmpty()) {
463 name = m_url.fileName();
467 emit finishedTextResolving();
479 void KUrlNavigatorButton::openSubDirsMenu(
KJob* job)
481 Q_ASSERT(job == m_subDirsJob);
484 if (job->
error() || m_subDirs.isEmpty()) {
490 setDisplayHintEnabled(PopupActiveHint,
true);
493 if (m_subDirsMenu != 0) {
494 m_subDirsMenu->close();
495 m_subDirsMenu->deleteLater();
499 m_subDirsMenu =
new KUrlNavigatorMenu(
this);
500 initMenu(m_subDirsMenu, 0);
502 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
503 const int popupX = leftToRight ? width() - arrowWidth() - BorderWidth : 0;
504 const QPoint popupPos = parentWidget()->mapToGlobal(geometry().bottomLeft() + QPoint(popupX, 0));
506 QPointer<QObject> guard(
this);
508 const QAction* action = m_subDirsMenu->exec(popupPos);
517 const int result = action->data().toInt();
519 url.
addPath(m_subDirs[result].first);
520 emit clicked(url, Qt::LeftButton);
524 delete m_subDirsMenu;
527 setDisplayHintEnabled(PopupActiveHint,
false);
530 void KUrlNavigatorButton::replaceButton(
KJob* job)
532 Q_ASSERT(job == m_subDirsJob);
534 m_replaceButton =
false;
536 if (job->
error() || m_subDirs.isEmpty()) {
543 const QString currentDir = m_url.fileName();
544 int currentIndex = 0;
545 const int subDirsCount = m_subDirs.count();
546 while (currentIndex < subDirsCount) {
547 if (m_subDirs[currentIndex].first == currentDir) {
555 int targetIndex = currentIndex - m_wheelSteps;
556 if (targetIndex < 0) {
558 }
else if (targetIndex >= subDirsCount) {
559 targetIndex = subDirsCount - 1;
563 url.
addPath(m_subDirs[targetIndex].first);
565 emit clicked(url, Qt::LeftButton);
570 void KUrlNavigatorButton::cancelSubDirsRequest()
572 m_openSubDirsTimer->stop();
573 if (m_subDirsJob != 0) {
574 m_subDirsJob->kill();
579 QString KUrlNavigatorButton::plainText()
const
583 const QString source = text();
584 const int sourceLength = source.length();
587 dest.reserve(sourceLength);
591 while (sourceIndex < sourceLength) {
592 if (source.at(sourceIndex) == QLatin1Char(
'&')) {
594 if (sourceIndex >= sourceLength) {
598 dest[destIndex] = source.at(sourceIndex);
606 int KUrlNavigatorButton::arrowWidth()
const
610 if (!m_subDir.isEmpty()) {
611 width = height() / 2;
620 bool KUrlNavigatorButton::isAboveArrow(
int x)
const
622 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
623 return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth());
626 bool KUrlNavigatorButton::isTextClipped()
const
628 int availableWidth = width() - 2 * BorderWidth;
629 if (!m_subDir.isEmpty()) {
630 availableWidth -= arrowWidth() - BorderWidth;
633 QFont adjustedFont(font());
634 adjustedFont.setBold(m_subDir.isEmpty());
635 return QFontMetrics(adjustedFont).width(plainText()) >= availableWidth;
638 void KUrlNavigatorButton::updateMinimumWidth()
640 const int oldMinWidth = minimumWidth();
642 int minWidth = sizeHint().width();
646 else if (minWidth > 150) {
650 if (oldMinWidth != minWidth) {
651 setMinimumWidth(minWidth);
655 void KUrlNavigatorButton::initMenu(KUrlNavigatorMenu* menu,
int startIndex)
657 connect(menu, SIGNAL(middleMouseButtonClicked(QAction*)),
658 this, SLOT(slotMenuActionClicked(QAction*)));
659 connect(menu, SIGNAL(urlsDropped(QAction*,QDropEvent*)),
660 this, SLOT(urlsDropped(QAction*,QDropEvent*)));
662 menu->setLayoutDirection(Qt::LeftToRight);
664 const int maxIndex = startIndex + 30;
665 const int lastIndex = qMin(m_subDirs.count() - 1, maxIndex);
666 for (
int i = startIndex; i <= lastIndex; ++i) {
667 const QString subDirName = m_subDirs[i].first;
668 const QString subDirDisplayName = m_subDirs[i].second;
670 text.replace(QLatin1Char(
'&'), QLatin1String(
"&&"));
671 QAction* action =
new QAction(text,
this);
672 if (m_subDir == subDirName) {
673 QFont font(action->font());
675 action->setFont(font);
678 menu->addAction(action);
680 if (m_subDirs.count() > maxIndex) {
682 menu->addSeparator();
683 KUrlNavigatorMenu* subDirsMenu =
new KUrlNavigatorMenu(menu);
684 subDirsMenu->setTitle(
i18nc(
"@action:inmenu",
"More"));
685 initMenu(subDirsMenu, maxIndex);
686 menu->addMenu(subDirsMenu);
692 #include "kurlnavigatorbutton_p.moc"