Skip to content

Commit

Permalink
Merge pull request #403 from ghutchis/register-script-commands
Browse files Browse the repository at this point in the history
Initial support for handling extension and tool RPC commands
  • Loading branch information
ghutchis authored Sep 13, 2023
2 parents ab3301b + 80e77b1 commit a9a65ae
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 13 deletions.
85 changes: 84 additions & 1 deletion avogadro/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ MainWindow::MainWindow(const QStringList& fileNames, bool disableSettings)
&MainWindow::setActiveTool);
connect(extension, &QtGui::ExtensionPlugin::requestActiveDisplayTypes,
this, &MainWindow::setActiveDisplayTypes);
connect(extension, &QtGui::ExtensionPlugin::registerCommand, this,
&MainWindow::registerExtensionCommand);
extension->registerCommands();

buildMenu(extension);
m_extensions.append(extension);
}
Expand Down Expand Up @@ -1486,6 +1490,38 @@ bool MainWindow::exportFile(bool async)
return saveFileAs(reply.second, reply.first->newInstance(), async);
}

bool MainWindow::exportFile(const QString& fileName, bool async)
{
if (fileName.isEmpty()) {
return false;
}

// Create one of our writers to save the file:
FileFormat* writer = nullptr;

std::vector<const FileFormat*> writers =
Io::FileFormatManager::instance().fileFormatsFromFileExtension(
QFileInfo(fileName).suffix().toStdString(),
FileFormat::File | FileFormat::Write);

if (!writers.empty()) {
writer = writers[0]->newInstance();
return saveFileAs(fileName, writer, async);
}

return false;
}

std::string MainWindow::exportString(const std::string& format)
{
std::string output;
auto* mol = qobject_cast<Molecule*>(m_moleculeModel->activeMolecule());

Io::FileFormatManager::instance().writeString(*mol, output, format);

return output;
}

bool MainWindow::saveFileAs(const QString& fileName, Io::FileFormat* writer,
bool async)
{
Expand Down Expand Up @@ -1849,7 +1885,7 @@ void MainWindow::buildMenu()
#ifndef Q_OS_MAC
action->setIcon(QIcon::fromTheme("document-export"));
#endif
connect(action, &QAction::triggered, this, &MainWindow::exportFile);
connect(action, SIGNAL(triggered()), this, SLOT(exportFile()));
// Export graphics
action = new QAction(tr("&Graphics…"), this);
m_menuBuilder->addAction(exportPath, action, 100);
Expand Down Expand Up @@ -2062,6 +2098,10 @@ void MainWindow::buildTools()
m_toolToolBar->addAction(action);

connect(action, &QAction::triggered, this, &MainWindow::toolActivated);
connect(toolPlugin, &ToolPlugin::registerCommand, this,
&MainWindow::registerToolCommand);
toolPlugin->registerCommands();

++index;
}

Expand Down Expand Up @@ -2380,4 +2420,47 @@ void MainWindow::clearQueuedFiles()
}
}

void MainWindow::registerToolCommand(QString command, QString description)
{
if (m_toolCommandMap.contains(command))
return;

m_commandDescriptionsMap.insert(command, description);

// get the calling plugin
auto* tool(qobject_cast<ToolPlugin*>(sender()));
if (!tool)
return;

m_toolCommandMap.insert(command, tool);
}

void MainWindow::registerExtensionCommand(QString command, QString description)
{
if (m_extensionCommandMap.contains(command))
return;

m_commandDescriptionsMap.insert(command, description);

// get the calling plugin
auto* extension(qobject_cast<ExtensionPlugin*>(sender()));
if (!extension)
return;

m_extensionCommandMap.insert(command, extension);
}

bool MainWindow::handleCommand(const QString& command,
const QVariantMap& options)
{
if (m_toolCommandMap.contains(command)) {
auto* tool = m_toolCommandMap.value(command);
return tool->handleCommand(command, options);
} else if (m_extensionCommandMap.contains(command)) {
auto* extension = m_extensionCommandMap.value(command);
return extension->handleCommand(command, options);
}
return false;
}

} // End of Avogadro namespace
46 changes: 42 additions & 4 deletions avogadro/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define AVOGADRO_MAINWINDOW_H

#include <QtCore/QStringList>
#include <QtCore/QVariantMap>
#include <QtWidgets/QMainWindow>

#ifdef QTTESTING
Expand Down Expand Up @@ -92,6 +93,25 @@ public slots:

void exportGraphics(QString fileName);

/**
* Export a file, using the full selection of formats capable of writing.
* The format will be guessed based on the filename extension.
* If @a async is true (default), the file is saved asynchronously.
* @return If @a async is true, this function returns true if a suitable
* writer was found (not if the write was successful). If @a async is
* false, the return value indicates whether or not the file was written
* successfully.
*/
bool exportFile(const QString& fileName, bool async = true);

/**
* Export a file, using the full selection of formats capable of writing.
* Will use @a format to determine the file format to use.
* @return String-representation of the exported file, or an empty string if
* the export failed.
*/
std::string exportString(const std::string& format);

