diff --git a/starviewer/doc/help/shortcuts/Starviewer_Shortcuts_guide.docx b/starviewer/doc/help/shortcuts/Starviewer_Shortcuts_guide.docx index 2c9ffcbba7..21ea80d40d 100644 Binary files a/starviewer/doc/help/shortcuts/Starviewer_Shortcuts_guide.docx and b/starviewer/doc/help/shortcuts/Starviewer_Shortcuts_guide.docx differ diff --git a/starviewer/doc/help/usermanual/Starviewer_User_guide.docx b/starviewer/doc/help/usermanual/Starviewer_User_guide.docx index 3d6cfa7d02..780899dfb5 100644 Binary files a/starviewer/doc/help/usermanual/Starviewer_User_guide.docx and b/starviewer/doc/help/usermanual/Starviewer_User_guide.docx differ diff --git a/starviewer/releasenotes/changelog.html b/starviewer/releasenotes/changelog.html index 8781c0aafa..3d30f937d3 100644 --- a/starviewer/releasenotes/changelog.html +++ b/starviewer/releasenotes/changelog.html @@ -59,6 +59,17 @@

Starviewer Changelog

+
+
+ Starviewer 0.13.2 + (Publicat el 3 de maig de 2016) +
+
+ +
+
Starviewer 0.13.1 diff --git a/starviewer/src/core/core.pro b/starviewer/src/core/core.pro index 4e8fe60cc0..a2a90f75a7 100644 --- a/starviewer/src/core/core.pro +++ b/starviewer/src/core/core.pro @@ -21,7 +21,8 @@ FORMS += qlogviewerbase.ui \ q2dviewerconfigurationscreenbase.ui \ qlayoutoptionswidgetbase.ui \ qnotificationpopupbase.ui \ - qfusionlayoutwidgetbase.ui + qfusionlayoutwidgetbase.ui \ + qexternalapplicationconfigurationscreenbase.ui TRANSLATIONS += core_ca_ES.ts \ core_es_ES.ts \ @@ -426,7 +427,10 @@ HEADERS += extensionfactory.h \ hangingprotocolfiller.h \ qfusionlayoutwidget.h \ gridicon.h \ - itemmenu.h + itemmenu.h \ + qexternalapplicationconfigurationscreen.h \ + externalapplication.h \ + externalapplicationsmanager.h SOURCES += extensionmediator.cpp \ displayableid.cpp \ @@ -800,7 +804,10 @@ SOURCES += extensionmediator.cpp \ hangingprotocolfiller.cpp \ qfusionlayoutwidget.cpp \ gridicon.cpp \ - itemmenu.cpp + itemmenu.cpp \ + qexternalapplicationconfigurationscreen.cpp \ + externalapplication.cpp \ + externalapplicationsmanager.cpp win32 { HEADERS += windowsfirewallaccess.h \ diff --git a/starviewer/src/core/core_ca_ES.ts b/starviewer/src/core/core_ca_ES.ts index 5418646e4f..4090b7f87d 100644 --- a/starviewer/src/core/core_ca_ES.ts +++ b/starviewer/src/core/core_ca_ES.ts @@ -82,7 +82,7 @@ La previsualització no està disponible - + PNG (*.png) @@ -1174,6 +1174,127 @@ S'inhabilitarà l'ombreig, de manera que no es visualitzarà com s&apo D'acord + + udg::QExternalApplicationConfigurationScreen + + + + URL + URL + + + + Command + Ordre + + + + + New application + Nova aplicació + + + + udg::QExternalApplicationConfigurationScreenBase + + + External applications + Aplicacions externes + + + + Delete + Elimina + + + + Down + Baixa + + + + Add command + Afegeix ordre + + + + Type + Tipus + + + + Name + Nom + + + + URL + Url + URL + + + + Up + Puja + + + + <html> +<head/> +<body> + +<p>External applications can be an URL which is opened with the system's default browser or a system command.</p> +<p>If an URL or command has the following parameters, then they will be replaced with the corresponding real values before doing the launch.</p> + +<ul> +<li><b>{%StudyInstanceUID%}</b> Study UID.</li> +<li><b>{%SeriesInstanceUID%}</b> Series UID.</li> +<li><b>{%AccessionNumber%}</b> Accession number.</li> +<li><b>{%PatientID%}</b> Patient ID.</li> +</ul> +<p>Be aware of the security risks when defining external applications. Code injection attacks through command invocation may happen when opening a malicious DICOM file. Files may also be altered by a MITM attacker between the user and the PACS server.</p> +</body> +</html> + <html> +<head/> +<body> + +<p>External applications can be an URL which is opened with the system's default browser or a system command.</p> +<p>If an URL or command has the following parameters, then they will be replaced with the corresponding real values before doing the launch.</p> + +<ul> +<li><b>{%StudyInstanceUID%}</b> Study UID.</li> +<li><b>{%SeriesInstanceUID%}</b> Series UID.</li> +<li><b>{%AccessionNumber%}</b> Accession number.</li> +<li><b>{%PatientID%}</b> Patient ID.</li> +</ul> +<p>When launching an URL with browser parameter values will be percent encoded (url encoding).</p> +<p>Be aware of the security risks when defining external applications. Code injection attacks through command invocation may happen when opening a malicious DICOM file. Files may also be altered by a MITM attacker between the user and the PACS server.</p> +</body> +</html> + <html> +<head/> +<body> + +<p>Les aplicacions externes consisteixen en una URL que pot ser oberta per el navegador o executada com una ordre del sistema.</p> +<p>Si una URL o ordre té els següents paràmetres, llavors aquests seràn substituïts per els seus valors reals corresponents en el moment de fer el llançament.</p> + +<ul> +<li><b>{%StudyInstanceUID%}</b> UID de l'estudi.</li> +<li><b>{%SeriesInstanceUID%}</b> UID de la sèrie.</li> +<li><b>{%AccessionNumber%}</b> Nombre d'adhesió.</li> +<li><b>{%PatientID%}</b> ID del pacient.</li> +</ul> +<p>Tingueu en compte els riscs en definir aplicacions externes. A través d'un fitxer DICOM maligne es poden produïr atacs d'injecció de codi a través de l'invocació d'ordres. Els fitxers també poden veure's alterats per mitjà d'atacs MITM entre l'usuari i el servidor PACS.</p> +</body> +</html> + + + + Add URL + Afegeix URL + + udg::QFusionLayoutWidget diff --git a/starviewer/src/core/core_en_GB.ts b/starviewer/src/core/core_en_GB.ts index 72f9b89ceb..0a30aa38cb 100644 --- a/starviewer/src/core/core_en_GB.ts +++ b/starviewer/src/core/core_en_GB.ts @@ -82,7 +82,7 @@ - + PNG (*.png) @@ -1166,6 +1166,112 @@ Shading will be disabled, it will not render as expected. + + udg::QExternalApplicationConfigurationScreen + + + + URL + + + + + Command + + + + + + New application + + + + + udg::QExternalApplicationConfigurationScreenBase + + + External applications + + + + + Delete + + + + + Down + + + + + Add command + + + + + Type + + + + + Name + + + + + URL + Url + + + + + Up + + + + + <html> +<head/> +<body> + +<p>External applications can be an URL which is opened with the system's default browser or a system command.</p> +<p>If an URL or command has the following parameters, then they will be replaced with the corresponding real values before doing the launch.</p> + +<ul> +<li><b>{%StudyInstanceUID%}</b> Study UID.</li> +<li><b>{%SeriesInstanceUID%}</b> Series UID.</li> +<li><b>{%AccessionNumber%}</b> Accession number.</li> +<li><b>{%PatientID%}</b> Patient ID.</li> +</ul> +<p>Be aware of the security risks when defining external applications. Code injection attacks through command invocation may happen when opening a malicious DICOM file. Files may also be altered by a MITM attacker between the user and the PACS server.</p> +</body> +</html> + <html> +<head/> +<body> + +<p>External applications can be an URL which is opened with the system's default browser or a system command.</p> +<p>If an URL or command has the following parameters, then they will be replaced with the corresponding real values before doing the launch.</p> + +<ul> +<li><b>{%StudyInstanceUID%}</b> Study UID.</li> +<li><b>{%SeriesInstanceUID%}</b> Series UID.</li> +<li><b>{%AccessionNumber%}</b> Accession number.</li> +<li><b>{%PatientID%}</b> Patient ID.</li> +</ul> +<p>When launching an URL with browser parameter values will be percent encoded (url encoding).</p> +<p>Be aware of the security risks when defining external applications. Code injection attacks through command invocation may happen when opening a malicious DICOM file. Files may also be altered by a MITM attacker between the user and the PACS server.</p> +</body> +</html> + + + + + Add URL + + + udg::QFusionLayoutWidget diff --git a/starviewer/src/core/core_es_ES.ts b/starviewer/src/core/core_es_ES.ts index 05eba286e9..7e8cd9af68 100644 --- a/starviewer/src/core/core_es_ES.ts +++ b/starviewer/src/core/core_es_ES.ts @@ -82,7 +82,7 @@ Previsualización no disponible - + PNG (*.png) @@ -1174,6 +1174,127 @@ Se deshabilitará el sombreado, de manera que no se visualizará como se esperab Aceptar + + udg::QExternalApplicationConfigurationScreen + + + + URL + URL + + + + Command + Comando + + + + + New application + Aplicacion nueva + + + + udg::QExternalApplicationConfigurationScreenBase + + + External applications + Aplicaciones externas + + + + Delete + Eliminar + + + + Down + Bajar + + + + Add command + Añadir comando + + + + Type + Tipo + + + + Name + Nombre + + + + URL + Url + URL + + + + Up + Subir + + + + <html> +<head/> +<body> + +<p>External applications can be an URL which is opened with the system's default browser or a system command.</p> +<p>If an URL or command has the following parameters, then they will be replaced with the corresponding real values before doing the launch.</p> + +<ul> +<li><b>{%StudyInstanceUID%}</b> Study UID.</li> +<li><b>{%SeriesInstanceUID%}</b> Series UID.</li> +<li><b>{%AccessionNumber%}</b> Accession number.</li> +<li><b>{%PatientID%}</b> Patient ID.</li> +</ul> +<p>Be aware of the security risks when defining external applications. Code injection attacks through command invocation may happen when opening a malicious DICOM file. Files may also be altered by a MITM attacker between the user and the PACS server.</p> +</body> +</html> + <html> +<head/> +<body> + +<p>External applications can be an URL which is opened with the system's default browser or a system command.</p> +<p>If an URL or command has the following parameters, then they will be replaced with the corresponding real values before doing the launch.</p> + +<ul> +<li><b>{%StudyInstanceUID%}</b> Study UID.</li> +<li><b>{%SeriesInstanceUID%}</b> Series UID.</li> +<li><b>{%AccessionNumber%}</b> Accession number.</li> +<li><b>{%PatientID%}</b> Patient ID.</li> +</ul> +<p>When launching an URL with browser parameter values will be percent encoded (url encoding).</p> +<p>Be aware of the security risks when defining external applications. Code injection attacks through command invocation may happen when opening a malicious DICOM file. Files may also be altered by a MITM attacker between the user and the PACS server.</p> +</body> +</html> + <html> +<head/> +<body> + +<p>Las aplicaciones externas son una URL que se puede abrir con el navegador o ser ejecutada cómo un comando del sistema.</p> +<p>Si una URL o comando tiene los siguientes parámetros, estos serán reemplazados por los valores reales correspondientes en el momento de realizar el lanzamiento.</p> + +<ul> +<li><b>{%StudyInstanceUID%}</b> UID del estudio.</li> +<li><b>{%SeriesInstanceUID%}</b> UID de la serie.</li> +<li><b>{%AccessionNumber%}</b> Número de adhesion.</li> +<li><b>{%PatientID%}</b> ID del paciente.</li> +</ul> +<p>Tenga en cuenta los riesgos que conlleva definir aplicaciones externas. A través de un fichero DICOM malicioso se pueden perpetrar ataques de inyección de código a través de la invocacion de los comandos. Los ficheros también pueden verse alterados en ataques MITM entre el usuario y el servidor PACS.</p> +</body> +</html> + + + + Add URL + Añadir URL + + udg::QFusionLayoutWidget diff --git a/starviewer/src/core/coresettings.cpp b/starviewer/src/core/coresettings.cpp index 564e44226f..377e0d9a77 100644 --- a/starviewer/src/core/coresettings.cpp +++ b/starviewer/src/core/coresettings.cpp @@ -95,6 +95,8 @@ const QString CoreSettings::DefaultPACSListToQuery("PACS/defaultPACSListToQuery" //TODO:Aquesta clau està duplicada a InputOutputSettings const QString CoreSettings::PacsListConfigurationSectionName = "PacsList"; +const QString CoreSettings::ExternalApplicationsConfigurationSectionName = "ExternalApplications"; + const QString Q2DViewerBase("2DViewer/"); const QString CoreSettings::EnableQ2DViewerSliceScrollLoop(Q2DViewerBase + "enable2DViewerSliceScrollLoop"); const QString CoreSettings::EnableQ2DViewerPhaseScrollLoop(Q2DViewerBase + "enable2DViewerPhaseScrollLoop"); diff --git a/starviewer/src/core/coresettings.h b/starviewer/src/core/coresettings.h index 27f93f8dd4..be3f2f0e4f 100644 --- a/starviewer/src/core/coresettings.h +++ b/starviewer/src/core/coresettings.h @@ -112,6 +112,9 @@ class CoreSettings : public DefaultSettings { //TODO: Aquesta clau està duplicada a InputOutputSettings static const QString PacsListConfigurationSectionName; + /// List containing the external applications. + static const QString ExternalApplicationsConfigurationSectionName; + /// Defineix si el loop està habilitat en l'scroll d'imatges/fases del visor 2D static const QString EnableQ2DViewerSliceScrollLoop; static const QString EnableQ2DViewerPhaseScrollLoop; diff --git a/starviewer/src/core/externalapplication.cpp b/starviewer/src/core/externalapplication.cpp new file mode 100644 index 0000000000..d643fe84eb --- /dev/null +++ b/starviewer/src/core/externalapplication.cpp @@ -0,0 +1,133 @@ +/************************************************************************************* + Copyright (C) 2014 Laboratori de Gràfics i Imatge, Universitat de Girona & + Institut de Diagnòstic per la Imatge. + Girona 2014. All rights reserved. + http://starviewer.udg.edu + + This file is part of the Starviewer (Medical Imaging Software) open source project. + It is subject to the license terms in the LICENSE file found in the top-level + directory of this distribution and at http://starviewer.udg.edu/license. No part of + the Starviewer (Medical Imaging Software) open source project, including this file, + may be copied, modified, propagated, or distributed except according to the + terms contained in the LICENSE file. + *************************************************************************************/ + +#include "externalapplication.h" + +#include "logging.h" + +#include +#include +#include + +namespace udg { + + +ExternalApplication::ExternalApplication(QString name, QString url, ExternalApplicationType type) +{ + this->setName(name); + this->setUrl(url); + this->setType(type); +} + +ExternalApplication::~ExternalApplication() +{ + +} + +void ExternalApplication::setName(QString name) +{ + m_name = name; +} + +void ExternalApplication::setUrl(QString url) +{ + m_url = url; +} + +void ExternalApplication::setType(ExternalApplicationType type) +{ + m_type = type; +} + +QString ExternalApplication::getName() const +{ + return m_name; +} + +QString ExternalApplication::getUrl() const +{ + return m_url; +} + +ExternalApplication::ExternalApplicationType ExternalApplication::getType() const +{ + return m_type; +} + +QString ExternalApplication::getReplacedUrl(const QHash &replacements) const +{ + if (m_url.isNull()) + { + return QString(); + } + + QString replacedUrl; + int begin = 0; + while(true) + { + int fieldStart = m_url.indexOf("{%", begin); + if (fieldStart < 0) //start delimiter not found + { + break; + } + replacedUrl += m_url.midRef(begin, fieldStart-begin); //The string from where we where to the current delimiter + fieldStart += 2; //Skip the two characters of the delimiter + + int fieldEnd = m_url.indexOf("%}", fieldStart); + if (fieldEnd < 0) //end delimiter not found + { + break; + } + + const QString &fieldName = m_url.mid(fieldStart, fieldEnd-fieldStart); + if (this->getType() == ExternalApplicationType::Url) + { + replacedUrl += QUrl::toPercentEncoding(replacements[fieldName]); + } + else + { + replacedUrl += replacements[fieldName]; + } + fieldEnd += 2; //Skip the two characters of the delimiter + begin = fieldEnd; + } + replacedUrl += m_url.midRef(begin); + return replacedUrl; +} + +bool ExternalApplication::launch(const QHash &replacements) const +{ + bool success = false; + const QString& replacedUrl = this->getReplacedUrl(replacements); + if (this->getType() == Url) + { + INFO_LOG("Opening URL " + replacedUrl); + const QUrl& url = QUrl(replacedUrl); + success = QDesktopServices::openUrl(url); + } + else if (this->getType() == Command) + { + if (success = QProcess::startDetached(replacedUrl)) //Ninja code: assignation inside an if condition. + { + INFO_LOG("Running command " + replacedUrl); + } + else + { + ERROR_LOG("Command " + replacedUrl + " could not be started."); + } + } + return success; +} + +} diff --git a/starviewer/src/core/externalapplication.h b/starviewer/src/core/externalapplication.h new file mode 100644 index 0000000000..5235d37c45 --- /dev/null +++ b/starviewer/src/core/externalapplication.h @@ -0,0 +1,85 @@ +/************************************************************************************* + Copyright (C) 2014 Laboratori de Gràfics i Imatge, Universitat de Girona & + Institut de Diagnòstic per la Imatge. + Girona 2014. All rights reserved. + http://starviewer.udg.edu + + This file is part of the Starviewer (Medical Imaging Software) open source project. + It is subject to the license terms in the LICENSE file found in the top-level + directory of this distribution and at http://starviewer.udg.edu/license. No part of + the Starviewer (Medical Imaging Software) open source project, including this file, + may be copied, modified, propagated, or distributed except according to the + terms contained in the LICENSE file. + *************************************************************************************/ + +#ifndef EXTERNALAPPLICATION_H +#define EXTERNALAPPLICATION_H + +#include +#include + +namespace udg { + +/** + * @brief The ExternalApplication class made with an Url and a name. + * + * The name, is the name of the external application, this name will be displayed on the external applications menu. + * + * The concept of an external application is launching an URL with the system default browser and replacing some URL parameters or fields with information of + * the currently opened study. + * + * Fields are defined on the url with this style: {%fieldName%}. + * + * When the application is launched a hash with the key as the field name and the value as the value of the field has to be passed in order to do the + * substitution. + * + */ +class ExternalApplication +{ +public: + enum ExternalApplicationType { Url, Command }; + + explicit ExternalApplication(QString name = QString(), QString url = QString(), ExternalApplicationType type = Url); + ~ExternalApplication(); + + void setName(QString name); + QString getName() const; + void setUrl(QString url); + /// @return Url with fields {%fieldName%}. + QString getUrl() const; + /// @brief Returns an URL with the fields replaced. + /// + /// Given a hash map with the field names and its values, the funcion searches for the fields written as {%fieldName%} and replaces them with the value on + /// the hash map. + /// + /// If the type is set to URL, parameters will pass a percent encoding (url encoding) to sanitize them. + /// + /// @param replacements Hash map where the key is the field name. + /// @return URL with the parameters replaced. + QString getReplacedUrl(const QHash &replacements) const; + void setType(ExternalApplicationType type); + /// @brief Application type, which can be an URL or a command. + ExternalApplicationType getType() const; + + /// @brief Launch the URL with the system default browser. + /// + /// The launched URL is given by the method getReplacedUrl(). + /// + /// If the type is URL, then the system default browser is launched. + /// + /// If the type is Cmd, then the URL is run as a command. + /// + /// @param replacements Hash map where the key is the field name. + /// @return Returns true if launch has been successful. + bool launch(const QHash &replacements = QHash()) const; + +private: + QString m_name; + QString m_url; + ExternalApplicationType m_type; + +}; + +} + +#endif // EXTERNALAPPLICATION_H diff --git a/starviewer/src/core/externalapplicationsmanager.cpp b/starviewer/src/core/externalapplicationsmanager.cpp new file mode 100644 index 0000000000..211c5996bc --- /dev/null +++ b/starviewer/src/core/externalapplicationsmanager.cpp @@ -0,0 +1,110 @@ +/************************************************************************************* + Copyright (C) 2014 Laboratori de Gràfics i Imatge, Universitat de Girona & + Institut de Diagnòstic per la Imatge. + Girona 2014. All rights reserved. + http://starviewer.udg.edu + + This file is part of the Starviewer (Medical Imaging Software) open source project. + It is subject to the license terms in the LICENSE file found in the top-level + directory of this distribution and at http://starviewer.udg.edu/license. No part of + the Starviewer (Medical Imaging Software) open source project, including this file, + may be copied, modified, propagated, or distributed except according to the + terms contained in the LICENSE file. + *************************************************************************************/ + +#include "externalapplicationsmanager.h" + +#include "coresettings.h" +#include "externalapplication.h" +#include "logging.h" +#include "patient.h" +#include "volume.h" + + +namespace udg { + +ExternalApplicationsManager::ExternalApplicationsManager(QObject *parent) : QObject(parent) +{ + +} + +ExternalApplicationsManager::~ExternalApplicationsManager() +{ + Settings settings; + Settings::SettingListType list = settings.getList(CoreSettings::ExternalApplicationsConfigurationSectionName); +} + +QList ExternalApplicationsManager::getApplications() const +{ + QList applications; + Settings settings; + Settings::SettingListType list = settings.getList(CoreSettings::ExternalApplicationsConfigurationSectionName); + + foreach (Settings::SettingsListItemType item, list) + { + ExternalApplication::ExternalApplicationType type; + if (item["type"] == "url") + { + type = ExternalApplication::ExternalApplicationType::Url; + } + else if (item["type"] == "cmd") + { + type = ExternalApplication::ExternalApplicationType::Command; + } + else + { + ERROR_LOG("Unexpected external application type"); + break; + } + + ExternalApplication application(item["name"].toString(), item["url"].toString(), type); + applications.append(application); + } + return applications; +} + +void ExternalApplicationsManager::setApplications(const QList &applications) +{ + Settings::SettingListType list; + Settings settings; + + foreach(ExternalApplication application, applications) + { + Settings::SettingsListItemType item; + item["name"] = application.getName(); + item["url"] = application.getUrl(); + item["type"] = application.getType() == ExternalApplication::ExternalApplicationType::Url ? "url" : "cmd"; + list.append(item); + } + + settings.setList(CoreSettings::ExternalApplicationsConfigurationSectionName, list); + emit onApplicationsChanged(); +} + +void ExternalApplicationsManager::cleanParameters() +{ + m_parameters = QHash(); +} + +void ExternalApplicationsManager::setParameters(Volume* volume) +{ + m_parameters = QHash(); + m_parameters["StudyInstanceUID"] = volume->getStudy()->getInstanceUID(); + m_parameters["SeriesInstanceUID"] = volume->getSeries()->getInstanceUID(); + m_parameters["AccessionNumber"] = volume->getStudy()->getAccessionNumber(); + m_parameters["PatientID"] = volume->getPatient()->getID(); +} + +const QHash& ExternalApplicationsManager::getParameters() const +{ + return m_parameters; +} + +bool ExternalApplicationsManager::launch(const ExternalApplication &application) const +{ + return application.launch(getParameters()); +} + +} + + diff --git a/starviewer/src/core/externalapplicationsmanager.h b/starviewer/src/core/externalapplicationsmanager.h new file mode 100644 index 0000000000..df7dc650a3 --- /dev/null +++ b/starviewer/src/core/externalapplicationsmanager.h @@ -0,0 +1,87 @@ +/************************************************************************************* + Copyright (C) 2014 Laboratori de Gràfics i Imatge, Universitat de Girona & + Institut de Diagnòstic per la Imatge. + Girona 2014. All rights reserved. + http://starviewer.udg.edu + + This file is part of the Starviewer (Medical Imaging Software) open source project. + It is subject to the license terms in the LICENSE file found in the top-level + directory of this distribution and at http://starviewer.udg.edu/license. No part of + the Starviewer (Medical Imaging Software) open source project, including this file, + may be copied, modified, propagated, or distributed except according to the + terms contained in the LICENSE file. + *************************************************************************************/ + +#ifndef EXTERNALAPPLICATIONSMANAGER_H +#define EXTERNALAPPLICATIONSMANAGER_H + +#include "singleton.h" + +#include +#include +#include + +namespace udg { + +class Volume; +class ExternalApplication; + +/** + * @brief Singleton to manage external applications + * + * ExternalApplicationsManager singleton reads and saves the external applications to the settings. + * + * It also emits an event when the list of applications is changed, thus the UI can be updated. + * + * This singleton is the point where the parameters to be replaced are collected. When the active study changes, setParameters should be called. + */ +class ExternalApplicationsManager : public QObject, public Singleton +{ + Q_OBJECT +public: + /// @brief Reads the external applications from the settings. + /// @return List of external applications + QList getApplications() const; + + /// @brief Writes the given external applications to the settings. + void setApplications(const QList &applications); + + /// @brief Empties the parameters list. + /// + /// You should call this when no study is active. Parameters will be empty when an application is launched. + void cleanParameters(); + + /// @brief Reads the parameters to replace for the current active study. + /// + /// This function should be called when an study becomes active. It reads the information to fill the parameters from the given volume. + /// + /// @param volume Volume that is currently active + void setParameters(Volume* volume); + + /// @return Parameter names and correspondant values. + const QHash& getParameters() const; + + /// @brief Launches the given application. + /// + /// The given application is launched with the parameters given by getParameters(). + /// + /// @return Returns true if launch has been successful. + bool launch(const ExternalApplication &application) const; + +protected: + friend class Singleton; + explicit ExternalApplicationsManager(QObject *parent = 0); + ~ExternalApplicationsManager(); + +signals: + /// @brief Emmited when the external applications list is set and written to the settings + void onApplicationsChanged(); + +private: + QHash m_parameters; + +}; + +} + +#endif // EXTERNALAPPLICATIONSMANAGER_H diff --git a/starviewer/src/core/qexternalapplicationconfigurationscreen.cpp b/starviewer/src/core/qexternalapplicationconfigurationscreen.cpp new file mode 100644 index 0000000000..b063b94be8 --- /dev/null +++ b/starviewer/src/core/qexternalapplicationconfigurationscreen.cpp @@ -0,0 +1,178 @@ +/************************************************************************************* + Copyright (C) 2014 Laboratori de Gràfics i Imatge, Universitat de Girona & + Institut de Diagnòstic per la Imatge. + Girona 2014. All rights reserved. + http://starviewer.udg.edu + + This file is part of the Starviewer (Medical Imaging Software) open source project. + It is subject to the license terms in the LICENSE file found in the top-level + directory of this distribution and at http://starviewer.udg.edu/license. No part of + the Starviewer (Medical Imaging Software) open source project, including this file, + may be copied, modified, propagated, or distributed except according to the + terms contained in the LICENSE file. + *************************************************************************************/ + +#include "externalapplicationsmanager.h" + +#include "coresettings.h" +#include "externalapplication.h" +#include "logging.h" +#include "qexternalapplicationconfigurationscreen.h" + +#include + +namespace udg { + +QExternalApplicationConfigurationScreen::QExternalApplicationConfigurationScreen(QWidget *parent) + : QWidget(parent) +{ + setupUi(this); + checkGrayeds(); + this->setExternalApplications(ExternalApplicationsManager::instance()->getApplications()); + + connect(m_buttonAddCommand, SIGNAL(clicked()), this, SLOT(addCommandClicked())); + connect(m_buttonAddUrl, SIGNAL(clicked()), this, SLOT(buttonAddUrlClicked())); + connect(m_buttonDelete, SIGNAL(clicked()), this, SLOT(buttonDeleteClicked())); + connect(m_buttonDown, SIGNAL(clicked()), this, SLOT(buttonDownClicked())); + connect(m_buttonUp, SIGNAL(clicked()), this, SLOT(buttonUpClicked())); + connect(m_tableWidget, SIGNAL(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)), this, SLOT(tableCurrentItemChanged(QTableWidgetItem*, QTableWidgetItem*))); +} + +QExternalApplicationConfigurationScreen::~QExternalApplicationConfigurationScreen() +{ + +} + +void QExternalApplicationConfigurationScreen::setExternalApplications(const QList &externalApplications) +{ + QList::const_iterator it; + for (it = externalApplications.constBegin(); it != externalApplications.constEnd(); ++it) + { + this->addApplication(*it); + } +} + +QList QExternalApplicationConfigurationScreen::getExternalApplications() const +{ + QList applications; + for (int i = 0; i < m_tableWidget->rowCount(); i++) + { + ExternalApplication::ExternalApplicationType type = + m_tableWidget->item(i,0)->text() == tr("URL") ? + ExternalApplication::ExternalApplicationType::Url : + ExternalApplication::ExternalApplicationType::Command; + QString name = m_tableWidget->item(i, 1)->text(); + QString url = m_tableWidget->item(i, 2)->text(); + applications.append(ExternalApplication(name, url, type)); + } + return applications; +} + +void QExternalApplicationConfigurationScreen::addApplication(const ExternalApplication &externalApplication) +{ + QTableWidgetItem* typeWidget = new QTableWidgetItem(); + QTableWidgetItem* nameWidget = new QTableWidgetItem(externalApplication.getName()); + QTableWidgetItem* urlWidget = new QTableWidgetItem(externalApplication.getUrl()); + typeWidget->setFlags(Qt::ItemIsSelectable); + + if (externalApplication.getType() == ExternalApplication::ExternalApplicationType::Command) + { + typeWidget->setText(tr("Command")); + } + else + { + typeWidget->setText(tr("URL")); + } + + m_tableWidget->insertRow(m_tableWidget->rowCount()); + m_tableWidget->setItem(m_tableWidget->rowCount()-1, 0, typeWidget); + m_tableWidget->setItem(m_tableWidget->rowCount()-1, 1, nameWidget); + m_tableWidget->setItem(m_tableWidget->rowCount()-1, 2, urlWidget); + + checkGrayeds(); +} + +void QExternalApplicationConfigurationScreen::moveItem(int shift) +{ + //This wants to be understandable code: + //Do not blame me for efficiency, compiler will make it efficient. + int currentRow = m_tableWidget->currentRow(); + int currentCol = m_tableWidget->currentColumn(); + if (currentRow >= 0) + { + int maxRow = m_tableWidget->rowCount() - 1; + int minRow = 0; + int swappingRow = qMin(maxRow,qMax(currentRow + shift, minRow)); + + QTableWidgetItem* currentRowCol0 = m_tableWidget->takeItem(currentRow, 0); + QTableWidgetItem* currentRowCol1 = m_tableWidget->takeItem(currentRow, 1); + QTableWidgetItem* currentRowCol2 = m_tableWidget->takeItem(currentRow, 2); + QTableWidgetItem* swappingRowCol0 = m_tableWidget->takeItem(swappingRow, 0); + QTableWidgetItem* swappingRowCol1 = m_tableWidget->takeItem(swappingRow, 1); + QTableWidgetItem* swappingRowCol2 = m_tableWidget->takeItem(swappingRow, 2); + + m_tableWidget->setItem(currentRow, 0, swappingRowCol0); + m_tableWidget->setItem(currentRow, 1, swappingRowCol1); + m_tableWidget->setItem(currentRow, 2, swappingRowCol2); + m_tableWidget->setItem(swappingRow, 0, currentRowCol0); + m_tableWidget->setItem(swappingRow, 1, currentRowCol1); + m_tableWidget->setItem(swappingRow, 2, currentRowCol2); + + m_tableWidget->setCurrentItem(m_tableWidget->item(swappingRow, currentCol)); + } +} + +void QExternalApplicationConfigurationScreen::checkGrayeds() +{ + int maxRow = m_tableWidget->rowCount() - 1; + int currentRow = m_tableWidget->currentRow(); + + m_buttonDelete->setEnabled(currentRow >= 0); + m_buttonUp->setEnabled(currentRow > 0 && currentRow <= maxRow); + m_buttonDown->setEnabled(currentRow >= 0 && currentRow < maxRow); +} + +void QExternalApplicationConfigurationScreen::buttonUpClicked() +{ + moveItem(-1); +} + +void QExternalApplicationConfigurationScreen::buttonDownClicked() +{ + moveItem(1); +} + +void QExternalApplicationConfigurationScreen::buttonAddUrlClicked() +{ + ExternalApplication newApp(tr("New application"), "http://www.starviewer.org", ExternalApplication::ExternalApplicationType::Url); + this->addApplication(newApp); +} + +void QExternalApplicationConfigurationScreen::addCommandClicked() +{ + ExternalApplication newApp(tr("New application"), "echo \"Starviewer Medical {%AccessionNumber%}\"", ExternalApplication::ExternalApplicationType::Command); + this->addApplication(newApp); +} + +void QExternalApplicationConfigurationScreen::buttonDeleteClicked() +{ + int rowToRemove = m_tableWidget->currentRow(); + if (rowToRemove >= 0) + { + m_tableWidget->removeRow(rowToRemove); + checkGrayeds(); + } +} + +void QExternalApplicationConfigurationScreen::tableCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous) +{ + checkGrayeds(); +} + +void QExternalApplicationConfigurationScreen::closeEvent(QCloseEvent *event) +{ + ExternalApplicationsManager::instance()->setApplications(this->getExternalApplications()); + event->accept(); +} + +} diff --git a/starviewer/src/core/qexternalapplicationconfigurationscreen.h b/starviewer/src/core/qexternalapplicationconfigurationscreen.h new file mode 100644 index 0000000000..4865d45ce7 --- /dev/null +++ b/starviewer/src/core/qexternalapplicationconfigurationscreen.h @@ -0,0 +1,84 @@ +/************************************************************************************* + Copyright (C) 2014 Laboratori de Gràfics i Imatge, Universitat de Girona & + Institut de Diagnòstic per la Imatge. + Girona 2014. All rights reserved. + http://starviewer.udg.edu + + This file is part of the Starviewer (Medical Imaging Software) open source project. + It is subject to the license terms in the LICENSE file found in the top-level + directory of this distribution and at http://starviewer.udg.edu/license. No part of + the Starviewer (Medical Imaging Software) open source project, including this file, + may be copied, modified, propagated, or distributed except according to the + terms contained in the LICENSE file. + *************************************************************************************/ + +#ifndef QEXTERNALAPPLICATIONCONFIGURATIONSCREEN_H +#define QEXTERNALAPPLICATIONCONFIGURATIONSCREEN_H + +#include "ui_qexternalapplicationconfigurationscreenbase.h" + +#include + +namespace udg { + +class ExternalApplication; + +/** + @brief External application configuration GUI + + This GUI is used to edit the external applications list. When properly + configured, the entries set here appear on a menu. + */ +class QExternalApplicationConfigurationScreen : public QWidget, private Ui::QExternalApplicationConfigurationScreenBase { +Q_OBJECT +public: + /// @brief Constructs the GUI and fills the list. + /// The constructor retrieves the information from the settings and fills the GUI list automatically using the setExternalApplications method. + /// @param parent + explicit QExternalApplicationConfigurationScreen(QWidget *parent = 0); + ~QExternalApplicationConfigurationScreen(); + + /// @brief Use it to show the applications + /// @param externalApplications List of external applications to show on the list. + void setExternalApplications(const QList &externalApplications); + + /// @brief Generates the list of the external applications edited by the user. + /// @return List of the applications on the GUI list. + QList getExternalApplications() const; + + /// @brief Adds applications to the GUI application list. + /// @param externalApplication Application to add. + void addApplication(const ExternalApplication &externalApplication); + +protected: + /// @brief On close the configuration is saved. + /// Make sure that you call the close() function before destroying this class. + /// @param event + void closeEvent(QCloseEvent *event); + +private: + + /// @brief Moves a table row up or down according to a given shift. + /// @param shift Amount of + void moveItem(int shift = -1); + + /// @brief Enables or disables the buttons. + /// + /// Checks the situation with the table widget and enables or disables up, down and delete buttons. They are only enabled when needed. + /// + /// You should invoke this method when the table data changes. + void checkGrayeds(); + +private slots: + void addCommandClicked(); + void buttonAddUrlClicked(); + void buttonDeleteClicked(); + void buttonDownClicked(); + void buttonUpClicked(); + void tableCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous); + +}; + +} // End namespace udg + +#endif diff --git a/starviewer/src/core/qexternalapplicationconfigurationscreenbase.ui b/starviewer/src/core/qexternalapplicationconfigurationscreenbase.ui new file mode 100644 index 0000000000..ced9f8856c --- /dev/null +++ b/starviewer/src/core/qexternalapplicationconfigurationscreenbase.ui @@ -0,0 +1,138 @@ + + + udg::QExternalApplicationConfigurationScreenBase + + + + 0 + 0 + 663 + 455 + + + + + 0 + 0 + + + + External applications + + + + :/images/system-run-big.svg:/images/system-run-big.svg + + + + + + Delete + + + + + + + Down + + + + + + + Add command + + + + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + 150 + + + true + + + false + + + + Type + + + + + Name + + + + + URL + + + + + + + + Up + + + + + + + + 0 + 0 + + + + <html> +<head/> +<body> + +<p>External applications can be an URL which is opened with the system's default browser or a system command.</p> +<p>If an URL or command has the following parameters, then they will be replaced with the corresponding real values before doing the launch.</p> + +<ul> +<li><b>{%StudyInstanceUID%}</b> Study UID.</li> +<li><b>{%SeriesInstanceUID%}</b> Series UID.</li> +<li><b>{%AccessionNumber%}</b> Accession number.</li> +<li><b>{%PatientID%}</b> Patient ID.</li> +</ul> +<p>Be aware of the security risks when defining external applications. Code injection attacks through command invocation may happen when opening a malicious DICOM file. Files may also be altered by a MITM attacker between the user and the PACS server.</p> +</body> +</html> + + + true + + + + + + + Add URL + + + + + + + + + + + + diff --git a/starviewer/src/core/shortcuts.cpp b/starviewer/src/core/shortcuts.cpp index c77fdde0e1..ee94e631ba 100644 --- a/starviewer/src/core/shortcuts.cpp +++ b/starviewer/src/core/shortcuts.cpp @@ -87,6 +87,19 @@ const QString Shortcuts::MoveToPreviousDesktop(ShortcutsBase + "MoveToPreviousDe const QString Shortcuts::MoveToNextDesktop(ShortcutsBase + "MoveToNextDesktop"); const QString Shortcuts::MaximizeMultipleScreens(ShortcutsBase + "MaximizeMultipleScreens"); +const QString Shortcuts::ExternalApplication1(ShortcutsBase + "ExternalApplication1"); +const QString Shortcuts::ExternalApplication2(ShortcutsBase + "ExternalApplication2"); +const QString Shortcuts::ExternalApplication3(ShortcutsBase + "ExternalApplication3"); +const QString Shortcuts::ExternalApplication4(ShortcutsBase + "ExternalApplication4"); +const QString Shortcuts::ExternalApplication5(ShortcutsBase + "ExternalApplication5"); +const QString Shortcuts::ExternalApplication6(ShortcutsBase + "ExternalApplication6"); +const QString Shortcuts::ExternalApplication7(ShortcutsBase + "ExternalApplication7"); +const QString Shortcuts::ExternalApplication8(ShortcutsBase + "ExternalApplication8"); +const QString Shortcuts::ExternalApplication9(ShortcutsBase + "ExternalApplication9"); +const QString Shortcuts::ExternalApplication10(ShortcutsBase + "ExternalApplication10"); +const QString Shortcuts::ExternalApplication11(ShortcutsBase + "ExternalApplication11"); +const QString Shortcuts::ExternalApplication12(ShortcutsBase + "ExternalApplication12"); + Shortcuts::Shortcuts() { } @@ -346,6 +359,55 @@ void Shortcuts::init() shortcutsList.clear(); shortcutsList.append(QString("F10")); settingsRegistry->addSetting(ToggleComparativeStudiesMode, shortcutsList); + + shortcutsList.clear(); + shortcutsList.append(QString("Shift+F1")); + settingsRegistry->addSetting(ExternalApplication1, shortcutsList); + + shortcutsList.clear(); + shortcutsList.append(QString("Shift+F2")); + settingsRegistry->addSetting(ExternalApplication2, shortcutsList); + + shortcutsList.clear(); + shortcutsList.append(QString("Shift+F3")); + settingsRegistry->addSetting(ExternalApplication3, shortcutsList); + + shortcutsList.clear(); + shortcutsList.append(QString("Shift+F4")); + settingsRegistry->addSetting(ExternalApplication4, shortcutsList); + + shortcutsList.clear(); + shortcutsList.append(QString("Shift+F5")); + settingsRegistry->addSetting(ExternalApplication5, shortcutsList); + + shortcutsList.clear(); + shortcutsList.append(QString("Shift+F6")); + settingsRegistry->addSetting(ExternalApplication6, shortcutsList); + + shortcutsList.clear(); + shortcutsList.append(QString("Shift+F7")); + settingsRegistry->addSetting(ExternalApplication7, shortcutsList); + + shortcutsList.clear(); + shortcutsList.append(QString("Shift+F8")); + settingsRegistry->addSetting(ExternalApplication8, shortcutsList); + + shortcutsList.clear(); + shortcutsList.append(QString("Shift+F9")); + settingsRegistry->addSetting(ExternalApplication9, shortcutsList); + + shortcutsList.clear(); + shortcutsList.append(QString("Shift+F10")); + settingsRegistry->addSetting(ExternalApplication10, shortcutsList); + + shortcutsList.clear(); + shortcutsList.append(QString("Shift+F11")); + settingsRegistry->addSetting(ExternalApplication11, shortcutsList); + + shortcutsList.clear(); + shortcutsList.append(QString("Shift+F12")); + settingsRegistry->addSetting(ExternalApplication12, shortcutsList); + } } // End namespace udg diff --git a/starviewer/src/core/shortcuts.h b/starviewer/src/core/shortcuts.h index 5f01932893..1aa60fc02b 100644 --- a/starviewer/src/core/shortcuts.h +++ b/starviewer/src/core/shortcuts.h @@ -92,6 +92,19 @@ class Shortcuts : public DefaultSettings { static const QString MoveToPreviousDesktop; static const QString MoveToNextDesktop; static const QString MaximizeMultipleScreens; + + static const QString ExternalApplication1; + static const QString ExternalApplication2; + static const QString ExternalApplication3; + static const QString ExternalApplication4; + static const QString ExternalApplication5; + static const QString ExternalApplication6; + static const QString ExternalApplication7; + static const QString ExternalApplication8; + static const QString ExternalApplication9; + static const QString ExternalApplication10; + static const QString ExternalApplication11; + static const QString ExternalApplication12; }; } diff --git a/starviewer/src/core/starviewerapplication.h b/starviewer/src/core/starviewerapplication.h index f7c9003dbb..cbd2c67970 100644 --- a/starviewer/src/core/starviewerapplication.h +++ b/starviewer/src/core/starviewerapplication.h @@ -19,8 +19,8 @@ namespace udg { -const QString StarviewerVersionString("0.13.1"); -const QString StarviewerBuildID("2016020100"); +const QString StarviewerVersionString("0.13.2"); +const QString StarviewerBuildID("2016050300"); // Indica per aquesta versió d'starviewer quina és la revisió de bd necessària const int StarviewerDatabaseRevisionRequired(9592); diff --git a/starviewer/src/crashreporter/crashreporter_ca_ES.ts b/starviewer/src/crashreporter/crashreporter_ca_ES.ts index 08b18ea35d..9f4e893559 100644 --- a/starviewer/src/crashreporter/crashreporter_ca_ES.ts +++ b/starviewer/src/crashreporter/crashreporter_ca_ES.ts @@ -57,7 +57,7 @@ QObject - + PNG (*.png) PNG (*.png) diff --git a/starviewer/src/crashreporter/crashreporter_en_GB.ts b/starviewer/src/crashreporter/crashreporter_en_GB.ts index 0feca00eb4..a303da961f 100644 --- a/starviewer/src/crashreporter/crashreporter_en_GB.ts +++ b/starviewer/src/crashreporter/crashreporter_en_GB.ts @@ -57,7 +57,7 @@ QObject - + PNG (*.png) diff --git a/starviewer/src/crashreporter/crashreporter_es_ES.ts b/starviewer/src/crashreporter/crashreporter_es_ES.ts index bb60b151dc..efc0a25829 100644 --- a/starviewer/src/crashreporter/crashreporter_es_ES.ts +++ b/starviewer/src/crashreporter/crashreporter_es_ES.ts @@ -57,7 +57,7 @@ QObject - + PNG (*.png) PNG (*.png) diff --git a/starviewer/src/extensions/main/q2dviewer/q2dviewerextension.cpp b/starviewer/src/extensions/main/q2dviewer/q2dviewerextension.cpp index 9de6b7a8cf..10fe541f45 100644 --- a/starviewer/src/extensions/main/q2dviewer/q2dviewerextension.cpp +++ b/starviewer/src/extensions/main/q2dviewer/q2dviewerextension.cpp @@ -39,6 +39,7 @@ #include "transferfunctionmodel.h" #include "transferfunctionmodelfiller.h" #include "hangingprotocol.h" +#include "externalapplicationsmanager.h" #ifndef STARVIEWER_LITE #include "qrelatedstudieswidget.h" @@ -639,6 +640,7 @@ void Q2DViewerExtension::changeSelectedViewer(Q2DViewerWidget *viewerWidget) disconnect(m_lastSelectedViewer->getViewer(), SIGNAL(viewChanged(int)), this, SLOT(updateDICOMInformationButton())); disconnect(m_lastSelectedViewer->getViewer(), SIGNAL(viewerStatusChanged()), this, SLOT(updateExporterToolButton())); disconnect(m_lastSelectedViewer->getViewer(), SIGNAL(volumeChanged(Volume*)), this, SLOT(updateTransferFunctionComboBoxWithCurrentViewerModel())); + disconnect(m_lastSelectedViewer->getViewer(), SIGNAL(volumeChanged(Volume*)), this, SLOT(updateExternalApplicationsWithCurrentView(Volume*))); // És necessari associar cada cop al viewer actual les associacions del menú de la tool d'screen shot ScreenShotTool *screenShotTool = dynamic_cast(m_lastSelectedViewer->getViewer()->getToolProxy()->getTool("ScreenShotTool")); @@ -662,6 +664,17 @@ void Q2DViewerExtension::changeSelectedViewer(Q2DViewerWidget *viewerWidget) connect(viewerWidget->getViewer(), SIGNAL(viewChanged(int)), SLOT(updateDICOMInformationButton())); connect(m_lastSelectedViewer->getViewer(), SIGNAL(viewerStatusChanged()), this, SLOT(updateExporterToolButton())); connect(selected2DViewer, SIGNAL(volumeChanged(Volume*)), this, SLOT(updateTransferFunctionComboBoxWithCurrentViewerModel())); + connect(m_lastSelectedViewer->getViewer(), SIGNAL(volumeChanged(Volume*)), this, SLOT(updateExternalApplicationsWithCurrentView(Volume*))); + + // Update external application parameters + if (!m_lastSelectedViewer->getViewer()->getInputs().isEmpty()) + { + ExternalApplicationsManager::instance()->setParameters(m_lastSelectedViewer->getViewer()->getInput(0)); + } + else + { + ExternalApplicationsManager::instance()->cleanParameters(); + } // És necessari associar cada cop al viewer actual les associacions del menú de la tool d'screen shot ScreenShotTool *screenShotTool = dynamic_cast(viewerWidget->getViewer()->getToolProxy()->getTool("ScreenShotTool")); @@ -693,6 +706,7 @@ void Q2DViewerExtension::changeSelectedViewer(Q2DViewerWidget *viewerWidget) m_cineController->setQViewer(0); m_thickSlabWidget->unlink(); updateTransferFunctionComboBox(0); + ExternalApplicationsManager::instance()->cleanParameters(); } } } @@ -1009,6 +1023,14 @@ void Q2DViewerExtension::updateTransferFunctionComboBoxWithCurrentViewerModel() updateTransferFunctionComboBox(m_workingArea->getSelectedViewer()->getViewer()->getTransferFunctionModel()); } +void Q2DViewerExtension::updateExternalApplicationsWithCurrentView(Volume *volume) +{ + if (volume) + { + ExternalApplicationsManager::instance()->setParameters(volume); + } +} + void Q2DViewerExtension::updateTransferFunctionComboBox(TransferFunctionModel *transferFunctionModel) { disconnect(m_transferFunctionComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setTransferFunctionToCurrentViewer(int))); diff --git a/starviewer/src/extensions/main/q2dviewer/q2dviewerextension.h b/starviewer/src/extensions/main/q2dviewer/q2dviewerextension.h index c30b9974c8..b09ae9f3db 100644 --- a/starviewer/src/extensions/main/q2dviewer/q2dviewerextension.h +++ b/starviewer/src/extensions/main/q2dviewer/q2dviewerextension.h @@ -196,6 +196,9 @@ private slots: /// current viewer. void updateTransferFunctionComboBoxWithCurrentViewerModel(); + /// Updates external application parameters from the currently active view. + void updateExternalApplicationsWithCurrentView(Volume* volume); + /// Sets the transfer function at the given index in the current model to the current viewer. void setTransferFunctionToCurrentViewer(int transferFunctionIndex); diff --git a/starviewer/src/extensions/main/q2dviewer/translations_ca_ES.ts b/starviewer/src/extensions/main/q2dviewer/translations_ca_ES.ts index a271678b57..01dbf9d6cb 100644 --- a/starviewer/src/extensions/main/q2dviewer/translations_ca_ES.ts +++ b/starviewer/src/extensions/main/q2dviewer/translations_ca_ES.ts @@ -32,116 +32,116 @@ udg::Q2DViewerExtension - + Search related studies Search prior studies Cerca estudis relacionats - + Export viewer image(s) to DICOM and send them to a PACS server Exporta les imatges del visor a DICOM i envia-les a un servidor PACS - + Text Text - + Show/Hide viewer's textual information Mostra/amaga la informació textual dels visors - + Overlays Overlays - + Show/Hide image overlays Mostra/amaga els overlays de les imatges - + Shutters Shutters - + Show/Hide shutter layer Mostra/amaga els shutters - + Dump DICOM information of the current image Bolca la informació DICOM de la imatge actual - + Propagate properties between viewers (%1) Propaga les propietats entre els visors (%1) - + Propagate Propaga - + Save current series image... Desa la imatge de la sèrie actual... - + Save all images of the current series... Save all images from current series... Desa totes les imatges de la sèrie actual... - + All Tots - + Choose a VOI LUT preset Choose VOI LUT Presets Escull una VOI LUT predeterminada - + Deactivate manual synchronization in all viewers (%1) Desactiva la sincronització manual a tots els visors (%1) - + None Cap - + Save the current image in a standard image format Desa la imatge actual en un format estàndard - + Save all the images in the selected viewer in a standard image format Desa totes les imatges del visualitzador seleccionat en un format estàndard - + Activate manual synchronization in all viewers (%1) Activate manual synchronization all viewers Activa la sincronització manual a tots els visors (%1) - + Export to DICOM Exporta a DICOM - + This action is not allowed because the selected viewer is empty. No es permet aquesta acció perque el visor és buit. diff --git a/starviewer/src/extensions/main/q2dviewer/translations_en_GB.ts b/starviewer/src/extensions/main/q2dviewer/translations_en_GB.ts index b9f83c215a..a448b0dc6f 100644 --- a/starviewer/src/extensions/main/q2dviewer/translations_en_GB.ts +++ b/starviewer/src/extensions/main/q2dviewer/translations_en_GB.ts @@ -32,116 +32,116 @@ udg::Q2DViewerExtension - + Search related studies Search prior studies - + Export viewer image(s) to DICOM and send them to a PACS server - + Text - + Show/Hide viewer's textual information - + Overlays - + Show/Hide image overlays - + Shutters - + Show/Hide shutter layer - + Dump DICOM information of the current image - + Propagate properties between viewers (%1) - + Propagate - + Save current series image... - + Save all images of the current series... Save all images from current series... - + All - + Choose a VOI LUT preset Choose VOI LUT Presets - + Deactivate manual synchronization in all viewers (%1) - + None - + Save the current image in a standard image format - + Save all the images in the selected viewer in a standard image format - + Activate manual synchronization in all viewers (%1) Activate manual synchronization all viewers - + Export to DICOM - + This action is not allowed because the selected viewer is empty. diff --git a/starviewer/src/extensions/main/q2dviewer/translations_es_ES.ts b/starviewer/src/extensions/main/q2dviewer/translations_es_ES.ts index d668fab48c..17400ce149 100644 --- a/starviewer/src/extensions/main/q2dviewer/translations_es_ES.ts +++ b/starviewer/src/extensions/main/q2dviewer/translations_es_ES.ts @@ -32,116 +32,116 @@ udg::Q2DViewerExtension - + Search related studies Search prior studies Buscar estudios relacionados - + Export viewer image(s) to DICOM and send them to a PACS server Exportar imágenes del visor a DICOM y enviarlas a un servidor PACS - + Text Texto - + Show/Hide viewer's textual information Mostrar/esconder la información textual de los visores - + Overlays Overlays - + Show/Hide image overlays Mostrar/esconder los overlays de las imágenes - + Shutters Shutters - + Show/Hide shutter layer Mostrar/esconder los shutters - + Dump DICOM information of the current image Volcar información DICOM de la imagen actual - + Propagate properties between viewers (%1) Propagar propiedades entre visores (%1) - + Propagate Propagar - + Save current series image... Guardar la imagen de la serie actual... - + Save all images of the current series... Save all images from current series... Guardar todas las imágenes de la serie actual... - + All Todos - + Choose a VOI LUT preset Choose VOI LUT Presets Elegir una VOI LUT predeterminada - + Deactivate manual synchronization in all viewers (%1) Desactivar la sincronización manual en todos los visores (%1) - + None Ninguno - + Save the current image in a standard image format Guardar la imagen actual en un formato estándar - + Save all the images in the selected viewer in a standard image format Guardar todas la imágenes del visualizador seleccionado en un formato estándar - + Activate manual synchronization in all viewers (%1) Activate manual synchronization all viewers Activar la sincronización manual en todos los visores (%1) - + Export to DICOM Exportar a DICOM - + This action is not allowed because the selected viewer is empty. No se permite esta acción porque el visor está vacío. diff --git a/starviewer/src/inputoutput/inputoutput_ca_ES.ts b/starviewer/src/inputoutput/inputoutput_ca_ES.ts index c909536cf4..e85320f188 100644 --- a/starviewer/src/inputoutput/inputoutput_ca_ES.ts +++ b/starviewer/src/inputoutput/inputoutput_ca_ES.ts @@ -93,6 +93,56 @@ Close all %1 windows and try again. Tanqueu totes les finestres de %1 i torneu a intentar-ho. + + + Can't reinstall database because the current database can't be removed. + + + + + Can't read database creation script. Can't create the database. + + + + + Database creation script failed. + + + + + You don't have write permission on cache image directory. Retrieval or importing of new studies will fail. + No teniu permisos d'escriptura en el directori de memòria cau d'imatges. La descàrrega o importació de nous estudis fallarà. + + + + You don't have write permission on the database directory. Can't create the database. + + + + + You don't have write permission on %1 database. Retrieval or importing of new studies will fail. + No teniu permisos d'escriptura a la base de dades de %1. La descàrrega o importació de nous estudis fallarà. + + + + Unable to create the cache image directory. Please check user permissions. + No s'ha pogut crear el directori de meòria cau d'imatges. Comproveu els permisos d'usuari. + + + + Unable to create the database directory. Please check user permissions. + + + + + Reinstalling database + S'està reinstal·lant la base de dades + + + + Current database is of a newer version. In order to run %1, local studies must be deleted and the database reinstalled. Do you want to continue? + + udg::CacheTest @@ -171,74 +221,30 @@ udg::DICOMDIRImporter - + Importing image %1 of series %2 from study %3, %4 Importing Image %1 of Series %2 from Study %3, %4 S'està important la imatge %1 de la sèrie %2 de l'estudi %3, %4 - + Importing series %1 of study %2, %3 Importing Series %1 of Study %2, %3 S'està important la sèrie %1 de l'estudi %2, %3 - + Importing study %1, %2 Importing Study %1, %2 S'està important l'estudi %1, %2 - + Importing images from DICOMDIR Importing Images from DICOMDIR S'estan important les imatges del DICOMDIR - - udg::DatabaseInstallation - - - Unable to create database, be sure you have write permission on the database directory. - No s'ha pogut crear la base de dades, assegureu-vos que teniu permisos d'escriptura en el directori de la base de dades. - - - - You don't have write permission on %1 database. Retrieval or importing of new studies will fail. - No teniu permisos d'escriptura a la base de dades de %1. La descàrrega o importació de nous estudis fallarà. - - - - Unable to upgrade database file, be sure you have write permission on the database directory. - No s'ha pogut actualitzar l'arxiu de base de dades, asseguri's que té permisos d'escriptura en el directori de base de dades. - - - - Unable to create the cache image directory. Please check user permissions. - No s'ha pogut crear el directori de meòria cau d'imatges. Comproveu els permisos d'usuari. - - - - You don't have write permission on cache image directory. Retrieval or importing of new studies will fail. - No teniu permisos d'escriptura en el directori de memòria cau d'imatges. La descàrrega o importació de nous estudis fallarà. - - - - Reinstalling database - S'està reinstal·lant la base de dades - - - - Updating database - S'està actualitzant la base de dades - - - - Current database is of newer version. In order to run %1, local studies must be deleted and the database will be reinstalled. Do you want to continue? - Current database is of newer version. In order to run %1, local studies must be deleted and database will be reinstalled. Do you want to continue? - La base de dades és d'una versió posterior a l'actual. Per continuar executant l'%1 es reinstal·larà la base de dades i s'esborraran els estudis descarregats. Voleu continuar? - - udg::EchoToPACSTest diff --git a/starviewer/src/inputoutput/inputoutput_en_GB.ts b/starviewer/src/inputoutput/inputoutput_en_GB.ts index 00494ac283..aff5892fbf 100644 --- a/starviewer/src/inputoutput/inputoutput_en_GB.ts +++ b/starviewer/src/inputoutput/inputoutput_en_GB.ts @@ -93,6 +93,56 @@ Close all %1 windows and try again. + + + Can't reinstall database because the current database can't be removed. + + + + + Can't read database creation script. Can't create the database. + + + + + Database creation script failed. + + + + + You don't have write permission on cache image directory. Retrieval or importing of new studies will fail. + + + + + You don't have write permission on the database directory. Can't create the database. + + + + + You don't have write permission on %1 database. Retrieval or importing of new studies will fail. + + + + + Unable to create the cache image directory. Please check user permissions. + + + + + Unable to create the database directory. Please check user permissions. + + + + + Reinstalling database + + + + + Current database is of a newer version. In order to run %1, local studies must be deleted and the database reinstalled. Do you want to continue? + + udg::CacheTest @@ -174,74 +224,30 @@ udg::DICOMDIRImporter - + Importing image %1 of series %2 from study %3, %4 Importing Image %1 of Series %2 from Study %3, %4 - + Importing series %1 of study %2, %3 Importing Series %1 of Study %2, %3 - + Importing study %1, %2 Importing Study %1, %2 - + Importing images from DICOMDIR Importing Images from DICOMDIR - - udg::DatabaseInstallation - - - Unable to create database, be sure you have write permission on the database directory. - - - - - You don't have write permission on %1 database. Retrieval or importing of new studies will fail. - - - - - Unable to upgrade database file, be sure you have write permission on the database directory. - - - - - Unable to create the cache image directory. Please check user permissions. - - - - - You don't have write permission on cache image directory. Retrieval or importing of new studies will fail. - - - - - Reinstalling database - - - - - Updating database - - - - - Current database is of newer version. In order to run %1, local studies must be deleted and the database will be reinstalled. Do you want to continue? - Current database is of newer version. In order to run %1, local studies must be deleted and database will be reinstalled. Do you want to continue? - - - udg::EchoToPACSTest diff --git a/starviewer/src/inputoutput/inputoutput_es_ES.ts b/starviewer/src/inputoutput/inputoutput_es_ES.ts index 74394609f5..8fb25a7670 100644 --- a/starviewer/src/inputoutput/inputoutput_es_ES.ts +++ b/starviewer/src/inputoutput/inputoutput_es_ES.ts @@ -93,6 +93,56 @@ Close all %1 windows and try again. Cierre todas las ventanas de %1 e inténtelo de nuevo. + + + Can't reinstall database because the current database can't be removed. + + + + + Can't read database creation script. Can't create the database. + + + + + Database creation script failed. + + + + + You don't have write permission on cache image directory. Retrieval or importing of new studies will fail. + No tiene permisos de escritura en el directorio de caché de imágenes. La descarga o importación de nuevos estudios fallará. + + + + You don't have write permission on the database directory. Can't create the database. + + + + + You don't have write permission on %1 database. Retrieval or importing of new studies will fail. + No tiene permisos de escritura en la base de datos de %1. La descarga o importación de nuevos estudios fallará. + + + + Unable to create the cache image directory. Please check user permissions. + No se ha podido crear el directorio de caché de imágenes. Compruebe los permisos de usuario. + + + + Unable to create the database directory. Please check user permissions. + + + + + Reinstalling database + Reinstalando la base de datos + + + + Current database is of a newer version. In order to run %1, local studies must be deleted and the database reinstalled. Do you want to continue? + + udg::CacheTest @@ -174,74 +224,30 @@ udg::DICOMDIRImporter - + Importing image %1 of series %2 from study %3, %4 Importing Image %1 of Series %2 from Study %3, %4 Importando imagen %1 de la serie %2 del estudio %3, %4 - + Importing series %1 of study %2, %3 Importing Series %1 of Study %2, %3 Importando serie %1 del estudio %2, %3 - + Importing study %1, %2 Importing Study %1, %2 Importando estudio %1, %2 - + Importing images from DICOMDIR Importing Images from DICOMDIR Importando imágenes del DICOMDIR - - udg::DatabaseInstallation - - - Unable to create database, be sure you have write permission on the database directory. - No se ha podido crear la base de datos, asegúrese que tiene permisos de escritura en el directorio de la base de datos. - - - - You don't have write permission on %1 database. Retrieval or importing of new studies will fail. - No tiene permisos de escritura en la base de datos de %1. La descarga o importación de nuevos estudios fallará. - - - - Unable to upgrade database file, be sure you have write permission on the database directory. - No se ha podido actualizar el archivo de base de datos, asegúrese que tiene permisos de escritura en el directorio de base de datos. - - - - Unable to create the cache image directory. Please check user permissions. - No se ha podido crear el directorio de caché de imágenes. Compruebe los permisos de usuario. - - - - You don't have write permission on cache image directory. Retrieval or importing of new studies will fail. - No tiene permisos de escritura en el directorio de caché de imágenes. La descarga o importación de nuevos estudios fallará. - - - - Reinstalling database - Reinstalando la base de datos - - - - Updating database - Actualizando la base de datos - - - - Current database is of newer version. In order to run %1, local studies must be deleted and the database will be reinstalled. Do you want to continue? - Current database is of newer version. In order to run %1, local studies must be deleted and database will be reinstalled. Do you want to continue? - La base de datos es de una versión posterior a la actual. Para continuar ejecutando %1 se reinstalará la base de datos y se borraran los estudios descargados. Quiere continuar? - - udg::EchoToPACSTest diff --git a/starviewer/src/interface/interface_ca_ES.ts b/starviewer/src/interface/interface_ca_ES.ts index 07c24cc766..008e76780e 100644 --- a/starviewer/src/interface/interface_ca_ES.ts +++ b/starviewer/src/interface/interface_ca_ES.ts @@ -4,17 +4,17 @@ QConfigurationDialogBase - + Show Advanced Options Mostra les opcions avançades - + Close Tanca - + Alt+B Alt+B @@ -103,24 +103,24 @@ udg::ExtensionHandler - + Loading, please wait... S'està carregant, espereu... - + Patient Loading Patient loading S'està carregant el pacient - + Sorry, an error occurred while loading data from patients:<br>%1 Sorry, an error occurred while loading the data of patients:<br> %1 S'ha produït un error mentre es carregaven les dades dels pacients:<br>%1 - + Sorry, it seems that there is no patient data that can be loaded. Sorry, it seems that there is no patient data we can load. Sembla que no hi ha dades de pacient que es puguin carregar. @@ -186,334 +186,349 @@ udg::QApplicationMainWindow - + &New Window &New Finestra &nova - + &Open Files... &Open file... &Obre fitxers... - + Open one or several existing volume files Open an existing volume file Obre un o diversos fitxers de volum existents - + &About &Quant a - + Show the application's About box Mostra el missatge informatiu de l'aplicació - + E&xit &Surt - + Exit the application Surt de l'aplicació - + &Close Tan&ca - + &File &Fitxer - + &Help &Ajuda - + &Language &Idioma - + Open a new working window Obre una nova finestra de treball - + &PACS... &PACS... - + Open PACS Query Screen Obre la finestra de consulta del PACS - + &Visualization &Visualització - + Open an existing DICOM folder Obre una carpeta DICOM existent - + Language Switch Canvi d'idioma - + Close current extension page Close the current extension page Tanca la pàgina de l'extensió actual - + Show log file Mostra el fitxer de registre - + &Window F&inestra - + Open the %1 Application Obre l'aplicació %1 - + Open Files from a Directory... Open files from a directory... Obre els fitxers d'un directori... - + &Local Database Studies... &Local Database Studies Estudis de la base de dades &local... - + Browse local database studies Browse Local Database Studies Navega pels estudis de la base de dades local - + Open DICOMDIR... Obre un DICOMDIR... - + Open DICOMDIR from CD, DVD, USB flash drive or hard disk Open DICOMDIR from CD, DVD, Pendrive or hard disk Obre un DICOMDIR des d'un CD, DVD, memòria USB o disc dur - + Maximize to Multiple Screens Maximize To Multiple Screen Maximitza a múltiples pantalles - + Maximize the window to as many screens as possible Maximize The Window To As Many Screens As Possible Maximitza la finestra a totes les pantalles possibles - - + + Move to Screen Move To Screen Mou a la pantalla - + Move the window to the screen... Move The Window To The Screen ... Mou la finestra a la pantalla... - + Move to previous screen Desplaça a la pantalla anterior - + Move the window to the previous screen Desplaça la finestra a la pantalla anterior - + Move to next screen Desplaça a la pantalla següent - + Move the window to the next screen Desplaça la finestra a la pantalla següent - + User Guide User guide Guia d'usuari - + Open user guide Open User guide Potser millor mostra la guia d'usuari? Obre la guia d'usuari - + Quick Start Guide Quick Start guide Guia ràpida - + Open quick start guide Open Quick Start guide Obre la guia ràpida - + Shortcuts Guide Shortcuts guide Guia de dreceres - + Open shortcuts guide Open Shortcuts guide Obre la guia de dreceres - + Show Log File Mostra el fitxer de registre - + &Release Notes &Release notes &Notes de la versió - + Show the application's release notes for current version Mostra les notes de la versió actual de l'aplicació - + &Configuration... &Configuració... - + &Run Diagnosis Tests &Run diagnosis tests &Executa els tests de diagnosi - + Run %1 diagnosis tests Run %1 diagnosis test Executa els tests de diagnosi de l'%1 - + &Tools &Eines - + Catalan Català - + Spanish Castellà - + English Anglès - + + &External applications + &Aplicació externa + + + Switch to %1 language Switch to %1 Language Canvia a l'idioma %1 - + Changes will take effect the next time you start the application Els canvis tindran efecte la pròxima vegada que inicieu l'aplicació - + + External application launch error + Error en llançar l'aplicació externa + + + + There has been an error launching the external application. + S'ha produït un error en llançar l'aplicació externa. + + + Beta Version Beta version Versió beta - + <h2>%1</h2><p align='justify'>This is a preview release of %1 used exclusively for testing purposes.</p><p align='justify'>This version is intended for radiologists and our test-team members. Users of this version should not expect extensions to function properly.</p><p align='justify'>If you want to help us to improve %1, please report any found bug or any feature request you may have by sending an e-mail to: <a href="mailto:%2">%2</a></p><h3>We really appreciate your feedback!</h3> <h2>%1</h2><p align='justify'>This is a preview release of %1 used exclusively for testing purposes.</p><p align='justify'>This version is intended for radiologists and our test-team members. Users of this version should not expect the extensions function properly.</p><p align='justify'>If you want to help us to improve %1, please report any found bug or any feature request you may have by sending an e-mail to: <a href="mailto:%2">%2</a></p><h3>We really appreciate your feedback!</h3> <h2>%1</h2><p align='justify'>Aquesta és una versió preliminar de l'%1 que s'hauria d'utilitzar exclusivament amb finalitats de verificació.</p><p align='justify'>Aquesta versió està dirigida als radiòlegs i al nostre equip de proves. En aquesta versió és possible que les extensions no funcionin correctament.</p><p align='justify'>Si ens voleu ajudar a millorar l'%1 informeu-nos de qualsevol error o funcionalitat que necessiteu enviant-nos un correu electrònic a: <a href="mailto:%2">%2.</a></p><h3>Us agraïm molt la vostra col·laboració.</h3> - + Please activate "Listen to RIS requests" option in %1 configuration to retrieve studies from SAP. Please activate "Listen RIS Request" option in %1 configuration to retrieve studies from SAP. Activeu l'opció «Escolta peticions del RIS» a la configuració de l'%1 per poder rebre estudis del SAP. - + Loading S'està carregant - + Loading data, please wait... S'estan carregant les dades, espereu... - + &Exams... &Estudis... - + Browse exams Explora estudis - + Modify %1 configuration Modifica la configuració de l'%1 - + There have been some errors: There have been some errors: @@ -521,7 +536,7 @@ - + You can resolve this error at Tools > Configuration > Local Database. @@ -532,37 +547,42 @@ You can resolve this error at Tools > Configuration > Local Database. udg::QConfigurationDialog - + %1 Configuration Configuració de l'%1 - + 2D Viewer Layout Distribució dels visors 2D - + PACS PACS - + Local Database Base de dades local - + RIS Listener Escolta peticions del RIS - + DICOMDIR - + + External application + Aplicació externa + + + 2D Viewer Visor 2D diff --git a/starviewer/src/interface/interface_en_GB.ts b/starviewer/src/interface/interface_en_GB.ts index b34828644a..f9a9166a0b 100644 --- a/starviewer/src/interface/interface_en_GB.ts +++ b/starviewer/src/interface/interface_en_GB.ts @@ -4,17 +4,17 @@ QConfigurationDialogBase - + Show Advanced Options - + Close - + Alt+B @@ -103,24 +103,24 @@ udg::ExtensionHandler - + Loading, please wait... - + Patient Loading Patient loading - + Sorry, an error occurred while loading data from patients:<br>%1 Sorry, an error occurred while loading the data of patients:<br> %1 - + Sorry, it seems that there is no patient data that can be loaded. Sorry, it seems that there is no patient data we can load. @@ -186,340 +186,355 @@ udg::QApplicationMainWindow - + &Close - + &New Window &New - + &Open Files... &Open file... - + Open one or several existing volume files Open an existing volume file - + &About - + Show the application's About box - + E&xit - + Exit the application - + &File - + &Language - + &Help - + Open a new working window - + &PACS... - + Open PACS Query Screen - + &Visualization - + Open an existing DICOM folder - + Language Switch - + Close current extension page Close the current extension page - + Show log file - + &Window - + Open the %1 Application - + Open Files from a Directory... Open files from a directory... - + &Local Database Studies... &Local Database Studies - + Browse local database studies Browse Local Database Studies - + Open DICOMDIR... - + Open DICOMDIR from CD, DVD, USB flash drive or hard disk Open DICOMDIR from CD, DVD, Pendrive or hard disk - + Maximize to Multiple Screens Maximize To Multiple Screen - + Maximize the window to as many screens as possible Maximize The Window To As Many Screens As Possible - - + + Move to Screen Move To Screen - + Move the window to the screen... Move The Window To The Screen ... - + Move to previous screen - + Move the window to the previous screen - + Move to next screen - + Move the window to the next screen - + User Guide User guide - + Open user guide Open User guide - + Quick Start Guide Quick Start guide - + Open quick start guide Open Quick Start guide - + Shortcuts Guide Shortcuts guide - + Open shortcuts guide Open Shortcuts guide - + Show Log File - + &Release Notes &Release notes - + Show the application's release notes for current version - + &Configuration... - + &Run Diagnosis Tests &Run diagnosis tests - + Run %1 diagnosis tests Run %1 diagnosis test - + &Tools - + Catalan - + Spanish - + English - + + &External applications + + + + Switch to %1 language Switch to %1 Language - + Changes will take effect the next time you start the application - + + External application launch error + + + + + There has been an error launching the external application. + + + + Beta Version Beta version - + <h2>%1</h2><p align='justify'>This is a preview release of %1 used exclusively for testing purposes.</p><p align='justify'>This version is intended for radiologists and our test-team members. Users of this version should not expect extensions to function properly.</p><p align='justify'>If you want to help us to improve %1, please report any found bug or any feature request you may have by sending an e-mail to: <a href="mailto:%2">%2</a></p><h3>We really appreciate your feedback!</h3> <h2>%1</h2><p align='justify'>This is a preview release of %1 used exclusively for testing purposes.</p><p align='justify'>This version is intended for radiologists and our test-team members. Users of this version should not expect the extensions function properly.</p><p align='justify'>If you want to help us to improve %1, please report any found bug or any feature request you may have by sending an e-mail to: <a href="mailto:%2">%2</a></p><h3>We really appreciate your feedback!</h3> - + Please activate "Listen to RIS requests" option in %1 configuration to retrieve studies from SAP. Please activate "Listen RIS Request" option in %1 configuration to retrieve studies from SAP. - + Loading - + Loading data, please wait... - + &Exams... - + Browse exams - + Modify %1 configuration - + There have been some errors: There have been some errors: - + You can resolve this error at Tools > Configuration > Local Database. @@ -530,37 +545,42 @@ You can resolve this error at Tools > Configuration > Local Database. udg::QConfigurationDialog - + %1 Configuration - + 2D Viewer Layout - + PACS - + Local Database - + RIS Listener - + DICOMDIR - + + External application + + + + 2D Viewer diff --git a/starviewer/src/interface/interface_es_ES.ts b/starviewer/src/interface/interface_es_ES.ts index 6ea05632ae..ea4ae788a4 100644 --- a/starviewer/src/interface/interface_es_ES.ts +++ b/starviewer/src/interface/interface_es_ES.ts @@ -4,17 +4,17 @@ QConfigurationDialogBase - + Show Advanced Options Mostrar opciones avanzadas - + Close Cerrar - + Alt+B Alt+B @@ -103,24 +103,24 @@ udg::ExtensionHandler - + Loading, please wait... Cargando, espere... - + Patient Loading Patient loading Cargando paciente - + Sorry, an error occurred while loading data from patients:<br>%1 Sorry, an error occurred while loading the data of patients:<br> %1 Ha habido un error mientras se cargaban los datos de los pacientes:<br>%1 - + Sorry, it seems that there is no patient data that can be loaded. Sorry, it seems that there is no patient data we can load. Parece que no hay datos de paciente que se puedan cargar. @@ -186,333 +186,348 @@ udg::QApplicationMainWindow - + &New Window &New &Nueva ventana - + &Open Files... &Open file... &Abrir archivos... - + Open one or several existing volume files Open an existing volume file Abrir uno o varios archivos de volumen existentes - + &About &Acerca de - + Show the application's About box Muestra información de la aplicación - + E&xit &Salir - + Exit the application Salir de la aplicación - + &Close &Cerrar - + &File &Archivo - + &Help A&yuda - + &Language &Idioma - + Open a new working window Abrir una nueva ventana de trabajo - + &PACS... &PACS... - + Open PACS Query Screen Abrir el diálogo de consulta del PACS - + &Visualization &Visualización - + Open an existing DICOM folder Abrir una carpeta DICOM existente - + Language Switch Cambio de idioma - + Close current extension page Close the current extension page Cerrar la página de la extensión actual - + Show log file Mostrar archivo de registro - + &Window V&entana - + Open the %1 Application Abrir la aplicación %1 - + Open Files from a Directory... Open files from a directory... Abrir los archivos de un directorio... - + &Local Database Studies... &Local Database Studies Estudios de la base de datos &local... - + Browse local database studies Browse Local Database Studies Explorar estudios de la base de datos local - + Open DICOMDIR... Abrir DICOMDIR... - + Open DICOMDIR from CD, DVD, USB flash drive or hard disk Open DICOMDIR from CD, DVD, Pendrive or hard disk Abrir DICOMDIR desde CD, DVD, memoria USB o disco duro - + Maximize to Multiple Screens Maximize To Multiple Screen Maximizar a múltiples pantallas - + Maximize the window to as many screens as possible Maximize The Window To As Many Screens As Possible Maximizar la ventana a todas las pantallas posibles - - + + Move to Screen Move To Screen Mover a la pantalla - + Move the window to the screen... Move The Window To The Screen ... Mover la ventana a la pantalla... - + Move to previous screen Desplazar a la pantalla anterior - + Move the window to the previous screen Desplazar la ventana a la pantalla anterior - + Move to next screen Desplazar a la pantalla siguiente - + Move the window to the next screen Desplazar la ventana a la pantalla siguiente - + User Guide User guide Guía de usuario - + Open user guide Open User guide Abrir la guía de usuario - + Quick Start Guide Quick Start guide Guía rápida - + Open quick start guide Open Quick Start guide Abrir la guía rápida - + Shortcuts Guide Shortcuts guide Guía de accesos rápidos - + Open shortcuts guide Open Shortcuts guide Abrir la guía de accesos rápidos - + Show Log File Mostrar archivo de registro - + &Release Notes &Release notes &Notas de la versión - + Show the application's release notes for current version Mostrar las notas de la versión actual de la aplicación - + &Configuration... &Configuración... - + &Run Diagnosis Tests &Run diagnosis tests &Ejecutar tests de diagnóstico - + Run %1 diagnosis tests Run %1 diagnosis test Ejecutar los tests de diagnóstico de %1 - + &Tools &Herramientas - + Catalan Catalán - + Spanish Español - + English Inglés - + + &External applications + &Aplicación externa + + + Switch to %1 language Switch to %1 Language Cambiar al idioma %1 - + Changes will take effect the next time you start the application Los canvios tendrán efecto la próxima vez que inicie la aplicación - + + External application launch error + Error en lanzar la aplicación externa + + + + There has been an error launching the external application. + Ha ocurrido un error en lanzar la aplicación externa. + + + Beta Version Beta version Versión beta - + <h2>%1</h2><p align='justify'>This is a preview release of %1 used exclusively for testing purposes.</p><p align='justify'>This version is intended for radiologists and our test-team members. Users of this version should not expect extensions to function properly.</p><p align='justify'>If you want to help us to improve %1, please report any found bug or any feature request you may have by sending an e-mail to: <a href="mailto:%2">%2</a></p><h3>We really appreciate your feedback!</h3> <h2>%1</h2><p align='justify'>This is a preview release of %1 used exclusively for testing purposes.</p><p align='justify'>This version is intended for radiologists and our test-team members. Users of this version should not expect the extensions function properly.</p><p align='justify'>If you want to help us to improve %1, please report any found bug or any feature request you may have by sending an e-mail to: <a href="mailto:%2">%2</a></p><h3>We really appreciate your feedback!</h3> <h2>%1</h2><p align='justify'>Esta es una versión preliminar de %1, que se tendría que utilizar exclusivamente con fines de verificación.</p><p align='justify'>Esta versión está dirigida a los radiólogos y a nuestro equipo de pruebas. En esta versión es posible que las extensiones no funcionen correctamente.</p><p align='justify'>Si quiere ayudarnos a mejorar %1 infórmenos de cualquier error o funcionalidad que necesite enviando un correo electrónico a: <a href="mailto:%2">%2.</a></p><h3>Agradecemos mucho su colaboración.</h3> - + Please activate "Listen to RIS requests" option in %1 configuration to retrieve studies from SAP. Please activate "Listen RIS Request" option in %1 configuration to retrieve studies from SAP. Active la opción «Escuchar peticiones del RIS» en la configuración de %1 para poder recibir estudios del SAP. - + Loading Cargando - + Loading data, please wait... Cargando los datos, espere... - + &Exams... &Estudios... - + Browse exams Explorar estudios - + Modify %1 configuration Modificar la configuración de %1 - + There have been some errors: There have been some errors: @@ -520,7 +535,7 @@ - + You can resolve this error at Tools > Configuration > Local Database. @@ -531,37 +546,42 @@ You can resolve this error at Tools > Configuration > Local Database. udg::QConfigurationDialog - + %1 Configuration Configuración de %1 - + 2D Viewer Layout Distribución de los visores 2D - + PACS PACS - + Local Database Base de datos local - + RIS Listener Escuchar peticiones del RIS - + DICOMDIR - + + External application + Aplicacion externa + + + 2D Viewer Visor 2D diff --git a/starviewer/src/interface/qapplicationmainwindow.cpp b/starviewer/src/interface/qapplicationmainwindow.cpp index 964afbb99f..550e4d0644 100644 --- a/starviewer/src/interface/qapplicationmainwindow.cpp +++ b/starviewer/src/interface/qapplicationmainwindow.cpp @@ -31,6 +31,8 @@ #include "starviewerapplicationcommandline.h" #include "risrequestwrapper.h" #include "qaboutdialog.h" +#include "externalapplication.h" +#include "externalapplicationsmanager.h" // Pel LanguageLocale #include "coresettings.h" @@ -410,6 +412,9 @@ void QApplicationMainWindow::createMenus() createLanguageMenu(); m_toolsMenu->addAction(m_configurationAction); m_toolsMenu->addAction(m_runDiagnosisTestsAction); + m_externalApplicationsMenu = 0; + createExternalApplicationsMenu(); + connect(ExternalApplicationsManager::instance(), SIGNAL(onApplicationsChanged()), this, SLOT(createExternalApplicationsMenu())); // Menú 'window' m_windowMenu = menuBar()->addMenu(tr("&Window")); @@ -460,6 +465,55 @@ void QApplicationMainWindow::createLanguageMenu() } } +void QApplicationMainWindow::createExternalApplicationsMenu() +{ + QList externalApplications = ExternalApplicationsManager::instance()->getApplications(); + delete m_externalApplicationsMenu; + + if (externalApplications.length() == 0) //If no external applications are defined, do not create the menu; + { + m_externalApplicationsMenu = 0; + return; + } + + m_externalApplicationsMenu = m_toolsMenu->addMenu(tr("&External applications")); + m_externalApplicationsMenu->setIcon(QIcon(":/images/system-run.svg")); + + QSignalMapper *signalMapper = new QSignalMapper(m_externalApplicationsMenu); + connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(launchExternalApplication(int))); + + QVector> shortcutVector(12); + shortcutVector[0] = ShortcutManager::getShortcuts(Shortcuts::ExternalApplication1); + shortcutVector[1] = ShortcutManager::getShortcuts(Shortcuts::ExternalApplication2); + shortcutVector[2] = ShortcutManager::getShortcuts(Shortcuts::ExternalApplication3); + shortcutVector[3] = ShortcutManager::getShortcuts(Shortcuts::ExternalApplication4); + shortcutVector[4] = ShortcutManager::getShortcuts(Shortcuts::ExternalApplication5); + shortcutVector[5] = ShortcutManager::getShortcuts(Shortcuts::ExternalApplication6); + shortcutVector[6] = ShortcutManager::getShortcuts(Shortcuts::ExternalApplication7); + shortcutVector[7] = ShortcutManager::getShortcuts(Shortcuts::ExternalApplication8); + shortcutVector[8] = ShortcutManager::getShortcuts(Shortcuts::ExternalApplication9); + shortcutVector[9] = ShortcutManager::getShortcuts(Shortcuts::ExternalApplication10); + shortcutVector[10] = ShortcutManager::getShortcuts(Shortcuts::ExternalApplication11); + shortcutVector[11] = ShortcutManager::getShortcuts(Shortcuts::ExternalApplication12); + + QListIterator i(externalApplications); + int position = 0; + while (i.hasNext()) + { + const ExternalApplication& extApp = i.next(); + QAction* action = new QAction(extApp.getName(),0); //When added to a QMenu, that menu becomes the parent. + if (position < shortcutVector.size()) + { + action->setShortcuts(shortcutVector[position]); + } + + m_externalApplicationsMenu->addAction(action); + signalMapper->setMapping(action, position); + connect(action, SIGNAL(triggered()), signalMapper, SLOT(map())); + position++; + } +} + QAction* QApplicationMainWindow::createLanguageAction(const QString &language, const QString &locale) { Settings settings; @@ -503,6 +557,21 @@ void QApplicationMainWindow::switchToLanguage(QString locale) QMessageBox::information(this, tr("Language Switch"), tr("Changes will take effect the next time you start the application")); } +void QApplicationMainWindow::launchExternalApplication(int i) +{ + QList externalApplications = ExternalApplicationsManager::instance()->getApplications(); + if (i < 0 && i >= externalApplications.size()) + { + ERROR_LOG("Trying to launch an unexistant external application"); + } + const ExternalApplication &app = externalApplications.at(i); + if (!ExternalApplicationsManager::instance()->launch(app)) + { + //Launch failed. + QMessageBox::critical(this, tr("External application launch error"), tr("There has been an error launching the external application.")); + } +} + QApplicationMainWindow* QApplicationMainWindow::setPatientInNewWindow(Patient *patient) { QApplicationMainWindow *newMainWindow = openBlankWindow(); diff --git a/starviewer/src/interface/qapplicationmainwindow.h b/starviewer/src/interface/qapplicationmainwindow.h index 5b6f943947..8589f240b1 100644 --- a/starviewer/src/interface/qapplicationmainwindow.h +++ b/starviewer/src/interface/qapplicationmainwindow.h @@ -39,6 +39,7 @@ class QLogViewer; class Patient; class StatsWatcher; class ApplicationVersionChecker; +class ExternalApplication; class QApplicationMainWindow : public QMainWindow { Q_OBJECT @@ -106,8 +107,8 @@ Q_OBJECT /// Escriu la configuració amb la que s'engegarà el programa el pròxim cop. \TODO: s'hauria de cridar també quan obrim una finestra nova? void writeSettings(); - // Fa accessibles les extensions a través dels menús. S'hauria de cridar quan ja - // tenim un input vàlid i es poden carregar volums + /// Fa accessibles les extensions a través dels menús. S'hauria de cridar quan ja + /// tenim un input vàlid i es poden carregar volums void enableExtensions(); /// Marca aquesta aplicació com a aplicació beta, mostrant informació a l'usuari. @@ -124,9 +125,9 @@ Q_OBJECT void computeDefaultToolTextSize(); private slots: - // Mètode genèric que s'assabenta del progrés de càrrega d'un volum i el notifica d'alguna manera en l'interfície - // com per exemple un QProgressDialog o en un label - // @param progress valor del progrés de càrrega + /// Mètode genèric que s'assabenta del progrés de càrrega d'un volum i el notifica d'alguna manera en l'interfície + /// com per exemple un QProgressDialog o en un label + /// @param progress valor del progrés de càrrega void updateVolumeLoadProgressNotification(int progress); /// Mostra el diàleg on s'explica que és una versió beta. @@ -141,6 +142,11 @@ private slots: /// Canvia a l'idioma indicat void switchToLanguage(QString locale); + /// Launches the corresponding external application. (Where the number is + /// the position on the list). If application launch has failed, shows an + /// error message box. + void launchExternalApplication(int i); + /// Maximitza a tantes pantalles com es pugui void maximizeMultipleScreens(); @@ -172,6 +178,13 @@ private slots: /// Mostra el diàleg que executa els diagnosis test void showDiagnosisTestDialog(); + /// @brief External applications submenu with the defined external applications. + /// + /// When called multiple times, deletes the previous menu and regenerates a new one. + /// + /// If no external applications are defined, the menu is not created. + void createExternalApplicationsMenu(); + private: /// L'àrea de mini-aplicacions ExtensionWorkspace *m_extensionWorkspace; @@ -184,6 +197,7 @@ private slots: QMenu *m_visualizationMenu; QMenu *m_windowMenu; QMenu *m_languageMenu; + QMenu *m_externalApplicationsMenu; QMenu *m_helpMenu; QMenu *m_toolsMenu; QMenu *m_moveWindowToDesktopMenu; diff --git a/starviewer/src/interface/qconfigurationdialog.cpp b/starviewer/src/interface/qconfigurationdialog.cpp index 70dcdfa733..5c923332db 100644 --- a/starviewer/src/interface/qconfigurationdialog.cpp +++ b/starviewer/src/interface/qconfigurationdialog.cpp @@ -11,7 +11,7 @@ may be copied, modified, propagated, or distributed except according to the terms contained in the LICENSE file. *************************************************************************************/ - +#include #include "qconfigurationdialog.h" #ifndef STARVIEWER_LITE @@ -23,6 +23,7 @@ #include "qdicomdirconfigurationscreen.h" #include "q2dviewerconfigurationscreen.h" #include "q2dviewerlayoutconfigurationscreen.h" +#include "qexternalapplicationconfigurationscreen.h" #include "starviewerapplication.h" namespace udg { @@ -62,6 +63,10 @@ QConfigurationDialog::QConfigurationDialog(QWidget *parent, Qt::WindowFlags f) QDICOMDIRConfigurationScreen *dicomdirScreen = new QDICOMDIRConfigurationScreen(this); this->addConfigurationWidget(dicomdirScreen, tr("DICOMDIR"), AdvancedConfiguration); + // External applications configuration + QExternalApplicationConfigurationScreen *externalApplicationScreen = new QExternalApplicationConfigurationScreen(this); + this->addConfigurationWidget(externalApplicationScreen, tr("External application"), AdvancedConfiguration); + connect(m_viewAdvancedOptions, SIGNAL(stateChanged(int)), SLOT(setViewAdvancedConfiguration())); m_optionsList->setCurrentRow(0); @@ -72,6 +77,24 @@ QConfigurationDialog::~QConfigurationDialog() { } +void QConfigurationDialog::closeEvent(QCloseEvent *event) +{ + bool close = true; + foreach (QWidget *screen, m_configurationScreenWidgets) + { + close = close && screen->close(); + } + if (close) + { + event->accept(); + } + else + { + event->ignore(); + } + +} + void QConfigurationDialog::setViewAdvancedConfiguration() { foreach (QListWidgetItem *item, m_configurationListItems.values(AdvancedConfiguration)) @@ -124,6 +147,7 @@ void QConfigurationDialog::addConfigurationWidget(QWidget *widget, const QString item->setIcon(widget->windowIcon()); m_configurationListItems.insert(type, item); + m_configurationScreenWidgets.append(widget); } } diff --git a/starviewer/src/interface/qconfigurationdialog.h b/starviewer/src/interface/qconfigurationdialog.h index 10d9d87e10..d0aa13d84a 100644 --- a/starviewer/src/interface/qconfigurationdialog.h +++ b/starviewer/src/interface/qconfigurationdialog.h @@ -16,8 +16,8 @@ #define UDGQCONFIGURATIONDIALOG_H #include "ui_qconfigurationdialogbase.h" -#include +#include #include namespace udg { @@ -32,6 +32,10 @@ Q_OBJECT ~QConfigurationDialog(); +protected: + /// @brief Calls .close() on each configuration subdialog. + void closeEvent(QCloseEvent* event); + private slots: /// S'encarrega d'amagar/mostrar les opcions de configuració depenent si s'ha seleccionat o no l'opció /// de mostrar opcions avançades. @@ -46,6 +50,7 @@ private slots: private: QMultiMap m_configurationListItems; + QList m_configurationScreenWidgets; }; } diff --git a/starviewer/src/interface/qconfigurationdialogbase.ui b/starviewer/src/interface/qconfigurationdialogbase.ui index 652430967d..5cc2216c80 100644 --- a/starviewer/src/interface/qconfigurationdialogbase.ui +++ b/starviewer/src/interface/qconfigurationdialogbase.ui @@ -21,7 +21,16 @@ 6 - + + 9 + + + 9 + + + 9 + + 9 @@ -29,7 +38,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -77,7 +95,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -131,13 +158,14 @@ + m_okButton clicked() QConfigurationDialogBase - accept() + close() 312 diff --git a/starviewer/src/main/images/system-run-big.svg b/starviewer/src/main/images/system-run-big.svg new file mode 100644 index 0000000000..db6d4d0ef0 --- /dev/null +++ b/starviewer/src/main/images/system-run-big.svg @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/starviewer/src/main/images/system-run.svg b/starviewer/src/main/images/system-run.svg new file mode 100644 index 0000000000..026319d3f7 --- /dev/null +++ b/starviewer/src/main/images/system-run.svg @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/starviewer/src/main/main.qrc b/starviewer/src/main/main.qrc index 98263089cf..e488cbd987 100644 --- a/starviewer/src/main/main.qrc +++ b/starviewer/src/main/main.qrc @@ -165,5 +165,7 @@ ../../COPYRIGHT ../../LICENSE ../../NOTICE + images/system-run-big.svg + images/system-run.svg diff --git a/starviewer/tests/auto/unittests/core/core.pri b/starviewer/tests/auto/unittests/core/core.pri index bbb671bf52..799117b46d 100644 --- a/starviewer/tests/auto/unittests/core/core.pri +++ b/starviewer/tests/auto/unittests/core/core.pri @@ -85,7 +85,8 @@ SOURCES += $$PWD/test_image.cpp \ $$PWD/test_griditerator.cpp \ $$PWD/test_voilut.cpp \ $$PWD/test_hangingprotocolimagesetrestriction.cpp \ - $$PWD/test_hangingprotocolimagesetrestrictionexpression.cpp + $$PWD/test_hangingprotocolimagesetrestrictionexpression.cpp \ + $$PWD/test_externalapplication.cpp win32 { SOURCES += $$PWD/test_windowsfirewallaccess.cpp \ diff --git a/starviewer/tests/auto/unittests/core/test_externalapplication.cpp b/starviewer/tests/auto/unittests/core/test_externalapplication.cpp new file mode 100644 index 0000000000..74a5ed7812 --- /dev/null +++ b/starviewer/tests/auto/unittests/core/test_externalapplication.cpp @@ -0,0 +1,79 @@ +#include "autotest.h" + +#include "externalapplication.h" + +using namespace udg; + +class test_ExternalApplication : public QObject { +Q_OBJECT +private slots: + + void getReplacedUrl_ReturnsExpectedValues_data(); + void getReplacedUrl_ReturnsExpectedValues(); +}; + + +Q_DECLARE_METATYPE(ExternalApplication) + +void test_ExternalApplication::getReplacedUrl_ReturnsExpectedValues_data() +{ + QTest::addColumn("extApp"); + QTest::addColumn>("replacements"); + QTest::addColumn("expectedString"); + + QHash replacements; + + replacements = QHash(); + QTest::newRow("test0.1") << ExternalApplication() << replacements << QString(); + QTest::newRow("test0.2") << ExternalApplication(QString(),QString()) << replacements << QString(); + QTest::newRow("test0.3") << ExternalApplication("","") << replacements << ""; + QTest::newRow("test0.4") << ExternalApplication("test","http://example.com") << replacements << "http://example.com"; + QTest::newRow("test0.5") << ExternalApplication("test","http://example{%dummy%}.com") << replacements << "http://example.com"; + + replacements = QHash(); + replacements["foo"] = "bar"; + QTest::newRow("test1.1") << ExternalApplication("test","http://example.com/{%dummy%}") << replacements << "http://example.com/"; + QTest::newRow("test1.2") << ExternalApplication("test","{%foo%}") << replacements << "bar"; + QTest::newRow("test1.3") << ExternalApplication("test","http://example.com/{%foo%}") << replacements << "http://example.com/bar"; + QTest::newRow("test1.4") << ExternalApplication("test","http://example.com/{%foo%}{%foo%}") << replacements << "http://example.com/barbar"; + QTest::newRow("test1.5") << ExternalApplication("test","http://example.com/{%foo%}foo%}") << replacements << "http://example.com/barfoo%}"; + + replacements = QHash(); + replacements["thug"] = "life"; + replacements["nyan"] = "cat"; + replacements["n"] = ""; + replacements["%"] = "percent"; + replacements["{%"] = "percentopen"; + QTest::newRow("test2.1") << ExternalApplication("test","http://example.com/{%nyan%}{%thug%}") << replacements << "http://example.com/catlife"; + QTest::newRow("test2.2") << ExternalApplication("test","{%nyan%}://example.com/") << replacements << "cat://example.com/"; + QTest::newRow("test2.3") << ExternalApplication("test","{%n%}") << replacements << ""; + QTest::newRow("test2.4") << ExternalApplication("test","a{%n%}") << replacements << "a"; + QTest::newRow("test2.5") << ExternalApplication("test","{%n%}a") << replacements << "a"; + QTest::newRow("test2.6") << ExternalApplication("test","{%nyan%}{%n%}") << replacements << "cat"; + QTest::newRow("test2.7") << ExternalApplication("test","{%n%}{%nyan%}") << replacements << "cat"; + QTest::newRow("test2.8") << ExternalApplication("test","{%%}") << replacements << ""; + QTest::newRow("test2.9") << ExternalApplication("test","{%%%}") << replacements << "percent"; + QTest::newRow("test2.10") << ExternalApplication("test","{%{%%}") << replacements << "percentopen"; + + replacements = QHash(); + replacements["percentencoding"] = "33657-->1.2413 (2%)"; + QTest::newRow("test3.1") << ExternalApplication("test","http://exam ple.com/{%percentencoding%}") << replacements << "http://exam ple.com/33657--%3E1.2413%20%282%25%29"; + QTest::newRow("test3.2") << ExternalApplication("test","http://exam ple.com/{%percentencoding%}",ExternalApplication::ExternalApplicationType::Command) << replacements << "http://exam ple.com/33657-->1.2413 (2%)"; + + +} + +void test_ExternalApplication::getReplacedUrl_ReturnsExpectedValues() +{ + typedef QHash ReplacementsType; //TODO: mirar si es pot posar directe al QFETCH + QFETCH(ExternalApplication, extApp); + QFETCH(ReplacementsType, replacements); + QFETCH(QString, expectedString); + + QCOMPARE(extApp.getReplacedUrl(replacements), expectedString); +} + + +DECLARE_TEST(test_ExternalApplication) + +#include "test_externalapplication.moc"