
/* 
   A* -------------------------------------------------------------------
   B* This file contains source code for the PyMOL computer program
   C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific. 
   D* -------------------------------------------------------------------
   E* It is unlawful to modify or remove this copyright notice.
   F* -------------------------------------------------------------------
   G* Please see the accompanying LICENSE file for further information. 
   H* -------------------------------------------------------------------
   I* Additional authors of this source file include:
   -* 
   -* 
   -*
   Z* -------------------------------------------------------------------
*/
#include"os_python.h"

#include"os_predef.h"
#include"os_std.h"
#include"os_gl.h"
#include"MemoryDebug.h"
#include "main.h"
#include "Base.h"
#include "ButMode.h"
#include "Scene.h"
#include "Util.h"
#include "Ortho.h"
#include "Setting.h"
#include "P.h"
#include "Text.h"
#include "Menu.h"
#include "CGO.h"
#include "Movie.h"

#define cButModeLineHeight DIP2PIXEL(12)
#define cButModeLeftMargin DIP2PIXEL(2)
#define cButModeTopMargin DIP2PIXEL(1)
#define cButModeBottomMargin DIP2PIXEL(2)

struct CButMode : public Block {
  CodeType Code[cButModeCount + 1] {};
  int NCode {};
  int Mode[cButModeInputCount] {};
  int NBut {};
  float Rate { 0.0f };
  float RateShown { 0.0f };
  float Samples { 0.0f }, Delay { 0.0f };
  float TextColor1[3] = { 0.5f, 0.5f, 1.0f };
  float TextColor2[3] = { 0.8f, 0.8f, 0.8f };
  float TextColor3[3] = { 1.0f, 0.5f, 0.5f };
  int DeferCnt { 0 };
  float DeferTime { 0.0f };

  CButMode(PyMOLGlobals * G) : Block(G) {}

  int click(int button, int x, int y, int mod) override;
  void draw(CGO *orthoCGO) override;
  bool fastDraw(CGO* orthoCGO) override;
};


/*========================================================================*/
Block *ButModeGetBlock(PyMOLGlobals * G)
{
  CButMode *I = G->ButMode;
  {
    return (I);
  }
}

int ButModeGetHeight(PyMOLGlobals * G)
{
  if(SettingGetGlobal_b(G, cSetting_mouse_grid))
    return DIP2PIXEL(124);
  else
    return DIP2PIXEL(40);
}


/*========================================================================*/
int ButModeGet(PyMOLGlobals * G, int button)
{
  CButMode *I = G->ButMode;
  if((button >= 0) && (button < I->NBut)) {
    return I->Mode[button];
  }
  return 0;
}

void ButModeSet(PyMOLGlobals * G, int button, int action)
{
  CButMode *I = G->ButMode;
  if((button >= 0) && (button < I->NBut) && (action >= 0) && (action < I->NCode)) {
    I->Mode[button] = action;
    OrthoDirty(G);
  }
}


/*========================================================================*/
void ButModeSetRate(PyMOLGlobals * G, float interval)
{
  CButMode *I = G->ButMode;

  if(interval >= 0.001F) {      /* sub-millisecond, defer... */
    if(I->DeferCnt) {
      interval = (interval + I->DeferTime) / (I->DeferCnt + 1);
      I->DeferCnt = 0;
      I->DeferTime = 0.0F;
    }
    I->Delay -= interval;
    if(interval < 1.0F) {
      I->Samples *= 0.95 * (1.0F - interval);
      I->Rate *= 0.95 * (1.0F - interval);
    } else {
      I->Samples = 0.0F;
      I->Rate = 0.0F;
    }

    I->Samples++;
    I->Rate += 1.0F / interval;
  } else {
    I->DeferCnt++;
    I->DeferTime += interval;
  }
}


/*========================================================================*/
void ButModeResetRate(PyMOLGlobals * G)
{
  CButMode *I = G->ButMode;
  I->Samples = 0.0;
  I->Rate = 0.0;
  I->RateShown = 0;
  I->Delay = 0;
}


/*========================================================================*/
void ButModeFree(PyMOLGlobals * G)
{
  DeleteP(G->ButMode);
}


