--- /dev/null
+INCLUDE(${crea_USE_FILE})
+
--- /dev/null
+# We have to find crea
+FIND_PACKAGE(crea REQUIRED)
\ No newline at end of file
--- /dev/null
+#include <BlockScopeWxApp.h>
+#include <wx/wx.h>
+
+
+class DummyWxApp : public wxApp
+{
+public:
+ bool OnInit( );
+ int OnExit() { return true; }
+};
+
+IMPLEMENT_APP_NO_MAIN(DummyWxApp);
+
+
+bool DummyWxApp::OnInit( )
+{
+// std::cout << "OnInit()"<<std::endl;
+ wxApp::OnInit();
+#ifdef __WXGTK__
+ //See http://www.wxwindows.org/faqgtk.htm#locale
+ setlocale(LC_NUMERIC, "C");
+#endif
+ return true;
+}
+
+
+BlockScopeWxApp::BlockScopeWxApp()
+{
+ mNeedToUninitialize = false;
+ if (wxApp::GetInstance()==0)
+ {
+ wxApp::SetInstance(new DummyWxApp);
+ wxInitialize();
+ mNeedToUninitialize = true;
+ }
+}
+
+BlockScopeWxApp::~BlockScopeWxApp()
+{
+ if (mNeedToUninitialize) wxUninitialize();
+}
+
+
+
+
--- /dev/null
+#ifndef __wxAppNoMain_h__
+#define __wxAppNoMain_h__
+
+
+class BlockScopeWxApp
+{
+public:
+ BlockScopeWxApp();
+ ~BlockScopeWxApp();
+private:
+ bool mNeedToUninitialize;
+};
+
+
+#endif
--- /dev/null
+SET(LIBRARY_NAME creaImageIO2)
+
+
+FILE(GLOB SOURCES_CREAIMAGEIO
+ # SQLite
+ CppSQLite3.cpp
+ #
+ creaImageIOGimmick.cpp
+ creaImageIOSynchron.cpp
+ creaImageIOPACSConnection.cpp
+
+ # Abstract views
+ creaImageIOTreeView.cpp
+
+ # settings
+ creaImageIOSettings.cpp
+
+ BlockScopeWxApp.cpp
+ creaImageIOGimmickReaderDialog.cpp
+ creaImageIOExternalGimmick.cpp
+ # Viewer
+ creaImageIOWxViewer.cpp
+ creaImageIOGimmickView.cpp
+ creaImageIOListener.cpp
+)
+
+
+
+ # Attributed tree data structure
+FILE(GLOB SOURCES_CREAIMAGEIO_TREE
+ creaImageIOTree.cpp
+ creaImageIOTreeAttributeDescriptor.cpp
+ creaImageIOTreeDescriptor.cpp
+ creaImageIOTreeNode.cpp
+ creaImageIOTreeLevelDescriptor.cpp
+ # Tree Handlers
+ creaImageIOTreeHandler.cpp
+ creaImageIOTreeHandlerImageAdder.cpp
+ creaImageIOSQLiteTreeHandler.cpp
+ )
+IF(USE_GDCM)
+ FILE(GLOB SOURCES_CREAIMAGEIO_IMG_DICOM_READER
+ creaImageIODicomImageReader.cpp)
+ENDIF(USE_GDCM)
+
+
+IF(USE_GDCM2)
+ FILE(GLOB SOURCES_CREAIMAGEIO_IMG_DICOM_READER
+ creaImageIODicomImageReader2.cpp)
+ENDIF(USE_GDCM2)
+
+ # Image Readers
+FILE(GLOB SOURCES_CREAIMAGEIO_IMG_READER
+ creaImageIOAbstractImageReader.cpp
+ creaImageIOImageReader.cpp
+ creaImageIOUltrasonixImageReader.cpp
+ creaImageIOVtkImageReader.cpp
+ creaImageIOMultiThreadImageReader.cpp
+ ${SOURCES_CREAIMAGEIO_IMG_DICOM_READER}
+ )
+
+
+# The wxWidgets-based components
+if (USE_WXWIDGETS)
+FILE(GLOB SOURCES_CREAIMAGEIO_WX
+ creaImageIOWxAttributeSelectionPanel.cpp
+ creaImageIOWxCustomizeConfigPanel.cpp
+ creaImageIOWxDescriptorPanel.cpp
+ creaImageIOWxEditFieldsPanel.cpp
+ creaImageIOWxExportDlg.cpp
+ creaImageIOWxDumpPanel.cpp
+ creaImageIOWxGimmickView.cpp
+ creaImageIOWxGimmickReaderDialog.cpp
+ creaImageIOWxGimmickFrame.cpp
+ creaImageIOWxGimmickPanel.cpp
+ creaImageIOWxGimmickTools.cpp
+ creaImageIOWxListenerPanel.cpp
+ creaImageIOWxPACSConnectionPanel.cpp
+ creaImageIOWxTreeView.cpp
+ )
+endif()
+
+# Header Files
+FILE(GLOB HEADER_CREAIMAGEIO creaImageIOImagePointerHolder.h)
+FILE(GLOB SOURCES_CREAIMAGEIO_PACS PACS/*.cpp)
+FILE(GLOB HEADER_CREAIMAGEIO_PACS PACS/*.h)
+
+
+SOURCE_GROUP("Source Files" FILES ${SOURCES_CREAIMAGEIO})
+SOURCE_GROUP("Header Files" FILES ${HEADER_CREAIMAGEIO})
+SOURCE_GROUP("Source Files\\GUI" FILES ${SOURCES_CREAIMAGEIO_WX})
+if(BUILD_CREA_PACS)
+ SOURCE_GROUP("Source Files\\PACS" FILES ${SOURCES_CREAIMAGEIO_PACS})
+ SOURCE_GROUP("Header Files\\PACS" FILES ${HEADER_CREAIMAGEIO_PACS})
+endif(BUILD_CREA_PACS)
+SOURCE_GROUP("Source Files\\Readers" FILES ${SOURCES_CREAIMAGEIO_IMG_READER}
+ ${SOURCES_CREAIMAGEIO_IMG_DICOM_READER})
+SOURCE_GROUP("Source Files\\Tree" FILES ${SOURCES_CREAIMAGEIO_TREE})
+
+
+SET( PRIMITIVE_SRCS
+ ${SOURCES_CREAIMAGEIO}
+ ${HEADER_CREAIMAGEIO}
+ ${SOURCES_CREAIMAGEIO_IMG_READER}
+ ${SOURCES_CREAIMAGEIO_IMG_DICOM_READER}
+ ${SOURCES_CREAIMAGEIO_WX}
+ ${SOURCES_CREAIMAGEIO_TREE}
+)
+
+if( BUILD_CREA_PACS)
+ SET (SRCS
+ ${PRIMITIVE_SRCS}
+ ${SOURCES_CREAIMAGEIO_PACS}
+ ${HEADER_CREAIMAGEIO_PACS}
+ )
+else (BUILD_CREA_PACS)
+ SET (SRCS
+ ${PRIMITIVE_SRCS}
+ )
+endif (BUILD_CREA_PACS)
+
+
+
+
+OPTION(${LIBRARY_NAME}_BUILD_SHARED
+ "Build ${LIBRARY_NAME} as a shared library (dynamic) ?" ON)
+IF (${LIBRARY_NAME}_BUILD_SHARED)
+ SET(CREAIMAGEIO_BUILD_SHARED SHARED)
+ crea_DEFINE(CREAIMAGEIO_BUILD_SHARED)
+ENDIF(${LIBRARY_NAME}_BUILD_SHARED)
+
+crea_DEFINE(CREAIMAGEIO_EXPORT_SYMBOLS)
+
+ADD_LIBRARY(${LIBRARY_NAME} ${CREAIMAGEIO_BUILD_SHARED} ${SRCS})
+
+
+TARGET_LINK_LIBRARIES(${LIBRARY_NAME}
+ ${crea_LIBRARIES}
+ ${creaBruker_LIBRARIES}
+ ${WXWIDGETS_LIBRARIES}
+ ${VTK_LIBRARIES}
+ ${GDCM_LIBRARIES}
+ ${BOOST_LIBRARIES}
+ sqlite3)
+
+#----------------------------------------------------------------------------
+# INSTALLS LIBRARY
+FILE(GLOB HEADERS "*.h" "*.txx")
+INSTALL(
+ FILES ${HEADERS}
+ DESTINATION include/${LIBRARY_NAME}
+ )
+
+
+IF (WIN32)
+ SET(CREAIMAGEIO_LIB_PATH bin)
+ELSE (WIN32)
+ SET(CREAIMAGEIO_LIB_PATH lib)
+ENDIF(WIN32)
+
+INSTALL(
+ TARGETS ${LIBRARY_NAME}
+ DESTINATION ${CREAIMAGEIO_LIB_PATH})
+
+ # Sets the settings for macro CREA_ADVANCED_INSTALL_LIBRARY_FOR_CMAKE
+ SET(${LIBRARY_NAME}_INSTALL_FOLDER ${LIBRARY_NAME})
+ SET(${LIBRARY_NAME}_LIBRARIES ${LIBRARY_NAME})
+
+# FILE(RELATIVE_PATH
+# ${LIBRARY_NAME}_BUILD_TREE_RELATIVE_INCLUDE_PATHS
+# ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+# )
+SET(${LIBRARY_NAME}_BUILD_TREE_RELATIVE_INCLUDE_PATHS
+ src2
+ win32
+)
+
+
+ IF ( ${PROJECT_BINARY_DIR} STREQUAL ${EXECUTABLE_OUTPUT_PATH} )
+ SET(CILFC_EXECUTABLE_OUTPUT_REL_PATH ".")
+ ELSE ( ${PROJECT_BINARY_DIR} STREQUAL ${EXECUTABLE_OUTPUT_PATH} )
+ FILE(RELATIVE_PATH
+ CILFC_EXECUTABLE_OUTPUT_REL_PATH
+ ${PROJECT_BINARY_DIR} ${EXECUTABLE_OUTPUT_PATH})
+ ENDIF ( ${PROJECT_BINARY_DIR} STREQUAL ${EXECUTABLE_OUTPUT_PATH} )
+
+ IF(UNIX)
+ SET(${LIBRARY_NAME}_BUILD_TREE_RELATIVE_LIBRARY_PATHS
+ ${CILFC_EXECUTABLE_OUTPUT_REL_PATH})
+ SET(${LIBRARY_NAME}_INSTALL_TREE_RELATIVE_LIBRARY_PATHS lib)
+ ELSE(UNIX)
+ SET(${LIBRARY_NAME}_BUILD_TREE_RELATIVE_LIBRARY_PATHS
+ ${CILFC_EXECUTABLE_OUTPUT_REL_PATH})
+ SET(${LIBRARY_NAME}_INSTALL_TREE_RELATIVE_LIBRARY_PATHS bin)
+ ENDIF(UNIX)
+ SET(${LIBRARY_NAME}_INSTALL_TREE_RELATIVE_INCLUDE_PATHS include/${LIBRARY_NAME})
+
+ SET(${LIBRARY_NAME}_HAS_ADDITIONAL_CONFIG_FILE TRUE)
+SET(${LIBRARY_NAME}_ADDITIONAL_CONFIG_FILE
+ ${PROJECT_SOURCE_DIR}/src2/Additional${LIBRARY_NAME}Config.cmake.in)
+SET(${LIBRARY_NAME}_ADDITIONAL_USE_FILE
+ ${PROJECT_SOURCE_DIR}/src2/AdditionalUse${LIBRARY_NAME}.cmake.in)
+
+ # Invoke the advanced macro
+ CREA_ADVANCED_INSTALL_LIBRARY_FOR_CMAKE(${LIBRARY_NAME})
+IF (WIN32)
+ SET(INPUT_DATA_DIR ${PROJECT_SOURCE_DIR}/src2/data)
+ SET(OUTPUT_DATA_DIR ${PROJECT_BINARY_DIR}/bin/Shared/gimmick)
+ELSE (WIN32)
+ SET(INPUT_DATA_DIR ${PROJECT_SOURCE_DIR}/src2/data)
+ SET(OUTPUT_DATA_DIR ${PROJECT_BINARY_DIR}/share/gimmick)
+ENDIF (WIN32)
+CREA_CPDIR(${INPUT_DATA_DIR} ${OUTPUT_DATA_DIR})
+
+
+#CREA_INSTALL_LIBRARY_FOR_CMAKE(${LIBRARY_NAME})
+#-----------------------------------------------------------------------------
+
+OPTION( BUILD_DOXYGEN_DOC "Build doxygen doc ?" OFF)
+IF(BUILD_DOXYGEN_DOC)
+ SUBDIRS(doxygen)
+ENDIF(BUILD_DOXYGEN_DOC)
+
+INCLUDE_DIRECTORIES(
+# ${PROJECT_BINARY_DIR}
+ ${PROJECT_SOURCE_DIR}/src2
+# ${PROJECT_SOURCE_DIR}/src2/CppSQLite3
+ )
--- /dev/null
+////////////////////////////////////////////////////////////////////////////////
+
+// CppSQLite3 - A C++ wrapper around the SQLite3 embedded database library.
+
+//
+
+// Copyright (c) 2004 Rob Groves. All Rights Reserved. rob.groves@btinternet.com
+
+//
+
+// Permission to use, copy, modify, and distribute this software and its
+
+// documentation for any purpose, without fee, and without a written
+
+// agreement, is hereby granted, provided that the above copyright notice,
+
+// this paragraph and the following two paragraphs appear in all copies,
+
+// modifications, and distributions.
+
+//
+
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
+
+// INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
+
+// PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+
+// EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//
+
+// THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+
+// PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF
+
+// ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". THE AUTHOR HAS NO OBLIGATION
+
+// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+//
+
+// V3.0 03/08/2004 -Initial Version for sqlite3
+
+//
+
+// V3.1 16/09/2004 -Implemented getXXXXField using sqlite3 functions
+
+// -Added CppSQLiteDB3::tableExists()
+
+////////////////////////////////////////////////////////////////////////////////
+
+#include "CppSQLite3.h"
+
+#include <cstdlib>
+
+
+
+
+
+// Named constant for passing to CppSQLite3Exception when passing it a string
+
+// that cannot be deleted.
+
+static const bool DONT_DELETE_MSG=false;
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Prototypes for SQLite functions not included in SQLite DLL, but copied below
+
+// from SQLite encode.c
+
+////////////////////////////////////////////////////////////////////////////////
+
+int sqlite3_encode_binary(const unsigned char *in, int n, unsigned char *out);
+
+int sqlite3_decode_binary(const unsigned char *in, unsigned char *out);
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+
+CppSQLite3Exception::CppSQLite3Exception(const int nErrCode,
+
+ const char* szErrMess,
+
+ bool bDeleteMsg/*=true*/) :
+
+ mnErrCode(nErrCode)
+
+{
+
+ mpszErrMess = sqlite3_mprintf("%s[%d]: %s",
+
+ errorCodeAsString(nErrCode),
+
+ nErrCode,
+
+ szErrMess ? szErrMess : "");
+
+ /*
+
+ if (bDeleteMsg && szErrMess)
+
+ {
+
+ sqlite3_free(szErrMess);
+
+ }
+
+ */
+
+}
+
+
+
+CppSQLite3Exception::CppSQLite3Exception(const int nErrCode,
+
+ char* szErrMess,
+
+ bool bDeleteMsg/*=true*/) :
+
+ mnErrCode(nErrCode)
+
+{
+
+ mpszErrMess = sqlite3_mprintf("%s[%d]: %s",
+
+ errorCodeAsString(nErrCode),
+
+ nErrCode,
+
+ szErrMess ? szErrMess : "");
+
+
+
+ if (bDeleteMsg && szErrMess)
+
+ {
+
+ sqlite3_free(szErrMess);
+
+ }
+
+}
+
+
+
+CppSQLite3Exception::CppSQLite3Exception(const CppSQLite3Exception& e) :
+
+ mnErrCode(e.mnErrCode)
+
+{
+
+ mpszErrMess = 0;
+
+ if (e.mpszErrMess)
+
+ {
+
+ mpszErrMess = sqlite3_mprintf("%s", e.mpszErrMess);
+
+ }
+
+}
+
+
+
+
+
+const char* CppSQLite3Exception::errorCodeAsString(int nErrCode)
+
+{
+
+ switch (nErrCode)
+
+ {
+
+ case SQLITE_OK : return "SQLITE_OK";
+
+ case SQLITE_ERROR : return "SQLITE_ERROR";
+
+ case SQLITE_INTERNAL : return "SQLITE_INTERNAL";
+
+ case SQLITE_PERM : return "SQLITE_PERM";
+
+ case SQLITE_ABORT : return "SQLITE_ABORT";
+
+ case SQLITE_BUSY : return "SQLITE_BUSY";
+
+ case SQLITE_LOCKED : return "SQLITE_LOCKED";
+
+ case SQLITE_NOMEM : return "SQLITE_NOMEM";
+
+ case SQLITE_READONLY : return "SQLITE_READONLY";
+
+ case SQLITE_INTERRUPT : return "SQLITE_INTERRUPT";
+
+ case SQLITE_IOERR : return "SQLITE_IOERR";
+
+ case SQLITE_CORRUPT : return "SQLITE_CORRUPT";
+
+ case SQLITE_NOTFOUND : return "SQLITE_NOTFOUND";
+
+ case SQLITE_FULL : return "SQLITE_FULL";
+
+ case SQLITE_CANTOPEN : return "SQLITE_CANTOPEN";
+
+ case SQLITE_PROTOCOL : return "SQLITE_PROTOCOL";
+
+ case SQLITE_EMPTY : return "SQLITE_EMPTY";
+
+ case SQLITE_SCHEMA : return "SQLITE_SCHEMA";
+
+ case SQLITE_TOOBIG : return "SQLITE_TOOBIG";
+
+ case SQLITE_CONSTRAINT : return "SQLITE_CONSTRAINT";
+
+ case SQLITE_MISMATCH : return "SQLITE_MISMATCH";
+
+ case SQLITE_MISUSE : return "SQLITE_MISUSE";
+
+ case SQLITE_NOLFS : return "SQLITE_NOLFS";
+
+ case SQLITE_AUTH : return "SQLITE_AUTH";
+
+ case SQLITE_FORMAT : return "SQLITE_FORMAT";
+
+ case SQLITE_RANGE : return "SQLITE_RANGE";
+
+ case SQLITE_ROW : return "SQLITE_ROW";
+
+ case SQLITE_DONE : return "SQLITE_DONE";
+
+ case CPPSQLITE_ERROR : return "CPPSQLITE_ERROR";
+
+ default: return "UNKNOWN_ERROR";
+
+ }
+
+}
+
+
+
+
+
+CppSQLite3Exception::~CppSQLite3Exception()
+
+{
+
+ if (mpszErrMess)
+
+ {
+
+ sqlite3_free(mpszErrMess);
+
+ mpszErrMess = 0;
+
+ }
+
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+
+CppSQLite3Buffer::CppSQLite3Buffer()
+
+{
+
+ mpBuf = 0;
+
+}
+
+
+
+
+
+CppSQLite3Buffer::~CppSQLite3Buffer()
+
+{
+
+ clear();
+
+}
+
+
+
+
+
+void CppSQLite3Buffer::clear()
+
+{
+
+ if (mpBuf)
+
+ {
+
+ sqlite3_free(mpBuf);
+
+ mpBuf = 0;
+
+ }
+
+
+
+}
+
+
+
+
+
+const char* CppSQLite3Buffer::format(const char* szFormat, ...)
+
+{
+
+ clear();
+
+ va_list va;
+
+ va_start(va, szFormat);
+
+ mpBuf = sqlite3_vmprintf(szFormat, va);
+
+ va_end(va);
+
+ return mpBuf;
+
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+
+CppSQLite3Binary::CppSQLite3Binary() :
+
+ mpBuf(0),
+
+ mnBinaryLen(0),
+
+ mnBufferLen(0),
+
+ mnEncodedLen(0),
+
+ mbEncoded(false)
+
+{
+
+}
+
+
+
+
+
+CppSQLite3Binary::~CppSQLite3Binary()
+
+{
+
+ clear();
+
+}
+
+
+
+
+
+void CppSQLite3Binary::setBinary(const unsigned char* pBuf, int nLen)
+
+{
+
+ mpBuf = allocBuffer(nLen);
+
+ memcpy(mpBuf, pBuf, nLen);
+
+}
+
+
+
+
+
+void CppSQLite3Binary::setEncoded(const unsigned char* pBuf)
+
+{
+
+ clear();
+
+
+
+ mnEncodedLen = strlen((const char*)pBuf);
+
+ mnBufferLen = mnEncodedLen + 1; // Allow for NULL terminator
+
+
+
+ mpBuf = (unsigned char*)malloc(mnBufferLen);
+
+
+
+ if (!mpBuf)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Cannot allocate memory",
+
+ DONT_DELETE_MSG);
+
+ }
+
+
+
+ memcpy(mpBuf, pBuf, mnBufferLen);
+
+ mbEncoded = true;
+
+}
+
+
+
+
+
+const unsigned char* CppSQLite3Binary::getEncoded()
+
+{
+
+ if (!mbEncoded)
+
+ {
+
+ unsigned char* ptmp = (unsigned char*)malloc(mnBinaryLen);
+
+ memcpy(ptmp, mpBuf, mnBinaryLen);
+
+ mnEncodedLen = sqlite3_encode_binary(ptmp, mnBinaryLen, mpBuf);
+
+ free(ptmp);
+
+ mbEncoded = true;
+
+ }
+
+
+
+ return mpBuf;
+
+}
+
+
+
+
+
+const unsigned char* CppSQLite3Binary::getBinary()
+
+{
+
+ if (mbEncoded)
+
+ {
+
+ // in/out buffers can be the same
+
+ mnBinaryLen = sqlite3_decode_binary(mpBuf, mpBuf);
+
+
+
+ if (mnBinaryLen == -1)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Cannot decode binary",
+
+ DONT_DELETE_MSG);
+
+ }
+
+
+
+ mbEncoded = false;
+
+ }
+
+
+
+ return mpBuf;
+
+}
+
+
+
+
+
+int CppSQLite3Binary::getBinaryLength()
+
+{
+
+ getBinary();
+
+ return mnBinaryLen;
+
+}
+
+
+
+
+
+unsigned char* CppSQLite3Binary::allocBuffer(int nLen)
+
+{
+
+ clear();
+
+
+
+ // Allow extra space for encoded binary as per comments in
+
+ // SQLite encode.c See bottom of this file for implementation
+
+ // of SQLite functions use 3 instead of 2 just to be sure ;-)
+
+ mnBinaryLen = nLen;
+
+ mnBufferLen = 3 + (257*nLen)/254;
+
+
+
+ mpBuf = (unsigned char*)malloc(mnBufferLen);
+
+
+
+ if (!mpBuf)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Cannot allocate memory",
+
+ DONT_DELETE_MSG);
+
+ }
+
+
+
+ mbEncoded = false;
+
+
+
+ return mpBuf;
+
+}
+
+
+
+
+
+void CppSQLite3Binary::clear()
+
+{
+
+ if (mpBuf)
+
+ {
+
+ mnBinaryLen = 0;
+
+ mnBufferLen = 0;
+
+ free(mpBuf);
+
+ mpBuf = 0;
+
+ }
+
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+
+CppSQLite3Query::CppSQLite3Query()
+
+{
+
+ mpVM = 0;
+
+ mbEof = true;
+
+ mnCols = 0;
+
+ mbOwnVM = false;
+
+}
+
+
+
+
+
+CppSQLite3Query::CppSQLite3Query(const CppSQLite3Query& rQuery)
+
+{
+
+ mpVM = rQuery.mpVM;
+
+ // Only one object can own the VM
+
+ const_cast<CppSQLite3Query&>(rQuery).mpVM = 0;
+
+ mbEof = rQuery.mbEof;
+
+ mnCols = rQuery.mnCols;
+
+ mbOwnVM = rQuery.mbOwnVM;
+
+}
+
+
+
+
+
+CppSQLite3Query::CppSQLite3Query(sqlite3* pDB,
+
+ sqlite3_stmt* pVM,
+
+ bool bEof,
+
+ bool bOwnVM/*=true*/)
+
+{
+
+ mpDB = pDB;
+
+ mpVM = pVM;
+
+ mbEof = bEof;
+
+ mnCols = sqlite3_column_count(mpVM);
+
+ mbOwnVM = bOwnVM;
+
+}
+
+
+
+
+
+CppSQLite3Query::~CppSQLite3Query()
+
+{
+
+ try
+
+ {
+
+ finalize();
+
+ }
+
+ catch (...)
+
+ {
+
+ }
+
+}
+
+
+
+
+
+CppSQLite3Query& CppSQLite3Query::operator=(const CppSQLite3Query& rQuery)
+
+{
+
+ try
+
+ {
+
+ finalize();
+
+ }
+
+ catch (...)
+
+ {
+
+ }
+
+ mpVM = rQuery.mpVM;
+
+ // Only one object can own the VM
+
+ const_cast<CppSQLite3Query&>(rQuery).mpVM = 0;
+
+ mbEof = rQuery.mbEof;
+
+ mnCols = rQuery.mnCols;
+
+ mbOwnVM = rQuery.mbOwnVM;
+
+ return *this;
+
+}
+
+
+
+
+
+int CppSQLite3Query::numFields()
+
+{
+
+ checkVM();
+
+ return mnCols;
+
+}
+
+
+
+
+
+const char* CppSQLite3Query::fieldValue(int nField)
+
+{
+
+ checkVM();
+
+
+
+ if (nField < 0 || nField > mnCols-1)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Invalid field index requested",
+
+ DONT_DELETE_MSG);
+
+ }
+
+
+
+ return (const char*)sqlite3_column_text(mpVM, nField);
+
+}
+
+
+
+
+
+const char* CppSQLite3Query::fieldValue(const char* szField)
+
+{
+
+ int nField = fieldIndex(szField);
+
+ return (const char*)sqlite3_column_text(mpVM, nField);
+
+}
+
+
+
+
+
+int CppSQLite3Query::getIntField(int nField, int nNullValue/*=0*/)
+
+{
+
+ if (fieldDataType(nField) == SQLITE_NULL)
+
+ {
+
+ return nNullValue;
+
+ }
+
+ else
+
+ {
+
+ return sqlite3_column_int(mpVM, nField);
+
+ }
+
+}
+
+
+
+
+
+int CppSQLite3Query::getIntField(const char* szField, int nNullValue/*=0*/)
+
+{
+
+ int nField = fieldIndex(szField);
+
+ return getIntField(nField, nNullValue);
+
+}
+
+
+
+
+
+double CppSQLite3Query::getFloatField(int nField, double fNullValue/*=0.0*/)
+
+{
+
+ if (fieldDataType(nField) == SQLITE_NULL)
+
+ {
+
+ return fNullValue;
+
+ }
+
+ else
+
+ {
+
+ return sqlite3_column_double(mpVM, nField);
+
+ }
+
+}
+
+
+
+
+
+double CppSQLite3Query::getFloatField(const char* szField, double fNullValue/*=0.0*/)
+
+{
+
+ int nField = fieldIndex(szField);
+
+ return getFloatField(nField, fNullValue);
+
+}
+
+
+
+
+
+const char* CppSQLite3Query::getStringField(int nField, const char* szNullValue/*=""*/)
+
+{
+
+ if (fieldDataType(nField) == SQLITE_NULL)
+
+ {
+
+ return szNullValue;
+
+ }
+
+ else
+
+ {
+
+ return (const char*)sqlite3_column_text(mpVM, nField);
+
+ }
+
+}
+
+
+
+
+
+const char* CppSQLite3Query::getStringField(const char* szField, const char* szNullValue/*=""*/)
+
+{
+
+ int nField = fieldIndex(szField);
+
+ return getStringField(nField, szNullValue);
+
+}
+
+
+
+
+
+const unsigned char* CppSQLite3Query::getBlobField(int nField, int& nLen)
+
+{
+
+ checkVM();
+
+
+
+ if (nField < 0 || nField > mnCols-1)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Invalid field index requested",
+
+ DONT_DELETE_MSG);
+
+ }
+
+
+
+ nLen = sqlite3_column_bytes(mpVM, nField);
+
+ return (const unsigned char*)sqlite3_column_blob(mpVM, nField);
+
+}
+
+
+
+
+
+const unsigned char* CppSQLite3Query::getBlobField(const char* szField, int& nLen)
+
+{
+
+ int nField = fieldIndex(szField);
+
+ return getBlobField(nField, nLen);
+
+}
+
+
+
+
+
+bool CppSQLite3Query::fieldIsNull(int nField)
+
+{
+
+ return (fieldDataType(nField) == SQLITE_NULL);
+
+}
+
+
+
+
+
+bool CppSQLite3Query::fieldIsNull(const char* szField)
+
+{
+
+ int nField = fieldIndex(szField);
+
+ return (fieldDataType(nField) == SQLITE_NULL);
+
+}
+
+
+
+
+
+int CppSQLite3Query::fieldIndex(const char* szField)
+
+{
+
+ checkVM();
+
+
+
+ if (szField)
+
+ {
+
+ for (int nField = 0; nField < mnCols; nField++)
+
+ {
+
+ const char* szTemp = sqlite3_column_name(mpVM, nField);
+
+
+
+ if (strcmp(szField, szTemp) == 0)
+
+ {
+
+ return nField;
+
+ }
+
+ }
+
+ }
+
+
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Invalid field name requested",
+
+ DONT_DELETE_MSG);
+
+}
+
+
+
+
+
+const char* CppSQLite3Query::fieldName(int nCol)
+
+{
+
+ checkVM();
+
+
+
+ if (nCol < 0 || nCol > mnCols-1)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Invalid field index requested",
+
+ DONT_DELETE_MSG);
+
+ }
+
+
+
+ return sqlite3_column_name(mpVM, nCol);
+
+}
+
+
+
+
+
+const char* CppSQLite3Query::fieldDeclType(int nCol)
+
+{
+
+ checkVM();
+
+
+
+ if (nCol < 0 || nCol > mnCols-1)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Invalid field index requested",
+
+ DONT_DELETE_MSG);
+
+ }
+
+
+
+ return sqlite3_column_decltype(mpVM, nCol);
+
+}
+
+
+
+
+
+int CppSQLite3Query::fieldDataType(int nCol)
+
+{
+
+ checkVM();
+
+
+
+ if (nCol < 0 || nCol > mnCols-1)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Invalid field index requested",
+
+ DONT_DELETE_MSG);
+
+ }
+
+
+
+ return sqlite3_column_type(mpVM, nCol);
+
+}
+
+
+
+
+
+bool CppSQLite3Query::eof()
+
+{
+
+ checkVM();
+
+ return mbEof;
+
+}
+
+
+
+
+
+void CppSQLite3Query::nextRow()
+
+{
+
+ checkVM();
+
+
+
+ int nRet = sqlite3_step(mpVM);
+
+
+
+ if (nRet == SQLITE_DONE)
+
+ {
+
+ // no rows
+
+ mbEof = true;
+
+ }
+
+ else if (nRet == SQLITE_ROW)
+
+ {
+
+ // more rows, nothing to do
+
+ }
+
+ else
+
+ {
+
+ nRet = sqlite3_finalize(mpVM);
+
+ mpVM = 0;
+
+ const char* szError = sqlite3_errmsg(mpDB);
+
+ throw CppSQLite3Exception(nRet,
+
+ (char*)szError,
+
+ DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+void CppSQLite3Query::finalize()
+
+{
+
+ if (mpVM && mbOwnVM)
+
+ {
+
+ int nRet = sqlite3_finalize(mpVM);
+
+ mpVM = 0;
+
+ if (nRet != SQLITE_OK)
+
+ {
+
+ const char* szError = sqlite3_errmsg(mpDB);
+
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);
+
+ }
+
+ }
+
+}
+
+
+
+
+
+void CppSQLite3Query::checkVM()
+
+{
+
+ if (mpVM == 0)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Null Virtual Machine pointer",
+
+ DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+
+CppSQLite3Table::CppSQLite3Table()
+
+{
+
+ mpaszResults = 0;
+
+ mnRows = 0;
+
+ mnCols = 0;
+
+ mnCurrentRow = 0;
+
+}
+
+
+
+
+
+CppSQLite3Table::CppSQLite3Table(const CppSQLite3Table& rTable)
+
+{
+
+ mpaszResults = rTable.mpaszResults;
+
+ // Only one object can own the results
+
+ const_cast<CppSQLite3Table&>(rTable).mpaszResults = 0;
+
+ mnRows = rTable.mnRows;
+
+ mnCols = rTable.mnCols;
+
+ mnCurrentRow = rTable.mnCurrentRow;
+
+}
+
+
+
+
+
+CppSQLite3Table::CppSQLite3Table(char** paszResults, int nRows, int nCols)
+
+{
+
+ mpaszResults = paszResults;
+
+ mnRows = nRows;
+
+ mnCols = nCols;
+
+ mnCurrentRow = 0;
+
+}
+
+
+
+
+
+CppSQLite3Table::~CppSQLite3Table()
+
+{
+
+ try
+
+ {
+
+ finalize();
+
+ }
+
+ catch (...)
+
+ {
+
+ }
+
+}
+
+
+
+
+
+CppSQLite3Table& CppSQLite3Table::operator=(const CppSQLite3Table& rTable)
+
+{
+
+ try
+
+ {
+
+ finalize();
+
+ }
+
+ catch (...)
+
+ {
+
+ }
+
+ mpaszResults = rTable.mpaszResults;
+
+ // Only one object can own the results
+
+ const_cast<CppSQLite3Table&>(rTable).mpaszResults = 0;
+
+ mnRows = rTable.mnRows;
+
+ mnCols = rTable.mnCols;
+
+ mnCurrentRow = rTable.mnCurrentRow;
+
+ return *this;
+
+}
+
+
+
+
+
+void CppSQLite3Table::finalize()
+
+{
+
+ if (mpaszResults)
+
+ {
+
+ sqlite3_free_table(mpaszResults);
+
+ mpaszResults = 0;
+
+ }
+
+}
+
+
+
+
+
+int CppSQLite3Table::numFields()
+
+{
+
+ checkResults();
+
+ return mnCols;
+
+}
+
+
+
+
+
+int CppSQLite3Table::numRows()
+
+{
+
+ checkResults();
+
+ return mnRows;
+
+}
+
+
+
+
+
+const char* CppSQLite3Table::fieldValue(int nField)
+
+{
+
+ checkResults();
+
+
+
+ if (nField < 0 || nField > mnCols-1)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Invalid field index requested",
+
+ DONT_DELETE_MSG);
+
+ }
+
+
+
+ int nIndex = (mnCurrentRow*mnCols) + mnCols + nField;
+
+ return mpaszResults[nIndex];
+
+}
+
+
+
+
+
+const char* CppSQLite3Table::fieldValue(const char* szField)
+
+{
+
+ checkResults();
+
+
+
+ if (szField)
+
+ {
+
+ for (int nField = 0; nField < mnCols; nField++)
+
+ {
+
+ if (strcmp(szField, mpaszResults[nField]) == 0)
+
+ {
+
+ int nIndex = (mnCurrentRow*mnCols) + mnCols + nField;
+
+ return mpaszResults[nIndex];
+
+ }
+
+ }
+
+ }
+
+
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Invalid field name requested",
+
+ DONT_DELETE_MSG);
+
+}
+
+
+
+
+
+int CppSQLite3Table::getIntField(int nField, int nNullValue/*=0*/)
+
+{
+
+ if (fieldIsNull(nField))
+
+ {
+
+ return nNullValue;
+
+ }
+
+ else
+
+ {
+
+ return atoi(fieldValue(nField));
+
+ }
+
+}
+
+
+
+
+
+int CppSQLite3Table::getIntField(const char* szField, int nNullValue/*=0*/)
+
+{
+
+ if (fieldIsNull(szField))
+
+ {
+
+ return nNullValue;
+
+ }
+
+ else
+
+ {
+
+ return atoi(fieldValue(szField));
+
+ }
+
+}
+
+
+
+
+
+double CppSQLite3Table::getFloatField(int nField, double fNullValue/*=0.0*/)
+
+{
+
+ if (fieldIsNull(nField))
+
+ {
+
+ return fNullValue;
+
+ }
+
+ else
+
+ {
+
+ return atof(fieldValue(nField));
+
+ }
+
+}
+
+
+
+
+
+double CppSQLite3Table::getFloatField(const char* szField, double fNullValue/*=0.0*/)
+
+{
+
+ if (fieldIsNull(szField))
+
+ {
+
+ return fNullValue;
+
+ }
+
+ else
+
+ {
+
+ return atof(fieldValue(szField));
+
+ }
+
+}
+
+
+
+
+
+const char* CppSQLite3Table::getStringField(int nField, const char* szNullValue/*=""*/)
+
+{
+
+ if (fieldIsNull(nField))
+
+ {
+
+ return szNullValue;
+
+ }
+
+ else
+
+ {
+
+ return fieldValue(nField);
+
+ }
+
+}
+
+
+
+
+
+const char* CppSQLite3Table::getStringField(const char* szField, const char* szNullValue/*=""*/)
+
+{
+
+ if (fieldIsNull(szField))
+
+ {
+
+ return szNullValue;
+
+ }
+
+ else
+
+ {
+
+ return fieldValue(szField);
+
+ }
+
+}
+
+
+
+
+
+bool CppSQLite3Table::fieldIsNull(int nField)
+
+{
+
+ checkResults();
+
+ return (fieldValue(nField) == 0);
+
+}
+
+
+
+
+
+bool CppSQLite3Table::fieldIsNull(const char* szField)
+
+{
+
+ checkResults();
+
+ return (fieldValue(szField) == 0);
+
+}
+
+
+
+
+
+const char* CppSQLite3Table::fieldName(int nCol)
+
+{
+
+ checkResults();
+
+
+
+ if (nCol < 0 || nCol > mnCols-1)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Invalid field index requested",
+
+ DONT_DELETE_MSG);
+
+ }
+
+
+
+ return mpaszResults[nCol];
+
+}
+
+
+
+
+
+void CppSQLite3Table::setRow(int nRow)
+
+{
+
+ checkResults();
+
+
+
+ if (nRow < 0 || nRow > mnRows-1)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Invalid row index requested",
+
+ DONT_DELETE_MSG);
+
+ }
+
+
+
+ mnCurrentRow = nRow;
+
+}
+
+
+
+
+
+void CppSQLite3Table::checkResults()
+
+{
+
+ if (mpaszResults == 0)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Null Results pointer",
+
+ DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+
+CppSQLite3Statement::CppSQLite3Statement()
+
+{
+
+ mpDB = 0;
+
+ mpVM = 0;
+
+}
+
+
+
+
+
+CppSQLite3Statement::CppSQLite3Statement(const CppSQLite3Statement& rStatement)
+
+{
+
+ mpDB = rStatement.mpDB;
+
+ mpVM = rStatement.mpVM;
+
+ // Only one object can own VM
+
+ const_cast<CppSQLite3Statement&>(rStatement).mpVM = 0;
+
+}
+
+
+
+
+
+CppSQLite3Statement::CppSQLite3Statement(sqlite3* pDB, sqlite3_stmt* pVM)
+
+{
+
+ mpDB = pDB;
+
+ mpVM = pVM;
+
+}
+
+
+
+
+
+CppSQLite3Statement::~CppSQLite3Statement()
+
+{
+
+ try
+
+ {
+
+ finalize();
+
+ }
+
+ catch (...)
+
+ {
+
+ }
+
+}
+
+
+
+
+
+CppSQLite3Statement& CppSQLite3Statement::operator=(const CppSQLite3Statement& rStatement)
+
+{
+
+ mpDB = rStatement.mpDB;
+
+ mpVM = rStatement.mpVM;
+
+ // Only one object can own VM
+
+ const_cast<CppSQLite3Statement&>(rStatement).mpVM = 0;
+
+ return *this;
+
+}
+
+
+
+
+
+int CppSQLite3Statement::execDML()
+
+{
+
+ checkDB();
+
+ checkVM();
+
+
+
+ const char* szError=0;
+
+
+
+ int nRet = sqlite3_step(mpVM);
+
+
+
+ if (nRet == SQLITE_DONE)
+
+ {
+
+ int nRowsChanged = sqlite3_changes(mpDB);
+
+
+
+ nRet = sqlite3_reset(mpVM);
+
+
+
+ if (nRet != SQLITE_OK)
+
+ {
+
+ szError = sqlite3_errmsg(mpDB);
+
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);
+
+ }
+
+
+
+ return nRowsChanged;
+
+ }
+
+ else
+
+ {
+
+ nRet = sqlite3_reset(mpVM);
+
+ szError = sqlite3_errmsg(mpDB);
+
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+CppSQLite3Query CppSQLite3Statement::execQuery()
+
+{
+
+ checkDB();
+
+ checkVM();
+
+
+
+ int nRet = sqlite3_step(mpVM);
+
+
+
+ if (nRet == SQLITE_DONE)
+
+ {
+
+ // no rows
+
+ return CppSQLite3Query(mpDB, mpVM, true/*eof*/, false);
+
+ }
+
+ else if (nRet == SQLITE_ROW)
+
+ {
+
+ // at least 1 row
+
+ return CppSQLite3Query(mpDB, mpVM, false/*eof*/, false);
+
+ }
+
+ else
+
+ {
+
+ nRet = sqlite3_reset(mpVM);
+
+ const char* szError = sqlite3_errmsg(mpDB);
+
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+void CppSQLite3Statement::bind(int nParam, const char* szValue)
+
+{
+
+ checkVM();
+
+ int nRes = sqlite3_bind_text(mpVM, nParam, szValue, -1, SQLITE_TRANSIENT);
+
+
+
+ if (nRes != SQLITE_OK)
+
+ {
+
+ throw CppSQLite3Exception(nRes,
+
+ "Error binding string param",
+
+ DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+void CppSQLite3Statement::bind(int nParam, const int nValue)
+
+{
+
+ checkVM();
+
+ int nRes = sqlite3_bind_int(mpVM, nParam, nValue);
+
+
+
+ if (nRes != SQLITE_OK)
+
+ {
+
+ throw CppSQLite3Exception(nRes,
+
+ "Error binding int param",
+
+ DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+void CppSQLite3Statement::bind(int nParam, const double dValue)
+
+{
+
+ checkVM();
+
+ int nRes = sqlite3_bind_double(mpVM, nParam, dValue);
+
+
+
+ if (nRes != SQLITE_OK)
+
+ {
+
+ throw CppSQLite3Exception(nRes,
+
+ "Error binding double param",
+
+ DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+void CppSQLite3Statement::bind(int nParam, const unsigned char* blobValue, int nLen)
+
+{
+
+ checkVM();
+
+ int nRes = sqlite3_bind_blob(mpVM, nParam,
+
+ (const void*)blobValue, nLen, SQLITE_TRANSIENT);
+
+
+
+ if (nRes != SQLITE_OK)
+
+ {
+
+ throw CppSQLite3Exception(nRes,
+
+ "Error binding blob param",
+
+ DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+void CppSQLite3Statement::bindNull(int nParam)
+
+{
+
+ checkVM();
+
+ int nRes = sqlite3_bind_null(mpVM, nParam);
+
+
+
+ if (nRes != SQLITE_OK)
+
+ {
+
+ throw CppSQLite3Exception(nRes,
+
+ "Error binding NULL param",
+
+ DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+void CppSQLite3Statement::reset()
+
+{
+
+ if (mpVM)
+
+ {
+
+ int nRet = sqlite3_reset(mpVM);
+
+
+
+ if (nRet != SQLITE_OK)
+
+ {
+
+ const char* szError = sqlite3_errmsg(mpDB);
+
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);
+
+ }
+
+ }
+
+}
+
+
+
+
+
+void CppSQLite3Statement::finalize()
+
+{
+
+ if (mpVM)
+
+ {
+
+ int nRet = sqlite3_finalize(mpVM);
+
+ mpVM = 0;
+
+
+
+ if (nRet != SQLITE_OK)
+
+ {
+
+ const char* szError = sqlite3_errmsg(mpDB);
+
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);
+
+ }
+
+ }
+
+}
+
+
+
+
+
+void CppSQLite3Statement::checkDB()
+
+{
+
+ if (mpDB == 0)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Database not open",
+
+ DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+void CppSQLite3Statement::checkVM()
+
+{
+
+ if (mpVM == 0)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Null Virtual Machine pointer",
+
+ DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+
+CppSQLite3DB::CppSQLite3DB()
+
+{
+
+ mpDB = 0;
+
+ mnBusyTimeoutMs = 60000; // 60 seconds
+
+}
+
+
+
+
+
+CppSQLite3DB::CppSQLite3DB(const CppSQLite3DB& db)
+
+{
+
+ mpDB = db.mpDB;
+
+ mnBusyTimeoutMs = 60000; // 60 seconds
+
+}
+
+
+
+
+
+CppSQLite3DB::~CppSQLite3DB()
+
+{
+
+ close();
+
+}
+
+
+
+
+
+CppSQLite3DB& CppSQLite3DB::operator=(const CppSQLite3DB& db)
+
+{
+
+ mpDB = db.mpDB;
+
+ mnBusyTimeoutMs = 60000; // 60 seconds
+
+ return *this;
+
+}
+
+
+
+
+
+void CppSQLite3DB::open(const char* szFile)
+
+{
+
+ int nRet = sqlite3_open(szFile, &mpDB);
+
+
+
+ if (nRet != SQLITE_OK)
+
+ {
+
+ const char* szError = sqlite3_errmsg(mpDB);
+
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);
+
+ }
+
+
+
+ setBusyTimeout(mnBusyTimeoutMs);
+
+}
+
+
+
+
+
+void CppSQLite3DB::close()
+
+{
+
+ if (mpDB)
+
+ {
+
+ sqlite3_close(mpDB);
+
+ mpDB = 0;
+
+ }
+
+}
+
+
+
+
+
+CppSQLite3Statement CppSQLite3DB::compileStatement(const char* szSQL)
+
+{
+
+ checkDB();
+
+
+
+ sqlite3_stmt* pVM = compile(szSQL);
+
+ return CppSQLite3Statement(mpDB, pVM);
+
+}
+
+
+
+
+
+bool CppSQLite3DB::tableExists(const char* szTable)
+
+{
+
+ char szSQL[128];
+
+ sprintf(szSQL,
+
+ "select count(*) from sqlite_master where type='table' and name='%s'",
+
+ szTable);
+
+ int nRet = execScalar(szSQL);
+
+ return (nRet > 0);
+
+}
+
+
+
+
+
+int CppSQLite3DB::execDML(const char* szSQL)
+
+{
+
+ checkDB();
+
+
+
+ char* szError=0;
+
+
+
+ int nRet = sqlite3_exec(mpDB, szSQL, 0, 0, &szError);
+
+
+
+ if (nRet == SQLITE_OK)
+
+ {
+
+ return sqlite3_changes(mpDB);
+
+ }
+
+ else
+
+ {
+
+ throw CppSQLite3Exception(nRet, szError);
+
+ }
+
+}
+
+
+
+
+
+CppSQLite3Query CppSQLite3DB::execQuery(const char* szSQL)
+
+{
+
+ checkDB();
+
+
+
+ sqlite3_stmt* pVM = compile(szSQL);
+
+
+
+ int nRet = sqlite3_step(pVM);
+
+
+
+ if (nRet == SQLITE_DONE)
+
+ {
+
+ // no rows
+
+ return CppSQLite3Query(mpDB, pVM, true/*eof*/);
+
+ }
+
+ else if (nRet == SQLITE_ROW)
+
+ {
+
+ // at least 1 row
+
+ return CppSQLite3Query(mpDB, pVM, false/*eof*/);
+
+ }
+
+ else
+
+ {
+
+ nRet = sqlite3_finalize(pVM);
+
+ const char* szError= sqlite3_errmsg(mpDB);
+
+ throw CppSQLite3Exception(nRet, (char*)szError, DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+int CppSQLite3DB::execScalar(const char* szSQL)
+
+{
+
+ CppSQLite3Query q = execQuery(szSQL);
+
+
+
+ if (q.eof() || q.numFields() < 1)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Invalid scalar query",
+
+ DONT_DELETE_MSG);
+
+ }
+
+
+
+ return atoi(q.fieldValue(0));
+
+}
+
+
+
+
+
+CppSQLite3Table CppSQLite3DB::getTable(const char* szSQL)
+
+{
+
+ checkDB();
+
+
+
+ char* szError=0;
+
+ char** paszResults=0;
+
+ int nRet;
+
+ int nRows(0);
+
+ int nCols(0);
+
+
+
+ nRet = sqlite3_get_table(mpDB, szSQL, &paszResults, &nRows, &nCols, &szError);
+
+
+
+ if (nRet == SQLITE_OK)
+
+ {
+
+ return CppSQLite3Table(paszResults, nRows, nCols);
+
+ }
+
+ else
+
+ {
+
+ throw CppSQLite3Exception(nRet, szError);
+
+ }
+
+}
+
+
+
+
+
+sqlite_int64 CppSQLite3DB::lastRowId()
+
+{
+
+ return sqlite3_last_insert_rowid(mpDB);
+
+}
+
+
+
+
+
+void CppSQLite3DB::setBusyTimeout(int nMillisecs)
+
+{
+
+ mnBusyTimeoutMs = nMillisecs;
+
+ sqlite3_busy_timeout(mpDB, mnBusyTimeoutMs);
+
+}
+
+
+
+
+
+void CppSQLite3DB::checkDB()
+
+{
+
+ if (!mpDB)
+
+ {
+
+ throw CppSQLite3Exception(CPPSQLITE_ERROR,
+
+ "Database not open",
+
+ DONT_DELETE_MSG);
+
+ }
+
+}
+
+
+
+
+
+sqlite3_stmt* CppSQLite3DB::compile(const char* szSQL)
+
+{
+
+ checkDB();
+
+
+
+ char* szError=0;
+
+ const char* szTail=0;
+
+ sqlite3_stmt* pVM;
+
+
+
+ int nRet = sqlite3_prepare(mpDB, szSQL, -1, &pVM, &szTail);
+
+
+
+ if (nRet != SQLITE_OK)
+
+ {
+
+ throw CppSQLite3Exception(nRet, szError);
+
+ }
+
+
+
+ return pVM;
+
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+// SQLite encode.c reproduced here, containing implementation notes and source
+
+// for sqlite3_encode_binary() and sqlite3_decode_binary()
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+
+/*
+
+** 2002 April 25
+
+**
+
+** The author disclaims copyright to this source code. In place of
+
+** a legal notice, here is a blessing:
+
+**
+
+** May you do good and not evil.
+
+** May you find forgiveness for yourself and forgive others.
+
+** May you share freely, never taking more than you give.
+
+**
+
+*************************************************************************
+
+** This file contains helper routines used to translate binary data into
+
+** a null-terminated string (suitable for use in SQLite) and back again.
+
+** These are convenience routines for use by people who want to store binary
+
+** data in an SQLite database. The code in this file is not used by any other
+
+** part of the SQLite library.
+
+**
+
+** $Id: CppSQLite3.cpp,v 1.4 2010/03/16 15:46:13 cervenansky Exp $
+
+*/
+
+
+
+/*
+
+** How This Encoder Works
+
+**
+
+** The output is allowed to contain any character except 0x27 (') and
+
+** 0x00. This is accomplished by using an escape character to encode
+
+** 0x27 and 0x00 as a two-byte sequence. The escape character is always
+
+** 0x01. An 0x00 is encoded as the two byte sequence 0x01 0x01. The
+
+** 0x27 character is encoded as the two byte sequence 0x01 0x03. Finally,
+
+** the escape character itself is encoded as the two-character sequence
+
+** 0x01 0x02.
+
+**
+
+** To summarize, the encoder works by using an escape sequences as follows:
+
+**
+
+** 0x00 -> 0x01 0x01
+
+** 0x01 -> 0x01 0x02
+
+** 0x27 -> 0x01 0x03
+
+**
+
+** If that were all the encoder did, it would work, but in certain cases
+
+** it could double the size of the encoded string. For example, to
+
+** encode a string of 100 0x27 characters would require 100 instances of
+
+** the 0x01 0x03 escape sequence resulting in a 200-character output.
+
+** We would prefer to keep the size of the encoded string smaller than
+
+** this.
+
+**
+
+** To minimize the encoding size, we first add a fixed offset value to each
+
+** byte in the sequence. The addition is modulo 256. (That is to say, if
+
+** the sum of the original character value and the offset exceeds 256, then
+
+** the higher order bits are truncated.) The offset is chosen to minimize
+
+** the number of characters in the string that need to be escaped. For
+
+** example, in the case above where the string was composed of 100 0x27
+
+** characters, the offset might be 0x01. Each of the 0x27 characters would
+
+** then be converted into an 0x28 character which would not need to be
+
+** escaped at all and so the 100 character input string would be converted
+
+** into just 100 characters of output. Actually 101 characters of output -
+
+** we have to record the offset used as the first byte in the sequence so
+
+** that the string can be decoded. Since the offset value is stored as
+
+** part of the output string and the output string is not allowed to contain
+
+** characters 0x00 or 0x27, the offset cannot be 0x00 or 0x27.
+
+**
+
+** Here, then, are the encoding steps:
+
+**
+
+** (1) Choose an offset value and make it the first character of
+
+** output.
+
+**
+
+** (2) Copy each input character into the output buffer, one by
+
+** one, adding the offset value as you copy.
+
+**
+
+** (3) If the value of an input character plus offset is 0x00, replace
+
+** that one character by the two-character sequence 0x01 0x01.
+
+** If the sum is 0x01, replace it with 0x01 0x02. If the sum
+
+** is 0x27, replace it with 0x01 0x03.
+
+**
+
+** (4) Put a 0x00 terminator at the end of the output.
+
+**
+
+** Decoding is obvious:
+
+**
+
+** (5) Copy encoded characters except the first into the decode
+
+** buffer. Set the first encoded character aside for use as
+
+** the offset in step 7 below.
+
+**
+
+** (6) Convert each 0x01 0x01 sequence into a single character 0x00.
+
+** Convert 0x01 0x02 into 0x01. Convert 0x01 0x03 into 0x27.
+
+**
+
+** (7) Subtract the offset value that was the first character of
+
+** the encoded buffer from all characters in the output buffer.
+
+**
+
+** The only tricky part is step (1) - how to compute an offset value to
+
+** minimize the size of the output buffer. This is accomplished by testing
+
+** all offset values and picking the one that results in the fewest number
+
+** of escapes. To do that, we first scan the entire input and count the
+
+** number of occurances of each character value in the input. Suppose
+
+** the number of 0x00 characters is N(0), the number of occurances of 0x01
+
+** is N(1), and so forth up to the number of occurances of 0xff is N(255).
+
+** An offset of 0 is not allowed so we don't have to test it. The number
+
+** of escapes required for an offset of 1 is N(1)+N(2)+N(40). The number
+
+** of escapes required for an offset of 2 is N(2)+N(3)+N(41). And so forth.
+
+** In this way we find the offset that gives the minimum number of escapes,
+
+** and thus minimizes the length of the output string.
+
+*/
+
+
+
+/*
+
+** Encode a binary buffer "in" of size n bytes so that it contains
+
+** no instances of characters '\'' or '\000'. The output is
+
+** null-terminated and can be used as a string value in an INSERT
+
+** or UPDATE statement. Use sqlite3_decode_binary() to convert the
+
+** string back into its original binary.
+
+**
+
+** The result is written into a preallocated output buffer "out".
+
+** "out" must be able to hold at least 2 +(257*n)/254 bytes.
+
+** In other words, the output will be expanded by as much as 3
+
+** bytes for every 254 bytes of input plus 2 bytes of fixed overhead.
+
+** (This is approximately 2 + 1.0118*n or about a 1.2% size increase.)
+
+**
+
+** The return value is the number of characters in the encoded
+
+** string, excluding the "\000" terminator.
+
+*/
+
+int sqlite3_encode_binary(const unsigned char *in, int n, unsigned char *out){
+
+ int i, j, e, m;
+
+ int cnt[256];
+
+ if( n<=0 ){
+
+ out[0] = 'x';
+
+ out[1] = 0;
+
+ return 1;
+
+ }
+
+ memset(cnt, 0, sizeof(cnt));
+
+ for(i=n-1; i>=0; i--){ cnt[in[i]]++; }
+
+ m = n;
+
+ for(i=1; i<256; i++){
+
+ int sum;
+
+ if( i=='\'' ) continue;
+
+ sum = cnt[i] + cnt[(i+1)&0xff] + cnt[(i+'\'')&0xff];
+
+ if( sum<m ){
+
+ m = sum;
+
+ e = i;
+
+ if( m==0 ) break;
+
+ }
+
+ }
+
+ out[0] = e;
+
+ j = 1;
+
+ for(i=0; i<n; i++){
+
+ int c = (in[i] - e)&0xff;
+
+ if( c==0 ){
+
+ out[j++] = 1;
+
+ out[j++] = 1;
+
+ }else if( c==1 ){
+
+ out[j++] = 1;
+
+ out[j++] = 2;
+
+ }else if( c=='\'' ){
+
+ out[j++] = 1;
+
+ out[j++] = 3;
+
+ }else{
+
+ out[j++] = c;
+
+ }
+
+ }
+
+ out[j] = 0;
+
+ return j;
+
+}
+
+
+
+/*
+
+** Decode the string "in" into binary data and write it into "out".
+
+** This routine reverses the encoding created by sqlite3_encode_binary().
+
+** The output will always be a few bytes less than the input. The number
+
+** of bytes of output is returned. If the input is not a well-formed
+
+** encoding, -1 is returned.
+
+**
+
+** The "in" and "out" parameters may point to the same buffer in order
+
+** to decode a string in place.
+
+*/
+
+int sqlite3_decode_binary(const unsigned char *in, unsigned char *out){
+
+ int i, c, e;
+
+ e = *(in++);
+
+ i = 0;
+
+ while( (c = *(in++))!=0 ){
+
+ if( c==1 ){
+
+ c = *(in++);
+
+ if( c==1 ){
+
+ c = 0;
+
+ }else if( c==2 ){
+
+ c = 1;
+
+ }else if( c==3 ){
+
+ c = '\'';
+
+ }else{
+
+ return -1;
+
+ }
+
+ }
+
+ out[i++] = (c + e)&0xff;
+
+ }
+
+ return i;
+
+}
+
--- /dev/null
+////////////////////////////////////////////////////////////////////////////////
+
+// CppSQLite3 - A C++ wrapper around the SQLite3 embedded database library.
+
+//
+
+// Copyright (c) 2004 Rob Groves. All Rights Reserved. rob.groves@btinternet.com
+
+//
+
+// Permission to use, copy, modify, and distribute this software and its
+
+// documentation for any purpose, without fee, and without a written
+
+// agreement, is hereby granted, provided that the above copyright notice,
+
+// this paragraph and the following two paragraphs appear in all copies,
+
+// modifications, and distributions.
+
+//
+
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
+
+// INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
+
+// PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+
+// EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//
+
+// THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+
+// PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF
+
+// ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". THE AUTHOR HAS NO OBLIGATION
+
+// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+//
+
+// V3.0 03/08/2004 -Initial Version for sqlite3
+
+//
+
+// V3.1 16/09/2004 -Implemented getXXXXField using sqlite3 functions
+
+// -Added CppSQLiteDB3::tableExists()
+
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef _CppSQLite3_H_
+
+#define _CppSQLite3_H_
+
+
+
+#include "sqlite3.h"
+
+#include <cstdio>
+
+#include <cstring>
+
+
+
+#define CPPSQLITE_ERROR 1000
+
+
+
+class CppSQLite3Exception
+
+{
+
+public:
+
+
+
+ CppSQLite3Exception(const int nErrCode,
+
+ char* szErrMess,
+
+ bool bDeleteMsg=true);
+
+ CppSQLite3Exception(const int nErrCode,
+
+ const char* szErrMess,
+
+ bool bDeleteMsg=true);
+
+
+
+ CppSQLite3Exception(const CppSQLite3Exception& e);
+
+
+
+ virtual ~CppSQLite3Exception();
+
+
+
+ const int errorCode() { return mnErrCode; }
+
+
+
+ const char* errorMessage() { return mpszErrMess; }
+
+
+
+ static const char* errorCodeAsString(int nErrCode);
+
+
+
+private:
+
+
+
+ int mnErrCode;
+
+ char* mpszErrMess;
+
+};
+
+
+
+
+
+class CppSQLite3Buffer
+
+{
+
+public:
+
+
+
+ CppSQLite3Buffer();
+
+
+
+ ~CppSQLite3Buffer();
+
+
+
+ const char* format(const char* szFormat, ...);
+
+
+
+ operator const char*() { return mpBuf; }
+
+
+
+ void clear();
+
+
+
+private:
+
+
+
+ char* mpBuf;
+
+};
+
+
+
+
+
+class CppSQLite3Binary
+
+{
+
+public:
+
+
+
+ CppSQLite3Binary();
+
+
+
+ ~CppSQLite3Binary();
+
+
+
+ void setBinary(const unsigned char* pBuf, int nLen);
+
+ void setEncoded(const unsigned char* pBuf);
+
+
+
+ const unsigned char* getEncoded();
+
+ const unsigned char* getBinary();
+
+
+
+ int getBinaryLength();
+
+
+
+ unsigned char* allocBuffer(int nLen);
+
+
+
+ void clear();
+
+
+
+private:
+
+
+
+ unsigned char* mpBuf;
+
+ int mnBinaryLen;
+
+ int mnBufferLen;
+
+ int mnEncodedLen;
+
+ bool mbEncoded;
+
+};
+
+
+
+
+
+class CppSQLite3Query
+
+{
+
+public:
+
+
+
+ CppSQLite3Query();
+
+
+
+ CppSQLite3Query(const CppSQLite3Query& rQuery);
+
+
+
+ CppSQLite3Query(sqlite3* pDB,
+
+ sqlite3_stmt* pVM,
+
+ bool bEof,
+
+ bool bOwnVM=true);
+
+
+
+ CppSQLite3Query& operator=(const CppSQLite3Query& rQuery);
+
+
+
+ virtual ~CppSQLite3Query();
+
+
+
+ int numFields();
+
+
+
+ int fieldIndex(const char* szField);
+
+ const char* fieldName(int nCol);
+
+
+
+ const char* fieldDeclType(int nCol);
+
+ int fieldDataType(int nCol);
+
+
+
+ const char* fieldValue(int nField);
+
+ const char* fieldValue(const char* szField);
+
+
+
+ int getIntField(int nField, int nNullValue=0);
+
+ int getIntField(const char* szField, int nNullValue=0);
+
+
+
+ double getFloatField(int nField, double fNullValue=0.0);
+
+ double getFloatField(const char* szField, double fNullValue=0.0);
+
+
+
+ const char* getStringField(int nField, const char* szNullValue="");
+
+ const char* getStringField(const char* szField, const char* szNullValue="");
+
+
+
+ const unsigned char* getBlobField(int nField, int& nLen);
+
+ const unsigned char* getBlobField(const char* szField, int& nLen);
+
+
+
+ bool fieldIsNull(int nField);
+
+ bool fieldIsNull(const char* szField);
+
+
+
+ bool eof();
+
+
+
+ void nextRow();
+
+
+
+ void finalize();
+
+
+
+private:
+
+
+
+ void checkVM();
+
+
+
+ sqlite3* mpDB;
+
+ sqlite3_stmt* mpVM;
+
+ bool mbEof;
+
+ int mnCols;
+
+ bool mbOwnVM;
+
+};
+
+
+
+
+
+class CppSQLite3Table
+
+{
+
+public:
+
+
+
+ CppSQLite3Table();
+
+
+
+ CppSQLite3Table(const CppSQLite3Table& rTable);
+
+
+
+ CppSQLite3Table(char** paszResults, int nRows, int nCols);
+
+
+
+ virtual ~CppSQLite3Table();
+
+
+
+ CppSQLite3Table& operator=(const CppSQLite3Table& rTable);
+
+
+
+ int numFields();
+
+
+
+ int numRows();
+
+
+
+ const char* fieldName(int nCol);
+
+
+
+ const char* fieldValue(int nField);
+
+ const char* fieldValue(const char* szField);
+
+
+
+ int getIntField(int nField, int nNullValue=0);
+
+ int getIntField(const char* szField, int nNullValue=0);
+
+
+
+ double getFloatField(int nField, double fNullValue=0.0);
+
+ double getFloatField(const char* szField, double fNullValue=0.0);
+
+
+
+ const char* getStringField(int nField, const char* szNullValue="");
+
+ const char* getStringField(const char* szField, const char* szNullValue="");
+
+
+
+ bool fieldIsNull(int nField);
+
+ bool fieldIsNull(const char* szField);
+
+
+
+ void setRow(int nRow);
+
+
+
+ void finalize();
+
+
+
+private:
+
+
+
+ void checkResults();
+
+
+
+ int mnCols;
+
+ int mnRows;
+
+ int mnCurrentRow;
+
+ char** mpaszResults;
+
+};
+
+
+
+
+
+class CppSQLite3Statement
+
+{
+
+public:
+
+
+
+ CppSQLite3Statement();
+
+
+
+ CppSQLite3Statement(const CppSQLite3Statement& rStatement);
+
+
+
+ CppSQLite3Statement(sqlite3* pDB, sqlite3_stmt* pVM);
+
+
+
+ virtual ~CppSQLite3Statement();
+
+
+
+ CppSQLite3Statement& operator=(const CppSQLite3Statement& rStatement);
+
+
+
+ int execDML();
+
+
+
+ CppSQLite3Query execQuery();
+
+
+
+ void bind(int nParam, const char* szValue);
+
+ void bind(int nParam, const int nValue);
+
+ void bind(int nParam, const double dwValue);
+
+ void bind(int nParam, const unsigned char* blobValue, int nLen);
+
+ void bindNull(int nParam);
+
+
+
+ void reset();
+
+
+
+ void finalize();
+
+
+
+private:
+
+
+
+ void checkDB();
+
+ void checkVM();
+
+
+
+ sqlite3* mpDB;
+
+ sqlite3_stmt* mpVM;
+
+};
+
+
+
+
+
+class CppSQLite3DB
+
+{
+
+public:
+
+
+
+ CppSQLite3DB();
+
+
+
+ virtual ~CppSQLite3DB();
+
+
+
+ void open(const char* szFile);
+
+
+
+ void close();
+
+
+
+ bool tableExists(const char* szTable);
+
+
+
+ int execDML(const char* szSQL);
+
+
+
+ CppSQLite3Query execQuery(const char* szSQL);
+
+
+
+ int execScalar(const char* szSQL);
+
+
+
+ CppSQLite3Table getTable(const char* szSQL);
+
+
+
+ CppSQLite3Statement compileStatement(const char* szSQL);
+
+
+
+ sqlite_int64 lastRowId();
+
+
+
+ void interrupt() { sqlite3_interrupt(mpDB); }
+
+
+
+ void setBusyTimeout(int nMillisecs);
+
+
+
+ static const char* SQLiteVersion() { return SQLITE_VERSION; }
+
+
+
+private:
+
+
+
+ CppSQLite3DB(const CppSQLite3DB& db);
+
+ CppSQLite3DB& operator=(const CppSQLite3DB& db);
+
+
+
+ sqlite3_stmt* compile(const char* szSQL);
+
+
+
+ void checkDB();
+
+
+
+ sqlite3* mpDB;
+
+ int mnBusyTimeoutMs;
+
+};
+
+
+
+#endif
+
--- /dev/null
+namespace creaImageIO
+{
+ /**
+ * \ingroup View
+ */
+ //=====================================================================
+
+ //=====================================================================
+ ///Abstract class that handles views, attributes and previews (GUI) for Gimmick.
+ class GimmickView
+ {
+ public:
+ /// Ctor
+ GimmickView();
+ /// Virtual destructor
+ virtual ~GimmickView();
+
+ //====================================================================
+ // General
+ //====================================================================
+
+ /// Returns the size of the current selection
+ virtual int GetSelectionSize() { return 0; }
+ /// Returns true if there is a valid selection
+ virtual bool IsSelectionValid(){ return false; }
+ /// Returns the vector of full filenames of selected images
+ virtual void GetSelectedFiles(std::vector<std::string>&){ return; }
+ /// Returns the vector of images corresponding to selection
+ virtual void GetSelectedImages(std::vector<vtkImageData*>&){ return; }
+ /// Returns the vector of DicomNode corresponding to selection
+ virtual void GetSelectedDicomNodes(std::vector<DicomNode*>&){ return; }
+ /// Returns the DicomNode corresponding to the tree item
+ virtual DicomNode* GetDicomNodeOfItem(const TreeItemId& i);
+
+
+ /// Type of list of DicomDatabase
+ typedef std::vector<DicomDatabase*> DicomDatabaseListType;
+ /// Returns the list of DicomDatabase open
+ virtual DicomDatabaseListType& GetDicomDatabaseList()
+ { return null; }
+ /// Returns the list of DicomDatabase open (const)
+ virtual const DicomDatabaseListType& GetDicomDatabaseList() const
+ { return null; }
+
+ protected:
+ ///Opens an existing database, or else, creates a local database.
+ virtual void OpenOrNewDatabase(bool open){ return; }
+ ///Shows the help
+ virtual void ShowHelp();
+
+ private:
+ ///Gets the extension of the database
+ const std::string& GetDatabaseExtension() { return null; }
+ ///Sets the extension of the database
+ virtual void SetDatabaseExtension(const std::string& ext){ return; }
+
+
+ //====================================================================
+ // Preview Display Related
+ //====================================================================
+
+
+ ///Shows the image sent as a parameter
+ private:
+ virtual void ShowImage(vtkImageData* image){ return; }
+
+ //====================================================================
+ // Favorites Related
+ //====================================================================
+
+
+ public:
+ ///Loads or creates a favorites database
+ virtual void LoadOrCreateFavoritesDatabase(){ return; }
+ private:
+ ///Creates the user settings directory
+ void CreateUserSettingsDirectory(){ return; }
+ ///Obtains the user settings directory
+ const std::string& GetUserSettingsDirectory(){ return null; }
+
+ //====================================================================
+ // Attribute Display Related
+ //====================================================================
+
+
+ ///Shows the Information regarding the node sent as a parameter
+ private:
+ virtual void ShowInformation(DicomNode*){ return; }
+
+ //====================================================================
+ // Tree Display Related
+ //====================================================================
+
+ protected:
+ /// Completely rebuilds the view with
+ /// current DicomDatabaseList
+ virtual void RebuildView(){ return; }
+ /// Recursively updates the part of the view corresponding
+ /// to the DicomDatabase passed
+ /// i.e. creates items for the DicomNode which do not have
+ /// deletes obsolete items (whose DicomNode has been deleted)
+ virtual void UpdateDicomDatabaseView(DicomDatabase*){ return; }
+ /// Recursively updates the part of the view corresponding
+ /// to the DicomNode provided.
+ /// parent is its parent in the tree (where to insert / remove it)
+ virtual void UpdateDicomNodeView(DicomNode* n, const TreeItemId& parent){ return; }
+
+ private:
+ ///Type definition of the data regarding the tree
+ typedef WxGimmickTreeItemData TreeItemData;
+ ///Gets the item data of the tree item passed as a parameter
+ TreeItemData* GetItemData(const TreeItemId& id){ return null; }
+ ///Type definition of the data insid a node of the tree
+ typedef WxGimmickDicomNodeData NodeData;
+
+
+ //====================================================================
+ // Class Attributes
+ //====================================================================
+
+
+ int mSelectionType;
+ int mSelectionMaxImageDimension;
+ int mCurrentSelectionImageSize[4];
+
+ ///Existent Database List
+ DicomDatabaseListType mDicomDatabaseList;
+ ///Favorites database
+ DicomDatabase* mFavoriteDatabase;
+
+ ///Path to the database list file
+ std::string mDatabaseListFile;
+ ///Extension of the database
+ std::string mDatabaseExtension;
+
+ bool mJustStarted;
+
+ int mFirstDicomDatabaseIconIndex;
+
+ // Previewer
+ vtkImageViewer2* mViewer;
+
+ int mx1,mx2,my1,my2,mz1,mz2;
+ double mspx,mspy,mspz;
+
+ // Image preview :
+ // Multi-thread image reader
+ MultiThreadImageReader mReader;
+ // map of images name to node
+ std::map<std::string,DicomNode*> mImageFileNameToNode;
+
+ //Controller which manages the interaction with the model
+ Gimmick* controller;
+
+ };
+ // EO class GimmickView
+ //=====================================================================
+
+} // EO namespace creaImageIO
+
+// EOF
+#endif
\ No newline at end of file
--- /dev/null
+New architecture created on 04/02/09 following a MVC approach
+
+Elements of the architecture:
+
+* Data structure: Attributed tree
+--------------------------------------
+in namespace creaImageIO::tree
+Tree : Attributed tree structure
+ inherits Node
+ holds a vector of root TreeNode
+TreeData : Abstract class to store user data on a tree
+Node :
+ belong to a Tree,
+ holds a pointer on parent Node,
+ holds a vector of children Node
+NodeData : Abstract class to store user data on a tree node
+Descriptor : Descriptor of the structure a tree (number of levels, descriptors of each level, ...)
+
+ holds a vector of LevelDescriptor
+LevelDescriptor :
+ holds a vector of TreeAttributeDescriptor
+AttributeDescriptor :
+ stores name, dicom group/elem, flags
+Comparator: Abstract definition of a comparator of Node
+ Comparison is done by operator()(Node* const &, Node* const &)
+ComparatorWithOrder : Abstract Comparator whose order can be reversed
+ Concrete comparison is done by method compare(Node* const &, Node* const &)
+LexicographicalComparator : A Comparator which stores a vector of Comparators and which performs lexicographical comparison
+
+IntComparator : Compares the values of a given Attribute of the Nodes which is decoded as an int value
+FloatComparator : Compares the values of a given Attribute of the Nodes which is decoded as a float value
+StringComparator : Compares the values of a given Attribute of the Nodes which is decoded as a string value
+
+
+* Models : TreeHandler and descendants
+--------------------------------------
+
+TreeHandler : Abstract class which 'handles' a Tree structure.
+ Can:
+ Load the children of a given Node
+
+SQLiteTreeHandler : Concrete TreeHandler which manages a tree stored in a sqlite database
+CppSQLite3.h / CppSQLite3.cpp : C++ interface to sqlite db
+...
+
+ImageFinder : Parses (recursively) a part of a filesystem to look for known images and load their attributes in order to add the images to a Tree (submission via a TreeHandler::AddBranch)
+
+
+* Image handling
+----------------
+
+creaImageIOImageReader.h/cpp :
+SpecificImageReader
+ImageReader
+
+creaImageIOMultiThreadImageReader.h/cpp
+creaImageIOIndexedHeap.h/cpp
+
+* Views :
+---------
+TreeView
+WxTreeView
+WxGimmickView
+QTreeView
+QGimmickView
+...
+
+* Controller :
+--------------
+Gimmick
+...
--- /dev/null
+#ifndef __creaImageIOAbstractImageReader_h_INCLUDED__
+#define __creaImageIOAbstractImageReader_h_INCLUDED__
+
+
+#include <vtkImageData.h>
+#include <string>
+#include <map>
+#include <vector>
+#include "creaImageIOTreeAttributeMapType.h"
+
+namespace creaImageIO
+{
+
+
+ /**
+ * \ingroup IO
+ */
+
+ //=====================================================================
+ /// Abstract image reader
+ class AbstractImageReader
+ {
+ public:
+ AbstractImageReader() {}
+ virtual ~AbstractImageReader() {}
+
+ /// Get the reader's name
+ const std::string& GetName() const { return mName; }
+
+ /// Add file extensions read by the reader
+ virtual void PushBackExtensions(std::vector<std::string>&) {}
+
+ /// Test if file is read by this reader
+ virtual bool CanRead(const std::string& filename) { return false; }
+
+ /// return for a file a 2D VTkImage
+ virtual vtkImageData* ReadImage(const std::string& filename) { return 0; }
+
+ /// Read the attributes for a file
+ virtual void ReadAttributes(const std::string& filename,
+ tree::AttributeMapType& attr) {}
+
+ protected:
+
+ /// Set the reader's name
+ void SetName(const std::string& s) { mName = s; }
+ private:
+ std::string mName;
+ };
+ //=====================================================================
+
+
+
+} // namespace creaImageIO
+
+
+
+#endif // #ifndef __creaImageIOAbstractImageReader_h_INCLUDED__
--- /dev/null
+#include <creaImageIODicomImageReader.h>
+
+#include <vtkGdcmReader.h>
+
+
+
+#include <creaImageIOSystem.h>
+#include "boost/filesystem/path.hpp"
+
+#include <creaImageIOTreeAttributeDescriptor.h>
+#include <vtkStringArray.h>
+#include <creaImageIOGimmick.h>
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+namespace creaImageIO
+{
+
+ //=====================================================================
+ DicomImageReader::DicomImageReader()
+ {
+ mReader = vtkGdcmReader::New();
+//EED mReader->SetFlipY(false);
+ SetName ( "Dicom" );
+
+ };
+ //=====================================================================
+
+ //=====================================================================
+ DicomImageReader::~DicomImageReader()
+ {
+ mReader->Delete();
+ }
+ //=====================================================================
+
+ //=====================================================================
+ bool DicomImageReader::CanRead(const std::string& filename)
+ {
+ GDCM_NAME_SPACE::Document*doc;
+ GDCM_NAME_SPACE::File* file = GDCM_NAME_SPACE::File::New();
+ file->SetLoadMode( GDCM_NAME_SPACE::LD_ALL);
+ file->SetFileName(filename.c_str());
+ file->Load();
+ bool ok = file->IsReadable();
+ if(!ok)
+ {
+ doc = (GDCM_NAME_SPACE::Document*)file;
+ ok = doc->IsReadable();
+ }
+ file->Delete();
+ return ok;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ vtkImageData* DicomImageReader::ReadImage(const std::string& filename)
+ {
+ vtkImageData* im = 0;
+ try
+ {
+ mReader->SetFileName(filename.c_str());
+ mReader->Update();
+ im = vtkImageData::New();
+ im->ShallowCopy(mReader->GetOutput());
+ }
+ catch (...)
+ {
+ if (im!=0) im->Delete();
+ im = 0;
+ }
+ return im;
+ }
+
+ //=====================================================================
+ void DicomImageReader::PushBackExtensions(std::vector<std::string>& v)
+ {
+ v.push_back("dcm");
+ v.push_back("");
+ }
+ //=====================================================================
+
+ //========================================================================
+ std::string irclean(const std::string& str)
+ {
+ if(str.size() > 0)
+ {
+ if (str == "GDCM::Unfound")
+ {
+ return "";
+ }
+ if (str[str.size()-1]==' ')
+ {
+ return irclean(str.substr(0,str.size()-1));
+ }
+ if (str[str.size()-1]==0)
+ {
+ return irclean(str.substr(0,str.size()-1));
+ }
+ }
+
+ return str;
+ }
+ //========================================================================
+
+ //=====================================================================
+ void DicomImageReader::ReadAttributes(const std::string& filename,
+ std::map<std::string,std::string>& attr)
+ {
+ GimmickMessage(2,"Reading attributes from DICOM file '"
+ <<filename<<"'"<<std::endl);
+
+ GDCM_NAME_SPACE::File* file = GDCM_NAME_SPACE::File::New();
+ //boost::shared_ptr<GDCM_NAME_SPACE::File> file(GDCM_NAME_SPACE::File::New());//, DicomImageReader::deleter());
+
+ GDCM_NAME_SPACE::Document *doc= GDCM_NAME_SPACE::File::New();
+ doc->SetLoadMode( GDCM_NAME_SPACE::LD_ALL);
+ doc->SetFileName(filename.c_str());
+ doc->Load();
+ file->SetLoadMode( GDCM_NAME_SPACE::LD_ALL);
+ file->SetFileName(filename.c_str());
+ file->Load();
+ if (file->IsReadable())// ||((GDCM_NAME_SPACE::Document*) file)->IsReadable())
+ {
+ std::map<std::string,std::string>::iterator i;
+ for (i=attr.begin();i!=attr.end();++i)
+ {
+ if ( i->first == "D0004_1500" )
+ {
+ boost::filesystem::path full_path(filename);
+ std::string f = full_path.leaf();
+ i->second = f;
+ }
+ else if ( i->first == "FullFileName" )
+ {
+ i->second = filename;
+ }
+ else if ( i->first == "FullFileDirectory" )
+ {
+ std::string::size_type last_pos = filename.find_last_of("//");
+ //find first separator
+ i->second = filename.substr(0, last_pos);
+ }
+ else
+ {
+ uint16_t el;
+ uint16_t gr;
+
+ tree::AttributeDescriptor::GetDicomGroupElementFromKey(i->first,gr,el);
+ if ( ( gr!=0 ) && ( el!=0 ) )
+ {
+ std::string val = file->GetEntryString(gr,el);
+ i->second = irclean(val);
+ }
+ }
+ }
+ }
+ }
+
+ //=====================================================================
+
+} // namespace creaImageIO
+
--- /dev/null
+#ifndef __creaImageIODicomImageReader_h_INCLUDED__
+#define __creaImageIODicomImageReader_h_INCLUDED__
+
+
+#include <creaImageIOAbstractImageReader.h>
+#include <gdcmFile.h>
+// forward decl
+class vtkGdcmReader;
+
+namespace creaImageIO
+{
+
+
+ /**
+ * \ingroup IO
+ */
+
+ //=====================================================================
+ /// Concrete image reader for DICOM images
+ class DicomImageReader : virtual public AbstractImageReader
+ {
+ public:
+ DicomImageReader();
+ virtual ~DicomImageReader();
+
+ /// Add file extensions read by the reader
+ virtual void PushBackExtensions(std::vector<std::string>&);
+ /// Test if file is read by this reader
+ virtual bool CanRead(const std::string& filename);
+ /// return for a file a 2D VTkImage
+ virtual vtkImageData* ReadImage(const std::string& filename);
+ /// Read the attributes for a file
+ virtual void ReadAttributes(const std::string& filename,
+ tree::AttributeMapType& attr);
+
+ private:
+ vtkGdcmReader* mReader;
+ struct deleter
+ {
+
+ };
+ friend struct deleter;
+ };
+ //=====================================================================
+
+
+
+} // namespace creaImageIO
+
+
+
+#endif // #ifndef __creaImageIODicomImageReader_h_INCLUDED__
--- /dev/null
+#include <creaImageIODicomImageReader2.h>
+
+
+
+#include <creaImageIOSystem.h>
+#include "boost/filesystem/path.hpp"
+
+#include <creaImageIOTreeAttributeDescriptor.h>
+#include <vtkStringArray.h>
+#include <creaImageIOGimmick.h>
+
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+namespace creaImageIO
+{
+
+ //=====================================================================
+ DicomImageReader::DicomImageReader()
+ {
+ mReader = vtkGDCMImageReader::New();
+ SetName ( "Dicom" );
+
+ };
+ //=====================================================================
+
+ //=====================================================================
+ DicomImageReader::~DicomImageReader()
+ {
+ mReader->Delete();
+ }
+ //=====================================================================
+
+ //=====================================================================
+ bool DicomImageReader::CanRead(const std::string& filename)
+ {
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ return reader.Read();
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ vtkImageData* DicomImageReader::ReadImage(const std::string& filename)
+ {
+ vtkImageData* im = 0;
+ try
+ {
+ mReader->SetFileName(filename.c_str());
+ mReader->Update();
+ im = vtkImageData::New();
+ im->ShallowCopy(mReader->GetOutput());
+ }
+ catch (...)
+ {
+ if (im!=0) im->Delete();
+ im = 0;
+ }
+ return im;
+ }
+
+ //=====================================================================
+ void DicomImageReader::PushBackExtensions(std::vector<std::string>& v)
+ {
+ v.push_back("dcm");
+ v.push_back("");
+ }
+ //=====================================================================
+
+ //========================================================================
+ std::string irclean(const std::string& str)
+ {
+ if(str.size() > 0)
+ {
+ if (str == "GDCM::Unfound")
+ {
+ return "";
+ }
+ if (str[str.size()-1]==' ')
+ {
+ return irclean(str.substr(0,str.size()-1));
+ }
+ if (str[str.size()-1]==0)
+ {
+ return irclean(str.substr(0,str.size()-1));
+ }
+ }
+
+ return str;
+ }
+ //========================================================================
+
+ //=====================================================================
+ void DicomImageReader::ReadAttributes(const std::string& filename,
+ std::map<std::string,std::string>& attr)
+ {
+ GimmickMessage(2,"Reading attributes from DICOM file '"
+ <<filename<<"'"<<std::endl);
+
+
+ gdcm::Reader reader;
+ reader.SetFileName( filename.c_str() );
+ if (reader.Read())
+ {
+ std::map<std::string,std::string>::iterator i;
+ for (i=attr.begin();i!=attr.end();++i)
+ {
+ if ( i->first == "D0004_1500" )
+ {
+ boost::filesystem::path full_path(filename);
+ std::string f = full_path.leaf();
+ i->second = f;
+ }
+ else if ( i->first == "FullFileName" )
+ {
+ i->second = filename;
+ }
+ else if ( i->first == "FullFileDirectory" )
+ {
+ std::string::size_type last_pos = filename.find_last_of("//");
+ //find first separator
+ i->second = filename.substr(0, last_pos);
+ }
+ else
+ {
+ uint16_t el;
+ uint16_t gr;
+
+ tree::AttributeDescriptor::GetDicomGroupElementFromKey(i->first,gr,el);
+ if ( ( gr!=0 ) && ( el!=0 ) )
+ {
+ gdcm::DataElement de( gdcm::Tag(gr,el) );
+ std::string val = GetStringValueFromTag(reader.GetFile().GetDataSet().GetDataElement(gdcm::Tag(gr,el)));
+ i->second = irclean(val);
+ }
+ }
+ }
+ }
+ }
+
+void DicomImageReader::ReadAttributes2(const std::string& filename,
+ std::map<std::string,std::string>& attr)
+ {
+
+ if(!b_loaded)
+ {
+ std::map<std::string,std::string>::iterator i;
+ for (i=attr.begin();i!=attr.end();++i)
+ {
+ if ( i->first == "D0004_1500" || i->first == "FullFileName" || i->first == "FullFileDirectory" )
+ {
+
+ }
+ else
+ {
+ uint16_t el;
+ uint16_t gr;
+
+ tree::AttributeDescriptor::GetDicomGroupElementFromKey(i->first,gr,el);
+ mscan.AddTag(gdcm::Tag(gr,el) );
+ }
+ }
+ b_loaded = true;
+ }
+ bool b = mscan.IsKey(filename.c_str());
+ if( b )
+ {
+ const gdcm::Scanner::TagToValue &mapping = mscan.GetMapping(filename.c_str());
+ gdcm::Scanner::TagToValue::const_iterator it = mapping.begin();
+ std::map<std::string, std::string>::iterator i;
+ for (i=attr.begin();i!=attr.end();++i, ++it)
+ {
+ if ( i->first == "D0004_1500" )
+ {
+ boost::filesystem::path full_path(filename);
+ std::string f = full_path.leaf();
+ i->second = f;
+ }
+ else if ( i->first == "FullFileName" )
+ {
+ i->second = filename;
+ }
+ else if ( i->first == "FullFileDirectory" )
+ {
+ std::string::size_type last_pos = filename.find_last_of("//");
+ //find first separator
+ i->second = filename.substr(0, last_pos);
+ }
+ else
+ {
+ const char *value = it->second;
+ i->second = irclean(it->second);
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ const std::string DicomImageReader::GetStringValueFromTag(const gdcm::DataElement& de)
+{
+ static std::string buffer;
+ buffer = ""; // cleanup previous call
+
+
+ const gdcm::ByteValue *bv = de.GetByteValue();
+ if( bv ) // Can be Type 2
+ {
+ buffer = std::string( bv->GetPointer(), bv->GetLength() );
+ // Will be padded with at least one \0
+ }
+
+
+ // Since return is a const char* the very first \0 will be considered
+ return buffer.c_str();
+}
+ //=====================================================================
+
+} // namespace creaImageIO
+
--- /dev/null
+#ifndef __creaImageIODicomImageReader_h_INCLUDED__
+#define __creaImageIODicomImageReader_h_INCLUDED__
+
+
+#include <creaImageIOAbstractImageReader.h>
+#if defined(USE_GDCM2)
+#include <gdcmReader.h>
+#include <vtkGDCMImageReader.h>
+#include <gdcmScanner.h>
+#endif
+
+class vtkGDCMImageReader;
+
+namespace creaImageIO
+{
+
+
+ /**
+ * \ingroup IO
+ */
+
+ //=====================================================================
+ /// Concrete image reader for DICOM images
+ class DicomImageReader : virtual public AbstractImageReader
+ {
+ public:
+ DicomImageReader();
+ virtual ~DicomImageReader();
+
+ /// Add file extensions read by the reader
+ virtual void PushBackExtensions(std::vector<std::string>&);
+ /// Test if file is read by this reader
+ virtual bool CanRead(const std::string& filename);
+ /// return for a file a 2D VTkImage
+ virtual vtkImageData* ReadImage(const std::string& filename);
+ /// Read the attributes for a file
+ virtual void ReadAttributes(const std::string& filename,
+ tree::AttributeMapType& attr);
+ void ReadAttributes2(const std::string& filename,
+ tree::AttributeMapType& attr);
+
+ private:
+ const std::string GetStringValueFromTag( const gdcm::DataElement& ds);
+ vtkGDCMImageReader *mReader;
+ gdcm::Scanner mscan;
+ bool b_loaded;
+ struct deleter
+ {
+ void operator()(gdcm::File* p)
+ {
+ delete p;
+ }
+ };
+ friend struct deleter;
+ };
+ //=====================================================================
+
+
+
+} // namespace creaImageIO
+
+
+
+#endif // #ifndef __creaImageIODicomImageReader_h_INCLUDED__
--- /dev/null
+#include <creaImageIODicomScanner.h>
+#include "creaImageIOSystem.h"
+#include <boost/filesystem.hpp>
+#include "boost/algorithm/string.hpp"
+
+
+
+#include <creaImageIOSystem.h>
+#include "boost/filesystem/path.hpp"
+
+#include <creaImageIOTreeAttributeDescriptor.h>
+#include <vtkStringArray.h>
+#include <creaImageIOGimmick.h>
+#include <gdcmDirectory.h>
+
+#include <fstream>
+
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+namespace creaImageIO
+{
+
+ //=====================================================================
+ DicomImageScanner::DicomImageScanner()
+ {
+ mReader = vtkGDCMImageReader::New();
+ mscan.ClearTags();
+ b_loaded = false;
+ };
+ //=====================================================================
+
+ //=====================================================================
+ DicomImageScanner::~DicomImageScanner()
+ {
+ mReader->Delete();
+ }
+ //=====================================================================
+
+ //=====================================================================
+ bool DicomImageScanner::addDirectory(std::string& filename, std::map<std::string,std::string>& attr)
+ {
+ if(!b_loaded)
+ {
+ mscan.ClearTags();
+ std::map<std::string,std::string>::iterator i;
+ int j= 0;
+ for (i=attr.begin();i!=attr.end();++i, j++)
+ {
+ if ( i->first == "D0004_1500" || i->first == "FullFileName" || i->first == "FullFileDirectory" )
+ {
+
+ }
+ else
+ {
+ uint16_t el;
+ uint16_t gr;
+
+ tree::AttributeDescriptor::GetDicomGroupElementFromKey(i->first,gr,el);
+ mscan.AddTag(gdcm::Tag(gr,el) );
+
+ }
+ }
+ b_loaded = true;
+ }
+ gdcm::Directory d;
+
+ boost::algorithm::replace_all(filename,"\\", "/");
+ d.Load(filename.c_str(),true);
+ mscan.Scan(d.GetFilenames());
+
+ return true;
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ vtkImageData* DicomImageScanner::ReadImage(const std::string& filename)
+ {
+ vtkImageData* im = 0;
+ try
+ {
+ mReader->SetFileName(filename.c_str());
+ mReader->Update();
+ im = vtkImageData::New();
+ im->ShallowCopy(mReader->GetOutput());
+ }
+ catch (...)
+ {
+ if (im!=0) im->Delete();
+ im = 0;
+ }
+ return im;
+ }
+
+
+ //=====================================================================
+
+ //========================================================================
+ std::string DicomImageScanner::irclean(const std::string& str)
+ {
+ if(str.size() > 0)
+ {
+ if (str == "GDCM::Unfound")
+ {
+ return "";
+ }
+ if (str[str.size()-1]==' ')
+ {
+ return irclean(str.substr(0,str.size()-1));
+ }
+ if (str[str.size()-1]==0)
+ {
+ return irclean(str.substr(0,str.size()-1));
+ }
+ }
+
+ return str;
+ }
+ //========================================================================
+
+ //=====================================================================
+
+void DicomImageScanner::ReadAttributes(const std::string& filename,
+ std::map<std::string,std::string>& attr)
+ {
+ std::string name = "E:\\data-images\\dicoms\\CD1\\DICOM\\09112417\\37390000/31582235";
+
+ bool b = mscan.IsKey(filename.c_str());
+ if( b ) {
+ const gdcm::Scanner::TagToValue &mapping = mscan.GetMapping(filename.c_str());
+ gdcm::Scanner::TagToValue::const_iterator it = mapping.begin();
+
+ std::map<std::string, std::string>::iterator i;
+ int j= 0;
+
+
+
+ for (;it != mapping.end(); ++it)
+ {
+ /*std::stringstream val;
+ val << "D";
+ val << std::hex << it->first.GetGroup() ;
+ val << "_";;
+ val << std::hex << it->first.GetElement();
+ var >> key*/
+ char key[12] ;
+
+ sprintf(key,"D%04x_%04x", it->first.GetGroup(), it->first.GetElement());
+ attr[key] = irclean(it->second);
+ }
+
+
+ /*
+
+
+ for (i=attr.begin();i!=attr.end();++i, j++)
+ {*/
+ if ( attr.find("D0004_1500") != attr.end())
+ {
+ boost::filesystem::path full_path(filename);
+ std::string f = full_path.leaf();
+ attr["D0004_1500"] = f;
+ }
+ if ( attr.find("FullFileName")!= attr.end())
+ {
+ attr["FullFileName"] = filename;
+ }
+ if ( attr.find("FullFileDirectory" )!= attr.end())
+ {
+ std::string::size_type last_pos = filename.find_last_of("//");
+ //find first separator
+ attr["FullFileDirectory" ] = filename.substr(0, last_pos);
+ }
+ // else
+ // {
+
+ // uint16_t el;
+ // uint16_t gr;
+
+ // tree::AttributeDescriptor::GetDicomGroupElementFromKey(i->first,gr,el);
+
+ // mscan.AddTag(gdcm::Tag(gr,el) );
+
+ // // const char *value = it->second;
+ // i->second = irclean(it->second);
+ // ++it;
+ // }
+ //}
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ const std::string DicomImageScanner::GetStringValueFromTag(const gdcm::DataElement& de)
+{
+ static std::string buffer;
+ buffer = ""; // cleanup previous call
+
+
+ const gdcm::ByteValue *bv = de.GetByteValue();
+ if( bv ) // Can be Type 2
+ {
+ buffer = std::string( bv->GetPointer(), bv->GetLength() );
+ // Will be padded with at least one \0
+ }
+
+
+ // Since return is a const char* the very first \0 will be considered
+ return buffer.c_str();
+}
+ //=====================================================================
+
+} // namespace creaImageIO
+
--- /dev/null
+#ifndef __creaImageIODicomScanner_h_INCLUDED__
+#define __creaImageIODicomScanner_h_INCLUDED__
+
+#include "creaImageIOTree.h"
+
+#if defined(USE_GDCM2)
+#include <gdcmReader.h>
+#include <vtkGDCMImageReader.h>
+#include <gdcmScanner.h>
+#endif
+
+class vtkGDCMImageReader;
+
+namespace creaImageIO
+{
+
+
+ /**
+ * \ingroup IO
+ */
+
+ //=====================================================================
+ /// Concrete image reader for DICOM images
+ class DicomImageScanner
+ {
+ public:
+ DicomImageScanner();
+ virtual ~DicomImageScanner();
+
+ /// Add file extensions read by the reader
+ bool addDirectory(std::string& filename, std::map<std::string,std::string>& attr);
+
+ /// return for a file a 2D VTkImage
+ virtual vtkImageData* ReadImage(const std::string& filename);
+ /// Read the attributes for a file
+ virtual void ReadAttributes(const std::string& filename,
+ tree::AttributeMapType& attr);
+ void ReadAttributes2(const std::string& filename,
+ tree::AttributeMapType& attr);
+
+ std::string irclean(const std::string& str);
+ private:
+ const std::string GetStringValueFromTag( const gdcm::DataElement& ds);
+ vtkGDCMImageReader *mReader;
+ gdcm::Scanner mscan;
+ bool b_loaded;
+
+ };
+ //=====================================================================
+
+
+
+} // namespace creaImageIO
+
+
+
+#endif // #ifndef __creaImageIODicomScanner_h_INCLUDED__
--- /dev/null
+#include <creaImageIOExternalGimmick.h>
+
+
+vtkImageData* getImageDataDialog() {
+ // wxApp::OnInit();
+ #ifdef __WXGTK__
+ //See http://www.wxwindows.org/faqgtk.htm#locale
+ setlocale(LC_NUMERIC, "C");
+ #endif
+ wxInitAllImageHandlers();
+
+ creaImageIO::SetGimmickMessageLevel(5);
+ creaImageIO::SetGimmickDebugMessageLevel(0);
+
+ int min_dim = GIMMICK_2D_IMAGE_SELECTION;
+ int max_dim = GIMMICK_3D_IMAGE_SELECTION;
+ int output_dim = NATIVE;
+ int threads = 1;
+
+ creaImageIO::WxGimmickReaderDialog w(
+ 0,
+ -1,
+ "localdatabase_Descriptor.dscp",
+ "Local Database",
+ _T("Select image(s) - Gimmick! (c) CREATIS-LRMN 2008"),
+ wxDefaultPosition,
+ wxSize(1200,800),
+ min_dim,
+ max_dim,
+ output_dim,
+ threads);
+ w.ShowModal();
+
+ if (w.GetReturnCode() == wxID_OK)
+ {
+ std::cout << "$$$$ main : user clicked 'OK' $$$$"<<std::endl;
+ std::cout << "$$$$ selected files : "<<std::endl;
+ //Puts filenames
+ std::vector<std::string> s;
+ w.GetSelectedFiles(s);
+ std::vector<std::string>::iterator i;
+ for (i=s.begin();i!=s.end();++i)
+ {
+ std::cout << *i << std::endl;
+ }
+ std::cout << "$$$$ "<<std::endl;
+
+ //Puts images
+ std::vector<vtkImageData*> images;
+ w.GetSelectedImages(images,output_dim);
+ std::cout<<images.size()<<std::endl;
+
+ w.OnExit();
+ return images[0];
+
+ //crea::VtkBasicSlicer(images.front());
+ //images.front()->Delete();
+
+
+ }
+ else if (w.GetReturnCode() == wxID_CANCEL)
+ {
+ w.OnExit();
+ std::cout << "$$$$ main : user clicked 'CANCEL' $$$$"<<std::endl;
+ }
+ else
+ {
+ w.OnExit();
+ std::cout << "$$$$ main : dialog ended without return code ! $$$$"
+ <<std::endl;
+ }
+
+ // std::cout << "$$$$ main : deleting dialog"<<std::endl;
+ // delete w;
+ std::cout << "$$$$$$$$$$$$$$$$$$$$ main ended "<<std::endl;
+ return NULL;
+ }
+
+
--- /dev/null
+#ifndef __creaImageIOExternalGimmick_h_INCLUDED__
+#define __creaImageIOExternalGimmick_h_INCLUDED__
+
+#include "creaImageIOGimmickReaderDialog.h"
+#include <creaImageIOWxGimmickReaderDialog.h>
+
+ extern "C"
+ {
+ CREAIMAGEIO_EXPORT vtkImageData* getImageDataDialog();
+ }
+#endif
+
--- /dev/null
+
+#include <creaImageIOGimmick.h>
+
+#include <creaImageIOSystem.h>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+//#include "io.h"
+#ifndef PATH_MAX // If not defined yet : do it
+# define PATH_MAX 2048
+#endif
+#include <creaImageIOGimmick.h>
+#include <boost/algorithm/string.hpp>
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+
+
+namespace creaImageIO
+{
+ //==============================================================
+ Gimmick::Gimmick()
+ : mImageAdder(0)
+ {
+ RegisterGimmickMessageTypes();
+ mSettings=0;
+ mSynchronizer=0;
+ mLocalDescpName = "localdatabase_Descriptor.dscp";
+ mLocalDBName = "Local database";
+ }
+ //==============================================================
+
+
+ //==============================================================
+ Gimmick::~Gimmick()
+ {
+
+ if(mSettings!=0)
+ {
+ mSettings->writeSettingsFile();
+ delete mSettings;
+ }
+ if(mSynchronizer!=0)
+ {
+ delete mSynchronizer;
+ }
+ }
+ //==============================================================
+
+ //==============================================================
+ void Gimmick::Initialize(const std::string i_namedescp, const std::string i_namedb)
+ {
+ mLocalDescpName = i_namedescp;
+ mLocalDBName = i_namedb;
+ Initialize();
+ }
+
+ //==============================================================
+ void Gimmick::Initialize()
+ {
+ std::string i_nameDB = mLocalDBName;
+ // Create the UserSettings dir if does not exist
+ CreateUserSettingsDirectory();
+ // Sets the current directory to the home dir
+ mCurrentDirectory = GetHomeDirectory();
+ mSynchronizer= new Synchronizer(GetUserSettingsDirectory()+"Shared/gimmick/");
+
+ mSettings = new Settings(mCurrentDirectory);
+
+ std::string dbpath = GetLocalDatabasePath();
+
+ // Create or open local database
+ std::string dpath= mCurrentDirectory + "/.gimmick/Shared/gimmick/" + mLocalDescpName;
+
+ boost::algorithm::replace_all( dpath,
+ INVALID_FILE_SEPARATOR ,
+ VALID_FILE_SEPARATOR);
+ mLocalDatabase = createDB(i_nameDB, dpath, dbpath);
+ // Add it to the TreeHandlerMap
+ mTreeHandlerMap[i_nameDB] = mLocalDatabase;
+
+ //Add additional DB from user Settings
+ addDBSettings();
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+ // add DB to TreeHandler Map //
+ // @param i_name : DB name //
+ // @param i_location : DB location //
+ // return : - //
+ ////////////////////////////////////////////////////////////////////////
+ void Gimmick::addDB(const std::string &i_name,
+ const std::string &i_location)
+ {
+ if(mTreeHandlerMap.find(i_name) == mTreeHandlerMap.end())
+ {
+ mTreeHandlerMap[i_name] = new SQLiteTreeHandler(i_location);
+ mTreeHandlerMap[i_name]->Open(true);
+ mSettings->addDB(i_location);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // create a DB from a attributes descriptor file for medical images //
+ // @param i_name : DB name //
+ // @param i_locDesc : location of descriptor file //
+ // @param i_locDB : location of DB //
+ // return : the SQLiteTreeHandler object on DB //
+ /////////////////////////////////////////////////////////////////////////
+ SQLiteTreeHandler* Gimmick::createDB(const std::string &i_name,
+ const std::string &i_locDesc,
+ const std::string &i_locDB)
+ {
+ SQLiteTreeHandler* sqlTreeH( new SQLiteTreeHandler(i_locDB) );
+ // Create or open local database
+ if (! boost::filesystem::exists(i_locDB) )
+ {
+ std::string mess = "Local database '";
+ mess += i_locDB;
+ mess += "' does not exist : creating it";
+ GimmickMessage(1,mess<<std::endl);
+
+ // CREATING DB STRUCTURE
+ sqlTreeH->GetTree().GetDescriptor().createDescriptorfromFile(i_locDesc);
+ if ( ! sqlTreeH->Create(true) )
+ {
+ GimmickError("ERROR CREATING '"<<i_locDB<<"'");
+ }
+ sqlTreeH->SetAttribute(0,"Name",i_name);
+ }
+ else
+ {
+ /// Open and test it
+
+ GimmickDebugMessage(1,"Opening local database '" <<i_locDB<< "' " << std::endl);
+ if ( !sqlTreeH->Open(true) )
+ {
+ GimmickError("ERROR OPENING '"<<i_locDB<<"'");
+ }
+ }
+ return sqlTreeH;
+ }
+
+ //==============================================================
+ void Gimmick::Finalize()
+ {
+ if(mTreeHandlerMap.size() >0)
+ {
+ // delete SQLiteTreeHandler Object
+ for( TreeHandlerMapType::const_iterator it = mTreeHandlerMap.begin();
+ it!= mTreeHandlerMap.end();
+ ++it)
+ {
+ delete it->second;
+ }
+ }
+ }
+ //==============================================================
+
+ //================================================================
+ // file separator
+#if defined(_WIN32)
+#define VALID_FILE_SEPARATOR "\\"
+#define INVALID_FILE_SEPARATOR "/"
+#else
+#define INVALID_FILE_SEPARATOR "\\"
+#define VALID_FILE_SEPARATOR "/"
+#endif
+ //================================================================
+
+ //================================================================
+ const std::string& Gimmick::GetHomeDirectory()
+ {
+ if (mHomeDirectory.size()==0)
+ {
+#if defined(__GNUC__)
+ mHomeDirectory = getenv("HOME");
+#elif defined(_WIN32)
+ mHomeDirectory = getenv("USERPROFILE");
+#endif
+ }
+ return mHomeDirectory;
+ }
+ //================================================================
+ const std::string& Gimmick::GetUserSettingsDirectory()
+ {
+ if (mUserSettingsDirectory.size()==0)
+ {
+ mUserSettingsDirectory = GetHomeDirectory();
+ mUserSettingsDirectory += "/.gimmick/";
+ boost::algorithm::replace_all( mUserSettingsDirectory,
+ INVALID_FILE_SEPARATOR ,
+ VALID_FILE_SEPARATOR);
+ }
+ return mUserSettingsDirectory;
+ }
+ //================================================================
+
+
+ //================================================================
+ const std::string& Gimmick::GetLocalDatabasePath()
+ {
+ if (mLocalDatabasePath.size()==0)
+ {
+ mLocalDatabasePath = GetUserSettingsDirectory();
+ mLocalDatabasePath += "Shared/gimmick/";
+ mLocalDatabasePath += mLocalDBName;
+ mLocalDatabasePath +=".sqlite3";
+ boost::algorithm::replace_all( mLocalDatabasePath,
+ INVALID_FILE_SEPARATOR ,
+ VALID_FILE_SEPARATOR);
+ }
+ return mLocalDatabasePath;
+ }
+
+ //========================================================================
+
+ //========================================================================
+ void Gimmick::CreateUserSettingsDirectory()
+ {
+
+ // std::string st("C:/Documents and Settings/cervenansky/.gimmick/");
+ // boost::algorithm::replace_all( st,
+ // INVALID_FILE_SEPARATOR ,
+ // VALID_FILE_SEPARATOR);
+ //const boost::filesystem::path mpath(st);
+//C:\Documents and Settings\cervenansky\.gimmick");
+ //if ( !boost::filesystem::exists( path ) ) return ;
+ // boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end
+ // for ( boost::filesystem::directory_iterator itr( path ); itr != end_itr; ++itr )
+ // {
+ //// If is directory & recurse : do recurse
+ // if ( boost::filesystem::is_directory(itr->status()) )
+ // return;
+ // }
+
+ //JCP settings dir 02/10/2009
+ const std::string settingsdirectory = GetUserSettingsDirectory();
+ //boost::algorithm::replace_all( mUserSettingsDirectory,
+ // INVALID_FILE_SEPARATOR ,
+ // VALID_FILE_SEPARATOR);
+;//("E:\frederic");
+ //("C:\\Documents and Settings\\cervenansky\\.gimmick\\"); // settingsdirectory );
+ bool isdir = false;
+ isdir = boost::filesystem::is_directory(settingsdirectory); // settingsdirectory );
+ if (! isdir )
+ {
+ GimmickMessage(1,"Directory '"<<GetUserSettingsDirectory()<<"' "
+ << "does not exist : creating it"<<std::endl);
+
+ if ( ! boost::filesystem::create_directory( GetUserSettingsDirectory() ) )
+ {
+ GimmickError("ERROR CREATING '"<<GetUserSettingsDirectory()<<"'");
+ }
+ }
+
+ std::string setDir=GetUserSettingsDirectory();
+ boost::algorithm::replace_all( setDir,
+ INVALID_FILE_SEPARATOR ,
+ VALID_FILE_SEPARATOR);
+ setDir+="Shared/";
+ boost::filesystem::create_directory( setDir );
+ setDir+="gimmick/";
+ boost::filesystem::create_directory( setDir );
+ setDir+=mLocalDescpName;
+
+ if(!boost::filesystem::is_regular(setDir))
+ {
+ char name[PATH_MAX];
+ crea::System::GetAppPath(name,PATH_MAX);
+ std::cout<<name<<std::endl;
+
+ std::string path=name;
+ path=path.substr(0,path.size()-1);
+ path=path.substr(0,path.find_last_of("/"));
+ //Creating directories
+
+// The following stuff works on Linux, NOT CHECKED on Windows // JPR
+
+#if defined(_WIN32)
+ path+="/bin/Shared/gimmick/";
+#endif
+
+#if defined (LINUX)
+ path+="/../share/gimmick/";
+#endif
+#if defined(__APPLE__)
+ path+="/../../../../share/gimmick/";
+#endif
+
+
+path+= mLocalDescpName;
+
+ std::cout <<"From: " << path <<std::endl;
+ std::cout <<"To: " << setDir <<std::endl;
+ boost::algorithm::replace_all( path,
+ INVALID_FILE_SEPARATOR ,
+ VALID_FILE_SEPARATOR);
+ boost::filesystem::copy_file(path,setDir);
+ }
+
+ }
+ //========================================================================
+
+
+ //========================================================================
+ /// Sets message level
+ void Gimmick::SetMessageLevel(int l)
+ {
+ SetGimmickMessageLevel(l);
+ }
+ //========================================================================
+
+ //========================================================================
+ /// Sets message level
+ void Gimmick::SetDebugMessageLevel(int l)
+ {
+ SetGimmickDebugMessageLevel(l);
+ }
+ //========================================================================
+
+ //========================================================================
+ /// Returns the tree handler with the given name
+ TreeHandler* Gimmick::GetTreeHandler(const std::string& name) const
+ {
+ TreeHandlerMapType::const_iterator i;
+ i = GetTreeHandlerMap().find(name);
+ if ( i == GetTreeHandlerMap().end() )
+ {
+ GimmickError("TreeHandler '"<<name<<"' does not exist");
+ }
+ return i->second;
+ }
+
+ //========================================================================
+ /// Add the files to the tree handler
+ void Gimmick::AddFiles(const std::string& d,
+ const std::vector<std::string>& filenames)
+ {
+ GimmickMessage(2,"Adding files to '"<<d<<"'"<<std::endl);
+
+ mImageAdder.SetCurrentDatabase(d);
+ mImageAdder.SetTreeHandler(GetTreeHandler(d));
+ mImageAdder.SetSynchronizer(mSynchronizer);
+ mImageAdder.AddFiles(filenames);
+ }
+ //========================================================================
+
+ //========================================================================
+ /// Add a dir to the local database
+ void Gimmick::AddDir(const std::string& d, const std::string& f,
+ bool recurse)
+ {
+ GimmickMessage(2,"Adding dir '"<<f<<"' to '"<<d<<"' recurse:"
+ <<recurse<<std::endl);
+
+ TreeHandler * handler=GetTreeHandler(d);
+ mImageAdder.SetCurrentDatabase(d);
+ mImageAdder.SetTreeHandler(handler);
+ mImageAdder.SetSynchronizer(mSynchronizer);
+ mImageAdder.AddDirectory(f,recurse);
+ }
+
+ //========================================================================
+
+ //========================================================================
+ void Gimmick::RemoveFile(const std::string& d,
+ tree::Node* node)
+ {
+ mImageAdder.SetCurrentDatabase(d);
+ mImageAdder.SetSynchronizer(mSynchronizer);
+ mImageAdder.RemoveFile(node);
+ }
+ //========================================================================
+
+ //void Gimmick::Anonymize(std::vector<std::string>i_files) { }
+ //========================================================================
+
+ void Gimmick::CopyFiles(const std::vector<std::string>& filenames, const std::string& d )
+ {
+ TreeHandler * handler=GetTreeHandler(d);
+ mImageAdder.SetCurrentDatabase(d);
+ mImageAdder.SetTreeHandler(handler);
+ mImageAdder.SetSynchronizer(mSynchronizer);
+ mImageAdder.CopyFiles(filenames, mSettings->getValue(SETTINGS_COPY_PATH));
+ }
+
+ //========================================================================
+
+ std::string Gimmick::Synchronize(const std::string& d, bool repair, bool checkAttributes)
+ {
+ TreeHandler * handler=GetTreeHandler(d);
+ mImageAdder.SetCurrentDatabase(d);
+ mImageAdder.SetTreeHandler(handler);
+ mImageAdder.SetSynchronizer(mSynchronizer);
+ return mImageAdder.Synchronize(repair, checkAttributes);
+ }
+
+ //========================================================================
+ ///
+ void Gimmick::Print(const std::string& d)
+ {
+ GetTreeHandler(d)->GetTree().Print();
+ }
+ //========================================================================
+
+ void Gimmick::GetSetting(const std::string& name, std::string& value)
+ {
+ value = mSettings->getValue(name);
+ }
+ //========================================================================
+
+ //========================================================================
+
+ void Gimmick::GetAttributes(const std::string& d,
+ const std::string& filename,
+ const std::vector<std::string>& params,
+ std::vector<std::string>& results)
+ {
+ TreeHandler * handler=GetTreeHandler(d);
+ mImageAdder.SetCurrentDatabase(d);
+ mImageAdder.SetTreeHandler(handler);
+ mImageAdder.SetSynchronizer(mSynchronizer);
+ mImageAdder.GetAttributes(params, filename, results);
+ }
+ //========================================================================
+
+ //========================================================================
+
+ void Gimmick::UpdateSetting(const std::string& name, const std::string& value)
+ {
+ mSettings->updateSetting(name,value);
+ mSettings->writeSettingsFile();
+ }
+ //========================================================================
+
+ void Gimmick::DeleteDrive(const std::string& drive)
+ {
+ for( TreeHandlerMapType::const_iterator it = mTreeHandlerMap.begin();
+ it!= mTreeHandlerMap.end();
+ ++it)
+ {
+ mImageAdder.SetTreeHandler(it->second);
+ mImageAdder.DeleteDriveFromMainDB(drive);
+ }
+ mImageAdder.SetSynchronizer(mSynchronizer);
+ mImageAdder.DeleteDriveFromOtherDB(drive);
+ }
+
+ //========================================================================
+ void Gimmick::EditField(tree::Node* node, const std::string& d, const std::string& name, const std::string& key, const std::string& val)
+ {
+ TreeHandler * handler=GetTreeHandler(d);
+ mImageAdder.SetCurrentDatabase(d);
+ mImageAdder.SetTreeHandler(handler);
+ mImageAdder.EditField(node,name,key,val);
+ }
+ //========================================================================
+
+ ////////////////////////////////////////////////////////////////////////
+ // add DB from Settings file //
+ // @param : - //
+ // return : - //
+ ////////////////////////////////////////////////////////////////////////
+ void Gimmick::addDBSettings()
+ {
+
+ std::string pathSettings = mSettings->getValue(SETTINGS_DBPATH);
+
+ // split to find all paths
+ std::vector<std::string> paths;
+ std::string separator = ";";
+ std::string::size_type last_pos = pathSettings.find_first_not_of(separator);
+ //find first separator
+ std::string::size_type pos = pathSettings.find_first_of(separator, last_pos);
+ while(std::string::npos != pos || std::string::npos != last_pos)
+ {
+ paths.push_back(pathSettings.substr(last_pos, pos - last_pos));
+ last_pos = pathSettings.find_first_not_of(separator, pos);
+ pos = pathSettings.find_first_of(separator, last_pos);
+ }
+
+ std::vector<std::string>::iterator it_path = paths.begin();
+ for(; it_path != paths.end(); ++it_path)
+ {
+ pos = it_path->find_last_of("\\");
+ last_pos = it_path->find_last_of(".");
+ std::string name = it_path->substr(pos +1, last_pos -pos-1 );
+ addDB(name, it_path->c_str());
+ }
+ }
+}
--- /dev/null
+#ifndef __creaImageIOGimmick_h_INCLUDED__
+#define __creaImageIOGimmick_h_INCLUDED__
+
+#include <creaImageIOSQLiteTreeHandler.h>
+#include <creaImageIOTreeHandlerImageAdder.h>
+#include <creaImageIOSynchron.h>
+#include <creaImageIOSettings.h>
+
+
+
+#ifdef _DEBUG
+#include <crtdbg.h>
+#define DEBUG_NEW new(_NORMAL_BLOCK ,__FILE__, __LINE__)
+#else
+#define DEBUG_NEW new
+#endif
+// Only when asked
+/*
+#ifdef TRACKING_MEMORY_LEAKS
+#ifdef WIN32
+#pragma warning(disable : 4291)
+#endif
+
+void * operator new( size_t size, int line, const char *file );
+void * operator new[]( size_t size, int line, const char *file );
+void operator delete( void *p );
+void operator delete[]( void *p );
+
+#ifdef OMISCID_NEW
+#undef OMISCID_NEW
+#endif
+
+#define OMISCID_NEW new( __LINE__, __FILE__ )
+
+#else // TRACKING_MEMORY_LEAKS is not defined
+
+#define OMISCID_NEW new
+
+#endif*/
+
+
+namespace creaImageIO
+{
+ /**
+ * \defgroup Controller Controller
+ */
+ /**
+ * \defgroup View View
+ */
+ /**
+ * \defgroup Model Model
+ */
+ /**
+ * \defgroup GUI Top level graphical user interfaces
+ */
+ /**
+ * \defgroup IO Image I/O classes
+ */
+ /**
+ * \defgroup Tree Attributed tree management
+ */
+ /**
+ * \defgroup Previewer Preview related
+ */
+
+ /**
+ * \ingroup Controller
+ */
+
+ //=======================================================================
+ /// Central controler of the gimmick application
+ class CREAIMAGEIO_EXPORT Gimmick
+ {
+ public:
+ /// Ctor
+ Gimmick();
+ /// Dtor
+ ~Gimmick();
+
+ ///
+ typedef TreeHandlerImageAdder::Progress AddProgress;
+ typedef TreeHandlerImageAdder::ProgressCallbackType AddProgressCallbackType;
+
+ /// Adds the function f to the list of functions to call
+ /// when addition operations progres.
+ /// f is of type AddProgressCallbackType which is:
+ /// void (*AddProgressCallbackType)(AddProgress&)
+ /// To pass a member function 'f' of an instance 'c' of a class 'C'
+ /// as callback you have to 'bind' it, i.e. call:
+ /// ConnectAddProgressObserver ( boost::bind( &C::f , c, _1 ) );
+ void ConnectAddProgressObserver( AddProgressCallbackType callback )
+ { mImageAdder.ConnectProgressObserver(callback); }
+
+ ///
+ const AddProgress& GetAddProgress() const { return mImageAdder.GetProgress(); }
+
+ /// Initializes with default values (read/creates databases, etc.)
+ void Initialize();
+
+ /// Initializes with the local database descriptor in the path given (read/creates databases, etc.)
+ void Initialize(const std::string namedescp, const std::string namedb = "Local Database");
+
+ /// Finalize (closes databases, etc.)
+ void Finalize();
+
+ /// Sets level for messages "Gimmick!"
+ static void SetMessageLevel(int level);
+ /// Sets level for debug messages "Gimmick! DEBUG"
+ static void SetDebugMessageLevel(int level);
+
+ /// Type of map from TreeHandler name to TreeHandler*
+ typedef std::map<std::string, TreeHandler*> TreeHandlerMapType;
+
+ typedef std::map<std::string, TreeHandler*>::const_iterator ItTreeHandlerMap;
+
+ /// Returns the TreeHandlerMap (ref)
+ TreeHandlerMapType& GetTreeHandlerMap() { return mTreeHandlerMap; }
+
+ /// Returns the TreeHandlerMap (const ref)
+ const TreeHandlerMapType& GetTreeHandlerMap() const
+ { return mTreeHandlerMap; }
+
+ /// Add the files to the given TreeHandler
+ void AddFiles(const std::string& handler,
+ const std::vector<std::string>& filenames);
+
+ /// Add a dir to the given TreeHandler
+ void AddDir(const std::string& handler, const std::string& path,
+ bool recurse);
+
+ /// Removes a file from the given TreeHandler
+ void RemoveFile(const std::string& d,
+ tree::Node* filename);
+
+ ///Deletes the given drive name from the databases
+ void DeleteDrive(const std::string& drive);
+
+
+ /// Copies the files into the local directory
+ void CopyFiles(const std::vector<std::string>& filenames, const std::string& d );
+
+
+ ///Synchronizes the loaded data with the database d. If repair is true the database will be updated, otherwise
+ ///only a warning sign will be issued
+ std::string Synchronize(const std::string& d, bool repair, bool checkAttributes);
+
+ /// Prints the tree handled by the handler
+ void Print(const std::string& handler);
+
+ ///create a DB from a given descriptor file and for a specific location
+ SQLiteTreeHandler* createDB(const std::string &i_name,
+ const std::string &i_locDesc,
+ const std::string &i_locDB);
+
+ /// add an existent DB
+ void addDB(const std::string &i_nameDB, const std::string &i_locationDB);
+
+ /// Returns the TreeHandler with a given name
+ TreeHandler* GetTreeHandler(const std::string& name) const;
+
+ ///
+ SQLiteTreeHandler* GetLocalDatabase() { return mLocalDatabase; }
+
+ const SQLiteTreeHandler* GetLocalDatabase() const { return mLocalDatabase; }
+
+ ///Returns the given setting value for the given setting parameter
+ void GetSetting(const std::string& name, std::string& value);
+
+ ///Updates the settings file
+ void UpdateSetting(const std::string& name, const std::string& value);
+
+ /// add DB from Settings file
+ void addDBSettings();
+
+ ///Edits the field described by the name and key provided with the value given
+ void EditField(tree::Node* node, const std::string& d, const std::string& name, const std::string& key, const std::string& val);
+
+ /// Returns the attributes in results described in params
+ void GetAttributes(const std::string& d,
+ const std::string& filename,
+ const std::vector<std::string>& params,
+ std::vector<std::string>& results);
+
+ ///
+ const std::string& GetHomeDirectory();
+ const std::string& GetUserSettingsDirectory();
+ void CreateUserSettingsDirectory();
+ const std::string& GetLocalDatabasePath();
+
+
+
+ //=============================================
+ private:
+ SQLiteTreeHandler* mLocalDatabase;
+ TreeHandlerMapType mTreeHandlerMap;
+ Synchronizer* mSynchronizer;
+
+ std::string mCurrentDirectory;
+ std::string mHomeDirectory;
+ std::string mUserSettingsDirectory;
+ std::string mLocalDatabasePath;
+ Settings *mSettings;
+ TreeHandlerImageAdder mImageAdder;
+ std::string mLocalDBName;
+ std::string mLocalDescpName;
+ };
+ // EO class Gimmick
+ //=======================================================================
+
+
+} // EO namespace creaImageIO
+
+// EOF
+#endif
+
+
--- /dev/null
+#include <creaImageIOGimmickReaderDialog.h>
+#include <creaImageIOWxGimmickReaderDialog.h>
+
+namespace creaImageIO
+{
+
+ bool GimmickReaderDialog(std::vector<vtkImageData*>& images,
+ const std::string i_namedescp ,
+ const std::string i_namedb ,
+ const std::string& title,
+ int posx,
+ int posy,
+ int sizex,
+ int sizey,
+ int image_min_type,
+ int image_max_type,
+ int image_out_dim,
+ int nb_threads)
+ {
+
+
+ creaImageIO::WxGimmickReaderDialog w(0,
+ -1,
+ i_namedescp,
+ i_namedb,
+ crea::std2wx(title),
+ wxPoint(posx,posy),
+ wxSize(sizex,sizey),
+ image_min_type,
+ image_max_type,
+ nb_threads);
+ w.ShowModal();
+
+ if (w.GetReturnCode() == wxID_OK)
+ {
+ w.GetSelectedImages(images,image_out_dim);
+ return true;
+ }
+ else if (w.GetReturnCode() == wxID_CANCEL)
+ {
+ return false;
+ }
+ else
+ {
+ std::cout << "!! ERROR : GimmickReaderDialog : dialog ended without return code !"
+ <<std::endl;
+ return false;
+ }
+
+
+ }
+
+}
--- /dev/null
+#ifndef __creaImageIOGimmickReaderDialog_h_INCLUDED__
+#define __creaImageIOGimmickReaderDialog_h_INCLUDED__
+
+#include "creaImageIOSystem.h"
+#include <vtkImageData.h>
+#include <vector>
+
+ extern "C"
+ {
+ CREAIMAGEIO_EXPORT vtkImageData* getImageDataDialog();
+ }
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup GUI
+ * \brief Pops up a WxGimmickReaderDialog
+ * returns true if the user clicked 'Ok', false if 'Cancel'
+ * and fills the vector of images
+ */
+ bool CREAIMAGEIO_EXPORT GimmickReaderDialog
+ (std::vector<vtkImageData*>& images,
+ const std::string i_namedescp ,
+ const std::string i_namedb = "Local Database",
+ const std::string& title = "Select images",
+ int posx = 0, int posy = 0,
+ int sizex = 1200, int sizey = 800,
+ int image_min_type = 2,
+ int image_max_type = 3,
+ int image_out_dim = 2,
+ int nb_threads = 1);
+
+}
+
+#endif
--- /dev/null
+#include <creaImageIOGimmickView.h>
+#include <creaImageIOSystem.h>
+#include "boost/filesystem.hpp"
+
+#if defined(USE_GDCM)
+#include <gdcmGlobal.h>
+#include <gdcmSerieHelper.h>
+#include <vtkGdcmReader.h>
+#endif
+
+#if defined(USE_GDCM2)
+#include <vtkGDCMImageReader.h>
+#endif
+
+
+namespace fs = boost::filesystem;
+namespace creaImageIO
+{
+
+ ///Class used to represent the actual state of the image selected and to perform comparisons on its values
+ class ImageExtent
+ {
+ public:
+ ImageExtent(const std::string& x, const std::string& y, const std::string& z, const std::string& t)
+ {
+ sscanf(x.c_str(),"%d",&mExtent[0]);
+ sscanf(y.c_str(),"%d",&mExtent[1]);
+ sscanf(z.c_str(),"%d",&mExtent[2]);
+ sscanf(t.c_str(),"%d",&mExtent[3]);
+ if(x==""){mExtent[0]=1;}
+ if(y==""){mExtent[1]=1;}
+ if(z==""){mExtent[2]=1;}
+ if(t==""){mExtent[3]=1;}
+
+ if (mExtent[3]>1) mDim=4;
+ else if (mExtent[2]>1) mDim=3;
+ else if (mExtent[1]>1) mDim=2;
+ else if (mExtent[0]>1) mDim=1;
+ else mDim=0;
+ }
+
+
+ ///Clears the extent
+ void Clear() { mExtent[0] = mExtent[1] = mExtent[2] = mExtent[3] = 1; }
+
+ ///Returns true if the two extents are compatible
+ bool IsCompatible( const ImageExtent& );
+
+ ///Adds the extent passed as a parameter to the current extent
+ void Add ( const ImageExtent& );
+
+ ///Returns the ieth position of the extent
+ int Get(int i) { return mExtent[i]; }
+
+ ///Returns the dimension of the current image
+ void SetDimension(int dim) { mDim=dim; }
+
+ ///Returns the dimension of the current image
+ int GetDimension() { return mDim; }
+
+ private:
+ int mExtent[4];
+ int mDim;
+ };
+
+ //======================================================================
+
+ //======================================================================
+ // CTor
+ GimmickView::GimmickView(boost::shared_ptr<Gimmick> gimmick, int threads)
+ : mGimmick(gimmick),
+ mReader(threads)
+ {
+ GimmickDebugMessage(1,"GimmickView::GimmickView"
+ <<std::endl);
+ // Anciently started the threads ...
+ // Threads now automatically start at first image request
+ //mReader.Start();
+
+ }
+ //======================================================================
+
+ //======================================================================
+ /// Destructor
+ GimmickView::~GimmickView()
+ {
+ GimmickDebugMessage(1,"GimmickView::~GimmickView"
+ <<std::endl);
+ }
+ //======================================================================
+
+ //======================================================================
+ /// Initializes the view :
+ /// Creates the TreeViews for all the TreeHandler of the Controller
+ ///
+ void GimmickView::Initialize()
+ {
+ mReaderStarted=false;
+ }
+ //======================================================================
+
+ //======================================================================
+ /// Finalize
+ void GimmickView::Finalize()
+ {
+ }
+
+ //======================================================================
+
+ //======================================================================
+ /// Create the tree views
+ void GimmickView::CreateTreeViews()
+ {
+ GimmickMessage(2,"Creating the tree views"<<std::endl);
+ Gimmick::TreeHandlerMapType::const_iterator i;
+ for (i = mGimmick->GetTreeHandlerMap().begin();
+ i!= mGimmick->GetTreeHandlerMap().end();
+ ++i)
+ {
+ this->CreateTreeView(i->second);
+ }
+ }
+
+ /// Create a tree view with a given name
+ void GimmickView::CreateSingleTreeView(std::string &i_name)
+ {
+ this->CreateTreeView(mGimmick->GetTreeHandlerMap()[i_name]);
+
+ }
+
+
+ //======================================================================
+
+ //======================================================================
+ /// Updates the TreeView of given name from level l to bottom
+ /// (calls the virtual method TreeView::Update())
+ void GimmickView::UpdateTreeViewLevel(const std::string& t, int l)
+ {
+ TreeViewMapType::iterator i;
+ i = GetTreeViewMap().find(t);
+ if ( i == GetTreeViewMap().end() )
+ {
+ GimmickError("INTERNAL ERROR : GimmickView::UpdateTreeView : '"
+ <<t<<"' is not in TreeViewMap");
+ }
+ i->second->UpdateLevel(l);
+ }
+
+ //======================================================================
+ /// Clears the status and begins a new selection process
+ void GimmickView::ResetExtent()
+ {
+ if(mImageExtent!=0)
+ {
+ mImageExtent.reset();
+ }
+ valid=true;
+ }
+
+ //======================================================================
+
+ //======================================================================
+ bool ImageExtent::IsCompatible(const ImageExtent& ie)
+ {
+ bool compatible=true;
+ ImageExtent * extent= (ImageExtent*)&ie;
+ if((*extent).Get(0)!=Get(0)
+ || (*extent).Get(1)!=Get(1))
+ {
+ compatible=false;
+ }
+ return compatible;
+ }
+
+ //======================================================================
+
+ //======================================================================
+ void ImageExtent::Add(const ImageExtent& ie)
+ {
+ ImageExtent * extent= (ImageExtent*)&ie;
+ mExtent[2]+=(*extent).Get(2);
+ if(mExtent[2]>1)
+ {
+ SetDimension(3);
+ }
+ }
+
+ //======================================================================
+ /// No selected image
+ bool GimmickView::NoValidateSelected ()
+ {
+ GimmickDebugMessage(2,"Validating selected"<<std::endl);
+ std::string mMessage;
+ mMessage="Cannot have 0 images selected!";
+ valid=false;
+ modifyValidationSignal(valid);
+ SetMessage(mMessage);
+ return valid;
+ }
+
+ //======================================================================
+ ///Validates the dimension compliance of the images with the maximum and
+ ///minimum given, and between their sizes
+ bool GimmickView::ValidateSelected (tree::Node* sel, int min_dim, int max_dim)
+ {
+ GimmickDebugMessage(2,"Validating selected"<<std::endl);
+ std::string mMessage;
+
+ if(sel==0)
+ {
+ mMessage="Cannot have 0 images selected!";
+ valid=false;
+ }
+ else
+ {
+ boost::shared_ptr<ImageExtent> ie=boost::shared_ptr<ImageExtent>(new ImageExtent((*sel).GetAttribute("D0028_0010"),
+ (*sel).GetAttribute("D0028_0011"),
+ (*sel).GetAttribute("D0028_0012"),
+ ""));
+ if(mImageExtent==0)
+ {
+ mImageExtent=ie;
+ if((mImageExtent->Get(min_dim-1)<2)||(mImageExtent->Get(max_dim)>1))
+ {
+ valid=false;
+ }
+ else
+ {
+ std::stringstream out;
+ out << mImageExtent->GetDimension() << "D image " << mImageExtent->Get(0) << "x"<< mImageExtent->Get(1) << "x"<< mImageExtent->Get(2) <<" selected";
+ mMessage = out.str();
+ mImageExtent->SetDimension(2);
+ valid=true;
+ }
+ }
+ else
+ {
+ if(mImageExtent->IsCompatible(*ie))
+ {
+ if(mImageExtent->GetDimension()==max_dim && mImageExtent->Get(max_dim)>2)
+ {
+ std::stringstream out;
+ out<<"Cannot add this image to selection : would result in a "<<mImageExtent->GetDimension()+1<<"D image!";
+ mMessage=out.str();
+ valid=false;
+ }
+ else if(max_dim<3)
+ {
+ std::stringstream out;
+ out<<"Selecting "<<mImageExtent->GetDimension()<<"D images is not allowed !";
+ mMessage=out.str();
+ valid=false;
+ }
+ else if(min_dim==3 && (ie->Get(2)+mImageExtent->Get(2))<2)
+ {
+ std::stringstream out;
+ out << "Cannot build the selection as it would result in a ";
+ out << mImageExtent->GetDimension();
+ out << "D image, and the minimum is ";
+ out << min_dim;
+ out << "D!";
+ mMessage=out.str();
+ valid=false;
+ }
+ else
+ {
+ mImageExtent->Add(*ie);
+ std::stringstream out;
+ out << mImageExtent->GetDimension() << "D image " << mImageExtent->Get(0) << "x"<< mImageExtent->Get(1) << "x"<< mImageExtent->Get(2) <<" selected";
+ mMessage = out.str();
+ }
+ }
+ else
+ {
+ mMessage="The selected images are not compatible.";
+ valid=false;
+ }
+ }
+ }
+
+ modifyValidationSignal(valid);
+ SetMessage(mMessage);
+ return valid;
+ }
+
+ //======================================================================
+ void GimmickView::modifyValidationSignal(bool ivalid)
+ {
+ mValidationSignal(ivalid);
+ }
+
+ //======================================================================
+ ///Reads Images (Non Threaded)
+void GimmickView::ReadImagesNotThreaded(std::vector<vtkImageData*>& s, std::vector<std::string> im, int dimension)
+{
+ mReader.Stop();
+/* remember!
+
+#define GIMMICK_NO_IMAGE_SELECTION 0
+#define GIMMICK_2D_IMAGE_SELECTION 2
+#define GIMMICK_3D_IMAGE_SELECTION 3
+#define GIMMICK_4D_IMAGE_SELECTION 4
+
+#define NATIVE 0
+#define _2D 2
+#define _3D 3
+
+*/
+ // Create the output data
+ if (im.size()==1)
+ {
+ vtkImageData * out=vtkImageData::New();
+ out->ShallowCopy(mReader.GetImage(im.front()));
+ s.push_back(out);
+ }
+ else if (im.size()>1) // Test inutile ? JPR
+ {
+ vtkImageData* first = mReader.GetImage( im.front());
+ if (dimension == 2)
+ {
+ // n3D
+ std::vector<std::string>::iterator it;
+ for (it=im.begin(); it!=im.end(); ++it)
+ {
+ vtkImageData* out = vtkImageData::New();
+ out->ShallowCopy(mReader.GetImage(*it));
+ s.push_back(out);
+ }
+ }
+ else
+ {
+ // n*2D to 3D
+ vtkImageData* out = vtkImageData::New();
+// out->CopyStructure(first);
+ out->SetScalarType(first->GetScalarType());
+ out->SetNumberOfScalarComponents(first->GetNumberOfScalarComponents());
+ int ext[6];
+ //first->GetExtent(ext); // JPR
+ first->GetWholeExtent(ext); // renvoie egalement 0,0 en Z // JPR
+
+ if(ext[5] == 0)
+ {
+ ext[5] = im.size()-1;
+ }
+ else
+ {
+ ext[5] = ext[5] * im.size()-1; // to deal with multiframes - JPR
+ }
+ out->SetExtent(ext);
+
+ // LG : TODO : Z Spacing ?
+
+ int dim[3];
+ first->GetDimensions(dim);
+
+ out->SetDimensions(dim[0], dim[1], im.size() );
+ out->AllocateScalars();
+ out->Update();
+
+ unsigned long imsize = dim[0] * dim[1];
+ imsize = imsize * dim[2] ; // deal with multiframes // JPR
+
+
+//EED 03-11-2009
+ // differents formats char , short, etc...
+ // differents components 1..3 ex. jpg ->RGB 3
+ imsize = imsize * first->GetScalarSize() * first->GetNumberOfScalarComponents();
+
+
+ // Order the file name vector
+
+ double spc[3];
+ first->GetSpacing(spc);
+ spc[2]=OrderTheFileNameVector(im);
+
+ out->SetSpacing(spc);
+
+ int slice = 0;
+ std::vector<std::string>::iterator it;
+
+ for (it=im.begin(); it!=im.end(); ++it)
+ {
+ vtkImageData* cur = mReader.GetImage( (*it) );
+ memcpy(out->GetScalarPointer(0,0,slice), cur->GetScalarPointer(0,0,0), imsize);
+ slice++;
+ }
+ s.push_back(out);
+
+ } // dimension == 3
+
+ } // size >1
+
+}
+ //======================================================================
+
+#if defined(USE_GDCM)
+ double GimmickView::OrderTheFileNameVector(std::vector<std::string> &im)
+{
+ double spacing=1;
+ typedef std::vector<GDCM_NAME_SPACE::File* > FileList;
+ FileList fileVector;
+ //GDCM_NAME_SPACE::File *f = GDCM_NAME_SPACE::File::New();
+ GDCM_NAME_SPACE::SerieHelper *sh = GDCM_NAME_SPACE::SerieHelper::New();
+ std::vector<std::string> lstAux;
+ std::vector<std::string>::iterator it;
+ for (it=im.begin(); it!=im.end(); ++it)
+ {
+ ///\TODO liberer les GDCM_NAME_SPACE::File a la fin! // JPR
+ GDCM_NAME_SPACE::File *f = GDCM_NAME_SPACE::File::New();
+ f->SetFileName(*it);
+ f->Load();
+ if (f->IsReadable())
+ {
+ fileVector.push_back(f);
+ } else {
+ lstAux.push_back(*it);
+ }
+ } // for
+
+
+ if ((fileVector.size()>1) && (sh->IsCoherent( &fileVector )))
+ {
+ sh->OrderFileList(&fileVector);
+ spacing= sh->GetZSpacing();
+ im.clear();
+ int i;
+ for (i=0; i<fileVector.size(); i++)
+ {
+ im.push_back( (fileVector[i])->GetFileName() );
+ }
+ for (i=0; i<lstAux.size(); i++)
+ {
+ im.push_back( lstAux[i] );
+ }
+ }else {
+ std::sort( im.begin(), im.end() );
+ }
+
+ return spacing;
+}
+
+
+#endif
+
+#if defined(USE_GDCM2)
+ // TO DO
+ double GimmickView::OrderTheFileNameVector(std::vector<std::string> &im)
+ {
+ return 1;
+ }
+#endif
+//======================================================================
+
+
+void GimmickView::ReadImagesNotThreadedInVector(std::vector<vtkImageData*>& s, std::vector<std::string> im, int dimension)
+{
+ // Create the output data
+ if (im.size()==1)
+ {
+ // Only one image : give it
+ vtkImageData* out = vtkImageData::New();
+ GimmickDebugMessage(3, "State Check: Full Filename: "
+ <<im.front()
+ <<std::endl);
+ out->ShallowCopy(mReader.GetImage(im.front()));
+ s.push_back( out );
+ }
+ else if (im.size()>1) // Test inutile ? JPR
+ {
+ vtkImageData* first = mReader.GetImage( im.front());
+ if (dimension == 2)
+ {
+ // n3D
+ std::vector<std::string>::iterator it;
+ for (it=im.begin(); it!=im.end(); ++it)
+ {
+ vtkImageData* out = vtkImageData::New();
+ out->ShallowCopy(mReader.GetImage(*it));
+ s.push_back(out);
+ }
+ }
+ else
+ {
+ // n2D to 3D // NO!
+ // n *2D + T in a vector :
+
+ std::vector<std::string>::iterator it;
+ for (it=im.begin(); it!=im.end(); ++it)
+ {
+ vtkImageData* out = mReader.GetImage( (*it));
+ s.push_back(out);
+ }
+ }
+ }
+}
+ //======================================================================
+
+ //======================================================================
+ ///Requests the reading of an image
+ void GimmickView::RequestReading(tree::Node* n,
+ int prio, int selection_index, boost::shared_ptr<ImagePointerHolder> p)
+ {
+ if(!mReaderStarted)
+ {
+ mReader.Start();
+ mReaderStarted=true;
+ }
+ ImageEventType t(n,selection_index);
+ t.pointerHolder = p;
+ mImageEventMap[n->GetAttribute("FullFileName")] = t;
+ mReader.Request(this,n->GetAttribute("FullFileName"),prio);
+ }
+ //======================================================================
+
+ //======================================================================
+ void GimmickView::
+ OnMultiThreadImageReaderEvent(const std::string& filename,
+ MultiThreadImageReaderUser::EventType e,
+ vtkImageData* image)
+ {
+ GimmickDebugMessage(7,
+ "MultiThreadImageReader event : "<<e<<std::endl);
+ if (e==ImageLoaded)
+ {
+ if (filename.size()==0)
+ {
+ //What to do in this case?
+ /*
+ GimmickDebugMessage(5,
+ "Pushing unknown image in queue"
+ <<std::endl);
+ mImageEventQueue.push_back(ImageEventType(image));*/
+ return;
+ }
+ ImageEventTypeMap::iterator i;
+//JCP 22-06-2009, test mImageEventMap.size() > 0
+ if(mImageEventMap.size()>0){
+ i = mImageEventMap.find(filename);
+ if (i!=mImageEventMap.end())
+ {
+ GimmickDebugMessage(5,
+ "Putting image of file '"<<filename<<"' on pointer"
+ <<std::endl);
+ ImageEventType ie(i->second);
+ ie.image = image;
+ ie.pointerHolder->Set(ie.image);
+ //mImageEventMap.erase(i);
+ }
+ }
+ }
+ else if (e==Error)
+ {
+ std::string mess="ERROR: MultiThreadImageReader: Cannot read image in file ";
+ mess+=filename;
+ mess+="\n";
+ GimmickMessage(1,mess);
+ ImageEventTypeMap::iterator i;
+ i = mImageEventMap.find(filename);
+ if (i!=mImageEventMap.end())
+ {
+ ImageEventType ie(i->second);
+ ie.image = image;
+ ie.pointerHolder->Set(GetDefaultImage());
+ //mImageEventMap.erase(i);
+ }
+ }
+
+ else if (e==ImageUnloaded)
+ {
+ std::string mess="Unloaded image in file ";
+ mess+=filename;
+ mess+="\n";
+ GimmickMessage(1,mess);
+ ImageEventTypeMap::iterator i;
+ i = mImageEventMap.find(filename);
+ if (i!=mImageEventMap.end())
+ {
+ ImageEventType ie(i->second);
+ ie.image = image;
+ ie.pointerHolder->Set(GetDefaultImage());
+ //mImageEventMap.erase(i);
+ }
+ }
+ }
+
+ //====================================================================
+
+ //====================================================================
+ void GimmickView::ConnectValidationObserver(ValidationCallbackType callback)
+ {
+ mValidationSignal.connect(callback);
+ }
+
+} // EO namespace creaImageIO
+
+//////////////////////////////////////////////////////////////////////
+//void GimmickView::Anonymize(std::vector<std::string> i_filenames, int type)
+// {
+ //if(type == 0)
+ //{
+ // // Get private key/certificate
+ // gdcm::CryptographicMessageSyntax cms;
+ //if( !dumb_mode )
+ // {
+ // if( !GetRSAKeys(cms, rsa_path.c_str(), cert_path.c_str() ) )
+ // {
+ // return 1;
+ // }
+ // cms.SetCipherType( ciphertype );
+ // }
+
+ //// Setup gdcm::Anonymizer
+ //gdcm::Anonymizer anon;
+ //if( !dumb_mode )
+ // anon.SetCryptographicMessageSyntax( &cms );
+
+ //if( dumb_mode )
+ // {
+ // for(unsigned int i = 0; i < nfiles; ++i)
+ // {
+ // const char *in = filenames[i].c_str();
+ // const char *out = outfilenames[i].c_str();
+ // if( !AnonymizeOneFileDumb(anon, in, out, empty_tags, remove_tags, replace_tags_value) )
+ // {
+ // //std::cerr << "Could not anonymize: " << in << std::endl;
+ // return 1;
+ // }
+ // }
+ // }
+ //else
+ // {
+ // for(unsigned int i = 0; i < nfiles; ++i)
+ // {
+ // const char *in = filenames[i].c_str();
+ // const char *out = outfilenames[i].c_str();
+ // if( !AnonymizeOneFile(anon, in, out) )
+ // {
+ // //std::cerr << "Could not anonymize: " << in << std::endl;
+ // return 1;
+ // }
+ // }
+ // }
+// }
\ No newline at end of file
--- /dev/null
+#ifndef __creaImageIOGimmickView_h_INCLUDED__
+#define __creaImageIOGimmickView_h_INCLUDED__
+
+#include <creaImageIOGimmick.h>
+#include <creaImageIOTreeView.h>
+#include <creaImageIOSystem.h>
+#include <creaImageIOImagePointerHolder.h>
+
+//#include <map>
+#include <vtkImageData.h>
+#include <creaImageIOMultiThreadImageReader.h>
+
+// Signal/slot mechanism for progress events
+#include <boost/signal.hpp>
+#include <boost/bind.hpp>
+
+#define GIMMICK_NO_IMAGE_SELECTION 0
+#define GIMMICK_2D_IMAGE_SELECTION 2
+#define GIMMICK_3D_IMAGE_SELECTION 3
+#define GIMMICK_4D_IMAGE_SELECTION 4
+
+#define NATIVE 0
+#define _2D 2
+#define _3D 3
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup View
+ */
+
+ class ImageExtent;
+ //=====================================================================
+
+ //=====================================================================
+ ///Abstract class that handles views, attributes and previews (GUI) for Gimmick.
+ class GimmickView: public MultiThreadImageReaderUser
+ {
+ public:
+ /// Ctor
+ GimmickView(boost::shared_ptr<Gimmick>, int number_of_threads = 0 );
+ /// Virtual destructor
+ virtual ~GimmickView();
+ /// Initializes the view :
+ /// Creates the TreeViews for all the TreeHandler of the Controller
+ ///
+ virtual void Initialize();
+
+ /// Type of map from View name to TreeView*
+ /// (This map is equivalent for Views of the TreeHandlerMap of Gimmick)
+ typedef std::map<std::string, TreeView*> TreeViewMapType;
+
+ /// Returns the TreeViewMap (ref)
+ TreeViewMapType& GetTreeViewMap() { return mTreeViewMap; }
+ /// Returns the TreeViewMap (const ref)
+ const TreeViewMapType& GetTreeViewMap() const
+ { return mTreeViewMap; }
+
+ /// Finalize
+ virtual void Finalize();
+
+ ///Returns the maximal priority
+ int GetMaximalPriority(){return mReader.GetMaximalPriority();}
+
+ ///Adds the selected Images to the given vector
+ virtual void GetSelectedImagesInVector(std::vector<vtkImageData*>& s, int dim)
+ { GimmickError("INTERNAL ERROR : GetSelectedImagesInVector not implemented"); }
+
+ ///Adds the selected Images to the given vector and validates to see if they comply with the given parameter (4D)
+ virtual void GetSelectedImages(std::vector<vtkImageData*>& s, int dim)
+ { GimmickError("INTERNAL ERROR : GetSelectedImages not implemented"); }
+
+ virtual void GetSelectedFiles(std::vector<std::string>& s)
+ { GimmickError("INTERNAL ERROR : GetSelectedFiles not implemented"); }
+
+ virtual void GetImages(int dim, std::vector<std::string> files, std::vector<vtkImageData*>& s)
+ { GimmickError("INTERNAL ERROR : GetImages not implemented"); }
+
+ virtual void OnSelectionChange(const std::vector<tree::Node*>& s, bool isSelection, int selection, bool mProcess)
+ { GimmickError("INTERNAL ERROR : OnSelectionChange not implemented"); }
+
+ virtual void ClearSelection()
+ { GimmickError("INTERNAL ERROR : ClearSelection not implemented"); }
+
+ ///Adds a file to ignore
+ virtual void AddIgnoreFile(tree::Node* toRemove)
+ { GimmickError("INTERNAL ERROR : AddIgnoreFile not implemented"); }
+
+ ///Copies selected files
+ virtual void CopyFiles(const std::vector<std::string>& filenames)
+ { GimmickError("INTERNAL ERROR : CopyFiles not implemented"); }
+
+ ///Edits the fields of a given node
+ virtual void CreateEditFieldsDialog(tree::Node* node, std::vector<std::string> names, std::vector<std::string> keys)
+ { GimmickError("INTERNAL ERROR : EditFields not implemented"); }
+
+ /// Anonymize or de-anonymize data
+ void Anonymize(std::vector<std::string> i_filenames, int type);
+
+
+ /// Display all Dicom Tags
+ virtual void DumpTags(const std::string filename)
+ {GimmickError("INTERNAL ERROR : DumpTags not implemented"); }
+
+ ///Edits the fields of a given node
+ virtual void ExportToStorage(const std::vector<std::string> keys)
+ { GimmickError("INTERNAL ERROR : ExportToStorage not implemented"); }
+
+ ///Copies selected files
+ virtual void SaveAs(const std::vector<std::string>& filenames)
+ { GimmickError("INTERNAL ERROR : SaveAs not implemented"); }
+
+ /// No selected image
+ bool NoValidateSelected();
+
+ ///Validates the dimension compliance of the images with the maximum and minimum given, and between their sizes
+ bool ValidateSelected (tree::Node* sel, int min_dim, int max_dim);
+
+ ///Reads the vector of images, builds it in the dimension required and returns them in the supplied vector.
+ void ReadImagesNotThreaded(std::vector<vtkImageData*>& s,std::vector<std::string> files, int dim);
+
+ ///Reads the vector of images, builds it in the dimension required and returns them in the supplied vector.
+ void ReadImagesNotThreadedInVector(std::vector<vtkImageData*>& s,std::vector<std::string> files, int dim);
+
+///Requests the reading of an image with priority and index in the
+ /// current selection (-1 if not in selection)
+ //void RequestReading(tree::Node* n, int prio, int selection_index , ImagePointerHolder *p);
+ void RequestReading(tree::Node* n, int prio, int selection_index , boost::shared_ptr<ImagePointerHolder> p);
+
+ ///Obtains the message of the state
+ std::string GetMessage(){return mMess;}
+
+ ///Obtains the message of the state
+ void SetMessage(std::string mess){mMess=mess;}
+
+ ///Resets the data of the extent and begins a new selection
+ void ResetExtent();
+
+ /// Create the tree views
+ void CreateTreeViews();
+
+ /// Create a tree view with a given name
+ void CreateSingleTreeView(std::string &i_name);
+
+ /// Create the tree view for TreeHandler provided
+ virtual void CreateTreeView( TreeHandler* )
+ { GimmickError("INTERNAL ERROR : CreateTreeView not implemented"); }
+
+ /// Updates the TreeView of given name from level l to bottom
+ /// (calls the virtual method TreeView::UpdateLevel(l))
+ virtual void UpdateTreeViewLevel(const std::string&, int l);
+
+ // Multi-thread image reader callback
+ void OnMultiThreadImageReaderEvent(const std::string& filename,
+ MultiThreadImageReaderUser::EventType t,
+ vtkImageData* image);
+
+ vtkImageData* GetDefaultImage() { return mReader.GetImage(""); }
+
+ //=============================================
+ typedef boost::signal<void (bool)> ValidationSignalType;
+ typedef ValidationSignalType::slot_function_type ValidationCallbackType;
+ //=============================================
+
+ //==================================================================
+ /// Adds the function f to the list of functions to call
+ /// when the addition progresses.
+ /// f is of type ProgressCallbackType which is:
+ /// void (*ProgressCallbackType)(Progress&)
+ /// To pass a member function 'f' of an instance 'c' of a class 'C'
+ /// as callback you have to 'bind' it, i.e. call:
+ /// ConnectValidationObserver ( boost::bind( &C::f , c, _1 ) );
+ void ConnectValidationObserver(ValidationCallbackType callback);
+ //==================================================================
+
+ void modifyValidationSignal(bool ivalid);
+
+ private:
+
+ double OrderTheFileNameVector(std::vector<std::string> &im);
+
+ /// Controller which manages the interaction with the model
+ boost::shared_ptr< Gimmick> mGimmick;
+
+ /// The views
+ TreeViewMapType mTreeViewMap;
+
+ /// The message that results from the validation
+ std::string mMess;
+
+ /// Multi-thread image reader
+ MultiThreadImageReader mReader;
+
+ /// Internal type of image reading event
+ /// If the image pointer is non null then the image is available (loaded)
+ /// else it has been unloaded
+ struct ImageEventType
+ {
+ ImageEventType( tree::Node* no = 0,
+ int sel_index = -1)
+// ImagePointerHolder* ph= 0)
+ : node(no), index(sel_index){}//, pointerHolder(ph){}
+ ImageEventType(vtkImageData* im )
+ : image(im) {}
+ tree::Node* node;
+ vtkImageData* image;
+ int index;
+ boost::shared_ptr<ImagePointerHolder> pointerHolder;
+ };
+ typedef std::map<std::string,ImageEventType> ImageEventTypeMap;
+ /// Map of images' names to ImageEventType
+ /// Used to associated a filename to a the data of a request
+ ImageEventTypeMap mImageEventMap;
+
+ // queue of image event
+ typedef std::deque<ImageEventType> ImageEventQueueType;
+
+ //ImageEventQueueType mImageEventQueue;
+
+ ///The current image extent
+ boost::shared_ptr<ImageExtent> mImageExtent;
+
+ ///The validation signal
+ ValidationSignalType mValidationSignal;
+
+ ///Boolean that determines if the selection is valid
+ bool valid;
+
+ ///Boolean that determines if the reader has been started
+ bool mReaderStarted;
+
+ };
+ // EO class GimmickView
+ //=====================================================================
+
+
+} // EO namespace creaImageIO
+
+// EOF
+#endif
--- /dev/null
+#ifndef __creaImageIOImagePointerHolder_INCLUDED__
+#define __creaImageIOImagePointerHolder_INCLUDED__
+
+#include <creaImageIOSystem.h>
+#include <vtkImageData.h>
+#include <boost/thread/mutex.hpp>
+
+
+namespace creaImageIO
+{
+ // The class that holds the pointer to the images to show
+ class ImagePointerHolder
+ {
+ public:
+ ImagePointerHolder(vtkImageData* im):
+ mImage(im)
+ {}
+ ImagePointerHolder();
+ void Set(vtkImageData* im){boost::mutex::scoped_lock lock(mMutex);mImage=im;}
+ vtkImageData* Get(){boost::mutex::scoped_lock lock(mMutex);return mImage;}
+ private:
+ vtkImageData* mImage;
+ /// The mutex
+ boost::mutex mMutex;
+ };
+}
+#endif
+
+
--- /dev/null
+#include <creaImageIOImageReader.h>
+#include <creaImageIOTreeAttributeDescriptor.h>
+#include <creaImageIOSystem.h>
+
+#include <creaImageIOVtkImageReader.h>
+#if defined (USE_GDCM)
+ #include <creaImageIODicomImageReader.h>
+#endif
+#if defined(USE_GDCM2)
+ #include <creaImageIODicomImageReader2.h>
+#endif
+#include <creaImageIOUltrasonixImageReader.h>
+#include <vtkPNGReader.h>
+#include <vtkTIFFReader.h>
+#include <vtkJPEGReader.h>
+#include <vtkBMPReader.h>
+#include <vtkSLCReader.h>
+#include <vtkMetaImageReader.h>
+//#include <vtkGESignalReader.h>
+
+
+#include "boost/filesystem/path.hpp"
+
+namespace creaImageIO
+{
+
+
+
+
+
+ //=====================================================================
+ ImageReader::ImageReader()
+ :
+ mUnreadableImage(0),
+ mLastFilename("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&")
+ {
+ // std::cout << "#### ImageReader::ImageReader()"<<std::endl;
+ if (mUnreadableImage!=0) return;
+
+ Register( boost::shared_ptr<AbstractImageReader>(new VtkImageReader(vtkPNGReader::New(), "PNG", ".png")));
+ Register(boost::shared_ptr<AbstractImageReader>(new VtkImageReader(vtkTIFFReader::New(), "JPEG", ".jpeg")));
+ Register(boost::shared_ptr<AbstractImageReader>(new VtkImageReader(vtkJPEGReader::New())));
+ Register(boost::shared_ptr<AbstractImageReader>(new VtkImageReader(vtkBMPReader::New())));
+ Register(boost::shared_ptr<AbstractImageReader>(new VtkImageReader(vtkSLCReader::New())));
+ Register(boost::shared_ptr<AbstractImageReader>(new VtkImageReader(vtkMetaImageReader::New(),"MHD",".mhd")));
+ // Register(new VtkImageReader(vtkGESignalReader::New()));
+ Register(boost::shared_ptr<AbstractImageReader>(new DicomImageReader));
+ Register(boost::shared_ptr<AbstractImageReader>(new UltrasonixImageReader));
+
+ UnRegister(".txt");
+
+ mUnreadableImage = vtkImageData::New();
+ int dim[3];
+ dim[0] = dim[1] = 128;
+ dim[2] = 1;
+ mUnreadableImage->SetDimensions ( dim );
+ mUnreadableImage->SetScalarTypeToUnsignedChar();
+ mUnreadableImage->AllocateScalars();
+ for (int i=0;i<dim[0];i++)
+ for (int j=0;j<dim[1];j++)
+ mUnreadableImage->SetScalarComponentFromFloat(i,j,0,0,0);
+ for (int i=0;i<dim[0];i++)
+ {
+ mUnreadableImage->SetScalarComponentFromFloat(i,i,0,0,255);
+ mUnreadableImage->SetScalarComponentFromFloat(dim[0]-1-i,i,0,0,255);
+ }
+
+
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ ImageReader::~ImageReader()
+ {
+
+ // for (i=mReader.begin(); i!=mReader.end(); i++)
+ // {
+ //delete (*i);
+ // }
+// mReader.clear();
+ if (mUnreadableImage!=0)
+ {
+ mUnreadableImage->Delete();
+ mUnreadableImage = 0;
+ }
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void ImageReader::Register(boost::shared_ptr<AbstractImageReader> r)
+ {
+ mReader.push_back(r);
+
+ }
+
+ void ImageReader::UnRegister(const std::string i_val)
+ {
+ mUnReader.push_back(i_val);
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ // Returns true iff the file is readable
+ bool ImageReader::ShallNotRead( const std::string& filename )
+ {
+ bool ok = true;
+ if(filename != "")
+ {
+ std::vector<std::string >::iterator i ;
+ for (i=mUnReader.begin(); i!=mUnReader.end(); i++)
+ {
+
+ if ( (*i).c_str() == filename)
+ {
+ ok = false;
+ break;
+ }
+ }
+ }
+ return ok;
+
+ }
+
+
+ //=====================================================================
+ // Returns true iff the file is readable
+ bool ImageReader::CanRead( const std::string& filename )
+ {
+ bool ok = false;
+
+ if( !ShallNotRead(filename))
+ {
+ return ok;
+ }
+ if(filename != "")
+ {
+ std::vector<boost::shared_ptr<AbstractImageReader> >::iterator i;
+ for (i=mReader.begin(); i!=mReader.end(); i++)
+ {
+ ok = (*i)->CanRead(filename);
+ if (ok)
+ {
+ mLastFilename = filename;
+ mLastReader = *i;
+ break;
+ }
+ }
+ }
+ return ok;
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ // Reads the file (CanRead must be called before : no test here)
+ vtkImageData* ImageReader::ReadImage( const std::string& filename)
+ {
+ if (mLastFilename!=filename)
+ {
+ if (!CanRead(filename))
+ {
+ vtkImageData* im = vtkImageData::New();
+ im->ShallowCopy(mUnreadableImage);
+ return im;
+ }
+ }
+ vtkImageData* i = mLastReader->ReadImage(mLastFilename);
+ if (i==0)
+ {
+ i = vtkImageData::New();
+ i->ShallowCopy(mUnreadableImage);
+ }
+ return i;
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ void ImageReader::ReadAttributes(const std::string& filename,
+ std::map<std::string,std::string>& attr)
+ {
+ if (mLastFilename!=filename)
+ {
+ if (!CanRead(filename))
+ {
+ return;
+ }
+ }
+ mLastReader->ReadAttributes(mLastFilename,attr);
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ /// Pushes back all kwown extensions (without dot) in the vector given
+ void ImageReader::PushBackExtensions(std::vector<std::string>& v)
+ {
+ std::vector<boost::shared_ptr<AbstractImageReader> >::iterator i;
+ for (i=mReader.begin(); i!=mReader.end(); i++)
+ {
+ (*i)->PushBackExtensions(v);
+ }
+ }
+ //=====================================================================
+
+} // namespace creaImageIO
--- /dev/null
+#ifndef __creaImageIOImageReader_h_INCLUDED__
+#define __creaImageIOImageReader_h_INCLUDED__
+
+#include <creaImageIOSystem.h>
+#include <creaImageIOAbstractImageReader.h>
+
+namespace creaImageIO
+{
+
+
+ /**
+ * \ingroup IO
+ * \brief Generic image reader which uses all the specific concrete image reader of the lib (tif, jpg, dicom, ...)
+ */
+ class CREAIMAGEIO_EXPORT ImageReader : virtual public AbstractImageReader
+ {
+ public:
+ ImageReader();
+ ~ImageReader();
+
+ /// Pushes back all kwown extensions (without dot) in the vector given
+ void PushBackExtensions(std::vector<std::string>&);
+
+ /// Returns true iff the file is readable
+ bool CanRead( const std::string& filename);
+ /// Reads and returns the image data.
+ /// Returns an "Unreadable image" picture if fails
+ vtkImageData* ReadImage( const std::string& filename);
+
+ /// Reads the attributes of the image.
+ /// Requested attributes names are provided as keys
+ /// in a string to string map
+ /// On return, the values of the map are the values
+ /// of the attributes (empty string if not available).
+ void ReadAttributes(const std::string& filename,
+ tree::AttributeMapType& attr);
+
+ /// Exclude specific readers
+ /// TO DO...
+
+
+ protected:
+
+ /// Register a reader
+ void Register( boost::shared_ptr<AbstractImageReader> );
+
+ std::vector<boost::shared_ptr<AbstractImageReader> > mReader;
+ vtkImageData* mUnreadableImage;
+
+ std::string mLastFilename;
+ boost::shared_ptr<AbstractImageReader> mLastReader;
+
+ bool ShallNotRead( const std::string& filename );
+
+ void UnRegister(const std::string i_val);
+
+ std::vector <std::string> mUnReader;
+
+ private:
+
+ }; // class ImageReader
+ //=====================================================================
+
+
+
+} // namespace creaImageIO
+
+
+
+#endif // #ifndef __creaImageIOImageReader_h_INCLUDED__
--- /dev/null
+/*
+
+*/
+/*! \file
+ \brief Indexed priority queues handled by binary trees.
+*/
+#ifndef __creaImageIOIndexedHeap_h_INCLUDED__
+#define __creaImageIOIndexedHeap_h_INCLUDED__
+
+#include <vector>
+
+namespace creaImageIO
+{
+
+
+
+ template <class T,
+ class Comparator/*=Less<T>*/,
+ class Indexer/*=Index<T> */>
+ class IndexedHeap ;
+ template < class T,
+ class C,
+ class I>
+ std::ostream& operator << (std::ostream&, const IndexedHeap<T,C,I>& );
+
+
+ //template <class T, class Comparator=std::less<T>, class Index=IndexIndex<T> > class SlicedIndexedHeap;
+
+
+ //========================================================================
+ /// \brief Indexed priority queues handled by binary trees.
+ ///
+ /// Heap Allows :
+ /// - log(n) insertion
+ /// - constant time acces to the first element
+ /// - log(n) removal of the first element
+ /// - log(n) priority change of a random element
+ /// Indexation Allows :
+ /// - constant time access to a random element (for priority change)
+ ///
+ /// The Indexer is an unary_function<T,int&> whose operator()(T& t)
+ /// returns a reference on an integer which
+ /// is maintained by the IndexedHeap in order to provide at any time
+ /// the position of the object t in the Heap
+ /// (hence allowing constant time random access to an object).
+ template <class T,
+ class Comparator /*=Less<T>*/,
+ class Indexer /*=Index<T>*/>
+ class IndexedHeap
+ {
+ // friend class SlicedIndexedHeap<T,Comparator,Index>;
+ public :
+
+ //======================================================================
+ /// Constructor
+ IndexedHeap () {}
+ /// Constructor
+ IndexedHeap ( const Comparator& comp, const Indexer& ind ) ;
+ /// Destructor
+ ~IndexedHeap() { }
+ /// Sets the comparator
+ void set( const Comparator& comp );
+ /// Sets the Index
+ void set( const Indexer& ind );
+ //======================================================================
+
+ //======================================================================
+ /// inserts an element in the Heap and returns its position
+ int insert(T);
+ /// return a reference on the first element of the Heap
+ T& top();
+ /// return a constant reference on the first element of the Heap
+ const T& top() const;
+ /// removes and returns the first element of the Heap
+ T remove_top();
+ /// removes and returns the nth element
+ T remove(int n);
+ /// returns the size of the Heap
+ inline int size() const {return m_p.size(); }
+ /// empties the Heap
+ void clear();
+ //======================================================================
+
+ //======================================================================
+ /// returns a constant on the stack of elements
+ const std::vector<T> & stack() const {return m_p;}
+ /// returns a reference to the ith element of the stack
+ T& operator [] (int i) { return m_p[i];}
+ /// returns a constant reference to the ith element of the stack
+ const T& operator [] (int i) const { return m_p[i];}
+ /// returns the index (position) of t
+ inline int index(T& t) { return (*m_i)(t); }
+ //======================================================================
+
+ //======================================================================
+ /// returns the position of the father of i
+ inline int father( int i ) const;
+ /// returns the position of the right son of i
+ inline int rightson( int i ) const;
+ /// returns the position of the leftson of i
+ inline int leftson( int i ) const;
+ //======================================================================
+ /// swaps ith and jth elements
+ inline void swap(int i, int j);
+ /// remonte un element dans le tas tant qu'il n'est pas a sa place.
+ /// renvoie la position finale
+ inline int upsort(int);
+ /// descend un element dans le tas tant qu'il n'est pas a sa place.
+ /// renvoie la position finale
+ inline int downsort(int);
+ //======================================================================
+
+ protected :
+ /// binary tree handled by a vector
+ std::vector<T> m_p;
+ /// comparator pointer
+ const Comparator* m_c;
+ /// Index pointer
+ const Indexer* m_i;
+ };
+ //========================================================================
+ // EO class IndexedHeap
+ //========================================================================
+
+
+#include "creaImageIOIndexedHeap.txx"
+
+
+};
+//===========================================================================
+// EO namespace creaImageIO
+//===========================================================================
+
+
+
+//===========================================================================
+// EOF
+//===========================================================================
+#endif
--- /dev/null
+/*
+
+*/
+/*! \file
+ \brief Code of IndexedHeap
+*/
+//============================================================================
+template <class T, class CT, class IT>
+std::ostream& operator << (std::ostream& s, const IndexedHeap<T,CT,IT>& t)
+{
+ s << "[";
+ for (int i=0; i<t.size(); i++) s << t[i] << " " ;
+ s << "]";
+ return s;
+}
+//============================================================================
+
+
+//===========================================================
+template <class T, class CT, class IT>
+IndexedHeap<T,CT,IT>::IndexedHeap ( const CT& comp, const IT& index )
+ : m_c(&comp), m_i(&index)
+{}
+//===========================================================
+
+
+//===========================================================
+template <class T, class CT, class IT>
+void IndexedHeap<T,CT,IT>::set( const CT& comp )
+{
+ m_c = ∁
+}
+//===========================================================
+
+//===========================================================
+template <class T, class CT, class IT>
+void IndexedHeap<T,CT,IT>::set ( const IT& index )
+{
+ m_i = &index;
+}
+//===========================================================
+
+
+//===========================================================
+template <class T, class CT, class IT>
+int IndexedHeap<T,CT,IT>::insert(T t)
+{
+ m_p.push_back(t);
+ (*m_i)(t) = size()-1;
+ return upsort(size()-1);
+}
+//===========================================================
+
+//===========================================================
+template <class T, class CT, class IT>
+T& IndexedHeap<T,CT,IT>::top()
+{
+ // lglASSERT( size() > 0)
+ return m_p.front();
+}
+//===========================================================
+
+
+//===========================================================
+template <class T, class CT, class IT>
+const T& IndexedHeap<T,CT,IT>::top() const
+{
+ // lglASSERT( size() > 0)
+ return m_p.front();
+}
+//===========================================================
+
+//===========================================================
+template <class T, class CT, class IT>
+T IndexedHeap<T,CT,IT>::remove_top()
+{
+ // lglASSERT( size() > 0 )
+ T f(m_p[0]);
+ (*m_i)(f) = -1;
+ T last = m_p.back();
+ m_p.pop_back();
+ if (m_p.size()>0)
+ {
+ m_p[0] = last;
+ (*m_i)(last) = 0;
+ downsort(0);
+ }
+ return f;
+}
+//============================================================================
+
+
+
+//============================================================================
+template <class T, class CT, class IT>
+T IndexedHeap<T,CT,IT>::remove(int n)
+{
+ // lglASSERT ( (n>=0)&&(n<size()) )
+ T f(m_p[n]);
+ (*m_i)(f) = -1;
+ T last = m_p.back();
+ m_p.pop_back();
+ if (m_p.size()>0)
+ {
+ m_p[n] = last;
+ (*m_i)(last) = n;
+ downsort(n);
+ }
+ return f;
+}
+//============================================================================
+
+
+//============================================================================
+template <class T, class CT, class IT>
+void IndexedHeap<T,CT,IT>::clear()
+{
+ for (typename std::vector<T>::iterator i=m_p.begin(); i!=m_p.end(); ++i)
+ {
+ (*m_i)(*i)=-1;
+ }
+ m_p.clear();
+}
+//============================================================================
+
+
+//============================================================================
+template <class T, class CT, class IT>
+int IndexedHeap<T,CT,IT>::father( int i) const
+{
+ return ((i-1)/2);
+}
+//============================================================================
+
+//============================================================================
+template <class T, class CT, class IT>
+int IndexedHeap<T,CT,IT>::rightson( int i) const
+{
+ return (i*2+2);
+}
+//============================================================================
+
+//============================================================================
+template <class T, class CT, class IT>
+int IndexedHeap<T,CT,IT>::leftson( int i) const
+{
+ return (i*2+1);
+}
+//============================================================================
+
+//============================================================================
+template <class T, class CT, class IT>
+void IndexedHeap<T,CT,IT>::swap(int i, int j)
+{
+ T tmp = m_p[i];
+ m_p[i] = m_p[j];
+ m_p[j] = tmp;
+ // update indices
+ (*m_i)(m_p[i]) = i;
+ (*m_i)(m_p[j]) = j;
+}
+//============================================================================
+
+
+
+//============================================================================
+template <class T, class CT, class IT>
+int IndexedHeap<T,CT,IT>::upsort(int i)
+{
+ //if (i==0) return i;
+ int j = father(i);
+ while ((i>0)&&(*m_c)(m_p[i],m_p[j]))
+ {
+ swap(i,j);
+ i = j;
+ j = father(j);
+ }
+ return i;
+}
+//============================================================================
+
+
+//============================================================================
+template <class T, class CT, class IT>
+int IndexedHeap<T,CT,IT>::downsort(int i)
+{
+ do
+ {
+
+ unsigned int ls = leftson(i);
+ if (ls<m_p.size())
+ {
+ bool lc = ((*m_c)(m_p[i],m_p[ls]));
+ unsigned int rs = ls + 1;
+ bool rc = true;
+ if (rs<m_p.size()) rc = ((*m_c)(m_p[i],m_p[rs]));
+ if ( !lc )
+ {
+ if ( !rc )
+ {
+ if ((*m_c)(m_p[ls],m_p[rs]))
+ {
+ swap(i,ls);
+ i = ls;
+ }
+ else
+ {
+ swap(i,rs);
+ i = rs;
+ }
+ }
+ else
+ {
+ swap(i,ls);
+ i = ls;
+ }
+ }
+ else if ( !rc )
+ {
+ swap(i,rs);
+ i = rs;
+ }
+ else return i;
+ }
+ else return i;
+ }
+ while (true);
+ return i;
+}
+//============================================================================
+
+
+//============================================================================
+// EOF
+//============================================================================
--- /dev/null
+#include <creaImageIOListener.h>
+#include <creaImageIOSystem.h>
+#include "boost/filesystem.hpp"
+#include <boost/filesystem/operations.hpp>
+#include <boost/utility.hpp>
+
+namespace fs = boost::filesystem;
+using boost::filesystem::path;
+
+using namespace crea;
+
+namespace creaImageIO
+{
+ //=====================================================================
+ // CTor
+ Listener::Listener()
+ {
+
+ boost::mutex::scoped_lock lock(mMutex);
+ GimmickDebugMessage(6,"Listener::Listener"
+ <<std::endl);
+ mDrive="E:";
+ mMounted=false;
+ mAddFiles=false;
+ mRemoveFiles=true;
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ /// Destructor
+ Listener::~Listener()
+ {
+ boost::mutex::scoped_lock lock(mMutex);
+ GimmickDebugMessage(6,"Listener::~Listener"
+ <<std::endl);
+ }
+ //=====================================================================
+
+ void* Listener::Entry()
+ {
+ GimmickDebugMessage(6,"Listener::Entry()"<<std::endl);
+ while(!TestDestroy())
+ {
+ try
+ {
+ fs::exists( mDrive );
+ if(!mMounted && mAddFiles)
+ {
+ mMounted=true;
+ SendSignal(mMounted);
+ }
+ else if(!mMounted)
+ {
+ mMounted=true;
+ }
+
+ }
+ catch (...)
+ {
+ if(mMounted && mRemoveFiles)
+ {
+ mMounted=false;
+ SendSignal(mMounted);
+ }
+ else if(mMounted)
+ {
+ mMounted=false;
+ }
+ }
+
+ clock_t endwait;
+ endwait = clock () + 0.001 * CLOCKS_PER_SEC ;
+ while (clock() < endwait ) {}
+
+ }
+ return 0;
+ }
+
+ //=====================================================================
+
+ //=====================================================================
+ void Listener::OnExit()
+ {
+ GimmickDebugMessage(6,"Listener::OnExit() "<<std::endl);
+ }
+
+ //====================================================================
+ void Listener::ConnectObserver(MountingCallbackType callback)
+ {
+ mMountingSignal.connect(callback);
+ }
+
+ //======================================================================
+ void Listener::SendSignal(bool mount)
+ {
+ mMountingSignal(mount);
+ }
+
+
+
+
+} // EO namespace creaImageIO
+
--- /dev/null
+#ifndef __creaImageIOListener_h_INCLUDED__
+#define __creaImageIOListener_h_INCLUDED__
+
+#include <creaImageIOSystem.h>
+#include <stdio.h>
+#include <time.h>
+#include <wx/thread.h>
+// Signal/slot mechanism
+#include <boost/signal.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread/mutex.hpp>
+
+namespace creaImageIO
+{
+
+ class Listener : public wxThread
+ {
+ public:
+ /// Ctors
+ Listener();
+ /// Dtor
+ virtual ~Listener();
+ ///Thread method that is executed once create is called
+ void* Entry();
+ ///Thread method called upon exiting
+ void OnExit();
+ ///Sets the new state of adding files
+ void SetAddFilesState(bool addFiles){boost::mutex::scoped_lock lock(mMutex);mAddFiles=addFiles;}
+ ///Sets the new state of removing files
+ void SetRemoveFilesState(bool removeFiles){boost::mutex::scoped_lock lock(mMutex);mRemoveFiles=removeFiles;}
+ ///Sets the new monitored drive
+ void SetMonitoredDrive(const std::string& dr){boost::mutex::scoped_lock lock(mMutex);mDrive=dr;}
+ ///Puts the name of the monitored drive in the given string
+ void GetMonitoredDrive(std::string& drive){drive=mDrive;}
+
+ ///Related with signals
+ //=============================================
+ typedef boost::signal<void (bool)> MountingSignalType;
+ typedef MountingSignalType::slot_function_type MountingCallbackType;
+ //=============================================
+
+ //==================================================================
+ void ConnectObserver(MountingCallbackType callback);
+ //==================================================================
+
+ ///Sends a boost::signal to alert that the drive has changed its state (mounted/unmounted)
+ void SendSignal(bool ivalid);
+
+
+ private:
+ /// The mutex
+ boost::mutex mMutex;
+ /// Boolean that declares if the files that are read on CD mount should be added
+ bool mAddFiles;
+ /// Boolean that declares if, on CD unmount, the files that were in the drive should be removed
+ bool mRemoveFiles;
+ ///Boolean that declares if a unit has been mounted
+ bool mMounted;
+ ///The monitored drive
+ std::string mDrive;
+ ///The validation signal
+ MountingSignalType mMountingSignal;
+ };
+
+}
+
+#endif
--- /dev/null
+#include <creaImageIOMultiThreadImageReader.h>
+#include <creaImageIOImageReader.h>
+#include <wx/utils.h>
+#include <creaImageIOSystem.h>
+
+#include <creaImageIOGimmick.h>
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+namespace creaImageIO
+{
+
+ //=====================================================================
+ void MultiThreadImageReaderUser::MultiThreadImageReaderSendEvent
+ ( const std::string& filename,
+ EventType type,
+ vtkImageData* image)
+ {
+ wxMutexLocker lock(mMultiThreadImageReaderUserMutex);
+
+ this->OnMultiThreadImageReaderEvent(filename,type,image);
+ }
+ //=====================================================================
+
+ //=====================================================================
+ class ThreadedImageReader: public wxThread
+ {
+ public:
+ ThreadedImageReader(MultiThreadImageReader* tir) :
+ mMultiThreadImageReader(tir)
+ {}
+
+ void* Entry();
+ void OnExit();
+
+ vtkImageData* Read(const std::string& filename);
+
+ struct deleter
+ {
+ void operator()(ThreadedImageReader* p)
+ {
+ p->Delete();
+ }
+ };
+ friend struct deleter;
+
+
+ private:
+ ImageReader mReader;
+ MultiThreadImageReader* mMultiThreadImageReader;
+
+ };
+
+ //=====================================================================
+
+
+ //=====================================================================
+ MultiThreadImageReader::MultiThreadImageReader(int number_of_threads)
+ : //mDoNotSignal(false),
+ mReader(0),
+ mTotalMem(0),
+ mTotalMemMax(1000000)
+ {
+ // std::cout << "#### MultiThreadImageReader::MultiThreadImageReader("
+ // << " #threads= " << number_of_threads <<" )"<<std::endl;
+
+ mDone = false;
+ // Create the threads
+ for (int i=0; i<number_of_threads; i++)
+ {
+ //ThreadedImageReader* t = new ThreadedImageReader(this);
+ boost::shared_ptr<ThreadedImageReader> t(new ThreadedImageReader(this), ThreadedImageReader::deleter());
+ mThreadedImageReaderList.push_back(t);
+ std::cout << " ===> Thread "<<i
+ <<" successfully added"<< std::endl;
+ }
+ mNumberOfThreadedReadersRunning = 0;
+ // Init the queue
+ mQueue.set(mComparator);
+ mQueue.set(mIndexer);
+ //
+ // no thread : alloc self reader
+// if (number_of_threads==0)
+// {
+ mReader = new ImageReader();
+// }
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool MultiThreadImageReader::Start()
+ {
+
+ // std::cout << "#### MultiThreadImageReader::Start()"
+ // <<std::endl;
+ if (mNumberOfThreadedReadersRunning > 0) return true;
+
+ ThreadedImageReaderListType::iterator i;
+ for (i =mThreadedImageReaderList.begin();
+ i!=mThreadedImageReaderList.end();
+ i++)
+ {
+ (*i)->Create();
+ if ( (*i)->Run() != wxTHREAD_NO_ERROR )
+ {
+ std::cout << "ERROR starting a thread"<< std::endl;
+ return false;
+ }
+ else
+ {
+ std::cout << " ===> Thread "<<(*i)->GetCurrentId()
+ <<" successfully created"<< std::endl;
+
+ }
+ }
+ wxMutexLocker locker(GetMultiThreadImageReaderUserMutex());
+ // std::cout << "EO Start : #Threads running = "
+ // << mNumberOfThreadedReadersRunning<<std::endl;
+
+ return true;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void MultiThreadImageReader::Stop()
+ {
+// std::cout << "#### MultiThreadImageReader::Stop()"
+// <<std::endl;
+ // std::cout << "Sending stop order to the threads..."<<std::endl;
+ if (mDone) return;
+
+ ThreadedImageReaderListType::iterator i;
+ for (i =mThreadedImageReaderList.begin();
+ i!=mThreadedImageReaderList.end();
+ i++)
+ { std::cout << " ===> Thread "<<(*i)->GetCurrentId()
+ <<" successfully stopped"<< std::endl;
+ if((*i)->IsAlive())
+ {(*i)->Pause();
+ (*i).reset();
+ // (*i)->Delete();
+ }
+ }
+ mThreadedImageReaderList.clear();
+ // Wait a little to be sure that all threads have stopped
+ // A better way to do this ?
+ // wxMilliSleep(1000);
+ // New method : the threads generate a stop event when they have finished
+ // We wait until all threads have stopped
+// std::cout << "Waiting for stop signals..."<<std::endl;
+ do
+ {
+ // Sleep a little
+ wxMilliSleep(10);
+ // Lock
+ {
+ wxMutexLocker locker(GetMultiThreadImageReaderUserMutex());
+// std::cout << "#Threads running = "
+// << mNumberOfThreadedReadersRunning<<std::endl;
+ // Break if all readers have stopped
+ if (mNumberOfThreadedReadersRunning <= 0)
+ {
+ break;
+ }
+ }
+ }
+ while (true);
+// std::cout << "All threads stopped : OK "<<std::endl;
+
+ ImageMapType::iterator j;
+ for (j =mImages.begin();
+ j!=mImages.end();
+ ++j)
+
+ {
+ delete j->first;
+ }
+ mImages.clear();
+ mDone = true;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ MultiThreadImageReader::~MultiThreadImageReader()
+ {
+ // std::cout << "#### MultiThreadImageReader::~MultiThreadImageReader()"
+ // <<std::endl;
+ Stop();
+ if (mReader) delete mReader;
+ mThreadedImageReaderList.clear();
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void MultiThreadImageReader::UpdateUnloadPriority(ImageToLoadPtr p,
+ int priority)
+ {
+ // not in unload queue : ciao
+ if (p->UnloadIndex()<0) return;
+ int old_prio = p->GetPriority();
+ if (priority > old_prio)
+ {
+ p->SetPriority(priority);
+ mUnloadQueue.downsort(p->UnloadIndex());
+ }
+ else if ( old_prio > priority )
+ {
+ p->SetPriority(priority);
+ mUnloadQueue.upsort(p->UnloadIndex());
+ }
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void MultiThreadImageReader::Request( MultiThreadImageReaderUser* user,
+ const std::string& filename,
+ int priority )
+ {
+ wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
+
+ if (mNumberOfThreadedReadersRunning==0)
+// if (mThreadedImageReaderList.size()==0)
+ {
+ // no detached reader : use self reader
+ ImageToLoad itl(user,filename);
+ ImageMapType::iterator i = mImages.find(&itl);
+ if (i!=mImages.end())
+ {
+ ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
+ // Already inserted
+ if (pitl->GetImage() != 0)
+ {
+ // Already read
+ pitl->SetUser(user);
+ UpdateUnloadPriority(pitl,priority);
+ SignalImageRead(pitl,false);
+ return; // pitl->GetImage();
+ }
+ }
+ ImageToLoadPtr pitl = new ImageToLoad(user,filename,0);
+ mImages[pitl] = 0;
+ pitl->SetImage(mReader->ReadImage(filename));
+ UpdateUnloadPriority(pitl,priority);
+ SignalImageRead(pitl,true);
+ // return pitl->GetImage();
+ return;
+ }
+
+ ImageToLoad itl(user,filename);
+ ImageMapType::iterator i = mImages.find(&itl);
+ if (i!=mImages.end())
+ {
+ // Already inserted
+ if (i->first->GetImage() != 0)
+ {
+ // Already read : ok :signal the user
+ UpdateUnloadPriority(i->first,priority);
+ SignalImageRead(i->first,false);
+ return;
+ }
+ /// Already requested : change the priority
+ ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
+ pitl->SetPriority(priority);
+ // Already in queue
+ if (pitl->Index()>=0)
+ {
+ // Re-sort the queue
+ mQueue.upsort(pitl->Index());
+ }
+ // Not read but not in queue = being read = ok
+ else
+ {
+
+ }
+ }
+ else
+ {
+ // Never requested before or unloaded
+ ImageToLoadPtr pitl = new ImageToLoad(user,filename,priority);
+ mImages[pitl] = 0;
+ mQueue.insert(pitl);
+ }
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void MultiThreadImageReader::OnMultiThreadImageReaderEvent
+ (const std::string& filename,
+ MultiThreadImageReaderUser::EventType e,
+ vtkImageData* image)
+ {
+ if ((e==MultiThreadImageReaderUser::ImageLoaded) &&
+ (filename == mRequestedFilename))
+ {
+ mRequestedImage = image;
+ }
+ else if (e==MultiThreadImageReaderUser::ThreadedReaderStarted)
+ {
+ mNumberOfThreadedReadersRunning++;
+ // std::cout << "#TR=" << mNumberOfThreadedReadersRunning << std::endl;
+ }
+ else if (e==MultiThreadImageReaderUser::ThreadedReaderStopped)
+ {
+
+ mNumberOfThreadedReadersRunning--;
+ // std::cout << "#TR=" << mNumberOfThreadedReadersRunning << std::endl;
+ }
+ }
+ //=====================================================================
+
+ //=====================================================================
+ vtkImageData* MultiThreadImageReader::GetImage(const std::string& filename)
+ {
+ // Start();
+ // std::cout << "** MultiThreadImageReader::GetImage('"<<filename<<"')"
+ // <<std::endl;
+
+ do
+ {
+ // wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
+
+ // std::cout << "** MultiThreadImageReader::GetImage('"<<filename
+ // <<"') lock ok"
+ // <<std::endl;
+
+ // if (mNumberOfThreadedReadersRunning==0)
+ // if (mThreadedImageReaderList.size()==0)
+ if (true)
+ {
+ ImageToLoad itl(this,filename);
+ ImageMapType::iterator i = mImages.find(&itl);
+ if (i!=mImages.end())
+ {
+ ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
+ // Already inserted
+ if (pitl->GetImage() != 0)
+ {
+ // Already read
+ UpdateUnloadPriority(pitl,
+ GetMaximalPriorityWithoutLocking()+1);
+ return pitl->GetImage();
+ }
+ }
+ ImageToLoadPtr pitl = new ImageToLoad(this,filename,0);
+ mImages[pitl] = 0;
+ pitl->SetImage(mReader->ReadImage(filename));
+ UpdateUnloadPriority(pitl,
+ GetMaximalPriorityWithoutLocking()+1);
+ return pitl->GetImage();
+ }
+
+ /*
+ mRequestedFilename = filename;
+ mRequestedImage = 0;
+ ImageToLoad itl(this,filename);
+ ImageMapType::iterator i = mImages.find(&itl);
+ if (i!=mImages.end())
+ {
+ // Already inserted in queue
+ if (i->first->GetImage() != 0)
+ {
+ // Already read : ok : return it
+ return i->first->GetImage();
+ }
+ /// Already requested : change the priority
+ ImageToLoadPtr pitl = const_cast<ImageToLoadPtr>(i->first);
+ pitl->SetPriority( GetMaximalPriorityWithoutLocking() + 1 );
+ pitl->SetUser( this );
+ // Already in queue
+ if (pitl->Index()>=0)
+ {
+ // Re-sort the queue
+ mQueue.upsort(pitl->Index());
+ }
+ // Not read but not in queue = being read = ok
+ else
+ {
+ pitl->SetUser( this );
+ }
+ }
+ else
+ {
+
+ // Never requested before or unloaded
+ ImageToLoadPtr pitl =
+ new ImageToLoad(this,filename,
+ GetMaximalPriorityWithoutLocking() + 1);
+ mImages[pitl] = 0;
+ mQueue.insert(pitl);
+ }
+ */
+ }
+ while (0);
+
+ // std::cout << "Waiting..."<<std::endl;
+
+ /*
+ // Waiting that it is read
+ int n = 0;
+ do
+ {
+ // std::cout << n++ << std::endl;
+ wxMilliSleep(10);
+ do
+ {
+ // wxMutexLocker lock(mMutex);
+ wxMutexLocker lock(GetMultiThreadImageReaderUserMutex());
+ if (mRequestedImage!=0)
+ {
+ return mRequestedImage;
+ }
+ }
+ while (0);
+ }
+ while (true);
+ //
+ */
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void MultiThreadImageReader::SignalImageRead(ImageToLoadPtr p,
+ bool purge)
+ {
+
+// std::cout << "MultiThreadImageReader::SignalImageRead" <<std::endl;
+ // std::cout << "this="<<this <<std::endl;
+ // std::cout << "user="<<p->GetUser() <<std::endl;
+
+ if ( p->GetUser() == this )
+ GetMultiThreadImageReaderUserMutex().Unlock();
+
+ p->GetUser()->MultiThreadImageReaderSendEvent
+ (p->GetFilename(),
+ MultiThreadImageReaderUser::ImageLoaded,
+ p->GetImage());
+
+ /*
+ AN ATTEMPT TO UNLOAD OLDEST IMAGE IF EXCEEDED A CERTAIN MEMORY QUOTA
+ BUGGY : TO FIX
+ */
+ if (!purge) return;
+ GimmickMessage(5,"Image '"<<p->GetFilename()<<"' read"<<std::endl);
+
+ // wxMutexLocker lock(GetMultiThreadImageReaderUserMutex());
+
+ mUnloadQueue.insert(p);
+ p->GetImage()->UpdateInformation();
+ p->GetImage()->PropagateUpdateExtent();
+ long ImMem = p->GetImage()->GetEstimatedMemorySize();
+ mTotalMem += ImMem;
+
+ GimmickMessage(5,"==> Image in memory = "<<mUnloadQueue.size()<<std::endl);
+ GimmickMessage(5,"==> Total mem = "<<mTotalMem<<" Ko"<<std::endl);
+
+ // return;
+
+ while (mTotalMem > mTotalMemMax)
+ {
+ GimmickMessage(5,
+ " ! Exceeded max of "
+ << mTotalMemMax << " Ko : unloading oldest image ... "
+ << std::endl);
+ if ( mUnloadQueue.size() <= 1 )
+ {
+ GimmickMessage(5,
+ " Only one image : cannot load AND unload it !!"
+ <<std::endl);
+ break;
+
+ }
+ ImageToLoadPtr unload = mUnloadQueue.remove_top();
+ MultiThreadImageReaderUser* user = unload->GetUser();
+
+ /*
+ if ((user!=0)&&(user!=this))
+ {
+ user->GetMultiThreadImageReaderUserMutex().Lock();
+ }
+ */
+
+ std::string filename = unload->GetFilename();
+
+ GimmickMessage(5,"'" << filename << "'" << std::endl);
+ mTotalMem -= unload->GetImage()->GetEstimatedMemorySize();
+
+ GimmickMessage(5," ==> Total mem = "<<mTotalMem<<" Ko "<<std::endl);
+
+ if (user!=0)
+ {
+ // std::cout << "unlock..."<<std::endl;
+ // user->GetMultiThreadImageReaderUserMutex().Unlock();
+ // std::cout << "event"<<std::endl;
+ user->MultiThreadImageReaderSendEvent
+ (filename,
+ MultiThreadImageReaderUser::ImageUnloaded,
+ 0);
+ // std::cout << "event ok"<<std::endl;
+ }
+
+ if (unload->Index()>=0)
+ {
+ // GimmickMessage(5,"still in queue"<<std::endl);
+ }
+ unload->Index() = -1;
+
+
+ ImageMapType::iterator it = mImages.find(unload);
+ if (it!=mImages.end())
+ {
+ mImages.erase(it);
+ }
+ // std::cout << "delete..."<<std::endl;
+ delete unload;
+ // std::cout << "delete ok."<<std::endl;
+
+ }
+ }
+ //=====================================================================
+
+ //=====================================================================
+ int MultiThreadImageReader::GetMaximalPriority()
+ {
+ wxMutexLocker lock(GetMultiThreadImageReaderUserMutex()); //mMutex);
+ return GetMaximalPriorityWithoutLocking();
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ int MultiThreadImageReader::GetMaximalPriorityWithoutLocking()
+ {
+ long max = 0;
+ if (mQueue.size()>0)
+ {
+ max = mQueue.top()->GetPriority();
+ }
+ if (mUnloadQueue.size()>0)
+ {
+ int max2 = mUnloadQueue.top()->GetPriority();
+ if (max2>max) max=max2;
+ }
+ return max;
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ //=====================================================================
+ //=====================================================================
+ //=====================================================================
+
+ //=====================================================================
+ void* ThreadedImageReader::Entry()
+ {
+ // std::cout << "### Thread "<<GetCurrentId()<<"::Entry()"
+ // << std::endl;
+
+ mMultiThreadImageReader->MultiThreadImageReaderSendEvent
+ ("",
+ MultiThreadImageReaderUser::ThreadedReaderStarted,
+ 0);
+
+ // While was not deleted
+ while (!TestDestroy())
+ {
+ //std::cout << "### Thread "<<GetCurrentId()<<" still alive" << std::endl;
+
+ // Lock the mutex
+ mMultiThreadImageReader->MultiThreadImageReaderEventLock();
+ //mMutex.Lock();
+ // If image in queue
+ if (mMultiThreadImageReader->mQueue.size()>0)
+ {
+ MultiThreadImageReader::ImageToLoadPtr i =
+ mMultiThreadImageReader->mQueue.remove_top();
+
+ mMultiThreadImageReader->MultiThreadImageReaderEventUnlock();
+ //mMutex.Unlock();
+
+
+ // std::cout << "### Thread "<<GetCurrentId()<<" : reading '"
+ // << i->GetFilename() << "'" << std::endl;
+
+ // Do the job
+ vtkImageData* im = Read(i->GetFilename());
+
+ // Store it in the map
+ mMultiThreadImageReader->MultiThreadImageReaderEventLock();
+ //mMutex.Lock();
+ MultiThreadImageReader::ImageToLoad itl(0,i->GetFilename());
+ MultiThreadImageReader::ImageMapType::iterator it =
+ mMultiThreadImageReader->mImages.find(&itl);
+ MultiThreadImageReader::ImageToLoadPtr
+ pitl = const_cast<MultiThreadImageReader::ImageToLoadPtr>
+ (it->first);
+ pitl->SetImage(im);
+ mMultiThreadImageReader->SignalImageRead(pitl,true);//i->GetFilename());
+ mMultiThreadImageReader->MultiThreadImageReaderEventUnlock(); //mMutex.Unlock();
+
+ // std::cout << "### Thread "<<GetCurrentId()<<" : reading '"
+ // << i->GetFilename() << "' : DONE" << std::endl;
+
+ }
+ else
+ {
+ mMultiThreadImageReader->MultiThreadImageReaderEventUnlock();
+ //mMutex.Unlock();
+ // Wait a little to avoid blocking
+ Sleep(10);
+ }
+ };
+ // std::cout << "### Thread "<<GetCurrentId()<<" stopping"
+ // << std::endl;
+
+ return 0;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void ThreadedImageReader::OnExit()
+ {
+ mMultiThreadImageReader->MultiThreadImageReaderSendEvent
+ ("",
+ MultiThreadImageReaderUser::ThreadedReaderStopped,
+ 0);
+ }
+ //=====================================================================
+
+ //=====================================================================
+ vtkImageData* ThreadedImageReader::Read(const std::string& filename)
+ {
+ return mReader.ReadImage(filename);
+ }
+ //=====================================================================
+
+} // namespace creaImageIO
--- /dev/null
+#ifndef __creaImageIOThreadedImageReader_h_INCLUDED__
+#define __creaImageIOThreadedImageReader_h_INCLUDED__
+
+#include <creaImageIOSystem.h>
+#include <creaImageIOImageReader.h>
+#include <creaImageIOIndexedHeap.h>
+#include <map>
+#include <deque>
+#include <wx/thread.h>
+#include <queue>
+
+
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup IO
+ */
+ //=====================================================================
+ class ThreadedImageReader;
+ class MultiThreadImageReader;
+ //=====================================================================
+
+ //=====================================================================
+ class CREAIMAGEIO_EXPORT MultiThreadImageReaderUser
+ {
+ public:
+ friend class ThreadedImageReader;
+ friend class MultiThreadImageReader;
+
+ MultiThreadImageReaderUser() {}
+ virtual ~MultiThreadImageReaderUser() {}
+
+ typedef enum
+ {
+ ThreadedReaderStarted,
+ ThreadedReaderStopped,
+ ImageLoaded,
+ ImageUnloaded,
+ Error
+ }
+ EventType;
+ /// The virtual method to overload by MultiThreadImageReader users
+ /// It is called when an image has been loaded or unloaded
+ /// Provides :
+ /// * The image file name which was requested
+ /// * The type of event
+ /// * If type==ImageLoaded the image pointer, else NULL pointer
+ virtual void OnMultiThreadImageReaderEvent( const std::string& filename,
+ EventType type,
+ vtkImageData* image)
+ {}
+ inline void MultiThreadImageReaderEventLock()
+ { mMultiThreadImageReaderUserMutex.Lock(); }
+ inline void MultiThreadImageReaderEventUnlock()
+ { mMultiThreadImageReaderUserMutex.Unlock(); }
+ inline wxMutex& GetMultiThreadImageReaderUserMutex()
+ { return mMultiThreadImageReaderUserMutex; }
+ private:
+ ///
+ void MultiThreadImageReaderSendEvent( const std::string& filename,
+ EventType type,
+ vtkImageData* image);
+ wxMutex mMultiThreadImageReaderUserMutex;
+ };
+ //=====================================================================
+
+ //=====================================================================
+ ///
+ /// TAKE CARE : For the moment it only supports a **SINGLE USER**
+
+ ///Class that allows parallel lectures of several images
+ class MultiThreadImageReader : public MultiThreadImageReaderUser
+ {
+ public:
+ friend class ThreadedImageReader;
+
+ /// Ctor with the number of threads to use
+ MultiThreadImageReader(int number_of_threads = 1);
+ /// Dtor
+ ~MultiThreadImageReader();
+
+ /// Starts the reader = create the threads which start to check
+ /// periodically the queue of requested images to read
+ bool Start();
+ /// Stops the reader = stops the threads and delete the images loaded
+ void Stop();
+
+ /// Request the image "filename" with a given priority
+ /// When the image is ready (or an error occurred)
+ /// The observer's callback is invoked
+ void Request( MultiThreadImageReaderUser* user,
+ const std::string& filename,
+ int priority );
+
+ /// Request the image "filename" immediately
+ /// Blocks until image loaded
+ /// (no user callback but image returned)
+ vtkImageData* GetImage(const std::string& filename);
+
+ ///
+ int GetMaximalPriority();
+
+ ///
+ void OnMultiThreadImageReaderEvent( const std::string& filename,
+ EventType type,
+ vtkImageData* image);
+
+ protected:
+ bool mDone;
+ int GetMaximalPriorityWithoutLocking();
+ ///Class that represents an image to be loaded
+ class ImageToLoad
+ {
+ public:
+ ImageToLoad( MultiThreadImageReaderUser* user,
+ const std::string& filename,
+ int prio=0)
+ : mUser(user),
+ mFilename(filename),
+ mPriority(prio),
+ mIndex(-1),
+ mUnloadIndex(-1),
+ mImage(0)
+ {}
+ ~ImageToLoad()
+ {
+ if (mImage>0)
+ {
+ // std::cout << "Refs = "<<mImage->GetReferenceCount()<<std::endl;
+ mImage->Delete();
+ }
+ }
+ MultiThreadImageReaderUser* GetUser() const { return mUser; }
+ void SetUser( MultiThreadImageReaderUser* u ) { mUser = u; }
+ const std::string& GetFilename() const { return mFilename; }
+ int GetPriority() const { return mPriority; }
+ void SetPriority(int p) { mPriority=p; }
+ int& Index() { return mIndex; }
+ int& UnloadIndex() { return mUnloadIndex; }
+ vtkImageData* GetImage() const { return mImage; }
+ void SetImage( vtkImageData* i ) { mImage=i; }
+ private:
+ MultiThreadImageReaderUser* mUser;
+ std::string mFilename;
+ int mPriority;
+ int mIndex;
+ int mUnloadIndex;
+ vtkImageData* mImage;
+ };
+ //
+
+ /// Type of pointer on an ImageToLoad struct
+ typedef ImageToLoad* ImageToLoadPtr;
+
+ /// ImageToLoadPtr comparator on priority (for image queue)
+ struct ImageToLoadPtrPriorityComparator
+ {
+ bool operator() (ImageToLoadPtr const & a, ImageToLoadPtr const & b)
+ const
+ {
+ return ( a->GetPriority() > b->GetPriority() );
+ }
+ };
+ /// ImageToLoadPtr comparator on inverse priority (for image to unload queue)
+ struct ImageToLoadPtrInversePriorityComparator
+ {
+ bool operator() (ImageToLoadPtr const & a, ImageToLoadPtr const & b)
+ const
+ {
+ return ( a->GetPriority() < b->GetPriority() );
+ }
+ };
+
+
+ /// ImageToLoadPtr comparator on filename (for image map)
+ struct ImageToLoadPtrFilenameComparator
+ {
+ bool operator() (ImageToLoadPtr const & a, ImageToLoadPtr const & b)
+ const
+ {
+ return ( a->GetFilename() < b->GetFilename() );
+ }
+ };
+
+ /// ImageToLoadPtr indexer for image queue
+ struct ImageToLoadPtrIndexer
+ {
+ int& operator()(ImageToLoadPtr & t) const { return t->Index(); }
+ };
+ /// ImageToLoadPtr indexer for to unload image queue
+ struct ImageToUnloadPtrIndexer
+ {
+ int& operator()(ImageToLoadPtr & t) const { return t->UnloadIndex(); }
+ };
+
+ /// The callback from threaded readers when an image is read
+ void SignalImageRead(ImageToLoadPtr p, bool purge);
+
+ /// The type of map of images
+ typedef std::map<ImageToLoadPtr,vtkImageData*,
+ ImageToLoadPtrFilenameComparator> ImageMapType;
+ /// The map of images
+ ImageMapType mImages;
+ /// Comparator for the image to load queue
+ ImageToLoadPtrPriorityComparator mComparator;
+ /// Indexer for the image to load queue
+ ImageToLoadPtrIndexer mIndexer;
+ /// The image to load priority queue
+ IndexedHeap<ImageToLoadPtr,
+ ImageToLoadPtrPriorityComparator,
+ ImageToLoadPtrIndexer> mQueue;
+
+ /// The type of list of threaded readers
+ typedef std::vector<boost::shared_ptr<ThreadedImageReader> > ThreadedImageReaderListType;
+ //typedef std::vector<ThreadedImageReader* > ThreadedImageReaderListType;
+ ThreadedImageReaderListType mThreadedImageReaderList;
+ /// The number of currently running threaded readers
+ int mNumberOfThreadedReadersRunning;
+ /// The mutex used to access safely internal data from any thread
+ /// LG : Removed ! We now use the embedded mutex in User from which
+ /// we inherit...
+ // wxMutex mMutex;
+
+ /// For GetImage : the filename requested
+ std::string mRequestedFilename;
+ /// For GetImage : the image requested
+ vtkImageData* mRequestedImage;
+
+ /// If number of threads == 0 then uses an internal non-threaded reader
+ ImageReader* mReader;
+
+ /// The type of list of images loaded
+ /// used to unload oldest image when memory limit exceeded
+ /// The image to unload priority queue
+ IndexedHeap<ImageToLoadPtr,
+ ImageToLoadPtrInversePriorityComparator,
+ ImageToUnloadPtrIndexer> mUnloadQueue;
+
+ void UpdateUnloadPriority(ImageToLoadPtr p, int priority);
+ long mTotalMem;
+ long mTotalMemMax;
+
+
+ }; // class MultiThreadImageReader
+ //=====================================================================
+
+
+
+} // namespace creaImageIO
+
+
+
+#endif // #ifndef __creaImageIOThreadedImageReader_h_INCLUDED__
--- /dev/null
+
+#include <creaImageIOPACSConnection.h>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+//#include <boost/asio.hpp>
+
+//using boost::asio::ip::tcp;
+
+enum { max_length = 3086 };
+using namespace std;
+namespace creaImageIO
+{
+ PACSConnection::PACSConnection(std::string command)
+ {
+ /*
+ try
+ {
+
+ boost::asio::io_service io_service;
+
+ tcp::resolver resolver(io_service);
+ tcp::resolver::query query(tcp::v4(), "localhost", "3306");
+ tcp::resolver::iterator iterator = resolver.resolve(query);
+
+ tcp::socket s(io_service);
+ s.connect(*iterator);
+
+ size_t request_length = strlen(command.c_str());
+ boost::asio::write(s, boost::asio::buffer(command.c_str(), request_length));
+
+ char reply[max_length];
+ size_t reply_length = boost::asio::read(s,
+ boost::asio::buffer(reply, request_length));
+ std::cout << "Reply is: ";
+ std::cout.write(reply, reply_length);
+ std::cout << "\n";
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+ */
+ }
+
+}
+
+
+
--- /dev/null
+#ifndef __creaImageIOPACSConnection_h_INCLUDED__
+#define __creaImageIOPACSConnection_h_INCLUDED__
+#include <string>
+
+namespace creaImageIO
+{
+class PACSConnection
+ {
+ public:
+ /// Ctor
+ PACSConnection(std::string command);
+ };// EO PACSConnection
+} // EO namespace creaImageIO
+
+// EOF
+#endif
+
--- /dev/null
+#include <creaImageIOSQLiteTreeHandler.h>
+#include <creaImageIOSystem.h>
+#include <creaImageIOGimmick.h>
+#include <creaImageIOTree.h>
+
+#include "CppSQLite3.h"
+
+#include <sys/stat.h>
+
+#include <deque>
+
+#include <creaWx.h>
+#include <boost/algorithm/string.hpp>
+using namespace crea;
+
+
+
+namespace creaImageIO
+{
+ using namespace tree;
+
+
+ //=============================================================
+ SQLiteTreeHandler::SQLiteTreeHandler(const std::string& filename)
+ : mFileName(filename)
+ {
+ mDB = new CppSQLite3DB;
+ mIsAdding=false;
+ //GimmickMessage(1,"SQLite version : " <<std::string(mDB->SQLiteVersion())<< std::endl);
+ }
+ //=============================================================
+
+ //=============================================================
+ SQLiteTreeHandler::~SQLiteTreeHandler()
+ {
+ delete mDB;
+ }
+ //=============================================================
+
+
+ //=====================================================================
+
+
+ //=====================================================================
+ bool SQLiteTreeHandler::Open(bool writable)
+ {
+ // std::cout << "***> SQLiteTreeHandler::Open('"<<GetFileName()<<"')"<<std::endl;
+ SetWritable(writable);
+ return DBOpen();
+ }
+
+ //=====================================================================
+ bool SQLiteTreeHandler::Create(bool writable)
+ {
+ // std::cout << "***> SQLiteTreeHandler::New('"<<GetFileName()<<"')"<<std::endl;
+ SetWritable(writable);
+ return DBCreate();
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool SQLiteTreeHandler::Close()
+ {
+ return true;
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool SQLiteTreeHandler::Destroy()
+ {
+ return false;
+ }
+
+ //=====================================================================
+
+ //=====================================================================
+ int SQLiteTreeHandler::LoadChildren(tree::Node* parent, int maxlevel)
+ {
+ if (parent==0) parent = GetTree().GetTree();
+ return DBLoadChildren(parent,maxlevel);
+ }
+ //=====================================================================
+
+
+
+
+ //=====================================================================
+ void SQLiteTreeHandler::UnLoad(tree::Node* n)
+ {
+ }
+ //=====================================================================
+
+ //=====================================================================
+ int SQLiteTreeHandler::AddBranch( const AttributeMapType& attr )
+ {
+ tree::Node* parent = DBGetParent(attr);
+ DBGraftToParent(parent,attr);
+ return (parent->GetLevel()+1);
+
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool SQLiteTreeHandler::Remove(tree::Node* node)
+ {
+ DBRecursiveRemoveNode(node);
+
+ // std::cout << "DELETE"<<std::endl;
+ bool remove=false;
+ tree::Node* parent=node->GetParent();
+ if (parent)
+ {
+ int nC = parent->RemoveChildrenFromList(node);
+ if(nC>0 && parent->GetLevel()>0)
+ {
+ std::stringstream out;
+ out <<nC;
+ SetAttribute(parent,"NumberOfChildren",out.str());
+ }
+ else
+ {
+ remove=true;
+ }
+
+ }
+ delete node;
+ if(remove&&parent->GetLevel()>0)
+ {
+ Remove(parent);
+ }
+ // std::cout << "DELETE OK"<<std::endl;
+ return true;
+ }
+
+ //=====================================================================
+
+ //=====================================================================
+ /// Sets an attribute of a Node
+ bool SQLiteTreeHandler::SetAttribute(tree::Node* n,
+ const std::string& key,
+ const std::string& value)
+ {
+ if (n==0) n=GetTree().GetTree();
+ return DBSetAttribute(n,key,value);
+ }
+ //=====================================================================
+ //=====================================================================
+ /// Sets an attribute
+ void SQLiteTreeHandler::SetAttribute(const std::string& levelDescriptor,
+ const std::string& key,
+ const std::string& value,
+ const std::string& searchParam,
+ const std::string& searchVal)
+ {
+ DBSetAttribute(levelDescriptor,key,value,searchParam, searchVal);
+ }
+ //=====================================================================
+ /// Deletes a tuple
+ void SQLiteTreeHandler::DeleteTuple(std::string levelDescriptor,
+ std::string key, std::string value)
+ {
+ DBDelete(levelDescriptor,key,value);
+ }
+ //=====================================================================
+
+
+
+
+
+
+
+
+
+
+
+
+
+ //=====================================================================
+ // SQLite DB specific methods
+ //=====================================================================
+
+
+
+
+ //=====================================================================
+ char* format_sql(const std::string& s)
+ {
+ return sqlite3_mprintf("%q",s.c_str());
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+#define QUERYDB(QUER,RES) \
+ try \
+ { \
+ GimmickMessage(2,"SQL query: '"<<QUER<<"'"<<std::endl); \
+ RES = mDB->execQuery(QUER.c_str()); \
+ } \
+ catch (CppSQLite3Exception& e) \
+ { \
+ GimmickError("SQLite query '"<<QUER<<"' : " \
+ << e.errorCode() << ":" \
+ << e.errorMessage() ); \
+ } \
+
+ //=====================================================================
+
+ //=====================================================================
+#define UPDATEDB(UP) \
+ try \
+ { \
+ GimmickMessage(2,"SQL update: '"<<UP<<"'"<<std::endl); \
+ mDB->execDML(UP.c_str()); \
+ } \
+ catch (CppSQLite3Exception& e) \
+ { \
+ GimmickError("SQLite update '"<<UP<<"' Error : " \
+ << e.errorCode() << ":" \
+ << e.errorMessage() ); \
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool SQLiteTreeHandler::DBOpen()
+ {
+ GimmickMessage(1,"Opening SQLite database '"<<GetFileName()
+ <<"' ... "<<std::endl);
+ // OPENING FILE
+ if (!boost::filesystem::exists(GetFileName()))
+ {
+ return false;
+ }
+
+ try
+ {
+ mDB->open(GetFileName().c_str());
+ }
+ catch (CppSQLite3Exception& e)
+ {
+ GimmickError("Opening '"<<GetFileName()<<"' : "
+ << e.errorCode() << ":"
+ << e.errorMessage());
+ return false;
+ }
+ // IMPORT TREE DESCRIPTION (AND TEST DB VALIDITY)
+ if (!DBImportTreeDescription())
+ {
+ return false;
+ }
+
+ GimmickDebugMessage(1,"Opening SQLite database '"<<GetFileName()
+ <<"' ... OK"<<std::endl);
+ return true;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ bool SQLiteTreeHandler::DBCreate()
+ {
+ GimmickMessage(1,"Creating SQLite database '"<<GetFileName()
+ <<"' ... "<<std::endl);
+
+ if (boost::filesystem::exists(GetFileName()))
+ {
+ GimmickError(GetFileName()<<"' : "
+ << "file already exists");
+ return false;
+ }
+
+ // OPENING
+ try
+ {
+ mDB->open(GetFileName().c_str());
+ }
+ catch (CppSQLite3Exception& e)
+ {
+ GimmickError(e.errorCode() << ":"
+ << e.errorMessage() <<std::endl);
+ return false;
+ }
+
+
+ // CREATING TABLES
+
+ std::string command;
+ // Create LEVELS table
+ command = "create table LEVELS\n";
+ command += "( Name text )\n";
+ UPDATEDB(command);
+ int l;
+ // Iterate the Levels
+ for (l=0; l<GetTree().GetNumberOfLevels(); l++)
+ {
+ command = "INSERT INTO LEVELS (Name) VALUES ('";
+ command += GetTree().GetLevelDescriptor(l).GetName();
+ command += "')";
+ UPDATEDB(command);
+
+ // Create table of level (for level>0, i.e. not Root)
+ if (l>=0)
+ {
+ command = "CREATE TABLE ";
+ command += GetTree().GetLevelDescriptor(l).GetName();
+ command += "\n(\nID INTEGER PRIMARY KEY";
+ if (l>1)
+ {
+ command += ",\nPARENT_ID int not null";
+ }
+ SQLAppendAttributesDefinition(l,command);
+ if (l>1)
+ {
+ command += ",\nconstraint FK_PARENT foreign key (PARENT_ID) references ";
+ command += GetTree().GetLevelDescriptor(l-1).GetName();
+ command += "(ID) on delete restrict on update restrict";
+ }
+ command += "\n)";
+ UPDATEDB(command);
+
+
+ // Add Attribute 'ID' to Description
+ GetTree().GetDescriptor().Add
+ (AttributeDescriptor( "ID",
+ "Database Identifier",
+ 0,0,
+ AttributeDescriptor::PRIVATE
+ ),l);
+
+ if (l>1)
+ {
+ // Add Attribute 'PARENT_ID' to Description
+ GetTree().GetDescriptor().Add
+ (AttributeDescriptor( "PARENT_ID",
+ "Database Parent Identifier",
+ 0,0,
+ AttributeDescriptor::PRIVATE
+ ),l);
+ }
+
+ }
+
+ // Create table *_ATTRIBUTES
+
+ command = "CREATE TABLE ";
+ command += GetTree().GetLevelDescriptor(l).GetName();
+ command += "_Attributes\n(\n";
+ command += "Key text,\n";
+ command += "Name text,\n";
+ command += "DicomGroup int,\n";
+ command += "DicomElement int,\n";
+ command += "Flags int\n";
+ command += "\n)";
+ UPDATEDB(command);
+
+ // Fill the table *_ATTRIBUTES
+ LevelDescriptor::AttributeDescriptorListType::const_iterator i;
+ for (i = GetTree().GetAttributeDescriptorList(l).begin();
+ i != GetTree().GetAttributeDescriptorList(l).end();
+ ++i)
+ {
+
+ std::stringstream insert;
+ insert << "INSERT INTO "
+ << GetTree().GetLevelDescriptor(l).GetName()
+ << "_Attributes (Key,Name,DicomGroup,DicomElement,Flags) "
+ << "VALUES ('"
+ << i->GetKey() << "','"
+ << i->GetName() << "',"
+ << i->GetGroup() << ","
+ << i->GetElement() << ","
+ << i->GetFlags() << ");";
+ UPDATEDB(insert.str());
+ }
+
+ } // For l=0...
+
+ // Initialize the root attributes
+ GetTree().InitializeAttributeMap();
+ // Insert the root in the level 0 table
+ DBInsert(GetTree().GetTree());
+
+
+ GetTree().SetChildrenLoaded(true);
+ GimmickMessage(1,"Creating SQLite database '"<<GetFileName()
+ <<"' ... OK"<<std::endl);
+ return true;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void SQLiteTreeHandler::SQLAppendAttributesDefinition(int level,
+ std::string& s)
+ {
+ LevelDescriptor::AttributeDescriptorListType::const_iterator i;
+ for (i = GetTree().GetAttributeDescriptorList(level).begin();
+ i != GetTree().GetAttributeDescriptorList(level).end();
+ ++i)
+ {
+ // if (i->second.flags==1) continue;
+ s += ",\n";
+ s += i->GetKey();
+ s += " text";
+ }
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool SQLiteTreeHandler::DBImportTreeDescription()
+ {
+ GimmickMessage(1,"Importing tree description for database ..."
+ <<std::endl);
+
+ // Test table 'LEVELS' existence
+ if ( ! mDB->tableExists("LEVELS") )
+ {
+ GimmickMessage(1,"!! ERROR : Table 'LEVELS' does not exist"
+ <<std::endl);
+ return false;
+ }
+
+ tree::Descriptor& desc = GetTree().GetDescriptor();
+ // clears the existing one
+ desc.Clear();
+
+ int nblevel = 0;
+ std::string query = "SELECT * FROM LEVELS";
+ CppSQLite3Query q;
+ QUERYDB(query,q);
+
+ while (!q.eof())
+ {
+ std::string name = q.getStringField(0);
+ GimmickMessage(2," * Importing level '"<<name<<"'"<<std::endl);
+ desc.Add(LevelDescriptor(name));
+ nblevel++;
+ q.nextRow();
+ }
+
+ for (int level = 0; level < nblevel; ++level )
+ {
+ std::string table = GetTree().GetLevelDescriptor(level).GetName();
+ table += "_Attributes";
+ // Test table 'LEVELS' existence
+ if ( ! mDB->tableExists(table.c_str()) )
+ {
+ GimmickMessage(1,"!! ERROR : Table '"<<table<<"' does not exist"
+ <<std::endl);
+ return false;
+ }
+
+ std::string query = "SELECT * FROM ";
+ query += table;
+ CppSQLite3Query q;
+ QUERYDB(query,q);
+
+ GimmickMessage(2," * Level '"
+ <<GetTree().GetLevelDescriptor(level).GetName()
+ <<"'"<<std::endl);
+
+ // Test that ID and PARENT_ID mandatory attributes exist
+ bool ID_found = false;
+ bool PARENT_ID_found = false;
+ if (level==0) ID_found = true;
+ if (level<=1) PARENT_ID_found = true;
+
+ while (!q.eof())
+ {
+ std::string key(q.getStringField(0));
+ std::string name(q.getStringField(1));
+ GimmickMessage(2," - Importing attribute '"<<key<<"' '"<<name
+ <<"'"<<std::endl);
+ desc.Add
+ (AttributeDescriptor( key, // Key
+ name, // Name
+ q.getIntField(2), // Group
+ q.getIntField(3), // Element
+ q.getIntField(4) // Flags
+ ),level);
+ if ( key == "ID" )
+ {
+ ID_found = true;
+ }
+ if ( key == "PARENT_ID" )
+ {
+ PARENT_ID_found = true;
+ }
+ q.nextRow();
+ }
+
+ if ( ! (ID_found || PARENT_ID_found ) )
+ {
+ GimmickMessage(1,"!! ERROR : Table '"<<table
+ <<"' does not contain mandatory attribute ID or PARENT_ID"
+ <<std::endl);
+ return false;
+
+ }
+ }
+
+
+ // Create the attributes table for Root (i.e. Tree)
+ LevelDescriptor::AttributeDescriptorListType::const_iterator a;
+ for (a = GetTree().GetAttributeDescriptorList(0).begin();
+ a!= GetTree().GetAttributeDescriptorList(0).end();
+ ++a)
+ {
+
+ GetTree().UnsafeSetAttribute( a->GetKey(), "" );
+ }
+
+ // Reading Root attributes
+ // Query DB
+ query = "SELECT * FROM ";
+ query += GetTree().GetLevelDescriptor(0).GetName();
+ QUERYDB(query,q);
+
+ for (int fld = 0; fld < q.numFields(); fld++)
+ {
+ GetTree().UnsafeSetAttribute(q.fieldName(fld),
+ q.getStringField(fld));
+ }
+
+ GimmickMessage(1,"Importing tree description from database ... OK"
+ <<std::endl);
+ return true;
+ }
+ //=====================================================================
+
+ //========================================================================
+ ///
+ void SQLformat(std::string i_str, std::string &o_str)
+ {
+ // quote must be doubled
+ boost::algorithm::replace_all(i_str,"'","''");
+ // Found strange strings which contained NULL char INSIDE string
+ int i,size=i_str.size();
+ for (i=0;i<size;++i)
+ {
+ if (i_str[i]==0)
+ {
+ i_str = i_str.substr(0,i);
+ break;
+ }
+ }
+ o_str = i_str;
+ }
+ //========================================================================
+
+ //=====================================================================
+ void SQLiteTreeHandler::SQLAppendAttributesValues(tree::Node* n,
+ std::string& str)
+ {
+ GimmickMessage(4,"SQLAppendAttributesValues"<<std::endl);
+ std::string atts="";
+ std::string values="";
+ std::string out ="";
+ tree::Node::AttributeMapType::iterator i;
+ for (i = n->GetAttributeMap().begin();
+ i != n->GetAttributeMap().end();
+ i++)
+ {
+ if (i->first=="ID")
+ {
+ continue;
+ }
+
+ atts += "'" + i->first + "'";
+ SQLformat(i->second, out);
+ values += "'" + out + "'";
+ atts += ",";
+ values += ",";
+ GimmickMessage(4,"'"<<i->first<<"' = '"<<i->second<<"'"<<std::endl);
+ }
+ atts[atts.size()-1]=' ';
+ values[values.size()-1]=' ';
+
+ str = "("+atts+") VALUES ("+values+")";
+ GimmickMessage(4,"Result = '"<<str<<"'"<<std::endl);
+ }
+ //=====================================================================
+
+ //=====================================================================
+ tree::Node* SQLiteTreeHandler::DBGetParent( const AttributeMapType& attr)
+ {
+ Node* parent = GetTree().GetTree();
+ bool go_down;
+ do
+ {
+ go_down = false;
+ // Load the children of the current parent
+ DBLoadChildren(parent);
+ // Iterate the children
+ tree::Node::ChildrenListType::const_iterator i;
+ for (i = parent->GetChildrenList().begin();
+ i!= parent->GetChildrenList().end();
+ ++i)
+ {
+ if ( (*i)->Matches( attr ) )
+ {
+ go_down = true;
+ parent = *i;
+ break;
+ }
+ }
+ }
+ while (go_down);
+ return parent;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ int SQLiteTreeHandler::DBLoadChildren(tree::Node* node,
+ int numberoflevels)
+ {
+ if (node->GetLevel()+1 >= node->GetTree()->GetNumberOfLevels() )
+ return 0;
+
+ GimmickMessage(2,"Loading children of '"<<node->GetLabel()
+ <<"'"<<std::endl);
+
+ int nbloaded = 0;
+ // If children loaded we do not have to do it but we need to recurse
+ // in order to load the children's children if necessary, and so on...
+ if (node->GetChildrenLoaded())
+ {
+ // Iterate the children
+
+ tree::Node::ChildrenListType::iterator i;
+ for (i = node->GetChildrenList().begin();
+ i!= node->GetChildrenList().end();
+ ++i)
+ {
+ nbloaded += DBLoadChildren(*i,numberoflevels-1);
+ }
+ node->SetChildrenLoaded(true);
+ return nbloaded;
+
+ }
+ else
+ {
+ /// If children not loaded : do it and recurse
+
+ // Query DB
+ int level = node->GetLevel();
+ std::string query = "SELECT * FROM ";
+
+ query += GetTree().GetLevelDescriptor(level+1).GetName();
+ if (level>0)
+ {
+ query += " WHERE PARENT_ID='" + node->GetAttribute("ID")
+ + "'";
+ }
+GimmickDebugMessage(1, "query : '" <<query <<std::endl);
+ CppSQLite3Query q;
+ QUERYDB(query,q);
+
+ int p=0;
+ while (!q.eof())
+ {
+
+ // std::cout << "DBLoadCh : creating node level "<<level+1<<std::endl;
+
+ nbloaded++;
+ Node* n = new Node(node);
+ for (int fld = 0; fld < q.numFields(); fld++)
+ {
+ n->UnsafeSetAttribute(q.fieldName(fld),q.getStringField(fld));
+ }
+
+ // recurse
+ if ( numberoflevels != 1 )
+ {
+ // msw[2].Pause();
+ nbloaded += DBLoadChildren(n, numberoflevels-1);
+ // msw[2].Resume();
+ }
+ // next entry in db
+ q.nextRow();
+ }
+
+ node->SetChildrenLoaded(true);
+
+
+ // msw[2].Pause();
+ return nbloaded;
+ }
+ }
+ //=====================================================================
+
+ //======================================================================
+ void SQLiteTreeHandler::DBInsert(tree::Node* n)
+ {
+ GimmickMessage(2,"Inserting in DB '"<<n->GetLabel()
+ <<"'"<<std::endl);
+ std::string val;
+ SQLAppendAttributesValues(n,val);
+ std::string insert("INSERT INTO ");
+ insert += GetTree().GetLevelDescriptor(n->GetLevel()).GetName();
+ insert += " " + val + ";";
+
+ UPDATEDB(insert);
+
+ // Store DB id of newly created node;
+ long lastrow = mDB->lastRowId();
+ std::stringstream ri;
+ ri << mDB->lastRowId();
+ n->SetAttribute("ID",ri.str());
+ }
+ //======================================================================
+
+ //======================================================================
+ /// Graft the branch defined by the attributes to the parent
+ void SQLiteTreeHandler::DBGraftToParent( tree::Node* parent,
+ const AttributeMapType& attr)
+ {
+ // std::cout <<"Grafting to parent '"<<parent->GetLabel()
+ // <<"'"<<std::endl;
+
+ for (int level = parent->GetLevel()+1;
+ level < GetTree().GetNumberOfLevels();
+ level++)
+ {
+ // Create Node
+ tree::Node* child = new tree::Node(parent,attr);
+ child->SetChildrenLoaded(true);
+ if (level>1)
+ {
+ int nc = GetNumberOfChildren(parent)+1;
+
+ // std::cout<<"Number of children "<<parent->GetNumberOfChildren()<<std::endl;
+ std::stringstream out;
+ out << nc;
+ SetAttribute(parent,"NumberOfChildren",out.str());
+ }
+
+ // Set PARENT_ID if necessary
+ if ( parent->GetLevel()>0 )
+ child->SetAttribute("PARENT_ID",parent->GetAttribute("ID"));
+
+ // Insert in DB
+ DBInsert(child);
+
+ // Down one level
+ parent = child;
+ }
+ }
+ //======================================================================
+
+
+ //=====================================================================
+ /// Sets an attribute of a Node
+ bool SQLiteTreeHandler::DBSetAttribute(tree::Node* n,
+ const std::string& key,
+ const std::string& value)
+ {
+ GimmickMessage(3,"Setting Attribute of '"<<n->GetLabel()<<
+ "' "<<key<<"='"<<value<<"'"<<std::endl);
+
+ n->SetAttribute(key,value);
+ std::string sql = "UPDATE ";
+ sql += GetTree().GetLevelDescriptor(n->GetLevel()).GetName();
+ sql += " SET ";
+ sql += key;
+ sql += " = '";
+ sql += value;
+ sql += "' WHERE ID = '";
+ sql += n->GetAttribute("ID");
+ sql +="'";
+ // sql += " LIMIT 1";
+ UPDATEDB(sql);
+ return true;
+ }
+
+ //=====================================================================
+ /// Sets an attribute of a Node
+ void SQLiteTreeHandler::DBSetAttribute(const std::string& levelDescriptor,
+ const std::string& key,
+ const std::string& value,
+ const std::string& searchParam,
+ const std::string& searchVal)
+ {
+
+ std::string sql = "UPDATE ";
+ sql += levelDescriptor;
+ sql += " SET ";
+ sql += key;
+ sql += " = '";
+ sql += value;
+ sql += "' WHERE ";
+ sql += searchParam;
+ sql += " = '";
+ sql += searchVal;
+ sql += "'";
+ std::cout<<sql<<std::endl;
+ UPDATEDB(sql);
+ }
+ //=====================================================================
+ void SQLiteTreeHandler::DBRecursiveRemoveNode(Node* node)
+ {
+
+ std::string query = "DELETE FROM ";
+
+
+ query += GetTree().GetLevelDescriptor(node->GetLevel()).GetName();
+
+ query += " WHERE ID='"+ node->GetAttribute("ID") + "';";
+
+ UPDATEDB(query);
+ GimmickDebugMessage(2,
+ " Deleting '"
+ <<node->GetLabel()<<"' with ID '"
+ <<node->GetAttribute("ID")
+ <<"' in level "<< GetTree().GetLevelDescriptor(node->GetLevel()).GetName()
+ <<std::endl);
+
+
+ if(node->GetNumberOfChildren()!=0)
+ {
+ Node::ChildrenListType::iterator i;
+ for (i = node->GetChildrenList().begin();
+ i != node->GetChildrenList().end();
+ i++)
+ {
+ DBRecursiveRemoveNode((*i));
+ }
+ }
+ else if(node->GetLevel()<GetTree().GetNumberOfLevels()-1)
+ {
+ DBRecursiveRemoveNode(node->GetLevel()+1,node->GetAttribute("ID"));
+ }
+ }
+
+ //=====================================================================
+ void SQLiteTreeHandler::DBRecursiveRemoveNode(int level, std::string parentId)
+ {
+ std::stringstream out;
+ std::stringstream result;
+ out<<"SELECT ID FROM "<<GetTree().GetLevelDescriptor(level).GetName()<<" WHERE PARENT_ID='"<<parentId<<"'";
+
+ CppSQLite3Query q;
+ QUERYDB(out.str(),q);
+
+ while (!q.eof())
+ {
+ for (int fld = 0; fld < q.numFields(); fld++)
+ {
+ result<<q.getStringField(fld)<<"#";
+ }
+ q.nextRow();
+ }
+ std::string res=result.str();
+ size_t ini=0;
+ size_t fin=0;
+ while(fin<res.size()-1)
+ {
+ fin=res.find('#',ini);
+ DBDelete(GetTree().GetLevelDescriptor(level).GetName(),"ID",res.substr(ini,fin-ini));
+ if(level<GetTree().GetNumberOfLevels()-1)
+ {
+ DBRecursiveRemoveNode(level+1,res.substr(ini,fin-ini));
+ }
+ ini=fin+1;
+ }
+
+
+ }
+
+ //=====================================================================
+ void SQLiteTreeHandler::DBDelete(std::string levelDescriptor, std::string key, std::string value)
+ {
+
+ std::stringstream query;
+ query<<"DELETE FROM "<<levelDescriptor<<" WHERE "<<key<<"='"<<value<<"';";
+
+ UPDATEDB(query.str());
+ GimmickDebugMessage(2," Deleting: Query: "<<query.str()<<std::endl);
+ }
+
+
+ //=====================================================================
+ void SQLiteTreeHandler::GetAttribute(std::string levelDescriptor,
+ std::string searchParam,
+ std::string searchVal,
+ std::string key,
+ std::string& result)
+ {
+ std::stringstream out;
+ std::stringstream results;
+ out<<"SELECT "<<key<<" FROM "<<levelDescriptor;
+ if(searchParam!="")
+ {
+ out<<" WHERE "<<searchParam<<"='"<<searchVal<<"'";
+ }
+
+ CppSQLite3Query q;
+ QUERYDB(out.str(),q);
+
+
+ while (!q.eof())
+ {
+ for (int fld = 0; fld < q.numFields(); fld++)
+ {
+ results<<q.getStringField(fld);
+ if(searchParam=="")
+ {
+ results<<"#";
+ }
+ }
+ q.nextRow();
+ }
+ result=results.str();
+
+ }
+ //=====================================================================
+ unsigned int SQLiteTreeHandler::GetNumberOfChildren(tree::Node* n)
+ {
+ // Query DB
+ int nb=0;
+ int level = n->GetLevel();
+
+ if(level<GetTree().GetNumberOfLevels()&& level>0)
+ {
+ std::string query = "SELECT NumberOfChildren FROM ";
+ query += GetTree().GetLevelDescriptor(level).GetName();
+ if (level>0)
+ {
+ query += " WHERE ID='" + n->GetAttribute("ID")
+ + "'";
+ }
+ CppSQLite3Query q;
+ QUERYDB(query,q);
+
+
+ while (!q.eof())
+ {
+ for (int fld = 0; fld < q.numFields(); fld++)
+ {
+ nb=q.getIntField(fld);
+ }
+ q.nextRow();
+ }
+ }
+ /*
+ if(nb==0)
+ {
+ nb=1;
+ }
+ */
+ return nb;
+ }
+
+ //=====================================================================
+ void SQLiteTreeHandler::GetTopLevelNodeId(const std::string& searchParam, const std::string& searchValue, std::string& parent_id)
+ {
+ int level=GetTree().GetNumberOfLevels()-1;
+ std::string sp=searchParam.c_str();
+ std::string sv=searchValue.c_str();
+
+ while(level>1)
+ {
+ std::stringstream out;
+ std::stringstream results;
+ out<<"SELECT PARENT_ID FROM "<<GetTree().GetLevelDescriptor(level).GetName();
+ out<<" WHERE "<<sp<<"='"<<sv<<"'";
+ CppSQLite3Query q;
+ QUERYDB(out.str(),q);
+
+
+ while (!q.eof())
+ {
+ for (int fld = 0; fld < q.numFields(); fld++)
+ {
+ results<<q.getStringField(fld);
+ }
+ q.nextRow();
+ }
+ level=level-1;
+ sp="ID";
+ sv=results.str();
+ }
+ parent_id=sv;
+
+ }
+
+ //=====================================================================
+ void SQLiteTreeHandler::RemoveEntries(const std::string i_table,
+ const std::string i_attribute,
+ const std::string i_operand,
+ const std::string i_val)
+ {
+ std::stringstream query;
+ query<<"DELETE FROM "<<i_table<<" WHERE "<<i_attribute<<" "<<i_operand<<" '"<<i_val<<"'";
+ UPDATEDB(query.str());
+ }
+
+ //=====================================================================
+ void SQLiteTreeHandler::BeginTransaction()
+ {
+ std::stringstream out;
+ out<<"begin transaction;";
+ UPDATEDB(out.str());
+ }
+
+ //=====================================================================
+ void SQLiteTreeHandler::EndTransaction()
+ {
+ std::stringstream out;
+ out<<"commit transaction;";
+ UPDATEDB(out.str());
+ }
+
+} // namespace creaImageIO
--- /dev/null
+#ifndef __creaImageIOSQLiteTreeHandler_h_INCLUDED__
+#define __creaImageIOSQLiteTreeHandler_h_INCLUDED__
+
+#include <creaImageIOTreeHandler.h>
+
+class CppSQLite3DB;
+
+namespace creaImageIO
+{
+
+
+ /**
+ * \ingroup Model
+ */
+ //=======================================================================
+ /// Concrete TreeHandler which manages a Tree stored in a sqlite database
+ class SQLiteTreeHandler : virtual public TreeHandler
+ {
+ public:
+ //====================================================================
+ /// Ctor with database file name
+ SQLiteTreeHandler(const std::string& filename);
+ /// Dtor
+ virtual ~SQLiteTreeHandler();
+ //====================================================================
+
+ //====================================================================
+ /// Returns the sqlite db file name
+ const std::string& GetFileName() const { return mFileName; }
+ //====================================================================
+
+ //====================================================================
+ // QUERY METHODS
+ /// Is the 'source' readable ?
+ virtual bool IsReadable() { return true; }
+ /// Is the 'source' writable ?
+ virtual bool IsWritable() { return true; }
+ //====================================================================
+
+
+ //====================================================================
+ // INITIALIZATION / FINALIZATION
+ //====================================================================
+
+ //====================================================================
+ /// Opens an existing 'source'
+ // Default mode is read only
+ // If IsWritable and writable==true then opens in read/write mode
+ virtual bool Open(bool writable = false);
+ /// Closes the 'source'
+ virtual bool Close();
+ /// Creates a new 'source'
+ // Default mode is read only
+ // If IsWritable and writable==true then opens in read/write mode
+ virtual bool Create(bool writable = false);
+ /// Destroys the 'source'
+ virtual bool Destroy();
+ /// Begins a transaction
+ virtual void BeginTransaction();
+ ///Commits results and ends transaction
+ virtual void EndTransaction();
+ //====================================================================
+
+
+ //====================================================================
+ // READ METHODS
+ //====================================================================
+
+
+ //====================================================================
+ /// Returns the number of children of the Node *WITHOUT LOADING THEM*
+ // REM : The Tree itself is a Node and asking for its number of
+ // children returns the number of children of level 1.
+ virtual unsigned int GetNumberOfChildren(tree::Node* n);
+ //====================================================================
+
+ //====================================================================
+ /// Returns the attribute requested. Useful for synchronization.
+ virtual void GetAttribute(std::string levelDescriptor,
+ std::string searchParam,
+ std::string searchVal,
+ std::string key,
+ std::string& result);
+ //====================================================================
+
+
+ //====================================================================
+ /// Recursively loads the children of node 'parent' until maxlevel
+ // is reached.
+ // If parent == NULL or parent == tree then starts with the 'children' of
+ // the tree itself.
+ // Returns the total number of children loaded.
+ virtual int LoadChildren(tree::Node* parent, int maxlevel);
+ //====================================================================
+
+ //====================================================================
+ /// Unloads the Node and its descendants
+ // WITHOUT altering the source, e.g. the database
+ virtual void UnLoad(tree::Node* n);
+ ///====================================================================
+
+ //====================================================================
+ /// Returns the top level node id for the given search param and search value
+ virtual void GetTopLevelNodeId(const std::string& searchParam,
+ const std::string& searchValue,
+ std::string& parent_id);
+ ///====================================================================
+
+ //====================================================================
+ // WRITE METHODS : WORK ONLY IN WRITE MODE
+ //====================================================================
+ /// Adds a branch in the tree with the attributes provided
+ // returns the Level in the tree where the branch was connected
+ // (-1 for error, 0 for top level, etc. )
+ // Of course the branch is loaded on exit
+ virtual int AddBranch( const AttributeMapType& attr );
+ // Removes the node and its descendants
+ virtual bool Remove(tree::Node*);
+ // Sets an attribute of a Node
+ virtual bool SetAttribute(tree::Node*,
+ const std::string& key,
+ const std::string& value);
+ // Sets an attribute
+ virtual void SetAttribute(const std::string& levelDescriptor,
+ const std::string& key,
+ const std::string& value,
+ const std::string& searchParam,
+ const std::string& searchVal);
+ //Deletes the tuple that matches the parameters given
+ virtual void DeleteTuple(std::string levelDescriptor, std::string key, std::string value);
+ //Deletes the entries that match the parameters given
+ virtual void RemoveEntries(const std::string i_table,
+ const std::string i_attribute,
+ const std::string i_operand,
+ const std::string i_val);
+
+ //====================================================================
+
+
+
+ protected:
+ //======================================================================
+ /// Open the database
+ bool DBOpen();
+ /// Import the Tree::Description from database (verifies the structure)
+ bool DBImportTreeDescription();
+ //======================================================================
+ //======================================================================
+ // Creation
+ /// Creates a new database on disk and the tables
+ bool DBCreate();
+ /// Appends to string s the SQL command to create the attributes of a given level
+ void SQLAppendAttributesDefinition(int level, std::string& s);
+ //======================================================================
+
+ //======================================================================
+
+ /// Returns the parent to which the branch defined by the attributes
+ // provided must be grafted
+ tree::Node* DBGetParent( const AttributeMapType& attr);
+ //======================================================================
+
+ //======================================================================
+
+ /// Loads the children of Node parent
+ // Can recurse to numberoflevels levels
+ // \return The total number of Node loaded (may be at different levels)
+ int DBLoadChildren( tree::Node* parent, int numberoflevels = 1);
+ //======================================================================
+
+ //======================================================================
+
+ /// Appends to string s the SQL command to set the attributes values
+ // of node n
+ void SQLAppendAttributesValues(tree::Node* n, std::string& s);
+ //======================================================================
+
+ //======================================================================
+
+ /// Graft the branch defined by the attributes to the parent
+ void DBGraftToParent( tree::Node* parent, const AttributeMapType& attr);
+ //======================================================================
+ //======================================================================
+
+ /// Sets an attribute of a Node and updates the database
+ bool DBSetAttribute(tree::Node*,
+ const std::string& key,
+ const std::string& value);
+ //======================================================================
+ //======================================================================
+ /// Sets an attribute and updates the database
+ void DBSetAttribute(const std::string& levelDescriptor,
+ const std::string& key,
+ const std::string& value,
+ const std::string& searchParam,
+ const std::string& searchVal);
+ //======================================================================
+ //======================================================================
+
+ /// Inserts the Node in the database
+ void DBInsert(tree::Node* n);
+ //======================================================================
+
+
+ //======================================================================
+
+ /// Deletes the tuple that matches the value specified in the given key and that belongs to the given level
+ void DBDelete(std::string levelDescriptor, std::string key, std::string value);
+ //======================================================================
+
+ //======================================================================
+
+ /// Recursively Removes the nodes whose parent is given as a parameter
+ void DBRecursiveRemoveNode(tree::Node* node);
+ /// Recursively Removes the nodes found in the given level with the given parent id
+ void DBRecursiveRemoveNode(int level, std::string parentId);
+
+ //======================================================================
+
+
+ private:
+ /// The DB
+ CppSQLite3DB* mDB;
+ /// The physical location associated to the DicomDatabase (directory, db file...)
+ std::string mFileName;
+ /// Is the DB writable ?
+ bool mWritable;
+ void SetWritable(bool w) { mWritable = w; }
+ bool GetWritable() const { return mWritable; }
+ bool mIsAdding;
+
+
+ };
+ // EO class SQLiteTreeHandler
+ //=======================================================================
+
+
+} // EO namespace creaImageIO
+
+// EOF
+#endif
+
--- /dev/null
+#include <creaImageIOSettings.h>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <iostream>
+#include <fstream>
+
+// Memory tracking allocation
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+using namespace boost;
+namespace po = boost::program_options;
+
+namespace creaImageIO
+{
+ Settings::Settings(const std::string i_path)
+ {
+ //need to position path in user directory first.
+ m_SettingsFileName = i_path + "\\.gimmick\\Shared\\gimmick\\app.config";
+ //Test if Settings File exist
+ if(!boost::filesystem::exists(m_SettingsFileName) )
+ {
+ createFile();
+ }
+ std::ifstream ifs(m_SettingsFileName.c_str());
+ std::string line;
+ std::string sets;
+ if (ifs.is_open())
+ {
+ while (! ifs.eof() )
+ {
+ getline(ifs,line);
+ sets += line;
+ }
+ ifs.close();
+ }
+ std::vector<std::string> Keys;
+ Keys.push_back(SETTINGS_SYNC_EVENT);
+ Keys.push_back(SETTINGS_DBPATH);
+ Keys.push_back(SETTINGS_SYNC_FREQ);
+ Keys.push_back(SETTINGS_COPY_PATH);
+ Keys.push_back(SETTINGS_REMOVE_PATIENT_DISPLAY);
+ readSettings(Keys, sets);
+
+ }
+
+ Settings::~Settings()
+ {
+
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // create the config file //
+ //@param : - //
+ // return : - //
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ void Settings::createFile()
+ {
+ m_SettingsMap[SETTINGS_SYNC_FREQ] = "12";
+ m_SettingsMap[SETTINGS_SYNC_EVENT] = "end";
+ m_SettingsMap[SETTINGS_DBPATH] = "";
+ m_SettingsMap[SETTINGS_DICOM_LIBRARY] = "gdcm";
+ m_SettingsMap[SETTINGS_COPY_PATH] = m_SettingsFileName.substr(0,m_SettingsFileName.find_last_of('\\')+1)+"Copied files";
+ m_SettingsMap[SETTINGS_REMOVE_PATIENT_DISPLAY] = "0";
+ writeSettingsFile();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // read Settings from config file //
+ // @param i_keys : list of keys //
+ // @param i_file : text from config file //
+ // return : -
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ void Settings::readSettings(std::vector<std::string> &i_Keys, const std::string &i_file)
+ {
+ std::vector<std::string>::iterator it_key = i_Keys.begin();
+ for(; it_key< i_Keys.end(); ++it_key)
+ {
+ size_t fpos = i_file.find(it_key->c_str());
+ size_t lpos = i_file.rfind(it_key->c_str());
+ if(fpos != std::string::npos && lpos != std::string::npos)
+ {
+ m_SettingsMap[it_key->c_str()] = i_file.substr(fpos + it_key->size(),lpos-fpos - it_key->size());
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // Update settings in config file //
+ // @param key : Key to update //
+ // @param value: New value to set //
+ // return : -
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ void Settings::updateSetting(const std::string& key, const std::string &val)
+ {
+ m_SettingsMap[key.c_str()] = val;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // add a path to a DB //
+ // @param i_path : DB path to add //
+ // return : - //
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ void Settings::addDB(const std::string &i_path)
+ {
+ if(m_SettingsMap[SETTINGS_DBPATH].find(i_path) == std::string::npos)
+ {
+ m_SettingsMap[SETTINGS_DBPATH] += i_path + ";";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // remove a path to a DB //
+ // @param i_path : DB path to delete (don't exist anymore) //
+ // return : -
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ void Settings::removeDB(const std::string &i_path)
+ {
+ boost::algorithm::replace_all(m_SettingsMap[SETTINGS_DBPATH],i_path + ";","");
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // write Settings buffer from //
+ // @param o_file : settings buffer //
+ // //
+ // return : - //
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ void Settings::writeSettings(std::ofstream &o_file)
+ {
+ std::map<std::string, std::string>::iterator it_map = m_SettingsMap.begin();
+ std::stringstream st;
+ for(; it_map != m_SettingsMap.end(); ++it_map)
+ {
+ o_file << it_map->first.c_str();
+ o_file << it_map->second.c_str();
+ o_file << it_map->first.c_str();
+ o_file << std::endl;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // write Settings file //
+ // @param : - //
+ // return : -
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ void Settings::writeSettingsFile()
+ {
+ std::ofstream ofs(m_SettingsFileName.c_str());
+ ofs.clear();
+ writeSettings(ofs);
+ ofs.close();
+ }
+}
+
--- /dev/null
+#include <boost/program_options.hpp>
+#include <map>
+
+#define SETTINGS_DICOM_LIBRARY "<DICOM Library>"
+#define SETTINGS_SYNC_EVENT "<syncro_event>"
+#define SETTINGS_SYNC_FREQ "<syncro_frequency>"
+#define SETTINGS_DBPATH "<dbpath>"
+#define SETTINGS_COPY_PATH "<copy_path>"
+#define SETTINGS_REMOVE_PATIENT_DISPLAY "<remove_patient>"
+
+
+namespace creaImageIO
+{
+ class Settings{
+ public :
+ Settings(const std::string i_path);
+ ~Settings();
+
+ //get the value for a given option
+ const std::string getValue(const std::string i_key){return m_SettingsMap[i_key];}
+
+ void addDB(const std::string &i_path);
+
+ void removeDB(const std::string &i_path);
+
+ void updateSetting(const std::string& key, const std::string &val);
+
+ //write configuration file
+ void writeSettingsFile();
+
+ private :
+ // Settings Key-Value Map
+ std::map<std::string, std::string> m_SettingsMap;
+
+ //read the configuration file
+ void readSettings(std::vector<std::string> &i_Keys, const std::string &i_file);
+ // create the configuration file
+ void createFile();
+ void writeSettings(std::ofstream &o_filebuf);
+ std::string m_SettingsFileName;
+
+
+ };
+}
--- /dev/null
+#include <creaImageIOSynchron.h>
+#include <creaImageIOSystem.h>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+
+
+//namespace fs = boost::filesystem;
+
+//=====================================================================
+
+
+namespace creaImageIO
+{
+
+ //=====================================================================
+ #define QUERYSYNCDB(QUER,RES) \
+ try \
+ { \
+ RES = mDB->execQuery(QUER.c_str()); \
+ } \
+ catch (CppSQLite3Exception& e) \
+ { \
+ GimmickError("SQLite query '"<<QUER<<"' Error : " \
+ << e.errorCode() << ":" \
+ << e.errorMessage() ); \
+ }
+ //=====================================================================
+ #define UPDATESYNCDB(UP) \
+ try \
+ { \
+ mDB->execDML(UP.c_str()); \
+ } \
+ catch (CppSQLite3Exception& e) \
+ { \
+ GimmickError("SQLite update '"<<UP<<"' Error : " \
+ << e.errorCode() << ":" \
+ << e.errorMessage() ); \
+ }
+ //=====================================================================
+
+ Synchronizer::Synchronizer(const std::string& path)
+ {
+ pathDB = path + "maintenance_database.db3";
+ mDB = new CppSQLite3DB;
+ Initialize();
+ }
+
+ //=====================================================================
+ Synchronizer::~Synchronizer()
+ {
+ delete mDB;
+ }
+
+ //=====================================================================
+ void Synchronizer::Initialize()
+ {
+ if (!boost::filesystem::exists(pathDB))
+ {
+ CreateDB();
+ }
+
+ // OPENING
+ else
+ {
+ try
+ {
+ mDB->open(pathDB.c_str());
+ }
+ catch (CppSQLite3Exception& e)
+ {
+ GimmickError("Opening '"<<pathDB<<"' : "
+ << e.errorCode() << ":"
+ << e.errorMessage());
+ }
+ }
+ // get the ADD operations List
+ //UpdateAddList(pathDB);
+ }
+
+ //=====================================================================
+ void Synchronizer::CreateDB()
+ {
+ mDB->open(pathDB.c_str());
+ // CREATING TABLES
+ std::string command;
+ command = "CREATE TABLE ";
+ command += "ADD_OPS";
+ command += "\n(\nADD_KEY INTEGER PRIMARY KEY";
+ command += ",\nPATH text";
+ command += ",\nRECURSIVE boolean";
+ command += ",\nFILES_ADDED int";
+ command += ",\nREFERENCEDDB text";
+ command += "\n)";
+ UPDATESYNCDB(command);
+
+ command = "CREATE TABLE ";
+ command += "IGNORED_FILES";
+ command += "\n(\nID INTEGER PRIMARY KEY";
+ command += ",\nADD_KEY integer";
+ command += ",\nPATH text";
+ command += ",\nREMOVE boolean";
+ command += ",\nTIME datetext";
+ command += "\n)";
+ UPDATESYNCDB(command);
+ }
+
+ //=====================================================================
+ void Synchronizer::CleanName(std::string& str) const
+ {
+ size_t pos;
+ do
+ {
+ pos = str.find('\\');
+ if (pos!=-1)
+ {
+ str.replace(pos, 1, "/");
+ }
+ }
+ while (pos!=-1);
+ }
+
+ //=====================================================================
+ void Synchronizer::GetFileList(std::vector<AddList> & list, const std::string& refdb)
+ {
+ CleanList(refdb);
+ list=mAddList;
+ }
+
+ //=====================================================================
+ void Synchronizer::GetIgnoredFiles(const std::string& key, std::vector<std::string> &ignoreList)
+ {
+ ignoreList=GetIgnoreList(key);
+ }
+
+//=====================================================================
+ void Synchronizer::UpdateAddList(const std::string& refdb)
+ {
+ std::string query = "SELECT * FROM ADD_OPS WHERE REFERENCEDDB = '"+refdb+"';";
+ CppSQLite3Query res;
+ QUERYSYNCDB(query, res);
+ while (!res.eof())
+ {
+ AddList temp = AddList(res);
+ mAddList.push_back(temp);
+ res.nextRow();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // remove an entry of the DB
+ //@param i_table : table where to do the remove
+ // @param i_key : the add_key reference (one entry to remove for ADD_OP table, many for IGNORED_FILES table
+ //@result : -
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ void Synchronizer::RemoveEntry(const std::string i_table, const std::string i_key)
+ {
+ std::string query = "DELETE FROM " + i_table + " WHERE ADD_KEY = '" + i_key +"'";
+ UPDATESYNCDB(query);
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // remove several entries of the DB
+ // @param i_table : table where to do the remove
+ // @param i_attribute: attribute to match
+ // @param i_operand : operand to use
+ // @param i_val : the reference
+ //@result : -
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ void Synchronizer::RemoveEntries(const std::string i_table,
+ const std::string i_attribute,
+ const std::string i_operand,
+ const std::string i_val)
+ {
+ std::stringstream query;
+ query<<"DELETE FROM "<<i_table<<" WHERE "<<i_attribute<<" "<<i_operand<<" '"<<i_val<<"'";
+ UPDATESYNCDB(query.str());
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // clean DataBase if an operation has no child anymore
+ // @param refdb: the database segement to clean
+ // @result : -
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ void Synchronizer::CleanList(const std::string& refdb)
+ {
+ mAddList.clear();
+ UpdateAddList(refdb);
+ std::vector<AddList>::iterator it_add = mAddList.begin();
+ for(;it_add <mAddList.end(); ++it_add)
+ {
+ if(it_add->nbFiles == "0")
+ {
+ RemoveEntry("ADD_OPS", it_add->key);
+ RemoveEntry("IGNORED_FILES", it_add->key);
+
+ }
+ }
+ mAddList.clear();
+ UpdateAddList(refdb);
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Inserts a new add operation in the database
+ // @param path: the path of the directory that was added
+ // @param recursive: shows if the action was called recursively or not
+ // @param nChildren: the number of files affected by the operation
+ // @param refdb: the referenced database
+ // @result : The operation has been added
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ void Synchronizer::InsertAddOp(const std::string& path, const std::string& recursive, const std::string& nChildren, const std::string& refdb)
+ {
+ std::string insert;
+ std::string pat=path.c_str();
+ CleanName(pat);
+ insert="INSERT INTO ADD_OPS (PATH,RECURSIVE,FILES_ADDED,REFERENCEDDB) VALUES('";
+ insert+=pat+"','";
+ insert+=recursive+"',";
+ insert+=nChildren+",'";
+ insert+=refdb+"');";
+ UPDATESYNCDB(insert);
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Inserts a new ignored file in the database
+ // @param add_key: the key of the add_op to which it corresponds
+ // @param path: the path of the directory that was added
+ // @param remove: shows if the file was removed or not
+ // @param time: the time in which the file was removed
+ // @result : The file has been inserted
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ void Synchronizer::InsertIgnoreFile(const std::string& addKey, const std::string& path, const std::string& remove, const std::string& time, const std::string& refdb )
+ {
+ std::string pat=path.c_str();
+ CleanName(pat);
+ std::string id=GetAttribute("ID","IGNORED_FILES","PATH",pat,refdb);
+ if(id.compare("")==0)
+ {
+ std::string insert;
+ insert="INSERT INTO IGNORED_FILES (ADD_KEY,PATH,REMOVE,TIME) VALUES('";
+ insert+=addKey+"','";
+ insert+=pat+"','";
+ insert+=remove+"',";
+ insert+=time+");";
+ UPDATESYNCDB(insert);
+ }
+ else
+ {
+ //Gets the add key
+ std::string ak=GetAttribute("ADD_KEY","IGNORED_FILES","ID",id,refdb);
+ //gets the parent database to check if the file has been added to the current database
+ std::string parentDB=GetAttribute("*","ADD_OPS","ADD_KEY",ak,refdb);
+ //If there is no such entry, add it
+ if(parentDB.compare("")==0)
+ {
+ std::string insert;
+ insert="INSERT INTO IGNORED_FILES (ADD_KEY,PATH,REMOVE,TIME) VALUES('";
+ insert+=addKey+"','";
+ insert+=pat+"','";
+ insert+=remove+"',";
+ insert+=time+");";
+ UPDATESYNCDB(insert);
+ }
+ else
+ {
+ //Sets the new add key attribute for the file
+ SetAttribute("ADD_KEY","IGNORED_FILES",addKey,"ID", id,refdb);
+ //Sets the new remove attribute for the file
+ SetAttribute("REMOVE","IGNORED_FILES",remove,"ID", id,refdb);
+ //Sets the new time attribute for the file
+ SetAttribute("TIME","IGNORED_FILES",time,"ID", id,refdb);
+ }
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // get the files name to ignore for a add operation synchronization
+ // @param : the add key
+ //@result : list (path) of ignore files
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ std::vector<std::string> Synchronizer::GetIgnoreList(const std::string &i_key)
+ {
+ mIgnoreList.clear();
+ std::vector<std::string> i_names;
+ std::string query = "SELECT * FROM IGNORED_FILES WHERE ADD_KEY = ";
+ query+=i_key;
+ CppSQLite3Query res;
+ QUERYSYNCDB(query, res);
+ while (!res.eof())
+ {
+ RemoveList temp = RemoveList(res);
+ if(temp.remove.compare("0")==0)
+ {
+ mIgnoreList.push_back(temp);
+ }
+ res.nextRow();
+ }
+ std::vector<RemoveList>::iterator it;
+
+ for(it = mIgnoreList.begin();it != mIgnoreList.end(); ++it)
+ {
+ i_names.push_back((*it).path);
+ }
+ return i_names;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Gets the required attribute in the required table
+ // @param attribute: the attribute to look for
+ // @param table: the table to look in
+ // @param searchParam: the search parameter
+ // @param searchValue: the search value
+ // @result : required attribute
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ std::string Synchronizer::GetAttribute(const std::string& attribute,
+ const std::string& table,
+ const std::string& searchParam,
+ const std::string& searchValue,
+ const std::string& refdb)
+ {
+ std::stringstream query;
+ std::string result;
+ std::string sVal=searchValue.c_str();
+ CleanName(sVal);
+ query<<"SELECT "<<attribute<<" FROM "<<table<<" WHERE "<<searchParam<<" = '"<<sVal;
+ if(table.compare("ADD_OPS")==0)
+ {
+ query<<"' AND REFERENCEDDB = '"<<refdb<<"';";
+ }
+ else
+ {
+ query<<"';";
+ }
+ CppSQLite3Query res;
+ QUERYSYNCDB(query.str(), res);
+ while (!res.eof())
+ {
+ result=res.getStringField(0);
+ res.nextRow();
+ }
+ return result;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ // Sets the attribute value in the required table and column
+ // @param attribute: the attribute to look for
+ // @param table: the table to look in
+ // @param value: the value to set
+ // @param searchParam: the search parameter
+ // @param searchValue: the search value
+ // @result : attribute value changed
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ void Synchronizer::SetAttribute(const std::string& attribute,
+ const std::string& table,
+ const std::string& value,
+ const std::string& searchParam,
+ const std::string& searchValue,
+ const std::string& refdb)
+ {
+ std::string val=value.c_str();
+ std::string sVal=searchValue.c_str();
+ CleanName(val);
+ CleanName(sVal);
+ std::string sql = "UPDATE ";
+ sql+=table;
+ sql+=" SET ";
+ sql += attribute;
+ sql += " = '";
+ sql += val;
+ sql += "' WHERE ";
+ sql += searchParam;
+ sql += " = '";
+ sql += sVal;
+ if(table.compare("ADD_OPS")==0)
+ {
+ sql += "' AND REFERENCEDDB = '";
+ sql += refdb;
+ }
+ sql += "';";
+ UPDATESYNCDB(sql);
+ }
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+ // get the files name to ignore for a add operation synchronization
+ // @param : the add key
+ //@result : list (path) of ignore files
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+ void Synchronizer::GetList(const std::string i_db)
+ {
+ mList.clear();
+ std::vector<std::string> i_names;
+ std::vector<std::string> keys;
+ CppSQLite3Query res;
+ std::string query ="SELECT ADD_KEY, REFERENCEDDB FROM ADD_OPS";
+ QUERYSYNCDB(query, res);
+ keys.clear();
+ while (!res.eof())
+ {
+ std::string key(res.getStringField(0));
+ std::string db(res.getStringField(1));
+ if (db == i_db)
+ {
+ keys.push_back(key);
+ }
+ res.nextRow();
+ }
+ query = "SELECT PATH, REMOVE FROM IGNORED_FILES WHERE";
+ if(keys.size() > 0)
+ {
+ for (int i=0; i < keys.size(); i++)
+ {
+ query += " ADD_KEY = " + keys[i];
+ query += " AND";
+ }
+ query = query.substr(0,query.size() - 4);
+ }
+ else
+ {
+ query += " ADD_KEY = -1";
+ }
+ QUERYSYNCDB(query, res);
+ while (!res.eof())
+ {
+ std::string file(res.getStringField(0));
+ std::string ignore(res.getStringField(1));
+ mList[file] = ignore == "0"? true : false;
+ res.nextRow();
+ }
+ }
+
+ bool Synchronizer::isIndexed(const std::string filename)
+ {
+ bool valid = true;
+ std::string name(filename);
+ boost::algorithm::replace_all( name,"\\" , "/");
+ std::map <std::string, bool>::iterator it_list = mList.begin();
+ for(;it_list != mList.end(); it_list++)
+ {
+ if(it_list->first == name)
+ {
+ valid = false;
+ break;
+ }
+ }
+ return valid;
+ }
+}
+
+
--- /dev/null
+#ifndef __creaImageIOSynchron_h_INCLUDED__
+#define __creaImageIOSynchron_h_INCLUDED__
+
+#include <string>
+#include <map>
+#include <iostream>
+#include <vector>
+#include "CppSQLite3.h"
+
+namespace creaImageIO
+{
+ using namespace std;
+ //================================================================================================================
+ ///Represents the list of currently added files
+ class AddList
+ {
+ public :
+ ///Key to be added into the database
+ std::string key;
+ ///Path of the directory
+ std::string path;
+ /// Defines if the operation was recursive or not
+ std::string recursive;
+ ///Number of added files
+ std::string nbFiles;
+ ///Ctor
+ AddList(CppSQLite3Query& res):
+ key(res.getStringField(0)),
+ path(res.getStringField(1)),
+ recursive(res.getStringField(2)),
+ nbFiles(res.getStringField(3))
+ {}
+ };
+ //================================================================================================================
+
+ //================================================================================================================
+ ///Represents the list of currently removed files
+ class RemoveList
+ {
+ public :
+ ///Key to be added into the database
+ std::string key;
+ ///Path of the remove file
+ std::string path;
+ ///Defines if the file was removed or not
+ std::string remove;
+ ///Time of the last change of the file
+ std::string time;
+ ///Ctor
+ RemoveList(CppSQLite3Query& res):
+ key(res.getStringField(1)),
+ path(res.getStringField(2)),
+ remove(res.getStringField(3)),
+ time(res.getStringField(4))
+ {}
+ };
+ //================================================================================================================
+
+ //================================================================================================================
+ ///In charge of the synchronization of the database and the disk state.
+ class Synchronizer
+ {
+ public:
+ ///Ctor
+ Synchronizer(const std::string& path);
+ ///Dtor
+ virtual ~Synchronizer();
+ ///Initializes the database
+ void Initialize();
+ ///Inserts an add operation to the database
+ void InsertAddOp(const std::string& path,
+ const std::string& recursive,
+ const std::string& nChildren,
+ const std::string& refdb);
+ ///Inserts a file to be ignored
+ void InsertIgnoreFile(const std::string& addKey,
+ const std::string& path,
+ const std::string& remove,
+ const std::string& time,
+ const std::string& refdb);
+ ///Removes an entry that matches the given parameter
+ void RemoveEntry(const std::string i_table, const std::string i_key);
+ ///Removes several entries
+ void RemoveEntries(const std::string i_table,
+ const std::string i_attribute,
+ const std::string i_operand,
+ const std::string i_key);
+ ///Gets the list of AddFiles
+ void GetFileList(std::vector<AddList>& files , const std::string& refdb);
+ ///Gets the list of ignored files
+ void GetIgnoredFiles(const std::string& key, std::vector<std::string> &ignoreList);
+ ///Gets the attribute that matches the parameters
+ std::string GetAttribute(const std::string& attribute,
+ const std::string& table,
+ const std::string& searchParam,
+ const std::string& searchValue,
+ const std::string& refdb);
+ ///Sets an attribute to an entry that matches the given parameters
+ void SetAttribute(const std::string& attribute,
+ const std::string& table,
+ const std::string& value,
+ const std::string& searchParam,
+ const std::string& searchValue,
+ const std::string& refdb);
+ // Get the List of indexed files (removed or not)
+ void GetList(const std::string i_db);
+ // Test to not if a file is indexed on db or not
+ bool isIndexed(const std::string filename);
+ // List of all indexed files
+ std::map <std::string, bool> mList;
+ ///The current AddList
+ std::vector<AddList> mAddList;
+ ///The current RemoveList
+ std::vector<RemoveList> mIgnoreList;
+ private :
+
+ /// The DB
+ CppSQLite3DB* mDB;
+ ///Path of the current database
+ std::string pathDB;
+ ///Creates a new database
+ void CreateDB();
+ ///Updates the AddList
+ void UpdateAddList(const std::string& refdb);
+ ///Cleans the list in case operations are no longer useful (0 added files)
+ void CleanList(const std::string& refdb);
+ ///Cleans the name (changes slashes and backslashes according to the system)
+ void CleanName(std::string& str) const;
+ ///Gets the ignore list
+ std::vector<std::string> GetIgnoreList(const std::string &i_key);
+
+ };
+ //================================================================================================================
+
+
+}
+// EOF
+#endif
+
--- /dev/null
+#include <creaImageIOSystem.h>
+#include <creaImageIOSynchronizer.h>
+#include "boost/filesystem.hpp"
+
+namespace fs = boost::filesystem;
+
+namespace creaImageIO
+{
+
+ //==============================================================
+ Synchronizer::Synchronizer(TreeHandler * th)
+ : mHandler(th)
+ {
+
+ }
+ //==============================================================
+
+ //==============================================================
+ Synchronizer::~Synchronizer()
+ {
+
+ }
+ //==============================================================
+
+ //==============================================================
+ std::string Synchronizer::Synchronize(bool update)
+ {
+ GimmickMessage(1,"Synchronizing "<<std::endl);
+ int id=1;
+ std::stringstream mess;
+ std::string file;
+ mHandler->GetAttribute("Image","","","FullFileName",file);
+ size_t ini=0;
+ size_t fin=0;
+ while(fin<file.size()-1)
+ {
+ fin=file.find('#',ini);
+ SynchronizeFile(update,file.substr(ini,fin-ini),mess);
+ ini=fin+1;
+ }
+ if(mess.str()=="")
+ {
+ mess<<"Database up to date"<<std::endl;
+ }
+ GimmickMessage(1,mess.str());
+ return mess.str();
+ }
+ //==============================================================
+
+ //==============================================================
+ void Synchronizer::SynchronizeFile(bool update, std::string file, std::stringstream& mess)
+ {
+ if(!FileExists(file))
+ {
+ if(update)
+ {
+ mHandler->DeleteTuple("Image","FullFileName",file);
+ mess<<file<<" has been removed from the DB"<<std::endl;
+ }
+ else
+ {
+ mess<<file<<" State: Non existant"<<std::endl;
+ }
+ }
+ else
+ {
+ AttributesMatch(update,file,mess);
+ }
+ }
+ //==============================================================
+
+ //==============================================================
+ bool Synchronizer::FileExists(std::string file)
+ {
+ GimmickDebugMessage(4,"Verifying if file "<<file<<" exists"<<std::endl);
+ bool exists=true;
+ if ( !fs::exists( file ) )
+ {
+ exists=false;
+ }
+ return exists;
+ }
+ //==============================================================
+
+ //==============================================================
+ void Synchronizer::AttributesMatch(bool update, std::string file, std::stringstream& mess)
+ {
+ std::map< std::string, std::string> attr;
+ mHandler->GetTree().GetDescriptor().BuildAttributeMap(attr);
+ mReader.ReadAttributes(file,attr);
+ tree::LevelDescriptor::AttributeDescriptorListType adl= mHandler->GetTree().GetAttributeDescriptorList(mHandler->GetTree().GetNumberOfLevels()-1);
+ tree::LevelDescriptor::AttributeDescriptorListType::const_iterator a;
+ for (a = adl.begin();a!=adl.end();++a)
+ {
+ std::string databaseVal;
+ mHandler->GetAttribute("Image","FullFileName",file,a->GetKey(),databaseVal);
+ std::string fileVal=attr.find(a->GetKey())->second;
+ if ( a->GetFlags()==0 && databaseVal.compare(fileVal)!=0 )
+ {
+ if(update)
+ {
+ mHandler->SetAttribute("Image",a->GetKey(),fileVal,"FullFileName", file);
+ mess<<file<<" has been updated in the DB"<<std::endl;
+ }
+ else
+ {
+ mess<<file<<" State: Attributes differ"<<std::endl;
+ }
+ }
+ }
+ }
+ //==============================================================
+
+}
\ No newline at end of file
--- /dev/null
+#ifndef __creaImageIOSynchronizer_h_INCLUDED__
+#define __creaImageIOSynchronizer_h_INCLUDED__
+
+#include <creaImageIOSQLiteTreeHandler.h>
+#include <creaImageIOImageReader.h>
+
+namespace creaImageIO
+{
+
+//=======================================================================
+/// Synchronizes a given database with disk
+class Synchronizer
+ {
+ public:
+ /// Ctor
+ Synchronizer(TreeHandler* th);
+ /// Dtor
+ ~Synchronizer();
+ ///Sets the tree handler to use in order to synchronize with a given database
+ void SetTreeHandler(TreeHandler * handler){mHandler=handler;}
+ ///Synchronizes the database in the current tree handler with disk by doing the passed action. If it is true,
+ ///the database will be updated, otherwise a warning message will be returned.
+ std::string Synchronize(bool update);
+ ///Checks if the file given as a parameter exists in the drive
+ bool FileExists(std::string file);
+ ///Checks if the attributes of the node given as a parameter matchwith it correspondent file in disk
+ ///NB: This method doesn't check the existence of the file, so FileExists should be called before.
+ void AttributesMatch(bool update, std::string file, std::stringstream& mess);
+
+
+ private:
+ ///The tree handler
+ TreeHandler* mHandler;
+ ///The image reader
+ ImageReader mReader;
+ ///Synchronizes the given file, doing the action required and returning the result on the string supplied
+ void SynchronizeFile(bool update, std::string file, std::stringstream& message);
+
+ };
+} // EO namespace creaImageIO
+
+// EOF
+#endif
\ No newline at end of file
--- /dev/null
+#include "creaImageIOSystem.h"
+
+namespace creaImageIO
+{
+
+
+} // namespace
\ No newline at end of file
--- /dev/null
+#ifndef __creaImageIOSystem_INCLUDED__
+#define __creaImageIOSystem_INCLUDED__
+
+#include "creaMessageManager.h"
+
+
+
+#if defined(_WIN32)
+
+ #ifdef CREAIMAGEIO_EXPORT_SYMBOLS
+ #define CREAIMAGEIO_EXPORT __declspec( dllexport )
+#else
+ #define CREAIMAGEIO_EXPORT __declspec( dllimport )
+ #endif
+ #define CREAIMAGEIO_CDECL __cdecl
+#else
+ #define CREAIMAGEIO_EXPORT
+ #define CREAIMAGEIO_CDECL
+#endif // defined(_WIN32)
+
+
+namespace creaImageIO
+{
+ //==============================================================
+ inline void RegisterGimmickMessageTypes()
+ {
+ static bool first_time = true;
+ if (first_time)
+ {
+ crea::MessageManager::RegisterMessageType("Gimmick!",
+ "Gimmick",1);
+ crea::MessageManager::RegisterMessageType("Gimmick! DEBUG",
+ "Gimmick",0);
+ first_time = false;
+ }
+ }
+ //==============================================================
+ inline void SetGimmickMessageLevel(int l)
+ {
+ RegisterGimmickMessageTypes();
+ crea::MessageManager::SetMessageLevel("Gimmick!",l);
+ }
+ //==============================================================
+ inline void SetGimmickDebugMessageLevel(int l)
+ {
+ RegisterGimmickMessageTypes();
+ crea::MessageManager::SetMessageLevel("Gimmick! DEBUG",l);
+ }
+ //==============================================================
+
+ inline void deleteGimmickDebugMessage()
+ {
+ delete crea::MessageManager::GetInstance();
+ }
+
+
+#define GimmickMessage(LEV,MESS) \
+ creaMessage("Gimmick!",LEV,"[Gimmick!] "<<MESS);
+#define GimmickDebugMessage(LEV,MESS) \
+ creaDebugMessage("Gimmick! DEBUG",LEV,"[Gimmick!] DEBUG: "<<MESS);
+#define GimmickError(MESS) \
+ creaError("[Gimmick!] "<<MESS);
+
+} // namespace
+
+#endif
+
--- /dev/null
+#include <creaImageIOTimestampDatabaseHandler.h>
+#include <creaImageIOSystem.h>
+
+#include "CppSQLite3.h"
+
+#include <sys/stat.h>
+
+#include <deque>
+
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string/replace.hpp>
+
+namespace creaImageIO
+{
+ using namespace tree;
+ //=============================================================
+ TimestampDatabaseHandler::TimestampDatabaseHandler(const std::string& filename)
+ : mFileName(filename)
+ {
+ mDB = new CppSQLite3DB;
+ GimmickMessage(1,"SQLite version : "
+ <<std::string(mDB->SQLiteVersion())<< std::endl);
+ }
+ //=============================================================
+
+ //=============================================================
+ TimestampDatabaseHandler::~TimestampDatabaseHandler()
+ {
+ delete mDB;
+ }
+ //=============================================================
+ //=====================================================================
+ bool TimestampDatabaseHandler::Open()
+ {
+ return DBOpen();
+ }
+
+ //=====================================================================
+ bool TimestampDatabaseHandler::Create()
+ {
+ return DBCreate();
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool TimestampDatabaseHandler::Close()
+ {
+ return true;
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool TimestampDatabaseHandler::Destroy()
+ {
+ return false;
+ }
+
+
+
+
+
+
+ //=====================================================================
+ // SQLite DB specific methods
+ //=====================================================================
+ //=====================================================================
+#define QUERYTIMESTAMPDB(QUER,RES) \
+ try \
+ { \
+ GimmickMessage(2,"SQL query: '"<<QUER<<"'"<<std::endl); \
+ RES = mDB->execQuery(QUER.c_str()); \
+ } \
+ catch (CppSQLite3Exception& e) \
+ { \
+ GimmickError("SQLite query '"<<QUER<<"' : " \
+ << e.errorCode() << ":" \
+ << e.errorMessage() ); \
+ } \
+ //=====================================================================
+#define UPDATETIMESTAMPDB(UP) \
+ try \
+ { \
+ GimmickMessage(2,"SQL update: '"<<UP<<"'"<<std::endl); \
+ mDB->execDML(UP.c_str()); \
+ } \
+ catch (CppSQLite3Exception& e) \
+ { \
+ GimmickError("SQLite update '"<<UP<<"' Error : " \
+ << e.errorCode() << ":" \
+ << e.errorMessage() ); \
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ bool TimestampDatabaseHandler::DBOpen()
+ {
+ GimmickMessage(1,"Opening SQLite database '"<<GetFileName()
+ <<"' ... "<<std::endl);
+ // OPENING FILE
+ if (!boost::filesystem::exists(GetFileName()))
+ {
+ return false;
+ }
+
+ try
+ {
+ mDB->open(GetFileName().c_str());
+ }
+ catch (CppSQLite3Exception& e)
+ {
+ GimmickError("Opening '"<<GetFileName()<<"' : "
+ << e.errorCode() << ":"
+ << e.errorMessage());
+ return false;
+ }
+
+ GimmickDebugMessage(1,"Opening SQLite database '"<<GetFileName()
+ <<"' ... OK"<<std::endl);
+ return true;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ bool TimestampDatabaseHandler::DBCreate()
+ {
+ GimmickMessage(1,"Creating SQLite database '"<<GetFileName()
+ <<"' ... "<<std::endl);
+
+ if (boost::filesystem::exists(GetFileName()))
+ {
+ GimmickError(GetFileName()<<"' : "
+ << "file already exists");
+ return false;
+ }
+
+ // OPENING
+ try
+ {
+ mDB->open(GetFileName().c_str());
+ }
+ catch (CppSQLite3Exception& e)
+ {
+ GimmickError(e.errorCode() << ":"
+ << e.errorMessage() <<std::endl);
+ return false;
+ }
+
+
+ // CREATING TABLES
+
+ std::string command;
+
+
+ command = "CREATE TABLE ";
+ command += "FILES";
+ command += "\n(\nID INTEGER PRIMARY KEY";
+ command += ",\nPARENT_ID int not null";
+ command += ",\nPATH text";
+ command += ",\nLastModified datetext";
+ command += ",\nLastRead datetext";
+ command += ",\nTopLevelNodeId text";
+ command += ",\nReferencedDB text";
+ command += ",\nconstraint FK_PARENT foreign key (PARENT_ID) references ";
+ command += "FILES";
+ command += "(ID) on delete restrict on update restrict";
+
+ command += "\n)";
+ UPDATETIMESTAMPDB(command);
+
+ return true;
+ }
+
+
+
+ //=====================================================================
+ void TimestampDatabaseHandler::CleanPath(std::string& str) const
+ {
+ size_t pos;
+ do
+ {
+ pos = str.find('\\');
+ if ((int)pos!=-1)
+ {
+ str.replace(pos, 1, "/");
+ }
+ }
+ while ((int)pos!=-1);
+ }
+ //=====================================================================
+
+ bool TimestampDatabaseHandler::AddDirectory(const std::string& parent,
+ const std::string& path,
+ const time_t lastModif,
+ const time_t lastRead,
+ const std::string& refdb)
+ {
+ bool valid=false;
+ std::string par=parent.c_str();
+ std::string pat=path.c_str();
+ CleanPath(par);
+ CleanPath(pat);
+
+ std::string pathId=IsIndexed(pat,refdb);
+ //Case: It is a root parent
+ if(parent.compare("")==0)
+ {
+ if(pathId.compare("")==0)
+ {
+ AddFile(pat,lastModif,lastRead,refdb);
+ valid=true;
+ }
+ else
+ {
+ valid=CheckTimestamp(pathId, lastModif, refdb);
+ }
+ }
+ else
+ {
+ std::string parentId=IsIndexed(par,refdb);
+ //Case: Parent is not in database
+ if(parentId.compare("")==0)
+ {
+ AddFile(par,lastModif,lastRead,refdb);
+ parentId=IsIndexed(par,refdb);
+ }
+
+ //Case path is not in database
+ if(pathId.compare("")==0)
+ {
+ AddFile(parentId,pat,lastModif,lastRead,refdb);
+ valid=true;
+ }
+ //Parent and path are in the database
+ else
+ {
+ SetAttribute("PARENT_ID",parentId,"ID", pathId);
+ valid=CheckTimestamp(pathId, lastModif, refdb);
+ }
+ }
+ return valid;
+
+ }
+
+ //=====================================================================
+
+ void TimestampDatabaseHandler::AddFile(const std::string& path, const time_t lastModif, const time_t lastRead, const std::string& refdb)
+ {
+ std::stringstream out;
+ out<<"INSERT INTO FILES (PARENT_ID,PATH,LastModified,LastRead,ReferencedDB) VALUES(0,'"<<path<<"',";
+ out<<lastModif<<","<<lastRead<<",'"<<refdb<<"');";
+ UPDATETIMESTAMPDB(out.str());
+
+ }
+
+ //=====================================================================
+
+ void TimestampDatabaseHandler::AddFile(const std::string& parentId,
+ const std::string& path,
+ const time_t lastModif,
+ const time_t lastRead,
+ const std::string& refdb)
+ {
+ std::stringstream out;
+ out<<"INSERT INTO FILES (PARENT_ID,PATH,LastModified,LastRead,ReferencedDB) VALUES("<<parentId<<",'"<<path<<"',";
+ out<<lastModif<<","<<lastRead<<",'"<<refdb<<"');";
+ UPDATETIMESTAMPDB(out.str());
+ }
+
+ //=====================================================================
+ std::string TimestampDatabaseHandler::IsIndexed(const std::string& path, const std::string& refdb)
+ {
+ std::string pat=path.c_str();
+ CleanPath(pat);
+ std::stringstream out;
+ std::stringstream result;
+ out<<"SELECT ID FROM FILES WHERE PATH='"<<pat<<"' AND REFERENCEDDB='"<<refdb<<"';";
+
+ CppSQLite3Query q;
+ QUERYTIMESTAMPDB(out.str(),q);
+
+
+ while (!q.eof())
+ {
+ for (int fld = 0; fld < q.numFields(); fld++)
+ {
+ result<<q.getStringField(fld);
+ }
+ q.nextRow();
+ }
+
+ return result.str();
+ }
+
+ //=====================================================================
+ void TimestampDatabaseHandler::SetAttribute(const std::string& attName,
+ const std::string& attValue,
+ const std::string& searchParam,
+ const std::string& searchValue)
+ {
+ std::string av=attValue.c_str();
+ std::string sv=searchValue.c_str();
+ CleanPath(av);
+ CleanPath(sv);
+
+ std::string sql = "UPDATE FILES SET ";
+ sql += attName;
+ sql += " = '";
+ sql += av;
+ sql += "' WHERE ";
+ sql += searchParam;
+ sql += " = '";
+ sql += sv;
+ sql += "'";
+ UPDATETIMESTAMPDB(sql);
+ }
+
+ //=====================================================================
+ void TimestampDatabaseHandler::RemoveNode(const std::string& searchAtt, const tree::Node* node, const std::string& refdb)
+ {
+ int n=node->GetNumberOfChildren();
+ if(n>0)
+ {
+ std::vector<tree::Node*> children=node->GetChildrenList();
+ std::vector<tree::Node*>::iterator it;
+ for(it=children.begin();it!=children.end();++it)
+ {
+ RemoveNode(searchAtt,(*it),refdb);
+ }
+ }
+ else if(node->GetLevel()==3)
+ {
+ RemoveFile(searchAtt,node->GetAttribute("FullFileName"),refdb);
+ }
+ else
+ {
+ DBRemove("TopLevelNodeId",node->GetAttribute("ID"),refdb);
+ }
+
+
+ }
+ //=====================================================================
+ void TimestampDatabaseHandler::RemoveFile(const std::string& searchAtt, const std::string& searchVal, const std::string& refdb )
+ {
+
+ std::stringstream result;
+ std::string sel="SELECT PARENT_ID FROM FILES WHERE "+searchAtt+"='"+searchVal+"' AND REFERENCEDDB='"+refdb+"';";
+
+ CppSQLite3Query q;
+ QUERYTIMESTAMPDB(sel,q);
+
+ while (!q.eof())
+ {
+ for (int fld = 0; fld < q.numFields(); fld++)
+ {
+ result<<q.getStringField(fld);
+ }
+ q.nextRow();
+ }
+ DBRemove(searchAtt,searchVal,refdb);
+
+ int nChildren=0;
+ sel="SELECT ID FROM FILES WHERE PARENT_ID='"+result.str()+"'";
+ CppSQLite3Query q2;
+ QUERYTIMESTAMPDB(sel,q2);
+ while (!q2.eof())
+ {
+ nChildren++;
+ q2.nextRow();
+ }
+ if(nChildren<1)
+ {
+ if(!result.str().compare("0"))
+ {
+ RemoveFile("ID",result.str(),refdb);
+ }
+ else
+ {
+ DBRemove("ID",result.str(),refdb);
+ }
+ }
+ }
+
+ //=====================================================================
+ void TimestampDatabaseHandler::DBRemove(const std::string& searchAtt, const std::string& searchVal, const std::string& refdb)
+ {
+
+ std::string query = "DELETE FROM FILES WHERE "+searchAtt+"='"+ searchVal + "' AND REFERENCEDDB='"+refdb+"';";
+ UPDATETIMESTAMPDB(query);
+ }
+
+ //=====================================================================
+ bool TimestampDatabaseHandler::CheckTimestamp(const std::string pathId, const time_t lastModif, const std::string& refdb)
+ {
+ std::string sel="SELECT LastModified FROM FILES WHERE ID='"+pathId+"' AND REFERENCEDDB='"+refdb+"';";
+ CppSQLite3Query q;
+ QUERYTIMESTAMPDB(sel,q);
+ double timestamp;
+
+ while (!q.eof())
+ {
+ for (int fld = 0; fld < q.numFields(); fld++)
+ {
+ timestamp=q.getFloatField(fld);
+ }
+ q.nextRow();
+ }
+
+
+ std::stringstream lm;
+ lm<<lastModif;
+ double modif=atof((lm.str()).c_str());
+ if(timestamp<modif)
+ {
+ SetAttribute("LastModified",lm.str(),"ID",pathId);
+ return true;
+ }
+ return false;
+ }
+
+ //=====================================================================
+ void TimestampDatabaseHandler::RemoveEntries(const std::string i_table,
+ const std::string i_attribute,
+ const std::string i_operand,
+ const std::string i_val)
+ {
+ std::stringstream query;
+ query<<"DELETE FROM "<<i_table<<" WHERE "<<i_attribute<<" "<<i_operand<<" '"<<i_val<<"'";
+ UPDATETIMESTAMPDB(query.str());
+ }
+
+}// namespace creaImageIO
+
--- /dev/null
+#ifndef __creaImageIOTimestampDatabaseHandler_h_INCLUDED__
+#define __creaImageIOTimestampDatabaseHandler_h_INCLUDED__
+#include <vector>
+#include <map>
+#include <creaImageIOTree.h>
+class CppSQLite3DB;
+
+namespace creaImageIO
+{
+ using namespace std;
+//=======================================================================
+ /// Concrete TreeHandler which manages a Tree stored in a sqlite database
+ class TimestampDatabaseHandler
+ {
+ public:
+ //====================================================================
+ /// Ctor with database file name
+ TimestampDatabaseHandler(const std::string& filename);
+ /// Dtor
+ virtual ~TimestampDatabaseHandler();
+ //====================================================================
+
+ //====================================================================
+ /// Returns the sqlite db file name
+ const std::string& GetFileName() const { return mFileName; }
+ //====================================================================
+
+ //====================================================================
+ // INITIALIZATION / FINALIZATION
+ //====================================================================
+
+ //====================================================================
+ /// Opens an existing 'source'
+ bool Open();
+ /// Closes the 'source'
+ bool Close();
+ /// Creates a new 'source'
+ bool Create();
+ /// Destroys the 'source'
+ bool Destroy();
+ //====================================================================
+
+ //====================================================================
+ // READ / WRITE
+ //====================================================================
+ //====================================================================
+ ///Returns the id of the path if it's indexed, blank otherwise
+ std::string IsIndexed(const std::string& path, const std::string& refdb);
+ ///Sets the current path's parent
+ bool AddDirectory(const std::string& parent,
+ const std::string& path,
+ const time_t lastModif,
+ const time_t lastRead,
+ const std::string& refdb);
+ ///Adds a new file to the database without a parent
+ void AddFile(const std::string& path, const time_t lastModif, const time_t lastRead, const std::string& refdb);
+ ///Adds a new file to the database with a parent
+ void AddFile(const std::string& parentId,const std::string& path, const time_t lastModif, const time_t lastRead, const std::string& refdb);
+ ///Sets the attribute to the value passed as parameter where the searchParameter is searchValue
+ void SetAttribute(const std::string& attName,
+ const std::string& attValue,
+ const std::string& searchParam,
+ const std::string& searchValue);
+ ///Removes the given node
+ void RemoveNode(const std::string& searchAtt, const tree::Node* node, const std::string& refdb);
+ ///Removes the filename with the given pathname
+ void RemoveFile(const std::string& searchAtt, const std::string& searchVal, const std::string& refdb);
+ ///Cleans the path name
+ void CleanPath(std::string& str) const;
+ ///Checks the timestamp in the database and compares it with the given one.
+ //If there is a difference, it will return false, otherwise it will return true.
+ bool CheckTimestamp(const std::string pathId, const time_t lastModif, const std::string& refdb);
+ ///Removes the entries that match the given parameters
+ void RemoveEntries(const std::string i_table,
+ const std::string i_attribute,
+ const std::string i_operand,
+ const std::string i_val);
+
+ //====================================================================
+
+
+ protected:
+ //======================================================================
+ /// Open the database
+ bool DBOpen();
+ //======================================================================
+ //======================================================================
+ // Creation
+ /// Creates a new database on disk and the tables
+ bool DBCreate();
+ //======================================================================
+ //======================================================================
+ // Removes a file from the database
+ void DBRemove(const std::string& searchAtt, const std::string& searchVal, const std::string& refdb);
+
+ private:
+ /// The DB
+ CppSQLite3DB* mDB;
+ /// The physical location associated to the DicomDatabase (directory, db file...)
+ std::string mFileName;
+
+ };
+ // EO class
+ //=======================================================================
+
+
+} // EO namespace creaImageIO
+
+// EOF
+#endif
+
--- /dev/null
+#include <creaImageIOTree.h>
+#include <creaImageIOSystem.h>
+
+
+namespace creaImageIO
+{
+ namespace tree
+ {
+
+ Tree::Tree()
+ : Node(0)
+ {
+ GimmickMessage(5,"Default Tree constructor"
+ << std::endl);
+
+ }
+
+ Tree::~Tree()
+ {
+
+ }
+
+ void Tree::Print() const
+ {
+ GimmickMessage(1,GetLabel()<<std::endl);
+ ChildrenListType::const_iterator i;
+ for (i=GetChildrenList().begin(); i!=GetChildrenList().end(); i++)
+ {
+ (*i)->Print();
+ }
+
+ }
+ }
+}
--- /dev/null
+#ifndef __creaImageIOTree_h_INCLUDED__
+#define __creaImageIOTree_h_INCLUDED__
+
+#include <creaImageIOTreeNode.h>
+
+namespace creaImageIO
+{
+
+ namespace tree
+ {
+ /**
+ * \ingroup Tree
+ */
+ //=====================================================================
+ /// Abstract class to store user data on a Tree
+ struct TreeData
+ {
+ TreeData() {}
+ virtual ~TreeData() {}
+ };
+ //=====================================================================
+
+ //=====================================================================
+ /// An attributed Tree structure
+ /** \ingroup Tree
+ */
+ class Tree : public Node
+ {
+ public:
+ /// Ctor
+ Tree();
+ /// Virtual destructor
+ virtual ~Tree();
+
+ /// Returns the descriptor of the tree
+
+ /// Returns the tree to which the node belongs
+ virtual Tree* GetTree() { return this; }
+ /// Returns the tree to which the node belongs
+ virtual const Tree* GetTree() const { return this; }
+ /// Returns the level of the node in the tree
+ virtual int GetLevel() const { return 0; }
+
+ /// Returns the Descriptor of the tree (const)
+ const Descriptor& GetDescriptor() const { return mDescriptor; }
+ /// Returns the descriptor of the tree
+ Descriptor& GetDescriptor() { return mDescriptor; }
+
+ /// Returns the number of levels of the tree
+ unsigned int GetNumberOfLevels()
+ { return GetDescriptor().GetNumberOfLevels(); }
+
+ /// Returns the LevelDescriptor of a given level (const ref)
+ const LevelDescriptor& GetLevelDescriptor(int level) const
+ { return GetDescriptor().GetLevelDescriptor(level); }
+
+ /// Returns the AttributeDescriptorList of a given level (const ref)
+ const LevelDescriptor::AttributeDescriptorListType&
+ GetAttributeDescriptorList(int level) const
+ { return GetDescriptor().GetAttributeDescriptorList(level); }
+
+ virtual void Print() const;
+
+ private:
+ Descriptor mDescriptor;
+
+ };
+ // EO class Tree
+ //=====================================================================
+
+ } // EO namespace tree
+
+} // EO namespace creaImageIO
+
+// EOF
+#endif
--- /dev/null
+#include <creaImageIOTreeAttributeDescriptor.h>
+#include <creaImageIOSystem.h>
+
+
+#if defined(USE_GDCM)
+#include <gdcmGlobal.h>
+#include <gdcmDictSet.h>
+#endif
+
+#if defined(USE_GDCM2)
+#include <gdcmGlobal.h>
+#include <gdcmDicts.h>
+#include <gdcmDict.h>
+#endif
+
+#include <boost/algorithm/string/replace.hpp>
+
+namespace creaImageIO
+{
+
+ namespace tree
+ {
+
+ //========================================================================
+ void AttributeDescriptor::CleanName(std::string& str) const
+ {
+ // quote must be doubled for SQL
+ // crea::Utils::Replace( str, "'", "''" );
+ boost::algorithm::replace_all(str,"'","''");
+ // Found strange strings which contained NULL char INSIDE string
+ int i,size=str.size();
+ for (i=0;i<size;++i)
+ {
+ if (str[i]==0)
+ {
+ str = str.substr(0,i);
+ break;
+ }
+ }
+ }
+ //========================================================================
+
+ //=====================================================================
+ // Ctor with key, name and flags
+ AttributeDescriptor::AttributeDescriptor(const std::string& key,
+ const std::string& name,
+ unsigned int flags)
+ : mKey(key), mName(name), mGroup(0), mElement(0), mFlags(flags)
+ {
+
+ CleanName(mName);
+ GimmickDebugMessage(3,"AttributeDescriptor : '"<<key
+ <<"' ["<<flags<<"]"<<std::endl);
+ GimmickDebugMessage(3,"='"<<mName<<"'"<<std::endl);
+ }
+
+ //=====================================================================
+
+ //=====================================================================
+ // Ctor with dicom group, elem and flags
+ // The key is built as 'Dgroup_elem'
+ // The user name is retreived from dicom dictionnary
+ AttributeDescriptor::AttributeDescriptor(unsigned short group,
+ unsigned short element,
+ unsigned int flags)
+ : mGroup(group), mElement(element), mFlags(flags)
+ {
+
+ //GDCM_NAME_SPACE::TagKey tag(group,element);
+ char ctag[12];
+ sprintf(ctag,"D%04x_%04x",group,element);
+ mKey = ctag;
+
+ GimmickDebugMessage(3,"AttributeDescriptor : '"<<mKey
+ <<"' ["<<flags<<"]"<<std::endl);
+
+#if defined(USE_GDCM)
+ // Retrieve the name from gdcm dict
+ GDCM_NAME_SPACE::DictEntry* entry =
+ GDCM_NAME_SPACE::Global::GetDicts()
+ ->GetDefaultPubDict()->GetEntry(mGroup,mElement);
+
+ if (entry)
+ {
+ mName = entry->GetName();
+ CleanName(mName);
+ GimmickDebugMessage(3,"='"<<mName<<"'"<<std::endl);
+ }
+ else
+ {
+ GimmickMessage(1,"!! WARNING : tag '"<<mKey
+ <<"' is not in DICOM dictionnary ! "
+ <<"Considering it as a user attribute"
+ << std::endl);
+ mName = "UNKNOWN";
+ mGroup = mElement = 0;
+ }
+#endif
+
+
+
+
+#if defined(USE_GDCM2)
+ // Retrieve the name from gdcm dict
+ const gdcm::Global& g = gdcm::Global::GetInstance();
+ const gdcm::Dicts &dicts = g.GetDicts();
+ const gdcm::Dict &dict = dicts.GetPublicDict();
+ gdcm::DictEntry dictentry = dict.GetDictEntry(gdcm::Tag(mGroup, mElement));
+
+ mName = dictentry.GetName();
+ if(!mName.empty())
+ {
+ CleanName(mName);
+ GimmickDebugMessage(3,"='"<<mName<<"'"<<std::endl);
+ }
+ else
+ {
+ GimmickMessage(1,"!! WARNING : tag '"<<mKey
+ <<"' is not in DICOM dictionnary ! "
+ <<"Considering it as a user attribute"
+ << std::endl);
+ mName = "UNKNOWN";
+ mGroup = mElement = 0;
+ }
+#endif
+
+ }
+ //=====================================================================
+
+
+ //=====================================================================
+ /// Extracts group and element from a key of the form "Dgroup_elem"
+ void AttributeDescriptor::GetDicomGroupElementFromKey(const std::string& key,
+ unsigned short& group,
+ unsigned short& elem)
+ {
+ group = elem = 0;
+ if ( (key.size()==10) &&
+ (key[0] == 'D') &&
+ (key[5] == '_') )
+ {
+ sscanf(key.c_str(),"D%04hx_%04hx ",&group,&elem);
+ GimmickDebugMessage(3,"GetDicomGroupElementFromKey '"<<key<<"' : " <<group<<"|"<<elem<<std::endl);
+ }
+ else
+ {
+ GimmickMessage(5,"GetDicomGroupElementFromKey '"<<key<<"' : "
+ <<" not a DICOM key format"<<std::endl);
+ }
+ return;
+ }
+
+ //=====================================================================
+ /// test if the type is a date
+ bool AttributeDescriptor::isDateEntry() const
+ {
+
+ bool btest = false;
+ // Retrieve the name from gdcm dict
+#if defined(USE_GDCM)
+ GDCM_NAME_SPACE::DictEntry* entry = GDCM_NAME_SPACE::Global::GetDicts()->GetDefaultPubDict()->GetEntry(GetGroup(),GetElement());
+ if( entry != 0)
+ {
+ if( entry->GetVR().str() == "DA" )
+ {
+ btest = true;
+ }
+ }
+#endif
+#if defined(USE_GDCM2)
+ const gdcm::Global& g = gdcm::Global::GetInstance();
+ const gdcm::Dicts &dicts = g.GetDicts();
+ const gdcm::Dict &dict = dicts.GetPublicDict();
+ if(mGroup != 0 && mElement != 0)
+ {
+ gdcm::DictEntry dictentry = dict.GetDictEntry(gdcm::Tag(GetGroup(), GetElement()));
+ if( gdcm::VR::GetVRString(dictentry.GetVR()) == "DA")
+ {
+ btest = true;
+ }
+ }
+#endif
+ return btest;
+ }
+
+ //=====================================================================
+ /// test if the type is a time
+ bool AttributeDescriptor::isTimeEntry() const
+ {
+
+ bool btest = false;
+#if defined(USE_GDCM)
+ // Retrieve the name from gdcm dict
+ GDCM_NAME_SPACE::DictEntry* entry = GDCM_NAME_SPACE::Global::GetDicts()->GetDefaultPubDict()->GetEntry(GetGroup(),GetElement());
+ if( entry != 0)
+ {
+ if( entry->GetVR().str() == "TM" )
+ {
+ btest = true;
+ }
+ }
+#endif
+
+#if defined(USE_GDCM2)
+ const gdcm::Global& g = gdcm::Global::GetInstance(); // sum of all knowledge !
+ const gdcm::Dicts &dicts = g.GetDicts();
+ const gdcm::Dict &dict = dicts.GetPublicDict(); // Part 6
+ if(mGroup != 0 && mElement != 0)
+ {
+ gdcm::DictEntry dictentry = dict.GetDictEntry(gdcm::Tag(mGroup, mElement));
+ if(gdcm::VR::GetVRString(dictentry.GetVR()) == "TM")
+ {
+ btest = true;
+ }
+ }
+#endif
+
+ return btest;
+ }
+
+
+ //=====================================================================
+ /// Decodes the type of the attribute
+ void AttributeDescriptor::DecodeType(unsigned int& typ) const
+ {
+ std::string type="";
+#if defined(USE_GDCM)
+ // Retrieve the name from gdcm dict
+ GDCM_NAME_SPACE::DictEntry* entry =
+ GDCM_NAME_SPACE::Global::GetDicts()
+ ->GetDefaultPubDict()->GetEntry(GetGroup(),GetElement());
+
+ if (entry==0)
+ {
+ typ = 2;
+ return;
+ }
+ type = entry->GetVR().str();
+#endif
+#if defined(USE_GDCM2)
+ const gdcm::Global& g = gdcm::Global::GetInstance(); // sum of all knowledge !
+ const gdcm::Dicts &dicts = g.GetDicts();
+ const gdcm::Dict &dict = dicts.GetPublicDict(); // Part 6
+ gdcm::DictEntry dictentry = dict.GetDictEntry(gdcm::Tag(mGroup, mElement));
+ type = gdcm::VR::GetVRString(dictentry.GetVR());
+#endif
+
+ GimmickDebugMessage(3,"VR Value is "<<type<<"!"<<std::endl);
+ if(type=="AS" ||
+ type=="DA" ||
+ type=="FL" ||
+ type=="FD" ||
+ type=="IS" ||
+ type=="SL" ||
+ type=="SS" ||
+ type=="UI" ||
+ type=="US" ||
+ type=="SH")
+ {
+ // Numerical
+ typ = 1;
+ }
+ else
+ {
+ // String
+ typ = 2;
+ }
+
+ }
+ //=====================================================================
+
+ } // EO namespace tree
+
+} // EO namespace creaImageIO
--- /dev/null
+#ifndef __creaImageIOTreeAttributeDescriptor_h_INCLUDED__
+#define __creaImageIOTreeAttributeDescriptor_h_INCLUDED__
+
+#include <string>
+//#include <iostream>
+
+namespace creaImageIO
+{
+
+ namespace tree
+ {
+ /**
+ * \ingroup Tree
+ */
+ //=====================================================================
+ /// Descriptor of an attribute of a node of a Tree (name, dicom group/element)
+ class AttributeDescriptor
+ {
+ public:
+ /// Flags
+ /// The attribute is hidden (not visible to user)
+ static const unsigned int PRIVATE;
+ /// The attribute enters in unique identifier constitution
+ static const unsigned int IDENTIFIER;
+ /// The attribute enters in label constitution (for printing)
+ static const unsigned int LABEL;
+ /// The attribute can be edited
+ static const unsigned int EDITABLE;
+
+ /// Types
+ /// The attribute is of numeric type
+ static const int NUMBER=1;
+ /// The attribute is of string type
+ static const int STRING=2;
+ /// The attribute's type is unknown
+ static const int UNKNOWN=0;
+
+ /// Default ctor
+ AttributeDescriptor()
+ : mKey(""), mName(""), mGroup(0), mElement(0), mFlags(0)
+ {
+ }
+ /// Ctor with all explicitely
+ AttributeDescriptor(const std::string& key,
+ const std::string& name,
+ unsigned short group,
+ unsigned short element,
+ unsigned int flags)
+ : mKey(key), mName(name), mGroup(group), mElement(element),
+ mFlags(flags)
+ {
+ }
+
+ // Ctor with key, name and flags
+ AttributeDescriptor(const std::string& key,
+ const std::string& name,
+ unsigned int flags = 0);
+ // Ctor with dicom group, elem and flags
+ // The key is built as 'Dgroup_elem'
+ // The user name is retreived from dicom dictionnary
+ AttributeDescriptor(unsigned short group,
+ unsigned short element,
+ unsigned int flags = 0);
+ /// Returns the key of the attribute
+ const std::string& GetKey() const { return mKey; }
+ /// Returns the name of the attribute
+ const std::string& GetName() const { return mName; }
+ /// Returns the DICOM group code of the attribute
+ unsigned short GetGroup() const { return mGroup; }
+ /// Returns the DICOM element code of the attribute
+ unsigned short GetElement() const { return mElement; }
+ /// Returns the flags of the attribute
+ unsigned int GetFlags() const { return mFlags; }
+
+ /// Extracts group and element from a key of the form "Dgroup_elem"
+ static void GetDicomGroupElementFromKey(const std::string& key,
+ unsigned short& group,
+ unsigned short& elem);
+ /// Cleans the name:
+ /// Replace simple quote by double quotes
+ /// Cut string at NULL chars
+ void CleanName(std::string& str) const;
+ ///Decodes the type of attribute into the existing ones
+ void DecodeType(unsigned int& type) const;
+
+ /// Determines if Attribute is a date
+ bool isDateEntry() const;
+
+ /// Determines if Attribute is a time
+ bool isTimeEntry() const;
+
+ private:
+ std::string mKey;
+ std::string mName;
+ unsigned short mGroup;
+ unsigned short mElement;
+ unsigned int mFlags;
+ };
+ // EO class AttributeDescriptor
+ //=====================================================================
+
+
+
+ } // EO namespace tree
+
+} // EO namespace creaImageIO
+
+
+
+
+
+#endif // #ifndef __creaImageIOTreeAttributeDescriptor_h_INCLUDED__
--- /dev/null
+#ifndef __creaImageIOTreeAttributeMapType_h_INCLUDED__
+#define __creaImageIOTreeAttributeMapType_h_INCLUDED__
+
+#include <map>
+#include <string>
+#include <iostream>
+
+namespace creaImageIO
+{
+
+ /**
+ * \ingroup Tree
+ */
+ namespace tree
+ {
+ typedef std::map<std::string,std::string> AttributeMapType;
+
+
+
+
+ }
+
+}
+
+//=====================================================================
+inline std::ostream& operator<<(std::ostream& s,
+ const creaImageIO::tree::AttributeMapType& d)
+{
+ creaImageIO::tree::AttributeMapType::const_iterator i;
+ for (i=d.begin();i!=d.end();++i)
+ {
+ s << "'" << i->first << "'='" << i->second << "'" << std::endl;
+ }
+ return s;
+}
+//=====================================================================
+
+
+#endif
--- /dev/null
+#include <creaImageIOTreeComparators.h>
--- /dev/null
+#ifndef __creaImageIOTreeNodeComparators_h_INCLUDED__
+#define __creaImageIOTreeNodeComparators_h_INCLUDED__
+
+#include <vector>
+#include <iostream>
+
+namespace creaImageIO
+{
+
+ namespace tree
+ {
+
+
+ class Node;
+
+
+ /**
+ * \ingroup Tree
+ */
+ //=====================================================================
+ /// Abstract definition of a comparator of Node
+ struct Comparator
+ {
+ virtual ~Comparator() {}
+ virtual bool operator() (Node* const & x, Node* const & y) = 0;
+ };
+ //=====================================================================
+
+
+
+ //=====================================================================
+ /// Abstract Comparator whose order can be reversed
+ struct ComparatorWithOrder : public Comparator
+ {
+ ComparatorWithOrder(bool reverse_order = false)
+ : mReverseOrder(reverse_order) {}
+ virtual ~ComparatorWithOrder() {}
+
+
+ virtual bool compare(Node* const &, Node* const &) = 0;
+
+ virtual bool operator() (Node* const & x, Node* const & y)
+ {
+ if (mReverseOrder) return this->compare(y,x);
+ return this->compare(x,y);
+ };
+
+ bool mReverseOrder;
+ };
+ //=====================================================================
+
+
+
+ //=====================================================================
+ /// A Comparator which stores a vector of Comparators and
+ /// which performs lexicographical comparison
+ class LexicographicalComparator : public Comparator
+ {
+ public:
+ LexicographicalComparator(const std::string& name)
+ : mName(name) {}
+ ~LexicographicalComparator() {}
+
+ const std::string& GetName() const { return mName; }
+ void SetName(const std::string& s) { mName = s; }
+ void Clear() { mComparator.clear(); }
+ void DeleteComparators()
+ {
+ std::vector<Comparator*>::iterator i;
+ for (i =mComparator.begin();
+ i!=mComparator.end();
+ ++i)
+ {
+ delete *i;
+ }
+ mComparator.clear();
+ }
+ void Add(Comparator* c) { mComparator.push_back(c); }
+
+ bool operator() (Node* const & x, Node * const & y);
+
+ private:
+ std::string mName;
+ std::vector<Comparator*> mComparator;
+ };
+ //=====================================================================
+
+
+
+
+ //===================================================================
+ /// Comparator which compares the values of a given Attribute of the Nodes which is decoded as an int value
+ struct IntComparator :
+ public ComparatorWithOrder
+ {
+ IntComparator(const std::string& key,
+ bool reverse_order)
+ :
+ ComparatorWithOrder(reverse_order),
+ mKey(key)
+ {}
+ virtual bool compare(Node* const & x, Node* const & y);
+
+ private:
+ std::string mKey;
+ };
+ //===================================================================
+
+ //===================================================================
+ /// Comparator which compares the values of a given Attribute of the Nodes which is decoded as an float value
+ struct FloatComparator :
+ public ComparatorWithOrder
+ {
+ FloatComparator(const std::string& key,
+ bool reverse_order )
+ :
+ ComparatorWithOrder(reverse_order),
+ mKey(key)
+ {}
+
+ virtual bool compare(Node* const & x, Node* const & y);
+
+ private:
+ std::string mKey;
+ };
+ //===================================================================
+
+ //===================================================================
+ /// Comparator which compares the values of a given Attribute of the Nodes which is decoded as a string value
+ struct StringComparator :
+ public ComparatorWithOrder
+ {
+ StringComparator(const std::string& key,
+ bool reverse_order )
+ :
+ ComparatorWithOrder(reverse_order),
+ mKey(key)
+ {}
+
+ virtual bool compare(Node* const & x, Node* const & y);
+
+ private:
+ std::string mKey;
+ };
+ //===================================================================
+
+ //===================================================================
+#define INT_FIELD_COMP(NAME,FIELD) \
+ struct Node##NAME##Comparator : \
+ public IntComparator \
+ { \
+ Node##NAME##Comparator(bool o = false) : \
+ IntComparator(FIELD,o) \
+ {} \
+ }
+ //==================================================================
+
+ //===================================================================
+#define FLOAT_FIELD_COMP(NAME,FIELD) \
+ struct Node##NAME##Comparator : \
+ public FloatComparator \
+ { \
+ Node##NAME##Comparator(bool o = false) : \
+ FloatComparator(FIELD,o) \
+ {} \
+ }
+ //===================================================================
+
+ //===================================================================
+#define STRING_FIELD_COMP(NAME,FIELD) \
+ struct Node##NAME##Comparator : \
+ public StringComparator \
+ { \
+ Node##NAME##Comparator(bool o = false) : \
+ StringComparator(FIELD,o) \
+ {} \
+ }
+ //===================================================================
+
+
+
+ //===================================================================
+ // Patient comparators
+ ///Compares the names of the patients
+ STRING_FIELD_COMP(PatientName,"A0010_0010");
+ ///Compares the sex of the patients
+ STRING_FIELD_COMP(PatientSex, "A0010_0040");
+ ///Compares the birthdays of the patients
+ STRING_FIELD_COMP(PatientBirthday, "A0010_0030");
+ //===================================================================
+
+ //===================================================================
+ // Study comparators
+ ///Compares the dates of the studies
+ STRING_FIELD_COMP(StudyDate,"A0008_0020");
+ ///Compares the description of the studies
+ STRING_FIELD_COMP(StudyDescription,"A0008_1030");
+ //===================================================================
+
+ //===================================================================
+ // Series comparators
+ ///Compares the modality of the series
+ STRING_FIELD_COMP(Modality,"A0008_0060");
+ ///Compares the description of the series
+ STRING_FIELD_COMP(SeriesDescription,"A0008_103E");
+ ///Compares the date of the series
+ STRING_FIELD_COMP(SeriesDate,"A0008_0021");
+ //===================================================================
+
+ //===================================================================
+ // Image comparators
+ ///Compares the number of the images
+ INT_FIELD_COMP(ImageNumber,"A0020_0013");
+ ///Compares the location of the images
+ FLOAT_FIELD_COMP(SliceLocation,"A0020_1041");
+ ///Compares the filename of the images
+ STRING_FIELD_COMP(FullFileName,"FullFileName");
+ //===================================================================
+ } // namespace tree
+
+} // namespace creaImageIO
+
+
+
+#endif // #ifndef __creaImageIOComparators_h_INCLUDED__
--- /dev/null
+#include <creaImageIOTreeDescriptor.h>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <creaImageIOSystem.h>
+#include <boost/filesystem.hpp>
+
+
+#include <fstream>
+
+
+namespace creaImageIO
+{
+
+ namespace tree
+ {
+
+ //==================================================================
+ /// The attribute is hidden (not visible to user)
+ const unsigned int AttributeDescriptor::PRIVATE = 1;
+ /// The attribute enters in unique identifier constitution
+ const unsigned int AttributeDescriptor::IDENTIFIER = 2;
+ /// The attribute can be edited
+ const unsigned int AttributeDescriptor::EDITABLE = 3;
+ /// the attribute describes the node
+ const unsigned int AttributeDescriptor::LABEL = 4;
+ //==================================================================
+
+ //==================================================================
+ Descriptor::Descriptor()
+ {
+ CreateLevel0Descriptor();
+ }
+ //==================================================================
+
+ //==================================================================
+ Descriptor::~Descriptor()
+ {
+ }
+ //==================================================================
+
+ //==================================================================
+ void Descriptor::CreateLevel0Descriptor()
+ {
+ Add(LevelDescriptor("Root"));
+ }
+ //==================================================================
+
+ //==================================================================
+ /// Creates the default descriptor
+ void Descriptor::CreateDefault()
+ {
+ // clears the existing one
+ Clear();
+
+ // Creates the level 0 descriptor
+ CreateLevel0Descriptor();
+ // Creates the attribute "Name"
+ Add(AttributeDescriptor("Name","Name",
+ AttributeDescriptor::LABEL),0);
+
+ // Patient level
+ Add(LevelDescriptor("Patient"));
+ Add(AttributeDescriptor("NumberOfChildren","#Series",0),1); // Number of Series
+ Add(AttributeDescriptor(0x0010,0x0010, // Patient name
+ AttributeDescriptor::LABEL),1);
+ Add(AttributeDescriptor(0x0010,0x0040),1); // Patient sex
+ Add(AttributeDescriptor(0x0010,0x0030),1); // Patient birthday
+ Add(AttributeDescriptor(0x0010,0x0020, // Patient ID
+ AttributeDescriptor::IDENTIFIER),1);
+
+ // Study-series level
+ Add(LevelDescriptor("Series"));
+ Add(AttributeDescriptor("NumberOfChildren","#Images",0),2); // Number of images
+ Add(AttributeDescriptor(0x0008,0x0060, // Modality
+ AttributeDescriptor::LABEL),2);
+ Add(AttributeDescriptor(0x0008,0x1030),2); // Study Description
+ Add(AttributeDescriptor(0x0008,0x103E),2); // Description
+ Add(AttributeDescriptor(0x0008,0x0080),2); // Institution Name
+ Add(AttributeDescriptor(0x0008,0x0081),2); // Institution Adress
+ Add(AttributeDescriptor(0x0008,0x1010),2); // Station Name
+ Add(AttributeDescriptor(0x0008,0x1048),2); // Physician of Record
+ Add(AttributeDescriptor(0x0008,0x1050),2); // Performing Physician's Name
+ Add(AttributeDescriptor(0x0018,0x1030),2); // Protocol Name
+
+ Add(AttributeDescriptor(0x0020,0x0010),2); // Study ID
+ Add(AttributeDescriptor(0x0008,0x0020),2); // Study Date
+ Add(AttributeDescriptor(0x0008,0x0030),2); // Study Time
+ Add(AttributeDescriptor(0x0008,0x0050),2); // Study Accession Number
+ Add(AttributeDescriptor(0x0008,0x0005),2); // Specific character set
+ Add(AttributeDescriptor(0x0008,0x0021),2); // Series Date
+ Add(AttributeDescriptor(0x0008,0x0031),2); // Series time
+
+ Add(AttributeDescriptor(0x0020,0x000D // Study Instance UID
+ ),2);//AttributeDescriptor::IDENTIFIER),2);
+ Add(AttributeDescriptor(0x0020,0x000E, // Series Instance UID
+ AttributeDescriptor::IDENTIFIER),2);
+ // |
+ // AttributeDescriptor::LABEL),2);
+
+
+ // Image level
+ Add(LevelDescriptor("Image"));
+
+ Add(AttributeDescriptor(0x0020,0x0013),3); // Image Number
+
+ Add(AttributeDescriptor(0x0028,0x0010),3); // Rows
+ Add(AttributeDescriptor(0x0028,0x0011),3); // Columns
+ Add(AttributeDescriptor(0x0028,0x0012),3); // Planes
+ Add(AttributeDescriptor(0x0028,0x0002),3); // Sample per pixels
+ Add(AttributeDescriptor(0x0028,0x0008),3); // Number of Frames
+ Add(AttributeDescriptor(0x0028,0x0004),3); // Photometric Interpretation
+ Add(AttributeDescriptor(0x0028,0x0103),3); // Pixel Representation
+
+ Add(AttributeDescriptor(0x0020,0x0032),3); // Image Position Patient
+ Add(AttributeDescriptor(0x0020,0x0037),3); // Image Orientation Patient
+ Add(AttributeDescriptor(0x0020,0x1041),3); // Slice Location
+ Add(AttributeDescriptor(0x0028,0x0006),3); // Planar Configuration
+
+ Add(AttributeDescriptor(0x0028,0x0030),3); // Pixel Spacing
+ Add(AttributeDescriptor(0x0028,0x0100),3); // AlocatedBits
+ Add(AttributeDescriptor(0x0028,0x0101),3); // StoredBits
+
+ Add(AttributeDescriptor(0x0008,0x0008),3); // Image Type
+ Add(AttributeDescriptor(0x0008,0x0023),3); // Content Date
+ Add(AttributeDescriptor(0x0008,0x0033),3); // Content Time
+
+ Add(AttributeDescriptor(0x0020,0x4000),3); // Image Comments
+
+ Add(AttributeDescriptor(0x0004,0x1500, // File Name
+ AttributeDescriptor::LABEL),3);
+ Add(AttributeDescriptor(0x0028,0x1052),3); // Rescale Intercept
+ Add(AttributeDescriptor(0x0028,0x1053),3); // Rescale Slope
+
+ Add(AttributeDescriptor(0x0050,0x0004),3); // Calibration Image
+
+ Add(AttributeDescriptor(0x0020,0x0052 // Frame Reference UID
+ ),3);
+ Add(AttributeDescriptor(0x0008,0x0016),3); // SOP Class UID
+ Add(AttributeDescriptor("FullFileName", // Full file name
+ "Full file name",
+ AttributeDescriptor::IDENTIFIER),3);
+
+ }
+
+ //////////////////////////////////////////////////////////////
+ // create a descriptor (name, attributes...) from a file) //
+ // @param : file path //
+ // return : - //
+ //////////////////////////////////////////////////////////////
+ void Descriptor::createDescriptorfromFile(const std::string &i_name)
+ {
+ Clear();
+
+ // read file and put in buffer
+ std::ifstream i_file(i_name.c_str());
+ std::stringstream buffer;
+ buffer << i_file.rdbuf();
+ std::string line;
+ bool bname;
+ int ilevel = -1;
+
+
+ while(std::getline(buffer, line))
+ {
+ if(line =="<level>")
+ { //increment levels.
+ ilevel++;
+ bname = true;
+ }
+ else if(bname)
+ {
+ // For each level, a name to describe it
+ Add(LevelDescriptor(line));
+ bname = false;
+ }
+ else if(line.empty()) // to avoid end line
+ {
+ return;
+ }
+ else
+ {
+ // split line to find all tags
+ std::vector<std::string> descriptors;
+ std::string separator = " ";
+ std::string::size_type last_pos = line.find_first_not_of(separator);
+ //find first separator
+ std::string::size_type pos = line.find_first_of(separator, last_pos);
+ while(std::string::npos != pos || std::string::npos != last_pos)
+ {
+ descriptors.push_back(line.substr(last_pos, pos - last_pos));
+ last_pos = line.find_first_not_of(separator, pos);
+ pos = line.find_first_of(separator, last_pos);
+ }
+
+ // By default, the last tag is at zero and not recorded but if take in count
+ unsigned int flag = 0;
+ if(descriptors.size() == 4)
+ {
+ std::stringstream val;
+ val << std::dec << descriptors[3];
+ val>> flag;
+ }
+
+ // if Dicom tag, use "group" and "element" descriptor
+ if(descriptors[0] == "D")
+ { std::stringstream val, val2;
+ unsigned short group;
+ unsigned short element;
+ val << std::dec << descriptors[1] ;
+ val >> std::hex >> group;
+ val2 << std::dec << descriptors[2];
+ val2 >> std::hex >> element;
+ Add(AttributeDescriptor( group,element,flag), ilevel);
+ }
+
+ else if(descriptors[0].find("#") != -1)
+ {
+ // commented line continue to next line
+ }
+ else
+ { boost::algorithm::replace_all(descriptors[2],"_"," ");
+ Add(AttributeDescriptor( descriptors[1].c_str(),descriptors[2].c_str(),flag), ilevel);
+ }
+ }
+ }
+ }
+
+ //==================================================================
+
+ //==================================================================
+ /// Adds a LevelDescriptor at the end of the list
+ void Descriptor::Add(const LevelDescriptor& d)
+ {
+ mLevelDescriptorList.push_back(d);
+ }
+ //==================================================================
+
+ //==================================================================
+ /// Adds an AttributeDescriptor to level l
+ void Descriptor::Add(const AttributeDescriptor& d, int l)
+ {
+ mLevelDescriptorList[l].Add(d);
+ // TO DO : update DicomTagToName and NameToDicomTag map
+ }
+ //==================================================================
+
+ //==================================================================
+ /// Clears the Descriptor
+ void Descriptor::Clear()
+ {
+ mLevelDescriptorList.clear();
+ }
+
+ //==================================================================
+
+ //==================================================================
+ /// Builds the key to value map of all the attributes of the tree
+ void Descriptor::BuildAttributeMap( std::map<std::string,std::string>& map ) const
+ {
+ map.clear();
+ LevelDescriptorListType::const_iterator l;
+ for (l = GetLevelDescriptorList().begin();
+ l!= GetLevelDescriptorList().end();
+ ++l)
+ {
+ LevelDescriptor::AttributeDescriptorListType::const_iterator a;
+ for (a = l->GetAttributeDescriptorList().begin();
+ a!= l->GetAttributeDescriptorList().end();
+ ++a)
+ {
+ map[a->GetKey()]="";
+ }
+ }
+ }
+ }
+}
--- /dev/null
+#ifndef __creaImageIOTreeDescriptor_h_INCLUDED__
+#define __creaImageIOTreeDescriptor_h_INCLUDED__
+
+#include <creaImageIOTreeAttributeMapType.h>
+#include <creaImageIOTreeLevelDescriptor.h>
+
+
+namespace creaImageIO
+{
+ using namespace std;
+ namespace tree
+ {
+
+ /**
+ * \ingroup Tree
+ */
+
+ ///Descriptor of the structure of a Tree (number of levels, descriptors of each level, ...).
+ //Any Tree has at least one level (level 0) of name "Root"
+
+ class Descriptor
+ {
+ public:
+ /// Ctor : creates the mandatory level 0 descriptor called "Root"
+ Descriptor();
+ /// Destructor
+ ~Descriptor();
+
+ /// Loads from a xml description file
+ void LoadXML(const std::string& filename);
+ /// Creates the default descriptor
+ void CreateDefault();
+
+ /// Returns the number of levels of the tree
+ unsigned int GetNumberOfLevels()
+ { return mLevelDescriptorList.size(); }
+
+ /// Returns the LevelDescriptor of a given level (const ref)
+ const LevelDescriptor& GetLevelDescriptor(int level) const
+ { return mLevelDescriptorList[level]; }
+
+ /// Returns the AttributeDescriptorList of a given level (const ref)
+ const LevelDescriptor::AttributeDescriptorListType&
+ GetAttributeDescriptorList(int level) const
+ { return mLevelDescriptorList[level].GetAttributeDescriptorList(); }
+
+
+ /// Adds a LevelDescriptor at the end of the list
+ void Add(const LevelDescriptor&);
+
+ /// Adds an AttributeDescriptor to level l
+ void Add(const AttributeDescriptor&, int l);
+
+
+ /// Builds the key to value map of all the attributes of the tree
+ void BuildAttributeMap( AttributeMapType& ) const;
+
+ /// The type of LevelDescriptor container
+ typedef std::vector<LevelDescriptor> LevelDescriptorListType;
+
+ /// Returns the list of tree levels (const)
+ const LevelDescriptorListType& GetLevelDescriptorList() const { return mLevelDescriptorList; }
+
+ /// Clears the Descriptor
+ void Clear();
+
+ //Create Attribute Descriptors from a file
+ void createDescriptorfromFile(const std::string &i_file);
+
+ private:
+ LevelDescriptorListType mLevelDescriptorList;
+ /// Creates the mandatory level 0 descriptor called "Root"
+ /// (assumes the list is empty)
+ void CreateLevel0Descriptor();
+
+ };
+ // EO class Descriptor
+ //=====================================================================
+ } // EO namespace tree
+
+} // EO namespace creaImageIO
+
+// EOF
+#endif
--- /dev/null
+#ifndef __creaImageIOTreeHandler_h_INCLUDED__
+#define __creaImageIOTreeHandler_h_INCLUDED__
+
+#include <creaImageIOTree.h>
+
+namespace creaImageIO
+{
+
+
+ //=======================================================================
+ //class TreeHandlerStatistics;
+ //=======================================================================
+ /**
+ * \ingroup Model
+ */
+ //=======================================================================
+
+ /// Abstract class which 'handles' a Tree structure
+ class TreeHandler
+ {
+ public:
+
+ //====================================================================
+ // typedef TreeHandlerStatistics Statistics;
+ //====================================================================
+
+ //====================================================================
+ /// Ctor
+ TreeHandler() {}
+ /// Virtual dtor
+ virtual ~TreeHandler() {}
+ //====================================================================
+
+ //====================================================================
+ /// Returns the Tree handled
+ tree::Tree& GetTree() { return mTree; }
+ /// Returns the Tree handled (const)
+ const tree::Tree& GetTree() const { return mTree; }
+ //====================================================================
+
+ //====================================================================
+ // QUERY METHODS
+ /// Is the 'source' readable ?
+ virtual bool IsReadable() { return false; }
+ /// Is the 'source' writable ?
+ virtual bool IsWritable() { return false; }
+ //====================================================================
+
+
+ //====================================================================
+ // INITIALIZATION / FINALIZATION
+ //====================================================================
+
+ //====================================================================
+ /// Opens an existing 'source'
+ // Default mode is read only
+ // If IsWritable and writable==true then opens in read/write mode
+ virtual bool Open(bool writable = false) { return false; }
+ /// Closes the 'source'
+ virtual bool Close() { return false; }
+ /// Creates a new 'source'
+ // Default mode is read only
+ // If IsWritable and writable==true then opens in read/write mode
+ virtual bool Create(bool writable = false) { return false; }
+ /// Destroys the 'source'
+ virtual bool Destroy() { return false; }
+ /// Begins a transaction
+ virtual void BeginTransaction(){}
+ ///Commits results and ends transaction
+ virtual void EndTransaction(){}
+ //====================================================================
+
+
+ //====================================================================
+ // READ METHODS
+ //====================================================================
+
+
+ //====================================================================
+ /// Returns the number of children of the Node *WITHOUT LOADING THEM*
+ // REM : The Tree itself is a Node and asking for its number of
+ // children returns the number of children of level 1.
+ virtual unsigned int GetNumberOfChildren(tree::Node* n) { return 0; }
+ //====================================================================
+
+ //====================================================================
+ /// Returns the attribute requested. Useful for synchronization.
+ virtual void GetAttribute(std::string levelDescriptor,
+ std::string searchParam,
+ std::string searchVal,
+ std::string key,
+ std::string& result){}
+ //====================================================================
+
+ //====================================================================
+ /// Recursively loads the children of node 'parent' until maxlevel
+ // is reached.
+ // If maxlevel <= 0 then loads all the sub-tree rooted at parent
+ // If parent == NULL or parent == tree then starts with the 'children' of
+ // the tree itself.
+ // Returns the total number of children loaded.
+ virtual int LoadChildren(tree::Node* parent, int maxlevel)
+ { return 0; }
+ //====================================================================
+
+ //====================================================================
+ /// Unloads the Node and its descendants
+ // WITHOUT altering the source, e.g. the database
+ virtual void UnLoad(tree::Node* n) { return; }
+ //====================================================================
+
+ //====================================================================
+ /// Returns the top level node id for the given search param and search value
+ virtual void GetTopLevelNodeId(const std::string& searchParam,
+ const std::string& searchValue,
+ std::string& parent_id){ return; }
+ ///====================================================================
+
+
+ //====================================================================
+ // WRITE METHODS : WORK ONLY IN WRITE MODE
+ //====================================================================
+ typedef tree::Node::AttributeMapType AttributeMapType;
+ /// Adds a branch in the tree with the attributes provided
+ // returns the Level in the tree where the branch was connected
+ // (-1 for error, 0 for top level, etc. )
+ // Of course the branch is loaded on exit
+ virtual int AddBranch( const AttributeMapType& ) { return -1; }
+ /// Removes the node and its descendants
+ virtual bool Remove(tree::Node*) { return false; }
+ /// Sets an attribute of a Node
+ virtual bool SetAttribute(tree::Node*,
+ const std::string& key,
+ const std::string& value) { return false; }
+ // Sets an attribute
+ virtual void SetAttribute(const std::string& levelDescriptor,
+ const std::string& key,
+ const std::string& value,
+ const std::string& searchParam,
+ const std::string& searchVal){}
+ //Deletes the tuple that matches the parameters given
+ virtual void DeleteTuple(std::string levelDescriptor, std::string key, std::string value){}
+ //Deletes the entries that match the parameters given
+ virtual void RemoveEntries(const std::string i_table,
+ const std::string i_attribute,
+ const std::string i_operand,
+ const std::string i_val){}
+
+ //====================================================================
+
+
+ private:
+ /// The handled tree
+ tree::Tree mTree;
+
+ };
+ // EO class TreeHandler
+ //=======================================================================
+ /*
+ //=======================================================================
+ /// Memorizes statistics on operations done by a tree handler
+ // (nodes created, removed, ...)
+ class TreeHandlerStatistics
+ {
+ public:
+ //====================================================================
+ /// Ctor
+ TreeHandler(TreeHandler* tree) : mTreeHandler(tree) { Reset(); }
+ /// Dtor
+ ~TreeHandler() {}
+ /// Resets the stats
+ void Reset();
+ /// Prints the stats
+ void Print();
+
+ ///
+ void CreateNode(int level) { mNumberCreatedNode[level]++; }
+ void DeleteNode(int level) { mNumberDeletedNode[level]++; }
+
+ protected:
+ TreeHandler* mTreeHandler;
+ std::vector<int> mNumberCreatedNode;
+ std::vector<int> mNumberDeletedNode;
+
+
+ ///====================================================================
+ };
+ // EO class TreeHandlerStatistics
+ //=======================================================================
+ */
+
+} // EO namespace creaImageIO
+
+// EOF
+#endif
+
--- /dev/null
+#include <creaImageIOTreeHandlerImageAdder.h>
+#include <creaImageIOSystem.h>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/utility.hpp>
+
+
+namespace fs = boost::filesystem;
+using boost::filesystem::path;
+using boost::next;
+using boost::prior;
+
+
+using namespace crea;
+using namespace boost;
+
+namespace creaImageIO
+{
+ //====================================================================
+ // Ctor
+ TreeHandlerImageAdder::TreeHandlerImageAdder(TreeHandler* tree)
+ : mTreeHandler(tree)
+ {
+ }
+ // Dtor
+ TreeHandlerImageAdder::~TreeHandlerImageAdder()
+ {
+ }
+ //====================================================================
+
+ //====================================================================
+ void TreeHandlerImageAdder::ConnectProgressObserver(ProgressCallbackType callback)
+ {
+ mProgressSignal.connect(callback);
+ }
+ //====================================================================
+
+ //=====================================================================
+ bool TreeHandlerImageAdder::IsHandledFile( const std::string& filename)
+ {
+ return (mReader.CanRead(filename));
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void TreeHandlerImageAdder::AddFiles( const std::vector<std::string>& filenames)
+ {
+ mProgress.Reset();
+
+ unsigned int nbf = filenames.size();
+ std::vector<std::string>::const_iterator i;
+ mSynchronizer->GetList(mCurrentDB);
+ for (i=filenames.begin();i!=filenames.end();++i)
+ {
+
+ mProgress.IncNumberScannedFiles();
+ if (IsHandledFile(*i))
+ {
+ mProgress.IncNumberHandledFiles();
+ if(mSynchronizer->isIndexed(*i))
+ {
+ mSynchronizer->InsertAddOp((*i),"0","1",mCurrentDB);
+ std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","ADD_OPS","PATH",(*i),mCurrentDB);
+ std::stringstream removedOn;
+ removedOn<<time(0);
+ mSynchronizer->InsertIgnoreFile(addKey,(*i),"0",removedOn.str(),mCurrentDB);
+ AddFile(*i);
+ }
+ }
+ mProgressSignal(mProgress);
+ if (mProgress.GetStop()) break;
+ }
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void TreeHandlerImageAdder::AddDirectory( const std::string& directory,
+ bool recurse)
+ {
+ mProgress.Reset();
+ std::stringstream files;
+
+ std::stringstream rec;
+ rec<<recurse;
+ mSynchronizer->InsertAddOp(directory,rec.str(),"0",mCurrentDB);
+ std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","ADD_OPS","PATH",directory,mCurrentDB);
+ mTreeHandler->BeginTransaction();
+ mSynchronizer->GetList(mCurrentDB);
+ //AddDirectoryRecursor( directory, recurse, addKey );
+ DicomImageScanner sc;
+ AddDirectoryRecursorScanner(directory, recurse, addKey, sc, false );
+
+ int nFiles=GetProgress().GetNumberAddedFiles();
+ files<<nFiles;
+ mSynchronizer->SetAttribute("FILES_ADDED","ADD_OPS",files.str(),"ADD_KEY",addKey,mCurrentDB);
+ mTreeHandler->EndTransaction();
+ GimmickDebugMessage(3,mProgress<<std::endl);
+ }
+
+ //=====================================================================
+ void TreeHandlerImageAdder::AddFile( const std::string& filename )
+ {
+ GimmickDebugMessage(4,"Adding '"<<filename<<"'"<<std::endl);
+ std::map< std::string, std::string> attr;
+ mTreeHandler->GetTree().GetDescriptor().BuildAttributeMap(attr);
+
+
+ mReader.ReadAttributes(filename,attr);
+ //// TO DO Create a function to test if the SOP Instance ID (0008,0018) is not already on DB
+ //// test befor if this attr is present on DB if not don't perform the test!!!
+ //bool bSOPIID = false;
+ //std::map<std::string, std::string>::iterator it_att = attr.begin();
+ //for(; it_att != attr.end(); it_att++)
+ //{
+ // if (it_att->first == "D0008_0018")
+ // {
+ // bSOPIID = mTreeHandler->TestSOPIID(it_attr->second);
+ // break;
+ // }
+ //}
+ //if(bSOPIID)
+ // return;
+
+ int lev = mTreeHandler->AddBranch(attr);
+
+ // update the progress according to lev
+ if (lev<mTreeHandler->GetTree().GetNumberOfLevels())
+ mProgress.IncNumberAddedFiles();
+ }
+ //=====================================================================
+
+ void TreeHandlerImageAdder::RemoveFile( tree::Node* node)
+ {
+ int n=node->GetNumberOfChildren();
+ if(n>0)
+ {
+ RemoveFiles(node->GetChildrenList());
+ }
+ else
+ {
+ std::string path=node->GetAttribute("FullFileName");
+ //Gets the add key
+ std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","IGNORED_FILES","PATH",path,mCurrentDB);
+ //Gets the number of files added
+ int files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",addKey,mCurrentDB)).c_str());
+ files=files-1;
+ std::stringstream out;
+ out<<files;
+ //Sets the new number of files
+ mSynchronizer->SetAttribute("FILES_ADDED","ADD_OPS",out.str(),"ADD_KEY",addKey,mCurrentDB);
+ //Sets the file as removed
+ mSynchronizer->SetAttribute("REMOVE","IGNORED_FILES","1","PATH = '"+path+"' AND ADD_KEY",addKey,mCurrentDB);
+ }
+ }
+
+ //=====================================================================
+
+ void TreeHandlerImageAdder::RemoveFiles(const std::vector<tree::Node*>& nodes)
+ {
+ std::vector<tree::Node*>::const_iterator it;
+ for(it=nodes.begin();it!=nodes.end();++it)
+ {
+ int n=(*it)->GetNumberOfChildren();
+ if(n>0)
+ {
+ RemoveFiles((*it)->GetChildrenList());
+ }
+ else
+ {
+ std::string path=(*it)->GetAttribute("FullFileName");
+ //Gets the add key
+ std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","IGNORED_FILES","PATH",path,mCurrentDB);
+ //Gets the number of files added
+ int files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",addKey,mCurrentDB)).c_str());
+ files=files-1;
+ std::stringstream out;
+ out<<files;
+ //Sets the new number of files
+ mSynchronizer->SetAttribute("FILES_ADDED","ADD_OPS",out.str(),"ADD_KEY",addKey,mCurrentDB);
+ //Sets the file as removed
+ mSynchronizer->SetAttribute("REMOVE","IGNORED_FILES","1","PATH = '"+path+"' AND ADD_KEY",addKey,mCurrentDB);
+ }
+
+ }
+ }
+
+
+
+ //=======================================================================
+ //=====================================================================
+#if defined(USE_GDCM2)
+
+ void TreeHandlerImageAdder::AddDirectoryRecursorScanner(const std::string &dirpath,
+ bool recursive,const std::string &addKey, DicomImageScanner i_sc, bool b_loaded)
+ {
+ GimmickDebugMessage(4,"Scanning '"<<dirpath<<"'"<<std::endl);
+ mProgress.IncNumberScannedDirs();
+
+ if ( !fs::exists( dirpath ) ) return;
+ time_t lastModif=fs::last_write_time(dirpath);
+
+
+
+ std::map< std::string, std::string> attr;
+ mTreeHandler->GetTree().GetDescriptor().BuildAttributeMap(attr);
+ std::string path = dirpath.c_str();
+ i_sc.addDirectory(path, attr);
+
+
+ fs::directory_iterator end_itr; // default construction yields past-the-end
+ for ( fs::directory_iterator itr( dirpath );
+ itr != end_itr;
+ ++itr )
+ {
+
+ // If is directory & recurse : do recurse
+ if ( fs::is_directory(itr->status()) )
+ {
+ if (recursive)
+ {
+ AddDirectoryRecursorScanner( itr->string(), recursive, addKey, i_sc, true);
+ }
+ }
+ else
+ {
+ std::string parent_id;
+ // tTest if directory (and only it) exists or not.
+ bool valid = mSynchronizer->isIndexed(itr->string());//true;//=mTimestampHandler->AddDirectory(dirpath, itr->string(), lastModif, time(0),mCurrentDB);
+ if(valid)
+ {
+
+ mProgress.IncNumberScannedFiles();
+ boost::algorithm::replace_all( itr->string(),
+ INVALID_FILE_SEPARATOR ,
+ VALID_FILE_SEPARATOR);
+ i_sc.ReadAttributes(itr->string(),attr);
+ mTreeHandler->GetTopLevelNodeId("FullFileName",itr->string(),parent_id);
+ mTreeHandler->AddBranch(attr);
+ mProgress.IncNumberHandledFiles();
+ std::stringstream removedOn;
+ removedOn<<time(0);
+ mSynchronizer->InsertIgnoreFile(addKey, itr->string(),"0",removedOn.str(),mCurrentDB);
+
+
+ mProgressSignal(mProgress);
+ if (mProgress.GetStop())
+ {
+ //itr = end_itr;
+ break;
+ }
+ }
+
+ }
+ }
+ }
+#endif
+ //=====================================================================
+ void TreeHandlerImageAdder::AddDirectoryRecursor(const std::string &dirpath,
+ bool recursive,
+ const std::string &addKey)
+ {
+ GimmickDebugMessage(4,"Scanning '"<<dirpath<<"'"<<std::endl);
+ mProgress.IncNumberScannedDirs();
+
+ if ( !fs::exists( dirpath ) ) return;
+ time_t lastModif=fs::last_write_time(dirpath);
+
+
+ fs::directory_iterator end_itr; // default construction yields past-the-end
+ for ( fs::directory_iterator itr( dirpath );
+ itr != end_itr;
+ ++itr )
+ {
+ // If is directory & recurse : do recurse
+ if ( fs::is_directory(itr->status()) )
+ {
+ if (recursive)
+ {
+ AddDirectoryRecursor( itr->string(), recursive, addKey);
+ }
+ }
+ else
+ {
+ std::string parent_id;
+ // tTest if directory (and only it) exists or not.
+ bool valid = mSynchronizer->isIndexed(itr->string());//true;//=mTimestampHandler->AddDirectory(dirpath, itr->string(), lastModif, time(0),mCurrentDB);
+ if(valid)
+ {
+ mProgress.IncNumberScannedFiles();
+ if (IsHandledFile(itr->string()))
+ {
+ mProgress.IncNumberHandledFiles();
+ AddFile( itr->string() );
+ mTreeHandler->GetTopLevelNodeId("FullFileName",itr->string(),parent_id);
+ std::stringstream removedOn;
+ removedOn<<time(0);
+ mSynchronizer->InsertIgnoreFile(addKey, itr->string(),"0",removedOn.str(),mCurrentDB);
+ }
+
+ mProgressSignal(mProgress);
+ if (mProgress.GetStop())
+ {
+ //itr = end_itr;
+ break;
+ }
+
+ }
+ }
+ }
+
+ }
+ //=======================================================================
+
+
+ //=======================================================================
+
+ void TreeHandlerImageAdder::CheckSyncDirectory(const std::string &dirpath,
+ bool recursive,
+ bool repair,
+ bool checkAttributes,
+ std::vector<std::string> &i_ignorefiles,
+ std::vector<std::string> & attsModified,
+ std::vector<std::string> & newfiles)
+ {
+ if ( !fs::exists( dirpath ) ) return;
+ fs::directory_iterator end_itr; // default construction yields past-the-end
+
+ for ( fs::directory_iterator itr( dirpath ); itr != end_itr; ++itr )
+ {
+ // If is directory & recurse : do recurse
+ if ( fs::is_directory(itr->status()) )
+ {
+ if (recursive)
+ {
+ CheckSyncDirectory( itr->string(), recursive, repair, checkAttributes, i_ignorefiles, attsModified, newfiles);
+ }
+ }
+ else
+ {
+ if (IsHandledFile(itr->string()))
+ {
+ bool bfound = false;
+ for(std::vector<std::string>::iterator it_new = i_ignorefiles.begin(); it_new < i_ignorefiles.end(); ++it_new)
+ {
+ if((*it_new) == itr->string())
+ {
+ bfound = true;
+ //Additional checking of attributes
+ if(checkAttributes)
+ {
+ CheckAttributes(repair,(*it_new),attsModified);
+ }
+ i_ignorefiles.erase(it_new);
+ break;
+ }
+ }
+ if(!bfound && i_ignorefiles.size()>0 )
+ {
+ newfiles.push_back( itr->string() );
+ }
+ }
+ }
+ }
+ }
+
+ //=======================================================================
+
+ //=======================================================================
+
+ std::string TreeHandlerImageAdder::Synchronize(bool repair, bool checkAttributes)
+ {
+ std::vector<AddList> fileList;
+ std::vector<std::string> ignoreList;
+ std::vector<std::string> newFiles;
+ std::vector<std::string> attsModified;
+ std::stringstream mess;
+ std::vector<AddList>::iterator iter;
+
+ //Gets the list of added files
+ mSynchronizer->GetFileList(fileList,mCurrentDB);
+
+ std::vector<std::string>::iterator i;
+ //Actions to take if the user doesn't want to repair
+ if(!repair)
+ {
+ //Iterates to see if they are in sync
+ for(iter=fileList.begin();iter!=fileList.end();++iter)
+ {
+ mSynchronizer->GetIgnoredFiles((*iter).key,ignoreList);
+ bool rec=true;
+ if((*iter).recursive=="0"){rec=false;}
+ CheckSyncDirectory((*iter).path,rec,repair,checkAttributes,ignoreList,attsModified,newFiles);
+ }
+
+ //Add to message the result of new found files
+ mess<<"New Files Found: "<<newFiles.size()<<std::endl;
+ if(newFiles.size()>0)
+ {
+ mess<<"Filenames: "<<std::endl;
+ for(i=newFiles.begin();i!=newFiles.end();++i)
+ {
+ mess<<*i<<std::endl;
+ }
+ }
+
+ //Add to message the result of missing files
+ mess<<"Missing Files: "<<ignoreList.size()<<std::endl;
+ if(ignoreList.size()>0)
+ {
+ mess<<"Filenames: "<<std::endl;
+ for(i=ignoreList.begin();i!=ignoreList.end();++i)
+ {
+ mess<<*i<<std::endl;
+ }
+ }
+
+ //In the case that the user wants to check the attributes...
+ if(checkAttributes)
+ {
+ //... add to message the result of files that have been changed.
+ mess<<"Files with different attributes: "<<attsModified.size()<<std::endl;
+ if(attsModified.size()>0)
+ {
+ mess<<"Filenames: "<<std::endl;
+ for(i=attsModified.begin();i!=attsModified.end();++i)
+ {
+ mess<<*i<<std::endl;
+ }
+ }
+ }
+
+ }
+
+ //Actions to take if the user wants to repair
+ else
+ {
+ int nf=0;
+ //Iterates to see if they are in sync
+ for(iter=fileList.begin();iter!=fileList.end();++iter)
+ {
+ mSynchronizer->GetIgnoredFiles((*iter).key,ignoreList);
+ bool rec=true;
+ if((*iter).recursive=="0"){rec=false;}
+ CheckSyncDirectory((*iter).path,rec,repair,checkAttributes,ignoreList,attsModified,newFiles);
+
+ //For the new files, add them
+ for (i=newFiles.begin();i!=newFiles.end();++i)
+ {
+ if (IsHandledFile(*i))
+ {
+ std::stringstream removedOn;
+ removedOn<<time(0);
+ mSynchronizer->InsertIgnoreFile((*iter).key,(*i),"0",removedOn.str(),mCurrentDB);
+ //Gets the number of files added
+ int files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",(*iter).key,mCurrentDB)).c_str());
+ files=files+1;
+ std::stringstream out;
+ out<<files;
+ //Sets the new number of files
+ mSynchronizer->SetAttribute("FILES_ADDED","ADD_OPS",out.str(),"ADD_KEY",(*iter).key,mCurrentDB);
+ AddFile(*i);
+ }
+ }
+ nf+=newFiles.size();
+ newFiles.clear();
+
+ }
+ //Reports number of added files
+ mess<<"Files Added: "<<nf<<std::endl;
+
+ //Removes the necessary files and reports the results
+ if(ignoreList.size()>0)
+ {
+ tree::Node* node;
+ mTreeHandler->LoadChildren(NULL,4);
+ for(i=ignoreList.begin();i!=ignoreList.end();++i)
+ {
+ FindNode(mTreeHandler->GetTree().GetChildrenList()[0],3,"FullFileName",*i,node);
+ RemoveFile(node);
+ mTreeHandler->Remove(node);
+ }
+ }
+ mess<<"Files Removed: "<<ignoreList.size()<<std::endl;
+ //In the case that the user wants to check the attributes...
+ if(checkAttributes)
+ {
+ //... add to message the result of files that were modified.
+ mess<<"Files Modified: "<<attsModified.size()<<std::endl;
+ }
+ }
+ return mess.str();
+
+ }
+ //=======================================================================
+
+ void TreeHandlerImageAdder::CheckAttributes(bool repair, std::string& file, std::vector<std::string>& attsModified)
+ {
+ std::map< std::string, std::string> attr;
+ mTreeHandler->GetTree().GetDescriptor().BuildAttributeMap(attr);
+ mReader.ReadAttributes(file,attr);
+ tree::LevelDescriptor::AttributeDescriptorListType adl= mTreeHandler->GetTree().GetAttributeDescriptorList(mTreeHandler->GetTree().GetNumberOfLevels()-1);
+ tree::LevelDescriptor::AttributeDescriptorListType::const_iterator a;
+ for (a = adl.begin();a!=adl.end();++a)
+ {
+ std::string databaseVal;
+ mTreeHandler->GetAttribute("Image","FullFileName",file,a->GetKey(),databaseVal);
+ std::string fileVal=attr.find(a->GetKey())->second;
+ if ( a->GetFlags()==0 && databaseVal == fileVal)
+ {
+ if(repair)
+ {
+ mTreeHandler->SetAttribute("Image",a->GetKey(),fileVal,"FullFileName", file);
+ }
+ attsModified.push_back(file);
+ }
+
+ }
+
+ }
+
+ //=======================================================================
+
+
+ void TreeHandlerImageAdder::FindNode(tree::Node* parent, int level, const std::string& searchParam, const std::string& searchVal, tree::Node*& node)
+ {
+ if(level>1)
+ {
+ std::vector<tree::Node*>::iterator iter;
+ for(iter=parent->GetChildrenList().begin();iter!=parent->GetChildrenList().end();++iter)
+ {
+ FindNode(*iter,level-1,searchParam,searchVal,node);
+ }
+ }
+ else
+ {
+ if(parent->GetAttribute(searchParam).compare(searchVal)==0)
+ {
+ node=parent;
+ }
+
+ }
+ }
+
+ void TreeHandlerImageAdder::SaveAs(const std::vector<std::string>& filenames, std::vector<vtkImageData *> i_images)
+ {
+ std::vector<std::string>::const_iterator it_file = filenames.begin();
+ std::vector<vtkImageData *>::iterator it_image = i_images.begin();
+ /* mWriter.CanWrite(".jpeg");
+ for(; it_file != filenames.end(); ++it_file, ++it_image)
+ mWriter.WriteImage(it_file->c_str(), (vtkImageData &)it_image);*/
+ }
+
+ //=======================================================================
+ void TreeHandlerImageAdder::FindNodePartial(tree::Node* parent, int level, const std::string& searchParam, const std::string& searchVal, tree::Node*& node)
+ {
+ if(level>1)
+ {
+ std::vector<tree::Node*>::iterator iter;
+ for(iter=parent->GetChildrenList().begin();iter!=parent->GetChildrenList().end() && node==0 ;++iter)
+ {
+ FindNodePartial(*iter,level-1,searchParam,searchVal,node);
+ }
+ }
+ else
+ {
+ if(parent->GetAttribute(searchParam).find(searchVal)<9000)
+ {
+ node=parent;
+ return;
+ }
+
+ }
+ }
+
+ //=======================================================================
+
+ void TreeHandlerImageAdder::CopyFiles(const std::vector<std::string>& filenames, const std::string directory )
+ {
+ std::vector<std::string>::const_iterator i;
+ if(!boost::filesystem::exists(directory))
+ {
+ boost::filesystem::create_directory(boost::filesystem::path(directory));
+ mSynchronizer->InsertAddOp(directory,"0","0",mCurrentDB);
+ }
+ std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","ADD_OPS","PATH",directory,mCurrentDB);
+ size_t last;
+ std::vector<std::string> newNames;
+ for(i=filenames.begin();i!=filenames.end();++i)
+ {
+ std::string dir=directory.c_str();
+ if(boost::filesystem::exists(*i) && (*i).find(dir)==std::string::npos)
+ {
+ std::string path=*i;
+ last=(*i).find_last_of('/');
+ std::string f="\\"+(*i).substr(last+1);
+
+ int p=1;
+ std::stringstream out;
+ out<<directory<<f;
+ while(boost::filesystem::exists(out.str()))
+ {
+ out.str("");
+ out<<directory<<f.substr(0,f.size()-4)<<"("<<p<<")"<<f.substr(f.size()-4);
+ p++;
+ }
+ std::string result=out.str();
+ boost::filesystem::copy_file((*i),result);
+
+ //To update image database
+ mTreeHandler->SetAttribute("Image","FullFileName",result,"FullFileName", (*i));
+
+ //To update maintenance database
+ //1.Add the new path and increase number of children on new operation.
+ std::stringstream removedOn;
+ removedOn<<time(0);
+ //Inserts the file
+ mSynchronizer->InsertIgnoreFile(addKey, result,"0",removedOn.str(),mCurrentDB);
+ //Gets the number of files added
+ int files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",addKey,mCurrentDB)).c_str());
+ files=files+1;
+ std::stringstream fil;
+ fil<<files;
+ //Sets the new number of files
+ mSynchronizer->SetAttribute("FILES_ADDED","ADD_OPS",fil.str(),"ADD_KEY",addKey,mCurrentDB);
+ fil.str("");
+
+ //2.Set the old path as removed and decrease number of children on old operation.
+ //Gets the old add key
+ std::string oldAddKey=mSynchronizer->GetAttribute("ADD_KEY","IGNORED_FILES","PATH",path,mCurrentDB);
+ //Sets the file as removed
+ mSynchronizer->SetAttribute("REMOVE","IGNORED_FILES","1","PATH = '"+path+"' AND ADD_KEY",oldAddKey,mCurrentDB);
+ //Gets the number of files added
+ files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",oldAddKey,mCurrentDB)).c_str());
+ files=files-1;
+ fil<<files;
+ //Sets the new number of files
+ mSynchronizer->SetAttribute("FILES_ADDED","ADD_OPS",fil.str(),"ADD_KEY",oldAddKey,mCurrentDB);
+
+ }
+
+ }
+ }
+
+ //=======================================================================
+
+ void TreeHandlerImageAdder::DeleteDriveFromMainDB(const std::string& drive)
+ {
+ //Delete from local database and others
+ tree::Node* node=0;
+ mTreeHandler->LoadChildren(NULL,4);
+ FindNodePartial(mTreeHandler->GetTree().GetChildrenList()[0],3,"FullFileName",drive,node);
+ while(node!=0)
+ {
+ mTreeHandler->Remove(node);
+ node=0;
+ mTreeHandler->LoadChildren(NULL,4);
+ FindNodePartial(mTreeHandler->GetTree().GetChildrenList()[0],3,"FullFileName",drive,node);
+ }
+ }
+
+ //=======================================================================
+
+ void TreeHandlerImageAdder::DeleteDriveFromOtherDB(const std::string& drive)
+ {
+ //Delete from maintenance
+ mSynchronizer->RemoveEntries("ADD_OPS", "PATH", "LIKE", drive+"%");
+ mSynchronizer->RemoveEntries("IGNORED_FILES", "PATH", "LIKE", drive+"%");
+ }
+
+ //=======================================================================
+ void TreeHandlerImageAdder::EditField(tree::Node* node, const std::string& name, const std::string& key, const std::string& val)
+ {
+ node->SetAttribute(key,val);
+ mTreeHandler->SetAttribute(node,key,val);
+ }
+
+ //=======================================================================
+ void TreeHandlerImageAdder::GetAttributes(const std::vector<std::string>& params,
+ const std::string& filename,
+ std::vector<std::string>& results)
+ {
+ std::vector<std::string>::const_iterator i;
+ std::string result;
+ for(i=params.begin();i!=params.end();i++)
+ {
+ mTreeHandler->GetAttribute("Image","FullFileName",filename,*i,result);
+ results.push_back(result);
+ }
+ }
+
+
+}
--- /dev/null
+#ifndef __creaImageIOTreeHandlerImageAdder_h_INCLUDED__
+#define __creaImageIOTreeHandlerImageAdder_h_INCLUDED__
+
+#include <creaImageIOTreeHandler.h>
+#include <creaImageIOSynchron.h>
+#include <creaImageIOImageReader.h>
+//#include <creaImageIOImageWriter.h>
+
+// Signal/slot mechanism for progress events
+#include <boost/signal.hpp>
+#include <boost/bind.hpp>
+#if defined(USE_GDCM2)
+#include "creaImageIODicomScanner.h"
+#endif
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup Model
+ */
+
+ //=======================================================================
+ /// Object which can add images files to a TreeHandler. Is able to parse (recursively) a part of a filesystem to look for known images and load their attributes in order to add the images to a Tree (submission via a TreeHandler::AddBranch)
+
+ class TreeHandlerImageAdder
+ {
+
+ public:
+ //====================================================================
+ /// Ctor
+ TreeHandlerImageAdder(TreeHandler* tree);
+ /// Dtor
+ ~TreeHandlerImageAdder();
+ /// Sets the TreeHandler
+ void SetTreeHandler(TreeHandler* tree) { mTreeHandler = tree;}
+
+ /// Sets the synchronizer
+ void SetSynchronizer(Synchronizer* s){mSynchronizer=s;}
+ /// Sets the synchronizer
+ void SetCurrentDatabase(std::string cur){mCurrentDB=cur;}
+ //====================================================================
+
+ //====================================================================
+ /// Structure which holds progress information
+ /// To stop the image adder use SetStop()
+ class Progress
+ {
+ public:
+ Progress() { Reset(); }
+ ~Progress() {}
+
+ void Reset()
+ {
+ mStop = false;
+ mNumberScannedFiles = 0;
+ mNumberScannedDirs = 0;
+ mNumberHandledFiles = 0;
+ mNumberAddedFiles = 0;
+ }
+
+ int GetNumberScannedFiles() const { return mNumberScannedFiles; }
+ int GetNumberScannedDirs() const { return mNumberScannedDirs; }
+ int GetNumberHandledFiles() const { return mNumberHandledFiles; }
+ int GetNumberAddedFiles() const { return mNumberAddedFiles; }
+
+ void IncNumberScannedFiles() { mNumberScannedFiles++; }
+ void IncNumberScannedDirs() { mNumberScannedDirs++; }
+ void IncNumberHandledFiles() { mNumberHandledFiles++; }
+ void IncNumberAddedFiles() { mNumberAddedFiles++; }
+
+ void SetStop() { mStop = true; }
+ bool GetStop() const { return mStop; }
+
+ private:
+ bool mStop;
+ int mNumberScannedFiles;
+ int mNumberScannedDirs;
+ int mNumberHandledFiles;
+ int mNumberAddedFiles;
+ };
+ //=============================================
+
+ //=============================================
+ const Progress& GetProgress() const { return mProgress; }
+ //=============================================
+
+ //=============================================
+ typedef boost::signal<void (Progress&)> ProgressSignalType;
+ typedef ProgressSignalType::slot_function_type ProgressCallbackType;
+ //=============================================
+
+ //==================================================================
+ /// Adds the function f to the list of functions to call
+ /// when the addition progresses.
+ /// f is of type ProgressCallbackType which is:
+ /// void (*ProgressCallbackType)(Progress&)
+ /// To pass a member function 'f' of an instance 'c' of a class 'C'
+ /// as callback you have to 'bind' it, i.e. call:
+ /// ConnectProgressObserver ( boost::bind( &C::f , c, _1 ) );
+ void ConnectProgressObserver(ProgressCallbackType callback);
+ //==================================================================
+
+ //====================================================================
+ /// Returns if the file can be read or not
+ bool IsHandledFile( const std::string& filename);
+ /// Adds a list of files to the TreeHandler
+ void AddFiles( const std::vector<std::string>& filename );
+ /// (Recursively) adds the files of a directory to the TreeHandler
+ void AddDirectory( const std::string& directory,
+ bool recurse);
+
+
+#if defined(USE_GDCM2)
+ void AddDirectoryRecursorScanner(const std::string &dirpath,
+ bool recursive,const std::string &addKey, DicomImageScanner i_sc, bool b_loaded);
+#endif
+
+ /// Removes a file from the databases
+ void RemoveFile(tree::Node* node);
+ /// Removes files from the databases
+ void RemoveFiles(const std::vector<tree::Node*>& nodes);
+ /// Synchronizes the DB and disk by repeating the operations the user has done and returns a report
+ std::string Synchronize(bool repair, bool checkAttributes);
+ ///Recursively checks if the directory is synchronized and optionally the state of the attributes
+ void CheckSyncDirectory(const std::string &dirpath,
+ bool recursive,
+ bool repair,
+ bool checkAttributes,
+ std::vector<std::string> &i_ignorefiles,
+ std::vector<std::string> & attsModified,
+ std::vector<std::string> & newfiles);
+ ///Copies the files indicated in the vector and updates all databases
+ void CopyFiles(const std::vector<std::string>& filenames, const std::string directory );
+
+ ///Saves as the files indicated in the vector in a specific directory
+ void SaveAs(const std::vector<std::string>& filenames, std::vector<vtkImageData *> i_images);
+ ///Finds the node that matches the specified parameters
+ void FindNode(tree::Node* parent, int level,
+ const std::string& searchParam,
+ const std::string& searchVal,
+ tree::Node*& node);
+
+ ///Finds the nodes that partially match the searchVal
+ void FindNodePartial(tree::Node* parent, int level, const std::string& searchParam, const std::string& searchVal, tree::Node*& node);
+
+ ///Checks the attributes of the database against the ones in disk
+ void CheckAttributes(bool repair, std::string& file, std::vector<std::string>& attsModified);
+ ///Deletes the drive with the given name (use for all databases except maintenance and timestamp)
+ void DeleteDriveFromMainDB(const std::string& drive);
+ ///Deletes the drive with the given name (use for maintenance and timestamp databases)
+ void DeleteDriveFromOtherDB(const std::string& drive);
+ ///Edits the given field and sets the new parameters
+ void EditField(tree::Node* node, const std::string& name, const std::string& key, const std::string& val);
+ ///Returns the demanded attributes for the given file
+ void GetAttributes(const std::vector<std::string>& params,
+ const std::string& filename,
+ std::vector<std::string>& results);
+
+
+
+
+ //====================================================================
+
+ private:
+
+ /// Adds a single file to the TreeHandler
+ /// **WITHOUT** testing wether it is handled or not
+ /// hence you have to call IsHandledFile before using AddFile!
+ void AddFile( const std::string& filename );
+
+ /// Recursive method which does the main job for AddDirectory
+ void AddDirectoryRecursor( const std::string& directory,
+ bool recurse,
+ const std::string &addKey);
+
+ TreeHandler* mTreeHandler;
+ Synchronizer* mSynchronizer;
+ ImageReader mReader;
+ //ImageWriter mWriter;
+ std::string mCurrentDB;
+
+ Progress mProgress;
+ ProgressSignalType mProgressSignal;
+
+
+ };
+ // EO class TreeHandlerImageAdder
+ //=======================================================================
+
+
+
+
+
+} // EO namespace creaImageIO
+
+#include <iostream>
+inline std::ostream& operator << ( std::ostream& o,
+ const creaImageIO::TreeHandlerImageAdder::Progress& p)
+{
+ o << p.GetNumberScannedFiles() << " files - "
+ << p.GetNumberScannedDirs() << " dirs - "
+ << p.GetNumberHandledFiles() << " handled -"
+ << p.GetNumberAddedFiles() << " added";
+ return o;
+}
+
+// EOF
+#endif
+
--- /dev/null
+#include <creaImageIOTreeLevelDescriptor.h>
+#include <creaImageIOSystem.h>
+
+
+namespace creaImageIO
+{
+ namespace tree
+ {
+ /// Adds the AttributeDescriptor to the list
+ void LevelDescriptor::Add(const AttributeDescriptor& a)
+ {
+ GimmickMessage(5,"Adding Attribute Descriptor '"<<a.GetKey()
+ <<"' to LevelDescriptor"
+ <<std::endl);
+ mAttributeDescriptorList.push_back(a);
+ if ( a.GetFlags() & AttributeDescriptor::IDENTIFIER )
+ {
+ GimmickMessage(6,"Is an IDENTIFIER"<<std::endl);
+ mIdentifierList.push_back(a.GetKey());
+ }
+ if ( a.GetFlags() & AttributeDescriptor::LABEL )
+ {
+ GimmickMessage(6,"Is a LABEL"<<std::endl);
+ mLabelList.push_back(a.GetKey());
+ }
+ }
+
+
+
+ }
+}
+
--- /dev/null
+#ifndef __creaImageIOTreeLevelDescriptor_h_INCLUDED__
+#define __creaImageIOTreeLevelDescriptor_h_INCLUDED__
+
+#include <creaImageIOTreeAttributeDescriptor.h>
+#include <vector>
+
+namespace creaImageIO
+{
+
+ namespace tree
+ {
+ /**
+ * \ingroup Tree
+ */
+ //=====================================================================
+ /// Descriptor of a level of a Tree (name, attributes, ...)
+ class LevelDescriptor
+ {
+ public:
+ /// Ctor with name
+ LevelDescriptor(const std::string& name) : mName(name) {}
+ /// Destructor
+ ~LevelDescriptor() {}
+
+ /// Returns the name of the level
+ const std::string& GetName() const { return mName; }
+
+ /// Returns the number of attributes of the level
+ unsigned int GetNumberOfAttributes() const
+ { return mAttributeDescriptorList.size(); }
+
+ /// Adds the AttributeDescriptor to the list
+ void Add(const AttributeDescriptor&);
+
+ /// The type of attribute container
+ typedef std::vector<AttributeDescriptor> AttributeDescriptorListType;
+
+ /// Returns the list of AttributeDescriptor (const)
+ const AttributeDescriptorListType& GetAttributeDescriptorList() const
+ { return mAttributeDescriptorList; }
+
+ /// \return The list of attributes with flag IDENTIFIER set
+ const std::vector<std::string>& GetIdentifierList() const
+ { return mIdentifierList; }
+ /// \return The list of attributes with flag LABEL set
+ const std::vector<std::string>& GetLabelList() const
+ { return mLabelList; }
+
+ private:
+ std::string mName;
+ AttributeDescriptorListType mAttributeDescriptorList;
+ /// The list of attributes with flag IDENTIFIER set
+ std::vector<std::string> mIdentifierList;
+ /// The list of attributes with flag LABEL set
+ std::vector<std::string> mLabelList;
+
+ };
+ // EO class LevelDescriptor
+ //=====================================================================
+ }// EO namespace tree
+
+
+} // EO namespace creaImageIO
+
+// EOF
+#endif
--- /dev/null
+#include <creaImageIOTreeNode.h>
+#include <creaImageIOTree.h>
+#include <creaImageIOSystem.h>
+#include <algorithm>
+#include <creaImageIOGimmick.h>
+
+namespace creaImageIO
+{
+ namespace tree
+ {
+
+ //=============================================================
+ /// Ctor with parent
+ Node::Node(Node* parent)
+ : mParent(parent),//mData(0),
+ mChildrenLoaded(false)
+ {
+ mData.reset();
+ if (parent)
+ {
+ GimmickDebugMessage(6,"Default Node constructor (level "<<GetLevel()<<")"
+ << std::endl);
+ // Insert into parent's children list
+ InitializeAttributeMap();
+ parent->GetChildrenList().push_back(this);
+ }
+ else
+ {
+ GimmickDebugMessage(6,"Default Node constructor without parent"
+ << std::endl);
+ }
+ }
+ //=============================================================
+
+ //=============================================================
+ /// Ctor with parent and attributes map
+ Node::Node(Node* parent, const AttributeMapType& attr)
+ : mParent(parent),//mData(0),
+ mChildrenLoaded(false)
+ {
+ mData.reset();
+ GimmickDebugMessage(6,"Node constructor (level "<<GetLevel()<<")"
+ << std::endl);
+
+ if (parent)
+ {
+ // Insert into parent's children list
+ parent->GetChildrenList().push_back(this);
+ // Initialize attributes
+ LevelDescriptor::AttributeDescriptorListType::const_iterator a;
+ for (a = GetTree()->GetAttributeDescriptorList(GetLevel()).begin();
+ a!= GetTree()->GetAttributeDescriptorList(GetLevel()).end();
+ ++a)
+ {
+ std::string v;
+ AttributeMapType::const_iterator i = attr.find(a->GetKey());
+ if ( i != attr.end() )
+ {
+ v = i->second;
+ }
+ GimmickDebugMessage(6,"Setting attribute '"<<a->GetName()<<"' = '"
+ <<v<<"'"<<std::endl);
+ UnsafeSetAttribute( a->GetKey(), v );
+ }
+ }
+
+ }
+ //=============================================================
+
+
+ //=============================================================
+ Node::~Node()
+ {
+ GimmickDebugMessage(6,"Node destructor"
+ << std::endl);
+ ChildrenListType::iterator i;
+ for (i=GetChildrenList().begin(); i!=GetChildrenList().end(); i++)
+ {
+ delete *i;
+ }
+ mData.reset();
+ }
+ //=============================================================
+
+
+ //=============================================================
+ /// Initializes the attribute map i.e. creates the entries
+ void Node::InitializeAttributeMap()
+ {
+ // Initialize attributes
+ LevelDescriptor::AttributeDescriptorListType::const_iterator a;
+ for (a = GetTree()->GetAttributeDescriptorList(GetLevel()).begin();
+ a!= GetTree()->GetAttributeDescriptorList(GetLevel()).end();
+ ++a)
+ {
+ UnsafeSetAttribute( a->GetKey(), "" );
+ }
+ }
+ //=============================================================
+
+ //=============================================================
+ /// Returns the level descriptor of the node
+ const LevelDescriptor& Node::GetLevelDescriptor() const
+ {
+ return GetTree()->GetLevelDescriptor(GetLevel());
+ }
+
+ //=============================================================
+
+ //=============================================================
+ /// Returns the attribute descriptor of the passed parameter
+ const AttributeDescriptor& Node::GetAttributeDescriptor(const std::string& k)const
+ {
+ LevelDescriptor::AttributeDescriptorListType::const_iterator a;
+ for (a = GetTree()->GetAttributeDescriptorList(GetLevel()).begin();
+ a!= GetTree()->GetAttributeDescriptorList(GetLevel()).end();
+ ++a)
+ {
+
+ if(a->GetKey()==k)
+ {
+ return *a;
+ }
+
+ }
+ return *a;
+ }
+ //=============================================================
+
+ //=============================================================
+ int Node::RemoveChildrenFromList(Node* node)
+ {
+ ChildrenListType::iterator i = find(GetChildrenList().begin(),
+ GetChildrenList().end(),
+ node);
+ if (i != GetChildrenList().end())
+ {
+ GetChildrenList().erase(i);
+ }
+ return GetChildrenList().size();
+ }
+ //=============================================================
+
+ //=============================================================
+ const std::string& Node::GetAttribute(const std::string& k) const
+ {
+ // std::cout << "this = "<<(void*)this<<std::endl;
+ // std::cout << "mFieldValueMap="<<(void*)(&mFieldValueMap)<<std::endl;
+ AttributeMapType::const_iterator i = mAttributeMap.find(k);
+ if (i == mAttributeMap.end())
+ {
+ static std::string def("");
+ return def;
+ // CREAIMAGEIO_ERROR("DicomNode::GetFieldValue : no field with key '"<<k<<"'");
+ }
+ return i->second;
+ }
+ //=============================================================
+
+ //=============================================================
+ void Node::SetAttribute(const std::string& k,
+ const std::string& v)
+ {
+ AttributeMapType::iterator i = mAttributeMap.find(k);
+ if (i==mAttributeMap.end())
+ {
+ std::cout<<"[Gimmick!] Node::SetAttribute : no attribute with key '"
+ <<k<<"'"<<std::endl;
+ creaError( "[Gimmick!] Node::SetAttribute : no attribute with key '"
+ <<k<<"'");
+ }
+ i->second = v;
+ }
+ //=============================================================
+
+ //=============================================================
+ bool Node::Matches( const AttributeMapType& m ) const
+ {
+ GimmickDebugMessage(2,"'"<<GetLabel()<<"' matching..."<<std::endl);
+ const std::vector<std::string>& id
+ = GetLevelDescriptor().GetIdentifierList();
+ std::vector<std::string>::const_iterator i;
+ for (i = id.begin(); i != id.end(); ++i)
+ {
+ if (mAttributeMap.find(*i)->second != m.find(*i)->second )
+ {
+ GimmickDebugMessage(2,"IDENTIFIER '"<<*i<<"' values do not match"<<std::endl);
+ return false;
+ }
+ GimmickDebugMessage(2,"IDENTIFIER '"<<*i<<"' values match"<<std::endl);
+ }
+ return true;
+ }
+ //=============================================================
+
+ //=============================================================
+ void Node::Print() const
+ {
+ std::string mess;
+ for (int i = 0; i<GetLevel(); ++i) mess += " ";
+ mess += "|_ " + GetLabel();
+ GimmickMessage(1,mess<<std::endl);
+ ChildrenListType::const_iterator j;
+ for (j=GetChildrenList().begin(); j!=GetChildrenList().end(); j++)
+ {
+ (*j)->Print();
+ }
+ }
+ //=============================================================
+
+ //=============================================================
+ std::string Node::GetLabel() const
+ {
+ std::string l;
+ const std::vector<std::string>& label
+ = GetLevelDescriptor().GetLabelList();
+
+ std::vector<std::string>::const_iterator i;
+ for (i = label.begin(); i != label.end(); )
+ {
+ GimmickDebugMessage(9,"LABEL '"<<*i<<"'"<<std::endl);
+ AttributeMapType::const_iterator j = mAttributeMap.find(*i);
+ if (j != mAttributeMap.end())
+ {
+ l += j->second;
+ ++i;
+ if (i != label.end()) l += "|";
+ }
+ else
+ {
+ GimmickError("Node::GetLabel() : LABEL attribute '"
+ <<*i
+ <<"' is not in node attribute map (should be!)" );
+ }
+ }
+ if (l.size()==0) l="?";
+ return l;
+ }
+ //=============================================================
+
+ }
+
+}
+
--- /dev/null
+#ifndef __creaImageIOTreeNode_h_INCLUDED__
+#define __creaImageIOTreeNode_h_INCLUDED__
+
+#include <creaImageIOTreeDescriptor.h>
+#include <creaImageIOTreeComparators.h>
+#include<boost/filesystem/operations.hpp>
+#include <vector>
+#include <map>
+
+
+namespace creaImageIO
+{
+
+ namespace tree
+ {
+ /**
+ * \ingroup Tree
+ */
+ //=====================================================================
+ /// Forward declaration of Tree
+ class Tree;
+ //=====================================================================
+
+ //=====================================================================
+ /// Abstract class to store user data on a Tree node
+ struct NodeData
+ {
+ NodeData() {}
+ virtual ~NodeData() {}
+ };
+ //=====================================================================
+
+
+ //=====================================================================
+ /// Node of an attributed Tree structure
+ class Node
+ {
+ public:
+ typedef std::map<std::string,std::string> AttributeMapType;
+
+
+ /// Ctor with parent
+ Node(Node* parent);
+ /// Ctor with parent and attributes map
+ Node(Node* parent, const AttributeMapType& );
+ /// Virtual destructor
+ virtual ~Node();
+
+ /// Initializes the attribute map i.e. creates the entries
+ void InitializeAttributeMap();
+
+ /// Returns the level descriptor of the node
+ const LevelDescriptor& GetLevelDescriptor() const;
+
+
+ /// Returns the tree to which the node belongs
+ virtual Tree* GetTree() { return mParent->GetTree(); }
+ /// Returns the tree to which the node belongs
+ virtual const Tree* GetTree() const { return mParent->GetTree(); }
+ /// Returns the level of the node in the tree
+ virtual int GetLevel() const { return mParent->GetLevel()+1; }
+
+
+ /// Returns the parent of the node
+ Node* GetParent() const { return mParent; }
+
+ /// Returns the number of children of the node.
+ /// Warning : if the children are not loaded then might return 0
+ /// even if the node has children !
+ /// see TreeHandler::GetNumberOfChildren
+ unsigned int GetNumberOfChildren() const { return mChildren.size(); }
+
+ /// Returns true iff the node's children are loaded
+ bool GetChildrenLoaded() const { return mChildrenLoaded; }
+
+ /// Sets the node's children
+ void SetChildrenLoaded(bool l) { mChildrenLoaded = l; }
+
+ /// The type of children container
+ typedef std::vector<Node*> ChildrenListType;
+ /// Returns the list of children
+ ChildrenListType& GetChildrenList() { return mChildren; }
+ /// Returns the list of children (const)
+ const ChildrenListType& GetChildrenList() const { return mChildren; }
+
+ /// Remove the given children from the children list
+ int RemoveChildrenFromList(Node*);
+
+
+ /// Get the Attributes Map
+ AttributeMapType& GetAttributeMap() { return mAttributeMap; }
+
+ /// Get the Attributes Map
+ const AttributeMapType& GetAttributeMap() const { return mAttributeMap; }
+
+ /// Get the Attribute for a specific key
+ const std::string& GetAttribute(const std::string& k) const;
+
+ /// Get the Attribute for a specific key without OS dependance (not implemented)
+ // TODO : backslash OS uniformity
+ const std::string& GetCleanAttribute(const std::string& k) const;
+
+ /// Set an Attribute for a specific key
+ void SetAttribute(const std::string& k, const std::string& v);
+
+ /// Set an Attribute for a specific key(unsafe mode)
+ void UnsafeSetAttribute(const std::string& k, const std::string& v)
+ { mAttributeMap[k] = v; }
+
+ /// Get Descriptor for an Attribute
+ const AttributeDescriptor& GetAttributeDescriptor(const std::string& k)const;
+
+ /// Returns true if the KEY attributes of the node match those of the map provided
+ bool Matches( const AttributeMapType& ) const;
+
+ /// Returns the node data casted into the type T
+ template<class T> T GetData() const
+ { if (mData!=0) return dynamic_cast<T>(mData); return 0; }
+
+ /// Sets the node data. Deletes existing data if any.
+ void SetData(boost::shared_ptr<NodeData> d) {mData.reset(); mData = d; }//{ if (mData) delete mData; mData = d; }
+
+ /// Sorts the children of the node
+ void SortChildren(const LexicographicalComparator&);
+
+ /// Print the node
+ virtual void Print() const;
+
+ /// Get the Label of the node
+ std::string GetLabel() const;
+
+
+ private:
+ /// The parent of the node
+ Node* mParent;
+ /// The list of children
+ ChildrenListType mChildren;
+ /// The map of attributes
+ AttributeMapType mAttributeMap;
+ /// User data
+ boost::shared_ptr<NodeData> mData;
+ /// Are the children loaded ?
+ bool mChildrenLoaded;
+ /// The number of children
+ // int mNumberOfChildren;
+
+ }; // class Node
+ //=====================================================================
+
+ } // namespace tree
+
+
+} // namespace creaImageIO
+
+
+
+#endif // #ifndef __creaImageIOTreeNode_h_INCLUDED__
--- /dev/null
+#include <creaImageIOTreeView.h>
+#include <creaImageIOSystem.h>
+
+
+#include <creaImageIOGimmick.h>
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+namespace creaImageIO
+{
+ // CTor
+ TreeView::TreeView(TreeHandler* handler, GimmickView* gimmick )
+ : mTreeHandler(handler),
+ mGimmickView(gimmick)
+ {
+ GimmickDebugMessage(1,"TreeView::TreeView"
+ <<std::endl);
+ }
+
+ /// Destructor
+ TreeView::~TreeView()
+ {
+ GimmickDebugMessage(1,"TreeView::~TreeView"
+ <<std::endl);
+ }
+
+
+
+
+} // EO namespace creaImageIO
+
+
--- /dev/null
+#ifndef __creaImageIOTreeView_h_INCLUDED__
+#define __creaImageIOTreeView_h_INCLUDED__
+
+#include <creaImageIOTreeHandler.h>
+#include <creaImageIOTimestampDatabaseHandler.h>
+#include <creaImageIOSystem.h>
+#include <vtkImageData.h>
+
+namespace creaImageIO
+{
+
+ class GimmickView;
+
+ /**
+ * \ingroup View
+ */
+
+ //=====================================================================
+
+ //=====================================================================
+ /// Abstract class that handles the view of a Tree through its TreeHandler
+ class TreeView
+ {
+ public:
+ /// Ctor
+ TreeView(TreeHandler*,GimmickView*);
+ /// Virtual destructor
+ virtual ~TreeView();
+
+
+ /// Updates the view of a level given the selected items of upper level
+ virtual void UpdateLevel( int )
+ { GimmickError("INTERNAL ERROR : TreeView::UpdateLevel not overloaded");}
+
+ ///Removes selected nodes
+ virtual void RemoveSelected(std::string &i_save )
+ { GimmickError("INTERNAL ERROR : TreeView::RemoveSelected not overloaded");}
+
+ ///Validates the selected images
+ virtual void ValidateSelectedImages()
+ { GimmickError("INTERNAL ERROR : TreeView::ValidateSelected not overloaded");}
+
+ ///Returns the last selected level
+ virtual unsigned int GetLastSelectedLevel(){GimmickError("INTERNAL ERROR : TreeView::GetLastSelectedLevel not overloaded");}
+
+ ///Returns the maximum number of levels
+ virtual int GetNumberOfLevels(){ GimmickError("INTERNAL ERROR : TreeView::GetLevels not overloaded"); }
+ ///Gets the current selections filenames
+ virtual void GetSelectedAsString(std::vector<std::string>&s){ GimmickError("INTERNAL ERROR : TreeView::GetSelectedAsString not overloaded"); }
+
+ /// Gets the user selected data from the level passed as a parameter
+ virtual const std::vector<tree::Node*>& GetSelected(int level){ GimmickError("INTERNAL ERROR : TreeView::GetSelected not overloaded"); }
+
+ /// Gets the next nodes on the list, be it up(true) or down(false).
+ virtual void GetNodes(std::vector<tree::Node*>& nodes, bool direction){ GimmickError("INTERNAL ERROR : TreeView::GetNodes not overloaded"); }
+
+ /// Gets the attributes that are being shown and the ones that have been blocked on a specific level
+ virtual void GetAttributes(std::vector<std::string>& areShown, std::vector<std::string>& notShown, int level){ GimmickError("INTERNAL ERROR : TreeView::GetAttributes not overloaded"); }
+
+ ///Sets the non visible attributes and refreshes the GUI
+ virtual void SetNonVisibleAttributes(const std::vector<std::string>& notShown, int level){ GimmickError("INTERNAL ERROR : TreeView::SetNonVisibleAttributes not overloaded"); }
+
+ ///Creates a new listctrl
+ virtual void CreateCtrl(std::vector<std::string>& notShown, int nlevel){ GimmickError("INTERNAL ERROR : TreeView::CreateCtrl not overloaded"); }
+
+ protected:
+ TreeHandler* GetTreeHandler() { return mTreeHandler; }
+ GimmickView* GetGimmickView() { return mGimmickView; }
+
+ private:
+ /// The TreeHandler with which it corresponds
+ TreeHandler* mTreeHandler;
+ /// The GimmickView which holds the TreeView
+ GimmickView* mGimmickView;
+
+ };
+ // EO class TreeView
+ //=====================================================================
+
+
+} // EO namespace creaImageIO
+
+// EOF
+#endif
--- /dev/null
+
+#include "creaImageIOSystem.h"
+#include "creaImageIOUltrasonixImageReader.h"
+#include <creaVtk.h>
+#include <boost/filesystem/path.hpp>
+namespace creaImageIO
+{
+#define HEADER_SIZE 19
+#define TYPE_RF 16
+#define TYPE_B8 4
+#define TYPE_B32 8
+
+
+ //=====================================================================
+ UltrasonixImageReader::UltrasonixImageReader()
+ {
+ SetName("Ultrasonix");
+ }
+ //=====================================================================
+
+ //=====================================================================
+ UltrasonixImageReader::~UltrasonixImageReader()
+ {
+ }
+ //=====================================================================
+
+ //=====================================================================
+ struct Ultrasonix_header
+ {
+ // frames, width, height, ultrasounds frequency, sampling rate
+ int type, frame, width, height, frequency, samplingRate;
+ };
+ //=====================================================================
+
+
+ //=====================================================================
+ bool ReadHeader( FILE *Ultrasonix_file, Ultrasonix_header& h )
+ {
+ //int *header=(int*)malloc(sizeof(int)*HEADER_SIZE);
+ int header[HEADER_SIZE];
+ fread(header, sizeof(int), HEADER_SIZE, Ultrasonix_file);
+ if (ferror(Ultrasonix_file))
+ return false;
+ h.type = header[0];
+ h.frame = header[1];
+ h.height = header[2];
+ h.width = header[3];
+ h.frequency = header[14];
+ h.samplingRate = header[15];
+ //free(header);
+ return true;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ bool UltrasonixImageReader::CanRead(const std::string& filename)
+ {
+ long size;
+ bool ok = false;
+ FILE *Ultrasonix_file=fopen(filename.c_str(), "rb");
+ if (Ultrasonix_file)
+ {
+ Ultrasonix_header h;
+ if (!ReadHeader(Ultrasonix_file, h) )
+ {
+ fclose(Ultrasonix_file);
+ std::cout << "cannot read Ultrasonix header for file [" << filename << "]" << std::endl;
+ return false;
+ }
+
+ fseek(Ultrasonix_file,0,SEEK_END); // go to end of file
+ if (h.type == TYPE_RF)
+ size = (ftell(Ultrasonix_file) - (HEADER_SIZE+h.frame) * sizeof(int)) / sizeof(short);
+ else if (h.type == TYPE_B8)
+ size = (ftell(Ultrasonix_file) - HEADER_SIZE * sizeof(int)) / sizeof(char);
+ else if (h.type == TYPE_B32)
+ size = (ftell(Ultrasonix_file) - HEADER_SIZE * sizeof(int)) / sizeof(int);
+
+ // check if the data size corresponds to the dimensions of the images
+ if (size == h.width * h.height * h.frame)
+ ok = true;
+
+ fclose(Ultrasonix_file);
+ }
+ return ok;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ vtkImageData* UltrasonixImageReader::ReadImage(const std::string& filename)
+ {
+ FILE *Ultrasonix_file=fopen(filename.c_str(),"rb");
+ if (!Ultrasonix_file)
+ {
+ std::cout << "cannot open file [" << filename << "]" << std::endl;
+ return 0;
+ }
+ Ultrasonix_header h;
+ if (!ReadHeader(Ultrasonix_file,h))
+ {
+ std::cout << "cannot read Ultrasonix header for file [" << filename << "]" << std::endl;
+ fclose(Ultrasonix_file);
+ return 0;
+ }
+
+ long frame_size = h.height * h.width;
+ long im_size = frame_size * h.frame;
+
+ short *dataRF, *ptrRF;
+ char *dataB8, *ptrB8;
+ int *dataB32, *ptrB32;
+ vtkImageData* im;
+ int temp;
+
+ switch (h.type)
+ {
+ case TYPE_RF:
+ dataRF = (short*)malloc(sizeof(short)*im_size);
+ ptrRF = dataRF;
+
+ for (int k=0; k<h.frame; k++)
+ {
+ int frame_number;
+ fread(&frame_number, sizeof(int), 1, Ultrasonix_file);
+ fread(ptrRF,sizeof(short), frame_size, Ultrasonix_file);
+ ptrRF += frame_size;
+ }
+ fclose(Ultrasonix_file);
+
+ im = crea::NewVtkImageDataFromRaw( dataRF, h.width, h.height, h.frame);
+ break;
+
+ case TYPE_B8:
+ dataB8 = (char*)malloc(sizeof(char)*im_size);
+ ptrB8 = dataB8;
+ for (int k=0; k<h.frame; k++)
+ {
+ fread(ptrB8,sizeof(char), frame_size, Ultrasonix_file);
+ ptrB8 += frame_size;
+ }
+ // in mode b frames width and height are inverted
+ temp = h.width;
+ h.width = h.height;
+ h.height = temp;
+
+ fclose(Ultrasonix_file);
+
+ im = crea::NewVtkImageDataFromRaw( dataB8, h.width, h.height, h.frame);
+ break;
+
+ case TYPE_B32:
+ dataB32 = (int*)malloc(sizeof(int)*im_size);
+ ptrB32 = dataB32;
+ for (int k=0; k<h.frame; k++)
+ {
+ fread(ptrB32, sizeof(int), frame_size, Ultrasonix_file);
+ ptrB32 += frame_size;
+ }
+ // in B mode frames width and height are inverted
+ temp = h.width;
+ h.width = h.height;
+ h.height = temp;
+
+ fclose(Ultrasonix_file);
+
+ im = crea::NewVtkImageDataFromRaw( dataB32, h.width, h.height, h.frame);
+ break;
+ }
+
+ return im;
+}
+ //=====================================================================
+
+
+ //=====================================================================
+ void UltrasonixImageReader::PushBackExtensions(std::vector<std::string>& v)
+ {
+ v.push_back("Ultrasonix");
+ }
+ //=====================================================================
+
+
+
+ //=====================================================================
+ void UltrasonixImageReader::ReadAttributes(const std::string& filename,
+ std::map<std::string,std::string>& attr)
+ {
+ GimmickMessage(2,"Reading attributes from '" << filename << std::endl);
+
+ FILE *Ultrasonix_file = fopen(filename.c_str(), "rb");
+ if (!Ultrasonix_file)
+ {
+ std::cout << "cannot open RF file [" << filename << "]" << std::endl;
+ return;
+ }
+
+ Ultrasonix_header h;
+ if (!ReadHeader(Ultrasonix_file, h))
+ {
+ fclose(Ultrasonix_file);
+ std::cout << "cannot read Ultrasonix Attributes for RF file [" << filename << "]" << std::endl;
+ return;
+ }
+
+ fclose(Ultrasonix_file);
+
+ // Columns
+ char cols[128];
+ sprintf(cols,"%i", h.width);
+ // Rows
+ char rows[128];
+ sprintf(rows,"%i", h.height);
+ // Planes
+ char planes[128];
+ sprintf(planes,"%i", h.frame);
+ // Sampling frequency
+ char samplingFrequency[128];
+ sprintf(samplingFrequency,"%i", h.samplingRate);
+ // Transducer frequency
+ char transducerFrequency[128];
+ sprintf(transducerFrequency,"%i", h.frequency);
+
+ //
+ std::map<std::string,std::string>::iterator i;
+ if ( (i = attr.find("FullFileName")) != attr.end())
+ {
+ i->second = filename;
+ }
+ if ( (i = attr.find("D0004_1500")) != attr.end())
+ {
+ boost::filesystem::path full_path(filename);
+ std::string f = full_path.leaf();
+ i->second = f;
+ }
+ if ( (i = attr.find("D0028_0010")) != attr.end())
+ {
+ i->second = rows;
+ }
+ if ( (i = attr.find("D0028_0011")) != attr.end())
+ {
+ i->second = cols;
+ }
+ if ( (i = attr.find("D0028_0012")) != attr.end())
+ {
+ i->second = planes;
+ }
+ if ( (i = attr.find("D003a_001a")) != attr.end())
+ {
+ i->second = samplingFrequency;
+ }
+ if ( (i = attr.find("D0018_6030")) != attr.end())
+ {
+ i->second = transducerFrequency;
+ }
+
+ GimmickMessage(2,"Attributes map:"<<std::endl<<attr<<std::endl);
+ return;
+}
+ //=====================================================================
+
+} // namespace creaImageIO
--- /dev/null
+#ifndef __creaImageIOUltrasonixImageReader_h_INCLUDED__
+#define __creaImageIOUltrasonixImageReader_h_INCLUDED__
+
+#include <creaImageIOAbstractImageReader.h>
+#include <creaImageIOSystem.h>
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup IO
+ */
+
+ //=====================================================================
+ /// Concrete image reader for ultrasonix 'rf' files
+ class CREAIMAGEIO_EXPORT UltrasonixImageReader : virtual public AbstractImageReader
+ {
+ public:
+ UltrasonixImageReader();
+
+ virtual ~UltrasonixImageReader();
+ /// Add file extensions read by the reader
+ virtual void PushBackExtensions(std::vector<std::string>&);
+ /// Test if file is read by this reader
+ virtual bool CanRead(const std::string& filename);
+ /// return for a file a 2D VTkImage
+ virtual vtkImageData* ReadImage(const std::string& filename);
+ /// Read the attributes for a file
+ virtual void ReadAttributes(const std::string& filename,
+ tree::AttributeMapType& attr);
+
+ };
+ //=====================================================================
+
+} // namespace creaImageIO
+
+#endif // #ifndef __creaImageIOUltrasonixImageReader_h_INCLUDED__
--- /dev/null
+#include <creaImageIOVtkImageReader.h>
+#include <vtkImageReader2.h>
+#include <creaImageIOSystem.h>
+#include "boost/filesystem/path.hpp"
+
+namespace creaImageIO{
+ //=====================================================================
+ VtkImageReader::VtkImageReader(vtkImageReader2* r,
+ const std::string& name,
+ const std::string& extensions)
+ : mReader(r), mExtensions(extensions)
+ {
+ if (name.size() == 0)
+ {
+ const char *test =mReader->GetDescriptiveName();
+ if(test != "")
+ {
+ SetName ( "toto");// mReader->GetDescriptiveName());
+ }
+
+ }
+ else
+ {
+ SetName ( name );
+ }
+ GimmickDebugMessage(5,"Constructing vtkImageReader : "<<GetName()
+ <<std::endl);
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ VtkImageReader::~VtkImageReader()
+ {
+
+ mReader->Delete();
+ }
+ //=====================================================================
+
+ //=====================================================================
+ bool VtkImageReader::CanRead(const std::string& filename)
+ {
+
+ return (mReader->CanReadFile(filename.c_str())!=0);
+/* if(filename != "")
+ {
+ return (mReader->CanReadFile(filename.c_str())!=0);
+ }
+ else
+ {
+ return false;
+ }*/
+ }
+ //=====================================================================
+
+ //=====================================================================
+ vtkImageData* VtkImageReader::ReadImage(const std::string& filename)
+ {
+ vtkImageData* im = 0;
+ try
+ {
+ mReader->SetFileName(filename.c_str());
+ mReader->Update();
+ im = vtkImageData::New();
+ im->ShallowCopy(mReader->GetOutput());
+ }
+ catch (...)
+ {
+ if (im!=0) im->Delete();
+ im = 0;
+ }
+ return im;
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void SplitExtensionsString ( const std::string& str,
+ const std::string& delimiters,
+ std::vector<std::string>& tokens)
+ {
+ // Skip delimiters at beginning.
+ std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
+ // Find first delimiter.
+ std::string::size_type pos = str.find_first_of(delimiters, lastPos);
+
+ while (std::string::npos != pos || std::string::npos != lastPos)
+ {
+ // Found a token, add it to the vector.
+ // SPECIFIC : REMOVE INITIAL DOT (lastPos + 1)
+ tokens.push_back(str.substr(lastPos+1, pos - lastPos));
+ // Skip delimiters. Note the "not_of"
+ lastPos = str.find_first_not_of(delimiters, pos);
+ // Find next delimiter
+ pos = str.find_first_of(delimiters, lastPos);
+ }
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ void VtkImageReader::PushBackExtensions(std::vector<std::string>& v)
+ {
+ std::string ext = mExtensions;
+ if (ext.size()==0) ext = mReader->GetFileExtensions ();
+
+ SplitExtensionsString(ext," ",v);
+ }
+ //=====================================================================
+
+
+
+ //=====================================================================
+ void VtkImageReader::ReadAttributes(const std::string& filename,
+ std::map<std::string,std::string>& attr)
+ {
+ GimmickMessage(2,"Reading attributes from '"<<filename<<std::endl);
+ // Get image dimensions
+ // How to get the image info without loading it in vtk ?
+ mReader->SetFileName(filename.c_str());
+ mReader->Update(); //OpenFile();
+ int ext[6];
+ mReader->GetDataExtent(ext);
+ // Columns
+ char cols[128];
+ sprintf(cols,"%i",ext[1]-ext[0]);
+ // Rows
+ char rows[128];
+ sprintf(rows,"%i",ext[3]-ext[2]);
+ // Planes
+ char planes[128];
+ sprintf(planes,"%i",ext[5]-ext[4]);
+
+ std::map<std::string,std::string>::iterator i;
+ if ( (i = attr.find("FullFileName")) != attr.end())
+ {
+ i->second = filename;
+ }
+ if ( (i = attr.find("D0004_1500")) != attr.end())
+ {
+ boost::filesystem::path full_path(filename);
+ std::string f = full_path.leaf();
+ i->second = f;
+ }
+ if ( (i = attr.find("D0028_0010")) != attr.end())
+ {
+ i->second = rows;
+ }
+ if ( (i = attr.find("D0028_0011")) != attr.end())
+ {
+ i->second = cols;
+ }
+
+ if ( (i = attr.find("D0028_0012")) != attr.end())
+ {
+ i->second = planes;
+ }
+ if ( (i = attr.find("FullFileDirectory")) != attr.end())
+ {
+ std::string::size_type last_pos = filename.find_last_of("//");
+ i->second = filename.substr(0, last_pos);
+ }
+
+ GimmickMessage(2,"Attributes map:"<<std::endl<<attr<<std::endl);
+ }
+ //=====================================================================
+
+} // namespace creaImageIO
--- /dev/null
+#ifndef __creaImageIOVtkImageReader_h_INCLUDED__
+#define __creaImageIOVtkImageReader_h_INCLUDED__
+
+
+#include <creaImageIOAbstractImageReader.h>
+
+// forward decl
+class vtkImageReader2;
+
+namespace creaImageIO
+{
+
+
+ /**
+ * \ingroup IO
+ */
+
+ //=====================================================================
+ /// Concrete image reader based on a vtkImageReader2
+ class VtkImageReader : virtual public AbstractImageReader
+ {
+ public:
+ VtkImageReader(vtkImageReader2* reader,
+ const std::string& name = "",
+ const std::string& extensions = "");
+
+ virtual ~VtkImageReader();
+
+
+
+ /// Add file extensions read by the reader
+ virtual void PushBackExtensions(std::vector<std::string>&);
+ /// Test if file is read by this reader
+ virtual bool CanRead(const std::string& filename);
+ /// return for a file a 2D VTkImage
+ virtual vtkImageData* ReadImage(const std::string& filename);
+ /// Read the attributes for a file
+ virtual void ReadAttributes(const std::string& filename,
+ tree::AttributeMapType& attr);
+
+ private:
+ vtkImageReader2* mReader;
+ std::string mExtensions;
+ };
+ //=====================================================================
+
+
+
+} // namespace creaImageIO
+
+
+
+#endif // #ifndef __creaImageIOVtkImageReader_h_INCLUDED__
--- /dev/null
+#include <creaImageIOWxAttributeSelectionPanel.h>
+#include <creaImageIOSystem.h>
+
+#include <creaImageIOGimmick.h>
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+namespace creaImageIO
+{
+ const int ID_COMBO = 180;
+ // CTor
+ WxAttributeSelectionPanel::WxAttributeSelectionPanel(wxWindow *parent,
+ wxDialog* dial,
+ WxGimmickView* view,
+ std::vector<std::string> sAtts,
+ std::vector<std::string> nsAtts,
+ int numLev)
+ : wxPanel( parent,
+ -1, wxDefaultPosition,
+ wxDefaultSize,
+ wxRESIZE_BORDER |
+ wxSYSTEM_MENU |
+ wxCLOSE_BOX |
+ wxMAXIMIZE_BOX |
+ wxMINIMIZE_BOX |
+ wxCAPTION
+ ),
+ dialog(dial),
+ shownA(sAtts),
+ notShownA(nsAtts),
+ mView(view)
+ {
+ GimmickDebugMessage(1,"WxCustomizeConfigPanel::WxCustomizeConfigPanel"
+ <<std::endl);
+ wxStaticText * aa=new wxStaticText(this,-1,_T(" Currently shown attributes for level: "), wxPoint(5,10));
+ wxArrayString as;
+ std::stringstream out;
+ for(int i=1;i<=numLev;i++)
+ {
+ out<<i;
+ as.Add(crea::std2wx(out.str()));
+ out.str("");
+ }
+ levels=new wxComboBox(this, ID_COMBO,_T("1"),wxPoint(190, 5),wxDefaultSize,as);
+ wxStaticText * na=new wxStaticText(this,-1,_T(" Currently hidden attributes: "), wxPoint(255,10));
+
+ shownAtts=new wxListCtrl(this, wxID_ANY, wxPoint(5,30), wxSize(160,90), wxLC_REPORT | wxLC_NO_HEADER );
+
+ shownAtts->InsertColumn(0,
+ crea::std2wx(""),
+ wxLIST_FORMAT_LEFT);
+ shownAtts->SetColumnWidth(0,155);
+ shownAtts->Show();
+
+ wxButton *add = new wxButton(this,wxID_ANY,_T(">>"), wxPoint(170,50) );
+ Connect( add->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxAttributeSelectionPanel::OnAdd );
+
+ wxButton *remove = new wxButton(this,wxID_ANY,_T("<<"), wxPoint(170,70) );
+ Connect( remove->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxAttributeSelectionPanel::OnRemove );
+
+ notShownAtts=new wxListCtrl(this, wxID_ANY, wxPoint(255,30), wxSize(160,90), wxLC_REPORT | wxLC_NO_HEADER );
+
+ notShownAtts->InsertColumn(0,
+ crea::std2wx(""),
+ wxLIST_FORMAT_LEFT);
+ notShownAtts->SetColumnWidth(0,155);
+ notShownAtts->Show();
+ LoadCtrls();
+
+ wxButton *save = new wxButton(this,wxID_ANY,_T("Save Changes"), wxPoint(5,130) );
+ Connect( save->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxAttributeSelectionPanel::OnSaveConfig );
+
+ Layout();
+ }
+
+ /// Destructor
+ WxAttributeSelectionPanel::~WxAttributeSelectionPanel()
+ {
+ GimmickDebugMessage(1,"WxAttributeSelectionPanel::~WxAttributeSelectionPanel"
+ <<std::endl);
+ }
+
+ void WxAttributeSelectionPanel::OnSaveConfig(wxCommandEvent& event)
+ {
+ int n=levels->GetSelection();
+ if(n<0){n=0;}
+ mView->OnAttributesChanged(notShownA,n);
+ dialog->Destroy();
+ }
+
+ void WxAttributeSelectionPanel::OnAdd(wxCommandEvent& event)
+ {
+ long item = -1;
+ for ( ;; )
+ {
+ item = shownAtts->GetNextItem(item,
+ wxLIST_NEXT_ALL,
+ wxLIST_STATE_SELECTED);
+ if ( item == -1 )
+ break;
+
+ std::string change = crea::wx2std(shownAtts->GetItemText(item));
+ std::vector<std::string>::iterator it;
+ bool found=false;
+ for(it=shownA.begin();it!=shownA.end()&&!found;++it)
+ {
+ if((*it).compare(change)==0)
+ {
+ found=true;
+ }
+ }
+ shownA.erase(it-1);
+ notShownA.push_back(change);
+ }
+ LoadCtrls();
+
+ }
+
+ void WxAttributeSelectionPanel::OnRemove(wxCommandEvent& event)
+ {
+
+ long item = -1;
+ for ( ;; )
+ {
+ item = notShownAtts->GetNextItem(item,
+ wxLIST_NEXT_ALL,
+ wxLIST_STATE_SELECTED);
+ if ( item == -1 )
+ break;
+
+ std::string change = crea::wx2std(notShownAtts->GetItemText(item));
+ std::vector<std::string>::iterator it;
+ bool found=false;
+ for(it=notShownA.begin();it!=notShownA.end()&&!found;++it)
+ {
+ if((*it).compare(change)==0)
+ {
+ found=true;
+ }
+ }
+ notShownA.erase(it-1);
+ shownA.push_back(change);
+
+ }
+ LoadCtrls();
+
+ }
+
+
+ void WxAttributeSelectionPanel::LoadCtrls()
+ {
+
+ wxListItem item;
+ item.SetMask(wxLIST_MASK_STATE |
+ wxLIST_MASK_TEXT |
+ // wxLIST_MASK_IMAGE |
+ wxLIST_MASK_DATA |
+ // wxLIST_MASK_WIDTH |
+ wxLIST_MASK_FORMAT
+ );
+ std::vector<std::string>::iterator it;
+ shownAtts->DeleteAllItems();
+ notShownAtts->DeleteAllItems();
+ for(it=shownA.begin();it!=shownA.end();++it)
+ {
+ item.SetText(crea::std2wx(*it));
+ shownAtts->InsertItem(item);
+ }
+
+
+ for(it=notShownA.begin();it!=notShownA.end();++it)
+ {
+ item.SetText(crea::std2wx(*it));
+ notShownAtts->InsertItem(item);
+ }
+
+ }
+ void WxAttributeSelectionPanel::OnComboChange(wxCommandEvent& event)
+ {
+ int n=levels->GetSelection()+1;
+ mView->GetVisibleAttributes(shownA,notShownA,n);
+ LoadCtrls();
+ }
+
+//======================================================================
+BEGIN_EVENT_TABLE(WxAttributeSelectionPanel, wxPanel)
+EVT_COMBOBOX (ID_COMBO,WxAttributeSelectionPanel::OnComboChange)
+END_EVENT_TABLE()
+//======================================================================
+
+} // EO namespace creaImageIO
+
+
--- /dev/null
+#ifndef __creaImageIOWxAttributeSelectionPanel_h_INCLUDED__
+#define __creaImageIOWxAttributeSelectionPanel_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+#include <creaWx.h>
+#include <creaImageIOWxGimmickView.h>
+#include <wx/listctrl.h>
+
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup GUI
+ */
+ //=====================================================================
+ //=====================================================================
+ class WxAttributeSelectionPanel : public wxPanel
+ {
+ public:
+ WxAttributeSelectionPanel();
+ WxAttributeSelectionPanel(wxWindow *parent,
+ wxDialog* dial,
+ WxGimmickView* view,
+ std::vector<std::string> sAtts,
+ std::vector<std::string> nsAtts,
+ int numLev);
+
+ ~WxAttributeSelectionPanel();
+ ///Saves the configuration
+ void OnSaveConfig(wxCommandEvent& event);
+ ///Changes items from shown to notShown
+ void OnAdd(wxCommandEvent& event);
+ ///Changes items from notShown to shown
+ void OnRemove(wxCommandEvent& event);
+ void OnComboChange(wxCommandEvent& event);
+
+
+ private :
+ ///Loads the information on the vectors onto the lists
+ void LoadCtrls();
+ std::vector<std::string> shownA;
+ std::vector<std::string> notShownA;
+ wxComboBox* levels;
+ wxListCtrl* shownAtts;
+ wxListCtrl* notShownAtts;
+ wxDialog* dialog;
+ WxGimmickView* mView;
+
+
+ DECLARE_EVENT_TABLE()
+
+ }; // class WxAttributeSelectionPanel
+ //=====================================================================
+
+
+} // EO namespace creaImageIO
+
+
+#endif // USE_WIDGETS
+// EOF
+#endif
+
--- /dev/null
+#include <creaImageIOWxCustomizeConfigPanel.h>
+#include <creaImageIOSystem.h>
+namespace creaImageIO
+{
+ // CTor
+ WxCustomizeConfigPanel::WxCustomizeConfigPanel(wxWindow *parent, wxDialog* dial, WxGimmickView* view, const std::string& cPath,
+ const std::string& dPath,
+ const std::string& sEvent,
+ const std::string& sFreq)
+ : wxPanel( parent,
+ -1, wxDefaultPosition,
+ wxDefaultSize,
+ wxRESIZE_BORDER |
+ wxSYSTEM_MENU |
+ wxCLOSE_BOX |
+ wxMAXIMIZE_BOX |
+ wxMINIMIZE_BOX |
+ wxCAPTION
+ ),
+ dialog(dial),
+ copyP (cPath),
+ databaseP(dPath),
+ syncEv(sEvent),
+ syncFr(sFreq),
+ mView(view)
+ {
+ GimmickDebugMessage(1,"WxCustomizeConfigPanel::WxCustomizeConfigPanel"
+ <<std::endl);
+ wxStaticText * cp=new wxStaticText(this,-1,_T(" Copy Path: "), wxPoint(5,10));
+ copyPath=new wxTextCtrl(this, wxID_ANY, crea::std2wx(copyP), wxPoint(150,10), wxSize(250,20));
+
+ wxStaticText * dp=new wxStaticText(this,-1,_T(" Database Path: "), wxPoint(5,40));
+ dbPath=new wxTextCtrl(this, wxID_ANY, crea::std2wx(databaseP), wxPoint(150,40), wxSize(250,20));
+
+ wxStaticText * se=new wxStaticText(this,-1,_T(" Synchronization Event: "), wxPoint(5,70));
+ syncEvent=new wxTextCtrl(this, wxID_ANY, crea::std2wx(syncEv), wxPoint(150,70), wxSize(250,20));
+
+ wxStaticText * sf=new wxStaticText(this,-1,_T(" Synchronization Frequency: "), wxPoint(5,100));
+ syncFrequency=new wxTextCtrl(this, wxID_ANY, crea::std2wx(syncFr), wxPoint(150,100), wxSize(250,20));
+
+ wxButton *save = new wxButton(this,wxID_ANY,_T("Save Changes"), wxPoint(5,130) );
+ Connect( save->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxCustomizeConfigPanel::OnSaveConfig );
+
+ Layout();
+ }
+
+ /// Destructor
+ WxCustomizeConfigPanel::~WxCustomizeConfigPanel()
+ {
+ GimmickDebugMessage(1,"WxCustomizeConfigPanel::~WxCustomizeConfigPanel"
+ <<std::endl);
+ }
+
+ void WxCustomizeConfigPanel::OnSaveConfig(wxCommandEvent& event)
+ {
+ mView->OnSaveSettingsCallback(crea::wx2std(copyPath->GetValue()),
+ crea::wx2std(dbPath->GetValue()),
+ crea::wx2std(syncEvent->GetValue()),
+ crea::wx2std(syncFrequency->GetValue()));
+ dialog->Destroy();
+ }
+
+//======================================================================
+
+//======================================================================
+
+} // EO namespace creaImageIO
+
+
--- /dev/null
+#ifndef __creaImageIOWxCustomizeConfigPanel_h_INCLUDED__
+#define __creaImageIOWxCustomizeConfigPanel_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+#include <creaWx.h>
+#include <creaImageIOWxGimmickView.h>
+
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup GUI
+ */
+ //=====================================================================
+ //=====================================================================
+ class WxCustomizeConfigPanel : public wxPanel
+ {
+ public:
+ WxCustomizeConfigPanel();
+ WxCustomizeConfigPanel(wxWindow *parent,
+ wxDialog* dial,
+ WxGimmickView* view,
+ const std::string& cPath,
+ const std::string& dPath,
+ const std::string& sEvent,
+ const std::string& sFreq);
+
+ ~WxCustomizeConfigPanel();
+ ///Saves the configuration
+ void OnSaveConfig(wxCommandEvent& event);
+
+ private :
+ std::string copyP;
+ std::string databaseP;
+ std::string syncEv;
+ std::string syncFr;
+ wxTextCtrl* copyPath;
+ wxTextCtrl* dbPath;
+ wxTextCtrl* syncEvent;
+ wxTextCtrl* syncFrequency;
+ wxDialog* dialog;
+ WxGimmickView* mView;
+
+
+ }; // class WxCustomizeConfigPanel
+ //=====================================================================
+
+
+} // EO namespace creaImageIO
+
+
+#endif // USE_WIDGETS
+// EOF
+#endif
+
--- /dev/null
+#include "creaImageIOWxDescriptorPanel.h"
+#include <creaImageIOSystem.h>
+#if defined(USE_GDCM)
+#include <gdcmGlobal.h>
+#include <gdcmDictSet.h>
+#endif
+
+#if defined(USE_GDCM2)
+#include <gdcmGlobal.h>
+#include <gdcmDicts.h>
+#include <gdcmDict.h>
+#endif
+#include <boost/algorithm/string.hpp>
+
+namespace creaImageIO
+{
+ // CTor
+
+ WxDescriptorPanel::WxDescriptorPanel(wxWindow *parent, const std::string path)
+ : wxDialog(parent, -1,_T("Descriptor Creation"), wxDefaultPosition, wxSize(550,550)) , m_path(path)
+{
+
+
+ GimmickDebugMessage(1,"WxDescriptorPanel::WxDescriptorPanel"
+ <<std::endl);
+
+ lv = 0;
+ ownatt["FullFileName"] = "Full_File_Name";
+ ownatt["FullFileDirectory"] = "Full_File_Directory";
+
+
+ // START BUTTONS
+ wxButton *NewDescriptor = new wxButton(this, -1,_T("Create a new descriptor"), wxPoint(10,7) );
+ Connect( NewDescriptor->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnNew );
+
+ wxButton *LoadDescriptor = new wxButton(this, -1,_T("Load a descriptor"), wxPoint(150,7) );
+ Connect( LoadDescriptor->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnLoad );
+
+ wxStaticLine *line1 = new wxStaticLine(this, -1, wxPoint(5,40), wxSize(540,2));
+
+ // LEVEL
+ wxStaticText * LevelText=new wxStaticText(this,-1,_T(" Level: "), wxPoint(5,50));
+ LevelCtrl=new wxTextCtrl(this, ID_GR_CTRL,_T("patient"), wxPoint(50,50), wxSize(50,25));
+ wxButton *addLevel = new wxButton(this, ID_LEVEL_ADD,_T("add a level"), wxPoint(150,50) );
+ Connect( addLevel->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnAddLevel );
+
+ wxStaticLine *line2 = new wxStaticLine(this, -1, wxPoint(5,75), wxSize(540,2));
+
+ // ATTRIBUTES
+
+ wxStaticText * GR=new wxStaticText(this,-1,_T(" DICOM Group: "), wxPoint(5,110));
+ GRCtrl=new wxTextCtrl(this, ID_GR_CTRL,_T("0x0010"), wxPoint(82,110), wxSize(50,25));
+ Connect( GRCtrl->GetId(), wxEVT_COMMAND_TEXT_UPDATED , (wxObjectEventFunction) &WxDescriptorPanel::OnDicomAttribute );
+
+ wxStaticText * EL=new wxStaticText(this,-1,_T(" DICOM Element: "), wxPoint(140,110));
+ ELCtrl=new wxTextCtrl(this, ID_EL_CTRL,_T("0x0010"), wxPoint(230,110), wxSize(50,25));
+ Connect( ELCtrl->GetId(), wxEVT_COMMAND_TEXT_UPDATED , (wxObjectEventFunction) &WxDescriptorPanel::OnDicomAttribute );
+
+
+ wxString choices[3];
+ choices[0] = _T("Unknow Attribute");
+ std::map<std::string, std::string>::iterator it_att =ownatt.begin();
+ for(int i = 1; it_att != ownatt.end(); it_att++, i++)
+ {
+ choices[i] = crea::std2wx(it_att->second);
+ }
+
+
+ AttributeCombo = new wxComboBox(this, ID_ATTRIBUTE_CTRL,_T(""),wxPoint(300,110), wxSize(120,25),3,choices, wxCB_READONLY);
+ AttributeCombo->SetSelection(0);
+
+
+ wxButton *addAttribute = new wxButton(this, ID_ATTRIBUTE_ADD,_T("add an attribute"), wxPoint(440,110) );
+ Connect( addAttribute->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnAddAttribute );
+
+ wxStaticLine *line3 = new wxStaticLine(this, -1, wxPoint(5,140), wxSize(540,2));
+
+ // RESULT
+
+ ResultCtrl=new wxTextCtrl(this, ID_EL_CTRL,_T(""), wxPoint(5,150), wxSize(250,310), wxTE_READONLY| wxMac | wxTE_MULTILINE | wxTE_RICH );
+ wxButton *RemoveCtrl = new wxButton(this, ID_REMOVE_ADD,_T("Remove an entry"), wxPoint(280,200) );
+ Connect( RemoveCtrl->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnRemove );
+
+ wxStaticLine *line4 = new wxStaticLine(this, -1, wxPoint(5,470), wxSize(540,2));
+ // VALIDATION BUTTON
+ wxButton *Ok = new wxButton(this, -1,_T("OK"), wxPoint(10,480) );
+ Connect( Ok->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnOK );
+
+ wxButton *Apply = new wxButton(this, -1,_T("APPLY"), wxPoint(150,480) );
+ Connect( Apply->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnApply );
+
+ wxButton *Cancel = new wxButton(this, wxID_CANCEL,_T("CANCEL"), wxPoint(250,480) );
+// Connect( Cancel->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxCloseEvent) &wxWindow::Close );
+
+ Layout();
+ CreateDescriptor(0);
+ }
+
+ /// Destructor
+ WxDescriptorPanel::~WxDescriptorPanel()
+ {
+ GimmickDebugMessage(1,"WxCustomizeConfigPanel::~WxCustomizeConfigPanel"
+ <<std::endl);
+ }
+
+ //////////////////////////////////////////////////////////
+ // Add an attribute //
+ // @param event : Wxevent //
+ // @return : - //
+ //////////////////////////////////////////////////
+ void WxDescriptorPanel::OnAddAttribute(wxCommandEvent& event)
+ {
+ std::string name_lv;
+ std::string name_att;
+ if (AttributeCombo->GetSelection() == 0)
+ {
+ name_att = "D" + crea::wx2std(GRCtrl->GetValue()) + "_" + crea::wx2std(ELCtrl->GetValue());
+ }
+ else
+ {
+ wxString wd = AttributeCombo->GetValue();
+ std::string st = crea::wx2std(wd);
+ name_att = OwnAttribute(st);
+ }
+ onAddAttribute(crea::wx2std(AttributeCombo->GetValue()), name_att);
+ }
+ //////////////////////////////////////////////////////////
+ // add an attribute //
+ // @param att : attribute //
+ // @param name_att : 's name //
+ // @param level : level to add the attribute //
+ // @return : - //
+ //////////////////////////////////////////////////
+ void WxDescriptorPanel::onAddAttribute( const std::string &att, const std::string &name_att,std::string level )
+ {
+ if(lv == 0)
+ {
+ wxMessageBox(_T("Need a level first!"),crea::std2wx("WARNING"),wxOK,this);
+ }
+ else
+ {
+ if( !att.empty() )
+ {
+ // Find Name of level
+ if(level.empty())
+ {
+ level = findLevel();
+ }
+
+ if (!addAtribute(level, name_att))
+ {
+ wxMessageBox(_T("Attribute already used in this level"),crea::std2wx("WARNING"),wxOK,this);
+ }
+ else
+ {
+ ResultCtrl->SetInsertionPoint(InsertPt);
+ for (int i = 1; i<=lv;i++)
+ {
+ ResultCtrl->WriteText(_T(" "));
+ }
+ ResultCtrl->WriteText(_T("| - "));
+ ResultCtrl->WriteText(crea::std2wx(att));
+ wxTextAttr ResultAttr(ResultCtrl->GetDefaultStyle());
+ ResultAttr.SetTextColour(*wxWHITE);
+ ResultCtrl->SetDefaultStyle(ResultAttr);
+ std::string text = " ";
+ ResultCtrl->WriteText(crea::std2wx(" " + name_att));
+ ResultAttr.SetTextColour(*wxBLACK);
+ ResultCtrl->SetDefaultStyle(ResultAttr);
+ ResultCtrl->WriteText(_T("\n"));
+ }
+ InsertPt = ResultCtrl->GetInsertionPoint();
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////////
+ // add a level //
+ // @param event : Wxevent //
+ // @return : - //
+ //////////////////////////////////////////////////
+ void WxDescriptorPanel::OnAddLevel(wxCommandEvent& event)
+ {
+ if( !LevelCtrl->GetValue().IsEmpty() )
+ {
+ onAddLevel(crea::wx2std(LevelCtrl->GetValue()));
+ }
+ }
+
+ //////////////////////////////////////////////////////////
+ // add a level //
+ // @param level : level's name //
+ // @return : - //
+ //////////////////////////////////////////////////
+ void WxDescriptorPanel::onAddLevel(const std::string &level)
+ {
+ if(addLevel(level))
+ {
+ wxMessageBox(_T("Level already used"),crea::std2wx(("WARNING")),wxOK,this);
+ return;
+ }
+
+ lv++;
+ ResultCtrl->SetInsertionPoint(InsertPt);
+ for (int i = 1; i<lv;i++)
+ {
+ ResultCtrl->WriteText(_T(" "));
+ }
+ if(lv>1)
+ { ResultCtrl->WriteText(_T("| \n"));
+ for (int i = 1; i<lv;i++)
+ {
+ ResultCtrl->WriteText(_T(" "));
+ }
+ ResultCtrl->WriteText(_T("|_"));
+ }
+
+ wxTextAttr ResultAttr(ResultCtrl->GetDefaultStyle());
+ ResultAttr.SetTextColour(*wxRED);
+ ResultCtrl->SetDefaultStyle(ResultAttr);
+ ResultCtrl->WriteText(crea::std2wx(level));
+ ResultAttr.SetTextColour(*wxBLACK);
+ ResultCtrl->SetDefaultStyle(ResultAttr);
+ ResultCtrl->WriteText(_T("\n"));
+ InsertPt = ResultCtrl->GetInsertionPoint();
+
+ }
+
+ //////////////////////////////////////////////////////////
+ // Find a DICOM attribute from group and element values //
+ // @param event : Wxevent //
+ // @return : - //
+ //////////////////////////////////////////////////
+ void WxDescriptorPanel::OnDicomAttribute(wxCommandEvent& event)
+ {
+ int i = 0;
+ if(!GRCtrl->GetValue().IsEmpty() && !ELCtrl->GetValue().IsEmpty()
+ && GRCtrl->GetValue().Len() == 6 && ELCtrl->GetValue().Len() == 6 && AttributeCombo->GetSelection() == 0)
+ {
+
+ std::string gr = crea::wx2std(GRCtrl->GetValue());
+ std::string el = crea::wx2std(ELCtrl->GetValue());
+ std::stringstream val;
+
+ unsigned short group;
+ unsigned short element;
+ val << std::dec << gr ;
+ val >> std::hex >> group;
+ val.clear();
+ val << std::dec << el ;
+ val >> std::hex >> element;
+#if defined(USE_GDCM)
+ // Retrieve the name from gdcm dict
+ GDCM_NAME_SPACE::DictEntry* entry = GDCM_NAME_SPACE::Global::GetDicts()->GetDefaultPubDict()->GetEntry(group, element);
+ // AttributeCombo->Clear();
+ if(entry)
+ {
+ AttributeCombo->Delete(0);
+ AttributeCombo->Insert(crea::std2wx(entry->GetName()), 0);
+ }
+ else
+ {
+ AttributeCombo->Delete(0);
+ AttributeCombo->Insert(_T("Unknown Attribute"),0);
+ }
+#endif
+ AttributeCombo->SetSelection(0);
+
+ }
+
+ }
+
+
+ //////////////////////////////////////////////////////////
+ // determine values for own attributes //
+ // @param name : attribute's name //
+ // @param key : indicates key map or not //
+ // @return : - //
+ //////////////////////////////////////////////////
+ std::string WxDescriptorPanel::OwnAttribute(const std::string name)
+ {
+ std::string result;
+
+ std::map<std::string, std::string>::iterator it_att = ownatt.begin();
+ for(; it_att != ownatt.end(); it_att++)
+ {
+ if(it_att->second == name)
+ {
+ result = it_att->first.c_str();
+ break;
+ }
+ }
+ return result;
+ }
+
+ //////////////////////////////////////////////////////////
+ // Find a level in function of position in Return Ctrl //
+ // @param - : //
+ // @return : - //
+ //////////////////////////////////////////////////
+ std::string WxDescriptorPanel::findLevel()
+ {
+ long column;
+ long line;
+
+ ResultCtrl->PositionToXY( ResultCtrl->GetInsertionPoint(),&column, &line);
+ std::string tx(crea::wx2std(ResultCtrl->GetRange(0, ResultCtrl->XYToPosition(0,line+1))).c_str());
+ std::string::size_type level_pos_start = tx.rfind("|_");
+ if(level_pos_start == -1)
+ {
+ level_pos_start = 0;
+ }
+ else
+ {
+ level_pos_start += 2;
+ }
+
+ std::string::size_type level_pos_end = tx.find_first_of("\n",level_pos_start);
+ return tx.substr(level_pos_start,level_pos_end - level_pos_start);
+ }
+
+ //////////////////////////////////////////////////////
+ // Remove an item //
+ // @param event : Wxevent //
+ // @return : - //
+ //////////////////////////////////////////////////
+ void WxDescriptorPanel::OnRemove(wxCommandEvent& event)
+ {
+ long line;
+ long column;
+ long pos_start;
+ long pos_end;
+
+ pos_start = ResultCtrl->GetInsertionPoint();
+ ResultCtrl->PositionToXY( pos_start,&column, &line);
+ if (line == 0)
+ {
+ std::string name("root");
+ RemoveLevel(name);
+ ResultCtrl->Clear();
+ lv = 0;
+ }
+ else
+ {
+ wxString text = ResultCtrl->GetLineText(line);
+ if ( text.Find(_T("|_")) == -1)
+ {
+ std::string level = findLevel();
+ // find GR and EL values to remove
+ std::string tx = crea::wx2std(text);
+ std::string::size_type EL_start_pos = tx.find_last_of(" ");
+ RemoveAttribute(level, tx.substr(EL_start_pos+1,tx.size() - EL_start_pos));
+ ResultCtrl->Remove( ResultCtrl->XYToPosition(0,line), ResultCtrl->XYToPosition(0,line+1));
+ }
+ else
+ {
+ RemoveLevel(crea::wx2std(text.AfterFirst('_')));
+ lv = text.Find(_T("|"))/3;
+ pos_start= ResultCtrl->XYToPosition(0,line-1);
+ ResultCtrl->SetInsertionPointEnd();
+ pos_end = ResultCtrl->GetInsertionPoint();
+ ResultCtrl->Remove(pos_start, pos_end);
+ }
+ }
+
+ }
+ //////////////////////////////////////////////
+ // create a descriptor structure //
+ // @param name : level's name to add //
+ // @return : boolean result //
+ //////////////////////////////////////////////////
+ void WxDescriptorPanel::CreateDescriptor(int type)
+ {
+ if(type == 0) // First initialization
+ {
+ outDscp.clear();
+ outDscp += "<level>";
+ outDscp += "\n";
+ outDscp += "root";
+ outDscp += "\n";
+ outDscp += "O Name Name 4";
+ outDscp += "\n";
+ }
+ if(type == 1)
+ {
+ if(lv > 1)
+ {
+ outDscp += "O NumberOfChildren ";
+ outDscp += crea::wx2std(LevelCtrl->GetValue());
+ outDscp += "s";
+ outDscp += "\n";
+ }
+ outDscp += "<level>";
+ outDscp += "\n";
+ outDscp += crea::wx2std(LevelCtrl->GetValue());
+ outDscp += "\n";
+
+ }
+ if(type == 2)
+ {
+ outDscp += "D";
+ outDscp += " ";
+ outDscp += crea::wx2std(GRCtrl->GetValue());
+ outDscp += " ";
+ outDscp += crea::wx2std(ELCtrl->GetValue());
+ outDscp += " ";
+ outDscp += "3";
+ outDscp += "\n";
+ }
+
+
+ }
+
+
+ //////////////////////////////////////////////////////
+ // add a level //
+ // @param name : level's name to add //
+ // @return : boolean result //
+ //////////////////////////////////////////////////
+ bool WxDescriptorPanel::addLevel(const std::string &name)
+ {
+ bool bfound = false;
+ std::map<std::string, std::vector <std::string> >::iterator it_tree = DscpTree.begin();
+ for (;it_tree != DscpTree.end(); it_tree++)
+ {
+ if(it_tree->first == name)
+ {
+ bfound = true;
+ break;
+ }
+ }
+ if(!bfound)
+ {
+ lvlist[lv] = name;
+ std::vector <std::string> branch;
+ DscpTree[name] = branch;
+ }
+ return bfound;
+ }
+
+ //////////////////////////////////////////////////////
+ // remove a level //
+ // @param name : level's name to remove //
+ // @return : boolean result //
+ //////////////////////////////////////////////////
+ bool WxDescriptorPanel::RemoveLevel(const std::string &name)
+ {
+ bool bresult = false;
+ std::map<int, std::string>::iterator it_list= lvlist.begin();
+ for(; it_list != lvlist.end(); it_list++)
+ {
+ if(it_list->second == name)
+ {
+ break;
+ }
+ }
+ std::map<int, std::string>::iterator it_list2 = it_list;
+ for(;it_list != lvlist.end(); it_list++)
+ {
+ std::map<std::string, std::vector <std::string> >::iterator it_tree = DscpTree.begin();
+ for (;it_tree != DscpTree.end(); it_tree++)
+ {
+ if(it_tree->first == name)
+ {
+ DscpTree.erase(it_tree);
+ break;
+ }
+ }
+ }
+ lvlist.erase(it_list2, lvlist.end());
+ return bresult;
+ }
+
+
+ //////////////////////////////////////////////////////
+ // add an attribute in a level //
+ // @param level : level's name to add attribute //
+ // @param name : attribute's name //
+ // @return : boolean result //
+ //////////////////////////////////////////////////
+ bool WxDescriptorPanel::addAtribute(const std::string &level, const std::string &name)
+ {
+ bool bresult = true;
+ std::map<std::string, std::vector <std::string> >::iterator it_tree = DscpTree.begin();
+ for (;it_tree != DscpTree.end(); it_tree++)
+ {
+ if (it_tree->first.c_str() == level)
+ {
+ std::vector<std::string>::iterator it_branch = it_tree->second.begin();
+ for(;it_branch != it_tree->second.end(); it_branch++)
+ {
+ if(it_branch->c_str() == name)
+ {
+ bresult = false;
+ }
+ }
+ if(bresult)
+ {
+ it_tree->second.push_back(name);
+ break;
+ }
+ }
+ }
+ return bresult;
+ }
+
+ //////////////////////////////////////////////////////
+ // remove an attribute from a level //
+ // @param level : level's name to remove attribute //
+ // @param name : attribute's name //
+ // @return : boolean result //
+ //////////////////////////////////////////////////
+ bool WxDescriptorPanel::RemoveAttribute(const std::string &level, const std::string &name)
+ {
+ bool bresult = false;
+ std::map<std::string, std::vector <std::string> >::iterator it_tree = DscpTree.begin();
+ for (;it_tree != DscpTree.end(); it_tree++)
+ {
+ if(it_tree->first == level)
+ {
+ std::vector<std::string>::iterator it_branch = it_tree->second.begin();
+ cout << it_tree->second.size();
+ for(;it_branch != it_tree->second.end(); it_branch++)
+ {
+ if(it_branch->c_str() == name)
+ {
+ bresult = true;
+ it_tree->second.erase(it_branch);
+ break;
+ }
+ }
+ }
+ }
+ return bresult;
+ }
+
+ //////////////////////////////////////////////////
+ // create a new descriptor //
+ // @param event : WxEvent //
+ // @return : - //
+ //////////////////////////////////////////////////
+ void WxDescriptorPanel::OnNew(wxCommandEvent &Event)
+ {
+ LevelCtrl->SetValue(_T("patient"));
+ ResultCtrl->Clear();
+ DscpTree.clear();
+ lv = 0;
+ }
+
+ //////////////////////////////////////////////////
+ // Load a descriptor file //
+ // @param event : WxEvent //
+ // @return : - //
+ //////////////////////////////////////////////////
+ void WxDescriptorPanel::OnLoad(wxCommandEvent &Event)
+ {
+ long style = wxOPEN | wxFILE_MUST_EXIST;
+ LevelCtrl->SetValue(_T("patient"));
+ ResultCtrl->Clear();
+ DscpTree.clear();
+ lv = 0;
+
+ std::string wc("*.dscp");
+ wxFileDialog* FD = new wxFileDialog( 0,
+ _T("Select file"),
+ crea::std2wx(m_path),
+ _T(""),
+ crea::std2wx(wc),
+ style,
+ wxDefaultPosition);
+ if (FD->ShowModal()==wxID_OK)
+ {
+ loadDescriptor(crea::wx2std(FD->GetPath()).c_str());
+ }
+
+ }
+
+ //////////////////////////////////////////////////
+ // Save a descriptor //
+ // @param event : WxEvent //
+ // @return : - //
+ //////////////////////////////////////////////////
+ void WxDescriptorPanel::OnOK(wxCommandEvent &Event)
+ {
+ saveDescriptor();
+ wxWindow::Close();
+ }
+
+ /////////////////////////////////////////////////////
+ // Save a descriptor and apply it (create a new DB//
+ // @param event : WxEvent //
+ // @return : - //
+ /////////////////////////////////////////////////////
+ void WxDescriptorPanel::OnApply(wxCommandEvent &Event)
+ {
+ m_DscpFile = saveDescriptor();
+ wxWindow::Close();
+ SetReturnCode(ID_DSCP_APPLY);
+ }
+
+ const std::string WxDescriptorPanel::saveDescriptor()
+ {
+ std::string file = "";
+ long style = wxSAVE;
+ std::string wc("*.dscp");
+ wxFileDialog* FD = new wxFileDialog( 0,
+ _T("Select file"),
+ _T(""),
+ _T(""),
+ crea::std2wx(wc),
+ style,
+ wxDefaultPosition);
+
+
+ if (FD->ShowModal()==wxID_OK)
+ {
+ createDescriptorFile();
+ file = crea::wx2std(FD->GetPath()).c_str();
+ std::ofstream ofs(file.c_str());
+ ofs.clear();
+ ofs << outDscp;
+ ofs.close();
+ }
+ return file.c_str();
+ }
+
+ ///////////////////////////////////////////////////////
+ // Cancel action //
+ // @param event : WxEvent //
+ // @return : - //
+ ///////////////////////////////////////////////////////
+
+ void WxDescriptorPanel::OnCancel(wxCommandEvent& event)
+ {
+ }
+
+ ///////////////////////////////////////////////////////
+ // create a descriptor file //
+ // @param - : //
+ // @return : - //
+ ///////////////////////////////////////////////////////
+ void WxDescriptorPanel::createDescriptorFile()
+ {
+
+ outDscp.clear();
+ outDscp += "<level>";
+ outDscp += "\n";
+ outDscp += "Root";
+ outDscp += "\n";
+ outDscp += "O Name Name 4";
+ outDscp += "\n";
+ std::map<std::string, std::vector <std::string> >::iterator it_tree = DscpTree.begin();
+ std::map<int, std::string >::iterator it_lv_nb = lvlist.begin();
+ std::map<int, std::string >::iterator it_lv = lvlist.begin();
+ it_lv_nb++;
+ for (;it_lv != lvlist.end(); it_lv++)
+ {
+ outDscp +="<level>";
+ outDscp += "\n";
+ outDscp += it_lv->second.c_str();
+ outDscp += "\n";
+ if(it_lv_nb != lvlist.end())
+ {
+ outDscp += "O NumberOfChildren ";
+ outDscp += it_lv_nb->second.c_str();
+ outDscp += "s";
+ outDscp += "\n";
+ it_lv_nb++;
+ }
+ std::vector<std::string>::iterator it_branch = DscpTree[it_lv->second.c_str()].begin();
+ for(;it_branch != DscpTree[it_lv->second.c_str()].end(); it_branch++)
+ {
+ std::string att = it_branch->c_str();
+ if(att[0] == 'D' && att[7] == '_' && att.size() == 14)
+ {
+ outDscp += "D ";
+ outDscp += att.substr(1,6) + " "; // GR
+ outDscp += att.substr(8,6) + " ";// EL
+ outDscp += "3";
+ outDscp += "\n";
+ }
+ else
+ {
+ outDscp += "O ";
+ outDscp += it_branch->c_str();
+ outDscp += " ";
+ outDscp += ownatt[att];
+ outDscp += " ";
+ outDscp += "2";
+ outDscp += "\n";
+ }
+ }
+
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////
+ // load a descriptor //
+ // @param i_name : file name to load //
+ // @return : - //
+ /////////////////////////////////////////////////////
+ void WxDescriptorPanel::loadDescriptor(const std::string i_name)
+ {
+ std::ifstream i_file(i_name.c_str());
+ std::stringstream buffer;
+ buffer << i_file.rdbuf();
+ std::string line;
+ std::string level;
+
+#if defined(USE_GDCM2)
+ const gdcm::Global& g = gdcm::Global::GetInstance(); // sum of all knowledge !
+ const gdcm::Dicts &dicts = g.GetDicts();
+ const gdcm::Dict &dict = dicts.GetPublicDict(); // Part 6
+#endif
+
+
+ bool bname;
+ int ilevel = -1;
+
+
+ while(std::getline(buffer, line))
+ {
+ if(line =="<level>")
+ { //increment levels.
+ ilevel++;
+ bname = true;
+ }
+ else if(bname)
+ {
+ // For each level, a name to describe it
+ level = line;
+ if(ilevel>0)
+ {
+ onAddLevel(level);
+ }
+ bname = false;
+ }
+ else
+ {
+ // split line to find all tags
+ std::vector<std::string> descriptors;
+ std::string separator = " ";
+ std::string::size_type last_pos = line.find_first_not_of(separator);
+ //find first separator
+ std::string::size_type pos = line.find_first_of(separator, last_pos);
+ while(std::string::npos != pos || std::string::npos != last_pos)
+ {
+ descriptors.push_back(line.substr(last_pos, pos - last_pos));
+ last_pos = line.find_first_not_of(separator, pos);
+ pos = line.find_first_of(separator, last_pos);
+ }
+
+ // By default, the last tag is at zero and not recorded but if take in count
+ unsigned int flag = 0;
+ if(descriptors.size() == 4)
+ {
+ std::stringstream val;
+ val << std::dec << descriptors[3];
+ val>> flag;
+ }
+
+ // if Dicom tag, use "group" and "element" descriptor
+ if(descriptors[0] == "D")
+ { std::stringstream val, val2;
+ unsigned short group;
+ unsigned short element;
+ val << std::dec << descriptors[1] ;
+ val >> std::hex >> group;
+ val2 << std::dec << descriptors[2];
+ val2 >> std::hex >> element;
+ std::string compose = "D";
+ compose += descriptors[1];
+ compose += "_";
+ compose += descriptors[2];
+#if defined(USE_GDCM)
+ GDCM_NAME_SPACE::DictEntry* entry = GDCM_NAME_SPACE::Global::GetDicts()->GetDefaultPubDict()->GetEntry(group, element);
+ if(ilevel>0)
+ {
+ onAddAttribute( entry->GetName(),compose, level);
+ }
+#endif
+
+#if defined(USE_GDCM2)
+ gdcm::DictEntry dictentry = dict.GetDictEntry(gdcm::Tag(group, element));
+ if(ilevel>0)
+ {
+ onAddAttribute( dictentry.GetName(),compose, level);
+ }
+
+
+#endif
+ }
+ else if(descriptors[0].find("#") != -1)
+ {
+ // commented line continue to next line
+ }
+ else // "O" means if user's own tag.
+ {
+ boost::algorithm::replace_all(descriptors[2],"_"," ");
+ if(ilevel>0 && descriptors[1] != "NumberOfChildren" )
+ {
+ onAddAttribute( descriptors[2].c_str(),descriptors[1].c_str(), level);
+ }
+ }
+ }
+ }
+ }
+
+//======================================================================
+
+//======================================================================
+
+} // EO namespace creaImageIO
+
+
--- /dev/null
+#ifndef __creaImageIOWxDescriptorPanel_h_INCLUDED__
+#define __creaImageIOWxDescriptorPanel_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+#include <creaWx.h>
+#include <string>
+#include <map>
+#include <vector>
+#include <creaImageIOWxGimmickView.h>
+#include <wx/listctrl.h>
+#include <wx/statline.h>
+
+
+#define ID_DESCRIPTOR 1500
+#define ID_LEVEL_CTRL ID_DESCRIPTOR+1
+#define ID_LEVEL_ADD ID_DESCRIPTOR+2
+#define ID_GR_CTRL ID_DESCRIPTOR+3
+#define ID_EL_CTRL ID_DESCRIPTOR+4
+#define ID_NAME_CTRL ID_DESCRIPTOR+5
+#define ID_ATTRIBUTE_CTRL ID_DESCRIPTOR+6
+#define ID_ATTRIBUTE_ADD ID_DESCRIPTOR+7
+#define ID_REMOVE_ADD ID_DESCRIPTOR+8
+#define ID_DSCP_APPLY ID_DESCRIPTOR+9
+
+namespace creaImageIO
+{
+
+ /**
+ * \ingroup GUI
+ */
+ //=====================================================================
+ //=====================================================================
+ /// Gimmick DB are based on descriptors with a tree structure .
+ /// Each level contains attributes (DICOM or other) to identify data
+ /// WxDescriptorPanel allows creation, modification and save of descriptors.
+ ///
+
+ class WxDescriptorPanel : public wxDialog
+ {
+ public:
+ WxDescriptorPanel();
+ WxDescriptorPanel(wxWindow *parent, const std::string path);
+ ~WxDescriptorPanel();
+
+ // Get file to load Descriptor
+ const std::string& GetDescriptor(){ return m_DscpFile;}
+
+ private :
+
+ // WxControls
+ wxTextCtrl *LevelCtrl; //TextCtrl to enter level's name
+ wxTextCtrl *GRCtrl; // TextCtrl to enter DICOM Group value
+ wxTextCtrl *ELCtrl; // TextCtrl to enter DICOM element value
+ wxTextCtrl *ResultCtrl; //TextCtrl to visualize Descriptor
+ wxComboBox *AttributeCombo; // Combox to choose Attribute values
+
+ /// Add an attribute (DICOM or own)
+ void OnAddAttribute(wxCommandEvent& event);
+
+ /// Add an attribute (DICOM or own)
+ void onAddAttribute( const std::string &att, const std::string &name_att, std::string level = "");
+
+ /// Add a Level
+ void OnAddLevel(wxCommandEvent& event);
+
+ /// Add a Level
+ void onAddLevel(const std::string &level);
+
+ /// Find a DICOM attribute from group and element values
+ void OnDicomAttribute(wxCommandEvent& event);
+
+ /// Remove a value (level or attribute)
+ void OnRemove(wxCommandEvent& event);
+
+ /// Create a new descriptor
+ void OnNew(wxCommandEvent& event);
+
+ /// Load an exsitant descriptor
+ void OnLoad(wxCommandEvent& event);
+
+ /// Save a descriptor
+ void OnOK(wxCommandEvent& event);
+
+ /// Save a descriptor and use it
+ void OnApply(wxCommandEvent& event);
+
+ /// Cancel
+ void OnCancel(wxCommandEvent& event);
+
+ /// Create a descriptor structure
+ void CreateDescriptor(int type);
+
+ /// add a level
+ bool addLevel(const std::string &name);
+
+ /// add an attribute
+ bool addAtribute(const std::string &level, const std::string &name);
+
+ /// remove an attribute
+ bool RemoveAttribute(const std::string &level, const std::string &name);
+
+ /// remove a level
+ bool RemoveLevel(const std::string &name);
+
+ /// Create a descriptor file
+ void createDescriptorFile();
+
+ /// load a descriptor file
+ void loadDescriptor(const std::string i_name);
+
+ /// find a level'name
+ std::string findLevel();
+
+ /// determine values for own attributes
+ std::string OwnAttribute(const std::string name);
+
+ /// save a descriptor in a file
+ const std::string saveDescriptor();
+
+ /// number of level
+ int lv;
+
+ /// Insert point in result control
+ long InsertPt;
+
+ /// Output file for Descriptor
+ std::string m_DscpFile;
+
+ /// Output Descriptor
+ std::string outDscp;
+
+ // Vector of attributes
+ std::vector<std::string> VLevel;
+
+ // map of level, attributes
+ std::map<std::string, std::vector<std::string> > DscpTree;
+
+ // map of own application attributes
+ std::map<std::string, std::string> ownatt;
+
+ // List of added level
+ std::map<int, std::string> lvlist;
+
+ // DB Gimmick path
+ std::string m_path;
+
+ }; // class WxDescriptorPanel
+ //=====================================================================
+
+
+} // EO namespace creaImageIO
+
+
+#endif // USE_WIDGETS
+// EOF
+#endif
+
--- /dev/null
+#include <creaImageIOWxDumpPanel.h>
+#include <creaImageIOSystem.h>
+#include <creaImageIOGimmick.h>
+#if defined(USE_GDCM)
+#include <gdcmGlobal.h>
+#include <gdcmDictSet.h>
+#include "gdcmFile.h"
+#include "gdcmDocument.h"
+#include "gdcmFileHelper.h"
+#endif
+#include "icons/save.xpm"
+
+namespace creaImageIO
+{
+ // CTor
+ WxDumpPanel::WxDumpPanel(wxWindow *parent, std::string i_filename)
+ : wxDialog(parent, -1,_T("DICOM TAGS"), wxDefaultPosition, wxSize(550,580)), filename(i_filename)
+ {
+ int size = 16;
+ mIcon = new wxImageList(size,size,true);
+ mIcon->Add(wxBitmap(wxBitmap(wxIcon(save_xpm)).ConvertToImage().Rescale(size, size)));
+ wxToolBar *mToolBar = new wxToolBar(this,-1,wxDefaultPosition,wxDefaultSize);
+ mToolBar->AddTool( DUMP_SAVE_ID,_T("Save"), mIcon->GetBitmap(0), _T("Save Dicom Tags in text file"));
+ mToolBar->Realize();
+ DumpText = new wxTextCtrl( this, wxID_ANY,_T(""), wxPoint(5,30), wxSize(520,510), wxTE_READONLY| wxMac | wxTE_MULTILINE | wxTE_RICH );
+ Layout();
+ Print();
+ }
+
+ // DTor
+ WxDumpPanel::~WxDumpPanel(){}
+
+ ///////////////////////////////////////////////////
+ /// Display in a control Text all dicom tags
+ ///////////////////////////////////////////////////
+ void WxDumpPanel::Print()
+ {
+ std::stringstream os;
+ if ( !filename.empty()) // ====== Deal with a single file ======
+ {
+ /* GDCM_NAME_SPACE::File *f = GDCM_NAME_SPACE::File::New();
+ f->SetLoadMode(GDCM_NAME_SPACE::LD_ALL);
+ f->SetFileName( filename );
+ f->SetMaxSizeLoadEntry(0xffff);
+ f->Load();
+ GDCM_NAME_SPACE::FileHelper *fh = GDCM_NAME_SPACE::FileHelper::New(f);
+ f->SetLoadMode(GDCM_NAME_SPACE::LD_NOSEQ |GDCM_NAME_SPACE::LD_NOSHADOW);
+ fh->SetPrintLevel( 0 );
+ fh->Print(os);
+ std::string result;
+ std::string line;
+ while(std::getline(os, line))
+ {
+ result +=clean(line.c_str());
+ result += "\n";
+ }
+ DumpText->SetValue(crea::std2wx(result));
+
+ std::string pixelType =f->GetPixelType();
+ int nX,nY,nZ,nT,sPP,planarConfig;
+
+ nX=f->GetXSize();
+ nY=f->GetYSize();
+ nZ=f->GetZSize();
+ nT=f->GetTSize();*/
+ }
+ }
+
+
+ const std::string WxDumpPanel::clean(const std::string &i_line)
+ {
+
+ if (i_line.substr(4,1) == "|")
+ {
+ std::string tag;
+ std::string line;
+ std:string signification;
+ std::string value;
+ std::string resultat;
+
+ tag = "(" + i_line.substr(0,9) + ")";
+ line = i_line.substr(14,i_line.size()-10);
+ int pos1 = line.find_first_of("[");
+ int pos2 = line.find_first_of("]");
+ signification = line.substr(pos1+1, pos2-pos1-1);
+ line = line.substr(pos2+1);
+ pos1 = line.find_first_of("[");
+ pos2 = line.find_first_of("]");
+ value = line.substr(pos1+1, pos2-pos1-1);
+ resultat = tag + " " + signification + ": " +value;
+ return resultat;
+ }
+ else
+ {
+ return i_line;
+ }
+ }
+
+///////////////////////////////////////////////////
+/// wxEvent to save Dicom Tags in a text file //
+///////////////////////////////////////////////////
+ void WxDumpPanel::SaveInfos(wxCommandEvent& event)
+ {
+ wxFileDialog* FD = new wxFileDialog( 0,_T("Select file"), _T(""), _T(""),
+ crea::std2wx("*.txt"), wxOPEN, wxDefaultPosition);
+ if (FD->ShowModal()==wxID_OK)
+ {
+ wxBusyCursor busy;
+ std::ofstream ofs(crea::wx2std(FD->GetPath()).c_str());
+ ofs.clear();
+ ofs << crea::wx2std(DumpText->GetValue());;
+ ofs.close();
+ }
+ Close();
+ }
+
+////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////
+BEGIN_EVENT_TABLE(WxDumpPanel, wxDialog)
+ EVT_TOOL(DUMP_SAVE_ID, WxDumpPanel::SaveInfos)
+END_EVENT_TABLE()
+}
+
--- /dev/null
+#ifndef __creaImageIOWxDumpPanel_h_INCLUDED__
+#define __creaImageIOWxDumpPanel_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+#include <creaWx.h>
+#include <creaImageIOWxGimmickView.h>
+
+#define DUMP_SAVE_ID 1800
+
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup GUI
+ */
+ //=====================================================================
+ //=====================================================================
+ /// Display all dicom tags from a given DICOM file
+ class WxDumpPanel : public wxDialog
+ {
+ public:
+
+ /// Constructor
+ WxDumpPanel(wxWindow *parent, std::string i_filename);
+
+ /// Destructor
+ ~WxDumpPanel();
+
+
+ private :
+
+ const std::string clean(const std::string &i_line);
+ /// Display in a control Text all dicom tags
+ void Print();
+
+ /// wxEvent to save Dicom Tags in a text file
+ void SaveInfos(wxCommandEvent& event);
+
+ /// Parent DialogBox
+ wxDialog* dialog;
+
+ /// Icons list
+ wxImageList *mIcon;
+
+ /// ControlText to display tags
+ wxTextCtrl *DumpText;
+
+ /// Dicom file to display
+ std::string filename;
+
+ DECLARE_EVENT_TABLE()
+
+ }; // class WxEditFieldsPanel
+ //=====================================================================
+
+
+} // EO namespace creaImageIO
+
+
+#endif // USE_WIDGETS
+// EOF
+#endif
+
--- /dev/null
+#include <creaImageIOWxEditFieldsPanel.h>
+#include <creaImageIOSystem.h>
+#include <wx/arrstr.h>
+
+//using namespace tree;
+namespace creaImageIO
+{
+ const int ID_COMBO = 140;
+ // CTor
+ WxEditFieldsPanel::WxEditFieldsPanel(wxWindow *parent, wxDialog* dial, WxGimmickView* view, tree::Node* nod,
+ const std::vector<std::string> name,
+ const std::vector<std::string> key)
+ : wxPanel( parent,
+ -1, wxDefaultPosition,
+ wxDefaultSize,
+ wxRESIZE_BORDER |
+ wxSYSTEM_MENU |
+ wxCLOSE_BOX |
+ wxMAXIMIZE_BOX |
+ wxMINIMIZE_BOX |
+ wxCAPTION
+ ),
+ dialog(dial),
+ node (nod),
+ names(name),
+ keys(key),
+ mView(view)
+ {
+ GimmickDebugMessage(1,"WxCustomizeConfigPanel::WxCustomizeConfigPanel"
+ <<std::endl);
+ wxStaticText * cp=new wxStaticText(this,-1,_T(" Attribute to change: "), wxPoint(5,10));
+ wxArrayString as;
+ std::vector<std::string>::const_iterator it;
+ for(it=names.begin();it!=names.end();++it)
+ {
+ as.Add(crea::std2wx(*it));
+ }
+ attributes=new wxComboBox(this, ID_COMBO, crea::std2wx(names.front()), wxPoint(110, 10), wxDefaultSize,as);
+ std::string val=node->GetAttribute(keys[0]);
+ if(val.compare("")==0){val="?";}
+
+ wxStaticText * av=new wxStaticText(this,-1,_T(" Current Value: "), wxPoint(5,40));
+ actualVal=new wxStaticText(this,-1,crea::std2wx(val), wxPoint(110,40));
+
+ wxStaticText * nv=new wxStaticText(this,-1,_T(" New Value: "), wxPoint(5,70));
+ newVal=new wxTextCtrl(this, wxID_ANY, crea::std2wx(val), wxPoint(110,70), wxSize(220,20));
+
+ wxButton *save = new wxButton(this,wxID_ANY,_T("Save Changes"), wxPoint(5,100) );
+ Connect( save->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxEditFieldsPanel::OnEdit );
+
+ Layout();
+ }
+
+ /// Destructor
+ WxEditFieldsPanel::~WxEditFieldsPanel()
+ {
+ GimmickDebugMessage(1,"WxEditFieldsPanel::~WxEditFieldsPanel"
+ <<std::endl);
+ }
+
+ void WxEditFieldsPanel::OnEdit(wxCommandEvent& event)
+ {
+ std::string val=crea::wx2std(newVal->GetValue());
+ int sel=attributes->GetSelection();
+ if(sel==-1)
+ {
+ sel=0;
+ }
+ mView->OnFieldsEdited(node,names[sel],keys[sel],val);
+ dialog->Destroy();
+ }
+
+ void WxEditFieldsPanel::OnComboChange(wxCommandEvent& event)
+ {
+ std::string val=node->GetAttribute(keys[attributes->GetSelection()]);
+ if(val.compare("")==0){val="?";}
+ actualVal->SetLabel(crea::std2wx(val));
+ newVal->SetValue(crea::std2wx(val));
+ }
+
+//======================================================================
+BEGIN_EVENT_TABLE(WxEditFieldsPanel, wxPanel)
+EVT_COMBOBOX (ID_COMBO,WxEditFieldsPanel::OnComboChange)
+END_EVENT_TABLE()
+//======================================================================
+
+} // EO namespace creaImageIO
+
--- /dev/null
+#ifndef __creaImageIOWxEditFieldsPanel_h_INCLUDED__
+#define __creaImageIOWxEditFieldsPanel_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+#include <creaWx.h>
+#include <creaImageIOWxGimmickView.h>
+
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup GUI
+ */
+ //=====================================================================
+ //=====================================================================
+ class WxEditFieldsPanel : public wxPanel
+ {
+ public:
+ WxEditFieldsPanel();
+ WxEditFieldsPanel(wxWindow *parent,
+ wxDialog* dial,
+ WxGimmickView* view,
+ tree::Node* nod,
+ const std::vector<std::string> name,
+ const std::vector<std::string> key);
+
+ ~WxEditFieldsPanel();
+ ///Saves the configuration
+ void OnEdit(wxCommandEvent& event);
+ void OnComboChange(wxCommandEvent& event);
+
+ private :
+ tree::Node* node;
+ std::vector<std::string> names;
+ std::vector<std::string> keys;
+ wxDialog* dialog;
+ WxGimmickView* mView;
+ wxComboBox* attributes;
+ wxStaticText * actualVal;
+ wxTextCtrl* newVal;
+
+ DECLARE_EVENT_TABLE()
+
+
+ }; // class WxEditFieldsPanel
+ //=====================================================================
+
+
+} // EO namespace creaImageIO
+
+
+#endif // USE_WIDGETS
+// EOF
+#endif
+
--- /dev/null
+#include <creaImageIOWxExportDlg.h>
+
+namespace creaImageIO
+{
+ // CTor
+ WxExportDlg::WxExportDlg(wxWindow *parent, const std::vector<std::string> storages)
+ : wxDialog(parent, -1,_T("EXPORT FILES TO STORAGE"), wxDefaultPosition, wxSize(260,150))
+ {
+ int size = 16;
+
+ wxStaticText * ExportText=new wxStaticText(this,-1,_T(" Storage to export: "), wxPoint(5,10));
+ wxArrayString names;
+ std::vector<std::string>::const_iterator it = storages.begin();
+ for(;it != storages.end(); it++)
+ {
+ names.Add(crea::std2wx(*it));
+ }
+ ExportCombo = new wxComboBox(this, ID_EXPORTCOMBO_CTRL,_T(""),wxPoint(120,10), wxSize(120,25),names);
+ ExportCombo->SetSelection(0);
+ // Connect( ExportCombo->GetId(), wxEVT_COMMAND_TEXT_UPDATED , (wxObjectEventFunction) &WxDescriptorPanel::OnDicomAttribute );
+
+ // VALIDATION BUTTON
+ wxButton *Ok = new wxButton(this, -1,_T("OK"), wxPoint(5,50) );
+ Connect( Ok->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxExportDlg::OnOk );
+
+ wxButton *Cancel = new wxButton(this, wxID_CANCEL,_T("CANCEL"), wxPoint(100,50) );
+ Layout();
+
+ }
+
+ WxExportDlg::~WxExportDlg(){}
+
+ void WxExportDlg::OnOk(wxCommandEvent &event)
+ {
+ m_name = crea::wx2std(ExportCombo->GetValue());
+ Close();
+ SetReturnCode(ID_EXPORT_OK);
+ }
+}
--- /dev/null
+#ifndef __creaImageIOWxExportDlg_h_INCLUDED__
+#define __creaImageIOWxExportDlg_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+#include <creaWx.h>
+#include <creaImageIOWxGimmickView.h>
+
+#define ID_EXPORTCOMBO_CTRL 1801
+#define ID_EXPORT_OK 1802
+
+namespace creaImageIO{
+ /**
+ * \ingroup GUI
+ */
+ //=====================================================================
+ //=====================================================================
+ /// Gimmick can handle multiple database with different organisations.
+ /// WxDescriptorPanel allows to select the storage to export data.
+ ///
+ class WxExportDlg : public wxDialog
+ {
+ public:
+ ///CTor
+ WxExportDlg(wxWindow *parent, const std::vector<std::string> storages);
+ ///DTor
+ ~WxExportDlg();
+
+ /// Get selected storage
+ const std::string& GetStorage(){ return m_name;}
+
+ private:
+
+ /// Storage ComboBox
+ wxComboBox *ExportCombo;
+
+ ///Validate selected storage
+ void OnOk(wxCommandEvent &event);
+
+ /// Storage name
+ std::string m_name;
+ };
+}
+#endif // USE_WIDGETS
+// EOF
+#endif
--- /dev/null
+#include <creaImageIOWxGimmickFrame.h>
+#include <creaImageIOSystem.h>
+#include <creaImageIOGimmick.h>
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+namespace creaImageIO
+{
+ // CTor
+ WxGimmickFrame::WxGimmickFrame(wxWindow *parent,
+ wxWindowID id,
+ wxString title,
+ const wxPoint& pos,
+ const wxSize& size,
+ int threads)
+ : wxFrame( parent,
+ id,
+ title,
+ pos,
+ size,
+ wxRESIZE_BORDER |
+ wxSYSTEM_MENU |
+ wxCLOSE_BOX |
+ wxMAXIMIZE_BOX |
+ wxMINIMIZE_BOX |
+ wxCAPTION
+ ),
+ // mGimmick(0),
+ mView(0)
+ {
+ GimmickDebugMessage(1,"WxGimmickFrame::WxGimmickFrame"
+ <<std::endl);
+ wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
+
+ try {
+
+// mGimmick = boost::shared_ptr<Gimmick>(new Gimmick());
+ mGimmick->Initialize();
+
+ int min_dim = GIMMICK_2D_IMAGE_SELECTION;
+ int max_dim = GIMMICK_3D_IMAGE_SELECTION;
+ mView = new WxGimmickView(mGimmick,
+ this,
+ -1,
+ wxDefaultPosition,
+ size,
+ min_dim,
+ max_dim,
+ threads);
+ mView->Initialize();
+ }
+ catch (crea::Exception e)
+ {
+ e.Print();
+ return;
+ }
+
+ topsizer->Add( mView,1,wxGROW,0);
+
+ SetSizer( topsizer );
+ Layout();
+ }
+
+ /// Destructor
+ WxGimmickFrame::~WxGimmickFrame()
+ {
+ GimmickDebugMessage(1,"WxGimmickFrame::~WxGimmickFrame"
+ <<std::endl);
+ if (mView)
+ {
+ delete mView;
+ }
+ if (mGimmick)
+ {
+ mGimmick->Finalize();
+// delete mGimmick;
+ }
+ }
+
+
+ //================================================================
+ // BEGIN_EVENT_TABLE(WxGimmickFrame, wxDialog)
+ // END_EVENT_TABLE()
+ //================================================================
+
+
+} // EO namespace creaImageIO
+
+
--- /dev/null
+#ifndef __creaImageIOWxGimmickFrame_h_INCLUDED__
+#define __creaImageIOWxGimmickFrame_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+
+#include <creaImageIOWxGimmickView.h>
+#include <creaWx.h>
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup GUI
+ */
+ //=====================================================================
+ //=====================================================================
+ class WxGimmickFrame : public wxFrame
+ {
+ public:
+ WxGimmickFrame();
+ WxGimmickFrame(wxWindow *parent,
+ const wxWindowID id,
+ wxString title,
+ const wxPoint& pos,
+ const wxSize& size,
+ int threads = 0);
+
+ ~WxGimmickFrame();
+
+ // Gimmick* GetGimmick() { return mGimmick; }
+ // typedef WxGimmick ViewType;
+ // typedef WxGimmickView::EventType EventType;
+
+
+
+
+ // void OnSelChanged(EventType& event);
+ // void OnContextualMenu(EventType& event);
+ // void OnMenuTest(wxCommandEvent& event);
+ // void OnButtonOk(wxCommandEvent& event);
+ // void OnButtonCancel(wxCommandEvent& event);
+
+ // DECLARE_EVENT_TABLE();
+ private :
+
+ boost::shared_ptr<Gimmick> mGimmick;
+ WxGimmickView* mView;
+
+ }; // class WxGimmickFrame
+ //=====================================================================
+
+
+} // EO namespace creaImageIO
+
+
+#endif // USE_WIDGETS
+// EOF
+#endif
--- /dev/null
+#include <creaImageIOWxGimmickPanel.h>
+#include <creaImageIOSystem.h>
+#include <creaImageIOGimmick.h>
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+namespace creaImageIO
+{
+ // CTor
+ WxGimmickPanel::WxGimmickPanel(wxWindow *parent,
+ wxWindowID id,
+ const wxPoint& pos,
+ const wxSize& size,
+ const std::string i_namedescp ,
+ const std::string i_namedb ,
+ int threads)
+ : wxPanel( parent,
+ id,
+ pos,
+ size,
+ wxRESIZE_BORDER |
+ wxSYSTEM_MENU |
+ wxCLOSE_BOX |
+ wxMAXIMIZE_BOX |
+ wxMINIMIZE_BOX |
+ wxCAPTION
+ ),
+ //mGimmick(0),
+ mView(0)
+ {
+ GimmickDebugMessage(1,"WxGimmickPanel::WxGimmickPanel"
+ <<std::endl);
+ wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
+
+ try {
+
+ mGimmick = boost::shared_ptr<Gimmick>(new Gimmick());
+ mGimmick->Initialize(i_namedescp,i_namedb);
+ int min_dim = GIMMICK_2D_IMAGE_SELECTION;
+ int max_dim = GIMMICK_3D_IMAGE_SELECTION;
+ mView = new WxGimmickView(mGimmick,
+ this,
+ -1,
+ wxDefaultPosition,
+ size,
+ min_dim,
+ max_dim,
+ threads);
+ mView->Initialize();
+ // Connect the AddProgress callback
+ mView->ConnectValidationObserver ( boost::bind( &WxGimmickPanel::OnSelectedImage , this, _1 ) );
+ }
+ catch (crea::Exception e)
+ {
+ e.Print();
+ return;
+ }
+
+ topsizer->Add( mView,1,wxGROW,0);
+
+ SetSizer( topsizer );
+ Layout();
+ }
+
+ /// Destructor
+ WxGimmickPanel::~WxGimmickPanel()
+ {
+ GimmickDebugMessage(1,"WxGimmickPanel::~WxGimmickPanel"
+ <<std::endl);
+ if (mView)
+ {
+ delete mView;
+ }
+ if (mGimmick)
+ {
+ mGimmick->Finalize();
+ }
+ }
+
+//======================================================================
+
+//======================================================================
+
+ ///Callback method on a selection
+ void WxGimmickPanel::OnSelectedImage(bool t)
+ {
+ mSendImageSignal(t);
+ }
+
+ void WxGimmickPanel::AddImagesToDB(std::string dir)
+ {
+ mView->AddDir(dir);
+ }
+
+ //================================================================
+ // BEGIN_EVENT_TABLE(WxGimmickPanel, wxDialog)
+ // END_EVENT_TABLE()
+ //================================================================
+
+
+ //====================================================================
+
+ //====================================================================
+ void WxGimmickPanel::ConnectSendImageObserver(SendImageCallbackType callback)
+ {
+ mSendImageSignal.connect(callback);
+ }
+
+} // EO namespace creaImageIO
+
+
--- /dev/null
+#ifndef __creaImageIOWxGimmickPanel_h_INCLUDED__
+#define __creaImageIOWxGimmickPanel_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+
+// Signal/slot mechanism for progress events
+#include <boost/signal.hpp>
+#include <boost/bind.hpp>
+
+#include <creaImageIOWxGimmickView.h>
+#include <creaWx.h>
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup GUI
+ */
+ //=====================================================================
+ //=====================================================================
+ class CREAIMAGEIO_EXPORT WxGimmickPanel : public wxPanel
+ {
+ public:
+ WxGimmickPanel();
+ WxGimmickPanel(wxWindow *parent,
+ const wxWindowID id,
+ const wxPoint& pos,
+ const wxSize& size,
+ const std::string i_namedescp ,
+ const std::string i_namedb = "Local Database",
+ int threads = 0);
+
+ ~WxGimmickPanel();
+
+ //=============================================
+ typedef boost::signal<void (bool)> SendImageSignalType;
+ typedef SendImageSignalType::slot_function_type SendImageCallbackType;
+ //=============================================
+
+ //==================================================================
+ /// Adds the function f to the list of functions to call
+ /// when the addition progresses.
+ /// f is of type ProgressCallbackType which is:
+ /// void (*ProgressCallbackType)(Progress&)
+ /// To pass a member function 'f' of an instance 'c' of a class 'C'
+ /// as callback you have to 'bind' it, i.e. call:
+ /// ConnectSendImageObserver ( boost::bind( &C::f , c, _1 ) );
+ void ConnectSendImageObserver(SendImageCallbackType callback);
+ //==================================================================
+
+ //===============================================================================================
+ //Image Selection
+ //===============================================================================================
+
+ void GetSelectedImages(std::vector<vtkImageData*>& s, int dim)
+ {
+ mView->GetSelectedImages(s, dim);
+ }
+
+ void GetSelectedImagesInVector(std::vector<vtkImageData*>& s, int dim)
+ {
+ mView->GetSelectedImagesInVector(s, dim);
+ }
+
+ void OnSelectedImage(bool t);
+
+ void AddImagesToDB(std::string dir);
+
+ // DECLARE_EVENT_TABLE();
+ private :
+
+ boost::shared_ptr<Gimmick> mGimmick;
+ WxGimmickView* mView;
+
+ ///The sendImage signal
+ SendImageSignalType mSendImageSignal;
+
+ }; // class WxGimmickPanel
+ //=====================================================================
+
+
+} // EO namespace creaImageIO
+
+
+#endif // USE_WIDGETS
+// EOF
+#endif
--- /dev/null
+#include <creaImageIOWxGimmickReaderDialog.h>
+#include <creaImageIOSystem.h>
+#include <creaImageIOGimmick.h>
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+namespace creaImageIO
+{
+ // CTor
+ WxGimmickReaderDialog::WxGimmickReaderDialog(wxWindow *parent,
+ wxWindowID id,
+ const std::string i_namedescp,
+ const std::string i_namedb,
+ wxString title,
+ const wxPoint& pos,
+ const wxSize& size,
+ int min_dim,
+ int max_dim,
+ int output_dim, // never used ?!? // JPR
+ int threads)
+ : wxDialog( parent,
+ id,
+ title,
+ pos,
+ size,
+ wxRESIZE_BORDER |
+ wxSYSTEM_MENU |
+ wxCLOSE_BOX |
+ wxMAXIMIZE_BOX |
+ wxMINIMIZE_BOX |
+ wxCAPTION
+ ),
+ // mGimmick(0),
+ mView(0)
+ {
+ GimmickDebugMessage(1,"WxGimmickReaderDialog::WxGimmickReaderDialog"
+ <<std::endl);
+ mtopsizer = new wxBoxSizer(wxVERTICAL);
+
+ try {
+
+ mGimmick = boost::shared_ptr<Gimmick>(new Gimmick());
+ mGimmick->Initialize(i_namedescp,i_namedb);
+
+ mView = new WxGimmickView(mGimmick,
+ this,
+ TVID,
+ wxDefaultPosition,
+ size,
+ min_dim,
+ max_dim,
+ threads);
+ mView->Initialize();
+ // Connect the AddProgress callback
+ mView->ConnectValidationObserver
+ ( boost::bind( &WxGimmickReaderDialog::OnValid , this, _1 ) );
+ }
+ catch (crea::Exception e)
+ {
+ e.Print();
+ return;
+ }
+
+ mtopsizer->Add( mView,1,wxGROW,0);
+
+ wxSizer* bsizer = this->CreateSeparatedButtonSizer(wxOK|wxCANCEL);
+ /*mOkButton = new wxButton(this, wxID_OK, _T("OK"), wxPoint(170,50));
+ mCancelButton = new wxButton(this, wxID_CANCEL, _T("CANCEL"), wxPoint(210,50));
+ */ mOkButton = (wxButton*)FindWindowById(GetAffirmativeId(), this);
+ mCancelButton = (wxButton*)FindWindowById(GetEscapeId(), this);
+
+ mOkButton->Enable(false);
+ mtopsizer->Add ( bsizer, 0, wxGROW );
+
+ SetSizer( mtopsizer );
+
+ Layout();
+ }
+
+ /// Destructor
+ WxGimmickReaderDialog::~WxGimmickReaderDialog()
+ {
+ GimmickDebugMessage(1,"WxGimmickReaderDialog::~WxGimmickReaderDialog"
+ <<std::endl);
+ if (mView)
+ {
+ delete mView;
+ }
+ if (mGimmick)
+ {
+ mGimmick->Finalize();
+ }
+ deleteMessage();
+ }
+
+void WxGimmickReaderDialog::deleteMessage()
+ {
+ deleteGimmickDebugMessage();
+ };
+ ///Callback method on a selection
+ void WxGimmickReaderDialog::OnValid(bool t)
+ {
+ mOkButton->Enable(t);
+ }
+
+ //================================================================
+ //BEGIN_EVENT_TABLE(WxGimmickReaderDialog, wxDialog)
+ //END_EVENT_TABLE()
+ //================================================================
+
+} // EO namespace creaImageIO
+
--- /dev/null
+#ifndef __creaImageIOWxGimmickReaderDialog_h_INCLUDED__
+#define __creaImageIOWxGimmickReaderDialog_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+
+#include <creaImageIOWxGimmickView.h>
+#include "creaImageIOSystem.h"
+#include <creaWx.h>
+#include "wx/wx.h"
+namespace creaImageIO
+{
+ /**
+ * \ingroup GUI
+ */
+
+ //=====================================================================
+ //=====================================================================
+ class CREAIMAGEIO_EXPORT WxGimmickReaderDialog : public wxDialog
+ {
+ public:
+ WxGimmickReaderDialog();
+ WxGimmickReaderDialog(wxWindow *parent,
+ const wxWindowID id,
+ const std::string i_namedescp ,
+ const std::string i_namedb ,
+ wxString title,
+ const wxPoint& pos,
+ const wxSize& size,
+ int image_min_dim = GIMMICK_2D_IMAGE_SELECTION,
+ int image_max_dim = GIMMICK_3D_IMAGE_SELECTION,
+ int output_dim = NATIVE,
+ int threads = 0);
+
+ boost::shared_ptr<Gimmick> GetGimmick() { return mGimmick; }
+ // typedef WxGimmick ViewType;
+ typedef WxGimmickView::EventType EventType;
+
+ ~WxGimmickReaderDialog();
+
+ //===============================================================================================
+ //Image Selection
+ //===============================================================================================
+
+ void GetSelectedImages(std::vector<vtkImageData*>& s, int dim)
+ { mView->GetSelectedImages(s, dim); }
+
+ void GetSelectedImagesInVector(std::vector<vtkImageData*>& s, int dim)
+ { mView->GetSelectedImagesInVector(s, dim); }
+
+ void GetSelectedFiles(std::vector<std::string>& s)
+ { mView->GetSelectedFiles(s); }
+
+ void OnSelChanged(EventType& event);
+ void OnContextualMenu(EventType& event);
+ void OnMenuTest(wxCommandEvent& event);
+ void OnValid(bool valid);
+ void OnExit(){ mView->StopPlayer(); }
+ void deleteMessage();
+ // void OnButtonOk(wxCommandEvent& event);
+ // void OnButtonCancel(wxCommandEvent& event);
+
+ private :
+
+ boost::shared_ptr<Gimmick> mGimmick;
+ WxGimmickView* mView;
+
+ wxButton* mOkButton;
+ wxButton* mCancelButton;
+ wxBoxSizer *mtopsizer;
+
+ enum
+ {
+ TVID = 1
+ // OKID = 2,
+ // CANCELID = 3
+ };
+
+ }; // class WxGimmickReaderDialog
+ //=====================================================================
+
+} // EO namespace creaImageIO
+
+
+#endif // USE_WIDGETS
+// EOF
+#endif
--- /dev/null
+#include <creaImageIOWxGimmickTools.h>
+
+
+namespace creaImageIO
+{
+ /**
+ ** Begin of the threshold panel
+ **/
+ WxGimmickTools::WxGimmickTools(wxWindow* parent, wxString mCurrentDirectory)
+ : wxPanel(parent, -1, wxDefaultPosition, wxSize(300,250), wxBORDER_SUNKEN)
+ {
+ _currentDir = mCurrentDirectory;
+ _addFiles = true;
+ _mhd = false;
+
+ _inputPath = new wxTextCtrl(this, wxID_ANY, _T(""), wxDefaultPosition, wxSize(400,30));
+ _outputPath = new wxTextCtrl(this, wxID_ANY, _T(""), wxDefaultPosition, wxSize(400,30));
+ _addCheckBox = new wxCheckBox(this, -1, _T("Add Images to Database?") );
+ _mhdCheckBox = new wxCheckBox(this, -1, _T("Convert to MHD?") );
+ _addCheckBox->SetValue(_addFiles);
+ _mhdCheckBox->SetValue(_mhd);
+
+ wxButton *inputDir = new wxButton(this, wxID_ANY,_T("Input Directory"), wxDefaultPosition, wxSize(140,30) );
+ wxButton *outputDir = new wxButton(this, wxID_ANY,_T("Output Directory"), wxDefaultPosition, wxSize(140,30) );
+
+ Connect( inputDir->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxGimmickTools::onInputDir );
+ Connect( outputDir->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxGimmickTools::onOutputDir );
+ Connect( _addCheckBox->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED, (wxObjectEventFunction) &WxGimmickTools::onAddToDatabase );
+ Connect( _mhdCheckBox->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED, (wxObjectEventFunction) &WxGimmickTools::onMHD );
+
+ wxFlexGridSizer *textSizer = new wxFlexGridSizer(1,2);
+ textSizer->Add( new wxStaticText(this, -1, _T("Transform a Bruker image into Dicom / MHD format.")), 1, wxGROW );
+ textSizer->Add( new wxStaticText(this, -1, _T("If checkbox is selected images will be loaded into the DB.")), 1, wxGROW );
+
+ wxFlexGridSizer *sizer = new wxFlexGridSizer(2,5);
+ sizer->Add( new wxStaticText(this, -1, _T(" ")), 1, wxGROW );
+ sizer->Add( new wxStaticText(this, -1, _T(" ")), 1, wxGROW );
+ sizer->Add( _inputPath, 1, wxGROW );
+ sizer->Add( inputDir, 1, wxGROW );
+ sizer->Add( _outputPath, 1, wxGROW );
+ sizer->Add( outputDir, 1, wxGROW );
+ sizer->Add( new wxStaticText(this, -1, _T(" ")), 1, wxGROW );
+ sizer->Add( new wxStaticText(this, -1, _T(" ")), 1, wxGROW );
+ sizer->Add( _addCheckBox, 1, wxGROW );
+ sizer->Add( _mhdCheckBox, 1, wxGROW );
+
+ wxFlexGridSizer *topSizer = new wxFlexGridSizer(1, 2);
+ topSizer->Add( textSizer, 1, wxGROW );
+ topSizer->Add( sizer, 1, wxGROW );
+ this->SetSizer( topSizer );
+ this->SetAutoLayout( true );
+ this->Layout();
+ }
+
+ WxGimmickTools::~WxGimmickTools()
+ {
+
+ }
+
+ wxString WxGimmickTools::getInputDir()
+ {
+ return _inputPath->GetValue();
+ }
+
+ wxString WxGimmickTools::getOutputDir()
+ {
+ return _outputPath->GetValue();
+ }
+
+ bool WxGimmickTools::getAddToDBCheckBoxValue()
+ {
+ return _addCheckBox->GetValue();
+ }
+
+ bool WxGimmickTools::getMHDCheckBoxValue()
+ {
+ return _mhdCheckBox->GetValue();
+ }
+
+ void WxGimmickTools::onInputDir(wxCommandEvent& event)
+ {
+ long style = wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST;
+ wxDirDialog* FD = new wxDirDialog( 0, _T("Select the Input Directory"), _currentDir, style);
+
+ if (FD->ShowModal()==wxID_OK)
+ {
+ _inputPath->SetValue(FD->GetPath());
+ }
+ }
+
+ void WxGimmickTools::onOutputDir(wxCommandEvent& event)
+ {
+ long style = wxDD_DEFAULT_STYLE;
+ wxDirDialog* FD = new wxDirDialog( 0, _T("Select the Output Directory"), _currentDir, style);
+
+ if (FD->ShowModal()==wxID_OK)
+ {
+ _outputPath->SetValue(FD->GetPath());
+ }
+ }
+
+ void WxGimmickTools::onAddToDatabase(wxCommandEvent& event)
+ {
+ _addFiles = _addCheckBox->GetValue();
+ }
+
+ void WxGimmickTools::onMHD(wxCommandEvent& event)
+ {
+ _mhd = _mhdCheckBox->GetValue();
+ }
+
+} // EO namespace creaImageIO
+
+
--- /dev/null
+#ifndef __creaImageIOWxGimmickTools_h_INCLUDED__
+#define __creaImageIOWxGimmickTools_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+
+#include <creaWx.h>
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup Tools
+ */
+ //=====================================================================
+
+ //=====================================================================
+
+ class WxGimmickTools : public wxPanel
+ {
+ public:
+ WxGimmickTools(wxWindow *parent, wxString mCurrentDirectory);
+ ~WxGimmickTools();
+
+ wxString getInputDir();
+ wxString getOutputDir();
+ bool getAddToDBCheckBoxValue();
+ bool getMHDCheckBoxValue();
+ private:
+ wxCheckBox * _addCheckBox;
+ wxCheckBox * _mhdCheckBox;
+ wxTextCtrl * _inputPath;
+ wxTextCtrl * _outputPath;
+ wxString _currentDir;
+ bool _addFiles;
+ bool _mhd;
+ void onInputDir(wxCommandEvent& event);
+ void onOutputDir(wxCommandEvent& event);
+ void onAddToDatabase(wxCommandEvent& event);
+ void onMHD(wxCommandEvent& event);
+ };
+
+} // EO namespace creaImageIO
+
+#endif // USE_WIDGETS
+// EOF
+#endif
--- /dev/null
+#include <creaImageIOPACSConnection.h>
+#include <creaImageIOWxPACSConnectionPanel.h>
+#include <creaImageIOWxGimmickView.h>
+#include <creaImageIOWxTreeView.h>
+#include <creaImageIOSystem.h>
+#include <creaImageIOWxCustomizeConfigPanel.h>
+#include <creaImageIOWxListenerPanel.h>
+#include <creaImageIOWxEditFieldsPanel.h>
+#include <creaImageIOWxAttributeSelectionPanel.h>
+#include <creaImageIOWxDescriptorPanel.h>
+#include <creaImageIOWxDumpPanel.h>
+#include <creaImageIOWxExportDlg.h>
+
+using namespace crea;
+// Icons
+#include "icons/accept.xpm"
+#include "icons/add.xpm"
+#include "icons/folder-down.xpm"
+#include "icons/page-down.xpm"
+#include "icons/remove.xpm"
+#include "icons/database-add.xpm"
+#include "icons/create-database.xpm"
+#include "icons/help.xpm"
+#include "icons/synchronize.xpm"
+#include "icons/settings.xpm"
+#include "icons/tools.xpm"
+//#include "icons/import.xpm"
+
+#include <wx/imaglist.h>
+#include <wx/popupwin.h>
+#include<boost/filesystem/operations.hpp>
+#if defined(BUILD_BRUKER)
+ #include "bruker2dicom.h"
+#endif
+
+
+#include <creaImageIOGimmick.h>
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+
+namespace creaImageIO
+{
+
+ //======================================================================
+ // The ids of the different tools
+ enum
+ {
+ TOOL_ADDFILES_ID = 1,
+ TOOL_ADDDIR_ID = 2,
+ TOOL_ADDDATABASE_ID = 3,
+ TOOL_REMOVE_ID = 4,
+ TOOL_SYNCHRONIZE_ID = 5,
+ TOOL_HELP_ID = 6,
+ TOOL_SETTINGS_ID = 7,
+ TOOL_TOOLS_ID = 8,
+ TOOL_CREATEDB_ID = 9,
+ TOOL_PACS_ID = 10
+ };
+ //======================================================================
+
+ //================================================================
+ //
+ const int icon_number = 11;
+ // Icon ids
+ typedef enum
+ {
+ Icon_create_database,
+ Icon_accept,
+ Icon_add,
+ Icon_folder_down,
+ Icon_page_down,
+ Icon_database_add,
+ Icon_remove,
+ Icon_synchronize,
+ Icon_help,
+ Icon_settings,
+ Icon_tools
+ }
+ icon_id;
+ //================================================================
+
+ //================================================================
+ /*
+ const icon_id Icon[5] = { Icon_Database,
+ Icon_Patient,
+ Icon_Study,
+ Icon_Series,
+ Icon_Image };
+ */
+ //================================================================
+
+
+ //======================================================================
+ // CTor
+ WxGimmickView::WxGimmickView(boost::shared_ptr<Gimmick> gimmick,
+ wxWindow *parent,
+ const wxWindowID id,
+ const wxPoint& pos,
+ const wxSize& size,
+ int min_dim,
+ int max_dim,
+ int number_of_threads)
+ : wxPanel(parent,id,pos,size),
+ GimmickView(gimmick, number_of_threads),
+ mProgressDialog(0),
+ mConstructed(false)
+ {
+ GimmickDebugMessage(1,"WxGimmickView::WxGimmickView"
+ <<std::endl);
+ // Sets the current directory to the home dir
+ mCurrentDirectory = std2wx(gimmick->GetHomeDirectory());
+
+ // Connect the AddProgress callback
+ gimmick->ConnectAddProgressObserver
+ ( boost::bind( &WxGimmickView::OnAddProgress , this, _1 ) );
+
+ // Create the list of icons (mIcon)
+ CreateIconList();
+
+ // Global sizer
+ msizer = new wxBoxSizer(wxVERTICAL);
+
+ // Create the tool bar
+ CreateToolBar();
+ msizer->Add( mToolBar, 0, wxGROW, 0);
+
+ // Split part below toolbar into notebook for views and panel
+ // for preview, messages...
+ mSplitter = new wxSplitterWindow( this , -1);
+
+ // Notebook
+ mNotebook = new wxNotebook(mSplitter,
+ -1, wxDefaultPosition, wxDefaultSize, 0);
+
+ //Gimmick
+ mGimmick=gimmick;
+
+ mSelectionMaxDimension = max_dim;
+ mSelectionMinDimension = min_dim;
+
+ // Create the views
+ CreateTreeViews();
+
+ // Bottom panel
+ mBottomPanel = new wxPanel(mSplitter,-1);
+
+ mbottom_sizer = new wxBoxSizer(wxVERTICAL); //HORIZONTAL);
+
+
+ // Previewer
+ mViewer = new WxViewer(mBottomPanel, wxID_ANY, wxT("Gimmick! Viewer"),wxDefaultPosition, wxDefaultSize );
+ //pointers.push_back(new ImagePointerHolder(GetDefaultImage())
+ pointers.push_back(boost::shared_ptr<creaImageIO::ImagePointerHolder>(new ImagePointerHolder(GetDefaultImage())));
+
+ mViewer->SetImageVector(pointers);
+ mViewer->StartPlayer();
+
+
+ mbottom_sizer->Add(mViewer,1,wxGROW,1);
+ // mViewer->Show();
+
+ mText = new wxStaticText(mBottomPanel, wxID_ANY, wxT("Welcome to Gimmick!"));
+ mbottom_sizer->Add(mText,0,wxGROW,0);
+
+
+
+ mBottomPanel->SetSizer(mbottom_sizer);
+
+ // Splitting
+ int hsize = size.GetHeight();
+
+ int top_minsize = 450;
+ int bottom_minsize = 50;
+
+ mSplitter->SetMinimumPaneSize( bottom_minsize );
+ mSplitter->SplitHorizontally( mNotebook, mBottomPanel,
+ top_minsize);
+
+ msizer->Add( mSplitter, 1, wxGROW, 0);
+
+ mProgressDialog=0;
+ SetSizer( msizer );
+ SetAutoLayout(true);
+ Layout();
+ //mListener=new Listener();
+ //mListener->ConnectObserver(boost::bind( &WxGimmickView::OnDriveMount, this, _1 ) );
+ //mListener->Create();
+ // mListener->Run();
+ // mListener->Pause();
+
+ mConstructed = true;
+ }
+ //======================================================================
+
+ //======================================================================
+ /// Destructor
+ WxGimmickView::~WxGimmickView()
+ {
+ // stop the viewer before application exit.
+ mViewer->StopPlayer();
+ GimmickDebugMessage(1,"WxGimmickView::~WxGimmickView"
+ <<std::endl);
+ delete mIcon;
+ delete mViewer;
+ //if(mListener->IsAlive()) { mListener->Delete(); }
+ }
+ //======================================================================
+
+ //======================================================================
+ /// Creates the tool bar
+ void WxGimmickView::CreateToolBar()
+ {
+ long style = wxTB_HORIZONTAL | wxNO_BORDER | wxTB_TEXT;
+ mToolBar = new wxToolBar(this,-1,wxDefaultPosition,wxDefaultSize,
+ style);
+
+ mToolAddFile = mToolBar->AddTool( TOOL_ADDFILES_ID,
+ _T("Add file(s)"),
+ mIcon->GetBitmap(Icon_page_down),
+ _T("Add one or more file to database")
+ );
+ mToolAddDir = mToolBar->AddTool( TOOL_ADDDIR_ID,
+ _T("Add folder"),
+ mIcon->GetBitmap(Icon_folder_down),
+ _T("Add the content of a folder to database")
+ );
+ mToolAddDatabase = mToolBar->AddTool( TOOL_ADDDATABASE_ID,
+ _T("Open database"),
+ mIcon->GetBitmap(Icon_database_add),
+ _T("Open a local or distant database")
+ );
+ mToolRemove = mToolBar->AddTool( TOOL_REMOVE_ID,
+ _T("Remove"),
+ mIcon->GetBitmap(Icon_remove),
+ _T("Remove selected items")
+ );
+ mToolSynchronize = mToolBar->AddTool( TOOL_SYNCHRONIZE_ID,
+ _T("Synchronize"),
+ mIcon->GetBitmap(Icon_synchronize),
+ _T("Synchronizes the database with disk")
+ );
+ mToolHelp = mToolBar->AddTool( TOOL_HELP_ID,
+ _T("Help"),
+ mIcon->GetBitmap(Icon_help),
+ _T("Open help window")
+ );
+ mToolSettings = mToolBar->AddTool( TOOL_SETTINGS_ID,
+ _T("System settings"),
+ mIcon->GetBitmap(Icon_settings),
+ _T("Allows the modification of various system settings")
+ );
+ mToolTools = mToolBar->AddTool( TOOL_TOOLS_ID,
+ _T("Tools"),
+ mIcon->GetBitmap(Icon_tools),
+ _T("Applies tools to images")
+ );
+ mToolAddFile = mToolBar->AddTool( TOOL_CREATEDB_ID,
+ _T("Create database"),
+ mIcon->GetBitmap(Icon_create_database),
+ _T("Create DB from an Attributes Descriptor file")
+ );
+#if defined(BUILD_PACS)
+ mToolAddFile = mToolBar->AddTool( TOOL_PACS_ID,
+ _T("PACS Connection,"),
+ mIcon->GetBitmap(Icon_create_database),
+ _T("Echo, Find and Get to a PACS")
+ );
+#endif
+ //const wxBitmap& bitmap1, const wxString& shortHelpString = "", wxItemKind kind = wxITEM_NORMAL)
+
+ mToolBar->Realize();
+ }
+ //======================================================================
+
+
+ //======================================================================
+ /// Create the tree view for TreeHandler provided
+ void WxGimmickView::CreateTreeView( TreeHandler* h)
+ {
+ std::string name(h->GetTree().GetAttribute("Name"));
+ GimmickMessage(2,"Creating the tree view for '"<<
+ name<<"'"<<std::endl);
+ // Create the WxTreeView
+ WxTreeView* view = new WxTreeView(h, this, mNotebook, -1);
+
+ // TO DO : TEST THAT A VIEW WITH SAME NAME IS NOT
+ // ALREADY IN THE MAP
+ GetTreeViewMap()[name] = view;
+
+ // Add Notebook page
+ mNotebook->AddPage( view, crea::std2wx(name) );
+
+ }
+
+ //======================================================================
+ void WxGimmickView::GetSelectedImages(std::vector<vtkImageData*>& s, int dim)
+ {
+ std::vector<std::string> files;
+ GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->GetSelectedAsString(files);
+ ReadImagesNotThreaded(s, files, dim);
+ }
+
+ //======================================================================
+ void WxGimmickView::GetSelectedImagesInVector(std::vector<vtkImageData*>& s, int dim)
+ {
+ std::vector<std::string> files;
+ GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->GetSelectedAsString(files);
+ ReadImagesNotThreadedInVector(s, files, dim);
+ }
+ //======================================================================
+
+ //======================================================================
+ void WxGimmickView::GetSelectedFiles(std::vector<std::string>& s)
+ {
+ GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->GetSelectedAsString(s);
+ }
+ //======================================================================
+
+ //======================================================================
+ void WxGimmickView::GetImages(int dim,
+ const std::vector<std::string>& files,
+ std::vector<vtkImageData*>& s)
+ {
+ ReadImagesNotThreaded(s,files,dim);
+ }
+ //======================================================================
+
+
+ //=================================================
+ void WxGimmickView::CreateIconList()
+ {
+ // Size of the icons;
+ int size = 16;
+
+ wxIcon icons[20];
+ // should correspond to Icon_xxx enum
+ icons[Icon_accept] = wxIcon(accept_xpm);
+ icons[Icon_add] = wxIcon(add_xpm);
+ icons[Icon_folder_down] = wxIcon(folder_down_xpm);
+ icons[Icon_page_down] = wxIcon(page_down_xpm);
+ icons[Icon_remove] = wxIcon(remove_xpm);
+ icons[Icon_database_add] = wxIcon(database_add_xpm);
+ icons[Icon_help] = wxIcon(help_xpm);
+ icons[Icon_synchronize] = wxIcon(synchronize_xpm);
+ icons[Icon_create_database] = wxIcon(create_database_xpm);
+ icons[Icon_settings] = wxIcon(settings_xpm);
+ icons[Icon_tools] = wxIcon(tools_xpm);
+
+ // unsigned int NbIcons = 8;
+ // Make an image list containing small icons
+ mIcon = new wxImageList(size,size,true);
+
+ // Make all icons the same size = size of the first one
+ int sizeOrig = icons[0].GetWidth();
+ for ( size_t i = 0; i < icon_number; i++ )
+ {
+ if ( size == sizeOrig )
+ {
+ mIcon->Add(icons[i]);
+ }
+ else
+ {
+ mIcon->Add(wxBitmap(wxBitmap(icons[i]).ConvertToImage().Rescale(size, size)));
+ }
+ }
+ }
+ //=================================================
+
+
+ //=================================================
+ void WxGimmickView::OnAddFiles(wxCommandEvent& event)
+ {
+ mViewer->StopPlayer();
+ long style = wxOPEN | wxFILE_MUST_EXIST | wxFD_MULTIPLE;
+ std::string wc("*");
+ wxFileDialog* FD = new wxFileDialog( 0,
+ _T("Select file"),
+ _T(""),
+ _T(""),
+ crea::std2wx(wc),
+ style,
+ wxDefaultPosition);
+
+ if (FD->ShowModal()==wxID_OK)
+ {
+ wxBusyCursor busy;
+
+ wxArrayString files;
+ FD->GetPaths(files);
+ unsigned int i;
+ std::vector<std::string> filenames;
+ for (i=0;i<files.GetCount();++i)
+ {
+ filenames.push_back(wx2std(files[i]));
+ GimmickMessage(2,"Adding File "<<files[i]<<"."<<std::endl);
+ }
+
+ mProgressDialog =
+ new wxProgressDialog(_T("Adding file(s)"),
+ _T(""),
+ 1000,
+ this,
+ wxPD_ELAPSED_TIME |
+ // wxPD_ESTIMATED_TIME |
+ // wxPD_REMAINING_TIME |
+ wxPD_CAN_ABORT );
+
+ // TO DO : select the current tree handler
+ mGimmick->AddFiles(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),filenames);
+
+ mProgressDialog->Pulse(_T("Updating view..."));
+
+ UpdateTreeViewLevel(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),1);
+ delete mProgressDialog;
+ DisplayAddSummary();
+
+ }
+ mViewer->StartPlayer();
+ }
+ //=================================================
+
+ //=================================================
+ void WxGimmickView::OnAddDir(wxCommandEvent& event)
+ {
+ mViewer->StopPlayer();
+ std::string name = crea::wx2std(mNotebook->GetCurrentPage()->GetName());
+ long style = wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST;
+ wxDirDialog* FD =
+ new wxDirDialog( 0,
+ _T("Select directory"),
+ mCurrentDirectory,
+ style);
+
+ if (FD->ShowModal()==wxID_OK)
+ {
+ std::string dirname = wx2std (FD->GetPath());
+ bool recurse = isNeedRecursive(dirname);
+ if (recurse)
+ {
+ recurse = wxMessageBox(_T("Recurse into sub-directories ?"), _T("Scan directory"), wxYES_NO,this ) == wxYES ? true : false;
+ }
+
+ wxBusyCursor busy;
+ wxString title(_T("Adding directory"));
+ if (recurse)
+ title = _T("Adding directory (recursive)");
+ mProgressDialog =
+ new wxProgressDialog(_T("Adding directory"),
+ _T(""),
+ NumberFilesToAdd(dirname,recurse),
+ this,
+ wxPD_ELAPSED_TIME |
+ wxPD_SMOOTH |
+ // wxPD_ESTIMATED_TIME |
+ // wxPD_REMAINING_TIME |
+ wxPD_CAN_ABORT );
+
+ mCurrentDirectory = FD->GetPath();
+ mGimmick->AddDir(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),dirname,recurse);
+ mProgressDialog->Pulse(_T("Updating view..."));
+
+ UpdateTreeViewLevel(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),1);
+ delete mProgressDialog;
+ DisplayAddSummary();
+ }
+ mViewer->StartPlayer();
+ delete FD;
+ }
+
+
+ //=================================================
+ // Determines number of files potentially to add to database
+ int WxGimmickView::NumberFilesToAdd(const std::string &dirpath, bool recursive)
+ {
+ int nb = 0;
+ if ( !boost::filesystem::exists( dirpath ) ) return nb;
+ boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end
+ for ( boost::filesystem::directory_iterator itr( dirpath ); itr != end_itr; ++itr )
+ {
+ // If is directory & recurse : do recurse
+ if ( boost::filesystem::is_directory(itr->status()) )
+ {
+ if (recursive)
+ {
+ nb += NumberFilesToAdd(itr->string(), recursive);
+ }
+ }
+ else
+ {
+ nb++;
+ }
+ }
+ return nb;
+ }
+
+ //=================================================
+ // Test a directory to know if contains sub-directory to analyze
+ bool WxGimmickView::isNeedRecursive(std::string i_name)
+ {
+ boost::filesystem::directory_iterator iter(i_name), end_iter;
+ bool bfindir = false;
+ for(; iter != end_iter; ++iter)
+ {
+ if(boost::filesystem::is_directory(*iter))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ //=================================================
+
+ //=================================================
+ void WxGimmickView::OnSelectionChange(const std::vector<tree::Node*>& sel, bool isSelection, int selection, bool needProcess)
+ {
+ GimmickDebugMessage(5,
+ "WxGimmickView::OnSelectionChange"
+ <<std::endl);
+ wxBusyCursor busy;
+ bool valid=true;
+
+ if(sel.size()==0)
+ {
+ valid= ValidateSelected(NULL,
+ mSelectionMinDimension,
+ mSelectionMaxDimension );
+ }
+ else if(needProcess)
+ {
+ ResetExtent();
+ std::vector<tree::Node*>::const_iterator i;
+ for(i=sel.begin();i!=sel.end()&&valid;++i)
+ {
+ valid= ValidateSelected((*i),
+ mSelectionMinDimension,
+ mSelectionMaxDimension );
+ }
+ }
+ else if(isSelection)
+ {
+ valid= ValidateSelected(sel.front(),
+ mSelectionMinDimension,
+ mSelectionMaxDimension );
+ }
+ else
+ {
+ ResetExtent();
+ std::vector<tree::Node*>::const_iterator i;
+ for(i=sel.begin();i!=sel.end()&&valid;++i)
+ {
+ valid= ValidateSelected((*i),
+ mSelectionMinDimension,
+ mSelectionMaxDimension );
+ }
+ }
+ mText->SetLabel(crea::std2wx(GetMessage()));
+ /*if(valid)
+ {
+ ReadImageThreaded(sel);
+ }
+ else
+ {
+ ClearSelection();
+ }*/
+ ReadImageThreaded(sel);
+ }
+
+ //==================================================
+
+ //==================================================
+ ///Reads Images (Threaded)
+ void WxGimmickView::ReadImageThreaded(const std::vector<tree::Node*>& sel)
+ {
+ GimmickDebugMessage(5,
+ "ReadImageThreaded"
+ <<std::endl);
+ int maxprio = GetMaximalPriority();
+ int prio = maxprio + 2000;
+
+ if(sel.size()>0)
+ {
+ //First load the selected images
+ mCurImageItemToShow = sel.front();
+ pointers.clear();
+ int index = 0;
+ std::vector<tree::Node*>::const_iterator selected;
+ for(selected=sel.begin();selected!=sel.end();++selected)
+ {
+ GimmickDebugMessage(5,
+ "Requesting image from selected "
+ <<(*selected)->GetAttribute("FullFileName")
+ <<std::endl);
+ //ImagePointerHolder* ph=new ImagePointerHolder(GetDefaultImage());
+ boost::shared_ptr<ImagePointerHolder> ph(new ImagePointerHolder(GetDefaultImage()));
+ pointers.push_back(ph);
+ RequestReading(*selected,prio,index,ph);
+ // AddEntryToMap(*selected);
+ prio--;
+ index++;
+ }
+ mViewer->SetImageVector(pointers);
+ //Going up
+ prio = maxprio + 20;
+ std::vector<tree::Node*> up;
+ GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->GetNodes(up,true);
+ std::vector<tree::Node*>::iterator iterUp;
+ for(iterUp=up.begin();iterUp!=up.end();++iterUp)
+ {
+ GimmickDebugMessage(5,
+ "Requesting image from neighbors up "
+ <<(*iterUp)->GetAttribute("FullFileName")
+ <<std::endl);
+// ImagePointerHolder* ph=new ImagePointerHolder(GetDefaultImage());
+ boost::shared_ptr<ImagePointerHolder> ph(new ImagePointerHolder(GetDefaultImage()));
+ RequestReading(*iterUp,prio,-1,ph);
+ // AddEntryToMap(*iterUp);
+ prio--;
+ if (prio == maxprio) break;
+ }
+
+ //Going down
+ prio = maxprio + 19;
+ std::vector<tree::Node*> down;
+ GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->GetNodes(down,false);
+ std::vector<tree::Node*>::iterator iterDown;
+ for(iterDown=down.begin();iterDown!=down.end();++iterDown)
+ {
+ GimmickDebugMessage(5,
+ "Requesting image from neighbors down "
+ <<(*iterDown)->GetAttribute("FullFileName")
+ <<std::endl);
+ //ImagePointerHolder* ph=new ImagePointerHolder(GetDefaultImage());
+ boost::shared_ptr<ImagePointerHolder> ph(new ImagePointerHolder(GetDefaultImage()));
+ RequestReading(*iterDown,prio,-1,ph);
+ // AddEntryToMap(*iterDown);
+ prio--;
+ if (prio == maxprio) break;
+ }
+ }
+ else
+ {
+ pointers.clear();
+ //ImagePointerHolder* ph=new ImagePointerHolder(GetDefaultImage());
+ boost::shared_ptr<ImagePointerHolder> ph(new ImagePointerHolder(GetDefaultImage()));
+ pointers.push_back(ph);
+ mViewer->SetImageVector(pointers);
+ }
+ }
+
+ //==================================================
+
+ //==================================================
+
+#if defined(WIN32)
+ //==================================================
+ void WxGimmickView::OnInternalIdle()
+ {
+ if (!mConstructed) return;
+ static bool first_time = true;
+ if (false)
+ {
+ first_time = false;
+ }
+ // GimmickMessage(1,"WxGimmickView : Refresh viewer"<<std::endl);
+ // mViewer->StartPlayer();
+ if(mViewer)
+ {
+ mViewer->RefreshIfNecessary();
+ }
+ }
+#else
+ void WxGimmickView::UpdateWindowUI(long flags)
+ {
+ if(mViewer)
+ {
+ mViewer->RefreshIfNecessary();
+ }
+ }
+#endif
+ //==================================================
+
+ //==================================================
+ void WxGimmickView::ClearSelection()
+ {
+ pointers.clear();
+ pointers.push_back(boost::shared_ptr<creaImageIO::ImagePointerHolder>(new ImagePointerHolder(GetDefaultImage())));
+ //pointers.push_back(new ImagePointerHolder(GetDefaultImage()));
+ mViewer->SetImageVector(pointers);
+ mViewer->RefreshIfNecessary();
+ ResetExtent();
+ }
+
+ //=================================================
+
+ //=================================================
+ void WxGimmickView::OnRemove(wxCommandEvent& event)
+ {
+ //TODO Select current tree handler
+ wxBusyCursor busy;
+ std::string remove;
+ mGimmick->GetSetting(SETTINGS_REMOVE_PATIENT_DISPLAY,remove);
+ GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->RemoveSelected(remove);
+ mGimmick->UpdateSetting(SETTINGS_REMOVE_PATIENT_DISPLAY,remove);
+ ClearSelection();
+ }
+ //=================================================
+
+
+ //=================================================
+ void WxGimmickView::AddIgnoreFile(tree::Node* toRemove)
+ {
+ mGimmick->RemoveFile(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),toRemove);
+ // GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->UpdateLevel(1);
+ }
+
+ //=================================================
+ void WxGimmickView::CopyFiles(const std::vector<std::string>& filenames)
+ {
+ mGimmick->CopyFiles(filenames, crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())));
+ wxMessageBox(std2wx("The selected files have been copied"),_T("Copy files"),wxOK,this);
+ }
+
+ //=================================================
+ void WxGimmickView::AddDir(std::string dirName)
+ {
+ mProgressDialog = new wxProgressDialog(_T("Adding directory"),_T(""),1000,this,wxPD_ELAPSED_TIME |wxPD_CAN_ABORT );
+ mCurrentDirectory = crea::std2wx(dirName);
+ mGimmick->AddDir(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),dirName,true);
+ mProgressDialog->Pulse(_T("Updating view..."));
+
+ UpdateTreeViewLevel(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),1);
+ delete mProgressDialog;
+ DisplayAddSummary();
+ }
+
+ //=================================================
+ void WxGimmickView::OnSynchronize(wxCommandEvent& event)
+ {
+ wxBusyCursor busy;
+ const wxString choices[] = { _T("Check database for files deletion and addition and give a report."),
+ _T("Check database for files deletion, addition and attributes change. Then give a report."),
+ _T("Repair database (remove deleted files and add new files)."),
+ _T("Repair database (remove deleted files, add new files and reset changed attributes).") } ;
+
+ wxSingleChoiceDialog dialog(this,
+ _T("Select one of the following synchronization actions:\n")
+ _T("Please note that, due to the heavy amount of operations required, this action might take a while."),
+ _T("Synchronization Settings"),
+ WXSIZEOF(choices), choices);
+
+ //dialog.SetSelection(0);
+
+ if (dialog.ShowModal() == wxID_OK)
+ {
+ wxBusyCursor busy;
+ int sel=dialog.GetSelection();
+ bool repair=false;
+ bool checkAttributes=false;
+ if(sel==2 || sel==3){repair=true;}
+ if(sel==1 || sel==3){checkAttributes=true;}
+ std::string mess=mGimmick->Synchronize(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),repair, checkAttributes);
+ wxMessageBox(std2wx(mess),_T("Synchronization result"),wxOK,this);
+ if(sel==2 || sel==3){
+ GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->UpdateLevel(1);
+ }
+
+ }
+ }
+ //=================================================
+
+ //=================================================
+ void WxGimmickView::OnSettings(wxCommandEvent& event)
+ {
+ wxDialog* dial= new wxDialog (this,-1,_T("System Settings"),wxDefaultPosition, wxSize(450,220));
+ wxBoxSizer *siz = new wxBoxSizer(wxVERTICAL);
+ // Notebook
+ wxNotebook* nb= new wxNotebook(dial, -1, wxDefaultPosition, wxDefaultSize, 0);
+
+ siz->Add( nb,1,wxGROW ,0);
+ CreateSettingsDialog(nb,dial);
+ dial->SetSizer(siz);
+ dial->ShowModal();
+ }
+
+ //=================================================
+ void WxGimmickView::OnImportExport(wxCommandEvent &Event)
+ {
+ wxBusyCursor busy;
+ // Test if one image is selected => export
+ // if not =>import
+ if (GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))])
+ {
+ ExportImages();
+ }
+ else
+ {
+ ImportImages();
+ }
+ }
+
+ void WxGimmickView::ExportImages()
+ {
+ //Archive selection: name, emplacement
+ //same process than copy local but to a zip
+ // if settings are yes "always ask for descriptor addition", ask
+ // if settings are yes, adding descriptor
+ }
+
+ void WxGimmickView::ImportImages()
+ {
+ //Find the *.zip
+ //dezip
+ // Contain a descriptor.text
+ // create a new database, and add to database
+ // if not, add to current database
+ //
+ }
+
+ //=================================================
+ //AndresDonadio
+ void WxGimmickView::OnTools(wxCommandEvent& event)
+ {
+ mViewer->StopPlayer();
+
+ wxDialog* dial = new wxDialog (this,-1,_T("Tools"),wxDefaultPosition, wxSize(550,350));
+
+ wxSizer* buttonsSizer = dial->CreateSeparatedButtonSizer(wxOK|wxCANCEL);
+ wxNotebook* nb= new wxNotebook(dial, -1, wxDefaultPosition, wxDefaultSize, 0);
+ wxBoxSizer *dialSizer = new wxBoxSizer(wxVERTICAL);
+ dialSizer->Add(nb,1,wxGROW,0);
+ dialSizer->Add(buttonsSizer,0,wxGROW);
+
+#if defined(BUILD_BRUKER)
+ //First page: Bruker Image Reader
+ WxGimmickTools * gimmickTools = new WxGimmickTools(nb, mCurrentDirectory);
+ nb->AddPage( gimmickTools, _T("Bruker Image Reader") );
+#endif
+
+ dial->SetSizer(dialSizer, true);
+ dial->Layout();
+ dial->ShowModal();
+
+ if (dial->GetReturnCode() == wxID_OK)
+ {
+#if defined(BUILD_BRUKER)
+ if (nb->GetSelection()==0)//Selection: Bruker Image Reader
+ {
+ std::string inputDir = crea::wx2std(gimmickTools->getInputDir());
+ std::string outputDir = crea::wx2std(gimmickTools->getOutputDir());
+
+ bool addToDB = gimmickTools->getAddToDBCheckBoxValue();
+
+ if (inputDir.compare("")!=0 && outputDir.compare("")!=0)
+ {
+ if ( wxMessageBox(_T("Depending on the amount of Data the process can take between 1 and 5 minutes. Do you want to continue?"),
+ _T("Please confirm"), wxICON_QUESTION | wxYES_NO) == wxYES )
+ {
+ Bruker2Dicom b2d;
+ b2d.SetInputDirectory(inputDir);
+ b2d.SetOutputDirectory(outputDir);
+ b2d.SetConvertModeToDicom();
+ b2d.verbose=false;
+ b2d.Execute();
+
+ if (addToDB)
+ {
+ mProgressDialog = new wxProgressDialog(_T("Adding directory"),_T(""),1000,this,wxPD_ELAPSED_TIME |wxPD_CAN_ABORT );
+ mCurrentDirectory = gimmickTools->getOutputDir();
+ mGimmick->AddDir(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),outputDir,true);
+ mProgressDialog->Pulse(_T("Updating view..."));
+
+ UpdateTreeViewLevel(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),1);
+ delete mProgressDialog;
+ DisplayAddSummary();
+ }
+ }
+ }
+ else
+ {
+ wxMessageBox(_T("One or both of the directory fields are empty"),_T("Empty Fields"),wxOK,this);
+ }
+ }
+ delete gimmickTools;
+#endif
+ }
+ mViewer->StartPlayer();
+ }
+
+ //=================================================
+
+ void WxGimmickView::CreateSettingsDialog(wxNotebook* nb, wxDialog* dial)
+ {
+ //First page: Customization of configurations
+ //Copy Path string
+ std::string cp;
+ mGimmick->GetSetting(SETTINGS_COPY_PATH,cp);
+ //Database Path String
+ std::string dp;
+ mGimmick->GetSetting(SETTINGS_DBPATH,dp);
+ //Syncronization Event String
+ std::string se;
+ mGimmick->GetSetting(SETTINGS_SYNC_EVENT,se);
+ //Syncronization Frequency String
+ std::string sf;
+ mGimmick->GetSetting(SETTINGS_SYNC_FREQ,sf);
+
+ WxCustomizeConfigPanel * customConfig=new WxCustomizeConfigPanel(nb,dial,this,cp,dp,se,sf);
+
+ nb->AddPage( customConfig, crea::std2wx("Customize Configuration") );
+
+ //Second page: Creation of Databases
+ /*wxPanel* databaseCreation=new wxPanel(nb);
+ nb->AddPage( databaseCreation, crea::std2wx("Create Database") );*/
+
+ //Second page (temporary): Connection to PACS
+ WxPACSConnectionPanel* pacs=new WxPACSConnectionPanel(nb,dial, this);
+ nb->AddPage( pacs, crea::std2wx("Connect to PACS") );
+
+ //Third page: CD/DVD Watch
+ WxListenerPanel* cdWatch=new WxListenerPanel(nb,dial, this,true);//, mListener->IsPaused());
+ nb->AddPage( cdWatch, crea::std2wx("CD/DVD") );
+
+ //Fourth page: Selection of attributes to show
+ std::vector<std::string> shown;
+ std::vector<std::string> nShown;
+ GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->GetAttributes(shown,nShown,1);
+ int nLev=GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->GetNumberOfLevels();
+ WxAttributeSelectionPanel* attSelection=new WxAttributeSelectionPanel(nb,dial,this,shown,nShown,nLev);
+ nb->AddPage( attSelection, crea::std2wx("Selection of Attributes") );
+ }
+
+ //===================================================================
+ void WxGimmickView::GetVisibleAttributes(std::vector<std::string>& shown,
+ std::vector<std::string>& nShown, int level)
+ {
+ GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->GetAttributes(shown,nShown,level);
+ }
+
+ //===================================================================
+ void WxGimmickView::OnAttributesChanged(const std::vector<std::string>& nShown, int level)
+ {
+ GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->SetNonVisibleAttributes(nShown,level);
+ std::vector<std::string> n=nShown;
+ GetTreeViewMap()[crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection()))]->CreateCtrl(n,level);
+ }
+ //===================================================================
+ void WxGimmickView::OnSaveSettingsCallback(const std::string& copyPath,
+ const std::string& dbPath,
+ const std::string& syncEvent,
+ const std::string& syncFreq)
+ {
+ mGimmick->UpdateSetting(SETTINGS_COPY_PATH,copyPath);
+ mGimmick->UpdateSetting(SETTINGS_DBPATH,dbPath);
+ mGimmick->UpdateSetting(SETTINGS_SYNC_EVENT,syncEvent);
+ mGimmick->UpdateSetting(SETTINGS_SYNC_FREQ,syncFreq);
+ }
+
+ //===================================================================
+ void WxGimmickView::OnListenerCallback(const std::string& drive, bool addFiles, bool removeFiles)
+ {
+ mListener->SetMonitoredDrive(drive);
+ mListener->SetAddFilesState(addFiles);
+ mListener->SetRemoveFilesState(removeFiles);
+ }
+
+ //========================================================================
+
+ void WxGimmickView::OnDriveMount(bool mount)
+ {
+ GimmickMessage(1, "Gimmick::OnDriveMount"<<std::endl);
+ std::string drive;
+ mListener->GetMonitoredDrive(drive);
+
+ if(mount)
+ {
+ mViewer->StopPlayer();
+ wxBusyCursor busy;
+ wxString title(_T("Adding drive"));
+ mProgressDialog =
+ new wxProgressDialog(_T("Adding drive"),
+ _T(""),
+ 1000,
+ this,
+ wxPD_ELAPSED_TIME |
+ // wxPD_ESTIMATED_TIME |
+ // wxPD_REMAINING_TIME |
+ wxPD_CAN_ABORT );
+ mCurrentDirectory = crea::std2wx(drive);
+ mGimmick->AddDir(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),drive,true);
+ mProgressDialog->Pulse(_T("Updating view..."));
+
+ UpdateTreeViewLevel(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),1);
+ delete mProgressDialog;
+ DisplayAddSummary();
+ mViewer->StartPlayer();
+ }
+ else
+ {
+ mGimmick->DeleteDrive(drive);
+ UpdateTreeViewLevel(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),1);
+ }
+ }
+
+ //========================================================================
+
+ void WxGimmickView::StartListeningThread()
+ {
+ mListener->Resume();
+ }
+
+ //========================================================================
+
+ void WxGimmickView::StopListeningThread()
+ {
+ mListener->Pause();
+ }
+
+ //========================================================================
+ void WxGimmickView::CreateEditFieldsDialog(tree::Node* node, std::vector<std::string> names, std::vector<std::string> keys)
+ {
+ wxDialog* dial= new wxDialog (this,-1,crea::std2wx("Edit Fields for node "+node->GetLabel()),wxDefaultPosition, wxSize(350,155));
+ wxBoxSizer *siz = new wxBoxSizer(wxVERTICAL);
+ WxEditFieldsPanel* ef = new WxEditFieldsPanel(dial, dial, this, node, names, keys);
+
+ siz->Add( ef,1,wxGROW ,0);
+ dial->SetSizer(siz);
+ dial->ShowModal();
+ }
+
+
+
+ //========================================================================
+ void WxGimmickView::DumpTags(std::string i_filename)
+ {
+ WxDumpPanel* pan= new WxDumpPanel (this,i_filename);
+ pan->ShowModal();
+ }
+
+ //========================================================================
+ void WxGimmickView::ExportToStorage(const std::vector<std::string> i_filenames)
+ {
+ std::vector<std::string> storages;
+ Gimmick::TreeHandlerMapType::iterator it = mGimmick->GetTreeHandlerMap().begin();
+ for(;it != mGimmick->GetTreeHandlerMap().end(); it++)
+ {
+ storages.push_back(it->first);
+ }
+
+ WxExportDlg* exp= new WxExportDlg(this,storages);
+ if ( exp->ShowModal() ==ID_EXPORT_OK)
+ {
+ std::string storage = exp->GetStorage();
+ mProgressDialog =
+ new wxProgressDialog(_T("Adding file(s)"),
+ _T(""),
+ 1000,
+ this,
+ wxPD_ELAPSED_TIME |
+ // wxPD_ESTIMATED_TIME |
+ // wxPD_REMAINING_TIME |
+ wxPD_CAN_ABORT );
+ mGimmick->AddFiles(storage,i_filenames);
+ mProgressDialog->Pulse(_T("Updating view..."));
+ UpdateTreeViewLevel(storage,1);
+ delete mProgressDialog;
+ DisplayAddSummary();
+ }
+ }
+
+
+
+ //========================================================================
+ void WxGimmickView::OnFieldsEdited(tree::Node* node, const std::string& name, const std::string& key, const std::string& val)
+ {
+ mGimmick->EditField(node, crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())), name, key, val);
+ UpdateTreeViewLevel(crea::wx2std(mNotebook->GetPageText(mNotebook->GetSelection())),1);
+ }
+
+ //=================================================
+ /// AddProgress Gimmick callback
+ void WxGimmickView::OnAddProgress( Gimmick::AddProgress& p)
+ {
+ char mess[200];
+ sprintf(mess,"%i dirs : %i files :\n %i handled - %i added",
+ p.GetNumberScannedDirs(),
+ p.GetNumberScannedFiles(),
+ p.GetNumberHandledFiles(),
+ p.GetNumberAddedFiles());
+ // std::cout << "OnAddProgress "<<mess<<std::endl;
+ wxString s(wxString::From8BitData(mess));
+ // std::cout << "Pulse"<<std::endl;
+ if (!mProgressDialog->Pulse(s))
+ {
+ p.SetStop();
+ }
+ // std::cout << "OnAddProgress ok"<<std::endl;
+ }
+ //=================================================
+
+ //=================================================
+ void WxGimmickView::DisplayAddSummary()
+ {
+ const Gimmick::AddProgress& p = mGimmick->GetAddProgress();
+ std::stringstream mess;
+ mess << "Dirs \tscanned\t: " << p.GetNumberScannedDirs() << "\n";
+ mess << "Files\tscanned\t: " << p.GetNumberScannedFiles() << "\n";
+ mess << "Files\thandled\t: " << p.GetNumberHandledFiles() << "\n\n";
+ mess << "Files\tadded \t: " << p.GetNumberAddedFiles() << "\n\n";
+ wxMessageBox(std2wx(mess.str()),_T("Addition result"),wxOK,this);
+ }
+
+ ////////////////////////////////////////////////
+ // Add a DB to application //
+ // @param event : WxEvent //
+ // @return : - //
+ ////////////////////////////////////////////////
+ void WxGimmickView::OnAddDB(wxCommandEvent& event)
+ {
+ //Select DB
+ long style = wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST;
+ std::string wc("*.sqlite3*");
+ wxFileDialog* FD = new wxFileDialog( 0,
+ _T("Select file"),
+ _T(""),
+ _T(""),
+ crea::std2wx(wc),
+ style,
+ wxDefaultPosition);
+
+ if (FD->ShowModal()==wxID_OK)
+ {
+ wxBusyCursor busy;
+ wxArrayString files;
+ FD->GetPaths(files);
+ std::stringstream st;
+ for(int i = 0; i< files.size(); i++)
+ {
+ //get name of DB (file name)
+ size_t pos = files[i].find_last_of(_T("\\"));
+ std::string name = crea::wx2std(files[i].substr(pos+1));
+ pos = name.find_last_of(".");
+ name = name.substr(0,pos);
+ //create TreeHandler
+ mGimmick->addDB(name, crea::wx2std(files[i]));
+ //create TreeView
+ CreateSingleTreeView(name);
+ }
+ }
+ }
+ ////////////////////////////////////////////////////
+ // Create a DB from an Attributes Descriptor files //
+ // @param event : WxEvent //
+ // @return : - //
+ //////////////////////////////////////////////////
+ void WxGimmickView::OnCreateDB(wxCommandEvent& event)
+ {
+ // PACSConnection("");
+ WxDescriptorPanel * DescriptorPan = new WxDescriptorPanel(this, mGimmick->GetHomeDirectory());
+ DescriptorPan->Layout();
+ if ( DescriptorPan->ShowModal() == ID_DSCP_APPLY)
+ {
+ wxBusyCursor busy;
+ std::string file(DescriptorPan->GetDescriptor());
+ if (!file.empty())
+ {
+ size_t pos = file.find_last_of("\\");
+ std::string name = file.substr(pos+1);
+ std::string directory = file.substr(0,pos);
+ pos = name.find_last_of(".");
+ name = name.substr(0,pos);
+ //get directory to store DB
+ directory += "\\" + name + ".sqlite3";
+ //create createDB
+ mGimmick->createDB(name, file,directory);
+ //create TreeHandler
+ mGimmick->addDB(name, directory);
+ //create TreeView
+ CreateSingleTreeView(name);
+ }
+ }
+ }
+
+ //=================================================
+
+ //=================================================
+ BEGIN_EVENT_TABLE(WxGimmickView, wxPanel)
+ EVT_TOOL(TOOL_CREATEDB_ID, WxGimmickView::OnCreateDB)
+ EVT_TOOL(TOOL_ADDFILES_ID, WxGimmickView::OnAddFiles)
+ EVT_TOOL(TOOL_ADDDIR_ID, WxGimmickView::OnAddDir)
+ EVT_TOOL(TOOL_ADDDATABASE_ID, WxGimmickView::OnAddDB)
+ EVT_TOOL(TOOL_REMOVE_ID, WxGimmickView::OnRemove)
+ EVT_TOOL(TOOL_SYNCHRONIZE_ID, WxGimmickView::OnSynchronize)
+ EVT_TOOL(TOOL_SETTINGS_ID, WxGimmickView::OnSettings)
+ EVT_TOOL(TOOL_TOOLS_ID, WxGimmickView::OnTools)
+ END_EVENT_TABLE()
+ //=================================================
+
+} // EO namespace creaImageIO
+
--- /dev/null
+#ifndef __creaImageIOWxGimmickView_h_INCLUDED__
+#define __creaImageIOWxGimmickView_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+
+#include <creaImageIOGimmickView.h>
+#include <creaImageIOWxViewer.h>
+#include <creaImageIOWxGimmickTools.h>
+#include <creaImageIOListener.h>
+#include <creaWx.h>
+
+#include "wx/progdlg.h"
+
+#include "wx/wx.h"
+#include <wx/splitter.h>
+#include <wx/toolbar.h>
+#include <wx/tbarbase.h>
+#include <wx/notebook.h>
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup View
+ */
+ //=====================================================================
+
+ //=====================================================================
+ /// Concrete derivative of GimmickView which implements a wxWidgets-based view
+
+ class WxGimmickView : public wxPanel, virtual public GimmickView
+ {
+ public:
+
+ typedef int EventType;
+
+ /// Ctor
+ WxGimmickView(boost::shared_ptr<Gimmick>,
+ wxWindow *parent,
+ const wxWindowID id,
+ const wxPoint& pos, const wxSize& size,
+ int min_dim = GIMMICK_2D_IMAGE_SELECTION,
+ int max_dim = GIMMICK_3D_IMAGE_SELECTION,
+ int number_of_threads = 0);
+ /// Virtual destructor
+ virtual ~WxGimmickView();
+
+ /// Returns the selected files
+ ///(overloaded from GimmickView)
+ void GetSelectedFiles(std::vector<std::string>& s);
+
+ /// Returns the selected Images so that they comply with the
+ /// given parameter(4D) (overloaded from GimmickView)
+ void GetSelectedImages(std::vector<vtkImageData*>& s, int dim);
+ void GetSelectedImagesInVector(std::vector<vtkImageData*>& s, int dim);
+
+ /// Returns the images indicated by the filenames in the vector
+ /// so that they comply with the given parameter(dim)
+ //(overloaded from GimmickView)
+ void GetImages(int dim, const std::vector<std::string>& files,
+ std::vector<vtkImageData*>& s);
+
+ /// Callback called when a selection from a TreeView has changed
+ //(overloaded from GimmickView)
+ void OnSelectionChange(const std::vector<tree::Node*>& s,
+ bool isSelection, int selection, bool mProcess);
+ ///Stops the player
+ void StopPlayer(){mViewer->StopPlayer();}
+ ///Adds a file to ignore
+ void AddIgnoreFile(tree::Node* toRemove);
+ ///Resets the default image
+ void ClearSelection();
+ ///Copies selected files
+ void CopyFiles(const std::vector<std::string>& filenames);
+ ///Add selected files to the Database
+ void AddDir(std::string dirName);
+
+
+ ///Sends a request to read the currently selected node and the ones that surround it.
+ void ReadImageThreaded(const std::vector<tree::Node*>& sel);
+
+ ///Saves the settings to the file
+ void OnSaveSettingsCallback(const std::string& copyPath,
+ const std::string& dbPath,
+ const std::string& syncEvent,
+ const std::string& syncFreq);
+
+ ///Changes listener state
+ void OnListenerCallback(const std::string& drive, bool addFiles, bool removeFiles);
+
+ ///Acts upon a drive mount
+ void OnDriveMount(bool mount);
+
+ ///Starts the listening thread on the CD/DVD drive
+ void StartListeningThread();
+
+ ///Stops the listening thread on the CD/DVD drive
+ void StopListeningThread();
+
+ ///Called upon when a field has been edited
+ void OnFieldsEdited(tree::Node* node, const std::string& name, const std::string& key, const std::string& val);
+
+ ///Called upon to return the visible attributes of the current tab
+ void GetVisibleAttributes(std::vector<std::string>& shown,std::vector<std::string>& nShown, int level);
+
+ ///Called when there has been a change in the visible attributes of a tree view
+ void OnAttributesChanged(const std::vector<std::string>& nShown, int level);
+
+ protected:
+ /// Creates the tool bar
+ void CreateToolBar();
+
+ /// Create the tree view for TreeHandler provided
+ /// (overloaded from GimmickView)
+ void CreateTreeView( TreeHandler* );
+
+
+ private:
+ wxBoxSizer *mbottom_sizer;
+ wxBoxSizer *msizer;
+ /// Is set to true at the end of constructor
+ /// (in order to lock callbacks from threaded objects or event
+ /// before everything is ok)
+ bool mConstructed;
+ /// The ToolBar and the tools
+ wxToolBar* mToolBar;
+ wxToolBarToolBase* mToolAddFile;
+ wxToolBarToolBase* mToolAddDir;
+ wxToolBarToolBase* mToolRemove;
+ wxToolBarToolBase* mToolAddDatabase;
+ wxToolBarToolBase* mToolHelp;
+ wxToolBarToolBase* mToolSynchronize;
+ wxToolBarToolBase* mToolSettings;
+ wxToolBarToolBase* mToolTools;
+
+ wxSplitterWindow* mSplitter;
+ wxPanel* mBottomPanel;
+ wxStaticText * mText;
+ wxNotebook* mNotebook;
+
+ /// The list of icons
+ wxImageList * mIcon;
+ void CreateIconList();
+
+ boost::shared_ptr<Gimmick> mGimmick;
+
+ Listener* mListener;
+
+ /// Callback for adding files
+ void OnAddFiles(wxCommandEvent& event);
+
+ /// Callback for adding dir
+ void OnAddDir(wxCommandEvent& event);
+
+ /// Callback for removing files
+ void OnRemove(wxCommandEvent& event);
+
+ /// Callback for synchronization
+ void OnSynchronize(wxCommandEvent& event);
+
+ /// Callback for settings edition
+ void OnSettings(wxCommandEvent& event);
+
+ /// Callback for settings edition
+ void OnTools(wxCommandEvent& event);
+
+ /// Callback for Import/Export images
+ void OnImportExport(wxCommandEvent& event);
+
+ // Import Images from an archive
+ void ImportImages();
+
+ //Export Images to an archive
+ void ExportImages();
+
+ ///Creates the settings dialog (the pages inside and the information)
+ void CreateSettingsDialog(wxNotebook* nb, wxDialog* dial);
+
+ /// Display a message box with the last addition statistics
+ void DisplayAddSummary();
+
+ /// Test a directory to know if contains sub-directory to analyze
+ bool isNeedRecursive(std::string i_name);
+
+ /// Determines number of files potentially to add to database
+ int NumberFilesToAdd(const std::string &dirpath, bool recursive);
+
+ /// AddProgress Gimmick callback
+ void OnAddProgress( Gimmick::AddProgress& );
+
+#if defined(WIN32)
+ /// Called upon to refresh the viewer once there are no actions to be done
+ void OnInternalIdle();
+#else
+ void UpdateWindowUI(long flags = wxUPDATE_UI_NONE);
+#endif
+ /// callback to add a database
+ void OnAddDB(wxCommandEvent& event);
+
+ ///Create a DB from an Attributes Descriptor files
+ void OnCreateDB(wxCommandEvent& event);
+
+ std::string ExtractName(const std::string &i_name);
+
+ ///Edits the fields of a given node
+ void CreateEditFieldsDialog(tree::Node* node, std::vector<std::string> names, std::vector<std::string> keys);
+
+
+ /// Display all Dicom Tags
+ void DumpTags(const std::string i_filename);
+
+ /// Export from Storage to Storage
+ void ExportToStorage(const std::vector<std::string> i_filenames);
+
+ /// Progress dialog
+ wxProgressDialog* mProgressDialog;
+
+ ///The selection's maximum dimension
+ int mSelectionMaxDimension;
+
+ ///The selection's minimum dimension
+ int mSelectionMinDimension;
+
+ ///Image previewer
+ WxViewer* mViewer;
+
+ ///Currently Displayed Node
+ tree::Node* mCurImageItemToShow;
+
+ //Pointer holders for images to be shown
+ std::vector< boost::shared_ptr<ImagePointerHolder> > pointers;
+
+ wxString mCurrentDirectory;
+
+ DECLARE_EVENT_TABLE()
+ };
+ // EO class WxGimmickView
+ //=====================================================================
+
+} // EO namespace creaImageIO
+
+#endif // USE_WIDGETS
+// EOF
+#endif
--- /dev/null
+#include <creaImageIOWxListenerPanel.h>
+#include <creaImageIOSystem.h>
+
+namespace creaImageIO
+{
+ // CTor
+ WxListenerPanel::WxListenerPanel(wxWindow *parent, wxDialog* dial, WxGimmickView* view, bool stat)
+ : wxPanel( parent,
+ -1, wxDefaultPosition,
+ wxDefaultSize,
+ wxRESIZE_BORDER |
+ wxSYSTEM_MENU |
+ wxCLOSE_BOX |
+ wxMAXIMIZE_BOX |
+ wxMINIMIZE_BOX |
+ wxCAPTION
+ ),
+ dialog(dial),
+ mView(view)
+ {
+ GimmickDebugMessage(1,"WxListener::WxListener"
+ <<std::endl);
+ state=stat;
+ wxStaticText * cp=new wxStaticText(this,-1,_T(" Drive to monitor: "), wxPoint(5,15));
+ const wxString choices[] = { _T("D:"),
+ _T("E:"),
+ _T("F:"),
+ _T("G:") };
+ drives=new wxComboBox(this, -1,crea::std2wx("E:"),wxPoint(100, 10),wxDefaultSize,4,choices);
+
+ addCheckBox = new wxCheckBox(this, -1, _T("Automatically add images to the database when CD/DVD is mounted?"), wxPoint(5,45) );
+ addCheckBox->SetValue(true);
+ removeCheckBox = new wxCheckBox(this, -1, _T("Automatically remove images from the database when CD/DVD is unmounted?"), wxPoint(5,75) );
+ removeCheckBox->SetValue(true);
+ Connect( addCheckBox->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED, (wxObjectEventFunction) &WxListenerPanel::OnAdd );
+ Connect( removeCheckBox->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED, (wxObjectEventFunction) &WxListenerPanel::OnRemove );
+ std::string name;
+ if (state){name="Start Monitoring Drive";}
+ else {name="Stop Monitoring Drive";}
+ wxButton *start = new wxButton(this,wxID_ANY,crea::std2wx(name), wxPoint(5,110) );
+ Connect( start->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxListenerPanel::OnChangeThreadState );
+
+ wxButton *save = new wxButton(this,wxID_ANY,_T("Save Changes"), wxPoint(130,110) );
+ Connect( save->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxListenerPanel::OnChangeListenState );
+
+
+ //topsizer->Add( mView,1,wxGROW,0);
+
+// SetSizer( topsizer );
+ Layout();
+ }
+
+ /// Destructor
+ WxListenerPanel::~WxListenerPanel()
+ {
+ GimmickDebugMessage(1,"WxCustomizeConfigPanel::~WxCustomizeConfigPanel"
+ <<std::endl);
+ }
+
+ void WxListenerPanel::OnAdd(wxCommandEvent& event)
+ {
+ addFiles = addCheckBox->GetValue();
+ }
+
+ void WxListenerPanel::OnRemove(wxCommandEvent& event)
+ {
+ removeFiles = removeCheckBox->GetValue();
+ }
+
+ void WxListenerPanel::OnChangeListenState(wxCommandEvent& event)
+ {
+ mView->OnListenerCallback(crea::wx2std(drives->GetValue()),addFiles, removeFiles);
+ dialog->Destroy();
+ }
+
+ void WxListenerPanel::OnChangeThreadState(wxCommandEvent& event)
+ {
+ if(state)
+ {
+ mView->StartListeningThread();
+ }
+ else
+ {
+ mView->StopListeningThread();
+ }
+ dialog->Destroy();
+ }
+
+//======================================================================
+
+//======================================================================
+
+} // EO namespace creaImageIO
+
+
--- /dev/null
+#ifndef __creaImageIOWxListenerPanel_h_INCLUDED__
+#define __creaImageIOWxListenerPanel_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+#include <creaWx.h>
+#include <creaImageIOWxGimmickView.h>
+
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup GUI
+ */
+ //=====================================================================
+ //=====================================================================
+ class WxListenerPanel : public wxPanel
+ {
+ public:
+ WxListenerPanel();
+ WxListenerPanel(wxWindow *parent,
+ wxDialog* dial,
+ WxGimmickView* view,
+ bool stat);
+
+ ~WxListenerPanel();
+ ///Saves the configuration
+ void OnChangeListenState(wxCommandEvent& event);
+ ///Changes the thread state(start/stop)
+ void OnChangeThreadState(wxCommandEvent& event);
+ ///Changes the state of the add boolean
+ void OnAdd(wxCommandEvent& event);
+ ///Changes the state of the remove boolean
+ void OnRemove(wxCommandEvent& event);
+
+
+ private :
+ bool addFiles;
+ bool removeFiles;
+ bool state;
+ wxCheckBox* addCheckBox;
+ wxCheckBox* removeCheckBox;
+ wxComboBox* drives;
+ wxDialog* dialog;
+ WxGimmickView* mView;
+
+
+ }; // class WxListener
+ //=====================================================================
+
+
+} // EO namespace creaImageIO
+
+
+#endif // USE_WIDGETS
+// EOF
+#endif
+
+
--- /dev/null
+#include <creaImageIOWxPACSConnectionPanel.h>
+#include <creaImageIOPACSConnection.h>
+#include <creaImageIOSystem.h>
+
+namespace creaImageIO
+{
+ // CTor
+ WxPACSConnectionPanel::WxPACSConnectionPanel(wxWindow *parent, wxDialog* dial, WxGimmickView* view)
+ : wxPanel( parent,
+ -1, wxDefaultPosition,
+ wxDefaultSize,
+ wxRESIZE_BORDER |
+ wxSYSTEM_MENU |
+ wxCLOSE_BOX |
+ wxMAXIMIZE_BOX |
+ wxMINIMIZE_BOX |
+ wxCAPTION
+ ),
+ dialog(dial),
+ mView(view)
+ {
+ GimmickDebugMessage(1,"WxPACSConnectionPanel::WxPACSConnectionPanel"
+ <<std::endl);
+ wxStaticText * dicId=new wxStaticText(this,-1,_T(" DICOM Identification: "), wxPoint(5,5));
+ wxStaticText * aet=new wxStaticText(this,-1,_T(" AETitle: "), wxPoint(5,25));
+ aeTitle=new wxTextCtrl(this, wxID_ANY, _T("MyAeTitle"), wxPoint(75,25), wxSize(220,20));
+
+ wxStaticText * pn=new wxStaticText(this,-1,_T(" Port Number: "), wxPoint(5,53));
+ pNumber=new wxTextCtrl(this, wxID_ANY, _T("3306"), wxPoint(75,50), wxSize(220,20));
+ wxStaticText * adv1=new wxStaticText(this,-1,_T(" (1 - 131072) "), wxPoint(300,53));
+
+ wxStaticText * ad=new wxStaticText(this,-1,_T(" Address: "), wxPoint(5,80));
+ address=new wxTextCtrl(this, wxID_ANY, _T("localhost"), wxPoint(75,75), wxSize(220,20));
+
+ wxButton *query = new wxButton(this,wxID_ANY,_T("Query PACS Server"), wxPoint(5,110) );
+ Connect( query->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxPACSConnectionPanel::OnQueryPACS );
+
+ Layout();
+ }
+
+ /// Destructor
+ WxPACSConnectionPanel::~WxPACSConnectionPanel()
+ {
+ GimmickDebugMessage(1,"WxPACSConnectionPanel::~WxPACSConnectionPanel"
+ <<std::endl);
+ }
+
+ void WxPACSConnectionPanel::OnQueryPACS(wxCommandEvent& event)
+ {
+ PACSConnection* pc=new PACSConnection(crea::wx2std(aeTitle->GetValue()));
+ //mView->OnListenerCallback(crea::wx2std(drives->GetValue()),addFiles, removeFiles);
+ //dialog->Destroy();
+ }
+
+//======================================================================
+
+//======================================================================
+
+} // EO namespace creaImageIO
+
+
--- /dev/null
+#ifndef __creaImageIOWxPACSConnectionPanel_h_INCLUDED__
+#define __creaImageIOWxPACSConnectionPanel_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+#include <creaWx.h>
+#include <creaImageIOWxGimmickView.h>
+
+
+namespace creaImageIO
+{
+ /**
+ * \ingroup GUI
+ */
+ //=====================================================================
+ //=====================================================================
+ class WxPACSConnectionPanel : public wxPanel
+ {
+ public:
+ WxPACSConnectionPanel();
+ WxPACSConnectionPanel(wxWindow *parent,
+ wxDialog* dial,
+ WxGimmickView* view);
+
+ ~WxPACSConnectionPanel();
+ ///Queries the PACS
+ void OnQueryPACS(wxCommandEvent& event);
+
+ private :
+ wxTextCtrl* aeTitle;
+ wxTextCtrl* pNumber;
+ wxTextCtrl* address;
+ wxDialog* dialog;
+ WxGimmickView* mView;
+
+
+ }; // class WxPACSConnectionPanel
+ //=====================================================================
+
+
+} // EO namespace creaImageIO
+
+
+#endif // USE_WIDGETS
+// EOF
+#endif
+
+
--- /dev/null
+#include <creaImageIOWxTreeView.h>
+#include <creaImageIOGimmickView.h>
+#include <creaImageIOSystem.h>
+#include <wx/splitter.h>
+#include <wx/gdicmn.h>
+#include <boost/date_time/gregorian/gregorian.hpp>
+#include <creaImageIOGimmick.h>
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+//=====================================================================
+namespace creaImageIO
+{
+
+ //=====================================================================
+}
+//=====================================================================
+
+//=====================================================================
+///Comparing function for ordering algorithm. Takes parameters as strings.
+int wxCALLBACK CompareFunctionStrings(long item1, long item2, long sortData)
+{
+ creaImageIO::ItemData* data1 = (creaImageIO::ItemData*)item1;
+ creaImageIO::ItemData* data2 = (creaImageIO::ItemData*)item2;
+
+ const std::string& s1(*(data1->attr));
+ const std::string& s2(*(data2->attr));
+ if(sortData==1)
+ {
+ // inverse the order
+ if (s1 < s2)
+ return 1;
+ if (s1 > s2)
+ return -1;
+
+ return 0;
+ }
+ else
+ {
+ if (s1 < s2)
+ return -1;
+ if (s1 > s2)
+ return 1;
+
+ return 0;
+
+ }
+}
+//=====================================================================
+
+//=====================================================================
+///Comparing function for ordering algorithm. Takes parameters as ints.
+int wxCALLBACK CompareFunctionInts(long item1, long item2, long sortData)
+{
+ creaImageIO::ItemData* data1 = (creaImageIO::ItemData*)item1;
+ creaImageIO::ItemData* data2 = (creaImageIO::ItemData*)item2;
+
+ const std::string& s1(*(data1->attr));
+ const std::string& s2(*(data2->attr));
+
+ int val1=atoi(s1.c_str());
+ int val2=atoi(s2.c_str());
+
+ if(sortData==1)
+ {
+ // inverse the order
+ if (val1 < val2)
+ return 1;
+ if (val1 > val2)
+ return -1;
+
+ return 0;
+ }
+ else
+ {
+ if (val1 < val2)
+ return -1;
+ if (val1 > val2)
+ return 1;
+
+ return 0;
+
+ }
+
+}
+
+//=====================================================================
+
+
+//=====================================================================
+namespace creaImageIO
+{
+ //=====================================================================
+ // CTor
+ WxTreeView::WxTreeView(TreeHandler* handler,
+ GimmickView* gimmick,
+ wxWindow* parent,
+ const wxWindowID id)
+ : wxPanel(parent,id),
+ TreeView(handler, gimmick)
+ {
+ GimmickDebugMessage(1,"WxTreeView::WxTreeView"
+ <<std::endl);
+
+
+ // Split part below toolbar into notebook for views and panel
+ // for preview, messages...
+ // TO DO : Splitter
+ // mSplitter = new wxSplitterWindow( this , -1);
+
+ // Global sizer
+ msizer = new wxBoxSizer(wxHORIZONTAL);
+
+ int ctrl_style = wxLC_REPORT | wxLC_VRULES;
+ int col_style = wxLIST_FORMAT_LEFT;
+
+ // Creating the ListCtrl for the levels > 0 (not for Root level)
+ for (int i = 0;
+ i < handler->GetTree().GetNumberOfLevels() -1;
+ ++i)
+ {
+ GimmickDebugMessage(5,"Creating view for level "<<i
+ <<std::endl);
+ LevelType level;
+ level.SelectedUpToDate = true;
+ level.SortColumn = 0;
+
+ // If the first level : parent = this
+ wxWindow* sparent = this;
+ // else parent = last splitter
+ if (i>0)
+ sparent = mLevelList[i-1].wxSplitter;
+
+ level.wxSplitter = new wxSplitterWindow( sparent , -1);
+ if(i!=0)
+ {
+ level.wxSplitter->Show(false);
+ }
+ // level.wxSplitter->SetMinimumPaneSize(100);
+
+ wxListCtrl* ctrl = new wxListCtrl(level.wxSplitter,
+ i,
+ wxDefaultPosition,
+ wxDefaultSize,
+ ctrl_style);
+ level.wxCtrl = ctrl;
+ level.wxSplitter->Initialize(ctrl);
+
+ // Create the columns : one for each attribute of the level
+ int col = 0;
+ std::string title;
+
+ tree::LevelDescriptor::AttributeDescriptorListType::const_iterator a;
+ for (a = handler->GetTree().GetAttributeDescriptorList(i+1).begin();
+ a != handler->GetTree().GetAttributeDescriptorList(i+1).end();
+ ++a)
+
+{
+
+ GimmickDebugMessage(5,"Creating column "<<col<<" : "
+ <<a->GetName()
+ <<std::endl);
+
+ if(a->GetFlags()!=creaImageIO::tree::AttributeDescriptor::PRIVATE)
+ {
+
+ if(a->GetName()=="UNKNOWN")
+ {
+ title = "#";
+ title += handler->GetTree().GetLevelDescriptor(i+1).GetName();
+ if (title[title.size()-1]!='s')
+ title += "s";
+
+ }
+ else
+ {
+ title=a->GetName();
+ }
+ std::string temp = a->GetKey();
+ if (temp.compare("ID") != 0)
+ {
+
+ ctrl->InsertColumn(col,
+ crea::std2wx(title),
+ col_style);
+ col++;
+ }
+ level.key.push_back(a->GetKey());
+ }
+
+ }
+
+ mLevelList.push_back(level);
+ }
+
+#if wxUSE_MENUS
+
+ // Column Menu
+ menu =new wxMenu;
+ wxMenuItem* m1=menu->Append(wxID_ANY, _T("&Sort ascending"));
+ wxMenuItem* m2=menu->Append(wxID_ANY, _T("&Sort descending"));
+ wxMenuItem* m3=menu->Append(wxID_ANY, _T("&Filter"));
+ mAscendingID=m1->GetId();
+ mDescendingID=m2->GetId();
+ mFilterID=m3->GetId();
+ Connect( mAscendingID, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(WxTreeView::OnPopupSort) );
+ Connect( mDescendingID, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(WxTreeView::OnPopupSort) );
+ Connect( mFilterID, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(WxTreeView::OnPopupFilter) );
+
+
+ ////SubMenuItem EXPORT
+ subExportMenu = new wxMenu;
+ wxMenuItem *subExp1 = subExportMenu->Append(wxID_ANY, _T("&Export to Storage"));
+ Connect( subExp1->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(WxTreeView::OnExportToStorage) );
+
+ //ItemMenu
+ menuItem =new wxMenu;
+
+
+ wxMenuItem* m2Item=menuItem->Append(wxID_ANY, _T("&Local Copy"));
+ wxMenuItem* m3Item=menuItem->Append(wxID_ANY, _T("&Edit Fields"));
+ wxMenuItem* m4Item=menuItem->Append(wxID_ANY, _T("&Display Dicom Tags"));
+ menuItem->AppendSubMenu(subExportMenu, wxT("&Export"));
+
+#if defined(USE_GDCM_ANOM)
+ wxMenuItem* m1Item=menuItem->Append(wxID_ANY, _T("&Anonymize"));
+ mAnonymizingID=m1Item->GetId();
+ Connect( mAnonymizingID, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(WxTreeView::OnAnonymize) );
+#endif
+ mLocalCopyID=m2Item->GetId();
+ mEditFieldID=m3Item->GetId();
+ mDumpID=m4Item->GetId();
+
+
+ Connect( mLocalCopyID, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(WxTreeView::OnLocalCopy) );
+ Connect( mEditFieldID, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(WxTreeView::OnEditField) );
+ Connect( mDumpID, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(WxTreeView::OnDumpTags) );
+
+
+
+#endif // wxUSE_MENUS
+ /// Initialize the first level splitter
+
+ msizer->Add( mLevelList[0].wxSplitter ,1, wxGROW ,0);
+ // mColumnSelected=1;
+ mLastSelected=0;
+ mLastLevel=0;
+ // mDirection=true;
+
+ mIgnoreSelectedChanged = false;
+
+ //CreateColorPalette();
+ UpdateLevel(1);
+
+ SetSizer( msizer );
+ SetAutoLayout(true);
+ Layout();
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ /// Destructor
+ WxTreeView::~WxTreeView()
+ {
+ GimmickDebugMessage(1,"WxTreeView::~WxTreeView"
+ <<std::endl);
+ delete menu;
+ delete menuItem;
+
+ }
+ //=====================================================================
+
+
+
+ //=====================================================================
+ const std::vector<tree::Node*>& WxTreeView::GetSelected(int level)
+ {
+ std::vector<tree::Node*>& sel = mLevelList[0].Selected;
+ // if (GetSelectedUpToDate(level))
+ int l = level - 1;
+ // the selection of upper level
+ if(mLevelList.size() == level -1)
+ sel = mLevelList.back().Selected;
+ else
+ sel= mLevelList[l].Selected;
+ if (sel.size() > 0)
+ {
+ sel.clear();
+ }
+ if (level == 1)
+ {
+ sel.push_back(GetTreeHandler()->GetTree().GetTree());
+ }
+ else if (level < mLevelList.size()+2 )
+ {
+ long item = -1;
+ for ( ;; )
+ {
+ item = GetCtrl(l-1)->GetNextItem(item,
+ wxLIST_NEXT_ALL,
+ wxLIST_STATE_SELECTED);
+ if ( item == -1 )
+ break;
+ long adr = GetCtrl(l-1)->GetItemData(item);
+ tree::Node* n = ((ItemData*)adr)->node;
+ if(mLastSelected==item)
+ {
+ std::vector<tree::Node*>::iterator it;
+ it = sel.begin();
+ it = sel.insert ( it , n );
+ }
+ else
+ {
+
+ sel.push_back(n);
+ }
+
+ }
+ /*int n = GetCtrl(l-1)->GetItemCount();
+ for (int i = 0; i<n; i++)
+ {
+ std::cout<<GetCtrl(l-1)->GetItemState(i,wxLIST_STATE_SELECTED)<<std::endl;
+ if ( GetCtrl(l-1)->GetItemState(i,wxLIST_STATE_SELECTED))
+ {
+ long adr = GetCtrl(l-1)->GetItemData(i);
+ tree::Node* n = ((ItemData*)adr)->node;
+ if(mLastSelected==i)
+ {
+ std::vector<tree::Node*>::iterator it;
+ it = sel.begin();
+ it = sel.insert ( it , n );
+ }
+ else
+ {
+
+ sel.push_back(n);
+ }
+ }
+ }*/
+ }
+ else
+ {
+ // NOTHING
+ }
+
+ // return mLevelList[level-1].Selected;
+ return sel;
+ }
+
+ //=====================================================================
+
+ //=====================================================================
+ ///Removes selected nodes on last selected level
+ // NOT SPECIFIC
+ void WxTreeView::RemoveSelected(std::string &i_save)
+ {
+ bool erase=false;
+
+ unsigned int tempLevel = mLastLevel;
+ mLastLevel+=1;
+ const std::vector<tree::Node*>& sel=GetSelected(mLastLevel+1);
+ // if no selection, no remove action.
+ if(sel.size() != 0)
+ {
+
+ std::stringstream out;
+ std::string levelName=GetTreeHandler()->GetTree().GetLevelDescriptor(mLastLevel).GetName();
+ out<<"Delete ";
+ out<<sel.size();
+ if(sel.size()>1&&levelName.at(levelName.size()-1)!='s')
+ {
+ out<<" "<<levelName;
+ out<<"s?";
+ }
+ else
+ {
+ out<<" "<<GetTreeHandler()->GetTree().GetLevelDescriptor(mLastLevel).GetName()<<"?";
+ }
+ if (wxMessageBox(crea::std2wx(out.str()),
+ _T("Remove Files"),
+ wxYES_NO,this ) == wxYES)
+ {
+ erase = true;
+ }
+ if(erase)
+ {
+ GetGimmickView()->modifyValidationSignal(false);
+ bool needRefresh=false;
+ std::vector<tree::Node*>::const_iterator i;
+ for (i=sel.begin(); i!=sel.end(); ++i)
+ {
+ GimmickMessage(1,
+ "deleting '"
+ <<(*i)->GetLabel()
+ <<"'"<<mLastLevel
+ <<std::endl);
+ if((*i)->GetParent()->GetNumberOfChildren()<2)
+ {
+ needRefresh=true;
+ }
+ //tree::Node* n = new (tree::Node*)(*i);
+ GetTreeHandler()->LoadChildren((*i),4);
+ GetGimmickView()->AddIgnoreFile(*i);
+ GetTreeHandler()->Remove(*i);
+ }
+
+ if(needRefresh && mLastLevel>1)
+ {
+ UpdateLevel(mLastLevel-2);
+ }
+ else if(mLastLevel>1)
+ {
+ UpdateLevel(mLastLevel-1);
+ }
+ else
+ {
+ UpdateLevel(mLastLevel);
+ }
+ }
+ }
+ else
+ {
+ // no need to incremente level
+ mLastLevel = tempLevel;
+ }
+
+ if (erase && mLastLevel == 1 && i_save == "0")
+ {
+
+ RemoveAlertDlg *dial = new RemoveAlertDlg(this, crea::std2wx("Remove files"), wxSize(370,100));
+ //dial->ShowModal();
+ if (dial->ShowModal() == wxID_OK)
+ {
+ i_save = dial->isChecked() == false? "0" : "1";
+ }
+
+ }
+ }
+
+
+ //=====================================================================
+ /// Updates a level of the view (adds or removes children, etc.)
+ void WxTreeView::UpdateLevel( int level )
+ {
+ GimmickDebugMessage(1,
+ GetTreeHandler()->GetTree().GetLabel()
+ <<"WxTreeView::UpdateLevel(level "
+ <<level
+ <<")"
+ <<std::endl);
+
+ wxBusyCursor busy;
+ RecursiveUpdateLevel(level);
+ int i;
+ for (i=0; i<level-1; i++)
+ {
+ if (!GetSplitter(i)->IsSplit())
+ GetSplitter(i)->SplitVertically( GetCtrl(i), GetSplitter(i+1),
+ 100 );
+ }
+ if (GetSplitter(i)->IsSplit()) GetSplitter(i)->Unsplit();
+
+ }
+ //=====================================================================
+
+ //=====================================================================
+ /// Recursive method called upon by UpdateLevel to refresh all windows
+ void WxTreeView::RecursiveUpdateLevel( int level )
+ {
+ GimmickDebugMessage(1,
+ GetTreeHandler()->GetTree().GetLabel()
+ <<"WxTreeView::RecursiveUpdateLevel(level "
+ <<level
+ <<")"<<std::endl);
+
+
+ const std::vector<tree::Node*>& sel(GetSelected(level));
+
+ int l = level - 1;
+
+ // to speed up inserting we hide the control temporarily
+ GetCtrl(l)->Hide();
+ GetCtrl(l)->DeleteAllItems();
+
+ std::vector<tree::Node*>::const_iterator i;
+
+ for (i=sel.begin(); i!=sel.end(); ++i)
+ {
+ GimmickDebugMessage(1,
+ "adding children of '"
+ <<(*i)->GetLabel()
+ <<"'"
+ <<std::endl);
+ int _id=0;
+
+ //Adds items and sets their attributes
+
+ GetTreeHandler()->LoadChildren(*i,1);
+ tree::Node::ChildrenListType::reverse_iterator j;
+ for (j = (*i)->GetChildrenList().rbegin();
+ j!= (*i)->GetChildrenList().rend();
+ ++j)
+ {
+ GimmickDebugMessage(1,
+ "adding children "
+ <<(*j)->GetLabel()
+ <<"'"
+ <<std::endl);
+
+ wxListItem item;
+ item.SetMask(wxLIST_MASK_STATE |
+ wxLIST_MASK_TEXT |
+ // wxLIST_MASK_IMAGE |
+ wxLIST_MASK_DATA |
+ // wxLIST_MASK_WIDTH |
+ wxLIST_MASK_FORMAT
+ );
+
+ ItemData* data = new ItemData();
+ data->node = *j;
+ data->id = _id;
+
+ item.SetId(_id);
+ item.SetData(data);
+
+ _id++;
+ GetCtrl(l)->InsertItem(item);
+
+ //Setting attributes
+ for (int k=0; k<GetCtrl(l)->GetColumnCount(); ++k)
+ {
+ std::string val;
+ // Temporary correction : it works but no explanation about the problem FCY
+
+ if(k==0 && level <3)
+ {
+ val = (*j)->GetAttribute("NumberOfChildren");
+ }
+ else
+ val = (*j)->GetAttribute(mLevelList[l].key[k]);
+ if(((*j)->GetAttributeDescriptor(mLevelList[l].key[k])).isDateEntry()) // Date
+ {
+ // std::cout << "["<<val<< "]" << std::endl;
+ std::string valtmp(val);
+ try
+ {
+ boost::gregorian::date d1(boost::gregorian::from_undelimited_string(val));
+ val = to_iso_extended_string(d1);
+ }
+ catch (...)
+ {
+ val = valtmp;
+ }
+ // std::cout << "["<<val<< "]" << std::endl;
+ }
+ else if(((*j)->GetAttributeDescriptor(mLevelList[l].key[k])).isTimeEntry()) // Time
+ {
+ if ((val.size()>6) &&
+ (val != "" || val != " "))
+ val = val.substr(0,2) + " : "
+ + val.substr(2,2) + " : "
+ + val.substr(4,2);
+ }
+ else
+ {
+ if (val.size()==0) val = "?";
+ }
+ if (val.size()==0) val = "X";
+ item.SetText( crea::std2wx(val));
+ item.SetColumn(k);
+
+ GetCtrl(l)->SetItem(item);
+ }
+ item.Clear();
+
+ }
+ }
+
+ SortLevel(l);
+ GetCtrl(l)->Show();
+ }
+ //=====================================================================
+
+
+ //================================================================
+ void WxTreeView::OnItemDeSelected(wxListEvent& event)
+ {
+ GimmickDebugMessage(1,
+ GetTreeHandler()->GetTree().GetLabel()
+ <<" WxTreeView::OnItemDeselected"<<std::endl);
+ // retrieve the level
+ wxObject* obj = event.GetEventObject();
+ unsigned int level = 0;
+ for (level = 0; level<mLevelList.size(); ++level)
+ {
+ if ( GetCtrl(level) == obj ) break;
+ }
+ SetSelectedUpToDate(level,false);
+ // to allow a first selection in images TreeView
+ if (level==mLevelList.size()-1)
+ OnItemSelected(event);
+ }
+ //================================================================
+
+ //================================================================
+ void WxTreeView::OnItemSelected(wxListEvent& event)
+ {
+
+ GimmickDebugMessage(1,
+ GetTreeHandler()->GetTree().GetLabel()
+ <<" WxTreeView::OnItemSelected"<<std::endl);
+
+ if (mIgnoreSelectedChanged)
+ {
+ GimmickDebugMessage(1,
+ " mIgnoreSelectedChanged true: returning"
+ <<std::endl);
+ return;
+ }
+
+
+
+ wxListItem info;
+ info.m_itemId = event.m_itemIndex;
+ mLastSelected = event.m_itemIndex;
+ // retrieve the level
+ wxObject* obj = event.GetEventObject();
+ unsigned int level = 0;
+ for (level = 0; level<mLevelList.size(); ++level)
+ {
+ if ( GetCtrl(level) == obj ) break;
+ }
+ mLastLevel=level;
+ GimmickDebugMessage(1,
+ " Level "<<level+1
+ <<std::endl);
+
+ // Update the children level (if selection not at last level)
+ if (level<mLevelList.size()-1)
+ {
+
+ UpdateLevel( level + 2 );
+ // Reset the viewer setting the default image
+ GetGimmickView()->ClearSelection();
+ }
+ // Select all images if the selection is at series level
+ if (level==mLevelList.size()-2)
+ SelectAll(level+1);
+ // Validate selected images if the selection is at image level
+ if (level==(mLevelList.size()-1)) //&&mProcess)
+ {
+ if(event.GetEventType()==wxEVT_COMMAND_LIST_ITEM_SELECTED)
+ {
+ ValidateSelectedImages (true);
+ }
+ else
+ {
+ ValidateSelectedImages (false);
+ }
+ }
+
+ }
+ //================================================================
+
+ //================================================================
+ void WxTreeView::SelectAll(int level)
+ {
+ long item = -1;
+ // int level=mLevelList.size()-1;
+ for ( ;; )
+ {
+ item = GetCtrl(level)->GetNextItem(item,
+ wxLIST_NEXT_ALL);
+ if ( item == -1 )
+ break;
+
+ if(item==(GetCtrl(level)->GetItemCount()-1))
+ {
+ mIgnoreSelectedChanged = false;//mProcess=true;
+ }
+ else
+ {
+ mIgnoreSelectedChanged = true;// mProcess=false;
+ }
+ GetCtrl(level)->SetItemState(item,wxLIST_STATE_SELECTED, wxLIST_MASK_STATE
+ | wxLIST_MASK_TEXT |wxLIST_MASK_IMAGE | wxLIST_MASK_DATA | wxLIST_MASK_WIDTH | wxLIST_MASK_FORMAT);
+ }
+ }
+
+ //================================================================
+ //================================================================
+
+ void WxTreeView::OnColClick(wxListEvent& event)
+ {
+ mColumnSelected = event.m_col;
+ wxPoint clientpt;
+ clientpt.x = wxGetMousePosition().x - this->GetScreenPosition().x;
+ clientpt.y = wxGetMousePosition().y - this->GetScreenPosition().y;
+ senderCtrl = event.GetEventObject();
+ unsigned int level = 0;
+ for (level = 0; level<mLevelList.size(); ++level)
+ {
+ if ( GetCtrl(level) == senderCtrl ) break;
+ }
+ PopupMenu(menu, clientpt);
+
+ }
+
+ //================================================================
+ //================================================================
+
+ void WxTreeView::OnItemMenu(wxListEvent &event)
+ {
+ wxPoint clientpt;
+ clientpt.x = wxGetMousePosition().x - this->GetScreenPosition().x;
+ clientpt.y = wxGetMousePosition().y - this->GetScreenPosition().y;
+ senderCtrl = event.GetEventObject();
+ unsigned int level = 0;
+ for (level = 0; level<mLevelList.size(); ++level)
+ {
+ if ( GetCtrl(level) == senderCtrl ) break;
+ }
+ long* ptr=0;
+ int flag;
+ mLastRightLevel=level;
+ mLastRightSelected=GetCtrl(level)->HitTest(wxPoint(0,clientpt.y-8),flag,ptr);
+ PopupMenu(menuItem, clientpt);
+
+ }
+
+ //================================================================
+ //================================================================
+
+ void WxTreeView::OnPopupFilter(wxCommandEvent& event)
+ {
+ wxBusyCursor busy;
+ GimmickDebugMessage(7,
+ "WxTreeView::OnEndLabelEdit"
+ <<std::endl);
+ unsigned int level = 0;
+ for (level = 0; level<mLevelList.size(); ++level)
+ {
+ if ( GetCtrl(level) == senderCtrl ) break;
+ }
+ std::string filter = crea::wx2std(wxGetTextFromUser(_T("Enter the filter to apply"), _T("Filter On Column")));
+
+ std::string att;
+
+ long it = -1;
+ UpdateLevel(level+1);
+
+ std::vector<long> items;
+ bool in=false;
+ int del=0;
+ for ( ;; )
+ {
+ it = GetCtrl(level)->GetNextItem(it,
+ wxLIST_NEXT_ALL);
+ if ( it == -1 )
+ break;
+
+ long adr = GetCtrl(level)->GetItemData(it);
+ tree::Node* nod = ((ItemData*)adr)->node;
+ att=(*nod).GetAttribute(mLevelList[level].key[mColumnSelected]);
+
+
+ if(att.find(filter)>900)
+ {
+
+ if(!in)
+ {
+ in=true;
+ }
+ else
+ {
+ del+=1;
+ }
+
+ items.push_back(it-del);
+ }
+
+ }
+ std::vector<long>::iterator iter;
+ for(iter=items.begin();iter!=items.end();++iter)
+ {
+ GetCtrl(level)->DeleteItem(*iter);
+ }
+ GetGimmickView()->ClearSelection();
+ }
+ //================================================================
+
+ //================================================================
+ void WxTreeView::OnPopupSort(wxCommandEvent& event)
+ {
+ wxBusyCursor busy;
+ unsigned int level = 0;
+ for (level = 0; level<mLevelList.size(); ++level)
+ {
+ if ( GetCtrl(level) == senderCtrl ) break;
+ }
+ mLevelList[level].SortColumn = mColumnSelected;
+
+ if(event.GetId()==mAscendingID)
+ {
+ mLevelList[level].SortAscending = true;
+ }
+ else if(event.GetId()==mDescendingID)
+ {
+ mLevelList[level].SortAscending = false;
+ }
+
+ SortLevel(level);
+ }
+ //================================================================
+
+ void WxTreeView::OnAnonymizer(wxCommandEvent &event)
+ {
+ wxBusyCursor busy;
+ std::vector<std::string> filesname;
+ std::vector<tree::Node*> nodes;
+// nodes.push_back(((ItemData*)GetCtrl(mLastRightLevel)->GetItemData(mLastRightSelected))->node);
+ if(nodes.size() != 0)
+ {
+ GetFilenamesAsString(nodes,filesname);
+ // GetGimmickView()->Anonymize(filesname,0);
+ }
+
+ }
+
+ //================================================================
+ void WxTreeView::OnLocalCopy(wxCommandEvent& event)
+ {
+ wxBusyCursor busy;
+
+ unsigned int tempLevel = mLastLevel;
+ mLastLevel+=1;
+ const std::vector<tree::Node*>& sel=GetSelected(mLastLevel+1);
+
+ if(sel.size() != 0)
+ {
+ bool copy=false;
+ std::stringstream out;
+ std::string levelName=GetTreeHandler()->GetTree().GetLevelDescriptor(mLastLevel).GetName();
+ out<<"Copy ";
+ out<<sel.size();
+ if(sel.size()>1&&levelName.at(levelName.size()-1)!='s')
+ {
+ out<<" "<<levelName;
+ out<<"s to .gimmick?";
+ }
+ else
+ {
+ out<<" "<<GetTreeHandler()->GetTree().GetLevelDescriptor(mLastLevel).GetName()<<" to .gimmick?";
+ }
+ if (wxMessageBox(crea::std2wx(out.str()),
+ _T("Remove Files"),
+ wxYES_NO,this ) == wxYES)
+ {
+ copy = true;
+ }
+ if(copy)
+ {
+ std::vector<std::string> s;
+ GetFilenamesAsString(sel,s);
+ GetGimmickView()->CopyFiles(s);
+ }
+ }
+ else
+ {
+ mLastLevel = tempLevel;
+ }
+
+
+ }
+ //================================================================
+
+ //================================================================
+ void WxTreeView::OnEditField(wxCommandEvent& event)
+ {
+ if(mLastRightSelected!=-1)
+ {
+ tree::Node* node=((ItemData*)GetCtrl(mLastRightLevel)->GetItemData(mLastRightSelected))->node;
+ tree::LevelDescriptor::AttributeDescriptorListType::const_iterator a;
+ std::vector<std::string> names;
+ std::vector<std::string> keys;
+ for (a = GetTreeHandler()->GetTree().GetAttributeDescriptorList(mLastRightLevel+1).begin();
+ a != GetTreeHandler()->GetTree().GetAttributeDescriptorList(mLastRightLevel+1).end();
+ ++a)
+ {
+ if(a->GetFlags()==creaImageIO::tree::AttributeDescriptor::EDITABLE)
+ {
+ names.push_back(a->GetName());
+ keys.push_back(a->GetKey());
+ }
+ }
+ GetGimmickView()->CreateEditFieldsDialog(node,names,keys);
+ }
+ }
+
+ //================================================================
+
+ //================================================================
+
+ void WxTreeView::OnExportToStorage(wxCommandEvent &event)
+ {
+ std::vector<std::string> filesname;
+ std::vector<tree::Node*> nodes;
+ nodes.push_back(((ItemData*)GetCtrl(mLastRightLevel)->GetItemData(mLastRightSelected))->node);
+ GetFilenamesAsString(nodes,filesname);
+ GetGimmickView()->ExportToStorage(filesname);
+ }
+
+ //================================================================
+
+ //================================================================
+
+ void WxTreeView::OnDumpTags(wxCommandEvent &event)
+ {
+ if(mLastRightSelected!=-1)
+ {
+ tree::Node* node=((ItemData*)GetCtrl(mLastRightLevel)->GetItemData(mLastRightSelected))->node;
+ tree::LevelDescriptor::AttributeDescriptorListType::const_iterator a;
+ std::vector<std::string> names;
+ std::vector<std::string> keys;
+ for (a = GetTreeHandler()->GetTree().GetAttributeDescriptorList(mLastRightLevel+1).begin();
+ a != GetTreeHandler()->GetTree().GetAttributeDescriptorList(mLastRightLevel+1).end();
+ ++a)
+ {
+ if(a->GetKey()=="FullFileName")
+ {
+ GetGimmickView()->DumpTags(node->GetAttribute("FullFileName"));
+ return;
+ }
+ }
+ }
+ }
+
+
+ //================================================================
+
+ //================================================================
+ void WxTreeView::SortLevel(int level)
+ {
+ GimmickDebugMessage(1,
+ "WxTreeView::SortLevel("
+ <<level<<")"
+ <<std::endl);
+ //Obtain the column name and the level that needs to be organized
+
+ // int l = level - 1;
+ //Sets the data for the items to be sorted
+ // std::string att;
+ unsigned int ty=0;
+ int nbselected = 0;
+ int n = GetCtrl(level)->GetItemCount();
+ for (int i = 0; i < n; i++)
+ {
+
+ //Gets current item data
+ ItemData* data = (ItemData*)GetCtrl(level)->GetItemData(i);
+
+ //Extracts the node and the type of attribute
+ tree::Node* nod = data->node;
+ if(i==0)
+ {
+ (*nod).GetAttributeDescriptor
+ (mLevelList[level].key[mLevelList[level].SortColumn])
+ .DecodeType( ty );
+ }
+ //Obtains the organizing attribute
+ data->attr = & (*nod).GetAttribute
+ (mLevelList[level].key[mLevelList[level].SortColumn]);
+ //Selected ?
+ data->selected = false;
+ if (GetCtrl(level)->GetItemState(i,wxLIST_STATE_SELECTED)>0)
+ {
+ data->selected = true;
+ nbselected++;
+ }
+
+ }
+ GimmickDebugMessage(1,
+ "WxTreeView::OnSort : "
+ <<nbselected<<" selected before sorting"
+ <<std::endl);
+
+ mIgnoreSelectedChanged = true;
+ //
+ if (mLevelList[level].SortAscending)
+ {
+
+ if(ty==1)
+ {
+ GetCtrl(level)->SortItems(CompareFunctionInts, 0);
+ }
+ else
+ {
+ GetCtrl(level)->SortItems(CompareFunctionStrings, 0);
+ }
+
+ }
+ else
+ {
+ if(ty==1)
+ {
+ GetCtrl(level)->SortItems(CompareFunctionInts, 1);
+ }
+ else
+ {
+ GetCtrl(level)->SortItems(CompareFunctionStrings, 1);
+ }
+ }
+
+
+ // Reselects the unselected
+ n = GetCtrl(level)->GetItemCount();
+ int after = 0;
+ for (int i = 0; i < n; i++)
+ {
+
+ //Gets current item data
+ ItemData* data = (ItemData*)GetCtrl(level)->GetItemData(i);
+
+ // long item = -1;
+ // for ( ;; )
+ // {
+ // item = GetCtrl(level)->GetNextItem(item,wxLIST_NEXT_ALL);
+ // if ( item == -1 ) break;
+ //Gets current item data
+ // ItemData* data = (ItemData*)GetCtrl(level)->GetItemData(item);
+ // was selected ?
+
+ if (data->selected)
+ {
+ nbselected--;
+ if (nbselected==0)
+ {
+ // if it is the last one we must process the selection
+ mIgnoreSelectedChanged = false;
+ }
+ GetCtrl(level)->SetItemState(i,
+ wxLIST_STATE_SELECTED,
+ wxLIST_MASK_STATE
+ | wxLIST_MASK_TEXT
+ | wxLIST_MASK_IMAGE
+ | wxLIST_MASK_DATA
+ | wxLIST_MASK_WIDTH
+ | wxLIST_MASK_FORMAT);
+ }
+ if (GetCtrl(level)->GetItemState(i,wxLIST_STATE_SELECTED)>0)
+ {
+ after++;
+ }
+
+
+ }
+ mIgnoreSelectedChanged = false;
+ GimmickDebugMessage(1,
+ "WxTreeView::SortLevel : "
+ <<after<<" selected after sorting"
+ <<std::endl);
+
+ }
+ //================================================================
+
+
+ //================================================================
+ void WxTreeView::ValidateSelectedImages(bool isSelection)
+ {
+ GimmickDebugMessage(7,
+ "WxTreeView::ValidateSelectedImages"
+ <<std::endl);
+ const std::vector<tree::Node*>& sel(GetSelected(mLevelList.size()+1));
+ GetGimmickView()->OnSelectionChange(sel,
+ isSelection,(mLastSelected-1),
+ !mIgnoreSelectedChanged);
+
+ }
+ //================================================================
+
+
+ //================================================================
+ void WxTreeView::GetNodes(std::vector<tree::Node*>& nodes, bool direction)
+ {
+ long item = mLastSelected;
+ int level=mLevelList.size()-1;
+ //Gets current item data
+ long adr = GetCtrl(level)->GetItemData(item);
+ //Extracts the node
+ tree::Node* nod = ((ItemData*)adr)->node;
+ for ( ;; )
+ {
+ if(direction)
+ {
+ item = GetCtrl(level)->GetNextItem(item,
+ wxLIST_NEXT_ABOVE);
+ }
+ else
+ {
+ item = GetCtrl(level)->GetNextItem(item,
+ wxLIST_NEXT_BELOW);
+ }
+ if ( item == -1 || item==0 )
+ {
+ break;
+ }
+ if(GetCtrl(level)->GetItemState(item, wxLIST_STATE_SELECTED)==0 )
+ {
+
+ adr = GetCtrl(level)->GetItemData(item);
+ nod = ((ItemData*)adr)->node;
+ nodes.push_back(nod);
+ }
+ }
+
+ }
+ //================================================================
+ //=================================================
+ void WxTreeView::OnKeyDown(wxListEvent &event)
+ {
+ if(event.GetKeyCode() == WXK_DELETE)
+ {
+ wxBusyCursor busy;
+ std::string temp = "0";
+ RemoveSelected(temp);
+ GetGimmickView()->ClearSelection();
+ }
+
+ }
+ //================================================================
+
+ //================================================================
+ // Should be in another place : not specific !
+ void WxTreeView::GetSelectedAsString(std::vector<std::string>&s)
+ {
+ int level=mLevelList.size();
+ const std::vector<tree::Node*>& sel=GetSelected(level+1);
+ std::vector<tree::Node*>::const_iterator i;
+
+ for (i=sel.begin(); i!=sel.end(); ++i)
+ {
+ std::string filename=(*i)->GetAttribute("FullFileName");
+ s.push_back(filename);
+ }
+ }
+
+ //================================================================
+ void WxTreeView::GetFilenamesAsString(const std::vector<tree::Node*>& nodes, std::vector<std::string>&s)
+ {
+ std::vector<tree::Node*>::const_iterator i;
+
+ for (i=nodes.begin(); i!=nodes.end(); ++i)
+ {
+ if((*i)->GetLevel()<mLevelList.size())
+ {
+ GetTreeHandler()->LoadChildren(*i,0);
+ GetFilenamesAsString((*i)->GetChildrenList(),s);
+ }
+ else
+ {
+ std::string filename=(*i)->GetAttribute("FullFileName");
+ s.push_back(filename);
+ }
+ }
+ }
+
+ //================================================================
+
+ //================================================================
+ void WxTreeView::GetAttributes(std::vector<std::string>& areShown, std::vector<std::string>& notShown, int level)
+ {
+ areShown.clear();
+ notShown.clear();
+ tree::LevelDescriptor::AttributeDescriptorListType::const_iterator a;
+ for (a = GetTreeHandler()->GetTree().GetAttributeDescriptorList(level).begin();
+ a != GetTreeHandler()->GetTree().GetAttributeDescriptorList(level).end();
+ ++a)
+ {
+ if(a->GetFlags()==creaImageIO::tree::AttributeDescriptor::EDITABLE && IsAttributeVisible(a->GetName(),level))
+ {
+ areShown.push_back(a->GetName());
+ }
+ }
+ notShown=mLevelList[level-1].notShownAtts;
+ }
+
+ //================================================================
+ void WxTreeView::SetNonVisibleAttributes(const std::vector<std::string>& notShown, int nlevel)
+ {
+ mLevelList[nlevel].notShownAtts=notShown;
+ }
+
+ //================================================================
+ void WxTreeView::CreateCtrl(std::vector<std::string>& notShown, int nlevel)
+ {
+ int ctrl_style = wxLC_REPORT | wxLC_VRULES;
+ int col_style = wxLIST_FORMAT_LEFT;
+ LevelType level;
+ mLevelList[nlevel].SelectedUpToDate = true;
+ mLevelList[nlevel].SortColumn = 0;
+ mLevelList[nlevel].key.clear();
+
+ mLevelList[nlevel].wxCtrl = new wxListCtrl(mLevelList[nlevel].wxSplitter,
+ nlevel,
+ wxDefaultPosition,
+ wxDefaultSize,
+ ctrl_style);
+ wxWindow* oldWin=mLevelList[nlevel].wxSplitter->GetWindow1();
+ mLevelList[nlevel].wxSplitter->ReplaceWindow(oldWin,mLevelList[nlevel].wxCtrl);
+ mLevelList[nlevel].wxSplitter->Initialize(mLevelList[nlevel].wxCtrl);
+
+ // Create the columns : one for each attribute of the level
+ int col = 0;
+ std::string title;
+
+ tree::LevelDescriptor::AttributeDescriptorListType::const_iterator a;
+ for (a = GetTreeHandler()->GetTree().GetAttributeDescriptorList(nlevel+1).begin();
+ a != GetTreeHandler()->GetTree().GetAttributeDescriptorList(nlevel+1).end();
+ ++a)
+
+ {
+ if(a->GetFlags()!=creaImageIO::tree::AttributeDescriptor::PRIVATE && IsAttributeVisible(a->GetName(),nlevel+1))
+ {
+ title=a->GetName();
+ std::string temp = a->GetKey();
+ if (temp.compare("ID") != 0)
+ {
+ mLevelList[nlevel].wxCtrl->InsertColumn(col,
+ crea::std2wx(title),
+ col_style);
+ col++;
+ }
+ mLevelList[nlevel].key.push_back(a->GetKey());
+ }
+
+ }
+ oldWin->Destroy();
+ UpdateLevel(1);
+ }
+
+ //================================================================
+ bool WxTreeView::IsAttributeVisible(const std::string& val, int level)
+ {
+ std::vector<std::string> ns=mLevelList[level-1].notShownAtts;
+ std::vector<std::string>::iterator it;
+ bool found=false;
+ for(it=ns.begin();it!=ns.end()&&!found;++it)
+ {
+ if(val.compare(*it)==0)
+ {
+ found=true;
+ }
+ }
+
+ return !found;
+ }
+ //================================================================
+ //================================================================
+
+ RemoveAlertDlg::RemoveAlertDlg(wxWindow *parent,
+ wxString title,
+ const wxSize& size)
+ : wxDialog( parent,
+ wxID_ANY,
+ title,
+ wxDefaultPosition,
+ size,
+ wxDEFAULT_DIALOG_STYLE)
+ {
+ wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
+
+ //std::string out("To reload deleted patient, you should synchronize your database before."); // JPR
+ //wxTextCtrl *text = new wxTextCtrl(this, wxID_ANY,crea::std2wx(out),wxDefaultPosition, wxSize(500,20));
+ wxTextCtrl *text = new wxTextCtrl(this, wxID_ANY,
+ _T("To reload deleted patient, you should synchronize your database before."),
+ wxDefaultPosition, wxSize(500,20));
+ mcheck = new wxCheckBox(this, 5478, _T("Do not display this warning again!"));
+ Connect( mcheck->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED , (wxObjectEventFunction) &RemoveAlertDlg::onCheck );
+ wxSizer* buttonsSizer = this->CreateSeparatedButtonSizer(wxOK|wxCANCEL);
+
+ topsizer->Add(text);
+ topsizer->Add(mcheck,0,wxGROW);
+ topsizer->Add(buttonsSizer,0,wxGROW);
+ SetSizer(topsizer, true);
+ mSave = false;
+ Layout();
+ }
+ RemoveAlertDlg::~RemoveAlertDlg(){};
+ bool RemoveAlertDlg::isChecked()
+ {
+ return mSave;
+ }
+ void RemoveAlertDlg::onCheck(wxCommandEvent &Event)
+ {
+ mSave = mcheck->IsChecked();
+ }
+
+
+ //================================================================
+ //================================================================
+ BEGIN_EVENT_TABLE(WxTreeView, wxPanel)
+ /*
+ EVT_SIZE(MyFrame::OnSize)
+
+ EVT_MENU(LIST_QUIT, MyFrame::OnQuit)
+ EVT_MENU(LIST_ABOUT, MyFrame::OnAbout)
+ EVT_MENU(LIST_LIST_VIEW, MyFrame::OnListView)
+ EVT_MENU(LIST_REPORT_VIEW, MyFrame::OnReportView)
+ EVT_MENU(LIST_ICON_VIEW, MyFrame::OnIconView)
+ EVT_MENU(LIST_ICON_TEXT_VIEW, MyFrame::OnIconTextView)
+ EVT_MENU(LIST_SMALL_ICON_VIEW, MyFrame::OnSmallIconView)
+ EVT_MENU(LIST_SMALL_ICON_TEXT_VIEW, MyFrame::OnSmallIconTextView)
+ EVT_MENU(LIST_VIRTUAL_VIEW, MyFrame::OnVirtualView)
+ EVT_MENU(LIST_SMALL_VIRTUAL_VIEW, MyFrame::OnSmallVirtualView)
+
+ EVT_MENU(LIST_FOCUS_LAST, MyFrame::OnFocusLast)
+ EVT_MENU(LIST_TOGGLE_FIRST, MyFrame::OnToggleFirstSel)
+ EVT_MENU(LIST_DESELECT_ALL, MyFrame::OnDeselectAll)
+ EVT_MENU(LIST_SELECT_ALL, MyFrame::OnSelectAll)
+ EVT_MENU(LIST_DELETE, MyFrame::OnDelete)
+ EVT_MENU(LIST_ADD, MyFrame::OnAdd)
+ EVT_MENU(LIST_EDIT, MyFrame::OnEdit)
+ EVT_MENU(LIST_DELETE_ALL, MyFrame::OnDeleteAll)
+ EVT_MENU(LIST_SORT, MyFrame::OnSort)
+ EVT_MENU(LIST_SET_FG_COL, MyFrame::OnSetFgColour)
+ EVT_MENU(LIST_SET_BG_COL, MyFrame::OnSetBgColour)
+ EVT_MENU(LIST_TOGGLE_MULTI_SEL, MyFrame::OnToggleMultiSel)
+ EVT_MENU(LIST_SHOW_COL_INFO, MyFrame::OnShowColInfo)
+ EVT_MENU(LIST_SHOW_SEL_INFO, MyFrame::OnShowSelInfo)
+ EVT_MENU(LIST_FREEZE, MyFrame::OnFreeze)
+ EVT_MENU(LIST_THAW, MyFrame::OnThaw)
+ EVT_MENU(LIST_TOGGLE_LINES, MyFrame::OnToggleLines)
+ EVT_MENU(LIST_MAC_USE_GENERIC, MyFrame::OnToggleMacUseGeneric)
+
+ EVT_UPDATE_UI(LIST_SHOW_COL_INFO, MyFrame::OnUpdateShowColInfo)
+ EVT_UPDATE_UI(LIST_TOGGLE_MULTI_SEL, MyFrame::OnUpdateToggleMultiSel)
+END_EVENT_TABLE()
+
+BEGIN_EVENT_TABLE(MyListCtrl, wxListCtrl)
+ EVT_LIST_BEGIN_DRAG(LIST_CTRL, MyListCtrl::OnBeginDrag)
+ EVT_LIST_BEGIN_RDRAG(LIST_CTRL, MyListCtrl::OnBeginRDrag)
+
+ EVT_LIST_BEGIN_LABEL_EDIT(-1, WxTreeView::OnBeginLabelEdit)
+ EVT_LIST_END_LABEL_EDIT(-1, WxTreeView::OnEndLabelEdit)
+
+ EVT_LIST_DELETE_ITEM(LIST_CTRL, MyListCtrl::OnDeleteItem)
+ EVT_LIST_DELETE_ALL_ITEMS(LIST_CTRL, MyListCtrl::OnDeleteAllItems)
+#if WXWIN_COMPATIBILITY_2_4
+ EVT_LIST_GET_INFO(LIST_CTRL, MyListCtrl::OnGetInfo)
+ EVT_LIST_SET_INFO(LIST_CTRL, MyListCtrl::OnSetInfo)
+#endif
+ */
+ EVT_LIST_KEY_DOWN(-1, WxTreeView::OnKeyDown)
+ EVT_LIST_ITEM_SELECTED(-1, WxTreeView::OnItemSelected)
+ EVT_LIST_ITEM_RIGHT_CLICK(-1, WxTreeView::OnItemMenu)
+ EVT_LIST_ITEM_DESELECTED(-1, WxTreeView::OnItemDeSelected)
+ /*
+ EVT_LIST_KEY_DOWN(LIST_CTRL, MyListCtrl::OnListKeyDown)
+ EVT_LIST_ITEM_ACTIVATED(LIST_CTRL, MyListCtrl::OnActivated)
+ EVT_LIST_ITEM_FOCUSED(LIST_CTRL, MyListCtrl::OnFocused)
+*/
+ EVT_LIST_COL_RIGHT_CLICK(-1, WxTreeView::OnColClick)
+
+ EVT_LIST_COL_CLICK(-1, WxTreeView::OnColClick)
+
+ //EVT_LEFT_DOWN(WxTreeView::OnMouseClick)
+ /*
+ EVT_LIST_COL_BEGIN_DRAG(LIST_CTRL, MyListCtrl::OnColBeginDrag)
+ EVT_LIST_COL_DRAGGING(LIST_CTRL, MyListCtrl::OnColDragging)
+ EVT_LIST_COL_END_DRAG(LIST_CTRL, MyListCtrl::OnColEndDrag)
+
+ EVT_LIST_CACHE_HINT(LIST_CTRL, MyListCtrl::OnCacheHint)
+
+#if USE_CONTEXT_MENU
+ EVT_CONTEXT_MENU(MyListCtrl::OnContextMenu)
+#endif
+ EVT_CHAR(MyListCtrl::OnChar)
+
+ EVT_RIGHT_DOWN(MyListCtrl::OnRightClick)
+ */
+END_EVENT_TABLE()
+
+} // EO namespace creaImageIO
+
--- /dev/null
+#ifndef __creaImageIOWxTreeView_h_INCLUDED__
+#define __creaImageIOWxTreeView_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+
+#include <creaImageIOTreeView.h>
+#include <creaWx.h>
+
+#include <wx/listctrl.h>
+#include <wx/splitter.h>
+//#include <vector>
+const std::string empty_string("");
+namespace creaImageIO
+{
+
+ //=====================================================================
+ /// Data stored by the list items
+ struct ItemData
+ {
+ ItemData() : node(0), id(-1), attr(&empty_string) {}
+ // The corresponding Node
+ tree::Node* node;
+ // The id ?
+ int id;
+ // The pointer on the current attribute string to sort on
+ const std::string* attr;
+ // Was the item previously selected ?
+ // Useful for reselecting the item after sort
+ bool selected;
+ };
+ /**
+ * \ingroup View
+ */
+ //=====================================================================
+
+ //=====================================================================
+ /// Abstract class that handles the view of a Tree through its TreeHandler
+ class WxTreeView : public wxPanel, virtual public TreeView
+ {
+ public:
+ /// Ctor
+ WxTreeView(TreeHandler*, GimmickView*,
+ wxWindow* parent, const wxWindowID id);
+ /// Virtual destructor
+ virtual ~WxTreeView();
+
+
+ /// Updates the view of a level given the selected items of upper level
+ virtual void UpdateLevel( int );
+
+ ///Removes selected nodes on given level
+ virtual void RemoveSelected(std::string &i_save);
+
+ ///Returns the last selected level
+ virtual unsigned int GetLastSelectedLevel(){return mLastLevel;}
+
+ /// Callback for item selection
+ void OnItemSelected(wxListEvent& event);
+
+ /// Callback for item deselection
+ void OnItemDeSelected(wxListEvent& event);
+
+ /// Callback for column click
+ void OnColClick(wxListEvent& event);
+
+ /// Callback when the user needs the items sorted
+ void OnPopupSort(wxCommandEvent& event);
+
+ ///Callback when the user need the items filtered
+ void OnPopupFilter(wxCommandEvent& event);
+
+ ///Callback when the user needs the item copied to the local disk
+ void OnLocalCopy(wxCommandEvent& event);
+
+
+ ///Callback when the user needs the item copied to the local disk
+ void OnAnonymizer(wxCommandEvent& event);
+
+
+ ///Callback when the user needs to edit a field
+ void OnEditField(wxCommandEvent& event);
+
+ ///Callback when the user needs to display alll dicom tags for a file
+ void OnDumpTags(wxCommandEvent &event);
+
+ ///Callback when the user needs to transfer data from storage to storage
+ void OnExportToStorage(wxCommandEvent &event);
+
+ ///Callback on mouse click
+ void OnMouseClick(wxMouseEvent& event);
+
+ /// Displays a menu for items
+ void OnItemMenu(wxListEvent &event);
+
+ /// Gets the attributes that are being shown and the ones that have been blocked on a specific level
+ void GetAttributes(std::vector<std::string>& areShown, std::vector<std::string>& notShown, int level);
+
+ ///Sets the non visible attributes and refreshes the GUI
+ void SetNonVisibleAttributes(const std::vector<std::string>& notShown, int level);
+
+ ///Creates a new listctrl
+ void CreateCtrl(std::vector<std::string>& notShown, int nlevel);
+
+ ///Returns true if the attribute passed as a parameter is visible or not
+ bool IsAttributeVisible(const std::string& val, int level);
+
+ /// Actual processing of item selection/deselection
+ /// Called by OnItemSelected and OnItemDeSelected
+ // void ProcessItem
+ private:
+ wxBoxSizer *msizer;
+ /// The struct holding the data for one level
+ /// Holds the wx control and other data
+ /// such as the vector of attribute keys corresponding to the columns
+ struct LevelType
+ {
+ // The List Control
+ wxListCtrl* wxCtrl;
+ wxSplitterWindow* wxSplitter;
+ std::vector<std::string> key;
+ // The vector of currently selected nodes of the level
+ std::vector<tree::Node*> Selected;
+ // True iff the vector Selected is up to date
+ bool SelectedUpToDate;
+ // The column used for sorting
+ unsigned int SortColumn;
+ ///Boolean that defines the direction of the sort
+ ///True is ascending order and false is descending
+ bool SortAscending;
+ //The vector of not shown attributes
+ std::vector<std::string> notShownAtts;
+ };
+ /// The vector of levels : one for each level of the tree
+ std::vector<LevelType> mLevelList;
+
+ /// return the wxListCtrl of one level
+ wxListCtrl* GetCtrl(int l) { return mLevelList[l].wxCtrl; }
+ /// return the wxSplitter of one level
+ wxSplitterWindow* GetSplitter(int l) { return mLevelList[l].wxSplitter; }
+ //Returns the maximum number of levels
+ int GetNumberOfLevels(){ return mLevelList.size(); }
+ /// Gets the user selected data from the level passed as a parameter
+ /// Updates the vector if necessary
+ const std::vector<tree::Node*>& GetSelected(int level);
+ /// Set the bool SelectedUpToDate for level l
+ void SetSelectedUpToDate(int l, bool v) { mLevelList[l].SelectedUpToDate = v; }
+ /// Get the bool SelectedUpToDate for level l
+ bool GetSelectedUpToDate(int l) { return mLevelList[l].SelectedUpToDate; }
+ ///Validates the selected images
+ void ValidateSelectedImages(bool isSelection);
+ ///Gets selected filenames
+ void GetSelectedAsString(std::vector<std::string>&s);
+ ///Gets the filenames of the given nodes and returns them on the given vector. Is recursive.
+ void GetFilenamesAsString(const std::vector<tree::Node*>& nodes, std::vector<std::string>&s);
+ /// Gets the next nodes on the list, be it up(true) or down(false).
+ void GetNodes(std::vector<tree::Node*>& nodes, bool direction);
+ /// Updates the view of a level given the selected items of upper level
+ /// Recursive method
+ virtual void RecursiveUpdateLevel( int );
+ ///Selects all the elements of a level
+ void SelectAll(int level);
+ ///UnSelects all the elements of a level
+ void UnSelectAll(int level);
+
+ void OnKeyDown(wxListEvent &event);
+ /// Sorts the level
+ void SortLevel(int level);
+
+ /// Currently Selected Column
+ int mColumnSelected;
+ ///The last selected item on the list (left click)
+ long mLastSelected;
+
+ ///The last selected item on the list (right click)
+ long mLastRightSelected;
+
+ ///The last selected level (by right click)
+ int mLastRightLevel;
+ ///The color map
+ typedef std::map<tree::Node*,wxColour> ColorMap;
+ typedef std::pair<tree::Node*,wxColour> NodeColorPair;
+ ColorMap mColorMap;
+ ///Initial color palette
+ std::vector<std::string> mColorPalette;
+
+ wxMenu* menu;
+
+ wxObject* senderCtrl;
+ int mAscendingID;
+ int mDescendingID;
+ int mFilterID;
+ unsigned int mLastLevel;
+
+ wxMenu* menuItem;
+ wxMenu *subExportMenu;
+ int mAnonymizingID;
+ int mLocalCopyID;
+ int mEditFieldID;
+ int mDumpID;
+ int mExportID;
+ int mExport2StorageID;
+
+ // If set to true then OnSelectedChanged returns immediately.
+ // Used to do avoid useless process during multiple selections
+ // or sorting
+ bool mIgnoreSelectedChanged;
+
+ DECLARE_EVENT_TABLE()
+ };
+ // EO class WxTreeView
+ //=====================================================================
+
+ class RemoveAlertDlg : public wxDialog
+ {
+ public:
+ RemoveAlertDlg(wxWindow *parent,
+ wxString title,
+ const wxSize& size);
+ ~RemoveAlertDlg();
+
+ bool isChecked();
+
+ private :
+ void onCheck(wxCommandEvent &Event);
+ bool mSave;
+ wxCheckBox *mcheck;
+
+ };
+
+
+} // EO namespace creaImageIO
+
+
+#endif // USE_WIDGETS
+// EOF
+#endif
--- /dev/null
+
+#include <creaImageIOWxViewer.h>
+#include <creaImageIOSystem.h>
+#include <fstream>
+#include <vtkCamera.h>
+#include <vtkRenderer.h>
+#include <vtkImageData.h>
+#include <creawxVTKRenderWindowInteractor.h>
+#include <creaMessageManager.h>
+#include <stdio.h>
+#include <time.h>
+
+
+using namespace crea;
+
+namespace creaImageIO
+{
+
+ //=====================================================================
+
+ //=====================================================================
+ class WxViewerPlayer: public wxThread
+ {
+ public:
+ WxViewerPlayer(WxViewer* v) :
+ mWxViewer(v)
+ {}
+
+ void* Entry();
+ void OnExit();
+
+ private:
+
+ WxViewer* mWxViewer;
+ };
+
+ //=====================================================================
+
+
+
+
+
+ //=====================================================================
+ // CTor
+ WxViewer::WxViewer(wxWindow *parent,
+ wxWindowID id,
+ wxString title,
+ const wxPoint& pos,
+ const wxSize& size)
+ : wxPanel( parent,
+ id,
+ pos,
+ size)
+ {
+ wxMutexLocker lock(mMutex);
+ GimmickDebugMessage(6,"WxViewer::WxViewer"
+ <<std::endl);
+
+ mNeedRefresh = false;
+ mLastImageShown = NULL;
+
+ // previewer
+ mInteractor = new crea::creawxVTKRenderWindowInteractor(this,-1);
+ mInteractor->UseCaptureMouseOn();
+
+ mViewer = vtkImageViewer2::New();
+ mViewer->SetupInteractor ( mInteractor );
+
+ mCurrent = 0;
+ mPlayer = 0;
+
+ // Grid to place checkbox and slider
+ mflexSizer = new wxFlexGridSizer(1,2,1,1);
+ //Slider
+ mslide = new wxSlider(this,-1,0,0,1, wxDefaultPosition, wxSize(400,40), wxSL_HORIZONTAL | wxSL_LABELS);
+ Connect( mslide->GetId(), wxEVT_COMMAND_SLIDER_UPDATED , (wxObjectEventFunction) &WxViewer::OnSlide );
+ //CheckBox
+ mcheck = new wxCheckBox(this,5123,crea::std2wx("Cine Loop"));
+ Connect( mcheck->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED , (wxObjectEventFunction) &WxViewer::OnCineLoop );
+ mcheck->SetValue(false);
+ mflexSizer->Add(mcheck,0, wxFIXED_MINSIZE);
+ mflexSizer-> Add( mslide,1,wxALIGN_CENTER | wxFIXED_MINSIZE );
+
+ // Sizer for Previewer and GridSizer
+ mtopSizer = new wxBoxSizer(wxVERTICAL);
+ mtopSizer->Add(mflexSizer,0);
+ mtopSizer-> Add( mInteractor ,1,wxGROW,0);
+ SetSizer(mtopSizer,true);
+
+ Update();
+ Layout();
+ }
+ //=====================================================================
+
+ //=====================================================================
+ /// Destructor
+ WxViewer::~WxViewer()
+ {
+ wxMutexLocker lock(mMutex);
+ GimmickDebugMessage(6,"WxViewer::~WxViewer"
+ <<std::endl);
+ // TO DO : desallocate cleanly
+ if(mPlayer)
+ {
+ mPlayer->Pause();
+ mPlayer->Delete();
+ mPlayer = 0;
+ }
+ delete mInteractor;
+ //delete mslide;
+ //delete mflexSizer;
+ }
+ //=====================================================================
+
+ //================================================================
+ void WxViewer::SetImageVector(std::vector<boost::shared_ptr<ImagePointerHolder> >& pointers)
+ {
+ wxMutexLocker lock(mMutex);
+ GimmickDebugMessage(6,"WxViewer::SetImageVector"<<std::endl);
+ imagePointers=pointers;
+
+ mslide->SetMax(pointers.size());
+ // Refresh don't work, TO MODIFY
+ mslide->Refresh();
+ mslide->ClearTicks();
+ mslide->Hide();
+ mslide->Show();
+ StartPlayer();
+ }
+
+ //================================================================
+
+ void WxViewer::ShowNextImage()
+ {
+
+
+ mMutex.Unlock();
+ wxMutexLocker lock(mMutex);
+
+
+ GimmickMessage(2,"WxViewer::ShowNextImage() "
+ <<mCurrent+1<<"/"
+ <<imagePointers.size()<<std::endl);
+
+ if(imagePointers.size()>0)
+ {
+ if (mCurrent<imagePointers.size())
+ {
+ boost::shared_ptr<ImagePointerHolder> iph = imagePointers[mCurrent];
+ //ImagePointerHolder* iph= imagePointers[mCurrent];
+ vtkImageData* currIm=iph->Get();
+ ShowImage(currIm);
+ if ( currIm != mLastImageShown )
+ {
+ mNeedRefresh = true;
+ mLastImageShown = currIm;
+ }
+ mCurrent++;
+ }
+ else
+ {
+ mCurrent = 0;
+ //ImagePointerHolder* iph=imagePointers[mCurrent];
+ boost::shared_ptr<ImagePointerHolder> iph = imagePointers[mCurrent];
+ vtkImageData* currIm=iph->Get();
+ ShowImage(currIm);
+ if ( currIm != mLastImageShown )
+ {
+ mNeedRefresh = true;
+ mLastImageShown = currIm;
+ }
+ mCurrent++;
+ }
+ }
+ }
+ //================================================================
+
+ //=====================================================================
+ void WxViewer::ShowImage(vtkImageData* im)
+ {
+ GimmickDebugMessage(6,"WxViewer::ShowImage"
+ <<std::endl);
+ if (im==0) return;
+
+ mViewer->SetInput(im);
+
+ mViewer->SetSlice( 0 );
+
+ int x1,x2,y1,y2,z1,z2;
+ double spx,spy,spz;
+ im->Update();
+
+//std::cout << "in WxViewer::ShowImage PrintSelf() =";
+//im->PrintSelf(std::cout, vtkIndent(2));
+
+ im->GetSpacing(spx,spy,spz);
+ //im->GetExtent (x1,x2,y1,y2,z1,z2); // JPR
+ im->GetWholeExtent (x1,x2,y1,y2,z1,z2);
+/*
+std::cout << "in WxViewer::ShowImage GetWholeExtent ext =";
+ std::cout << " [x1]=" << x1;
+ std::cout << " [x2]=" << x2;
+ std::cout << " [y1]=" << y1;
+ std::cout << " [y2]=" << y2;
+ std::cout << " [z1]=" << z1;
+ std::cout << " [z2]=" << z2;
+std::cout << std::endl;
+*/
+ if ((x1!=mx1) ||
+ (x2!=mx2) ||
+ (y1!=my1) ||
+ (y2!=my2) ||
+ (z1!=mz1) ||
+ (z2!=mz2) ||
+ (spx!=mspx) ||
+ (spy!=mspy) ||
+ (spz!=mspz)
+ )
+ {
+ mx1 = x1;
+ mx2 = x2;
+ my1 = y1;
+ my2 = y2;
+ mz1 = z1;
+ mz2 = z2;
+ mspx = spx;
+ mspy = spy;
+ mspz = spz;
+
+ double *range = im->GetScalarRange();
+ mViewer->SetColorWindow(range[1] - range[0]);
+ mViewer->SetColorLevel(0.5 * (range[1] + range[0]));
+
+ mViewer->GetRenderer()->ResetCamera();
+ double bounds[6];
+
+ mViewer->GetRenderer()->ComputeVisiblePropBounds(bounds);
+
+ mViewer->GetRenderer()->ResetCameraClippingRange(bounds);
+ mViewer->GetRenderer()->SetBackground(0.1,0.1,0.2);
+ }
+ }
+ //================================================================
+
+ //================================================================
+ bool WxViewer::RefreshIfNecessary()
+ {
+ if (mNeedRefresh)
+ {
+ GimmickDebugMessage(10,"WxViewer : Refreshing"<<std::endl);
+
+ mInteractor->Render();
+ mNeedRefresh = false;
+ return true;
+ }
+ return false;
+ }
+ //================================================================
+
+ //==================================================
+ void WxViewer::StopPlayer()
+ {
+ wxMutexLocker lock(mMutex);
+ if (mPlayer==0 ) return;
+ mPlayer->Delete();
+ mPlayer=0;
+ }
+ //================================================================
+
+ //==================================================
+ void WxViewer::StartPlayer()
+ {
+ if(mcheck->IsChecked())
+ {
+ // wxMutexLocker lock(mMutex);
+ if (mPlayer != 0) return;
+ mPlayer = new WxViewerPlayer(this);
+ mPlayer->Create();
+ mPlayer->Run();
+ }
+ else
+ {
+ ShowNextImage();
+ }
+ }
+
+ //================================================================
+
+ //==================================================
+
+ void WxViewer::OnCineLoop(wxCommandEvent &Event)
+ {
+ if(!mcheck->IsChecked())
+ {
+ mPlayer->Pause();
+ mPlayer->Delete();
+ mPlayer = 0;
+ }
+ StartPlayer();
+ }
+
+ //================================================================
+
+ //==================================================
+
+ void WxViewer::OnSlide(wxCommandEvent &Event)
+ {
+ mCurrent = mslide->GetValue();
+ StartPlayer();
+ }
+ //================================================================
+
+ //==================================================
+
+ void WxViewer::SetValue()
+ {
+ mslide->SetValue(mCurrent);
+ }
+
+ // BEGIN_EVENT_TABLE(WxGimmickFrame, wxDialog)
+ // END_EVENT_TABLE()
+ //================================================================
+
+//========================================================================
+//========================================================================
+//========================================================================
+//========================================================================
+//========================================================================
+//========================================================================
+//========================================================================
+//========================================================================
+
+ void* WxViewerPlayer::Entry()
+ {
+
+ GimmickDebugMessage(6,"WxViewerPlayer::Entry()"<<std::endl);
+
+ while(!TestDestroy())
+ {
+
+ mWxViewer->ShowNextImage();
+ mWxViewer->SetValue();
+ ::wxWakeUpIdle();
+ clock_t endwait;
+ endwait = clock () + 0.2 * CLOCKS_PER_SEC ;
+ while (clock() < endwait ) {}
+
+ }
+ return 0;
+ }
+
+ //=====================================================================
+
+ //=====================================================================
+ void WxViewerPlayer::OnExit()
+ {
+ GimmickDebugMessage(6,"WxViewerPlayer::OnExit() "<<std::endl);
+ }
+
+
+} // EO namespace creaImageIO
+
--- /dev/null
+#ifndef __creaImageIOWxViewer_h_INCLUDED__
+#define __creaImageIOWxViewer_h_INCLUDED__
+
+#ifdef USE_WXWIDGETS
+
+// wx
+#include <creaWx.h>
+#include <wx/image.h>
+#include <wx/imaglist.h>
+#include <wx/splitter.h>
+
+#include <creaImageIOSystem.h>
+#include <creaImageIOImagePointerHolder.h>
+
+// For image preview
+// vtk and wxvtk classes
+#include "creawxVTKRenderWindowInteractor.h"
+#include "vtkImageViewer2.h"
+
+namespace creaImageIO
+{
+
+ class WxViewerPlayer;
+
+ class WxViewer : public wxPanel
+ {
+
+ public:
+ /// Ctors
+ WxViewer();
+ WxViewer(wxWindow *parent,
+ const wxWindowID id,
+ wxString title,
+ const wxPoint& pos,
+ const wxSize& size);
+ /// Dtor
+ virtual ~WxViewer();
+ /// Shows the next image in the image vector
+ void ShowNextImage();
+ ///Starts the image player
+ void StartPlayer();
+ ///Stops the image player
+ void StopPlayer();
+ ///Refreshes the interface if the current image shown has changed
+ bool RefreshIfNecessary();
+ ///Sets a new image vector to be read
+ void SetImageVector(std::vector<boost::shared_ptr<ImagePointerHolder> > &pointers);
+
+ /// Set value of slider control
+ void SetValue();
+
+ private:
+
+ /// Event to resume or start cine loop
+ void OnCineLoop(wxCommandEvent &Event);
+
+ /// Event to change displayed frames with slide control
+ void OnSlide(wxCommandEvent &Event);
+
+
+ ///Shows the image passed as parameter
+ void ShowImage(vtkImageData* im);
+ /// Previewer
+ vtkImageViewer2* mViewer;
+
+ ///Slider
+ wxSlider *mslide ;
+
+ ///CheckBox to cine loop
+ wxCheckBox *mcheck;
+
+ /// Associated wxvtk interactor
+ crea::creawxVTKRenderWindowInteractor *mInteractor;
+
+ /// Current extent
+ int mx1,mx2,my1,my2,mz1,mz2;
+ /// Current spacing
+ double mspx,mspy,mspz;
+ /// Current image shown
+ int mCurrent;
+ ///The threaded movie player
+ WxViewerPlayer* mPlayer;
+ /// The mutex
+ wxMutex mMutex;
+ /// Boolean that declares if the player needs to be refreshed
+ bool mNeedRefresh;
+ ///Last image shown
+ vtkImageData* mLastImageShown;
+ ///The vectors of images to be shown
+ std::vector< boost::shared_ptr<ImagePointerHolder> > imagePointers;
+
+ /// Sizers to preview images
+ wxFlexGridSizer *mflexSizer;
+ wxBoxSizer *mtopSizer;
+
+ };
+
+}
+
+#endif // USE_WIDGETS
+// EOF
+#endif
--- /dev/null
+#include <creatisIOComparator.h>
+
+namespace creaImageIO{
+
+ Comparator::Comparator(tree::Node *i_tree, std::string i_tag) : m_discr(i_tag): bdiscr(false)
+ {
+ std::map< std::string, std::string> attr;
+ i_tree->GetDescriptor().BuildAttributeMap(attr);
+ std::map<std::string, std::string>::iterator it_att = attr.begin();
+ for(; it_att != attr.end(); it_att++)
+ {
+ if (it_att->first == m_discr)
+ {
+ bdiscr = true;
+ break;
+ }
+ }
+ tree::Node::ChildrenListType::reverse_iterator j;
+ for (j = (*i)->GetChildrenList().rbegin();
+ j!= (*i)->GetChildrenList().rend();
+ ++j)
+ {
+ GimmickDebugMessage(1,
+ "adding children "
+ <<(*j)->GetLabel()
+ <<"'"
+ <<std::endl);
+
+ wxListItem item;
+ item.SetMask(wxLIST_MASK_STATE |
+ wxLIST_MASK_TEXT |
+ // wxLIST_MASK_IMAGE |
+ wxLIST_MASK_DATA |
+ // wxLIST_MASK_WIDTH |
+ wxLIST_MASK_FORMAT
+ );
+
+ ItemData* data = new ItemData();
+ data->node = *j;
+ data->id = _id;
+
+ item.SetId(_id);
+ item.SetData(data);
+
+ _id++;
+ GetCtrl(l)->InsertItem(item);
+
+ //Setting attributes
+ for (int k=0; k<GetCtrl(l)->GetColumnCount(); ++k)
+ {
+ std::string val;
+ // Temporary correction : it works but no explanation about the problem FCY
+
+ if(k==0 && level <3)
+ {
+ val = (*j)->GetAttribute("NumberOfChildren");
+ }
+ else
+ val = (*j)->GetAttribute(mLevelList[l].key[k]);
+
+ }
+}
\ No newline at end of file
--- /dev/null
+#ifndef __creatisIOComparator_h_INCLUDED__
+#define __creatisIOComparator_h_INCLUDED__
+
+#include <creaImageIOTreeHandler.h>
+
+namespace creaImageIO{
+ class Comparator{
+ public:
+
+ Comparator(tree::Node *tr, std::string i_tag);
+ ~Comparator();
+ bool compare(std::string i_SOPIID);
+ void add(std::string i_SOPIID);
+ bool hasToCompare(){return bdiscr);
+
+ private:
+ std::vector<std::string> m_SOPIID;
+ std::string m_discr; //tag_discriminant
+ bool bdiscr;
+
+ };
+}
+#endif
\ No newline at end of file
--- /dev/null
+<level>
+Root
+O Name Name 4
+<level>
+Patient
+O NumberOfChildren #Series
+D 0x0010 0x0010 4
+D 0x0010 0x0040 3
+D 0x0010 0x0030 3
+D 0x0010 0x0020 2
+<level>
+Series
+O NumberOfChildren #Images
+D 0x0008 0x0060 4
+D 0x0008 0x1030 3
+D 0x0008 0x103E 3
+D 0x0008 0x0080 3
+#D 0x0008 0x0081 3
+#D 0x0008 0x1010 3
+#D 0x0008 0x1048 3
+#D 0x0008 0x1050 3
+D 0x0018 0x1030 3
+D 0x0020 0x0010 3
+D 0x0008 0x0020 3
+D 0x0008 0x0030 3
+D 0x0008 0x0050 3
+#D 0x0008 0x0005 3
+D 0x0008 0x0021 3
+D 0x0008 0x0031 3
+D 0x0020 0x000D 3
+D 0x0020 0x000E 2
+<level>
+Image
+D 0x0020 0x0013 3
+D 0x0028 0x0010 3
+D 0x0028 0x0011 3
+D 0x0028 0x0008 3
+#D 0x0028 0x0103 3
+D 0x0020 0x0032 3
+D 0x0020 0x0037 3
+D 0x0020 0x1041 3
+D 0x0028 0x0030 3
+#D 0x0028 0x0100 3
+#D 0x0028 0x0101 3
+#D 0x0008 0x0008 3
+D 0x0008 0x0023 3
+D 0x0008 0x0033 3
+D 0x0020 0x4000 3
+#D 0x0004 0x1500 4
+#D 0x0004 0x1501 4
+#D 0x0028 0x1052 3
+#D 0x0028 0x1053 3
+#D 0x0050 0x0004 3
+#D 0x0020 0x0052 3
+#D 0x0008 0x0016 3
+O FullFileName Full_file_name 2
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
+
+<head>
+<meta http-equiv="Content-Language" content="es-co" />
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>Architecture</title>
+</head>
+
+<body>
+
+<h1 id="head-854b062d360d04ce4f5012114afa9ff5f11f21ba">Architecture</h1>
+<span class="anchor" id="line-6"></span><span class="anchor" id="line-7"></span>
+<p class="line867"></p>
+<h2 id="head-230b785b2112730d4f8aaffcda83ccd28c9c8d83">Generalities</h2>
+<span class="anchor" id="line-8"></span><span class="anchor" id="line-9"></span>
+<ul>
+ <li>cvs : creatis/creaImageIO <span class="anchor" id="line-10"></span>
+ <ul>
+ <li style="list-style-type: none;">
+ <p class="line862">cvs co -D
+ :PROTOCOL:USERNAME@cvs.creatis.insa-lyon.fr:/cvs/creatis creaImageIO
+ <span class="anchor" id="line-11"></span></p>
+ </li>
+ </ul>
+ </li>
+ <li>09/02/09 : <span class="anchor" id="line-12"></span>
+ <ul>
+ <li>new sources in src2 : create a lib called creaImageIO2
+ <span class="anchor" id="line-13"></span></li>
+ <li>command line app : appli/gimmick <span class="anchor" id="line-14">
+ </span></li>
+ <li>cmake option : BUILD_V2 to build the new version
+ <span class="anchor" id="line-15"></span></li>
+ </ul>
+ </li>
+ <li>All C++ files begin with creaImageIO <span class="anchor" id="line-16">
+ </span></li>
+ <li>All creaImageIO classes are in a namespace creaImageIO
+ <span class="anchor" id="line-17"></span><span class="anchor" id="line-18">
+ </span></li>
+</ul>
+<p class="line867"></p>
+<h2 id="head-fd2930d25892babc425be6c8bf5e10dd28f776bb">General stucture</h2>
+<span class="anchor" id="line-19"></span><span class="anchor" id="line-20">
+</span>
+<ul>
+ <li>The main data structure : attributed tree
+ <span class="anchor" id="line-21"></span>
+ <ul>
+ <li>all related files are of the form : creaImageIOTree
+ <span class="anchor" id="line-22"></span></li>
+ <li>all related classes are in a namespace : creaImageIO::tree
+ <span class="anchor" id="line-23"></span></li>
+ </ul>
+ </li>
+ <li>
+ <p class="line862">The abstract 'Model' : <a class="nonexistent">TreeHandler</a>
+ = manages a data source (read-only; read/write)
+ <span class="anchor" id="line-24"></span></p>
+ <ul>
+ <li>
+ <p class="line862">Concrete models : now SQLiteTreeHandler; future
+ IRodsTreeHandler, <a class="nonexistent">FavoritesTreeHandler</a>?
+ <span class="anchor" id="line-25"></span></p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <p class="line862">The controller 'Gimmick' : controls different
+ <a class="nonexistent">TreeHandlers</a>: <span class="anchor" id="line-26">
+ </span></p>
+ <ul>
+ <li>Local database handler <span class="anchor" id="line-27"></span>
+ </li>
+ <li>Favorites handler <span class="anchor" id="line-28"></span></li>
+ <li>Other which are open on user demand
+ <span class="anchor" id="line-29"></span></li>
+ </ul>
+ </li>
+ <li>The Views: <span class="anchor" id="line-30"></span>
+ <ul>
+ <li>
+ <p class="line862">Abstract <a class="nonexistent">GimmickView</a> :
+ Dialog <span class="anchor" id="line-31"></span></p>
+ </li>
+ <li>
+ <p class="line862">Concrete <a class="nonexistent">GimmickView</a> : now
+ <a class="nonexistent">WxGimmickView</a>; future QGimmickView ?
+ <span class="anchor" id="line-32"></span></p>
+ </li>
+ <li>
+ <p class="line862">Do we need to have abstract classes for
+ <a class="nonexistent">GimmickView</a> components ?
+ <span class="anchor" id="line-33"></span></p>
+ <ul>
+ <li>
+ <p class="line891"><a class="nonexistent">TreeView</a> : is
+ associated with a <a class="nonexistent">TreeHandler</a> to
+ visualize its Tree <span class="anchor" id="line-34"></span></p>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">AttributeView</a> :
+ visualizes a set of attributes (only one for a
+ <a class="nonexistent">GimmickView</a>)
+ <span class="anchor" id="line-35"></span></p>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">ImageView</a> : preview of
+ images <span class="anchor" id="line-36"></span>
+ <span class="anchor" id="line-37"></span>
+ <span class="anchor" id="line-38"></span></p>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+</ul>
+<p class="line867"></p>
+<h2 id="head-b16e3563d729255863452b289e971da6a51d945b">Classes</h2>
+<span class="anchor" id="line-39"></span><span class="anchor" id="line-40">
+</span>
+<p class="line867"></p>
+<h3 id="head-4b6a5f3be677d7dfd21607ab2394efda67858131">Data structure:
+Attributed tree</h3>
+<span class="anchor" id="line-41"></span><span class="anchor" id="line-42">
+</span>
+<p class="line874">in namespace creaImageIO::tree
+<span class="anchor" id="line-43"></span><span class="anchor" id="line-44">
+</span></p>
+<ul>
+ <li>Tree <span class="anchor" id="line-45"></span>
+ <ul>
+ <li>Attributed tree structure <span class="anchor" id="line-46"></span>
+ </li>
+ <li>inherits Node <span class="anchor" id="line-47"></span></li>
+ <li>
+ <p class="line862">holds a vector of root <a class="nonexistent">
+ TreeNode</a> <span class="anchor" id="line-48"></span></p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">TreeData</a>
+ <span class="anchor" id="line-49"></span></p>
+ <ul>
+ <li>Abstract class to store user data on a tree
+ <span class="anchor" id="line-50"></span></li>
+ </ul>
+ </li>
+ <li>Node <span class="anchor" id="line-51"></span>
+ <ul>
+ <li>belong to a Tree, <span class="anchor" id="line-52"></span></li>
+ <li>holds a pointer on parent Node, <span class="anchor" id="line-53">
+ </span></li>
+ <li>holds a vector of children Node <span class="anchor" id="line-54">
+ </span></li>
+ </ul>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">NodeData</a>
+ <span class="anchor" id="line-55"></span></p>
+ <ul>
+ <li>Abstract class to store user data on a tree node
+ <span class="anchor" id="line-56"></span></li>
+ </ul>
+ </li>
+ <li>Descriptor <span class="anchor" id="line-57"></span>
+ <ul>
+ <li>Descriptor of the structure a tree (number of levels, descriptors of
+ each level, ...) <span class="anchor" id="line-58"></span></li>
+ <li>
+ <p class="line862">holds a vector of <a class="nonexistent">
+ LevelDescriptor</a> <span class="anchor" id="line-59"></span></p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">LevelDescriptor</a>
+ <span class="anchor" id="line-60"></span></p>
+ <ul>
+ <li>
+ <p class="line862">holds a vector of <a class="nonexistent">
+ TreeAttributeDescriptor</a> <span class="anchor" id="line-61"></span>
+ </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">AttributeDescriptor</a>
+ <span class="anchor" id="line-62"></span></p>
+ <ul>
+ <li>stores name, dicom group/elem, flags
+ <span class="anchor" id="line-63"></span></li>
+ </ul>
+ </li>
+ <li>Comparator <span class="anchor" id="line-64"></span>
+ <ul>
+ <li>Abstract definition of a comparator of Node
+ <span class="anchor" id="line-65"></span></li>
+ <li>
+ <p class="line862">Comparison is done by operator()(Node* const &, Node*
+ const &) <span class="anchor" id="line-66"></span></p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">ComparatorWithOrder</a>
+ <span class="anchor" id="line-67"></span></p>
+ <ul>
+ <li>Abstract Comparator whose order can be reversed
+ <span class="anchor" id="line-68"></span></li>
+ <li>
+ <p class="line862">Concrete comparison is done by method compare(Node*
+ const &, Node* const &) <span class="anchor" id="line-69"></span></p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">LexicographicalComparator</a>
+ <span class="anchor" id="line-70"></span></p>
+ <ul>
+ <li>A Comparator which stores a vector of Comparators and which performs
+ lexicographical comparison <span class="anchor" id="line-71"></span>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">IntComparator</a> : Compares the
+ values of a given Attribute of the Nodes which is decoded as an int value
+ <span class="anchor" id="line-72"></span></p>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">FloatComparator</a> : Compares the
+ values of a given Attribute of the Nodes which is decoded as a float value
+ <span class="anchor" id="line-73"></span></p>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">StringComparator</a> : Compares
+ the values of a given Attribute of the Nodes which is decoded as a string
+ value <span class="anchor" id="line-74"></span>
+ <span class="anchor" id="line-75"></span><span class="anchor" id="line-76">
+ </span></p>
+ </li>
+</ul>
+<p class="line867"></p>
+<h3 id="head-df11fea98849928d47fd0fca777300beeb1de441">Models : TreeHandler and
+descendants</h3>
+<span class="anchor" id="line-77"></span><span class="anchor" id="line-78">
+</span>
+<ul>
+ <li>
+ <p class="line891"><a class="nonexistent">TreeHandler</a> : Abstract class
+ which 'handles' a Tree structure. <span class="anchor" id="line-79"></span>
+ </p>
+ <ul>
+ <li style="list-style-type: none;">Can:
+ <span class="anchor" id="line-80"></span>Load the children of a given
+ Node <span class="anchor" id="line-81"></span>
+ <span class="anchor" id="line-82"></span></li>
+ </ul>
+ </li>
+ <li class="gap">
+ <p class="line862">SQLiteTreeHandler : Concrete <a class="nonexistent">
+ TreeHandler</a> which manages a tree stored in a sqlite database
+ <span class="anchor" id="line-83"></span></p>
+ </li>
+ <li>CppSQLite3.h / CppSQLite3.cpp : C++ interface to sqlite db
+ <span class="anchor" id="line-84"></span></li>
+</ul>
+<p class="line874">... <span class="anchor" id="line-85"></span>
+<span class="anchor" id="line-86"></span></p>
+<ul>
+ <li>
+ <p class="line891"><a class="nonexistent">ImageFinder</a> : Parses (recursively)
+ a part of a filesystem to look for known images and load their attributes in
+ order to add the images to a Tree (submission via a <a class="nonexistent">
+ TreeHandler</a>::<a class="nonexistent">AddBranch</a>)
+ <span class="anchor" id="line-87"></span><span class="anchor" id="line-88">
+ </span><span class="anchor" id="line-89"></span></p>
+ </li>
+</ul>
+<p class="line867"></p>
+<h3 id="head-5d6e60823befe28469fe2d44ec8a889c06ea3b72">Image handling</h3>
+<span class="anchor" id="line-90"></span><span class="anchor" id="line-91">
+</span>
+<ul>
+ <li>creaImageIOImageReader.h/cpp : <span class="anchor" id="line-92"></span>
+ <ul>
+ <li>
+ <p class="line891"><a class="nonexistent">SpecificImageReader</a>
+ <span class="anchor" id="line-93"></span></p>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">ImageReader</a>
+ <span class="anchor" id="line-94"></span></p>
+ </li>
+ </ul>
+ </li>
+ <li>creaImageIOMultiThreadImageReader.h/cpp
+ <span class="anchor" id="line-95"></span></li>
+ <li>creaImageIOIndexedHeap.h/txx <span class="anchor" id="line-96"></span>
+ <span class="anchor" id="line-97"></span></li>
+</ul>
+<p class="line867"></p>
+<h3 id="head-6b69e886af29f9ef5bb7eb8b646980dd2963c2cf">Controller</h3>
+<span class="anchor" id="line-98"></span><span class="anchor" id="line-99">
+</span>
+<ul>
+ <li>Gimmick <span class="anchor" id="line-100"></span>
+ <span class="anchor" id="line-101"></span></li>
+</ul>
+<p class="line867"></p>
+<h3 id="head-4e100fce838267c2c7182a13a0137ed08ed1c75a">Views</h3>
+<span class="anchor" id="line-102"></span><span class="anchor" id="line-103">
+</span>
+<ul>
+ <li>
+ <p class="line891"><a class="nonexistent">GimmickView</a> : Abstract
+ <span class="anchor" id="line-104"></span></p>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">WxGimmickView</a>
+ <span class="anchor" id="line-105"></span></p>
+ <ul>
+ <li>
+ <p class="line891"><a class="nonexistent">WxTreeVie</a>w</p>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">WxAttributeView</a>
+ <span class="anchor" id="line-107"></span></p>
+ </li>
+ <li>
+ <p class="line891"><a class="nonexistent">WxImageView</a>
+ <span class="anchor" id="line-108"></span></p>
+ </li>
+ </ul>
+ </li>
+ <li>QGimmickView <span class="anchor" id="line-109"></span>
+ <ul>
+ <li>... </li>
+ </ul>
+ </li>
+</ul>
+
+</body>
+
+</html>
--- /dev/null
+
+
+MACRO(CREA_BUILD_DOXYGEN_DOC NAME INPUT DOC_RELATIVE_INSTALL_PATH PREDEFINED)
+
+ #--------------------------------------------------------------------------
+ SET(USE_DOXYGEN ON CACHE BOOL "" FORCE)
+
+ # Name
+ SET(DOXYGEN_PROJECT_NAME "${NAME}")
+
+ # Inputs
+ STRING(REGEX REPLACE ";" " " DOXYGEN_INPUT "${INPUT}")
+
+ # Output dirs
+ SET(DOXYGEN_HTML_OUTPUT ".")
+ SET(DOXYGEN_OUTPUT ${PROJECT_BINARY_DIR}/${DOC_RELATIVE_INSTALL_PATH})
+ IF(NOT IS_DIRECTORY ${DOXYGEN_OUTPUT}/${DOXYGEN_HTML_OUTPUT})
+ FILE(MAKE_DIRECTORY ${DOXYGEN_OUTPUT}/${DOXYGEN_HTML_OUTPUT})
+ ENDIF(NOT IS_DIRECTORY ${DOXYGEN_OUTPUT}/${DOXYGEN_HTML_OUTPUT})
+
+ # Doc exclude
+ SET(DOXYGEN_EXCLUDE "CppSQLite3.h;CppSQLite3.cpp")
+ STRING(REGEX REPLACE ";" " " DOXYGEN_EXCLUDE "${DOXYGEN_EXCLUDE}")
+
+ # Log file name
+ SET(DOXYGEN_LOGFILE "${CMAKE_CURRENT_BINARY_DIR}/doxygen.log")
+
+ # Predefined symbols
+ STRING(REGEX REPLACE ";" " " DOXYGEN_DOC_PREDEFINED "${PREDEFINED}")
+
+ #---------------------------------------------------------------------------
+ # DOT verification
+ IF(DOT)
+ GET_FILENAME_COMPONENT(DOXYGEN_DOT_PATH ${DOT} PATH)
+ SET(DOXYGEN_HAVE_DOT "YES")
+ ELSE(DOT)
+ SET(DOXYGEN_DOT_PATH "")
+ SET(DOXYGEN_HAVE_DOT "NO")
+ ENDIF(DOT)
+
+ #---------------------------------------------------------------------------
+ # Create file and project
+ CONFIGURE_FILE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.txt.in
+ ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.txt
+ @ONLY IMMEDIATE
+ )
+
+
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${DOXYGEN_OUTPUT}/${DOXYGEN_HTML_OUTPUT}/index.html
+ COMMAND
+ ${DOXYGEN}
+ ARGS
+ ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.txt
+ # DEPENDS bbtk bbi
+ )
+
+ ADD_CUSTOM_TARGET(doxygen_${NAME} ALL
+ DEPENDS ${DOXYGEN_OUTPUT}/${DOXYGEN_HTML_OUTPUT}/index.html
+ )
+
+# INSTALL(
+# DIRECTORY
+# ${DOXYGEN_OUTPUT}/${DOXYGEN_HTML_OUTPUT}
+# DESTINATION
+# ${BBTK_DOXYGEN_INSTALL_PATH}/${DOC_RELATIVE_INSTALL_PATH}
+# )
+ #--------------------------------------------------------------------------
+
+ENDMACRO(CREA_BUILD_DOXYGEN_DOC)
+
+
+
+
+FILE(GLOB PAGES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.htm")
+#MESSAGE(STATUS "DOXYGEN HTML PAGES = ${PAGES}")
+FOREACH(PAGE ${PAGES})
+ CONFIGURE_FILE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/${PAGE}
+ ${PROJECT_BINARY_DIR}/doxygen/${PAGE}
+ IMMEDIATE
+ )
+#MESSAGE(STATUS "COPYING ${PAGE} TO ${PROJECT_BINARY_DIR}/doxygen/${PAGE}")
+ENDFOREACH(PAGE)
+
+
+FILE(GLOB IMAGES_PAGE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.png" "*.jpg")
+FOREACH(IMAGE_PAGE ${IMAGES_PAGE})
+ CONFIGURE_FILE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/${IMAGE_PAGE}
+ ${PROJECT_BINARY_DIR}/doxygen/${IMAGE_PAGE}
+ COPYONLY
+ )
+ENDFOREACH(IMAGE_PAGE)
+
+
+
+CONFIGURE_FILE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/DoxyMainPage.txt.in
+ ${CMAKE_CURRENT_BINARY_DIR}/DoxyMainPage.txt
+ @ONLY IMMEDIATE
+ )
+
+SET(INPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/DoxyMainPage.txt
+ ${PROJECT_SOURCE_DIR}/src2
+ ${PROJECT_SOURCE_DIR}/appli/gimmick
+ ${PROJECT_SOURCE_DIR}/appli/wxGimmick
+ ${PROJECT_SOURCE_DIR}/appli/TestWxGimmickReaderDialog
+ )
+
+SET(DOXYGEN_DOC_PREDEFINED USE_WXWIDGETS)
+
+CREA_BUILD_DOXYGEN_DOC(
+ ${CMAKE_PROJECT_NAME}_lib
+ "${INPUT}"
+ "doxygen"
+ "${CREA_DEFINITIONS}"
+ )
+
+#MESSAGE("DOX : ${CREA_DEFINITIONS}")
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Language" content="es-co" />
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>Class Diagram</title>
+<style type="text/css">
+.style1 {
+ font-size: x-large;
+}
+</style>
+</head>
+
+<body>
+
+<p class="style1"><strong>Class Diagram</strong></p>
+<img src="ClassDiagram.png" width="789" height="326" />
+
+</body>
+
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Language" content="es-co" />
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>Class Diagram</title>
+<style type="text/css">
+.style1 {
+ font-size: x-large;
+}
+</style>
+</head>
+
+<body>
+
+<p class="style1"><strong>Component Diagram</strong></p>
+<img src="ComponentDiagram.png"/>
+
+</body>
+
+</html>
--- /dev/null
+/**
+ * \mainpage creaImageIO documentation
+
+\htmlonly
+<H2>Architecture Description (v2)</H2>
+<br>
+<a href="Architecture.htm"> Architecture </a>
+<br>
+
+<H2>Diagrams of the architecture</H2>
+<br>
+<a href="ClassDiagram.htm"> Class Diagram </a>
+<br>
+<a href="ComponentDiagram.htm"> Component Diagram </a>
+<br>
+<a href="LayerDiagram.htm"> Layer Diagram </a>
+<br>
+
+<H2>Sequence Diagrams</H2>
+<br>
+<a href="SeqAddFile.htm"> On "Add Files" to a database </a>
+<br>
+<a href="SeqAddDir.htm"> On "Add Directories" to a database </a>
+<br>
+<a href="SeqRemove.htm"> On "Remove Files" from database </a>
+<br>
+<a href="SeqCreateDB.htm"> On "Create a database" </a>
+<br>
+<a href="SeqSelChanged.htm"> On "Selection has changed" </a>
+<br>
+<a href="SeqGetImages.htm"> On "Get Selected Images as VTK structures" </a>
+<br>
+
+\endhtmlonly
+
+
+
+
+
+
+ */
\ No newline at end of file
--- /dev/null
+# Doxyfile 1.5.5
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = @DOXYGEN_PROJECT_NAME@
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @PROJECT_VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = @DOXYGEN_OUTPUT@
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
+# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish,
+# and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+# DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE = @DOXYGEN_LOGFILE@
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = @DOXYGEN_INPUT@
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.h \
+ *.cxx \
+ *.txx
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE = @DOXYGEN_EXCLUDE@
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS = *.cxx
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = YES
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 3
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = @DOXYGEN_HTML_OUTPUT@
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = @DOXYGEN_DOC_PREDEFINED@
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = @DOXYGEN_HAVE_DOT@
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = YES
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = "@DOXYGEN_DOT_PATH@"
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is enabled by default, which results in a transparent
+# background. Warning: Depending on the platform used, enabling this option
+# may lead to badly anti-aliased labels on the edges of a graph (i.e. they
+# become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Language" content="es-co" />
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>Layer Diagram</title>
+<style type="text/css">
+.style1 {
+ font-size: x-large;
+}
+</style>
+</head>
+
+<body>
+
+<p class="style1"><strong>Currently implemented Layer Architecture (includes GUI less applications and wxWidgets derived applications)</strong></p>
+<img src="LayerDiagram.jpg"/>
+
+</body>
+
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Language" content="es-co" />
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>Class Diagram</title>
+<style type="text/css">
+.style1 {
+ font-size: x-large;
+}
+</style>
+</head>
+
+<body>
+
+<p class="style1"><strong>Adding a Directory</strong></p>
+<img src="SeqAddDir.png"/>
+
+</body>
+
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Language" content="es-co" />
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>Class Diagram</title>
+<style type="text/css">
+.style1 {
+ font-size: x-large;
+}
+</style>
+</head>
+
+<body>
+
+<p class="style1"><strong>Adding a File</strong></p>
+<img src="SeqAddFile.png"/>
+
+</body>
+
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Language" content="es-co" />
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>Class Diagram</title>
+<style type="text/css">
+.style1 {
+ font-size: x-large;
+}
+</style>
+</head>
+
+<body>
+
+<p class="style1"><strong>Creating a Database</strong></p>
+<img src="SeqCreateDB.png"/>
+
+</body>
+
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Language" content="es-co" />
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>Class Diagram</title>
+<style type="text/css">
+.style1 {
+ font-size: x-large;
+}
+</style>
+</head>
+
+<body>
+
+<p class="style1"><strong>On Get Images</strong></p>
+<img src="SeqGetImages.png"/>
+
+</body>
+
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Language" content="es-co" />
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>Class Diagram</title>
+<style type="text/css">
+.style1 {
+ font-size: x-large;
+}
+</style>
+</head>
+
+<body>
+
+<p class="style1"><strong>Remove a file</strong></p>
+<img src="SeqRemove.png"/>
+
+</body>
+
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Language" content="es-co" />
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>Class Diagram</title>
+<style type="text/css">
+.style1 {
+ font-size: x-large;
+}
+</style>
+</head>
+
+<body>
+
+<p class="style1"><strong>On Selection Changed</strong></p>
+<img src="SeqSelChanged.png"/>
+
+</body>
+
+</html>
--- /dev/null
+/* XPM */
+static const char * accept_xpm[] = {
+"48 48 773 2",
+" c None",
+". c #D8DBD9",
+"+ c #D4D5D4",
+"@ c #D4D3D3",
+"# c #D6D5D5",
+"$ c #D5D4D4",
+"% c #D3D2D2",
+"& c #D3D4D2",
+"* c #D6D9D7",
+"= c #D5D6D5",
+"- c #DBDBDB",
+"; c #E5E4E4",
+"> c #ECECEC",
+", c #F2F2F2",
+"' c #F5F5F5",
+") c #F7F7F7",
+"! c #F8F7F7",
+"~ c #F6F6F6",
+"{ c #F5F4F4",
+"] c #F0F0F0",
+"^ c #EAE9E9",
+"/ c #E1E1E1",
+"( c #D7D7D7",
+"_ c #D0D1D0",
+": c #D9D9D9",
+"< c #E7E7E7",
+"[ c #F3F3F3",
+"} c #FAFAFA",
+"| c #FDFDFD",
+"1 c #F3F9F4",
+"2 c #E0F0E2",
+"3 c #FCFCFC",
+"4 c #F8F8F8",
+"5 c #EFEFEF",
+"6 c #E2E1E1",
+"7 c #D3D3D2",
+"8 c #DADADA",
+"9 c #F9F9F9",
+"0 c #FEFEFE",
+"a c #FCFDFC",
+"b c #D7EBDC",
+"c c #99CEAA",
+"d c #5CB17B",
+"e c #28994F",
+"f c #078938",
+"g c #008536",
+"h c #E6E5E5",
+"i c #D6D7D6",
+"j c #E8E7E7",
+"k c #F2F8F3",
+"l c #A6D5B4",
+"m c #41A564",
+"n c #0C8C3E",
+"o c #008736",
+"p c #038938",
+"q c #048939",
+"r c #058A3A",
+"s c #008735",
+"t c #F2F8F4",
+"u c #E0DFDF",
+"v c #CDCFCD",
+"w c #DCDDDC",
+"x c #F2F1F1",
+"y c #A9D7B6",
+"z c #2E9D56",
+"A c #008836",
+"B c #018937",
+"C c #0F9042",
+"D c #1C964C",
+"E c #269B54",
+"F c #2B9E59",
+"G c #2FA05B",
+"H c #30A15D",
+"I c #A9D6B6",
+"J c #D3D4D3",
+"K c #E1E0E0",
+"L c #DFF0E3",
+"M c #44A866",
+"N c #008936",
+"O c #088E3C",
+"P c #1C984C",
+"Q c #2DA15A",
+"R c #32A35E",
+"S c #31A45D",
+"T c #32A45E",
+"U c #31A35D",
+"V c #2DA05A",
+"W c #F1F1F1",
+"X c #D7D6D6",
+"Y c #E4E3E3",
+"Z c #B3DBBE",
+"` c #1C9749",
+" . c #008A36",
+".. c #058E3A",
+"+. c #1B994B",
+"@. c #2EA25B",
+"#. c #31A55D",
+"$. c #32A55E",
+"%. c #31A65D",
+"&. c #32A65E",
+"*. c #32A75E",
+"=. c #1C9649",
+"-. c #F3F2F2",
+";. c #D9D9D8",
+">. c #E2E2E2",
+",. c #A1D4AF",
+"'. c #0B903C",
+"). c #008C36",
+"!. c #0D9340",
+"~. c #2AA157",
+"{. c #31A75D",
+"]. c #31A85D",
+"^. c #31A95D",
+"/. c #31AA5D",
+"(. c #2AA057",
+"_. c #0B903D",
+":. c #A0D3B0",
+"<. c #DFDFDF",
+"[. c #A0D4AF",
+"}. c #018D36",
+"|. c #018E37",
+"1. c #169847",
+"2. c #30A55C",
+"3. c #32A75D",
+"4. c #32A95D",
+"5. c #32AA5D",
+"6. c #31AA5C",
+"7. c #32AB5D",
+"8. c #33AB5D",
+"9. c #34AC5D",
+"0. c #35AC5D",
+"a. c #36AC5D",
+"b. c #37AC5C",
+"c. c #37AC5D",
+"d. c #35AC5C",
+"e. c #32AB5C",
+"f. c #38AC62",
+"g. c #36AA61",
+"h. c #30A45C",
+"i. c #DADBDA",
+"j. c #F4F4F4",
+"k. c #B3DCBF",
+"l. c #0B923C",
+"m. c #018F37",
+"n. c #189B4A",
+"o. c #30A75C",
+"p. c #37AD5D",
+"q. c #3CAD5C",
+"r. c #40AE5D",
+"s. c #43AE5D",
+"t. c #46AF5C",
+"u. c #47B05D",
+"v. c #48B05D",
+"w. c #48B05C",
+"x. c #47AF5C",
+"y. c #45AF5D",
+"z. c #42AE5D",
+"A. c #3EAE5C",
+"B. c #3AAD5D",
+"C. c #39AE5F",
+"D. c #A8DCBA",
+"E. c #B0DEC0",
+"F. c #3BAE65",
+"G. c #189A4A",
+"H. c #ECEBEB",
+"I. c #1D9B49",
+"J. c #009036",
+"K. c #169C47",
+"L. c #30A95C",
+"M. c #33AC5C",
+"N. c #37AD5C",
+"O. c #3EAD5C",
+"P. c #44AF5C",
+"Q. c #49B05C",
+"R. c #4CB05C",
+"S. c #4EB15C",
+"T. c #4FB25C",
+"U. c #51B25C",
+"V. c #52B35C",
+"W. c #53B35C",
+"X. c #51B35C",
+"Y. c #50B25C",
+"Z. c #4DB15C",
+"`. c #4BB05C",
+" + c #8ECE9C",
+".+ c #BCE3CA",
+"++ c #3CB065",
+"@+ c #30A85C",
+"#+ c #169B47",
+"$+ c #008F36",
+"%+ c #1C9A49",
+"&+ c #FBFBFB",
+"*+ c #45AD66",
+"=+ c #009236",
+"-+ c #0D9A40",
+";+ c #30AA5B",
+">+ c #33AC5D",
+",+ c #43AE5C",
+"'+ c #49B05D",
+")+ c #56B45B",
+"!+ c #58B45C",
+"~+ c #5AB55C",
+"{+ c #5BB65B",
+"]+ c #5CB65C",
+"^+ c #5DB65C",
+"/+ c #5DB75B",
+"(+ c #5DB75C",
+"_+ c #5CB65B",
+":+ c #5BB65C",
+"<+ c #59B55C",
+"[+ c #57B45B",
+"}+ c #7DC583",
+"|+ c #F6FBF7",
+"1+ c #FFFFFF",
+"2+ c #D1ECDA",
+"3+ c #47B36B",
+"4+ c #30A95B",
+"5+ c #0D9940",
+"6+ c #009136",
+"7+ c #44AC66",
+"8+ c #A9DAB6",
+"9+ c #009336",
+"0+ c #059739",
+"a+ c #2AA856",
+"b+ c #39AD5D",
+"c+ c #4AB05D",
+"d+ c #56B45C",
+"e+ c #5CB75C",
+"f+ c #5EB75B",
+"g+ c #60B85B",
+"h+ c #62B95B",
+"i+ c #63B95B",
+"j+ c #64BA5B",
+"k+ c #65BA5B",
+"l+ c #61B95B",
+"m+ c #6FBF6B",
+"n+ c #EAF6EA",
+"o+ c #D8EEDF",
+"p+ c #58B974",
+"q+ c #2AA756",
+"r+ c #A9D9B6",
+"s+ c #E1E1E0",
+"t+ c #F2F9F3",
+"u+ c #2EA755",
+"v+ c #009635",
+"w+ c #1EA34A",
+"x+ c #3FAE5C",
+"y+ c #5FB75B",
+"z+ c #66BB5A",
+"A+ c #68BC5A",
+"B+ c #69BC5A",
+"C+ c #6BBD5A",
+"D+ c #6CBD5A",
+"E+ c #6CBD5B",
+"F+ c #6CBE5B",
+"G+ c #6ABD5A",
+"H+ c #6CBE5F",
+"I+ c #CFEACB",
+"J+ c #B7E0C5",
+"K+ c #3AAA58",
+"L+ c #3AAD5C",
+"M+ c #1CA24A",
+"N+ c #009535",
+"O+ c #2EA656",
+"P+ c #A5D9B3",
+"Q+ c #009634",
+"R+ c #109C3B",
+"S+ c #42AE5A",
+"T+ c #4CB15C",
+"U+ c #57B45C",
+"V+ c #5FB85B",
+"W+ c #69BC5B",
+"X+ c #6BBD5B",
+"Y+ c #6DBE5B",
+"Z+ c #6FBF5C",
+"`+ c #71BF5D",
+" @ c #72C05E",
+".@ c #73C05F",
+"+@ c #74C05F",
+"@@ c #72BF5E",
+"#@ c #AFDBA4",
+"$@ c #E7F5ED",
+"%@ c #29A35C",
+"&@ c #31A551",
+"*@ c #4AB05C",
+"=@ c #3DAD5A",
+"-@ c #0C9B3B",
+";@ c #A5D9B4",
+">@ c #42B163",
+",@ c #109B34",
+"'@ c #33A74B",
+")@ c #54B35C",
+"!@ c #66BB5B",
+"~@ c #6ABD5B",
+"{@ c #70BF5D",
+"]@ c #74C060",
+"^@ c #75C161",
+"/@ c #76C162",
+"(@ c #78C163",
+"_@ c #78C263",
+":@ c #79C264",
+"<@ c #92CE81",
+"[@ c #F5FBF4",
+"}@ c #FBFDFC",
+"|@ c #53B57D",
+"1@ c #25A04A",
+"2@ c #52B25C",
+"3@ c #2FA64B",
+"4@ c #0B9A34",
+"5@ c #40B063",
+"6@ c #E6E6E5",
+"7@ c #D8EEDB",
+"8@ c #1B9E3C",
+"9@ c #52B02E",
+"0@ c #4EB158",
+"a@ c #65BB5B",
+"b@ c #77C162",
+"c@ c #7BC366",
+"d@ c #7DC467",
+"e@ c #7EC468",
+"f@ c #7FC469",
+"g@ c #80C46A",
+"h@ c #7FC46A",
+"i@ c #82C66D",
+"j@ c #E5F3E0",
+"k@ c #FEFFFE",
+"l@ c #91D0AC",
+"m@ c #159A44",
+"n@ c #5DB759",
+"o@ c #5AB55B",
+"p@ c #4BB058",
+"q@ c #4CAE2F",
+"r@ c #149D3D",
+"s@ c #D7EEDC",
+"t@ c #EFEEEE",
+"u@ c #A1D6A9",
+"v@ c #26A033",
+"w@ c #7CC034",
+"x@ c #75C056",
+"y@ c #67BC5A",
+"z@ c #76C161",
+"A@ c #81C56B",
+"B@ c #83C66D",
+"C@ c #84C66D",
+"D@ c #84C76E",
+"E@ c #83C66C",
+"F@ c #84C76D",
+"G@ c #C4E4B9",
+"H@ c #C6E6D4",
+"I@ c #129949",
+"J@ c #5EB757",
+"K@ c #61B85B",
+"L@ c #73BF56",
+"M@ c #76BE35",
+"N@ c #219E33",
+"O@ c #9ED5A9",
+"P@ c #F6F5F5",
+"Q@ c #6FC079",
+"R@ c #3CA830",
+"S@ c #8BC63D",
+"T@ c #94CC50",
+"U@ c #6EBE5B",
+"V@ c #75C160",
+"W@ c #7DC468",
+"X@ c #7EC469",
+"Y@ c #82C56A",
+"Z@ c #7DC364",
+"`@ c #79C05E",
+" # c #76BF5A",
+".# c #75BE57",
+"+# c #75BD57",
+"@# c #91CB7A",
+"## c #F8FCF7",
+"$# c #ECF7F1",
+"%# c #31A663",
+"&# c #47AE53",
+"*# c #67BB5B",
+"=# c #68BB5A",
+"-# c #86C43E",
+";# c #37A631",
+"># c #6DBF7A",
+",# c #47AD4E",
+"'# c #51AF2E",
+")# c #93CA45",
+"!# c #9CCF4F",
+"~# c #88C853",
+"{# c #6EBE5C",
+"]# c #7AC265",
+"^# c #97D086",
+"/# c #CBE7C2",
+"(# c #86C772",
+"_# c #7EC364",
+":# c #76BE5A",
+"<# c #73BD54",
+"[# c #74BD54",
+"}# c #75BD55",
+"|# c #76BE56",
+"1# c #77BE56",
+"2# c #77BE57",
+"3# c #7ABF5B",
+"4# c #DCEED3",
+"5# c #6CC090",
+"6# c #2BA24D",
+"7# c #7AC266",
+"8# c #71C05E",
+"9# c #87C753",
+"0# c #8EC846",
+"a# c #4CAD2F",
+"b# c #44AC4E",
+"c# c #D7DAD7",
+"d# c #E3E3E3",
+"e# c #33A436",
+"f# c #62B52E",
+"g# c #97CC4A",
+"h# c #9BCE4E",
+"i# c #9BCE4F",
+"j# c #83C659",
+"k# c #D9EDD3",
+"l# c #D6EDD5",
+"m# c #7CC161",
+"n# c #74BC54",
+"o# c #76BD56",
+"p# c #79BE57",
+"q# c #7BBF59",
+"r# c #7CBF5A",
+"s# c #7EC15B",
+"t# c #7FC15B",
+"u# c #7FC15C",
+"v# c #B5DBA1",
+"w# c #FEFEFD",
+"x# c #BBE2CC",
+"y# c #109743",
+"z# c #76C168",
+"A# c #82C56C",
+"B# c #82C558",
+"C# c #94CB4A",
+"D# c #5CB32F",
+"E# c #2FA236",
+"F# c #F5FAF4",
+"G# c #35A533",
+"H# c #6BB92D",
+"I# c #99CD4C",
+"J# c #9ACE4F",
+"K# c #84C661",
+"L# c #80C56A",
+"M# c #9FD38E",
+"N# c #FDFEFD",
+"O# c #D9EED7",
+"P# c #87C56C",
+"Q# c #7DC05B",
+"R# c #80C15C",
+"S# c #82C25E",
+"T# c #84C35F",
+"U# c #85C460",
+"V# c #86C460",
+"W# c #93CA71",
+"X# c #F6FBF3",
+"Y# c #EAF6EF",
+"Z# c #2BA35E",
+"`# c #4DAE4C",
+" $ c #75BE59",
+".$ c #81C569",
+"+$ c #82C66C",
+"@$ c #82C55F",
+"#$ c #97CC4D",
+"$$ c #66B72F",
+"%$ c #31A334",
+"&$ c #E4E4E4",
+"*$ c #E5F3E2",
+"=$ c #3AA733",
+"-$ c #70BB2D",
+";$ c #9ACE4E",
+">$ c #99CE51",
+",$ c #8CCA6A",
+"'$ c #DCEFD5",
+")$ c #E4F3E2",
+"!$ c #91CA75",
+"~$ c #87C561",
+"{$ c #89C662",
+"]$ c #8BC763",
+"^$ c #8DC864",
+"/$ c #8EC966",
+"($ c #D2E9C2",
+"_$ c #FEFFFF",
+":$ c #6FC192",
+"<$ c #29A047",
+"[$ c #72BC54",
+"}$ c #7EC466",
+"|$ c #89C866",
+"1$ c #98CD4E",
+"2$ c #6BB92F",
+"3$ c #37A633",
+"4$ c #E6F3E2",
+"5$ c #40AA32",
+"6$ c #72BB2D",
+"7$ c #B3DA7A",
+"8$ c #EFF8EE",
+"9$ c #9ED182",
+"0$ c #91CA66",
+"a$ c #93CB67",
+"b$ c #95CB69",
+"c$ c #AED88C",
+"d$ c #FCFDFA",
+"e$ c #B7E0C9",
+"f$ c #179A47",
+"g$ c #75BD5B",
+"h$ c #7EC05B",
+"i$ c #7ABE58",
+"j$ c #78BE43",
+"k$ c #90C940",
+"l$ c #98CD4F",
+"m$ c #6CBA2F",
+"n$ c #3DA833",
+"o$ c #E3E2E2",
+"p$ c #45AC32",
+"q$ c #99CD4D",
+"r$ c #94CA41",
+"s$ c #DDEEC4",
+"t$ c #F1F8F0",
+"u$ c #AED892",
+"v$ c #9BCE6C",
+"w$ c #9FD071",
+"x$ c #E6F3DA",
+"y$ c #F2F9F5",
+"z$ c #28A25B",
+"A$ c #5CB557",
+"B$ c #80C159",
+"C$ c #7EC03D",
+"D$ c #82C025",
+"E$ c #84C226",
+"F$ c #93CA40",
+"G$ c #6AB92E",
+"H$ c #42AB32",
+"I$ c #4EB036",
+"J$ c #6CBA2E",
+"K$ c #9BCF4F",
+"L$ c #97CC47",
+"M$ c #85C227",
+"N$ c #A3D160",
+"O$ c #F4FAF1",
+"P$ c #F5FAF5",
+"Q$ c #B8DD9D",
+"R$ c #C2E1A2",
+"S$ c #7AC69B",
+"T$ c #33A54C",
+"U$ c #91CA67",
+"V$ c #8FC965",
+"W$ c #88C550",
+"X$ c #83C12F",
+"Y$ c #83C124",
+"Z$ c #82C023",
+"`$ c #95CB4A",
+" % c #67B82F",
+".% c #4BAE35",
+"+% c #D5D5D4",
+"@% c #E9E9E9",
+"#% c #FBFAFA",
+"$% c #67BA50",
+"%% c #66B730",
+"&% c #9ACD4C",
+"*% c #88C42D",
+"=% c #97CC57",
+"-% c #F3F9EF",
+";% c #F6FBF4",
+">% c #CFEADB",
+",% c #159A4A",
+"'% c #8BC864",
+")% c #92C953",
+"!% c #87C334",
+"~% c #90C946",
+"{% c #61B631",
+"]% c #64B94E",
+"^% c #DADCD9",
+"/% c #8ECC7C",
+"(% c #60B636",
+"_% c #8DC73D",
+":% c #90C83A",
+"<% c #9ACD5A",
+"[% c #F0F8EA",
+"}% c #FAFDFB",
+"|% c #4BB176",
+"1% c #48AC37",
+"2% c #86C22C",
+"3% c #88C53F",
+"4% c #5DB435",
+"5% c #8DCB7C",
+"6% c #B8DFAC",
+"7% c #5EB53E",
+"8% c #82C235",
+"9% c #98CD4A",
+"0% c #85C228",
+"a% c #98CD57",
+"b% c #F0F8EC",
+"c% c #A9DABE",
+"d% c #1C9B3C",
+"e% c #7CBE25",
+"f% c #7CC036",
+"g% c #5BB43C",
+"h% c #B7DEAB",
+"i% c #E2F1DD",
+"j% c #66B84B",
+"k% c #73BC31",
+"l% c #8CC636",
+"m% c #93CA4C",
+"n% c #EEF7E9",
+"o% c #F1F9F5",
+"p% c #209F53",
+"q% c #5FB42B",
+"r% c #8AC537",
+"s% c #6EBA32",
+"t% c #63B748",
+"u% c #E2F1DC",
+"v% c #E4E4E3",
+"w% c #FDFDFC",
+"x% c #86C770",
+"y% c #6CBA3E",
+"z% c #7FBF25",
+"A% c #96CB50",
+"B% c #EBF5E4",
+"C% c #FDFEFE",
+"D% c #2EA234",
+"E% c #80C024",
+"F% c #82C024",
+"G% c #7ABE26",
+"H% c #68B83D",
+"I% c #84C66F",
+"J% c #D6D6D5",
+"K% c #C6E4BA",
+"L% c #6BBA4C",
+"M% c #79BD2A",
+"N% c #95CB4D",
+"O% c #EBF6E6",
+"P% c #D8EEE2",
+"Q% c #159A48",
+"R% c #73BB27",
+"S% c #80BF24",
+"T% c #73BB2C",
+"U% c #68B94A",
+"V% c #C4E4BA",
+"W% c #F6FAF4",
+"X% c #84C56A",
+"Y% c #75BC41",
+"Z% c #7EBF25",
+"`% c #8FC843",
+" & c #E9F5E3",
+".& c #58B780",
+"+& c #42A930",
+"@& c #7ABD26",
+"#& c #71BB41",
+"$& c #81C469",
+"%& c #DAD9D9",
+"&& c #F7F6F6",
+"*& c #CBE6BE",
+"=& c #76BD54",
+"-& c #7ABE31",
+";& c #81C024",
+">& c #92C948",
+",& c #E6F3DD",
+"'& c #C0E4D0",
+")& c #1A9B41",
+"!& c #78BD25",
+"~& c #7EBF24",
+"{& c #75BC32",
+"]& c #74BC52",
+"^& c #C9E5BE",
+"/& c #98CE7D",
+"(& c #7DC050",
+"_& c #7CBE28",
+":& c #91C845",
+"<& c #E5F3DE",
+"[& c #3EAC6D",
+"}& c #4EAE2E",
+"|& c #77BD29",
+"1& c #79BE4F",
+"2& c #95CC7C",
+"3& c #ECF5E7",
+"4& c #8CC86B",
+"5& c #80C149",
+"6& c #7DBF25",
+"7& c #8CC63C",
+"8& c #E3F2DB",
+"9& c #AADABF",
+"0& c #1C9B38",
+"a& c #7DBF24",
+"b& c #7CC049",
+"c& c #8AC669",
+"d& c #EBF5E7",
+"e& c #D5EBC9",
+"f& c #8BC765",
+"g& c #82C243",
+"h& c #8EC740",
+"i& c #D1EACA",
+"j& c #2EA560",
+"k& c #5EB32B",
+"l& c #7BBE26",
+"m& c #7EC144",
+"n& c #89C564",
+"o& c #D4EAC9",
+"p& c #D8D8D6",
+"q& c #E9E9E8",
+"r& c #CEE7BE",
+"s& c #8FC964",
+"t& c #86C445",
+"u& c #7DBF2F",
+"v& c #3DA731",
+"w& c #82C346",
+"x& c #8CC863",
+"y& c #CDE6BD",
+"z& c #DDDDDC",
+"A& c #D1E8BF",
+"B& c #99CE6D",
+"C& c #8EC851",
+"D& c #7DBF29",
+"E& c #7FBF24",
+"F& c #7ABE2A",
+"G& c #8AC650",
+"H& c #97CD6C",
+"I& c #D0E8BF",
+"J& c #EEEEED",
+"K& c #DBEDCC",
+"L& c #A5D37A",
+"M& c #97CC5F",
+"N& c #81C135",
+"O& c #7CBE26",
+"P& c #7FC136",
+"Q& c #95CB5F",
+"R& c #A3D278",
+"S& c #DAEDCC",
+"T& c #EFF7E9",
+"U& c #B3DA8E",
+"V& c #9FD06D",
+"W& c #90C951",
+"X& c #7EC02D",
+"Y& c #7DBE25",
+"Z& c #7BBF2E",
+"`& c #B2DA8D",
+" * c #D9ECC7",
+".* c #AED884",
+"+* c #A2D26F",
+"@* c #93CA55",
+"#* c #85C33A",
+"$* c #7DBF28",
+"%* c #7BBE29",
+"&* c #83C33B",
+"** c #91CA56",
+"=* c #ADD784",
+"-* c #D8ECC7",
+";* c #E0E0DF",
+">* c #F8FBF5",
+",* c #D9EDC7",
+"'* c #B6DB8F",
+")* c #A8D476",
+"!* c #A2D16A",
+"~* c #97CC5A",
+"{* c #8EC84A",
+"]* c #87C43E",
+"^* c #82C236",
+"/* c #80C132",
+"(* c #8DC84A",
+"_* c #A1D16A",
+":* c #A7D475",
+"<* c #B5DB8F",
+"[* c #DCDCDA",
+"}* c #ECF5E3",
+"|* c #D5EABF",
+"1* c #C2E1A0",
+"2* c #AED77F",
+"3* c #A7D473",
+"4* c #AAD575",
+"5* c #AAD574",
+"6* c #ADD77F",
+"7* c #EEF6E7",
+"8* c #E6E6E6",
+" . + @ # $ % & * ",
+" = - ; > , ' ) ! ! ~ { ] ^ / ( _ ",
+" : < [ } | | | | 1 2 2 1 | | | 3 4 5 6 7 ",
+" 8 > 9 0 a b c d e f g g g g f e d c b a | ' h % ",
+" i j 4 | k l m n o o o p q r r q p o o s n m l t 3 [ u v ",
+" w x 3 | y z s A B C D E F G H H G F E D C B A s z I | } ^ J ",
+" K 4 0 L M N N O P Q R R S T T S T T S T R U V P O N A M L | W X ",
+" Y 9 | Z ` ...+.@.S #.$.$.%.&.&.%.*.&.%.$.$.#.$.S @.+... .=.Z | -.;. ",
+" >.9 | ,.'.).!.~.#.%.{.{.].^.^././././././.^.^.].{.%.#.S (.!.)._.:.| -.X ",
+" <.9 | [.}.|.1.2.3.].4.5.6.7.8.9.0.a.b.c.a.d.9.8.e.5.5.f.g.*.h.1.|.}.[.| W J ",
+" i.j.0 k.l.m.n.o.^.5.e.9.p.q.r.s.t.u.v.w.v.v.x.y.z.A.B.C.D.E.F.4.o.G.|.l.k.| ^ v ",
+" H.| L I.J.K.L./.M.N.O.P.Q.R.S.T.U.V.W.W.W.W.X.Y.T.Z.`. +a 0 .+++/.@+#+$+%+L } u ",
+" u &+| *+=+-+;+>+B.,+'+Z.Y.W.)+!+~+{+]+^+/+(+^+_+:+<+[+}+|+1+0 1+2+3+7.4+5+6+7+| [ % ",
+" , | 8+9+0+a+b+s.c+S.V.d+<+e+f+g+h+i+j+j+k+j+j+j+h+l+m+n+1+1+0 1+1+o+p+a.q+0+=+r+3 h ",
+" s+3 t+u+v+w+x+Q.S.W.[+{+y+l+j+z+A+B+C+D+E+F+F+D+C+G+H+I+0 0 0 0 0 0 0 J+K+L+M+N+O+t+' 7 ",
+" 5 0 P+Q+R+S+T+V.U+]+V+i+z+W+X+Y+Z+`+ @.@.@+@+@.@.@@@#@0 1+1+1+0 1+1+$@%@&@*@=@-@v+;@| 6 ",
+" <.9 a >@,@'@S.)@~+V+i+!@~@Y+{@ @]@^@/@(@_@:@:@:@:@_@<@[@0 1+1+1+0 1+}@|@1@[+2@T+3@4@5@a 5 _ ",
+" 6@| 7@8@9@0@d+_+l+a@W+Y+`+.@^@b@:@c@d@e@f@g@h@g@h@i@j@1+0 1+1+1+0 k@l@m@n@V+o@)@p@q@r@s@4 ( ",
+" t@| u@v@w@x@f+h+y@E+{@.@z@_@c@e@g@A@B@C@D@C@B@E@F@G@0 0 0 0 0 0 0 H@I@J@~@z+K@{+L@M@N@O@3 / ",
+" P@| Q@R@S@T@B+B+U@ @V@:@W@X@A@B@F@Y@Z@`@ #.#+#+#@###1+1+0 1+1+1+$#%#&#]@{@E+*#=#T@-#;#>#| ^ ",
+"< 9 | ,#'#)#!#~#{#.@z@]#^#/#(#F@_#:#<#[#}#|#1#2#3#4#0 1+1+0 1+1+1+5#6#7#_@V@8#Y+9#!#0#a#b#| ] c#",
+"d#3 | e#f#g#h#i#j#b@c@h@k#0 l#m#n#o#p#q#r#s#t#u#v#w#0 0 0 0 0 0 x#y#z#A#e@]#z@B#i#h#C#D#E#| { & ",
+"6 3 F#G#H#I#!#!#J#K#L#M#N#1+0 O#P#Q#R#S#T#U#V#W#X#1+0 1+1+0 1+Y#Z#`# $.$+$f@@$J#!#!##$$$%$F#~ % ",
+"&$| *$=$-$;$!#!#h#>$,$'$0 1+0 1+)$!$~${$]$^$/$($1+1+0 1+1+0 _$:$<$2#n#[$}$|$>$h#!#!#1$2$3$)$! $ ",
+"; 3 4$5$6$;$h#h#h#h#7$N#0 0 0 0 0 8$9$0$a$b$c$d$0 0 0 0 0 0 e$f$g$h$i$}#j$k$h#h#h#h#l$m$n$*$! # ",
+"o$3 F#p$-$q$!#!#h#r$s$1+0 1+0 1+1+0 t$u$v$w$x$0 1+1+0 1+1+y$z$A${$T#B$C$D$E$F$h#!#!##$G$H$F#) @ ",
+"; 3 | I$J$g#!#K$L$M$N$O$0 1+0 1+1+0 1+P$Q$R$N#0 1+1+0 1+0 S$T$U$V$W$X$Y$Z$Y$M$L$K$!#`$ %.%| ' +%",
+"@%#%| $%%%)#h#&%*%Z$Z$=%-%0 0 0 0 0 0 0 a ;%0 0 0 0 0 0 >%,%'%)%!%Z$Z$Z$Z$Z$Z$*%&%h#~%{%]%| , ^%",
+" ) | /%(%_%h#:%Y$Y$Y$Y$<%[%0 1+1+0 1+1+0 1+1+0 1+1+0 }%|%1%2%Y$Y$Z$Y$Y$Z$Y$Y$Y$:%h#3%4%5%| > ",
+" , | 6%7%8%9%0%Z$Y$Y$Y$Z$a%b%1+1+0 1+1+0 1+1+0 1+1+0 c%d%e%Y$Y$Y$Z$Y$Y$Z$Y$Y$Z$0%g#f%g%h%| ; ",
+" ^ | i%j%k%l%Z$Z$Z$Z$Z$Z$Z$m%n%0 0 0 0 0 0 0 0 0 0 o%p%q%Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$r%s%t%u%} - ",
+" v%&+w%x%y%z%Y$Z$Y$Y$Y$Z$Y$Z$A%B%0 1+1+0 1+1+0 1+C%S$D%E%Z$Y$Y$Y$Z$Y$Y$Z$Y$Y$Z$F%G%H%I%w%[ J% ",
+" j.0 K%L%M%F%Z$Y$Y$Y$Z$Y$Z$Y$N%O%1+1+0 1+1+0 1+P%Q%R%Y$Z$Y$Y$Y$Z$Y$Y$Z$Y$Y$Z$S%T%U%V%0 < ",
+" < | W%X%Y%Z%Z$Z$Z$Z$Z$Z$Z$Z$Z$`% &0 0 0 0 0 N#.&+&Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$F%@&#&$&W%9 %& ",
+" &&0 *&=&-&;&Y$Y$Y$Z$Y$Z$Y$Y$Z$>&,&0 1+1+0 '&)&!&Y$Y$Z$Y$Y$Y$Z$Y$Y$Z$Y$Y$~&{&]&^&| > ",
+" < | | /&(&_&Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$:&<&0 0 0 [&}&Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$;&|&1&2&| 4 8 ",
+" -.| 3&4&5&6&F%Y$Z$Y$Z$Y$Y$Z$Y$Y$7&8&1+9&0&a&Z$Y$Y$Z$Y$Y$Y$Z$Y$Y$Z$;&G%b&c&d&3 j ",
+" v%9 0 e&f&g&Z%F%Z$Y$Z$Y$Y$Z$Y$Y$Z$h&i&j&k&Y$Z$Y$Y$Z$Y$Y$Y$Z$Y$Y$;&l&m&n&o&0 x p& ",
+" q&3 | r&s&t&6&Z$Y$Z$Y$Y$Z$Y$Y$Z$Y$u&v&F%Y$Z$Y$Y$Z$Y$Y$Y$Z$Y$;&@&w&x&y&| 4 z& ",
+" > 3 | A&B&C&D&;&Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$Z$E&F&G&H&I&| 9 K ",
+" J&3 | K&L&M&N&Z%F%Y$Z$Y$Y$Z$Y$Y$Z$Y$Y$Z$Y$Y$Z$Y$F%;&O&P&Q&R&S&| 9 Y ",
+" > 3 0 T&U&V&W&X&~&;&Y$Y$Z$Y$Y$Z$Y$Y$Z$Y$Y$Z$;&Y&Z&C&V&`&T&0 9 >. ",
+" @%9 | | *.*+*@*#*$*~&S%;&;&F%F%;&;&E&Y&%*&***+*=*-*| | j.;* ",
+" v%-.| 0 >*,*'*)*!*~*{*]*^*/*/*^*-#(*~*_*:*<*,*>*| &+H.[* ",
+" < &&| 0 | }*|*1*2*3*4*4*4*5*3*6*1*|*}*| 0 3 , u ",
+" < j.&+| | | | | >*7*7*>*| | | | | 9 5 6 ",
+" &$^ , ) #%3 3 3 | 3 3 9 P@t@8*u ",
+" @%; o$; &$6 d#< "};
--- /dev/null
+/* XPM */
+static const char * add_xpm[] = {
+"48 48 636 2",
+" c None",
+". c #D8DBD9",
+"+ c #D4D5D4",
+"@ c #D4D3D3",
+"# c #D6D5D5",
+"$ c #D5D4D4",
+"% c #D3D2D2",
+"& c #D3D4D2",
+"* c #D6D9D7",
+"= c #D5D6D5",
+"- c #DBDBDB",
+"; c #E5E4E4",
+"> c #ECECEC",
+", c #F2F2F2",
+"' c #F5F5F5",
+") c #F7F7F7",
+"! c #F8F7F7",
+"~ c #F6F6F6",
+"{ c #F5F4F4",
+"] c #F0F0F0",
+"^ c #EAE9E9",
+"/ c #E1E1E1",
+"( c #D7D7D7",
+"_ c #D0D1D0",
+": c #D9D9D9",
+"< c #E7E7E7",
+"[ c #F3F3F3",
+"} c #FAFAFA",
+"| c #FDFDFD",
+"1 c #F3F9F4",
+"2 c #E0F0E2",
+"3 c #FCFCFC",
+"4 c #F8F8F8",
+"5 c #EFEFEF",
+"6 c #E2E1E1",
+"7 c #D3D3D2",
+"8 c #DADADA",
+"9 c #F9F9F9",
+"0 c #FEFEFE",
+"a c #FCFDFC",
+"b c #D7EBDC",
+"c c #99CEAA",
+"d c #5CB17B",
+"e c #28994F",
+"f c #078938",
+"g c #008536",
+"h c #E6E5E5",
+"i c #D6D7D6",
+"j c #E8E7E7",
+"k c #F2F8F3",
+"l c #A6D5B4",
+"m c #41A564",
+"n c #0C8C3E",
+"o c #008736",
+"p c #038938",
+"q c #048939",
+"r c #058A3A",
+"s c #008735",
+"t c #F2F8F4",
+"u c #E0DFDF",
+"v c #CDCFCD",
+"w c #DCDDDC",
+"x c #F2F1F1",
+"y c #A9D7B6",
+"z c #2E9D56",
+"A c #008836",
+"B c #018937",
+"C c #0F9042",
+"D c #1C964C",
+"E c #269B54",
+"F c #2B9E59",
+"G c #2FA05B",
+"H c #30A15D",
+"I c #A9D6B6",
+"J c #D3D4D3",
+"K c #E1E0E0",
+"L c #DFF0E3",
+"M c #44A866",
+"N c #008936",
+"O c #088E3C",
+"P c #1C984C",
+"Q c #2DA15A",
+"R c #32A35E",
+"S c #31A45D",
+"T c #32A45E",
+"U c #31A35D",
+"V c #2DA05A",
+"W c #F1F1F1",
+"X c #D7D6D6",
+"Y c #E4E3E3",
+"Z c #B3DBBE",
+"` c #1C9749",
+" . c #008A36",
+".. c #058E3A",
+"+. c #1B994B",
+"@. c #2EA25B",
+"#. c #31A55D",
+"$. c #32A55E",
+"%. c #31A65D",
+"&. c #32A65E",
+"*. c #32A75E",
+"=. c #1C9649",
+"-. c #F3F2F2",
+";. c #D9D9D8",
+">. c #E2E2E2",
+",. c #A1D4AF",
+"'. c #0B903C",
+"). c #008C36",
+"!. c #0D9340",
+"~. c #2AA157",
+"{. c #31A75D",
+"]. c #31A85D",
+"^. c #31A95D",
+"/. c #31AA5D",
+"(. c #2AA057",
+"_. c #0B903D",
+":. c #A0D3B0",
+"<. c #DFDFDF",
+"[. c #A0D4AF",
+"}. c #018D36",
+"|. c #018E37",
+"1. c #169847",
+"2. c #30A55C",
+"3. c #32A75D",
+"4. c #32A95D",
+"5. c #32AA5D",
+"6. c #31AA5C",
+"7. c #32AB5D",
+"8. c #33AB5D",
+"9. c #6DC38B",
+"0. c #B2DFC2",
+"a. c #5BBA80",
+"b. c #23A454",
+"c. c #32AB5C",
+"d. c #30A45C",
+"e. c #DADBDA",
+"f. c #F4F4F4",
+"g. c #B3DCBF",
+"h. c #0B923C",
+"i. c #018F37",
+"j. c #189B4A",
+"k. c #30A75C",
+"l. c #34AC5D",
+"m. c #37AD5D",
+"n. c #3CAD5C",
+"o. c #40AE5D",
+"p. c #43AE5D",
+"q. c #9AD3A6",
+"r. c #FFFFFF",
+"s. c #73C395",
+"t. c #23A04E",
+"u. c #3EAE5C",
+"v. c #3AAD5D",
+"w. c #36AC5D",
+"x. c #33AC5D",
+"y. c #189A4A",
+"z. c #ECEBEB",
+"A. c #1D9B49",
+"B. c #009036",
+"C. c #169C47",
+"D. c #30A95C",
+"E. c #33AC5C",
+"F. c #37AD5C",
+"G. c #3EAD5C",
+"H. c #44AF5C",
+"I. c #49B05C",
+"J. c #4CB05C",
+"K. c #4EB15C",
+"L. c #4FB25C",
+"M. c #A0D5A6",
+"N. c #2AA24E",
+"O. c #4DB15C",
+"P. c #4BB05C",
+"Q. c #47AF5C",
+"R. c #42AE5C",
+"S. c #3BAD5C",
+"T. c #35AC5C",
+"U. c #30A85C",
+"V. c #169B47",
+"W. c #008F36",
+"X. c #1C9A49",
+"Y. c #FBFBFB",
+"Z. c #45AD66",
+"`. c #009236",
+" + c #0D9A40",
+".+ c #30AA5B",
+"++ c #43AE5C",
+"@+ c #49B05D",
+"#+ c #50B25C",
+"$+ c #53B35C",
+"%+ c #56B45B",
+"&+ c #58B45C",
+"*+ c #5AB55C",
+"=+ c #A5D7A6",
+"-+ c #2FA44D",
+";+ c #57B45B",
+">+ c #55B35C",
+",+ c #52B35C",
+"'+ c #4CB15C",
+")+ c #47AF5D",
+"!+ c #37AC5C",
+"~+ c #30A95B",
+"{+ c #0D9940",
+"]+ c #009136",
+"^+ c #44AC66",
+"/+ c #A9DAB6",
+"(+ c #009336",
+"_+ c #059739",
+":+ c #2AA856",
+"<+ c #39AD5D",
+"[+ c #4AB05D",
+"}+ c #56B45C",
+"|+ c #59B55C",
+"1+ c #5CB75C",
+"2+ c #5EB75B",
+"3+ c #60B85B",
+"4+ c #62B95B",
+"5+ c #AAD9A5",
+"6+ c #33A64D",
+"7+ c #5BB65C",
+"8+ c #58B55C",
+"9+ c #51B25C",
+"0+ c #3FAE5D",
+"a+ c #2AA756",
+"b+ c #A9D9B6",
+"c+ c #E1E1E0",
+"d+ c #F2F9F3",
+"e+ c #2EA755",
+"f+ c #009635",
+"g+ c #1EA34A",
+"h+ c #3FAE5C",
+"i+ c #5BB65B",
+"j+ c #5FB75B",
+"k+ c #61B95B",
+"l+ c #64BA5B",
+"m+ c #66BB5A",
+"n+ c #68BC5A",
+"o+ c #69BC5A",
+"p+ c #AEDBA5",
+"q+ c #37A84D",
+"r+ c #67BB5A",
+"s+ c #65BB5B",
+"t+ c #63B95B",
+"u+ c #5AB55B",
+"v+ c #55B45C",
+"w+ c #46AF5C",
+"x+ c #3AAD5C",
+"y+ c #1CA24A",
+"z+ c #009535",
+"A+ c #2EA656",
+"B+ c #A5D9B3",
+"C+ c #009634",
+"D+ c #109C3B",
+"E+ c #42AE5A",
+"F+ c #57B45C",
+"G+ c #5CB65C",
+"H+ c #5FB85B",
+"I+ c #69BC5B",
+"J+ c #6BBD5B",
+"K+ c #6DBE5B",
+"L+ c #6FBF5C",
+"M+ c #71BF5D",
+"N+ c #B2DDA7",
+"O+ c #3CA94E",
+"P+ c #6FBE5C",
+"Q+ c #6ABD5B",
+"R+ c #68BC5B",
+"S+ c #65BA5B",
+"T+ c #5EB85B",
+"U+ c #4AB05C",
+"V+ c #3DAD5A",
+"W+ c #0C9B3B",
+"X+ c #A5D9B4",
+"Y+ c #42B163",
+"Z+ c #109B34",
+"`+ c #33A74B",
+" @ c #54B35C",
+".@ c #66BB5B",
+"+@ c #70BF5D",
+"@@ c #72C05E",
+"#@ c #74C060",
+"$@ c #75C161",
+"%@ c #76C162",
+"&@ c #B5DDAA",
+"*@ c #3FAA50",
+"=@ c #75C060",
+"-@ c #74C05F",
+";@ c #72BF5E",
+">@ c #6CBD5B",
+",@ c #5DB75C",
+"'@ c #52B25C",
+")@ c #2FA64B",
+"!@ c #0B9A34",
+"~@ c #40B063",
+"{@ c #E6E6E5",
+"]@ c #D8EEDB",
+"^@ c #1B9E3C",
+"/@ c #52B02E",
+"(@ c #4EB158",
+"_@ c #5CB65B",
+":@ c #73C05F",
+"<@ c #77C162",
+"[@ c #79C264",
+"}@ c #7BC366",
+"|@ c #7DC467",
+"1@ c #B8DFAD",
+"2@ c #42AC53",
+"3@ c #7AC365",
+"4@ c #75C160",
+"5@ c #63BA5B",
+"6@ c #4BB058",
+"7@ c #4CAE2F",
+"8@ c #149D3D",
+"9@ c #D7EEDC",
+"0@ c #EFEEEE",
+"a@ c #A1D6A9",
+"b@ c #26A033",
+"c@ c #7CC034",
+"d@ c #75C056",
+"e@ c #67BC5A",
+"f@ c #76C161",
+"g@ c #78C263",
+"h@ c #7EC468",
+"i@ c #80C46A",
+"j@ c #81C56B",
+"k@ c #83C66D",
+"l@ c #BCE0AF",
+"m@ c #45AD56",
+"n@ c #7FC469",
+"o@ c #7DC367",
+"p@ c #6EBE5B",
+"q@ c #61B85B",
+"r@ c #73BF56",
+"s@ c #76BE35",
+"t@ c #219E33",
+"u@ c #9ED5A9",
+"v@ c #F6F5F5",
+"w@ c #6FC079",
+"x@ c #3CA830",
+"y@ c #8BC63D",
+"z@ c #94CC50",
+"A@ c #7EC469",
+"B@ c #84C76D",
+"C@ c #82C56A",
+"D@ c #7DC364",
+"E@ c #B6DDA8",
+"F@ c #43AC52",
+"G@ c #81C569",
+"H@ c #83C66C",
+"I@ c #80C56B",
+"J@ c #7DC468",
+"K@ c #7AC265",
+"L@ c #67BB5B",
+"M@ c #68BB5A",
+"N@ c #86C43E",
+"O@ c #37A631",
+"P@ c #6DBF7A",
+"Q@ c #47AD4E",
+"R@ c #51AF2E",
+"S@ c #93CA45",
+"T@ c #9CCF4F",
+"U@ c #88C853",
+"V@ c #6EBE5C",
+"W@ c #9AD28C",
+"X@ c #AFDBA7",
+"Y@ c #AEDBA8",
+"Z@ c #B0DBA9",
+"`@ c #B1DCAB",
+" # c #B2DCAC",
+".# c #B4DDAD",
+"+# c #B3DDAC",
+"@# c #AFDAA6",
+"## c #ABD8A1",
+"$# c #ABD8A0",
+"%# c #D1EACB",
+"&# c #B3DEC5",
+"*# c #91CE9B",
+"=# c #AEDAA6",
+"-# c #B3DDAB",
+";# c #B1DCAA",
+"># c #AFDBA9",
+",# c #ADDBA7",
+"'# c #ACDAA6",
+")# c #81C884",
+"!# c #4CB152",
+"~# c #87C753",
+"{# c #8EC846",
+"]# c #4CAD2F",
+"^# c #44AC4E",
+"/# c #D7DAD7",
+"(# c #E3E3E3",
+"_# c #33A436",
+":# c #62B52E",
+"<# c #97CC4A",
+"[# c #9BCE4E",
+"}# c #9BCE4F",
+"|# c #83C659",
+"1# c #CBE7C3",
+"2# c #9ED5B6",
+"3# c #34A645",
+"4# c #94CB4A",
+"5# c #5CB32F",
+"6# c #2FA236",
+"7# c #F5FAF4",
+"8# c #35A533",
+"9# c #6BB92D",
+"0# c #99CD4C",
+"a# c #9ACE4F",
+"b# c #D3EAC1",
+"c# c #39A744",
+"d# c #97CC4D",
+"e# c #66B72F",
+"f# c #31A334",
+"g# c #E4E4E4",
+"h# c #E5F3E2",
+"i# c #3AA733",
+"j# c #70BB2D",
+"k# c #9ACE4E",
+"l# c #D9EDBD",
+"m# c #98CD4E",
+"n# c #6BB92F",
+"o# c #37A633",
+"p# c #E4F3E2",
+"q# c #E6F3E2",
+"r# c #40AA32",
+"s# c #72BB2D",
+"t# c #D9ECBC",
+"u# c #98CD4F",
+"v# c #6CBA2F",
+"w# c #3DA833",
+"x# c #E3E2E2",
+"y# c #45AC32",
+"z# c #99CD4D",
+"A# c #D9ECBB",
+"B# c #6AB92E",
+"C# c #42AB32",
+"D# c #4EB036",
+"E# c #6CBA2E",
+"F# c #9BCF4F",
+"G# c #97CC47",
+"H# c #D2E8B0",
+"I# c #38A743",
+"J# c #95CB4A",
+"K# c #67B82F",
+"L# c #4BAE35",
+"M# c #D5D5D4",
+"N# c #E9E9E9",
+"O# c #FBFAFA",
+"P# c #67BA50",
+"Q# c #66B730",
+"R# c #9ACD4C",
+"S# c #88C42D",
+"T# c #A6D362",
+"U# c #B6DB88",
+"V# c #B2DA89",
+"W# c #B3DA8A",
+"X# c #B8DD97",
+"Y# c #BFE0A9",
+"Z# c #C1E1AE",
+"`# c #DDEFD3",
+" $ c #9DD3A2",
+".$ c #BFE0A8",
+"+$ c #B7DD96",
+"@$ c #8AC867",
+"#$ c #61B532",
+"$$ c #90C946",
+"%$ c #61B631",
+"&$ c #64B94E",
+"*$ c #DADCD9",
+"=$ c #8ECC7C",
+"-$ c #60B636",
+";$ c #8DC73D",
+">$ c #90C83A",
+",$ c #83C124",
+"'$ c #82C023",
+")$ c #86C22C",
+"!$ c #8EC741",
+"~$ c #98CC59",
+"{$ c #CAE5AB",
+"]$ c #4FB048",
+"^$ c #8EC740",
+"/$ c #88C53F",
+"($ c #5DB435",
+"_$ c #8DCB7C",
+":$ c #B8DFAC",
+"<$ c #5EB53E",
+"[$ c #82C235",
+"}$ c #98CD4A",
+"|$ c #85C228",
+"1$ c #84C128",
+"2$ c #BDDE8E",
+"3$ c #45AA30",
+"4$ c #7CC036",
+"5$ c #5BB43C",
+"6$ c #B7DEAB",
+"7$ c #E2F1DD",
+"8$ c #66B84B",
+"9$ c #73BC31",
+"0$ c #8CC636",
+"a$ c #BBDD87",
+"b$ c #45AA2F",
+"c$ c #8AC537",
+"d$ c #6EBA32",
+"e$ c #63B748",
+"f$ c #E2F1DC",
+"g$ c #E4E4E3",
+"h$ c #FDFDFC",
+"i$ c #86C770",
+"j$ c #6CBA3E",
+"k$ c #7FBF25",
+"l$ c #82C024",
+"m$ c #7ABE26",
+"n$ c #68B83D",
+"o$ c #84C66F",
+"p$ c #D6D6D5",
+"q$ c #C6E4BA",
+"r$ c #6BBA4C",
+"s$ c #79BD2A",
+"t$ c #80BF24",
+"u$ c #73BB2C",
+"v$ c #68B94A",
+"w$ c #C4E4BA",
+"x$ c #F6FAF4",
+"y$ c #84C56A",
+"z$ c #75BC41",
+"A$ c #7EBF25",
+"B$ c #7ABD26",
+"C$ c #71BB41",
+"D$ c #81C469",
+"E$ c #DAD9D9",
+"F$ c #F7F6F6",
+"G$ c #CBE6BE",
+"H$ c #76BD54",
+"I$ c #7ABE31",
+"J$ c #81C024",
+"K$ c #7EBF24",
+"L$ c #75BC32",
+"M$ c #74BC52",
+"N$ c #C9E5BE",
+"O$ c #98CE7D",
+"P$ c #7DC050",
+"Q$ c #7CBE28",
+"R$ c #77BD29",
+"S$ c #79BE4F",
+"T$ c #95CC7C",
+"U$ c #ECF5E7",
+"V$ c #8CC86B",
+"W$ c #80C149",
+"X$ c #7DBF25",
+"Y$ c #7CC049",
+"Z$ c #8AC669",
+"`$ c #EBF5E7",
+" % c #D5EBC9",
+".% c #8BC765",
+"+% c #82C243",
+"@% c #7BBE26",
+"#% c #7EC144",
+"$% c #89C564",
+"%% c #D4EAC9",
+"&% c #D8D8D6",
+"*% c #E9E9E8",
+"=% c #CEE7BE",
+"-% c #8FC964",
+";% c #86C445",
+">% c #A6D262",
+",% c #D0E7AC",
+"'% c #79C26B",
+")% c #5CB22B",
+"!% c #82C346",
+"~% c #8CC863",
+"{% c #CDE6BD",
+"]% c #DDDDDC",
+"^% c #D1E8BF",
+"/% c #99CE6D",
+"(% c #8EC851",
+"_% c #7DBF29",
+":% c #7FBF24",
+"<% c #7ABE2A",
+"[% c #8AC650",
+"}% c #97CD6C",
+"|% c #D0E8BF",
+"1% c #EEEEED",
+"2% c #DBEDCC",
+"3% c #A5D37A",
+"4% c #97CC5F",
+"5% c #81C135",
+"6% c #7CBE26",
+"7% c #7FC136",
+"8% c #95CB5F",
+"9% c #A3D278",
+"0% c #DAEDCC",
+"a% c #EFF7E9",
+"b% c #B3DA8E",
+"c% c #9FD06D",
+"d% c #90C951",
+"e% c #7EC02D",
+"f% c #7DBE25",
+"g% c #7BBF2E",
+"h% c #B2DA8D",
+"i% c #D9ECC7",
+"j% c #AED884",
+"k% c #A2D26F",
+"l% c #93CA55",
+"m% c #85C33A",
+"n% c #7DBF28",
+"o% c #7BBE29",
+"p% c #83C33B",
+"q% c #91CA56",
+"r% c #ADD784",
+"s% c #D8ECC7",
+"t% c #E0E0DF",
+"u% c #F8FBF5",
+"v% c #D9EDC7",
+"w% c #B6DB8F",
+"x% c #A8D476",
+"y% c #A2D16A",
+"z% c #97CC5A",
+"A% c #8EC84A",
+"B% c #87C43E",
+"C% c #82C236",
+"D% c #80C132",
+"E% c #8DC84A",
+"F% c #A1D16A",
+"G% c #A7D475",
+"H% c #B5DB8F",
+"I% c #DCDCDA",
+"J% c #ECF5E3",
+"K% c #D5EABF",
+"L% c #C2E1A0",
+"M% c #AED77F",
+"N% c #A7D473",
+"O% c #AAD575",
+"P% c #AAD574",
+"Q% c #ADD77F",
+"R% c #EEF6E7",
+"S% c #E6E6E6",
+" . + @ # $ % & * ",
+" = - ; > , ' ) ! ! ~ { ] ^ / ( _ ",
+" : < [ } | | | | 1 2 2 1 | | | 3 4 5 6 7 ",
+" 8 > 9 0 a b c d e f g g g g f e d c b a | ' h % ",
+" i j 4 | k l m n o o o p q r r q p o o s n m l t 3 [ u v ",
+" w x 3 | y z s A B C D E F G H H G F E D C B A s z I | } ^ J ",
+" K 4 0 L M N N O P Q R R S T T S T T S T R U V P O N A M L | W X ",
+" Y 9 | Z ` ...+.@.S #.$.$.%.&.&.%.*.&.%.$.$.#.$.S @.+... .=.Z | -.;. ",
+" >.9 | ,.'.).!.~.#.%.{.{.].^.^././././././.^.^.].{.%.#.S (.!.)._.:.| -.X ",
+" <.9 | [.}.|.1.2.3.].4.5.6.7.8.9.0.0.0.0.0.0.a.b.c.5.5.4.].*.d.1.|.}.[.| W J ",
+" e.f.0 g.h.i.j.k.^.5.c.l.m.n.o.p.q.r.r.0 r.r.0 s.t.u.v.w.x.7.5.4.k.y.|.h.g.| ^ v ",
+" z.| L A.B.C.D./.E.F.G.H.I.J.K.L.M.0 0 0 0 0 0 s.N.O.P.Q.R.S.T.c./.U.V.W.X.L } u ",
+" u Y.| Z.`. +.+x.v.++@+O.#+$+%+&+*+=+r.r.0 r.r.0 s.-+;+>+,+L.'+)+o.!+7.~+{+]+^+| [ % ",
+" , | /+(+_+:+<+p.[+K.,+}+|+1+2+3+4+5+r.r.0 r.r.0 s.6+3+2+7+8+>+9+O.Q.0+w.a+_+`.b+3 h ",
+" c+3 d+e+f+g+h+I.K.$+;+i+j+k+l+m+n+o+p+0 0 0 0 0 0 s.q+r+s+t+3+2+u+v+9+'+w+x+y+z+A+d+' 7 ",
+" 5 0 B+C+D+E+'+,+F+G+H+t+m+I+J+K+L+M+N+r.r.0 r.r.0 s.O+P+K+Q+R+S+4+T+u+v+#+U+V+W+f+X+| 6 ",
+" <.9 a Y+Z+`+K. @*+H+t+.@Q+K++@@@#@$@%@&@r.r.0 r.r.0 s.*@=@-@;@L+>@I+S+k+,@8+'@'+)@!@~@a 5 _ ",
+" {@| ]@^@/@(@}+_@k+s+I+K+M+:@$@<@[@}@|@1@r.r.0 r.r.0 s.2@3@[@<@4@@@L+>@n+5@H+u+ @6@7@8@9@4 ( ",
+" 0@| a@b@c@d@2+4+e@>@+@:@f@g@}@h@i@j@k@l@0 0 0 0 0 0 s.m@j@n@o@3@<@4@@@p@Q+m+q@i+r@s@t@u@3 / ",
+" v@| w@x@y@z@o+o+p@@@4@g@}@A@j@k@B@C@D@E@r.r.0 r.r.0 s.F@G@k@H@I@J@K@<@#@+@>@L@M@z@N@O@P@| ^ ",
+"< 9 | Q@R@S@T@U@V@W@X@Y@Z@`@ #.#+#@###$#%#r.r.0 r.r.0 &#*###=#-#+# #;#>#,#'#)#!#~#T@{#]#^#| ] /#",
+"(#3 | _#:#<#[#}#|#1#0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2#3#}#[#4#5#6#| { & ",
+"6 3 7#8#9#0#T@T@a#b#r.r.0 r.0 r.r.0 r.r.0 r.r.0 r.r.0 r.r.0 r.r.r.0 r.r.0 r.2#c#T@T@d#e#f#7#~ % ",
+"g#| h#i#j#k#T@T@[#l#r.r.0 r.0 r.r.0 r.r.0 r.r.0 r.r.0 r.r.0 r.r.r.0 r.r.0 r.2#c#T@T@m#n#o#p#! $ ",
+"; 3 q#r#s#k#[#[#[#t#0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2#c#[#[#u#v#w#h#! # ",
+"x#3 7#y#j#z#T@T@[#A#r.r.0 r.0 r.r.0 r.r.0 r.r.0 r.r.0 r.r.0 r.r.r.0 r.r.0 r.2#c#T@T@d#B#C#7#) @ ",
+"; 3 | D#E#<#T@F#G#H#r.r.0 r.0 r.r.0 r.r.0 r.r.0 r.r.0 r.r.0 r.r.r.0 r.r.0 r.2#I#F#T@J#K#L#| ' M#",
+"N#O#| P#Q#S@[#R#S#T#U#V#V#V#V#W#X#Y#Z#Z#`#0 0 0 0 0 0 &# $Z#.$+$W#V#V#V#V#V#@$#$R#[#$$%$&$| , *$",
+" ) | =$-$;$[#>$,$,$,$,$'$,$'$,$,$)$!$~${$r.r.0 r.r.0 s.]$^$)$,$,$'$,$,$'$,$,$,$>$[#/$($_$| > ",
+" , | :$<$[$}$|$'$,$,$,$'$,$'$,$,$'$,$1$2$r.r.0 r.r.0 s.3$'$,$,$,$'$,$,$'$,$,$'$|$<#4$5$6$| ; ",
+" ^ | 7$8$9$0$'$'$'$'$'$'$'$'$'$'$'$'$'$a$0 0 0 0 0 0 s.b$'$'$'$'$'$'$'$'$'$'$'$'$c$d$e$f$} - ",
+" g$Y.h$i$j$k$,$'$,$,$,$'$,$'$,$,$'$,$,$a$r.r.0 r.r.0 s.b$'$,$,$,$'$,$,$'$,$,$'$l$m$n$o$h$[ p$ ",
+" f.0 q$r$s$l$'$,$,$,$'$,$'$,$,$'$,$,$a$r.r.0 r.r.0 s.b$'$,$,$,$'$,$,$'$,$,$'$t$u$v$w$0 < ",
+" < | x$y$z$A$'$'$'$'$'$'$'$'$'$'$'$'$a$0 0 0 0 0 0 s.b$'$'$'$'$'$'$'$'$'$'$l$B$C$D$x$9 E$ ",
+" F$0 G$H$I$J$,$,$,$'$,$'$,$,$'$,$,$a$r.r.0 r.r.0 s.b$'$,$,$,$'$,$,$'$,$,$K$L$M$N$| > ",
+" < | | O$P$Q$'$'$'$'$'$'$'$'$'$'$'$a$0 0 0 0 0 0 s.b$'$'$'$'$'$'$'$'$'$J$R$S$T$| 4 8 ",
+" -.| U$V$W$X$l$,$'$,$'$,$,$'$,$,$a$r.r.0 r.r.0 s.b$'$,$,$,$'$,$,$'$J$m$Y$Z$`$3 j ",
+" g$9 0 %.%+%A$l$'$,$'$,$,$'$,$,$a$r.r.0 r.r.0 s.b$'$,$,$,$'$,$,$J$@%#%$%%%0 x &% ",
+" *%3 | =%-%;%X$'$,$'$,$,$'$,$,$>%,%,%,%,%,%,%'%)%'$,$,$,$'$,$J$B$!%~%{%| 4 ]% ",
+" > 3 | ^%/%(%_%J$'$'$'$'$'$'$'$'$'$'$'$'$'$'$'$'$'$'$'$'$:%<%[%}%|%| 9 K ",
+" 1%3 | 2%3%4%5%A$l$,$'$,$,$'$,$,$'$,$,$'$,$,$'$,$l$J$6%7%8%9%0%| 9 Y ",
+" > 3 0 a%b%c%d%e%K$J$,$,$'$,$,$'$,$,$'$,$,$'$J$f%g%(%c%h%a%0 9 >. ",
+" N#9 | | i%j%k%l%m%n%K$t$J$J$l$l$J$J$:%f%o%p%q%k%r%s%| | f.t% ",
+" g$-.| 0 u%v%w%x%y%z%A%B%C%D%D%C%N@E%z%F%G%H%v%u%| Y.z.I% ",
+" < F$| 0 | J%K%L%M%N%O%O%O%P%N%Q%L%K%J%| 0 3 , u ",
+" < f.Y.| | | | | u%R%R%u%| | | | | 9 5 6 ",
+" g#^ , ) O#3 3 3 | 3 3 9 v@0@S%u ",
+" N#; x#; g#6 (#< "};
--- /dev/null
+/* XPM */
+static const char * create_database_xpm[] = {
+"48 48 816 2",
+" c None",
+". c #BAC3C7",
+"+ c #B4BDC2",
+"@ c #B1BABF",
+"# c #B5BEC3",
+"$ c #BAC2C6",
+"% c #BDC5C9",
+"& c #BAC2C7",
+"* c #B6BFC3",
+"= c #B2BBC0",
+"- c #ABB5BA",
+"; c #B8C1C7",
+"> c #AEB8BE",
+", c #C8CFD3",
+"' c #D9DDE0",
+") c #E7EAEC",
+"! c #E9ECED",
+"~ c #E6E9EB",
+"{ c #DDE1E4",
+"] c #D1D7DA",
+"^ c #C8CFD2",
+"/ c #C4CBCF",
+"( c #C0C8CC",
+"_ c #BEC6CB",
+": c #C0C7CC",
+"< c #C2CACE",
+"[ c #C5CCD0",
+"} c #CCD2D6",
+"| c #D7DDE0",
+"1 c #E3E7E9",
+"2 c #E8EBED",
+"3 c #DDE1E3",
+"4 c #C9D0D3",
+"5 c #ACB6BC",
+"6 c #B2BBC1",
+"7 c #EEF0F2",
+"8 c #D7DCDF",
+"9 c #B8C0C5",
+"0 c #A1ADB3",
+"a c #A2AEB4",
+"b c #A6B1B8",
+"c c #C1C9CE",
+"d c #C8D0D4",
+"e c #C8D0D6",
+"f c #C6CFD5",
+"g c #C0CBD2",
+"h c #BBC6CE",
+"i c #B4C1C9",
+"j c #ACBAC2",
+"k c #A2B1BA",
+"l c #99A9B2",
+"m c #92A2AC",
+"n c #94A3AC",
+"o c #9BA8AF",
+"p c #ADB8BE",
+"q c #CFD5D9",
+"r c #EAEDEF",
+"s c #E0E4E6",
+"t c #B9C2C7",
+"u c #B8C1C6",
+"v c #EEF0F1",
+"w c #D6DBDF",
+"x c #A8B3B9",
+"y c #9AA8B0",
+"z c #B4BEC6",
+"A c #CED6DC",
+"B c #E6EBEF",
+"C c #ECF0F3",
+"D c #F0F3F5",
+"E c #F3F6F7",
+"F c #F3F6F8",
+"G c #F1F4F6",
+"H c #EDF1F4",
+"I c #E9EEF1",
+"J c #E3E9EE",
+"K c #DFE6EB",
+"L c #D9E2E8",
+"M c #D4DEE5",
+"N c #D0DBE2",
+"O c #CBD7DF",
+"P c #C5D3DC",
+"Q c #BFCFD9",
+"R c #B9CAD5",
+"S c #B4C7D2",
+"T c #AEC1CF",
+"U c #A3B7C4",
+"V c #96A8B5",
+"W c #899BA6",
+"X c #C8CFD4",
+"Y c #F0F2F3",
+"Z c #BCC4C8",
+"` c #CED4D8",
+" . c #EBEEEF",
+".. c #A6B2B8",
+"+. c #ABB8C0",
+"@. c #CCD6DD",
+"#. c #D8E1E7",
+"$. c #E0E6EB",
+"%. c #E4EAEE",
+"&. c #EAEFF2",
+"*. c #EEF1F4",
+"=. c #F8F9FA",
+"-. c #F9FAFB",
+";. c #F4F6F8",
+">. c #EEF2F5",
+",. c #EBEFF2",
+"'. c #E4EAEF",
+"). c #E0E7EC",
+"!. c #DAE3E8",
+"~. c #C6D3DD",
+"{. c #B5C7D3",
+"]. c #AFC2CF",
+"^. c #ABBFCD",
+"/. c #A8BCCB",
+"(. c #A5B9C8",
+"_. c #9FB4C1",
+":. c #91A5B1",
+"<. c #94A4AD",
+"[. c #DDE2E5",
+"}. c #D8DDDF",
+"|. c #FCFCFC",
+"1. c #B0BBC2",
+"2. c #C2CED6",
+"3. c #CFDAE2",
+"4. c #EFF2F5",
+"5. c #A2B7C5",
+"6. c #9EB4C2",
+"7. c #99AFBD",
+"8. c #8FA2AD",
+"9. c #FCFCFD",
+"0. c #FDFDFD",
+"a. c #D0DAE1",
+"b. c #CAD7DF",
+"c. c #EDF0F3",
+"d. c #E0E6EC",
+"e. c #A7BCCB",
+"f. c #A4BAC8",
+"g. c #A1B7C5",
+"h. c #9DB4C2",
+"i. c #9AB1C0",
+"j. c #FAFBFB",
+"k. c #D5DEE3",
+"l. c #C1CED4",
+"m. c #F5F8F9",
+"n. c #D5DFE5",
+"o. c #CED9E1",
+"p. c #D3DDE4",
+"q. c #D6E0E6",
+"r. c #DCE4EA",
+"s. c #E2E8ED",
+"t. c #E5EBEF",
+"u. c #EDF1F3",
+"v. c #E6ECEF",
+"w. c #DDE5EA",
+"x. c #D7E1E7",
+"y. c #D3DDE5",
+"z. c #CAD6DE",
+"A. c #C4D2DC",
+"B. c #BECED8",
+"C. c #B8C9D5",
+"D. c #B4C6D2",
+"E. c #A7BBCA",
+"F. c #A4B9C8",
+"G. c #A8BCC9",
+"H. c #E3E9ED",
+"I. c #E8EDF1",
+"J. c #628293",
+"K. c #ADBEC8",
+"L. c #F4F6F7",
+"M. c #ECF1F4",
+"N. c #D5DFE6",
+"O. c #D2DCE3",
+"P. c #CCD8E0",
+"Q. c #C9D5DE",
+"R. c #C2D1DB",
+"S. c #BCCCD7",
+"T. c #B7C9D4",
+"U. c #B2C4D1",
+"V. c #ADC0CE",
+"W. c #AABECC",
+"X. c #A6BBCA",
+"Y. c #B0C2CE",
+"Z. c #F5F7F9",
+"`. c #DDE5EB",
+" + c #AABDCB",
+".+ c #5E7F91",
+"++ c #628394",
+"@+ c #728FA0",
+"#+ c #AABCC6",
+"$+ c #E1E7EB",
+"%+ c #F7F9FA",
+"&+ c #F0F3F6",
+"*+ c #E6EBF0",
+"=+ c #E5EAEF",
+"-+ c #DBE4E9",
+";+ c #C0D0D9",
+">+ c #BACBD6",
+",+ c #B6C8D3",
+"'+ c #B7C8D4",
+")+ c #C8D5DE",
+"!+ c #DAE3E9",
+"~+ c #E3EAEE",
+"{+ c #C3D2DB",
+"]+ c #ADC1CE",
+"^+ c #A4B9C7",
+"/+ c #668698",
+"(+ c #6A899B",
+"_+ c #7391A1",
+":+ c #87A1B0",
+"<+ c #ABBDC8",
+"[+ c #CFD9E0",
+"}+ c #E7ECEF",
+"|+ c #F2F5F7",
+"1+ c #F3F5F7",
+"2+ c #EFF3F5",
+"3+ c #EBF0F3",
+"4+ c #E9EEF2",
+"5+ c #F6F8FA",
+"6+ c #DFE7EC",
+"7+ c #658697",
+"8+ c #6E8D9E",
+"9+ c #7291A2",
+"0+ c #7794A6",
+"a+ c #7B98A9",
+"b+ c #829DAE",
+"c+ c #91AAB9",
+"d+ c #AFC2CD",
+"e+ c #BECDD6",
+"f+ c #E6ECF0",
+"g+ c #FBFCFC",
+"h+ c #D2DDE4",
+"i+ c #BACBD5",
+"j+ c #819DAE",
+"k+ c #88A3B3",
+"l+ c #8EA7B7",
+"m+ c #93ACBB",
+"n+ c #99B0BF",
+"o+ c #A0B6C4",
+"p+ c #A6BBC9",
+"q+ c #ACBFCE",
+"r+ c #C1D0DA",
+"s+ c #FAFBFC",
+"t+ c #D8E2E8",
+"u+ c #93ABBB",
+"v+ c #A6BAC9",
+"w+ c #D6DFE6",
+"x+ c #728D9D",
+"y+ c #7693A3",
+"z+ c #6E8D9F",
+"A+ c #B6C5CF",
+"B+ c #AAB5BB",
+"C+ c #CAD1D5",
+"D+ c #788D99",
+"E+ c #698594",
+"F+ c #7390A1",
+"G+ c #D9E0E6",
+"H+ c #B4C0C8",
+"I+ c #9EACB5",
+"J+ c #C2CACF",
+"K+ c #ECEFF0",
+"L+ c #EBEDEF",
+"M+ c #909EA5",
+"N+ c #607580",
+"O+ c #4B6370",
+"P+ c #47606D",
+"Q+ c #526C7A",
+"R+ c #5F7988",
+"S+ c #6C8695",
+"T+ c #7B94A3",
+"U+ c #859EAD",
+"V+ c #8FA6B5",
+"W+ c #9AB0BE",
+"X+ c #A3B8C7",
+"Y+ c #DCE4E9",
+"Z+ c #E0E6EA",
+"`+ c #DEE4E7",
+" @ c #DBE0E3",
+".@ c #CDD4D8",
+"+@ c #ADB9BF",
+"@@ c #8D9DA6",
+"#@ c #6E828D",
+"$@ c #4B6471",
+"%@ c #49626E",
+"&@ c #5C727D",
+"*@ c #819199",
+"=@ c #DDE2E4",
+"-@ c #D4D9DC",
+";@ c #8F9EA6",
+">@ c #5C7380",
+",@ c #576F7C",
+"'@ c #57707D",
+")@ c #58717E",
+"!@ c #5B7481",
+"~@ c #5E7784",
+"{@ c #607986",
+"]@ c #627A88",
+"^@ c #667E8B",
+"/@ c #6A818E",
+"(@ c #6A818D",
+"_@ c #687F8B",
+":@ c #677E8A",
+"<@ c #657C89",
+"[@ c #617985",
+"}@ c #5C7481",
+"|@ c #5A727E",
+"1@ c #768993",
+"2@ c #FEFEFE",
+"3@ c #889AA4",
+"4@ c #687F8C",
+"5@ c #728793",
+"6@ c #F9FAFA",
+"7@ c #D4DEDD",
+"8@ c #C6D1D8",
+"9@ c #E7EBED",
+"0@ c #94A5AF",
+"a@ c #7E929E",
+"b@ c #8295A1",
+"c@ c #ADB6BD",
+"d@ c #BEC5C9",
+"e@ c #C6CBCF",
+"f@ c #BDC4C8",
+"g@ c #CED2D5",
+"h@ c #E9EBEC",
+"i@ c #ABC1C0",
+"j@ c #648395",
+"k@ c #B4C3CC",
+"l@ c #EDF0F2",
+"m@ c #D2D9DE",
+"n@ c #A7B5BE",
+"o@ c #97A8B3",
+"p@ c #93A5B0",
+"q@ c #9AA9B3",
+"r@ c #BCC5CA",
+"s@ c #E3E4E6",
+"t@ c #F3F4F4",
+"u@ c #E5F1E8",
+"v@ c #D4EADB",
+"w@ c #B9DDC3",
+"x@ c #B7DCC1",
+"y@ c #D2E8D9",
+"z@ c #E2EFE5",
+"A@ c #EFF1F0",
+"B@ c #E2E3E3",
+"C@ c #C7D1CB",
+"D@ c #7B97A6",
+"E@ c #B2C2CB",
+"F@ c #E9EEF0",
+"G@ c #D7DEE3",
+"H@ c #C1CDD4",
+"I@ c #AFBEC8",
+"J@ c #A4B5C1",
+"K@ c #A3B4BF",
+"L@ c #A3B4C0",
+"M@ c #BBC5CC",
+"N@ c #E9EAEB",
+"O@ c #F5F9F5",
+"P@ c #A2D3B1",
+"Q@ c #48A86C",
+"R@ c #128F41",
+"S@ c #018636",
+"T@ c #008636",
+"U@ c #0F8D3F",
+"V@ c #43A567",
+"W@ c #98CEA9",
+"X@ c #EEF3EF",
+"Y@ c #E5E4E4",
+"Z@ c #6F8E9F",
+"`@ c #8AA4B2",
+" # c #B0C1CC",
+".# c #F2F4F6",
+"+# c #E8EDF0",
+"@# c #DBE2E8",
+"## c #D6DEE4",
+"$# c #D3DCE3",
+"%# c #D2DBE2",
+"&# c #D1DAE1",
+"*# c #D5DEE4",
+"=# c #D8E0E5",
+"-# c #F7F8F7",
+";# c #B0DABC",
+"># c #21974D",
+",# c #038A38",
+"'# c #109142",
+")# c #20994F",
+"!# c #2A9F57",
+"~# c #2FA15C",
+"{# c #30A25C",
+"]# c #2CA059",
+"^# c #249B53",
+"/# c #169447",
+"(# c #078B3B",
+"_# c #199346",
+":# c #A4D4B2",
+"<# c #EFF0EF",
+"[# c #CCD3CE",
+"}# c #7C98A9",
+"|# c #86A1B1",
+"1# c #94ABBA",
+"2# c #A4B9C6",
+"3# c #B4C5D0",
+"4# c #C2D0D9",
+"5# c #CFDAE1",
+"6# c #D8E1E8",
+"7# c #ECF1F3",
+"8# c #F2F2F2",
+"9# c #F5F9F6",
+"0# c #75C08C",
+"a# c #078E3B",
+"b# c #129444",
+"c# c #29A057",
+"d# c #31A55D",
+"e# c #32A65E",
+"f# c #32A75E",
+"g# c #31A65D",
+"h# c #32A55E",
+"i# c #31A45D",
+"j# c #2EA25A",
+"k# c #1C994C",
+"l# c #098E3C",
+"m# c #66B881",
+"n# c #ECF1ED",
+"o# c #CBD4CE",
+"p# c #E5E9EB",
+"q# c #FBFBFB",
+"r# c #69BB82",
+"s# c #048F38",
+"t# c #1D9C4D",
+"u# c #32A95D",
+"v# c #33AA5D",
+"w# c #36AB5D",
+"x# c #A3D9B6",
+"y# c #AFDEC1",
+"z# c #AFDEC0",
+"A# c #A0D7B5",
+"B# c #2BA65A",
+"C# c #32A85D",
+"D# c #28A056",
+"E# c #0A913D",
+"F# c #57B375",
+"G# c #F3F4F3",
+"H# c #C7CFC9",
+"I# c #DAE1E6",
+"J# c #F8F8F8",
+"K# c #90CEA2",
+"L# c #029037",
+"M# c #21A04F",
+"N# c #34AA5D",
+"O# c #3AAD5D",
+"P# c #41AE5C",
+"Q# c #47B05C",
+"R# c #4DB15D",
+"S# c #E9F5EB",
+"T# c #E6F4EC",
+"U# c #32A658",
+"V# c #45AF5C",
+"W# c #3FAE5C",
+"X# c #38AC5D",
+"Y# c #2CA459",
+"Z# c #09923D",
+"`# c #7AC391",
+" $ c #EBECEB",
+".$ c #EBEEF0",
+"+$ c #D6EDDB",
+"@$ c #119941",
+"#$ c #1BA04A",
+"$$ c #3CAD5C",
+"%$ c #50B25C",
+"&$ c #56B45C",
+"*$ c #5AB65B",
+"=$ c #5EB85C",
+"-$ c #EBF6EB",
+";$ c #FFFFFF",
+">$ c #3AA957",
+",$ c #59B55B",
+"'$ c #54B35C",
+")$ c #4DB15C",
+"!$ c #43AE5C",
+"~$ c #39AC5D",
+"{$ c #29A556",
+"]$ c #0D963E",
+"^$ c #C6E4CE",
+"/$ c #D9DBD9",
+"($ c #62BC7D",
+"_$ c #0E9B3C",
+":$ c #42AE5B",
+"<$ c #58B55B",
+"[$ c #5FB85B",
+"}$ c #65BB5B",
+"|$ c #69BC5B",
+"1$ c #6DBE5D",
+"2$ c #EDF7EB",
+"3$ c #41AC57",
+"4$ c #68BB5B",
+"5$ c #63BA5B",
+"6$ c #5DB75B",
+"7$ c #4CB15C",
+"8$ c #3FAE5D",
+"9$ c #1BA049",
+"0$ c #4DB36E",
+"a$ c #F2F1F1",
+"b$ c #6E8A99",
+"c$ c #718EA0",
+"d$ c #E2E7EA",
+"e$ c #E2F2E4",
+"f$ c #29A440",
+"g$ c #3BAA4D",
+"h$ c #56B45B",
+"i$ c #67BB5B",
+"j$ c #6DBE5C",
+"k$ c #72BF5F",
+"l$ c #76C061",
+"m$ c #79C264",
+"n$ c #EEF7EC",
+"o$ c #47AE5A",
+"p$ c #74C060",
+"q$ c #70BF5D",
+"r$ c #6BBD5B",
+"s$ c #64BA5B",
+"t$ c #5CB75B",
+"u$ c #52B35C",
+"v$ c #40AD59",
+"w$ c #20A23E",
+"x$ c #D1EAD7",
+"y$ c #CDD5CD",
+"z$ c #A6B2B9",
+"A$ c #E1E5E7",
+"B$ c #7B909D",
+"C$ c #6B8797",
+"D$ c #728E9F",
+"E$ c #7B98A8",
+"F$ c #C2D0DA",
+"G$ c #EFF1F3",
+"H$ c #ADDBB4",
+"I$ c #52B030",
+"J$ c #6BBC54",
+"K$ c #78C163",
+"L$ c #7CC367",
+"M$ c #80C56A",
+"N$ c #83C66C",
+"O$ c #EFF8ED",
+"P$ c #4BB05E",
+"Q$ c #7FC469",
+"R$ c #7BC265",
+"S$ c #76C161",
+"T$ c #60B85B",
+"U$ c #65BA59",
+"V$ c #56B33D",
+"W$ c #93D1A0",
+"X$ c #E2E1E1",
+"Y$ c #EFF1F2",
+"Z$ c #9AA7AD",
+"`$ c #627580",
+" % c #4C6370",
+".% c #435C69",
+"+% c #6E8897",
+"@% c #7D96A5",
+"#% c #89A2B2",
+"$% c #96ADBC",
+"%% c #F8F8F9",
+"&% c #82C786",
+"*% c #73BC32",
+"=% c #93CB50",
+"-% c #78C365",
+";% c #A7D89C",
+">% c #ACD9A2",
+",% c #B0DBA5",
+"'% c #B2DCA7",
+")% c #B1DBA5",
+"!% c #AFDAA1",
+"~% c #F5FAF3",
+"{% c #F0F8F4",
+"]% c #90CE9B",
+"^% c #AFDAA2",
+"/% c #AEDAA4",
+"(% c #ABD8A0",
+"_% c #A5D79C",
+":% c #5EB862",
+"<% c #8ECA51",
+"[% c #79C044",
+"}% c #70C07C",
+"|% c #EDECEC",
+"1% c #8E9DA5",
+"2% c #58707D",
+"3% c #536C79",
+"4% c #627A87",
+"5% c #677E8B",
+"6% c #82959F",
+"7% c #76C172",
+"8% c #82C338",
+"9% c #9BCE4E",
+"0% c #A2D36E",
+"a% c #F8FCF7",
+"b% c #F9FCFB",
+"c% c #5BB66A",
+"d% c #86C64A",
+"e% c #5AB557",
+"f% c #FDFEFE",
+"g% c #8497A1",
+"h% c #647C89",
+"i% c #647B88",
+"j% c #80939D",
+"k% c #72C063",
+"l% c #86C43A",
+"m% c #ABD669",
+"n% c #FAFCF6",
+"o% c #5CB76A",
+"p% c #8BC84C",
+"q% c #57B44C",
+"r% c #F3F3F3",
+"s% c #CFD8DE",
+"t% c #E1E6E8",
+"u% c #889AA5",
+"v% c #778C99",
+"w% c #8C9DA8",
+"x% c #81C771",
+"y% c #85C439",
+"z% c #AAD568",
+"A% c #FAFCF5",
+"B% c #5BB76A",
+"C% c #8AC74B",
+"D% c #66BB55",
+"E% c #638294",
+"F% c #BBC9D1",
+"G% c #CAD3D8",
+"H% c #A1B0B9",
+"I% c #90A2AD",
+"J% c #8EA1AC",
+"K% c #8FA1AC",
+"L% c #99A9B3",
+"M% c #F6F7F8",
+"N% c #97D087",
+"O% c #7FC136",
+"P% c #96CB45",
+"Q% c #B4DA84",
+"R% c #B3DA89",
+"S% c #B4DB8C",
+"T% c #BADE9D",
+"U% c #C1E1AC",
+"V% c #F7FBF5",
+"W% c #F1F9F5",
+"X% c #A0D5A6",
+"Y% c #BBDE9E",
+"Z% c #B4DB8D",
+"`% c #B0D987",
+" & c #6EBB43",
+".& c #99CD4B",
+"+& c #82C447",
+"@& c #8ACA7A",
+"#& c #EFEFEF",
+"$& c #638495",
+"%& c #7F9AA9",
+"&& c #B9C7D0",
+"*& c #E8ECEF",
+"=& c #E8ECEE",
+"-& c #D1D9DE",
+";& c #BBC7CF",
+">& c #ABB9C4",
+",& c #A5B5C0",
+"'& c #A1B2BD",
+")& c #A0B1BC",
+"!& c #A3B3BE",
+"~& c #BFE2B4",
+"{& c #73BC36",
+"]& c #95CB44",
+"^& c #83C124",
+"/& c #82C023",
+"(& c #83C126",
+"_& c #89C434",
+":& c #F1F8E7",
+"<& c #4EAF41",
+"[& c #8CC637",
+"}& c #76BF42",
+"|& c #ACD99F",
+"1& c #E7E6E6",
+"2& c #7593A3",
+"3& c #94ABB8",
+"4& c #B7C7D0",
+"5& c #DBE3E8",
+"6& c #EBEFF1",
+"7& c #E2E8EC",
+"8& c #DBE2E7",
+"9& c #D5DDE3",
+"0& c #CFD9DF",
+"a& c #CDD7DE",
+"b& c #CBD5DD",
+"c& c #C9D4DC",
+"d& c #E9ECEF",
+"e& c #E8F4E3",
+"f& c #73BD49",
+"g& c #85C22A",
+"h& c #83C125",
+"i& c #EFF7E4",
+"j& c #4CAE3D",
+"k& c #7FC028",
+"l& c #6ABA43",
+"m& c #DCEED6",
+"n& c #D7DDD3",
+"o& c #92AAB9",
+"p& c #A9BCC9",
+"q& c #B9C9D3",
+"r& c #C7D4DC",
+"s& c #E1E8ED",
+"t& c #E7EDF0",
+"u& c #9BD084",
+"v& c #7CBE2E",
+"w& c #74BC2D",
+"x& c #8CCA75",
+"y& c #F6F6F6",
+"z& c #F0F2F4",
+"A& c #E2F1DB",
+"B& c #7EC150",
+"C& c #81C025",
+"D& c #82C124",
+"E& c #7DBF25",
+"F& c #74BD48",
+"G& c #D7ECCF",
+"H& c #E4E5E2",
+"I& c #DDE4E9",
+"J& c #BCDFA9",
+"K& c #81C13F",
+"L& c #82C024",
+"M& c #80C024",
+"N& c #78BE3B",
+"O& c #AED89A",
+"P& c #607F91",
+"Q& c #EBEEF1",
+"R& c #AFD993",
+"S& c #86C441",
+"T& c #CBE5A8",
+"U& c #D0E9BB",
+"V& c #C1E2AE",
+"W& c #5DB435",
+"X& c #7FBF24",
+"Y& c #7DC03D",
+"Z& c #A4D387",
+"`& c #F8F9F7",
+" * c #DADDD5",
+".* c #6D8491",
+"+* c #638394",
+"@* c #F2F4F5",
+"#* c #F9FBF7",
+"$* c #BADE9E",
+"%* c #90C950",
+"&* c #82C128",
+"** c #7EBF28",
+"=* c #87C54B",
+"-* c #B1DA94",
+";* c #F4F8F1",
+">* c #E1E4DC",
+",* c #728792",
+"'* c #F5F6F7",
+")* c #FBFCFA",
+"!* c #D7EBC4",
+"~* c #9FD06C",
+"{* c #8BC642",
+"]* c #81C027",
+"^* c #81C024",
+"/* c #7EBF27",
+"(* c #86C440",
+"_* c #99CD66",
+":* c #D1E9BD",
+"<* c #E2E5DE",
+"[* c #758893",
+"}* c #738A97",
+"|* c #738F9E",
+"1* c #7793A4",
+"2* c #FAFAFB",
+"3* c #D2E9BB",
+"4* c #B3DA8B",
+"5* c #98CD5D",
+"6* c #91C94D",
+"7* c #8DC746",
+"8* c #8CC746",
+"9* c #8FC84D",
+"0* c #95CB5B",
+"a* c #B0D886",
+"b* c #CEE7B6",
+"c* c #F4F9F1",
+"d* c #778B96",
+"e* c #798E9A",
+"f* c #7C929F",
+"g* c #8399A7",
+"h* c #89A1B0",
+"i* c #90A8B7",
+"j* c #97ADBC",
+"k* c #DEE5EA",
+"l* c #E5EAEE",
+"m* c #EAEDF0",
+"n* c #E3E6E8",
+"o* c #D5DADC",
+"p* c #F0F1F2",
+"q* c #FAFBF9",
+"r* c #F2F8ED",
+"s* c #E6F3DA",
+"t* c #D8ECC6",
+"u* c #D7ECC5",
+"v* c #E5F2D8",
+"w* c #F2F8EC",
+"x* c #F8FAF7",
+"y* c #F4F4F3",
+"z* c #E2E4DF",
+"A* c #8496A2",
+"B* c #82949E",
+"C* c #81929C",
+"D* c #83949E",
+"E* c #8797A1",
+"F* c #8B9BA4",
+"G* c #8D9CA4",
+"H* c #8D9BA4",
+"I* c #8B99A1",
+"J* c #A4AFB6",
+"K* c #E7EDDF",
+"L* c #EDEDEB",
+"M* c #EFEEEE",
+"N* c #EBEBEB",
+"O* c #E4E9DD",
+" . + @ # $ % & * = - ; ",
+" > , ' ) ! ~ { ] ^ / ( _ : < [ } | 1 2 ) 3 4 5 ",
+" 6 ' 7 8 9 0 a b > ; c d e e f g h i j k l m n o p q r s t ",
+" u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W y X Y Z ",
+" ` ...+.@.#.$.%.&.*.E =.-.;.>.,.'.).!.M N O ~.Q R {.].^./.(._.:.<.[.}. ",
+" |.1.2.3.M L $.%.&.*.F -.-.;.4.,.'.).!.M N O ~.Q R {.].^./.(.5.6.7.8.9. ",
+" 0.a.b.3.M #.K J I c.G ;.;.G H I J d.L M N O P Q R S T ^.e.f.g.h.i.6.j.k. ",
+" l.m.n.o.p.q.r.s.t.&.u.4.4.H ,.v.s.w.x.y.o.z.A.B.C.D.T ^.E.F.g.h.G.H.I. ",
+" J.K.L.M.r.N.L K s.B I ,.,.I v.H.K !.n.O.P.Q.R.S.T.U.V.W.X.F.Y.p.Z.`. + ",
+" .+++@+#+$+%+&+&.*+=+J '.'.J s.K -+q.p.3.O ~.;+>+,+'+S.)+!+&+=.~+{+]+^+ ",
+" .+++/+(+_+:+<+[+}+|+%+%+Z.1+2+H 3+4+I.B t.t.4+H F %+-.5+H 6+M )+>+]+^+ ",
+" .+++7+(+8+9+0+a+b+c+U d+e+O M L ).f+4+M.&+;.%+0.g+Z.H t.K #.h+)+i+V.^+ ",
+" .+++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.).I 1+9.s+;.H t.K t+h+)+>+]+^+ ",
+" .+++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.).I 1+9.s+;.H t.K t+h+)+>+]+^+ ",
+" .+++7+(+8+9+0+a+j+k+l+u+n+o+v+q+{.r+P.w+).I |+9.s+;.H t.K #.h+)+i+V.^+ ",
+" ++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.).I 1+9.s+;.H t.K t+h+)+>+]+ ",
+" x+y+z+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.).I 1+9.s+;.H t.K t+h+P.A+ ",
+" B+) C+D+E+F+a+j+k+l+m+n+o+p+q+{.r+P.q.).I 1+9.s+;.H t.G+H+I+J+K+. ",
+" < L+M+N+O+P+P+Q+R+S+T+U+V+W+X+^.{.r+P.N.Y+Z+`+ @.@+@@@#@$@P+%@&@*@=@-@ ",
+" g+;@>@,@'@'@,@'@'@'@'@)@!@~@{@]@^@/@(@_@:@<@[@}@'@'@'@,@'@'@'@,@|@1@g+ ",
+" 2@3@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@5@6@7@ ",
+" 8@9@0@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@b@n c@d@e@e@f@g@h@i@ ",
+" j@k@l@m@n@o@p@p@p@p@p@p@p@p@p@p@p@p@p@p@p@p@p@p@q@r@s@t@u@v@w@x@y@z@A@B@C@ ",
+" .+++D@E@F@l@G@H@I@J@K@L@K@L@L@K@L@L@K@L@L@K@L@M@N@O@P@Q@R@S@T@T@T@U@V@W@X@Y@ ",
+" .+++7+(+Z@`@ #k.F@Z..#+#$+@###$#%#a.&#$#*#=#{ -#;#>#,#'#)#!#~#{#]#^#/#(#_#:#<#[# ",
+" .+++/+(+8+9+0+}#|#1#2#3#4#5#6#`.%.4+7#>.G 8#9#0#a#b#c#d#e#e#f#g#e#h#i#j#k#l#m#n#o# ",
+" .+++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.p#q#r#s#t#g#u#v#w#x#y#z#A#B#u#C#e#D#E#F#G#H# ",
+" .+++7+(+8+9+0+a+j+k+l+u+n+o+v+q+{.r+P.I#J#K#L#M#N#O#P#Q#R#S#2@2@T#U#V#W#X#v#Y#Z#`# $ ",
+" .+++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P..$+$@$#$$$Q#%$&$*$=$-$;$2@T#>$,$'$)$!$~${$]$^$/$ ",
+" ++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+%#q#($_$:$%$<$[$}$|$1$2$;$2@T#3$4$5$6$&$7$8$9$0$a$ ",
+" b$c$8+9+0+a+j+k+l+u+n+o+v+q+{.r+d$e$f$g$h$[$i$j$k$l$m$n$2@2@T#o$p$q$r$s$t$u$v$w$x$y$ ",
+" z$A$X B$C$D$E$j+k+l+m+n+o+p+q+{.F$G$H$I$J$s$r$k$K$L$M$N$O$;$2@T#P$Q$R$S$q$|$T$U$V$W$X$ ",
+" . Y$Z$`$ %.%P+Q+R++%@%#%$%o+p+q+{.P %%&%*%=%-%;%>%,%'%)%!%~%;$2@{%]%^%,%/%(%_%:%<%[%}%|% ",
+" g+1%2%3%3%3%3%3%3%3%3%3%3%)@~@4%5%6%q#7%8%9%0%a%2@2@2@2@2@2@2@2@2@2@2@2@2@2@b%c%9%d%e%a$ ",
+" f%g%h%i%h%h%i%h%h%h%i%h%i%h%h%i%h%j%q#k%l%9%m%n%2@;$;$2@;$;$;$2@;$;$2@;$;$2@b%o%9%p%q%r% ",
+" s%t%u%v%v%v%v%v%v%v%v%v%v%v%v%v%v%w%q#x%y%9%z%A%2@2@2@2@2@2@2@2@2@2@2@2@2@2@b%B%9%C%D%8# ",
+" E%F%.#G%H%I%J%K%K%K%J%K%J%K%K%J%K%L%M%N%O%9%P%Q%R%R%S%T%U%V%;$2@W%X%Y%Z%R%R%`% &.&+&@&#& ",
+" .+$&%&&&*&=&-&;&>&,&'&)&)&)&)&)&)&!&.$~&{&]&^&^&/&^&^&(&_&:&;$2@T#<&(&^&^&/&^&^&[&}&|&1& ",
+" .+++/+(+2&3&4&5&G ;.6&7&8&9&0&a&b&c&d&e&f&g&^&^&/&^&^&/&h&i&;$2@T#j&/&^&^&/&^&^&k&l&m&n& ",
+" .+++7+(+8+9+0+a+j+o&p&q&r&M w.s&t&C l@0.u&v&/&/&/&/&/&/&h&i&2@2@T#j&/&/&/&/&/&/&w&x&y& ",
+" .+++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.z&A&B&C&^&/&^&^&/&h&i&;$2@T#j&/&^&^&/&D&E&F&G&H& ",
+" .+++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.I&q#J&K&L&/&^&^&/&h&i&;$2@T#j&/&^&^&/&M&N&O&G# ",
+" P&++7+(+8+9+0+a+j+k+l+u+n+o+v+q+{.r+P.w+Q&0.R&S&L&/&/&/&^&T&U&U&V&W&/&/&/&X&Y&Z&`& * ",
+" .*+*/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.).@*#*$*%*&*L&/&^&^&^&/&^&^&/&L&**=*-*;*>* ",
+" ,*S+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.).I '*)*!*~*{*]*L&^&^&/&D&^*/*(*_*:*`&<* ",
+" [*}*|*1*a+j+k+l+u+n+o+v+q+{.r+P.w+).I |+J#2*V%3*4*5*6*7*8*9*0*a*b*c*r% ",
+" d*e*f*g*h*i*j*6.(.^.{.r+P.n.k*l*m*v n*o*p*q*r*s*t*u*v*w*x*y*z* ",
+" A*B*C*D*E*F*G*G*H*I*J* K*L*#&M*N*O* "};
--- /dev/null
+/* XPM */
+static const char * database_add_xpm[] = {
+"48 48 816 2",
+" c None",
+". c #BAC3C7",
+"+ c #B4BDC2",
+"@ c #B1BABF",
+"# c #B5BEC3",
+"$ c #BAC2C6",
+"% c #BDC5C9",
+"& c #BAC2C7",
+"* c #B6BFC3",
+"= c #B2BBC0",
+"- c #ABB5BA",
+"; c #B8C1C7",
+"> c #AEB8BE",
+", c #C8CFD3",
+"' c #D9DDE0",
+") c #E7EAEC",
+"! c #E9ECED",
+"~ c #E6E9EB",
+"{ c #DDE1E4",
+"] c #D1D7DA",
+"^ c #C8CFD2",
+"/ c #C4CBCF",
+"( c #C0C8CC",
+"_ c #BEC6CB",
+": c #C0C7CC",
+"< c #C2CACE",
+"[ c #C5CCD0",
+"} c #CCD2D6",
+"| c #D7DDE0",
+"1 c #E3E7E9",
+"2 c #E8EBED",
+"3 c #DDE1E3",
+"4 c #C9D0D3",
+"5 c #ACB6BC",
+"6 c #B2BBC1",
+"7 c #EEF0F2",
+"8 c #D7DCDF",
+"9 c #B8C0C5",
+"0 c #A1ADB3",
+"a c #A2AEB4",
+"b c #A6B1B8",
+"c c #C1C9CE",
+"d c #C8D0D4",
+"e c #C8D0D6",
+"f c #C6CFD5",
+"g c #C0CBD2",
+"h c #BBC6CE",
+"i c #B4C1C9",
+"j c #ACBAC2",
+"k c #A2B1BA",
+"l c #99A9B2",
+"m c #92A2AC",
+"n c #94A3AC",
+"o c #9BA8AF",
+"p c #ADB8BE",
+"q c #CFD5D9",
+"r c #EAEDEF",
+"s c #E0E4E6",
+"t c #B9C2C7",
+"u c #B8C1C6",
+"v c #EEF0F1",
+"w c #D6DBDF",
+"x c #A8B3B9",
+"y c #9AA8B0",
+"z c #B4BEC6",
+"A c #CED6DC",
+"B c #E6EBEF",
+"C c #ECF0F3",
+"D c #F0F3F5",
+"E c #F3F6F7",
+"F c #F3F6F8",
+"G c #F1F4F6",
+"H c #EDF1F4",
+"I c #E9EEF1",
+"J c #E3E9EE",
+"K c #DFE6EB",
+"L c #D9E2E8",
+"M c #D4DEE5",
+"N c #D0DBE2",
+"O c #CBD7DF",
+"P c #C5D3DC",
+"Q c #BFCFD9",
+"R c #B9CAD5",
+"S c #B4C7D2",
+"T c #AEC1CF",
+"U c #A3B7C4",
+"V c #96A8B5",
+"W c #899BA6",
+"X c #C8CFD4",
+"Y c #F0F2F3",
+"Z c #BCC4C8",
+"` c #CED4D8",
+" . c #EBEEEF",
+".. c #A6B2B8",
+"+. c #ABB8C0",
+"@. c #CCD6DD",
+"#. c #D8E1E7",
+"$. c #E0E6EB",
+"%. c #E4EAEE",
+"&. c #EAEFF2",
+"*. c #EEF1F4",
+"=. c #F8F9FA",
+"-. c #F9FAFB",
+";. c #F4F6F8",
+">. c #EEF2F5",
+",. c #EBEFF2",
+"'. c #E4EAEF",
+"). c #E0E7EC",
+"!. c #DAE3E8",
+"~. c #C6D3DD",
+"{. c #B5C7D3",
+"]. c #AFC2CF",
+"^. c #ABBFCD",
+"/. c #A8BCCB",
+"(. c #A5B9C8",
+"_. c #9FB4C1",
+":. c #91A5B1",
+"<. c #94A4AD",
+"[. c #DDE2E5",
+"}. c #D8DDDF",
+"|. c #FCFCFC",
+"1. c #B0BBC2",
+"2. c #C2CED6",
+"3. c #CFDAE2",
+"4. c #EFF2F5",
+"5. c #A2B7C5",
+"6. c #9EB4C2",
+"7. c #99AFBD",
+"8. c #8FA2AD",
+"9. c #FCFCFD",
+"0. c #FDFDFD",
+"a. c #D0DAE1",
+"b. c #CAD7DF",
+"c. c #EDF0F3",
+"d. c #E0E6EC",
+"e. c #A7BCCB",
+"f. c #A4BAC8",
+"g. c #A1B7C5",
+"h. c #9DB4C2",
+"i. c #9AB1C0",
+"j. c #FAFBFB",
+"k. c #D5DEE3",
+"l. c #C1CED4",
+"m. c #F5F8F9",
+"n. c #D5DFE5",
+"o. c #CED9E1",
+"p. c #D3DDE4",
+"q. c #D6E0E6",
+"r. c #DCE4EA",
+"s. c #E2E8ED",
+"t. c #E5EBEF",
+"u. c #EDF1F3",
+"v. c #E6ECEF",
+"w. c #DDE5EA",
+"x. c #D7E1E7",
+"y. c #D3DDE5",
+"z. c #CAD6DE",
+"A. c #C4D2DC",
+"B. c #BECED8",
+"C. c #B8C9D5",
+"D. c #B4C6D2",
+"E. c #A7BBCA",
+"F. c #A4B9C8",
+"G. c #A8BCC9",
+"H. c #E3E9ED",
+"I. c #E8EDF1",
+"J. c #628293",
+"K. c #ADBEC8",
+"L. c #F4F6F7",
+"M. c #ECF1F4",
+"N. c #D5DFE6",
+"O. c #D2DCE3",
+"P. c #CCD8E0",
+"Q. c #C9D5DE",
+"R. c #C2D1DB",
+"S. c #BCCCD7",
+"T. c #B7C9D4",
+"U. c #B2C4D1",
+"V. c #ADC0CE",
+"W. c #AABECC",
+"X. c #A6BBCA",
+"Y. c #B0C2CE",
+"Z. c #F5F7F9",
+"`. c #DDE5EB",
+" + c #AABDCB",
+".+ c #5E7F91",
+"++ c #628394",
+"@+ c #728FA0",
+"#+ c #AABCC6",
+"$+ c #E1E7EB",
+"%+ c #F7F9FA",
+"&+ c #F0F3F6",
+"*+ c #E6EBF0",
+"=+ c #E5EAEF",
+"-+ c #DBE4E9",
+";+ c #C0D0D9",
+">+ c #BACBD6",
+",+ c #B6C8D3",
+"'+ c #B7C8D4",
+")+ c #C8D5DE",
+"!+ c #DAE3E9",
+"~+ c #E3EAEE",
+"{+ c #C3D2DB",
+"]+ c #ADC1CE",
+"^+ c #A4B9C7",
+"/+ c #668698",
+"(+ c #6A899B",
+"_+ c #7391A1",
+":+ c #87A1B0",
+"<+ c #ABBDC8",
+"[+ c #CFD9E0",
+"}+ c #E7ECEF",
+"|+ c #F2F5F7",
+"1+ c #F3F5F7",
+"2+ c #EFF3F5",
+"3+ c #EBF0F3",
+"4+ c #E9EEF2",
+"5+ c #F6F8FA",
+"6+ c #DFE7EC",
+"7+ c #658697",
+"8+ c #6E8D9E",
+"9+ c #7291A2",
+"0+ c #7794A6",
+"a+ c #7B98A9",
+"b+ c #829DAE",
+"c+ c #91AAB9",
+"d+ c #AFC2CD",
+"e+ c #BECDD6",
+"f+ c #E6ECF0",
+"g+ c #FBFCFC",
+"h+ c #D2DDE4",
+"i+ c #BACBD5",
+"j+ c #819DAE",
+"k+ c #88A3B3",
+"l+ c #8EA7B7",
+"m+ c #93ACBB",
+"n+ c #99B0BF",
+"o+ c #A0B6C4",
+"p+ c #A6BBC9",
+"q+ c #ACBFCE",
+"r+ c #C1D0DA",
+"s+ c #FAFBFC",
+"t+ c #D8E2E8",
+"u+ c #93ABBB",
+"v+ c #A6BAC9",
+"w+ c #D6DFE6",
+"x+ c #728D9D",
+"y+ c #7693A3",
+"z+ c #6E8D9F",
+"A+ c #B6C5CF",
+"B+ c #AAB5BB",
+"C+ c #CAD1D5",
+"D+ c #788D99",
+"E+ c #698594",
+"F+ c #7390A1",
+"G+ c #D9E0E6",
+"H+ c #B4C0C8",
+"I+ c #9EACB5",
+"J+ c #C2CACF",
+"K+ c #ECEFF0",
+"L+ c #EBEDEF",
+"M+ c #909EA5",
+"N+ c #607580",
+"O+ c #4B6370",
+"P+ c #47606D",
+"Q+ c #526C7A",
+"R+ c #5F7988",
+"S+ c #6C8695",
+"T+ c #7B94A3",
+"U+ c #859EAD",
+"V+ c #8FA6B5",
+"W+ c #9AB0BE",
+"X+ c #A3B8C7",
+"Y+ c #DCE4E9",
+"Z+ c #E0E6EA",
+"`+ c #DEE4E7",
+" @ c #DBE0E3",
+".@ c #CDD4D8",
+"+@ c #ADB9BF",
+"@@ c #8D9DA6",
+"#@ c #6E828D",
+"$@ c #4B6471",
+"%@ c #49626E",
+"&@ c #5C727D",
+"*@ c #819199",
+"=@ c #DDE2E4",
+"-@ c #D4D9DC",
+";@ c #8F9EA6",
+">@ c #5C7380",
+",@ c #576F7C",
+"'@ c #57707D",
+")@ c #58717E",
+"!@ c #5B7481",
+"~@ c #5E7784",
+"{@ c #607986",
+"]@ c #627A88",
+"^@ c #667E8B",
+"/@ c #6A818E",
+"(@ c #6A818D",
+"_@ c #687F8B",
+":@ c #677E8A",
+"<@ c #657C89",
+"[@ c #617985",
+"}@ c #5C7481",
+"|@ c #5A727E",
+"1@ c #768993",
+"2@ c #FEFEFE",
+"3@ c #889AA4",
+"4@ c #687F8C",
+"5@ c #728793",
+"6@ c #F9FAFA",
+"7@ c #D4DEDD",
+"8@ c #C6D1D8",
+"9@ c #E7EBED",
+"0@ c #94A5AF",
+"a@ c #7E929E",
+"b@ c #8295A1",
+"c@ c #ADB6BD",
+"d@ c #BEC5C9",
+"e@ c #C6CBCF",
+"f@ c #BDC4C8",
+"g@ c #CED2D5",
+"h@ c #E9EBEC",
+"i@ c #ABC1C0",
+"j@ c #648395",
+"k@ c #B4C3CC",
+"l@ c #EDF0F2",
+"m@ c #D2D9DE",
+"n@ c #A7B5BE",
+"o@ c #97A8B3",
+"p@ c #93A5B0",
+"q@ c #9AA9B3",
+"r@ c #BCC5CA",
+"s@ c #E3E4E6",
+"t@ c #F3F4F4",
+"u@ c #E5F1E8",
+"v@ c #D4EADB",
+"w@ c #B9DDC3",
+"x@ c #B7DCC1",
+"y@ c #D2E8D9",
+"z@ c #E2EFE5",
+"A@ c #EFF1F0",
+"B@ c #E2E3E3",
+"C@ c #C7D1CB",
+"D@ c #7B97A6",
+"E@ c #B2C2CB",
+"F@ c #E9EEF0",
+"G@ c #D7DEE3",
+"H@ c #C1CDD4",
+"I@ c #AFBEC8",
+"J@ c #A4B5C1",
+"K@ c #A3B4BF",
+"L@ c #A3B4C0",
+"M@ c #BBC5CC",
+"N@ c #E9EAEB",
+"O@ c #F5F9F5",
+"P@ c #A2D3B1",
+"Q@ c #48A86C",
+"R@ c #128F41",
+"S@ c #018636",
+"T@ c #008636",
+"U@ c #0F8D3F",
+"V@ c #43A567",
+"W@ c #98CEA9",
+"X@ c #EEF3EF",
+"Y@ c #E5E4E4",
+"Z@ c #6F8E9F",
+"`@ c #8AA4B2",
+" # c #B0C1CC",
+".# c #F2F4F6",
+"+# c #E8EDF0",
+"@# c #DBE2E8",
+"## c #D6DEE4",
+"$# c #D3DCE3",
+"%# c #D2DBE2",
+"&# c #D1DAE1",
+"*# c #D5DEE4",
+"=# c #D8E0E5",
+"-# c #F7F8F7",
+";# c #B0DABC",
+"># c #21974D",
+",# c #038A38",
+"'# c #109142",
+")# c #20994F",
+"!# c #2A9F57",
+"~# c #2FA15C",
+"{# c #30A25C",
+"]# c #2CA059",
+"^# c #249B53",
+"/# c #169447",
+"(# c #078B3B",
+"_# c #199346",
+":# c #A4D4B2",
+"<# c #EFF0EF",
+"[# c #CCD3CE",
+"}# c #7C98A9",
+"|# c #86A1B1",
+"1# c #94ABBA",
+"2# c #A4B9C6",
+"3# c #B4C5D0",
+"4# c #C2D0D9",
+"5# c #CFDAE1",
+"6# c #D8E1E8",
+"7# c #ECF1F3",
+"8# c #F2F2F2",
+"9# c #F5F9F6",
+"0# c #75C08C",
+"a# c #078E3B",
+"b# c #129444",
+"c# c #29A057",
+"d# c #31A55D",
+"e# c #32A65E",
+"f# c #32A75E",
+"g# c #31A65D",
+"h# c #32A55E",
+"i# c #31A45D",
+"j# c #2EA25A",
+"k# c #1C994C",
+"l# c #098E3C",
+"m# c #66B881",
+"n# c #ECF1ED",
+"o# c #CBD4CE",
+"p# c #E5E9EB",
+"q# c #FBFBFB",
+"r# c #69BB82",
+"s# c #048F38",
+"t# c #1D9C4D",
+"u# c #32A95D",
+"v# c #33AA5D",
+"w# c #36AB5D",
+"x# c #A3D9B6",
+"y# c #AFDEC1",
+"z# c #AFDEC0",
+"A# c #A0D7B5",
+"B# c #2BA65A",
+"C# c #32A85D",
+"D# c #28A056",
+"E# c #0A913D",
+"F# c #57B375",
+"G# c #F3F4F3",
+"H# c #C7CFC9",
+"I# c #DAE1E6",
+"J# c #F8F8F8",
+"K# c #90CEA2",
+"L# c #029037",
+"M# c #21A04F",
+"N# c #34AA5D",
+"O# c #3AAD5D",
+"P# c #41AE5C",
+"Q# c #47B05C",
+"R# c #4DB15D",
+"S# c #E9F5EB",
+"T# c #E6F4EC",
+"U# c #32A658",
+"V# c #45AF5C",
+"W# c #3FAE5C",
+"X# c #38AC5D",
+"Y# c #2CA459",
+"Z# c #09923D",
+"`# c #7AC391",
+" $ c #EBECEB",
+".$ c #EBEEF0",
+"+$ c #D6EDDB",
+"@$ c #119941",
+"#$ c #1BA04A",
+"$$ c #3CAD5C",
+"%$ c #50B25C",
+"&$ c #56B45C",
+"*$ c #5AB65B",
+"=$ c #5EB85C",
+"-$ c #EBF6EB",
+";$ c #FFFFFF",
+">$ c #3AA957",
+",$ c #59B55B",
+"'$ c #54B35C",
+")$ c #4DB15C",
+"!$ c #43AE5C",
+"~$ c #39AC5D",
+"{$ c #29A556",
+"]$ c #0D963E",
+"^$ c #C6E4CE",
+"/$ c #D9DBD9",
+"($ c #62BC7D",
+"_$ c #0E9B3C",
+":$ c #42AE5B",
+"<$ c #58B55B",
+"[$ c #5FB85B",
+"}$ c #65BB5B",
+"|$ c #69BC5B",
+"1$ c #6DBE5D",
+"2$ c #EDF7EB",
+"3$ c #41AC57",
+"4$ c #68BB5B",
+"5$ c #63BA5B",
+"6$ c #5DB75B",
+"7$ c #4CB15C",
+"8$ c #3FAE5D",
+"9$ c #1BA049",
+"0$ c #4DB36E",
+"a$ c #F2F1F1",
+"b$ c #6E8A99",
+"c$ c #718EA0",
+"d$ c #E2E7EA",
+"e$ c #E2F2E4",
+"f$ c #29A440",
+"g$ c #3BAA4D",
+"h$ c #56B45B",
+"i$ c #67BB5B",
+"j$ c #6DBE5C",
+"k$ c #72BF5F",
+"l$ c #76C061",
+"m$ c #79C264",
+"n$ c #EEF7EC",
+"o$ c #47AE5A",
+"p$ c #74C060",
+"q$ c #70BF5D",
+"r$ c #6BBD5B",
+"s$ c #64BA5B",
+"t$ c #5CB75B",
+"u$ c #52B35C",
+"v$ c #40AD59",
+"w$ c #20A23E",
+"x$ c #D1EAD7",
+"y$ c #CDD5CD",
+"z$ c #A6B2B9",
+"A$ c #E1E5E7",
+"B$ c #7B909D",
+"C$ c #6B8797",
+"D$ c #728E9F",
+"E$ c #7B98A8",
+"F$ c #C2D0DA",
+"G$ c #EFF1F3",
+"H$ c #ADDBB4",
+"I$ c #52B030",
+"J$ c #6BBC54",
+"K$ c #78C163",
+"L$ c #7CC367",
+"M$ c #80C56A",
+"N$ c #83C66C",
+"O$ c #EFF8ED",
+"P$ c #4BB05E",
+"Q$ c #7FC469",
+"R$ c #7BC265",
+"S$ c #76C161",
+"T$ c #60B85B",
+"U$ c #65BA59",
+"V$ c #56B33D",
+"W$ c #93D1A0",
+"X$ c #E2E1E1",
+"Y$ c #EFF1F2",
+"Z$ c #9AA7AD",
+"`$ c #627580",
+" % c #4C6370",
+".% c #435C69",
+"+% c #6E8897",
+"@% c #7D96A5",
+"#% c #89A2B2",
+"$% c #96ADBC",
+"%% c #F8F8F9",
+"&% c #82C786",
+"*% c #73BC32",
+"=% c #93CB50",
+"-% c #78C365",
+";% c #A7D89C",
+">% c #ACD9A2",
+",% c #B0DBA5",
+"'% c #B2DCA7",
+")% c #B1DBA5",
+"!% c #AFDAA1",
+"~% c #F5FAF3",
+"{% c #F0F8F4",
+"]% c #90CE9B",
+"^% c #AFDAA2",
+"/% c #AEDAA4",
+"(% c #ABD8A0",
+"_% c #A5D79C",
+":% c #5EB862",
+"<% c #8ECA51",
+"[% c #79C044",
+"}% c #70C07C",
+"|% c #EDECEC",
+"1% c #8E9DA5",
+"2% c #58707D",
+"3% c #536C79",
+"4% c #627A87",
+"5% c #677E8B",
+"6% c #82959F",
+"7% c #76C172",
+"8% c #82C338",
+"9% c #9BCE4E",
+"0% c #A2D36E",
+"a% c #F8FCF7",
+"b% c #F9FCFB",
+"c% c #5BB66A",
+"d% c #86C64A",
+"e% c #5AB557",
+"f% c #FDFEFE",
+"g% c #8497A1",
+"h% c #647C89",
+"i% c #647B88",
+"j% c #80939D",
+"k% c #72C063",
+"l% c #86C43A",
+"m% c #ABD669",
+"n% c #FAFCF6",
+"o% c #5CB76A",
+"p% c #8BC84C",
+"q% c #57B44C",
+"r% c #F3F3F3",
+"s% c #CFD8DE",
+"t% c #E1E6E8",
+"u% c #889AA5",
+"v% c #778C99",
+"w% c #8C9DA8",
+"x% c #81C771",
+"y% c #85C439",
+"z% c #AAD568",
+"A% c #FAFCF5",
+"B% c #5BB76A",
+"C% c #8AC74B",
+"D% c #66BB55",
+"E% c #638294",
+"F% c #BBC9D1",
+"G% c #CAD3D8",
+"H% c #A1B0B9",
+"I% c #90A2AD",
+"J% c #8EA1AC",
+"K% c #8FA1AC",
+"L% c #99A9B3",
+"M% c #F6F7F8",
+"N% c #97D087",
+"O% c #7FC136",
+"P% c #96CB45",
+"Q% c #B4DA84",
+"R% c #B3DA89",
+"S% c #B4DB8C",
+"T% c #BADE9D",
+"U% c #C1E1AC",
+"V% c #F7FBF5",
+"W% c #F1F9F5",
+"X% c #A0D5A6",
+"Y% c #BBDE9E",
+"Z% c #B4DB8D",
+"`% c #B0D987",
+" & c #6EBB43",
+".& c #99CD4B",
+"+& c #82C447",
+"@& c #8ACA7A",
+"#& c #EFEFEF",
+"$& c #638495",
+"%& c #7F9AA9",
+"&& c #B9C7D0",
+"*& c #E8ECEF",
+"=& c #E8ECEE",
+"-& c #D1D9DE",
+";& c #BBC7CF",
+">& c #ABB9C4",
+",& c #A5B5C0",
+"'& c #A1B2BD",
+")& c #A0B1BC",
+"!& c #A3B3BE",
+"~& c #BFE2B4",
+"{& c #73BC36",
+"]& c #95CB44",
+"^& c #83C124",
+"/& c #82C023",
+"(& c #83C126",
+"_& c #89C434",
+":& c #F1F8E7",
+"<& c #4EAF41",
+"[& c #8CC637",
+"}& c #76BF42",
+"|& c #ACD99F",
+"1& c #E7E6E6",
+"2& c #7593A3",
+"3& c #94ABB8",
+"4& c #B7C7D0",
+"5& c #DBE3E8",
+"6& c #EBEFF1",
+"7& c #E2E8EC",
+"8& c #DBE2E7",
+"9& c #D5DDE3",
+"0& c #CFD9DF",
+"a& c #CDD7DE",
+"b& c #CBD5DD",
+"c& c #C9D4DC",
+"d& c #E9ECEF",
+"e& c #E8F4E3",
+"f& c #73BD49",
+"g& c #85C22A",
+"h& c #83C125",
+"i& c #EFF7E4",
+"j& c #4CAE3D",
+"k& c #7FC028",
+"l& c #6ABA43",
+"m& c #DCEED6",
+"n& c #D7DDD3",
+"o& c #92AAB9",
+"p& c #A9BCC9",
+"q& c #B9C9D3",
+"r& c #C7D4DC",
+"s& c #E1E8ED",
+"t& c #E7EDF0",
+"u& c #9BD084",
+"v& c #7CBE2E",
+"w& c #74BC2D",
+"x& c #8CCA75",
+"y& c #F6F6F6",
+"z& c #F0F2F4",
+"A& c #E2F1DB",
+"B& c #7EC150",
+"C& c #81C025",
+"D& c #82C124",
+"E& c #7DBF25",
+"F& c #74BD48",
+"G& c #D7ECCF",
+"H& c #E4E5E2",
+"I& c #DDE4E9",
+"J& c #BCDFA9",
+"K& c #81C13F",
+"L& c #82C024",
+"M& c #80C024",
+"N& c #78BE3B",
+"O& c #AED89A",
+"P& c #607F91",
+"Q& c #EBEEF1",
+"R& c #AFD993",
+"S& c #86C441",
+"T& c #CBE5A8",
+"U& c #D0E9BB",
+"V& c #C1E2AE",
+"W& c #5DB435",
+"X& c #7FBF24",
+"Y& c #7DC03D",
+"Z& c #A4D387",
+"`& c #F8F9F7",
+" * c #DADDD5",
+".* c #6D8491",
+"+* c #638394",
+"@* c #F2F4F5",
+"#* c #F9FBF7",
+"$* c #BADE9E",
+"%* c #90C950",
+"&* c #82C128",
+"** c #7EBF28",
+"=* c #87C54B",
+"-* c #B1DA94",
+";* c #F4F8F1",
+">* c #E1E4DC",
+",* c #728792",
+"'* c #F5F6F7",
+")* c #FBFCFA",
+"!* c #D7EBC4",
+"~* c #9FD06C",
+"{* c #8BC642",
+"]* c #81C027",
+"^* c #81C024",
+"/* c #7EBF27",
+"(* c #86C440",
+"_* c #99CD66",
+":* c #D1E9BD",
+"<* c #E2E5DE",
+"[* c #758893",
+"}* c #738A97",
+"|* c #738F9E",
+"1* c #7793A4",
+"2* c #FAFAFB",
+"3* c #D2E9BB",
+"4* c #B3DA8B",
+"5* c #98CD5D",
+"6* c #91C94D",
+"7* c #8DC746",
+"8* c #8CC746",
+"9* c #8FC84D",
+"0* c #95CB5B",
+"a* c #B0D886",
+"b* c #CEE7B6",
+"c* c #F4F9F1",
+"d* c #778B96",
+"e* c #798E9A",
+"f* c #7C929F",
+"g* c #8399A7",
+"h* c #89A1B0",
+"i* c #90A8B7",
+"j* c #97ADBC",
+"k* c #DEE5EA",
+"l* c #E5EAEE",
+"m* c #EAEDF0",
+"n* c #E3E6E8",
+"o* c #D5DADC",
+"p* c #F0F1F2",
+"q* c #FAFBF9",
+"r* c #F2F8ED",
+"s* c #E6F3DA",
+"t* c #D8ECC6",
+"u* c #D7ECC5",
+"v* c #E5F2D8",
+"w* c #F2F8EC",
+"x* c #F8FAF7",
+"y* c #F4F4F3",
+"z* c #E2E4DF",
+"A* c #8496A2",
+"B* c #82949E",
+"C* c #81929C",
+"D* c #83949E",
+"E* c #8797A1",
+"F* c #8B9BA4",
+"G* c #8D9CA4",
+"H* c #8D9BA4",
+"I* c #8B99A1",
+"J* c #A4AFB6",
+"K* c #E7EDDF",
+"L* c #EDEDEB",
+"M* c #EFEEEE",
+"N* c #EBEBEB",
+"O* c #E4E9DD",
+" . + @ # $ % & * = - ; ",
+" > , ' ) ! ~ { ] ^ / ( _ : < [ } | 1 2 ) 3 4 5 ",
+" 6 ' 7 8 9 0 a b > ; c d e e f g h i j k l m n o p q r s t ",
+" u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W y X Y Z ",
+" ` ...+.@.#.$.%.&.*.E =.-.;.>.,.'.).!.M N O ~.Q R {.].^./.(._.:.<.[.}. ",
+" |.1.2.3.M L $.%.&.*.F -.-.;.4.,.'.).!.M N O ~.Q R {.].^./.(.5.6.7.8.9. ",
+" 0.a.b.3.M #.K J I c.G ;.;.G H I J d.L M N O P Q R S T ^.e.f.g.h.i.6.j.k. ",
+" l.m.n.o.p.q.r.s.t.&.u.4.4.H ,.v.s.w.x.y.o.z.A.B.C.D.T ^.E.F.g.h.G.H.I. ",
+" J.K.L.M.r.N.L K s.B I ,.,.I v.H.K !.n.O.P.Q.R.S.T.U.V.W.X.F.Y.p.Z.`. + ",
+" .+++@+#+$+%+&+&.*+=+J '.'.J s.K -+q.p.3.O ~.;+>+,+'+S.)+!+&+=.~+{+]+^+ ",
+" .+++/+(+_+:+<+[+}+|+%+%+Z.1+2+H 3+4+I.B t.t.4+H F %+-.5+H 6+M )+>+]+^+ ",
+" .+++7+(+8+9+0+a+b+c+U d+e+O M L ).f+4+M.&+;.%+0.g+Z.H t.K #.h+)+i+V.^+ ",
+" .+++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.).I 1+9.s+;.H t.K t+h+)+>+]+^+ ",
+" .+++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.).I 1+9.s+;.H t.K t+h+)+>+]+^+ ",
+" .+++7+(+8+9+0+a+j+k+l+u+n+o+v+q+{.r+P.w+).I |+9.s+;.H t.K #.h+)+i+V.^+ ",
+" ++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.).I 1+9.s+;.H t.K t+h+)+>+]+ ",
+" x+y+z+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.).I 1+9.s+;.H t.K t+h+P.A+ ",
+" B+) C+D+E+F+a+j+k+l+m+n+o+p+q+{.r+P.q.).I 1+9.s+;.H t.G+H+I+J+K+. ",
+" < L+M+N+O+P+P+Q+R+S+T+U+V+W+X+^.{.r+P.N.Y+Z+`+ @.@+@@@#@$@P+%@&@*@=@-@ ",
+" g+;@>@,@'@'@,@'@'@'@'@)@!@~@{@]@^@/@(@_@:@<@[@}@'@'@'@,@'@'@'@,@|@1@g+ ",
+" 2@3@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@4@5@6@7@ ",
+" 8@9@0@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@a@b@n c@d@e@e@f@g@h@i@ ",
+" j@k@l@m@n@o@p@p@p@p@p@p@p@p@p@p@p@p@p@p@p@p@p@p@q@r@s@t@u@v@w@x@y@z@A@B@C@ ",
+" .+++D@E@F@l@G@H@I@J@K@L@K@L@L@K@L@L@K@L@L@K@L@M@N@O@P@Q@R@S@T@T@T@U@V@W@X@Y@ ",
+" .+++7+(+Z@`@ #k.F@Z..#+#$+@###$#%#a.&#$#*#=#{ -#;#>#,#'#)#!#~#{#]#^#/#(#_#:#<#[# ",
+" .+++/+(+8+9+0+}#|#1#2#3#4#5#6#`.%.4+7#>.G 8#9#0#a#b#c#d#e#e#f#g#e#h#i#j#k#l#m#n#o# ",
+" .+++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.p#q#r#s#t#g#u#v#w#x#y#z#A#B#u#C#e#D#E#F#G#H# ",
+" .+++7+(+8+9+0+a+j+k+l+u+n+o+v+q+{.r+P.I#J#K#L#M#N#O#P#Q#R#S#2@2@T#U#V#W#X#v#Y#Z#`# $ ",
+" .+++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P..$+$@$#$$$Q#%$&$*$=$-$;$2@T#>$,$'$)$!$~${$]$^$/$ ",
+" ++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+%#q#($_$:$%$<$[$}$|$1$2$;$2@T#3$4$5$6$&$7$8$9$0$a$ ",
+" b$c$8+9+0+a+j+k+l+u+n+o+v+q+{.r+d$e$f$g$h$[$i$j$k$l$m$n$2@2@T#o$p$q$r$s$t$u$v$w$x$y$ ",
+" z$A$X B$C$D$E$j+k+l+m+n+o+p+q+{.F$G$H$I$J$s$r$k$K$L$M$N$O$;$2@T#P$Q$R$S$q$|$T$U$V$W$X$ ",
+" . Y$Z$`$ %.%P+Q+R++%@%#%$%o+p+q+{.P %%&%*%=%-%;%>%,%'%)%!%~%;$2@{%]%^%,%/%(%_%:%<%[%}%|% ",
+" g+1%2%3%3%3%3%3%3%3%3%3%3%)@~@4%5%6%q#7%8%9%0%a%2@2@2@2@2@2@2@2@2@2@2@2@2@2@b%c%9%d%e%a$ ",
+" f%g%h%i%h%h%i%h%h%h%i%h%i%h%h%i%h%j%q#k%l%9%m%n%2@;$;$2@;$;$;$2@;$;$2@;$;$2@b%o%9%p%q%r% ",
+" s%t%u%v%v%v%v%v%v%v%v%v%v%v%v%v%v%w%q#x%y%9%z%A%2@2@2@2@2@2@2@2@2@2@2@2@2@2@b%B%9%C%D%8# ",
+" E%F%.#G%H%I%J%K%K%K%J%K%J%K%K%J%K%L%M%N%O%9%P%Q%R%R%S%T%U%V%;$2@W%X%Y%Z%R%R%`% &.&+&@&#& ",
+" .+$&%&&&*&=&-&;&>&,&'&)&)&)&)&)&)&!&.$~&{&]&^&^&/&^&^&(&_&:&;$2@T#<&(&^&^&/&^&^&[&}&|&1& ",
+" .+++/+(+2&3&4&5&G ;.6&7&8&9&0&a&b&c&d&e&f&g&^&^&/&^&^&/&h&i&;$2@T#j&/&^&^&/&^&^&k&l&m&n& ",
+" .+++7+(+8+9+0+a+j+o&p&q&r&M w.s&t&C l@0.u&v&/&/&/&/&/&/&h&i&2@2@T#j&/&/&/&/&/&/&w&x&y& ",
+" .+++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.z&A&B&C&^&/&^&^&/&h&i&;$2@T#j&/&^&^&/&D&E&F&G&H& ",
+" .+++/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.I&q#J&K&L&/&^&^&/&h&i&;$2@T#j&/&^&^&/&M&N&O&G# ",
+" P&++7+(+8+9+0+a+j+k+l+u+n+o+v+q+{.r+P.w+Q&0.R&S&L&/&/&/&^&T&U&U&V&W&/&/&/&X&Y&Z&`& * ",
+" .*+*/+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.).@*#*$*%*&*L&/&^&^&^&/&^&^&/&L&**=*-*;*>* ",
+" ,*S+(+8+9+0+a+j+k+l+m+n+o+p+q+{.r+P.q.).I '*)*!*~*{*]*L&^&^&/&D&^*/*(*_*:*`&<* ",
+" [*}*|*1*a+j+k+l+u+n+o+v+q+{.r+P.w+).I |+J#2*V%3*4*5*6*7*8*9*0*a*b*c*r% ",
+" d*e*f*g*h*i*j*6.(.^.{.r+P.n.k*l*m*v n*o*p*q*r*s*t*u*v*w*x*y*z* ",
+" A*B*C*D*E*F*G*G*H*I*J* K*L*#&M*N*O* "};
--- /dev/null
+/* XPM */
+static const char * folder_down_xpm[] = {
+"48 48 649 2",
+" c None",
+". c #C2AA68",
+"+ c #BBA260",
+"@ c #B79E63",
+"# c #E9DEA7",
+"$ c #BCA05D",
+"% c #BB9F5C",
+"& c #BFA35C",
+"* c #C0A45B",
+"= c #C2A65A",
+"- c #C4A859",
+"; c #C4A958",
+"> c #C3A857",
+", c #C3A958",
+"' c #CDB977",
+") c #F1E8B1",
+"! c #FFFAC3",
+"~ c #CFB266",
+"{ c #D1B465",
+"] c #CCAE63",
+"^ c #D0B263",
+"/ c #D1B462",
+"( c #D1B460",
+"_ c #D0B35E",
+": c #CFB25D",
+"< c #CCB05C",
+"[ c #CBAF5B",
+"} c #CAAE5A",
+"| c #C8AC59",
+"1 c #C7AB58",
+"2 c #C4A959",
+"3 c #D0BB78",
+"4 c #EBE0A6",
+"5 c #FDF8C1",
+"6 c #FEF9C2",
+"7 c #D9BC72",
+"8 c #CFB36D",
+"9 c #D9BD6F",
+"0 c #E4C772",
+"a c #EECF73",
+"b c #F3D473",
+"c c #F1D272",
+"d c #EECF6F",
+"e c #E9CB6C",
+"f c #E5C66A",
+"g c #E0C167",
+"h c #DDBE65",
+"i c #D9BB63",
+"j c #D4B761",
+"k c #CDB05C",
+"l c #C8AD5F",
+"m c #D6C280",
+"n c #F1E8AF",
+"o c #FFF9C0",
+"p c #FFFAC0",
+"q c #FEF9C0",
+"r c #C6AB6C",
+"s c #F2D781",
+"t c #F9DD82",
+"u c #FADD81",
+"v c #FADC7E",
+"w c #FADC7B",
+"x c #F9DB79",
+"y c #F9DA77",
+"z c #F5D673",
+"A c #E5C76A",
+"B c #DDBF65",
+"C c #CCAF5C",
+"D c #CBB163",
+"E c #DBC989",
+"F c #F4EBB1",
+"G c #FEF8BE",
+"H c #FFF9BF",
+"I c #FEF9BF",
+"J c #E1C77B",
+"K c #FAE086",
+"L c #FADE83",
+"M c #F9DB7B",
+"N c #F6D674",
+"O c #EDCE6F",
+"P c #E1C267",
+"Q c #CBAF5D",
+"R c #D7C482",
+"S c #F6EDB2",
+"T c #FEF8BD",
+"U c #E8CE7E",
+"V c #FBE086",
+"W c #F2D372",
+"X c #EACB6C",
+"Y c #D5B761",
+"Z c #CAB273",
+"` c #FDF5BA",
+" . c #FEF8BC",
+".. c #FFF8BC",
+"+. c #EACF7F",
+"@. c #D0BC80",
+"#. c #FFF8BB",
+"$. c #FEF7BA",
+"%. c #D1BD82",
+"&. c #FEF7B9",
+"*. c #F9DA78",
+"=. c #D5B861",
+"-. c #D1BD81",
+";. c #FEF5B6",
+">. c #D1BD80",
+",. c #FEF4B3",
+"'. c #FDF4B3",
+"). c #D1BC7F",
+"!. c #FDF3B1",
+"~. c #EACF80",
+"{. c #FBE087",
+"]. c #FADF84",
+"^. c #FBDE82",
+"/. c #FADD7F",
+"(. c #FADC7C",
+"_. c #F9DB7A",
+":. c #FADB78",
+"<. c #F7D775",
+"[. c #F3D472",
+"}. c #EFD070",
+"|. c #EBCC6D",
+"1. c #E6C86B",
+"2. c #E2C368",
+"3. c #DEC066",
+"4. c #DABC64",
+"5. c #D6B962",
+"6. c #D1B45F",
+"7. c #D1BC7E",
+"8. c #FEF3AF",
+"9. c #FDF2AF",
+"0. c #D1BC7D",
+"a. c #FEF2AC",
+"b. c #FDF1AB",
+"c. c #FAE087",
+"d. c #FADD82",
+"e. c #D1BB7C",
+"f. c #FDF0A9",
+"g. c #D0BB7B",
+"h. c #FDEFA6",
+"i. c #FCEFA6",
+"j. c #E6C96B",
+"k. c #E2C468",
+"l. c #D2B55F",
+"m. c #D0BA7A",
+"n. c #FDEEA4",
+"o. c #FCEEA4",
+"p. c #F7D875",
+"q. c #E7C96B",
+"r. c #D7B962",
+"s. c #D0BA79",
+"t. c #FDEEA2",
+"u. c #FCEDA2",
+"v. c #ECCD6D",
+"w. c #DBBC64",
+"x. c #D0B978",
+"y. c #FCEC9F",
+"z. c #E3C468",
+"A. c #D0B977",
+"B. c #FDEB9D",
+"C. c #FCEB9D",
+"D. c #F8D876",
+"E. c #D0B876",
+"F. c #FCEA9A",
+"G. c #D0B875",
+"H. c #FBE998",
+"I. c #FAE899",
+"J. c #F0E2A5",
+"K. c #E7DEB6",
+"L. c #E3DEC7",
+"M. c #E2DFD4",
+"N. c #E0DED8",
+"O. c #E1DED6",
+"P. c #E1DDCE",
+"Q. c #E3DCBF",
+"R. c #E9DEAD",
+"S. c #CFB776",
+"T. c #EDE0AC",
+"U. c #E4E0D0",
+"V. c #EDEDEB",
+"W. c #E2EDE5",
+"X. c #C3E1CB",
+"Y. c #97CCA7",
+"Z. c #8BC79E",
+"`. c #8EC8A0",
+" + c #A7D4B4",
+".+ c #D4E8DA",
+"++ c #E8EDE9",
+"@+ c #E1E0E0",
+"#+ c #C9CEC8",
+"$+ c #FADF85",
+"%+ c #FBDE83",
+"&+ c #FADC7D",
+"*+ c #F8D976",
+"=+ c #F4D573",
+"-+ c #F0D170",
+";+ c #ECCD6E",
+">+ c #E7CA6B",
+",+ c #DFC066",
+"'+ c #DBBD64",
+")+ c #D2B86D",
+"!+ c #D6CFBF",
+"~+ c #F1F1F0",
+"{+ c #C8E3CF",
+"]+ c #57AF76",
+"^+ c #169045",
+"/+ c #05883A",
+"(+ c #0D8C41",
+"_+ c #118E44",
+":+ c #0B8B3F",
+"<+ c #07893B",
+"[+ c #2E9B57",
+"}+ c #85C59A",
+"|+ c #E6EFE8",
+"1+ c #E2E1E1",
+"2+ c #FAE088",
+"3+ c #FADD80",
+"4+ c #F0D171",
+"5+ c #EDCE6E",
+"6+ c #E8CA6C",
+"7+ c #E4C569",
+"8+ c #DFC067",
+"9+ c #DCBD65",
+"0+ c #D7C080",
+"a+ c #E4E1DA",
+"b+ c #E0EFE4",
+"c+ c #58B176",
+"d+ c #058A39",
+"e+ c #159347",
+"f+ c #289D56",
+"g+ c #31A25D",
+"h+ c #30A15D",
+"i+ c #249A53",
+"j+ c #108F42",
+"k+ c #179245",
+"l+ c #9ACFAB",
+"m+ c #EEEEEE",
+"n+ c #C6CFC9",
+"o+ c #FBE088",
+"p+ c #FADB79",
+"q+ c #DAC37F",
+"r+ c #E8E6DE",
+"s+ c #CEE7D4",
+"t+ c #22994D",
+"u+ c #0F9241",
+"v+ c #2AA058",
+"w+ c #31A45D",
+"x+ c #32A55E",
+"y+ c #32A65E",
+"z+ c #35A760",
+"A+ c #34A860",
+"B+ c #34A760",
+"C+ c #33A65F",
+"D+ c #31A55D",
+"E+ c #32A45E",
+"F+ c #31A35D",
+"G+ c #259C53",
+"H+ c #0B8F3E",
+"I+ c #6BBA86",
+"J+ c #E8EEE9",
+"K+ c #CECFCE",
+"L+ c #E9CF80",
+"M+ c #DEC16F",
+"N+ c #E6E2D7",
+"O+ c #D2E9D7",
+"P+ c #1C9848",
+"Q+ c #169748",
+"R+ c #30A55C",
+"S+ c #31A75D",
+"T+ c #32A95D",
+"U+ c #34AA5D",
+"V+ c #4DB570",
+"W+ c #D2ECDB",
+"X+ c #D1ECDB",
+"Y+ c #96D2AF",
+"Z+ c #24A354",
+"`+ c #31A85D",
+" @ c #2EA25A",
+".@ c #0D9240",
+"+@ c #67BA82",
+"@@ c #EFF0EF",
+"#@ c #BECAC2",
+"$@ c #EDD382",
+"%@ c #F8D977",
+"&@ c #E4C66A",
+"*@ c #E0D5B4",
+"=@ c #EAF4EC",
+"-@ c #2FA257",
+";@ c #149846",
+">@ c #32A85D",
+",@ c #35AB5D",
+"'@ c #3BAD5D",
+")@ c #42AF5C",
+"!@ c #48B05C",
+"~@ c #67BD74",
+"{@ c #FEFEFE",
+"]@ c #B4DEC6",
+"^@ c #2DA451",
+"/@ c #45AF5C",
+"(@ c #3EAD5C",
+"_@ c #37AC5D",
+":@ c #2EA55B",
+"<@ c #0D943F",
+"[@ c #8BCB9F",
+"}@ c #E8E7E7",
+"|@ c #D3B976",
+"1@ c #F9DF89",
+"2@ c #F9D977",
+"3@ c #E2CA82",
+"4@ c #F1F0EE",
+"5@ c #79C490",
+"6@ c #0B963E",
+"7@ c #31A95B",
+"8@ c #3EAD5D",
+"9@ c #50B25C",
+"0@ c #57B45C",
+"a@ c #5BB65B",
+"b@ c #76C273",
+"c@ c #FFFFFF",
+"d@ c #39A850",
+"e@ c #58B55B",
+"f@ c #53B35C",
+"g@ c #4CB15C",
+"h@ c #42AE5C",
+"i@ c #29A557",
+"j@ c #0F9640",
+"k@ c #DAECDE",
+"l@ c #D3D5D3",
+"m@ c #BDA36A",
+"n@ c #F1D889",
+"o@ c #FAE28D",
+"p@ c #FBE18A",
+"q@ c #F5D573",
+"r@ c #F1D271",
+"s@ c #E9CA6C",
+"t@ c #E4D9B8",
+"u@ c #E7F3E9",
+"v@ c #0C983C",
+"w@ c #2BA752",
+"x@ c #52B35C",
+"y@ c #59B55B",
+"z@ c #60B85B",
+"A@ c #66BB5B",
+"B@ c #69BC5B",
+"C@ c #82C774",
+"D@ c #42AC50",
+"E@ c #67BB5B",
+"F@ c #62B95B",
+"G@ c #5CB75B",
+"H@ c #55B45C",
+"I@ c #4AB15C",
+"J@ c #3DAD5C",
+"K@ c #1A9F49",
+"L@ c #69BE84",
+"M@ c #ECEBEB",
+"N@ c #D7BE7B",
+"O@ c #FAE38F",
+"P@ c #FAE18A",
+"Q@ c #E7CB75",
+"R@ c #EFECE4",
+"S@ c #8DCF9F",
+"T@ c #2AA43C",
+"U@ c #4BB05B",
+"V@ c #57B55B",
+"W@ c #61B85B",
+"X@ c #68BC5B",
+"Y@ c #6EBE5C",
+"Z@ c #72C05F",
+"`@ c #76C161",
+" # c #8CCB7A",
+".# c #4AAF54",
+"+# c #74C060",
+"@# c #70BE5D",
+"## c #6ABD5B",
+"$# c #64BA5B",
+"%# c #51B25C",
+"&# c #42AE59",
+"*# c #27A442",
+"=# c #E3EFE6",
+"-# c #C6D2C5",
+";# c #DFC680",
+"># c #FBE38F",
+",# c #E6D08B",
+"'# c #F5F6F4",
+")# c #52B567",
+"!# c #79C040",
+"~# c #61B85A",
+"{# c #6CBE5C",
+"]# c #75C061",
+"^# c #7DC46D",
+"/# c #78C166",
+"(# c #80C56A",
+"_# c #94CE80",
+":# c #50B159",
+"<# c #7EC469",
+"[# c #7CC367",
+"}# c #7AC36A",
+"|# c #6ABD5C",
+"1# c #68BB5B",
+"2# c #5EB75B",
+"3# c #71BF56",
+"4# c #48AE3B",
+"5# c #B1DDBA",
+"6# c #DCDBDB",
+"7# c #E0C881",
+"8# c #E7D5A1",
+"9# c #EBF4EB",
+"0# c #45AC43",
+"a# c #92CA46",
+"b# c #87C753",
+"c# c #6FBE5C",
+"d# c #76C162",
+"e# c #B5DDA9",
+"f# c #E0F1E5",
+"g# c #51B369",
+"h# c #73BE5A",
+"i# c #8AC870",
+"j# c #48AD4D",
+"k# c #77BF5B",
+"l# c #BDE1B1",
+"m# c #D8EEDE",
+"n# c #4DB165",
+"o# c #70BF5E",
+"p# c #71BF59",
+"q# c #96CC50",
+"r# c #6ABA42",
+"s# c #8DCE98",
+"t# c #E0C781",
+"u# c #E9DBB1",
+"v# c #E4F2E4",
+"w# c #48AC33",
+"x# c #98CD4C",
+"y# c #9BCE4F",
+"z# c #87C75A",
+"A# c #BADFAF",
+"B# c #FCFEFC",
+"C# c #E0F1E7",
+"D# c #55B469",
+"E# c #8DC973",
+"F# c #50B052",
+"G# c #B9DEA8",
+"H# c #DEF1E6",
+"I# c #4EB265",
+"J# c #8DC951",
+"K# c #9BCE4E",
+"L# c #7BC147",
+"M# c #77C377",
+"N# c #EEEDED",
+"O# c #DFC780",
+"P# c #FAE085",
+"Q# c #EADDB6",
+"R# c #E5F2E4",
+"S# c #50AF30",
+"T# c #9ACE4E",
+"U# c #9CCF4F",
+"V# c #ADD76E",
+"W# c #FAFDF8",
+"X# c #DDF0E5",
+"Y# c #72C185",
+"Z# c #B8E0C9",
+"`# c #9AD2A4",
+" $ c #AEDCC1",
+".$ c #65B94B",
+"+$ c #81C448",
+"@$ c #74C16D",
+"#$ c #EFEFEF",
+"$$ c #DEC680",
+"%$ c #FAE390",
+"&$ c #FAE28E",
+"*$ c #FAE18B",
+"=$ c #F9DB7C",
+"-$ c #EADCB1",
+";$ c #E7F3E5",
+">$ c #56B233",
+",$ c #98CD4D",
+"'$ c #94CA41",
+")$ c #BADD84",
+"!$ c #FCFDFA",
+"~$ c #EBF6F0",
+"{$ c #F1F9F4",
+"]$ c #FAFDFB",
+"^$ c #D9EFE3",
+"/$ c #52B14D",
+"($ c #92CA47",
+"_$ c #80C346",
+":$ c #86C97B",
+"<$ c #DEC580",
+"[$ c #FBE390",
+"}$ c #FBE18B",
+"|$ c #FBE189",
+"1$ c #FBDF84",
+"2$ c #FADD7E",
+"3$ c #FADB7A",
+"4$ c #E9D8A1",
+"5$ c #EFF6ED",
+"6$ c #66B948",
+"7$ c #93CB47",
+"8$ c #98CC48",
+"9$ c #84C227",
+"0$ c #83C124",
+"a$ c #B6DB7E",
+"b$ c #FBFDF9",
+"c$ c #D2ECDC",
+"d$ c #51B14B",
+"e$ c #81C024",
+"f$ c #88C42D",
+"g$ c #9ACE4D",
+"h$ c #78C043",
+"i$ c #A6D79A",
+"j$ c #EBEAEA",
+"k$ c #DDC47F",
+"l$ c #E9D28B",
+"m$ c #F8F8F6",
+"n$ c #84C770",
+"o$ c #88C640",
+"p$ c #8BC532",
+"q$ c #82C023",
+"r$ c #84C126",
+"s$ c #D2EBDC",
+"t$ c #56B34D",
+"u$ c #7EBF24",
+"v$ c #8FC83C",
+"w$ c #6CBB40",
+"x$ c #CBE7C3",
+"y$ c #DCC47F",
+"z$ c #E9CD74",
+"A$ c #F4F1E7",
+"B$ c #B9DEAC",
+"C$ c #73BD39",
+"D$ c #83C125",
+"E$ c #B9DC83",
+"F$ c #D9EFE2",
+"G$ c #7FC02A",
+"H$ c #71BD52",
+"I$ c #EEF5EC",
+"J$ c #D2DACD",
+"K$ c #DCC37E",
+"L$ c #EACC6D",
+"M$ c #EBDEB8",
+"N$ c #F4F9F2",
+"O$ c #73BD4F",
+"P$ c #7EBF26",
+"Q$ c #D3ECDD",
+"R$ c #50B04A",
+"S$ c #72BB33",
+"T$ c #A7D697",
+"U$ c #F2F1F1",
+"V$ c #DBC27E",
+"W$ c #E6CD80",
+"X$ c #F7F5F1",
+"Y$ c #BADEAA",
+"Z$ c #79BE3B",
+"`$ c #82C024",
+" % c #BFDF8D",
+".% c #FDFEFD",
+"+% c #DCF0E4",
+"@% c #56B354",
+"#% c #7DBF24",
+"$% c #7BBE29",
+"%% c #7AC054",
+"&% c #EEF6EA",
+"*% c #DDE1D9",
+"=% c #DAC27D",
+"-% c #FBE391",
+";% c #F6D774",
+">% c #EADCB5",
+",% c #F6FAF5",
+"'% c #99CE7A",
+")% c #7EBF32",
+"!% c #C2E194",
+"~% c #E4F3EA",
+"{% c #52B253",
+"]% c #7DBF25",
+"^% c #7FBF25",
+"/% c #7CC047",
+"(% c #C8E5BA",
+"_% c #F1F1F1",
+":% c #D7BF7B",
+"<% c #E6C76A",
+"[% c #E1C36C",
+"}% c #EFEEE2",
+"|% c #EDF6E8",
+"1% c #99CE74",
+"2% c #81C132",
+"3% c #B6DA7F",
+"4% c #66B94F",
+"5% c #81C249",
+"6% c #BBDEA6",
+"7% c #F9F9F9",
+"8% c #CBB273",
+"9% c #F3F3F2",
+"0% c #A6D47E",
+"a% c #88C540",
+"b% c #81C026",
+"c% c #82C124",
+"d% c #80C02D",
+"e% c #8FC857",
+"f% c #C4E3AF",
+"g% c #F7F9F6",
+"h% c #DDE2D7",
+"i% c #E3CA81",
+"j% c #F8E08D",
+"k% c #F6D87A",
+"l% c #F8D979",
+"m% c #F7FAF4",
+"n% c #C5E3AA",
+"o% c #96CC5E",
+"p% c #86C439",
+"q% c #80C025",
+"r% c #80C024",
+"s% c #80C02B",
+"t% c #8BC749",
+"u% c #A5D379",
+"v% c #DDEED0",
+"w% c #DEE2D8",
+"x% c #D5BB76",
+"y% c #F9DF88",
+"z% c #F8DD85",
+"A% c #F5DA81",
+"B% c #E8EBE3",
+"C% c #FAFAFA",
+"D% c #EFF7E9",
+"E% c #C8E4AC",
+"F% c #ACD77F",
+"G% c #96CB58",
+"H% c #90C94C",
+"I% c #8DC747",
+"J% c #8EC848",
+"K% c #92CA50",
+"L% c #9CCF65",
+"M% c #B6DC91",
+"N% c #D9ECC7",
+"O% c #FAFBF9",
+"P% c #E4DCCD",
+"Q% c #FAF8F2",
+"R% c #F8FAF5",
+"S% c #F2F8EC",
+"T% c #E1F0D3",
+"U% c #D8ECC5",
+"V% c #DAEDC9",
+"W% c #EBF5E1",
+"X% c #F3F8EE",
+"Y% c #FAFAF9",
+"Z% c #F2F2F1",
+"`% c #E0E6D9",
+" & c #C5AC71",
+".& c #F3D57F",
+"+& c #E9EBE1",
+"@& c #EAECE8",
+"#& c #E3EADB",
+" . + @ # ",
+" $ % & * = - ; > , ' ) ! ",
+" ~ { ] ^ / ( _ : < [ } | 1 2 3 4 5 6 6 ",
+" 7 8 9 0 a b c d e f g h i j _ : k [ } l m n o p q p p ",
+" r s t u v w x y z c d e A g B i j _ : C D E F G H H H I H H ",
+" J K L u v M x y N c O e A P B i j _ Q R S T T T T T T T T T ",
+" U V L u v w x y N W d X A P B i Y _ Z ` ..... ....... ..... ",
+" +.V L u v w x y N W d X A P B i Y _ @.#.$.#.#.$.#.#.#.$.#.#. ",
+" +.K L u v M x y N W d X A P B i Y _ %.&.&.&.&.&.&.&.&.&.&.&. ",
+" +.V L u v w x *.N W d X A P B i =._ -.;.;.;.;.;.;.;.;.;.;.;. ",
+" +.V L u v w x *.N W d X A P B i =._ >.,.'.,.,.'.,.,.,.'.,.,. ",
+" +.K L u v M x y N W d X A P B i =._ ).!.!.!.!.!.!.!.!.!.!.!. ",
+" ~.{.].^./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.8.8.9.8.8.8.9.8.8. ",
+" ~.{.].^./.(._.:.<.[.}.|.1.2.3.4.5.6.0.a.b.a.a.b.a.a.a.b.a.a. ",
+" +.c.].d./.(._.*.<.[.}.|.1.2.3.4.5.6.e.f.f.f.f.f.f.f.f.f.f.f. ",
+" ~.{.].^./.(._.:.<.[.}.|.1.2.3.4.5.6.g.h.i.h.h.i.h.h.h.i.h.h. ",
+" ~.{.].^./.(._.:.<.[.}.|.j.k.3.4.5.l.m.n.o.n.n.o.n.n.n.o.n.n. ",
+" ~.{.].^./.(._.:.p.[.}.|.q.k.3.4.r.l.s.t.u.t.t.u.t.t.t.u.t.t. ",
+" +.c.].d./.(._.*.p.[.}.v.q.2.3.w.r.l.x.y.y.y.y.y.y.y.y.y.y.y. ",
+" ~.{.].^./.(._.:.p.[.}.v.q.z.3.w.r.l.A.B.C.B.B.C.B.B.B.C.B.B. ",
+" ~.{.].^./.(._.:.D.[.}.v.q.z.3.w.r.l.E.F.F.F.F.F.F.F.F.F.F.F. ",
+" +.c.].d./.(._.*.D.[.}.v.q.z.3.w.r.l.G.H.I.J.K.L.M.N.O.P.Q.R. ",
+" ~.{.].^./.(._.:.D.[.}.v.q.z.3.w.r.l.S.T.U.V.W.X.Y.Z.`. +.+++@+#+ ",
+" ~.{.$+%+/.&+_.:.*+=+-+;+>+z.,+'+r.)+!+~+{+]+^+/+(+_+_+:+<+[+}+|+1+ ",
+" ~.2+$+L 3+&+M x *+=+4+5+6+7+8+9+0+a+b+c+d+e+f+g+g+g+g+g+h+i+j+k+l+m+n+ ",
+" ~.o+$+%+3+&+M p+*+=+4+5+6+7+8+q+r+s+t+u+v+w+x+y+z+A+B+C+D+E+F+G+H+I+J+K+ ",
+" L+o+$+%+3+&+M p+*+=+4+5+6+7+M+N+O+P+Q+R+S+T+U+V+W+X+X+Y+Z+T+`+y+ @.@+@@@#@ ",
+" $@2+$+L 3+&+M x %@=+4+5+6+&@*@=@-@;@>@,@'@)@!@~@{@{@{@]@^@/@(@_@T+:@<@[@}@ ",
+" |@1@o+$+%+3+&+M p+2@=+4+5+6+3@4@5@6@7@8@!@9@0@a@b@c@c@c@]@d@e@f@g@h@_@i@j@k@l@ ",
+" m@n@o@p@o+$+%+3+&+M p+y q@r@5+s@t@u@v@w@/@x@y@z@A@B@C@c@c@c@]@D@E@F@G@H@I@J@K@L@M@ ",
+" N@O@o@P@2+$+L 3+&+M x y q@r@5+Q@R@S@T@U@V@W@X@Y@Z@`@ #{@{@{@]@.#+#@###$#a@%#&#*#=#-# ",
+" ;#>#o@p@o+$+%+u &+M p+y q@r@5+,#'#)#!#~#$#{#]#^#/#(#_#c@c@c@]@:#<#[#}#|#1#2#3#4#5#6# ",
+" 7#>#o@p@o+$+%+u &+M p+y q@r@5+8#9#0#a#b#c#d#e#f#g#h#i#c@c@c@]@j#k#l#m#n#o#p#q#r#s#}@ ",
+" t#O@o@P@2+$+L 3+&+M x y q@r@5+u#v#w#x#y#z#A#B#{@C#D#E#{@{@{@]@F#G#{@{@H#I#J#K#L#M#N# ",
+" O#>#o@p@o+P#%+u v M p+y q@r@5+Q#R#S#T#U#V#W#c@{@c@X#Y#c@c@c@Z#`#B#{@c@c@ $.$U#+$@$#$ ",
+" $$%$&$*$2+K ].u v =$x *.N W d -$;$>$,$K#'$)$!${@{@{@~${@{@{@{$]${@{@{@^$/$($K#_$:$m+ ",
+" <$[$&$}$|$K 1$u 2$=$3$*.N W d 4$5$6$7$8$9$0$a$b$c@c@{@c@c@c@{@c@c@{@c$d$e$f$g$h$i$j$ ",
+" k$[$&$}$|$K 1$u 2$=$3$*.N W d l$m$n$o$p$q$0$r$a$!$c@{@c@c@c@{@c@c@s$t$u$q$0$v$w$x$@+ ",
+" y$[$&$}$|$K 1$u 2$=$3$*.N W d z$A$B$C$D$q$0$0$0$E$!${@c@c@c@{@c@F$d$u$0$q$0$G$H$I$J$ ",
+" K$%$&$*$2+K ].u v =$x *.N W d L$M$N$O$P$q$q$q$q$0$a$!${@{@{@{@Q$R$e$q$q$q$e$S$T$U$ ",
+" V$[$&$}$|$K 1$d.2$=$3$*.N W d L$W$X$Y$Z$`$0$0$q$0$r$ %.%c@c@+%@%#%q$0$0$q$$%%%&%*% ",
+" =%-%&$}$|$K 1$d.2$=$3$*.;%W d L$1.>%,%'%)%0$0$q$0$0$q$!%.%~%{%]%0$q$0$0$^%/%(%_% ",
+" :%%$&$*$2+K ].d.v =$x *.;%W d L$<%[%}%|%1%2%`$q$q$q$q$q$3%4%e$q$q$q$q$^%5%6%7% ",
+" 8%%$&$}$|$K 1$d.2$=$3$*.N c d 9%|%0%a%b%c%0$q$0$0$0$q$0$0$e$d%e%f%g%h% ",
+" i%j%}$|$K 1$d.2$k%l% U$m%n%o%p%q%`$0$0$0$q$r%s%t%u%v%7%w% ",
+" x%y%z%A% B%C%D%E%F%G%H%I%J%K%L%M%N%O%_% ",
+" P%Q%R%S%T%U%V%W%X%Y%Z%`% ",
+" &.& +&N##$m+@&#& "};
--- /dev/null
+/* XPM */
+static const char * help_xpm[] = {
+"48 48 665 2",
+" c None",
+". c #D9D9DB",
+"+ c #D5D4D5",
+"@ c #D4D3D3",
+"# c #D6D5D5",
+"$ c #D5D4D4",
+"% c #D3D2D2",
+"& c #D3D2D3",
+"* c #D6D7D9",
+"= c #D5D5D6",
+"- c #DBDBDB",
+"; c #E5E4E4",
+"> c #ECECEC",
+", c #F2F2F2",
+"' c #F5F5F5",
+") c #F7F7F7",
+"! c #F8F7F7",
+"~ c #F6F6F6",
+"{ c #F5F4F4",
+"] c #F0F0F0",
+"^ c #EAE9E9",
+"/ c #E1E1E1",
+"( c #D7D7D7",
+"_ c #D0D0D1",
+": c #D9D9D9",
+"< c #E7E7E7",
+"[ c #F3F3F3",
+"} c #FAFAFA",
+"| c #FDFDFD",
+"1 c #F2F5F8",
+"2 c #DDE4EF",
+"3 c #FCFCFC",
+"4 c #F8F8F8",
+"5 c #EFEFEF",
+"6 c #E2E1E1",
+"7 c #DADADA",
+"8 c #F9F9F9",
+"9 c #FEFEFE",
+"0 c #FCFDFD",
+"a c #D7DDE9",
+"b c #9FA9C9",
+"c c #6B76AA",
+"d c #3A4B8F",
+"e c #222F7E",
+"f c #202A7A",
+"g c #3A4A8F",
+"h c #D7DDEA",
+"i c #E6E5E5",
+"j c #D6D6D7",
+"k c #E8E7E7",
+"l c #F2F4F8",
+"m c #ABB4D0",
+"n c #52619C",
+"o c #283581",
+"p c #1F2C7B",
+"q c #202C7B",
+"r c #202E7C",
+"s c #22307D",
+"t c #23317E",
+"u c #24327E",
+"v c #24327F",
+"w c #202D7C",
+"x c #E0DFDF",
+"y c #CECDCF",
+"z c #DCDCDD",
+"A c #F2F1F1",
+"B c #ADB7D2",
+"C c #425293",
+"D c #1F2D7B",
+"E c #1E2E7C",
+"F c #1F2F7D",
+"G c #2B3B85",
+"H c #37468B",
+"I c #3F4F91",
+"J c #445494",
+"K c #475696",
+"L c #485897",
+"M c #3F4E91",
+"N c #2B3B84",
+"O c #202F7D",
+"P c #1F2E7C",
+"Q c #D3D3D3",
+"R c #E1E0E0",
+"S c #DFE4EE",
+"T c #52649F",
+"U c #1E2F7D",
+"V c #1D2F7D",
+"W c #243682",
+"X c #35488D",
+"Y c #445696",
+"Z c #485A99",
+"` c #475B99",
+" . c #475A99",
+".. c #485A98",
+"+. c #455696",
+"@. c #36478D",
+"#. c #53659F",
+"$. c #F1F1F1",
+"%. c #D7D6D6",
+"&. c #E4E3E3",
+"*. c #B5C0D8",
+"=. c #32468C",
+"-. c #1C307E",
+";. c #203581",
+">. c #33488D",
+",. c #445897",
+"'. c #465B99",
+"). c #465C9A",
+"!. c #455D9A",
+"~. c #455C9A",
+"{. c #465C99",
+"]. c #34488D",
+"^. c #213581",
+"/. c #32458B",
+"(. c #B5BFD7",
+"_. c #F3F2F2",
+":. c #D9D8D9",
+"<. c #E2E2E2",
+"[. c #A4B1CF",
+"}. c #233984",
+"|. c #1B317F",
+"1. c #273D86",
+"2. c #405595",
+"3. c #445E9B",
+"4. c #435F9C",
+"5. c #425F9C",
+"6. c #41609C",
+"7. c #435E9B",
+"8. c #405495",
+"9. c #1C317E",
+"0. c #233883",
+"a. c #DFDFDF",
+"b. c #A3B1CF",
+"c. c #1B3280",
+"d. c #1B3480",
+"e. c #2C468C",
+"f. c #445C99",
+"g. c #435F9B",
+"h. c #415F9C",
+"i. c #40609C",
+"j. c #3F619D",
+"k. c #4D6DA4",
+"l. c #708AB6",
+"m. c #92A5C7",
+"n. c #A4B2CF",
+"o. c #B0BDD6",
+"p. c #B8C5DA",
+"q. c #AEB9D4",
+"r. c #9EA9CA",
+"s. c #7B87B5",
+"t. c #586CA4",
+"u. c #3D5897",
+"v. c #40609D",
+"w. c #445D9A",
+"x. c #445B99",
+"y. c #2D458B",
+"z. c #1B3380",
+"A. c #1B327F",
+"B. c #D3D3D4",
+"C. c #DADADB",
+"D. c #F4F4F4",
+"E. c #B4C1D8",
+"F. c #213B85",
+"G. c #193581",
+"H. c #2E498D",
+"I. c #425D9A",
+"J. c #3E629D",
+"K. c #3D639E",
+"L. c #5778AB",
+"M. c #B1C0D8",
+"N. c #EBEFF5",
+"O. c #FFFFFF",
+"P. c #FEFEFF",
+"Q. c #F4F3F8",
+"R. c #CDCCDF",
+"S. c #6D70A6",
+"T. c #3D5394",
+"U. c #3E609C",
+"V. c #3F609D",
+"W. c #1A3481",
+"X. c #213A85",
+"Y. c #B5C1D8",
+"Z. c #ECEBEB",
+"`. c #DFE5EF",
+" + c #2E4A8F",
+".+ c #173581",
+"++ c #2A488D",
+"@+ c #405E9B",
+"#+ c #3E619D",
+"$+ c #3D629E",
+"%+ c #3B639E",
+"&+ c #39639F",
+"*+ c #7F9BC1",
+"=+ c #EFF2F7",
+"-+ c #FCFCFD",
+";+ c #B7B1CF",
+">+ c #3E438B",
+",+ c #3B609C",
+"'+ c #3D629D",
+")+ c #415E9B",
+"!+ c #183481",
+"~+ c #2F498E",
+"{+ c #DFE5EE",
+"]+ c #FBFBFB",
+"^+ c #4E6AA3",
+"/+ c #163682",
+"(+ c #20438A",
+"_+ c #3E5F9C",
+":+ c #3C639E",
+"<+ c #3A639F",
+"[+ c #3764A0",
+"}+ c #3565A0",
+"|+ c #7B9BC1",
+"1+ c #F9FBFC",
+"2+ c #D6D2E3",
+"3+ c #3A3A85",
+"4+ c #375F9C",
+"5+ c #3B639F",
+"6+ c #3F5F9C",
+"7+ c #214289",
+"8+ c #173681",
+"9+ c #4E69A2",
+"0+ c #A9BAD4",
+"a+ c #143883",
+"b+ c #173D87",
+"c+ c #375B99",
+"d+ c #3366A1",
+"e+ c #507CAE",
+"f+ c #F4F7FA",
+"g+ c #B1AACB",
+"h+ c #303B86",
+"i+ c #38649F",
+"j+ c #385A99",
+"k+ c #183D86",
+"l+ c #163782",
+"m+ c #AAB9D4",
+"n+ c #F1F4F8",
+"o+ c #385B99",
+"p+ c #123984",
+"q+ c #285093",
+"r+ c #37649F",
+"s+ c #3366A0",
+"t+ c #3267A1",
+"u+ c #3469A3",
+"v+ c #B7CADF",
+"w+ c #FAFAFC",
+"x+ c #584D91",
+"y+ c #305898",
+"z+ c #3466A0",
+"A+ c #3665A0",
+"B+ c #3C629E",
+"C+ c #295092",
+"D+ c #A5B8D3",
+"E+ c #113A85",
+"F+ c #15428A",
+"G+ c #36619E",
+"H+ c #3466A1",
+"I+ c #3268A2",
+"J+ c #3168A3",
+"K+ c #507FB1",
+"L+ c #F7F9FB",
+"M+ c #E5E3EE",
+"N+ c #A6A9CA",
+"O+ c #9CAFCE",
+"P+ c #C2D2E4",
+"Q+ c #FAFBFD",
+"R+ c #A29BC1",
+"S+ c #2C3F89",
+"T+ c #3267A2",
+"U+ c #3266A1",
+"V+ c #3465A0",
+"W+ c #37609D",
+"X+ c #174289",
+"Y+ c #113984",
+"Z+ c #A6B8D3",
+"`+ c #466BA3",
+" @ c #0C3E88",
+".@ c #235395",
+"+@ c #3268A3",
+"@@ c #3269A4",
+"#@ c #316BA4",
+"$@ c #8CADCD",
+"%@ c #D4D0E2",
+"&@ c #42478D",
+"*@ c #3068A3",
+"=@ c #3270A8",
+"-@ c #3472AA",
+";@ c #9FBCD7",
+">@ c #CDC9DD",
+",@ c #2A2F7F",
+"'@ c #3169A3",
+")@ c #245395",
+"!@ c #0D3D87",
+"~@ c #466AA3",
+"{@ c #E6E5E6",
+"]@ c #D4DFEB",
+"^@ c #15458C",
+"/@ c #05569D",
+"(@ c #2F639F",
+"_@ c #3269A3",
+":@ c #326AA4",
+"<@ c #326BA5",
+"[@ c #316CA5",
+"}@ c #B8CCE0",
+"|@ c #645898",
+"1@ c #2F5D9D",
+"2@ c #3171AA",
+"3@ c #3271AA",
+"4@ c #6292BE",
+"5@ c #DCD9E7",
+"6@ c #292A7B",
+"7@ c #30629F",
+"8@ c #06569C",
+"9@ c #17458C",
+"0@ c #D5DFEB",
+"a@ c #EFEEEE",
+"b@ c #96AFCE",
+"c@ c #06428B",
+"d@ c #0F6CAD",
+"e@ c #3274AD",
+"f@ c #3169A4",
+"g@ c #316BA5",
+"h@ c #316CA6",
+"i@ c #316DA7",
+"j@ c #ABC4DB",
+"k@ c #D8E4EE",
+"l@ c #E9EEF4",
+"m@ c #F3F2F7",
+"n@ c #2B287A",
+"o@ c #2E6DA8",
+"p@ c #2E71AA",
+"q@ c #2F71AA",
+"r@ c #83ABCD",
+"s@ c #CCC7DC",
+"t@ c #2A3180",
+"u@ c #316AA4",
+"v@ c #3273AD",
+"w@ c #08428B",
+"x@ c #97AFCE",
+"y@ c #F6F5F5",
+"z@ c #5A82B2",
+"A@ c #024A92",
+"B@ c #1B76B4",
+"C@ c #3282B9",
+"D@ c #326DA6",
+"E@ c #326CA5",
+"F@ c #326EA7",
+"G@ c #316FA8",
+"H@ c #3270A9",
+"I@ c #3875AC",
+"J@ c #417BB0",
+"K@ c #4A83B5",
+"L@ c #5C90BD",
+"M@ c #6F9AC3",
+"N@ c #80A8CB",
+"O@ c #7EA1C7",
+"P@ c #133A87",
+"Q@ c #09599D",
+"R@ c #08599C",
+"S@ c #3F7EB2",
+"T@ c #E8EFF6",
+"U@ c #968EB9",
+"V@ c #2D488F",
+"W@ c #316EA7",
+"X@ c #326CA6",
+"Y@ c #3281B9",
+"Z@ c #1B76B3",
+"`@ c #034A92",
+" # c #5C82B2",
+".# c #225B9B",
+"+# c #01539A",
+"@# c #267DB7",
+"## c #3285BC",
+"$# c #327BB4",
+"%# c #326DA7",
+"&# c #326FA8",
+"*# c #3170A9",
+"=# c #3172AB",
+"-# c #1E67A4",
+";# c #0C5C9E",
+"># c #03559B",
+",# c #00549A",
+"'# c #00559A",
+")# c #4483B6",
+"!# c #E6EEF5",
+"~# c #F2F1F6",
+"{# c #484089",
+"]# c #3067A3",
+"^# c #327BB3",
+"/# c #267CB7",
+"(# c #235B9A",
+"_# c #E3E3E3",
+":# c #03458D",
+"<# c #035BA0",
+"[# c #2B80B9",
+"}# c #3185BC",
+"|# c #3186BD",
+"1# c #3177B0",
+"2# c #3171A9",
+"3# c #3172AA",
+"4# c #2F72AA",
+"5# c #06589C",
+"6# c #00559B",
+"7# c #00569B",
+"8# c #00579C",
+"9# c #02599D",
+"0# c #6398C2",
+"a# c #F9FBFD",
+"b# c #8077AB",
+"c# c #2C5195",
+"d# c #3170A8",
+"e# c #3177AF",
+"f# c #035AA0",
+"g# c #05448D",
+"h# c #D2D3D4",
+"i# c #F1F6F9",
+"j# c #00428B",
+"k# c #0460A5",
+"l# c #2F83BB",
+"m# c #3286BD",
+"n# c #3276AE",
+"o# c #286DA8",
+"p# c #0D5C9E",
+"q# c #01549A",
+"r# c #00569C",
+"s# c #00579D",
+"t# c #00589D",
+"u# c #00589E",
+"v# c #045BA0",
+"w# c #7DAACE",
+"x# c #FBFBFC",
+"y# c #9A94BD",
+"z# c #143987",
+"A# c #0E5B9D",
+"B# c #3275AE",
+"C# c #3186BC",
+"D# c #2F82BB",
+"E# c #0460A4",
+"F# c #E4E4E4",
+"G# c #DBE7F0",
+"H# c #00448D",
+"I# c #0562A7",
+"J# c #3084BB",
+"K# c #3187BE",
+"L# c #236AA6",
+"M# c #05579B",
+"N# c #00599F",
+"O# c #005A9F",
+"P# c #005AA0",
+"Q# c #7CABCE",
+"R# c #FBFCFD",
+"S# c #8E88B6",
+"T# c #143E8A",
+"U# c #05569B",
+"V# c #2369A6",
+"W# c #3285BD",
+"X# c #3187BD",
+"Y# c #3083BB",
+"Z# c #0562A6",
+"`# c #00438C",
+" $ c #DBE6F0",
+".$ c #00458D",
+"+$ c #0563A7",
+"@$ c #3184BB",
+"#$ c #3187BF",
+"$$ c #1E7CB9",
+"%$ c #025DA2",
+"&$ c #005BA0",
+"*$ c #005BA1",
+"=$ c #4287BA",
+"-$ c #FDFDFE",
+";$ c #6866A1",
+">$ c #0B4791",
+",$ c #1E7CB8",
+"'$ c #3183BB",
+")$ c #E3E2E2",
+"!$ c #00468E",
+"~$ c #0461A6",
+"{$ c #217EBA",
+"]$ c #036EB1",
+"^$ c #006CB1",
+"/$ c #0062A8",
+"($ c #00599E",
+"_$ c #005CA1",
+":$ c #005DA2",
+"<$ c #0561A5",
+"[$ c #BCD5E7",
+"}$ c #E6E4EE",
+"|$ c #565898",
+"1$ c #064F98",
+"2$ c #0062A7",
+"3$ c #006CB0",
+"4$ c #036DB1",
+"5$ c #217EB9",
+"6$ c #0461A5",
+"7$ c #034B92",
+"8$ c #035EA3",
+"9$ c #2B81B9",
+"0$ c #2881BB",
+"a$ c #046DB0",
+"b$ c #006BB0",
+"c$ c #006DB2",
+"d$ c #006AAF",
+"e$ c #0060A6",
+"f$ c #005EA3",
+"g$ c #005FA4",
+"h$ c #1C71AF",
+"i$ c #F3F8FB",
+"j$ c #EFEEF4",
+"k$ c #4F4D91",
+"l$ c #05539B",
+"m$ c #005CA2",
+"n$ c #0060A5",
+"o$ c #046CB0",
+"p$ c #035DA2",
+"q$ c #034B91",
+"r$ c #D4D4D5",
+"s$ c #E9E9E9",
+"t$ c #FBFAFA",
+"u$ c #2062A1",
+"v$ c #267DB8",
+"w$ c #2E84BC",
+"x$ c #0B6FB1",
+"y$ c #006EB3",
+"z$ c #006FB4",
+"A$ c #0064AA",
+"B$ c #0061A6",
+"C$ c #3E88BC",
+"D$ c #968DB8",
+"E$ c #10408E",
+"F$ c #0061A5",
+"G$ c #0064A9",
+"H$ c #006BAF",
+"I$ c #006AAE",
+"J$ c #2E83BC",
+"K$ c #2062A0",
+"L$ c #D8DADB",
+"M$ c #578AB8",
+"N$ c #1C77B4",
+"O$ c #1A78B5",
+"P$ c #0069AE",
+"Q$ c #0070B5",
+"R$ c #0070B6",
+"S$ c #4D97C7",
+"T$ c #5C4F92",
+"U$ c #065BA4",
+"V$ c #006DB1",
+"W$ c #0069AD",
+"X$ c #1A77B5",
+"Y$ c #1C76B4",
+"Z$ c #005399",
+"`$ c #5789B8",
+" % c #93B4D2",
+".% c #005096",
+"+% c #0F6EAF",
+"@% c #2C81BA",
+"#% c #056BAE",
+"$% c #0068AD",
+"%% c #0071B6",
+"&% c #0072B7",
+"*% c #328EC6",
+"=% c #9BC7E3",
+"-% c #9BC7E2",
+";% c #2C529A",
+">% c #026CB3",
+",% c #0071B7",
+"'% c #056BAD",
+")% c #0F6DAE",
+"!% c #004F95",
+"~% c #D3E1EC",
+"{% c #0A5699",
+"]% c #0260A5",
+"^% c #1573B2",
+"/% c #0067AB",
+"(% c #0070B4",
+"_% c #0073B8",
+":% c #0073B9",
+"<% c #0072B8",
+"[% c #0068AC",
+"}% c #025FA4",
+"|% c #0A5598",
+"1% c #E4E3E4",
+"2% c #3A79AF",
+"3% c #0066AB",
+"4% c #3C93C7",
+"5% c #69A6D1",
+"6% c #6AA5CF",
+"7% c #6AA4CF",
+"8% c #3470AF",
+"9% c #0564AC",
+"0% c #0067AC",
+"a% c #3A78AE",
+"b% c #A0BFD9",
+"c% c #005298",
+"d% c #0063A9",
+"e% c #96C4E1",
+"f% c #7F75A9",
+"g% c #0C529E",
+"h% c #0063A8",
+"i% c #005197",
+"j% c #F1F5F9",
+"k% c #286FAA",
+"l% c #0066AA",
+"m% c #96C4E0",
+"n% c #7F74A9",
+"o% c #0C519D",
+"p% c #0065AA",
+"q% c #286FA9",
+"r% c #F7F6F6",
+"s% c #A3C2DB",
+"t% c #96C3E0",
+"u% c #0C519C",
+"v% c #3E80B4",
+"w% c #96C3DF",
+"x% c #0C509B",
+"y% c #3E7FB4",
+"z% c #DCE8F1",
+"A% c #1969A7",
+"B% c #0C4F9B",
+"C% c #1968A7",
+"D% c #AECAE0",
+"E% c #0860A3",
+"F% c #96C2DE",
+"G% c #0C4F9A",
+"H% c #085FA2",
+"I% c #D6D7D8",
+"J% c #E8E8E9",
+"K% c #9ABED9",
+"L% c #61A3CD",
+"M% c #A5C9E1",
+"N% c #A5C8E1",
+"O% c #5270AB",
+"P% c #0758A0",
+"Q% c #9ABFDA",
+"R% c #0864A6",
+"S% c #0061A7",
+"T% c #0963A5",
+"U% c #EEEDED",
+"V% c #ADCCE2",
+"W% c #196FAE",
+"X% c #196EAD",
+"Y% c #ADCCE1",
+"Z% c #DCE9F2",
+"`% c #3D88BC",
+" & c #0063A7",
+".& c #3D87BB",
+"+& c #A3C7DF",
+"@& c #297DB7",
+"#& c #0065AB",
+"$& c #297DB6",
+"%& c #DFDFE0",
+"&& c #E3E3E4",
+"*& c #A0C6DF",
+"=& c #3B8ABF",
+"-& c #0A6EB0",
+";& c #3B8ABE",
+">& c #D3E4F0",
+",& c #93BFDC",
+"'& c #579DCA",
+")& c #207DB9",
+"!& c #579CC9",
+"~& c #F1F6FA",
+"{& c #DAE9F3",
+"]& c #DBE9F3",
+"^& c #E6E6E6",
+" . + @ # $ % & * ",
+" = - ; > , ' ) ! ! ~ { ] ^ / ( _ ",
+" : < [ } | | | | 1 2 2 1 | | | 3 4 5 6 & ",
+" 7 > 8 9 0 a b c d e f f f f e g c b h 0 | ' i % ",
+" j k 4 | l m n o p q r s t u v t s w q p o n m l 3 [ x y ",
+" z A 3 | B C D E F G H I J K L L K J M H N O P D C B | } ^ Q ",
+" R 4 9 S T U V W X Y Z Z ` ` ` ` ` ` ` .Z ..+.@.W U E #.S | $.%. ",
+" &.8 | *.=.-.;.>.,.` '.).).!.!.!.!.!.!.!.~.).{.` ` ,.].^.V /.(.0 _.:. ",
+" <.8 | [.}.|.1.2.{.!.3.3.4.5.5.6.6.6.6.6.6.5.5.7.3.!.).{.8.1.9.0.[.| _.%. ",
+" a.8 | b.c.d.e.f.3.g.h.6.i.j.k.l.m.n.o.p.q.r.s.t.u.v.6.5.7.w.x.y.z.A.b.0 $.B. ",
+" C.D.9 E.F.G.H.I.5.i.j.J.K.L.M.N.0 O.O.9 O.O.9 P.Q.R.S.T.U.V.6.5.I.H.W.X.Y.| ^ y ",
+" Z.| `. +.+++@+i.#+$+%+&+*+=+9 9 9 9 9 9 9 9 9 9 9 9 -+;+>+,+'+j.i.)+++!+~+{+} x ",
+" x ]+| ^+/+(+_+J.:+<+[+}+|+1+9 O.O.9 O.O.9 O.O.9 O.O.9 O.O.2+3+4+5+'+j.6+7+8+9+| [ % ",
+" , | 0+a+b+c+$+<+[+}+d+e+f+O.9 O.O.9 O.O.9 O.O.9 O.O.9 O.O.O.g+h+}+i+5+'+j+k+l+m+3 i ",
+" / 3 n+o+p+q+%+r+}+s+t+u+v+9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 w+x+y+z+A+&+B+C+p+j+n+' Q ",
+" 5 9 D+E+F+G+A+H+t+I+J+K+L+O.O.9 O.O.9 O.M+N+O+P+Q+O.O.9 O.O.O.9 R+S+T+U+V+[+W+X+Y+Z+| 6 ",
+" a.8 0 `+ @.@}+d+T++@@@#@$@9 O.O.9 O.O.9 %@&@*@=@-@;@O.O.9 O.O.O.9 >@,@'@I+t+s+A+)@!@~@0 5 _ ",
+" {@| ]@^@/@(@U+I+_@:@<@[@}@9 O.O.9 O.O.9 |@1@2@3@3@4@O.O.9 O.O.O.9 5@6@#@:@+@T+d+7@8@9@0@4 ( ",
+" a@| b@c@d@e@J+f@#@g@h@i@j@k@l@w+9 9 9 m@n@o@p@p@q@r@9 9 9 9 9 9 9 s@t@[@g@u@'@I+v@d@w@x@3 / ",
+" y@| z@A@B@C@D@#@E@D@F@G@H@I@J@K@L@M@N@O@P@Q@R@R@S@T@O.O.9 O.O.O.9 U@V@W@X@E@#@X@Y@Z@`@ #| ^ ",
+"< 8 | .#+#@###$#h@%#&#=@*#3@=#p@-#;#>#,#,#'#'#'#)#!#9 O.O.9 O.O.O.~#{#]#G@F@D@[@^###/#+#(#| ] * ",
+"_#3 | :#<#[#}#|#1#G@*#2#3#4#-#5#,#,#6#7#7#8#9#0#a#9 9 9 9 9 9 9 9 b#c#3#*#d#G@e#}#}#[#f#g#| { h#",
+"6 3 i#j#k#l#m#m#|#n#3@=#o#p#q#'#7#r#s#t#u#v#w#1+O.O.9 O.O.9 O.x#y#z#A#o#3#3@B#C#m###D#E#j#i#~ % ",
+"F#| G#H#I#J#m#m#K#m#1#L#M#,#6#r#s#u#N#O#P#Q#R#9 O.O.9 O.O.9 x#S#T#'#,#U#V#e#W#X#m###Y#Z#`#G#! $ ",
+"; 3 $.$+$@$}#|#K##$$$%$,#7#s#u#N#P#&$*$=$-$9 9 9 9 9 9 9 ~#;$>$u#r#6#,#%$,$#$X#|#}#'$Z#.$ $! # ",
+")$3 i#!$~$l#m#m#K#{$]$^$/$N#($O#&$_$:$<$[$O.O.9 O.O.9 O.}$|$1$&$O#u#u#2$3$4$5$X#m###D#6$!$i#) @ ",
+"; 3 | 7$8$9$m#|#0$a$b$^$c$d$e$_$:$f$g$h$i$O.O.9 O.O.9 j$k$l$f$m$*$n$d$c$^$b$o$0$|###[#p$q$| ' r$",
+"s$t$| u$u#v$}#w$x$d$b$^$c$y$z$^$A$B$B$C$9 9 9 9 9 9 9 D$E$F$n$G$b$z$y$c$3$H$I$x$J$}#/#t#K$| , L$",
+" ) | M$,#N$}#O$P$I$H$3$c$y$z$Q$R$Q$^$S$9 O.O.9 O.O.9 T$U$^$z$Q$Q$z$c$V$b$d$P$W$X$}#Y$Z$`$| > ",
+" , | %.%+%@%#%$%I$H$b$^$c$y$Q$Q$%%&%*%=%-%-%-%-%-%-%;%>%,%%%Q$z$y$c$^$b$d$P$$%'%@%)%!% %| ; ",
+" ^ | ~%{%]%^%/%$%P$d$H$3$c$y$z$(%Q$%%,%&%_%:%:%:%_%<%&%%%%%Q$z$y$c$V$3$d$I$W$[%/%^%}%|%~%} - ",
+" 1%]+0 2%t#3%/%[%P$I$d$b$^$c$y$z$(%Q$4%5%6%6%6%6%6%7%8%9%Q$z$z$y$c$^$b$d$P$$%0%/%3%s#a%0 [ = ",
+" D.9 b%c%d%3%/%$%P$d$H$3$^$c$y$z$Q$e%9 O.O.9 O.O.9 f%g%z$z$y$c$^$b$H$P$P$$%/%3%h%i%b%9 < ",
+" < | j%k%*$l%/%[%$%P$d$H$3$^$c$y$y$m%9 9 9 9 9 9 9 n%o%y$c$c$^$b$H$I$W$$%0%3%p%P#q%j%8 : ",
+" r%9 s%6#2$l%/%$%P$P$d$H$3$^$c$c$t%9 O.O.9 O.O.9 f%u%c$V$^$b$H$d$P$$%[%/%l%B$6#s%| > ",
+" < | | v%O#p%3%0%$%W$P$d$H$b$3$^$w%9 9 9 9 9 9 9 n%x%^$3$b$H$I$P$$%[%/%l%G$N#y%| 4 7 ",
+" _.| z%A%f$p%/%0%$%P$P$d$d$H$b$w%9 O.O.9 O.O.9 f%B%b$H$d$I$W$$%[%/%3%p%:$C%z%3 k ",
+" _#8 9 D%E%e$l%/%0%$%$%P$I$d$H$F%9 O.O.9 O.O.9 f%G%d$I$P$P$$%[%/%3%p%n$H%D%9 A I% ",
+" J%3 | K%_$B$l%3%/%[%$%P$P$I$L%M%M%M%N%M%M%N%O%P%P$P$$%$%0%/%3%p%B$*$K%| 4 z ",
+" > 3 | Q%R%S%p%l%/%/%[%$%$%W$W$P$P$P$P$P$P$W$W$$%$%[%/%/%l%p%B$T%Q%| 8 R ",
+" U%3 | V%W%2$p%l%3%/%/%[%$%$%$%$%$%$%$%$%$%[%0%/%/%3%p%G$B$X%Y%| 8 &. ",
+" > 3 9 Z%`%h%p%p%3%l%/%/%/%[%[%0%[%[%/%/%/%/%3%l%p%G$ &.&Z%9 8 <. ",
+" J%8 | | +&@&3%3%3%#&l%l%l%l%l%l%l%l%l%l%p%3%3%l%$&+&| | D.%& ",
+" &&_.| 9 i#*&=&-&P$P$$%0%0%3%3%0%0%$%$%W$-&;&*&i#| ]+Z.C. ",
+" < r%| 9 0 >&,&'&)&4$^$^$^$3$4$)&!&,&>&0 9 3 , x ",
+" < D.]+| | | | | ~&{&]&~&| | | | | 8 5 / ",
+" F#^ , ) t$3 3 3 | 3 3 8 y@a@^&a. ",
+" s$; )$; F#6 _#< "};
--- /dev/null
+/* XPM */
+static const char * page_down_xpm[] = {
+"48 48 546 2",
+" c None",
+". c #C6CCD0",
+"+ c #CBD0D4",
+"@ c #CBD1D4",
+"# c #CCD1D5",
+"$ c #CCD2D5",
+"% c #CDD2D5",
+"& c #CAD0D4",
+"* c #CACFD3",
+"= c #C9CFD3",
+"- c #C9CED3",
+"; c #C8CED2",
+"> c #C3C9CE",
+", c #ECEEF0",
+"' c #F8F9F9",
+") c #F9FAFA",
+"! c #FBFBFB",
+"~ c #FCFCFC",
+"{ c #FDFDFD",
+"] c #FAFBFB",
+"^ c #F7F8F9",
+"/ c #F6F7F8",
+"( c #F5F6F6",
+"_ c #F3F4F5",
+": c #F1F3F5",
+"< c #EEF0F1",
+"[ c #C5CBD0",
+"} c #EDEFF1",
+"| c #FBFCFC",
+"1 c #F0F2F4",
+"2 c #DDE1E4",
+"3 c #DCE0E2",
+"4 c #EFF1F2",
+"5 c #FAFAFB",
+"6 c #F4F5F6",
+"7 c #EAEDEF",
+"8 c #D8DCDF",
+"9 c #F1F2F3",
+"0 c #F1F3F4",
+"a c #D2D7DB",
+"b c #FEFEFE",
+"c c #F8F9FA",
+"d c #E8EAEC",
+"e c #FFFFFF",
+"f c #EDEFF0",
+"g c #D5D9DD",
+"h c #E4E7E9",
+"i c #F5F6F7",
+"j c #DCDFE2",
+"k c #D0D5D9",
+"l c #DEE2E4",
+"m c #DFE2E5",
+"n c #E0E3E6",
+"o c #E3E6E8",
+"p c #EAECEE",
+"q c #F2F3F4",
+"r c #D4D8DC",
+"s c #F0F2F3",
+"t c #EBEEF0",
+"u c #E9EBED",
+"v c #E5E9EA",
+"w c #E0E4E6",
+"x c #D9DDE0",
+"y c #D1D6DA",
+"z c #F2F3F5",
+"A c #E8EBED",
+"B c #E5E9EB",
+"C c #E1E5E8",
+"D c #C7CDD1",
+"E c #EFF1F3",
+"F c #E3E7EA",
+"G c #D8DEE1",
+"H c #E5E8EB",
+"I c #D8DDE1",
+"J c #ECEFF0",
+"K c #EAEDEE",
+"L c #E7EBED",
+"M c #F6F7F7",
+"N c #F3F4F6",
+"O c #E9ECEE",
+"P c #E7EAEC",
+"Q c #E4E8EB",
+"R c #E2E6E9",
+"S c #D7DDE1",
+"T c #EBEEEF",
+"U c #E6EAEC",
+"V c #E1E6E8",
+"W c #D7DCE1",
+"X c #E0E5E8",
+"Y c #D7DCE0",
+"Z c #E0E4E7",
+"` c #EDF0F1",
+" . c #E4E8EA",
+".. c #DFE4E7",
+"+. c #D6DBDF",
+"@. c #F2F4F5",
+"#. c #E9EBEC",
+"$. c #E1E2E3",
+"%. c #DEDFDF",
+"&. c #DFDFE0",
+"*. c #E0E0E0",
+"=. c #E0DFE0",
+"-. c #DFDFDF",
+";. c #DCDDDE",
+">. c #D9DBDE",
+",. c #D3D8DB",
+"'. c #EAEAEB",
+"). c #E8E8E8",
+"!. c #E6EDE7",
+"~. c #D1E7D8",
+"{. c #A3D2B0",
+"]. c #90C9A2",
+"^. c #91CAA2",
+"/. c #A6D3B3",
+"(. c #D3E8DA",
+"_. c #E9EFEA",
+":. c #E9E8E9",
+"<. c #D1D5D3",
+"[. c #FCFDFD",
+"}. c #F3F4F4",
+"|. c #E9E9E9",
+"1. c #DAEBDE",
+"2. c #6FBB8A",
+"3. c #249750",
+"4. c #058839",
+"5. c #0B8B40",
+"6. c #108E44",
+"7. c #118E44",
+"8. c #0C8C40",
+"9. c #07893B",
+"0. c #279852",
+"a. c #77BE8F",
+"b. c #DFEEE3",
+"c. c #ECEBEB",
+"d. c #C5D0C8",
+"e. c #EBEDEF",
+"f. c #EFF0F1",
+"g. c #EAF0EB",
+"h. c #7CC193",
+"i. c #098B3C",
+"j. c #109043",
+"k. c #249A53",
+"l. c #30A15C",
+"m. c #31A25D",
+"n. c #31A15D",
+"o. c #269C55",
+"p. c #129144",
+"q. c #0E8E3F",
+"r. c #83C599",
+"s. c #F0F4F1",
+"t. c #D9DBD9",
+"u. c #F0F0F1",
+"v. c #E1E1E1",
+"w. c #E2EEE5",
+"x. c #3FA664",
+"y. c #098F3D",
+"z. c #239C52",
+"A. c #31A35D",
+"B. c #32A55E",
+"C. c #31A55D",
+"D. c #33A65F",
+"E. c #33A75F",
+"F. c #33A65E",
+"G. c #31A45D",
+"H. c #269E55",
+"I. c #0C903F",
+"J. c #4DAD6E",
+"K. c #E5F0E8",
+"L. c #DCDEDC",
+"M. c #E6F0E8",
+"N. c #39A55F",
+"O. c #0B923F",
+"P. c #2BA258",
+"Q. c #31A65D",
+"R. c #32A85D",
+"S. c #33AA5D",
+"T. c #35AB5D",
+"U. c #C9E8D4",
+"V. c #CEEBD9",
+"W. c #B1DDC2",
+"X. c #27A356",
+"Y. c #31A85D",
+"Z. c #32A75E",
+"`. c #2EA45B",
+" + c #0E9341",
+".+ c #43AA66",
+"++ c #EDF4EE",
+"@+ c #D0D8D2",
+"#+ c #E6E9EB",
+"$+ c #E3E3E4",
+"%+ c #EEF0EE",
+"&+ c #54B374",
+"*+ c #08923C",
+"=+ c #2DA55A",
+"-+ c #38AC5D",
+";+ c #40AE5C",
+">+ c #46B05C",
+",+ c #4BB05C",
+"'+ c #F7FBF8",
+")+ c #D8EEE1",
+"!+ c #2CA454",
+"~+ c #47B05C",
+"{+ c #39AC5D",
+"]+ c #34AA5D",
+"^+ c #2FA65B",
+"/+ c #0D9540",
+"(+ c #61B97D",
+"_+ c #F5F5F5",
+":+ c #EEF1F2",
+"<+ c #F3F5F6",
+"[+ c #ECEEEF",
+"}+ c #E5E4E4",
+"|+ c #A7D8B6",
+"1+ c #07933A",
+"2+ c #26A453",
+"3+ c #3AAD5D",
+"4+ c #44AF5C",
+"5+ c #4EB15C",
+"6+ c #54B45C",
+"7+ c #59B55B",
+"8+ c #5DB75B",
+"9+ c #F8FCF8",
+"0+ c #34A754",
+"a+ c #5AB65B",
+"b+ c #55B45C",
+"c+ c #4FB25C",
+"d+ c #46AF5C",
+"e+ c #3BAD5D",
+"f+ c #2BA657",
+"g+ c #07943A",
+"h+ c #B9E0C3",
+"i+ c #E6E6E6",
+"j+ c #E2E6E8",
+"k+ c #EEF0F2",
+"l+ c #F1F2F4",
+"m+ c #E3E4E4",
+"n+ c #F5F4F4",
+"o+ c #2BA453",
+"p+ c #169E44",
+"q+ c #4EB25C",
+"r+ c #57B45C",
+"s+ c #64BA5B",
+"t+ c #68BC5B",
+"u+ c #6BBD5B",
+"v+ c #F9FCF8",
+"w+ c #3BA954",
+"x+ c #69BC5B",
+"y+ c #65BA5B",
+"z+ c #5EB85B",
+"A+ c #58B55C",
+"B+ c #42AE5C",
+"C+ c #1EA149",
+"D+ c #3FAD63",
+"E+ c #F2F5F2",
+"F+ c #E2E2E3",
+"G+ c #BDE2C5",
+"H+ c #189E34",
+"I+ c #3FAC55",
+"J+ c #53B35C",
+"K+ c #65BB5B",
+"L+ c #6CBD5B",
+"M+ c #71BF5E",
+"N+ c #75C060",
+"O+ c #78C163",
+"P+ c #40AB57",
+"Q+ c #75C061",
+"R+ c #6CBD5C",
+"S+ c #66BB5B",
+"T+ c #5EB75B",
+"U+ c #45AF59",
+"V+ c #1FA035",
+"W+ c #CAE8D1",
+"X+ c #DEE3E5",
+"Y+ c #E6E8EA",
+"Z+ c #EBEAEA",
+"`+ c #77C589",
+" @ c #5EB534",
+".@ c #64B958",
+"+@ c #61B85B",
+"@@ c #6ABD5B",
+"#@ c #80C56D",
+"$@ c #73BF63",
+"%@ c #7FC469",
+"&@ c #81C56A",
+"*@ c #FAFCF9",
+"=@ c #44AD5B",
+"-@ c #80C46A",
+";@ c #7CC367",
+">@ c #7FC56D",
+",@ c #6BBD5C",
+"'@ c #62B95B",
+")@ c #69BB58",
+"!@ c #5FB638",
+"~@ c #8ACD98",
+"{@ c #EFEFEF",
+"]@ c #DCE1E4",
+"^@ c #E2E5E6",
+"/@ c #F2F2F2",
+"(@ c #58B667",
+"_@ c #7EC139",
+":@ c #8EC952",
+"<@ c #6DBE5B",
+"[@ c #74C060",
+"}@ c #9ED38F",
+"|@ c #ECF7EC",
+"1@ c #5EB97A",
+"2@ c #6EBC5A",
+"3@ c #75BE57",
+"4@ c #3FAA52",
+"5@ c #78BF5B",
+"6@ c #A8D797",
+"7@ c #E6F4E7",
+"8@ c #59B774",
+"9@ c #6DBD5E",
+"0@ c #6FBF5B",
+"a@ c #90CB51",
+"b@ c #7DC13E",
+"c@ c #62BA67",
+"d@ c #F8F8F8",
+"e@ c #DADFE3",
+"f@ c #E4E7EA",
+"g@ c #DFE2E3",
+"h@ c #EBF1EB",
+"i@ c #52B24F",
+"j@ c #87C53E",
+"k@ c #9BCE4E",
+"l@ c #8BC856",
+"m@ c #A3D593",
+"n@ c #F8FCF7",
+"o@ c #F0F9F4",
+"p@ c #62BA7B",
+"q@ c #71BC58",
+"r@ c #44AB54",
+"s@ c #A5D48D",
+"t@ c #FBFDFA",
+"u@ c #F1F9F4",
+"v@ c #59B674",
+"w@ c #81C453",
+"x@ c #8AC743",
+"y@ c #57B350",
+"z@ c #FAFAFA",
+"A@ c #E2E7E9",
+"B@ c #DEE0E2",
+"C@ c #E9F1E8",
+"D@ c #55B348",
+"E@ c #8AC640",
+"F@ c #9CCF4F",
+"G@ c #A1D158",
+"H@ c #F3F9ED",
+"I@ c #EDF7F1",
+"J@ c #66BB7D",
+"K@ c #D8EEE2",
+"L@ c #7AC48E",
+"M@ c #F8FCF6",
+"N@ c #D3ECDD",
+"O@ c #53B250",
+"P@ c #8EC845",
+"Q@ c #5BB54D",
+"R@ c #DEE2E6",
+"S@ c #DFE3E7",
+"T@ c #E0E5E7",
+"U@ c #DDDFE1",
+"V@ c #EDF2EC",
+"W@ c #63B950",
+"X@ c #88C53E",
+"Y@ c #98CD4A",
+"Z@ c #AAD56A",
+"`@ c #F8FBF3",
+" # c #FAFDFB",
+".# c #F3FAF6",
+"+# c #F5FAF7",
+"@# c #5CB766",
+"## c #89C745",
+"$# c #8CC744",
+"%# c #66BA50",
+"&# c #D5DBDE",
+"*# c #DDE1E5",
+"=# c #DEE2E5",
+"-# c #DEE3E6",
+";# c #F4F4F4",
+"># c #79C368",
+",# c #81C23A",
+"'# c #9ACE4D",
+")# c #8AC531",
+"!# c #83C124",
+"~# c #A5D260",
+"{# c #F6FBF0",
+"]# c #E8F5EE",
+"^# c #5BB665",
+"/# c #7BBE25",
+"(# c #88C42D",
+"_# c #9ACD4C",
+":# c #84C440",
+"<# c #81C66C",
+"[# c #F9F9F9",
+"}# c #D3D9DD",
+"|# c #DAE0E3",
+"1# c #DBE0E3",
+"2# c #DDE2E5",
+"3# c #DADFE2",
+"4# c #EEEEEE",
+"5# c #A0D491",
+"6# c #73BD37",
+"7# c #92C940",
+"8# c #A5D25F",
+"9# c #61B967",
+"0# c #77BD26",
+"a# c #82C023",
+"b# c #90C83B",
+"c# c #77BF3C",
+"d# c #ADD99F",
+"e# c #F3F3F3",
+"f# c #D1D8DC",
+"g# c #D8DEE2",
+"h# c #D9DFE2",
+"i# c #D9DFE3",
+"j# c #DBE1E4",
+"k# c #E4E5E6",
+"l# c #D6ECCF",
+"m# c #67B83A",
+"n# c #82C12A",
+"o# c #A7D363",
+"p# c #F0F8F3",
+"q# c #84C228",
+"r# c #6BBA3E",
+"s# c #DFF0D9",
+"t# c #E7E7E7",
+"u# c #D0D6DA",
+"v# c #D6DCE0",
+"w# c #DADDE0",
+"x# c #F9F8F8",
+"y# c #82C567",
+"z# c #77BD2B",
+"A# c #A4D25F",
+"B# c #F7FBF1",
+"C# c #EAF6EF",
+"D# c #5AB664",
+"E# c #7ABE2B",
+"F# c #8FCB79",
+"G# c #F8F9F7",
+"H# c #CED4D9",
+"I# c #D4DADE",
+"J# c #D4DADF",
+"K# c #D5DBDF",
+"L# c #D5DADE",
+"M# c #D0E8C6",
+"N# c #76BD45",
+"O# c #7FBF25",
+"P# c #ABD56B",
+"Q# c #FAFDF7",
+"R# c #F0F8F4",
+"S# c #66BC72",
+"T# c #74BB26",
+"U# c #80C025",
+"V# c #78BE47",
+"W# c #DBEDD3",
+"X# c #EEEFEC",
+"Y# c #CBD3D8",
+"Z# c #D1D8DD",
+"`# c #D2D8DD",
+" $ c #D2D9DE",
+".$ c #D3D9DE",
+"+$ c #D7DBDE",
+"@$ c #F8F8F7",
+"#$ c #A9D692",
+"$$ c #7ABE3A",
+"%$ c #81C024",
+"&$ c #AED76F",
+"*$ c #F8FCFA",
+"=$ c #62BA70",
+"-$ c #73BB26",
+";$ c #82C024",
+">$ c #7DC03D",
+",$ c #B2DA9D",
+"'$ c #C9D1D6",
+")$ c #CFD6DB",
+"!$ c #D0D7DC",
+"~$ c #E0E2E4",
+"{$ c #F5F9F4",
+"]$ c #A3D385",
+"^$ c #7FC13B",
+"/$ c #80C024",
+"($ c #A8D466",
+"_$ c #7BC36A",
+":$ c #77BC26",
+"<$ c #83C23F",
+"[$ c #ABD78F",
+"}$ c #F8FAF6",
+"|$ c #E3E9DD",
+"1$ c #C7CFD5",
+"2$ c #CCD4D9",
+"3$ c #CDD4D9",
+"4$ c #CED5DA",
+"5$ c #E6E7E8",
+"6$ c #F5F9F3",
+"7$ c #AED88E",
+"8$ c #89C64A",
+"9$ c #7FC028",
+"0$ c #81C029",
+"a$ c #8CC74B",
+"b$ c #B7DC9B",
+"c$ c #F5F9F2",
+"d$ c #ECECEA",
+"e$ c #BAC2C8",
+"f$ c #BCC5CA",
+"g$ c #BDC5CA",
+"h$ c #BDC5CB",
+"i$ c #BEC6CB",
+"j$ c #BEC5CB",
+"k$ c #DFE1E3",
+"l$ c #F8FAF7",
+"m$ c #D0E8BA",
+"n$ c #9CCE68",
+"o$ c #87C43F",
+"p$ c #7FC027",
+"q$ c #80C027",
+"r$ c #8AC642",
+"s$ c #9ED06B",
+"t$ c #D3E9BF",
+"u$ c #FCFCFB",
+"v$ c #EBEBE9",
+"w$ c #DDE3D8",
+"x$ c #CDE7B6",
+"y$ c #B1D988",
+"z$ c #97CC5C",
+"A$ c #90C94D",
+"B$ c #8EC747",
+"C$ c #91C94E",
+"D$ c #98CD5E",
+"E$ c #B2DA89",
+"F$ c #D1E9BB",
+"G$ c #F7FAF4",
+"H$ c #E5E9E0",
+"I$ c #F6F5F5",
+"J$ c #F9FAF7",
+"K$ c #F2F8EC",
+"L$ c #E5F2D9",
+"M$ c #D9ECC7",
+"N$ c #E7F3DB",
+"O$ c #F2F8ED",
+"P$ c #FAFBF9",
+"Q$ c #F6F7F5",
+"R$ c #E8EBE4",
+"S$ c #E5ECDD",
+"T$ c #ECECEC",
+"U$ c #E6EDDF",
+" . + + @ # $ % % % % % % % % $ # @ + & * = - ; > ",
+" , ' ) ! ~ ~ { { { ~ { ~ { { ~ ~ ] ) ^ / ( _ : < [ ",
+" } ) | ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | ) ' / ( _ 1 2 3 ",
+" 4 ~ ~ { { ~ { { { ~ { ~ { { ~ { { ~ ~ 5 ' / 6 _ 7 8 _ ",
+" 9 { ~ { { ~ { { { ~ { ~ { { ~ { { ~ { ~ ) ^ / 6 0 a b , ",
+" 9 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ! c / ( _ a b b d ",
+" 9 { ~ { { ~ { { { { { { { { ~ { { ~ { { ~ ] ^ / 6 a b e b f ",
+" 9 { ~ { { { { b b { b { b { { { { ~ { { ~ | ) ^ ( g b e e b h ",
+" 9 ~ ~ ~ { { { { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ) ^ i g b b b b { j ",
+" 9 { ~ { { { b b b { b { b b { { { ~ { { ~ { ] ^ / k l m n o p q r ",
+" 9 { ~ { { { b b b { b { b b { { { ~ { { ~ { | c / s t u v w x y & ",
+" 9 ~ ~ { { { { { { { { { { { { { { ~ ~ ~ ~ ~ | c / 6 z 4 } 7 A B C D ",
+" 9 { ~ { b { b b b b b { b b { b { ~ { { ~ { | c / 6 z E } 7 A B F G ",
+" 9 { ~ { b { b b b { b { b b { b { ~ { { ~ { | c / 6 z E } 7 A B F G ",
+" 9 ~ ~ { { { { { { { { { { { { { { ~ ~ ~ ~ ~ ! c / 6 z 4 f 7 A H F I ",
+" 9 { ~ { { { b b b { b { b b { { { ~ { { ~ { ] ^ / 6 0 4 J K L H F I ",
+" 9 { ~ { { { b b b { b { b b { { { ~ { { ~ ~ ) ^ M N 1 4 J O P Q R S ",
+" 9 { ~ { { { { b b { b { b { { { { ~ { { ~ | ) ^ ( _ s < T O U Q V W ",
+" 9 ~ ~ ~ ~ ~ ~ { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ] ^ / 6 0 4 } 7 O B F X Y ",
+" 9 { ~ { { ~ { { { ~ { ~ { { ~ { { ~ { { | ) ^ i _ 1 4 J K A H F Z Y ",
+" 9 { ~ { { ~ { { { ~ { ~ { { ~ { { ~ { ~ ) ^ / 6 z s ` T O P .R ..+. ",
+" 4 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ] ' / 6 @.#.$.%.&.*.=.-.;.>.,. ",
+" } ] ~ { { ~ { { { ~ { ~ { { ~ { { ~ ] c / 6 '.-.).!.~.{.].^./.(._.:.<. ",
+" , ' ) ! [.~ { { { ~ { ~ { { ~ ~ ! ) ' / }.*.|.1.2.3.4.5.6.7.8.9.0.a.b.c.d. ",
+" e./ ^ c ) ! ~ ~ ~ ~ ~ ~ ~ ~ ! ) c ^ / f.-.g.h.i.j.k.l.m.m.m.m.n.o.p.q.r.s.t. ",
+" u ( M ^ ^ ' ) ) ] ] ] ] ) ) ' ^ / M u.v.w.x.y.z.A.B.C.D.E.E.F.C.B.G.H.I.J.K.L. ",
+" P _ 6 i / / ^ ^ ^ ^ ^ ^ ^ ^ / M 6 _ =.M.N.O.P.Q.R.S.T.U.V.V.W.X.S.Y.Z.`. +.+++@+ ",
+" #+s 0 _ 6 6 ( i M / / M i ( 6 _ _ $+%+&+*+=+S.-+;+>+,+'+b b )+!+~+;+{+]+^+/+(+_+ ",
+" h :+4 1 0 z _ _ <+_ 6 _ _ _ z 0 [+}+|+1+2+3+4+5+6+7+8+9+e e )+0+a+b+c+d+e+f+g+h+i+ ",
+" j+J } k+4 E s 1 1 l+l+1 1 s E 4 m+n+o+p+;+q+r+8+s+t+u+v+e e )+w+x+y+z+A+c+B+C+D+E+ ",
+" Z O 7 t J } < k+4 4 4 4 k+< } , F+G+H+I+J+8+K+L+M+N+O+v+b b )+P+Q+M+R+S+T+b+U+V+W+v. ",
+" X+A O O 7 7 T , , , J t t T 7 Y+Z+`+ @.@+@@@M+#@$@%@&@*@e e )+=@-@;@>@,@@@'@)@!@~@{@ ",
+" ]@B U L A O O O O O O O O O O ^@/@(@_@:@<@[@}@|@1@2@3@v+e e )+4@5@6@7@8@9@0@a@b@c@d@ ",
+" e@F f@Q B B U P P L L P P U B g@h@i@j@k@l@m@n@b o@p@q@v+b b )+r@s@t@b u@v@w@k@x@y@z@ ",
+" I X V A@F F Q Q Q Q Q Q Q .F B@C@D@E@F@G@H@e b e I@J@v+e e K@L@M@b e e N@O@F@P@Q@! ",
+" Y R@S@..T@X V R R R R R R C X U@V@W@X@k@Y@Z@`@b b b u@ #b b .#+#b b b o@@###k@$#%#z@ ",
+" &#]@*#=#R@R@-#S@S@....S@S@-#R@j ;#>#,#'#)#!#~#{#e e b e e e b e e b ]#^#/#(#_#:#<#[# ",
+" }#|#1#]@]@]@*#2#2#2#2#2#2#*#]@3#4#5#6#7#!#!#!#8#`@e b e e e b e e ]#9#0#a#!#b#c#d#e# ",
+" f#S g#h#i#|#1#j#j#j#j#j#j#1#|#i#k#l#m#n#a#!#!#a#o#`@b e e e b e p#^#0#!#a#!#q#r#s#t# ",
+" u#v#v#S S S S g#g#g#g#g#g#S S S w#x#y#z#a#a#a#a#a#A#B#b b b b C#D#/#a#a#a#a#E#F#G# ",
+" H#I#J#K#K#K#v#v#v#v#v#v#v#v#K#K#L#'.M#N#O#!#!#a#!#!#P#Q#e e R#S#T#a#!#!#a#U#V#W#X# ",
+" Y#Z#`# $.$.$J#J#J#J#J#J#J#I#.$.$ $+$@$#$$$%$!#a#!#!#a#&$Q#*$=$-$!#a#!#!#;$>$,$z@ ",
+" '$)$)$!$!$Z#Z#Z#Z#`#`#Z#Z#Z#Z#!$!$)$~${$]$^$/$a#a#a#a#a#($_$:$a#a#a#a#%$<$[$}$|$ ",
+" 1$2$3$4$4$4$)$)$)$)$)$)$)$)$4$4$4$3$3$5$6$7$8$9$;$!#a#!#!#!#a#!#!#;$0$a$b$c$d$ ",
+" e$f$f$g$h$h$h$h$i$i$i$j$h$h$h$h$g$f$f$j$k$l$m$n$o$p$%$!#!#!#a#%$q$r$s$t$u$v$ ",
+" w$_+c$x$y$z$A$B$B$C$D$E$F$G$d@ ",
+" H$I$J$K$L$M$M$N$O$P$Q$R$ ",
+" S$T${@{@T$U$ "};
--- /dev/null
+/* XPM */
+static const char * remove_xpm[] = {
+"48 48 705 2",
+" c None",
+". c #DDD8D8",
+"+ c #D6D4D4",
+"@ c #D4D3D3",
+"# c #D6D5D5",
+"$ c #D5D4D4",
+"% c #D3D2D2",
+"& c #D5D2D2",
+"* c #DBD6D6",
+"= c #D7D4D4",
+"- c #DCDBDB",
+"; c #E5E4E4",
+"> c #ECECEC",
+", c #F2F2F2",
+"' c #F5F5F5",
+") c #F7F7F7",
+"! c #F8F7F7",
+"~ c #F6F6F6",
+"{ c #F5F4F4",
+"] c #F0F0F0",
+"^ c #EAE9E9",
+"/ c #E1E1E1",
+"( c #D8D7D7",
+"_ c #D2CFCF",
+": c #DAD9D8",
+"< c #E7E7E7",
+"[ c #F3F3F3",
+"} c #FAFAFA",
+"| c #FDFDFD",
+"1 c #FBF3F3",
+"2 c #F5E0DE",
+"3 c #F5E0DF",
+"4 c #FCFCFC",
+"5 c #F8F8F8",
+"6 c #EFEFEF",
+"7 c #E2E1E1",
+"8 c #D4D2D2",
+"9 c #DADADA",
+"0 c #F9F9F9",
+"a c #FEFEFE",
+"b c #FDFCFC",
+"c c #F2D9D8",
+"d c #DEA39F",
+"e c #CA6F69",
+"f c #BA4038",
+"g c #AE271E",
+"h c #AA241B",
+"i c #AD271E",
+"j c #DEA29F",
+"k c #E6E5E5",
+"l c #D9D6D6",
+"m c #E8E7E7",
+"n c #FAF3F2",
+"o c #E3AEAB",
+"p c #C25750",
+"q c #B02D24",
+"r c #AC241B",
+"s c #AD241B",
+"t c #AE261D",
+"u c #AF281F",
+"v c #AF2820",
+"w c #AF2920",
+"x c #AC231A",
+"y c #E2AEAB",
+"z c #E0DFDF",
+"A c #D0CDCD",
+"B c #DEDCDC",
+"C c #F2F1F1",
+"D c #E4B0AD",
+"E c #BD4840",
+"F c #AD231A",
+"G c #AE241B",
+"H c #AF251C",
+"I c #B43129",
+"J c #B83C34",
+"K c #BC443D",
+"L c #BE4943",
+"M c #BF4C46",
+"N c #BF4E47",
+"O c #BB443D",
+"P c #B33129",
+"Q c #AE251C",
+"R c #AD231B",
+"S c #E4B1AD",
+"T c #E1E0E0",
+"U c #F5E1E0",
+"V c #C55952",
+"W c #AF241B",
+"X c #B22B23",
+"Y c #BA3C35",
+"Z c #BF4B44",
+"` c #C14F49",
+" . c #C24E48",
+".. c #C24F49",
+"+. c #C14E48",
+"@. c #B93C35",
+"#. c #C45952",
+"$. c #F1F1F1",
+"%. c #D7D6D6",
+"&. c #E4E3E3",
+"*. c #E7B9B6",
+"=. c #B93932",
+"-. c #B0241B",
+";. c #B32820",
+">. c #BB3B34",
+",. c #C14C46",
+"'. c #C34E48",
+"). c #C44F49",
+"!. c #C54E48",
+"~. c #C54F49",
+"{. c #C64F49",
+"]. c #C44E48",
+"^. c #C34F49",
+"/. c #BA3B34",
+"(. c #B22820",
+"_. c #B83931",
+":. c #F3F2F2",
+"<. c #D9D8D8",
+"[. c #E2E2E2",
+"}. c #E2A9A6",
+"|. c #B42B23",
+"1. c #B2241C",
+"2. c #B73028",
+"3. c #C14841",
+"4. c #C64E48",
+"5. c #C74E48",
+"6. c #C84E48",
+"7. c #C84F48",
+"8. c #C94F48",
+"9. c #C04841",
+"0. c #B1241C",
+"a. c #E0DEDE",
+"b. c #E3A9A5",
+"c. c #B3241C",
+"d. c #B4251D",
+"e. c #BB372F",
+"f. c #C44D47",
+"g. c #C84F49",
+"h. c #C94F49",
+"i. c #CA5049",
+"j. c #CA4F48",
+"k. c #BB362F",
+"l. c #DCDADA",
+"m. c #F4F4F4",
+"n. c #E9B9B7",
+"o. c #B72B23",
+"p. c #B5251D",
+"q. c #BE3932",
+"r. c #C64D47",
+"s. c #CB5049",
+"t. c #CB4F48",
+"u. c #CC5049",
+"v. c #CD5049",
+"w. c #CD4F48",
+"x. c #CE5049",
+"y. c #CE4F48",
+"z. c #CC4F48",
+"A. c #C74F49",
+"B. c #BD3932",
+"C. c #E9BAB7",
+"D. c #D1CDCD",
+"E. c #ECEBEB",
+"F. c #B6241C",
+"G. c #BE362F",
+"H. c #C84D47",
+"I. c #D97B76",
+"J. c #DF908B",
+"K. c #CF4F48",
+"L. c #D04F48",
+"M. c #D97A75",
+"N. c #C74D47",
+"O. c #B5241C",
+"P. c #FBFBFB",
+"Q. c #CA5953",
+"R. c #B7241C",
+"S. c #BD3028",
+"T. c #C94E47",
+"U. c #DB7B76",
+"V. c #FCF6F5",
+"W. c #FDFAFA",
+"X. c #E09692",
+"Y. c #D15049",
+"Z. c #D25049",
+"`. c #D24F48",
+" + c #D34F48",
+".+ c #D35049",
+"++ c #D14F48",
+"@+ c #E49A96",
+"#+ c #FEFBFB",
+"$+ c #FBF4F4",
+"%+ c #D87873",
+"&+ c #CA4F49",
+"*+ c #C84E47",
+"=+ c #BC3028",
+"-+ c #E7B0AD",
+";+ c #B9241C",
+">+ c #BC2920",
+",+ c #C74841",
+"'+ c #CF5049",
+")+ c #DC7E78",
+"!+ c #FBF0F0",
+"~+ c #FFFFFF",
+"{+ c #E1928E",
+"]+ c #D4514A",
+"^+ c #D44F48",
+"/+ c #D55049",
+"(+ c #D54F48",
+"_+ c #D45049",
+":+ c #E59691",
+"<+ c #F9EFEE",
+"[+ c #BB2820",
+"}+ c #B8241C",
+"|+ c #E2E0E0",
+"1+ c #FBF3F2",
+"2+ c #C64740",
+"3+ c #BA241C",
+"4+ c #C43C34",
+"5+ c #DC7B76",
+"6+ c #FCF9F8",
+"7+ c #E2928E",
+"8+ c #D74F48",
+"9+ c #D84F48",
+"0+ c #E79691",
+"a+ c #FDF9F9",
+"b+ c #D97873",
+"c+ c #C33C34",
+"d+ c #E7AEAA",
+"e+ c #BB241C",
+"f+ c #BF2C23",
+"g+ c #CC4D46",
+"h+ c #D05049",
+"i+ c #DE7B76",
+"j+ c #E59592",
+"k+ c #DB4F49",
+"l+ c #DB4E48",
+"m+ c #EA9996",
+"n+ c #DA7873",
+"o+ c #CB4D46",
+"p+ c #E7AEAB",
+"q+ c #CE5750",
+"r+ c #BF251B",
+"s+ c #C73D35",
+"t+ c #E48F8B",
+"u+ c #E5928E",
+"v+ c #DD504A",
+"w+ c #DD4E48",
+"x+ c #DD4F49",
+"y+ c #EA9591",
+"z+ c #FCFAF9",
+"A+ c #C37671",
+"B+ c #C84740",
+"C+ c #BE251C",
+"D+ c #CD5750",
+"E+ c #F4D9D8",
+"F+ c #C12D25",
+"G+ c #D0281C",
+"H+ c #CE4C44",
+"I+ c #D75049",
+"J+ c #E39592",
+"K+ c #FDF9F8",
+"L+ c #E7928E",
+"M+ c #E04E48",
+"N+ c #E04F49",
+"O+ c #EC9592",
+"P+ c #FEF9F9",
+"Q+ c #C57F7B",
+"R+ c #AB2E27",
+"S+ c #D14C45",
+"T+ c #D0271D",
+"U+ c #C02D25",
+"V+ c #EFEEEE",
+"W+ c #E5A39F",
+"X+ c #C3251B",
+"Y+ c #DC3528",
+"Z+ c #D95348",
+"`+ c #D64F48",
+" @ c #D94F48",
+".@ c #E4918D",
+"+@ c #FCF8F8",
+"@@ c #E89691",
+"#@ c #EE9A95",
+"$@ c #FEFBFA",
+"%@ c #FBF8F8",
+"&@ c #C27A75",
+"*@ c #AC2E27",
+"=@ c #D74D47",
+"-@ c #D85248",
+";@ c #DB3428",
+">@ c #C2251B",
+",@ c #F6F5F5",
+"'@ c #D76F69",
+")@ c #CA251B",
+"!@ c #DF4234",
+"~@ c #E15847",
+"{@ c #D65148",
+"]@ c #D84F49",
+"^@ c #DA4F49",
+"/@ c #DE504A",
+"(@ c #E7918D",
+"_@ c #AF312A",
+":@ c #D84C45",
+"<@ c #E15647",
+"[@ c #DF4034",
+"}@ c #C9251B",
+"|@ c #D7706A",
+"1@ c #CC4139",
+"2@ c #D0271C",
+"3@ c #E04B3D",
+"4@ c #E35A47",
+"5@ c #DF5847",
+"6@ c #DC4F49",
+"7@ c #DE4F49",
+"8@ c #DF4E48",
+"9@ c #E14F49",
+"0@ c #E25048",
+"a@ c #E8968F",
+"b@ c #AE2F27",
+"c@ c #DC4C45",
+"d@ c #DF4F49",
+"e@ c #DE5747",
+"f@ c #E35847",
+"g@ c #E0493D",
+"h@ c #CF261C",
+"i@ c #DCD6D6",
+"j@ c #E3E3E2",
+"k@ c #C7281F",
+"l@ c #D4291E",
+"m@ c #E15142",
+"n@ c #E25B46",
+"o@ c #E35F46",
+"p@ c #DE5647",
+"q@ c #DC4E48",
+"r@ c #DE4E48",
+"s@ c #E14F48",
+"t@ c #E25045",
+"u@ c #DF4435",
+"v@ c #DC3320",
+"w@ c #E48275",
+"x@ c #AC251B",
+"y@ c #DD4134",
+"z@ c #E14E46",
+"A@ c #E35D46",
+"B@ c #E25947",
+"C@ c #E14F42",
+"D@ c #C5281F",
+"E@ c #FCF3F3",
+"F@ c #C7251C",
+"G@ c #D72B1F",
+"H@ c #E25445",
+"I@ c #E35C47",
+"J@ c #E35F47",
+"K@ c #E46247",
+"L@ c #E05448",
+"M@ c #E14B3F",
+"N@ c #DD3827",
+"O@ c #DC311B",
+"P@ c #DC321A",
+"Q@ c #DC361B",
+"R@ c #E58575",
+"S@ c #AE291C",
+"T@ c #D7301A",
+"U@ c #DC301B",
+"V@ c #DD3727",
+"W@ c #E14A3F",
+"X@ c #DF5448",
+"Y@ c #E36147",
+"Z@ c #E35E47",
+"`@ c #E25245",
+" # c #D72A1F",
+".# c #C6251C",
+"+# c #FCF4F3",
+"@# c #E4E4E4",
+"## c #F8E1DF",
+"$# c #C9251C",
+"%# c #D92D20",
+"&# c #E25646",
+"*# c #E36047",
+"=# c #E46446",
+"-# c #E46546",
+";# c #E35647",
+"># c #E0483A",
+",# c #DC331F",
+"'# c #DC311A",
+")# c #DB3419",
+"!# c #DC3719",
+"~# c #DC3919",
+"{# c #DC3B19",
+"]# c #E68D7A",
+"^# c #FCFAFA",
+"/# c #AD291A",
+"(# c #D83719",
+"_# c #DC361A",
+":# c #DB3319",
+"<# c #DC301A",
+"[# c #DC311F",
+"}# c #E0463A",
+"|# c #E35547",
+"1# c #E46447",
+"2# c #E36246",
+"3# c #E25447",
+"4# c #D82B20",
+"5# c #C8251C",
+"6# c #F8E0DF",
+"7# c #CB241B",
+"8# c #E25C46",
+"9# c #E36046",
+"0# c #E46746",
+"a# c #E35B34",
+"b# c #DD3A1B",
+"c# c #DB3219",
+"d# c #DC3519",
+"e# c #DC3819",
+"f# c #DD3D18",
+"g# c #DD3F18",
+"h# c #EC917A",
+"i# c #FEFAF9",
+"j# c #CE837A",
+"k# c #DB3D18",
+"l# c #DD3C18",
+"m# c #DC3A19",
+"n# c #DC3419",
+"o# c #DB311A",
+"p# c #DD381C",
+"q# c #E25A34",
+"r# c #E46646",
+"s# c #E35E46",
+"t# c #E25A46",
+"u# c #E25547",
+"v# c #D92C20",
+"w# c #CA251C",
+"x# c #E3E2E2",
+"y# c #CD241C",
+"z# c #D92B1F",
+"A# c #E46346",
+"B# c #E25B37",
+"C# c #DF481A",
+"D# c #DF4817",
+"E# c #DE4218",
+"F# c #DD3B19",
+"G# c #DD3C19",
+"H# c #DE421A",
+"I# c #EB8D74",
+"J# c #FDFAF9",
+"K# c #E68A74",
+"L# c #DD3E19",
+"M# c #DE4018",
+"N# c #DE4717",
+"O# c #DF471A",
+"P# c #E25937",
+"Q# c #E25345",
+"R# c #D82A1F",
+"S# c #CB251C",
+"T# c #D0271F",
+"U# c #D8291E",
+"V# c #E35B47",
+"W# c #E35C3E",
+"X# c #DE441C",
+"Y# c #DE4518",
+"Z# c #DF4917",
+"`# c #DF4D16",
+" $ c #DE4517",
+".$ c #EB8F74",
+"+$ c #FEF9F8",
+"@$ c #FDF8F8",
+"#$ c #E68B74",
+"$$ c #DE4118",
+"%$ c #DE4417",
+"&$ c #DF4B16",
+"*$ c #DE4418",
+"=$ c #DE431C",
+"-$ c #E25A3E",
+";$ c #E35947",
+">$ c #E15042",
+",$ c #D8281E",
+"'$ c #CF271F",
+")$ c #E9E8E8",
+"!$ c #FBFAFA",
+"~$ c #D84039",
+"{$ c #D7261C",
+"]$ c #E14C3D",
+"^$ c #E35C43",
+"/$ c #DE4422",
+"($ c #DD4018",
+"_$ c #DE4817",
+":$ c #DF4C16",
+"<$ c #E05015",
+"[$ c #E05314",
+"}$ c #ED9A78",
+"|$ c #E79678",
+"1$ c #E05214",
+"2$ c #DF4E16",
+"3$ c #DF4A17",
+"4$ c #DE4318",
+"5$ c #DE4223",
+"6$ c #E25A44",
+"7$ c #E25847",
+"8$ c #E14A3D",
+"9$ c #D6261C",
+"0$ c #D74039",
+"a$ c #E26F69",
+"b$ c #D6251B",
+"c$ c #DF4334",
+"d$ c #E04B31",
+"e$ c #DD4019",
+"f$ c #DF4718",
+"g$ c #E05018",
+"h$ c #EC9772",
+"i$ c #FEFBF9",
+"j$ c #E79273",
+"k$ c #DF4E18",
+"l$ c #DE4617",
+"m$ c #E04931",
+"n$ c #E35747",
+"o$ c #DF4134",
+"p$ c #D5241B",
+"q$ c #E16F69",
+"r$ c #EDA29F",
+"s$ c #D7241B",
+"t$ c #DD3629",
+"u$ c #E25342",
+"v$ c #DD381E",
+"w$ c #DE4618",
+"x$ c #DE4917",
+"y$ c #EC9373",
+"z$ c #CF887A",
+"A$ c #E68F73",
+"B$ c #DD371E",
+"C$ c #E25142",
+"D$ c #DD3529",
+"E$ c #D5241C",
+"F$ c #ECA39F",
+"G$ c #F7D9D7",
+"H$ c #D92C25",
+"I$ c #DB291C",
+"J$ c #DE3F2E",
+"K$ c #EC9579",
+"L$ c #C27A76",
+"M$ c #AE3317",
+"N$ c #E05D12",
+"O$ c #E89A71",
+"P$ c #E79179",
+"Q$ c #DD4218",
+"R$ c #DB301A",
+"S$ c #DE3D2E",
+"T$ c #DA271D",
+"U$ c #D82C25",
+"V$ c #E25750",
+"W$ c #DA271B",
+"X$ c #DB2B1B",
+"Y$ c #DD3A19",
+"Z$ c #DD3F19",
+"`$ c #EA876D",
+" % c #C37A76",
+".% c #B0351A",
+"+% c #DD5913",
+"@% c #E25D12",
+"#% c #E25D14",
+"$% c #E89872",
+"%% c #E3836D",
+"&% c #DC3D19",
+"*% c #DB2E1A",
+"=% c #DB2A1B",
+"-% c #DA251B",
+";% c #E15650",
+">% c #F1AEAA",
+",% c #DA291A",
+"'% c #DB2D1A",
+")% c #E66F51",
+"!% c #FBF4F3",
+"~% c #AE3118",
+"{% c #DD5513",
+"]% c #E15913",
+"^% c #E25913",
+"/% c #E25813",
+"(% c #E15713",
+"_% c #E89978",
+":% c #FAF3F3",
+"<% c #B55953",
+"[% c #CD3419",
+"}% c #DC3619",
+"|% c #DB2B1A",
+"1% c #DB281B",
+"2% c #F1AEAB",
+"3% c #FCF3F2",
+"4% c #E14D3F",
+"5% c #E26D51",
+"6% c #FAEEEC",
+"7% c #AE2F18",
+"8% c #DE5314",
+"9% c #E05513",
+"0% c #E05613",
+"a% c #E05514",
+"b% c #E05414",
+"c% c #E79373",
+"d% c #F6EDEC",
+"e% c #B35853",
+"f% c #B92D19",
+"g% c #DB3719",
+"h% c #DB291A",
+"i% c #E04C3F",
+"j% c #F7F6F6",
+"k% c #F2B3AD",
+"l% c #DB2C1A",
+"m% c #E37055",
+"n% c #AF311B",
+"o% c #DB4D15",
+"p% c #E05115",
+"q% c #E05215",
+"r% c #DF4F18",
+"s% c #E69074",
+"t% c #B55C56",
+"u% c #B82D19",
+"v% c #DB3819",
+"w% c #DC341A",
+"x% c #DB2A1A",
+"y% c #F1B3AD",
+"z% c #E46452",
+"A% c #DA2A1A",
+"B% c #AD2C19",
+"C% c #DA4817",
+"D% c #E6917A",
+"E% c #DB3919",
+"F% c #DB3519",
+"G% c #E46352",
+"H% c #F9E3E0",
+"I% c #E04C30",
+"J% c #DC331A",
+"K% c #C4756E",
+"L% c #AD2B19",
+"M% c #DC4518",
+"N% c #DE4A17",
+"O% c #DF4717",
+"P% c #DC351A",
+"Q% c #DC3319",
+"R% c #E04A30",
+"S% c #E5E3E2",
+"T% c #F3BFB6",
+"U% c #DE4421",
+"V% c #D03819",
+"W% c #D93E19",
+"X% c #DD4118",
+"Y% c #DE4221",
+"Z% c #EAE8E8",
+"`% c #F1B2A4",
+" & c #DE4019",
+".& c #DD3919",
+"+& c #F1B1A4",
+"@& c #F1B3A4",
+"#& c #E04B20",
+"$& c #DD3B18",
+"%& c #DC3C18",
+"&& c #DD3E18",
+"*& c #DC3B18",
+"=& c #DD3A18",
+"-& c #DF4A20",
+";& c #EEEDED",
+">& c #FEFDFD",
+",& c #F4C2B5",
+"'& c #E25A2E",
+")& c #DC3119",
+"!& c #DB2F1A",
+"~& c #DC3019",
+"{& c #DE4317",
+"]& c #E2582E",
+"^& c #FAE5DF",
+"/& c #E7754F",
+"(& c #DE4017",
+"_& c #DE3F17",
+":& c #E6744F",
+"<& c #F3BDAC",
+"[& c #E56C3B",
+"}& c #DF4516",
+"|& c #DC3518",
+"1& c #DA2B1A",
+"2& c #DE4516",
+"3& c #E46A3C",
+"4& c #E1DFDE",
+"5& c #FCF4F2",
+"6& c #F3BDA9",
+"7& c #E77B4B",
+"8& c #E35E1C",
+"9& c #E15513",
+"0& c #E04B15",
+"a& c #DE4116",
+"b& c #DD3918",
+"c& c #DC3418",
+"d& c #DC3219",
+"e& c #DF4116",
+"f& c #E15413",
+"g& c #E25D1D",
+"h& c #DDDADA",
+"i& c #F9E0D7",
+"j& c #F2B69C",
+"k& c #EB9164",
+"l& c #E56D31",
+"m& c #E35E15",
+"n& c #E35F12",
+"o& c #E26011",
+"p& c #E25E15",
+"q& c #FCF5F2",
+"r& c #FAE4DE",
+"s& c #E2E1E0",
+"t& c #E5E3E3",
+"u& c #E0DFDE",
+"v& c #E9E9E8",
+" . + @ # $ % & * ",
+" = - ; > , ' ) ! ! ~ { ] ^ / ( _ ",
+" : < [ } | | | | 1 2 3 1 | | | 4 5 6 7 8 ",
+" 9 > 0 a b c d e f g h h h h i f e j c b | ' k % ",
+" l m 5 | n o p q r r s t u v w u t s r x q p y n 4 [ z A ",
+" B C 4 | D E F G H I J K L M N N M L O J P Q G R E S | } ^ & ",
+" T 5 a U V W W X Y Z ` ` ..... ..... ...` +.Z @.X G G #.U | $.%. ",
+" &.0 | *.=.-.;.>.,.'.'.).).!.~.~.!.{.~.!.).).].^.'.,./.(.W _.*.| :.<. ",
+" [.0 | }.|.1.2.3.].!.4.4.5.6.7.7.7.8.8.7.7.7.5.5.4.!.].].9.2.0.|.}.| :.%. ",
+" a.0 | b.c.d.e.f.{.5.g.h.8.i.i.j.i.i.j.i.i.j.i.i.8.h.g.g.5.{.f.k.d.1.b.b $.& ",
+" l.m.a n.o.p.q.r.7.h.8.i.s.t.u.v.w.v.x.y.x.v.w.v.u.z.s.i.i.8.h.A.r.B.p.o.C.| ^ D. ",
+" E.| U q.F.G.H.8.j.t.t.I.J.y.K.K.K.L.L.L.L.L.L.K.K.y.y.J.M.t.j.j.8.N.G.O.B.U } z ",
+" z P.| Q.R.S.T.i.s.z.x.U.V.W.X.Y.Z.`.Z.Z. +.+Z.`.Z.Z.++@+#+$+%+v.u.j.&+*+=+R.Q.| [ % ",
+" , | -+;+>+,+s.u.x.'+)+!+~+~+W.{+]+^+/+/+(+/+/+(+_+]+:+#+~+~+<+I.'+w.u.i.,+[+}+-+4 k ",
+" |+4 1+2+3+4+z.y.K.L.5+!+a a a a 6+7+8+9+9+9+9+9+9+8+0+a+a a a a <+b+K.y.w.t.c+3+2+1+' 8 ",
+" 6 a d+e+f+g+y.h+Y.i+V.~+a ~+~+a ~+W.j+k+k+l+k+k+l+m+#+a ~+~+~+a ~+$+n+h+'+y.o+f+3+p+| 7 ",
+" a.0 b q+r+s+K.L.Z..+t+W.~+a ~+~+a ~+~+W.u+v+w+x+v+y+#+~+a ~+~+~+a ~+z+A+B+Y.K.y.s+C+D+b 6 _ ",
+" k | E+F+G+H+Y.`._+/+I+J+W.a ~+~+a ~+~+a K+L+M+N+O+P+~+~+a ~+~+~+a z+Q+R+S+.+`.h+H+T+U+E+5 ( ",
+" V+| W+X+Y+Z+ +^+`+9+ @l+.@+@a a a a a a a W.@@#@$@a a a a a a a %@&@*@=@8+(+^+`.-@;@>@W+4 / ",
+" ,@| '@)@!@~@{@`+]@^@k+w+/@(@W.~+a ~+~+a ~+~+W.$@~+a ~+~+a ~+~+z+&@_@:@l+^@]@`+{@<@[@}@|@| ^ ",
+"< 0 | 1@2@3@4@5@ @k+6@7@8@9@0@a@W.a ~+~+a ~+~+a ~+~+a ~+~+a ~+z+Q+b@c@d@w+k+^@9+e@f@g@h@1@| ] i@",
+"j@4 | k@l@m@n@o@p@q@r@M+s@t@u@v@w@+@a a a a a a a a a a a a +@&@x@y@z@s@8@r@q@p@A@B@C@l@D@| { & ",
+"7 4 E@F@G@H@I@J@K@L@s@0@M@N@O@P@Q@R@W.~+a ~+~+a ~+~+a ~+~+z+&@S@T@U@V@W@s@N+X@Y@Z@4@`@ #.#+#~ % ",
+"@#| ##$#%#&#I@*#=#-#;#>#,#'#)#!#~#{#]#W.a ~+~+a ~+~+a ~+^#Q+/#(#_#:#<#[#}#|#1#2#Z@4@3#4#5###! $ ",
+"; 4 6#7#%##9#=#0#a#b#c#d#e#{#f#g#h#i#a a a a a a a a z+j#k#l#m#!#n#o#p#q#r#2#s#t#u#v#w#6#! # ",
+"x#4 E@y#z#H@I@*#A#B#C#D#E#F#G#g#H#I#i#~+a ~+~+a ~+~+a ~+~+J#K#H#L#{#~#M#N#O#P#2#Z@4@Q#R#S#E@) @ ",
+"; 4 | T#U#m@V#o@W#X#Y#Z#`#`# $E#.$+$~+~+a ~+~+a ~+~+a ~+~+a @$#$$$%$&$&$N#*$=$-$s#;$>$,$'$| ' = ",
+")$!$| ~${$]$t#^$/$($*$_$:$<$[$}$i#a a a a a a a a a a a a a a J#|$1$2$3$N#4$g#5$6$7$8$9$0$| , . ",
+" ) | a$b$c$;$d$F#e$4$f$3$g$h$i$~+a ~+~+a ~+~+z+^#~+a ~+~+a ~+~+J#j$k$Z#l$$$L#~#m$n$o$p$q$| > ",
+" , | r$s$t$u$v$~#L#E#w$x$y$+$~+~+a ~+~+a ~+^#Q+z$J#a ~+~+a ~+~+~+K+A$D#*$M#G#!#B$C$D$E$F$| ; ",
+" ^ | G$H$I$J$c#!#l#($*$K$i#a a a a a a a +@L$M$N$O$6+a a a a a a a J#P$Q$g#m#d#R$S$T$U$G$} - ",
+" &.P.b V$W$X$R$d#Y$Z$`$i#~+a ~+~+a ~+~+z+ %.%+%@%#%$%J#~+a ~+~+~+a ~+J#%%&%e#:#*%=%-%;%b [ = ",
+" m.a >%,%=%'%:#e#G#)%!%~+a ~+~+a ~+^#Q+~%{%]%^%/%(%_%J#a ~+~+~+a ~+:%<%[%}%o#|%1%W$2%a < ",
+" m | 3%4%'%|%R$d#~#f#5%6%a a a a +@L$7%8%9%0%9%a%b%[$c%6+a a a a d%e%f%g%:#*%h%|%i%3%0 : ",
+" j%a k%P@|%l%P@}%Y$L#m%6%~+~+z+ %n%o%p%q%1$q%q%p%<$r%s%W.~+~+d%t%u%v%w%R$|%x%<#y%| > ",
+" < | | z%w%A%*%:#!#m#L#5%!%z+Q+B%C%:$`#2$2$2$`#:$&$3$_$D%J#:%e%u%E%F%o#l%,%P@G%| 5 9 ",
+" :.| H%I%n#x%*%J%!#Y$f#5%K%L%M%f$_$Z#Z#N%3$Z#_$O%f$Y#4$%%<%f%v%P%o#'%h%Q%R%H%4 m ",
+" S%0 a T%U%n#|%*%:#}%~#G#V%W%E#4$*$Y#w$l$w$w$Y#*$E#X%e$&%[%g%d#'#'%x%P@Y%T%a C l ",
+" Z%4 | `%X%}%x%*%P@d#e#Y$l#L#e$($$$E#Q$E#$$X% &Z$f#F#.&}%)#R$l%h%n#e$+&| 5 B ",
+" > 4 | @&#&$&|%l%R$:#d#!#~#m#%&f#f#&&&&f#%&*&m#~#!#n#c#*%|%x%=&-&@&| 0 T ",
+" ;&4 >&,&'&%$)&|%'%R$c#w%}%!#e#e#~#~#e#e#!#d#n#P@!&l%x%~&{&]&,&>&0 &. ",
+" > 4 a ^&/&2$(&*%|%'%!&'#c#J%J%)#w%J%:#'#R$*%l%=%'%_&:$:&^&a 0 [. ",
+" Z%0 | | <&[&b%}&|&|%1&1&l%'%'%'%'%l%1&A%|%|&2&[$3&<&| | m.4& ",
+" S%:.| a 5&6&7&8&9&0&a&b&c&d&)&c&b&e&0&f&g&7&6&5&| P.E.h& ",
+" < j%| a b i&j&k&l&m&n&o&n&n&p&l&k&j&i&b a 4 , z ",
+" m m.P.| | | | | q&r&r&5&| | | | | 0 6 s& ",
+" t&^ , ) !$4 4 4 | 4 4 0 ,@V+k u& ",
+" v&; x#; @#7 j@< "};
--- /dev/null
+/* XPM */
+static const char * save_xpm[] = {
+"20 20 107 2",
+" c #FFFFFF",
+". c #E4E4FB",
+"+ c #C2C2F5",
+"@ c #ACABEF",
+"# c #A2A1EA",
+"$ c #9B9AE7",
+"% c #9493E4",
+"& c #8F8EE0",
+"* c #8D8CDE",
+"= c #A1A0E3",
+"- c #D1D1F1",
+"; c #DBDBFF",
+"> c #9392F8",
+", c #F6F8FB",
+"' c #4B58BE",
+") c #A4A3D4",
+"! c #E2E2FA",
+"~ c #BEBDFF",
+"{ c #8A89EF",
+"] c #EAEEF6",
+"^ c #3F52B8",
+"/ c #333367",
+"( c #C4C4E1",
+"_ c #BDBCF2",
+": c #8180E6",
+"< c #DEE5F0",
+"[ c #344DB3",
+"} c #7A79DF",
+"| c #7E7DBB",
+"1 c #A3A2EA",
+"2 c #7776DC",
+"3 c #D2DBEA",
+"4 c #2847AD",
+"5 c #7271D7",
+"6 c #5857A3",
+"7 c #9897E5",
+"8 c #6E6DD3",
+"9 c #C6D1E5",
+"0 c #1C41A7",
+"a c #6767CC",
+"b c #4D4D9A",
+"c c #9291E2",
+"d c #6766CC",
+"e c #BDCAE1",
+"f c #113BA1",
+"g c #5C5DC2",
+"h c #4A4A94",
+"i c #8B8ADF",
+"j c #BCBBFF",
+"k c #B7B6FF",
+"l c #6063C9",
+"m c #565EC4",
+"n c #5C5DC1",
+"o c #5456BA",
+"p c #47478F",
+"q c #8483DB",
+"r c #B4B3FF",
+"s c #AEADFF",
+"t c #A4A3FC",
+"u c #8F8EF4",
+"v c #8786EC",
+"w c #6C6BD1",
+"x c #4C50B2",
+"y c #444489",
+"z c #8281D9",
+"A c #AAA9FF",
+"B c #A09FFB",
+"C c #9493F5",
+"D c #4B5B71",
+"E c #526276",
+"F c #5B6A7D",
+"G c #4C4FB2",
+"H c #4449AA",
+"I c #494988",
+"J c #9898DF",
+"K c #A09FFF",
+"L c #9190F5",
+"M c #000000",
+"N c #343C47",
+"O c #CED9E9",
+"P c #D4DDEC",
+"Q c #677485",
+"R c #434BAE",
+"S c #3C42A3",
+"T c #6E6E9E",
+"U c #CDCDEF",
+"V c #9A99FF",
+"W c #68788D",
+"X c #D8E0ED",
+"Y c #E0E7F1",
+"Z c #737F8F",
+"` c #3541A3",
+" . c #3C42A2",
+".. c #353D9C",
+"+. c #BBBBD0",
+"@. c #C2C1F8",
+"#. c #2444AA",
+"$. c #213247",
+"%. c #ABB4C3",
+"&. c #B3BBC6",
+"*. c #BCC1CA",
+"=. c #C4C8CE",
+"-. c #484D56",
+";. c #333E9E",
+">. c #363D9C",
+",. c #8E92C6",
+"'. c #6D6C9A",
+" ",
+" ",
+" ",
+" . + @ # $ % & * = - ",
+" ; > , ' ) ",
+" ! ~ { , ] ^ / ( ",
+" _ ~ : , ] < [ } | ",
+" 1 ~ 2 , ] < 3 4 5 6 ",
+" 7 ~ 8 , ] < 3 9 0 a b ",
+" c ~ d , ] < 3 9 e f g h ",
+" i j k l m ' ^ [ 4 0 f n o p ",
+" q r s t u v : } 5 w n o x y ",
+" z A B C D D D D E F o G H I ",
+" J K L v D M N O P Q R H S T ",
+" U V v : D N W X Y Z ` ...+. ",
+" @.: #.$.%.&.*.=.-.;.>.,. ",
+" ( | 6 b h p y I '.+. ",
+" ",
+" ",
+" "};
--- /dev/null
+/* XPM */
+static const char *settings_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"40 40 69 1",
+" c #474A4A",
+". c #494D4D",
+"X c #575B5B",
+"o c #5B5F5F",
+"O c #5D6262",
+"+ c #626565",
+"@ c #656969",
+"# c #696D6D",
+"$ c #6D7171",
+"% c #727777",
+"& c #757A7A",
+"* c #7A7E7E",
+"= c #7C8282",
+"- c #828787",
+"; c #858A8A",
+": c #898E8E",
+"> c #818E92",
+", c #859295",
+"< c #8C9292",
+"1 c #899699",
+"2 c #8D999C",
+"3 c #919696",
+"4 c #949A9B",
+"5 c #9A9E9E",
+"6 c #949FA1",
+"7 c #999FA0",
+"8 c #95A2A4",
+"9 c #9CA3A3",
+"0 c #99A6A9",
+"q c #9DAAAD",
+"w c #A1A6A6",
+"e c #A4AAAB",
+"r c #A9ADAD",
+"t c #A2AEB1",
+"y c #A5B2B4",
+"u c #ABB2B3",
+"i c #ACB7B9",
+"p c #AEBABC",
+"a c #B1B6B6",
+"s c #B4BBBC",
+"d c #BABEBE",
+"f c #B4BEC0",
+"g c #B8BFC0",
+"h c #B6C1C3",
+"j c #BBC3C4",
+"k c #BBC6C8",
+"l c #BEC9CB",
+"z c #C2C5C5",
+"x c #C5CBCB",
+"c c #CACDCD",
+"v c #C3CED0",
+"b c #C6D1D3",
+"n c #CCD3D4",
+"m c #CCD7D9",
+"M c #CED9DB",
+"N c #D2D5D5",
+"B c #D5DADB",
+"V c #DBDDDD",
+"C c #D5DFE0",
+"Z c #D7E1E3",
+"A c #DDE1E1",
+"S c #E2E5E5",
+"D c #E5E8E8",
+"F c #EAEDED",
+"G c #EEF1F1",
+"H c #F3F5F5",
+"J c #F6F9F9",
+"K c #FAFBFB",
+"L c None",
+/* pixels */
+"LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL",
+"LLLLLLLLLLLLLLLLLihlbmxLLLLLLLLLLLLLLLLL",
+"LLLLLLLLLLLLLLLLLhbmVANLLLLLLLLLLLLLLLLL",
+"LLLLLLLLLLLLLLLLLlmCSSVdLLLLLLLLLLLLLLLL",
+"LLLLLLLLLLLLLLLLjmAAASAzLLLLLLLLLLLLLLLL",
+"LLLLLLLL80eLLLLkbVSSSSSVcLLLLxVVLLLLLLLL",
+"LLLLLL8880yiihMCAAAVBASSSSccBDDDScLLLLLL",
+"LLLLLL68qyphlnZANhewwrdNSSSSDDDDDSzLLLLL",
+"LLLLL40qypklmndw<;------5dVDDDDDDDaLLLLL",
+"LLLLL<tyfhlbzw<<3:;;;;;;;:rNDDDDDV-LLLLL",
+"LLLLLL4pkxmh<::3raaw53<;<<3wNDDDSrLLLLLL",
+"LLLLLL=uvmj6<:<9VDVVNNcu37wwaNDDn-LLLLLL",
+"LLLLLLLkmn53<<rNDDNzaarjzrrrrdSDBrLLLLLL",
+"LLLLLLsmMs444wcVKJxw55-*5arraaNSSxLLLLLL",
+"LLLLLLbCxw55wsrxjLLLLLL@#&aaaszVDVLLLLLL",
+"LLLLsjVAdw99r54;LLLLLLLLO+<addzNDSNnLLLL",
+"LrhbNVSVrwee5&LLLLLLLLLLLL%wdzzcDDDDAAhL",
+"LibmAAANurra*+LLLLLLLLLLLL@3zzccDDDDDZkL",
+"LsBZASSNauas+LLLLLLLLLLLLLL*ccccAFDAZMpL",
+"LsAAAAAcaaaa+LLLLLLLLLLLLLL&ccNcADAZMbyL",
+"LdAASVSNaddd+LLLLLLLLLLLLLL*NNNcSAZMlkeL",
+"LwNVASAVazdd&LLLLLLLLLLLLLL3NVNcZZmlkf4L",
+"L#*:rdAAazzze$LLLLLLLLLLLL&aVVNcMbdy9=@L",
+"LLLL$-VAzzccz;LLLLLLLLLLLL5NVAccbl<*LLLL",
+"LLLLLLdANacNVc:LLLLLLLLLL5VSSVdzksLLLLLL",
+"LLLLLL4VSazVVScrLLLLLLLLsNSDSchjp5LLLLLL",
+"LLLLLL:cVSaNSSSSnzLLLLxNDDDDnfgpy=LLLLLL",
+"LLLLLLdBSSNaVSDDDSASFGFFDDDNsgiyq4LLLLLL",
+"LLLLLLVSSVVSdcSDDFFFHJHGDAxffiq068LLLLLL",
+"LLLLLcSVVSSVVczNDFDHHJHGBlffyt06216LLLLL",
+"LLLLLzVVSVVSSVVNzcBSGHGClhfy0861,>,LLLLL",
+"LLLL+-BSVVSVNVVSVVNVSFZmffyy061,>1%LLLLL",
+"LLLLL.-zVVx4o:xNVAAAZZBliy:O;21>2= LLLLL",
+"LLLLLL+5Bz:oL+:<cVCBmMmk;=XLO:q04#LLLLLL",
+"LLLLLLL:a:LLLLLLeVMbllbqLLLLLLea%LLLLLLL",
+"LLLLLLLLLLLLLLLL4bxkhpk4LLLLLLLLLLLLLLLL",
+"LLLLLLLLLLLLLLLL;ghpyqs9LLLLLLLLLLLLLLLL",
+"LLLLLLLLLLLLLLLLLeshshxLLLLLLLLLLLLLLLLL",
+"LLLLLLLLLLLLLLLLL#4ryzNLLLLLLLLLLLLLLLLL",
+"LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL"
+};
--- /dev/null
+/* XPM */
+static const char *synchronize_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"48 50 155 2",
+" c #2FBF25",
+". c #32BF28",
+"X c #37BF2D",
+"o c #38BF2E",
+"O c #39A631",
+"+ c #3ABF31",
+"@ c #3BBE32",
+"# c #41B339",
+"$ c #47B13F",
+"% c #42BF39",
+"& c #2BC81F",
+"* c #2AE01D",
+"= c #29E31C",
+"- c #2AE31C",
+"; c #2AE21D",
+": c #2CE21F",
+"> c #2BE41D",
+", c #2AE61C",
+"< c #2BE41E",
+"1 c #27E819",
+"2 c #26ED18",
+"3 c #28EB19",
+"4 c #28EC1A",
+"5 c #29ED1B",
+"6 c #2AED1B",
+"7 c #2AE81D",
+"8 c #2BEB1D",
+"9 c #2DE91F",
+"0 c #2CEA1E",
+"q c #2DEA1F",
+"w c #2AEF1C",
+"e c #2BEF1D",
+"r c #2CEF1D",
+"t c #2CED1E",
+"y c #2CEE1E",
+"u c #2DEE1F",
+"i c #25F116",
+"p c #25F616",
+"a c #25F716",
+"s c #26F616",
+"d c #25F815",
+"f c #25F915",
+"g c #25FA15",
+"h c #24FB15",
+"j c #25FB15",
+"k c #25F816",
+"l c #26F816",
+"z c #26F916",
+"x c #27F917",
+"c c #25FB16",
+"v c #26FA16",
+"b c #26FB16",
+"n c #26FA17",
+"m c #27FA17",
+"M c #26FB17",
+"N c #24FD14",
+"B c #24FC15",
+"V c #25FC15",
+"C c #24FD15",
+"Z c #25FD15",
+"A c #24FE14",
+"S c #24FF14",
+"D c #24FE15",
+"F c #25FD16",
+"G c #26FC16",
+"H c #27F318",
+"J c #27F419",
+"K c #28F019",
+"L c #28F119",
+"P c #29F01A",
+"I c #28F519",
+"U c #28F619",
+"Y c #28F719",
+"T c #29F51A",
+"R c #29F71A",
+"E c #2AF61B",
+"W c #2BF11D",
+"Q c #2DF01E",
+"! c #2BF41D",
+"~ c #2BF61D",
+"^ c #27FA18",
+"/ c #28F818",
+"( c #28F919",
+") c #28FA19",
+"_ c #2CC321",
+"` c #2DC122",
+"' c #2DC222",
+"] c #2FC424",
+"[ c #34C22A",
+"{ c #35C32A",
+"} c #37C12C",
+"| c #34CD29",
+" . c #35CD29",
+".. c #34CE29",
+"X. c #37C82C",
+"o. c #39CD2F",
+"O. c #2ED622",
+"+. c #2FD623",
+"@. c #2EDC21",
+"#. c #2EDE21",
+"$. c #31D625",
+"%. c #33D427",
+"&. c #30DB23",
+"*. c #31D824",
+"=. c #31DB24",
+"-. c #31DC24",
+";. c #31DF24",
+":. c #33DD26",
+">. c #33DD27",
+",. c #34DF27",
+"<. c #33D028",
+"1. c #34D028",
+"2. c #35D32A",
+"3. c #3AD02F",
+"4. c #39D22E",
+"5. c #38D52C",
+"6. c #36DF2A",
+"7. c #37DF2B",
+"8. c #3BDB2F",
+"9. c #3BCB30",
+"0. c #3CC932",
+"q. c #3BCE30",
+"w. c #3CCF32",
+"e. c #3FD434",
+"r. c #2EE021",
+"t. c #2FE222",
+"y. c #2DE420",
+"u. c #2DE620",
+"i. c #2DE720",
+"p. c #2EE721",
+"a. c #2FE522",
+"s. c #2DEA20",
+"d. c #30E023",
+"f. c #30E123",
+"g. c #31E024",
+"h. c #33E326",
+"j. c #31E823",
+"k. c #37E32A",
+"l. c #41C137",
+"z. c #41CB37",
+"x. c #41C038",
+"c. c #45C63C",
+"v. c #41D236",
+"b. c #42D637",
+"n. c #4BAD44",
+"m. c #50AD49",
+"M. c #4EB346",
+"N. c #49BB40",
+"B. c #4ABC41",
+"V. c #54B34D",
+"C. c #55B34E",
+"Z. c #52B94B",
+"A. c #5AB354",
+"S. c #699C65",
+"D. c None",
+/* pixels */
+"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.O D.D.S.A.A.A.A.V.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.D.e.e N S S S S O.. 4 S N N N S y v.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.8 N N S S S b ..D.D.D.o.N N N S S S P D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.@ h S S S S S Q D.D.D.D.D.D.D.S S S S S S b X D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.% S S S S S S S D.D.D.D.D.D.D.D.D.b S S S S S S { D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.b S S S S S S D.D.D.D.D.D.D.D.D.D.X.S S S S S S b D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.b S S S S S S *.D.D.D.D.D.D.D.D.D.D.D.f.S S S S S S Y D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.#.S S S S S S b D.D.D.D.D.D.D.D.D.D.D.D.D.( S S S S S S $.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.S S S S S S S z.D.D.D.D.D.D.D.D.D.D.D.D.D.b.S S S S S S b D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.- S S S S S S b D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.( S S S S S S 6.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.b S S S S S S 6.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.f.S S S S S S ^ D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.<.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.S S S S S S S w.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.a.S S S S S S 0 D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.e S S S S S S a.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.S S S S S S S $.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.- S S S S S S Y D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D...s s b S S S S S S S b s s _ D.D.D.D.D.D.",
+"D.D.D.D.6.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.x.Y N N S S S S S S S S D.D.D.D.D.D.D.D.",
+"D.D.D.D.8 S S S S S S P D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.2 N S S S S S S b Z.D.D.D.D.D.D.D.D.",
+"D.D.D.D.;.S S S S S S 0 D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.f.S S S S N b D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.i S S S S S S -.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D...b S S b D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D., S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.y b D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.M.B.B.B.B.B.B.$ D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.V.s.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.b f s b b s d m.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.( S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.S S S S S S S D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.` S S S S S 5.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.$ S S S S S S S D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.] b S S S S S S t.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.* S S S S S S S D.D.D.D.D.D.D.D.",
+"D.D.D.D.c.S S S S S S S S S P D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.6.S S S S S S b D.D.D.D.D.D.D.D.",
+"D.D.D.- b S S S S S S S S S S S 0.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.j.S S S S S S 4 D.D.D.D.D.D.D.D.",
+"D.D.D.` _ ` y S S S S S S b ` ` D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.Y S S S S S S #.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.4.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.S S S S S S S { D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.N S S S S S S f.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.$.S S S S S S S D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.4 S S S S S S W D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.J S S S S S S : D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.e.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.S S S S S S S D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.~ S S S S S S k.D.D.D.D.D.D.D.D.D.D.D.D.D.D.a.S S S S S S y D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.v.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.y S S S S S S s.D.D.D.D.D.D.D.D.D.D.D.D.4 S S S S S S y D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.S S S S S S S 0.D.D.D.D.D.D.D.D.D.D.$.S S S S S S Y D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.@ S S S S S S b D.D.D.D.D.D.D.D.D.} S S S S S S S # D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.& S S S S S S ~ D.D.D.D.D.D.D.n.Y S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.% J S S S S S Y D.D.D.D.D.1.! S S S S S P D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.D.D.3 S S S S S N *.D.D.a.N N N S S S a.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.t.8.1 1 3 > ..} - ;.2.-.;.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.",
+"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D."
+};
--- /dev/null
+/* XPM */
+static const char *tools_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"48 48 254 2",
+" c #435C69",
+". c #47606D",
+"X c #49626E",
+"o c #4B6370",
+"O c #546D7A",
+"+ c #57707D",
+"@ c #59717E",
+"# c #008636",
+"$ c #058D3A",
+"% c #0C8E3E",
+"& c #029037",
+"* c #0B933D",
+"= c #0E9B3C",
+"- c #20A23E",
+"; c #4CAE3D",
+": c #52B030",
+"> c #5DB435",
+", c #56B33D",
+"< c #7EBF25",
+"1 c #74BC2D",
+"2 c #7DBF2B",
+"3 c #73BC34",
+"4 c #78BE3B",
+"5 c #7FC028",
+"6 c #7FC136",
+"7 c #7DC03D",
+"8 c #128F41",
+"9 c #139344",
+"0 c #199346",
+"q c #119941",
+"w c #1C9B4D",
+"e c #21974D",
+"r c #20994F",
+"t c #249B53",
+"y c #2A9F57",
+"u c #1BA04A",
+"i c #29A440",
+"p c #21A04F",
+"a c #3BAA4D",
+"s c #29A256",
+"d c #2DA35A",
+"f c #3AA957",
+"g c #32A55D",
+"h c #33AA5D",
+"j c #3CAD5D",
+"k c #4EAF41",
+"l c #57B44C",
+"z c #41AC57",
+"x c #43AE5B",
+"c c #47B05C",
+"v c #4CB15D",
+"b c #5AB557",
+"n c #53B35C",
+"m c #5AB65B",
+"M c #5FB85B",
+"N c #6CBB43",
+"B c #76BF42",
+"V c #74BD49",
+"C c #66BB55",
+"Z c #6BBC54",
+"A c #64BA5B",
+"S c #6BBD5B",
+"D c #71BF5E",
+"F c #43A567",
+"G c #48A86C",
+"H c #4DB36E",
+"J c #5EB862",
+"K c #5BB76A",
+"L c #57B375",
+"P c #62BC7D",
+"I c #79C044",
+"U c #7EC150",
+"Y c #75C061",
+"T c #7AC265",
+"R c #7FC469",
+"E c #76C172",
+"W c #70C07C",
+"Q c #82C024",
+"! c #83C229",
+"~ c #8BC536",
+"^ c #83C33B",
+"/ c #85C443",
+"( c #8CC745",
+") c #87C64B",
+"_ c #8AC74B",
+"` c #8DC84D",
+"' c #96CB45",
+"] c #91C94D",
+"[ c #9BCE4E",
+"{ c #8ECA51",
+"} c #92CA50",
+"| c #95CB5B",
+" . c #98CD5D",
+".. c #82C66B",
+"X. c #99CD66",
+"o. c #9FD06C",
+"O. c #81C771",
+"+. c #8CCA75",
+"@. c #8ACA7A",
+"#. c #A2D36E",
+"$. c #ABD669",
+"%. c #5D7582",
+"&. c #5F7988",
+"*. c #5E7F91",
+"=. c #617580",
+"-. c #617986",
+";. c #647C89",
+":. c #687F8C",
+">. c #607F91",
+",. c #6B818D",
+"<. c #638495",
+"1. c #6B8694",
+"2. c #6E8897",
+"3. c #668698",
+"4. c #6C8B9C",
+"5. c #728793",
+"6. c #758A95",
+"7. c #768C9A",
+"8. c #798E9A",
+"9. c #7E929E",
+"0. c #66B881",
+"q. c #69BB82",
+"w. c #728FA0",
+"e. c #7592A4",
+"r. c #7C96A5",
+"t. c #7B98A9",
+"y. c #75C08C",
+"u. c #7AC391",
+"i. c #82939D",
+"p. c #8496A1",
+"a. c #8399A7",
+"s. c #8C9BA4",
+"d. c #819DAE",
+"f. c #8C9DA8",
+"g. c #909EA5",
+"h. c #8FA1AC",
+"j. c #93A3AD",
+"k. c #9AA7AD",
+"l. c #9BA8AF",
+"z. c #87A1B1",
+"x. c #8BA5B5",
+"c. c #93A5B0",
+"v. c #94A8B5",
+"b. c #9AA9B2",
+"n. c #93ACBB",
+"m. c #99AFBD",
+"M. c #99B0BF",
+"N. c #A2AEB4",
+"B. c #A2B2BC",
+"V. c #ABB5BB",
+"C. c #AEB8BE",
+"Z. c #B1BABF",
+"A. c #82C786",
+"S. c #97D087",
+"D. c #9BD084",
+"F. c #90CE9B",
+"G. c #A4D387",
+"H. c #B1D986",
+"J. c #B3DA8A",
+"K. c #AFD993",
+"L. c #A5D79C",
+"P. c #A7D89C",
+"I. c #ADD99D",
+"U. c #B1DA94",
+"Y. c #BADE9E",
+"T. c #90CEA2",
+"R. c #98CEA9",
+"E. c #93D1A0",
+"W. c #A0D5A6",
+"Q. c #ADD9A2",
+"!. c #B1DBA6",
+"~. c #BCDFA9",
+"^. c #A2D5B3",
+"/. c #A3D9B6",
+"(. c #ADDBB4",
+"). c #B0DABC",
+"_. c #BFE2B4",
+"`. c #C1E2AD",
+"'. c #CBE5A8",
+"]. c #CEE7B6",
+"[. c #D1E9BC",
+"{. c #9DB4C2",
+"}. c #A1B6C3",
+"|. c #A4B9C7",
+" X c #ABBAC3",
+".X c #A6BAC9",
+"XX c #ABBECD",
+"oX c #B3BDC2",
+"OX c #ABC1C0",
+"+X c #AEC1CE",
+"@X c #B9C2C7",
+"#X c #B3C2CC",
+"$X c #BCC5CB",
+"%X c #AFDEC1",
+"&X c #B7DCC1",
+"*X c #B9DDC3",
+"=X c #B5C7D3",
+"-X c #B9C7D0",
+";X c #B7C8D4",
+":X c #BACBD5",
+">X c #BFCFD9",
+",X c #C0C7CC",
+"<X c #C3CBCE",
+"1X c #C7D1CB",
+"2X c #CCD4CE",
+"3X c #C3CDD3",
+"4X c #C8CFD3",
+"5X c #CAD1D5",
+"6X c #C2D1DB",
+"7X c #CAD6DD",
+"8X c #CFD9DF",
+"9X c #D7DDD3",
+"0X c #DADDD5",
+"qX c #D1D7DA",
+"wX c #D4DADE",
+"eX c #D9DCDC",
+"rX c #C6E4CE",
+"tX c #D7ECC5",
+"yX c #D8ECC6",
+"uX c #D7ECCF",
+"iX c #D1EAD7",
+"pX c #DCEED6",
+"aX c #D4EADA",
+"sX c #E2E4DE",
+"dX c #E6EBDE",
+"fX c #E4F2DA",
+"gX c #CDD8E0",
+"hX c #D3DDE4",
+"jX c #D9DDE0",
+"kX c #D6E0E6",
+"lX c #DBE1E5",
+"zX c #DCE4E9",
+"xX c #E3E4E4",
+"cX c #E2EFE5",
+"vX c #E1E7EB",
+"bX c #E4EAEE",
+"nX c #EBECED",
+"mX c #E2F2E4",
+"MX c #EEF6E4",
+"NX c #E6F4EC",
+"BX c #EDF4ED",
+"VX c #EFF8ED",
+"CX c #F1F8E7",
+"ZX c #F2F8ED",
+"AX c #E6ECF0",
+"SX c #EAEEF1",
+"DX c #EDF1F3",
+"FX c #F2F4F5",
+"GX c #F5F9F4",
+"HX c #F9FBF7",
+"JX c #F4F6F8",
+"KX c #F7F9FA",
+"LX c #FDFDFD",
+"PX c None",
+/* pixels */
+"PXPXPXPXPXPXPXPXPXPXPXPXPXPX@XoXoXoX@X$X$XoXoXC.@XPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPXPXPXPXPXPXPX X4XjXnXnXbXlXqX4X<X,X,X,X,X<X5XeXvXnXbXlX5XV.PXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPXPXPXPXoXjXDXjXoXN.N.N.C.@X,X4X5X5X4X:X$X#X XB.b.j.j.l. XqXnXlX@XPXPXPXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPXPX@XSXwXV.l.oXgXAXDXFXJXJXFXDXSXbXzXzXhXgXgX7X>X;X=X+X}.v.s.k.4XFX$XPXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPX5XnXV. X7XlXvXbXSXDXKXKXKXJXDXDXAXzXzXhXgXgXgX>X:X=X#XXXXX|.{.x.j.lXjXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPXLX X3XgXhXlXzXAXSXDXJXKXKXJXDXSXAXvXzXkXgXgX6X>X:X=X+XXX.X|.|.{.m.j.LXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPXLXhXgXhXhXkXzXbXSXDXJXJXJXJXDXAXAXzXkXhXhXgX6X>X:X=X+XXXXX|.|.{.{.{.KXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPX>XJXhXgXhXkXzXbXAXSXDXSXDXDXDXAXvXzXkXhXgXgX6X>X:X=X+XXX.X|.}.{.|.AXSXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPX<.XXGXDXzXkXzXzXvXAXAXSXSXAXAXbXvXzXhXgXgXgX6X:X;X+X+XXX{..X+XhXKXzX+XPXPXPXPXPXPXPXPXPXPXPX",
+"PXPX;.<.4..XvXKXDXDXAXAXvXAXAXAXvXzXzXkXhXgXgXgX6X:X;X;X>XgXkXJXKXvX>X+X|.PXPXPXPXPXPXPXPXPXPXPX",
+"PXPX<.<.3.4.w.z..XhXAXJXKXKXJXSXDXDXDXAXAXbXAXAXAXDXJXKXKXKXDXvXhXgX:X+X|.PXPXPXPXPXPXPXPXPXPXPX",
+"PXPX&.<.3.4.4.e.r.t.t.n.|.+X:XgXkXzXvXAXAXDXDXJXKXLXLXJXDXAXvXkXgXgX:X+X|.PXPXPXPXPXPXPXPXPXPXPX",
+"PXPX*.<.<.4.4.e.e.t.d.x.x.{.{.{..X+X=X>XgXkXzXSXFXLXKXJXDXAXzXkXhXgX:X+X|.PXPXPXPXPXPXPXPXPXPXPX",
+"PXPX*.<.3.3.4.w.e.t.d.z.x.n.{.{..X+X=X>XgXkXvXSXFXLXLXJXDXAXzXzXhX6X:XXX|.PXPXPXPXPXPXPXPXPXPXPX",
+"PXPX<.<.3.4.4.e.t.t.d.z.x.n.{.{..X+X=X>XgXkXvXSXFXLXLXJXDXAXvXkXhXgX:X+X|.PXPXPXPXPXPXPXPXPXPXPX",
+"PXPXPX<.3.4.4.e.e.t.d.x.x.{.{.{..X+X=X6XgXkXvXAXFXLXKXJXDXAXzXzXhX7X:X+XPXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPXPXPX7.r.w.e.e.t.d.x.z.{.{.}..X+X=X6XgXhXbXSXJXKXKXJXDXbXvXhXjXgX-XPXPXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPXPXC.bX5Xw.>.w.t.d.x.x.x.{.}..XXX=X>XgXhXbXSXJXLXLXJXDXAXzXoXN.<XSXoXPXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPX,XnXg.=. . %.&.:.r.d.x.{.|.XX=X6XhXhXjXxXzXjX7XC.s.5.o X @ i.xXeXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPXLXg.%.O %.%.O %.O %.O %.%.=.&.;.>.<.:.:.:.;.-.%.+ + O O O %.@ O @ 6.LXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPXLXs.:.:.:.:.-.:.:.:.:.:.:.:.:.:.:.:.:.:.;.:.:.:.:.:.:.:.:.:.:.:.:.5.KXPXPXPXPXPXPXPXPXPXPXPX",
+"PXPX7XbXj.i.i.i.r.w.i.i.i.i.r.8.8.r.i.i.i.r.r.i.9.9.9.9.p.j.C.$X<X4X,X5XnXOXPXPXPXPXPXPXPXPXPXPX",
+"PXPX<.#XSXgX Xv.c.j.j.j.j.j.c.c.c.j.c.c.f.j.c.c.c.c.l.@XxXFXnXaX*X).iXcXFXxX1XPXPXPXPXPXPXPXPXPX",
+"PXPX*.<.p.XXnXDXjX3XXX}.}.}.}.}.}.}.}.B.}.}.}.V.}.$XnXGX^.G 8 # # # # % F R.VXxXPXPXPXPXPXPXPXPX",
+"PXPX*.<.<.4.w.x.XXhXSXFXDXAXvXzXhXhXhXgXhXhXhXjXjXKX).e $ 8 w y d d d t 0 $ 0 ^.nX2XPXPXPXPXPXPX",
+"PXPX*.<.3.3.4.e.e.t.z.{.|.=X6XgXhXzXbXSXSXDXJXFXLXq.$ 9 s g g g h g g g g d w $ q.BX2XPXPXPXPXPX",
+"PXPX*.<.3.3.4.e.t.t.t.x.x.{.{.{..XXX=X6XgXhXbXHXq.* w g h h h /.%X%X/.d g h g s % L GX1XPXPXPXPX",
+"PXPX*.<.4.3.4.e.t.t.d.z.x.{.x.{.|.=X=X6XgXzXHXT.$ p h j c c v BXLXLXNXg F c h h d * u.nXPXPXPXPX",
+"PXPX*.<.3.4.4.e.t.t.d.x.x.n.{.{.|.XX;X6XgXSXaXq u j c v n m M BXLXLXBXf m n v c j s = rX0XPXPXPX",
+"PXPXPX<.3.3.4.e.e.t.d.z.x.n.{.{..XXX;X6XhXKXP = c v m m A S Y VXLXLXNXz S M M m v j w H FXPXPXPX",
+"PXPXPXPX4.w.4.e.e.t.t.z.x.{.{.{..XXX;X>XcXmXu a n b A Y U Y Y NXLXLXNXc Y Y A J m n z - iX2XPXPX",
+"PXPXPXV.zX4X9.1.e.t.d.z.x.n.{.}..XXX=X6XDX(.: I M Y Y T E R ..VXLXLXNXv T T Y U A A A , E.xXPXPX",
+"PXPX@XDXk.@ o O %.2.r.z.n.}..XXX;X7XJXA.3 [ T P.Q.!.!.!.!.HXLXLXGXF.!.!.Q.Q.P.J { I W nXPXPX",
+"PXPXLXs.+ O + O O O O O O O O + %.-.:.p.LXD ^ [ #.GXLXLXLXLXLXLXLXLXLXLXLXLXLXLXLXK [ ) b FXPXPX",
+"PXPXLXp.-.;.;.;.;.;.;.;.;.-.;.;.;.;.;.i.KXD / [ $.HXLXLXLXLXLXLXLXLXLXLXLXLXLXLXKXK [ ` l FXPXPX",
+"PXPXqXcXs.7.7.7.8.8.8.7.8.8.8.8.7.7.8.s.LXO.^ [ $.HXLXLXLXLXLXLXLXLXLXLXLXLXLXLXKXK [ ` C FXPXPX",
+"PXPX<.:XFX5XB.h.j.f.f.f.f.f.x.g.h.h.h.b.JXS.6 [ ' J.J.J.J.Y.'.GXLXLXGXR.Y.J.H.H.H.N [ / +.FXPXPX",
+"PXPX*.<.t.-XSXbXjX:X X}.B.B.B.B.B.B.M.B.SX_.4 ' Q Q Q Q Q Q ~ CXLXLXNXk ! ! Q Q Q Q ~ B I.xXPXPX",
+"PXPX*.<.3.4.e.v.=XzXJXJXSXvXhXhXhX7X7X7XnXmXV ! Q Q Q Q Q Q Q CXLXLXBX; Q Q Q Q Q Q ! N pX9XPXPX",
+"PXPX*.<.3.4.4.e.e.t.d.v.XX:XgXhXzXvXbXDXDXLXD.2 Q Q Q Q Q Q Q CXLXLXNX; Q Q Q Q Q Q 1 +.FXPXPXPX",
+"PXPX*.<.3.4.w.w.e.t.d.z.n.n.{.{..XXX;X6XgXDXdXU Q Q Q Q Q Q Q MXLXLXNX; Q Q Q Q Q 5 B uXxXPXPXPX",
+"PXPX*.<.3.4.4.w.e.t.d.z.x.{.{.{..XXX;X6XgXzXKX~./ Q Q Q Q Q Q CXLXHXNX; Q Q Q Q 5 4 I.FXPXPXPXPX",
+"PXPX<.<.3.3.4.e.r.t.d.x.x.n.{.{.|.XX=X6XgXhXSXLXK./ Q Q Q Q Q '.[.[.`.> Q Q Q < 7 G.GX0XPXPXPXPX",
+"PXPX1.<.3.4.4.e.e.t.d.x.x.x.{.{.|.XX=X6XgXhXbXJXHXY.} Q Q Q Q Q Q Q Q Q Q Q < ) K.GXsXPXPXPXPXPX",
+"PXPXPX5.5.4.4.w.e.t.d.x.x.x.{.{..XXX;X6XgXzXvXSXFXHXyXo.( Q Q Q Q Q Q Q < / X.[.HXsXPXPXPXPXPXPX",
+"PXPXPXPXPX6.7.7.e.t.d.z.n.{.{.{..XXX;X6XgXhXvXnXFXKXKXHX[.J. .] ( ( ` .H.].GXFXPXPXPXPXPXPXPXPX",
+"PXPXPXPXPXPXPXPX8.8.r.a.z.x.{.{.|.XX=X6XgXhXzXbXnXFXvXeXFXHXZXfXyXyXfXZXKXGXsXPXPXPXPXPXPXPXPXPX",
+"PXPXPXPXPXPXPXPXPXPXPXPXPXPXp.w.i.i.p.s.s.s.s.s.N.PXPXPXPXPXdXnXnXnXnXdXPXPXPXPXPXPXPXPXPXPXPXPX"
+};