Program Listing for File vtkMRMLLayerDMWidgetEventTranslationNode.cxx

Return to documentation for file (MRML/vtkMRMLLayerDMWidgetEventTranslationNode.cxx)

#include "vtkMRMLLayerDMWidgetEventTranslationNode.h"

// Slicer includes
#include <vtkMRMLAbstractWidget.h>
#include <vtkMRMLInteractionEventData.h>

// VTK includes
#include <vtkObjectFactory.h>

// STL includes
#include <functional>

vtkMRMLNodeNewMacro(vtkMRMLLayerDMWidgetEventTranslationNode);

void vtkMRMLLayerDMWidgetEventTranslationNode::PrintSelf(ostream& os, vtkIndent indent)
{
  Superclass::PrintSelf(os, indent);
  os << indent << "EventMap:" << std::endl;
  const auto& nextIndent = indent.GetNextIndent();
  os << ToString(this->EventMap, &nextIndent, "\n");
}

void vtkMRMLLayerDMWidgetEventTranslationNode::Copy(vtkMRMLNode* node)
{
  const auto other = vtkMRMLLayerDMWidgetEventTranslationNode::SafeDownCast(node);
  if (!other)
  {
    return;
  }
  this->EventMap = other->EventMap;
  this->Modified();
}

void vtkMRMLLayerDMWidgetEventTranslationNode::ReadXMLAttributes(const char** atts)
{
  MRMLNodeModifyBlocker blocker(this);
  Superclass::ReadXMLAttributes(atts);
  vtkMRMLReadXMLBeginMacro(atts);
  if (!strcmp(xmlReadAttName, "eventMap"))
  {
    this->EventMap = EventMapFromString(xmlReadAttValue);
    this->Modified();
  }
  vtkMRMLReadXMLEndMacro();
}

void vtkMRMLLayerDMWidgetEventTranslationNode::WriteXML(ostream& of, int indent)
{
  Superclass::WriteXML(of, indent);
  vtkMRMLWriteXMLBeginMacro(of);
  of << " eventMap=\"" << XMLAttributeEncodeString(ToString(this->EventMap, nullptr, "")) << "\"";
  vtkMRMLWriteXMLEndMacro();
}

const char* vtkMRMLLayerDMWidgetEventTranslationNode::GetNodeTagName()
{
  return "WidgetEventTranslationNode";
}

void vtkMRMLLayerDMWidgetEventTranslationNode::SetTranslation(int widgetState, unsigned long interactionEvent, unsigned long widgetEvent, int modifier)
{
  this->SetTranslation(EventKey{ widgetState, interactionEvent, modifier }, widgetEvent);
}

unsigned long vtkMRMLLayerDMWidgetEventTranslationNode::GetEndInteractionEvent(unsigned long startInteractionEvent)
{
  std::map<unsigned long, unsigned long> endInteractionMap{
    { vtkCommand::LeftButtonPressEvent, vtkCommand::LeftButtonReleaseEvent },
    { vtkCommand::MiddleButtonPressEvent, vtkCommand::MiddleButtonReleaseEvent },
    { vtkCommand::RightButtonPressEvent, vtkCommand::RightButtonReleaseEvent },
  };

  const auto it = endInteractionMap.find(startInteractionEvent);
  return it == endInteractionMap.end() ? vtkMRMLAbstractWidget::WidgetEventNone : it->second;
}

unsigned long vtkMRMLLayerDMWidgetEventTranslationNode::GetClickEvent(unsigned long releaseEvent)
{
  std::map<unsigned long, unsigned long> clickMap{
    { vtkCommand::LeftButtonReleaseEvent, vtkMRMLInteractionEventData::LeftButtonClickEvent },
    { vtkCommand::MiddleButtonReleaseEvent, vtkMRMLInteractionEventData::MiddleButtonClickEvent },
    { vtkCommand::RightButtonReleaseEvent, vtkMRMLInteractionEventData::RightButtonClickEvent },
  };

  const auto it = clickMap.find(releaseEvent);
  return it == clickMap.end() ? vtkMRMLAbstractWidget::WidgetEventNone : it->second;
}

void vtkMRMLLayerDMWidgetEventTranslationNode::SetTranslationClickAndDrag(int widgetState,
                                                                          unsigned long interactionEvent,
                                                                          int widgetStateDragging,
                                                                          unsigned long widgetStartEvent,
                                                                          unsigned long widgetEndEvent,
                                                                          int modifiers)
{
  const auto endInteractionEvent = GetEndInteractionEvent(interactionEvent);
  this->SetTranslation(widgetState, interactionEvent, widgetStartEvent, modifiers);
  this->SetTranslation(widgetStateDragging, vtkCommand::MouseMoveEvent, vtkMRMLAbstractWidget::WidgetEventMouseMove);
  this->SetTranslation(widgetStateDragging, endInteractionEvent, widgetEndEvent);
}