/*========================================================================*/
int CButMode::click(int button, int x, int y, int mod)
{
  int dy = (y - rect.bottom) / cButModeLineHeight;
  //  int dx = (x - block->rect.left);
  int forward = 1; 
  // TAKEN OUT : BB 12/11 : Mouse position should not have
  // an effect on whether the mouse ring goes forwards or 
  // backwards.
  // forward = (dx > ((block->rect.right - block->rect.left) / 2));
  // 
  /*  register CButMode *I=block->G->ButMode; */
  if(button == P_GLUT_BUTTON_SCROLL_BACKWARD || button == P_GLUT_RIGHT_BUTTON)
    forward = !forward;
  if(mod == cOrthoSHIFT)
    forward = !forward;
  if(dy < 2) {
    if(ButModeTranslate(m_G, P_GLUT_SINGLE_LEFT, 0) != cButModePickAtom) {
      if(!forward) {
        PLog(m_G, "cmd.mouse('select_backward')", cPLog_pym);
        OrthoCommandIn(m_G, "mouse select_backward,quiet=1");
      } else {
        PLog(m_G, "cmd.mouse('select_forward')", cPLog_pym);
        OrthoCommandIn(m_G, "mouse select_forward,quiet=1");
      }
    }
  } else {
    if(button == P_GLUT_RIGHT_BUTTON) {
      MenuActivate0Arg(m_G,x,y,x,y,false,"mouse_config");
    } else {
      if(!forward) {
        PLog(m_G, "cmd.mouse('backward')", cPLog_pym);
        OrthoCommandIn(m_G, "mouse backward,quiet=1");
      } else {
        PLog(m_G, "cmd.mouse('forward')", cPLog_pym);
        OrthoCommandIn(m_G, "mouse forward,quiet=1");
      }
    }
  }
  return (1);
}

