kde-workspace/khotkeys/kcm_hotkeys/kcm_hotkeys.cpp
2014-11-13 19:30:51 +02:00

437 lines
12 KiB
C++

/*
Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz>
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 "kcm_hotkeys.h"
#include "hotkeys_context_menu.h"
#include "ui_kcm_hotkeys.h"
#include "kcm_module_factory.h"
#include <typeinfo>
// ACTION_DATAS
#include "action_data/action_data_group.h"
// OUR ACTION WIDGETS
#include "action_group_widget.h"
#include "simple_action_data_widget.h"
#include "global_settings_widget.h"
// REST
#include "daemon/daemon.h"
#include "khotkeys_interface.h"
#include "hotkeys_model.h"
#include "hotkeys_proxy_model.h"
#include "hotkeys_tree_view.h"
#include "khotkeysglobal.h"
#include <QtDBus/QtDBus>
#include <KDE/KAboutData>
#include <KDE/KDebug>
#include <KDE/KLocale>
#include <KDE/KMessageBox>
#include <KDE/KPluginLoader>
#include <config-workspace.h>
class KCMHotkeysPrivate : public Ui::KCMHotkeysWidget
{
public:
KCMHotkeysPrivate( KCMHotkeys *host );
/** The model holding the shortcut settings. Beware! There is a proxy
* model between us and that model */
KHotkeysModel *model;
//! Our host
KCMHotkeys *q;
//! The currently shown dialog
HotkeysWidgetIFace *current;
//! The currently shown item
QModelIndex currentIndex;
/**
* Show the widget. If the current widget has changes allow
* cancelation ! of this action
*/
bool maybeShowWidget(const QModelIndex &next);
/**
* Applies the changes from the current item
*/
void applyCurrentItem();
/**
* A Hotkey was changed. Update the treeview.
*/
void _k_hotkeyChanged(KHotKeys::ActionDataBase*);
/**
* Activate the current item. The method is needed because of qt bug
*/
void _k_activateCurrentItem();
void load();
void save();
};
KCMHotkeys::KCMHotkeys( QWidget *parent, const QVariantList & /* args */ )
: KCModule( KCMModuleFactory::componentData(), parent )
,d( new KCMHotkeysPrivate(this) )
{
// Inform KCModule of the buttons we support
KCModule::setButtons(KCModule::Buttons(KCModule::Default | KCModule::Apply | KCModule::Help));
// Add the about data
KAboutData *about = new KAboutData(
"khotkeys",
0,
ki18n("KDE Hotkeys Configuration Module"),
WORKSPACE_VERSION_STRING,
KLocalizedString(),
KAboutData::License_GPL,
ki18n("Copyright 2008 (c) Michael Jansen")
);
about->addAuthor(
ki18n("Michael Jansen"),
ki18n("Maintainer"),
"kde@michael-jansen.biz" );
setAboutData(about);
// Tell KCModule we were changed.
connect(
d->action_group, SIGNAL(changed(bool)),
this, SIGNAL(changed(bool)) );
connect(
d->simple_action, SIGNAL(changed(bool)),
this, SIGNAL(changed(bool)) );
connect(
d->global_settings, SIGNAL(changed(bool)),
this, SIGNAL(changed(bool)) );
// Update TreeView if hotkeys was changed
connect(
d->simple_action, SIGNAL(changed(KHotKeys::ActionDataBase*)),
this, SLOT(_k_hotkeyChanged(KHotKeys::ActionDataBase*)));
connect(
d->action_group, SIGNAL(changed(KHotKeys::ActionDataBase*)),
this, SLOT(_k_hotkeyChanged(KHotKeys::ActionDataBase*)));
// Show the context menu
d->menu_button->setMenu(new HotkeysTreeViewContextMenu(d->tree_view));
// Switch to the global settings dialog
connect(d->settings_button, SIGNAL(clicked(bool)),
SLOT(showGlobalSettings()));
}
void KCMHotkeys::currentChanged( const QModelIndex &pCurrent, const QModelIndex &pPrevious )
{
// We're not interested in changes of columns. Just compare the rows
QModelIndex current =
pCurrent.isValid()
? pCurrent.sibling( pCurrent.row(), 0 )
: QModelIndex();
QModelIndex previous =
pPrevious.isValid()
? pPrevious.sibling( pPrevious.row(), 0 )
: QModelIndex();
// Now it's possible for previous and current to be the same
if (current==previous || current==d->currentIndex)
{
return;
}
// Current and previous differ. Ask user if there are unsaved changes
if ( !d->maybeShowWidget(current) )
{
// Bring focus back to the current item
d->tree_view->selectionModel()->setCurrentIndex(
d->currentIndex,
QItemSelectionModel::SelectCurrent);
// See http://www.qtsoftware.com/developer/task-tracker/index_html?method=entry&id=132516
QTimer::singleShot(0, this, SLOT(_k_activateCurrentItem()));
return;
}
if (!current.isValid())
{
return showGlobalSettings();
}
// Now go on and activate the new item;
KHotKeys::ActionDataBase *item = d->model->indexToActionDataBase( current );
QModelIndex typeOfIndex = d->model->index( current.row(), KHotkeysModel::TypeColumn, current.parent() );
switch (d->model->data( typeOfIndex ).toInt())
{
case KHotkeysModel::SimpleActionData:
{
KHotKeys::SimpleActionData *data = dynamic_cast<KHotKeys::SimpleActionData*>(item);
if (data)
{
d->simple_action->setActionData( data );
d->current = d->simple_action;
}
}
break;
case KHotkeysModel::ActionDataGroup:
{
KHotKeys::ActionDataGroup *group = dynamic_cast<KHotKeys::ActionDataGroup*>(item);
if (group)
{
d->action_group->setActionData( group );
d->current = d->action_group;
}
}
break;
default:
{
const std::type_info &ti = typeid(*item);
kDebug() << "##### Unknown ActionDataType " << ti.name();
}
} // switch
d->currentIndex = current;
d->stack->setCurrentWidget( d->current );
}
KCMHotkeys::~KCMHotkeys()
{
delete d; d=0;
}
void KCMHotkeys::defaults()
{
kWarning() << "not yet implemented!";
}
void KCMHotkeys::load()
{
showGlobalSettings();
d->load();
}
void KCMHotkeys::showGlobalSettings()
{
d->current = d->global_settings;
d->currentIndex = QModelIndex();
d->tree_view->setCurrentIndex(d->currentIndex);
d->global_settings->copyFromObject();
d->stack->setCurrentWidget( d->global_settings );
}
void KCMHotkeys::slotChanged()
{
emit changed(true);
}
void KCMHotkeys::slotReset()
{
showGlobalSettings();
}
void KCMHotkeys::save()
{
d->save();
emit changed(false);
}
// ==========================================================================
// KCMHotkeysPrivate
KCMHotkeysPrivate::KCMHotkeysPrivate( KCMHotkeys *host )
: Ui::KCMHotkeysWidget()
,model(NULL)
,q(host)
,current(NULL)
{
setupUi(q);
// Initialize the global part of the khotkeys lib ( handler ... )
KHotKeys::init_global_data(false, q);
}
void KCMHotkeysPrivate::load()
{
// Start khotkeys
KHotKeys::Daemon::start();
// disconnect the signals
if (tree_view->selectionModel())
{
QObject::disconnect(
tree_view->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
q, SLOT(currentChanged(QModelIndex,QModelIndex)) );
}
// Create a new model;
tree_view->setModel(new KHotkeysModel);
// Delete the old
delete model;
// Now use the old
model = tree_view->model();
model->load();
global_settings->setModel(model);
QObject::connect(
model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
q, SLOT(slotChanged()));
QObject::connect(
model, SIGNAL(rowsInserted(QModelIndex,int,int)),
q, SLOT(slotChanged()));
QObject::connect(
model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
q, SLOT(slotChanged()));
QObject::connect(
model, SIGNAL(modelAboutToBeReset()),
q, SLOT(slotReset()));
// reconnect the signals
QObject::connect(
tree_view->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
q, SLOT(currentChanged(QModelIndex,QModelIndex)) );
}
bool KCMHotkeysPrivate::maybeShowWidget(const QModelIndex &nextIndex)
{
kDebug();
// If the current widget is changed, ask user if switch is ok
if (current && (currentIndex != nextIndex) && current->isChanged())
{
int choice = KMessageBox::warningContinueCancel(
q,
i18n("The current action has unsaved changes. If you continue these changes will be lost."),
i18n("Save changes") );
if (choice != KMessageBox::Continue)
{
return false;
}
// Apply the changes from the current item
//applyCurrentItem();
//save();
}
return true;
}
void KCMHotkeysPrivate::save()
{
if (current)
applyCurrentItem();
// Write the settings
model->save();
if (!KHotKeys::Daemon::isRunning())
{
if (!KHotKeys::Daemon::start())
{
// On startup the demon does the updating stuff, therefore reload
// the actions.
model->load();
}
else
{
KMessageBox::error(
q,
"<qt>" + i18n("Unable to contact khotkeys. Your changes are saved, but they could not be activated.") + "</qt>" );
}
return;
}
// Inform kdedkhotkeys demon to reload settings
QDBusConnection bus = QDBusConnection::sessionBus();
QPointer<OrgKdeKhotkeysInterface> iface = new OrgKdeKhotkeysInterface(
"org.kde.kded",
"/modules/khotkeys",
bus,
q);
QDBusError err;
if(!iface->isValid())
{
err = iface->lastError();
if (err.isValid())
{
kError() << err.name() << ":" << err.message();
}
KMessageBox::error(
q,
"<qt>" + i18n("Unable to contact khotkeys. Your changes are saved, but they could not be activated.") + "</qt>" );
return;
}
// Reread the configuration. We have no possibility to check if it worked.
iface->reread_configuration();
}
void KCMHotkeysPrivate::applyCurrentItem()
{
Q_ASSERT( current );
// Only save when really changed
if (current->isChanged())
{
current->apply();
}
}
void KCMHotkeysPrivate::_k_hotkeyChanged(KHotKeys::ActionDataBase* hotkey)
{
model->emitChanged(hotkey);
}
void KCMHotkeysPrivate::_k_activateCurrentItem()
{
tree_view->selectionModel()->setCurrentIndex(
tree_view->selectionModel()->currentIndex(),
QItemSelectionModel::SelectCurrent);
}
#include "moc_kcm_hotkeys.cpp"