Commit 6eb19dd7 authored by promayon's avatar promayon
Browse files

NEW User can now add/remove specific action, saved in the settings they can be made permanent

NEW Setting dialog manages system-wide component and action extensions (showing camitk icon) as well as user component and action extensions (showing a "user" icon)
FIXED ActionManager code cleaning (follows singleton/construct at first use idiom, store action extension file name...etc...), autoload also loads user custom action
FIXED ActionExtension code now allows for direct management of map<filename,ActionExtions*) in ActionManager


git-svn-id: svn+ssh://scm.forge.imag.fr/var/lib/gforge/chroot/scmrepos/svn/camitk/trunk/camitk@67 ec899d31-69d1-42ba-9299-647d76f65fb3
parent 731c0595
......@@ -26,14 +26,26 @@ $CAMITK_LICENCE_END$
// -- Core stuff
#include "ActionExtension.h"
#include "ActionManager.h"
#include "Action.h"
namespace camitk
{
namespace camitk {
// -------------------- destructor --------------------
ActionExtension::~ActionExtension() {
while (!actions.empty()) {
delete actions.takeFirst();
}
}
// -------------------- registerAction --------------------
void ActionExtension::registerAction(Action* action) {
// simply register the action
ActionManager::addAction(action);
// simply add the action in the list
actions.append(action);
}
// -------------------- getActions --------------------
const camitk::ActionList& ActionExtension::getActions() {
return actions;
}
......
......@@ -39,7 +39,7 @@ namespace camitk
{
class Action;
// a bit dangerous, but greatly simply syntax (and explanation)
// a bit simplistic, but greatly simply syntax (and explanation)
// to be used in an ActionExtension register method
#define registerNewAction(X) registerAction(new X(this))
......@@ -62,7 +62,7 @@ protected :
public :
/// destructor
virtual ~ActionExtension() {};
virtual ~ActionExtension();
/// returns the action extension name (to be overriden in your ActionExtension)
virtual QString getName() = 0;
......@@ -72,10 +72,16 @@ public :
/// this method should just call registerNewAction(MyAction) for any MyAction class you need to register by this extension
virtual void init() = 0;
/// get the list of actions registered y this extension
const ActionList & getActions();
protected:
/// register an action instance
void registerAction(Action *);
/// the list of actions
ActionList actions;
};
}
......
......@@ -24,6 +24,7 @@ $CAMITK_LICENCE_END$
*****************************************************************************/
#include "ActionManager.h"
#include "Application.h"
#include "Component.h"
#include "Action.h"
#include "ActionExtension.h"
......@@ -33,13 +34,36 @@ $CAMITK_LICENCE_END$
#include <QPluginLoader>
#include <QMessageBox>
#include <QDir>
#include <QSettings>
namespace camitk {
// ---------------- allActions ----------------
QMap<QString,Action*> ActionManager::allActions;
// -------------------- getActionExtensionMap --------------------
QMap<QString, ActionExtension*> & ActionManager::getActionExtensionMap() {
static QMap<QString, ActionExtension*> actionExtensionMap;
return actionExtensionMap;
}
// -------------------- getActionMap --------------------
QMap<QString, Action*> & ActionManager::getActionMap() {
static QMap<QString, Action*> actionMap;
return actionMap;
}
// -------------------- getActions --------------------
const ActionList ActionManager::getActions() {
return getActionMap().values();
}
// -------------------- getActionExtensions --------------------
const QMap< QString, ActionExtension* >& ActionManager::getActionExtensions() {
return getActionExtensionMap();
}
// ---------------- actionLessThan ----------------
bool actionLessThan(const camitk::Action* a1, const camitk::Action * a2){
bool actionLessThan(const camitk::Action* a1, const camitk::Action * a2) {
// This method is needed by qsort in the sort method to sort action by name
return a1->getName() < a2->getName();
}
......@@ -55,14 +79,14 @@ ActionList ActionManager::sort(ActionSet actionSet) {
// ---------------- getAction ----------------
Action * ActionManager::getAction(QString name) {
return allActions.value(name);
return getActionMap().value(name);
}
// ---------------- getActionSet ----------------
ActionList ActionManager::getActions(Component *component) {
ActionSet actions;
QStringList componentHierarchy = component->getHierarchy();
foreach (Action * currentAct, ActionManager::allActions.values()) {
foreach (Action * currentAct, ActionManager::getActions()) {
if (componentHierarchy.contains(currentAct->getComponent()))
actions.insert(currentAct);
}
......@@ -98,26 +122,6 @@ ActionList ActionManager::getActions(ComponentList selComp, QString tag) {
}
// ---------------- addAction ----------------
void ActionManager::addAction(Action * action) {
// check if an action with same name was not already registered
if (ActionManager::allActions.contains(action->getName())) {
CAMITK_ERROR("ActionManager","addAction", "Cannot add action: " << action->getName().toStdString()
<< "(extension: " << action->getExtensionName().toStdString()
<< ", family: " << action->getFamily().toStdString()
<< ", description:\"" << action->getDescription().toStdString()
<< "\"): extension of same name already registered by extension \"" << ActionManager::allActions.value(action->getName())->getExtensionName().toStdString() << "\"");
}
else
ActionManager::allActions.insert(action->getName(),action);
}
// ---------------- removeAction ----------------
void ActionManager::removeAction(Action * action) {
ActionManager::allActions.remove(action->getName());
delete action;
}
// -------------------- autoloadActions --------------------
void ActionManager::autoloadActions() {
......@@ -133,8 +137,8 @@ void ActionManager::autoloadActions() {
QStringList extensionFile;
extensionFile << "*.so" << "*.dll" << ".dylib";
// loop to load component extension, taking into account internal dependencies (i.e. dependency between
// one component and another one.
// loop to load action extension, taking into account internal dependencies (i.e. dependency between
// one action and another one.
int i = 0;
QStringList extensionFileNames = actionsDir.entryList(extensionFile, QDir::Files);
// if there is more than 5 dependency levels, then you have two choices:
......@@ -145,7 +149,7 @@ void ActionManager::autoloadActions() {
do {
i = 0;
while (i < extensionFileNames.size()) {
if (loadAction(actionsDir.absoluteFilePath(extensionFileNames.at(i))))
if (loadExtension(actionsDir.absoluteFilePath(extensionFileNames.at(i))))
// this one is loaded, remove it from the list to load
extensionFileNames.removeAt(i);
else
......@@ -158,19 +162,46 @@ void ActionManager::autoloadActions() {
QMessageBox::warning(NULL, "ActionManager Opening Error...", "AutoLoad Extension failed after " + QString::number(tryNr) + " tries for the following action(s):<ul><li>" + extensionFileNames.join("</li><li>") + "</li></ul>Actions extension directory: <tt>" + Core::getActionDir() + "</tt>");
}
}
//-- user extensions
QSettings & settings = Application::getSettings();
settings.beginGroup ( "UserExtensions" );
QStringList userActions = settings.value("actions", QVariant(QStringList())).toStringList();
settings.endGroup();
foreach(QString userActionFile, userActions) {
loadExtension(userActionFile);
}
}
// -------------------- loadAction --------------------
bool ActionManager::loadAction(QString fileName) {
// -------------------- loadExtension --------------------
bool ActionManager::loadExtension(QString fileName) {
QPluginLoader pluginLoader(fileName);
QObject *extension = pluginLoader.instance();
if (extension) {
ActionExtension *cp = qobject_cast<ActionExtension *> (extension);
if (cp) {
cp->init();
ActionExtension *ext = qobject_cast<ActionExtension *> (extension);
if (ext) {
//-- register the filename
getActionExtensionMap().insert(fileName, ext);
// initialize all actions
ext->init();
//-- register actions
foreach(Action *action, ext->getActions()) {
// check if an action with same name was not already registered
if (getActionMap().contains(action->getName())) {
CAMITK_ERROR("ActionManager", "loadExtension", "Cannot add action: " << action->getName().toStdString()
<< "(extension: " << action->getExtensionName().toStdString()
<< ", loading from file: " << fileName.toStdString()
<< ", family: " << action->getFamily().toStdString()
<< ", description:\"" << action->getDescription().toStdString()
<< "\"): extension of same name already registered by extension \"" << ActionManager::getActionMap().value(action->getName())->getExtensionName().toStdString() << "\"");
}
else
getActionMap().insert(action->getName(), action);
}
return true;
}
}
......@@ -182,11 +213,21 @@ bool ActionManager::loadAction(QString fileName) {
}
// -------------------- unloadAction --------------------
bool ActionManager::unloadAction(Action * action) {
ActionManager::removeAction(action);
bool unloaded = true;
return unloaded;
// -------------------- unloadExtension --------------------
bool ActionManager::unloadExtension(QString fileName) {
if (getActionExtensionMap().contains(fileName)) {
ActionExtension *ext = getActionExtensionMap().value(fileName);
//-- unregister all actions
foreach(Action *action, ext->getActions()) {
getActionMap().remove(action->getName());
}
//-- unregister extension
getActionExtensionMap().remove(fileName);
//-- delete
delete ext;
}
else
return false;
}
}
......
......@@ -47,10 +47,6 @@ class Action;
*/
class CAMITK_API ActionManager {
public :
/// Only the class Action can access to the private member addAction(..)
friend class ActionExtension;
public :
/// @name static method to get an action or an action set
//@{
......@@ -80,29 +76,41 @@ public :
/// load an action extension using a .dll/.so/.dylib filename
/// @return false if it cannot be loaded
static bool loadAction(QString);
static bool loadExtension(QString);
/// unload an action extension using its .dll/.so/.dylib filename
static bool unloadExtension(QString);
/// unload a extension corresponding to a given action
static bool unloadAction(Action * );
/** get all the registered action extension.
* This is the public method (return a const, the extensions map is private
* and cannot be modified externally).
*/
static const QMap<QString, ActionExtension*> & getActionExtensions();
//@}
private:
/// all the registered actions, this is a QMap as actions are mainly sort/compared/process by name (key)
static QMap<QString,Action*> allActions;
/// add an action to the registered actions set
static void addAction(Action *);
/// remove an action from the registered actions set
static void removeAction(Action *);
/** all the registered actions, this is a QMap as actions are mainly sort/compared/process by name (key).
* This is the private (intern) method.
* The component extension map is updated by loadExtension, unloadExtension and autoloadExtensions.
* This method follows the "construct on first use" idiom/design-pattern.
* It therefore avoids the infamous "static initialization order fiasco",
* see http://www.parashift.com/c++-faq/ctors.html
*/
static QMap<QString,Action*> & getActionMap();
/** get the singleton map of loaded component plugins for files (the key is the file extension)
* This is the private (intern) method.
* The component extension map is updated by loadExtension, unloadExtension and autoloadExtensions.
* This method follows the "construct on first use" idiom/design-pattern.
* It therefore avoids the infamous "static initialization order fiasco",
* see http://www.parashift.com/c++-faq/ctors.html
*/
static QMap<QString, ActionExtension*> & getActionExtensionMap();
/// sort an ActionSet by action's name
static ActionList sort(ActionSet);
};
inline const ActionList ActionManager::getActions() {
return allActions.values();
}
}
......
......@@ -40,7 +40,7 @@
<bool>true</bool>
</property>
<property name="currentIndex">
<number>0</number>
<number>2</number>
</property>
<widget class="QWidget" name="generalTab">
<attribute name="title">
......@@ -271,12 +271,17 @@
</property>
<column>
<property name="text">
<string>Action</string>
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Aim Component</string>
<string>Target Component</string>
</property>
</column>
<column>
<property name="text">
<string>Location</string>
</property>
</column>
</widget>
......
......@@ -28,9 +28,10 @@ $CAMITK_LICENCE_END$
#include "Application.h"
#include "Core.h"
#include "ExtensionsManager.h"
#include "ComponentExtension.h"
#include "ActionExtension.h"
#include "ActionManager.h"
#include "Action.h"
#include "ComponentExtension.h"
#include "ObjectController.h"
#include "ui_Settings.h"
......@@ -53,6 +54,7 @@ SettingsDialog::SettingsDialog() : QDialog(qApp->activeWindow()) {
myUI->setupUi(this);
// -- update plugin viewers
readUserExtensions();
updateComponentExtensionList();
updateActionExtensionList();
......@@ -90,6 +92,24 @@ SettingsDialog::~SettingsDialog() {
objectController = NULL;
}
// ------------------------------ readUserExtensions -------------------------------
void SettingsDialog::readUserExtensions() {
QSettings & settings = Application::getSettings();
settings.beginGroup ( "UserExtensions" );
userComponents = settings.value("components", QVariant(QStringList())).toStringList();
userActions = settings.value("actions", QVariant(QStringList())).toStringList();
settings.endGroup();
}
// ------------------------------ writeUserExtensions -------------------------------
void SettingsDialog::writeUserExtensions() {
QSettings & settings = Application::getSettings();
settings.beginGroup ( "UserExtensions" );
settings.setValue("components", userComponents );
settings.setValue("actions", userActions );
settings.endGroup();
}
// ------------------------------ editSettings -------------------------------
void SettingsDialog::editSettings(QObject *qObj) {
editedObjectMap.insert(qObj->objectName(), qObj);
......@@ -111,7 +131,6 @@ void SettingsDialog::editSettings(QObject *qObj) {
// ------------------------------ updateComponentExtensionList -------------------------------
void SettingsDialog::updateComponentExtensionList() {
//QStringList dmpList = ExtensionsManager::getFileExtensions();
const QList<ComponentExtension *> & ceList = ExtensionsManager::getComponentExtensions().values().toSet().toList();
const QList<ComponentExtension *> & ceddList = ExtensionsManager::getAllDataDirectoryComponents().values().toSet().toList();
......@@ -122,20 +141,21 @@ void SettingsDialog::updateComponentExtensionList() {
myUI->componentExtensionList->setSortingEnabled(false); // temporarily for insertion!
// loop over all ManagerExtensions
// set the different columns text and pixmaps
// columns: name, state (i.e loaded or not), extension, location
unsigned int i;
for (i = 0;i < ceList.size();i++) {
ComponentExtension *ce = ceList.at(i);
// set the different columns text and pixmaps
// columns: name, state (i.e loaded or not), extension, location
myUI->componentExtensionList->setItem(i, 0, new QTableWidgetItem((ce->getAutoload() ? QPixmap(":/ok") : QPixmap()), ce->getName()));
// set the icon depending if this is a system-wide component (camitk icon) or user settings (user icon)
myUI->componentExtensionList->setItem(i, 0, new QTableWidgetItem((userComponents.contains(ce->getLocation()) ? QPixmap(":/user") : QPixmap(":/camiTKIcon")), ce->getName()));
myUI->componentExtensionList->setItem(i, 1, new QTableWidgetItem(ce->getFileExtensions().join(", ")));
myUI->componentExtensionList->setItem(i, 2, new QTableWidgetItem(ce->getLocation()));
}
// list all data directory plugin
for (;(i-ceList.size()) < ceddList.size();i++) {
ComponentExtension *ce = ceddList.at(i-ceList.size());
myUI->componentExtensionList->setItem(i, 0, new QTableWidgetItem((ce->getAutoload() ? QPixmap(":/ok") : QPixmap()), ce->getName()));
for (;(i - ceList.size()) < ceddList.size();i++) {
ComponentExtension *ce = ceddList.at(i - ceList.size());
myUI->componentExtensionList->setItem(i, 0, new QTableWidgetItem((userComponents.contains(ce->getLocation()) ? QPixmap(":/user") : QPixmap(":/camiTKIcon")), ce->getName()));
myUI->componentExtensionList->setItem(i, 1, new QTableWidgetItem("[directory]"));
myUI->componentExtensionList->setItem(i, 2, new QTableWidgetItem(ce->getLocation()));
}
......@@ -149,21 +169,26 @@ void SettingsDialog::updateComponentExtensionList() {
// ------------------------------ updateComponentExtensionList -------------------------------
void SettingsDialog::updateActionExtensionList() {
ActionList actList = ActionManager::getActions();
const QMap<QString, ActionExtension *> & aeMap = ActionManager::getActionExtensions();
// remove all the items
myUI->actionExtensionList->clearContents();
myUI->actionExtensionList->setRowCount(actList.size());
myUI->actionExtensionList->setRowCount(ActionManager::getActions().size());
myUI->actionExtensionList->verticalHeader()->setVisible(false);
myUI->actionExtensionList->setSortingEnabled(false); // temporarily for insertion!
// loop over all ManagerExtensions
int i = 0;
foreach(Action * action, actList) {
// loop over all ActionExtensions
unsigned int i = 0;
QMapIterator<QString, ActionExtension *> it(aeMap);
while (it.hasNext()) {
it.next();
// set the different columns text and pixmaps
// columns: name, state (i.e loaded or not), extension, location
myUI->actionExtensionList->setItem(i, 0, new QTableWidgetItem(action->getName()));
myUI->actionExtensionList->setItem(i++, 1, new QTableWidgetItem(action->getComponent()));
foreach (Action *action, it.value()->getActions()) {
myUI->actionExtensionList->setItem(i, 0, new QTableWidgetItem((userActions.contains(it.key()) ? QPixmap(":/user") : QPixmap(":/camiTKIcon")), action->getName()));
myUI->actionExtensionList->setItem(i, 1, new QTableWidgetItem(action->getComponent()));
myUI->actionExtensionList->setItem(i++, 2, new QTableWidgetItem(it.key()));
}
}
myUI->actionExtensionList->resizeColumnsToContents();
......@@ -226,14 +251,8 @@ void SettingsDialog::on_addComponentExtensionButton_released() {
Application::getMainWindow()->statusBar()->showMessage("Plugin " + (*it) + " loaded.");
// Add the filename in the application settings: this is a manually loaded plugin, it should therefore
// be kept in a list for the next run
QSettings & settings = Application::getSettings();
settings.beginGroup ( "UserExtensions" );
QStringList userComponents = settings.value("components", QVariant(QStringList())).toStringList();
if(!userComponents.contains((*it))) {
userComponents+=(*it);
settings.setValue("components", userComponents );
}
settings.endGroup();
userComponents += (*it);
writeUserExtensions();
}
++it; //next one!
......@@ -251,13 +270,9 @@ void SettingsDialog::on_removeComponentExtensionButton_released() {
// remove from settings
QString location = myUI->componentExtensionList->item(myUI->componentExtensionList->currentRow(), 2)->text();
QSettings & settings = Application::getSettings();
settings.beginGroup ( "UserExtensions" );
QStringList userComponents = settings.value("components", QVariant(QStringList())).toStringList();
userComponents.removeAll(location);
settings.setValue("components", userComponents );
settings.endGroup();
writeUserExtensions();
// update the display
updateComponentExtensionList();
......@@ -268,7 +283,7 @@ void SettingsDialog::on_removeComponentExtensionButton_released() {
// ------------------------------ on_componentExtensionList_cellClicked -------------------------------
void SettingsDialog::on_componentExtensionList_cellClicked(int row, int column) {
const ComponentExtension *ce = ExtensionsManager::getExtension(myUI->componentExtensionList->item(myUI->componentExtensionList->currentRow(), 0)->text());
myUI->removeComponentExtensionButton->setEnabled(!ce->getAutoload());
myUI->removeComponentExtensionButton->setEnabled(userComponents.contains(ce->getLocation()));
myUI->componentPluginDescription->setText(ce->getDescription());
}
......@@ -280,14 +295,15 @@ void SettingsDialog::on_addActionExtensionButton_released() {
QStringList::Iterator it = pluginFile.begin();
while (it != pluginFile.end()) {
if (!ActionManager::loadAction(*it)) {
if (!ActionManager::loadExtension(*it)) {
QMessageBox::warning(this, "ActionExtension Error", "Can't load plugin from file" + (*it) + "!\nTry recompile the plugin\n(plugin might be outdated)");
}
else {
Application::getMainWindow()->statusBar()->showMessage("Plugin " + (*it) + " loaded.");
// TODO if the filename is not in the component directory, save it
// in application settings manually load components: this is a manually loaded plugin, it should therefore
// Add the filename in the application settings: this is a manually loaded plugin, it should therefore
// be kept in a list for the next run
userActions += (*it);
writeUserExtensions();
}
++it; //next one!
......@@ -301,23 +317,36 @@ void SettingsDialog::on_addActionExtensionButton_released() {
// ----------------- on_removeActionExtensionButton_released ----------------------------
void SettingsDialog::on_removeActionExtensionButton_released() {
// remove the plugin
QString actionName = myUI->actionExtensionList->item(myUI->actionExtensionList->currentRow(), 0)->text();
foreach(Action * action, ActionManager::getActions()) {
if (actionName == action->getName())
ActionManager::unloadAction(action);
QString actionFileName = myUI->actionExtensionList->item(myUI->actionExtensionList->currentRow(), 2)->text();
ActionList actions = ActionManager::getActionExtensions().value(actionFileName)->getActions();
bool reallyDelete = true;
if (actions.size() > 1) {
QString actionName = myUI->actionExtensionList->item(myUI->actionExtensionList->currentRow(), 0)->text();
QStringList actionNames;
foreach(Action *a, actions) {
if (a->getName() != actionName)
actionNames << a->getName();
}
reallyDelete = (QMessageBox::warning(this, "Removing multiple actions", "Removing action \"" + actionName + " \" will also remove the following actions:<ul><li>" + actionNames.join("</li><li>") + "</li></ul>Are you sure you want to delete all these actions?", QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes);
}
// update the display
updateActionExtensionList();
if (reallyDelete) {
ActionManager::unloadExtension(actionFileName);
// remove from settings
userActions.removeAll(actionFileName);
writeUserExtensions();
// removing is allowed by selecting a new component extension
myUI->removeActionExtensionButton->setEnabled(false);
}
// update the display
updateActionExtensionList();
// removing is allowed by selecting a new component extension
myUI->removeActionExtensionButton->setEnabled(false);
}
}
// ------------------------------ on_actionExtensionList_cellClicked -------------------------------
void SettingsDialog::on_actionExtensionList_cellClicked(int row, int column) {
myUI->removeActionExtensionButton->setEnabled(true);
myUI->removeActionExtensionButton->setEnabled(userActions.contains(myUI->actionExtensionList->item(myUI->actionExtensionList->currentRow(), 2)->text()));
myUI->actionExtensionDescription->setText(ActionManager::getAction
(myUI->actionExtensionList->item(myUI->actionExtensionList->currentRow(), 0)->text())->getDescription());
}
......
......@@ -115,6 +115,18 @@ private:
/// Qt ui designed in Qt Designer
Ui::ui_Settings *myUI;
/// the list of user component extension locations
QStringList userComponents;
/// the list of user action extension locations
QStringList userActions;
/// get the current value of the settings
void readUserExtensions();
/// write the current value of the settings
void writeUserExtensions();
};
}
......
......@@ -114,14 +114,5 @@ QStringList ComponentExtension::getExportExtensions() const {
return getFileExtensions();
}
// -------------------- getAutoload --------------------
bool ComponentExtension::getAutoload() const{
return autoload;
}
// -------------------- setAutoload --------------------
void ComponentExtension::setAutoload(bool autoload) {
this->autoload = autoload;
}
}
......@@ -94,12 +94,6 @@ public:
dynamicLibraryFileName = loc;