void vtkMRMLLayerDMWidgetEventTranslationNode::SetTranslationKeyboard(int widgetState,
                                                                      const std::string& keySym,
                                                                      unsigned long widgetEvent,
                                                                      int modifier,
                                                                      int repeatCount,
                                                                      unsigned long keyEvent)
{
  this->SetTranslation(EventKey{ widgetState, keyEvent, modifier, EventKey::thresholdRepeatCount(repeatCount), keySym }, widgetEvent);
}

int vtkMRMLLayerDMWidgetEventTranslationNode::RemoveTranslationEvent(unsigned long widgetEvent)
{
  int erasedCount{};
  for (auto it = this->EventMap.begin(); it != this->EventMap.end();)
  {
    if (it->second == widgetEvent)
    {
      it = this->EventMap.erase(it);
      erasedCount++;
    }
    else
    {
      ++it;
    }
  }

  if (erasedCount > 0)
  {
    this->Modified();
  }
  return erasedCount;
}

void vtkMRMLLayerDMWidgetEventTranslationNode::SetTranslation(const EventKey& key, unsigned long widgetEvent)
{
  this->EventMap[key] = widgetEvent;
  this->Modified();
}

bool vtkMRMLLayerDMWidgetEventTranslationNode::BlockTranslationEvent(unsigned long widgetEvent, bool isBlocked)
{
  const auto wasBlocked = this->IsWidgetEventBlocked(widgetEvent);
  if (isBlocked)
  {
    this->BlockedEvents.emplace(widgetEvent);
  }
  else
  {
    this->BlockedEvents.erase(widgetEvent);
  }
  return wasBlocked;
}

bool vtkMRMLLayerDMWidgetEventTranslationNode::BlockAllTranslationEvents(bool isBlocked)
{
  const auto wasBlocked = this->IsBlocked;
  this->IsBlocked = isBlocked;
  return wasBlocked;
}

unsigned long vtkMRMLLayerDMWidgetEventTranslationNode::Translate(int widgetState, vtkMRMLInteractionEventData* eventData)
{
  if (!eventData)
  {
    return vtkMRMLAbstractWidget::WidgetEventNone;
  }

  const auto eventId = static_cast<unsigned long>(eventData->GetType());
  EventKey key{ widgetState, eventId, eventData->GetModifiers() };

  if (eventId == vtkCommand::KeyPressEvent)
  {
    key.repeatCount = EventKey::thresholdRepeatCount(eventData->GetKeyRepeatCount());
    key.keySym = eventData->GetKeySym();
  }

  return this->Translate(key);
}

void vtkMRMLLayerDMWidgetEventTranslationNode::Clear()
{
  this->EventMap.clear();
  this->Modified();
}

int vtkMRMLLayerDMWidgetEventTranslationNode::GetNumberOfTranslations() const
{
  return static_cast<int>(this->EventMap.size());
}

unsigned long vtkMRMLLayerDMWidgetEventTranslationNode::Translate(EventKey key) const
{
  // Early return if the translation node is blocked
  if (this->IsBlocked)
  {
    return vtkMRMLAbstractWidget::WidgetEventNone;
  }

  // Define relaxations strategies for the key matching
  const auto noRelaxation = [](EventKey k) { return k; };
  const auto relaxModifier = [](EventKey k)
  {
    k.modifier = (k.modifier == vtkEvent::AnyModifier) ? vtkEvent::NoModifier : k.modifier;
    return k;
  };
  const auto relaxState = [](EventKey k)
  {
    k.widgetState = vtkMRMLAbstractWidget::WidgetStateAny;
    return k;
  };
  const auto relaxClick = [](EventKey k)
  {
    const auto clickEvent = GetClickEvent(k.eventId);
    k.eventId = clickEvent != vtkMRMLAbstractWidget::WidgetEventNone ? clickEvent : k.eventId;
    return k;
  };

  // Iterate on the different relaxation combinations and return the first match
  const std::vector<std::function<EventKey(EventKey)>> relaxations{ noRelaxation, relaxModifier, relaxState, relaxClick };
  for (int i = 0; i < relaxations.size(); i++)
  {
    key = relaxations[i](key);
    for (int j = i; j < relaxations.size(); j++)
    {
      if (const auto widgetEvent = this->GetWidgetEvent(relaxations[j](key)); widgetEvent != vtkMRMLAbstractWidget::WidgetEventNone)
      {
        return widgetEvent;
      }
    }
  }
  return vtkMRMLAbstractWidget::WidgetEventNone;
}

