33 #include <QtCore/QTimer>
34 #include <QtGui/QPainter>
35 #include <QtGui/QKeyEvent>
36 #include <QtGui/QStyleOption>
41 QPointer<KUrlNavigatorMenu> KUrlNavigatorButton::m_subDirsMenu;
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()));
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*)));
107 if (adjustedText.isEmpty()) {
111 adjustedText.remove(QLatin1Char(
'\n'));
113 KUrlNavigatorButtonBase::setText(adjustedText);
114 updateMinimumWidth();
118 m_pendingTextChange =
false;
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());
147 if (m_showMnemonic != show) {
148 m_showMnemonic = show;
155 return m_showMnemonic;
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();
182 int textWidth = buttonWidth;
184 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
186 if (!m_subDir.isEmpty()) {
188 const int arrowSize = arrowWidth();
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);
216 style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &option, &painter,
this);
218 style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &option, &painter,
this);
225 painter.setPen(fgColor);
226 const bool clipped = isTextClipped();
227 const QRect textRect(textLeft, 0, textWidth, buttonHeight);
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());
260 if (isTextClipped()) {
261 setToolTip(plainText());
271 m_hoverArrow =
false;
278 switch (event->key()) {
281 emit
clicked(m_url, Qt::LeftButton);
288 KUrlNavigatorButtonBase::keyPressEvent(event);
295 if (!urls.isEmpty()) {
307 if (event->mimeData()->hasUrls()) {
309 event->acceptProposedAction();
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;
344 KUrlNavigatorButtonBase::dragLeaveEvent(event);
346 m_hoverArrow =
false;
353 if (isAboveArrow(event->x()) && (event->button() == Qt::LeftButton)) {
357 KUrlNavigatorButtonBase::mousePressEvent(event);
362 if (!isAboveArrow(event->x()) || (event->button() != Qt::LeftButton)) {
365 emit
clicked(m_url, event->button());
366 cancelSubDirsRequest();
368 KUrlNavigatorButtonBase::mouseReleaseEvent(event);
373 KUrlNavigatorButtonBase::mouseMoveEvent(event);
375 const bool hoverArrow = isAboveArrow(event->x());
376 if (hoverArrow != m_hoverArrow) {
377 m_hoverArrow = hoverArrow;
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));
441 const int result = action->data().toInt();
443 url.
addPath(m_subDirs.at(result).first);
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()) {
479 void KUrlNavigatorButton::openSubDirsMenu(
KJob* job)
481 Q_ASSERT(job == m_subDirsJob);
484 if (job->error() || m_subDirs.isEmpty()) {
489 qSort(m_subDirs.begin(), m_subDirs.end(), naturalLessThan);
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 const QAction* action = m_subDirsMenu->exec(popupPos);
508 const int result = action->data().toInt();
510 url.
addPath(m_subDirs[result].first);
511 emit
clicked(url, Qt::LeftButton);
515 delete m_subDirsMenu;
521 void KUrlNavigatorButton::replaceButton(
KJob* job)
523 Q_ASSERT(job == m_subDirsJob);
525 m_replaceButton =
false;
527 if (job->error() || m_subDirs.isEmpty()) {
531 qSort(m_subDirs.begin(), m_subDirs.end(), naturalLessThan);
535 int currentIndex = 0;
536 const int subDirsCount = m_subDirs.count();
537 while (currentIndex < subDirsCount) {
538 if (m_subDirs[currentIndex].first == currentDir) {
546 int targetIndex = currentIndex - m_wheelSteps;
547 if (targetIndex < 0) {
549 }
else if (targetIndex >= subDirsCount) {
550 targetIndex = subDirsCount - 1;
554 url.
addPath(m_subDirs[targetIndex].first);
556 emit
clicked(url, Qt::LeftButton);
561 void KUrlNavigatorButton::cancelSubDirsRequest()
563 m_openSubDirsTimer->stop();
564 if (m_subDirsJob != 0) {
565 m_subDirsJob->
kill();
570 QString KUrlNavigatorButton::plainText()
const
575 const int sourceLength = source.length();
578 dest.reserve(sourceLength);
582 while (sourceIndex < sourceLength) {
583 if (source.at(sourceIndex) == QLatin1Char(
'&')) {
585 if (sourceIndex >= sourceLength) {
589 dest[destIndex] = source.at(sourceIndex);
597 int KUrlNavigatorButton::arrowWidth()
const
601 if (!m_subDir.isEmpty()) {
602 width = height() / 2;
611 bool KUrlNavigatorButton::isAboveArrow(
int x)
const
613 const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
614 return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth());
617 bool KUrlNavigatorButton::isTextClipped()
const
620 if (!m_subDir.isEmpty()) {
624 QFont adjustedFont(font());
625 adjustedFont.setBold(m_subDir.isEmpty());
626 return QFontMetrics(adjustedFont).width(plainText()) >= availableWidth;
629 void KUrlNavigatorButton::updateMinimumWidth()
631 const int oldMinWidth = minimumWidth();
637 else if (minWidth > 150) {
641 if (oldMinWidth != minWidth) {
642 setMinimumWidth(minWidth);
646 void KUrlNavigatorButton::initMenu(KUrlNavigatorMenu* menu,
int startIndex)
648 connect(menu, SIGNAL(middleMouseButtonClicked(
QAction*)),
649 this, SLOT(slotMenuActionClicked(
QAction*)));
653 menu->setLayoutDirection(Qt::LeftToRight);
655 const int maxIndex = startIndex + 30;
656 const int lastIndex = qMin(m_subDirs.count() - 1, maxIndex);
657 for (
int i = startIndex; i <= lastIndex; ++i) {
658 const QString subDirName = m_subDirs[i].first;
659 const QString subDirDisplayName = m_subDirs[i].second;
661 text.replace(QLatin1Char(
'&'), QLatin1String(
"&&"));
663 if (m_subDir == subDirName) {
664 QFont font(action->font());
666 action->setFont(font);
669 menu->addAction(action);
671 if (m_subDirs.count() > maxIndex) {
673 menu->addSeparator();
674 KUrlNavigatorMenu* subDirsMenu =
new KUrlNavigatorMenu(menu);
675 subDirsMenu->setTitle(
i18nc(
"@action:inmenu",
"More"));
676 initMenu(subDirsMenu, maxIndex);
677 menu->addMenu(subDirsMenu);
683 #include "kurlnavigatorbutton_p.moc"
bool kill(KillVerbosity verbosity=Quietly)
int naturalCompare(const QString &a, const QString &b, Qt::CaseSensitivity caseSensitivity=Qt::CaseSensitive)
const char * name(StandardAction id)
StatJob * stat(const KUrl &url, JobFlags flags=DefaultFlags)
QString i18nc(const char *ctxt, const char *text)
ListJob * listDir(const KUrl &url, JobFlags flags=DefaultFlags, bool includeHidden=true)
void addPath(const QString &txt)
static KUrl::List fromMimeData(const QMimeData *mimeData, KUrl::MetaDataMap *metaData=0)
QString stringValue(uint field) const
QString csqueeze(const QString &str, int maxlen=40)
QString fileName(const DirectoryOptions &options=IgnoreTrailingSlash) const