views:

223

answers:

4

In my Qt application in the event handler for mouse press events I have such ugly code

void Render::Viewer::mousePressEvent(QMouseEvent* e)
{
  switch (e->button())
  {
  case Qt::LeftButton:
    switch (mode_)
    {
    case Render::Viewer::ModeView:
      switch (e->modifiers())
      {
      case Qt::NoModifier:
        ...
        break;
      ...
      default:
        break;
      }
      break;
    case Render::Viewer::ModeEdit:
      ...
      break;
    }
    break;
  case Qt::RightButton:
    ...
    break;
  }
}

Even without switching on mode_ variable the code looks terrible. =( Two many degrees of freedom: button type, modifiers, ... Absolutely unreadable.

Is there any ways to overcome such a "heap of switches"?

+2  A: 

It would be easier to read and maintain if you broke up tasks into their own functions:

void Render::Viewer::mousePressEvent(QMouseEvent* e) 
{ 
  switch (e->button()) 
  { 
  case Qt::LeftButton: 
    handleLeftButton(e);
    break;
  case Qt::RightButton: 
    handleRightButton(e);
    break; 
  } 
}

void Render::Viewer::handleLeftButton(QMouseEvent* e)
{
    switch (mode_) 
    { 
    case Render::Viewer::ModeView: 
      switch (e->modifiers()) 
      { 
      case Qt::NoModifier: 
        ... 
        break; 
      ... 
      default: 
        break; 
      } 
      break; 
    case Render::Viewer::ModeEdit: 
      ... 
      break; 
    } 
}

void Render::Viewer::handleRightButton(QMouseEvent* e)
{
  ...
}

Break it up into however many functions you need to make it readable.

Bill
Thanks, Bill. The answer is obvious. =) "Divide and conquer". How could I forget this principle?!
kemiisto
+4  A: 

One alternative approach is to use Qt's new State Machine Framework. I haven't used it myself, but from what I've read it's designed to replace your pile of state variables and switch statements with a simpler, more formal representation of the widget's behavior.

Jeremy Friesner
Neat, thanks for the link!
Bill
+4  A: 

Observe that nested switches can be reversed: inner switches can be lifted to the outside and vice versa. This way, you can lift the switch on mode_ to the outer level.

A possible solution is then to create an interface, say Mode, that handles events for a particular mode:

class Mode {
  public:
    virtual void mousePressEvent(QMouseEvent *e) = 0;
    // ... and so on for other events
};

Concrete implementations like ModeView and ModeEdit can then handle the events. If you do not want to handle all events in all cases, give this interface empty implementations instead of pure virtual functions. If there is shared functionality between particular modes, you can even create an intermediary class that those mode classes inherit from.

Let _mode be a pointer to a Mode to represent the current mode, and then your "master" handler becomes:

void Render::Viewer::mousePressEvent(QMouseEvent* e) {
  _mode->mousePressEvent(e);
}
Thomas
Thanks, Thomas. If i understand correctly this is a state design pattern. Isn't it?
kemiisto
Could be. \*googles...\* Yep, it is.
Thomas
+1  A: 

You could move some of your switch statements to functions

Nick