bool vtkMRMLLayerDMWidgetEventTranslationNode::IsWidgetEventBlocked(unsigned long widgetEvent) const
{
  return this->BlockedEvents.find(widgetEvent) != std::end(this->BlockedEvents);
}

unsigned long vtkMRMLLayerDMWidgetEventTranslationNode::GetWidgetEvent(const EventKey& key) const
{
  const auto it = this->EventMap.find(key);
  const auto widgetEvent = (it == this->EventMap.end()) ? vtkMRMLAbstractWidget::WidgetEventNone : it->second;
  return this->IsWidgetEventBlocked(widgetEvent) ? vtkMRMLAbstractWidget::WidgetEventNone : widgetEvent;
}

std::string vtkMRMLLayerDMWidgetEventTranslationNode::ToString(const std::pair<EventKey, unsigned long>& eventPair)
{
  const auto& [key, widgetEvent] = eventPair;
  std::stringstream ss;
  ss << "widgetState=" << key.widgetState << ",";
  ss << "eventId=" << key.eventId << ",";
  ss << "modifier=" << key.modifier << ",";
  ss << "repeatCount=" << key.repeatCount << ",";
  ss << "keySym=" << key.keySym << ",";
  ss << "widgetEvent=" << widgetEvent;
  return ss.str();
}

std::string vtkMRMLLayerDMWidgetEventTranslationNode::ToString(const std::map<EventKey, unsigned long>& eventMap, const vtkIndent* indent, const std::string& eol)
{
  std::stringstream ss;
  for (const auto& pair : eventMap)
  {
    if (indent)
    {
      ss << *indent;
    }

    ss << ToString(pair) << ";" << eol;
  }
  return ss.str();
}

std::map<vtkMRMLLayerDMWidgetEventTranslationNode::EventKey, unsigned long> vtkMRMLLayerDMWidgetEventTranslationNode::EventMapFromString(const std::string& value)
{
  std::stringstream ss(value);
  std::string token;
  constexpr auto delim = ';';

  std::map<EventKey, unsigned long> eventMap;
  while (std::getline(ss, token, delim))
  {
    const auto [key, widgetEvent] = EventPairFromString(token);
    eventMap[key] = widgetEvent;
  }
  return eventMap;
}

std::pair<vtkMRMLLayerDMWidgetEventTranslationNode::EventKey, unsigned long> vtkMRMLLayerDMWidgetEventTranslationNode::EventPairFromString(const std::string& value)
{
  // Helper function to trim the input string
  auto trim = [](std::string& s)
  {
    constexpr auto ws = " \t\n\r";
    const auto start = s.find_first_not_of(ws);
    if (start == std::string::npos)
    {
      s.clear();
      return;
    }

    const auto end = s.find_last_not_of(ws);
    s.erase(end + 1);
    s.erase(0, start);
  };

  // If input key name matches expected name, call the input assign function.
  // Swallows any exception raised during the assign function execution (for instance runtime errors).
  auto tryRead = [](const std::string& keyName, const std::string& name, const std::function<void()>& assignF)
  {
    if (keyName != name)
    {
      return;
    }
    try
    {
      assignF();
    }
    catch (...)
    {
    }
  };

  std::stringstream ss(value);
  std::string token;
  constexpr auto delim = ',';
  EventKey key;
  unsigned long widgetEvent = vtkMRMLAbstractWidget::WidgetEventNone;
  while (std::getline(ss, token, delim))
  {
    const auto pos = token.find('=');
    if (pos == std::string::npos)
    {
      continue;
    }

    std::string keyName = token.substr(0, pos);
    std::string val = token.substr(pos + 1);
    trim(keyName);
    trim(val);

    tryRead(keyName, "widgetState", [val, &key] { key.widgetState = std::stoi(val); });
    tryRead(keyName, "eventId", [val, &key] { key.eventId = std::stoul(val); });
    tryRead(keyName, "modifier", [val, &key] { key.modifier = std::stoi(val); });
    tryRead(keyName, "repeatCount", [val, &key] { key.repeatCount = EventKey::thresholdRepeatCount(std::stoi(val)); });
    tryRead(keyName, "keySym", [val, &key] { key.keySym = val; });
    tryRead(keyName, "widgetEvent", [val, &widgetEvent] { widgetEvent = std::stoul(val); });
  }
  return { key, widgetEvent };
}