kde-workspace/plasma/desktop/applets/kickoff/ui/launcher.cpp
2015-02-27 09:28:46 +00:00

1069 lines
36 KiB
C++

/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2007 Kevin Ottens <ervin@kde.org>
Copyright 2009 Ivan Cukic <ivan.cukic@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "ui/launcher.h"
// System
#include <unistd.h>
// Qt
#include <QApplication>
#include <QKeyEvent>
#include <QLabel>
#include <QMouseEvent>
#include <QPushButton>
#include <QStackedWidget>
#include <QTabBar>
#include <QToolButton>
#include <QVBoxLayout>
// KDE
#include <KDebug>
#include <KGlobalSettings>
#include <KIcon>
#include <kuser.h>
#include <Plasma/Theme>
#include <Plasma/Delegate>
#include <solid/device.h>
#include <solid/deviceinterface.h>
#include <KColorScheme>
// Local
#include "core/favoritesmodel.h"
#include "core/recentlyusedmodel.h"
#include "core/applicationmodel.h"
#include "core/leavemodel.h"
#include "core/itemhandlers.h"
//#include "core/searchmodel.h"
#include "core/krunnermodel.h"
#include "core/systemmodel.h"
#include "ui/itemdelegate.h"
#include "ui/contextmenufactory.h"
#include "ui/urlitemview.h"
#include "ui/flipscrollview.h"
#include "ui/searchbar.h"
#include "ui/tabbar.h"
#include "ui/contentareacap.h"
Q_DECLARE_METATYPE(QPersistentModelIndex)
using namespace Kickoff;
class Launcher::Private
{
public:
Private(Launcher *launcher)
: q(launcher)
, applet(0)
, urlLauncher(new UrlItemLauncher(launcher))
, searchModel(0)
, leaveModel(0)
, searchBar(0)
, footer(0)
, contentAreaHeader(0)
, contentArea(0)
, contentAreaFooter(0)
, contentSwitcher(0)
, applicationView(0)
, searchView(0)
, favoritesView(0)
, contextMenuFactory(0)
, autoHide(false)
, appViewIsReceivingKeyEvents(false)
, visibleItemCount(10)
, placement(Plasma::TopPosedLeftAlignedPopup)
, panelEdge(Plasma::BottomEdge)
{
}
~Private()
{
}
enum TabOrder { NormalTabOrder, ReverseTabOrder };
void setupEventHandler(QAbstractItemView *view)
{
view->viewport()->installEventFilter(q);
view->installEventFilter(q);
}
void addView(const QString& name, const QIcon& icon,
QAbstractItemModel *model = 0, QAbstractItemView *view = 0,
QWidget *headerWidget = 0)
{
view->setFrameStyle(QFrame::NoFrame);
// prevent the view from stealing focus from the search bar
view->setFocusPolicy(Qt::NoFocus);
view->setContextMenuPolicy(Qt::CustomContextMenu);
view->setSelectionMode(QAbstractItemView::SingleSelection);
view->setDragEnabled(true);
view->setAcceptDrops(true);
view->setDropIndicatorShown(true);
if (name == i18n("Favorites")) {
view->setDragDropMode(QAbstractItemView::DragDrop);
} else if(name == i18n("Applications") || name == i18n("Computer") ||
name == i18n("Recently Used")) {
view->setDragDropMode(QAbstractItemView::DragOnly);
}
view->setModel(model);
//view->setCurrentIndex(QModelIndex());
setupEventHandler(view);
connect(view, SIGNAL(customContextMenuRequested(QPoint)), q, SLOT(showViewContextMenu(QPoint)));
contentSwitcher->addTab(icon, name);
if (!headerWidget) {
contentArea->addWidget(view);
} else {
QWidget *parent = new QWidget;
parent->setLayout(new QVBoxLayout);
parent->layout()->setSpacing(0);
parent->layout()->setContentsMargins(0, 0, 0, 0);
parent->layout()->addWidget(headerWidget);
parent->layout()->addWidget(view);
contentArea->addWidget(parent);
}
}
void initTabs()
{
// Favorites view
setupFavoritesView();
// All Programs view
setupAllProgramsView();
// System view
setupSystemView();
// Recently Used view
setupRecentView();
// Leave view
setupLeaveView();
// Search Bar
setupSearchView();
}
void setupLeaveView()
{
leaveModel = new LeaveModel(q);
leaveModel->updateModel();
UrlItemView *view = new UrlItemView();
ItemDelegate *delegate = new ItemDelegate(q);
delegate->setRoleMapping(Plasma::Delegate::SubTitleRole, SubTitleRole);
delegate->setRoleMapping(Plasma::Delegate::SubTitleMandatoryRole, SubTitleMandatoryRole);
view->setItemDelegate(delegate);
view->setItemStateProvider(delegate);
addView(i18n("Leave"), KIcon("system-shutdown"), leaveModel, view);
}
void setupFavoritesView()
{
favoritesModel = new FavoritesModel(q);
UrlItemView *view = new UrlItemView();
ItemDelegate *delegate = new ItemDelegate(q);
delegate->setRoleMapping(Plasma::Delegate::SubTitleRole, SubTitleRole);
delegate->setRoleMapping(Plasma::Delegate::SubTitleMandatoryRole, SubTitleMandatoryRole);
view->setItemDelegate(delegate);
view->setItemStateProvider(delegate);
addView(i18n("Favorites"), KIcon("bookmarks"), favoritesModel, view);
QAction *sortAscendingAction = new QAction(KIcon("view-sort-ascending"),
i18n("Sort Alphabetically (A to Z)"), q);
QAction *sortDescendingAction = new QAction(KIcon("view-sort-descending"),
i18n("Sort Alphabetically (Z to A)"), q);
connect(favoritesModel, SIGNAL(rowsInserted(QModelIndex,int,int)), q, SLOT(focusFavoritesView()));
connect(sortAscendingAction, SIGNAL(triggered()), favoritesModel, SLOT(sortFavoritesAscending()));
connect(sortDescendingAction, SIGNAL(triggered()), favoritesModel, SLOT(sortFavoritesDescending()));
favoritesView = view;
QList<QAction*> actions;
actions << sortAscendingAction << sortDescendingAction;
contextMenuFactory->setViewActions(view, actions);
}
void setupAllProgramsView()
{
applicationModel = new ApplicationModel(q);
applicationModel->setDuplicatePolicy(ApplicationModel::ShowLatestOnlyPolicy);
FlipScrollView *view = new FlipScrollView();
applicationView = view;
ItemDelegate *delegate = new ItemDelegate(q);
delegate->setRoleMapping(Plasma::Delegate::SubTitleRole, SubTitleRole);
delegate->setRoleMapping(Plasma::Delegate::SubTitleMandatoryRole, SubTitleMandatoryRole);
applicationView->setItemDelegate(delegate);
applicationBreadcrumbs = new QWidget;
applicationBreadcrumbs->setMinimumHeight(ItemDelegate::HEADER_HEIGHT);
applicationBreadcrumbs->setLayout(new QHBoxLayout);
applicationBreadcrumbs->layout()->setContentsMargins(0, 0, 0, 0);
applicationBreadcrumbs->layout()->setSpacing(0);
QPalette palette = applicationBreadcrumbs->palette();
palette.setColor(QPalette::Window, palette.color(QPalette::Active, QPalette::Base));
applicationBreadcrumbs->setPalette(palette);
applicationBreadcrumbs->setAutoFillBackground(true);
connect(applicationView, SIGNAL(currentRootChanged(QModelIndex)),
q, SLOT(fillBreadcrumbs(QModelIndex)));
connect(applicationView, SIGNAL(focusNextViewLeft()),
q, SLOT(moveViewToLeft()));
q->fillBreadcrumbs(QModelIndex());
addView(i18n("Applications"), KIcon("applications-other"),
applicationModel, applicationView, applicationBreadcrumbs);
}
void setupRecentView()
{
recentlyUsedModel = new RecentlyUsedModel(q);
UrlItemView *view = new UrlItemView();
ItemDelegate *delegate = new ItemDelegate(q);
delegate->setRoleMapping(Plasma::Delegate::SubTitleRole, SubTitleRole);
delegate->setRoleMapping(Plasma::Delegate::SubTitleMandatoryRole, SubTitleMandatoryRole);
view->setItemDelegate(delegate);
view->setItemStateProvider(delegate);
addView(i18n("Recently Used"), KIcon("document-open-recent"), recentlyUsedModel, view);
QAction *clearApplications = new QAction(KIcon("edit-clear-history"), i18n("Clear Recent Applications"), q);
QAction *clearDocuments = new QAction(KIcon("edit-clear-history"), i18n("Clear Recent Documents"), q);
connect(clearApplications, SIGNAL(triggered()), recentlyUsedModel, SLOT(clearRecentApplications()));
connect(clearDocuments, SIGNAL(triggered()), recentlyUsedModel, SLOT(clearRecentDocuments()));
contextMenuFactory->setViewActions(view, QList<QAction*>() << clearApplications << clearDocuments);
}
void setupSystemView()
{
systemModel = new SystemModel(q);
UrlItemView *view = new UrlItemView();
ItemDelegate *delegate = new ItemDelegate(q);
delegate->setRoleMapping(Plasma::Delegate::SubTitleRole, SubTitleRole);
delegate->setRoleMapping(Plasma::Delegate::SubTitleMandatoryRole, SubTitleMandatoryRole);
view->setItemDelegate(delegate);
view->setItemStateProvider(delegate);
addView(i18n("Computer"), systemIcon(), systemModel, view);
}
void setupSearchView()
{
searchModel = new KRunnerModel(q);
UrlItemView *view = new UrlItemView();
ItemDelegate *delegate = new ItemDelegate(q);
delegate->setRoleMapping(Plasma::Delegate::SubTitleRole, SubTitleRole);
delegate->setRoleMapping(Plasma::Delegate::SubTitleMandatoryRole, SubTitleMandatoryRole);
view->setItemDelegate(delegate);
view->setItemStateProvider(delegate);
view->setModel(searchModel);
view->setFrameStyle(QFrame::NoFrame);
view->setSelectionMode(QAbstractItemView::SingleSelection);
// prevent view from stealing focus from the search bar
view->setFocusPolicy(Qt::StrongFocus);
view->setDragEnabled(true);
setupEventHandler(view);
connect(searchModel, SIGNAL(resultsAvailable()), q, SLOT(resultsAvailable()));
connect(searchBar, SIGNAL(queryChanged(QString)), searchModel, SLOT(setQuery(QString)));
connect(searchBar, SIGNAL(queryChanged(QString)), q, SLOT(focusSearchView(QString)));
view->setContextMenuPolicy(Qt::CustomContextMenu);
connect(view, SIGNAL(customContextMenuRequested(QPoint)), q, SLOT(showViewContextMenu(QPoint)));
contentArea->addWidget(view);
searchView = view;
}
void registerUrlHandlers()
{
UrlItemLauncher::addGlobalHandler(UrlItemLauncher::ExtensionHandler, "desktop", new ServiceItemHandler);
UrlItemLauncher::addGlobalHandler(UrlItemLauncher::ProtocolHandler, "leave", new LeaveItemHandler);
UrlItemLauncher::addGlobalHandler(UrlItemLauncher::ProtocolHandler, "krunner", new KRunnerItemHandler);
}
QIcon systemIcon()
{
QList<Solid::Device> batteryList = Solid::Device::listFromType(Solid::DeviceInterface::Battery, QString());
if (batteryList.isEmpty()) {
return KIcon("computer");
} else {
return KIcon("computer-laptop");
}
}
void setNorthLayout(TabOrder tabOrder)
{
contentSwitcher->setShape(QTabBar::RoundedNorth);
QLayout * layout = q->layout();
delete layout;
layout = new QVBoxLayout();
layout->addWidget(contentSwitcher);
layout->addWidget(contentAreaHeader);
layout->addWidget(contentArea);
layout->addWidget(contentAreaFooter);
layout->addWidget(searchBar);
layout->addWidget(footer);
layout->setSpacing(0);
layout->setMargin(0);
q->setLayout(layout);
setTabOrder(tabOrder);
}
void setSouthLayout(TabOrder tabOrder)
{
contentSwitcher->setShape(QTabBar::RoundedSouth);
QLayout * layout = q->layout();
delete layout;
layout = new QVBoxLayout();
layout->addWidget(footer);
layout->addWidget(searchBar);
layout->addWidget(contentAreaHeader);
layout->addWidget(contentArea);
layout->addWidget(contentAreaFooter);
layout->addWidget(contentSwitcher);
layout->setSpacing(0);
layout->setMargin(0);
q->setLayout(layout);
setTabOrder(tabOrder);
}
void setWestLayout(TabOrder tabOrder)
{
contentSwitcher->setShape(QTabBar::RoundedWest);
QLayout * layout = q->layout();
delete layout;
layout = new QHBoxLayout();
layout->addWidget(contentSwitcher);
QBoxLayout * contentLayout = new QVBoxLayout();
contentLayout->addWidget(contentAreaHeader);
contentLayout->addWidget(contentArea);
contentLayout->addWidget(contentAreaFooter);
layout->addItem(contentLayout);
QBoxLayout * layout2 = new QVBoxLayout();
if (tabOrder == NormalTabOrder) {
layout2->addLayout(layout);
layout2->addWidget(searchBar);
layout2->addWidget(footer);
} else {
layout2->addWidget(footer);
layout2->addWidget(searchBar);
layout2->addLayout(layout);
}
layout->setSpacing(0);
layout->setMargin(0);
layout2->setSpacing(0);
layout2->setMargin(0);
q->setLayout(layout2);
setTabOrder(tabOrder);
}
void setEastLayout(TabOrder tabOrder)
{
contentSwitcher->setShape(QTabBar::RoundedEast);
QLayout * layout = q->layout();
delete layout;
layout = new QHBoxLayout();
QBoxLayout * contentLayout = new QVBoxLayout();
contentLayout->addWidget(contentAreaHeader);
contentLayout->addWidget(contentArea);
contentLayout->addWidget(contentAreaFooter);
layout->addItem(contentLayout);
layout->addWidget(contentSwitcher);
QBoxLayout * layout2 = new QVBoxLayout();
if (tabOrder == NormalTabOrder) {
layout2->addLayout(layout);
layout2->addWidget(searchBar);
layout2->addWidget(footer);
} else {
layout2->addWidget(footer);
layout2->addWidget(searchBar);
layout2->addLayout(layout);
}
layout->setSpacing(0);
layout->setMargin(0);
layout2->setSpacing(0);
layout2->setMargin(0);
q->setLayout(layout2);
setTabOrder(tabOrder);
}
void setTabOrder(TabOrder newOrder)
{
// identify current TabOrder, assumes favoritesView is first in normal order
TabOrder oldOrder;
if (contentArea->widget(0) == favoritesView) {
oldOrder = NormalTabOrder;
} else {
oldOrder = ReverseTabOrder;
}
if (newOrder == oldOrder) {
return;
}
// remove the and widgets and store their data in a separate structure
// remove this first so we can cleanly remove the widgets controlled by contentSwitcher
contentArea->removeWidget(searchView);
Q_ASSERT(contentArea->count() == contentSwitcher->count());
QList<WidgetTabData> removedTabs;
for (int i = contentSwitcher->count() - 1; i >= 0 ; i--) {
WidgetTabData wtd;
wtd.tabText = contentSwitcher->tabText(i);
wtd.tabToolTip = contentSwitcher->tabToolTip(i);
wtd.tabWhatsThis = contentSwitcher->tabWhatsThis(i);
wtd.tabIcon = contentSwitcher->tabIcon(i);
wtd.widget = contentArea->widget(i);
removedTabs.append(wtd);
contentSwitcher->removeTab(i);
contentArea->removeWidget(contentArea->widget(i));
}
// then reinsert them in reversed order
int i = 0;
foreach(const WidgetTabData &wtd, removedTabs) {
contentSwitcher->addTab(wtd.tabIcon, wtd.tabText);
contentSwitcher->setTabToolTip(i, wtd.tabToolTip);
contentSwitcher->setTabWhatsThis(i, wtd.tabWhatsThis);
contentArea->addWidget(wtd.widget);
++i;
}
//finally replace the searchView
contentArea->addWidget(searchView);
}
struct WidgetTabData {
QString tabText;
QString tabToolTip;
QString tabWhatsThis;
QIcon tabIcon;
QWidget *widget;
};
Launcher * const q;
Plasma::Applet *applet;
UrlItemLauncher *urlLauncher;
FavoritesModel *favoritesModel;
ApplicationModel *applicationModel;
RecentlyUsedModel *recentlyUsedModel;
KRunnerModel *searchModel;
SystemModel *systemModel;
LeaveModel *leaveModel;
SearchBar *searchBar;
QWidget *footer;
QLabel *userinfo;
ContentAreaCap *contentAreaHeader;
QStackedWidget *contentArea;
ContentAreaCap *contentAreaFooter;
TabBar *contentSwitcher;
FlipScrollView *applicationView;
QWidget *applicationBreadcrumbs;
UrlItemView *searchView;
QAbstractItemView *favoritesView;
ContextMenuFactory *contextMenuFactory;
bool autoHide;
bool appViewIsReceivingKeyEvents;
int visibleItemCount;
Plasma::PopupPlacement placement;
Plasma::Location panelEdge;
};
Launcher::Launcher(QWidget *parent)
: QWidget(parent, Qt::Window)
, d(new Private(this))
{
init();
}
Launcher::Launcher(Plasma::Applet *applet)
: QWidget(0, Qt::Window)
, d(new Private(this))
{
init();
setApplet(applet);
}
void Launcher::init()
{
QVBoxLayout *layout = new QVBoxLayout;
layout->setSpacing(0);
layout->setMargin(0);
const int rightHeaderMargin = style()->pixelMetric(QStyle::PM_ScrollBarExtent);
d->searchBar = new SearchBar(this);
if (layoutDirection() == Qt::LeftToRight) {
d->searchBar->setContentsMargins(0, 0, rightHeaderMargin, 0);
} else {
d->searchBar->setContentsMargins(rightHeaderMargin, 0, 0, 0);
}
d->searchBar->installEventFilter(this);
d->contentAreaHeader = new ContentAreaCap(this);
d->contentArea = new QStackedWidget(this);
bool flipCap = true;
d->contentAreaFooter = new ContentAreaCap(this, flipCap);
d->contentSwitcher = new TabBar(this);
d->contentSwitcher->installEventFilter(this);
d->contentSwitcher->setIconSize(QSize(48, 48));
d->contentSwitcher->setShape(QTabBar::RoundedSouth);
connect(d->contentSwitcher, SIGNAL(currentChanged(int)),
d->contentArea, SLOT(setCurrentIndex(int)));
d->contextMenuFactory = new ContextMenuFactory(this);
d->initTabs();
d->registerUrlHandlers();
// Add status information footer
d->footer = new QWidget;
char hostname[256];
hostname[0] = '\0';
if (!gethostname(hostname, sizeof(hostname))) {
hostname[sizeof(hostname)-1] = '\0';
}
KUser user;
QString fullName = user.property(KUser::FullName).toString();
QString labelText;
if (fullName.isEmpty()) {
labelText = i18nc("login name, hostname", "User <b>%1</b> on <b>%2</b>", user.loginName(), hostname);
} else {
labelText = i18nc("full name, login name, hostname", "<b>%1 (%2)</b> on <b>%3</b>", fullName, user.loginName(), hostname);
}
d->userinfo = new QLabel(labelText);
QHBoxLayout *brandingLayout = new QHBoxLayout;
brandingLayout->setMargin(3);
brandingLayout->addSpacing(ItemDelegate::ITEM_LEFT_MARGIN - 3);
brandingLayout->addWidget(d->userinfo);
brandingLayout->addStretch(2);
brandingLayout->addSpacing(rightHeaderMargin);
d->footer->setLayout(brandingLayout);
layout->addWidget(d->footer);
layout->addWidget(d->searchBar);
layout->addWidget(d->contentAreaHeader);
layout->addWidget(d->contentArea);
layout->addWidget(d->contentAreaFooter);
layout->addWidget(d->contentSwitcher);
setLayout(layout);
setAttribute(Qt::WA_TranslucentBackground);
//setBackgroundRole(QPalette::AlternateBase);
//setAutoFillBackground(true);
updateThemedPalette();
connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()),
this, SLOT(updateThemedPalette()));
setAppViewIsReceivingKeyEvents(false); // Left/Right arrow keys are delivered elsewhere (for now)
}
void Launcher::updateThemedPalette()
{
QColor color = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
QPalette p = d->userinfo->palette();
p.setColor(QPalette::Normal, QPalette::WindowText, color);
p.setColor(QPalette::Inactive, QPalette::WindowText, color);
d->userinfo->setPalette(p);
}
QSize Launcher::minimumSizeHint() const
{
QSize size;
switch (d->panelEdge) {
case Plasma::LeftEdge:
case Plasma::RightEdge:
size.rheight() = d->searchBar->sizeHint().height() +
d->footer->sizeHint().height() +
qMax(d->favoritesView->sizeHintForRow(0) * 3 + ItemDelegate::HEADER_HEIGHT, d->contentSwitcher->sizeHint().height());
size.rwidth() = d->contentSwitcher->sizeHint().width() + d->favoritesView->sizeHint().width();
break;
case Plasma::TopEdge:
case Plasma::BottomEdge:
default:
size.rheight() = d->searchBar->sizeHint().height() +
d->contentSwitcher->sizeHint().height() + d->footer->sizeHint().height() +
d->favoritesView->sizeHintForRow(0) * 3 + ItemDelegate::HEADER_HEIGHT;
size.rwidth() = d->contentSwitcher->sizeHint().width();
break;
}
return size;
}
QSize Launcher::sizeHint() const
{
return QSize(minimumSizeHint().width(), 500);
}
void Launcher::setAutoHide(bool hide)
{
d->autoHide = hide;
}
bool Launcher::autoHide() const
{
return d->autoHide;
}
void Launcher::setSwitchTabsOnHover(bool switchOnHover)
{
if (d->applet && switchOnHover != d->contentSwitcher->switchTabsOnHover()) {
KConfigGroup cg = d->applet->globalConfig();
cg.writeEntry("SwitchTabsOnHover", switchOnHover);
emit configNeedsSaving();
}
d->contentSwitcher->setSwitchTabsOnHover(switchOnHover);
}
void Launcher::setShowAppsByName(bool showAppsByName)
{
//ApplicationModel *m_applicationModel = applicationModel;
const bool wasByName = d->applicationModel->nameDisplayOrder() == Kickoff::NameBeforeDescription;
if (d->applet && showAppsByName != wasByName) {
KConfigGroup cg = d->applet->config();
cg.writeEntry("ShowAppsByName", showAppsByName);
emit configNeedsSaving();
}
if (showAppsByName) {
d->applicationModel->setNameDisplayOrder(Kickoff::NameBeforeDescription);
d->applicationModel->setPrimaryNamePolicy(Kickoff::ApplicationModel::AppNamePrimary);
d->recentlyUsedModel->setNameDisplayOrder(Kickoff::NameBeforeDescription);
d->favoritesModel->setNameDisplayOrder(Kickoff::NameBeforeDescription);
d->searchModel->setNameDisplayOrder(Kickoff::NameBeforeDescription);
} else {
d->applicationModel->setNameDisplayOrder(Kickoff::NameAfterDescription);
d->applicationModel->setPrimaryNamePolicy(Kickoff::ApplicationModel::GenericNamePrimary);
d->recentlyUsedModel->setNameDisplayOrder(Kickoff::NameAfterDescription);
d->favoritesModel->setNameDisplayOrder(Kickoff::NameAfterDescription);
d->searchModel->setNameDisplayOrder(Kickoff::NameAfterDescription);
}
}
void Launcher::setShowRecentlyInstalled(bool showRecentlyInstalled)
{
const bool wasShowRecentlyInstalled = d->applicationModel->showRecentlyInstalled();
if (d->applet && showRecentlyInstalled != wasShowRecentlyInstalled) {
KConfigGroup cg = d->applet->config();
cg.writeEntry("ShowRecentlyInstalled", showRecentlyInstalled);
emit configNeedsSaving();
}
d->applicationModel->setShowRecentlyInstalled(showRecentlyInstalled);
}
bool Launcher::switchTabsOnHover() const
{
return d->contentSwitcher->switchTabsOnHover();
}
bool Launcher::showAppsByName() const
{
return d->applicationModel->nameDisplayOrder() == Kickoff::NameBeforeDescription;
}
bool Launcher::showRecentlyInstalled() const
{
return d->applicationModel->showRecentlyInstalled();
}
void Launcher::setVisibleItemCount(int count)
{
d->visibleItemCount = count;
}
int Launcher::visibleItemCount() const
{
return d->visibleItemCount;
}
void Launcher::setAppViewIsReceivingKeyEvents(bool isReceiving)
{
d->appViewIsReceivingKeyEvents = isReceiving;
}
bool Launcher::appViewIsReceivingKeyEvents() const
{
return d->appViewIsReceivingKeyEvents;
}
void Launcher::setApplet(Plasma::Applet *applet)
{
KConfigGroup cg = applet->globalConfig();
setSwitchTabsOnHover(cg.readEntry("SwitchTabsOnHover", switchTabsOnHover()));
cg = applet->config();
setShowAppsByName(cg.readEntry("ShowAppsByName", showAppsByName()));
setVisibleItemCount(cg.readEntry("VisibleItemsCount", visibleItemCount()));
setShowRecentlyInstalled(cg.readEntry("ShowRecentlyInstalled", showRecentlyInstalled()));
d->applet = applet;
d->contextMenuFactory->setApplet(applet);
d->applicationModel->setApplet(applet);
}
void Launcher::reset()
{
d->contentSwitcher->setCurrentIndexWithoutAnimation(d->contentArea->indexOf(d->favoritesView));
d->contentArea->setCurrentWidget(d->favoritesView);
d->searchBar->clear();
d->applicationView->viewRoot();
d->leaveModel->updateModel();
}
Launcher::~Launcher()
{
delete d;
}
void Launcher::focusSearchView(const QString& query)
{
bool queryEmpty = query.isEmpty();
d->contentSwitcher->setVisible(queryEmpty);
if (!queryEmpty) {
d->contentArea->setCurrentWidget(d->searchView);
} else {
focusFavoritesView();
}
}
void Launcher::focusFavoritesView()
{
d->contentSwitcher->setCurrentIndex(d->contentArea->indexOf(d->favoritesView));
d->contentArea->setCurrentWidget(d->favoritesView);
d->contentSwitcher->setVisible(true);
d->searchBar->clear();
setAppViewIsReceivingKeyEvents(false);
}
void Launcher::moveViewToLeft()
{
d->contentSwitcher->setCurrentIndex(d->contentArea->indexOf(d->favoritesView));
d->contentArea->setCurrentWidget(d->favoritesView);
d->contentSwitcher->setVisible(true);
d->searchBar->clear();
setAppViewIsReceivingKeyEvents(false);
}
bool Launcher::eventFilter(QObject *object, QEvent *event)
{
// deliver unhandled key presses from the search bar
// (mainly arrow keys, enter) to the active view
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if ((object == d->contentSwitcher || object == d->searchBar)) {
// left/right should Nav the tabbar, unless ApplicationView is receiving them.
if (keyEvent->modifiers() == Qt::NoModifier &&
(keyEvent->key() == Qt::Key_Left || keyEvent->key() == Qt::Key_Right)) {
kDebug() << "launcher, at filter #2";
if (d->applicationView->isVisible() && appViewIsReceivingKeyEvents()) {
QCoreApplication::sendEvent(d->applicationView, event);
return true;
}
if (object == d->contentSwitcher) {
return false;
} else {
QCoreApplication::sendEvent(d->contentSwitcher, event);
return true;
}
}
// Next: Up/Down should ALWAYS go into the ApplicationView, if it is visible.
if (keyEvent->modifiers() == Qt::NoModifier &&
(keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)) {
kDebug() << "launcher, at filter #3A";
if (d->applicationView->isVisible()) {
kDebug() << "launcher, at filter #3B";
setAppViewIsReceivingKeyEvents(true);
QCoreApplication::sendEvent(d->applicationView, event);
return true;
}
}
// Third: Up/Down, Key_Enter, and Key_Return must be explicitly sent into the ApplicationView., if it is receiving Keys
if (keyEvent->key() == Qt::Key_Up ||
keyEvent->key() == Qt::Key_Down ||
keyEvent->key() == Qt::Key_Enter ||
keyEvent->key() == Qt::Key_Return) {
kDebug() << "launcher, at filter #3";
if (d->applicationView->isVisible()) {
if (appViewIsReceivingKeyEvents()) {
QCoreApplication::sendEvent(d->applicationView, event);
return true;
}
}
}
// if the search view is visible, we are passing the events to it
if (d->searchView->isVisible()) {
if (!d->searchView->initializeSelection()
|| keyEvent->key() == Qt::Key_Return
|| keyEvent->key() == Qt::Key_Enter) {
kDebug() << "Passing the event to the search view" << event;
QCoreApplication::sendEvent(d->searchView, event);
}
return true;
}
// getting for the active view, and passing the events to it
QAbstractItemView *activeView = qobject_cast<QAbstractItemView*>(d->contentArea->currentWidget());
if (activeView) {
QCoreApplication::sendEvent(activeView, event);
return true;
}
}
}
// the mouse events we are interested in are delivered to the viewport,
// other events are delivered to the view itself.
QAbstractItemView *view = qobject_cast<QAbstractItemView*>(object);
if (!view) {
view = qobject_cast<QAbstractItemView*>(object->parent());
}
if (view) {
QModelIndex openIndex;
if (event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *mouseEvent = (QMouseEvent*)event;
const QModelIndex index = view->indexAt(mouseEvent->pos());
if (index.isValid() &&
index.model()->hasChildren(index) == false &&
mouseEvent->button() == Qt::LeftButton) {
openIndex = index;
}
} else if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = (QKeyEvent*)event;
const QModelIndex index = view->currentIndex();
if (index.isValid() && index.model()->hasChildren(index) == false &&
(keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)) {
openIndex = index;
}
}
if (openIndex.isValid()) {
d->urlLauncher->openItem(openIndex);
// Clear the search bar when enter was pressed
if (event->type() == QEvent::KeyPress) {
d->searchBar->clear();
}
if (d->autoHide) {
emit aboutToHide();
}
return true;
}
}
return QWidget::eventFilter(object, event);
}
void Launcher::showViewContextMenu(const QPoint& pos)
{
QAbstractItemView *view = qobject_cast<QAbstractItemView*>(sender());
if (view) {
const QModelIndex index = view->indexAt(pos);
d->contextMenuFactory->showContextMenu(view, index, pos);
}
}
void Launcher::hideEvent(QHideEvent *event)
{
Q_UNUSED(event)
reset();
d->systemModel->stopRefreshingUsageInfo();
}
void Launcher::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Escape) {
emit aboutToHide();
}
#if 0
// allow tab switching by pressing the left or right arrow keys
if (event->key() == Qt::Key_Left && d->contentSwitcher->currentIndex() > 0) {
d->contentSwitcher->setCurrentIndex(d->contentSwitcher->currentIndex() - 1);
} else if (event->key() == Qt::Key_Right &&
d->contentSwitcher->currentIndex() < d->contentSwitcher->count() - 1) {
d->contentSwitcher->setCurrentIndex(d->contentSwitcher->currentIndex() + 1);
}
#endif
}
void Launcher::showEvent(QShowEvent *e)
{
d->searchBar->setFocus();
d->systemModel->refreshUsageInfo();
QWidget::showEvent(e);
}
void Launcher::resultsAvailable()
{
const QModelIndex root = d->searchModel->index(0, 0);
d->searchView->setCurrentIndex(d->searchModel->index(0, 0, root));
}
void Launcher::setLauncherOrigin(const Plasma::PopupPlacement placement, Plasma::Location location)
{
if (d->placement != placement) {
d->placement = placement;
Private::TabOrder normalOrder;
Private::TabOrder reverseOrder;
//QTabBar attempts to be smart and flips what we say in rtl mode so say the opposite when the tabbar is horizontal
if (QApplication::layoutDirection() == Qt::RightToLeft) {
normalOrder = Private::ReverseTabOrder;
reverseOrder = Private::NormalTabOrder;
} else {
normalOrder = Private::NormalTabOrder;
reverseOrder = Private::ReverseTabOrder;
}
switch (placement) {
case Plasma::TopPosedRightAlignedPopup:
d->setSouthLayout(reverseOrder);
break;
case Plasma::LeftPosedTopAlignedPopup:
//when the tabbar is vertical it's fine
d->setEastLayout(Private::NormalTabOrder);
break;
case Plasma::LeftPosedBottomAlignedPopup:
d->setEastLayout(Private::ReverseTabOrder);
break;
case Plasma::BottomPosedLeftAlignedPopup:
d->setNorthLayout(normalOrder);
break;
case Plasma::BottomPosedRightAlignedPopup:
d->setNorthLayout(reverseOrder);
break;
case Plasma::RightPosedTopAlignedPopup:
d->setWestLayout(Private::NormalTabOrder);
break;
case Plasma::RightPosedBottomAlignedPopup:
d->setWestLayout(Private::ReverseTabOrder);
break;
case Plasma::TopPosedLeftAlignedPopup:
default:
d->setSouthLayout(normalOrder);
break;
}
}
d->panelEdge = location;
reset();
}
void Launcher::fillBreadcrumbs(const QModelIndex &index)
{
QList<QWidget*> children = d->applicationBreadcrumbs->findChildren<QWidget*>();
foreach (QWidget *child, children) {
child->setParent(0);
child->hide();
child->deleteLater();
}
QHBoxLayout *layout = static_cast<QHBoxLayout*>(d->applicationBreadcrumbs->layout());
while (layout->count() > 0) {
delete layout->takeAt(0);
}
layout->addStretch(10);
QModelIndex current = index;
while (current.isValid()) {
addBreadcrumb(current, current == index);
current = current.parent();
}
// show a '>' only if the index is valid, and therefore All Applications is not alone up there
addBreadcrumb(QModelIndex(), !index.isValid());
}
void Launcher::addBreadcrumb(const QModelIndex &index, bool isLeaf)
{
QPushButton *button = new QPushButton(d->applicationBreadcrumbs);
button->setFont(KGlobalSettings::smallestReadableFont());
button->setFlat(true);
button->setStyleSheet("* { padding: 4 }");
button->setCursor(Qt::PointingHandCursor);
QPalette palette = button->palette();
palette.setColor(QPalette::ButtonText, palette.color(QPalette::Disabled, QPalette::ButtonText));
button->setPalette(palette);
QString suffix;
if (isLeaf) {
button->setEnabled(false);
} else {
suffix = " >";
}
if (index.isValid()) {
button->setText(index.data().toString()+suffix);
} else {
button->setText(i18n("All Applications")+suffix);
}
QVariant data = QVariant::fromValue(QPersistentModelIndex(index));
button->setProperty("applicationIndex", data);
connect(button, SIGNAL(clicked()),
this, SLOT(breadcrumbActivated()));
QHBoxLayout *layout = static_cast<QHBoxLayout*>(d->applicationBreadcrumbs->layout());
layout->insertWidget(1, button);
}
void Launcher::breadcrumbActivated()
{
QPushButton *button = static_cast<QPushButton*>(sender());
QModelIndex index = button->property("applicationIndex").value<QPersistentModelIndex>();
d->applicationView->setCurrentRoot(index);
}
#include "moc_launcher.cpp"