diff --git a/Core/Code/Interactions/mitkEventMapper.cpp b/Core/Code/Interactions/mitkEventMapper.cpp index b97bcf57d2..63dcd8e325 100644 --- a/Core/Code/Interactions/mitkEventMapper.cpp +++ b/Core/Code/Interactions/mitkEventMapper.cpp @@ -1,700 +1,700 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ /** * EventMapping: * This class maps the Events, usually given by the OS or here by QT, to a MITK internal EventId. * It loads all information from the xml-file (possible, understandable Events with the mitkEventID). * If an event appears, the method MapEvent is called with the event params. * This Method looks up the event params, and tries to find an mitkEventId to it. * If yes, then sends the event and the found ID to the globalStateMachine, which handles all * further operations of that event. * */ #include "mitkEventMapper.h" #include "mitkInteractionConst.h" #include "mitkStateEvent.h" #include "mitkOperationEvent.h" #include "mitkGlobalInteraction.h" #include #include "mitkStandardFileLocations.h" //#include #include "mitkConfig.h" #include "mitkCoreObjectFactory.h" #include #include #include #include namespace mitk { vtkStandardNewMacro(EventMapper); } #ifdef MBI_INTERNAL_CONFERENCE #include #include #include #include #include #endif //MBI_INTERNAL_CONFERENCE //XML Event const std::string mitk::EventMapper::STYLE = "STYLE"; const std::string mitk::EventMapper::NAME = "NAME"; const std::string mitk::EventMapper::ID = "ID"; const std::string mitk::EventMapper::TYPE = "TYPE"; const std::string mitk::EventMapper::BUTTON = "BUTTON"; const std::string mitk::EventMapper::BUTTONSTATE = "BUTTONSTATE"; const std::string mitk::EventMapper::KEY = "KEY"; const std::string mitk::EventMapper::EVENTS = "events"; const std::string mitk::EventMapper::EVENT = "event"; mitk::EventMapper::EventDescriptionVec mitk::EventMapper::m_EventDescriptions; std::string mitk::EventMapper::m_XmlFileName; mitk::StateEvent mitk::EventMapper::m_StateEvent; std::string mitk::EventMapper::m_StyleName; struct ltstr { bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) < 0; } }; mitk::EventMapper::EventMapper() { //map with string to key for mapping string from xml-file to int m_EventConstMap["Type_None"] = mitk::Type_None; // invalid event m_EventConstMap["Type_Timer"] = mitk::Type_Timer; // timer event m_EventConstMap["Type_MouseButtonPress"] = mitk::Type_MouseButtonPress; // mouse button pressed m_EventConstMap["Type_MouseButtonRelease"] = mitk::Type_MouseButtonRelease; // mouse button released m_EventConstMap["Type_MouseButtonDblClick"] = mitk::Type_MouseButtonDblClick; // mouse button double click m_EventConstMap["Type_MouseMove"] = mitk::Type_MouseMove; // mouse move m_EventConstMap["Type_KeyPress"] = mitk::Type_KeyPress; // key pressed m_EventConstMap["Type_KeyRelease"] = mitk::Type_KeyRelease; // key released m_EventConstMap["Type_FocusIn"] = 8; // keyboard focus received m_EventConstMap["Type_FocusOut"] = 9; // keyboard focus lost m_EventConstMap["Type_Enter"] = 10; // mouse enters widget m_EventConstMap["Type_Leave"] = 11; // mouse leaves widget m_EventConstMap["Type_Paint"] = 12; // paint widget m_EventConstMap["Type_Move"] = 13; // move widget m_EventConstMap["Type_Resize"] = 14; // resize widget m_EventConstMap["Type_Create"] = 15; // after object creation m_EventConstMap["Type_Destroy"] = 16; // during object destruction m_EventConstMap["Type_Show"] = 17; // widget is shown m_EventConstMap["Type_Hide"] = 18; // widget is hidden m_EventConstMap["Type_Close"] = 19; // request to close widget m_EventConstMap["Type_Quit"] = 20; // request to quit application m_EventConstMap["Type_Reparent"] = 21; // widget has been reparented m_EventConstMap["Type_ShowMinimized"] = 22; // widget is shown minimized m_EventConstMap["Type_ShowNormal"] = 23; // widget is shown normal m_EventConstMap["Type_WindowActivate"] = 24; // window was activated m_EventConstMap["Type_WindowDeactivate"] = 25; // window was deactivated m_EventConstMap["Type_ShowToParent"] = 26; // widget is shown to parent m_EventConstMap["Type_HideToParent"] = 27; // widget is hidden to parent m_EventConstMap["Type_ShowMaximized"] = 28; // widget is shown maximized m_EventConstMap["Type_ShowFullScreen"] = 29; // widget is shown full-screen m_EventConstMap["Type_Accel"] = 30; // accelerator event m_EventConstMap["Type_Wheel"] = 31; // wheel event m_EventConstMap["Type_AccelAvailable"] = 32; // accelerator available event m_EventConstMap["Type_CaptionChange"] = 33; // caption changed m_EventConstMap["Type_IconChange"] = 34; // icon changed m_EventConstMap["Type_ParentFontChange"] = 35; // parent font changed m_EventConstMap["Type_ApplicationFontChange"] = 36;// application font changed m_EventConstMap["Type_ParentPaletteChange"] = 37; // parent palette changed m_EventConstMap["Type_ApplicationPaletteChange"] = 38;// application palette changed m_EventConstMap["Type_PaletteChange"] = 39; // widget palette changed m_EventConstMap["Type_Clipboard"] = 40; // internal clipboard event m_EventConstMap["Type_Speech"] = 42; // reserved for speech input m_EventConstMap["Type_SockAct"] = 50; // socket activation m_EventConstMap["Type_AccelOverride"] = 51; // accelerator override event m_EventConstMap["Type_DeferredDelete"] = 52; // deferred delete event m_EventConstMap["Type_DragEnter"] = 60; // drag moves into widget m_EventConstMap["Type_DragMove"] = 61; // drag moves in widget m_EventConstMap["Type_DragLeave"] = 62; // drag leaves or is cancelled m_EventConstMap["Type_Drop"] = 63; // actual drop m_EventConstMap["Type_DragResponse"] = 64; // drag accepted/rejected m_EventConstMap["Type_ChildInserted"] = 70; // new child widget m_EventConstMap["Type_ChildRemoved"] = 71; // deleted child widget m_EventConstMap["Type_LayoutHint"] = 72; // child min/max size changed m_EventConstMap["Type_ShowWindowRequest"] = 73; // widget's window should be mapped m_EventConstMap["Type_ActivateControl"] = 80; // ActiveX activation m_EventConstMap["Type_DeactivateControl"] = 81; // ActiveX deactivation m_EventConstMap["Type_ContextMenu"] = 82; // context popup menu m_EventConstMap["Type_IMStart"] = 83; // input method composition start m_EventConstMap["Type_IMCompose"] = 84; // input method composition m_EventConstMap["Type_IMEnd"] = 85; // input method composition end m_EventConstMap["Type_Accessibility"] = 86; // accessibility information is requested m_EventConstMap["Type_TabletMove"] = 87; // Wacom tablet event m_EventConstMap["Type_LocaleChange"] = 88; // the system locale changed m_EventConstMap["Type_LanguageChange"] = 89; // the application language changed m_EventConstMap["Type_LayoutDirectionChange"] = 90; // the layout direction changed m_EventConstMap["Type_Style"] = 91; // internal style event m_EventConstMap["Type_TabletPress"] = 92; // tablet press m_EventConstMap["Type_TabletRelease"] = 93; // tablet release // apparently not necessary, since the IDs can be assigned earlier (in the AddOns after they are generated in the driver) //m_EventConstMap["Type_TDMouseInput"] = mitk::Type_TDMouseInput; // 3D mouse input occured m_EventConstMap["Type_User"] = 1000; // first user event id m_EventConstMap["Type_MaxUser"] = 65535; // last user event id //ButtonState m_EventConstMap["BS_NoButton"] = mitk::BS_NoButton;//0x0000 m_EventConstMap["BS_LeftButton"] = mitk::BS_LeftButton;//0x0001 m_EventConstMap["BS_RightButton"] = mitk::BS_RightButton;//0x0002 m_EventConstMap["BS_MidButton"] = mitk::BS_MidButton;//0x0004 m_EventConstMap["BS_MouseButtonMask"] = mitk::BS_MouseButtonMask;//0x0007 m_EventConstMap["BS_ShiftButton"] = mitk::BS_ShiftButton;//0x0008 m_EventConstMap["BS_ControlButton"] = mitk::BS_ControlButton;//0x0010 m_EventConstMap["BS_AltButton"] = mitk::BS_AltButton;//0x0020 m_EventConstMap["BS_KeyButtonMask"] = mitk::BS_KeyButtonMask;//0x0038 m_EventConstMap["BS_Keypad"] = mitk::BS_Keypad;//0x4000 //Modifier m_EventConstMap["Mod_SHIFT"] = 0x00200000; m_EventConstMap["Mod_CTRL"] = 0x00400000; m_EventConstMap["Mod_ALT"] = 0x00800000; m_EventConstMap["Mod_MODIFIER_MASK"] = 0x00e00000; m_EventConstMap["Mod_UNICODE_ACCEL"] = 0x10000000; m_EventConstMap["Mod_ASCII_ACCEL"] = 0x10000000; //Key m_EventConstMap["Key_Escape"] = 0x1000; m_EventConstMap["Key_Tab"] = 0x1001; m_EventConstMap["Key_Backtab"] = 0x1002; m_EventConstMap["Key_BackTab"] = 0x1002; m_EventConstMap["Key_Backspace"] = 0x1003; m_EventConstMap["Key_BackSpace"] = 0x1003; m_EventConstMap["Key_Return"] = 0x1004; m_EventConstMap["Key_Enter"] = 0x1005; m_EventConstMap["Key_Insert"] = 0x1006; m_EventConstMap["Key_Delete"] = 0x1007; m_EventConstMap["Key_Pause"] = 0x1008; m_EventConstMap["Key_Print"] = 0x1009; m_EventConstMap["Key_SysReq"] = 0x100a; m_EventConstMap["Key_Home"] = 0x1010; m_EventConstMap["Key_End"] = 0x1011; m_EventConstMap["Key_Left"] = 0x1012; m_EventConstMap["Key_Up"] = 0x1013; m_EventConstMap["Key_Right"] = 0x1014; m_EventConstMap["Key_Down"] = 0x1015; m_EventConstMap["Key_Prior"] = 0x1016; m_EventConstMap["Key_PageUp"] = 0x1016; m_EventConstMap["Key_Next"] = 0x1017; m_EventConstMap["Key_PageDown"] = 0x1017; m_EventConstMap["Key_Shift"] = 0x1020; m_EventConstMap["Key_Control"] = 0x1021; m_EventConstMap["Key_Meta"] = 0x1022; m_EventConstMap["Key_Alt"] = 0x1023; m_EventConstMap["Key_CapsLock"] = 0x1024; m_EventConstMap["Key_NumLock"] = 0x1025; m_EventConstMap["Key_ScrollLock"] = 0x1026; m_EventConstMap["Key_F1"] = 0x1030; m_EventConstMap["Key_F2"] = 0x1031; m_EventConstMap["Key_F3"] = 0x1032; m_EventConstMap["Key_F4"] = 0x1033; m_EventConstMap["Key_F5"] = 0x1034; m_EventConstMap["Key_F6"] = 0x1035; m_EventConstMap["Key_F7"] = 0x1036; m_EventConstMap["Key_F8"] = 0x1037; m_EventConstMap["Key_F9"] = 0x1038; m_EventConstMap["Key_F10"] = 0x1039; m_EventConstMap["Key_F11"] = 0x103a; m_EventConstMap["Key_F12"] = 0x103b; m_EventConstMap["Key_F13"] = 0x103c; m_EventConstMap["Key_F14"] = 0x103d; m_EventConstMap["Key_F15"] = 0x103e; m_EventConstMap["Key_F16"] = 0x103f; m_EventConstMap["Key_F17"] = 0x1040; m_EventConstMap["Key_F18"] = 0x1041; m_EventConstMap["Key_F19"] = 0x1042; m_EventConstMap["Key_F20"] = 0x1043; m_EventConstMap["Key_F21"] = 0x1044; m_EventConstMap["Key_F22"] = 0x1045; m_EventConstMap["Key_F23"] = 0x1046; m_EventConstMap["Key_F24"] = 0x1047; m_EventConstMap["Key_F25"] = 0x1048; m_EventConstMap["Key_F26"] = 0x1049; m_EventConstMap["Key_F27"] = 0x104a; m_EventConstMap["Key_F28"] = 0x104b; m_EventConstMap["Key_F29"] = 0x104c; m_EventConstMap["Key_F30"] = 0x104d; m_EventConstMap["Key_F31"] = 0x104e; m_EventConstMap["Key_F32"] = 0x104f; m_EventConstMap["Key_F33"] = 0x1050; m_EventConstMap["Key_F34"] = 0x1051; m_EventConstMap["Key_F35"] = 0x1052; m_EventConstMap["Key_Super_L"] = 0x1053; m_EventConstMap["Key_Super_R"] = 0x1054; m_EventConstMap["Key_Menu"] = 0x1055; m_EventConstMap["Key_Hyper_L"] = 0x1056; m_EventConstMap["Key_Hyper_R"] = 0x1057; m_EventConstMap["Key_Help"] = 0x1058; m_EventConstMap["Key_Muhenkan"] = 0x1122; m_EventConstMap["Key_Henkan"] = 0x1123; m_EventConstMap["Key_Hiragana_Katakana"] = 0x1127; m_EventConstMap["Key_Zenkaku_Hankaku"] = 0x112A; m_EventConstMap["Key_Space"] = 0x20; m_EventConstMap["Key_Any"] = 0x20; m_EventConstMap["Key_Exclam"] = 0x21; m_EventConstMap["Key_QuoteDbl"] = 0x22; m_EventConstMap["Key_NumberSign"] = 0x23; m_EventConstMap["Key_Dollar"] = 0x24; m_EventConstMap["Key_Percent"] = 0x25; m_EventConstMap["Key_Ampersand"] = 0x26; m_EventConstMap["Key_Apostrophe"] = 0x27; m_EventConstMap["Key_ParenLeft"] = 0x28; m_EventConstMap["Key_ParenRight"] = 0x29; m_EventConstMap["Key_Asterisk"] = 0x2a; m_EventConstMap["Key_Plus"] = 0x2b; m_EventConstMap["Key_Comma"] = 0x2c; m_EventConstMap["Key_Minus"] = 0x2d; m_EventConstMap["Key_Period"] = 0x2e; m_EventConstMap["Key_Slash"] = 0x2f; m_EventConstMap["Key_0"] = 0x30; m_EventConstMap["Key_1"] = 0x31; m_EventConstMap["Key_2"] = 0x32; m_EventConstMap["Key_3"] = 0x33; m_EventConstMap["Key_4"] = 0x34; m_EventConstMap["Key_5"] = 0x35; m_EventConstMap["Key_6"] = 0x36; m_EventConstMap["Key_7"] = 0x37; m_EventConstMap["Key_8"] = 0x38; m_EventConstMap["Key_9"] = 0x39; m_EventConstMap["Key_Colon"] = 0x3a; m_EventConstMap["Key_Semicolon"] = 0x3b; m_EventConstMap["Key_Less"] = 0x3c; m_EventConstMap["Key_Equal"] = 0x3d; m_EventConstMap["Key_Greater"] = 0x3e; m_EventConstMap["Key_Question"] = 0x3f; m_EventConstMap["Key_At"] = 0x40; m_EventConstMap["Key_A"] = 0x41; m_EventConstMap["Key_B"] = 0x42; m_EventConstMap["Key_C"] = 0x43; m_EventConstMap["Key_D"] = 0x44; m_EventConstMap["Key_E"] = 0x45; m_EventConstMap["Key_F"] = 0x46; m_EventConstMap["Key_G"] = 0x47; m_EventConstMap["Key_H"] = 0x48; m_EventConstMap["Key_I"] = 0x49; m_EventConstMap["Key_J"] = 0x4a; m_EventConstMap["Key_K"] = 0x4b; m_EventConstMap["Key_L"] = 0x4c; m_EventConstMap["Key_M"] = 0x4d; m_EventConstMap["Key_N"] = 0x4e; m_EventConstMap["Key_O"] = 0x4f; m_EventConstMap["Key_P"] = 0x50; m_EventConstMap["Key_Q"] = 0x51; m_EventConstMap["Key_R"] = 0x52; m_EventConstMap["Key_S"] = 0x53; m_EventConstMap["Key_T"] = 0x54; m_EventConstMap["Key_U"] = 0x55; m_EventConstMap["Key_V"] = 0x56; m_EventConstMap["Key_W"] = 0x57; m_EventConstMap["Key_X"] = 0x58; m_EventConstMap["Key_Y"] = 0x59; m_EventConstMap["Key_Z"] = 0x5a; m_EventConstMap["Key_BracketLeft"] = 0x5b; m_EventConstMap["Key_Backslash"] = 0x5c; m_EventConstMap["Key_BracketRight"] = 0x5d; m_EventConstMap["Key_AsciiCircum"] = 0x5e; m_EventConstMap["Key_Underscore"] = 0x5f; m_EventConstMap["Key_QuoteLeft"] = 0x60; m_EventConstMap["Key_BraceLeft"] = 0x7b; m_EventConstMap["Key_Bar"] = 0x7c; m_EventConstMap["Key_BraceRight"] = 0x7d; m_EventConstMap["Key_AsciiTilde"] = 0x7e; m_EventConstMap["Key_nobreakspace"] = 0x0a0; m_EventConstMap["Key_exclamdown"] = 0x0a1; m_EventConstMap["Key_cent"] = 0x0a2; m_EventConstMap["Key_sterling"] = 0x0a3; m_EventConstMap["Key_currency"] = 0x0a4; m_EventConstMap["Key_yen"] = 0x0a5; m_EventConstMap["Key_brokenbar"] = 0x0a6; m_EventConstMap["Key_section"] = 0x0a7; m_EventConstMap["Key_diaeresis"] = 0x0a8; m_EventConstMap["Key_copyright"] = 0x0a9; m_EventConstMap["Key_ordfeminine"] = 0x0aa; m_EventConstMap["Key_guillemotleft"] = 0x0ab; m_EventConstMap["Key_notsign"] = 0x0ac; m_EventConstMap["Key_hyphen"] = 0x0ad; m_EventConstMap["Key_registered"] = 0x0ae; m_EventConstMap["Key_macron"] = 0x0af; m_EventConstMap["Key_degree"] = 0x0b0; m_EventConstMap["Key_plusminus"] = 0x0b1; m_EventConstMap["Key_twosuperior"] = 0x0b2; m_EventConstMap["Key_threesuperior"] = 0x0b3; m_EventConstMap["Key_acute"] = 0x0b4; m_EventConstMap["Key_mu"] = 0x0b5; m_EventConstMap["Key_paragraph"] = 0x0b6; m_EventConstMap["Key_periodcentered"] = 0x0b7; m_EventConstMap["Key_cedilla"] = 0x0b8; m_EventConstMap["Key_onesuperior"] = 0x0b9; m_EventConstMap["Key_masculine"] = 0x0ba; m_EventConstMap["Key_guillemotright"] = 0x0bb; m_EventConstMap["Key_onequarter"] = 0x0bc; m_EventConstMap["Key_onehalf"] = 0x0bd; m_EventConstMap["Key_threequarters"] = 0x0be; m_EventConstMap["Key_questiondown"] = 0x0bf; m_EventConstMap["Key_Agrave"] = 0x0c0; m_EventConstMap["Key_Aacute"] = 0x0c1; m_EventConstMap["Key_Acircumflex"] = 0x0c2; m_EventConstMap["Key_Atilde"] = 0x0c3; m_EventConstMap["Key_Adiaeresis"] = 0x0c4; m_EventConstMap["Key_Aring"] = 0x0c5; m_EventConstMap["Key_AE"] = 0x0c6; m_EventConstMap["Key_Ccedilla"] = 0x0c7; m_EventConstMap["Key_Egrave"] = 0x0c8; m_EventConstMap["Key_Eacute"] = 0x0c9; m_EventConstMap["Key_Ecircumflex"] = 0x0ca; m_EventConstMap["Key_Ediaeresis"] = 0x0cb; m_EventConstMap["Key_Igrave"] = 0x0cc; m_EventConstMap["Key_Iacute"] = 0x0cd; m_EventConstMap["Key_Icircumflex"] = 0x0ce; m_EventConstMap["Key_Idiaeresis"] = 0x0cf; m_EventConstMap["Key_ETH"] = 0x0d0; m_EventConstMap["Key_Ntilde"] = 0x0d1; m_EventConstMap["Key_Ograve"] = 0x0d2; m_EventConstMap["Key_Oacute"] = 0x0d3; m_EventConstMap["Key_Ocircumflex"] = 0x0d4; m_EventConstMap["Key_Otilde"] = 0x0d5; m_EventConstMap["Key_Odiaeresis"] = 0x0d6; m_EventConstMap["Key_multiply"] = 0x0d7; m_EventConstMap["Key_Ooblique"] = 0x0d8; m_EventConstMap["Key_Ugrave"] = 0x0d9; m_EventConstMap["Key_Uacute"] = 0x0da; m_EventConstMap["Key_Ucircumflex"] = 0x0db; m_EventConstMap["Key_Udiaeresis"] = 0x0dc; m_EventConstMap["Key_Yacute"] = 0x0dd; m_EventConstMap["Key_THORN"] = 0x0de; m_EventConstMap["Key_ssharp"] = 0x0df; m_EventConstMap["Key_agrave"] = 0x0e0; m_EventConstMap["Key_aacute"] = 0x0e1; m_EventConstMap["Key_acircumflex"] = 0x0e2; m_EventConstMap["Key_atilde"] = 0x0e3; m_EventConstMap["Key_adiaeresis"] = 0x0e4; m_EventConstMap["Key_aring"] = 0x0e5; m_EventConstMap["Key_ae"] = 0x0e6; m_EventConstMap["Key_ccedilla"] = 0x0e7; m_EventConstMap["Key_egrave"] = 0x0e8; m_EventConstMap["Key_eacute"] = 0x0e9; m_EventConstMap["Key_ecircumflex"] = 0x0ea; m_EventConstMap["Key_ediaeresis"] = 0x0eb; m_EventConstMap["Key_igrave"] = 0x0ec; m_EventConstMap["Key_iacute"] = 0x0ed; m_EventConstMap["Key_icircumflex"] = 0x0ee; m_EventConstMap["Key_idiaeresis"] = 0x0ef; m_EventConstMap["Key_eth"] = 0x0f0; m_EventConstMap["Key_ntilde"] = 0x0f1; m_EventConstMap["Key_ograve"] = 0x0f2; m_EventConstMap["Key_oacute"] = 0x0f3; m_EventConstMap["Key_ocircumflex"] = 0x0f4; m_EventConstMap["Key_otilde"] = 0x0f5; m_EventConstMap["Key_odiaeresis"] = 0x0f6; m_EventConstMap["Key_division"] = 0x0f7; m_EventConstMap["Key_oslash"] = 0x0f8; m_EventConstMap["Key_ugrave"] = 0x0f9; m_EventConstMap["Key_uacute"] = 0x0fa; m_EventConstMap["Key_ucircumflex"] = 0x0fb; m_EventConstMap["Key_udiaeresis"] = 0x0fc; m_EventConstMap["Key_yacute"] = 0x0fd; m_EventConstMap["Key_thorn"] = 0x0fe; m_EventConstMap["Key_ydiaeresis"] = 0x0ff; m_EventConstMap["Key_unknown"] = 0xffff; m_EventConstMap["Key_none"] = 0xffff; } mitk::EventMapper::~EventMapper() { } //##Documentation //## searches for the event in m_EventDescription and adds the corresponding eventID //## bool mitk::EventMapper::MapEvent(Event* event, GlobalInteraction* globalInteraction, int mitkPostedEventID ) { int eventID = mitkPostedEventID; if( mitkPostedEventID == 0 ) { //search the event in the list of event descriptions, if found, then take the number and produce a stateevent EventDescriptionVecIter iter; for (iter = m_EventDescriptions.begin(); iter!=m_EventDescriptions.end();iter++) { if (*iter == *event) break; } if (iter == m_EventDescriptions.end())//not found return false; eventID = (*iter).GetId(); } //set the Menger_Var m_StateEvent and send to StateMachine, which does everything further! m_StateEvent.Set( eventID, event ); /* Group and Object EventId: then EventMapper has the power to decide which operations hang together; each event causes n (n e N) operations (e.g. StateChanges, data-operations...). Undo must recall all these coherent operations, so all of the same objectId. But Undo has also the power to recall more operationsets, for example a set for building up a new object, so that a newly build up object is deleted after a Undo and not only the latest set point. The StateMachines::ExecuteAction have the power to descide weather a new GroupID has to be calculated (by example after the editing of a new object) A user interaction with the mouse is started by a mousePressEvent, continues with a MouseMove and finishes with a MouseReleaseEvent */ switch (event->GetType()) { case mitk::Type_MouseButtonPress://Increase mitk::OperationEvent::IncCurrObjectEventId(); break; case mitk::Type_MouseMove://same break; case mitk::Type_MouseButtonRelease://same break; case mitk::Type_User://same break; case mitk::Type_KeyPress://Increase mitk::OperationEvent::IncCurrObjectEventId(); break; default://increase mitk::OperationEvent::IncCurrObjectEventId(); } #ifdef MBI_INTERNAL_CONFERENCE //Conference - pass local events through if ( mitkPostedEventID == 0 ) { mitk::CoreObjectFactory::GetInstance()->MapEvent(event,eventID); } #endif //MBI_INTERNAL_CONFERENCE mitk::OperationEvent::ExecuteIncrement(); if ( globalInteraction != NULL ) { return globalInteraction->HandleEvent( &m_StateEvent ); } else { return mitk::GlobalInteraction::GetInstance()->HandleEvent(&m_StateEvent); } } bool mitk::EventMapper::LoadBehavior(std::string fileName) { if ( fileName.empty() ) return false; if (m_XmlFileName.length() > 0) { if (fileName.compare(m_XmlFileName) == 0) return true; // this is nothing bad, we already loaded this file. } this->SetFileName( fileName.c_str() ); m_XmlFileName = fileName.c_str(); return ( this->Parse() ); } bool mitk::EventMapper::LoadBehaviorString(std::string xmlString) { if ( xmlString.empty() ) return false; return ( this->Parse(xmlString.c_str(), xmlString.length()) ); } bool mitk::EventMapper::LoadStandardBehavior() { // Search for StateMachine.xml, bypass relative path in mitkSourceTree for additional search std::string xmlFileName = mitk::StandardFileLocations::GetInstance()->FindFile("StateMachine.xml", "Core/Code/Interactions"); if(xmlFileName != "") return LoadBehavior(xmlFileName); return false; } //##Documentation //## @brief converts the given const String declared in the xml-file //## to the defined const int inline int mitk::EventMapper::convertConstString2ConstInt(std::string input) { ConstMapIter tempIt = m_EventConstMap.find(input.c_str()); if (tempIt != m_EventConstMap.end()) { return (tempIt)->second; } //mitk::StatusBar::GetInstance()->DisplayText("Warning! from mitkEventMapper.cpp: Couldn't find matching Event Int from Event String in XML-File"); return -1;//for didn't find anything } void mitk::EventMapper::StartElement (const char *elementName, const char **atts) { if ( elementName == EVENT ) { // EventDescription(int type, int button, int buttonState,int key, std::string name, int id) EventDescription eventDescr( convertConstString2ConstInt( ReadXMLStringAttribut( TYPE, atts )), convertConstString2ConstInt( ReadXMLStringAttribut( BUTTON, atts )), ReadXMLIntegerAttribut( BUTTONSTATE, atts ), convertConstString2ConstInt( ReadXMLStringAttribut( KEY, atts )), ReadXMLStringAttribut( NAME, atts ), ReadXMLIntegerAttribut( ID, atts )); //check for a double entry unless it is an event for internal usage if (eventDescr.GetType()!= mitk::Type_User) { for (EventDescriptionVecIter iter = m_EventDescriptions.begin(); iter!=m_EventDescriptions.end(); iter++) { if (*iter == eventDescr) { - STATEMACHINE_WARN<<"Event description " << eventDescr.GetName() << " already present! Skipping event description"; + MITK_WARN << "Event description " << eventDescr.GetName() << " already present! Skipping event description"; return; } } } m_EventDescriptions.push_back(eventDescr); } else if ( elementName == EVENTS ) m_StyleName = ReadXMLStringAttribut( STYLE, atts ); } std::string mitk::EventMapper::GetStyleName() const { return m_StyleName; } std::string mitk::EventMapper::ReadXMLStringAttribut( std::string name, const char** atts ) { if(atts) { const char** attsIter = atts; while(*attsIter) { if ( name == *attsIter ) { attsIter++; return *attsIter; } attsIter++; attsIter++; } } return std::string(); } int mitk::EventMapper::ReadXMLIntegerAttribut( std::string name, const char** atts ) { std::string s = ReadXMLStringAttribut( name, atts ); static const std::string hex = "0x"; int result; if ( s[0] == hex[0] && s[1] == hex[1] ) result = strtol( s.c_str(), NULL, 16 ); else result = atoi( s.c_str() ); return result; } void mitk::EventMapper::SetStateEvent(mitk::Event* event) { m_StateEvent.Set( m_StateEvent.GetId(), event ); } bool mitk::EventMapper::RefreshStateEvent(mitk::StateEvent* stateEvent) { //search the event within stateEvent in the list of event descriptions, if found adapt stateEvent ID EventDescriptionVecIter iter; for (iter = m_EventDescriptions.begin(); iter!=m_EventDescriptions.end(); iter++) { if (*iter == *(stateEvent->GetEvent())) break; } if (iter != m_EventDescriptions.end())//found { stateEvent->Set((*iter).GetId(), stateEvent->GetEvent()); return true; } else return false; return false; } void mitk::EventMapper::AddEventMapperAddOn(mitk::EventMapperAddOn* newAddOn) { bool addOnAlreadyAdded = false; for(AddOnVectorType::const_iterator it = this->m_AddOnVector.begin();it != m_AddOnVector.end();it++) { if(*it == newAddOn) { addOnAlreadyAdded = true; break; } } if(!addOnAlreadyAdded) { m_AddOnVector.push_back(newAddOn); MITK_INFO << "AddOn Count: " << m_AddOnVector.size(); } } void mitk::EventMapper::RemoveEventMapperAddOn(mitk::EventMapperAddOn* unusedAddOn) { for(AddOnVectorType::iterator it = this->m_AddOnVector.begin();it != m_AddOnVector.end();it++) { if(*it == unusedAddOn) { m_AddOnVector.erase(it); break; } } } diff --git a/Core/Code/Interactions/mitkPointSetInteractor.cpp b/Core/Code/Interactions/mitkPointSetInteractor.cpp index a350342d58..b726c279a2 100644 --- a/Core/Code/Interactions/mitkPointSetInteractor.cpp +++ b/Core/Code/Interactions/mitkPointSetInteractor.cpp @@ -1,1182 +1,1182 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkPointSetInteractor.h" #include "mitkPointOperation.h" #include "mitkPositionEvent.h" #include "mitkPointSet.h" //#include "mitkStatusBar.h" #include "mitkDataNode.h" #include "mitkInteractionConst.h" #include "mitkAction.h" #include "mitkStateEvent.h" #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include "mitkStateMachineFactory.h" #include "mitkStateTransitionOperation.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" //how precise must the user pick the point //default value const int PRECISION = 5; mitk::PointSetInteractor ::PointSetInteractor(const char * type, DataNode* dataNode, int n) :Interactor(type, dataNode), m_Precision(PRECISION), m_N(n) { if (m_N==0) { STATEMACHINE_WARN<<"Instanciation of PointSetInteractor which takes care of 0 points does't make sense!\n"; STATEMACHINE_WARN<<"Setting number of points to 1!\n"; m_N = 1; } m_LastPoint.Fill(0); m_SumVec.Fill(0); this->InitAccordingToNumberOfPoints(); } mitk::PointSetInteractor::~PointSetInteractor() { } //##Documentation //## overwritten cause this class can handle it better! float mitk::PointSetInteractor::CanHandleEvent(StateEvent const* stateEvent) const { float returnValue = 0.0; //if it is a key event that can be handled in the current state, then return 0.5 mitk::DisplayPositionEvent const *disPosEvent = dynamic_cast (stateEvent->GetEvent()); //Key event handling: if (disPosEvent == NULL) { //check, if the current state has a transition waiting for that key event. if (this->GetCurrentState()->GetTransition(stateEvent->GetId())!=NULL) { return 0.5; } else { return 0; } } //on MouseMove do nothing! if (stateEvent->GetEvent()->GetType() == mitk::Type_MouseMove) { return 0; } //get the time of the sender to look for the right transition. mitk::BaseRenderer* sender = stateEvent->GetEvent()->GetSender(); if (sender != NULL) { unsigned int timeStep = sender->GetTimeStep(m_DataNode->GetData()); //if the event can be understood and if there is a transition waiting for that event mitk::State const* state = this->GetCurrentState(timeStep); if (state!= NULL) if (state->GetTransition(stateEvent->GetId())!=NULL) returnValue = 0.5;//it can be understood mitk::PointSet *pointSet = dynamic_cast(m_DataNode->GetData()); if ( pointSet != NULL ) { //if we have one point or more, then check if the have been picked if ( (pointSet->GetSize( timeStep ) > 0) && (pointSet->SearchPoint( disPosEvent->GetWorldPosition(), m_Precision, timeStep) > -1) ) { returnValue = 1.0; } } } return returnValue; } //TODO: add a new calculation of precision here! Input: StateEvent and Precision //the method does a 2D picking with display coordinates and display geometry. //Here the distance between the mouse position and the point is not as relative anymore! //float mitk::PointSetInteractor::CalculatePrecision(float precision, mitk::StateEvent stateEvent) //{ // mitk::BaseRenderer *renderer = stateEvent->GetEvent()->GetSender(); // if (renderer != NULL) // { // const mitk::DisplayGeometry* displayGeometry = renderer->GetDisplayGeometry(); // if (displayGeometry != NULL) // displayGeometry->WorldToDisplay(, lineFrom); // precision = // } // // return precision; // //} void mitk::PointSetInteractor::UnselectAll( unsigned int timeStep, ScalarType timeInMS ) { mitk::PointSet *pointSet = dynamic_cast( m_DataNode->GetData() ); if ( pointSet == NULL ) { return; } mitk::PointSet::DataType *itkPointSet = pointSet->GetPointSet( timeStep ); if ( itkPointSet == NULL ) { return; } mitk::PointSet::PointsContainer::Iterator it, end; end = itkPointSet->GetPoints()->End(); for (it = itkPointSet->GetPoints()->Begin(); it != end; it++) { int position = it->Index(); PointSet::PointDataType pointData = {0, false, PTUNDEFINED}; itkPointSet->GetPointData( position, &pointData ); //then declare an operation which unselects this point; //UndoOperation as well! if ( pointData.selected ) { mitk::Point3D noPoint; noPoint.Fill( 0 ); mitk::PointOperation *doOp = new mitk::PointOperation( OpDESELECTPOINT, timeInMS, noPoint, position); if ( m_UndoEnabled ) { mitk::PointOperation *undoOp = new mitk::PointOperation(OpSELECTPOINT, timeInMS, noPoint, position); OperationEvent *operationEvent = new OperationEvent( pointSet, doOp, undoOp ); m_UndoController->SetOperationEvent( operationEvent ); } pointSet->ExecuteOperation( doOp ); if ( !m_UndoEnabled ) delete doOp; } } } void mitk::PointSetInteractor::SelectPoint( int position, unsigned int timeStep, ScalarType timeInMS ) { mitk::PointSet *pointSet = dynamic_cast< mitk::PointSet * >( m_DataNode->GetData() ); //if List is empty, then no select of a point can be done! if ( (pointSet == NULL) || (pointSet->GetSize( timeStep ) <= 0) ) { return; } //dummyPoint... not needed anyway mitk::Point3D noPoint; noPoint.Fill(0); mitk::PointOperation *doOp = new mitk::PointOperation( OpSELECTPOINT, timeInMS, noPoint, position); if ( m_UndoEnabled ) { mitk::PointOperation* undoOp = new mitk::PointOperation( OpDESELECTPOINT, timeInMS, noPoint, position); OperationEvent *operationEvent = new OperationEvent(pointSet, doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } pointSet->ExecuteOperation( doOp ); if ( !m_UndoEnabled ) delete doOp; } bool mitk::PointSetInteractor::ExecuteAction( Action* action, mitk::StateEvent const* stateEvent ) { bool ok = false;//for return type bool //checking corresponding Data; has to be a PointSet or a subclass mitk::PointSet* pointSet = dynamic_cast(m_DataNode->GetData()); if ( pointSet == NULL ) { return false; } //get the timestep to support 3D+T const mitk::Event *theEvent = stateEvent->GetEvent(); mitk::ScalarType timeInMS = 0.0; //check if the current timestep has to be changed if ( theEvent ) { if (theEvent->GetSender() != NULL) { //additionaly to m_TimeStep we need timeInMS to satisfy the execution of the operations timeInMS = theEvent->GetSender()->GetTime(); } } //for reading on the points, Id's etc mitk::PointSet::DataType *itkPointSet = pointSet->GetPointSet( m_TimeStep ); if ( itkPointSet == NULL ) { return false; } mitk::PointSet::PointsContainer *points = itkPointSet->GetPoints(); /*Each case must watch the type of the event!*/ switch (action->GetActionId()) { case AcDONOTHING: ok = true; break; case AcCHECKOPERATION: //to check if the given Event is a DisplayPositionEvent. { mitk::DisplayPositionEvent const *dispPosEvent = dynamic_cast ( stateEvent->GetEvent()); if (dispPosEvent != NULL) { mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDYES, stateEvent->GetEvent()); this->HandleEvent( newStateEvent ); delete newStateEvent; } else { mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDNO, stateEvent->GetEvent()); this->HandleEvent( newStateEvent ); delete newStateEvent; } ok = true; break; } case AcADDPOINT: // Declare two operations: one for the selected state: deselect the last // one selected and select the new one the other operation is the add // operation: There the first empty place have to be found and the new // point inserted into that space { mitk::DisplayPositionEvent const *posEvent = dynamic_cast < const mitk::DisplayPositionEvent * > (stateEvent->GetEvent()); // Check if it is a DisplayEvent thrown in a 3D window. Then the // z-information is missing. Returning false might end in the state // full, but the last point couldn't be added, so the set wouldn't be // full. So a extra Action that checks the operationtype has been added. if ( posEvent == NULL ) { return false; } mitk::Point3D itkPoint; itkPoint = posEvent->GetWorldPosition(); // undo-supported deselect of all points in the DataList; if List is // empty, then nothing will be unselected this->UnselectAll( m_TimeStep, timeInMS ); // find the position, the point is to be added to: first entry with // empty index. If the Set is empty, then start with 0. if not empty, // then take the first index not occupied int lastPosition = 0; if (!points->empty()) { mitk::PointSet::PointsIterator it, end; it = points->Begin(); end = points->End(); while( it != end ) { if (!points->IndexExists(lastPosition)) break; ++it; ++lastPosition; } } PointOperation* doOp = new mitk::PointOperation( OpINSERT, timeInMS, itkPoint, lastPosition); if (m_UndoEnabled) { // difference between OpDELETE and OpREMOVE is, that OpDELETE deletes // a point at the end, and OpREMOVE deletes it from the given position // remove is better, cause we need the position to add or remove the // point anyway. We can get the last position from size() PointOperation *undoOp = new mitk::PointOperation( OpREMOVE, timeInMS, itkPoint, lastPosition); OperationEvent *operationEvent = new OperationEvent(pointSet, doOp, undoOp, "Add point"); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation pointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; //the point is added and directly selected in PintSet. So no need to call OpSELECTPOINT ok = true; // Update the display mitk::RenderingManager::GetInstance()->RequestUpdateAll(); break; } case AcINITMOVEMENT: { mitk::PositionEvent const *posEvent = dynamic_cast (stateEvent->GetEvent()); if (posEvent == NULL) return false; // start of the Movement is stored to calculate the undoKoordinate // in FinishMovement m_LastPoint = posEvent->GetWorldPosition(); // initialize a value to calculate the movement through all // MouseMoveEvents from MouseClick to MouseRelease m_SumVec.Fill(0); ok = true; break; } case AcMOVESELECTED://moves all selected Elements { mitk::PositionEvent const *posEvent = dynamic_cast (stateEvent->GetEvent()); if (posEvent == NULL) return false; mitk::Point3D newPoint, resultPoint; newPoint = posEvent->GetWorldPosition(); // search the elements in the list that are selected then calculate the // vector, because only with the vector we can move several elements in // the same direction // newPoint - lastPoint = vector // then move all selected and set the lastPoint = newPoint. // then add all vectors to a summeryVector (to be able to calculate the // startpoint for undoOperation) mitk::Vector3D dirVector = newPoint - m_LastPoint; //sum up all Movement for Undo in FinishMovement m_SumVec = m_SumVec + dirVector; mitk::PointSet::PointsIterator it, end; it = points->Begin(); end = points->End(); while( it != end ) { int position = it->Index(); if ( pointSet->GetSelectInfo(position, m_TimeStep) )//if selected { PointSet::PointType pt = pointSet->GetPoint(position, m_TimeStep); mitk::Point3D sumVec; sumVec[0] = pt[0]; sumVec[1] = pt[1]; sumVec[2] = pt[2]; resultPoint = sumVec + dirVector; PointOperation* doOp = new mitk::PointOperation(OpMOVE, timeInMS, resultPoint, position); //execute the Operation //here no undo is stored, because the movement-steps aren't interesting. // only the start and the end is interisting to store for undo. pointSet->ExecuteOperation(doOp); delete doOp; } ++it; } m_LastPoint = newPoint;//for calculation of the direction vector ok = true; // Update the display mitk::RenderingManager::GetInstance()->RequestUpdateAll(); break; } case AcREMOVEPOINT://remove the given Point from the list { //if the point to be removed is given by the positionEvent: mitk::PositionEvent const *posEvent = dynamic_cast (stateEvent->GetEvent()); if (posEvent != NULL) { mitk::Point3D itkPoint; itkPoint = posEvent->GetWorldPosition(); //search the point in the list int position = pointSet->SearchPoint(itkPoint, 0.0, m_TimeStep); //distance set to 0, cause we already got the exact point from last //State checkpointbut we also need the position in the list to remove it if (position>=0)//found a point { PointSet::PointType pt = pointSet->GetPoint(position, m_TimeStep); itkPoint[0] = pt[0]; itkPoint[1] = pt[1]; itkPoint[2] = pt[2]; //Undo PointOperation* doOp = new mitk::PointOperation(OpREMOVE, timeInMS, itkPoint, position); if (m_UndoEnabled) //write to UndoMechanism { PointOperation* undoOp = new mitk::PointOperation(OpINSERT, timeInMS, itkPoint, position); OperationEvent *operationEvent = new OperationEvent(pointSet, doOp, undoOp, "Remove point"); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation pointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; /*now select the point "position-1", and if it is the first in list, then contine at the last in list*/ //only then a select of a point is possible! if (pointSet->GetSize( m_TimeStep ) > 0) { if (position>0)//not the first in list { this->SelectPoint( position-1, m_TimeStep, timeInMS ); } //it was the first point in list, that was removed, so select //the last in list else { position = pointSet->GetSize( m_TimeStep ) - 1; //last in list this->SelectPoint( position, m_TimeStep, timeInMS ); }//else }//if ok = true; } } else //no position is given so remove all selected elements { //delete all selected points //search for the selected one and then declare the operations! mitk::PointSet::PointsContainer::Iterator it, end; it = points->Begin(); end = points->End(); int position = 0; int previousExistingPosition = -1;//to recognize the last existing position; needed because the iterator gets invalid if the point is deleted! int lastDelPrevExistPosition = -1; //the previous position of the last deleted point while (it != end) { if (points->IndexExists(it->Index())) { //if point is selected if ( pointSet->GetSelectInfo(it->Index(), m_TimeStep) ) { //get the coordinates of that point to be undoable PointSet::PointType selectedPoint = it->Value(); mitk::Point3D itkPoint; itkPoint[0] = selectedPoint[0]; itkPoint[1] = selectedPoint[1]; itkPoint[2] = selectedPoint[2]; position = it->Index(); PointOperation* doOp = new mitk::PointOperation(OpREMOVE, timeInMS, itkPoint, position); //Undo if (m_UndoEnabled) //write to UndoMechanism { PointOperation* undoOp = new mitk::PointOperation(OpINSERT, timeInMS, itkPoint, position); OperationEvent *operationEvent = new OperationEvent(pointSet, doOp, undoOp, "Remove point"); m_UndoController->SetOperationEvent(operationEvent); } pointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; //after delete the iterator is undefined, so start again //count to the last existing entry if (points->Size()>1 && points->IndexExists(previousExistingPosition)) { for (it = points->Begin(); it != points->End(); it++) { if (it->Index() == (unsigned int) previousExistingPosition) { lastDelPrevExistPosition = previousExistingPosition; break; //return if the iterator on the last existing position is found } } } else // size <= 1 or no previous existing position set { //search for the first existing position for (it = points->Begin(); it != points->End(); it++) if (points->IndexExists(it->Index())) { previousExistingPosition = it->Index(); break; } } //now that we have set the iterator, lets get sure, that the next it++ will not crash! if (it == end) { break; } }//if else { previousExistingPosition = it->Index(); } }//if index exists it++; }//while if (lastDelPrevExistPosition < 0)//the var has not been set because the first element was deleted and there was no prev position lastDelPrevExistPosition = previousExistingPosition; //go to the end /* * now select the point before the point/points that was/were deleted */ if (pointSet->GetSize( m_TimeStep ) > 0) //only then a select of a point is possible! { if (points->IndexExists(lastDelPrevExistPosition)) { this->SelectPoint( lastDelPrevExistPosition, m_TimeStep, timeInMS ); } else { //select the first existing element for (mitk::PointSet::PointsContainer::Iterator it = points->Begin(); it != points->End(); it++) if (points->IndexExists(it->Index())) { this->SelectPoint( it->Index(), m_TimeStep, timeInMS ); break; } } }//if ok = true; }//else }//case // Update the display mitk::RenderingManager::GetInstance()->RequestUpdateAll(); break; // Remove all Points that have been set at once. // TODO: Undo function not supported yet. case AcREMOVEALL: { if ( !points->empty() ) { PointSet::PointType pt; mitk::PointSet::PointsContainer::Iterator it, end; it = points->Begin(); end = points->End(); int position = 0; while ( it != end ) { position = it->Index(); if ( points->IndexExists( position ) ) { pt = pointSet->GetPoint( position, m_TimeStep ); PointOperation* doOp = new mitk::PointOperation( OpREMOVE, timeInMS, pt, position ); ++it; pointSet->ExecuteOperation( doOp ); delete doOp; } else it++; } } ok = true; // Update the display mitk::RenderingManager::GetInstance()->RequestUpdateAll(); break; } //Checking if the Point transmitted is close enough to one point. Then //generate a new event with the point and let this statemaschine //handle the event. case AcCHECKELEMENT: { mitk::PositionEvent const *posEvent = dynamic_cast (stateEvent->GetEvent()); if (posEvent != NULL) { mitk::Point3D worldPoint = posEvent->GetWorldPosition(); int position = pointSet->SearchPoint( worldPoint, m_Precision, m_TimeStep ); if (position>=0)//found a point near enough to the given point { //get that point, the one meant by the user! PointSet::PointType pt = pointSet->GetPoint(position, m_TimeStep); mitk::Point2D displPoint; displPoint[0] = worldPoint[0]; displPoint[1] = worldPoint[1]; //new Event with information YES and with the correct point mitk::PositionEvent const* newPosEvent = new mitk::PositionEvent(posEvent->GetSender(), Type_None, BS_NoButton, BS_NoButton, Key_none, displPoint, pt); mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDYES, newPosEvent); //call HandleEvent to leave the guard-state this->HandleEvent( newStateEvent ); delete newStateEvent; delete newPosEvent; ok = true; } else { //new Event with information NO mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDNO, posEvent); this->HandleEvent(newStateEvent ); delete newStateEvent; ok = true; } } else { - STATEMACHINE_DEBUG("OperationError")<GetType()<<" AcCHECKELEMENT expected PointOperation."; + MITK_DEBUG("OperationError")<GetType()<<" AcCHECKELEMENT expected PointOperation."; mitk::DisplayPositionEvent const *disPosEvent = dynamic_cast ( stateEvent->GetEvent()); if (disPosEvent != NULL) { //2d Koordinates for 3D Interaction; return false to redo //the last statechange mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDNO, disPosEvent); this->HandleEvent(newStateEvent ); delete newStateEvent; ok = true; } } break; } case AcCHECKONESELECTED: //check if there is a point that is selected { if (pointSet->GetNumberOfSelected(m_TimeStep)>0) { mitk::StateEvent* newStateEvent = new mitk::StateEvent( EIDYES, theEvent); this->HandleEvent( newStateEvent ); delete newStateEvent; } else //not selected then call event EIDNO { //new Event with information NO mitk::StateEvent* newStateEvent = new mitk::StateEvent( EIDNO, theEvent); this->HandleEvent( newStateEvent ); delete newStateEvent; } ok = true; break; } case AcCHECKSELECTED: /*check, if the given point is selected: if no, then send EIDNO if yes, then send EIDYES*/ // check, if: because of the need to look up the point again, it is // possible, that we grab the wrong point in case there are two same points // so maybe we do have to set a global index for further computation, // as long, as the mouse is moved... { int position = -1; mitk::PositionEvent const *posEvent = dynamic_cast (stateEvent->GetEvent()); if (posEvent == NULL) return false; mitk::Point3D worldPoint = posEvent->GetWorldPosition(); position = pointSet->SearchPoint(worldPoint, m_Precision, m_TimeStep); if (position>=0) { mitk::PositionEvent const *newPosEvent = new mitk::PositionEvent(posEvent->GetSender(), posEvent->GetType(), posEvent->GetButton(), posEvent->GetButtonState(), posEvent->GetKey(), posEvent->GetDisplayPosition(), posEvent->GetWorldPosition()); //if selected on true, then call Event EIDYES if (pointSet->GetSelectInfo(position, m_TimeStep)) { mitk::StateEvent* newStateEvent = new mitk::StateEvent( EIDYES, newPosEvent ); this->HandleEvent( newStateEvent ); delete newStateEvent; ok = true; //saving the spot for calculating the direction vector in moving m_LastPoint = posEvent->GetWorldPosition(); } else //not selected then call event EIDNO { //new Event with information NO mitk::StateEvent* newStateEvent = new mitk::StateEvent( EIDNO, newPosEvent ); this->HandleEvent( newStateEvent ); delete newStateEvent; ok = true; } delete newPosEvent; } //the position wasn't set properly. If necessary: search the given //point in list and set var position else { /* mitk::StatusBar::GetInstance()->DisplayText( "Message from mitkPointSetInteractor: Error in Actions! Check Config XML-file", 10000); */ ok = false; } break; } //generate Events if the set will be full after the addition of the // point or not. case AcCHECKNMINUS1: { // number of points not limited->pass on // "Amount of points in Set is smaller then N-1" if (m_N<0) { mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDSTSMALERNMINUS1, stateEvent->GetEvent()); this->HandleEvent( newStateEvent ); delete newStateEvent; ok = true; } else { if (pointSet->GetSize( m_TimeStep ) < m_N-1 ) //pointset after addition won't be full { mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDSTSMALERNMINUS1, stateEvent->GetEvent()); this->HandleEvent( newStateEvent ); delete newStateEvent; ok = true; } else //after the addition of a point, the container will be full { mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDSTLARGERNMINUS1, stateEvent->GetEvent()); this->HandleEvent(newStateEvent ); delete newStateEvent; ok = true; }//else }//else } break; case AcCHECKEQUALS1: { //the number of points in the list is 1 (or smaler) if (pointSet->GetSize( m_TimeStep ) <= 1) { mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDYES, stateEvent->GetEvent()); this->HandleEvent( newStateEvent ); delete newStateEvent; ok = true; } else //more than 1 points in list, so stay in the state! { mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDNO, stateEvent->GetEvent()); this->HandleEvent(newStateEvent ); delete newStateEvent; ok = true; } } break; case AcCHECKNUMBEROFPOINTS: { //the number of points in the list is 1 (or smaler), so will be empty after delete if (pointSet->GetSize( m_TimeStep ) <= 1) { mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDEMPTY, stateEvent->GetEvent()); this->HandleEvent( newStateEvent ); delete newStateEvent; ok = true; } else if (pointSet->GetSize( m_TimeStep ) <= m_N || m_N <= -1) //m_N is set to unlimited points allowed or more than 1 points in list, but not full, so stay in the state! { // if the number of points equals m_N and no point of the point set is selected switch to state EIDEQUALSN if ((pointSet->GetSize( m_TimeStep ) == m_N)&&(pointSet->GetNumberOfSelected()==0)) { mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDEQUALSN, stateEvent->GetEvent()); this->HandleEvent(newStateEvent ); delete newStateEvent; ok = true; } // if the number of points is small than or equal m_N and point(s) are selected stay in state else { mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDSMALLERN, stateEvent->GetEvent()); this->HandleEvent(newStateEvent ); delete newStateEvent; ok = true; } } else //pointSet->GetSize( m_TimeStep ) >=m_N. // This can happen if the points were not added // by interaction but by loading a .mps file { mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDEQUALSN, stateEvent->GetEvent()); this->HandleEvent(newStateEvent ); delete newStateEvent; ok = true; } } break; case AcSELECTPICKEDOBJECT://and deselect others { mitk::PositionEvent const *posEvent = dynamic_cast (stateEvent->GetEvent()); if (posEvent == NULL) return false; mitk::Point3D itkPoint; itkPoint = posEvent->GetWorldPosition(); //search the point in the list int position = pointSet->SearchPoint(itkPoint, 0.0, m_TimeStep); //distance set to 0, cause we already got the exact point from last //State checkpoint but we also need the position in the list to move it if (position>=0)//found a point { //first deselect the other points //undoable deselect of all points in the DataList this->UnselectAll( m_TimeStep, timeInMS); PointOperation* doOp = new mitk::PointOperation(OpSELECTPOINT, timeInMS, itkPoint, position); //Undo if (m_UndoEnabled) //write to UndoMechanism { PointOperation* undoOp = new mitk::PointOperation(OpDESELECTPOINT, timeInMS, itkPoint, position); OperationEvent *operationEvent = new OperationEvent(pointSet, doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation pointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; ok = true; } // Update the display mitk::RenderingManager::GetInstance()->RequestUpdateAll(); break; } case AcDESELECTOBJECT: { mitk::PositionEvent const *posEvent = dynamic_cast (stateEvent->GetEvent()); if (posEvent == NULL) return false; mitk::Point3D itkPoint; itkPoint = posEvent->GetWorldPosition(); //search the point in the list int position = pointSet->SearchPoint(itkPoint, 0.0, m_TimeStep); //distance set to 0, cause we already got the exact point from last // State checkpoint but we also need the position in the list to move it if (position>=0)//found a point { //Undo PointOperation* doOp = new mitk::PointOperation(OpDESELECTPOINT, timeInMS, itkPoint, position); if (m_UndoEnabled) //write to UndoMechanism { PointOperation* undoOp = new mitk::PointOperation(OpSELECTPOINT, timeInMS, itkPoint, position); OperationEvent *operationEvent = new OperationEvent(pointSet, doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation pointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; ok = true; } // Update the display mitk::RenderingManager::GetInstance()->RequestUpdateAll(); break; } case AcDESELECTALL: { //undo-supported able deselect of all points in the DataList this->UnselectAll( m_TimeStep, timeInMS ); ok = true; // Update the display mitk::RenderingManager::GetInstance()->RequestUpdateAll(); break; } case AcFINISHMOVEMENT: { mitk::PositionEvent const *posEvent = dynamic_cast (stateEvent->GetEvent()); if (posEvent == NULL) return false; //finish the movement: //the final point is m_LastPoint //m_SumVec stores the movement in a vector //the operation would not be necessary, but we need it for the undo Operation. //m_LastPoint is for the Operation //the point for undoOperation calculates from all selected //elements (point) - m_SumVec //search all selected elements and move them with undo-functionality. mitk::PointSet::PointsIterator it, end; it = points->Begin(); end = points->End(); while( it != end ) { int position = it->Index(); if ( pointSet->GetSelectInfo(position, m_TimeStep) )//if selected { PointSet::PointType pt = pointSet->GetPoint(position, m_TimeStep); Point3D itkPoint; itkPoint[0] = pt[0]; itkPoint[1] = pt[1]; itkPoint[2] = pt[2]; PointOperation* doOp = new mitk::PointOperation(OpMOVE, timeInMS, itkPoint, position); if ( m_UndoEnabled )//&& (posEvent->GetType() == mitk::Type_MouseButtonRelease) { //set the undo-operation, so the final position is undo-able //calculate the old Position from the already moved position - m_SumVec mitk::Point3D undoPoint = ( itkPoint - m_SumVec ); PointOperation* undoOp = new mitk::PointOperation(OpMOVE, timeInMS, undoPoint, position); OperationEvent *operationEvent = new OperationEvent(pointSet, doOp, undoOp, "Move point"); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation pointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; } ++it; } //set every variable for movement calculation to zero // commented out: increases usebility in derived classes. /*m_LastPoint.Fill(0); m_SumVec.Fill(0);*/ //increase the GroupEventId, so that the Undo goes to here this->IncCurrGroupEventId(); ok = true; // Update the display mitk::RenderingManager::GetInstance()->RequestUpdateAll(); break; } case AcCLEAR: { this->Clear( m_TimeStep, timeInMS ); // Update the display mitk::RenderingManager::GetInstance()->RequestUpdateAll(); break; } default: return Superclass::ExecuteAction( action, stateEvent ); } // indicate modification of data tree node m_DataNode->Modified(); return ok; } void mitk::PointSetInteractor::Clear( unsigned int timeStep, ScalarType timeInMS ) { mitk::Point3D point; point.Fill(0); mitk::PointSet *pointSet = dynamic_cast(m_DataNode->GetData()); if ( pointSet == NULL ) { return; } mitk::PointSet::DataType *itkPointSet = pointSet->GetPointSet( timeStep ); if ( itkPointSet == NULL ) { return; } //for reading on the points, Id's etc mitk::PointSet::PointsContainer *points = itkPointSet->GetPoints(); mitk::PointSet::PointsIterator it, end; it = points->Begin(); end = points->End(); while( (it != end) && (pointSet->GetSize( timeStep ) > 0) ) { point = pointSet->GetPoint( it->Index(), timeStep ); PointOperation *doOp = new mitk::PointOperation( OpREMOVE, timeInMS, point, it->Index()); //write to UndoMechanism if ( m_UndoEnabled ) { PointOperation *undoOp = new mitk::PointOperation( OpINSERT, timeInMS, point, it->Index()); OperationEvent *operationEvent = new OperationEvent( pointSet, doOp, undoOp ); m_UndoController->SetOperationEvent( operationEvent ); } //execute the Operation ++it; pointSet->ExecuteOperation( doOp ); if ( !m_UndoEnabled ) delete doOp; } //reset the statemachine this->ResetStatemachineToStartState(timeStep); } void mitk::PointSetInteractor::InitAccordingToNumberOfPoints() { if (m_DataNode == NULL) return; mitk::PointSet *pointSet = dynamic_cast(m_DataNode->GetData()); if ( pointSet != NULL ) { //resize the CurrentStateVector this->ExpandStartStateVector(pointSet->GetPointSetSeriesSize()); for (unsigned int timestep = 0; timestep < pointSet->GetPointSetSeriesSize(); timestep++) { //go to new timestep this->UpdateTimeStep(timestep); int numberOfPoints = pointSet->GetSize( timestep ); if (numberOfPoints == 0) continue; //pointset is empty else { //we have a set of loaded points. Deselect all points, because they are all set to selected when added! this->UnselectAll(timestep); if (numberOfPointsHandleEvent( newStateEvent ); delete newStateEvent; delete nullEvent; } else if (numberOfPoints>=m_N) { if (numberOfPoints>m_N) { STATEMACHINE_WARN<<"Point Set contains more points than needed!\n";//display a warning that there are too many points } //get the currentState to state "Set full" const mitk::Event* nullEvent = new mitk::Event(NULL, Type_User, BS_NoButton, BS_NoButton, Key_none); mitk::StateEvent* newStateEvent = new mitk::StateEvent(EIDEQUALSN, nullEvent); this->HandleEvent( newStateEvent ); delete newStateEvent; delete nullEvent; } } } } return; } void mitk::PointSetInteractor::DataChanged() { this->InitAccordingToNumberOfPoints(); return; } diff --git a/Core/Code/Interactions/mitkStateMachine.cpp b/Core/Code/Interactions/mitkStateMachine.cpp index a3c35cd2a0..bbc70b4857 100644 --- a/Core/Code/Interactions/mitkStateMachine.cpp +++ b/Core/Code/Interactions/mitkStateMachine.cpp @@ -1,316 +1,316 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkStateMachine.h" #include "mitkStateTransitionOperation.h" #include "mitkInteractionConst.h" #include "mitkInteractor.h" #include "mitkTransition.h" #include "mitkOperationEvent.h" #include "mitkStateEvent.h" #include "mitkAction.h" #include "mitkUndoController.h" #include #include "mitkGlobalInteraction.h" #include /** * @brief Constructor * daclares a new StateMachine and connects * it to a StateMachine of Type type; * Also the undo mechanism is instanciated and enabled/disabled **/ mitk::StateMachine::StateMachine(const char * type) : m_UndoController(NULL), m_Type("") { if(type!=NULL) //no need to throw a warning here, because the statemachine yet here doesn't have to be set up. { m_Type = type; //the statemachine doesn't know yet anything about the number of timesteps of the data. So we initialize it with one element. this->InitializeStartStates(1); } if (!m_UndoController) { m_UndoController = new UndoController(UndoController::VERBOSE_LIMITEDLINEARUNDO);//switch to LLU or add LLU /** * here the Undo mechanism is enabled / disabled for all interactors. **/ m_UndoEnabled = true; } m_TimeStep = 0; } mitk::StateMachine::~StateMachine() { //clean up map using deletes for ( mitk::StateMachine::ActionFunctionsMapType::iterator iter = m_ActionFunctionsMap.begin(); iter != m_ActionFunctionsMap.end(); ++iter ) { delete iter->second; } delete m_UndoController; } std::string mitk::StateMachine::GetType() const { return m_Type; } const mitk::State* mitk::StateMachine::GetCurrentState(unsigned int timeStep) const { if (m_CurrentStateVector.size() > timeStep) //the size of the vector has to be one integer higher than the timeStep. return m_CurrentStateVector[timeStep].GetPointer(); return NULL; } void mitk::StateMachine::ResetStatemachineToStartState(unsigned int timeStep) { mitk::State* startState = mitk::GlobalInteraction::GetInstance()->GetStartState((const char *)(&m_Type[0])); if ( m_UndoEnabled ) //write to UndoMechanism if Undo is enabled { //UNDO for this statechange; StateTransitionOperation* doOp = new StateTransitionOperation(OpSTATECHANGE, startState, timeStep); StateTransitionOperation* undoOp = new StateTransitionOperation(OpSTATECHANGE, m_CurrentStateVector[timeStep], timeStep); OperationEvent *operationEvent = new OperationEvent(((mitk::OperationActor*)(this)), doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } //can be done without calling this->ExecuteOperation() m_CurrentStateVector[timeStep] = startState; } bool mitk::StateMachine::HandleEvent(StateEvent const* stateEvent) { if (stateEvent == NULL) return false; if (m_CurrentStateVector.empty()) { STATEMACHINE_ERROR << "Error in mitkStateMachine.cpp: StateMachine not initialized!\n"; return false;//m_CurrentStateVector needs to be initialized! } if (m_TimeStep >= m_CurrentStateVector.size()) { STATEMACHINE_ERROR << "Error in mitkStateMachine.cpp: StateMachine not initialized for this time step!\n"; return false; } if (m_CurrentStateVector[m_TimeStep].IsNull()) { STATEMACHINE_ERROR << "Error in mitkStateMachine.cpp: StateMachine not initialized with the right temporal information!\n"; return false;//m_CurrentState needs to be initialized! } //get the Transition from m_CurrentState which waits for this EventId const Transition *tempTransition = m_CurrentStateVector[m_TimeStep]->GetTransition(stateEvent->GetId()); if (tempTransition == NULL) //no transition in this state for that EventId { return false; } //get next State State *tempNextState = tempTransition->GetNextState(); if (tempNextState == NULL) //wrong built up statemachine! { STATEMACHINE_ERROR << "Error in mitkStateMachine.cpp: StateMachinePattern not defined correctly!\n"; return false; } //and ActionId to execute later on if ( m_CurrentStateVector[m_TimeStep]->GetId() != tempNextState->GetId() )//statechange only if there is a real statechange { if ( m_UndoEnabled ) //write to UndoMechanism if Undo is enabled { //UNDO for this statechange; since we directly change the state, we don't need the do-Operation in case m_UndoEnables == false StateTransitionOperation* doOp = new StateTransitionOperation(OpSTATECHANGE, tempNextState, m_TimeStep); StateTransitionOperation* undoOp = new StateTransitionOperation(OpSTATECHANGE, m_CurrentStateVector[m_TimeStep], m_TimeStep); OperationEvent *operationEvent = new OperationEvent(((mitk::OperationActor*)(this)), doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } - STATEMACHINE_DEBUG("StateChange")<GetType()<<" from " << m_CurrentStateVector[m_TimeStep]->GetId() - << " " << m_CurrentStateVector[m_TimeStep]->GetName() - <<" to " << tempNextState->GetId() <<" "<GetName(); + STATEMACHINE_DEBUG << "from " << m_CurrentStateVector[m_TimeStep]->GetId() + << " " << m_CurrentStateVector[m_TimeStep]->GetName() + << " to " << tempNextState->GetId() <<" "<GetName(); //first following StateChange(or calling ExecuteOperation(tempNextStateOp)), then operation(action) m_CurrentStateVector[m_TimeStep] = tempNextState; } mitk::Transition::ActionVectorIterator actionIdIterator = tempTransition->GetActionBeginIterator(); mitk::Transition::ActionVectorConstIterator actionIdIteratorEnd = tempTransition->GetActionEndIterator(); bool ok = true; while ( actionIdIterator != actionIdIteratorEnd ) { if ( !ExecuteAction(*actionIdIterator, stateEvent) ) { ok = false; } actionIdIterator++; } return ok; } void mitk::StateMachine::EnableUndo(bool enable) { m_UndoEnabled = enable; } void mitk::StateMachine::IncCurrGroupEventId() { mitk::OperationEvent::IncCurrGroupEventId(); } /// look up which object method is associated to the given action and call the method bool mitk::StateMachine::ExecuteAction(Action* action, StateEvent const* stateEvent) { if (!action) return false; int actionId = action->GetActionId(); TStateMachineFunctor* actionFunction = m_ActionFunctionsMap[actionId]; if (!actionFunction) return false; bool retVal = actionFunction->DoAction(action, stateEvent); return retVal; } void mitk::StateMachine::AddActionFunction(int action, mitk::TStateMachineFunctor* functor) { if (!functor) return; // make sure double calls for same action won't cause memory leaks delete m_ActionFunctionsMap[action]; // delete NULL does no harm m_ActionFunctionsMap[action] = functor; } void mitk::StateMachine::ExecuteOperation(Operation* operation) { switch (operation->GetOperationType()) { case OpNOTHING: break; case OpSTATECHANGE: { mitk::StateTransitionOperation* stateTransOp = dynamic_cast(operation); if (stateTransOp == NULL) { STATEMACHINE_WARN<<"Error! see mitkStateMachine.cpp"; return; } unsigned int time = stateTransOp->GetTime(); m_CurrentStateVector[time] = stateTransOp->GetState(); } break; case OpTIMECHANGE: { mitk::StateTransitionOperation* stateTransOp = dynamic_cast(operation); if (stateTransOp == NULL) { STATEMACHINE_WARN<<"Error! see mitkStateMachine.cpp"; return; } m_TimeStep = stateTransOp->GetTime(); } break; case OpDELETE: { //delete this! //before all lower statemachines has to be deleted in a action //this->Delete();//might not work!!!check itk! } case OpUNDELETE: { //now the m_CurrentState has to be set on a special State //that way a delete of a StateMachine can be undone //IMPORTANT: The type has to be the same!!!Done by a higher instance, that creates this! mitk::StateTransitionOperation* stateTransOp = dynamic_cast(operation); if (stateTransOp != NULL) { unsigned int time = stateTransOp->GetTime(); m_CurrentStateVector[time] = stateTransOp->GetState(); } } default: ; } } void mitk::StateMachine::InitializeStartStates(unsigned int timeSteps) { //get the startstate of the pattern State::Pointer startState = mitk::GlobalInteraction::GetInstance()->GetStartState(m_Type.c_str()); if (startState.IsNull()) { STATEMACHINE_FATAL << "Fatal Error in mitkStateMachine.cpp: Initialization of statemachine unsuccessfull! Initialize GlobalInteraction!\n"; } //clear the vector m_CurrentStateVector.clear(); //add n=timesteps pointers pointing to to the startstate for (unsigned int i = 0; i < timeSteps; i++) m_CurrentStateVector.push_back(startState); } // Check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient pre-initialized // elements. // // NOTE: This method will never REDUCE the vector size; it should only // be used to make sure that the vector has enough elements to include the // specified time step. void mitk::StateMachine::ExpandStartStateVector(unsigned int timeSteps) { unsigned int oldSize = m_CurrentStateVector.size(); if ( timeSteps > oldSize ) { State::Pointer startState = mitk::GlobalInteraction::GetInstance()->GetStartState(m_Type.c_str()); for ( unsigned int i = oldSize; i < timeSteps; ++i ) m_CurrentStateVector.insert(m_CurrentStateVector.end(), startState); } } void mitk::StateMachine::UpdateTimeStep(unsigned int timeStep) { //don't need to fill up the memory if the time is uptodate if (timeStep == m_TimeStep) return; //create an operation that changes the time and send it to undocontroller StateTransitionOperation* doOp = new StateTransitionOperation(OpTIMECHANGE, NULL, timeStep); if ( m_UndoEnabled ) //write to UndoMechanism if Undo is enabled { StateTransitionOperation* undoOp = new StateTransitionOperation(OpTIMECHANGE, NULL, m_TimeStep); OperationEvent *operationEvent = new OperationEvent(((mitk::OperationActor*)(this)), doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } this->ExecuteOperation(doOp); } diff --git a/Core/Code/Interactions/mitkStateMachine.h b/Core/Code/Interactions/mitkStateMachine.h index 9b0958201e..3099c399b5 100644 --- a/Core/Code/Interactions/mitkStateMachine.h +++ b/Core/Code/Interactions/mitkStateMachine.h @@ -1,292 +1,292 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef STATEMACHINE_H_HEADER_INCLUDED_C18896BD #define STATEMACHINE_H_HEADER_INCLUDED_C18896BD #include #include #include "mitkOperationActor.h" #include #include "mitkState.h" #include "mitkUndoModel.h" namespace mitk { class Action; class StateEvent; class UndoController; // base class of statem machine functors class MITK_CORE_EXPORT TStateMachineFunctor { public: virtual bool DoAction(Action*, const StateEvent*)=0; // call using function virtual ~TStateMachineFunctor() {} }; // the template functor for arbitrary StateMachine derivations template class TSpecificStateMachineFunctor : public TStateMachineFunctor { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables TSpecificStateMachineFunctor(T* object, bool(T::*memberFunctionPointer)(Action*, const StateEvent*)) :m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } virtual ~TSpecificStateMachineFunctor() {} // virtual destructor // override function "Call" virtual bool DoAction(Action* action, const StateEvent* stateEvent) { return (*m_Object.*m_MemberFunctionPointer)(action, stateEvent); // execute member function } private: T* m_Object; // pointer to object bool (T::*m_MemberFunctionPointer)(Action*, const StateEvent*); // pointer to member function }; /// Can be uses by derived classes of StateMachine to connect action IDs to methods /// Assumes that there is a typedef Classname Self in classes that use this macro #define CONNECT_ACTION(a, f) \ StateMachine::AddActionFunction(a, new TSpecificStateMachineFunctor(this, &Self::f)); -#define STATEMACHINE_INFO MITK_INFO("StateMachine") -#define STATEMACHINE_WARN MITK_WARN("StateMachine") -#define STATEMACHINE_FATAL MITK_FATAL("StateMachine") -#define STATEMACHINE_ERROR MITK_ERROR("StateMachine") -#define STATEMACHINE_DEBUG MITK_DEBUG("StateMachine") +#define STATEMACHINE_INFO MITK_INFO("StateMachine") << "[type: " << GetType() << "] " +#define STATEMACHINE_WARN MITK_WARN("StateMachine") << "[type: " << GetType() << "] " +#define STATEMACHINE_FATAL MITK_FATAL("StateMachine") << "[type: " << GetType() << "] " +#define STATEMACHINE_ERROR MITK_ERROR("StateMachine") << "[type: " << GetType() << "] " +#define STATEMACHINE_DEBUG MITK_DEBUG("StateMachine") << "[type: " << GetType() << "] " /** @brief Superior statemachine @ingroup Interaction Realizes the methods, that every statemachine has to have. Undo can be enabled and disabled through EnableUndo. To implement your own state machine, you have to derive a class from mitk::StateMachine and either - override ExecuteAction() or - Write bool methods that take (Action*, const StateEvent*) as parameter and use the CONNECT_ACTION macro in your constructor The second version is recommended, since it provides more structured code. The following piece of code demonstrates how to use the CONNECT_ACTION macro. The important detail is to provide a typedef classname Self \code class LightSwitch : public StateMachine { public: mitkClassMacro(LightSwitch, StateMachine); // this creates the Self typedef LightSwitch(const char*); bool DoSwitchOn(Action*, const StateEvent*); bool DoSwitchOff(Action*, const StateEvent*); } LightSwitch::LightSwitch(const char* type) :StateMachine(type) { // make sure that AcSWITCHON and AcSWITCHOFF are defined int constants somewhere (e.g. mitkInteractionConst.h) CONNECT_ACTION( AcSWITCHON, DoSwitchOn ); CONNECT_ACTION( AcSWITCHOFF, DoSwitchOff ); } bool LightSwitch::DoSwitchOn(Action*, const StateEvent*) { std::cout << "Enlightenment" << std::endl; } bool LightSwitch::DoSwitchOff(Action*, const StateEvent*) { std::cout << "Confusion" << std::endl; } \endcode What CONNECT_ACTION does, is call StateMachine::AddActionFunction(...) to add some function pointer wrapping class (functor) to a std::map of StateMachine. Whenever StateMachines ExecuteAction is called, StateMachine will lookup the desired Action in its map and call the appropriate method in your derived class. **/ class MITK_CORE_EXPORT StateMachine : public itk::Object, public mitk::OperationActor { public: mitkClassMacro(StateMachine,itk::Object); /** * @brief New Macro with one parameter for creating this object with static New(..) method **/ mitkNewMacro1Param(Self, const char*); /** * @brief Map to connect action IDs with method calls. Use AddActionFunction or (even better) the CONNECT_ACTION macro to fill the map. **/ typedef std::map ActionFunctionsMapType; /** * @brief Type for a vector of StartStatePointers **/ typedef std::vector StartStateVectorType; /** * @brief Get the name and with this the type of the StateMachine **/ std::string GetType() const; /** * @brief handles an Event accordingly to its current State * * Statechange with Undo functionality; * EventMapper gives each event a new objectEventId * and a StateMachine::ExecuteAction can descide weather it gets a * new GroupEventId or not, depending on its state (e.g. finishedNewObject then new GroupEventId). * Object- and group-EventId can also be accessed through static methods from OperationEvent **/ virtual bool HandleEvent(StateEvent const* stateEvent); /** * @brief Enables or disabled Undo. **/ void EnableUndo(bool enable); /** * @brief A statemachine is also an OperationActor due to the UndoMechanism. * * The statechange is done in ExecuteOperation, so that the statechange can be undone by UndoMechanism. * Is set private here and in superclass it is set public, so UndoController * can reach ist, but it can't be overwritten by a subclass * *ATTENTION*: THIS METHOD SHOULD NOT BE CALLED FROM OTHER CLASSES DIRECTLY! **/ virtual void ExecuteOperation(Operation* operation); /** * @brief Friend so that UndoModel can call ExecuteOperation for Undo. **/ friend class UndoModel; friend class GlobalInteraction; protected: /** * @brief Default Constructor. Obsolete to instanciate it with this method! Use ::New(..) method instead. Set the "type" and with this the pattern of the StateMachine **/ StateMachine(const char * type); /** * @brief Default Destructor **/ ~StateMachine(); /** * @brief Adds the Function to ActionList. **/ void AddActionFunction(int action, TStateMachineFunctor* functor); /** * @brief Method called in HandleEvent after Statechange. * * Each statechange has actions, which can be assigned by it's number. * If you are developing a new statemachine, declare all your operations here and send them to Undo-Controller and to the Data. * Object- and group-EventId can also be accessed through static methods from OperationEvent **/ virtual bool ExecuteAction(Action* action, StateEvent const* stateEvent); /** * @brief returns the current state **/ const State* GetCurrentState(unsigned int timeStep = 0) const; /** * @brief if true, then UndoFunctionality is enabled * * Default value is true; **/ bool m_UndoEnabled; /** * @brief Friend protected function of OperationEvent; that way all StateMachines can set GroupEventId to be incremented! **/ void IncCurrGroupEventId(); /** * @brief holds an UndoController, that can be accessed from all StateMachines. For ExecuteAction **/ UndoController* m_UndoController; /** * @brief Resets the current state from the given timeStep to the StartState with undo functionality! Use carefully! * @param[in] timeStep If the statemachine has several timesteps to take care of, specify the according timestep **/ void ResetStatemachineToStartState(unsigned int timeStep = 0); /** * @brief Check if the number of timeSteps is equal to the number of stored StartStates. Nothing is changed if the number is equal. **/ void ExpandStartStateVector(unsigned int timeSteps); /** * @brief initializes m_CurrentStateVector **/ void InitializeStartStates(unsigned int timeSteps); /** * @brief Update the TimeStep of the statemachine with undo-support if undo enabled **/ virtual void UpdateTimeStep(unsigned int timeStep); /** * @brief Current TimeStep if the data which is to be interacted on, has more than 1 TimeStep **/ unsigned int m_TimeStep; private: /** * @brief The type of the StateMachine. This string specifies the StateMachinePattern that is loaded from the StateMachineFactory. **/ std::string m_Type; /** * @brief Points to the current state. **/ StartStateVectorType m_CurrentStateVector; /** * @brief Map of the added Functions **/ ActionFunctionsMapType m_ActionFunctionsMap; }; } // namespace mitk #endif /* STATEMACHINE_H_HEADER_INCLUDED_C18896BD */ diff --git a/Core/Code/Interactions/mitkStateMachineFactory.cpp b/Core/Code/Interactions/mitkStateMachineFactory.cpp index 6081743c5c..d528829f9e 100755 --- a/Core/Code/Interactions/mitkStateMachineFactory.cpp +++ b/Core/Code/Interactions/mitkStateMachineFactory.cpp @@ -1,462 +1,462 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkStateMachineFactory.h" #include "mitkGlobalInteraction.h" #include #include #include #include #include /** * @brief This class builds up all the necessary structures for a statemachine. * and stores one start-state for all built statemachines. **/ //mitk::StateMachineFactory::StartStateMap mitk::StateMachineFactory::m_StartStates; //mitk::StateMachineFactory::AllStateMachineMapType mitk::StateMachineFactory::m_AllStateMachineMap; //std::string mitk::StateMachineFactory::s_LastLoadedBehavior; //XML StateMachine const std::string STYLE = "STYLE"; const std::string NAME = "NAME"; const std::string ID = "ID"; const std::string START_STATE = "START_STATE"; const std::string NEXT_STATE_ID = "NEXT_STATE_ID"; const std::string EVENT_ID = "EVENT_ID"; const std::string SIDE_EFFECT_ID = "SIDE_EFFECT_ID"; const std::string ISTRUE = "TRUE"; const std::string ISFALSE = "FALSE"; const std::string STATE_MACHINE = "stateMachine"; const std::string STATE = "state"; const std::string TRANSITION = "transition"; const std::string STATE_MACHINE_NAME = "stateMachine"; const std::string ACTION = "action"; const std::string BOOL_PARAMETER = "boolParameter"; const std::string INT_PARAMETER = "intParameter"; const std::string FLOAT_PARAMETER = "floatParameter"; const std::string DOUBLE_PARAMETER = "doubleParameter"; const std::string STRING_PARAMETER = "stringParameter"; const std::string VALUE = "VALUE"; #include namespace mitk { vtkStandardNewMacro(StateMachineFactory); } mitk::StateMachineFactory::StateMachineFactory() : m_AktStateMachineName(""), m_SkipStateMachine(false) {} mitk::StateMachineFactory::~StateMachineFactory() { //free memory while (!m_AllStateMachineMap.empty()) { StateMachineMapType* temp = m_AllStateMachineMap.begin()->second; m_AllStateMachineMap.erase(m_AllStateMachineMap.begin()); delete temp; } //should not be necessary due to SmartPointers m_StartStates.clear(); //delete WeakPointer if (m_AktTransition) delete m_AktTransition; } /** * @brief Returns NULL if no entry with string type is found. **/ mitk::State* mitk::StateMachineFactory::GetStartState(const char * type) { StartStateMapIter tempState = m_StartStates.find(type); if( tempState != m_StartStates.end() ) return (tempState)->second.GetPointer(); MITK_ERROR << "Error in StateMachineFactory: StartState for pattern \""<< type<< "\"not found! StateMachine might not work!\n"; return NULL; } /** * @brief Loads the xml file filename and generates the necessary instances. **/ bool mitk::StateMachineFactory::LoadBehavior(std::string fileName) { if ( fileName.empty() ) return false; m_LastLoadedBehavior = fileName; this->SetFileName(fileName.c_str()); return this->Parse(); } /** * @brief Loads the xml string and generates the necessary instances. **/ bool mitk::StateMachineFactory::LoadBehaviorString(std::string xmlString) { if ( xmlString.empty() ) return false; m_LastLoadedBehavior = "String"; return ( this->Parse(xmlString.c_str(), xmlString.length()) ); } bool mitk::StateMachineFactory::LoadStandardBehavior() { std::string xmlFileName( mitk::StandardFileLocations::GetInstance()->FindFile("StateMachine.xml", "Core/Code/Interactions") ); if (!xmlFileName.empty()) return this->LoadBehavior(xmlFileName); else return false; } /** * @brief Recursive method, that parses this brand of * the stateMachine; if the history has the same * size at the end, then the StateMachine is correct **/ bool mitk::StateMachineFactory::RParse(mitk::State::StateMap* states, mitk::State::StateMapIter thisState, HistorySet *history) { history->insert((thisState->second)->GetId());//log our path //or thisState->first. but this seems safer std::set nextStatesSet = (thisState->second)->GetAllNextStates(); //remove loops in nextStatesSet; //nether do we have to go there, nor will it clear a deadlock std::set::iterator position = nextStatesSet.find((thisState->second)->GetId());//look for the same state in nextStateSet if (position != nextStatesSet.end()) {//found the same state we are in! nextStatesSet.erase(position);//delete it, cause, we don't have to go there a second time! } //nextStatesSet is empty, so deadlock! if ( nextStatesSet.empty() ) { MITK_INFO<::iterator i = nextStatesSet.begin(); i != nextStatesSet.end(); i++) { if ( history->find(*i) == history->end() )//if we haven't been in this nextstate { mitk::State::StateMapIter nextState = states->find(*i);//search the iterator for our nextState if (nextState == states->end()) { MITK_INFO<size() > 1)//only one state; don't have to be parsed for deadlocks! { //parse all the given states an check for deadlock or not connected states HistorySet *history = new HistorySet; mitk::State::StateMapIter firstState = states->begin(); //parse through all the given states, log the parsed elements in history bool ok = RParse( states, firstState, history); if ( (states->size() == history->size()) && ok ) { delete history; } else //ether !ok or sizeA!=sizeB { delete history; MITK_INFO<begin(); tempState != states->end(); tempState++) { //searched through the States and Connects all Transitions bool tempbool = ( ( tempState->second )->ConnectTransitions( states ) ); if ( tempbool == false ) { MITK_INFO< ok = m_AllStatesOfOneStateMachine.insert(mitk::State::StateMap::value_type(id , m_AktState)); if ( ok.second == false ) { MITK_INFO<AddTransition( m_AktTransition ); } else if ( name == ACTION ) { int actionId = ReadXMLIntegerAttribut( ID, atts ); m_AktAction = Action::New( actionId ); m_AktTransition->AddAction( m_AktAction ); } else if ( name == BOOL_PARAMETER ) { if ( !m_AktAction ) return; bool value = ReadXMLBooleanAttribut( VALUE, atts ); std::string name = ReadXMLStringAttribut( NAME, atts ); m_AktAction->AddProperty( name.c_str(), BoolProperty::New( value ) ); } else if ( name == INT_PARAMETER ) { if ( !m_AktAction ) return; int value = ReadXMLIntegerAttribut( VALUE, atts ); std::string name = ReadXMLStringAttribut( NAME, atts ); m_AktAction->AddProperty( name.c_str(), IntProperty::New( value ) ); } else if ( name == FLOAT_PARAMETER ) { if ( !m_AktAction ) return; float value = ReadXMLIntegerAttribut( VALUE, atts ); std::string name = ReadXMLStringAttribut( NAME, atts ); m_AktAction->AddProperty( name.c_str(), FloatProperty::New( value ) ); } else if ( name == DOUBLE_PARAMETER ) { if ( !m_AktAction ) return; double value = ReadXMLDoubleAttribut( VALUE, atts ); std::string name = ReadXMLStringAttribut( NAME, atts ); m_AktAction->AddProperty( name.c_str(), DoubleProperty::New( value ) ); } else if ( name == STRING_PARAMETER ) { if ( !m_AktAction ) return; std::string value = ReadXMLStringAttribut( VALUE, atts ); std::string name = ReadXMLStringAttribut( NAME, atts ); m_AktAction->AddProperty( name.c_str(), StringProperty::New( value ) ); } } void mitk::StateMachineFactory::EndElement (const char* elementName) { //bool ok = true; std::string name(elementName); //skip the state machine pattern because the name was not unique! if (m_SkipStateMachine && (name != STATE_MACHINE) ) return; if ( name == STATE_MACHINE_NAME ) { if (m_SkipStateMachine) { m_SkipStateMachine = false; return; } /*ok =*/ ConnectStates(&m_AllStatesOfOneStateMachine); m_AllStatesOfOneStateMachine.clear(); } else if ( name == STATE_MACHINE ) { //doesn't have to be done } else if ( name == TRANSITION ) { m_AktTransition = NULL; //pointer stored in its state. memory will be freed in destructor of class state } else if ( name == ACTION ) { m_AktAction = NULL; } else if ( name == STATE ) { m_AktState = NULL; } } std::string mitk::StateMachineFactory::ReadXMLStringAttribut( std::string name, const char** atts ) { if(atts) { const char** attsIter = atts; while(*attsIter) { if ( name == *attsIter ) { attsIter++; return *attsIter; } attsIter++; attsIter++; } } return std::string(); } int mitk::StateMachineFactory::ReadXMLIntegerAttribut( std::string name, const char** atts ) { std::string s = ReadXMLStringAttribut( name, atts ); return atoi( s.c_str() ); } float mitk::StateMachineFactory::ReadXMLFloatAttribut( std::string name, const char** atts ) { std::string s = ReadXMLStringAttribut( name, atts ); return (float) atof( s.c_str() ); } double mitk::StateMachineFactory::ReadXMLDoubleAttribut( std::string name, const char** atts ) { std::string s = ReadXMLStringAttribut( name, atts ); return atof( s.c_str() ); } bool mitk::StateMachineFactory::ReadXMLBooleanAttribut( std::string name, const char** atts ) { std::string s = ReadXMLStringAttribut( name, atts ); if ( s == ISTRUE ) return true; else return false; } mitk::State* mitk::StateMachineFactory::GetState( const char * type, int StateId ) { //check if the state exists AllStateMachineMapType::iterator i = m_AllStateMachineMap.find( type ); if ( i == m_AllStateMachineMap.end() ) return false; //get the statemachine of the state StateMachineMapType* sm = m_AllStateMachineMap[type]; //get the state from its statemachine if ( sm != NULL ) return (*sm)[StateId].GetPointer(); else return NULL; } bool mitk::StateMachineFactory::AddStateMachinePattern(const char * type, mitk::State* startState, mitk::StateMachineFactory::StateMachineMapType* allStatesOfStateMachine) { if (startState == NULL || allStatesOfStateMachine == NULL) return false; //check if the pattern has already been added StartStateMapIter tempState = m_StartStates.find(type); if( tempState != m_StartStates.end() ) { - STATEMACHINE_WARN << "Pattern " << type << " has already been added!\n"; + MITK_WARN << "Pattern " << type << " has already been added!\n"; return false; } //add the start state m_StartStates.insert(StartStateMap::value_type(type, startState)); //add all states of the new pattern to hold their references m_AllStateMachineMap.insert(AllStateMachineMapType::value_type(type, allStatesOfStateMachine)); return true; }