Context menu cleanups

- Open the context menu on the mouse-press event, not on the
  mouse-release event.
- Provide an explicit position-information and don't use
  QCursor::pos(). This fixes the issue that opening a context-menu
  by the keyboard opens below the cursor.
- Provide different signals in the KItemListController for
  the different context-menu types (item vs. view vs. header).
- Implement turning on/off roles by the header-context-menu.
This commit is contained in:
Peter Penz 2011-10-08 21:40:34 +02:00
parent b1b5a6db2e
commit 6551c000fc
14 changed files with 199 additions and 131 deletions

View file

@ -61,8 +61,11 @@
K_GLOBAL_STATIC(KModifierKeyInfo, m_keyInfo)
DolphinContextMenu::DolphinContextMenu(DolphinMainWindow* parent,
const QPoint& pos,
const KFileItem& fileInfo,
const KUrl& baseUrl) :
QObject(parent),
m_pos(pos),
m_mainWindow(parent),
m_fileInfo(fileInfo),
m_baseUrl(baseUrl),
@ -187,7 +190,7 @@ void DolphinContextMenu::openTrashContextMenu()
addShowMenuBarAction();
QAction *action = m_popup->exec(QCursor::pos());
QAction *action = m_popup->exec(m_pos);
if (action == emptyTrashAction) {
KonqOperations::emptyTrash(m_mainWindow);
} else if (action == addToPlacesAction) {
@ -212,7 +215,7 @@ void DolphinContextMenu::openTrashItemContextMenu()
QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
m_popup->addAction(propertiesAction);
if (m_popup->exec(QCursor::pos()) == restoreAction) {
if (m_popup->exec(m_pos) == restoreAction) {
KUrl::List selectedUrls;
foreach (const KFileItem &item, m_selectedItems) {
selectedUrls.append(item.url());
@ -299,7 +302,7 @@ void DolphinContextMenu::openItemContextMenu()
QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties");
m_popup->addAction(propertiesAction);
QAction* activatedAction = m_popup->exec(QCursor::pos());
QAction* activatedAction = m_popup->exec(m_pos);
if (activatedAction) {
if (activatedAction == addToPlacesAction) {
const KUrl selectedUrl(m_fileInfo.url());
@ -361,7 +364,7 @@ void DolphinContextMenu::openViewportContextMenu()
addShowMenuBarAction();
QAction* action = m_popup->exec(QCursor::pos());
QAction* action = m_popup->exec(m_pos);
if (addToPlacesAction && (action == addToPlacesAction)) {
const KUrl url = m_mainWindow->activeViewContainer()->url();
if (url.isValid()) {

View file

@ -65,6 +65,7 @@ public:
/**
* @parent Pointer to the main window the context menu
* belongs to.
* @pos Position in screen coordinates.
* @fileInfo Pointer to the file item the context menu
* is applied. If 0 is passed, the context menu
* is above the viewport.
@ -72,6 +73,7 @@ public:
* should be opened.
*/
DolphinContextMenu(DolphinMainWindow* parent,
const QPoint& pos,
const KFileItem& fileInfo,
const KUrl& baseUrl);
@ -190,6 +192,7 @@ private:
TrashContext = 2
};
QPoint m_pos;
DolphinMainWindow* m_mainWindow;
KFileItem m_fileInfo;

View file

@ -1374,13 +1374,14 @@ void DolphinMainWindow::slotSearchModeChanged(bool enabled)
#endif
}
void DolphinMainWindow::openContextMenu(const KFileItem& item,
void DolphinMainWindow::openContextMenu(const QPoint& pos,
const KFileItem& item,
const KUrl& url,
const QList<QAction*>& customActions)
{
QPointer<DolphinContextMenu> contextMenu = new DolphinContextMenu(this, item, url);
contextMenu->setCustomActions(customActions);
const DolphinContextMenu::Command command = contextMenu->open();
QWeakPointer<DolphinContextMenu> contextMenu = new DolphinContextMenu(this, pos, item, url);
contextMenu.data()->setCustomActions(customActions);
const DolphinContextMenu::Command command = contextMenu.data()->open();
switch (command) {
case DolphinContextMenu::OpenParentFolderInNewWindow: {
@ -1397,7 +1398,7 @@ void DolphinMainWindow::openContextMenu(const KFileItem& item,
break;
}
delete contextMenu;
delete contextMenu.data();
}
void DolphinMainWindow::updateToolBarMenu()
@ -2111,8 +2112,8 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container)
this, SLOT(toggleActiveView()));
connect(view, SIGNAL(tabRequested(KUrl)),
this, SLOT(openNewTab(KUrl)));
connect(view, SIGNAL(requestContextMenu(KFileItem,KUrl,QList<QAction*>)),
this, SLOT(openContextMenu(KFileItem,KUrl,QList<QAction*>)));
connect(view, SIGNAL(requestContextMenu(QPoint,KFileItem,KUrl,QList<QAction*>)),
this, SLOT(openContextMenu(QPoint,KFileItem,KUrl,QList<QAction*>)));
connect(view, SIGNAL(startedPathLoading(KUrl)),
this, SLOT(enableStopAction()));
connect(view, SIGNAL(finishedPathLoading(KUrl)),

View file

@ -439,13 +439,15 @@ private slots:
/**
* Opens the context menu on the current mouse position.
* @pos Position in screen coordinates.
* @item File item context. If item is null, the context menu
* should be applied to \a url.
* @url URL which contains \a item.
* @customActions Actions that should be added to the context menu,
* if the file item is null.
*/
void openContextMenu(const KFileItem& item,
void openContextMenu(const QPoint& pos,
const KFileItem& item,
const KUrl& url,
const QList<QAction*>& customActions);

View file

@ -86,8 +86,8 @@ DolphinPart::DolphinPart(QWidget* parentWidget, QObject* parent, const QVariantL
this, SLOT(slotItemActivated(KFileItem)));
connect(m_view, SIGNAL(tabRequested(KUrl)),
this, SLOT(createNewWindow(KUrl)));
connect(m_view, SIGNAL(requestContextMenu(KFileItem,KUrl,QList<QAction*>)),
this, SLOT(slotOpenContextMenu(KFileItem,KUrl,QList<QAction*>)));
connect(m_view, SIGNAL(requestContextMenu(QPoint,KFileItem,KUrl,QList<QAction*>)),
this, SLOT(slotOpenContextMenu(QPoint,KFileItem,KUrl,QList<QAction*>)));
connect(m_view, SIGNAL(selectionChanged(KFileItemList)),
m_extension, SIGNAL(selectionInfo(KFileItemList)));
connect(m_view, SIGNAL(selectionChanged(KFileItemList)),
@ -352,7 +352,8 @@ void DolphinPart::createNewWindow(const KUrl& url)
emit m_extension->createNewWindow(url);
}
void DolphinPart::slotOpenContextMenu(const KFileItem& _item,
void DolphinPart::slotOpenContextMenu(const QPoint& pos,
const KFileItem& _item,
const KUrl&,
const QList<QAction*>& customActions)
{
@ -430,7 +431,7 @@ void DolphinPart::slotOpenContextMenu(const KFileItem& _item,
actionGroups.insert("editactions", editActions);
emit m_extension->popupMenu(QCursor::pos(),
emit m_extension->popupMenu(pos,
items,
KParts::OpenUrlArguments(),
KParts::BrowserArguments(),

View file

@ -135,13 +135,15 @@ private Q_SLOTS:
void createNewWindow(const KUrl& url);
/**
* Opens the context menu on the current mouse position.
* @pos Position in screen coordinates.
* @item File item context. If item is null, the context menu
* should be applied to \a url.
* @url URL which contains \a item.
* @customActions Actions that should be added to the context menu,
* if the file item is null.
*/
void slotOpenContextMenu(const KFileItem& item,
void slotOpenContextMenu(const QPoint& pos,
const KFileItem& item,
const KUrl& url,
const QList<QAction*>& customActions);

View file

@ -35,7 +35,6 @@
#include <KIconEffect>
#include <KIO/NetAccess>
#include <KIO/PreviewJob>
#include <KMenu>
#include <KNewFileMenu>
#include <konqmimedata.h>
#include <konq_operations.h>

View file

@ -322,33 +322,45 @@ bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const
break;
}
return true;
} else {
if (event->buttons() & Qt::RightButton) {
m_selectionManager->clearSelection();
emit itemContextMenuRequested(m_pressedIndex, event->screenPos());
}
KItemListRubberBand* rubberBand = m_view->rubberBand();
QPointF startPos = m_pressedMousePos;
if (m_view->scrollOrientation() == Qt::Vertical) {
startPos.ry() += m_view->scrollOffset();
if (m_view->itemSize().width() < 0) {
// Use a special rubberband for views that have only one column and
// expand the rubberband to use the whole width of the view.
startPos.setX(0);
}
} else {
startPos.rx() += m_view->scrollOffset();
}
m_oldSelection = m_selectionManager->selectedItems();
rubberBand->setStartPosition(startPos);
rubberBand->setEndPosition(startPos);
rubberBand->setActive(true);
connect(rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandChanged()));
m_view->setAutoScroll(true);
return true;
}
if (event->buttons() & Qt::RightButton) {
m_selectionManager->clearSelection();
const QRectF headerBounds = m_view->headerBoundaries();
if (headerBounds.contains(event->pos())) {
emit headerContextMenuRequested(event->screenPos());
} else {
emit viewContextMenuRequested(event->screenPos());
}
return true;
}
KItemListRubberBand* rubberBand = m_view->rubberBand();
QPointF startPos = m_pressedMousePos;
if (m_view->scrollOrientation() == Qt::Vertical) {
startPos.ry() += m_view->scrollOffset();
if (m_view->itemSize().width() < 0) {
// Use a special rubberband for views that have only one column and
// expand the rubberband to use the whole width of the view.
startPos.setX(0);
}
} else {
startPos.rx() += m_view->scrollOffset();
}
m_oldSelection = m_selectionManager->selectedItems();
rubberBand->setStartPosition(startPos);
rubberBand->setEndPosition(startPos);
rubberBand->setActive(true);
connect(rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandChanged()));
m_view->setAutoScroll(true);
return false;
}
@ -437,8 +449,6 @@ bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, con
}
} else if (event->button() & Qt::MidButton) {
emit itemMiddleClicked(index);
} else if (event->button() & Qt::RightButton) {
emit contextMenuRequested(index, QPointF(event->pos()));
}
} else if (clearSelection) {
m_selectionManager->clearSelection();

View file

@ -108,7 +108,22 @@ public:
signals:
void itemActivated(int index);
void itemMiddleClicked(int index);
void contextMenuRequested(int index, const QPointF& pos);
/**
* Emitted if a context-menu is requested for the item with
* the index \a index. It is assured that the index is valid.
*/
void itemContextMenuRequested(int index, const QPointF& pos);
/**
* Emitted if a context-menu is requested for the KItemListView.
*/
void viewContextMenuRequested(const QPointF& pos);
/**
* Emitted if a context-menu is requested for the header of the KItemListView.
*/
void headerContextMenuRequested(const QPointF& pos);
/**
* Is emitted if the item with the index \p index gets hovered.

View file

@ -19,6 +19,8 @@
#include "kitemlistheader_p.h"
#include <KAction>
#include <KMenu>
#include "kitemmodelbase.h"
#include <QApplication>
@ -143,11 +145,15 @@ void KItemListHeader::paint(QPainter* painter, const QStyleOptionGraphicsItem* o
void KItemListHeader::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
event->accept();
updatePressedRoleIndex(event->pos());
m_pressedMousePos = event->pos();
m_roleOperation = isAboveRoleGrip(m_pressedMousePos, m_pressedRoleIndex) ?
ResizeRoleOperation : NoRoleOperation;
if (event->button() & Qt::LeftButton) {
updatePressedRoleIndex(event->pos());
m_pressedMousePos = event->pos();
m_roleOperation = isAboveRoleGrip(m_pressedMousePos, m_pressedRoleIndex) ?
ResizeRoleOperation : NoRoleOperation;
event->accept();
} else {
event->ignore();
}
}
void KItemListHeader::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)

View file

@ -434,6 +434,37 @@ bool KItemListView::isTransactionActive() const
return m_activeTransactions > 0;
}
void KItemListView::setHeaderShown(bool show)
{
if (show && !m_header) {
m_header = new KItemListHeader(this);
m_header->setPos(0, 0);
m_header->setModel(m_model);
m_header->setVisibleRoles(m_visibleRoles);
m_header->setVisibleRolesWidths(headerRolesWidths());
m_header->setZValue(1);
m_useHeaderWidths = false;
connect(m_header, SIGNAL(visibleRoleWidthChanged(QByteArray,qreal,qreal)),
this, SLOT(slotVisibleRoleWidthChanged(QByteArray,qreal,qreal)));
m_layouter->setHeaderHeight(m_header->size().height());
} else if (!show && m_header) {
delete m_header;
m_header = 0;
m_useHeaderWidths = false;
m_layouter->setHeaderHeight(0);
}
}
bool KItemListView::isHeaderShown() const
{
return m_header != 0;
}
QPixmap KItemListView::createDragPixmap(const QSet<int>& indexes) const
{
Q_UNUSED(indexes);
@ -1519,30 +1550,6 @@ void KItemListView::updateStretchedVisibleRolesSizes()
}
}
void KItemListView::setHeaderShown(bool show)
{
if (show && !m_header) {
m_header = new KItemListHeader(this);
m_header->setPos(0, 0);
m_header->setModel(m_model);
m_header->setVisibleRoles(m_visibleRoles);
m_header->setVisibleRolesWidths(headerRolesWidths());
m_header->setZValue(1);
m_useHeaderWidths = false;
connect(m_header, SIGNAL(visibleRoleWidthChanged(QByteArray,qreal,qreal)),
this, SLOT(slotVisibleRoleWidthChanged(QByteArray,qreal,qreal)));
m_layouter->setHeaderHeight(m_header->size().height());
} else if (!show && m_header) {
delete m_header;
m_header = 0;
m_useHeaderWidths = false;
m_layouter->setHeaderHeight(0);
}
}
qreal KItemListView::visibleRolesSizesWidthSum() const
{
qreal widthSum = 0;
@ -1565,6 +1572,11 @@ qreal KItemListView::visibleRolesSizesHeightSum() const
return heightSum;
}
QRectF KItemListView::headerBoundaries() const
{
return m_header ? m_header->geometry() : QRectF();
}
int KItemListView::calculateAutoScrollingIncrement(int pos, int range, int oldInc)
{
int inc = 0;

View file

@ -180,6 +180,13 @@ public:
void endTransaction();
bool isTransactionActive() const;
/**
* Turns on the header if \p show is true. Per default the
* header is not shown.
*/
void setHeaderShown(bool show);
bool isHeaderShown() const;
/**
* @return Pixmap that is used for a drag operation based on the
* items given by \a indexes. The default implementation returns
@ -346,12 +353,6 @@ private:
*/
void updateStretchedVisibleRolesSizes();
/**
* Turns on the header if \p show is true. Per default the
* header is not shown.
*/
void setHeaderShown(bool show);
/**
* @return Sum of the widths of all visible roles.
*/
@ -362,6 +363,12 @@ private:
*/
qreal visibleRolesSizesHeightSum() const;
/**
* @return Boundaries of the header. An empty rectangle is returned
* if no header is shown.
*/
QRectF headerBoundaries() const;
/**
* Helper function for triggerAutoScrolling().
* @param pos Logical position of the mouse relative to the range.

View file

@ -170,7 +170,9 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) :
connect(controller, SIGNAL(itemActivated(int)),
this, SLOT(slotItemActivated(int)));
connect(controller, SIGNAL(itemMiddleClicked(int)), this, SLOT(slotItemMiddleClicked(int)));
connect(controller, SIGNAL(contextMenuRequested(int,QPointF)), this, SLOT(slotContextMenuRequested(int,QPointF)));
connect(controller, SIGNAL(itemContextMenuRequested(int,QPointF)), this, SLOT(slotItemContextMenuRequested(int,QPointF)));
connect(controller, SIGNAL(viewContextMenuRequested(QPointF)), this, SLOT(slotViewContextMenuRequested(QPointF)));
connect(controller, SIGNAL(headerContextMenuRequested(QPointF)), this, SLOT(slotHeaderContextMenuRequested(QPointF)));
connect(controller, SIGNAL(itemExpansionToggleClicked(int)), this, SLOT(slotItemExpansionToggleClicked(int)));
connect(controller, SIGNAL(itemHovered(int)), this, SLOT(slotItemHovered(int)));
connect(controller, SIGNAL(itemUnhovered(int)), this, SLOT(slotItemUnhovered(int)));
@ -699,32 +701,6 @@ void DolphinView::mouseReleaseEvent(QMouseEvent* event)
setActive(true);
}
void DolphinView::contextMenuEvent(QContextMenuEvent* event)
{
Q_UNUSED(event);
const QPoint pos = m_container->mapFromGlobal(QCursor::pos());
const KItemListView* view = m_container->controller()->view();
if (view->itemAt(pos) < 0) {
// Only open the context-menu if the cursor is above the viewport
// (the context-menu for items is handled in slotContextMenuRequested())
requestContextMenu(KFileItem(), url(), QList<QAction*>());
}
}
void DolphinView::wheelEvent(QWheelEvent* event)
{
if (event->modifiers().testFlag(Qt::ControlModifier)) {
const int numDegrees = event->delta() / 8;
const int numSteps = numDegrees / 15;
setZoomLevel(zoomLevel() + numSteps);
event->accept();
} else {
event->ignore();
}
}
void DolphinView::activate()
{
setActive(true);
@ -760,14 +736,63 @@ void DolphinView::slotItemMiddleClicked(int index)
}
}
void DolphinView::slotContextMenuRequested(int index, const QPointF& pos)
void DolphinView::slotItemContextMenuRequested(int index, const QPointF& pos)
{
Q_UNUSED(pos);
if (GeneralSettings::showToolTips()) {
m_toolTipManager->hideToolTip();
}
const KFileItem item = fileItemModel()->fileItem(index);
emit requestContextMenu(item, url(), QList<QAction*>());
emit requestContextMenu(pos.toPoint(), item, url(), QList<QAction*>());
}
void DolphinView::slotViewContextMenuRequested(const QPointF& pos)
{
if (GeneralSettings::showToolTips()) {
m_toolTipManager->hideToolTip();
}
emit requestContextMenu(pos.toPoint(), KFileItem(), url(), QList<QAction*>());
}
void DolphinView::slotHeaderContextMenuRequested(const QPointF& pos)
{
QWeakPointer<KMenu> menu = new KMenu(QApplication::activeWindow());
KItemListView* view = m_container->controller()->view();
const QSet<QByteArray> visibleRolesSet = view->visibleRoles().toSet();
// Add all roles to the menu that can be shown or hidden by the user
const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance();
const QList<DolphinView::AdditionalInfo> keys = infoAccessor.keys();
foreach (const DolphinView::AdditionalInfo info, keys) {
const QByteArray& role = infoAccessor.role(info);
if (role != "name") {
const QString text = fileItemModel()->roleDescription(role);
QAction* action = menu.data()->addAction(text);
action->setCheckable(true);
action->setChecked(visibleRolesSet.contains(role));
action->setData(info);
}
}
QAction* action = menu.data()->exec(pos.toPoint());
if (action) {
// Show or hide the selected role
const DolphinView::AdditionalInfo info =
static_cast<DolphinView::AdditionalInfo>(action->data().toInt());
const QByteArray selectedRole = infoAccessor.role(info);
QList<QByteArray> visibleRoles = view->visibleRoles();
if (action->isChecked()) {
const int index = keys.indexOf(info);
visibleRoles.insert(index + 1, selectedRole);
} else {
visibleRoles.removeOne(selectedRole);
}
view->setVisibleRoles(visibleRoles);
}
delete menu.data();
}
void DolphinView::slotItemExpansionToggleClicked(int index)
@ -851,18 +876,6 @@ void DolphinView::emitSelectionChangedSignal()
emit selectionChanged(selectedItems());
}
void DolphinView::openContextMenu(const QPoint& pos,
const QList<QAction*>& customActions)
{
KFileItem item;
const int index = m_container->controller()->view()->itemAt(pos);
if (index >= 0) {
item = fileItemModel()->fileItem(index);
}
emit requestContextMenu(item, url(), customActions);
}
void DolphinView::dropUrls(const KFileItem& destItem,
const KUrl& destPath,
QDropEvent* event)

View file

@ -488,7 +488,8 @@ signals:
* for the URL should be shown and the custom actions \a customActions
* will be added.
*/
void requestContextMenu(const KFileItem& item,
void requestContextMenu(const QPoint& pos,
const KFileItem& item,
const KUrl& url,
const QList<QAction*>& customActions);
@ -551,8 +552,6 @@ signals:
protected:
virtual void mouseReleaseEvent(QMouseEvent* event);
virtual void contextMenuEvent(QContextMenuEvent* event);
virtual void wheelEvent(QWheelEvent* event);
private slots:
/**
@ -563,7 +562,9 @@ private slots:
void slotItemActivated(int index);
void slotItemMiddleClicked(int index);
void slotContextMenuRequested(int index, const QPointF& pos);
void slotItemContextMenuRequested(int index, const QPointF& pos);
void slotViewContextMenuRequested(const QPointF& pos);
void slotHeaderContextMenuRequested(const QPointF& pos);
void slotItemExpansionToggleClicked(int index);
void slotItemHovered(int index);
void slotItemUnhovered(int index);
@ -585,13 +586,6 @@ private slots:
*/
void emitSelectionChangedSignal();
/**
* Opens the context menu on position \a pos. The position
* is used to check whether the context menu is related to an
* item or to the viewport.
*/
void openContextMenu(const QPoint& pos, const QList<QAction*>& customActions);
/**
* Drops dragged URLs to the destination path \a destPath. If
* the URLs are dropped above an item inside the destination path,