Commit 27c68622 authored by saubatn's avatar saubatn
Browse files

NEW : File type association with camitk-imp.exe on Windows for opening.

      Each launch of an instance of IMP register it for file opening.
      Standard volume image and mesh files type (in the share/testdata directory) are handled.
      See the ImpMainWindow constructor for registered file type.
      Note : file association only works on Windows.

git-svn-id: svn+ssh://scm.forge.imag.fr/var/lib/gforge/chroot/scmrepos/svn/camitk/trunk/camitk@512 ec899d31-69d1-42ba-9299-647d76f65fb3
parent ed0214a1
......@@ -94,7 +94,7 @@ Action::ApplyStatus OpenAction::apply() {
}
else {
Application::showStatusBarMessage(tr ("Error loading files: ") + (*fileIterator));
return ERROR;
return FAILED;
}
}
else {
......
......@@ -65,5 +65,5 @@ Action::ApplyStatus RemoveLastInstanciatedAction::apply() {
Application::refresh();
return SUCCESS;
}
return ERROR;
return FAILED;
}
......@@ -71,5 +71,5 @@ Action::ApplyStatus SaveAction::apply() {
while (it != allTopLevel.constEnd() && Application::save(*it))
++it;
return (it == allTopLevel.constEnd())? SUCCESS :ERROR;
return (it == allTopLevel.constEnd())? SUCCESS : FAILED;
}
......@@ -68,9 +68,9 @@ Action::ApplyStatus SaveAllAction::apply() {
while (it != Application::getTopLevelComponents().constEnd() && Application::save(*it))
++it;
return (it == Application::getTopLevelComponents().constEnd()) ? SUCCESS :ERROR;
return (it == Application::getTopLevelComponents().constEnd()) ? SUCCESS : FAILED;
Application::showStatusBarMessage( tr ( "Ready." ) );
return ERROR;
return FAILED;
}
......@@ -182,7 +182,7 @@ Action::ApplyStatus SaveAsAction::apply() {
}
else {
comp->setFileName(compfileName);
return ERROR;
return FAILED;
}
}
else {
......@@ -191,6 +191,6 @@ Action::ApplyStatus SaveAsAction::apply() {
}
return ERROR;
return FAILED;
}
......@@ -71,6 +71,6 @@ Action::ApplyStatus SelectLastInstanciatedAction::apply() {
Application::refresh();
return SUCCESS;
}
return ERROR;
return FAILED;
}
......@@ -95,7 +95,7 @@ Action::ApplyStatus CenterMesh::apply() {
}
else {
CAMITK_INFO("CenterMesh", "apply transformation", "Error: filter output is of type: " << filter->GetOutputDataObject(0)->GetClassName());
return ERROR;
return FAILED;
}
Application::setProgressBarValue(100.0*(i+1.0)/((double)getTargets().size()));
......
......@@ -200,7 +200,7 @@ Action::ApplyStatus RigidTransform::apply() {
}
else {
CAMITK_INFO("RigidTransform", "applyTransform", "Error: filter output is of type: " << filterList[i]->GetOutputDataObject(0)->GetClassName());
return ERROR;
return FAILED;
}
// next filter
i++;
......
......@@ -72,6 +72,63 @@ ImpMainWindow::ImpMainWindow() : MainWindow( "imp ") {
showViewer(ActionViewer::getActionViewer(), false);
ActionViewer::getActionViewer()->setSearchPanelVisible(true);
showStatusBar(true);
#ifdef WIN32
// Use document windows parent class of MainWindow to register file type with camitk-imp for opening
DocumentWindow();
// file association to be opened with imp
registerFileType("camitk-imp.mha", // Document type name
"MHA volume image file", // User readable file type name
".mha", // file extension
0, // index of the icon to use for the files.
false); // register for DDE events
registerFileType("camitk-imp.vtk", // Document type name
"VTK mesh file", // User readable file type name
".vtk", // file extension
0, // index of the icon to use for the files.
false); // register for DDE events
registerFileType("camitk-imp.msh", // Document type name
"MSH mesh file", // User readable file type name
".msh", // file extension
0, // index of the icon to use for the files.
false); // register for DDE events
registerFileType("camitk-imp.mixed", // Document type name
"Volume image and mesh file",// User readable file type name
".mixed", // file extension
0, // index of the icon to use for the files.
false); // register for DDE events
registerFileType("camitk-imp.obj", // Document type name
"OBJ mesh file", // User readable file type name
".obj", // file extension
0, // index of the icon to use for the files.
false); // register for DDE events
registerFileType("camitk-imp.msh", // Document type name
"MSH mesh file", // User readable file type name
".msh", // file extension
0, // index of the icon to use for the files.
false); // register for DDE events
registerFileType("camitk-imp.wrl", // Document type name
"WRL mesh file", // User readable file type name
".wrl", // file extension
0, // index of the icon to use for the files.
false); // register for DDE events
registerFileType("camitk-imp.mtl", // Document type name
"MTL mesh file", // User readable file type name
".off", // file extension
0, // index of the icon to use for the files.
false); // register for DDE events
registerFileType("camitk-imp.mhd", // Document type name
"MHD volume image file", // User readable file type name
".mhd", // file extension
0, // index of the icon to use for the files.
false); // register for DDE events
registerFileType("camitk-imp.raw", // Document type name
"RAW volume image file", // User readable file type name
".raw", // file extension
0, // index of the icon to use for the files.
false); // register for DDE events
enableShellOpen();
#endif
}
// ------------- destructor -----------------
......
......@@ -26,6 +26,8 @@ set(CAMITKCORE_LIBRARY_PROPERTIES ${CAMITKCORE_LIBRARY_PROPERTIES}
SOVERSION "${CAMITK_VER_MAJ}"
)
# CAMITK_CORE_LIB needs to be SHARED and loaded only ONCE otherwise the extensions
# can have their own copy of CAMITK_CORE_LIB, which will generates loads of problems
# because of the Singleton design pattern and various factory bits in CAMITK_CORE_LIB.
......@@ -36,6 +38,10 @@ addSubProject(${CAMITK_CORE_LIB})
# set the library specific info (SONAME...)
set_target_properties(${CAMITK_CORE_LIB} PROPERTIES ${CAMITKCORE_LIBRARY_PROPERTIES} LINK_INTERFACE_LIBRARIES "")
if (WIN32)
# property required for camitk file association on windows platform compilation
set_target_properties(${CAMITK_CORE_LIB} PROPERTIES COMPILE_FLAGS "/Zc:wchar_t-")
endif()
target_link_libraries(${CAMITK_CORE_LIB} ${CAMITK_LIBRARIES})
......
......@@ -336,6 +336,7 @@ const QString Core::sharedDirectory() {
return sharedDir;
}
}
......
......@@ -139,7 +139,7 @@ public:
/// fall-back install directory (if the autodetection did not work...)
static const char *installDir;
/// version used for so name
static const char *soVersion;
static const char *soVersion;
///@}
private:
......
......@@ -203,7 +203,7 @@ Action::ApplyStatus Action::trigger ( QWidget * parent )
else
{
QMessageBox::information ( parent, "Cannot apply this Action to selection", "Sorry, but the action \"" + getName() + "\" cannot be applied on the currently selected component(s)" );
return ERROR;
return FAILED;
}
}
......
......@@ -176,7 +176,7 @@ public:
/// \enum ApplyStatus describes what happened during the application of an algorithm (i.e. results of the apply method)
enum ApplyStatus {
SUCCESS, ///< everything went according to plan
ERROR, ///< an error occured (usually it means that the apply() was interrupted)
FAILED, ///< apply() failed : an error occured (usually it means that the apply() was interrupted)
WARNING, ///< some (partial) error occured during the application of the algorithm
ABORTED, ///< the action was aborted before completion
TRIGGERED ///< the action was triggered only, but not applied
......
// -------------------------------------------------------------------------------------------------
/**
* @file
* @brief
* @author Gerolf Reinwardt
* @date 30.01.2011
*
* Copyright (c) 2011, Gerolf Reinwardt. All rights reserved.
*
* Simplified BSD License
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of Gerolf Reinwardt.
*/
// -------------------------------------------------------------------------------------------------
// ----- general includes --------------------------------------------------------------------------
#include <windows.h>
#include <QtGui/QMessageBox>
#include <QtGui/QApplication>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QRegExp>
#include <QString>
// ----- local includes ----------------------------------------------------------------------------
#include "DocumentWindow.h"
#include <iostream>
// ----- construction ------------------------------------------------------------------------------
DocumentWindow::DocumentWindow(QWidget* parent, Qt::WindowFlags flags) :
QMainWindow(parent, flags),
m_registerForDDE(false),
m_appAtomName(),
m_systemTopicAtomName("system"),
m_appAtom(0),
m_systemTopicAtom(0)
{
QFileInfo fi(qApp->applicationFilePath());
m_appAtomName = fi.baseName();
}
DocumentWindow::~DocumentWindow()
{
if(0 != m_appAtom)
{
::GlobalDeleteAtom(m_appAtom);
m_appAtom = 0;
}
if(0 != m_systemTopicAtom)
{
::GlobalDeleteAtom(m_systemTopicAtom);
m_systemTopicAtom = 0;
}
}
// ----- operators ---------------------------------------------------------------------------------
// ----- methods -----------------------------------------------------------------------------------
// ----- accessors ---------------------------------------------------------------------------------
// ----- public slots ------------------------------------------------------------------------------
// ----- protected slots ---------------------------------------------------------------------------
// ----- events ------------------------------------------------------------------------------------
bool DocumentWindow::winEvent(MSG *message, long *result)
{
switch(message->message)
{
case WM_DDE_INITIATE:
return ddeInitiate(message, result);
break;
case WM_DDE_EXECUTE:
return ddeExecute(message, result);
break;
case WM_DDE_TERMINATE:
return ddeTerminate(message, result);
break;
}
return QMainWindow::winEvent(message, result);
}
void DocumentWindow::ddeOpenFile(const QString&)
{
// this method will be overwritten, if the application uses the dde open command
}
void DocumentWindow::ddeNewFile(const QString&)
{
// this method will be overwritten, if the application uses the dde new command
}
void DocumentWindow::ddePrintFile(const QString&)
{
// this method will be overwritten, if the application uses the dde print command
}
void DocumentWindow::executeUnknownDdeCommand(const QString&, const QString&)
{
// this method will be overwritten, if the application uses other commands,
// then open, new and print via DDE and Explorer
}
void DocumentWindow::registerFileType(const QString& documentId,
const QString& fileTypeName,
const QString& fileExtension,
qint32 appIconIndex,
bool registerForDDE,
DdeCommands commands)
{
// first register the type ID of our server
if (!SetHkcrUserRegKey(documentId, fileTypeName))
return;
if (!SetHkcrUserRegKey(QString("%1\\DefaultIcon").arg(documentId),
QString("\"%1\",%2").arg(QDir::toNativeSeparators(qApp->applicationFilePath())).arg(appIconIndex)))
return;
m_registerForDDE = registerForDDE;
if(commands & DDEOpen)
registerCommand("Open", documentId, " %1", "[open(\"%1\")]");
if(commands & DDENew)
registerCommand("New", documentId, "-new %1", "[new(\"%1\")]");
if(commands & DDEPrint)
registerCommand("Print", documentId, "-print %1", "[print(\"%1\")]");
LONG lSize = _MAX_PATH * 2;
wchar_t szTempBuffer[_MAX_PATH * 2];
LONG lResult = ::RegQueryValue(HKEY_CURRENT_USER,
(LPCSTR)((const wchar_t*)fileExtension.utf16()),
(LPSTR)szTempBuffer,
&lSize);
QString temp = QString::fromUtf16((unsigned short*)szTempBuffer);
if (lResult != ERROR_SUCCESS || temp.isEmpty() || temp == documentId)
{
// no association for that suffix
if (!SetHkcrUserRegKey(fileExtension, documentId))
return;
SetHkcrUserRegKey(QString("%1\\ShellNew").arg(fileExtension), QLatin1String(""), QLatin1String("NullFile"));
}
}
void DocumentWindow::registerCommand(const QString& command,
const QString& documentId,
const QString cmdLineArg,
const QString ddeCommand)
{
QString commandLine = QDir::toNativeSeparators(qApp->applicationFilePath());
commandLine.prepend(QLatin1String("\""));
commandLine.append(QLatin1String("\""));
if(!cmdLineArg.isEmpty())
{
commandLine.append(QChar(' '));
commandLine.append(cmdLineArg);
}
if (!SetHkcrUserRegKey(QString("%1\\shell\\%2\\command").arg(documentId).arg(command), commandLine))
return; // just skip it
if(m_registerForDDE)
{
if (!SetHkcrUserRegKey(QString("%1\\shell\\%2\\ddeexec").arg(documentId).arg(command), ddeCommand))
return;
if (!SetHkcrUserRegKey(QString("%1\\shell\\%2\\ddeexec\\application").arg(documentId).arg(command), m_appAtomName))
return;
if (!SetHkcrUserRegKey(QString("%1\\shell\\%2\\ddeexec\\topic").arg(documentId).arg(command), m_systemTopicAtomName))
return;
}
}
void DocumentWindow::enableShellOpen()
{
if((0 != m_appAtom) || (0 != m_systemTopicAtom))
return;
m_appAtom = ::GlobalAddAtomW((const wchar_t*)m_appAtomName.utf16());
m_systemTopicAtom = ::GlobalAddAtomW((const wchar_t*)m_systemTopicAtomName.utf16());
}
// ----- private slots -----------------------------------------------------------------------------
// ----- private helpers ---------------------------------------------------------------------------
bool DocumentWindow::ddeInitiate(MSG* message, long* result)
{
if( (0 != LOWORD(message->lParam)) &&
(0 != HIWORD(message->lParam)) &&
(LOWORD(message->lParam) == m_appAtom) &&
(HIWORD(message->lParam) == m_systemTopicAtom))
{
// make duplicates of the incoming atoms (really adding a reference)
wchar_t atomName[_MAX_PATH];
Q_ASSERT(::GlobalGetAtomNameW(m_appAtom, atomName, _MAX_PATH - 1) != 0);
Q_ASSERT(::GlobalAddAtomW(atomName) == m_appAtom);
Q_ASSERT(::GlobalGetAtomNameW(m_systemTopicAtom, atomName, _MAX_PATH - 1) != 0);
Q_ASSERT(::GlobalAddAtomW(atomName) == m_systemTopicAtom);
// send the WM_DDE_ACK (caller will delete duplicate atoms)
::SendMessage((HWND)message->wParam, WM_DDE_ACK, (WPARAM)winId(), MAKELPARAM(m_appAtom, m_systemTopicAtom));
}
*result = 0;
return true;
}
bool DocumentWindow::ddeExecute(MSG* message, long* result)
{
// unpack the DDE message
UINT_PTR unused;
HGLOBAL hData;
//IA64: Assume DDE LPARAMs are still 32-bit
Q_ASSERT(::UnpackDDElParam(WM_DDE_EXECUTE, message->lParam, &unused, (UINT_PTR*)&hData));
QString command = QString::fromWCharArray((LPCWSTR)::GlobalLock(hData));
::GlobalUnlock(hData);
// acknowledge now - before attempting to execute
::PostMessage((HWND)message->wParam, WM_DDE_ACK, (WPARAM)winId(),
//IA64: Assume DDE LPARAMs are still 32-bit
ReuseDDElParam(message->lParam, WM_DDE_EXECUTE, WM_DDE_ACK, (UINT)0x8000, (UINT_PTR)hData));
// don't execute the command when the window is disabled
if (!isEnabled())
{
*result = 0;
return true;
}
QRegExp regCommand("^\\[(\\w+)\\((.*)\\)\\]$");
if(regCommand.exactMatch(command))
{
executeDdeCommand(regCommand.cap(1), regCommand.cap(2));
}
*result = 0;
return true;
}
bool DocumentWindow::ddeTerminate(MSG* message, long* result)
{
// The client or server application should respond by posting a WM_DDE_TERMINATE message.
::PostMessageW((HWND)message->wParam, WM_DDE_TERMINATE, (WPARAM)winId(), message->lParam);
return true;
}
bool DocumentWindow::SetHkcrUserRegKey(QString key, const QString& value, const QString& valueName)
{
HKEY hKey;
key.prepend("Software\\Classes\\");
LONG lRetVal = RegCreateKey(HKEY_CURRENT_USER,
key.toStdString().c_str(),
&hKey);
// LONG lRetVal = RegCreateKeyEx(HKEY_CURRENT_USER, key.toStdString().c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL);
if(ERROR_SUCCESS == lRetVal)
{
LONG lResult = ::RegSetValueExW(hKey,
valueName.isEmpty() ? 0 : (const wchar_t*)valueName.utf16(),
0,
REG_SZ,
(CONST BYTE*)value.utf16(),
(value.length() + 1) * sizeof(wchar_t));
if(::RegCloseKey(hKey) == ERROR_SUCCESS && lResult == ERROR_SUCCESS)
return true;
QMessageBox::warning(0, QString("Error in setting Registry values"),
QString("registration database update failed for key '%s'.").arg(key));
}
else
{
wchar_t buffer[4096];
::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, lRetVal, 0, buffer, 4096, 0);
QString szText = QString::fromUtf16((const ushort*)buffer);
QMessageBox::warning(this, QString("Error in setting Registry values"), szText);
}
return false;
}
void DocumentWindow::executeDdeCommand(const QString& command, const QString& params)
{
QRegExp regCommand("^\"(.*)\"$");
bool singleCommand = regCommand.exactMatch(params);
if((0 == command.compare("open", Qt::CaseInsensitive)) && singleCommand)
{
ddeOpenFile(regCommand.cap(1));
}
else if((0 == command.compare("new", Qt::CaseInsensitive)) && singleCommand)
{
ddeNewFile(regCommand.cap(1));
}
else if((0 == command.compare("print", Qt::CaseInsensitive)) && singleCommand)
{
ddePrintFile(regCommand.cap(1));
}
else
{
executeUnknownDdeCommand(command, params);
}
}
// -------------------------------------------------------------------------------------------------
/**
* @file
* @brief
* @author Gerolf Reinwardt
* @date 30. march 2011
*
* Copyright (c) 2011, Gerolf Reinwardt. All rights reserved.
*
* Simplified BSD License
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of Gerolf Reinwardt.
*/
// -------------------------------------------------------------------------------------------------
#ifdef WIN32 // do not use this subclass on other platform than WINDOWS
#ifndef WIDGET_H
#define WIDGET_H
// ----- general includes --------------------------------------------------------------------------
#include <windows.h>
#include <QtGui/QMainWindow>
// CamiTK include
#include "CAMITKAPI.h"
// ----- local includes ----------------------------------------------------------------------------
// ----- pre defines -------------------------------------------------------------------------------
// ----- class definition --------------------------------------------------------------------------
/**
* @short This class implements a main window with the ability to register file types on MS Windows and
* react on the corresponding DDE events to open / print the files in an Windows MDI typical manner.
*
* The usage is fairly easy. Derive your own MainWindow class from DocumentWindow instead of QMainWindow.
* Inside your constructor, call registerFileType and enableShellOpen. The example is build on top of the
* Qt MDI Example (http://doc.qt.nokia.com/4.7/mainwindows-mdi.html)
*
* @code
MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) :
DocumentWindow(parent, flags)
{
...
registerFileType("GiMdi.Document", "MDI Text Editor Document", ".gidoc", 0, true);
enableShellOpen();