static bool ButModeDrawFastImpl(Block * block, short definitely ORTHOCGOARG);
/*========================================================================*/
void CButMode::draw(CGO* orthoCGO)
{
  int x, y, a;
  int mode;
  const float *textColor = TextColor;
  const float *textColor2 = TextColor2;
  CButMode *I = this; // TODO: Remove during ButMode refactor
#define BLANK_STR "     "

  if(m_G->HaveGUI && m_G->ValidContext && ((rect.right - rect.left) > 6)) {
    if(SettingGetGlobal_b(m_G, cSetting_internal_gui_mode) == 0) {
      if (orthoCGO)
	CGOColorv(orthoCGO, BackColor);
#ifndef PURE_OPENGL_ES_2
      else
	glColor3fv(BackColor);
#endif
      fill(orthoCGO);
      drawLeftEdge(orthoCGO);
    } else {
      drawLeftEdge(orthoCGO);
      if (orthoCGO)
	CGOColor(orthoCGO, .5f, .5f, .5f);
#ifndef PURE_OPENGL_ES_2
      else
	glColor3f(0.5, 0.5, 0.5);
#endif
      drawTopEdge();
      textColor2 = OrthoGetOverlayColor(m_G);
      textColor = textColor2;
    }

    x = rect.left + cButModeLeftMargin;
    y = (rect.top - cButModeLineHeight) - cButModeTopMargin;

    TextSetColor(m_G, textColor);
    TextDrawStrAt(m_G, "Mouse Mode ", x + 1, y ORTHOCGOARGVAR);
    TextSetColor(m_G, TextColor3);
    TextDrawStrAt(m_G, SettingGetGlobal_s(m_G, cSetting_button_mode_name), x + DIP2PIXEL(88), y ORTHOCGOARGVAR);
    /*    TextDrawStrAt(m_G,"2-Bttn Selecting",x+88,y); */
    y -= cButModeLineHeight;

    if(SettingGetGlobal_b(m_G, cSetting_mouse_grid)) {

      TextSetColor(m_G, TextColor3);
      TextDrawStrAt(m_G, "Buttons", x + DIP2PIXEL(6), y ORTHOCGOARGVAR);
      TextSetColor(m_G, TextColor1);
      /*    TextDrawStrAt(m_G,"  Left Mddl Rght Scrl",x+48,y); */
      TextDrawStrAt(m_G, "    L    M    R  Wheel", x + DIP2PIXEL(43), y ORTHOCGOARGVAR);

      y -= cButModeLineHeight;
      /*    glColor3fv(I->Block->TextColor);
         TextDrawStrAt(m_G,"K",x,y-4); */
      TextSetColor(m_G, TextColor3);
      TextDrawStrAt(m_G, "&", x + DIP2PIXEL(12), y ORTHOCGOARGVAR);
      TextDrawStrAt(m_G, "Keys", x + DIP2PIXEL(24), y ORTHOCGOARGVAR);
      TextSetColor(m_G, textColor2);

      TextSetPos2i(m_G, x + DIP2PIXEL(64), y);
      for(a = 0; a < 3; a++) {
        mode = Mode[a];
        if(mode < 0)
          TextDrawStr(m_G, BLANK_STR ORTHOCGOARGVAR);
        else
          TextDrawStr(m_G, Code[mode] ORTHOCGOARGVAR);
      }
      mode = Mode[12];
      if(mode < 0)
        TextDrawStr(m_G, BLANK_STR ORTHOCGOARGVAR);
      else
        TextDrawStr(m_G, Code[mode] ORTHOCGOARGVAR);

      y -= cButModeLineHeight;
      /*    TextSetColor(m_G,I->Block->TextColor);
         TextDrawStrAt(m_G,"e",x+5,y-1); */
      TextSetColor(m_G, TextColor1);

      TextSetColor(m_G, TextColor1);
      TextDrawStrAt(m_G, "Shft ", x + DIP2PIXEL(24), y ORTHOCGOARGVAR);
      TextSetColor(m_G, textColor2);
      TextSetPos2i(m_G, x + DIP2PIXEL(64), y);
      for(a = 3; a < 6; a++) {
        mode = I->Mode[a];
        if(mode < 0)
          TextDrawStr(m_G, BLANK_STR ORTHOCGOARGVAR);
        else
          TextDrawStr(m_G, Code[mode] ORTHOCGOARGVAR);
      }
      mode = Mode[13];
      if(mode < 0)
        TextDrawStr(m_G, BLANK_STR ORTHOCGOARGVAR);
      else
        TextDrawStr(m_G, Code[mode] ORTHOCGOARGVAR);

      y -= cButModeLineHeight;
      /*    glColor3fv(I->Block->TextColor);
         TextDrawStrAt(m_G,"y",x+10,y+2); */
      TextSetColor(m_G, TextColor1);
      TextDrawStrAt(m_G, "Ctrl ", x + DIP2PIXEL(24), y ORTHOCGOARGVAR);
      TextSetColor(m_G, textColor2);
      TextSetPos2i(m_G, x + DIP2PIXEL(64), y);
      for(a = 6; a < 9; a++) {
        mode = I->Mode[a];
        if(mode < 0)
          TextDrawStr(m_G, BLANK_STR ORTHOCGOARGVAR);
        else
          TextDrawStr(m_G, Code[mode] ORTHOCGOARGVAR);
      }
      mode = I->Mode[14];
      if(mode < 0)
        TextDrawStr(m_G, BLANK_STR ORTHOCGOARGVAR);
      else
        TextDrawStr(m_G, Code[mode] ORTHOCGOARGVAR);
      y -= cButModeLineHeight;

      /*    glColor3fv(I->Block->TextColor);
         TextDrawStrAt(m_G,"s",x+15,y+3); */
      TextSetColor(m_G, TextColor1);
      TextSetColor(m_G, TextColor1);
      TextDrawStrAt(m_G, "CtSh ", x + DIP2PIXEL(24), y ORTHOCGOARGVAR);
      TextSetColor(m_G, textColor2);
      TextSetPos2i(m_G, x + DIP2PIXEL(64), y);
      for(a = 9; a < 12; a++) {
        mode = Mode[a];
        if(mode < 0)
          TextDrawStr(m_G, BLANK_STR ORTHOCGOARGVAR);
        else
          TextDrawStr(m_G, Code[mode] ORTHOCGOARGVAR);
      }
      mode = Mode[15];
      if(mode < 0)
        TextDrawStr(m_G, BLANK_STR ORTHOCGOARGVAR);
      else
        TextDrawStr(m_G, Code[mode] ORTHOCGOARGVAR);

      y -= cButModeLineHeight;

      TextSetColor(m_G, TextColor);
      TextSetColor(m_G, TextColor1);
      TextDrawStrAt(m_G, " SnglClk", x - DIP2PIXEL(8), y ORTHOCGOARGVAR);
      TextSetColor(m_G, textColor2);
      TextSetPos2i(m_G, x + DIP2PIXEL(64), y);
      for(a = 19; a < 22; a++) {
        mode = Mode[a];
        if(mode < 0)
          TextDrawStr(m_G, BLANK_STR ORTHOCGOARGVAR);
        else
          TextDrawStr(m_G, Code[mode] ORTHOCGOARGVAR);
      }
      TextSetColor(m_G, TextColor);
      y -= cButModeLineHeight;

      TextSetColor(m_G, TextColor);
      TextSetColor(m_G, TextColor1);
      TextDrawStrAt(m_G, " DblClk", x, y ORTHOCGOARGVAR);
      TextSetColor(m_G, textColor2);
      TextSetPos2i(m_G, x + DIP2PIXEL(64), y);
      for(a = 16; a < 19; a++) {
        mode = I->Mode[a];
        if(mode < 0)
          TextDrawStr(m_G, BLANK_STR ORTHOCGOARGVAR);
        else
          TextDrawStr(m_G, Code[mode] ORTHOCGOARGVAR);
      }
      TextSetColor(m_G, TextColor);
      y -= cButModeLineHeight;

    }

    {
      TextSetColor(m_G, textColor);
      if(ButModeTranslate(m_G, P_GLUT_SINGLE_LEFT, 0) == cButModePickAtom) {
        TextDrawStrAt(m_G, "Picking ", x, y ORTHOCGOARGVAR);
        TextSetColor(m_G, TextColor3);
        TextDrawStrAt(m_G, "Atoms (and Joints)", x + DIP2PIXEL(64), y ORTHOCGOARGVAR);
      } else {
        TextDrawStrAt(m_G, "Selecting ", x, y ORTHOCGOARGVAR);
        TextSetColor(m_G, TextColor3);
        switch (SettingGetGlobal_i(m_G, cSetting_mouse_selection_mode)) {
        case 0:
          TextDrawStrAt(m_G, "Atoms", x + DIP2PIXEL(80), y ORTHOCGOARGVAR);
          break;
        case 1:
          TextDrawStrAt(m_G, "Residues", x + DIP2PIXEL(80), y ORTHOCGOARGVAR);
          break;
        case 2:
          TextDrawStrAt(m_G, "Chains", x + DIP2PIXEL(80), y ORTHOCGOARGVAR);
          break;
        case 3:
          TextDrawStrAt(m_G, "Segments", x + DIP2PIXEL(80), y ORTHOCGOARGVAR);
          break;
        case 4:
          TextDrawStrAt(m_G, "Objects", x + DIP2PIXEL(80), y ORTHOCGOARGVAR);
          break;
        case 5:
          TextDrawStrAt(m_G, "Molecules", x + DIP2PIXEL(80), y ORTHOCGOARGVAR);
          break;
        case 6:
          TextDrawStrAt(m_G, "C-alphas", x + DIP2PIXEL(80), y ORTHOCGOARGVAR);
          break;
        }
      }
    }
  }
  if (!ORTHOCGOARGB || !(SettingGetGlobal_b(m_G, cSetting_show_frame_rate) || MoviePlaying(m_G))) {
    ButModeDrawFastImpl(this, true ORTHOCGOARGVAR);
  }
}