/**
* Move @a fileName as a plugin script (i.e. put it in the correct dir)
*/
Expand Down Expand Up @@ -125,6 +145,15 @@ public slots:
m_localeCodes = codes;
}

/**
* Handle script commands
* @param command The command to execute
* @param options The options to the command
*
* @return True if the command was handled, false otherwise
*/
bool handleCommand(const QString& command, const QVariantMap& options);

signals:
/**
* Emitted when the active molecule in the application has changed.
Expand Down Expand Up @@ -181,7 +210,7 @@ protected slots:
/**
* Save the current molecule to its current fileName. If it is not a standard
* format, offer to export and warn about possible data loss.
* If @a async is true (default), the file is loaded asynchronously.
* If @a async is true (default), the file is saved asynchronously.
* @return If @a async is true, this function returns true if a suitable
* writer was found (not if the write was successful). If @a async is
* false, the return value indicates whether or not the file was written
Expand All @@ -192,7 +221,7 @@ protected slots:
/**
* Prompt for a file location, and attempt to save the active molecule to the
* specified location.
* If @a async is true (default), the file is loaded asynchronously.
* If @a async is true (default), the file is saved asynchronously.
* @return If @a async is true, this function returns true if a suitable
* writer was found (not if the write was successful). If @a async is
* false, the return value indicates whether or not the file was written
Expand All @@ -202,7 +231,7 @@ protected slots:

/**
* Export a file, using the full selection of formats capable of writing.
* If @a async is true (default), the file is loaded asynchronously.
* If @a async is true (default), the file is saved asynchronously.
* @return If @a async is true, this function returns true if a suitable
* writer was found (not if the write was successful). If @a async is
* false, the return value indicates whether or not the file was written
Expand All @@ -213,7 +242,7 @@ protected slots:
/**
* If specified, use the FileFormat @a writer to save the file. This method
* takes ownership of @a writer and will delete it before returning.
* If @a async is true (default), the file is loaded asynchronously.
* If @a async is true (default), the file is saved asynchronously.
* @return If @a async is true, this function returns true if the write begins
* successfully (not if the writer completes). If @a async is
* false, the return value indicates whether or not the file was written
Expand Down Expand Up @@ -266,6 +295,10 @@ private slots:

void finishUpdateRequest(QNetworkReply*);

void registerToolCommand(QString command, QString description);

void registerExtensionCommand(QString command, QString description);

/**
* @brief Register file formats from extensions when ready.
*/
Expand Down Expand Up @@ -397,6 +430,11 @@ private slots:
QDockWidget* m_moleculeDock;
QList<QtGui::ToolPlugin*> m_tools;
QList<QtGui::ExtensionPlugin*> m_extensions;
// map from script commands to tools and extensions
QMap<QString, QtGui::ToolPlugin*> m_toolCommandMap;
QMap<QString, QtGui::ExtensionPlugin*> m_extensionCommandMap;
// used for help - provide description for a command
QMap<QString, QString> m_commandDescriptionsMap;

QAction* m_undo;
QAction* m_redo;
Expand Down
37 changes: 29 additions & 8 deletions avogadro/rpclistener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,16 @@ void RpcListener::messageReceived(const MoleQueue::Message& message)
MoleQueue::Message response = message.generateResponse();
response.setResult(true);
response.send();
} else if (method == "exportFile") {
// Save to the supplied file name
QString filename = params["fileName"].toString();

bool result = m_window->exportFile(filename);

// set response
MoleQueue::Message response = message.generateResponse();
response.setResult(result);
response.send();
} else if (method == "loadMolecule") {
// get molecule data and format
string content = params["content"].toString().toStdString();
Expand Down Expand Up @@ -211,14 +221,25 @@ void RpcListener::messageReceived(const MoleQueue::Message& message)
.arg(QString::fromStdString(FileFormatManager::instance().error())));
errorMessage.send();
}
} else { // invalid method
MoleQueue::Message errorMessage = message.generateErrorResponse();
errorMessage.setErrorCode(-32601);
errorMessage.setErrorMessage("Method not found");
QJsonObject errorDataObject;
errorDataObject.insert("request", message.toJsonObject());
errorMessage.setErrorData(errorDataObject);
errorMessage.send();
} else { // ask the main window to handle the message
QVariantMap options = params.toVariantMap();
bool success = m_window->handleCommand(method, options);

if (success) {
// send response
MoleQueue::Message response = message.generateResponse();
response.setResult(true);
response.send();
} else {
// send error response
MoleQueue::Message errorMessage = message.generateErrorResponse();
errorMessage.setErrorCode(-32601);
errorMessage.setErrorMessage("Method not found");
QJsonObject errorDataObject;
errorDataObject.insert("request", message.toJsonObject());
errorMessage.setErrorData(errorDataObject);
errorMessage.send();
}
}
}

Expand Down

0 comments on commit a9a65ae

Please sign in to comment.