bool CButMode::fastDraw(CGO* orthoCGO){
  return ButModeDrawFastImpl(this, false, orthoCGO);
}

static bool ButModeDrawFastImpl(Block * block, short definitely ORTHOCGOARG)
{
  PyMOLGlobals *G = block->m_G;
  CButMode *I = block->m_G->ButMode;
  int x, y;
  float *textColor = I->TextColor;
  float *textColor2 = I->TextColor2;

  if (!definitely && (!(SettingGetGlobal_b(G, cSetting_show_frame_rate) || MoviePlaying(G)))) {
    return false;
  }

  x = I->rect.left + cButModeLeftMargin;
  y = I->rect.bottom + cButModeLineHeight + cButModeBottomMargin;
  
  TextSetColor(G, I->TextColor);
  y -= cButModeLineHeight;
#ifndef PURE_OPENGL_ES_2
    {
        int buffer;
        /* TODO : Why do we only do this for the back right buffer,
         for performance? */
        glGetIntegerv(GL_DRAW_BUFFER, (GLint *) & buffer);
        if(buffer != GL_BACK_RIGHT) {
#else
    {
        {
#endif
            if(I->Delay <= 0.0F) {
                if(I->Samples > 0.0F)
                    I->RateShown = (I->Rate / I->Samples);
                else
                    I->RateShown = 0.0F;
                I->Delay = 0.2F;
            }
        }
    }

  {
    int has_movie = false;
    int frame_rate = SettingGetGlobal_b(G, cSetting_show_frame_rate);
    int nf;
    char rateStr[255];
    nf = SceneGetNFrame(G, &has_movie);
    if(nf == 0)
      nf = 1;
    TextSetColor(G, textColor);
    if(has_movie) {
      TextDrawStrAt(G, "Frame ", x, y ORTHOCGOARGVAR);
    } else {
      TextDrawStrAt(G, "State ", x, y ORTHOCGOARGVAR);
    }
    TextSetColor(G, textColor2);
    sprintf(rateStr, "%4d/%4d ", SceneGetFrame(G) + 1, nf);
    TextDrawStrAt(G, rateStr, x + DIP2PIXEL(48), y ORTHOCGOARGVAR);
    if(frame_rate) {
      sprintf(rateStr,"%5.1f",I->RateShown);
      TextDrawStrAt(G, rateStr, x + DIP2PIXEL(144), y ORTHOCGOARGVAR);
      TextSetColor(G, textColor);
      TextDrawStrAt(G, "Hz ", x + DIP2PIXEL(192), y ORTHOCGOARGVAR);
      TextSetColor(G, textColor2);
    } else if(has_movie) {
      TextSetColor(G, textColor);
      TextDrawStrAt(G, "State ", x + DIP2PIXEL(128), y ORTHOCGOARGVAR);
      TextSetColor(G, textColor2);
      sprintf(rateStr," %4d",SceneGetState(G)+1);
      TextDrawStrAt(G, rateStr, x + DIP2PIXEL(168), y ORTHOCGOARGVAR);
    } else if(frame_rate) {
    }
  }
  return true;
}


/*========================================================================*/
int ButModeInit(PyMOLGlobals * G)
{
  CButMode *I = NULL;
  if((I = (G->ButMode = new CButMode(G)))) {

    int a;

    I->Rate = 0.0;
    I->Samples = 0.0;
    I->RateShown = 0.0;
    I->Delay = 0.0;
    I->DeferCnt = 0;
    I->DeferTime = 0.0F;
    I->NCode = cButModeCount;
    I->NBut = cButModeInputCount;

    for(a = 0; a < I->NBut; a++) {
      I->Mode[a] = -1;
    }

    strcpy(I->Code[cButModeRotXYZ], "Rota ");
    strcpy(I->Code[cButModeRotZ], "RotZ ");
    strcpy(I->Code[cButModeTransXY], "Move ");
    strcpy(I->Code[cButModeTransZ], "MovZ ");
    strcpy(I->Code[cButModeClipNF], "Clip ");
    strcpy(I->Code[cButModeClipN], "ClpN ");
    strcpy(I->Code[cButModeClipF], "ClpF ");
    strcpy(I->Code[cButModePickAtom], "PkAt ");
    strcpy(I->Code[cButModePickBond], "PkBd ");
    strcpy(I->Code[cButModeTorFrag], "TorF ");
    strcpy(I->Code[cButModeRotFrag], "RotF ");
    strcpy(I->Code[cButModeMovFrag], "MovF ");
    strcpy(I->Code[cButModeLB], " lb  ");
    strcpy(I->Code[cButModeMB], " mb  ");
    strcpy(I->Code[cButModeRB], " rb  ");
    strcpy(I->Code[cButModeAddToLB], "+lb  ");
    strcpy(I->Code[cButModeAddToMB], "+mb  ");
    strcpy(I->Code[cButModeAddToRB], "+rb  ");
    strcpy(I->Code[cButModeOrigAt], "Orig ");
    strcpy(I->Code[cButModeRectAdd], "+lBx ");
    strcpy(I->Code[cButModeRectSub], "-lBx ");
    strcpy(I->Code[cButModeRect], "lbBx ");
    strcpy(I->Code[cButModeNone], "  -  ");
    strcpy(I->Code[cButModeCent], "Cent ");
    strcpy(I->Code[cButModePkTorBnd], "PkTB ");
    strcpy(I->Code[cButModeScaleSlab], "Slab ");
    strcpy(I->Code[cButModeMoveSlab], "MovS ");
    strcpy(I->Code[cButModePickAtom1], "Pk1  ");
    strcpy(I->Code[cButModeMoveAtom], "MovA ");
    strcpy(I->Code[cButModeMenu], "Menu ");
    strcpy(I->Code[cButModeSeleSet], "Sele ");
    strcpy(I->Code[cButModeSeleToggle], "+/-  ");
    strcpy(I->Code[cButModeSeleAddBox], "+Box ");
    strcpy(I->Code[cButModeSeleSubBox], "-Box ");
    strcpy(I->Code[cButModeMoveSlabAndZoom], "MvSZ ");
    strcpy(I->Code[cButModeSimpleClick], "Clik ");
    strcpy(I->Code[cButModeRotDrag], "RotD ");
    strcpy(I->Code[cButModeMovDrag], "MovD ");
    strcpy(I->Code[cButModeMovDragZ], "MvDZ ");
    strcpy(I->Code[cButModeRotObj], "RotO ");
    strcpy(I->Code[cButModeMovObj], "MovO ");
    strcpy(I->Code[cButModeMovObjZ], "MvOZ ");
    strcpy(I->Code[cButModeMovFragZ], "MvFZ ");
    strcpy(I->Code[cButModeMoveAtomZ], "MvAZ ");
    strcpy(I->Code[cButModeDragMol], "DrgM ");
    strcpy(I->Code[cButModeRotView], "RotV ");
    strcpy(I->Code[cButModeMovView], "MovV ");
    strcpy(I->Code[cButModeMovViewZ], "MvVZ ");
    strcpy(I->Code[cButModeDragObj], "DrgO ");
    strcpy(I->Code[cButModeInvMoveSlabAndZoom], "IMSZ ");
    strcpy(I->Code[cButModeInvTransZ], "IMvZ ");
    strcpy(I->Code[cButModeSeleSetBox], " Box ");
    strcpy(I->Code[cButModeInvRotZ], "IRtZ ");
    strcpy(I->Code[cButModeRotL], "RotL " );
    strcpy(I->Code[cButModeMovL], "MovL " );
    strcpy(I->Code[cButModeMvzL], "MvzL " );

    I->active = true;

    I->TextColor[0] = 0.2F;
    I->TextColor[1] = 1.0F;
    I->TextColor[2] = 0.2F;

    I->TextColor1[0] = 0.5F;
    I->TextColor1[1] = 0.5F;
    I->TextColor1[2] = 1.0F;

    I->TextColor2[0] = 0.8F;
    I->TextColor2[1] = 0.8F;
    I->TextColor2[2] = 0.8F;

    I->TextColor3[0] = 1.0F;
    I->TextColor3[1] = 0.5F;
    I->TextColor3[2] = 0.5F;

    OrthoAttach(G, I, cOrthoTool);
    return 1;
  } else
    return 0;
}


/*========================================================================*/
int ButModeCheckPossibleSingleClick(PyMOLGlobals * G, int button, int mod)
{
  int click_button = -1;
  switch (button) {
  case P_GLUT_LEFT_BUTTON:
    click_button = P_GLUT_SINGLE_LEFT;
    break;
  case P_GLUT_MIDDLE_BUTTON:
    click_button = P_GLUT_SINGLE_MIDDLE;
    break;
  case P_GLUT_RIGHT_BUTTON:
    click_button = P_GLUT_SINGLE_RIGHT;
    break;
  }
  if(click_button < 0)
    return false;
  else
    return (ButModeTranslate(G, click_button, mod) >= 0);
}

int ButModeTranslate(PyMOLGlobals * G, int button, int mod)
{
  int mode = cButModeNothing;
  CButMode *I = G->ButMode;
  switch (button) {
  case P_GLUT_LEFT_BUTTON:
    mode = 0;
    break;
  case P_GLUT_MIDDLE_BUTTON:
    mode = 1;
    break;
  case P_GLUT_RIGHT_BUTTON:
    mode = 2;
    break;
  case P_GLUT_BUTTON_SCROLL_FORWARD:
  case P_GLUT_BUTTON_SCROLL_BACKWARD:
    switch (mod) {
    case 0:
      mode = 12;
      break;
    case cOrthoSHIFT:
      mode = 13;
      break;
    case cOrthoCTRL:
      mode = 14;
      break;
    case (cOrthoCTRL + cOrthoSHIFT):
      mode = 15;
    }
    mod = 0;
    switch (I->Mode[mode]) {
    case cButModeScaleSlab:
      if(button == P_GLUT_BUTTON_SCROLL_FORWARD) {
        return cButModeScaleSlabExpand;
      } else {
        return cButModeScaleSlabShrink;
      }
      break;
    case cButModeMoveSlab:
      if(button == P_GLUT_BUTTON_SCROLL_FORWARD) {
        return cButModeMoveSlabForward;
      } else {
        return cButModeMoveSlabBackward;
      }
      break;
    case cButModeMoveSlabAndZoom:
      if(button == P_GLUT_BUTTON_SCROLL_FORWARD) {
        return cButModeMoveSlabAndZoomForward;
      } else {
        return cButModeMoveSlabAndZoomBackward;
      }
      break;
    case cButModeInvMoveSlabAndZoom:
      if(button != P_GLUT_BUTTON_SCROLL_FORWARD) {
        return cButModeMoveSlabAndZoomForward;
      } else {
        return cButModeMoveSlabAndZoomBackward;
      }
      break;
    case cButModeTransZ:
      if(button == P_GLUT_BUTTON_SCROLL_FORWARD) {
        return cButModeZoomForward;
      } else {
        return cButModeZoomBackward;
      }
      break;
    case cButModeInvTransZ:
      if(button != P_GLUT_BUTTON_SCROLL_FORWARD) {
        return cButModeZoomForward;
      } else {
        return cButModeZoomBackward;
      }
      break;
    }
    return -1;
    break;
  case P_GLUT_DOUBLE_LEFT:
  case P_GLUT_DOUBLE_MIDDLE:
  case P_GLUT_DOUBLE_RIGHT:
  case P_GLUT_SINGLE_LEFT:
  case P_GLUT_SINGLE_MIDDLE:
  case P_GLUT_SINGLE_RIGHT:
    switch (button) {
    case P_GLUT_DOUBLE_LEFT:
      mode = 16;
      break;
    case P_GLUT_DOUBLE_MIDDLE:
      mode = 17;
      break;
    case P_GLUT_DOUBLE_RIGHT:
      mode = 18;
      break;
    case P_GLUT_SINGLE_LEFT:
      mode = 19;
      break;
    case P_GLUT_SINGLE_MIDDLE:
      mode = 20;
      break;
    case P_GLUT_SINGLE_RIGHT:
      mode = 21;
      break;
    }
    switch (mod) {
    case cOrthoSHIFT:
      mode += 6;
      break;
    case cOrthoCTRL:
      mode += 12;
      break;
    case (cOrthoCTRL + cOrthoSHIFT):
      mode += 18;
      break;
    case cOrthoALT:
      mode += 24;
      break;
    case (cOrthoALT + cOrthoSHIFT):
      mode += 30;
      break;
    case (cOrthoALT + cOrthoCTRL):
      mode += 36;
      break;
    case (cOrthoALT + cOrthoCTRL + cOrthoSHIFT):
      mode += 42;
      break;
    }
    mod = 0;
    break;
  }
  switch (mod) {
  case 0:
    break;
  case cOrthoSHIFT:
    mode += 3;
    break;
  case cOrthoCTRL:
    mode += 6;
    break;
  case (cOrthoCTRL + cOrthoSHIFT):
    mode += 9;
    break;
  case cOrthoALT:
    mode += 68;
    break;
  case (cOrthoALT + cOrthoSHIFT):
    mode += 71;
    break;
  case (cOrthoALT + cOrthoCTRL):
    mode += 74;
    break;
  case (cOrthoALT + cOrthoCTRL + cOrthoSHIFT):
    mode += 77;
    break;
  }
  return (I->Mode[mode]);
}
